vidavis 0.0.6__tar.gz → 0.0.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {vidavis-0.0.6 → vidavis-0.0.7}/PKG-INFO +3 -3
  2. {vidavis-0.0.6 → vidavis-0.0.7}/pyproject.toml +1 -1
  3. {vidavis-0.0.6 → vidavis-0.0.7}/readme.rst +2 -2
  4. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/apps/_ms_raster.py +136 -102
  5. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_ms_plot.py +21 -20
  6. vidavis-0.0.7/src/vidavis/plot/ms_plot/_raster_plot_gui.py +91 -0
  7. {vidavis-0.0.6 → vidavis-0.0.7}/LICENSE +0 -0
  8. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/LICENSE.rst +0 -0
  9. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/__init__.py +0 -0
  10. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/apps/__init__.py +0 -0
  11. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/bokeh/__init__.py +0 -0
  12. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/bokeh/_palette.py +0 -0
  13. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/__init__.py +0 -0
  14. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/__init__.py +0 -0
  15. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/_ms_data.py +0 -0
  16. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/__init__.py +0 -0
  17. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_concat.py +0 -0
  18. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_coords.py +0 -0
  19. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_data.py +0 -0
  20. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_io.py +0 -0
  21. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_raster_data.py +0 -0
  22. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_select.py +0 -0
  23. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_stats.py +0 -0
  24. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_xds_data.py +0 -0
  25. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/plot/__init__.py +0 -0
  26. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/__init__.py +0 -0
  27. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_ms_plot_constants.py +0 -0
  28. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_ms_plot_selectors.py +0 -0
  29. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_raster_plot.py +0 -0
  30. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_raster_plot_inputs.py +0 -0
  31. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_time_ticks.py +0 -0
  32. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_xds_plot_axes.py +0 -0
  33. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/toolbox/__init__.py +0 -0
  34. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/toolbox/_app_context.py +0 -0
  35. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/toolbox/_logging.py +0 -0
  36. {vidavis-0.0.6 → vidavis-0.0.7}/src/vidavis/toolbox/_static.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vidavis
3
- Version: 0.0.6
3
+ Version: 0.0.7
4
4
  Summary: Radio astronomy visibility data visualization
5
5
  License: LGPL
6
6
  Author-email: Darrell Schiebel <darrell@schiebel.us>,Pam Harris <pharris@nrao.edu>
@@ -51,11 +51,11 @@ Install
51
51
  MSv2 Conversion
52
52
  ^^^^^^^^^^^^^^^
53
53
 
54
- To enable conversion from MSv2 to MSv4 with **python-casacore** use (this only works for Linux):
54
+ To enable conversion from MSv2 to MSv4 with **python-casacore** for Linux only:
55
55
 
56
56
  - :code:`pip install "xradio[python-casacore]"`
57
57
 
58
- On macOS it is required to pre-install `python-casacore` using:
58
+ On macOS it is required to pre-install **python-casacore**:
59
59
 
60
60
  - :code:`conda install -c conda-forge python-casacore`
61
61
 
@@ -15,7 +15,7 @@ dependencies = [
15
15
  ]
16
16
  requires-python = ">=3.11, <3.14"
17
17
  readme = "readme.rst"
18
- version = "0.0.6"
18
+ version = "0.0.7"
19
19
 
20
20
  [project.license]
21
21
  text = "LGPL"
@@ -42,11 +42,11 @@ Install
42
42
  MSv2 Conversion
43
43
  ^^^^^^^^^^^^^^^
44
44
 
45
- To enable conversion from MSv2 to MSv4 with **python-casacore** use (this only works for Linux):
45
+ To enable conversion from MSv2 to MSv4 with **python-casacore** for Linux only:
46
46
 
47
47
  - :code:`pip install "xradio[python-casacore]"`
48
48
 
49
- On macOS it is required to pre-install `python-casacore` using:
49
+ On macOS it is required to pre-install **python-casacore**:
50
50
 
51
51
  - :code:`conda install -c conda-forge python-casacore`
52
52
 
