vidavis 0.0.13__py3-none-any.whl → 0.0.14__py3-none-any.whl

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.
@@ -8,17 +8,15 @@ from bokeh.models.formatters import NumeralTickFormatter
8
8
  import holoviews as hv
9
9
  import numpy as np
10
10
  from pandas import to_datetime
11
- import panel as pn
12
11
 
13
12
  from vidavis.bokeh._palette import available_palettes
14
13
  from vidavis.data.measurement_set.processing_set._ps_coords import set_index_coordinates
15
- from vidavis.plot.ms_plot._check_raster_inputs import check_inputs
16
- from vidavis.plot.ms_plot._locate_points import cursor_changed, points_changed, box_changed, locate_point, locate_box
17
14
  from vidavis.plot.ms_plot._ms_plot import MsPlot
18
15
  from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, SPECTRUM_AXIS_OPTIONS, PS_SELECTION_OPTIONS, MS_SELECTION_OPTIONS
19
16
  from vidavis.plot.ms_plot._plot_inputs import inputs_changed
20
- from vidavis.plot.ms_plot._raster_plot_gui import create_raster_gui
21
17
  from vidavis.plot.ms_plot._raster_plot import RasterPlot
18
+ from vidavis.plot.ms_plot._raster_plot_gui import create_raster_gui
19
+ from vidavis.plot.ms_plot._raster_plot_inputs import RasterPlotInputs
22
20
  from vidavis.plot.ms_plot._time_ticks import get_time_formatter
23
21
 
24
22
  class MsRaster(MsPlot):
@@ -45,6 +43,8 @@ class MsRaster(MsPlot):
45
43
  def __init__(self, ms=None, log_level="info", log_to_file=True, show_gui=False):
46
44
  super().__init__(ms, log_level, log_to_file, show_gui, "MsRaster")
47
45
  self._raster_plot = RasterPlot()
46
+ self._plot_inputs = RasterPlotInputs()
47
+ self._plot_inputs.set_input('ms', self._ms_info['ms'])
48
48
 
49
49
  # Calculations for color limits
50
50
  self._spw_stats = {}
@@ -99,12 +99,10 @@ class MsRaster(MsPlot):
99
99
  For explanation and examples, see:
100
100
  https://xradio.readthedocs.io/en/latest/measurement_set/schema_and_api/measurement_set_api.html#xradio.measurement_set.ProcessingSetXdt.query
101
101
  '''
102
- if self._data and self._data.is_valid():
102
+ if self._ms_data and self._ms_data.is_valid():
103
103
  try:
104
- self._plot_inputs['selection'] |= kwargs
105
- if 'data_group_name' in kwargs:
106
- self._plot_inputs['data_group'] = kwargs['data_group_name']
107
- self._data.select_ps(query=query, string_exact_match=string_exact_match, **kwargs)
104
+ self._plot_inputs.set_selection(kwargs)
105
+ self._ms_data.select_ps(query=query, string_exact_match=string_exact_match, **kwargs)
108
106
  except KeyError as ke:
109
107
  error = "ProcessingSet selection yielded empty ProcessingSet."
110
108
  if not self._show_gui:
@@ -128,12 +126,10 @@ class MsRaster(MsPlot):
128
126
  For explanation of parameters and examples, see:
129
127
  https://xradio.readthedocs.io/en/latest/measurement_set/schema_and_api/measurement_set_api.html#xradio.measurement_set.MeasurementSetXdt.sel
130
128
  '''
131
- if self._data and self._data.is_valid():
129
+ if self._ms_data and self._ms_data.is_valid():
132
130
  try:
133
- self._plot_inputs['selection'] |= indexers_kwargs
134
- self._data.select_ms(indexers=indexers, method=method, tolerance=tolerance, drop=drop, **indexers_kwargs)
135
- if 'data_group_name' in indexers_kwargs:
136
- self._plot_inputs['data_group'] |= indexers_kwargs['data_group_name']
131
+ self._plot_inputs.set_selection(indexers_kwargs)
132
+ self._ms_data.select_ms(indexers=indexers, method=method, tolerance=tolerance, drop=drop, **indexers_kwargs)
137
133
  except KeyError as ke:
138
134
  error = str(ke).strip("\'")
139
135
  if not self._show_gui:
@@ -179,9 +175,7 @@ class MsRaster(MsPlot):
179
175
 
180
176
  If plot is successful, use show() or save() to view/save the plot.
