vidavis 0.0.12__py3-none-any.whl → 0.0.13__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 +124 -112
- vidavis/plot/ms_plot/{_raster_plot_inputs.py → _check_raster_inputs.py} +1 -1
- vidavis/plot/ms_plot/_locate_points.py +38 -6
- vidavis/plot/ms_plot/_ms_plot.py +20 -9
- vidavis/plot/ms_plot/_plot_inputs.py +19 -0
- vidavis/plot/ms_plot/_raster_plot_gui.py +50 -41
- {vidavis-0.0.12.dist-info → vidavis-0.0.13.dist-info}/METADATA +1 -1
- {vidavis-0.0.12.dist-info → vidavis-0.0.13.dist-info}/RECORD +11 -10
- {vidavis-0.0.12.dist-info → vidavis-0.0.13.dist-info}/WHEEL +0 -0
- {vidavis-0.0.12.dist-info → vidavis-0.0.13.dist-info}/licenses/LICENSE +0 -0
vidavis/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.0.
|
|
1
|
+
__version__ = '0.0.13'
|
vidavis/apps/_ms_raster.py
CHANGED
|
@@ -12,13 +12,14 @@ import panel as pn
|
|
|
12
12
|
|
|
13
13
|
from vidavis.bokeh._palette import available_palettes
|
|
14
14
|
from vidavis.data.measurement_set.processing_set._ps_coords import set_index_coordinates
|
|
15
|
-
from vidavis.plot.ms_plot.
|
|
16
|
-
from vidavis.plot.ms_plot._locate_points import locate_point, locate_box
|
|
15
|
+
from vidavis.plot.ms_plot._check_raster_inputs import check_inputs
|
|
16
|
+
from vidavis.plot.ms_plot._locate_points import cursor_changed, points_changed, box_changed, locate_point, locate_box
|
|
17
17
|
from vidavis.plot.ms_plot._ms_plot import MsPlot
|
|
18
18
|
from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, SPECTRUM_AXIS_OPTIONS, PS_SELECTION_OPTIONS, MS_SELECTION_OPTIONS
|
|
19
|
+
from vidavis.plot.ms_plot._plot_inputs import inputs_changed
|
|
19
20
|
from vidavis.plot.ms_plot._raster_plot_gui import create_raster_gui
|
|
20
|
-
from vidavis.plot.ms_plot._raster_plot_inputs import check_inputs
|
|
21
21
|
from vidavis.plot.ms_plot._raster_plot import RasterPlot
|
|
22
|
+
from vidavis.plot.ms_plot._time_ticks import get_time_formatter
|
|
22
23
|
|
|
23
24
|
class MsRaster(MsPlot):
|
|
24
25
|
'''
|
|
@@ -44,21 +45,13 @@ class MsRaster(MsPlot):
|
|
|
44
45
|
def __init__(self, ms=None, log_level="info", log_to_file=True, show_gui=False):
|
|
45
46
|
super().__init__(ms, log_level, log_to_file, show_gui, "MsRaster")
|
|
46
47
|
self._raster_plot = RasterPlot()
|
|
47
|
-
self._plot_data = None
|
|
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._last_plot_inputs = None
|
|
56
|
-
self._last_style_inputs = None
|
|
57
|
-
self._last_cursor = None
|
|
58
|
-
self._last_box = None
|
|
59
|
-
|
|
60
|
-
# Return plot for gui DynamicMap:
|
|
61
|
-
# Empty plot when ms not set or plot fails
|
|
54
|
+
# Return plot for gui DynamicMap: empty plot when ms not set or plot fails
|
|
62
55
|
self._empty_plot = self._create_empty_plot()
|
|
63
56
|
|
|
64
57
|
# Set default style and plot inputs to use when launching gui
|
|
@@ -66,7 +59,7 @@ class MsRaster(MsPlot):
|
|
|
66
59
|
self.plot()
|
|
67
60
|
self._launch_gui()
|
|
68
61
|
|
|
69
|
-
# Set filename TextInput to input ms, which triggers default plot
|
|
62
|
+
# Set filename TextInput widget to input ms, which triggers default plot
|
|
70
63
|
if 'ms' in self._ms_info and self._ms_info['ms']:
|
|
71
64
|
self._set_filename([self._ms_info['ms']]) # function expects list from FileBrowser widget
|
|
72
65
|
|
|
@@ -97,10 +90,8 @@ class MsRaster(MsPlot):
|
|
|
97
90
|
string_exact_match (bool): whether to require exact matches for string and string list columns (default True) or partial matches (False).
|
|
98
91
|
query (str): a Pandas query string to apply additional filtering.
|
|
99
92
|
**kwargs (dict): keyword arguments representing summary column names and values.
|
|
100
|
-
See
|
|
101
|
-
|
|
102
|
-
'field_name', 'source_name', 'field_coords', 'start_frequency', 'end_frequency'.
|
|
103
|
-
The selection is applied to the data within the MeasurementSets where applicable (polarization, scan_name, field_name)
|
|
93
|
+
See data_groups() and summary(data_group) and for selection keyword options. Use keyword 'data_group_name' for data group selection.
|
|
94
|
+
The selection is also applied to the data within the MeasurementSets where keyword is a coordinate (polarization, scan_name, field_name) and
|
|
104
95
|
string_exact_match=True, else the entire MS is selected.
|
|
105
96
|
Selections are cumulative until clear_selection() is called.
|
|
106
97
|
Raises exception with message if selection fails.
|
|
@@ -156,8 +147,8 @@ class MsRaster(MsPlot):
|
|
|
156
147
|
iter_axis=None, iter_range=None, subplots=None, color_mode=None, color_range=None, title=None, clear_plots=True):
|
|
157
148
|
'''
|
|
158
149
|
Create a raster plot of vis_axis data.
|
|
159
|
-
Plot axes include data dimensions (time, baseline/
|
|
160
|
-
The first spectral window
|
|
150
|
+
Plot axes include data dimensions (time, baseline/antenna_name, frequency, polarization).
|
|
151
|
+
The first spectral window by time and dimensions not set as plot axes will be automatically selected by first value if no user selection has been made, unless aggregated.
|
|
161
152
|
|
|
162
153
|
Args:
|
|
163
154
|
x_axis (str): Plot x-axis. Default 'baseline' ('antenna_name' for spectrum data).
|
|
@@ -253,14 +244,20 @@ class MsRaster(MsPlot):
|
|
|
253
244
|
for key, val in plot_inputs.items():
|
|
254
245
|
self._plot_inputs[key] = val
|
|
255
246
|
|
|
256
|
-
def _do_plot(self, plot_inputs):
|
|
247
|
+
def _do_plot(self, plot_inputs, is_gui_plot=False):
|
|
257
248
|
''' Create plot using plot inputs '''
|
|
258
249
|
if not self._plot_init:
|
|
259
250
|
self._init_plot(plot_inputs)
|
|
260
251
|
|
|
261
252
|
# Select vis_axis data to plot and update selection; returns xarray Dataset
|
|
262
253
|
raster_data = self._data.get_raster_data(plot_inputs)
|
|
263
|
-
|
|
254
|
+
|
|
255
|
+
# Save plot data for plot location callbacks unless layout (location not supported)
|
|
256
|
+
if not self._is_layout():
|
|
257
|
+
if is_gui_plot:
|
|
258
|
+
self._gui_plot_data = set_index_coordinates(raster_data, (self._plot_inputs['x_axis'], self._plot_inputs['y_axis']))
|
|
259
|
+
else:
|
|
260
|
+
self._plot_data = set_index_coordinates(raster_data, (self._plot_inputs['x_axis'], self._plot_inputs['y_axis']))
|
|
264
261
|
|
|
265
262
|
# Add params needed for plot: auto color range and ms name
|
|
266
263
|
self._set_auto_color_range(plot_inputs) # set calculated limits if auto mode
|
|
@@ -301,10 +298,11 @@ class MsRaster(MsPlot):
|
|
|
301
298
|
num_subplots = np.prod(subplots) if subplots else 1
|
|
302
299
|
num_iter_plots = min(num_iter_plots, num_subplots) if num_subplots > 1 else num_iter_plots
|
|
303
300
|
end_idx = start_idx + num_iter_plots
|
|
301
|
+
self._plot_inputs['iter_range'] = (start_idx, end_idx)
|
|
304
302
|
|
|
305
|
-
# Set each iter value in
|
|
306
|
-
if '
|
|
307
|
-
plot_inputs['
|
|
303
|
+
# Set each iter value in selection
|
|
304
|
+
if 'selection' not in plot_inputs:
|
|
305
|
+
plot_inputs['selection'] = {}
|
|
308
306
|
|
|
309
307
|
for i in range(start_idx, end_idx):
|
|
310
308
|
# Select iteration value and make plot
|
|
@@ -347,6 +345,22 @@ class MsRaster(MsPlot):
|
|
|
347
345
|
self._logger.info("Maximum dimensions for selected spw: %s", self._data.get_max_data_dims())
|
|
348
346
|
self._plot_init = True
|
|
349
347
|
|
|
348
|
+
def _is_layout(self):
|
|
349
|
+
''' Determine if plot is a layout using plot inputs '''
|
|
350
|
+
# Check if subplots is a layout
|
|
351
|
+
if self._plot_inputs['subplots'] is None or self._plot_inputs['subplots'] == (1, 1):
|
|
352
|
+
return False
|
|
353
|
+
|
|
354
|
+
# Check if iteration set and iter_range more than one plot
|
|
355
|
+
iter_length = 0
|
|
356
|
+
if self._plot_inputs['iter_axis'] is not None:
|
|
357
|
+
iter_range = self._plot_inputs['iter_range']
|
|
358
|
+
iter_range = (0, 0) if iter_range is None else iter_range
|
|
359
|
+
iter_length = len(range(iter_range[0], iter_range[1] + 1))
|
|
360
|
+
|
|
361
|
+
# Also check if previous plot(s) not cleared
|
|
362
|
+
return iter_length > 1 or len(self._plots) > 0
|
|
363
|
+
|
|
350
364
|
def _set_auto_color_range(self, plot_inputs):
|
|
351
365
|
''' Calculate stats for color limits for non-gui amplitude plots. '''
|
|
352
366
|
color_mode = plot_inputs['color_mode']
|
|
@@ -438,13 +452,14 @@ class MsRaster(MsPlot):
|
|
|
438
452
|
### Main callback to create plot if inputs changed
|
|
439
453
|
###
|
|
440
454
|
# pylint: disable=too-many-arguments, too-many-positional-arguments
|
|
441
|
-
def _update_plot(self, ms, do_plot, x, y, bounds):
|
|
455
|
+
def _update_plot(self, ms, do_plot, x, y, data, bounds):
|
|
442
456
|
''' Create plot with inputs from GUI, or update cursor/box location.
|
|
443
457
|
Callbacks:
|
|
444
458
|
ms (first plot) - return plot with default inputs
|
|
445
459
|
do_plot (Plot button clicked) - return plot with inputs from GUI
|
|
446
|
-
x, y (
|
|
447
|
-
|
|
460
|
+
x, y (PointerXY) - update cursor location box
|
|
461
|
+
data (PointDraw from point_draw tool) - update location of drawn points in tab and log
|
|
462
|
+
bounds (Bounds from box_select tool) - update location of points in box in tab and log
|
|
448
463
|
This function *must* return plot, even if empty plot or last plot, for DynamicMap.
|
|
449
464
|
'''
|
|
450
465
|
# Remove toast notification and collapse selection accordion
|
|
@@ -458,13 +473,18 @@ class MsRaster(MsPlot):
|
|
|
458
473
|
|
|
459
474
|
# User changed inputs without clicking Plot button, or callback for cursor/box.
|
|
460
475
|
if not do_plot and not self._first_gui_plot:
|
|
461
|
-
if
|
|
476
|
+
if cursor_changed(x, y, self._last_cursor):
|
|
462
477
|
# new cursor position - update cursor location box
|
|
463
478
|
self._update_cursor_location(x, y)
|
|
464
479
|
self._last_cursor = (x, y)
|
|
465
480
|
|
|
466
|
-
if self.
|
|
467
|
-
# new
|
|
481
|
+
if points_changed(data, self._last_points):
|
|
482
|
+
# new points position - update selected points location tab
|
|
483
|
+
self._update_points_location(data)
|
|
484
|
+
self._last_points = data
|
|
485
|
+
|
|
486
|
+
if box_changed(bounds, self._last_box):
|
|
487
|
+
# new box_select position - update selected box location tab
|
|
468
488
|
self._update_box_location(bounds)
|
|
469
489
|
self._last_box = bounds
|
|
470
490
|
|
|
@@ -484,9 +504,9 @@ class MsRaster(MsPlot):
|
|
|
484
504
|
self.clear_selection()
|
|
485
505
|
gui_plot = None
|
|
486
506
|
|
|
507
|
+
# Make plot if first plot or changed plot
|
|
487
508
|
style_inputs = self._raster_plot.get_plot_params()['style']
|
|
488
|
-
if self.
|
|
489
|
-
# First plot or changed plot
|
|
509
|
+
if inputs_changed(self._plot_inputs, self._last_plot_inputs) or inputs_changed(style_inputs, self._last_style_inputs):
|
|
490
510
|
try:
|
|
491
511
|
# Check inputs from GUI then plot
|
|
492
512
|
self._plot_inputs['data_dims'] = self._ms_info['data_dims']
|
|
@@ -510,60 +530,25 @@ class MsRaster(MsPlot):
|
|
|
510
530
|
self._last_style_inputs = style_inputs.copy()
|
|
511
531
|
self._last_plot_inputs['ps_selection'] = self._plot_inputs['ps_selection'].copy()
|
|
512
532
|
self._last_plot_inputs['ms_selection'] = self._plot_inputs['ms_selection'].copy()
|
|
533
|
+
|
|
534
|
+
# Save plot for return value if plot update not requested
|
|
535
|
+
self._last_gui_plot = gui_plot
|
|
536
|
+
|
|
537
|
+
# Remove selection not from user
|
|
513
538
|
if self._first_gui_plot and not do_plot:
|
|
514
539
|
self._plot_inputs['ps_selection'].clear()
|
|
515
540
|
self._plot_inputs['ms_selection'].clear()
|
|
516
541
|
|
|
517
|
-
# Save plot for return value if plot update not requested
|
|
518
|
-
self._last_gui_plot = gui_plot
|
|
519
542
|
self._first_gui_plot = False
|
|
520
543
|
|
|
521
|
-
# Add plot inputs, change plot button to outline, and stop spinner
|
|
522
|
-
self.
|
|
544
|
+
# Add plot inputs to GUI, change plot button to outline, and stop spinner
|
|
545
|
+
self._show_plot_inputs()
|
|
523
546
|
self._update_plot_status(False)
|
|
524
547
|
self._update_plot_spinner(False)
|
|
525
548
|
|
|
526
549
|
return gui_plot
|
|
527
550
|
# pylint: enable=too-many-arguments, too-many-positional-arguments
|
|
528
551
|
|
|
529
|
-
def _cursor_changed(self, x, y):
|
|
530
|
-
''' Check whether cursor position changed '''
|
|
531
|
-
if not x and not y:
|
|
532
|
-
return False # not cursor callback
|
|
533
|
-
if self._last_cursor and self._last_cursor == (x, y):
|
|
534
|
-
return False # same cursor
|
|
535
|
-
return True # new cursor or cursor changed
|
|
536
|
-
|
|
537
|
-
def _box_changed(self, bounds):
|
|
538
|
-
''' Check whether box position changed '''
|
|
539
|
-
if not bounds:
|
|
540
|
-
return False # no data, not box select callback
|
|
541
|
-
if self._last_box and self._last_box == bounds:
|
|
542
|
-
return False # same box
|
|
543
|
-
return True # new box or box changed
|
|
544
|
-
|
|
545
|
-
def _inputs_changed(self, style_inputs):
|
|
546
|
-
''' Check if inputs changed and need new plot '''
|
|
547
|
-
if not self._last_plot_inputs:
|
|
548
|
-
return True
|
|
549
|
-
|
|
550
|
-
for key, val in self._plot_inputs.items():
|
|
551
|
-
if not self._values_equal(val, self._last_plot_inputs[key]):
|
|
552
|
-
return True
|
|
553
|
-
|
|
554
|
-
for key, val in style_inputs.items():
|
|
555
|
-
if not self._values_equal(val, self._last_style_inputs[key]):
|
|
556
|
-
return True
|
|
557
|
-
return False
|
|
558
|
-
|
|
559
|
-
def _values_equal(self, val1, val2):
|
|
560
|
-
''' Test if values are set and equal, or not set (cannot compare value with None) '''
|
|
561
|
-
if val1 is not None and val2 is not None: # both set
|
|
562
|
-
return val1 == val2
|
|
563
|
-
if val1 is None and val2 is None: # both None
|
|
564
|
-
return True
|
|
565
|
-
return False # one set and other is None
|
|
566
|
-
|
|
567
552
|
def _do_gui_selection(self):
|
|
568
553
|
''' Apply selections selected in GUI '''
|
|
569
554
|
if self._plot_inputs['ps_selection']:
|
|
@@ -594,7 +579,7 @@ class MsRaster(MsPlot):
|
|
|
594
579
|
return layout_plot
|
|
595
580
|
|
|
596
581
|
# Make single Overlay raster plot for DynamicMap
|
|
597
|
-
plot = self._do_plot(self._plot_inputs)
|
|
582
|
+
plot = self._do_plot(self._plot_inputs, True)
|
|
598
583
|
plot_params = self._raster_plot.get_plot_params()
|
|
599
584
|
|
|
600
585
|
# Update color limits in gui with data range
|
|
@@ -606,7 +591,11 @@ class MsRaster(MsPlot):
|
|
|
606
591
|
|
|
607
592
|
self._logger.info("Plot update complete")
|
|
608
593
|
return plot.opts(
|
|
609
|
-
hv.opts.QuadMesh(
|
|
594
|
+
hv.opts.QuadMesh(
|
|
595
|
+
tools=['hover', 'box_select'],
|
|
596
|
+
selection_fill_alpha=0.5, # dim selected areas of plot
|
|
597
|
+
nonselection_fill_alpha=1.0, # do not dim unselected areas of plot
|
|
598
|
+
)
|
|
610
599
|
)
|
|
611
600
|
except RuntimeError as e:
|
|
612
601
|
error = f"Plot failed: {str(e)}"
|
|
@@ -631,7 +620,11 @@ class MsRaster(MsPlot):
|
|
|
631
620
|
)
|
|
632
621
|
)
|
|
633
622
|
return plot.opts(
|
|
634
|
-
hv.opts.QuadMesh(
|
|
623
|
+
hv.opts.QuadMesh(
|
|
624
|
+
tools=['hover', 'box_select'],
|
|
625
|
+
selection_fill_alpha=0.5, # dim selected areas of plot
|
|
626
|
+
nonselection_fill_alpha=1.0, # do not dim unselected areas of plot
|
|
627
|
+
)
|
|
635
628
|
)
|
|
636
629
|
|
|
637
630
|
def _set_plot_colorbar(self, plot, plot_params, plot_type):
|
|
@@ -834,15 +827,15 @@ class MsRaster(MsPlot):
|
|
|
834
827
|
spinner = self._gui_layout[2][2][1]
|
|
835
828
|
spinner.value = plot_clicked
|
|
836
829
|
|
|
837
|
-
def _update_plot_status(self,
|
|
838
|
-
''' Change button color when inputs change. '''
|
|
830
|
+
def _update_plot_status(self, plot_changed):
|
|
831
|
+
''' Change button color when plot inputs change. '''
|
|
839
832
|
if self._gui_layout:
|
|
840
833
|
# Set button color
|
|
841
834
|
button = self._gui_layout[2][2][0]
|
|
842
|
-
button.button_style = 'solid' if
|
|
835
|
+
button.button_style = 'solid' if plot_changed else 'outline'
|
|
843
836
|
|
|
844
|
-
def
|
|
845
|
-
''' Show inputs for raster plot in GUI '''
|
|
837
|
+
def _show_plot_inputs(self):
|
|
838
|
+
''' Show inputs for raster plot in GUI tab '''
|
|
846
839
|
if self._plot_params:
|
|
847
840
|
inputs_column = self._gui_layout[0][1]
|
|
848
841
|
inputs_column.clear()
|
|
@@ -856,14 +849,10 @@ class MsRaster(MsPlot):
|
|
|
856
849
|
# Convert plot values to selection values to select plot data
|
|
857
850
|
x_axis = self._plot_inputs['x_axis']
|
|
858
851
|
y_axis = self._plot_inputs['y_axis']
|
|
859
|
-
plot_data = set_index_coordinates(self._plot_data, (x_axis, y_axis))
|
|
860
852
|
cursor_position = {x_axis: x, y_axis: y}
|
|
861
|
-
cursor_location = locate_point(
|
|
862
|
-
self._update_cursor_box(cursor_location)
|
|
853
|
+
cursor_location = locate_point(self._gui_plot_data, cursor_position, self._plot_inputs['vis_axis'])
|
|
863
854
|
|
|
864
|
-
|
|
865
|
-
''' Update cursor location widget box with list of Panel StaticText widgets '''
|
|
866
|
-
cursor_location_box = self._gui_layout[0][0][1] # row 0 tab 0 row 1 in column
|
|
855
|
+
cursor_location_box = self._gui_layout[0][0][1] # row[0] tabs[0] column[1]
|
|
867
856
|
cursor_location_box.clear() # pn.WidgetBox
|
|
868
857
|
location_layout = pn.Column(pn.widgets.StaticText(name="Cursor Location"))
|
|
869
858
|
location_row = self._layout_point_location(cursor_location)
|
|
@@ -890,29 +879,54 @@ class MsRaster(MsPlot):
|
|
|
890
879
|
location_row.append(location_col)
|
|
891
880
|
return location_row
|
|
892
881
|
|
|
882
|
+
def _update_points_location(self, data):
|
|
883
|
+
''' Show data values for points in point_draw in tab and log '''
|
|
884
|
+
points_locate_column = self._gui_layout[0][2] # row[0] tabs[2]
|
|
885
|
+
points_locate_column.clear()
|
|
886
|
+
points = list(zip(data['x'], data['y']))
|
|
887
|
+
|
|
888
|
+
if points:
|
|
889
|
+
self._logger.info("Locate selected points:")
|
|
890
|
+
x_axis = self._plot_inputs['x_axis']
|
|
891
|
+
y_axis = self._plot_inputs['y_axis']
|
|
892
|
+
|
|
893
|
+
for point in list(zip(data['x'], data['y'])):
|
|
894
|
+
# Locate point
|
|
895
|
+
point_position = {x_axis: point[0], y_axis: point[1]}
|
|
896
|
+
point_location = locate_point(self._gui_plot_data, point_position, self._plot_inputs['vis_axis'])
|
|
897
|
+
# Format location and add to points locate column
|
|
898
|
+
location_layout = self._layout_point_location(point_location)
|
|
899
|
+
points_locate_column.append(location_layout)
|
|
900
|
+
points_locate_column.append(pn.layout.Divider())
|
|
901
|
+
|
|
902
|
+
# Format and add to log
|
|
903
|
+
location_list = [f"{static_text.name}={static_text.value}" for static_text in point_location]
|
|
904
|
+
self._logger.info(", ".join(location_list))
|
|
905
|
+
|
|
893
906
|
def _update_box_location(self, bounds):
|
|
894
|
-
''' Show data values for points in
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
907
|
+
''' Show data values for points in box_select in tab and log '''
|
|
908
|
+
box_locate_column = self._gui_layout[0][3] # row[0] tabs[3]
|
|
909
|
+
box_locate_column.clear()
|
|
910
|
+
if bounds:
|
|
911
|
+
x_axis = self._plot_inputs['x_axis']
|
|
912
|
+
y_axis = self._plot_inputs['y_axis']
|
|
913
|
+
box_bounds = {x_axis: (bounds[0], bounds[2]), y_axis: (bounds[1], bounds[3])}
|
|
914
|
+
npoints, point_locations = locate_box(self._gui_plot_data, box_bounds, self._plot_inputs['vis_axis'])
|
|
915
|
+
|
|
916
|
+
message = f"Locate {npoints} points"
|
|
917
|
+
message += " (only first 100 shown):" if npoints > 100 else ":"
|
|
918
|
+
self._logger.info(message)
|
|
919
|
+
box_locate_column.append(pn.pane.Str(message))
|
|
920
|
+
|
|
921
|
+
for point in point_locations:
|
|
922
|
+
# Format and add to box locate column
|
|
923
|
+
location_layout = self._layout_point_location(point)
|
|
924
|
+
box_locate_column.append(location_layout)
|
|
925
|
+
box_locate_column.append(pn.layout.Divider())
|
|
926
|
+
|
|
927
|
+
# Format and add to log
|
|
928
|
+
location_list = [f"{static_text.name}={static_text.value}" for static_text in point]
|
|
929
|
+
self._logger.info(", ".join(location_list))
|
|
916
930
|
|
|
917
931
|
###
|
|
918
932
|
### Callbacks for widgets which update plot inputs
|
|
@@ -968,9 +982,7 @@ class MsRaster(MsPlot):
|
|
|
968
982
|
else:
|
|
969
983
|
self._plot_inputs['iter_range'] = None
|
|
970
984
|
self._update_plot_status(True) # Change plot button to solid
|
|
971
|
-
# pylint: enable=too-many-arguments, too-many-positional-arguments
|
|
972
985
|
|
|
973
|
-
# pylint: disable=too-many-arguments, too-many-positional-arguments, unused-argument
|
|
974
986
|
def _set_ps_selection(self, query, name, intents, scan_name, spw_name, field_name, source_name, line_name):
|
|
975
987
|
''' Select ProcessingSet from gui using summary columns '''
|
|
976
988
|
inputs = locals()
|
|
@@ -5,7 +5,7 @@ Check inputs to MsRaster plot() or its GUI
|
|
|
5
5
|
from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, AGGREGATOR_OPTIONS
|
|
6
6
|
|
|
7
7
|
def check_inputs(inputs):
|
|
8
|
-
''' Check plot input types, and axis input values. '''
|
|
8
|
+
''' Check plot input types, and axis (plot, agg, iter) input values. '''
|
|
9
9
|
_set_baseline_antenna_axis(inputs)
|
|
10
10
|
_check_axis_inputs(inputs)
|
|
11
11
|
_check_agg_inputs(inputs)
|
|
@@ -10,6 +10,32 @@ import panel as pn
|
|
|
10
10
|
|
|
11
11
|
from vidavis.plot.ms_plot._ms_plot_constants import TIME_FORMAT
|
|
12
12
|
|
|
13
|
+
def cursor_changed(x, y, last_cursor):
|
|
14
|
+
''' Check whether cursor position changed '''
|
|
15
|
+
if not x and not y:
|
|
16
|
+
return False # not cursor callback
|
|
17
|
+
if last_cursor and last_cursor == (x, y):
|
|
18
|
+
return False # same cursor
|
|
19
|
+
return True # new cursor or cursor changed
|
|
20
|
+
|
|
21
|
+
def points_changed(data, last_points):
|
|
22
|
+
''' Check whether point positions changed '''
|
|
23
|
+
# No data = {'x': [], 'y': []}
|
|
24
|
+
if len(data['x']) == 0 and len(data['y']) == 0:
|
|
25
|
+
return False # not points callback
|
|
26
|
+
if last_points and last_points == data:
|
|
27
|
+
return False # same points
|
|
28
|
+
return True # new points, points changed, or points deleted
|
|
29
|
+
|
|
30
|
+
def box_changed(bounds, last_box):
|
|
31
|
+
''' Check whether box position changed '''
|
|
32
|
+
# No bounds = None
|
|
33
|
+
if not bounds:
|
|
34
|
+
return False # no data, not box select callback
|
|
35
|
+
if last_box and last_box == bounds:
|
|
36
|
+
return False # same box
|
|
37
|
+
return True # new box, box changed, or box deleted
|
|
38
|
+
|
|
13
39
|
def locate_point(xds, position, vis_axis):
|
|
14
40
|
'''
|
|
15
41
|
Get cursor location as values of coordinates and data vars.
|
|
@@ -47,7 +73,7 @@ def locate_box(xds, bounds, vis_axis):
|
|
|
47
73
|
selection = {}
|
|
48
74
|
for coord, val in bounds.items():
|
|
49
75
|
# Round index values to int for selection
|
|
50
|
-
selection[coord] = slice(
|
|
76
|
+
selection[coord] = slice(_get_selection_value(coord, val[0]), _get_selection_value(coord, val[1]))
|
|
51
77
|
sel_xds = xds.sel(indexers=None, method=None, tolerance=None, drop=False, **selection)
|
|
52
78
|
|
|
53
79
|
x_coord, y_coord = bounds.keys()
|
|
@@ -81,9 +107,9 @@ def _get_point_location(xds, position, vis_axis):
|
|
|
81
107
|
|
|
82
108
|
if xds:
|
|
83
109
|
try:
|
|
84
|
-
# Round index coordinates to int for selection
|
|
85
110
|
for coord, value in position.items():
|
|
86
|
-
|
|
111
|
+
# Round index coordinates to int and convert time to datetime if float for selection
|
|
112
|
+
position[coord] = _get_selection_value(coord, value)
|
|
87
113
|
|
|
88
114
|
sel_xds = xds.sel(indexers=None, method='nearest', tolerance=None, drop=False, **position)
|
|
89
115
|
for coord in sel_xds.coords:
|
|
@@ -112,9 +138,15 @@ def _get_point_location(xds, position, vis_axis):
|
|
|
112
138
|
values[vis_axis.upper()] = values.pop('VISIBILITY')
|
|
113
139
|
return values, units
|
|
114
140
|
|
|
115
|
-
def
|
|
116
|
-
'''
|
|
117
|
-
|
|
141
|
+
def _get_selection_value(coord, value):
|
|
142
|
+
''' Convert index coordinates to int and float time coordinate to datetime '''
|
|
143
|
+
if coord in ['baseline', 'antenna_name', 'polarization']:
|
|
144
|
+
# Round index coordinates to int for selecction
|
|
145
|
+
value = round(value)
|
|
146
|
+
elif coord == 'time' and isinstance(value, float):
|
|
147
|
+
# Bokeh datetime values are floating-point numbers: milliseconds since the Unix epoch
|
|
148
|
+
value = to_datetime(value, unit='ms', origin='unix')
|
|
149
|
+
return value
|
|
118
150
|
|
|
119
151
|
def _get_xda_val_unit(xda):
|
|
120
152
|
''' Return value and unit of xda (selected so only one value) '''
|
vidavis/plot/ms_plot/_ms_plot.py
CHANGED
|
@@ -6,7 +6,7 @@ import os
|
|
|
6
6
|
import time
|
|
7
7
|
|
|
8
8
|
from bokeh.io import export_png, export_svg
|
|
9
|
-
from bokeh.plotting import save
|
|
9
|
+
from bokeh.plotting import save
|
|
10
10
|
import hvplot
|
|
11
11
|
import holoviews as hv
|
|
12
12
|
import numpy as np
|
|
@@ -50,6 +50,19 @@ class MsPlot:
|
|
|
50
50
|
pn.config.notifications = True
|
|
51
51
|
self._toast = None # for destroy() with new plot or new notification
|
|
52
52
|
|
|
53
|
+
# Initialize gui panel for callbacks
|
|
54
|
+
self._gui_layout = None
|
|
55
|
+
self._first_gui_plot = True
|
|
56
|
+
self._last_gui_plot = None
|
|
57
|
+
self._gui_plot_data = None
|
|
58
|
+
|
|
59
|
+
# For _update_plot callback: check which inputs and point positions changed
|
|
60
|
+
self._last_plot_inputs = None
|
|
61
|
+
self._last_style_inputs = None
|
|
62
|
+
self._last_cursor = None
|
|
63
|
+
self._last_points = None
|
|
64
|
+
self._last_box = None
|
|
65
|
+
|
|
53
66
|
# Initialize plot inputs and params
|
|
54
67
|
self._plot_inputs = {'selection': {}}
|
|
55
68
|
self._plot_params = None
|
|
@@ -59,11 +72,9 @@ class MsPlot:
|
|
|
59
72
|
self._plots_locked = False
|
|
60
73
|
self._plots = []
|
|
61
74
|
|
|
62
|
-
# Initialize
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
self._first_gui_plot = True
|
|
66
|
-
self._last_gui_plot = None
|
|
75
|
+
# Initialize show() panel for callbacks
|
|
76
|
+
self._show_layout = None
|
|
77
|
+
self._plot_data = None
|
|
67
78
|
|
|
68
79
|
# Set data (if ms)
|
|
69
80
|
self._data = None
|
|
@@ -159,10 +170,10 @@ class MsPlot:
|
|
|
159
170
|
column = pn.Column()
|
|
160
171
|
for param in self._plot_params:
|
|
161
172
|
column.append(pn.pane.Str(param))
|
|
162
|
-
|
|
163
|
-
tabs.show(title=self._app_name, threaded=True)
|
|
173
|
+
self._show_layout = pn.Tabs(('Plot', bokeh_fig), ('Plot Inputs', column))
|
|
164
174
|
else:
|
|
165
|
-
|
|
175
|
+
self._show_layout = pn.pane.Bokeh(bokeh_fig)
|
|
176
|
+
self._show_layout.show(title=self._app_name, threaded=True)
|
|
166
177
|
|
|
167
178
|
def save(self, filename='ms_plot.png', fmt='auto', width=900, height=600):
|
|
168
179
|
'''
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
''' Utilities for inputs to MeasurementSet plots '''
|
|
2
|
+
|
|
3
|
+
def inputs_changed(plot_inputs, last_plot_inputs):
|
|
4
|
+
''' Check if inputs changed and need new plot '''
|
|
5
|
+
if not last_plot_inputs:
|
|
6
|
+
return True
|
|
7
|
+
|
|
8
|
+
for key, val in plot_inputs.items():
|
|
9
|
+
if not _values_equal(val, last_plot_inputs[key]):
|
|
10
|
+
return True
|
|
11
|
+
return False
|
|
12
|
+
|
|
13
|
+
def _values_equal(val1, val2):
|
|
14
|
+
''' Test if values are set and equal, or not set (cannot compare value with None) '''
|
|
15
|
+
if val1 is not None and val2 is not None: # both set
|
|
16
|
+
return val1 == val2
|
|
17
|
+
if val1 is None and val2 is None: # both None
|
|
18
|
+
return True
|
|
19
|
+
return False # one set and other is None
|
|
@@ -9,9 +9,42 @@ from vidavis.plot.ms_plot._ms_plot_selectors import (file_selector, title_select
|
|
|
9
9
|
|
|
10
10
|
def create_raster_gui(callbacks, data_dims, x_axis, y_axis):
|
|
11
11
|
''' Use Holoviz Panel to create a dashboard for plot inputs and raster plot display. '''
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
# Accordion of widgets for plot inputs
|
|
13
|
+
selectors = get_plot_input_selectors(callbacks, data_dims, x_axis, y_axis)
|
|
14
|
+
|
|
15
|
+
# Plot button and spinner while plotting
|
|
16
|
+
init_plot = plot_starter(callbacks['plot_updating'])
|
|
17
|
+
|
|
18
|
+
# Dynamic map for plot, with callback when inputs change or location needed
|
|
19
|
+
dmap, points = get_plot_dmap(callbacks, selectors, init_plot)
|
|
20
|
+
|
|
21
|
+
return pn.Row(
|
|
22
|
+
pn.Tabs( # Row [0]
|
|
23
|
+
('Plot', # Tabs[0]
|
|
24
|
+
pn.Column(
|
|
25
|
+
dmap * points, # [0] plot with hv.Points overlay for point_draw
|
|
26
|
+
pn.WidgetBox(), # [1] cursor location
|
|
27
|
+
)
|
|
28
|
+
),
|
|
29
|
+
('Plot Inputs', pn.Column()), # Tabs[1]
|
|
30
|
+
('Locate Selected Points', pn.Column()), # Tabs[2]
|
|
31
|
+
('Locate Selected Box', pn.Column()), # Tabs[3]
|
|
32
|
+
sizing_mode='stretch_width',
|
|
33
|
+
),
|
|
34
|
+
pn.Spacer(width=10), # Row [1]
|
|
35
|
+
pn.Column( # Row [2]
|
|
36
|
+
pn.Spacer(height=25), # Column[0]
|
|
37
|
+
selectors, # Column[1]
|
|
38
|
+
init_plot, # Column[2]
|
|
39
|
+
width_policy='min',
|
|
40
|
+
width=400,
|
|
41
|
+
sizing_mode='stretch_height',
|
|
42
|
+
),
|
|
43
|
+
sizing_mode='stretch_height',
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def get_plot_input_selectors(callbacks, data_dims, x_axis, y_axis):
|
|
47
|
+
''' Create accordion of widgets for plot inputs selection '''
|
|
15
48
|
# Select MS
|
|
16
49
|
file_selectors = file_selector('Path to MeasurementSet (ms or zarr) for plot', '~' , callbacks['filename'])
|
|
17
50
|
|
|
@@ -47,50 +80,26 @@ def create_raster_gui(callbacks, data_dims, x_axis, y_axis):
|
|
|
47
80
|
("Plot title", title_input), # [6]
|
|
48
81
|
)
|
|
49
82
|
selectors.toggle = True
|
|
83
|
+
return selectors
|
|
50
84
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
+
)
|
|
59
93
|
dmap = hv.DynamicMap(
|
|
60
94
|
pn.bind(
|
|
61
95
|
callbacks['update_plot'],
|
|
62
|
-
ms=
|
|
96
|
+
ms=selectors[0][0][0],
|
|
63
97
|
do_plot=init_plot[0],
|
|
64
98
|
),
|
|
65
99
|
streams=[
|
|
66
|
-
hv.streams.PointerXY(),
|
|
67
|
-
hv.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)
|
|
68
103
|
]
|
|
69
104
|
)
|
|
70
|
-
|
|
71
|
-
# ----------------------------------------------
|
|
72
|
-
# GUI LAYOUT OF PLOT TABS AND PLOT INPUTS COLUMN
|
|
73
|
-
# ----------------------------------------------
|
|
74
|
-
return pn.Row(
|
|
75
|
-
pn.Tabs( # Row [0]
|
|
76
|
-
('Plot',
|
|
77
|
-
pn.Column( # Tabs [0]
|
|
78
|
-
dmap, # [0]
|
|
79
|
-
pn.WidgetBox(), # [1] cursor location
|
|
80
|
-
)
|
|
81
|
-
),
|
|
82
|
-
('Plot Inputs', pn.Column()), # Tabs [1]
|
|
83
|
-
('Locate Selected Box', pn.Column()), # Tabs [2]
|
|
84
|
-
sizing_mode='stretch_width',
|
|
85
|
-
),
|
|
86
|
-
pn.Spacer(width=10), # Row [1]
|
|
87
|
-
pn.Column( # Row [2]
|
|
88
|
-
pn.Spacer(height=25), # Column [0]
|
|
89
|
-
selectors, # Column [1]
|
|
90
|
-
init_plot, # Column [2]
|
|
91
|
-
width_policy='min',
|
|
92
|
-
width=400,
|
|
93
|
-
sizing_mode='stretch_height',
|
|
94
|
-
),
|
|
95
|
-
sizing_mode='stretch_height',
|
|
96
|
-
)
|
|
105
|
+
return dmap, points
|
|
@@ -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=bWe7N7lbwNYzPl1n-_Y_qBErDddxVe8jnPRYqOxylSI,48397
|
|
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
|
|
@@ -18,21 +18,22 @@ vidavis/data/measurement_set/processing_set/_ps_stats.py,sha256=4uLImKCANXLUM8jO
|
|
|
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
|
-
vidavis/plot/ms_plot/
|
|
22
|
-
vidavis/plot/ms_plot/
|
|
21
|
+
vidavis/plot/ms_plot/_check_raster_inputs.py,sha256=a7u5wlDKTxWYW36-Xp3xd4c756SbYURdFkGHbUaX440,4786
|
|
22
|
+
vidavis/plot/ms_plot/_locate_points.py,sha256=GvuS5kDhIYancc314ofLoYtoRhT_jagG8HpzchhdPds,7474
|
|
23
|
+
vidavis/plot/ms_plot/_ms_plot.py,sha256=RShzvWzHq7iA9awuaWO8GiB1NkWuYkcNOMdToLWPnes,13246
|
|
23
24
|
vidavis/plot/ms_plot/_ms_plot_constants.py,sha256=cX_TQhKJ3hJzPuRYmuRJxue1sjq82yl_ZN2_w6TshmI,930
|
|
24
25
|
vidavis/plot/ms_plot/_ms_plot_selectors.py,sha256=BZQwARvMPdk78n6Rh2tOaSc8GenZBrxHZb14oFD9gJM,10785
|
|
26
|
+
vidavis/plot/ms_plot/_plot_inputs.py,sha256=pZL63n9FHSoo9cntf7lppQj44nkpiwnCz6VH-9Oh6ho,671
|
|
25
27
|
vidavis/plot/ms_plot/_raster_plot.py,sha256=lNa9i_eJ8F8Fc2zcHLRcaxKKOELk3x_QmXT__T76pg8,10999
|
|
26
|
-
vidavis/plot/ms_plot/_raster_plot_gui.py,sha256=
|
|
27
|
-
vidavis/plot/ms_plot/_raster_plot_inputs.py,sha256=vPjZPDuB26ENxwv4z7dUa_c5TqByXejScY6hqEnLFLU,4768
|
|
28
|
+
vidavis/plot/ms_plot/_raster_plot_gui.py,sha256=Kb0BLmkwtYHCqlVaSipbTTStst8RYggqwf1Wf5v8F4Q,4261
|
|
28
29
|
vidavis/plot/ms_plot/_time_ticks.py,sha256=j-DcPh7RfGE8iX2bPjLQDQPIbiAbmjiEWQnKmdMWA3I,1773
|
|
29
30
|
vidavis/plot/ms_plot/_xds_plot_axes.py,sha256=EeWvAbiKV33nEWdI8V3M0uwLTnycq4bFYBOyVWkxCu0,4429
|
|
30
31
|
vidavis/toolbox/__init__.py,sha256=jqFa-eziVz_frNnXxwjJFK36qNpz1H38s-VlpBcq-R8,1402
|
|
31
32
|
vidavis/toolbox/_app_context.py,sha256=H7gtF8RrAH46FqDcMobv3KM1Osbnapgu6aTG-m3VCWA,3049
|
|
32
33
|
vidavis/toolbox/_logging.py,sha256=OEisrd8FM8VTNBMc7neLh9ekelf29ZILYB5pScebly0,2739
|
|
33
34
|
vidavis/toolbox/_static.py,sha256=HJLMtClppgOJXWAtV6Umn5EqN80u0oZiIouQ1JsB9PM,2346
|
|
34
|
-
vidavis/__version__.py,sha256=
|
|
35
|
-
vidavis-0.0.
|
|
36
|
-
vidavis-0.0.
|
|
37
|
-
vidavis-0.0.
|
|
38
|
-
vidavis-0.0.
|
|
35
|
+
vidavis/__version__.py,sha256=6iFz7cldtJZlw_FSwnt97X1e8jfQ-fdVhcVMqnAiWh8,22
|
|
36
|
+
vidavis-0.0.13.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
|
|
37
|
+
vidavis-0.0.13.dist-info/METADATA,sha256=7fBkDctO-V9veQD-m1BbnimzfHgH-PXvEvmhNE_7z3I,2238
|
|
38
|
+
vidavis-0.0.13.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
|
|
39
|
+
vidavis-0.0.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|