@@ -7,15 +7,15 @@ import time
7
7
  from bokeh.models.formatters import NumeralTickFormatter
8
8
  import holoviews as hv
9
9
  import numpy as np
10
- import panel as pn
11
10
  from pandas import to_datetime
11
+ import panel as pn
12
12
 
13
13
  from vidavis.bokeh._palette import available_palettes
14
+ from vidavis.data.measurement_set.processing_set._ps_coords import set_index_coordinates
14
15
  from vidavis.plot.ms_plot._time_ticks import get_time_formatter
15
16
  from vidavis.plot.ms_plot._ms_plot import MsPlot
16
- from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, SPECTRUM_AXIS_OPTIONS, PS_SELECTION_OPTIONS, MS_SELECTION_OPTIONS
17
- from vidavis.plot.ms_plot._ms_plot_selectors import (file_selector, title_selector, style_selector, axis_selector,
18
- aggregation_selector, iteration_selector, selection_selector, plot_starter)
17
+ from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, SPECTRUM_AXIS_OPTIONS, PS_SELECTION_OPTIONS, MS_SELECTION_OPTIONS, TIME_FORMAT
18
+ from vidavis.plot.ms_plot._raster_plot_gui import create_raster_gui
19
19
  from vidavis.plot.ms_plot._raster_plot_inputs import check_inputs
20
20
  from vidavis.plot.ms_plot._raster_plot import RasterPlot
21
21
 
@@ -43,23 +43,21 @@ class MsRaster(MsPlot):
43
43
  def __init__(self, ms=None, log_level="info", log_to_file=True, show_gui=False):
44
44
  super().__init__(ms, log_level, log_to_file, show_gui, "MsRaster")
45
45
  self._raster_plot = RasterPlot()
46
+ self._plot_data = None
46
47
 
47
48
  # Calculations for color limits
48
49
  self._spw_stats = {}
49
50
  self._spw_color_limits = {}
50
51
 
51
52
  if show_gui:
52
- # GUI based on panel widgets
53
- self._gui_layout = None
54
-
55
- # Check if plot inputs changed and new plot is needed.
56
- # GUI can change subparams which do not change plot
53
+ # For checking if plot inputs or cursor position changed and new plot or cursor location info is needed.
57
54
  self._last_plot_inputs = None
58
55
  self._last_style_inputs = None
59
- # Last plot when no new plot created (plot inputs same) or is iter Layout plot (opened in tab)
60
- self._last_gui_plot = None
56
+ self._last_cursor = None
61
57
 
62
58
  # Return plot for gui DynamicMap:
59
+ # Last plot when no new plot created (plot inputs same) or is iter Layout plot (opened in tab)
60
+ self._last_gui_plot = None
63
61
  # Empty plot when ms not set or plot fails
64
62
  self._empty_plot = self._create_empty_plot()
65
63
 
@@ -165,25 +163,14 @@ class MsRaster(MsPlot):
165
163
  x_axis (str): Plot x-axis. Default 'baseline' ('antenna_name' for spectrum data).
166
164
  y_axis (str): Plot y-axis. Default 'time'.
167
165
  vis_axis (str): Complex visibility component to plot (amp, phase, real, imag). Default 'amp'.
168
- Call data_groups() to see options.
169
- MeasurementSet selection:
170
- 'data_group': name for correlated data, flags, weights, and uvw. Default value 'base'.
171
- Use data_groups() to get data group names.
172
- Dimensions:
173
- Visibility dimensions: 'baseline' 'time', 'frequency', 'polarization'
174
- Spectrum dimensions: 'antenna_name', 'time', 'frequency', 'polarization'
175
- Default value is index 0 (after user selection) for non-axis dimensions unless aggregated.
176
- Use antennas() to get antenna names. Select 'baseline' as "<name1> & <name2>".
177
- Use summary() to list frequencies and polarizations.
178
- TODO: how to select time?
179
- aggregator (str): reduction for rasterization. Default None.
166
+ aggregator (None, str): reduction for rasterization. Default None.
180
167
  Options include 'max', 'mean', 'min', 'std', 'sum', 'var'.