181
177
  '''
182
- inputs = locals() # collect arguments into dict (not unused as pylint complains!)
183
- if self._plot_inputs['selection']:
184
- self._logger.info("Create raster plot with selection: %s", self._plot_inputs['selection'])
178
+ inputs = locals() # collect arguments into dict
185
179
 
186
180
  start = time.time()
187
181
 
@@ -190,23 +184,28 @@ class MsRaster(MsPlot):
190
184
 
191
185
  # Get data dimensions if valid MS is set to check input axes
192
186
  if 'data_dims' in self._ms_info:
193
- inputs['data_dims'] = self._ms_info['data_dims'] if 'data_dims' in self._ms_info else None
187
+ data_dims = self._ms_info['data_dims'] if 'data_dims' in self._ms_info else None
188
+ inputs['data_dims'] = data_dims
194
189
 
195
- # Validate input arguments then set
196
- check_inputs(inputs)
197
- self._set_plot_inputs(inputs)
190
+ self._plot_inputs.set_inputs(inputs)
191
+ selection = self._plot_inputs.get_input('selection')
192
+ if selection:
193
+ self._logger.info("Create raster plot with selection: %s", selection)
194
+
195
+ # If previous plot was shown, unlink from data streams
196
+ super().unlink_plot_streams()
198
197
 
199
198
  if not self._show_gui:
200
199
  # Cannot plot if no MS
201
- if not self._data or not self._data.is_valid():
200
+ if not self._ms_data or not self._ms_data.is_valid():
202
201
  raise RuntimeError("Cannot plot MS: input MS path is invalid or missing.")
203
202
 
204
203
  # Create raster plot and add to plot list
205
204
  try:
206
- if self._plot_inputs['iter_axis']:
207
- self._do_iter_plot(self._plot_inputs)
205
+ if self._plot_inputs.get_input('iter_axis'):
206
+ self._do_iter_plot()
208
207
  else:
209
- plot = self._do_plot(self._plot_inputs)
208
+ plot = self._do_plot()
210
209
  self._plots.append(plot)
211
210
  except RuntimeError as e:
212
211
  error = f"Plot failed: {str(e)}"
@@ -239,160 +238,155 @@ class MsRaster(MsPlot):
239
238
  filename = f"{self._ms_info['basename']}_raster.png"
240
239
  super().save(filename, fmt, width, height)
241
240
 
242
- def _set_plot_inputs(self, plot_inputs):
243
- ''' Set inputs from latest plot() call '''
244
- for key, val in plot_inputs.items():
245
- self._plot_inputs[key] = val
246
-
247
- def _do_plot(self, plot_inputs, is_gui_plot=False):
241
+ def _do_plot(self, is_gui_plot=False):
248
242
  ''' Create plot using plot inputs '''
249
243
  if not self._plot_init:
250
- self._init_plot(plot_inputs)
244
+ self._init_plot()
251
245
 
252
246
  # Select vis_axis data to plot and update selection; returns xarray Dataset
253
- raster_data = self._data.get_raster_data(plot_inputs)
247
+ raster_data = self._ms_data.get_raster_data(self._plot_inputs.get_inputs())
254
248
 
255
249
  # Save plot data for plot location callbacks unless layout (location not supported)
256
- if not self._is_layout():
250
+ if not self._plot_inputs.is_layout():
251
+ x_axis = self._plot_inputs.get_input('x_axis')
252
+ y_axis = self._plot_inputs.get_input('y_axis')
257
253
  if is_gui_plot:
258
- self._gui_plot_data = set_index_coordinates(raster_data, (self._plot_inputs['x_axis'], self._plot_inputs['y_axis']))
254
+ self._gui_plot_data = set_index_coordinates(raster_data, (x_axis, y_axis))
259
255
  else:
260
- self._plot_data = set_index_coordinates(raster_data, (self._plot_inputs['x_axis'], self._plot_inputs['y_axis']))
256
+ self._plot_data = set_index_coordinates(raster_data, (x_axis, y_axis))
261
257
 
262
258
  # Add params needed for plot: auto color range and ms name
263
- self._set_auto_color_range(plot_inputs) # set calculated limits if auto mode
259
+ self._set_auto_color_range() # set calculated limits if auto mode
264
260
  ms_name = self._ms_info['basename'] # for title
261
+ plot_inputs = self._plot_inputs.get_inputs()
265
262
  self._raster_plot.set_plot_params(raster_data, plot_inputs, ms_name)
266
263
 
267
264
  # Show plot inputs in log
268
265
  super()._set_plot_params(plot_inputs | self._raster_plot.get_plot_params()['style'])
269
- self._logger.info("MsRaster plot parameters: %s", ", ".join(self._plot_params))
266
+ plot_params = [f"{key}={value}" for key, value in self._plot_params.items()]
267
+ self._logger.info("MsRaster plot parameters: %s", ", ".join(plot_params))
270
268
 
271
269
  # Make plot. Add data min/max if GUI is shown to update color limits range.
272
270
  return self._raster_plot.raster_plot(raster_data, self._logger, self._show_gui)
273
271
 
274
- def _do_iter_plot(self, plot_inputs):
272
+ def _do_iter_plot(self):
275
273
  ''' Create one plot per iteration value in iter_range which fits into subplots '''
276
274
  # Default (0, 0) (first iteration only). Use (0, -1) for all iterations.
277
275
  # If subplots is a grid, end iteration index is limited by the grid size.
278
276
  # If subplots is a single plot, all iteration plots in the range can be saved using export_range in save().
279
- iter_axis = plot_inputs['iter_axis']
280
- iter_range = plot_inputs['iter_range']
281
- subplots = plot_inputs['subplots']
282
-
283
- iter_range = (0, 0) if iter_range is None else iter_range
284
- start_idx, end_idx = iter_range
277
+ iter_axis = self._plot_inputs.get_input('iter_axis')
278
+ iter_range = self._plot_inputs.get_input('iter_range')
279
+ subplots = self._plot_inputs.get_input('subplots')
285
280
 
286
281
  # Init plot before getting iter values
287
- self._init_plot(plot_inputs)
288
-
289
- iter_values = self._data.get_dimension_values(iter_axis)
282
+ self._init_plot()
283
+ iter_values = self._ms_data.get_dimension_values(iter_axis)
290
284
  n_iter = len(iter_values)
291
285
 
286
+ iter_range = (0, 0) if iter_range is None else iter_range
287
+ start_idx, end_idx = iter_range
288
+ auto_range = end_idx == -1
289
+
292
290
  if start_idx >= n_iter:
293
291
  raise IndexError(f"iter_range start {start_idx} is greater than number of iterations {n_iter}")
294
292
  end_idx = n_iter if (end_idx == -1 or end_idx >= n_iter) else end_idx + 1
295
293
  num_iter_plots = end_idx - start_idx
296
294
 
297
- # Plot the minimum of iter range or subplots number of plots
295
+ # Plot the minimum of iter range or subplots number of plots.
296
+ # If subplots is single plot, plot all for save()
298
297
  num_subplots = np.prod(subplots) if subplots else 1
299
298
  num_iter_plots = min(num_iter_plots, num_subplots) if num_subplots > 1 else num_iter_plots
300
299
  end_idx = start_idx + num_iter_plots
301
- self._plot_inputs['iter_range'] = (start_idx, end_idx)
302
300
 
303
- # Set each iter value in selection
304
- if 'selection' not in plot_inputs:
305
- plot_inputs['selection'] = {}
301
+ if auto_range:
302
+ # For listing plot inputs
303
+ start_idx = start_idx.item() if isinstance(start_idx, np.int64) else start_idx
304
+ end_idx = end_idx.item() if isinstance(end_idx, np.int64) else end_idx
305
+ self._plot_inputs.set_input('auto_iter_range', (start_idx, end_idx - 1))
306
306
 
307
307
  for i in range(start_idx, end_idx):
308
308
  # Select iteration value and make plot
309
309
  value = iter_values[i]
310
310
  self._logger.info("Plot %s iteration index %s value %s", iter_axis, i, value)
311
- plot_inputs['selection'][iter_axis] = value
311
+ self._plot_inputs.set_selection({iter_axis: value})
312
312
  try:
313
- plot = self._do_plot(plot_inputs)
313
+ plot = self._do_plot()
314
314
  self._plots.append(plot)
315
315
  except RuntimeError as e:
316
316
  self._logger.info("Iteration plot for value %s failed: %s", str(value), str(e))
317
317
  continue
318
318
 
319
- def _init_plot(self, plot_inputs):
319
+ def _init_plot(self):
320
320
  ''' Apply automatic selection '''
321
321
  # Remove previous auto selections
322
322
  for key in ['dim_selection', 'auto_spw']:
323
- try:
324
- del self._plot_inputs[key]
325
- except KeyError:
326
- pass
323
+ self._plot_inputs.remove_input(key)
327
324
 
328
325
  # Automatically select data group and spw name if not user-selected
329
326
  auto_selection = {}
330
- if 'data_group' not in plot_inputs:
327
+ if not self._plot_inputs.get_input('data_group'):
331
328
  auto_selection['data_group_name'] = 'base'
332
- plot_inputs['data_group'] = 'base'
333
- if 'selection' not in plot_inputs or 'spw_name' not in plot_inputs['selection']:
334
- first_spw = self._data.get_first_spw(plot_inputs['data_group'])
329
+ self._plot_inputs.set_input('data_group', 'base')
330
+
331
+ data_group = self._plot_inputs.get_input('data_group')
332
+ spw_selection = self._plot_inputs.get_selection('spw_name')
333
+ if not spw_selection:
334
+ first_spw = self._ms_data.get_first_spw(data_group)
335
335
  auto_selection['spw_name'] = first_spw
336
- plot_inputs['auto_spw'] = first_spw # do not add to user selection
336
+ self._plot_inputs.set_input('auto_spw', first_spw) # keep separate from user selection
337
337
 
338
338
  if auto_selection:
339
339
  # Do selection and save to plot inputs
340
340
  self._logger.info("Automatic selection of data group and/or spw: %s", auto_selection)
341
- self._data.select_ps(query=None, string_exact_match=True, **auto_selection)
341
+ self._ms_data.select_ps(query=None, string_exact_match=True, **auto_selection)
342
342
 
343
343
  # Print data info for spw selection
344
- self._logger.info("Plotting %s msv4 datasets.", self._data.get_num_ms())
345
- self._logger.info("Maximum dimensions for selected spw: %s", self._data.get_max_data_dims())
344
+ self._logger.info("Plotting %s msv4 datasets.", self._ms_data.get_num_ms())
345
+ self._logger.info("Maximum dimensions for selected spw: %s", self._ms_data.get_max_data_dims())
346
346
  self._plot_init = True
347
347
 
348
- def _is_layout(self):
349
- ''' Determine if plot is a layout using plot inputs '''
350
- # Check if subplots is a layout
351
- if self._plot_inputs['subplots'] is None or self._plot_inputs['subplots'] == (1, 1):
352
- return False
353
-
354
- # Check if iteration set and iter_range more than one plot
355
- iter_length = 0
356
- if self._plot_inputs['iter_axis'] is not None:
357
- iter_range = self._plot_inputs['iter_range']
358
- iter_range = (0, 0) if iter_range is None else iter_range
359
- iter_length = len(range(iter_range[0], iter_range[1] + 1))
360
-
361
- # Also check if previous plot(s) not cleared
362
- return iter_length > 1 or len(self._plots) > 0
363
-
364
- def _set_auto_color_range(self, plot_inputs):
348
+ def _set_auto_color_range(self):
365
349
  ''' Calculate stats for color limits for non-gui amplitude plots. '''
366
- color_mode = plot_inputs['color_mode']
367
- color_limits = None
350
+ color_mode = self._plot_inputs.get_input('color_mode')
351
+ auto_color_limits = None
368
352
 
369
353
  if color_mode == 'auto':
370
- if plot_inputs['vis_axis']=='amp' and not plot_inputs['aggregator']:
354
+ if self._plot_inputs.get_input('vis_axis') == 'amp' and not self._plot_inputs.get_input('aggregator'):
371
355
  # For amplitude, limit colorbar range using stored per-spw ms stats
372
- spw_name = self._plot_inputs['selection']['spw_name'] if ('selection' in plot_inputs and 'spw_name' in plot_inputs['selection']) else self._plot_inputs['auto_spw']
356
+ spw_name = self._plot_inputs.get_selection('spw_name')
357
+ if not spw_name:
358
+ spw_name = self._plot_inputs.get_input('auto_spw')
359
+
373
360
  if spw_name in self._spw_color_limits:
374
- color_limits = self._spw_color_limits[spw_name]
361
+ auto_color_limits = self._spw_color_limits[spw_name]
375
362
  else:
376
363
  # Select spw name and data group only, no dimensions
377
- data_group = self._plot_inputs['data_group']
364
+ data_group = self._plot_inputs.get_input('data_group')
378
365
  spw_data_selection = {'spw_name': spw_name, 'data_group_name': data_group}
379
- color_limits = self._calc_amp_color_limits(spw_data_selection)
380
- self._spw_color_limits[spw_name] = color_limits
381
- plot_inputs['auto_color_range'] = color_limits
382
-
383
- if color_limits:
384
- self._logger.info("Setting amplitude color range: (%.4f, %.4f).", color_limits[0], color_limits[1])
366
+ auto_color_limits = self._calc_amp_color_limits(spw_data_selection)
367
+
368
+ if auto_color_limits:
369
+ # Convert to float for listing plot inputs
370
+ start, end = auto_color_limits
371
+ start = start.item() if isinstance(start, np.float64) else start
372
+ end = end.item() if isinstance(end, np.float64) else end
373
+ auto_color_limits = (start, end)
374
+ self._spw_color_limits[spw_name] = auto_color_limits
375
+ self._plot_inputs.set_input('auto_color_range', auto_color_limits)
376
+
377
+ if auto_color_limits:
378
+ self._logger.info("Setting amplitude color range: (%.4f, %.4f).", auto_color_limits[0], auto_color_limits[1])
385
379
  elif color_mode is None:
386
380
  self._logger.info("Autoscale color range")
387
381
  else:
388
- self._logger.info("Using manual color range: %s", plot_inputs['color_range'])
382
+ self._logger.info("Using manual color range: %s", self._plot_inputs.get_input('color_range'))
389
383
 
390
384
  def _calc_amp_color_limits(self, selection):
391
385
  # Calculate colorbar limits from amplitude stats for unflagged data in selected spw
392
386
  self._logger.info("Calculating stats for colorbar limits.")
393
387
  start = time.time()
394
388
 
395
- ms_stats = self._data.get_vis_stats(selection, 'amp')
389
+ ms_stats = self._ms_data.get_vis_stats(selection, 'amp')
396
390
  self._spw_stats['spw_name'] = ms_stats
397
391
  if not ms_stats:
398
392
  return None # autoscale
@@ -417,7 +411,7 @@ class MsRaster(MsPlot):
417
411
  if clear_plots:
418
412
  super().clear_plots()
419
413
 
420
- # Clear params set for last plot
414
+ # Clear params for last plot
421
415
  self._raster_plot.reset_plot_params()
422
416
 
423
417
  # Unitialize plot to redo auto selections if needed
@@ -443,8 +437,8 @@ class MsRaster(MsPlot):
443
437
  'update_plot': self._update_plot,
444
438
  }
445
439
  data_dims = self._ms_info['data_dims'] if 'data_dims' in self._ms_info else None
446
- x_axis = self._plot_inputs['x_axis']
447
- y_axis = self._plot_inputs['y_axis']
440
+ x_axis = self._plot_inputs.get_input('x_axis')
441
+ y_axis = self._plot_inputs.get_input('y_axis')
448
442
  self._gui_layout = create_raster_gui(callbacks, data_dims, x_axis, y_axis)
449
443
  self._gui_layout.show(title=self._app_name, threaded=True)
450
444
 
@@ -473,31 +467,20 @@ class MsRaster(MsPlot):
473
467
 
474
468
  # User changed inputs without clicking Plot button, or callback for cursor/box.
475
469
  if not do_plot and not self._first_gui_plot:
476
- if cursor_changed(x, y, self._last_cursor):
477
- # new cursor position - update cursor location box
478
- self._update_cursor_location(x, y)
479
- self._last_cursor = (x, y)
480
-
481
- if points_changed(data, self._last_points):
482
- # new points position - update selected points location tab
483
- self._update_points_location(data)
484
- self._last_points = data
485
-
486
- if box_changed(bounds, self._last_box):
487
- # new box_select position - update selected box location tab
488
- self._update_box_location(bounds)
489
- self._last_box = bounds
490
-
470
+ # Locate callbacks
471
+ super()._locate_cursor(x, y, self._gui_plot_data, self._gui_layout[0])
472
+ super()._locate_points(data, self._gui_plot_data, self._gui_layout[0])
473
+ super()._locate_box(bounds, self._gui_plot_data, self._gui_layout[0])
491
474
  # Not ready to update plot yet, return last plot.
492
- return self._last_gui_plot
475
+ return self._last_plot
493
476
 
494
477
  # First plot for input ms, or user clicked Plot button for new inputs
495
- if (self._set_ms(ms) or self._first_gui_plot) and self._data and self._data.is_valid():
478
+ if (self._set_ms(ms) or self._first_gui_plot) and self._ms_data and self._ms_data.is_valid():
496
479
  # New MS set and is valid
497
480
  self._update_gui_axis_options()
498
481
 
499
482
  # Add ms path to detect change and make new plot
500
- self._plot_inputs['ms'] = ms
483
+ self._plot_inputs.set_input('ms', ms)
501
484
 
502
485
  # Do new plot or resend last plot
503
486
  self._reset_plot()
@@ -506,12 +489,14 @@ class MsRaster(MsPlot):
506
489
 
507
490
  # Make plot if first plot or changed plot
508
491
  style_inputs = self._raster_plot.get_plot_params()['style']
509
- if inputs_changed(self._plot_inputs, self._last_plot_inputs) or inputs_changed(style_inputs, self._last_style_inputs):
492
+ plot_inputs = self._plot_inputs.get_inputs()
493
+ if inputs_changed(plot_inputs, self._last_plot_inputs) or inputs_changed(style_inputs, self._last_style_inputs):
510
494
  try:
511
495
  # Check inputs from GUI then plot
512
- self._plot_inputs['data_dims'] = self._ms_info['data_dims']
513
- check_inputs(self._plot_inputs)
514
- if 'ps_selection' in self._plot_inputs or 'ms_selection' in self._plot_inputs:
496
+ data_dims = self._ms_info['data_dims']
497
+ self._plot_inputs.set_input('data_dims', data_dims)
498
+ self._plot_inputs.check_inputs()
499
+ if 'ps_selection' in self._gui_selection or 'ms_selection' in self._gui_selection:
515
500
  self._do_gui_selection()
516
501
  gui_plot = self._do_gui_plot()
517
502
  except (ValueError, TypeError, KeyError, RuntimeError) as e:
@@ -520,25 +505,17 @@ class MsRaster(MsPlot):
520
505
  gui_plot = self._empty_plot
521
506
  else:
522
507
  # Subparam values changed but not applied to plot
523
- gui_plot = self._last_gui_plot
508
+ gui_plot = self._last_plot
524
509
 
525
510
  # Update plot inputs for gui
526
- self._set_plot_params(self._plot_inputs | style_inputs)
511
+ self._set_plot_params(plot_inputs | style_inputs)
527
512
 
528
513
  # Save inputs to see if changed next time
529
- self._last_plot_inputs = self._plot_inputs.copy()
514
+ self._last_plot_inputs = plot_inputs.copy()
530
515
  self._last_style_inputs = style_inputs.copy()
531
- self._last_plot_inputs['ps_selection'] = self._plot_inputs['ps_selection'].copy()
532
- self._last_plot_inputs['ms_selection'] = self._plot_inputs['ms_selection'].copy()
533
516
 
534
517
  # Save plot for return value if plot update not requested
535
- self._last_gui_plot = gui_plot
536
-
537
- # Remove selection not from user
538
- if self._first_gui_plot and not do_plot:
539
- self._plot_inputs['ps_selection'].clear()
540
- self._plot_inputs['ms_selection'].clear()
541
-
518
+ self._last_plot = gui_plot
542
519
  self._first_gui_plot = False
543
520
 
544
521
  # Add plot inputs to GUI, change plot button to outline, and stop spinner
@@ -551,35 +528,34 @@ class MsRaster(MsPlot):
551
528
 
552
529
  def _do_gui_selection(self):
553
530
  ''' Apply selections selected in GUI '''
554
- if self._plot_inputs['ps_selection']:
555
- self.select_ps(**self._plot_inputs['ps_selection'])
556
- if self._plot_inputs['ms_selection']:
557
- self.select_ms(**self._plot_inputs['ms_selection'])
531
+ if self._gui_selection['ps_selection']:
532
+ self.select_ps(**self._gui_selection['ps_selection'])
533
+ if self._gui_selection['ms_selection']:
534
+ self.select_ms(**self._gui_selection['ms_selection'])
558
535
 
559
536
  ###
560
537
  ### Create plot for DynamicMap
561
538
  ###
562
539
  def _do_gui_plot(self):
563
540
  ''' Create plot based on gui plot inputs '''
564
- if self._data and self._data.is_valid():
541
+ if self._ms_data and self._ms_data.is_valid():
565
542
  try:
566
- if self._plot_inputs['iter_axis']:
543
+ if self._plot_inputs.get_input('iter_axis'):
567
544
  # Make iter plot (possibly with subplots layout)
568
- self._do_iter_plot(self._plot_inputs)
569
- subplots = self._plot_inputs['subplots']
545
+ self._do_iter_plot()
546
+ subplots = self._plot_inputs.get_input('subplots')
570
547
  layout_plot, is_layout = super()._layout_plots(subplots)
571
-
572
548
  if is_layout:
573
549
  # Cannot show Layout in DynamicMap, show in new tab
574
550
  super().show()
575
551
  self._logger.info("Plot update complete")
576
- return self._last_gui_plot
552
+ return self._last_plot
577
553
  # Overlay raster plot for DynamicMap
578
554
  self._logger.info("Plot update complete")
579
555
  return layout_plot
580
556
 
581
557
  # Make single Overlay raster plot for DynamicMap
582
- plot = self._do_plot(self._plot_inputs, True)
558
+ plot = self._do_plot(True)
583
559
  plot_params = self._raster_plot.get_plot_params()
584
560
 
585
561
  # Update color limits in gui with data range
@@ -593,7 +569,7 @@ class MsRaster(MsPlot):
593
569
  return plot.opts(
594
570
  hv.opts.QuadMesh(
595
571
  tools=['hover', 'box_select'],
596
- selection_fill_alpha=0.5, # dim selected areas of plot
572
+ selection_fill_alpha=0.2, # dim selected areas of plot
597
573
  nonselection_fill_alpha=1.0, # do not dim unselected areas of plot
598
574
  )
599
575
  )
@@ -705,7 +681,7 @@ class MsRaster(MsPlot):
705
681
  # Update options for vis_axis selector
706
682
  vis_axis_selector = axis_selectors.objects[2]
707
683
  vis_axis_value = vis_axis_selector.value
708
- if self._data.get_correlated_data('base') == 'SPECTRUM':
684
+ if self._ms_data.get_correlated_data('base') == 'SPECTRUM':
709
685
  vis_axis_selector.options = SPECTRUM_AXIS_OPTIONS
710
686
  else:
711
687
  vis_axis_selector.options = VIS_AXIS_OPTIONS
@@ -731,8 +707,8 @@ class MsRaster(MsPlot):
731
707
 
732
708
  def _update_ps_selection_options(self, ps_selectors):
733
709
  ''' Set ProcessingSet gui options from ms summary '''
734
- if self._data and self._data.is_valid():
735
- summary = self._data.get_summary()
710
+ if self._ms_data and self._ms_data.is_valid():
711
+ summary = self._ms_data.get_summary()
736
712
  if summary is None:
737
713
  return
738
714
 
@@ -749,7 +725,7 @@ class MsRaster(MsPlot):
749
725
 
750
726
  def _update_ms_selection_options(self, ms_selectors):
751
727
  ''' Set MeasurementSet gui options from ms data '''
752
- if self._data and self._data.is_valid():
728
+ if self._ms_data and self._ms_data.is_valid():
753
729
  for selector in ms_selectors:
754
730
  selection_key = MS_SELECTION_OPTIONS[selector.name] if selector.name in MS_SELECTION_OPTIONS else None
755
731
  if selection_key:
@@ -782,7 +758,7 @@ class MsRaster(MsPlot):
782
758
  ''' Set up player with values when iter_axis is selected '''
783
759
  iter_axis = None if iter_axis == 'None' else iter_axis
784
760
  if iter_axis and self._gui_layout:
785
- iter_values = self._data.get_dimension_values(iter_axis)
761
+ iter_values = self._ms_data.get_dimension_values(iter_axis)
786
762
  if iter_values:
787
763
 
788
764
  iter_selectors = self._get_selector('iter')
@@ -812,7 +788,7 @@ class MsRaster(MsPlot):
812
788
 
813
789
  def _get_datetime_values(self, float_times):
814
790
  ''' Return list of float time values as list of datetime values for gui options '''
815
- time_attrs = self._data.get_dimension_attrs('time')
791
+ time_attrs = self._ms_data.get_dimension_attrs('time')
816
792
  datetime_values = []
817
793
  try:
818
794
  datetime_values = to_datetime(float_times, unit=time_attrs['units'], origin=time_attrs['format'])
@@ -835,105 +811,16 @@ class MsRaster(MsPlot):
835
811
  button.button_style = 'solid' if plot_changed else 'outline'
836
812
 
837
813
  def _show_plot_inputs(self):
838
- ''' Show inputs for raster plot in GUI tab '''
839
- if self._plot_params:
840
- inputs_column = self._gui_layout[0][1]
841
- inputs_column.clear()
842
- for param in self._plot_params:
843
- str_pane = pn.pane.Str(param)
844
- str_pane.margin = (0, 10)
845
- inputs_column.append(str_pane)
846
-
847
- def _update_cursor_location(self, x, y):
848
- ''' Show data values for cursor x,y position in cursor location box below plot '''
849
- # Convert plot values to selection values to select plot data
850
- x_axis = self._plot_inputs['x_axis']
851
- y_axis = self._plot_inputs['y_axis']
852
- cursor_position = {x_axis: x, y_axis: y}
853
- cursor_location = locate_point(self._gui_plot_data, cursor_position, self._plot_inputs['vis_axis'])
854
-
855
- cursor_location_box = self._gui_layout[0][0][1] # row[0] tabs[0] column[1]
856
- cursor_location_box.clear() # pn.WidgetBox
857
- location_layout = pn.Column(pn.widgets.StaticText(name="Cursor Location"))
858
- location_row = self._layout_point_location(cursor_location)
859
- # Add row of columns to column layout
860
- location_layout.append(location_row)
861
- # Add column layout to widget box
862
- cursor_location_box.append(location_layout)
863
-
864
- def _layout_point_location(self, text_list):
865
- ''' Layout list of StaticText in row of columns containing 3 rows '''
866
- location_row = pn.Row()
867
- location_col = pn.Column()
868
-
869
- for static_text in text_list:
870
- # 3 entries per column; append to row and start new column
871
- if len(location_col.objects) == 3:
872
- location_row.append(location_col)
873
- location_col = pn.Column()
874
-
875
- static_text.margin = (0, 10) # default (5, 10)
876
- location_col.append(static_text)
877
-
878
- # Add last column
879
- location_row.append(location_col)
880
- return location_row
881
-
882
- def _update_points_location(self, data):
883
- ''' Show data values for points in point_draw in tab and log '''
884
- points_locate_column = self._gui_layout[0][2] # row[0] tabs[2]
885
- points_locate_column.clear()
886
- points = list(zip(data['x'], data['y']))
887
-
888
- if points:
889
- self._logger.info("Locate selected points:")
890
- x_axis = self._plot_inputs['x_axis']
891
- y_axis = self._plot_inputs['y_axis']
892
-
893
- for point in list(zip(data['x'], data['y'])):
894
- # Locate point
895
- point_position = {x_axis: point[0], y_axis: point[1]}
896
- point_location = locate_point(self._gui_plot_data, point_position, self._plot_inputs['vis_axis'])
897
- # Format location and add to points locate column
898
- location_layout = self._layout_point_location(point_location)
899
- points_locate_column.append(location_layout)
900
- points_locate_column.append(pn.layout.Divider())
901
-
902
- # Format and add to log
903
- location_list = [f"{static_text.name}={static_text.value}" for static_text in point_location]
904
- self._logger.info(", ".join(location_list))
905
-
906
- def _update_box_location(self, bounds):
907
- ''' Show data values for points in box_select in tab and log '''
908
- box_locate_column = self._gui_layout[0][3] # row[0] tabs[3]
909
- box_locate_column.clear()
910
- if bounds:
911
- x_axis = self._plot_inputs['x_axis']
912
- y_axis = self._plot_inputs['y_axis']
913
- box_bounds = {x_axis: (bounds[0], bounds[2]), y_axis: (bounds[1], bounds[3])}
914
- npoints, point_locations = locate_box(self._gui_plot_data, box_bounds, self._plot_inputs['vis_axis'])
915
-
916
- message = f"Locate {npoints} points"
917
- message += " (only first 100 shown):" if npoints > 100 else ":"
918
- self._logger.info(message)
919
- box_locate_column.append(pn.pane.Str(message))
920
-
921
- for point in point_locations:
922
- # Format and add to box locate column
923
- location_layout = self._layout_point_location(point)
924
- box_locate_column.append(location_layout)
925
- box_locate_column.append(pn.layout.Divider())
926
-
927
- # Format and add to log
928
- location_list = [f"{static_text.name}={static_text.value}" for static_text in point]
929
- self._logger.info(", ".join(location_list))
814
+ ''' Show inputs for raster plot in column in GUI tab '''
815
+ inputs_column = self._gui_layout[0][1]
816
+ super()._fill_inputs_column(inputs_column)
930
817
 
931
818
  ###
932
819
  ### Callbacks for widgets which update plot inputs
933
820
  ###
934
821
  def _set_title(self, title):
935
822
  ''' Set title from gui text input '''
936
- self._plot_inputs['title'] = title
823
+ self._plot_inputs.set_input('title', title)
937
824
  self._update_plot_status(True) # Change plot button to solid
938
825
 
939
826
  def _set_style_params(self, unflagged_cmap, flagged_cmap, show_colorbar, show_flagged_colorbar):
@@ -942,65 +829,59 @@ class MsRaster(MsPlot):
942
829
 
943
830
  def _set_color_range(self, color_mode, color_range):
944
831
  ''' Set style params from gui '''
945
- color_mode = color_mode.split()[0]
946
- color_mode = None if color_mode == 'No' else color_mode
947
- self._plot_inputs['color_mode'] = color_mode
948
- self._plot_inputs['color_range'] = color_range
832
+ self._plot_inputs.set_color_inputs(color_mode, color_range)
949
833
  self._update_plot_status(True) # Change plot button to solid
950
834
 
951
835
  def _set_axes(self, x_axis, y_axis, vis_axis):
952
- ''' Set plot axis params from gui '''
953
- self._plot_inputs['x_axis'] = x_axis
954
- self._plot_inputs['y_axis'] = y_axis
955
- self._plot_inputs['vis_axis'] = vis_axis
836
+ ''' Set plot axis inputs from gui '''
837
+ self._plot_inputs.set_axis_inputs(x_axis, y_axis, vis_axis)
956
838
  self._update_plot_status(True) # Change plot button to solid
957
839
 
958
840
  def _set_aggregation(self, aggregator, agg_axes):
959
841
  ''' Set aggregation params from gui '''
960
- aggregator = None if aggregator== 'None' else aggregator
961
- self._plot_inputs['aggregator'] = aggregator
962
- self._plot_inputs['agg_axis'] = agg_axes # ignored if aggregator not set
842
+ self._plot_inputs.set_aggregation_inputs(aggregator, agg_axes)
963
843
  self._update_plot_status(True) # Change plot button to solid
964
844
 
965
845
  # pylint: disable=too-many-arguments, too-many-positional-arguments
966
846
  def _set_iteration(self, iter_axis, iter_value_type, iter_value, iter_start, iter_end, subplot_rows, subplot_columns):
967
847
  ''' Set iteration params from gui '''
968
848
  iter_axis = None if iter_axis == 'None' else iter_axis
969
- self._plot_inputs['iter_axis'] = iter_axis
970
- self._plot_inputs['subplots'] = (subplot_rows, subplot_columns)
971
-
849
+ iter_range = None
972
850
  if iter_axis:
851
+ # Set iter_range
973
852
  if iter_value_type == 'By Value':
974
853
  # Use index of iter_value for tuple
975
- if self._data and self._data.is_valid():
976
- iter_values = self._data.get_dimension_values(iter_axis)
854
+ if self._ms_data and self._ms_data.is_valid():
855
+ iter_values = self._ms_data.get_dimension_values(iter_axis)
977
856
  iter_index = iter_values.index(iter_value)
978
- self._plot_inputs['iter_range'] = (iter_index, iter_index)
857
+ iter_range = (iter_index, iter_index)
979
858
  else:
980
859
  # 'By Range': use range start and end values for tuple
981
- self._plot_inputs['iter_range'] = (iter_start, iter_end)
982
- else:
983
- self._plot_inputs['iter_range'] = None
860
+ iter_range = (iter_start, iter_end)
861
+
862
+ self._plot_inputs.set_iteration_inputs(iter_axis, iter_range, subplot_rows, subplot_columns)
984
863
  self._update_plot_status(True) # Change plot button to solid
864
+ # pylint: enable=too-many-arguments, too-many-positional-arguments
985
865
 
866
+ # pylint: disable=too-many-arguments, too-many-positional-arguments, unused-argument
986
867
  def _set_ps_selection(self, query, name, intents, scan_name, spw_name, field_name, source_name, line_name):
987
- ''' Select ProcessingSet from gui using summary columns '''
868
+ ''' Set ProcessingSet selection from gui using summary columns '''
988
869
  inputs = locals()
989
870
  ps_selection = {}
990
871
  for key, val in inputs.items():
991
872
  if key in PS_SELECTION_OPTIONS.values() and val:
992
873
  ps_selection[key] = val
993
- self._plot_inputs['ps_selection'] = ps_selection
874
+ self._gui_selection['ps_selection'] = ps_selection
994
875
  self._update_plot_status(True) # Change plot button to solid
995
876
 
996
877
  def _set_ms_selection(self, data_group, datetime, baseline, antenna1, antenna2, frequency, polarization):
997
- ''' Select MeasurementSet from gui using data group, dimensions, and coordinates '''
878
+ ''' Set MeasurementSet selection from gui using data group, dimensions, and coordinates '''
998
879
  inputs = locals()
999
880
  inputs['time'] = inputs.pop('datetime')
1000
881
  ms_selection = {}
1001
882
  for key, val in inputs.items():
1002
883
  if key in MS_SELECTION_OPTIONS.values() and val:
1003
884
  ms_selection[key] = val
1004
- self._plot_inputs['ms_selection'] = ms_selection
885
+ self._gui_selection['ms_selection'] = ms_selection
1005
886
  self._update_plot_status(True) # Change plot button to solid
1006
887
  # pylint: enable=too-many-arguments, too-many-positional-arguments, unused-argument