From 98db678fadc3da51ecd00296bf5bc25a930d931d Mon Sep 17 00:00:00 2001 From: Nicolas Trueba Date: Thu, 24 Apr 2025 16:33:27 -0400 Subject: [PATCH 1/8] this is my first attempt at using git --- visualization/xrt_metadata.py | 274 ++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 visualization/xrt_metadata.py diff --git a/visualization/xrt_metadata.py b/visualization/xrt_metadata.py new file mode 100644 index 000000000..696acc8c3 --- /dev/null +++ b/visualization/xrt_metadata.py @@ -0,0 +1,274 @@ + +from astropy.io import fits +import astropy.units as u + +import numpy as np + +from astropy.coordinates import SkyCoord + +from sunpy.coordinates import frames +from astropy.time import Time +import scipy.io as sio +from scipy.io import readsav +import urllib.request +import sys +sys.path.append('/Users/ntrueba/SOLAR/code/xrtstuff/') # +import xrt_metadata_plot as xplt +import xrt_metadata_download as xfetch + + + +from ipywidgets import Layout, interact, IntSlider,IntProgress, RadioButtons, FloatSlider,FloatRangeSlider,fixed + +#This module is designed to download and manage XRT observation metadata for previewing purposes + +# The first part contains the xrt_meta object, which is how we organize the metadata +# After this, we have the download functions + +class xrt_meta: + # The xrt_meta object accepts a list of .fits headers and organizes them + # Crucially, it separates metadata quantities by xrt filter + + def __init__(self, xrt_downloaded_files): + header_lis = xfetch.fetch_metadata(xrt_downloaded_files) + self.head_lis = header_lis # stores the unfiltered header list + self.fkey_lis = ['EC_FW1_','EC_FW2_'] # xrt filter keywords in fits header, used by sort_xfilter function to do xrt filter sorting + self.check_bool = True # Testing parameter, remove + # upon initialization, the object filters observations and creates the .metadata dictonary + self.metadata = self.organize_metadata() + + + def organize_metadata(self): + # testing statement, remove + if self.check_bool: + print('Meta') + + # sort observations by filter. + # filter_lis is a simple list containing strings for all filters in the data set (n_filter) + # hbtest is a list of length n_filter, each containing a list of headers for all observations for each filter + hbtest, filter_lis = self.sort_xfilters() + + + # Get list of DATE_OBS for each observation + # time_f_lis: obseration times for xrt obs, separated by xrt filter (same shape as hbtest) + # time_lis is a flattened version of this, it is not sorted and should only be with the get_time_range() function + time_lis, time_f_lis = self.get_time_grid(hbtest) + + # tmin and tmax are the time of first and last observations + # dts: the time separation in seconds, the difference between first and last observations + tmin, tmax, dts = self.get_time_range(time_lis) + + # filtered list of delta time in seconds - the difference between each observation time in seconds relative to the first observation + # this is very useful for quickly sincronizing observations when plotting + delta_t_lis = self.get_delta_T(time_f_lis, tmin) + + # The filtered metadata dictionary - more quantities can be easily added here + #asdasf + xdata_set = {'HEADER_LIST': hbtest, + 'FILTER_LIST': filter_lis, + 'TIME_LIST': time_f_lis, + 'MIN_TIME': tmin, + 'MAX_TIME': tmax, + 'TIME_RANGE':dts, + 'DELTA_TIME_LIST': delta_t_lis} + return xdata_set + + def sort_xfilters(self): + #test statement + if self.check_bool: + print('X1') + + # this function separates an unfilted list of .fits headers from a set of xrt observations and separates them by filter + hlen = len(self.head_lis) # length of input header list + filter_lis = [] # this will store the list of xrt filters found in our data set + header_f_lis = [] # this will store the xrt filter-sorted fits headers as separate lists + + for i in range(hlen): + fkey0 = self.fkey_lis[0] # xrt header keyword + fkey1 = self.fkey_lis[1] # xrt header keyword + header_i = self.head_lis[i] # current header + filter_n0 = header_i[fkey0] #extracting filter #1 from header + filter_n1 = header_i[fkey1] #extracting filter #2 from header + if False: # deprecated option to filter by FOV (partial vs FULL), can re-introduce + fovx = header_i['FOVX'] + filter_n2 = '' + if (fovx > 1000.0): + filter_n2 = '(FULL)' + + # the combined filter string + filter_n = filter_n0 +'/'+filter_n1#+filter_n2 + + # is this a new filter? + if filter_n not in filter_lis: + #yes? + filter_lis.append(filter_n) # add it to the list of filters we have in our data set + header_f_lis.append([header_i]) # add a new list of headers for this filter, containing the current header + else: + # no? + filter_i = filter_lis.index(filter_n) # what is the index of the filter? + header_f_lis[filter_i].append(header_i) # append this header to the list corresponding to this filter + # filter_lis are appended simultaneously, guaranteeing that filter_lis is organized the same as the first dimension of header_f_lis + # this process only captures the filters in our observations with no need to hard code anythin + return header_f_lis, filter_lis + + def get_time_grid(self, header_f_lis): + #test statement + if self.check_bool: + print('X2') + time_lis = [] + time_f_lis = [] + for i in range(len(header_f_lis)): + tlis = [] + for j in range(len(header_f_lis[i])): + htemp = header_f_lis[i][j] + htime = htemp['DATE_OBS'] + tlis.append(htime) + time_lis.append(htime) + time_f_lis.append(tlis) + return time_lis, time_f_lis + + def get_time_range(self, time_lis): + # check statement + if self.check_bool: + print('X3') + + # this function gives you: + # min_time : earliest obs + # max_time : latest obs + # time_range : difference between these two times in seconds + + time_obj = Time(np.asarray(time_lis), format='isot', scale='utc') + time_delt = time_obj - time_obj[0] + time_delt = time_delt.value*24.0*3600.0 + + min_i = np.argmin(time_delt) + max_i = np.argmax(time_delt) + min_time = time_lis[min_i] + max_time = time_lis[max_i] + + time_range = Time(max_time, format='isot', scale='utc') - Time(min_time, format='isot', scale='utc') + time_range = time_range.value*24.0*3600.0 + return min_time, max_time, time_range + + def get_delta_T(self, time_lis, min_time): + + # gives you an xrt-filter separated list: + # For each filter, an array of observation times relative to the earliest observation in the data set, in seconds + + if self.check_bool: + print('X4') + + dtlis = [] + for i in range(len(time_lis)): + dtt = Time(time_lis[i], format='isot', scale='utc')-Time(min_time, format='isot', scale='utc') + dtlis.append(dtt.value*24.0*3600.0) + return dtlis + + def get_frame_lis(self): + + # gives you an xrt-filter separated list: + # For each filter, a list containing coordinates for the four corners of the FOV for each observation + frame_f_lis = [] + bhl = self.metadata['HEADER_LIST'] + for i in range(len(bhl)): + # i represents each filter_i + frame_lis = [] + for j in range(len(bhl[i])): + # j is each obs in filter_i + frame_lis.append([self.get_xframet(bhl[i][j])]) # calling the function for each obs - currently using floats - see get_xframe for generalized version + frame_f_lis.append(frame_lis) + return frame_f_lis + + def get_xframet(self, head_i): + + # generates a square of coordines for the FOV for each obs + # simple function, needs to be generalized to include spacecraft roll + # using float values instead of astropy coordinates in a specific frame - might be desirable to work in a sunpy ecosystem + + fovx, fovy = head_i['FOVX'], head_i['FOVY'] + xcen, ycen = head_i['XCEN'], head_i['YCEN'] + + xc = (xcen + np.asarray([-1.0, 1.0, 1.0,-1.0,-1.0])*fovx*0.5) * u.arcsec + yc = (ycen + np.asarray([-1.0,-1.0, 1.0,1.0,-1.0])*fovy*0.5) * u.arcsec + return xc,yc + + def get_xframe(self, head_i, map_in = None): + fovx, fovy = head_i['FOVX'], head_i['FOVY'] + xcen, ycen = head_i['XCEN'], head_i['YCEN'] + + xc = (xcen + np.asarray([-1.0, 1.0, 1.0,-1.0,-1.0])*fovx*0.5) * u.arcsec + yc = (ycen + np.asarray([-1.0,-1.0, 1.0,1.0,-1.0])*fovy*0.5) * u.arcsec + obs_t = head_i['DATE_OBS'] + if map_in is None: + coords_out = SkyCoord(xc, yc, frame = frames.Helioprojective, observer='earth', obstime=obs_t) + else: + coords_out = SkyCoord(xc, yc, frame = map_in.coordinate_frame) + + return coords_out + + + def plot_preview(self): + # plot preamble here, needs to be cleaned up + # because we want to have an interactive plot, we don't want to recalculate some of these basic things every time the plot refreshes, so we calculate it here + + + #self.head_lis[0]['RSUN_OBS'] + trange = self.metadata['TIME_RANGE']# Time from first frame to last in seconds + filter_lis = self.metadata['FILTER_LIST'].copy() + flen = len(filter_lis) + filter_lis_b = ['All'] + filter_lis_b = filter_lis_b + filter_lis #filter list for interactive plot, including the 'All' option to plot all filters + + fov_lis = self.get_frame_lis()# FOV list (n_filters, n_frames(filter)) + time_lis0 = self.metadata['DELTA_TIME_LIST'] # Frame time in seconds relative to first frame (n_filters, n_frames(filter)) + time_lis_abs = self.metadata['TIME_LIST'] # Absolute time for each frame (n_filters, n_frames(filter)) + col_vals = xplt.get_pcol(flen) + + # Set-up Solar limb and grid lines + ang_arr = np.linspace(0.0,6.28,100) + lon_arr = np.sin(np.arange(0.0,0.01+np.pi/2.0,np.pi/12.0)) + rsun_p = 944.0 # this should not be hard-coded + xca, yca = np.cos(ang_arr)*rsun_p, np.sin(ang_arr)*rsun_p + yla, zla = [], [] + dt_bool = True #print the time distance between XRT frame and current frame? + fill_bool = True #use fill between function? makes preview slower + + + + t0d = np.datetime64(self.metadata['MIN_TIME']) #T0 for the observations + trp = [t0d,(np.timedelta64(int(np.round(trange)),'s')+t0d)] #Range for the full timeline + + for j in range(len(lon_arr)): + zval = rsun_p*lon_arr[j] + yline = rsun_p*((1.0 - lon_arr[j]**2.0)**0.5) + zla.append(zval) + yla.append(yline) + inputs = {} + inputs['xca'] = xca + inputs['yca'] = yca + inputs['rsun_p'] = rsun_p + inputs['zla'] = zla + inputs['lon_arr'] = lon_arr + inputs['yla'] = yla + inputs['xmeta'] = self + inputs['flen'] = flen + inputs['t0d'] = t0d + inputs['trp'] = trp + inputs['dt_bool'] = dt_bool + inputs['fov_lis']= fov_lis + inputs['fill_bool'] = fill_bool + inputs['filter_lis'] = filter_lis + inputs['time_lis0'] = time_lis0 + inputs['col_vals'] = col_vals + inputs['time_lis_abs'] = time_lis_abs + + # need to generalize when to use interactive vs animation etc + interact(xplt.interactive_plot, + mini_tline = True, + filter_selection = RadioButtons(options=filter_lis_b,index=0), + zoom_v=FloatSlider(value=1.0,min=0.5,max=2.0, layout=Layout(width='70%')), + tzoom = FloatRangeSlider(value=[0.0,1.0],min=0,max=1.0,step=0.01, layout=Layout(width='70%')), + time_ind=IntSlider(value=50,min=0,max=99,step=1, layout=Layout(width='70%')), + inputs = fixed(inputs)) + + return \ No newline at end of file From 681de8b1ed0e939e76c17325fb8b607b28994a25 Mon Sep 17 00:00:00 2001 From: Nicolas Trueba Date: Thu, 24 Apr 2025 16:56:33 -0400 Subject: [PATCH 2/8] testing line of code --- visualization/xrt_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visualization/xrt_metadata.py b/visualization/xrt_metadata.py index 696acc8c3..21d79c866 100644 --- a/visualization/xrt_metadata.py +++ b/visualization/xrt_metadata.py @@ -16,7 +16,7 @@ import xrt_metadata_plot as xplt import xrt_metadata_download as xfetch - +print('THIS IS A GITHUB TEST') from ipywidgets import Layout, interact, IntSlider,IntProgress, RadioButtons, FloatSlider,FloatRangeSlider,fixed From 2a57a0115e06934b2051ec557880dc1d7724dff2 Mon Sep 17 00:00:00 2001 From: Nicolas Trueba Date: Tue, 6 May 2025 15:24:56 -0400 Subject: [PATCH 3/8] Example notebook for FOV preview tool --- examples/testing_code.py | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 examples/testing_code.py diff --git a/examples/testing_code.py b/examples/testing_code.py new file mode 100644 index 000000000..1138f9743 --- /dev/null +++ b/examples/testing_code.py @@ -0,0 +1,70 @@ +""" +============================= +FOV tool test +============================= + +We are just testing stuff here +""" + +import time as timer +import matplotlib.pyplot as plt +from astropy.io import fits +import astropy.time +import astropy.units as u + +import sunpy +import ipywidgets as widgets +from ipywidgets import Layout, interact, IntSlider,IntProgress, RadioButtons, FloatSlider,FloatRangeSlider +import numpy as np +import matplotlib.pyplot as plt +from ipywidgets import interact +import numpy as np +from sunpy.net import Fido +from sunpy.net import attrs as a +import matplotlib.dates as mdates + +import sys +sys.path.append('/Users/ntrueba/SOLAR/code/GIT/xrtpy/xrtpy/visualization/fov/') # +import xrt_metadata as xmet +import xrt_metadata_plot as xplt + +############################################################################## +# FIDO SEARCH +# The tool is meant to preview metadata within the fido ecosystem - this means that fido is the slowest part of the code unless you are looking at a very small time frame + +# Define the time range of interest for solar observations +time_range = a.Time("2011-06-07 06:00:00", "2011-06-07 06:45:54") +# Specify the instrument as 'xrt' to search for Hinode X-Ray Telescope data +instrument = a.Instrument("xrt") + + +# This will return a catalog of available XRT data during t≠he specified period +start = timer.time() +xrt_downloaded_files = Fido.search(time_range, instrument) +end = timer.time() +length = end - start +print("It took", length, "seconds!") + + + +############################################################################## +#Metadata extraction and the XRT_meta structure +#The first function accepts the output of the fido search as an input and retrieves the corresponding metadata without having to download the data. +#By default, it downloads the level0 metadata (same as SSWIDL) which is very quick when dealing with many files. If you want the level 1 metadata, the syntax is (.., fast_bool = False) - this is only recommended for short observations (< 1hr), as it can retrieve metadata at a rate 5 obs/second +#The second function creates a handy metadata object for all the observations +#it contains a list of headers (xmeta.head_lis) +#and a dictonary (xmeta.metadata) containing important filter-separated quantities +#This could be super handy, as you get much more information than what fido gives you, so you can download observations that meet very specific conditions - let me know if you want something specific included here + + + +xmeta = xmet.xrt_meta(xrt_downloaded_files) + +############################################################################## +#Plotting backend +#The %matplotlib inline works best when using notebooks to avoid flickering. Ipywidgets is not available with HTML, but you can uncomment this line when you download the notebook. + + +#%matplotlib inline + +xmeta.plot_preview() \ No newline at end of file From 844c82bd6be1b90724459f75669b0c73a3e5a5a7 Mon Sep 17 00:00:00 2001 From: Nicolas Trueba Date: Tue, 6 May 2025 15:27:38 -0400 Subject: [PATCH 4/8] FOV tool code --- visualization/fov/testing_code.py | 48 ++++ visualization/fov/xrt_metadata.py | 275 ++++++++++++++++++++ visualization/fov/xrt_metadata_download.py | 174 +++++++++++++ visualization/fov/xrt_metadata_plot.py | 288 +++++++++++++++++++++ 4 files changed, 785 insertions(+) create mode 100644 visualization/fov/testing_code.py create mode 100644 visualization/fov/xrt_metadata.py create mode 100644 visualization/fov/xrt_metadata_download.py create mode 100644 visualization/fov/xrt_metadata_plot.py diff --git a/visualization/fov/testing_code.py b/visualization/fov/testing_code.py new file mode 100644 index 000000000..48754a545 --- /dev/null +++ b/visualization/fov/testing_code.py @@ -0,0 +1,48 @@ +import time as timer +import matplotlib.pyplot as plt +from astropy.io import fits +import astropy.time +import astropy.units as u + +import sunpy +import ipywidgets as widgets +from ipywidgets import Layout, interact, IntSlider,IntProgress, RadioButtons, FloatSlider,FloatRangeSlider +import numpy as np +import matplotlib.pyplot as plt +from ipywidgets import interact +import numpy as np +from sunpy.net import Fido +from sunpy.net import attrs as a +import matplotlib.dates as mdates + +import sys +sys.path.append('/Users/ntrueba/SOLAR/code/GIT/xrtpy/xrtpy/visualization/fov/') # +import xrt_metadata as xmet +import xrt_metadata_plot as xplt + + +# Define the time range of interest for solar observations +#time_range = a.Time("2011-06-07 06:00:00", "2011-06-07 07:30:54") +time_range = a.Time("2011-06-07 06:00:00", "2011-06-08 07:30:54") +# Specify the instrument as 'xrt' to search for Hinode X-Ray Telescope data +instrument = a.Instrument("xrt") + + +# This will return a catalog of available XRT data during t≠he specified period +start = timer.time() +xrt_downloaded_files = Fido.search(time_range, instrument) +end = timer.time() +length = end - start +print("It took", length, "seconds!") + + + +### + +xmeta = xmet.xrt_meta(xrt_downloaded_files) + +#### + +#%matplotlib inline + +xmeta.plot_preview() \ No newline at end of file diff --git a/visualization/fov/xrt_metadata.py b/visualization/fov/xrt_metadata.py new file mode 100644 index 000000000..8c14099aa --- /dev/null +++ b/visualization/fov/xrt_metadata.py @@ -0,0 +1,275 @@ + +from astropy.io import fits +import astropy.units as u + +import numpy as np + +from astropy.coordinates import SkyCoord + +from sunpy.coordinates import frames +from astropy.time import Time +import scipy.io as sio +from scipy.io import readsav +import urllib.request +import sys + +sys.path.append('/Users/ntrueba/SOLAR/code/GIT/xrtpy/xrtpy/visualization/fov/') +import xrt_metadata_plot as xplt +import xrt_metadata_download as xfetch + +print('THIS IS A GITHUB TEST') + +from ipywidgets import Layout, interact, IntSlider,IntProgress, RadioButtons, FloatSlider,FloatRangeSlider,fixed + +#This module is designed to download and manage XRT observation metadata for previewing purposes + +# The first part contains the xrt_meta object, which is how we organize the metadata +# After this, we have the download functions + +class xrt_meta: + # The xrt_meta object accepts a list of .fits headers and organizes them + # Crucially, it separates metadata quantities by xrt filter + + def __init__(self, xrt_downloaded_files): + header_lis = xfetch.fetch_metadata(xrt_downloaded_files) + self.head_lis = header_lis # stores the unfiltered header list + self.fkey_lis = ['EC_FW1_','EC_FW2_'] # xrt filter keywords in fits header, used by sort_xfilter function to do xrt filter sorting + self.check_bool = True # Testing parameter, remove + # upon initialization, the object filters observations and creates the .metadata dictonary + self.metadata = self.organize_metadata() + + + def organize_metadata(self): + # testing statement, remove + if self.check_bool: + print('Meta') + + # sort observations by filter. + # filter_lis is a simple list containing strings for all filters in the data set (n_filter) + # hbtest is a list of length n_filter, each containing a list of headers for all observations for each filter + hbtest, filter_lis = self.sort_xfilters() + + + # Get list of DATE_OBS for each observation + # time_f_lis: obseration times for xrt obs, separated by xrt filter (same shape as hbtest) + # time_lis is a flattened version of this, it is not sorted and should only be with the get_time_range() function + time_lis, time_f_lis = self.get_time_grid(hbtest) + + # tmin and tmax are the time of first and last observations + # dts: the time separation in seconds, the difference between first and last observations + tmin, tmax, dts = self.get_time_range(time_lis) + + # filtered list of delta time in seconds - the difference between each observation time in seconds relative to the first observation + # this is very useful for quickly sincronizing observations when plotting + delta_t_lis = self.get_delta_T(time_f_lis, tmin) + + # The filtered metadata dictionary - more quantities can be easily added here + #asdasf + xdata_set = {'HEADER_LIST': hbtest, + 'FILTER_LIST': filter_lis, + 'TIME_LIST': time_f_lis, + 'MIN_TIME': tmin, + 'MAX_TIME': tmax, + 'TIME_RANGE':dts, + 'DELTA_TIME_LIST': delta_t_lis} + return xdata_set + + def sort_xfilters(self): + #test statement + if self.check_bool: + print('X1') + + # this function separates an unfilted list of .fits headers from a set of xrt observations and separates them by filter + hlen = len(self.head_lis) # length of input header list + filter_lis = [] # this will store the list of xrt filters found in our data set + header_f_lis = [] # this will store the xrt filter-sorted fits headers as separate lists + + for i in range(hlen): + fkey0 = self.fkey_lis[0] # xrt header keyword + fkey1 = self.fkey_lis[1] # xrt header keyword + header_i = self.head_lis[i] # current header + filter_n0 = header_i[fkey0] #extracting filter #1 from header + filter_n1 = header_i[fkey1] #extracting filter #2 from header + if False: # deprecated option to filter by FOV (partial vs FULL), can re-introduce + fovx = header_i['FOVX'] + filter_n2 = '' + if (fovx > 1000.0): + filter_n2 = '(FULL)' + + # the combined filter string + filter_n = filter_n0 +'/'+filter_n1#+filter_n2 + + # is this a new filter? + if filter_n not in filter_lis: + #yes? + filter_lis.append(filter_n) # add it to the list of filters we have in our data set + header_f_lis.append([header_i]) # add a new list of headers for this filter, containing the current header + else: + # no? + filter_i = filter_lis.index(filter_n) # what is the index of the filter? + header_f_lis[filter_i].append(header_i) # append this header to the list corresponding to this filter + # filter_lis are appended simultaneously, guaranteeing that filter_lis is organized the same as the first dimension of header_f_lis + # this process only captures the filters in our observations with no need to hard code anythin + return header_f_lis, filter_lis + + def get_time_grid(self, header_f_lis): + #test statement + if self.check_bool: + print('X2') + time_lis = [] + time_f_lis = [] + for i in range(len(header_f_lis)): + tlis = [] + for j in range(len(header_f_lis[i])): + htemp = header_f_lis[i][j] + htime = htemp['DATE_OBS'] + tlis.append(htime) + time_lis.append(htime) + time_f_lis.append(tlis) + return time_lis, time_f_lis + + def get_time_range(self, time_lis): + # check statement + if self.check_bool: + print('X3') + + # this function gives you: + # min_time : earliest obs + # max_time : latest obs + # time_range : difference between these two times in seconds + + time_obj = Time(np.asarray(time_lis), format='isot', scale='utc') + time_delt = time_obj - time_obj[0] + time_delt = time_delt.value*24.0*3600.0 + + min_i = np.argmin(time_delt) + max_i = np.argmax(time_delt) + min_time = time_lis[min_i] + max_time = time_lis[max_i] + + time_range = Time(max_time, format='isot', scale='utc') - Time(min_time, format='isot', scale='utc') + time_range = time_range.value*24.0*3600.0 + return min_time, max_time, time_range + + def get_delta_T(self, time_lis, min_time): + + # gives you an xrt-filter separated list: + # For each filter, an array of observation times relative to the earliest observation in the data set, in seconds + + if self.check_bool: + print('X4') + + dtlis = [] + for i in range(len(time_lis)): + dtt = Time(time_lis[i], format='isot', scale='utc')-Time(min_time, format='isot', scale='utc') + dtlis.append(dtt.value*24.0*3600.0) + return dtlis + + def get_frame_lis(self): + + # gives you an xrt-filter separated list: + # For each filter, a list containing coordinates for the four corners of the FOV for each observation + frame_f_lis = [] + bhl = self.metadata['HEADER_LIST'] + for i in range(len(bhl)): + # i represents each filter_i + frame_lis = [] + for j in range(len(bhl[i])): + # j is each obs in filter_i + frame_lis.append([self.get_xframet(bhl[i][j])]) # calling the function for each obs - currently using floats - see get_xframe for generalized version + frame_f_lis.append(frame_lis) + return frame_f_lis + + def get_xframet(self, head_i): + + # generates a square of coordines for the FOV for each obs + # simple function, needs to be generalized to include spacecraft roll + # using float values instead of astropy coordinates in a specific frame - might be desirable to work in a sunpy ecosystem + + fovx, fovy = head_i['FOVX'], head_i['FOVY'] + xcen, ycen = head_i['XCEN'], head_i['YCEN'] + + xc = (xcen + np.asarray([-1.0, 1.0, 1.0,-1.0,-1.0])*fovx*0.5) * u.arcsec + yc = (ycen + np.asarray([-1.0,-1.0, 1.0,1.0,-1.0])*fovy*0.5) * u.arcsec + return xc,yc + + def get_xframe(self, head_i, map_in = None): + fovx, fovy = head_i['FOVX'], head_i['FOVY'] + xcen, ycen = head_i['XCEN'], head_i['YCEN'] + + xc = (xcen + np.asarray([-1.0, 1.0, 1.0,-1.0,-1.0])*fovx*0.5) * u.arcsec + yc = (ycen + np.asarray([-1.0,-1.0, 1.0,1.0,-1.0])*fovy*0.5) * u.arcsec + obs_t = head_i['DATE_OBS'] + if map_in is None: + coords_out = SkyCoord(xc, yc, frame = frames.Helioprojective, observer='earth', obstime=obs_t) + else: + coords_out = SkyCoord(xc, yc, frame = map_in.coordinate_frame) + + return coords_out + + + def plot_preview(self): + # plot preamble here, needs to be cleaned up + # because we want to have an interactive plot, we don't want to recalculate some of these basic things every time the plot refreshes, so we calculate it here + + + #self.head_lis[0]['RSUN_OBS'] + trange = self.metadata['TIME_RANGE']# Time from first frame to last in seconds + filter_lis = self.metadata['FILTER_LIST'].copy() + flen = len(filter_lis) + filter_lis_b = ['All'] + filter_lis_b = filter_lis_b + filter_lis #filter list for interactive plot, including the 'All' option to plot all filters + + fov_lis = self.get_frame_lis()# FOV list (n_filters, n_frames(filter)) + time_lis0 = self.metadata['DELTA_TIME_LIST'] # Frame time in seconds relative to first frame (n_filters, n_frames(filter)) + time_lis_abs = self.metadata['TIME_LIST'] # Absolute time for each frame (n_filters, n_frames(filter)) + col_vals = xplt.get_pcol(flen) + + # Set-up Solar limb and grid lines + ang_arr = np.linspace(0.0,6.28,100) + lon_arr = np.sin(np.arange(0.0,0.01+np.pi/2.0,np.pi/12.0)) + rsun_p = 944.0 # this should not be hard-coded + xca, yca = np.cos(ang_arr)*rsun_p, np.sin(ang_arr)*rsun_p + yla, zla = [], [] + dt_bool = True #print the time distance between XRT frame and current frame? + fill_bool = True #use fill between function? makes preview slower + + + + t0d = np.datetime64(self.metadata['MIN_TIME']) #T0 for the observations + trp = [t0d,(np.timedelta64(int(np.round(trange)),'s')+t0d)] #Range for the full timeline + + for j in range(len(lon_arr)): + zval = rsun_p*lon_arr[j] + yline = rsun_p*((1.0 - lon_arr[j]**2.0)**0.5) + zla.append(zval) + yla.append(yline) + inputs = {} + inputs['xca'] = xca + inputs['yca'] = yca + inputs['rsun_p'] = rsun_p + inputs['zla'] = zla + inputs['lon_arr'] = lon_arr + inputs['yla'] = yla + inputs['xmeta'] = self + inputs['flen'] = flen + inputs['t0d'] = t0d + inputs['trp'] = trp + inputs['dt_bool'] = dt_bool + inputs['fov_lis']= fov_lis + inputs['fill_bool'] = fill_bool + inputs['filter_lis'] = filter_lis + inputs['time_lis0'] = time_lis0 + inputs['col_vals'] = col_vals + inputs['time_lis_abs'] = time_lis_abs + + # need to generalize when to use interactive vs animation etc + interact(xplt.interactive_plot, + mini_tline = True, + filter_selection = RadioButtons(options=filter_lis_b,index=0), + zoom_v=FloatSlider(value=1.0,min=0.5,max=2.0, layout=Layout(width='70%')), + tzoom = FloatRangeSlider(value=[0.0,1.0],min=0,max=1.0,step=0.01, layout=Layout(width='70%')), + time_ind=IntSlider(value=50,min=0,max=99,step=1, layout=Layout(width='70%')), + inputs = fixed(inputs)) + + return \ No newline at end of file diff --git a/visualization/fov/xrt_metadata_download.py b/visualization/fov/xrt_metadata_download.py new file mode 100644 index 000000000..3d97632f2 --- /dev/null +++ b/visualization/fov/xrt_metadata_download.py @@ -0,0 +1,174 @@ +from astropy.io import fits +import astropy.units as u + +import numpy as np + +from astropy.coordinates import SkyCoord + +from sunpy.coordinates import frames +from astropy.time import Time +import scipy.io as sio +from scipy.io import readsav +import urllib.request + + +def download_metadata(xrt_downloaded_files, filen, overwrite=False): + url_lis = xrt_downloaded_files[0][:]['fileid'] + url_str_lis = [] + for i in range(len(url_lis)): + url_str_lis.append(url_lis[i]) + primary_hdu = fits.PrimaryHDU(data=np.ones((3, 3))) + c1 = fits.Column(name='URL', array=url_str_lis, format='100A') + c2 = fits.Column(name='header_int', array=np.asarray(range(len(url_str_lis)))+2, format='J') + table_hdu = fits.BinTableHDU.from_columns([c1, c2]) + + hdul2 = fits.HDUList([primary_hdu, table_hdu]) + #hlis = [] + + for i in range(len(url_str_lis)): + if (i%10 == 0): + print(int(1000.0*i/len(url_str_lis))/10.0,'%') + fsspec_kwargs = {"block_size": 100_000, "cache_type": "bytes"} + with fits.open(url_lis[i], use_fsspec=True, fsspec_kwargs=fsspec_kwargs) as hdul: + #hlis.append(hdul[0].header) + # Download a single header + t_header = hdul[0].header + image_hdu = fits.ImageHDU(data=np.ones((100, 100)), header=t_header,name="header"+str(i)) + hdul2.append(image_hdu) + return hdul2 + + #hdul2.writeto(filen,overwrite=overwrite) + +def date_to_meta(xrt_download_list): + time_lis = xrt_download_list[0]['Start Time'] + year_lis = xrt_download_list[0]['Start Time'].ymdhms.year + month_lis = xrt_download_list[0]['Start Time'].ymdhms.month + day_lis = xrt_download_list[0]['Start Time'].ymdhms.day + new_date = [] + file_lis = [] + for i in range(len(time_lis)): + year_str = str(year_lis[i]) + month_str = str(month_lis[i]) + day_str = str(day_lis[i]) + if len(day_str) < 2: + day_str = '0'+day_str + if len(month_str) < 2: + month_str = '0'+month_str + ndatei = year_str + month_str+ day_str + if ndatei in file_lis: + new_date.append(file_lis.index(ndatei)) + #new_date[file_lis.index(ndatei)].append(ndatei) + else: + file_lis.append(ndatei) + new_date.append(file_lis.index(ndatei)) + #print(ndatei,file_lis.index(ndatei)) + return file_lis, new_date + +def get_urls(file_n_lis, ggg): + nfile = len(file_n_lis) + geny_lis = [] + for i in range(nfile): + find_url = 'xrt'+file_n_lis[i] + findex = ggg.find(find_url) + gen_fn = ggg[findex:findex+35] + findex2 = gen_fn.find('geny') + gen_fn = gen_fn[:findex2+4] + geny_lis.append(gen_fn) + return geny_lis + +def get_metafile(geny_lis): + url_start = 'https://sot.lmsal.com/data/sot/metadata/sswdb/hinode/xrt/xrt_genxcat/' + ngeny = len(geny_lis) + meta_lis = [] + for i in range(ngeny): + print(i) + gen_fn = geny_lis[i] + f, h = urllib.request.urlretrieve(url_start + gen_fn) + print(i) + data2 = readsav(f)["p0"] + data_dict2 = {k : data2[k] for k in data2.dtype.names} + meta_lis.append(data_dict2) + return meta_lis + +#def mk_meta_header(meta_lis): + #print(data_dict2['DATE_OBS']) + +def meta_to_dict(data_dict, di): + dkeys = data_dict.keys() + hdict = {} + for dki in dkeys: + try: + hdict[dki] = data_dict[dki][di].decode('ascii') + except: + hdict[dki] = data_dict[dki][di] + return hdict + +def match_vso_to_cat(data_dict_lis, cat_fi, xrt_download): + + n_dict = len(data_dict_lis) + cat_time_lis = [] + for i in range(n_dict): + data_dict = data_dict_lis[i] + date_obs_cat = data_dict['DATE_OBS'] + cat_len = len(date_obs_cat) + cat_str = [] + for cat_bin in date_obs_cat: + cat_str.append(cat_bin.decode('ascii')) + cat_time = Time(np.asarray(cat_str), format='isot', scale='utc') + cat_time_lis.append(cat_time) + #print('yo') + min_ti_lis = [] + delt_lis = [] + delt_lisp = [] + delt_lism = [] + header_lis = [] + for i in range(len(xrt_download[0]['Start Time'])): + cat_time = cat_time_lis[cat_fi[i]] + stime = xrt_download[0]['Start Time'][i] + delt = cat_time - stime + delt = delt.value*24.0*3600.0 + min_ti = np.argmin(np.abs(delt)) + min_ti_lis.append(min_ti) + delt_lis.append(delt[min_ti]) + try: + delt_lisp.append(delt[min_ti+1]) + delt_lism.append(delt[min_ti-1]) + except: + print() + header_lis.append(meta_to_dict(data_dict_lis[cat_fi[i]],min_ti)) + return header_lis + +def get_html_lis(): + url_start = 'https://sot.lmsal.com/data/sot/metadata/sswdb/hinode/xrt/xrt_genxcat/' + with urllib.request.urlopen(url_start) as response: + + html = response.read() + #print(response.info()) + #print() + ggg = html.decode('utf-8') + return ggg + +def download_metadata_fast(xrt_downloaded_files, ggg=None): + if (ggg == None): + ggg = get_html_lis() + file_lis, new_date = date_to_meta(xrt_downloaded_files) + genyl = get_urls(file_lis, ggg) + print('downloading') + tmeta_lis = get_metafile(genyl) + print('done') + hlis3 = match_vso_to_cat(tmeta_lis, new_date, xrt_downloaded_files) + return hlis3 + +def fetch_metadata(xrt_downloaded_files, fast_bool = True): + if fast_bool: + print('Fast Metadata (Level 0)') + return download_metadata_fast(xrt_downloaded_files, ggg=None) + else: + print('Slow Metadata (Level 1)') + hdul = download_metadata(xrt_downloaded_files,'') + hlis = [] + for i in range(len(xrt_downloaded_files[0])): + hlis.append(hdul[i+2].header) + return hlis + + diff --git a/visualization/fov/xrt_metadata_plot.py b/visualization/fov/xrt_metadata_plot.py new file mode 100644 index 000000000..6e0652601 --- /dev/null +++ b/visualization/fov/xrt_metadata_plot.py @@ -0,0 +1,288 @@ + +import matplotlib.pyplot as plt +import numpy as np +import matplotlib.dates as mdates +import matplotlib.colors as mcolors + + +def get_pcol(ndata): + cmap = plt.get_cmap('rainbow') + ndim = ndata#+1 + col_arr = np.linspace(0.0,1.0,ndim) + col_lis = cmap(col_arr)#[:-1]) + return col_lis + +def lab_locs0(xc, yc): + xlocs = np.asarray([np.min(xc.value), np.max(xc.value), np.max(xc.value), np.min(xc.value)]) + ylocs = np.asarray([np.min(yc.value), np.min(yc.value), np.max(yc.value), np.max(yc.value)]) + hal = ['left' , 'right' , 'right' , 'left' ] + val = ['bottom' ,'bottom' , 'top' , 'top' ] + return xlocs, ylocs, hal, val + +def mk_label_locs(xlis0, ylis0, dycheck = 90.0): + xlis, ylis, ylis0 = np.asarray(xlis0), np.asarray(ylis0), np.asarray(ylis0) + xpos = np.min(xlis) + 300.0 + dist_bool = True + ymaxi = np.argmax(ylis) + for j in range(100):#while dist_bool: + good_bool = True + if False:#good_bool: + print() + print(ylis) + for i in range(len(ylis)): + if (i != ymaxi): + dyy = np.abs(ylis - ylis[i]) + dyy[i] = 10.0**3.0 + if (np.min(dyy) < dycheck): + #print(dyy) + ylis[i] = ylis[i] - dycheck + #ylisb[i] = ylisb[i] - dycheck*2.0 + good_bool = False + break + + if good_bool: + #print('yo!') + dist_bool = False + if not dist_bool: + break + dyl = ylis - ylis0 + ylisb = ylis0 + 0.5*dyl + return np.ones(len(xlis))*xpos, ylis, ylisb + +def fix_col(col_in, dcol_v): + colv = mcolors.to_rgb(col_in) + colv2 = np.asarray([colv[0], colv[1],colv[2]]) + sum_v = dcol_v - np.sum(colv2) + dcol = 1.0-colv2 + #plt.fill_between([i,i+1.0],[0.0]*2,[1.0]*2,color=colv2) + colv3 = colv2 + dcol*(sum_v)/np.sum(dcol) + return colv3 + + + + +def interactive_plot(filter_selection, + zoom_v, + tzoom, + time_ind, + mini_tline, + inputs): + + + # interactive plot function + + xca = inputs['xca'] + yca = inputs['yca'] + rsun_p = inputs['rsun_p'] + zla = inputs['zla'] + lon_arr = inputs['lon_arr'] + yla = inputs['yla'] + xmeta = inputs['xmeta'] + flen = inputs['flen'] + t0d = inputs['t0d'] + trp = inputs['trp'] + dt_bool = inputs['dt_bool'] + fov_lis = inputs['fov_lis'] + fill_bool = inputs['fill_bool'] + filter_lis = inputs['filter_lis'] + time_lis0 = inputs['time_lis0'] + col_vals = inputs['col_vals'] + time_lis_abs = inputs['time_lis_abs'] + + plt.close('all') + + #Set up Figure + + if mini_tline: + fig, axs2 = plt.subplots(3,1,figsize=(8,11), gridspec_kw={'height_ratios': [16, 1, 4]}) + axs = [axs2[0],axs2[2],axs2[1]] + jlen = 2 + else: + fig, axs = plt.subplots(2,1,figsize=(8,10), gridspec_kw={'height_ratios': [16, 4]}) + jlen = 1 + + ##plot the solar limb and grid lines + axs[0].plot(xca, yca,'w-') + axs[0].plot(np.asarray([-1.0,1.0])*rsun_p,np.zeros(2),'w-',lw=0.5) + for j in range(len(lon_arr)): + axs[0].plot(xca*lon_arr[j],yca,'w-',lw=0.5) + axs[0].plot([-yla[j],yla[j]],[zla[j]]*2,'w-',lw=0.5) + axs[0].plot([-yla[j],yla[j]],[-zla[j]]*2,'w-',lw=0.5) + + + # Zoom Button Affects this + time_arr = np.linspace(xmeta.metadata['TIME_RANGE']*tzoom[0],xmeta.metadata['TIME_RANGE']*tzoom[1],100) + + # In case we want to change the center point + #x0r, y0r = x0*rsun_p, y0*rsun_p + + + alpha_arr = np.ones(flen)*0.1 + alpha_arr2 = np.zeros(flen) + + if filter_selection == 'All': + alpha_arr = np.ones(flen) + fbool = True + else: + fbool = False + fi = filter_lis.index(filter_selection) + alpha_arr[fi] = 1.0 + alpha_arr2[fi] = 0.3 + + # Current time for timeline + time_ii = (np.timedelta64(int(np.round(time_arr[time_ind])),'s')+t0d) + + + for j in range(jlen): + axs[1+j].plot([time_ii]*2, [-0.5, flen-0.5], 'w-',lw=4,zorder=1000) + axs[1+j].plot([time_ii]*2, [-0.5, flen-0.5], 'b-',zorder=1001) + + xll, yll = [], [] + dtl = [] + fplis = [] + alpha_plis = [] + col_pvals = [] + # Range for the zoomed in timeline + trp2 = [np.timedelta64(int(np.round(time_arr[0])),'s')+t0d,(np.timedelta64(int(np.round(time_arr[-1])),'s')+t0d)] + + if mini_tline: + axs[2].plot([trp2[0]]*2, [-0.5, flen-0.5], 'w-',lw=2,zorder=200) + axs[2].plot([trp2[1]]*2, [-0.5, flen-0.5], 'w-',lw=2,zorder=200) + + if fill_bool: + axs[2].fill_between([trp[0],trp2[0]],[-0.5]*2,[flen-0.5]*2,color='k',alpha = 0.5,zorder=100) + axs[2].fill_between([trp[1],trp2[1]],[-0.5]*2,[flen-0.5]*2,color='k',alpha = 0.5,zorder=100) + + lout = axs[2].plot([trp[0],trp2[0]],[-8.0,-0.5],'w-',lw=0.5) + lout2 = axs[2].plot([trp[1],trp2[1]],[-8.0,-0.5],'w-',lw=0.5) + + + lout[0].set_clip_on(False) + lout2[0].set_clip_on(False) + + #print(fov_lis[0][0]) + + for i in range(flen): + for j in range(jlen): + axs[1+j].plot(trp,i*np.ones(2),'w-',alpha = 1.0,lw=0.5,zorder=1) + + #Find nearest obs + ti = np.argmin(np.abs(np.asarray(time_lis0[i])-time_arr[time_ind])) + + + + #Getting the locs the frame + xc, yc = fov_lis[i][ti][0][0],fov_lis[i][ti][0][1] #fov_lis[filter_i][time_i][0][x,y]' + dtv = time_lis0[i][ti]-time_arr[time_ind] + # Only if the frame is visible + if (np.abs(dtv) < 60.0): + label_bool = False + if fbool:#(i == fi): + label_bool = True + elif (i == fi): + label_bool = True + else: + label_bool = False + + if label_bool: + #this silly function just gives you the four corners of frame, + #plus the horizontal and vertical alignment, if you wanted to put the labels inside the frame + xlocs, ylocs, hal, val = lab_locs0(xc, yc)# this function gives + + #get distance between image and current time + dtl.append(dtv) + # corners [BL, BR, TR, TL] + xll.append(xlocs[-2]) #Top-right corner + yll.append(ylocs[-2]) + fplis.append(filter_lis[i]) + alpha_plis.append(alpha_arr[i]) + col_pvals.append(col_vals[i]) + #time list for the image points for each filter + tpl = np.array(time_lis_abs[i],dtype='datetime64[ms]')#[] + + + #plot the image times on the time line(s) + + for j in range(jlen): + axs[1+j].plot(tpl,np.ones(len(time_lis0[i]))*i, 'h',alpha = alpha_arr[i],color=col_vals[i]) + + # Only plot the frame if the observation is within 60s of the current time + if (np.abs(dtv) < 60.0): + axs[0].plot(xc, yc,'--',alpha = alpha_arr[i],color=col_vals[i],lw=1) + if fill_bool: + axs[0].fill_between([xc[0].value,xc[1].value],[yc[0].value]*2,[yc[2].value]*2,color=col_vals[i],alpha = alpha_arr2[i]) + + #highlight the current frame in time line(only if it is plotted) + axs[1].plot(tpl[ti], i, 'h',alpha = alpha_arr[i],color='white',markersize=15) + axs[1].plot(tpl[ti], i, 'h',alpha = alpha_arr[i],color=col_vals[i],markersize=10) + + #Add label to the time line for each filter + axs[1].text(trp2[-1]+np.timedelta64(10,'s'), i, filter_lis[i],alpha = alpha_arr[i], + fontsize=12,color=col_vals[i],horizontalalignment='left',verticalalignment='center',fontweight='bold') + + #This function makes sure the filter labels on the plot don't overlap and are legible + + if (len(dtl) > 0): + xlis, ylis, ylisb = mk_label_locs(xll, yll) + + for i in range(len(dtl)): + + if (np.abs(dtl[i]) < 60.0): + #Pre-amble handles the delta T for each frame + dyp = 50.0 + col_rgb = col_pvals[i]#fix_col(col_vals[i], 2.0) + if (dtl[i] >= 0.0): + sstr = '+' + else: + sstr = '-' + if dt_bool: + col_tim = [np.clip(np.abs(dtl[i])/20.0,0.0,1.0),0.0,0.0] + col_tim = [1.0,1.0-np.clip(np.abs(dtl[i])/20.0,0.0,1.0),1.0-np.clip(np.abs(dtl[i])/20.0,0.0,1.0)] + dtstr = sstr + str(np.round(10.0*np.abs(dtl[i]))/10.0) + + + + axs[0].plot([xlis[i], xll[i]], [ylis[i]+dyp,ylisb[i]],'-',alpha = alpha_plis[i],color=col_rgb,lw=1) + if dt_bool: + axs[0].text( xlis[i], ylis[i]+dyp, '('+dtstr+'s)',alpha = alpha_plis[i], + fontsize=10,color=col_tim,horizontalalignment='right',verticalalignment='bottom',fontweight='bold') + + + + axs[0].text( xlis[i], ylis[i]+dyp, fplis[i],alpha = alpha_plis[i],#*0.6, + fontsize=15,color=col_rgb,horizontalalignment='left',fontweight='bold',verticalalignment='bottom') + for j in range(jlen): + axs[1+j].set_yticks([]) + + plot_col = 'dimgrey' + for j in range(jlen+1): + axs[j].set_facecolor(plot_col) + + fig.patch.set_facecolor(plot_col) + axs[0].set_title(str(time_ii),fontsize=20,color='white') + axs[1].set_xlim(trp2) + if (jlen > 1): + axs[2].set_xlim(trp) + + locator = mdates.AutoDateLocator(minticks=4, maxticks=9) + formatter = mdates.ConciseDateFormatter(locator) + axs[1].xaxis.set_major_locator(locator) + axs[1].xaxis.set_major_formatter(formatter) + if (jlen > 1): + locator = mdates.AutoDateLocator(minticks=4, maxticks=9) + formatter = mdates.ConciseDateFormatter(locator) + axs[2].xaxis.set_major_locator(locator) + axs[2].xaxis.set_major_formatter(formatter) + axs[2].set_ylim([-0.75, flen-0.25]) + axs[0].set_xlim(np.asarray([-1300.0,1300.0])/zoom_v)#+x0r) + axs[0].set_ylim(np.asarray([-1300.0,1300.0])/zoom_v)#+y0r) + + #axs[2].tick_params(top=True, labeltop=True, bottom=False, labelbottom=False) + for ax in axs: + ax.tick_params(color='white', labelcolor='white') + for spine in ax.spines.values(): + spine.set_edgecolor('white') + + plt.show()#zebra + #plt.close('all') + From b20725b382ef33d82e0db6e0f77f64484e7c22b0 Mon Sep 17 00:00:00 2001 From: Nicolas Trueba Date: Wed, 14 May 2025 16:23:45 -0400 Subject: [PATCH 5/8] FOV tool code --- visualization/fov/testing_code.py | 48 ---- visualization/fov/xrt_metadata.py | 28 +- visualization/fov/xrt_metadata_plot.py | 348 ++++++++++++++++++++----- 3 files changed, 304 insertions(+), 120 deletions(-) delete mode 100644 visualization/fov/testing_code.py diff --git a/visualization/fov/testing_code.py b/visualization/fov/testing_code.py deleted file mode 100644 index 48754a545..000000000 --- a/visualization/fov/testing_code.py +++ /dev/null @@ -1,48 +0,0 @@ -import time as timer -import matplotlib.pyplot as plt -from astropy.io import fits -import astropy.time -import astropy.units as u - -import sunpy -import ipywidgets as widgets -from ipywidgets import Layout, interact, IntSlider,IntProgress, RadioButtons, FloatSlider,FloatRangeSlider -import numpy as np -import matplotlib.pyplot as plt -from ipywidgets import interact -import numpy as np -from sunpy.net import Fido -from sunpy.net import attrs as a -import matplotlib.dates as mdates - -import sys -sys.path.append('/Users/ntrueba/SOLAR/code/GIT/xrtpy/xrtpy/visualization/fov/') # -import xrt_metadata as xmet -import xrt_metadata_plot as xplt - - -# Define the time range of interest for solar observations -#time_range = a.Time("2011-06-07 06:00:00", "2011-06-07 07:30:54") -time_range = a.Time("2011-06-07 06:00:00", "2011-06-08 07:30:54") -# Specify the instrument as 'xrt' to search for Hinode X-Ray Telescope data -instrument = a.Instrument("xrt") - - -# This will return a catalog of available XRT data during t≠he specified period -start = timer.time() -xrt_downloaded_files = Fido.search(time_range, instrument) -end = timer.time() -length = end - start -print("It took", length, "seconds!") - - - -### - -xmeta = xmet.xrt_meta(xrt_downloaded_files) - -#### - -#%matplotlib inline - -xmeta.plot_preview() \ No newline at end of file diff --git a/visualization/fov/xrt_metadata.py b/visualization/fov/xrt_metadata.py index 8c14099aa..dcc543c96 100644 --- a/visualization/fov/xrt_metadata.py +++ b/visualization/fov/xrt_metadata.py @@ -5,7 +5,7 @@ import numpy as np from astropy.coordinates import SkyCoord - +import matplotlib.pyplot as plt from sunpy.coordinates import frames from astropy.time import Time import scipy.io as sio @@ -13,12 +13,10 @@ import urllib.request import sys -sys.path.append('/Users/ntrueba/SOLAR/code/GIT/xrtpy/xrtpy/visualization/fov/') +#sys.path.append('/Users/ntrueba/SOLAR/code/GIT/xrtpy/xrtpy/visualization/fov/') import xrt_metadata_plot as xplt import xrt_metadata_download as xfetch -print('THIS IS A GITHUB TEST') - from ipywidgets import Layout, interact, IntSlider,IntProgress, RadioButtons, FloatSlider,FloatRangeSlider,fixed #This module is designed to download and manage XRT observation metadata for previewing purposes @@ -208,7 +206,7 @@ def get_xframe(self, head_i, map_in = None): return coords_out - def plot_preview(self): + def plot_preview(self, ani_bool = True, d_mode = True,vertical_plot = True): # plot preamble here, needs to be cleaned up # because we want to have an interactive plot, we don't want to recalculate some of these basic things every time the plot refreshes, so we calculate it here @@ -223,7 +221,7 @@ def plot_preview(self): fov_lis = self.get_frame_lis()# FOV list (n_filters, n_frames(filter)) time_lis0 = self.metadata['DELTA_TIME_LIST'] # Frame time in seconds relative to first frame (n_filters, n_frames(filter)) time_lis_abs = self.metadata['TIME_LIST'] # Absolute time for each frame (n_filters, n_frames(filter)) - col_vals = xplt.get_pcol(flen) + col_vals, col_vals2 = xplt.get_pcol(flen) # Set-up Solar limb and grid lines ang_arr = np.linspace(0.0,6.28,100) @@ -260,16 +258,24 @@ def plot_preview(self): inputs['fill_bool'] = fill_bool inputs['filter_lis'] = filter_lis inputs['time_lis0'] = time_lis0 - inputs['col_vals'] = col_vals + inputs['col_vals'] = col_vals + inputs['col_vals2'] = col_vals2 inputs['time_lis_abs'] = time_lis_abs # need to generalize when to use interactive vs animation etc - interact(xplt.interactive_plot, + + if ani_bool: + interact(xplt.interactive_plot, mini_tline = True, + night_mode = d_mode, filter_selection = RadioButtons(options=filter_lis_b,index=0), zoom_v=FloatSlider(value=1.0,min=0.5,max=2.0, layout=Layout(width='70%')), tzoom = FloatRangeSlider(value=[0.0,1.0],min=0,max=1.0,step=0.01, layout=Layout(width='70%')), time_ind=IntSlider(value=50,min=0,max=99,step=1, layout=Layout(width='70%')), - inputs = fixed(inputs)) - - return \ No newline at end of file + inputs = fixed(inputs), + nfilt = fixed(flen), + alt_bool = vertical_plot) + else: + ani = xplt.make_animation(filter_lis_b,inputs, d_mode, vertical_plot,flen) + return ani + return #axs_lis \ No newline at end of file diff --git a/visualization/fov/xrt_metadata_plot.py b/visualization/fov/xrt_metadata_plot.py index 6e0652601..8ab74849a 100644 --- a/visualization/fov/xrt_metadata_plot.py +++ b/visualization/fov/xrt_metadata_plot.py @@ -1,16 +1,32 @@ import matplotlib.pyplot as plt +import matplotlib import numpy as np import matplotlib.dates as mdates import matplotlib.colors as mcolors - - -def get_pcol(ndata): - cmap = plt.get_cmap('rainbow') +from matplotlib.backends.backend_agg import FigureCanvasAgg +from matplotlib.figure import Figure +import matplotlib.animation as animation +cmap_b = matplotlib.colors.LinearSegmentedColormap.from_list("", ['#01EA79', + '#01EAFF', + '#952EFC', + '#F51A8C', + '#FF9138']) + +cmap_w = matplotlib.colors.LinearSegmentedColormap.from_list("", ['#019488', + '#003066', + '#51389B',#'#3D318B', + '#B42D7E', + '#FF504D', + '#E59500']) + +def get_pcol(ndata, n_mode = True): + cmap = cmap_b#plt.get_cmap('rainbow') + cmap2 = cmap_w ndim = ndata#+1 col_arr = np.linspace(0.0,1.0,ndim) - col_lis = cmap(col_arr)#[:-1]) - return col_lis + col_lis, col_lis2 = cmap(col_arr), cmap2(col_arr)#[:-1]) + return col_lis, col_lis2 def lab_locs0(xc, yc): xlocs = np.asarray([np.min(xc.value), np.max(xc.value), np.max(xc.value), np.min(xc.value)]) @@ -58,16 +74,158 @@ def fix_col(col_in, dcol_v): colv3 = colv2 + dcol*(sum_v)/np.sum(dcol) return colv3 +def interactive_plot(filter_selection, + zoom_v, + tzoom, + time_ind, + mini_tline, + night_mode, + inputs, + nfilt, + alt_bool): + #print('new interact2') + plt.close('all') + #alt_bool = False + if mini_tline: + if alt_bool: + fig, axs2 = plt.subplots(3,1,figsize=(9,8+nfilt*0.4), gridspec_kw={'height_ratios': [16, 1, nfilt]}) + axs = [axs2[0],axs2[2],axs2[1]] + else: + fig, axs2 = plt.subplot_mosaic([['t1','t1','im'],['t1','t1','im'],['t2','t2','im']], figsize=(18,6))#,['im','t0','t0'],['im','t3','t3']] + #axs2['t0'].axis('off') + #axs2['t3'].axis('off') + axs = [axs2['im'],axs2['t1'],axs2['t2']] + jlen = 2 + + else: + if alt_bool: + fig, axs = plt.subplots(2,1,figsize=(9,7+nfilt*0.4), gridspec_kw={'height_ratios': [16, nfilt]}) + + else: + fig, axs2 = plt.subplot_mosaic([['t1','t1','im'],['t1','t1','im']], figsize=(18,6))#,['im','t0','t0'],['im','t3','t3']] + #axs2['t0'].axis('off') + #axs2['t3'].axis('off') + #axs2['t2'].axis('off') + axs = [axs2['im'],axs2['t1']] + jlen = 1 + axs, fig = fov_plotter(axs, fig, + filter_selection, + zoom_v, + tzoom, + time_ind, + mini_tline, + night_mode, + jlen, + inputs, + alt_bool) + plt.show() -def interactive_plot(filter_selection, +def get_frames(filter_selection, zoom_v, tzoom, time_ind, mini_tline, - inputs): + night_mode, + inputs, + nfilt, + alt_bool): + #print('new interact2') + plt.close('all') + if mini_tline: + if alt_bool: + fig, axs2 = plt.subplots(3,1,figsize=(9,8+nfilt*0.4), gridspec_kw={'height_ratios': [16, 1, nfilt]}) + axs = [axs2[0],axs2[2],axs2[1]] + else: + fig, axs2 = plt.subplot_mosaic([['t1','t1','im'],['t1','t1','im'],['t2','t2','im']], figsize=(18,6))#,['im','t0','t0'],['im','t3','t3']] + #axs2['t0'].axis('off') + #axs2['t3'].axis('off') + axs = [axs2['im'],axs2['t1'],axs2['t2']] + jlen = 2 + else: + fig, axs = plt.subplots(2,1,figsize=(9,10), gridspec_kw={'height_ratios': [16, 4]}) + jlen = 1 + + axs, fig = fov_plotter(axs, fig, + filter_selection, + zoom_v, + tzoom, + time_ind, + mini_tline, + night_mode, + jlen, + inputs, + alt_bool) + return axs, fig + +def make_animation(filter_lis_b,inputs, dmode, alt_bool,nfilt,tlen = 20): + axs_lis = [] + + ti = np.linspace(0,99,tlen,dtype=int) + for i in range(tlen): + #print(ti[i]) + axs, fig = get_frames(filter_lis_b[0], + 1.0, + [0.0,1.0], + ti[i], + True, + dmode, + inputs, + nfilt, + alt_bool + ) + axs_lis.append(fig) + plt.close() + Xlis = [] + for i in range(tlen): + fig = axs_lis[i] + canvas = FigureCanvasAgg(fig) + + + # Retrieve a view on the renderer buffer + canvas.draw() + buf = canvas.buffer_rgba() + # convert to a NumPy array + X = np.asarray(buf) + Xlis.append(X) + plt.close('all') + if alt_bool: + fig, ax = plt.subplots(figsize=(8,11)) + else: + fig, ax = plt.subplots(figsize=(18,6)) + plt.axis('off') + #plt.axis("tight") # gets rid of white border + #plt.axis("image") + ims = [] + for i in range(tlen): + # make a Figure and attach it to a canvas. + X = Xlis[i] + #print(i) + im = ax.imshow(X, animated=True) + #plt.axis("tight") + if i == 0: + ax.imshow(X) # show an initial one first + #plt.close() + ims.append([im]) + fig.subplots_adjust(left=0, bottom=0, right=1, top=1)#, wspace=None, hspace=None) + ani = animation.ArtistAnimation(fig, ims, interval=200, blit=True, + repeat_delay=1000) + return ani #plt.show() + + +def fov_plotter(axs, fig, + filter_selection, + zoom_v, + tzoom, + time_ind, + mini_tline, + night_mode, + jlen, + inputs, + alt_bool): + # interactive plot function @@ -86,28 +244,38 @@ def interactive_plot(filter_selection, fill_bool = inputs['fill_bool'] filter_lis = inputs['filter_lis'] time_lis0 = inputs['time_lis0'] - col_vals = inputs['col_vals'] + col_vals1 = inputs['col_vals'] + col_vals2 = inputs['col_vals2'] time_lis_abs = inputs['time_lis_abs'] - plt.close('all') - #Set up Figure - - if mini_tline: - fig, axs2 = plt.subplots(3,1,figsize=(8,11), gridspec_kw={'height_ratios': [16, 1, 4]}) - axs = [axs2[0],axs2[2],axs2[1]] - jlen = 2 + + if night_mode: + col_vals = col_vals1.copy() + + plot_col2 = '#0F121F'#'#272A35'#'#282C3B'#'dimgrey' + plot_col1 = '#272A35'#'#0F121F'#'#161820' + line_col = '#03FFFF' + gcol = 'w' + lab_col = 'white' else: - fig, axs = plt.subplots(2,1,figsize=(8,10), gridspec_kw={'height_ratios': [16, 4]}) - jlen = 1 + col_vals = col_vals2.copy() + plot_col2 = '#FFFFFF'#'#272A35'#'#282C3B'#'dimgrey' + plot_col1 = '#E9E9E9'#'#0F121F'#'#161820' + line_col = '#272A35' + gcol = 'k' + lab_col = 'black' + + + ##plot the solar limb and grid lines - axs[0].plot(xca, yca,'w-') - axs[0].plot(np.asarray([-1.0,1.0])*rsun_p,np.zeros(2),'w-',lw=0.5) + axs[0].plot(xca, yca,gcol+'-') + axs[0].plot(np.asarray([-1.0,1.0])*rsun_p,np.zeros(2),gcol+'-',lw=0.5) for j in range(len(lon_arr)): - axs[0].plot(xca*lon_arr[j],yca,'w-',lw=0.5) - axs[0].plot([-yla[j],yla[j]],[zla[j]]*2,'w-',lw=0.5) - axs[0].plot([-yla[j],yla[j]],[-zla[j]]*2,'w-',lw=0.5) + axs[0].plot(xca*lon_arr[j],yca,gcol+'-',lw=0.5) + axs[0].plot([-yla[j],yla[j]],[zla[j]]*2,gcol+'-',lw=0.5) + axs[0].plot([-yla[j],yla[j]],[-zla[j]]*2,gcol+'-',lw=0.5) # Zoom Button Affects this @@ -121,21 +289,31 @@ def interactive_plot(filter_selection, alpha_arr2 = np.zeros(flen) if filter_selection == 'All': + fig_title = 'All Filters' + mini_title = '' + mini_col = lab_col alpha_arr = np.ones(flen) fbool = True else: + fig_title = 'Filter :'#filter_selection + mini_title = filter_selection + fbool = False fi = filter_lis.index(filter_selection) + mini_col = col_vals[fi] alpha_arr[fi] = 1.0 alpha_arr2[fi] = 0.3 # Current time for timeline time_ii = (np.timedelta64(int(np.round(time_arr[time_ind])),'s')+t0d) + for j in range(jlen): - axs[1+j].plot([time_ii]*2, [-0.5, flen-0.5], 'w-',lw=4,zorder=1000) - axs[1+j].plot([time_ii]*2, [-0.5, flen-0.5], 'b-',zorder=1001) + axs[1+j].plot([time_ii]*2, [-0.5, flen-0.5], color=line_col,lw=4,zorder=1000) + axs[1+j].plot([time_ii]*2, [-0.5, flen-0.5], gcol+'-',lw=1,zorder=1001) + + #axs[1+j].plot([time_ii]*2, [-0.5, flen-0.5], 'b-',zorder=1001) xll, yll = [], [] dtl = [] @@ -144,27 +322,33 @@ def interactive_plot(filter_selection, col_pvals = [] # Range for the zoomed in timeline trp2 = [np.timedelta64(int(np.round(time_arr[0])),'s')+t0d,(np.timedelta64(int(np.round(time_arr[-1])),'s')+t0d)] - + trp3 = [np.timedelta64(int(np.round(time_arr[1])),'s')+t0d,(np.timedelta64(int(np.round(time_arr[-2])),'s')+t0d)] if mini_tline: - axs[2].plot([trp2[0]]*2, [-0.5, flen-0.5], 'w-',lw=2,zorder=200) - axs[2].plot([trp2[1]]*2, [-0.5, flen-0.5], 'w-',lw=2,zorder=200) + x_vals = [trp3[0],trp2[0],trp2[0],trp3[0]] + y_vals = [-0.6,-0.4, flen-0.6, flen-0.4] + axs[2].plot(x_vals, y_vals,color=line_col,lw=2,zorder=200) + + x_vals = [trp3[1],trp2[1],trp2[1],trp3[1]] + y_vals = [-0.5,-0.5, flen-0.5, flen-0.5] + axs[2].plot(x_vals, y_vals,color=line_col,lw=2,zorder=200) if fill_bool: - axs[2].fill_between([trp[0],trp2[0]],[-0.5]*2,[flen-0.5]*2,color='k',alpha = 0.5,zorder=100) - axs[2].fill_between([trp[1],trp2[1]],[-0.5]*2,[flen-0.5]*2,color='k',alpha = 0.5,zorder=100) - - lout = axs[2].plot([trp[0],trp2[0]],[-8.0,-0.5],'w-',lw=0.5) - lout2 = axs[2].plot([trp[1],trp2[1]],[-8.0,-0.5],'w-',lw=0.5) + axs[2].fill_between([trp[0],trp2[0]],[-0.5]*2,[flen-0.5]*2,color=plot_col2,alpha = 0.5,zorder=100) + axs[2].fill_between([trp[1],trp2[1]],[-0.5]*2,[flen-0.5]*2,color=plot_col2,alpha = 0.5,zorder=100) + axs[2].fill_between([trp2[0],trp2[1]],[-0.5]*2,[flen-0.5]*2,color=plot_col1,alpha = 1.0,zorder=0) + if False: + lout = axs[2].plot([trp[0],trp2[0]],[-8.0,-0.5],'w-',lw=0.5) + lout2 = axs[2].plot([trp[1],trp2[1]],[-8.0,-0.5],'w-',lw=0.5) - lout[0].set_clip_on(False) - lout2[0].set_clip_on(False) + lout[0].set_clip_on(False) + lout2[0].set_clip_on(False) #print(fov_lis[0][0]) for i in range(flen): for j in range(jlen): - axs[1+j].plot(trp,i*np.ones(2),'w-',alpha = 1.0,lw=0.5,zorder=1) + axs[1+j].plot(trp,i*np.ones(2),gcol+'-',alpha = 1.0,lw=0.5,zorder=1) #Find nearest obs ti = np.argmin(np.abs(np.asarray(time_lis0[i])-time_arr[time_ind])) @@ -213,20 +397,33 @@ def interactive_plot(filter_selection, axs[0].fill_between([xc[0].value,xc[1].value],[yc[0].value]*2,[yc[2].value]*2,color=col_vals[i],alpha = alpha_arr2[i]) #highlight the current frame in time line(only if it is plotted) + axs[1].plot(tpl[ti], i, 'h',alpha = alpha_arr[i],color='white',markersize=15) axs[1].plot(tpl[ti], i, 'h',alpha = alpha_arr[i],color=col_vals[i],markersize=10) #Add label to the time line for each filter - axs[1].text(trp2[-1]+np.timedelta64(10,'s'), i, filter_lis[i],alpha = alpha_arr[i], - fontsize=12,color=col_vals[i],horizontalalignment='left',verticalalignment='center',fontweight='bold') + #+np.timedelta64(10,'s') + #time_ii = (np.timedelta64(int(np.round(time_arr[time_ind])),'s')+t0d) + dttt = trp2[-1] - (np.timedelta64(int(np.round(time_arr[-2])),'s')+t0d) + if alt_bool: + ggg = axs[1].text(trp2[-1]+dttt, i, filter_lis[i],alpha = alpha_arr[i], + fontsize=15,color=col_vals[i],horizontalalignment='left',verticalalignment='center',fontweight='bold') + else: + ggg = axs[1].text(trp2[0]-dttt, i, filter_lis[i],alpha = alpha_arr[i], + fontsize=15,color=col_vals[i],horizontalalignment='right',verticalalignment='center',fontweight='bold') + ggg.set_clip_on(False) #This function makes sure the filter labels on the plot don't overlap and are legible if (len(dtl) > 0): xlis, ylis, ylisb = mk_label_locs(xll, yll) - + + xlim_out = np.asarray([-1300.0,1300.0])/zoom_v + ylim_out = np.asarray([-1300.0,1300.0])/zoom_v + dyl = (ylim_out[1]-ylim_out[0])*0.33 + yh_lis = np.linspace(ylim_out[0]+dyl,ylim_out[1],flen+2) + #print(yh_lis) for i in range(len(dtl)): - if (np.abs(dtl[i]) < 60.0): #Pre-amble handles the delta T for each frame dyp = 50.0 @@ -241,48 +438,77 @@ def interactive_plot(filter_selection, dtstr = sstr + str(np.round(10.0*np.abs(dtl[i]))/10.0) - - axs[0].plot([xlis[i], xll[i]], [ylis[i]+dyp,ylisb[i]],'-',alpha = alpha_plis[i],color=col_rgb,lw=1) - if dt_bool: - axs[0].text( xlis[i], ylis[i]+dyp, '('+dtstr+'s)',alpha = alpha_plis[i], - fontsize=10,color=col_tim,horizontalalignment='right',verticalalignment='bottom',fontweight='bold') + #dxt = -2000.0 + + if alt_bool: + dxxx = 0.0#-2000.0 + else: + dxxx= 0.0#-2000.0 + if True: + axs[0].plot([xlis[i]+dxxx, xll[i]], [ylis[i]+dyp,ylisb[i]],'-',alpha = alpha_plis[i],color=col_rgb,lw=1) + if dt_bool: + axs[0].text( xlis[i]+dxxx, ylis[i]+dyp, '('+dtstr+'s)',alpha = alpha_plis[i], + fontsize=10,color=col_tim,horizontalalignment='right',verticalalignment='bottom',fontweight='bold') - axs[0].text( xlis[i], ylis[i]+dyp, fplis[i],alpha = alpha_plis[i],#*0.6, - fontsize=15,color=col_rgb,horizontalalignment='left',fontweight='bold',verticalalignment='bottom') + axs[0].text( xlis[i]+dxxx, ylis[i]+dyp, fplis[i],alpha = alpha_plis[i],#*0.6, + fontsize=15,color=col_rgb, horizontalalignment='left',fontweight='bold',verticalalignment='bottom') + else: + axs[0].plot([xlim_out[-1], xll[i]], [yh_lis[i+1],ylisb[i]],'-',alpha = alpha_plis[i],color=col_rgb,lw=2) for j in range(jlen): axs[1+j].set_yticks([]) - plot_col = 'dimgrey' + for j in range(jlen+1): - axs[j].set_facecolor(plot_col) + axs[j].set_facecolor(plot_col1) - fig.patch.set_facecolor(plot_col) - axs[0].set_title(str(time_ii),fontsize=20,color='white') + fig.patch.set_facecolor(plot_col2) + #axs[0].set_title('UTC '+str(time_ii),fontsize=20,color=mini_col) + axs[0].set_title(fig_title+mini_title , fontsize=35,color=mini_col,weight='bold') + axs[1].set_title(''+str(time_ii),fontsize=20,color=line_col)#,loc = 'left') + + #axs[2].set_title('Mini Timeline',fontsize=10,color='white',loc = 'left') + + + #fig.suptitle(fig_title+mini_title , fontsize=35,color=mini_col, x = 0.4,y = 0.98,weight='bold')#, ha = 'left'x = 0.15, axs[1].set_xlim(trp2) if (jlen > 1): axs[2].set_xlim(trp) + axs[2].set_title('Timeline Range Selection',fontsize=12,color=lab_col,zorder=2000) locator = mdates.AutoDateLocator(minticks=4, maxticks=9) formatter = mdates.ConciseDateFormatter(locator) axs[1].xaxis.set_major_locator(locator) axs[1].xaxis.set_major_formatter(formatter) if (jlen > 1): - locator = mdates.AutoDateLocator(minticks=4, maxticks=9) - formatter = mdates.ConciseDateFormatter(locator) - axs[2].xaxis.set_major_locator(locator) - axs[2].xaxis.set_major_formatter(formatter) + #locator = mdates.AutoDateLocator(minticks=4, maxticks=9) + #formatter = mdates.ConciseDateFormatter(locator) + #axs[2].xaxis.set_major_locator(locator) + #axs[2].xaxis.set_major_formatter(formatter) + #axs[2].set_xticks([]) axs[2].set_ylim([-0.75, flen-0.25]) - axs[0].set_xlim(np.asarray([-1300.0,1300.0])/zoom_v)#+x0r) - axs[0].set_ylim(np.asarray([-1300.0,1300.0])/zoom_v)#+y0r) + axs[2].axis('off') + + + if True: + axs[0].text( xlim_out[0]*0.95,ylim_out[-1]*0.98,'UTC '+str(time_ii),alpha = 0.7,#*0.6, + fontsize=15,color=mini_col, horizontalalignment='left',fontweight='bold',verticalalignment='top') + #if xlim_out[-1] < 1500: + # xlim_out[-1] = 1500 + + axs[0].set_xlim(xlim_out)#+x0r) + axs[0].set_ylim(ylim_out)#+y0r) #axs[2].tick_params(top=True, labeltop=True, bottom=False, labelbottom=False) for ax in axs: - ax.tick_params(color='white', labelcolor='white') + ax.tick_params(color=lab_col, labelcolor=lab_col) for spine in ax.spines.values(): - spine.set_edgecolor('white') - - plt.show()#zebra + spine.set_edgecolor(line_col)#'white') + + plt.tight_layout() + return axs, fig + #plt.show() + ##zebra #plt.close('all') From 2d558db1f81c9154feda2388267cce09b43b4a30 Mon Sep 17 00:00:00 2001 From: Nicolas Trueba Date: Tue, 20 May 2025 17:22:24 -0400 Subject: [PATCH 6/8] Update to code, added animations and other options --- examples/README.txt | 9 + ...testing_code.py => preview_observation.py} | 33 ++- ...ata_download.py => metadata_downloader.py} | 0 .../{xrt_metadata.py => metadata_manager.py} | 21 +- ..._metadata_plot.py => metadata_plotting.py} | 198 +++++++++++++++--- 5 files changed, 211 insertions(+), 50 deletions(-) rename examples/{testing_code.py => preview_observation.py} (73%) rename visualization/fov/{xrt_metadata_download.py => metadata_downloader.py} (100%) rename visualization/fov/{xrt_metadata.py => metadata_manager.py} (95%) rename visualization/fov/{xrt_metadata_plot.py => metadata_plotting.py} (74%) diff --git a/examples/README.txt b/examples/README.txt index d3289aaeb..808053b0a 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -3,3 +3,12 @@ Example Gallery *************** The gallery contains examples of how to use XRTpy. +======================== +XRT Stuff +======================== +- **Removing Light Leak** +======================== +XRT FOV GUI +======================== + +- **FOV tool test** diff --git a/examples/testing_code.py b/examples/preview_observation.py similarity index 73% rename from examples/testing_code.py rename to examples/preview_observation.py index 1138f9743..60c1b62ca 100644 --- a/examples/testing_code.py +++ b/examples/preview_observation.py @@ -25,25 +25,22 @@ import sys sys.path.append('/Users/ntrueba/SOLAR/code/GIT/xrtpy/xrtpy/visualization/fov/') # -import xrt_metadata as xmet -import xrt_metadata_plot as xplt +import metadata_manager as ObsMeta ############################################################################## # FIDO SEARCH # The tool is meant to preview metadata within the fido ecosystem - this means that fido is the slowest part of the code unless you are looking at a very small time frame # Define the time range of interest for solar observations -time_range = a.Time("2011-06-07 06:00:00", "2011-06-07 06:45:54") +time_range_1 = a.Time("2011-06-07 06:00:00", "2011-06-07 06:45:54") +time_range_2 = a.Time("2007-12-17 10:40:00", "2007-12-17 13:00:54") # Specify the instrument as 'xrt' to search for Hinode X-Ray Telescope data instrument = a.Instrument("xrt") # This will return a catalog of available XRT data during t≠he specified period -start = timer.time() -xrt_downloaded_files = Fido.search(time_range, instrument) -end = timer.time() -length = end - start -print("It took", length, "seconds!") +xrt_downloaded_files_1 = Fido.search(time_range_1, instrument) +xrt_downloaded_files_2 = Fido.search(time_range_2, instrument) @@ -58,13 +55,27 @@ -xmeta = xmet.xrt_meta(xrt_downloaded_files) +xrt_dset1 = ObsMeta.DatasetMetaManager(xrt_downloaded_files_1) +xrt_dset2 = ObsMeta.DatasetMetaManager(xrt_downloaded_files_2) ############################################################################## #Plotting backend #The %matplotlib inline works best when using notebooks to avoid flickering. Ipywidgets is not available with HTML, but you can uncomment this line when you download the notebook. +#%matplotlib inline +### +ani = xrt_dset2.plot_preview(ani_bool = False, d_mode=False) +plt.show() +############################################################################## +### +#We can also do nightmode -#%matplotlib inline +ani = xrt_dset1.plot_preview(ani_bool = False, d_mode=True, vertical_plot=True) +plt.show() + +############################################################################## +### +#We can also do a horizontal version -xmeta.plot_preview() \ No newline at end of file +ani = xrt_dset2.plot_preview(ani_bool = False, d_mode=True, vertical_plot=False) +plt.show() \ No newline at end of file diff --git a/visualization/fov/xrt_metadata_download.py b/visualization/fov/metadata_downloader.py similarity index 100% rename from visualization/fov/xrt_metadata_download.py rename to visualization/fov/metadata_downloader.py diff --git a/visualization/fov/xrt_metadata.py b/visualization/fov/metadata_manager.py similarity index 95% rename from visualization/fov/xrt_metadata.py rename to visualization/fov/metadata_manager.py index dcc543c96..c2416ddab 100644 --- a/visualization/fov/xrt_metadata.py +++ b/visualization/fov/metadata_manager.py @@ -13,9 +13,8 @@ import urllib.request import sys -#sys.path.append('/Users/ntrueba/SOLAR/code/GIT/xrtpy/xrtpy/visualization/fov/') -import xrt_metadata_plot as xplt -import xrt_metadata_download as xfetch +import metadata_plotting as xplt +import metadata_downloader as xfetch from ipywidgets import Layout, interact, IntSlider,IntProgress, RadioButtons, FloatSlider,FloatRangeSlider,fixed @@ -24,14 +23,14 @@ # The first part contains the xrt_meta object, which is how we organize the metadata # After this, we have the download functions -class xrt_meta: +class DatasetMetaManager:#xrt_meta: # The xrt_meta object accepts a list of .fits headers and organizes them # Crucially, it separates metadata quantities by xrt filter def __init__(self, xrt_downloaded_files): header_lis = xfetch.fetch_metadata(xrt_downloaded_files) self.head_lis = header_lis # stores the unfiltered header list - self.fkey_lis = ['EC_FW1_','EC_FW2_'] # xrt filter keywords in fits header, used by sort_xfilter function to do xrt filter sorting + self.fkey_lis = ['EC_FW1_','EC_FW2_'] # xrt filter keywords in fits header, used by sort_filter function to do xrt filter sorting self.check_bool = True # Testing parameter, remove # upon initialization, the object filters observations and creates the .metadata dictonary self.metadata = self.organize_metadata() @@ -45,7 +44,7 @@ def organize_metadata(self): # sort observations by filter. # filter_lis is a simple list containing strings for all filters in the data set (n_filter) # hbtest is a list of length n_filter, each containing a list of headers for all observations for each filter - hbtest, filter_lis = self.sort_xfilters() + hbtest, filter_lis = self.sort_filters() # Get list of DATE_OBS for each observation @@ -72,11 +71,7 @@ def organize_metadata(self): 'DELTA_TIME_LIST': delta_t_lis} return xdata_set - def sort_xfilters(self): - #test statement - if self.check_bool: - print('X1') - + def sort_filters(self): # this function separates an unfilted list of .fits headers from a set of xrt observations and separates them by filter hlen = len(self.head_lis) # length of input header list filter_lis = [] # this will store the list of xrt filters found in our data set @@ -206,7 +201,7 @@ def get_xframe(self, head_i, map_in = None): return coords_out - def plot_preview(self, ani_bool = True, d_mode = True,vertical_plot = True): + def plot_preview(self, ani_bool = True, d_mode = True, vertical_plot = True, demo = False): # plot preamble here, needs to be cleaned up # because we want to have an interactive plot, we don't want to recalculate some of these basic things every time the plot refreshes, so we calculate it here @@ -276,6 +271,6 @@ def plot_preview(self, ani_bool = True, d_mode = True,vertical_plot = True): nfilt = fixed(flen), alt_bool = vertical_plot) else: - ani = xplt.make_animation(filter_lis_b,inputs, d_mode, vertical_plot,flen) + ani = xplt.make_animation(filter_lis_b,inputs, d_mode, vertical_plot, flen, demo) return ani return #axs_lis \ No newline at end of file diff --git a/visualization/fov/xrt_metadata_plot.py b/visualization/fov/metadata_plotting.py similarity index 74% rename from visualization/fov/xrt_metadata_plot.py rename to visualization/fov/metadata_plotting.py index 8ab74849a..ab64aa0ad 100644 --- a/visualization/fov/xrt_metadata_plot.py +++ b/visualization/fov/metadata_plotting.py @@ -37,6 +37,7 @@ def lab_locs0(xc, yc): def mk_label_locs(xlis0, ylis0, dycheck = 90.0): xlis, ylis, ylis0 = np.asarray(xlis0), np.asarray(ylis0), np.asarray(ylis0) + xpos = np.min(xlis) + 300.0 dist_bool = True ymaxi = np.argmax(ylis) @@ -159,18 +160,19 @@ def get_frames(filter_selection, alt_bool) return axs, fig -def make_animation(filter_lis_b,inputs, dmode, alt_bool,nfilt,tlen = 20): +def make_animation2(filter_lis_b,inputs, dmode, alt_bool, nfilt, demo, tlen = 20): axs_lis = [] ti = np.linspace(0,99,tlen,dtype=int) + for i in range(tlen): #print(ti[i]) axs, fig = get_frames(filter_lis_b[0], 1.0, [0.0,1.0], ti[i], - True, + demo, dmode, inputs, nfilt, @@ -214,7 +216,71 @@ def make_animation(filter_lis_b,inputs, dmode, alt_bool,nfilt,tlen = 20): repeat_delay=1000) return ani #plt.show() +def make_animation(filter_lis_b,inputs, dmode, alt_bool,nfilt, demo, tlen = 20): + + axs_lis = [] + ti = np.linspace(0,99,tlen,dtype=int) + j_zoom = 1 + if demo: + j_zoom = 4 + jlis = [[0.0,1.0], + [0.2,0.5], + [0.4,0.7], + [0.1,0.2]] + + + for j in range(j_zoom): + for i in range(tlen): + #print(ti[i]) + axs, fig = get_frames(filter_lis_b[0], + 1.0, + jlis[j], + ti[i], + True, + dmode, + inputs, + nfilt, + alt_bool + ) + axs_lis.append(fig) + plt.close() + Xlis = [] + for i in range(len(axs_lis)): + fig = axs_lis[i] + canvas = FigureCanvasAgg(fig) + + + # Retrieve a view on the renderer buffer + canvas.draw() + buf = canvas.buffer_rgba() + # convert to a NumPy array + X = np.asarray(buf) + Xlis.append(X) + plt.close('all') + if alt_bool: + fig, ax = plt.subplots(figsize=(8,11)) + else: + fig, ax = plt.subplots(figsize=(18,6)) + plt.axis('off') + #plt.axis("tight") # gets rid of white border + #plt.axis("image") + ims = [] + for i in range(len(axs_lis)): + # make a Figure and attach it to a canvas. + X = Xlis[i] + #print(i) + im = ax.imshow(X, animated=True) + #plt.axis("tight") + if i == 0: + ax.imshow(X) # show an initial one first + #plt.close() + ims.append([im]) + fig.subplots_adjust(left=0, bottom=0, right=1, top=1)#, wspace=None, hspace=None) + ani = animation.ArtistAnimation(fig, ims, interval=200, blit=True, + repeat_delay=1000) + return ani #plt.show() + def fov_plotter(axs, fig, filter_selection, zoom_v, @@ -224,9 +290,10 @@ def fov_plotter(axs, fig, night_mode, jlen, inputs, - alt_bool): + alt_bool):#, + #focus_bool): - + focus_bool = False#True # interactive plot function xca = inputs['xca'] @@ -307,7 +374,13 @@ def fov_plotter(axs, fig, # Current time for timeline time_ii = (np.timedelta64(int(np.round(time_arr[time_ind])),'s')+t0d) + dtmax = 60.0 + dt_step = time_arr[1]-time_arr[0] + if (dt_step > dtmax): + dtmax = dt_step + if (dt_step > 999.0): + dtmax = 999.0 for j in range(jlen): axs[1+j].plot([time_ii]*2, [-0.5, flen-0.5], color=line_col,lw=4,zorder=1000) @@ -315,7 +388,9 @@ def fov_plotter(axs, fig, #axs[1+j].plot([time_ii]*2, [-0.5, flen-0.5], 'b-',zorder=1001) - xll, yll = [], [] + xll, yll, all, all2 = [], [], [], [] + xmm, ymm = [], [] + xll2 = [] dtl = [] fplis = [] alpha_plis = [] @@ -345,7 +420,12 @@ def fov_plotter(axs, fig, lout2[0].set_clip_on(False) #print(fov_lis[0][0]) - + minx_lis = [] + miny_lis = [] + maxx_lis = [] + maxy_lis = [] + #tobs_lis = [] + #xc_lis, yc_lis = [], [] for i in range(flen): for j in range(jlen): axs[1+j].plot(trp,i*np.ones(2),gcol+'-',alpha = 1.0,lw=0.5,zorder=1) @@ -354,12 +434,20 @@ def fov_plotter(axs, fig, ti = np.argmin(np.abs(np.asarray(time_lis0[i])-time_arr[time_ind])) + #min_x = 10.0**6.0 + #max_x = -10.0**6.0 + + #min_y = 10.0**6.0 + #max_y = -10.0**6.0 #Getting the locs the frame xc, yc = fov_lis[i][ti][0][0],fov_lis[i][ti][0][1] #fov_lis[filter_i][time_i][0][x,y]' dtv = time_lis0[i][ti]-time_arr[time_ind] # Only if the frame is visible - if (np.abs(dtv) < 60.0): + if (np.abs(dtv) < dtmax): + #tobs = (np.timedelta64(int(np.round(time_arr[0])),'s')+t0d) + + #tobs_lis.append(tobs) label_bool = False if fbool:#(i == fi): label_bool = True @@ -372,12 +460,23 @@ def fov_plotter(axs, fig, #this silly function just gives you the four corners of frame, #plus the horizontal and vertical alignment, if you wanted to put the labels inside the frame xlocs, ylocs, hal, val = lab_locs0(xc, yc)# this function gives - + xmm.append(np.mean(xlocs)) + ymm.append(np.mean(ylocs)) #get distance between image and current time dtl.append(dtv) # corners [BL, BR, TR, TL] - xll.append(xlocs[-2]) #Top-right corner - yll.append(ylocs[-2]) + if (np.mean(xlocs) > 0.0): + xll.append(xlocs[3]) #Top-right corner + yll.append(ylocs[3]) + all.append('right') + all2.append('left') + xll2.append(xlocs[0]-300.0) + else: + xll.append(xlocs[2]) + yll.append(ylocs[2]) + all.append('left') + all2.append('right') + xll2.append(xlocs[1]+300.0) fplis.append(filter_lis[i]) alpha_plis.append(alpha_arr[i]) col_pvals.append(col_vals[i]) @@ -386,12 +485,18 @@ def fov_plotter(axs, fig, #plot the image times on the time line(s) - + dttt = trp2[-1] - (np.timedelta64(int(np.round(time_arr[-2])),'s')+t0d) for j in range(jlen): axs[1+j].plot(tpl,np.ones(len(time_lis0[i]))*i, 'h',alpha = alpha_arr[i],color=col_vals[i]) # Only plot the frame if the observation is within 60s of the current time - if (np.abs(dtv) < 60.0): + if (np.abs(dtv) < dtmax): + minx_lis.append(np.min(xc.value)) + miny_lis.append(np.min(yc.value)) + maxx_lis.append(np.max(xc.value)) + maxy_lis.append(np.max(yc.value)) + #xc_lis.append(np.argmin(np.abs(xc))) + #yc_lis.append(yc) axs[0].plot(xc, yc,'--',alpha = alpha_arr[i],color=col_vals[i],lw=1) if fill_bool: axs[0].fill_between([xc[0].value,xc[1].value],[yc[0].value]*2,[yc[2].value]*2,color=col_vals[i],alpha = alpha_arr2[i]) @@ -404,7 +509,7 @@ def fov_plotter(axs, fig, #Add label to the time line for each filter #+np.timedelta64(10,'s') #time_ii = (np.timedelta64(int(np.round(time_arr[time_ind])),'s')+t0d) - dttt = trp2[-1] - (np.timedelta64(int(np.round(time_arr[-2])),'s')+t0d) + if alt_bool: ggg = axs[1].text(trp2[-1]+dttt, i, filter_lis[i],alpha = alpha_arr[i], fontsize=15,color=col_vals[i],horizontalalignment='left',verticalalignment='center',fontweight='bold') @@ -416,15 +521,50 @@ def fov_plotter(axs, fig, #This function makes sure the filter labels on the plot don't overlap and are legible if (len(dtl) > 0): + #xlis, ylis, ylisb = [], [], [] + #for h in range(len(dtl)): + # xcoor = np.asarray([]) xlis, ylis, ylisb = mk_label_locs(xll, yll) xlim_out = np.asarray([-1300.0,1300.0])/zoom_v ylim_out = np.asarray([-1300.0,1300.0])/zoom_v + + + if (len(dtl) > 0): + if True:#(zoom_v > 1.0): + zoom_val = 10.0**np.abs(np.log10(zoom_v)) + xmean, ymean = np.mean(xmm), np.mean(ymm) + intx = np.linspace(1.0,2.0,10,endpoint=True) + int_func = np.linspace(0.0,1.0,10,endpoint=True)**0.5 + #if (zoom_v <= 1.0): + # int_func = np.linspace(0.0,1.0,10,endpoint=True)**0.25 + + xcen = np.interp(zoom_val,intx, int_func)*xmean + ycen = np.interp(zoom_val,intx, int_func)*ymean + xlim_out = xlim_out + xcen + ylim_out = ylim_out + ycen + + if False:#focus_bool: + if (len(dtl) > 0): + over_p = 1.1 + xmin, xmax, ymin, ymax = np.min(np.asarray(minx_lis)),np.max(np.asarray(maxx_lis)),np.min(np.asarray(miny_lis)),np.max(np.asarray(maxy_lis)) + + mxp, dxp = (xmax+xmin)/2.0, (xmax-xmin)/2.0 + myp, dyp = (ymax+ymin)/2.0, (ymax-ymin)/2.0 + if (dxp > dyp): + dpp = dxp*over_p + else: + dpp = dyp*over_p + xlim_out = mxp + (np.asarray([-dpp, dpp])/zoom_v) + ylim_out = myp + (np.asarray([-dpp, dpp])/zoom_v) + dyl = (ylim_out[1]-ylim_out[0])*0.33 yh_lis = np.linspace(ylim_out[0]+dyl,ylim_out[1],flen+2) #print(yh_lis) for i in range(len(dtl)): - if (np.abs(dtl[i]) < 60.0): + + if (np.abs(dtl[i]) < dtmax): + tobs = (np.timedelta64(int(np.round(dtl[i])),'s')+time_ii) #Pre-amble handles the delta T for each frame dyp = 50.0 col_rgb = col_pvals[i]#fix_col(col_vals[i], 2.0) @@ -433,8 +573,12 @@ def fov_plotter(axs, fig, else: sstr = '-' if dt_bool: - col_tim = [np.clip(np.abs(dtl[i])/20.0,0.0,1.0),0.0,0.0] - col_tim = [1.0,1.0-np.clip(np.abs(dtl[i])/20.0,0.0,1.0),1.0-np.clip(np.abs(dtl[i])/20.0,0.0,1.0)] + col_f = np.clip(np.abs(dtl[i])*3.0/dtmax,0.0,1.0) + col_tim = [col_f,0.0,0.0] + if night_mode: + col_tim = [1.0,1.0-col_f,1.0-col_f] + dtstr2 = str(tobs) + dtstr2 = dtstr2[11:-4] dtstr = sstr + str(np.round(10.0*np.abs(dtl[i]))/10.0) @@ -445,15 +589,17 @@ def fov_plotter(axs, fig, else: dxxx= 0.0#-2000.0 if True: - axs[0].plot([xlis[i]+dxxx, xll[i]], [ylis[i]+dyp,ylisb[i]],'-',alpha = alpha_plis[i],color=col_rgb,lw=1) + axs[0].plot([xll2[i], xll[i]], [ylis[i]+dyp,ylisb[i]],'-',alpha = alpha_plis[i],color=col_rgb,lw=1) if dt_bool: - axs[0].text( xlis[i]+dxxx, ylis[i]+dyp, '('+dtstr+'s)',alpha = alpha_plis[i], - fontsize=10,color=col_tim,horizontalalignment='right',verticalalignment='bottom',fontweight='bold') + g1 = axs[0].text( xll2[i], ylis[i]+dyp, '('+dtstr+'s)',alpha = alpha_plis[i], + fontsize=10,color=col_tim,horizontalalignment=all2[i],verticalalignment='bottom',fontweight='bold') + g1.set_clip_on(True) - axs[0].text( xlis[i]+dxxx, ylis[i]+dyp, fplis[i],alpha = alpha_plis[i],#*0.6, - fontsize=15,color=col_rgb, horizontalalignment='left',fontweight='bold',verticalalignment='bottom') + g2 = axs[0].text( xll2[i], ylis[i]+dyp, fplis[i],alpha = alpha_plis[i],#*0.6, + fontsize=15,color=col_rgb, horizontalalignment=all[i],fontweight='bold',verticalalignment='bottom') + g2.set_clip_on(True) else: axs[0].plot([xlim_out[-1], xll[i]], [yh_lis[i+1],ylisb[i]],'-',alpha = alpha_plis[i],color=col_rgb,lw=2) for j in range(jlen): @@ -502,13 +648,13 @@ def fov_plotter(axs, fig, #axs[2].tick_params(top=True, labeltop=True, bottom=False, labelbottom=False) for ax in axs: - ax.tick_params(color=lab_col, labelcolor=lab_col) + ax.tick_params(color=lab_col, labelcolor=lab_col)#,direction='out',length=5) + #ax.tick_params(color=lab_col, labelcolor=lab_col,direction='in',which='minor') for spine in ax.spines.values(): spine.set_edgecolor(line_col)#'white') - + axs[0].xaxis.set_major_locator(plt.MaxNLocator(6)) + axs[0].yaxis.set_major_locator(plt.MaxNLocator(6)) + axs[0].grid(alpha=0.5,ls=':') plt.tight_layout() return axs, fig - #plt.show() - ##zebra - #plt.close('all') From c6b7d20e6f999d16fa68f46bad402f8e9c6a619e Mon Sep 17 00:00:00 2001 From: Nicolas Trueba Date: Wed, 21 May 2025 15:20:16 -0400 Subject: [PATCH 7/8] cleaned up things like removed white borders from figures --- examples/preview_observation.py | 5 ++- visualization/fov/metadata_manager.py | 2 +- visualization/fov/metadata_plotting.py | 57 +------------------------- 3 files changed, 5 insertions(+), 59 deletions(-) diff --git a/examples/preview_observation.py b/examples/preview_observation.py index 60c1b62ca..c381e5898 100644 --- a/examples/preview_observation.py +++ b/examples/preview_observation.py @@ -34,6 +34,7 @@ # Define the time range of interest for solar observations time_range_1 = a.Time("2011-06-07 06:00:00", "2011-06-07 06:45:54") time_range_2 = a.Time("2007-12-17 10:40:00", "2007-12-17 13:00:54") + # Specify the instrument as 'xrt' to search for Hinode X-Ray Telescope data instrument = a.Instrument("xrt") @@ -56,7 +57,7 @@ xrt_dset1 = ObsMeta.DatasetMetaManager(xrt_downloaded_files_1) -xrt_dset2 = ObsMeta.DatasetMetaManager(xrt_downloaded_files_2) +xrt_dset2 = ObsMeta.DatasetMetaManager(xrt_downloaded_files_2)## ############################################################################## #Plotting backend @@ -70,7 +71,7 @@ ### #We can also do nightmode -ani = xrt_dset1.plot_preview(ani_bool = False, d_mode=True, vertical_plot=True) +ani = xrt_dset1.plot_preview(ani_bool = False, d_mode=True, vertical_plot=True)## plt.show() ############################################################################## diff --git a/visualization/fov/metadata_manager.py b/visualization/fov/metadata_manager.py index c2416ddab..72ed77c03 100644 --- a/visualization/fov/metadata_manager.py +++ b/visualization/fov/metadata_manager.py @@ -15,7 +15,7 @@ import metadata_plotting as xplt import metadata_downloader as xfetch - +#print('wut') from ipywidgets import Layout, interact, IntSlider,IntProgress, RadioButtons, FloatSlider,FloatRangeSlider,fixed #This module is designed to download and manage XRT observation metadata for previewing purposes diff --git a/visualization/fov/metadata_plotting.py b/visualization/fov/metadata_plotting.py index ab64aa0ad..c601f2ca9 100644 --- a/visualization/fov/metadata_plotting.py +++ b/visualization/fov/metadata_plotting.py @@ -160,61 +160,6 @@ def get_frames(filter_selection, alt_bool) return axs, fig -def make_animation2(filter_lis_b,inputs, dmode, alt_bool, nfilt, demo, tlen = 20): - - axs_lis = [] - - ti = np.linspace(0,99,tlen,dtype=int) - - for i in range(tlen): - #print(ti[i]) - axs, fig = get_frames(filter_lis_b[0], - 1.0, - [0.0,1.0], - ti[i], - demo, - dmode, - inputs, - nfilt, - alt_bool - ) - axs_lis.append(fig) - plt.close() - Xlis = [] - for i in range(tlen): - fig = axs_lis[i] - canvas = FigureCanvasAgg(fig) - - - # Retrieve a view on the renderer buffer - canvas.draw() - buf = canvas.buffer_rgba() - # convert to a NumPy array - X = np.asarray(buf) - Xlis.append(X) - plt.close('all') - if alt_bool: - fig, ax = plt.subplots(figsize=(8,11)) - else: - fig, ax = plt.subplots(figsize=(18,6)) - plt.axis('off') - #plt.axis("tight") # gets rid of white border - #plt.axis("image") - ims = [] - for i in range(tlen): - # make a Figure and attach it to a canvas. - X = Xlis[i] - #print(i) - im = ax.imshow(X, animated=True) - #plt.axis("tight") - if i == 0: - ax.imshow(X) # show an initial one first - #plt.close() - ims.append([im]) - fig.subplots_adjust(left=0, bottom=0, right=1, top=1)#, wspace=None, hspace=None) - ani = animation.ArtistAnimation(fig, ims, interval=200, blit=True, - repeat_delay=1000) - return ani #plt.show() def make_animation(filter_lis_b,inputs, dmode, alt_bool,nfilt, demo, tlen = 20): @@ -259,7 +204,7 @@ def make_animation(filter_lis_b,inputs, dmode, alt_bool,nfilt, demo, tlen = 20): Xlis.append(X) plt.close('all') if alt_bool: - fig, ax = plt.subplots(figsize=(8,11)) + fig, ax = plt.subplots(figsize=(9,8+nfilt*0.4))#(8,11)) else: fig, ax = plt.subplots(figsize=(18,6)) plt.axis('off') From 043ac9e24dc0652c87ca269a760d642cbb80d147 Mon Sep 17 00:00:00 2001 From: Nicolas Trueba Date: Thu, 22 May 2025 13:51:56 -0400 Subject: [PATCH 8/8] added ipywidgets dependancy --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index c4a74e6ea..aee14b5ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ dependencies = [ "scikit-image>=0.21", "scipy>=1.11.1", "sunpy[map]>=5.1", + "ipywidgets>=8.0.0", ] optional-dependencies.dev = [