181
- agg_axis (str, list): which dimension to apply aggregator across. Default None.
168
+ agg_axis (None, str, list): which dimension to apply aggregator across. Default None.
182
169
  Options include one or more dimensions.
183
170
  If agg_axis is None and aggregator is set, aggregates over all non-axis dimensions.
184
171
  If one agg_axis is selected, the non-agg dimension will be selected.
185
- iter_axis (str): dimension over which to iterate values (using iter_range).
186
- iter_range (tuple): (start, end) inclusive index values for iteration plots.
172
+ iter_axis (None, str): dimension over which to iterate values (using iter_range).
173
+ iter_range (None, tuple): (start, end) inclusive index values for iteration plots.
187
174
  Default (0, 0) (first iteration only). Use (0, -1) for all iterations.
188
175
  If subplots is a grid, the range is limited by the grid size.
189
176
  If subplots is a single plot, all iteration plots in the range can be saved using export_range in save().
@@ -194,12 +181,12 @@ class MsRaster(MsPlot):
194
181
  Options include None (use data limits), 'auto' (calculate limits for amplitude), and 'manual' (use range in color_range).
195
182
  'auto' is equivalent to None if vis_axis is not 'amp'.
196
183
  When subplots is set, the 'auto' or 'manual' range will be used for all plots.
197
- color_range (tuple): (min, max) of colorbar to use if color_mode is 'manual'.
198
- title (str): Plot title, default None (no title)
184
+ color_range (None, tuple): (min, max) of colorbar to use if color_mode is 'manual'.
185
+ title (None, str): Plot title, default None (no title)
199
186
  Set title='ms' to generate title from ms name and iter_axis value, if any.
200
187
  clear_plots (bool): whether to clear list of plots. Default True.
201
188
 
