vidavis 0.0.14__py3-none-any.whl → 0.1.0__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.
- vidavis/__version__.py +1 -1
- vidavis/apps/_ms_raster.py +111 -151
- vidavis/data/measurement_set/processing_set/_ps_coords.py +4 -0
- vidavis/data/measurement_set/processing_set/_ps_data.py +15 -10
- vidavis/data/measurement_set/processing_set/_ps_raster_data.py +6 -3
- vidavis/data/measurement_set/processing_set/_ps_select.py +1 -1
- vidavis/plot/ms_plot/_locate_points.py +1 -1
- vidavis/plot/ms_plot/_ms_plot.py +63 -61
- vidavis/plot/ms_plot/_ms_plot_selectors.py +18 -11
- vidavis/plot/ms_plot/_plot_inputs.py +1 -1
- vidavis/plot/ms_plot/_raster_plot.py +2 -6
- vidavis/plot/ms_plot/_raster_plot_gui.py +15 -33
- vidavis/plot/ms_plot/_raster_plot_inputs.py +9 -10
- {vidavis-0.0.14.dist-info → vidavis-0.1.0.dist-info}/METADATA +3 -2
- {vidavis-0.0.14.dist-info → vidavis-0.1.0.dist-info}/RECORD +17 -17
- {vidavis-0.0.14.dist-info → vidavis-0.1.0.dist-info}/WHEEL +0 -0
- {vidavis-0.0.14.dist-info → vidavis-0.1.0.dist-info}/licenses/LICENSE +0 -0
vidavis/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.0
|
|
1
|
+
__version__ = '0.1.0'
|
vidavis/apps/_ms_raster.py
CHANGED
|
@@ -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
|
-
#
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
'
|
|
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
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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,
|
|
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
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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[
|
|
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
|
-
|
|
652
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
694
|
+
def _select_filename(self, filename):
|
|
741
695
|
''' Set filename in text box from file selector value (list) '''
|
|
742
|
-
if filename and self.
|
|
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
|
-
#
|
|
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.
|
|
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.
|
|
750
|
+
if self._gui_panel:
|
|
802
751
|
# Start spinner
|
|
803
|
-
spinner = self.
|
|
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.
|
|
757
|
+
if self._gui_panel:
|
|
809
758
|
# Set button color
|
|
810
|
-
button = self.
|
|
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.
|
|
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"
|
|
55
|
-
print(f"
|
|
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"
|
|
59
|
-
|
|
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[
|
|
62
|
-
|
|
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[
|
|
66
|
+
sources = [str(source) for source in row[9]]
|
|
65
67
|
print(f"source_name: {sources}")
|
|
66
|
-
lines = [str(line) for line in row[
|
|
68
|
+
lines = [str(line) for line in row[10]]
|
|
67
69
|
print(f"line_name: {lines}")
|
|
68
|
-
field_coords = row[
|
|
70
|
+
field_coords = row[11]
|
|
69
71
|
print(f"field_coords: ({field_coords[0]}) {field_coords[1]} {field_coords[2]}")
|
|
70
|
-
print(f"
|
|
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].
|
|
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,
|
|
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,
|
|
262
|
+
static_text.margin = (0, 5) # default (5, 10)
|
|
263
263
|
location_col.append(static_text)
|
|
264
264
|
|
|
265
265
|
# Add last column
|
vidavis/plot/ms_plot/_ms_plot.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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
|
|
148
|
+
def unlink_plot_locate(self):
|
|
149
149
|
''' Disconnect streams when plot data is going to be replaced '''
|
|
150
|
-
if self.
|
|
150
|
+
if self._show_panel and len(self._show_panel.objects) == 4:
|
|
151
151
|
# Remove dmap (streams with callback) from previous plot
|
|
152
|
-
self.
|
|
152
|
+
self._show_panel[0][0] = self._last_plot.opts(tools=['hover'])
|
|
153
153
|
# Remove locate widgets
|
|
154
|
-
self.
|
|
155
|
-
self.
|
|
156
|
-
self.
|
|
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.
|
|
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
|
-
|
|
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
|
|
181
|
+
# Show plots and plot inputs in tabs
|
|
183
182
|
if self._plot_inputs.is_layout():
|
|
184
|
-
self.
|
|
183
|
+
self._show_panel = pn.Tabs(('Plot', plot))
|
|
185
184
|
if inputs_column:
|
|
186
|
-
self.
|
|
185
|
+
self._show_panel.append(('Plot Inputs', inputs_column))
|
|
187
186
|
else:
|
|
188
|
-
plot =
|
|
189
|
-
hv.opts.QuadMesh(
|
|
190
|
-
|
|
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
|
-
|
|
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.
|
|
211
|
-
('Plot',
|
|
212
|
-
|
|
213
|
-
|
|
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.
|
|
220
|
-
self.
|
|
221
|
-
self.
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
413
|
-
self._locate_points(data, self._plot_data, self.
|
|
414
|
-
self._locate_box(bounds, self._plot_data, self.
|
|
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(
|
|
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
|
-
|
|
15
|
-
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
|
-
|
|
24
|
+
'~',
|
|
23
25
|
)
|
|
24
|
-
select_file = pn.bind(
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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(
|
|
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,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
|
|
124
|
-
return
|
|
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,
|
|
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,
|
|
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['
|
|
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
|
-
|
|
26
|
-
pn.WidgetBox(),
|
|
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,
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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 '''
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vidavis
|
|
3
|
-
Version: 0.0
|
|
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
7
|
Requires-Python: >=3.11, <3.14
|
|
8
|
-
Requires-Dist: bokeh
|
|
8
|
+
Requires-Dist: bokeh<3.9,>=3.7
|
|
9
9
|
Requires-Dist: graphviper
|
|
10
10
|
Requires-Dist: hvplot
|
|
11
11
|
Requires-Dist: matplotlib
|
|
12
|
+
Requires-Dist: panel==1.8.2
|
|
12
13
|
Requires-Dist: selenium
|
|
13
14
|
Description-Content-Type: text/x-rst
|
|
14
15
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
vidavis/LICENSE.rst,sha256=qzGpkvhDzf_MgF1PIn6rCmYPrcEhkfrBUchosLJj-U4,26371
|
|
2
2
|
vidavis/__init__.py,sha256=FVM92yTXUplR7tVHiGao0Y3Hd80pRTrwVIYDLjzICws,1709
|
|
3
3
|
vidavis/apps/__init__.py,sha256=ZQ5v1VFtjn3ztmuOHLOk5WbC1uLNIgL9rbHQ4v0zJwY,87
|
|
4
|
-
vidavis/apps/_ms_raster.py,sha256=
|
|
4
|
+
vidavis/apps/_ms_raster.py,sha256=EvN5T4baFXvrmKD29JEX24o51j2x83UizuJdXSqNXPY,40971
|
|
5
5
|
vidavis/bokeh/__init__.py,sha256=gdPPxBCe0enCSjvPFxkgMlhpVnAMFXKcw9GN28tVdXM,95
|
|
6
6
|
vidavis/bokeh/_palette.py,sha256=gzfJHuUgqxd8hJpZe-gQPFTCPq9f5I8uLEkHAK5FNDM,2480
|
|
7
7
|
vidavis/data/__init__.py,sha256=-RDRe0PYK6vPlhdRV2Dy1vGbnDGoXWDATmfxaR-gXcE,48
|
|
@@ -9,32 +9,32 @@ vidavis/data/measurement_set/__init__.py,sha256=7pnjaRYgQ88pydsMw1Z9sUiuO2udunRV
|
|
|
9
9
|
vidavis/data/measurement_set/_ms_data.py,sha256=7ATsPbGFAv8WtWk_PRxlfoLdqapd6eMvnpYTgtdEDjE,7726
|
|
10
10
|
vidavis/data/measurement_set/processing_set/__init__.py,sha256=TVQe5Nl4APCB2E1T1giAkOvSxg7OBNcOm9-BeZelY2Q,95
|
|
11
11
|
vidavis/data/measurement_set/processing_set/_ps_concat.py,sha256=uTuxE7krTu6SS7lwV9ITMsAxJ4BJhKOU9dLlQ9L1oNY,3513
|
|
12
|
-
vidavis/data/measurement_set/processing_set/_ps_coords.py,sha256=
|
|
13
|
-
vidavis/data/measurement_set/processing_set/_ps_data.py,sha256=
|
|
12
|
+
vidavis/data/measurement_set/processing_set/_ps_coords.py,sha256=SDp-0ebd94QjO_jPv00weiYmp-OD1XqsCzWvNc0D-94,3747
|
|
13
|
+
vidavis/data/measurement_set/processing_set/_ps_data.py,sha256=aO_J8ETi-Lqu4eNNKsKUHzvvnyRhww4hECUjbBS9Z9U,11626
|
|
14
14
|
vidavis/data/measurement_set/processing_set/_ps_io.py,sha256=VeNi-s1hozgCAGAGHs4NUXtlVFwUh-mkqrY9iYWOfW4,1717
|
|
15
|
-
vidavis/data/measurement_set/processing_set/_ps_raster_data.py,sha256=
|
|
16
|
-
vidavis/data/measurement_set/processing_set/_ps_select.py,sha256=
|
|
15
|
+
vidavis/data/measurement_set/processing_set/_ps_raster_data.py,sha256=m7d0qe5bS-XjGD_1mrpRn9dTobwG8WDidCmAaLP2TWQ,7378
|
|
16
|
+
vidavis/data/measurement_set/processing_set/_ps_select.py,sha256=AtEsLy3bSHEyFUFKM-OO6_-YwUdWOWcGkXkEgFxMaEE,10859
|
|
17
17
|
vidavis/data/measurement_set/processing_set/_ps_stats.py,sha256=4uLImKCANXLUM8jO8nFbBFMwfusS-JjsZ0ttbDFPrSE,8166
|
|
18
18
|
vidavis/data/measurement_set/processing_set/_xds_data.py,sha256=qLO2VkLINkSAQ7CGRFmpWQYpHrP4XoJJkwA4pp9DO8M,5253
|
|
19
19
|
vidavis/plot/__init__.py,sha256=thxe5vAGdpEiqoKPHLJoWUqKMVrUVx0ajpsGf5pVP98,95
|
|
20
20
|
vidavis/plot/ms_plot/__init__.py,sha256=wY0_7gY9M6K1D6tKQsr89L_uSs3seJlD-uicx7dx5Mo,74
|
|
21
21
|
vidavis/plot/ms_plot/_check_raster_inputs.py,sha256=a7u5wlDKTxWYW36-Xp3xd4c756SbYURdFkGHbUaX440,4786
|
|
22
|
-
vidavis/plot/ms_plot/_locate_points.py,sha256=
|
|
23
|
-
vidavis/plot/ms_plot/_ms_plot.py,sha256=
|
|
22
|
+
vidavis/plot/ms_plot/_locate_points.py,sha256=mWXGeUoXPLFdyxR2hCCAZkNtEhtPmZ39mm1-C92TPmE,10898
|
|
23
|
+
vidavis/plot/ms_plot/_ms_plot.py,sha256=D_VneQLzfHURyeB_DzyKyQHhQhpaLyNxrRTRJkWjjjo,18864
|
|
24
24
|
vidavis/plot/ms_plot/_ms_plot_constants.py,sha256=cX_TQhKJ3hJzPuRYmuRJxue1sjq82yl_ZN2_w6TshmI,930
|
|
25
|
-
vidavis/plot/ms_plot/_ms_plot_selectors.py,sha256=
|
|
26
|
-
vidavis/plot/ms_plot/_plot_inputs.py,sha256=
|
|
27
|
-
vidavis/plot/ms_plot/_raster_plot.py,sha256=
|
|
28
|
-
vidavis/plot/ms_plot/_raster_plot_gui.py,sha256=
|
|
29
|
-
vidavis/plot/ms_plot/_raster_plot_inputs.py,sha256=
|
|
25
|
+
vidavis/plot/ms_plot/_ms_plot_selectors.py,sha256=jBjthBjfXDf6W19HG8j8Pzxxzmnte5apeVDjsMy-dDg,11146
|
|
26
|
+
vidavis/plot/ms_plot/_plot_inputs.py,sha256=GeErBB3pYz6ecJiMTGQMNXkPeMLbWbYGmqL5dr8A46Q,687
|
|
27
|
+
vidavis/plot/ms_plot/_raster_plot.py,sha256=Uv0D-P7PyvcMBqFHqmbNxnDkUMekVok511WIvaA-JB0,10894
|
|
28
|
+
vidavis/plot/ms_plot/_raster_plot_gui.py,sha256=3oEfZqOI3sgxfCQx9t3i679TffV2466-KBUrJ-m8uik,3546
|
|
29
|
+
vidavis/plot/ms_plot/_raster_plot_inputs.py,sha256=kxR6-1Qn4IyQ4FjgpVZ9HEZ7EnPHxJZWomb0n_zaRwo,4071
|
|
30
30
|
vidavis/plot/ms_plot/_time_ticks.py,sha256=j-DcPh7RfGE8iX2bPjLQDQPIbiAbmjiEWQnKmdMWA3I,1773
|
|
31
31
|
vidavis/plot/ms_plot/_xds_plot_axes.py,sha256=EeWvAbiKV33nEWdI8V3M0uwLTnycq4bFYBOyVWkxCu0,4429
|
|
32
32
|
vidavis/toolbox/__init__.py,sha256=jqFa-eziVz_frNnXxwjJFK36qNpz1H38s-VlpBcq-R8,1402
|
|
33
33
|
vidavis/toolbox/_app_context.py,sha256=H7gtF8RrAH46FqDcMobv3KM1Osbnapgu6aTG-m3VCWA,3049
|
|
34
34
|
vidavis/toolbox/_logging.py,sha256=OEisrd8FM8VTNBMc7neLh9ekelf29ZILYB5pScebly0,2739
|
|
35
35
|
vidavis/toolbox/_static.py,sha256=HJLMtClppgOJXWAtV6Umn5EqN80u0oZiIouQ1JsB9PM,2346
|
|
36
|
-
vidavis/__version__.py,sha256=
|
|
37
|
-
vidavis-0.0.
|
|
38
|
-
vidavis-0.0.
|
|
39
|
-
vidavis-0.0.
|
|
40
|
-
vidavis-0.0.
|
|
36
|
+
vidavis/__version__.py,sha256=L6zbQIZKsAP-Knhm6fBcQFPoVdIDuejxze60qX23jiw,21
|
|
37
|
+
vidavis-0.1.0.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
|
|
38
|
+
vidavis-0.1.0.dist-info/METADATA,sha256=NT3za4lzXu6aMQgcMg0gRxeSAj_SLOUFQUHcjgWrlnI,2268
|
|
39
|
+
vidavis-0.1.0.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
|
|
40
|
+
vidavis-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|