vidavis 0.0.14__tar.gz → 0.1.0__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 (39) hide show
  1. {vidavis-0.0.14 → vidavis-0.1.0}/PKG-INFO +1 -1
  2. {vidavis-0.0.14 → vidavis-0.1.0}/pyproject.toml +3 -2
  3. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/apps/_ms_raster.py +111 -151
  4. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/processing_set/_ps_coords.py +4 -0
  5. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/processing_set/_ps_data.py +15 -10
  6. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/processing_set/_ps_raster_data.py +6 -3
  7. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/processing_set/_ps_select.py +1 -1
  8. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_locate_points.py +1 -1
  9. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_ms_plot.py +63 -61
  10. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_ms_plot_selectors.py +18 -11
  11. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_plot_inputs.py +1 -1
  12. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_raster_plot.py +2 -6
  13. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_raster_plot_gui.py +15 -33
  14. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_raster_plot_inputs.py +9 -10
  15. {vidavis-0.0.14 → vidavis-0.1.0}/LICENSE +0 -0
  16. {vidavis-0.0.14 → vidavis-0.1.0}/readme.rst +0 -0
  17. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/LICENSE.rst +0 -0
  18. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/__init__.py +0 -0
  19. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/apps/__init__.py +0 -0
  20. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/bokeh/__init__.py +0 -0
  21. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/bokeh/_palette.py +0 -0
  22. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/__init__.py +0 -0
  23. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/__init__.py +0 -0
  24. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/_ms_data.py +0 -0
  25. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/processing_set/__init__.py +0 -0
  26. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/processing_set/_ps_concat.py +0 -0
  27. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/processing_set/_ps_io.py +0 -0
  28. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/processing_set/_ps_stats.py +0 -0
  29. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/data/measurement_set/processing_set/_xds_data.py +0 -0
  30. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/__init__.py +0 -0
  31. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/__init__.py +0 -0
  32. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_check_raster_inputs.py +0 -0
  33. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_ms_plot_constants.py +0 -0
  34. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_time_ticks.py +0 -0
  35. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/plot/ms_plot/_xds_plot_axes.py +0 -0
  36. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/toolbox/__init__.py +0 -0
  37. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/toolbox/_app_context.py +0 -0
  38. {vidavis-0.0.14 → vidavis-0.1.0}/src/vidavis/toolbox/_logging.py +0 -0
  39. {vidavis-0.0.14 → vidavis-0.1.0}/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.14
3
+ Version: 0.1.0
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>
@@ -7,15 +7,16 @@ authors = [
7
7
  { name = "Pam Harris", email = "pharris@nrao.edu" },
8
8
  ]
9
9
  dependencies = [
10
- "bokeh==3.6.1",
10
+ "bokeh>=3.7, <3.9",
11
11
  "matplotlib",
12
+ "panel==1.8.2",
12
13
  "hvplot",
13
14
  "graphviper",
14
15
  "selenium",
15
16
  ]
16
17
  requires-python = ">=3.11, <3.14"
17
18
  readme = "readme.rst"
18
- version = "0.0.14"
19
+ version = "0.1.0"
19
20
 
20
21
  [project.license]
21
22
  text = "LGPL"
@@ -42,26 +42,30 @@ class MsRaster(MsPlot):
42
42
 
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
- self._raster_plot = RasterPlot()
46
45
  self._plot_inputs = RasterPlotInputs()
47
46
  self._plot_inputs.set_input('ms', self._ms_info['ms'])
47
+ self._raster_plot = RasterPlot()
48
48
 
49
49
  # Calculations for color limits
50
50
  self._spw_stats = {}
51
51
  self._spw_color_limits = {}
52
52
 
53
53
  if show_gui:
54
- # Return plot for gui DynamicMap: empty plot when ms not set or plot fails
55
- self._empty_plot = self._create_empty_plot()
56
-
57
- # Set default style and plot inputs to use when launching gui
54
+ # Set default style and plot inputs to use for empty plot and gui
58
55
  self.set_style_params()
59
56
  self.plot()
57
+
58
+ # Initial plot for gui
59
+ self._create_empty_plot()
60
+
61
+ # Create and show gui panel
60
62
  self._launch_gui()
61
63
 
62
- # Set filename TextInput widget to input ms, which triggers default plot
64
+ # Set filename TextInput widget to input ms
63
65
  if 'ms' in self._ms_info and self._ms_info['ms']:
64
- self._set_filename([self._ms_info['ms']]) # function expects list from FileBrowser widget
66
+ self._set_filename(self._ms_info['ms'])
67
+ self._update_gui_ms_options()
68
+ self._update_plot(do_plot=True)
65
69
 
66
70
  def colormaps(self):
67
71
  ''' List available colormap (Bokeh palettes). '''
@@ -179,6 +183,9 @@ class MsRaster(MsPlot):
179
183
 
180
184
  start = time.time()
181
185
 
186
+ # If previous plot was shown, unlink from data streams
187
+ super().unlink_plot_locate()
188
+
182
189
  # Clear for new plot
183
190
  self._reset_plot(clear_plots)
184
191
 
@@ -192,9 +199,6 @@ class MsRaster(MsPlot):
192
199
  if selection:
193
200
  self._logger.info("Create raster plot with selection: %s", selection)
194
201
 
195
- # If previous plot was shown, unlink from data streams
196
- super().unlink_plot_streams()
197
-
198
202
  if not self._show_gui:
199
203
  # Cannot plot if no MS
200
204
  if not self._ms_data or not self._ms_data.is_valid():
@@ -264,7 +268,7 @@ class MsRaster(MsPlot):
264
268
  # Show plot inputs in log
265
269
  super()._set_plot_params(plot_inputs | self._raster_plot.get_plot_params()['style'])
266
270
  plot_params = [f"{key}={value}" for key, value in self._plot_params.items()]
267
- self._logger.info("MsRaster plot parameters: %s", ", ".join(plot_params))
271
+ self._logger.info("MsRaster plot inputs: %s", ", ".join(plot_params))
268
272
 
269
273
  # Make plot. Add data min/max if GUI is shown to update color limits range.
270
274
  return self._raster_plot.raster_plot(raster_data, self._logger, self._show_gui)
@@ -423,7 +427,8 @@ class MsRaster(MsPlot):
423
427
  def _launch_gui(self):
424
428
  ''' Use Holoviz Panel to create and show a dashboard for plot inputs. '''