202
- If not show_gui and plotting is successful, use show() or save() to view/save the plot only.
189
+ If plot is successful, use show() or save() to view/save the plot.
203
190
  '''
204
191
  inputs = locals() # collect arguments into dict (not unused as pylint complains!)
205
192
  if self._plot_inputs['selection']:
@@ -273,6 +260,7 @@ class MsRaster(MsPlot):
273
260
 
274
261
  # Select vis_axis data to plot and update selection; returns xarray Dataset
275
262
  raster_data = self._data.get_raster_data(plot_inputs)
263
+ self._plot_data = raster_data
276
264
 
277
265
  # Add params needed for plot: auto color range and ms name
278
266
  self._set_auto_color_range(plot_inputs) # set calculated limits if auto mode
@@ -426,85 +414,34 @@ class MsRaster(MsPlot):
426
414
  ### -----------------------------------------------------------------------
427
415
  def _launch_gui(self):
428
416
  ''' Use Holoviz Panel to create a dashboard for plot inputs. '''
429
- # Select MS
430
- file_selectors = file_selector('Path to MeasurementSet (ms or zarr) for plot', '~' , self._set_filename)
431
-
432
- # Select style - colormaps, colorbar, color limits
433
- style_selectors = style_selector(self._set_style_params, self._set_color_range)
434
-
435
- # Set title
436
- title_input = title_selector(self._set_title)
437
-
438
- # Select x, y, and vis axis
417
+ callbacks = {
418
+ 'filename': self._set_filename,
419
+ 'style': self._set_style_params,
420
+ 'color': self._set_color_range,
421
+ 'axes': self._set_axes,
422
+ 'select_ps': self._set_ps_selection,
423
+ 'select_ms': self._set_ms_selection,
424
+ 'aggregation': self._set_aggregation,
425
+ 'iter_values': self._set_iter_values,
426
+ 'iteration': self._set_iteration,
427
+ 'title': self._set_title,
428
+ 'plot_updating': self._update_plot_spinner,
429
+ 'update_plot': self._update_plot,
430
+ }
431
+ data_dims = self._ms_info['data_dims'] if 'data_dims' in self._ms_info else None
439
432
  x_axis = self._plot_inputs['x_axis']
440
433
  y_axis = self._plot_inputs['y_axis']
441
- data_dims = self._ms_info['data_dims'] if 'data_dims' in self._ms_info else None
442
- axis_selectors = axis_selector(x_axis, y_axis, data_dims, True, self._set_axes)
443
-
444
- # Select from ProcessingSet and MeasurementSet
445
- selection_selectors = selection_selector(self._set_ps_selection, self._set_ms_selection)
446
-
447
- # Generic axis options, updated when ms is set
448
- axis_options = data_dims if data_dims else []
449
-
450
- # Select aggregator and axes to aggregate
451
- agg_selectors = aggregation_selector(axis_options, self._set_aggregation)
452
-
453
- # Select iter_axis and iter value or range
454
- iter_selectors = iteration_selector(axis_options, self._set_iter_values, self._set_iteration)
455
-
456
- # Put user input widgets in accordion with only one card active at a time
457
- selectors = pn.Accordion(
458
- ("Select file", file_selectors), # [0]
459
- ("Plot style", style_selectors), # [1]
460
- ("Data Selection", selection_selectors), # [2]
461
- ("Plot axes", axis_selectors), # [3]
462
- ("Aggregation", agg_selectors), # [4]
463
- ("Iteration", iter_selectors), # [5]
464
- ("Plot title", title_input), # [6]
465
- )
466
- selectors.toggle = True
467
-
468
- # Plot button and spinner while plotting
469
- init_plot = plot_starter(self._update_plot_spinner)
470
-
471
- # Connect plot to filename and selector widgets
472
- dmap = hv.DynamicMap(
473
- pn.bind(
474
- self._update_plot,
475
- ms=file_selectors[0][0],
476
- do_plot=init_plot[0],
477
- ),
478
- )
479
-
480
- # Layout plot and input widgets in a row
481
- self._gui_layout = pn.Row(
482
- pn.Tabs( # [0]
483
- ('Plot', dmap), # [0]
484
- ('Plot Inputs', pn.Column()), # [1]
485
- sizing_mode='stretch_width',
486
- ),
487
- pn.Spacer(width=10), # [1]
488
- pn.Column( # [2]
489
- pn.Spacer(height=25), # [0]
490
- selectors, # [1]
491
- init_plot, # [2]
492
- width_policy='min',
493
- width=400,
494
- sizing_mode='stretch_height',
495
- ),
496
- sizing_mode='stretch_height',
497
- )
498
-
434
+ self._gui_layout = create_raster_gui(callbacks, data_dims, x_axis, y_axis)
499
435
  # Show gui
500
- # print("gui layout:", self._gui_layout) # for debugging
501
436
  self._gui_layout.show(title=self._app_name, threaded=True)
502
437
 
503
438
  ###
504
439
  ### Main callback to create plot if inputs changed
505
440
  ###
506
- def _update_plot(self, ms, do_plot):
507
- ''' Create plot with inputs from GUI. Must return plot, even if empty plot, for DynamicMap. '''
441
+ def _update_plot(self, ms, do_plot, x, y):
442
+ ''' Create plot with inputs from GUI, or update cursor (x, y) information.
443
+ Must return plot, even if empty plot or same plot as before, for DynamicMap.
444
+ '''
508
445
  if self._toast:
509
446
  self._toast.destroy()
510
447
 
@@ -513,12 +450,18 @@ class MsRaster(MsPlot):
513
450
  # Launched GUI with no MS
514
451
  return self._empty_plot
515
452
 
516
- # If not first plot, user has to click Plot button (do_plot=True).
453
+ # If not first plot, user has to click Plot button (do_plot=True) to update plot.
454
+ # Respond to pointer callback only.
517
455
  first_plot = not self._last_gui_plot
518
456
  if not do_plot and not first_plot:
457
+ if not self._last_cursor or (self._last_cursor[0] != x or self._last_cursor[1] != y):
458
+ # Callback is for new cursor location
459
+ self._update_cursor_location(x, y)
460
+ self._last_cursor = (x, y)
519
461
  # Not ready to update plot yet, return last plot.
520
462
  return self._last_gui_plot
521
463
 
464
+ # Callback is for initial plot for input ms, or user clicked Plot button
522
465
  if (self._set_ms(ms) or first_plot) and self._data and self._data.is_valid():
523
466
  # New MS set and is valid
524
467
  self._update_gui_axis_options()
@@ -814,7 +757,6 @@ class MsRaster(MsPlot):
814
757
  #filename_input.width = len(filename[-1])
815
758
  filename_input.value = filename[-1]
816
759
 
817
-
818
760
  def _set_iter_values(self, iter_axis):
819
761
  ''' Set up player with values when iter_axis is selected '''
820
762
  iter_axis = None if iter_axis == 'None' else iter_axis
@@ -879,6 +821,98 @@ class MsRaster(MsPlot):
879
821
  for param in self._plot_params:
880
822
  inputs_column.append(pn.pane.Str(param))
881
823
 
824
+ def _update_cursor_location(self, x, y):
825
+ ''' Show metadata for cursor x,y position '''
826
+ # Convert plot values to selection values to select plot data
827
+ x_axis = self._plot_inputs['x_axis']
828
+ y_axis = self._plot_inputs['y_axis']
829
+ x = round(x) if x_axis == 'baseline' else x
830
+ y = round(y) if y_axis == 'baseline' else y
831
+ position = {x_axis: x, y_axis: y}
832
+ location_values, units = self._get_cursor_location_values(position)
833
+ self._update_cursor_box(location_values, units)
834
+
835
+ def _get_cursor_location_values(self, cursor_position):
836
+ values = cursor_position.copy()
837
+ units = {}
838
+
839
+ if self._plot_data:
840
+ try:
841
+ xds = set_index_coordinates(self._plot_data, tuple(cursor_position.keys()))
842
+ sel_xds = xds.sel(indexers=None, method='nearest', tolerance=None, drop=False, **cursor_position)
843
+ for coord in sel_xds.coords:
844
+ if coord != 'uvw_label':
845
+ val, unit = self._get_xda_val_unit(sel_xds[coord])
846
+ values[coord] = val
847
+ units[coord] = unit
848
+ for data_var in sel_xds.data_vars:
849
+ val, unit = self._get_xda_val_unit(sel_xds[data_var])
850
+ if data_var == 'UVW':
851
+ names = ['U', 'V', 'W']
852
+ for i, name in enumerate(names):
853
+ values[name] = val[i]
854
+ units[name] = unit[i]
855
+ else:
856
+ values[data_var] = val
857
+ units[data_var] = unit
858
+ except KeyError:
859
+ pass
860
+
861
+ # Set complex component name for visibilities
862
+ if 'VISIBILITY' in values:
863
+ values[self._plot_inputs['vis_axis'].capitalize()] = values.pop('VISIBILITY')
864
+ return values, units
865
+
866
+ def _update_cursor_box(self, location_values, units):
867
+ ''' Update cursor location widget box with info in dict '''
868
+ cursor_location_box = self._gui_layout[0][0][1]
869
+ cursor_location_box.clear() # pn.WidgetBox
870
+ location_layout = pn.Column(pn.widgets.StaticText(name="Cursor Location"))
871
+ info_row = pn.Row()
872
+ info_col = pn.Column()
873
+
874
+ for name, value in location_values.items():
875
+ # 4 entries per column; append to row and start new column
876
+ if len(info_col.objects) == 4:
877
+ info_row.append(info_col)
878
+ info_col = pn.Column()
879
+
880
+ if not isinstance(value, str):
881
+ if name == "FLAG":
882
+ value = "nan" if np.isnan(value) else int(value)
883
+ elif isinstance(value, float):
884
+ if np.isnan(value):
885
+ value = "nan"
886
+ elif value < 1e6:
887
+ value = f"{value:.4f}"
888
+ else:
889
+ value = f"{value:.4e}"
890
+ elif isinstance(value, np.datetime64):
891
+ value = to_datetime(np.datetime_as_string(value)).strftime(TIME_FORMAT)
892
+ units.pop(name) # no unit for datetime string
893
+ unit = units[name] if name in units else ""
894
+ info = pn.widgets.StaticText(name=name, value=f"{value} {unit}")
895
+ info.margin = (0, 10) # default (5, 10)
896
+ info_col.append(info)
897
+ info_row.append(info_col)
898
+ location_layout.append(info_row)
899
+ cursor_location_box.append(location_layout)
900
+
901
+ def _get_xda_val_unit(self, xda):
902
+ ''' Get value and unit as str not list '''
903
+ # Value
904
+ val = xda.values
905
+ if isinstance(val, np.ndarray) and val.size == 1:
906
+ val = val.item()
907
+ # Unit
908
+ try:
909
+ unit = xda.attrs['units']
910
+ unit = unit[0] if (isinstance(unit, list) and len(unit) == 1) else unit
911
+ unit = '' if unit == 'unkown' else unit
912
+ except KeyError:
913
+ unit = ''
914
+ return val, unit
915
+
882
916
  ###
883
917
  ### Callbacks for widgets which update plot inputs
884
918
  ###
@@ -59,6 +59,9 @@ class MsPlot:
59
59
  self._plots_locked = False
60
60
  self._plots = []
61
61
 
62
+ # Initialize gui
63
+ self._gui_layout = None
64
+
62
65
  # Set data (if ms)
63
66
  self._data = None
64
67
  self._ms_info = {}
@@ -144,7 +147,7 @@ class MsPlot:
144
147
  # Single plot or combine plots into layout using subplots (rows, columns)
145
148
  layout_plot = self._layout_plots(self._plot_inputs['subplots'])
146
149
 
147
- # Render plot as Bokeh Figure or GridPlot
150
+ # Render plot as Bokeh Figure or GridPlot so can show() in script without tying up thread
148
151
  bokeh_fig = hv.render(layout_plot)
149
152
 
150
153
  self._plots_locked = False
@@ -246,30 +249,28 @@ class MsPlot:
246
249
 
247
250
  def _set_ms(self, ms_path):
248
251
  ''' Set MsData and update ms info for input ms filepath (MSv2 or zarr), if set.
