vidavis 0.0.6__tar.gz → 0.0.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vidavis-0.0.6 → vidavis-0.0.8}/PKG-INFO +3 -3
- {vidavis-0.0.6 → vidavis-0.0.8}/pyproject.toml +1 -1
- {vidavis-0.0.6 → vidavis-0.0.8}/readme.rst +2 -2
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/apps/_ms_raster.py +139 -102
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_data.py +18 -7
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/plot/ms_plot/_ms_plot.py +21 -20
- vidavis-0.0.8/src/vidavis/plot/ms_plot/_raster_plot_gui.py +91 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/LICENSE +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/LICENSE.rst +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/__init__.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/apps/__init__.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/bokeh/__init__.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/bokeh/_palette.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/__init__.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/__init__.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/_ms_data.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/__init__.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_concat.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_coords.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_io.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_raster_data.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_select.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_stats.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_xds_data.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/plot/__init__.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/plot/ms_plot/__init__.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/plot/ms_plot/_ms_plot_constants.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/plot/ms_plot/_ms_plot_selectors.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/plot/ms_plot/_raster_plot.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/plot/ms_plot/_raster_plot_inputs.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/plot/ms_plot/_time_ticks.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/plot/ms_plot/_xds_plot_axes.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/toolbox/__init__.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/toolbox/_app_context.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/toolbox/_logging.py +0 -0
- {vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/toolbox/_static.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vidavis
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
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>
|
|
@@ -51,11 +51,11 @@ Install
|
|
|
51
51
|
MSv2 Conversion
|
|
52
52
|
^^^^^^^^^^^^^^^
|
|
53
53
|
|
|
54
|
-
To enable conversion from MSv2 to MSv4 with **python-casacore**
|
|
54
|
+
To enable conversion from MSv2 to MSv4 with **python-casacore** for Linux only:
|
|
55
55
|
|
|
56
56
|
- :code:`pip install "xradio[python-casacore]"`
|
|
57
57
|
|
|
58
|
-
On macOS it is required to pre-install
|
|
58
|
+
On macOS it is required to pre-install **python-casacore**:
|
|
59
59
|
|
|
60
60
|
- :code:`conda install -c conda-forge python-casacore`
|
|
61
61
|
|
|
@@ -42,11 +42,11 @@ Install
|
|
|
42
42
|
MSv2 Conversion
|
|
43
43
|
^^^^^^^^^^^^^^^
|
|
44
44
|
|
|
45
|
-
To enable conversion from MSv2 to MSv4 with **python-casacore**
|
|
45
|
+
To enable conversion from MSv2 to MSv4 with **python-casacore** for Linux only:
|
|
46
46
|
|
|
47
47
|
- :code:`pip install "xradio[python-casacore]"`
|
|
48
48
|
|
|
49
|
-
On macOS it is required to pre-install
|
|
49
|
+
On macOS it is required to pre-install **python-casacore**:
|
|
50
50
|
|
|
51
51
|
- :code:`conda install -c conda-forge python-casacore`
|
|
52
52
|
|
|
@@ -7,15 +7,15 @@ import time
|
|
|
7
7
|
from bokeh.models.formatters import NumeralTickFormatter
|
|
8
8
|
import holoviews as hv
|
|
9
9
|
import numpy as np
|
|
10
|
-
import panel as pn
|
|
11
10
|
from pandas import to_datetime
|
|
11
|
+
import panel as pn
|
|
12
12
|
|
|
13
13
|
from vidavis.bokeh._palette import available_palettes
|
|
14
|
+
from vidavis.data.measurement_set.processing_set._ps_coords import set_index_coordinates
|
|
14
15
|
from vidavis.plot.ms_plot._time_ticks import get_time_formatter
|
|
15
16
|
from vidavis.plot.ms_plot._ms_plot import MsPlot
|
|
16
|
-
from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, SPECTRUM_AXIS_OPTIONS, PS_SELECTION_OPTIONS, MS_SELECTION_OPTIONS
|
|
17
|
-
from vidavis.plot.ms_plot.
|
|
18
|
-
aggregation_selector, iteration_selector, selection_selector, plot_starter)
|
|
17
|
+
from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, SPECTRUM_AXIS_OPTIONS, PS_SELECTION_OPTIONS, MS_SELECTION_OPTIONS, TIME_FORMAT
|
|
18
|
+
from vidavis.plot.ms_plot._raster_plot_gui import create_raster_gui
|
|
19
19
|
from vidavis.plot.ms_plot._raster_plot_inputs import check_inputs
|
|
20
20
|
from vidavis.plot.ms_plot._raster_plot import RasterPlot
|
|
21
21
|
|
|
@@ -43,23 +43,21 @@ class MsRaster(MsPlot):
|
|
|
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
45
|
self._raster_plot = RasterPlot()
|
|
46
|
+
self._plot_data = None
|
|
46
47
|
|
|
47
48
|
# Calculations for color limits
|
|
48
49
|
self._spw_stats = {}
|
|
49
50
|
self._spw_color_limits = {}
|
|
50
51
|
|
|
51
52
|
if show_gui:
|
|
52
|
-
#
|
|
53
|
-
self._gui_layout = None
|
|
54
|
-
|
|
55
|
-
# Check if plot inputs changed and new plot is needed.
|
|
56
|
-
# GUI can change subparams which do not change plot
|
|
53
|
+
# For checking if plot inputs or cursor position changed and new plot or cursor location info is needed.
|
|
57
54
|
self._last_plot_inputs = None
|
|
58
55
|
self._last_style_inputs = None
|
|
59
|
-
|
|
60
|
-
self._last_gui_plot = None
|
|
56
|
+
self._last_cursor = None
|
|
61
57
|
|
|
62
58
|
# 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
|
|
63
61
|
# Empty plot when ms not set or plot fails
|
|
64
62
|
self._empty_plot = self._create_empty_plot()
|
|
65
63
|
|
|
@@ -165,25 +163,14 @@ class MsRaster(MsPlot):
|
|
|
165
163
|
x_axis (str): Plot x-axis. Default 'baseline' ('antenna_name' for spectrum data).
|
|
166
164
|
y_axis (str): Plot y-axis. Default 'time'.
|
|
167
165
|
vis_axis (str): Complex visibility component to plot (amp, phase, real, imag). Default 'amp'.
|
|
168
|
-
|
|
169
|
-
MeasurementSet selection:
|
|
170
|
-
'data_group': name for correlated data, flags, weights, and uvw. Default value 'base'.
|
|
171
|
-
Use data_groups() to get data group names.
|
|
172
|
-
Dimensions:
|
|
173
|
-
Visibility dimensions: 'baseline' 'time', 'frequency', 'polarization'
|
|
174
|
-
Spectrum dimensions: 'antenna_name', 'time', 'frequency', 'polarization'
|
|
175
|
-
Default value is index 0 (after user selection) for non-axis dimensions unless aggregated.
|
|
176
|
-
Use antennas() to get antenna names. Select 'baseline' as "<name1> & <name2>".
|
|
177
|
-
Use summary() to list frequencies and polarizations.
|
|
178
|
-
TODO: how to select time?
|
|
179
|
-
aggregator (str): reduction for rasterization. Default None.
|
|
166
|
+
aggregator (None, str): reduction for rasterization. Default None.
|
|
180
167
|
Options include 'max', 'mean', 'min', 'std', 'sum', 'var'.
|
|
181
|
-
agg_axis (str, list): which dimension to apply aggregator across. Default None.
|
|
168
|
+
agg_axis (None, str, list): which dimension to apply aggregator across. Default None.
|
|
182
169
|
Options include one or more dimensions.
|
|
183
170
|
If agg_axis is None and aggregator is set, aggregates over all non-axis dimensions.
|
|
184
171
|
If one agg_axis is selected, the non-agg dimension will be selected.
|
|
185
|
-
iter_axis (str): dimension over which to iterate values (using iter_range).
|
|
186
|
-
iter_range (tuple): (start, end) inclusive index values for iteration plots.
|
|
172
|
+
iter_axis (None, str): dimension over which to iterate values (using iter_range).
|
|
173
|
+
iter_range (None, tuple): (start, end) inclusive index values for iteration plots.
|
|
187
174
|
Default (0, 0) (first iteration only). Use (0, -1) for all iterations.
|
|
188
175
|
If subplots is a grid, the range is limited by the grid size.
|
|
189
176
|
If subplots is a single plot, all iteration plots in the range can be saved using export_range in save().
|
|
@@ -194,12 +181,12 @@ class MsRaster(MsPlot):
|
|
|
194
181
|
Options include None (use data limits), 'auto' (calculate limits for amplitude), and 'manual' (use range in color_range).
|
|
195
182
|
'auto' is equivalent to None if vis_axis is not 'amp'.
|
|
196
183
|
When subplots is set, the 'auto' or 'manual' range will be used for all plots.
|
|
197
|
-
color_range (tuple): (min, max) of colorbar to use if color_mode is 'manual'.
|
|
198
|
-
title (str): Plot title, default None (no title)
|
|
184
|
+
color_range (None, tuple): (min, max) of colorbar to use if color_mode is 'manual'.
|
|
185
|
+
title (None, str): Plot title, default None (no title)
|
|
199
186
|
Set title='ms' to generate title from ms name and iter_axis value, if any.
|
|
200
187
|
clear_plots (bool): whether to clear list of plots. Default True.
|
|
201
188
|
|
|
202
|
-
If
|
|
189
|
+
If plot is successful, use show() or save() to view/save the plot.
|
|
203
190
|
'''
|
|
204
191
|
inputs = locals() # collect arguments into dict (not unused as pylint complains!)
|
|
205
192
|
if self._plot_inputs['selection']:
|
|
@@ -273,6 +260,7 @@ class MsRaster(MsPlot):
|
|
|
273
260
|
|
|
274
261
|
# Select vis_axis data to plot and update selection; returns xarray Dataset
|
|
275
262
|
raster_data = self._data.get_raster_data(plot_inputs)
|
|
263
|
+
self._plot_data = raster_data
|
|
276
264
|
|
|
277
265
|
# Add params needed for plot: auto color range and ms name
|
|
278
266
|
self._set_auto_color_range(plot_inputs) # set calculated limits if auto mode
|
|
@@ -426,85 +414,34 @@ class MsRaster(MsPlot):
|
|
|
426
414
|
### -----------------------------------------------------------------------
|
|
427
415
|
def _launch_gui(self):
|
|
428
416
|
''' Use Holoviz Panel to create a dashboard for plot inputs. '''
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
417
|
+
callbacks = {
|
|
418
|
+
'filename': self._set_filename,
|
|
419
|
+
'style': self._set_style_params,
|
|
420
|
+
'color': self._set_color_range,
|
|
421
|
+
'axes': self._set_axes,
|
|
422
|
+
'select_ps': self._set_ps_selection,
|
|
423
|
+
'select_ms': self._set_ms_selection,
|
|
424
|
+
'aggregation': self._set_aggregation,
|
|
425
|
+
'iter_values': self._set_iter_values,
|
|
426
|
+
'iteration': self._set_iteration,
|
|
427
|
+
'title': self._set_title,
|
|
428
|
+
'plot_updating': self._update_plot_spinner,
|
|
429
|
+
'update_plot': self._update_plot,
|
|
430
|
+
}
|
|
431
|
+
data_dims = self._ms_info['data_dims'] if 'data_dims' in self._ms_info else None
|
|
439
432
|
x_axis = self._plot_inputs['x_axis']
|
|
440
433
|
y_axis = self._plot_inputs['y_axis']
|
|
441
|
-
|
|
442
|
-
axis_selectors = axis_selector(x_axis, y_axis, data_dims, True, self._set_axes)
|
|
443
|
-
|
|
444
|
-
# Select from ProcessingSet and MeasurementSet
|
|
445
|
-
selection_selectors = selection_selector(self._set_ps_selection, self._set_ms_selection)
|
|
446
|
-
|
|
447
|
-
# Generic axis options, updated when ms is set
|
|
448
|
-
axis_options = data_dims if data_dims else []
|
|
449
|
-
|
|
450
|
-
# Select aggregator and axes to aggregate
|
|
451
|
-
agg_selectors = aggregation_selector(axis_options, self._set_aggregation)
|
|
452
|
-
|
|
453
|
-
# Select iter_axis and iter value or range
|
|
454
|
-
iter_selectors = iteration_selector(axis_options, self._set_iter_values, self._set_iteration)
|
|
455
|
-
|
|
456
|
-
# Put user input widgets in accordion with only one card active at a time
|
|
457
|
-
selectors = pn.Accordion(
|
|
458
|
-
("Select file", file_selectors), # [0]
|
|
459
|
-
("Plot style", style_selectors), # [1]
|
|
460
|
-
("Data Selection", selection_selectors), # [2]
|
|
461
|
-
("Plot axes", axis_selectors), # [3]
|
|
462
|
-
("Aggregation", agg_selectors), # [4]
|
|
463
|
-
("Iteration", iter_selectors), # [5]
|
|
464
|
-
("Plot title", title_input), # [6]
|
|
465
|
-
)
|
|
466
|
-
selectors.toggle = True
|
|
467
|
-
|
|
468
|
-
# Plot button and spinner while plotting
|
|
469
|
-
init_plot = plot_starter(self._update_plot_spinner)
|
|
470
|
-
|
|
471
|
-
# Connect plot to filename and selector widgets
|
|
472
|
-
dmap = hv.DynamicMap(
|
|
473
|
-
pn.bind(
|
|
474
|
-
self._update_plot,
|
|
475
|
-
ms=file_selectors[0][0],
|
|
476
|
-
do_plot=init_plot[0],
|
|
477
|
-
),
|
|
478
|
-
)
|
|
479
|
-
|
|
480
|
-
# Layout plot and input widgets in a row
|
|
481
|
-
self._gui_layout = pn.Row(
|
|
482
|
-
pn.Tabs( # [0]
|
|
483
|
-
('Plot', dmap), # [0]
|
|
484
|
-
('Plot Inputs', pn.Column()), # [1]
|
|
485
|
-
sizing_mode='stretch_width',
|
|
486
|
-
),
|
|
487
|
-
pn.Spacer(width=10), # [1]
|
|
488
|
-
pn.Column( # [2]
|
|
489
|
-
pn.Spacer(height=25), # [0]
|
|
490
|
-
selectors, # [1]
|
|
491
|
-
init_plot, # [2]
|
|
492
|
-
width_policy='min',
|
|
493
|
-
width=400,
|
|
494
|
-
sizing_mode='stretch_height',
|
|
495
|
-
),
|
|
496
|
-
sizing_mode='stretch_height',
|
|
497
|
-
)
|
|
498
|
-
|
|
434
|
+
self._gui_layout = create_raster_gui(callbacks, data_dims, x_axis, y_axis)
|
|
499
435
|
# Show gui
|
|
500
|
-
# print("gui layout:", self._gui_layout) # for debugging
|
|
501
436
|
self._gui_layout.show(title=self._app_name, threaded=True)
|
|
502
437
|
|
|
503
438
|
###
|
|
504
439
|
### Main callback to create plot if inputs changed
|
|
505
440
|
###
|
|
506
|
-
def _update_plot(self, ms, do_plot):
|
|
507
|
-
''' Create plot with inputs from GUI
|
|
441
|
+
def _update_plot(self, ms, do_plot, x, y):
|
|
442
|
+
''' Create plot with inputs from GUI, or update cursor (x, y) information.
|
|
443
|
+
Must return plot, even if empty plot or same plot as before, for DynamicMap.
|
|
444
|
+
'''
|
|
508
445
|
if self._toast:
|
|
509
446
|
self._toast.destroy()
|
|
510
447
|
|
|
@@ -513,12 +450,18 @@ class MsRaster(MsPlot):
|
|
|
513
450
|
# Launched GUI with no MS
|
|
514
451
|
return self._empty_plot
|
|
515
452
|
|
|
516
|
-
# If not first plot, user has to click Plot button (do_plot=True).
|
|
453
|
+
# If not first plot, user has to click Plot button (do_plot=True) to update plot.
|
|
454
|
+
# Respond to pointer callback only.
|
|
517
455
|
first_plot = not self._last_gui_plot
|
|
518
456
|
if not do_plot and not first_plot:
|
|
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
|
+
self._update_cursor_location(x, y)
|
|
460
|
+
self._last_cursor = (x, y)
|
|
519
461
|
# Not ready to update plot yet, return last plot.
|
|
520
462
|
return self._last_gui_plot
|
|
521
463
|
|
|
464
|
+
# Callback is for initial plot for input ms, or user clicked Plot button
|
|
522
465
|
if (self._set_ms(ms) or first_plot) and self._data and self._data.is_valid():
|
|
523
466
|
# New MS set and is valid
|
|
524
467
|
self._update_gui_axis_options()
|
|
@@ -814,7 +757,6 @@ class MsRaster(MsPlot):
|
|
|
814
757
|
#filename_input.width = len(filename[-1])
|
|
815
758
|
filename_input.value = filename[-1]
|
|
816
759
|
|
|
817
|
-
|
|
818
760
|
def _set_iter_values(self, iter_axis):
|
|
819
761
|
''' Set up player with values when iter_axis is selected '''
|
|
820
762
|
iter_axis = None if iter_axis == 'None' else iter_axis
|
|
@@ -879,6 +821,101 @@ class MsRaster(MsPlot):
|
|
|
879
821
|
for param in self._plot_params:
|
|
880
822
|
inputs_column.append(pn.pane.Str(param))
|
|
881
823
|
|
|
824
|
+
def _update_cursor_location(self, x, y):
|
|
825
|
+
''' Show metadata for cursor x,y position '''
|
|
826
|
+
# Convert plot values to selection values to select plot data
|
|
827
|
+
x_axis = self._plot_inputs['x_axis']
|
|
828
|
+
y_axis = self._plot_inputs['y_axis']
|
|
829
|
+
x = round(x) if x_axis == 'baseline' else x
|
|
830
|
+
y = round(y) if y_axis == 'baseline' else y
|
|
831
|
+
position = {x_axis: x, y_axis: y}
|
|
832
|
+
location_values, units = self._get_cursor_location_values(position)
|
|
833
|
+
self._update_cursor_box(location_values, units)
|
|
834
|
+
|
|
835
|
+
def _get_cursor_location_values(self, cursor_position):
|
|
836
|
+
values = cursor_position.copy()
|
|
837
|
+
units = {}
|
|
838
|
+
|
|
839
|
+
if self._plot_data:
|
|
840
|
+
try:
|
|
841
|
+
xds = set_index_coordinates(self._plot_data, tuple(cursor_position.keys()))
|
|
842
|
+
sel_xds = xds.sel(indexers=None, method='nearest', tolerance=None, drop=False, **cursor_position)
|
|
843
|
+
for coord in sel_xds.coords:
|
|
844
|
+
if coord == 'uvw_label' or ('baseline_antenna' in coord and 'baseline_name' in sel_xds.coords):
|
|
845
|
+
continue
|
|
846
|
+
val, unit = self._get_xda_val_unit(sel_xds[coord])
|
|
847
|
+
values[coord] = val
|
|
848
|
+
units[coord] = unit
|
|
849
|
+
for data_var in sel_xds.data_vars:
|
|
850
|
+
if 'TIME_CENTROID' in data_var:
|
|
851
|
+
continue
|
|
852
|
+
val, unit = self._get_xda_val_unit(sel_xds[data_var])
|
|
853
|
+
if data_var == 'UVW':
|
|
854
|
+
names = ['U', 'V', 'W']
|
|
855
|
+
for i, name in enumerate(names):
|
|
856
|
+
values[name] = val[i]
|
|
857
|
+
units[name] = unit[i]
|
|
858
|
+
else:
|
|
859
|
+
values[data_var] = val
|
|
860
|
+
units[data_var] = unit
|
|
861
|
+
except KeyError:
|
|
862
|
+
pass
|
|
863
|
+
|
|
864
|
+
# Set complex component name for visibilities
|
|
865
|
+
if 'VISIBILITY' in values:
|
|
866
|
+
values[self._plot_inputs['vis_axis'].upper()] = values.pop('VISIBILITY')
|
|
867
|
+
return values, units
|
|
868
|
+
|
|
869
|
+
def _update_cursor_box(self, location_values, units):
|
|
870
|
+
''' Update cursor location widget box with info in dict '''
|
|
871
|
+
cursor_location_box = self._gui_layout[0][0][1]
|
|
872
|
+
cursor_location_box.clear() # pn.WidgetBox
|
|
873
|
+
location_layout = pn.Column(pn.widgets.StaticText(name="Cursor Location"))
|
|
874
|
+
info_row = pn.Row()
|
|
875
|
+
info_col = pn.Column()
|
|
876
|
+
|
|
877
|
+
for name, value in location_values.items():
|
|
878
|
+
# 4 entries per column; append to row and start new column
|
|
879
|
+
if len(info_col.objects) == 4:
|
|
880
|
+
info_row.append(info_col)
|
|
881
|
+
info_col = pn.Column()
|
|
882
|
+
|
|
883
|
+
if not isinstance(value, str):
|
|
884
|
+
if name == "FLAG":
|
|
885
|
+
value = "nan" if np.isnan(value) else int(value)
|
|
886
|
+
elif isinstance(value, float):
|
|
887
|
+
if np.isnan(value):
|
|
888
|
+
value = "nan"
|
|
889
|
+
elif value < 1e6:
|
|
890
|
+
value = f"{value:.4f}"
|
|
891
|
+
else:
|
|
892
|
+
value = f"{value:.4e}"
|
|
893
|
+
elif isinstance(value, np.datetime64):
|
|
894
|
+
value = to_datetime(np.datetime_as_string(value)).strftime(TIME_FORMAT)
|
|
895
|
+
units.pop(name) # no unit for datetime string
|
|
896
|
+
unit = units[name] if name in units else ""
|
|
897
|
+
info = pn.widgets.StaticText(name=name, value=f"{value} {unit}")
|
|
898
|
+
info.margin = (0, 10) # default (5, 10)
|
|
899
|
+
info_col.append(info)
|
|
900
|
+
info_row.append(info_col)
|
|
901
|
+
location_layout.append(info_row)
|
|
902
|
+
cursor_location_box.append(location_layout)
|
|
903
|
+
|
|
904
|
+
def _get_xda_val_unit(self, xda):
|
|
905
|
+
''' Get value and unit as str not list '''
|
|
906
|
+
# Value
|
|
907
|
+
val = xda.values
|
|
908
|
+
if isinstance(val, np.ndarray) and val.size == 1:
|
|
909
|
+
val = val.item()
|
|
910
|
+
# Unit
|
|
911
|
+
try:
|
|
912
|
+
unit = xda.attrs['units']
|
|
913
|
+
unit = unit[0] if (isinstance(unit, list) and len(unit) == 1) else unit
|
|
914
|
+
unit = '' if unit == 'unkown' else unit
|
|
915
|
+
except KeyError:
|
|
916
|
+
unit = ''
|
|
917
|
+
return val, unit
|
|
918
|
+
|
|
882
919
|
###
|
|
883
920
|
### Callbacks for widgets which update plot inputs
|
|
884
921
|
###
|
|
@@ -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):
|
|
@@ -59,6 +59,9 @@ class MsPlot:
|
|
|
59
59
|
self._plots_locked = False
|
|
60
60
|
self._plots = []
|
|
61
61
|
|
|
62
|
+
# Initialize gui
|
|
63
|
+
self._gui_layout = None
|
|
64
|
+
|
|
62
65
|
# Set data (if ms)
|
|
63
66
|
self._data = None
|
|
64
67
|
self._ms_info = {}
|
|
@@ -144,7 +147,7 @@ class MsPlot:
|
|
|
144
147
|
# Single plot or combine plots into layout using subplots (rows, columns)
|
|
145
148
|
layout_plot = self._layout_plots(self._plot_inputs['subplots'])
|
|
146
149
|
|
|
147
|
-
# Render plot as Bokeh Figure or GridPlot
|
|
150
|
+
# Render plot as Bokeh Figure or GridPlot so can show() in script without tying up thread
|
|
148
151
|
bokeh_fig = hv.render(layout_plot)
|
|
149
152
|
|
|
150
153
|
self._plots_locked = False
|
|
@@ -246,30 +249,28 @@ class MsPlot:
|
|
|
246
249
|
|
|
247
250
|
def _set_ms(self, ms_path):
|
|
248
251
|
''' Set MsData and update ms info for input ms filepath (MSv2 or zarr), if set.
|
|
249
|
-
Return whether ms changed (false if
|
|
252
|
+
Return whether ms changed (false if ms_path is None, not set yet), even if error. '''
|
|
250
253
|
self._ms_info['ms'] = ms_path
|
|
251
254
|
ms_error = ""
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if ms_changed:
|
|
255
|
-
try:
|
|
256
|
-
# Set new MS data
|
|
257
|
-
self._data = MsData(ms_path, self._logger)
|
|
258
|
-
data_path = self._data.get_path()
|
|
259
|
-
self._ms_info['ms'] = data_path
|
|
260
|
-
root, ext = os.path.splitext(os.path.basename(data_path))
|
|
261
|
-
while ext != '':
|
|
262
|
-
root, ext = os.path.splitext(root)
|
|
263
|
-
self._ms_info['basename'] = root
|
|
264
|
-
self._ms_info['data_dims'] = self._data.get_data_dimensions()
|
|
265
|
-
except RuntimeError as e:
|
|
266
|
-
ms_error = str(e)
|
|
267
|
-
self._data = None
|
|
255
|
+
if not ms_path or (self._data and self._data.is_ms_path(ms_path)):
|
|
256
|
+
return False
|
|
268
257
|
|
|
258
|
+
try:
|
|
259
|
+
# Set new MS data
|
|
260
|
+
self._data = MsData(ms_path, self._logger)
|
|
261
|
+
data_path = self._data.get_path()
|
|
262
|
+
self._ms_info['ms'] = data_path
|
|
263
|
+
root, ext = os.path.splitext(os.path.basename(data_path))
|
|
264
|
+
while ext != '':
|
|
265
|
+
root, ext = os.path.splitext(root)
|
|
266
|
+
self._ms_info['basename'] = root
|
|
267
|
+
self._ms_info['data_dims'] = self._data.get_data_dimensions()
|
|
268
|
+
except RuntimeError as e:
|
|
269
|
+
ms_error = str(e)
|
|
270
|
+
self._data = None
|
|
269
271
|
if ms_error:
|
|
270
272
|
self._notify(ms_error, 'error', 0)
|
|
271
|
-
|
|
272
|
-
return ms_changed
|
|
273
|
+
return True
|
|
273
274
|
|
|
274
275
|
def _notify(self, message, level, duration=3000):
|
|
275
276
|
''' Log message. If show_gui, notify user with toast for duration in ms.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Create interactive GUI for ms raster plotting
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
import holoviews as hv
|
|
6
|
+
import panel as pn
|
|
7
|
+
from vidavis.plot.ms_plot._ms_plot_selectors import (file_selector, title_selector, style_selector,
|
|
8
|
+
axis_selector, aggregation_selector, iteration_selector, selection_selector, plot_starter)
|
|
9
|
+
|
|
10
|
+
def create_raster_gui(callbacks, data_dims, x_axis, y_axis):
|
|
11
|
+
''' Use Holoviz Panel to create a dashboard for plot inputs and raster plot display. '''
|
|
12
|
+
# ------------------
|
|
13
|
+
# PLOT INPUTS COLUMN
|
|
14
|
+
# ------------------
|
|
15
|
+
# Select MS
|
|
16
|
+
file_selectors = file_selector('Path to MeasurementSet (ms or zarr) for plot', '~' , callbacks['filename'])
|
|
17
|
+
|
|
18
|
+
# Select style - colormaps, colorbar, color limits
|
|
19
|
+
style_selectors = style_selector(callbacks['style'], callbacks['color'])
|
|
20
|
+
|
|
21
|
+
# Select x, y, and vis axis
|
|
22
|
+
axis_selectors = axis_selector(x_axis, y_axis, data_dims, True, callbacks['axes'])
|
|
23
|
+
|
|
24
|
+
# Select from ProcessingSet and MeasurementSet
|
|
25
|
+
selection_selectors = selection_selector(callbacks['select_ps'], callbacks['select_ms'])
|
|
26
|
+
|
|
27
|
+
# Generic axis options, updated when ms is set
|
|
28
|
+
axis_options = data_dims if data_dims else []
|
|
29
|
+
|
|
30
|
+
# Select aggregator and axes to aggregate
|
|
31
|
+
agg_selectors = aggregation_selector(axis_options, callbacks['aggregation'])
|
|
32
|
+
|
|
33
|
+
# Select iter_axis and iter value or range
|
|
34
|
+
iter_selectors = iteration_selector(axis_options, callbacks['iter_values'], callbacks['iteration'])
|
|
35
|
+
|
|
36
|
+
# Set title
|
|
37
|
+
title_input = title_selector(callbacks['title'])
|
|
38
|
+
|
|
39
|
+
# Put user input widgets in accordion with only one card active at a time (toggle)
|
|
40
|
+
selectors = pn.Accordion(
|
|
41
|
+
("Select file", file_selectors), # [0]
|
|
42
|
+
("Plot style", style_selectors), # [1]
|
|
43
|
+
("Data Selection", selection_selectors), # [2]
|
|
44
|
+
("Plot axes", axis_selectors), # [3]
|
|
45
|
+
("Aggregation", agg_selectors), # [4]
|
|
46
|
+
("Iteration", iter_selectors), # [5]
|
|
47
|
+
("Plot title", title_input), # [6]
|
|
48
|
+
)
|
|
49
|
+
selectors.toggle = True
|
|
50
|
+
|
|
51
|
+
# Plot button and spinner while plotting
|
|
52
|
+
init_plot = plot_starter(callbacks['plot_updating'])
|
|
53
|
+
|
|
54
|
+
# -------------------------
|
|
55
|
+
# PLOT WITH CURSOR POSITION
|
|
56
|
+
# -------------------------
|
|
57
|
+
# Connect plot to filename and plot button; add pointer stream for cursor info
|
|
58
|
+
dmap = hv.DynamicMap(
|
|
59
|
+
pn.bind(
|
|
60
|
+
callbacks['update_plot'],
|
|
61
|
+
ms=file_selectors[0][0],
|
|
62
|
+
do_plot=init_plot[0],
|
|
63
|
+
),
|
|
64
|
+
streams=[hv.streams.PointerXY()] # for cursor location
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# ----------------------------------------------
|
|
68
|
+
# GUI LAYOUT OF PLOT TABS AND PLOT INPUTS COLUMN
|
|
69
|
+
# ----------------------------------------------
|
|
70
|
+
return pn.Row(
|
|
71
|
+
pn.Tabs( # Row [0]
|
|
72
|
+
('Plot',
|
|
73
|
+
pn.Column( # Tabs [0]
|
|
74
|
+
dmap, # [0]
|
|
75
|
+
pn.WidgetBox(), # [1] cursor location
|
|
76
|
+
)
|
|
77
|
+
),
|
|
78
|
+
('Plot Inputs', pn.Column()), # Tabs [1]
|
|
79
|
+
sizing_mode='stretch_width',
|
|
80
|
+
),
|
|
81
|
+
pn.Spacer(width=10), # Row [1]
|
|
82
|
+
pn.Column( # Row [2]
|
|
83
|
+
pn.Spacer(height=25), # Column [0]
|
|
84
|
+
selectors, # Column [1]
|
|
85
|
+
init_plot, # Column [2]
|
|
86
|
+
width_policy='min',
|
|
87
|
+
width=400,
|
|
88
|
+
sizing_mode='stretch_height',
|
|
89
|
+
),
|
|
90
|
+
sizing_mode='stretch_height',
|
|
91
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_concat.py
RENAMED
|
File without changes
|
{vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_coords.py
RENAMED
|
File without changes
|
|
File without changes
|
{vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_raster_data.py
RENAMED
|
File without changes
|
{vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_select.py
RENAMED
|
File without changes
|
{vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_ps_stats.py
RENAMED
|
File without changes
|
{vidavis-0.0.6 → vidavis-0.0.8}/src/vidavis/data/measurement_set/processing_set/_xds_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|