425
429
  callbacks = {
426
- 'filename': self._set_filename,
430
+ 'set_filename': self._set_filename,
431
+ 'select_filename': self._select_filename,
427
432
  'style': self._set_style_params,
428
433
  'color': self._set_color_range,
429
434
  'axes': self._set_axes,
@@ -433,105 +438,97 @@ class MsRaster(MsPlot):
433
438
  'iter_values': self._set_iter_values,
434
439
  'iteration': self._set_iteration,
435
440
  'title': self._set_title,
436
- 'plot_updating': self._update_plot_spinner,
437
441
  'update_plot': self._update_plot,
438
442
  }
443
+
439
444
  data_dims = self._ms_info['data_dims'] if 'data_dims' in self._ms_info else None
440
- x_axis = self._plot_inputs.get_input('x_axis')
441
- y_axis = self._plot_inputs.get_input('y_axis')
442
- self._gui_layout = create_raster_gui(callbacks, data_dims, x_axis, y_axis)
443
- self._gui_layout.show(title=self._app_name, threaded=True)
445
+ plot_info = {
446
+ 'ms': self._ms_info['ms'],
447
+ 'data_dims': data_dims,
448
+ 'x_axis': self._plot_inputs.get_input('x_axis'),
449
+ 'y_axis': self._plot_inputs.get_input('y_axis'),
450
+ }
451
+
452
+ self._gui_panel = create_raster_gui(callbacks, plot_info, self._empty_plot)
453
+ self._gui_panel.show(title=self._app_name, threaded=True)
444
454
 
445
455
  ###
446
456
  ### Main callback to create plot if inputs changed
447
457
  ###
448
458
  # pylint: disable=too-many-arguments, too-many-positional-arguments
449
- def _update_plot(self, ms, do_plot, x, y, data, bounds):
459
+ def _update_plot(self, do_plot):
450
460
  ''' Create plot with inputs from GUI, or update cursor/box location.
451
461
  Callbacks:
452
- ms (first plot) - return plot with default inputs
453
462
  do_plot (Plot button clicked) - return plot with inputs from GUI
454
- x, y (PointerXY) - update cursor location box
455
- data (PointDraw from point_draw tool) - update location of drawn points in tab and log
456
- bounds (Bounds from box_select tool) - update location of points in box in tab and log
457
463
  This function *must* return plot, even if empty plot or last plot, for DynamicMap.
458
464
  '''
465
+ if not do_plot or not self._gui_panel:
466
+ return
467
+
459
468
  # Remove toast notification and collapse selection accordion
460
469
  if self._toast:
461
470
  self._toast.destroy()
462
471
  self._get_selector("selectors").active = []
463
-
464
- if not ms:
465
- # GUI launched with no MS, nothing to plot
466
- return self._empty_plot
467
-
468
- # User changed inputs without clicking Plot button, or callback for cursor/box.
469
- if not do_plot and not self._first_gui_plot:
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])
474
- # Not ready to update plot yet, return last plot.
475
- return self._last_plot
476
-
477
- # First plot for input ms, or user clicked Plot button for new inputs
478
- if (self._set_ms(ms) or self._first_gui_plot) and self._ms_data and self._ms_data.is_valid():
479
- # New MS set and is valid
480
- self._update_gui_axis_options()
481
-
482
- # Add ms path to detect change and make new plot
483
- self._plot_inputs.set_input('ms', ms)
484
-
485
- # Do new plot or resend last plot
486
- self._reset_plot()
487
- self.clear_selection()
488
472
  gui_plot = None
489
473
 
490
- # Make plot if first plot or changed plot
491
- style_inputs = self._raster_plot.get_plot_params()['style']
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):
494
- try:
495
- # Check inputs from GUI then plot
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:
500
- self._do_gui_selection()
501
- gui_plot = self._do_gui_plot()
502
- except (ValueError, TypeError, KeyError, RuntimeError) as e:
503
- # Clear plot, inputs invalid
504
- self._notify(str(e), 'error', 0)
505
- gui_plot = self._empty_plot
506
- else:
507
- # Subparam values changed but not applied to plot
508
- gui_plot = self._last_plot
474
+ if self._plot_inputs.get_input('ms'):
475
+ # Start spinner
476
+ self._update_plot_status(True)
477
+ self._update_plot_spinner(True)
478
+
479
+ # Clear last plot
480
+ self._reset_plot()
481
+ self.clear_selection()
482
+ if 'ps_selection' in self._gui_selection or 'ms_selection' in self._gui_selection:
483
+ self._do_gui_selection()
484
+
485
+ # Make plot if first plot or changed plot
486
+ style_inputs = self._raster_plot.get_plot_params()['style']
487
+ plot_inputs = self._plot_inputs.get_inputs()
488
+ if inputs_changed(plot_inputs, self._last_plot_inputs) or inputs_changed(style_inputs, self._last_style_inputs):
489
+ try:
490
+ # Check inputs from GUI then plot
491
+ self._plot_inputs.set_input('data_dims', self._ms_info['data_dims'])
492
+ self._plot_inputs.check_inputs()
493
+ gui_plot = self._do_gui_plot()
494
+ self._last_gui_plot = gui_plot # save plot for locate callback
495
+ self._logger.info("Plot update complete")
496
+
497
+ # Put plot with dmap for locate streams in gui panel
498
+ dmap = self._get_locate_dmap(self._locate_gui_points)
499
+ self._gui_panel[0][0][0].object = gui_plot * dmap
500
+ except (ValueError, TypeError, KeyError, RuntimeError) as e:
501
+ # Clear plot, inputs invalid
502
+ self._notify(str(e), 'error', 0)
509
503
 
510
- # Update plot inputs for gui
504
+ # Update plot inputs for gui tab
511
505
  self._set_plot_params(plot_inputs | style_inputs)
506
+ self._show_plot_inputs()
512
507
 
513
- # Save inputs to see if changed next time
508
+ # Save inputs to check if changed next time
514
509
  self._last_plot_inputs = plot_inputs.copy()
515
510
  self._last_style_inputs = style_inputs.copy()
516
511
 
517
- # Save plot for return value if plot update not requested
518
- self._last_plot = gui_plot
519
- self._first_gui_plot = False
520
-
521
512
  # Add plot inputs to GUI, change plot button to outline, and stop spinner
522
- self._show_plot_inputs()
523
513
  self._update_plot_status(False)
524
514
  self._update_plot_spinner(False)
525
515
 
526
- return gui_plot
527
516
  # pylint: enable=too-many-arguments, too-many-positional-arguments
528
517
 