249
- Return whether ms changed (false if ms is None). '''
252
+ Return whether ms changed (false if ms_path is None, not set yet), even if error. '''
250
253
  self._ms_info['ms'] = ms_path
251
254
  ms_error = ""
252
- ms_changed = ms_path and (not self._data or not self._data.is_ms_path(ms_path))
253
-
254
- if ms_changed:
255
- try:
256
- # Set new MS data
257
- self._data = MsData(ms_path, self._logger)
258
- data_path = self._data.get_path()
259
- self._ms_info['ms'] = data_path
260
- root, ext = os.path.splitext(os.path.basename(data_path))
261
- while ext != '':
262
- root, ext = os.path.splitext(root)
263
- self._ms_info['basename'] = root
264
- self._ms_info['data_dims'] = self._data.get_data_dimensions()
265
- except RuntimeError as e:
266
- ms_error = str(e)
267
- self._data = None
255
+ if not ms_path or (self._data and self._data.is_ms_path(ms_path)):
256
+ return False
268
257
 
258
+ try:
259
+ # Set new MS data
260
+ self._data = MsData(ms_path, self._logger)
261
+ data_path = self._data.get_path()
262
+ self._ms_info['ms'] = data_path
263
+ root, ext = os.path.splitext(os.path.basename(data_path))
264
+ while ext != '':
265
+ root, ext = os.path.splitext(root)
266
+ self._ms_info['basename'] = root
267
+ self._ms_info['data_dims'] = self._data.get_data_dimensions()
268
+ except RuntimeError as e:
269
+ ms_error = str(e)
270
+ self._data = None
269
271
  if ms_error:
270
272
  self._notify(ms_error, 'error', 0)
271
-
272
- return ms_changed
273
+ return True
273
274
 
274
275
  def _notify(self, message, level, duration=3000):
275
276
  ''' Log message. If show_gui, notify user with toast for duration in ms.
@@ -0,0 +1,91 @@
1
+ '''
2
+ Create interactive GUI for ms raster plotting
3
+ '''
4
+
5
+ import holoviews as hv
6
+ import panel as pn
7
+ from vidavis.plot.ms_plot._ms_plot_selectors import (file_selector, title_selector, style_selector,
8
+ axis_selector, aggregation_selector, iteration_selector, selection_selector, plot_starter)
9
+
10
+ def create_raster_gui(callbacks, data_dims, x_axis, y_axis):
11
+ ''' Use Holoviz Panel to create a dashboard for plot inputs and raster plot display. '''
12
+ # ------------------
13
+ # PLOT INPUTS COLUMN
14
+ # ------------------
15
+ # Select MS
16
+ file_selectors = file_selector('Path to MeasurementSet (ms or zarr) for plot', '~' , callbacks['filename'])
17
+
18
+ # Select style - colormaps, colorbar, color limits
19
+ style_selectors = style_selector(callbacks['style'], callbacks['color'])
20
+
21
+ # Select x, y, and vis axis
22
+ axis_selectors = axis_selector(x_axis, y_axis, data_dims, True, callbacks['axes'])
23
+
24
+ # Select from ProcessingSet and MeasurementSet
25
+ selection_selectors = selection_selector(callbacks['select_ps'], callbacks['select_ms'])
26
+
27
+ # Generic axis options, updated when ms is set
28
+ axis_options = data_dims if data_dims else []
29
+
30
+ # Select aggregator and axes to aggregate
31
+ agg_selectors = aggregation_selector(axis_options, callbacks['aggregation'])
32
+
33
+ # Select iter_axis and iter value or range
34
+ iter_selectors = iteration_selector(axis_options, callbacks['iter_values'], callbacks['iteration'])
35
+
36
+ # Set title
37
+ title_input = title_selector(callbacks['title'])
38
+
39
+ # Put user input widgets in accordion with only one card active at a time (toggle)
40
+ selectors = pn.Accordion(
41
+ ("Select file", file_selectors), # [0]
42
+ ("Plot style", style_selectors), # [1]
43
+ ("Data Selection", selection_selectors), # [2]
44
+ ("Plot axes", axis_selectors), # [3]
45
+ ("Aggregation", agg_selectors), # [4]
46
+ ("Iteration", iter_selectors), # [5]
47
+ ("Plot title", title_input), # [6]
48
+ )
49
+ selectors.toggle = True
50
+
51
+ # Plot button and spinner while plotting
52
+ init_plot = plot_starter(callbacks['plot_updating'])
53
+
54
+ # -------------------------
55
+ # PLOT WITH CURSOR POSITION
56
+ # -------------------------
57
+ # Connect plot to filename and plot button; add pointer stream for cursor info
58
+ dmap = hv.DynamicMap(
59
+ pn.bind(
60
+ callbacks['update_plot'],
61
+ ms=file_selectors[0][0],
62
+ do_plot=init_plot[0],
63
+ ),
64
+ streams=[hv.streams.PointerXY()] # for cursor location
65
+ )
66
+
67
+ # ----------------------------------------------
68
+ # GUI LAYOUT OF PLOT TABS AND PLOT INPUTS COLUMN
69
+ # ----------------------------------------------
70
+ return pn.Row(
71
+ pn.Tabs( # Row [0]
72
+ ('Plot',
73
+ pn.Column( # Tabs [0]
74
+ dmap, # [0]
75
+ pn.WidgetBox(), # [1] cursor location
76
+ )
77
+ ),
78
+ ('Plot Inputs', pn.Column()), # Tabs [1]
79
+ sizing_mode='stretch_width',
80
+ ),
81
+ pn.Spacer(width=10), # Row [1]
82
+ pn.Column( # Row [2]
83
+ pn.Spacer(height=25), # Column [0]
84
+ selectors, # Column [1]
85
+ init_plot, # Column [2]
86
+ width_policy='min',
87
+ width=400,
88
+ sizing_mode='stretch_height',
89
+ ),
90
+ sizing_mode='stretch_height',
91
+ )
File without changes
File without changes
File without changes