vidavis 0.0.7__py3-none-any.whl → 0.0.9__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 +110 -109
- vidavis/data/measurement_set/processing_set/_ps_data.py +18 -7
- vidavis/plot/ms_plot/_locate_points.py +153 -0
- vidavis/plot/ms_plot/_ms_plot.py +4 -1
- vidavis/plot/ms_plot/_raster_plot_gui.py +11 -6
- {vidavis-0.0.7.dist-info → vidavis-0.0.9.dist-info}/METADATA +1 -1
- {vidavis-0.0.7.dist-info → vidavis-0.0.9.dist-info}/RECORD +10 -9
- {vidavis-0.0.7.dist-info → vidavis-0.0.9.dist-info}/WHEEL +0 -0
- {vidavis-0.0.7.dist-info → vidavis-0.0.9.dist-info}/licenses/LICENSE +0 -0
vidavis/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.0.
|
|
1
|
+
__version__ = '0.0.9'
|
vidavis/apps/_ms_raster.py
CHANGED
|
@@ -13,8 +13,9 @@ import panel as pn
|
|
|
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
15
|
from vidavis.plot.ms_plot._time_ticks import get_time_formatter
|
|
16
|
+
from vidavis.plot.ms_plot._locate_points import locate_point, locate_box
|
|
16
17
|
from vidavis.plot.ms_plot._ms_plot import MsPlot
|
|
17
|
-
from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, SPECTRUM_AXIS_OPTIONS, PS_SELECTION_OPTIONS, MS_SELECTION_OPTIONS
|
|
18
|
+
from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, SPECTRUM_AXIS_OPTIONS, PS_SELECTION_OPTIONS, MS_SELECTION_OPTIONS
|
|
18
19
|
from vidavis.plot.ms_plot._raster_plot_gui import create_raster_gui
|
|
19
20
|
from vidavis.plot.ms_plot._raster_plot_inputs import check_inputs
|
|
20
21
|
from vidavis.plot.ms_plot._raster_plot import RasterPlot
|
|
@@ -54,10 +55,9 @@ class MsRaster(MsPlot):
|
|
|
54
55
|
self._last_plot_inputs = None
|
|
55
56
|
self._last_style_inputs = None
|
|
56
57
|
self._last_cursor = None
|
|
58
|
+
self._last_box = None
|
|
57
59
|
|
|
58
60
|
# Return plot for gui DynamicMap:
|
|
59
|
-
# Last plot when no new plot created (plot inputs same) or is iter Layout plot (opened in tab)
|
|
60
|
-
self._last_gui_plot = None
|
|
61
61
|
# Empty plot when ms not set or plot fails
|
|
62
62
|
self._empty_plot = self._create_empty_plot()
|
|
63
63
|
|
|
@@ -66,9 +66,9 @@ class MsRaster(MsPlot):
|
|
|
66
66
|
self.plot()
|
|
67
67
|
self._launch_gui()
|
|
68
68
|
|
|
69
|
-
# Set filename TextInput to input ms
|
|
69
|
+
# Set filename TextInput to input ms, which triggers default plot
|
|
70
70
|
if 'ms' in self._ms_info and self._ms_info['ms']:
|
|
71
|
-
self._set_filename([self._ms_info['ms']]) # function expects list
|
|
71
|
+
self._set_filename([self._ms_info['ms']]) # function expects list from FileBrowser widget
|
|
72
72
|
|
|
73
73
|
def colormaps(self):
|
|
74
74
|
''' List available colormap (Bokeh palettes). '''
|
|
@@ -413,7 +413,7 @@ class MsRaster(MsPlot):
|
|
|
413
413
|
### Interactive GUI
|
|
414
414
|
### -----------------------------------------------------------------------
|
|
415
415
|
def _launch_gui(self):
|
|
416
|
-
''' Use Holoviz Panel to create a dashboard for plot inputs. '''
|
|
416
|
+
''' Use Holoviz Panel to create and show a dashboard for plot inputs. '''
|
|
417
417
|
callbacks = {
|
|
418
418
|
'filename': self._set_filename,
|
|
419
419
|
'style': self._set_style_params,
|
|
@@ -432,37 +432,47 @@ class MsRaster(MsPlot):
|
|
|
432
432
|
x_axis = self._plot_inputs['x_axis']
|
|
433
433
|
y_axis = self._plot_inputs['y_axis']
|
|
434
434
|
self._gui_layout = create_raster_gui(callbacks, data_dims, x_axis, y_axis)
|
|
435
|
-
# Show gui
|
|
436
435
|
self._gui_layout.show(title=self._app_name, threaded=True)
|
|
437
436
|
|
|
438
437
|
###
|
|
439
438
|
### Main callback to create plot if inputs changed
|
|
440
439
|
###
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
440
|
+
# pylint: disable=too-many-arguments, too-many-positional-arguments
|
|
441
|
+
def _update_plot(self, ms, do_plot, x, y, bounds):
|
|
442
|
+
''' Create plot with inputs from GUI, or update cursor/box location.
|
|
443
|
+
Callbacks:
|
|
444
|
+
ms (first plot) - return plot with default inputs
|
|
445
|
+
do_plot (Plot button clicked) - return plot with inputs from GUI
|
|
446
|
+
x, y (cursor position) - update cursor location box
|
|
447
|
+
bounds (box select) - update location of points in box in tab and log
|
|
448
|
+
This function *must* return plot, even if empty plot or last plot, for DynamicMap.
|
|
444
449
|
'''
|
|
450
|
+
# Remove toast notification and collapse selection accordion
|
|
445
451
|
if self._toast:
|
|
446
452
|
self._toast.destroy()
|
|
447
|
-
|
|
448
453
|
self._get_selector("selectors").active = []
|
|
454
|
+
|
|
449
455
|
if not ms:
|
|
450
|
-
#
|
|
456
|
+
# GUI launched with no MS, nothing to plot
|
|
451
457
|
return self._empty_plot
|
|
452
458
|
|
|
453
|
-
#
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if not self._last_cursor or (self._last_cursor[0] != x or self._last_cursor[1] != y):
|
|
458
|
-
# Callback is for new cursor location
|
|
459
|
+
# User changed inputs without clicking Plot button, or callback for cursor/box.
|
|
460
|
+
if not do_plot and not self._first_gui_plot:
|
|
461
|
+
if self._cursor_changed(x, y):
|
|
462
|
+
# new cursor position - update cursor location box
|
|
459
463
|
self._update_cursor_location(x, y)
|
|
460
464
|
self._last_cursor = (x, y)
|
|
465
|
+
|
|
466
|
+
if self._box_changed(bounds):
|
|
467
|
+
# new box_select position - update location for points in box
|
|
468
|
+
self._update_box_location(bounds)
|
|
469
|
+
self._last_box = bounds
|
|
470
|
+
|
|
461
471
|
# Not ready to update plot yet, return last plot.
|
|
462
472
|
return self._last_gui_plot
|
|
463
473
|
|
|
464
|
-
#
|
|
465
|
-
if (self._set_ms(ms) or
|
|
474
|
+
# First plot for input ms, or user clicked Plot button for new inputs
|
|
475
|
+
if (self._set_ms(ms) or self._first_gui_plot) and self._data and self._data.is_valid():
|
|
466
476
|
# New MS set and is valid
|
|
467
477
|
self._update_gui_axis_options()
|
|
468
478
|
|
|
@@ -495,17 +505,18 @@ class MsRaster(MsPlot):
|
|
|
495
505
|
# Update plot inputs for gui
|
|
496
506
|
self._set_plot_params(self._plot_inputs | style_inputs)
|
|
497
507
|
|
|
498
|
-
# Save inputs to see if changed
|
|
508
|
+
# Save inputs to see if changed next time
|
|
499
509
|
self._last_plot_inputs = self._plot_inputs.copy()
|
|
500
510
|
self._last_style_inputs = style_inputs.copy()
|
|
501
511
|
self._last_plot_inputs['ps_selection'] = self._plot_inputs['ps_selection'].copy()
|
|
502
512
|
self._last_plot_inputs['ms_selection'] = self._plot_inputs['ms_selection'].copy()
|
|
503
|
-
if
|
|
513
|
+
if self._first_gui_plot and not do_plot:
|
|
504
514
|
self._plot_inputs['ps_selection'].clear()
|
|
505
515
|
self._plot_inputs['ms_selection'].clear()
|
|
506
516
|
|
|
507
|
-
# Save plot if
|
|
517
|
+
# Save plot for return value if plot update not requested
|
|
508
518
|
self._last_gui_plot = gui_plot
|
|
519
|
+
self._first_gui_plot = False
|
|
509
520
|
|
|
510
521
|
# Add plot inputs, change plot button to outline, and stop spinner
|
|
511
522
|
self._update_plot_inputs()
|
|
@@ -513,6 +524,23 @@ class MsRaster(MsPlot):
|
|
|
513
524
|
self._update_plot_spinner(False)
|
|
514
525
|
|
|
515
526
|
return gui_plot
|
|
527
|
+
# pylint: enable=too-many-arguments, too-many-positional-arguments
|
|
528
|
+
|
|
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
|
|
516
544
|
|
|
517
545
|
def _inputs_changed(self, style_inputs):
|
|
518
546
|
''' Check if inputs changed and need new plot '''
|
|
@@ -578,7 +606,7 @@ class MsRaster(MsPlot):
|
|
|
578
606
|
|
|
579
607
|
self._logger.info("Plot update complete")
|
|
580
608
|
return plot.opts(
|
|
581
|
-
hv.opts.QuadMesh(tools=['hover'])
|
|
609
|
+
hv.opts.QuadMesh(tools=['hover', 'box_select'])
|
|
582
610
|
)
|
|
583
611
|
except RuntimeError as e:
|
|
584
612
|
error = f"Plot failed: {str(e)}"
|
|
@@ -588,7 +616,7 @@ class MsRaster(MsPlot):
|
|
|
588
616
|
return self._empty_plot
|
|
589
617
|
|
|
590
618
|
def _create_empty_plot(self):
|
|
591
|
-
''' Create empty Overlay plot for DynamicMap with colormap params and
|
|
619
|
+
''' Create empty Overlay plot for DynamicMap with colormap params and required tools enabled '''
|
|
592
620
|
plot_params = self._raster_plot.get_plot_params()
|
|
593
621
|
plot = hv.Overlay(
|
|
594
622
|
hv.QuadMesh([]).opts(
|
|
@@ -603,7 +631,7 @@ class MsRaster(MsPlot):
|
|
|
603
631
|
)
|
|
604
632
|
)
|
|
605
633
|
return plot.opts(
|
|
606
|
-
hv.opts.QuadMesh(tools=['hover'])
|
|
634
|
+
hv.opts.QuadMesh(tools=['hover', 'box_select'])
|
|
607
635
|
)
|
|
608
636
|
|
|
609
637
|
def _set_plot_colorbar(self, plot, plot_params, plot_type):
|
|
@@ -819,99 +847,72 @@ class MsRaster(MsPlot):
|
|
|
819
847
|
inputs_column = self._gui_layout[0][1]
|
|
820
848
|
inputs_column.clear()
|
|
821
849
|
for param in self._plot_params:
|
|
822
|
-
|
|
850
|
+
str_pane = pn.pane.Str(param)
|
|
851
|
+
str_pane.margin = (0, 10)
|
|
852
|
+
inputs_column.append(str_pane)
|
|
823
853
|
|
|
824
854
|
def _update_cursor_location(self, x, y):
|
|
825
|
-
''' Show
|
|
855
|
+
''' Show data values for cursor x,y position in cursor location box below plot '''
|
|
826
856
|
# Convert plot values to selection values to select plot data
|
|
827
857
|
x_axis = self._plot_inputs['x_axis']
|
|
828
858
|
y_axis = self._plot_inputs['y_axis']
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
859
|
+
plot_data = set_index_coordinates(self._plot_data, (x_axis, y_axis))
|
|
860
|
+
cursor_position = {x_axis: x, y_axis: y}
|
|
861
|
+
cursor_location = locate_point(plot_data, cursor_position, self._plot_inputs['vis_axis'])
|
|
862
|
+
self._update_cursor_box(cursor_location)
|
|
863
|
+
|
|
864
|
+
def _update_cursor_box(self, cursor_location):
|
|
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
|
|
867
|
+
cursor_location_box.clear() # pn.WidgetBox
|
|
868
|
+
location_layout = pn.Column(pn.widgets.StaticText(name="Cursor Location"))
|
|
869
|
+
location_row = self._layout_point_location(cursor_location)
|
|
870
|
+
# Add row of columns to column layout
|
|
871
|
+
location_layout.append(location_row)
|
|
872
|
+
# Add column layout to widget box
|
|
873
|
+
cursor_location_box.append(location_layout)
|
|
834
874
|
|
|
835
|
-
def
|
|
836
|
-
|
|
837
|
-
|
|
875
|
+
def _layout_point_location(self, text_list):
|
|
876
|
+
''' Layout list of StaticText in row of columns containing 3 rows '''
|
|
877
|
+
location_row = pn.Row()
|
|
878
|
+
location_col = pn.Column()
|
|
838
879
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
if coord != 'uvw_label':
|
|
845
|
-
val, unit = self._get_xda_val_unit(sel_xds[coord])
|
|
846
|
-
values[coord] = val
|
|
847
|
-
units[coord] = unit
|
|
848
|
-
for data_var in sel_xds.data_vars:
|
|
849
|
-
val, unit = self._get_xda_val_unit(sel_xds[data_var])
|
|
850
|
-
if data_var == 'UVW':
|
|
851
|
-
names = ['U', 'V', 'W']
|
|
852
|
-
for i, name in enumerate(names):
|
|
853
|
-
values[name] = val[i]
|
|
854
|
-
units[name] = unit[i]
|
|
855
|
-
else:
|
|
856
|
-
values[data_var] = val
|
|
857
|
-
units[data_var] = unit
|
|
858
|
-
except KeyError:
|
|
859
|
-
pass
|
|
880
|
+
for static_text in text_list:
|
|
881
|
+
# 3 entries per column; append to row and start new column
|
|
882
|
+
if len(location_col.objects) == 3:
|
|
883
|
+
location_row.append(location_col)
|
|
884
|
+
location_col = pn.Column()
|
|
860
885
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
values[self._plot_inputs['vis_axis'].capitalize()] = values.pop('VISIBILITY')
|
|
864
|
-
return values, units
|
|
886
|
+
static_text.margin = (0, 10) # default (5, 10)
|
|
887
|
+
location_col.append(static_text)
|
|
865
888
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
cursor_location_box.clear() # pn.WidgetBox
|
|
870
|
-
location_layout = pn.Column(pn.widgets.StaticText(name="Cursor Location"))
|
|
871
|
-
info_row = pn.Row()
|
|
872
|
-
info_col = pn.Column()
|
|
873
|
-
|
|
874
|
-
for name, value in location_values.items():
|
|
875
|
-
# 4 entries per column; append to row and start new column
|
|
876
|
-
if len(info_col.objects) == 4:
|
|
877
|
-
info_row.append(info_col)
|
|
878
|
-
info_col = pn.Column()
|
|
879
|
-
|
|
880
|
-
if not isinstance(value, str):
|
|
881
|
-
if name == "FLAG":
|
|
882
|
-
value = "nan" if np.isnan(value) else int(value)
|
|
883
|
-
elif isinstance(value, float):
|
|
884
|
-
if np.isnan(value):
|
|
885
|
-
value = "nan"
|
|
886
|
-
elif value < 1e6:
|
|
887
|
-
value = f"{value:.4f}"
|
|
888
|
-
else:
|
|
889
|
-
value = f"{value:.4e}"
|
|
890
|
-
elif isinstance(value, np.datetime64):
|
|
891
|
-
value = to_datetime(np.datetime_as_string(value)).strftime(TIME_FORMAT)
|
|
892
|
-
units.pop(name) # no unit for datetime string
|
|
893
|
-
unit = units[name] if name in units else ""
|
|
894
|
-
info = pn.widgets.StaticText(name=name, value=f"{value} {unit}")
|
|
895
|
-
info.margin = (0, 10) # default (5, 10)
|
|
896
|
-
info_col.append(info)
|
|
897
|
-
info_row.append(info_col)
|
|
898
|
-
location_layout.append(info_row)
|
|
899
|
-
cursor_location_box.append(location_layout)
|
|
889
|
+
# Add last column
|
|
890
|
+
location_row.append(location_col)
|
|
891
|
+
return location_row
|
|
900
892
|
|
|
901
|
-
def
|
|
902
|
-
'''
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
893
|
+
def _update_box_location(self, bounds):
|
|
894
|
+
''' Show data values for points in box in box location tab and log '''
|
|
895
|
+
x_axis = self._plot_inputs['x_axis']
|
|
896
|
+
y_axis = self._plot_inputs['y_axis']
|
|
897
|
+
plot_data = set_index_coordinates(self._plot_data, (x_axis, y_axis))
|
|
898
|
+
box_bounds = {x_axis: (bounds[0], bounds[2]), y_axis: (bounds[1], bounds[3])}
|
|
899
|
+
npoints, point_locations = locate_box(plot_data, box_bounds, self._plot_inputs['vis_axis'])
|
|
900
|
+
|
|
901
|
+
locate_column = self._gui_layout[0][2] # row 0 tab 2
|
|
902
|
+
locate_column.clear()
|
|
903
|
+
message = f"Locate {npoints} points"
|
|
904
|
+
message += " (only first 100 shown):" if npoints > 100 else ":"
|
|
905
|
+
self._logger.info(message)
|
|
906
|
+
|
|
907
|
+
for point in point_locations:
|
|
908
|
+
# Format and add to locate column
|
|
909
|
+
location_layout = self._layout_point_location(point)
|
|
910
|
+
locate_column.append(location_layout)
|
|
911
|
+
locate_column.append(pn.layout.Divider())
|
|
912
|
+
|
|
913
|
+
# Format and add to log
|
|
914
|
+
location_list = [f"{static_text.name}={static_text.value}" for static_text in point]
|
|
915
|
+
self._logger.info(", ".join(location_list))
|
|
915
916
|
|
|
916
917
|
###
|
|
917
918
|
### Callbacks for widgets which update plot inputs
|
|
@@ -29,8 +29,7 @@ class PsData:
|
|
|
29
29
|
if not ms:
|
|
30
30
|
raise RuntimeError("MS path not available for reading MeasurementSet")
|
|
31
31
|
|
|
32
|
-
# Open processing set from zarr
|
|
33
|
-
# Converts msv2 if ms path is not zarr
|
|
32
|
+
# Open processing set from zarr. Converts msv2 if ms path is not zarr
|
|
34
33
|
self._ps_xdt, self._zarr_path = get_processing_set(ms, logger)
|
|
35
34
|
|
|
36
35
|
self._logger = logger
|
|
@@ -120,6 +119,7 @@ class PsData:
|
|
|
120
119
|
def get_dimension_values(self, dimension):
|
|
121
120
|
''' Return sorted list of unique values for input dimension in selected ProcessingSet.
|
|
122
121
|
For 'time', returns datetime strings.
|
|
122
|
+
For spectrum datasets, 'antenna2' returns empty list.
|
|
123
123
|
'''
|
|
124
124
|
ps_xdt = self._get_ps_xdt()
|
|
125
125
|
dim_values = []
|
|
@@ -134,11 +134,18 @@ class PsData:
|
|
|
134
134
|
elif dimension == 'antenna2':
|
|
135
135
|
dimension = 'baseline_antenna2_name'
|
|
136
136
|
for ms_xdt in ps_xdt.values():
|
|
137
|
+
if dimension not in ms_xdt.coords:
|
|
138
|
+
if 'antenna1' in dimension and 'antenna_name' in ms_xdt.coords:
|
|
139
|
+
dimension = 'antenna_name' # spectrum dataset
|
|
140
|
+
else:
|
|
141
|
+
continue
|
|
137
142
|
try:
|
|
138
143
|
dim_values.extend([value.item() for value in ms_xdt[dimension].values])
|
|
139
144
|
except TypeError:
|
|
140
145
|
dim_values.append(ms_xdt[dimension].values.item())
|
|
141
146
|
|
|
147
|
+
if not dim_values:
|
|
148
|
+
return dim_values
|
|
142
149
|
return sorted(set(dim_values))
|
|
143
150
|
|
|
144
151
|
def _get_time_strings(self, ps):
|
|
@@ -152,13 +159,17 @@ class PsData:
|
|
|
152
159
|
return times
|
|
153
160
|
|
|
154
161
|
def _get_baselines(self, ps):
|
|
155
|
-
''' Return baseline strings as ant1_name & ant2_name
|
|
162
|
+
''' Return baseline strings as ant1_name & ant2_name.
|
|
163
|
+
For spectrum datasets, return list of antenna_name. '''
|
|
156
164
|
baselines = []
|
|
157
165
|
for ms_xdt in ps.values():
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
if 'antenna_name' in ms_xdt.coords:
|
|
167
|
+
baselines.extend(ms_xdt.antenna_name.values)
|
|
168
|
+
else:
|
|
169
|
+
ant1_names = ms_xdt.baseline_antenna1_name.values
|
|
170
|
+
ant2_names = ms_xdt.baseline_antenna2_name.values
|
|
171
|
+
for ant1, ant2 in zip(ant1_names, ant2_names):
|
|
172
|
+
baselines.append(f"{ant1} & {ant2}")
|
|
162
173
|
return baselines
|
|
163
174
|
|
|
164
175
|
def get_dimension_attrs(self, dim):
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Return x, y, and metadata for cursor position or multiple points in selected box.
|
|
3
|
+
Values are formatted into Panel StaticText and put in row/col format (cursor location)
|
|
4
|
+
or single line (box location).
|
|
5
|
+
'''
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from pandas import to_datetime
|
|
9
|
+
import panel as pn
|
|
10
|
+
|
|
11
|
+
from vidavis.plot.ms_plot._ms_plot_constants import TIME_FORMAT
|
|
12
|
+
|
|
13
|
+
def locate_point(xds, position, vis_axis):
|
|
14
|
+
'''
|
|
15
|
+
Get cursor location as values of coordinates and data vars.
|
|
16
|
+
xds (Xarray Dataset): data for plot
|
|
17
|
+
position (dict): {coordinate: value} of x and y axis positions
|
|
18
|
+
vis_axis (str): visibility component of complex value
|
|
19
|
+
Returns:
|
|
20
|
+
list of pn.widgets.StaticText(name, value) with value formatted for its type
|
|
21
|
+
'''
|
|
22
|
+
static_text_list = []
|
|
23
|
+
values, units = _get_point_location(xds, position, vis_axis)
|
|
24
|
+
|
|
25
|
+
# Rename baseline coordinate names to not confuse user for selection
|
|
26
|
+
baseline_names = {'baseline': 'baseline_id', 'baseline_name': 'baseline'}
|
|
27
|
+
for name, value in values.items():
|
|
28
|
+
name = baseline_names[name] if name in baseline_names else name
|
|
29
|
+
static_text = _get_location_text(name, value, units)
|
|
30
|
+
static_text_list.append(static_text)
|
|
31
|
+
return static_text_list
|
|
32
|
+
|
|
33
|
+
def locate_box(xds, bounds, vis_axis):
|
|
34
|
+
'''
|
|
35
|
+
Get location of each point in box bounds as values of coordinate and data vars.
|
|
36
|
+
xds (Xarray Dataset): data for plot
|
|
37
|
+
bounds (dict): {coordinate: (start, end)} of x and y axis ranges
|
|
38
|
+
vis_axis (str): visibility component of complex value
|
|
39
|
+
Returns:
|
|
40
|
+
list of list of pn.widgets.StaticText(name, value), one list per point.
|
|
41
|
+
'''
|
|
42
|
+
points = []
|
|
43
|
+
npoints = 0
|
|
44
|
+
|
|
45
|
+
if xds:
|
|
46
|
+
try:
|
|
47
|
+
selection = {}
|
|
48
|
+
for coord, val in bounds.items():
|
|
49
|
+
# Round index values to int for selection
|
|
50
|
+
selection[coord] = slice(_round_index_value(coord, val[0]), _round_index_value(coord, val[1]))
|
|
51
|
+
sel_xds = xds.sel(indexers=None, method=None, tolerance=None, drop=False, **selection)
|
|
52
|
+
|
|
53
|
+
x_coord, y_coord = bounds.keys()
|
|
54
|
+
npoints = sel_xds.sizes[x_coord] * sel_xds.sizes[y_coord]
|
|
55
|
+
counter = 0
|
|
56
|
+
|
|
57
|
+
for y in sel_xds[y_coord].values:
|
|
58
|
+
for x in sel_xds[x_coord].values:
|
|
59
|
+
position = {x_coord: x, y_coord: y}
|
|
60
|
+
points.append(locate_point(sel_xds, position, vis_axis))
|
|
61
|
+
counter += 1
|
|
62
|
+
if counter == 100:
|
|
63
|
+
break
|
|
64
|
+
if counter == 100:
|
|
65
|
+
break
|
|
66
|
+
except KeyError:
|
|
67
|
+
pass
|
|
68
|
+
return npoints, points
|
|
69
|
+
|
|
70
|
+
def _get_point_location(xds, position, vis_axis):
|
|
71
|
+
''' Select plot data xds with point x, y position, and return coord and data_var values describing the location.
|
|
72
|
+
xds (Xarray Dataset): data for plot
|
|
73
|
+
position (dict): {coordinate: value} of x and y axis positions
|
|
74
|
+
vis_axis (str): visibility component of complex value
|
|
75
|
+
Returns:
|
|
76
|
+
values (dict): {name: value} for each location item
|
|
77
|
+
units (dict): {name: unit} for each value which has a unit defined.
|
|
78
|
+
'''
|
|
79
|
+
values = position.copy()
|
|
80
|
+
units = {}
|
|
81
|
+
|
|
82
|
+
if xds:
|
|
83
|
+
try:
|
|
84
|
+
# Round index coordinates to int for selection
|
|
85
|
+
for coord, value in position.items():
|
|
86
|
+
position[coord] = _round_index_value(coord, value)
|
|
87
|
+
|
|
88
|
+
sel_xds = xds.sel(indexers=None, method='nearest', tolerance=None, drop=False, **position)
|
|
89
|
+
for coord in sel_xds.coords:
|
|
90
|
+
if coord == 'uvw_label' or ('baseline_antenna' in coord and 'baseline_name' in sel_xds.coords):
|
|
91
|
+
continue
|
|
92
|
+
val, unit = _get_xda_val_unit(sel_xds[coord])
|
|
93
|
+
values[coord] = val
|
|
94
|
+
units[coord] = unit
|
|
95
|
+
for data_var in sel_xds.data_vars:
|
|
96
|
+
if 'TIME_CENTROID' in data_var:
|
|
97
|
+
continue
|
|
98
|
+
val, unit = _get_xda_val_unit(sel_xds[data_var])
|
|
99
|
+
if data_var == 'UVW':
|
|
100
|
+
names = ['U', 'V', 'W']
|
|
101
|
+
for i, name in enumerate(names):
|
|
102
|
+
values[name] = val[i]
|
|
103
|
+
units[name] = unit[i]
|
|
104
|
+
else:
|
|
105
|
+
values[data_var] = val
|
|
106
|
+
units[data_var] = unit
|
|
107
|
+
except KeyError:
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
# Set complex component name for visibilities
|
|
111
|
+
if 'VISIBILITY' in values:
|
|
112
|
+
values[vis_axis.upper()] = values.pop('VISIBILITY')
|
|
113
|
+
return values, units
|
|
114
|
+
|
|
115
|
+
def _round_index_value(coord, value):
|
|
116
|
+
''' Round index coordinates to int for selecction '''
|
|
117
|
+
return round(value) if coord in ['baseline', 'antenna_name', 'polarization'] else value
|
|
118
|
+
|
|
119
|
+
def _get_xda_val_unit(xda):
|
|
120
|
+
''' Return value and unit of xda (selected so only one value) '''
|
|
121
|
+
# Value
|
|
122
|
+
value = xda.values
|
|
123
|
+
if isinstance(value, np.ndarray) and value.size == 1:
|
|
124
|
+
value = value.item()
|
|
125
|
+
|
|
126
|
+
# Unit
|
|
127
|
+
try:
|
|
128
|
+
unit = xda.attrs['units']
|
|
129
|
+
unit = unit[0] if (isinstance(unit, list) and len(unit) == 1) else unit
|
|
130
|
+
unit = '' if unit == 'unkown' else unit
|
|
131
|
+
except KeyError:
|
|
132
|
+
unit = ''
|
|
133
|
+
|
|
134
|
+
return value, unit
|
|
135
|
+
|
|
136
|
+
def _get_location_text(name, value, units):
|
|
137
|
+
''' Format value and unit (if any) and return Panel StaticText '''
|
|
138
|
+
if not isinstance(value, str):
|
|
139
|
+
# Format numeric and datetime values
|
|
140
|
+
if name == "FLAG":
|
|
141
|
+
value = "nan" if np.isnan(value) else int(value)
|
|
142
|
+
elif isinstance(value, float):
|
|
143
|
+
if np.isnan(value):
|
|
144
|
+
value = "nan"
|
|
145
|
+
elif value < 1e6:
|
|
146
|
+
value = f"{value:.4f}"
|
|
147
|
+
else:
|
|
148
|
+
value = f"{value:.4e}"
|
|
149
|
+
elif isinstance(value, np.datetime64):
|
|
150
|
+
value = to_datetime(np.datetime_as_string(value)).strftime(TIME_FORMAT)
|
|
151
|
+
units.pop(name) # no unit for datetime string
|
|
152
|
+
unit = units[name] if name in units else ""
|
|
153
|
+
return pn.widgets.StaticText(name=name, value=f"{value} {unit}")
|
vidavis/plot/ms_plot/_ms_plot.py
CHANGED
|
@@ -51,17 +51,21 @@ def create_raster_gui(callbacks, data_dims, x_axis, y_axis):
|
|
|
51
51
|
# Plot button and spinner while plotting
|
|
52
52
|
init_plot = plot_starter(callbacks['plot_updating'])
|
|
53
53
|
|
|
54
|
-
#
|
|
55
|
-
# PLOT WITH CURSOR POSITION
|
|
56
|
-
#
|
|
57
|
-
# Connect plot to filename and plot button; add
|
|
54
|
+
# ----------------------------------------
|
|
55
|
+
# PLOT WITH CURSOR POSITION AND BOX SELECT
|
|
56
|
+
# ----------------------------------------
|
|
57
|
+
# Connect plot to filename and plot button; add streams for cursor position and selected box
|
|
58
|
+
# 'update_plot' callback must have parameters (ms, do_plot, x, y, data)
|
|
58
59
|
dmap = hv.DynamicMap(
|
|
59
60
|
pn.bind(
|
|
60
61
|
callbacks['update_plot'],
|
|
61
62
|
ms=file_selectors[0][0],
|
|
62
63
|
do_plot=init_plot[0],
|
|
63
64
|
),
|
|
64
|
-
streams=[
|
|
65
|
+
streams=[
|
|
66
|
+
hv.streams.PointerXY(), # cursor location (x, y)
|
|
67
|
+
hv.streams.BoundsXY() # box location (bounds)
|
|
68
|
+
]
|
|
65
69
|
)
|
|
66
70
|
|
|
67
71
|
# ----------------------------------------------
|
|
@@ -75,7 +79,8 @@ def create_raster_gui(callbacks, data_dims, x_axis, y_axis):
|
|
|
75
79
|
pn.WidgetBox(), # [1] cursor location
|
|
76
80
|
)
|
|
77
81
|
),
|
|
78
|
-
('Plot Inputs', pn.Column()),
|
|
82
|
+
('Plot Inputs', pn.Column()), # Tabs [1]
|
|
83
|
+
('Locate Selected Box', pn.Column()), # Tabs [2]
|
|
79
84
|
sizing_mode='stretch_width',
|
|
80
85
|
),
|
|
81
86
|
pn.Spacer(width=10), # Row [1]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
vidavis/LICENSE.rst,sha256=qzGpkvhDzf_MgF1PIn6rCmYPrcEhkfrBUchosLJj-U4,26371
|
|
2
2
|
vidavis/__init__.py,sha256=nhvlqlYhl4NzFO2WPTYfhyZkNdpfkBUTUOgJWKCK4Tc,1681
|
|
3
3
|
vidavis/apps/__init__.py,sha256=ZQ5v1VFtjn3ztmuOHLOk5WbC1uLNIgL9rbHQ4v0zJwY,87
|
|
4
|
-
vidavis/apps/_ms_raster.py,sha256=
|
|
4
|
+
vidavis/apps/_ms_raster.py,sha256=lbq9GRGeyIV1a_kyS8nU0XfadMwQbOC7JZj6Zcp2uo8,47260
|
|
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
|
|
@@ -10,7 +10,7 @@ vidavis/data/measurement_set/_ms_data.py,sha256=7ATsPbGFAv8WtWk_PRxlfoLdqapd6eMv
|
|
|
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=bCRsYFJQTp65QEAk_wGZQ2rZNkn6E3Gg2gKKFQfV5mw,3499
|
|
12
12
|
vidavis/data/measurement_set/processing_set/_ps_coords.py,sha256=pdmojL-_55x3CjFcMIMuIqsI4ADG-TlLxc-RTgBQZC8,3640
|
|
13
|
-
vidavis/data/measurement_set/processing_set/_ps_data.py,sha256=
|
|
13
|
+
vidavis/data/measurement_set/processing_set/_ps_data.py,sha256=2Ss3iUdyIgtMuMTpl9mAGt4K89zgDmRsoTjbjurpDmc,11353
|
|
14
14
|
vidavis/data/measurement_set/processing_set/_ps_io.py,sha256=VeNi-s1hozgCAGAGHs4NUXtlVFwUh-mkqrY9iYWOfW4,1717
|
|
15
15
|
vidavis/data/measurement_set/processing_set/_ps_raster_data.py,sha256=6mU_V68lvJvIktEuyE5tWlyj7GaRmOnkCWSjHXc5lWs,7249
|
|
16
16
|
vidavis/data/measurement_set/processing_set/_ps_select.py,sha256=GhAPxZ5mwvmUhCqKETVkhecjbwfrtDAG7-ibM19GDg0,10831
|
|
@@ -18,11 +18,12 @@ 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/
|
|
21
|
+
vidavis/plot/ms_plot/_locate_points.py,sha256=ij8580kQmrTdsQ2CQ18txIFOMa1gipct1lJv0u5k7Yw,6101
|
|
22
|
+
vidavis/plot/ms_plot/_ms_plot.py,sha256=AZe4WbW5QCjX5hIir1W4H8Nd69NvxBQUiJhIHPCOTGE,12764
|
|
22
23
|
vidavis/plot/ms_plot/_ms_plot_constants.py,sha256=cX_TQhKJ3hJzPuRYmuRJxue1sjq82yl_ZN2_w6TshmI,930
|
|
23
24
|
vidavis/plot/ms_plot/_ms_plot_selectors.py,sha256=BZQwARvMPdk78n6Rh2tOaSc8GenZBrxHZb14oFD9gJM,10785
|
|
24
25
|
vidavis/plot/ms_plot/_raster_plot.py,sha256=lNa9i_eJ8F8Fc2zcHLRcaxKKOELk3x_QmXT__T76pg8,10999
|
|
25
|
-
vidavis/plot/ms_plot/_raster_plot_gui.py,sha256=
|
|
26
|
+
vidavis/plot/ms_plot/_raster_plot_gui.py,sha256=wpVFnnX9jL4G4b9OhCxgGpqrqUq4cvDLy4IIzYRMR18,3738
|
|
26
27
|
vidavis/plot/ms_plot/_raster_plot_inputs.py,sha256=vPjZPDuB26ENxwv4z7dUa_c5TqByXejScY6hqEnLFLU,4768
|
|
27
28
|
vidavis/plot/ms_plot/_time_ticks.py,sha256=j-DcPh7RfGE8iX2bPjLQDQPIbiAbmjiEWQnKmdMWA3I,1773
|
|
28
29
|
vidavis/plot/ms_plot/_xds_plot_axes.py,sha256=EeWvAbiKV33nEWdI8V3M0uwLTnycq4bFYBOyVWkxCu0,4429
|
|
@@ -30,8 +31,8 @@ vidavis/toolbox/__init__.py,sha256=jqFa-eziVz_frNnXxwjJFK36qNpz1H38s-VlpBcq-R8,1
|
|
|
30
31
|
vidavis/toolbox/_app_context.py,sha256=H7gtF8RrAH46FqDcMobv3KM1Osbnapgu6aTG-m3VCWA,3049
|
|
31
32
|
vidavis/toolbox/_logging.py,sha256=OEisrd8FM8VTNBMc7neLh9ekelf29ZILYB5pScebly0,2739
|
|
32
33
|
vidavis/toolbox/_static.py,sha256=HJLMtClppgOJXWAtV6Umn5EqN80u0oZiIouQ1JsB9PM,2346
|
|
33
|
-
vidavis/__version__.py,sha256=
|
|
34
|
-
vidavis-0.0.
|
|
35
|
-
vidavis-0.0.
|
|
36
|
-
vidavis-0.0.
|
|
37
|
-
vidavis-0.0.
|
|
34
|
+
vidavis/__version__.py,sha256=lZoBoCrPmDHiztV57wRru9CSJWCzBJ4qzAHBIoqWxZQ,21
|
|
35
|
+
vidavis-0.0.9.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
|
|
36
|
+
vidavis-0.0.9.dist-info/METADATA,sha256=KYi6LRLW_rmK7fD9OTP_TR349bNdJqD0bvPg8wP-k-o,2242
|
|
37
|
+
vidavis-0.0.9.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
|
|
38
|
+
vidavis-0.0.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|