518
+ def _locate_gui_points(self, x, y, data, bounds):
519
+ ''' Callback for locate streams '''
520
+ if self._gui_plot_data:
521
+ super()._locate_cursor(x, y, self._gui_plot_data, self._gui_panel[0])
522
+ super()._locate_points(data, self._gui_plot_data, self._gui_panel[0])
523
+ super()._locate_box(bounds, self._gui_plot_data, self._gui_panel[0])
524
+ return self._last_gui_plot
525
+
529
526
  def _do_gui_selection(self):
530
527
  ''' Apply selections selected in GUI '''
531
528
  if self._gui_selection['ps_selection']:
532
529
  self.select_ps(**self._gui_selection['ps_selection'])
533
530
  if self._gui_selection['ms_selection']:
534
- self.select_ms(**self._gui_selection['ms_selection'])
531
+ self.select_ms(**self._gui_selection['ms_selection'], drop=True)
535
532
 
536
533
  ###
537
534
  ### Create plot for DynamicMap
@@ -544,35 +541,22 @@ class MsRaster(MsPlot):
544
541
  # Make iter plot (possibly with subplots layout)
545
542
  self._do_iter_plot()
546
543
  subplots = self._plot_inputs.get_input('subplots')
547
- layout_plot, is_layout = super()._layout_plots(subplots)
548
- if is_layout:
544
+ layout_plot = super()._layout_plots(subplots)
545
+ if self._plot_inputs.is_layout():
549
546
  # Cannot show Layout in DynamicMap, show in new tab
550
547
  super().show()
551
- self._logger.info("Plot update complete")
552
548
  return self._last_plot
553
549
  # Overlay raster plot for DynamicMap
554
- self._logger.info("Plot update complete")
555
550
  return layout_plot
556
551
 
557
552
  # Make single Overlay raster plot for DynamicMap
558
- plot = self._do_plot(True)
559
- plot_params = self._raster_plot.get_plot_params()
553
+ gui_plot = self._do_plot(True)
560
554
 
561
555
  # Update color limits in gui with data range
556
+ plot_params = self._raster_plot.get_plot_params()
562
557
  self._update_color_range(plot_params)
563
558
 
564
- # Update colorbar labels and limits
565
- plot.QuadMesh.I = self._set_plot_colorbar(plot.QuadMesh.I, plot_params, "flagged")
566
- plot.QuadMesh.II = self._set_plot_colorbar(plot.QuadMesh.II, plot_params, "unflagged")
567
-
568
- self._logger.info("Plot update complete")
569
- return plot.opts(
570
- hv.opts.QuadMesh(
571
- tools=['hover', 'box_select'],
572
- selection_fill_alpha=0.2, # dim selected areas of plot
573
- nonselection_fill_alpha=1.0, # do not dim unselected areas of plot
574
- )
575
- )
559
+ return gui_plot
576
560
  except RuntimeError as e:
577
561
  error = f"Plot failed: {str(e)}"
578
562
  super()._notify(error, "error", 0)
@@ -583,7 +567,7 @@ class MsRaster(MsPlot):
583
567
  def _create_empty_plot(self):
584
568
  ''' Create empty Overlay plot for DynamicMap with colormap params and required tools enabled '''
585
569
  plot_params = self._raster_plot.get_plot_params()
586
- plot = hv.Overlay(
570
+ self._empty_plot = hv.Overlay(
587
571
  hv.QuadMesh([]).opts(
588
572
  colorbar=plot_params['style']['show_flagged_colorbar'],
589
573
  cmap=plot_params['style']['flagged_cmap'],
@@ -595,51 +579,14 @@ class MsRaster(MsPlot):
595
579
  responsive=True,
596
580
  )
597
581
  )
598
- return plot.opts(
599
- hv.opts.QuadMesh(
600
- tools=['hover', 'box_select'],
601
- selection_fill_alpha=0.5, # dim selected areas of plot
602
- nonselection_fill_alpha=1.0, # do not dim unselected areas of plot
603
- )
604
- )
605
-
606
- def _set_plot_colorbar(self, plot, plot_params, plot_type):
607
- ''' Update plot colorbar labels and limits
608
- plot_type is "unflagged" or "flagged"
609
- '''
610
- # Update colorbar labels if shown, else hide
611
- colorbar_key = plot_type + '_colorbar'
612
- if plot_params['plot'][colorbar_key]:
613
- c_label = plot_params['plot']['axis_labels']['c']['label']
614
- cbar_title = "Flagged " + c_label if plot_type == "flagged" else c_label
615
-
616
- plot = plot.opts(
617
- colorbar=True,
618
- backend_opts={
619
- "colorbar.title": cbar_title,
620
- }
621
- )
622
- else:
623
- plot = plot.opts(
624
- colorbar=False,
625
- )
626
-
627
- # Update plot color limits
628
- color_limits = plot_params['plot']['color_limits']
629
- if color_limits:
630
- plot = plot.opts(
631
- clim=color_limits,
632
- )
633
-
634
- return plot
635
582
 
636
583
  def _update_color_range(self, plot_params):
637
584
  ''' Set the start/end range on the colorbar to min/max of plot data '''
638
- if self._gui_layout and 'data' in plot_params and 'data_range' in plot_params['data']:
585
+ if self._gui_panel and 'data' in plot_params and 'data_range' in plot_params['data']:
639
586
  # Update range slider start and end to data min and max
640
587
  data_range = plot_params['data']['data_range']
641
588
  style_selectors = self._get_selector('style')
642
- range_slider = style_selectors[3][1]
589
+ range_slider = style_selectors[2][1]
643
590
  range_slider.start = data_range[0]
644
591
  range_slider.end = data_range[1]
645
592
 
@@ -648,13 +595,17 @@ class MsRaster(MsPlot):
648
595
  ###
649
596
  def _get_selector(self, name):
650
597
  ''' Return selector group for name, for setting options '''
651
- selectors = self._gui_layout[2][1]
652
- selectors_index = {'file': 0, 'style': 1, 'sel': 2, 'axes': 3, 'agg': 4, 'iter': 5, 'title': 6}
598
+ if not self._gui_panel:
599
+ return None
600
+
601
+ selectors = self._gui_panel[2][1]
653
602
  if name == "selectors":
654
603
  return selectors
604
+
605
+ selectors_index = {'file': 0, 'style': 1, 'sel': 2, 'axes': 3, 'agg': 4, 'iter': 5, 'title': 6}
655
606
  return selectors[selectors_index[name]]
656
607
 
657
- def _update_gui_axis_options(self):
608
+ def _update_gui_ms_options(self):
658
609
  ''' Set gui options from ms data '''
659
610
  if 'data_dims' in self._ms_info:
660
611
  data_dims = self._ms_info['data_dims']
@@ -721,7 +672,10 @@ class MsRaster(MsPlot):
721
672
  options.extend(value)
722
673
  else:
723
674
  options.append(value)
724
- selector.options = sorted(list(set(options)))
675
+ if options:
676
+ selector.options = sorted(list(set(options)))
677
+ else:
678
+ selector.options = ['']
725
679
 
726
680
  def _update_ms_selection_options(self, ms_selectors):
727
681
  ''' Set MeasurementSet gui options from ms data '''
@@ -737,27 +691,22 @@ class MsRaster(MsPlot):
737
691
  ###
738
692
  ### Callbacks for widgets which update other widgets
739
693
  ###
740
- def _set_filename(self, filename):
694
+ def _select_filename(self, filename):
741
695
  ''' Set filename in text box from file selector value (list) '''
742
- if filename and self._gui_layout:
696
+ if filename and self._gui_panel:
743
697
  file_selectors = self._get_selector('file')
744
698
 
745
699
  # Collapse FileSelector card
746
700
  file_selectors[1].collapsed = True
747
701
 
748
- # Change plot button color to indicate change unless ms path not set previously
702
+ # Set filename from last file in file selector
749
703
  filename_input = file_selectors[0][0]
750
- if filename_input.value:
751
- self._update_plot_status(True)
752
-
753
- # Set filename from last file in file selector (triggers _update_plot())
754
- #filename_input.width = len(filename[-1])
755
704
  filename_input.value = filename[-1]
756
705
 
757
706
  def _set_iter_values(self, iter_axis):
758
707
  ''' Set up player with values when iter_axis is selected '''
759
708
  iter_axis = None if iter_axis == 'None' else iter_axis
760
- if iter_axis and self._gui_layout:
709
+ if iter_axis and self._gui_panel:
761
710
  iter_values = self._ms_data.get_dimension_values(iter_axis)
762
711
  if iter_values:
763
712
 
@@ -798,26 +747,37 @@ class MsRaster(MsPlot):
798
747
 
799
748
  def _update_plot_spinner(self, plot_clicked):
800
749
  ''' Callback to start spinner when Plot button clicked. '''
801
- if self._gui_layout:
750
+ if self._gui_panel:
802
751
  # Start spinner
803
- spinner = self._gui_layout[2][2][1]
752
+ spinner = self._gui_panel[2][2][1]
804
753
  spinner.value = plot_clicked
805
754
 
806
755
  def _update_plot_status(self, plot_changed):
807
756
  ''' Change button color when plot inputs change. '''
808
- if self._gui_layout:
757
+ if self._gui_panel:
809
758
  # Set button color
810
- button = self._gui_layout[2][2][0]
759
+ button = self._gui_panel[2][2][0]
811
760
  button.button_style = 'solid' if plot_changed else 'outline'
812
761
 
813
762
  def _show_plot_inputs(self):
814
763
  ''' Show inputs for raster plot in column in GUI tab '''
815
- inputs_column = self._gui_layout[0][1]
764
+ inputs_column = self._gui_panel[0][1]
816
765
  super()._fill_inputs_column(inputs_column)
817
766
 
818
767
  ###
819
768
  ### Callbacks for widgets which update plot inputs
820
769
  ###
770
+ def _set_filename(self, filename):
771
+ ''' Set ms input from file text input '''
772
+ self._plot_inputs.set_input('ms', filename)
773
+ if self._set_ms(filename):
774
+ # New MS set, update gui input options
775
+ try:
776
+ self._update_gui_ms_options()
777
+ self._update_plot_status(True) # Change plot button to solid
778
+ except AttributeError:
779
+ self._update_plot_status(False)
780
+
821
781
  def _set_title(self, title):
822
782
  ''' Set title from gui text input '''
823
783
  self._plot_inputs.set_input('title', title)
@@ -15,6 +15,8 @@ def set_coordinates(ms_xdt):
15
15
 
16
16
  def set_datetime_coordinate(ms_xds):
17
17
  ''' Convert float time to datetime for plotting. '''
18
+ if 'time' not in ms_xds.coords:
19
+ return
18
20
  time_attrs = ms_xds.time.attrs
19
21
  try:
20
22
  ms_xds.coords['time'] = to_datetime(ms_xds.time, unit=time_attrs['units'], origin=time_attrs['format'])
@@ -47,6 +49,8 @@ def set_index_coordinates(ms_xds, coordinates):
47
49
 
48
50
  def _set_frequency_unit(ms_xdt):
49
51
  ''' Convert frequency to GHz. Note attrs (channel_width, reference_frequency) still have Hz units in dict '''
52
+ if 'frequency' not in ms_xdt.coords:
53
+ return
50
54
  if ms_xdt.frequency.attrs['units'] == "Hz":
51
55
  frequency_xda = ms_xdt.frequency / 1e9
52
56
  frequency_attrs = ms_xdt.frequency.attrs
@@ -51,23 +51,28 @@ class PsData:
51
51
  print(ps_summary)
52
52
  elif columns == "by_ms":
53
53
  for row in ps_summary.itertuples(index=False):
54
- print(f"MSv4 name: {row[0]}")
55
- print(f"intent: {row[1]}")
54
+ print(f"name: {row[0]}")
55
+ print(f"scan_intents: {row[1]}")
56
56
  shape = row[2]
57
57
  print(f"shape: {shape[0]} times, {shape[1]} baselines, {shape[2]} channels, {shape[3]} polarizations")
58
- print(f"polarization: {row[3]}")
59
- scans = [str(scan) for scan in row[4]]
58
+ print(f"execution_block_UID: {row[3]}")
59
+ print(f"polarization: {row[4]}")
60
+ scans = [str(scan) for scan in row[5]]
60
61
  print(f"scan_name: {scans}")
61
- print(f"spw_name: {row[5]}")
62
- fields = [str(field) for field in row[6]]
62
+ print(f"spw_name: {row[6]}")
63
+ print(f"spw_intents: {row[7]}")
64
+ fields = [str(field) for field in row[8]]
63
65
  print(f"field_name: {fields}")
64
- sources = [str(source) for source in row[7]]
66
+ sources = [str(source) for source in row[9]]
65
67
  print(f"source_name: {sources}")
66
- lines = [str(line) for line in row[8]]
68
+ lines = [str(line) for line in row[10]]
67
69
  print(f"line_name: {lines}")
68
- field_coords = row[9]
70
+ field_coords = row[11]
69
71
  print(f"field_coords: ({field_coords[0]}) {field_coords[1]} {field_coords[2]}")
70
- print(f"frequency range: {row[10]:e} - {row[11]:e}")
72
+ print(f"session_reference_UID: {row[12]}")
73
+ print(f"scheduling_block_UID: {row[13]}")
74
+ print(f"project_UID: {row[14]}")
75
+ print(f"frequency range: {row[15]:e} - {row[16]:e}")
71
76
  print("-----")
72
77
  else:
73
78
  if isinstance(columns, str):
@@ -41,12 +41,12 @@ def raster_data(ps_xdt, plot_inputs, logger):
41
41
  # Apply aggregator
42
42
  raster_xds = aggregate_data(raster_xds, plot_inputs, logger)
43
43
 
44
- logger.debug(f"Plotting visibility data with shape: {raster_xds[correlated_data].shape}")
44
+ logger.debug(f"Plotting visibility data with shape: {dict(raster_xds[correlated_data].sizes)}")
45
45
  return raster_xds
46
46
 
47
47
  def _select_ms(ps_xdt, logger, **selection):
48
- ''' Select ProcessingSet MeasurementSets for raster data '''
49
- return select_ms(ps_xdt, logger, indexers=None, method=None, tolerance=None, drop=False, **selection)
48
+ ''' Select ProcessingSet MeasurementSets for raster data. '''
49
+ return select_ms(ps_xdt, logger, indexers=None, method=None, tolerance=None, **selection)
50
50
 
51
51
  def _select_raster_dimensions(ps_xdt, plot_inputs, logger):
52
52
  ''' Select default dimensions if needed for raster data '''
@@ -64,6 +64,9 @@ def _select_raster_dimensions(ps_xdt, plot_inputs, logger):
64
64
  dim_selection[dim] = _get_first_dim_value(ps_xdt, dim, plot_inputs, logger)
65
65
  elif 'iter_axis' in plot_inputs and dim == plot_inputs['iter_axis']:
66
66
  dim_selection[dim] = selection[dim]
67
+ elif selection and dim in selection and isinstance(selection[dim], list):
68
+ dim_selection[dim] = selection[dim][0]
69
+
67
70
  if dim_selection:
68
71
  # Remove from selection for next plot
69
72
  if 'iter_axis' in plot_inputs and plot_inputs['iter_axis']:
@@ -192,7 +192,7 @@ def _select_time(ms_xdt, selection, method, tolerance, drop):
192
192
  else:
193
193
  # Select str or slice
194
194
  ms_xdt = ms_xdt.xr_ms.sel(indexers=None, method=time_method, tolerance=time_tolerance, drop=drop, **selection)
195
- if ms_xdt.time.size == 0:
195
+ if 'time' in ms_xdt.coords and ms_xdt.time.size == 0:
196
196
  return ms_xdt, False
197
197
  return ms_xdt, True
198
198
 
@@ -259,7 +259,7 @@ def _layout_point_location(text_list):
259
259
  location_row.append(location_col)
260
260
  location_col = pn.Column()
261
261
 
262
- static_text.margin = (0, 10) # default (5, 10)
262
+ static_text.margin = (0, 5) # default (5, 10)
263
263
  location_col.append(static_text)
264
264
 
265
265
  # Add last column
@@ -12,16 +12,11 @@ import holoviews as hv
12
12
  import numpy as np
13
13
  import panel as pn
14
14
  from selenium import webdriver
15
-
16
- try:
17
- from toolviper.utils.logger import get_logger, setup_logger
18
- _HAVE_TOOLVIPER = True
19
- except ImportError:
20
- _HAVE_TOOLVIPER = False
15
+ from toolviper.utils.logger import setup_logger
21
16
 
22
17
  from vidavis.data.measurement_set._ms_data import MsData
23
18
  from vidavis.plot.ms_plot._locate_points import cursor_changed, points_changed, box_changed, update_cursor_location, update_points_location, update_box_location
24
- from vidavis.toolbox import AppContext, get_logger
19
+ from vidavis.toolbox import AppContext
25
20
 
26
21
  class MsPlot:
27
22
 
@@ -33,11 +28,8 @@ class MsPlot:
33
28
  raise RuntimeError("Must provide ms/zarr path if gui not shown.")
34
29
 
35
30
  # Set logger: use toolviper logger else casalog else python logger
36
- if _HAVE_TOOLVIPER:
37
- self._logger = setup_logger(app_name, log_to_term=True, log_to_file=log_to_file, log_file=app_name.lower(), log_level=log_level.upper())
38
- else:
39
- self._logger = get_logger()
40
- self._logger.setLevel(log_level.upper())
31
+ self._logger = setup_logger(app_name, log_to_term=True, log_to_file=log_to_file, log_file=app_name.lower(), log_level=log_level.upper())
32
+ self._logger.propagate = False # avoid repeating unformatted log messages in console
41
33
 
42
34
  # Save parameters; ms set below
43
35
  self._show_gui = show_gui
@@ -63,21 +55,26 @@ class MsPlot:
63
55
  # Initialize gui panel for callbacks
64
56
  self._gui_plot_data = None
65
57
  self._gui_selection = {}
66
- self._gui_layout = None
67
- self._first_gui_plot = True
68
58
 
69
59
  # For _update_plot callback: check if inputs changed
70
60
  self._last_plot_inputs = None
71
61
  self._last_style_inputs = None
62
+ self._last_gui_plot = None # return value
72
63
 
73
64
  # For locate callback: check if points changed
74
65
  self._plot_axes = None
75
66
  self._last_cursor = None
76
67
  self._last_points = None
77
68
  self._last_box = None
78
-
79
- # Initialize non-gui show() panel for callbacks
80
- self._show_layout = None
69
+ self._locate_plot_options = {
70
+ 'tools': ['hover', 'box_select'],
71
+ 'selection_fill_alpha': 0.2, # dim selected areas of plot
72
+ 'nonselection_fill_alpha': 1.0, # do not dim unselected areas of plot
73
+ }
74
+
75
+ # Initialize panels for callbacks
76
+ self._gui_panel = None
77
+ self._show_panel = None
81
78
  self._plot_data = None
82
79
 
83
80
  # Set data (if ms)
@@ -144,34 +141,36 @@ class MsPlot:
144
141
  self._plots.clear()
145
142
  self._plot_params.clear()
146
143
  self._plot_axes = None
144
+ if self._gui_panel is not None:
145
+ self._gui_panel[0][2].clear() # locate points
146
+ self._gui_panel[0][3].clear() # locate box
147
147
 
148
- def unlink_plot_streams(self):
148
+ def unlink_plot_locate(self):
149
149
  ''' Disconnect streams when plot data is going to be replaced '''
150
- if self._show_layout and len(self._show_layout.objects) == 4:
150
+ if self._show_panel and len(self._show_panel.objects) == 4:
151
151
  # Remove dmap (streams with callback) from previous plot
152
- self._show_layout[0][0] = self._last_plot.opts(tools=['hover'])
152
+ self._show_panel[0][0] = self._last_plot.opts(tools=['hover'])
153
153
  # Remove locate widgets
154
- self._show_layout[0].pop(1) # cursor locate box
155
- self._show_layout.pop(3) # box locate tab
156
- self._show_layout.pop(2) # points locate tab
154
+ self._show_panel[0].pop(1) # cursor locate box
155
+ self._show_panel.pop(3) # box locate tab
156
+ self._show_panel.pop(2) # points locate tab
157
157
 
158
158
  def clear_selection(self):
159
159
  ''' Clear data selection and restore original ProcessingSet '''
160
160
  if self._ms_data:
161
161
  self._ms_data.clear_selection()
162
-
163
162
  self._plot_inputs.remove_input('selection')
164
163
 
165
164
  def show(self):
166
165
  '''
167
- Show interactive Bokeh plots in a browser. Plot tools include pan, zoom, hover, and save.
166
+ Show interactive Bokeh plots in a browser.
168
167
  '''
169
168
  if not self._plots:
170
169
  raise RuntimeError("No plots to show. Run plot() to create plot.")
171
170
 
172
171
  # Single plot or combine plots into layout using subplots (rows, columns)
173
172
  subplots = self._plot_inputs.get_input('subplots')
174
- layout_plot = self._layout_plots(subplots)
173
+ plot = self._layout_plots(subplots)
175
174
 
176
175
  # Add plot inputs column tab
177
176
  inputs_column = None
@@ -179,52 +178,39 @@ class MsPlot:
179
178
  inputs_column = pn.Column()
180
179
  self._fill_inputs_column(inputs_column)
181
180
 
182
- # Show plot and plot inputs in tabs
181
+ # Show plots and plot inputs in tabs
183
182
  if self._plot_inputs.is_layout():
184
- self._show_layout = pn.Tabs(('Plot', layout_plot))
183
+ self._show_panel = pn.Tabs(('Plot', plot))
185
184
  if inputs_column:
186
- self._show_layout.append(('Plot Inputs', inputs_column))
185
+ self._show_panel.append(('Plot Inputs', inputs_column))
187
186
  else:
188
- plot = layout_plot.opts(
189
- hv.opts.QuadMesh(
190
- tools=['hover', 'box_select'],
191
- selection_fill_alpha=0.2, # dim selected areas of plot
192
- nonselection_fill_alpha=1.0, # do not dim unselected areas of plot
193
- )
187
+ plot = plot.opts(
188
+ hv.opts.QuadMesh(**self._locate_plot_options),
189
+ hv.opts.Scatter(**self._locate_plot_options)
194
190
  )
195
191
  # Add DynamicMap for streams for single plot
196
- points = hv.Points([]).opts(
197
- size=5,
198
- fill_color='white'
199
- )
200
- dmap = hv.DynamicMap(
201
- self._locate,
202
- streams=[
203
- hv.streams.PointerXY(), # cursor location (x, y)
204
- hv.streams.PointDraw(source=points), # fixed points location (data)
205
- hv.streams.BoundsXY() # box location (bounds)
206
- ]
207
- )
192
+ dmap = self._get_locate_dmap(self._locate)
208
193
 
209
194
  # Create panel layout
210
- self._show_layout = pn.Tabs(
211
- ('Plot', pn.Column(
212
- plot * dmap * points,
213
- pn.WidgetBox(), # cursor info
195
+ self._show_panel = pn.Tabs(
196
+ ('Plot',
197
+ pn.Column(
198
+ plot * dmap,
199
+ pn.WidgetBox(), # cursor info
214
200
  )
215
201
  ),
216
202
  sizing_mode='stretch_width',
217
203
  )
218
204
  if inputs_column:
219
- self._show_layout.append(('Plot Inputs', inputs_column))
220
- self._show_layout.append(('Locate Selected Points', pn.Column()))
221
- self._show_layout.append(('Locate Selected Box', pn.Column()))
205
+ self._show_panel.append(('Plot Inputs', inputs_column))
206
+ self._show_panel.append(('Locate Selected Points', pn.Column()))
207
+ self._show_panel.append(('Locate Selected Box', pn.Column()))
222
208
 
223
209
  # return value for locate callback
224
210
  self._last_plot = plot
225
211
 
226
212
  # Show panel layout
227
- self._show_layout.show(title=self._app_name, threaded=True)
213
+ self._show_panel.show(title=self._app_name, threaded=True)
228
214
 
229
215
  def save(self, filename='ms_plot.png', fmt='auto', width=900, height=600):
230
216
  '''
@@ -245,10 +231,10 @@ class MsPlot:
245
231
  # Combine plots into layout using subplots (rows, columns) if not single plot.
246
232
  # Set fixed size for export.
247
233
  subplots = self._plot_inputs.get_input('subplots')
248
- layout_plot = self._layout_plots(subplots, (width, height))
234
+ plot = self._layout_plots(subplots, (width, height))
249
235
 
250
236
  iter_axis = self._plot_inputs.get_input('iter_axis')
251
- if not isinstance(layout_plot, hv.Layout) and iter_axis:
237
+ if not isinstance(plot, hv.Layout) and iter_axis:
252
238
  # Save iterated plots individually, with index appended to filename
253
239
  iter_range = self._plot_inputs.get_input('iter_range')
254
240
  plot_idx = 0 if iter_range is None else iter_range[0]
@@ -257,7 +243,7 @@ class MsPlot:
257
243
  self._save_plot(plot, exportname, fmt)
258
244
  plot_idx += 1
259
245
  else:
260
- self._save_plot(layout_plot, filename, fmt)
246
+ self._save_plot(plot, filename, fmt)
261
247
  self._logger.debug("Save elapsed time: %.2fs.", time.time() - start_time)
262
248
 
263
249
  def _layout_plots(self, subplots, fixed_size=None):
@@ -398,6 +384,22 @@ class MsPlot:
398
384
  str_pane.margin = (0, 10)
399
385
  inputs_tab_column.append(str_pane)
400
386
 
387
+ def _get_locate_dmap(self, callback):
388
+ ''' Return DynamicMap with streams callback to locate points '''
389
+ points = hv.Points([]).opts(
390
+ size=5,
391
+ fill_color='white'
392
+ )
393
+ dmap = hv.DynamicMap(
394
+ callback,
395
+ streams=[
396
+ hv.streams.PointerXY(), # cursor location (x, y)
397
+ hv.streams.PointDraw(source=points), # fixed points location (data)
398
+ hv.streams.BoundsXY() # box location (bounds)
399
+ ]
400
+ )
401
+ return dmap * points
402
+
401
403
  def _get_plot_axes(self):
402
404
  ''' Return x, y, vis axes '''
403
405
  if not self._plot_axes:
@@ -409,9 +411,9 @@ class MsPlot:
409
411
 
410
412
  def _locate(self, x, y, data, bounds):
411
413
  ''' Callback for all show plot streams '''
412
- self._locate_cursor(x, y, self._plot_data, self._show_layout)
413
- self._locate_points(data, self._plot_data, self._show_layout)
414
- self._locate_box(bounds, self._plot_data, self._show_layout)
414
+ self._locate_cursor(x, y, self._plot_data, self._show_panel)
415
+ self._locate_points(data, self._plot_data, self._show_panel)
416
+ self._locate_box(bounds, self._plot_data, self._show_panel)
415
417
  return self._last_plot
416
418
 
417
419
  def _locate_cursor(self, x, y, plot_data, tabs):
@@ -7,21 +7,23 @@ import panel as pn
7
7
  from vidavis.bokeh._palette import available_palettes
8
8
  from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, AGGREGATOR_OPTIONS, PS_SELECTION_OPTIONS, MS_SELECTION_OPTIONS, DEFAULT_UNFLAGGED_CMAP, DEFAULT_FLAGGED_CMAP
9
9
 
10
- def file_selector(description, start_dir, callback):
10
+ def file_selector(callbacks, ms):
11
11
  ''' Return a layout for file selection with input description and start directory.
12
12
  Includes a TextInput and a FileSelector, with a callback to set TextInput from FileSelector.
13
13
  '''
14
- filename = pn.widgets.TextInput(
15
- description=description,
14
+ input_filename = pn.widgets.TextInput(
15
+ description='Path to MeasurementSet (ms or zarr)',
16
16
  name="Filename",
17
17
  placeholder='Enter filename or use file browser below',
18
+ value=ms,
18
19
  sizing_mode='stretch_width',
19
20
  )
21
+ set_file = pn.bind(callbacks['set_filename'], input_filename)
20
22
 
21
23
  file_select = pn.widgets.FileSelector(
22
- start_dir,
24
+ '~',
23
25
  )
24
- select_file = pn.bind(callback, file_select)
26
+ select_file = pn.bind(callbacks['select_filename'], file_select)
25
27
 
26
28
  fs_card = pn.Card(
27
29
  file_select,
@@ -33,8 +35,9 @@ def file_selector(description, start_dir, callback):
33
35
 
34
36
  return pn.Column(
35
37
  pn.Row( # [0]
36
- filename, # [0]
37
- select_file # [1]
38
+ input_filename, # [0]
39
+ set_file, # [1]
40
+ select_file # [2]
38
41
  ),
39
42
  fs_card, # [1]
40
43
  width_policy='min',
@@ -90,18 +93,22 @@ def style_selector(style_callback, color_range_callback):
90
93
  pn.Row( # [1]
91
94
  colorbar_checkbox, # [0]
92
95
  flagged_colorbar_checkbox, # [1]
96
+ select_style, # [2]
93
97
  ),
94
- select_style, # [2]
95
- pn.Row( # [3]
98
+ pn.Row( # [2]
96
99
  color_mode_selector, # [0]
97
100
  color_range_slider, # [1]
101
+ select_color_range, # [2]
98
102
  ),
99
- select_color_range, # [4]
100
103
  width_policy='min',
101
104
  )
102
105
 
103
- def axis_selector(x_axis, y_axis, axis_options, include_vis, callback):
106
+ def axis_selector(plot_info, include_vis, callback):
104
107
  ''' Return layout of selectors for x-axis, y-axis, and vis-axis '''
108
+ axis_options = plot_info['data_dims'] if 'data_dims' in plot_info else []
109
+ x_axis = plot_info['x_axis'] if 'x_axis' in plot_info else ''
110
+ y_axis = plot_info['y_axis'] if 'y_axis' in plot_info else ''
111
+
105
112
  x_options = axis_options if axis_options else [x_axis]
106
113
  x_selector = pn.widgets.Select(
107
114
  name="X Axis",
@@ -2,7 +2,7 @@
2
2
 
3
3
  def inputs_changed(plot_inputs, last_plot_inputs):
4
4
  ''' Check if inputs changed and need new plot '''
5
- if not last_plot_inputs:
5
+ if plot_inputs and not last_plot_inputs:
6
6
  return True
7
7
 
8
8
  for key, val in plot_inputs.items():
@@ -2,8 +2,6 @@
2
2
  Class to create a raster plot of visibility/spectrum data using plot parameters.
3
3
  '''
4
4
 
5
- import holoviews as hv
6
-
7
5
  # hvPlot extensions used to plot xarray DataArray and pandas DataFrame
8
6
  # pylint: disable=unused-import
9
7
  import hvplot.xarray
@@ -120,10 +118,8 @@ class RasterPlot:
120
118
  if is_gui: # update data range for colorbar
121
119
  self._plot_params['data']['data_range'] = (xda.min().values.item(), xda.max().values.item())
122
120
 
123
- # Make Overlay plot with hover tools
124
- return (flagged_plot * unflagged_plot).opts(
125
- hv.opts.QuadMesh(tools=['hover'])
126
- )
121
+ # Make Overlay plot
122
+ return flagged_plot * unflagged_plot
127
123
 
128
124
  def _get_plot_title(self, data, plot_inputs, ms_name):
129
125
  ''' Form string containing ms name and selected values using data (xArray Dataset) '''
@@ -2,28 +2,31 @@
2
2
  Create interactive GUI for ms raster plotting
3
3
  '''
4
4
 
5
- import holoviews as hv
6
5
  import panel as pn
7
6
  from vidavis.plot.ms_plot._ms_plot_selectors import (file_selector, title_selector, style_selector,
8
7
  axis_selector, aggregation_selector, iteration_selector, selection_selector, plot_starter)
9
8
 
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. '''
9
+ def create_raster_gui(callbacks, plot_info, empty_plot):
10
+ ''' Use Holoviz Panel to create a dashboard for plot inputs and raster plot display.
11
+ ms (str): path to MS, if set
12
+ plot_info (dict): with keys 'ms', 'data_dims', 'x_axis', 'y_axis'
13
+ empty_plot (hv.Overlay): QuadMesh overlay plot with no data
14
+ '''
12
15
  # Accordion of widgets for plot inputs
13
- selectors = get_plot_input_selectors(callbacks, data_dims, x_axis, y_axis)
16
+ selectors = get_plot_input_selectors(callbacks, plot_info)
14
17
 
15
18
  # Plot button and spinner while plotting
16
- init_plot = plot_starter(callbacks['plot_updating'])
19
+ init_plot = plot_starter(callbacks['update_plot'])
17
20
 
18
21
  # Dynamic map for plot, with callback when inputs change or location needed
19
- dmap, points = get_plot_dmap(callbacks, selectors, init_plot)
22
+ #dmap, points = get_plot_dmap(callbacks, selectors, init_plot)
20
23
 
21
24
  return pn.Row(
22
25
  pn.Tabs( # Row [0]
23
26
  ('Plot', # Tabs[0]
24
27
  pn.Column(
25
- dmap * points, # [0] plot with hv.Points overlay for point_draw
26
- pn.WidgetBox(), # [1] cursor location
28
+ pn.pane.HoloViews(empty_plot), # [0] plot
29
+ pn.WidgetBox(), # [1] cursor location
27
30
  )
28
31
  ),
29
32
  ('Plot Inputs', pn.Column()), # Tabs[1]
@@ -43,21 +46,22 @@ def create_raster_gui(callbacks, data_dims, x_axis, y_axis):
43
46
  sizing_mode='stretch_height',
44
47
  )
45
48
 
46
- def get_plot_input_selectors(callbacks, data_dims, x_axis, y_axis):
49
+ def get_plot_input_selectors(callbacks, plot_info):
47
50
  ''' Create accordion of widgets for plot inputs selection '''
48
51
  # Select MS
49
- file_selectors = file_selector('Path to MeasurementSet (ms or zarr) for plot', '~' , callbacks['filename'])
52
+ file_selectors = file_selector(callbacks, plot_info['ms'])
50
53
 
51
54
  # Select style - colormaps, colorbar, color limits
52
55
  style_selectors = style_selector(callbacks['style'], callbacks['color'])
53
56
 
54
57
  # Select x, y, and vis axis
55
- axis_selectors = axis_selector(x_axis, y_axis, data_dims, True, callbacks['axes'])
58
+ axis_selectors = axis_selector(plot_info, True, callbacks['axes'])
56
59
 
57
60
  # Select from ProcessingSet and MeasurementSet
58
61
  selection_selectors = selection_selector(callbacks['select_ps'], callbacks['select_ms'])
59
62
 
60
63
  # Generic axis options, updated when ms is set
64
+ data_dims = plot_info['data_dims'] if 'data_dims' in plot_info else None
61
65
  axis_options = data_dims if data_dims else []
62
66
 
63
67
  # Select aggregator and axes to aggregate
@@ -81,25 +85,3 @@ def get_plot_input_selectors(callbacks, data_dims, x_axis, y_axis):
81
85
  )
82
86
  selectors.toggle = True
83
87
  return selectors
84
-
85
- def get_plot_dmap(callbacks, selectors, init_plot):
86
- ''' Dynamic map for updating plot from callback function '''
87
- # Connect plot to filename and plot button; add streams for cursor position, drawn points, and selected box
88
- # 'update_plot' callback must have parameters (ms, do_plot, x, y, data, bounds)
89
- points = hv.Points([]).opts(
90
- size=5,
91
- fill_color='white'
92
- )
93
- dmap = hv.DynamicMap(
94
- pn.bind(
95
- callbacks['update_plot'],
96
- ms=selectors[0][0][0],
97
- do_plot=init_plot[0],
98
- ),
99
- streams=[
100
- hv.streams.PointerXY(), # cursor location (x, y)
101
- hv.streams.PointDraw(source=points), # fixed cursor location (data)
102
- hv.streams.BoundsXY() # box location (bounds)
103
- ]
104
- )
105
- return dmap, points
@@ -10,7 +10,7 @@ class RasterPlotInputs:
10
10
  '''
11
11
 
12
12
  def __init__(self):
13
- self._plot_inputs = {}
13
+ self._plot_inputs = {'selection': {}}
14
14
 
15
15
  def get_inputs(self):
16
16
  ''' Getter for stored plot inputs '''
@@ -31,8 +31,6 @@ class RasterPlotInputs:
31
31
 
32
32
  def set_selection(self, selection):
33
33
  ''' Add selection dict to existing selection in plot inputs '''
34
- if 'selection' not in self._plot_inputs:
35
- self._plot_inputs['selection'] = {}
36
34
  self._plot_inputs['selection'] |= selection
37
35
  if 'data_group_name' in selection:
38
36
  self._plot_inputs['data_group'] = selection['data_group_name']
@@ -41,9 +39,7 @@ class RasterPlotInputs:
41
39
  ''' Return value for selection key '''
42
40
  try:
43
41
  return self.get_input('selection')[key]
44
- except (TypeError, KeyError):
45
- # TypeError: get_input returned None for 'selection'
46
- # KeyError: key not in selection
42
+ except KeyError:
47
43
  return None
48
44
 
49
45
  def set_inputs(self, plot_inputs):
@@ -54,10 +50,13 @@ class RasterPlotInputs:
54
50
 
55
51
  def remove_input(self, name):
56
52
  ''' Remove plot input with name, if it exists '''
57
- try:
58
- del self._plot_inputs[name]
59
- except KeyError:
60
- pass
53
+ if name == 'selection':
54
+ self._plot_inputs['selection'] = {}
55
+ else:
56
+ try:
57
+ del self._plot_inputs[name]
58
+ except KeyError:
59
+ pass
61
60
 
62
61
  def check_inputs(self):
63
62
  ''' Check input values are valid, adjust for data dims '''
File without changes
File without changes