vidavis 0.0.13__py3-none-any.whl → 0.0.14__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 +152 -271
- vidavis/plot/ms_plot/_locate_points.py +90 -8
- vidavis/plot/ms_plot/_ms_plot.py +174 -54
- vidavis/plot/ms_plot/_raster_plot_inputs.py +114 -0
- {vidavis-0.0.13.dist-info → vidavis-0.0.14.dist-info}/METADATA +1 -1
- {vidavis-0.0.13.dist-info → vidavis-0.0.14.dist-info}/RECORD +9 -8
- {vidavis-0.0.13.dist-info → vidavis-0.0.14.dist-info}/WHEEL +0 -0
- {vidavis-0.0.13.dist-info → vidavis-0.0.14.dist-info}/licenses/LICENSE +0 -0
vidavis/apps/_ms_raster.py
CHANGED
|
@@ -8,17 +8,15 @@ from bokeh.models.formatters import NumeralTickFormatter
|
|
|
8
8
|
import holoviews as hv
|
|
9
9
|
import numpy as np
|
|
10
10
|
from pandas import to_datetime
|
|
11
|
-
import panel as pn
|
|
12
11
|
|
|
13
12
|
from vidavis.bokeh._palette import available_palettes
|
|
14
13
|
from vidavis.data.measurement_set.processing_set._ps_coords import set_index_coordinates
|
|
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
14
|
from vidavis.plot.ms_plot._ms_plot import MsPlot
|
|
18
15
|
from vidavis.plot.ms_plot._ms_plot_constants import VIS_AXIS_OPTIONS, SPECTRUM_AXIS_OPTIONS, PS_SELECTION_OPTIONS, MS_SELECTION_OPTIONS
|
|
19
16
|
from vidavis.plot.ms_plot._plot_inputs import inputs_changed
|
|
20
|
-
from vidavis.plot.ms_plot._raster_plot_gui import create_raster_gui
|
|
21
17
|
from vidavis.plot.ms_plot._raster_plot import RasterPlot
|
|
18
|
+
from vidavis.plot.ms_plot._raster_plot_gui import create_raster_gui
|
|
19
|
+
from vidavis.plot.ms_plot._raster_plot_inputs import RasterPlotInputs
|
|
22
20
|
from vidavis.plot.ms_plot._time_ticks import get_time_formatter
|
|
23
21
|
|
|
24
22
|
class MsRaster(MsPlot):
|
|
@@ -45,6 +43,8 @@ class MsRaster(MsPlot):
|
|
|
45
43
|
def __init__(self, ms=None, log_level="info", log_to_file=True, show_gui=False):
|
|
46
44
|
super().__init__(ms, log_level, log_to_file, show_gui, "MsRaster")
|
|
47
45
|
self._raster_plot = RasterPlot()
|
|
46
|
+
self._plot_inputs = RasterPlotInputs()
|
|
47
|
+
self._plot_inputs.set_input('ms', self._ms_info['ms'])
|
|
48
48
|
|
|
49
49
|
# Calculations for color limits
|
|
50
50
|
self._spw_stats = {}
|
|
@@ -99,12 +99,10 @@ class MsRaster(MsPlot):
|
|
|
99
99
|
For explanation and examples, see:
|
|
100
100
|
https://xradio.readthedocs.io/en/latest/measurement_set/schema_and_api/measurement_set_api.html#xradio.measurement_set.ProcessingSetXdt.query
|
|
101
101
|
'''
|
|
102
|
-
if self.
|
|
102
|
+
if self._ms_data and self._ms_data.is_valid():
|
|
103
103
|
try:
|
|
104
|
-
self._plot_inputs
|
|
105
|
-
|
|
106
|
-
self._plot_inputs['data_group'] = kwargs['data_group_name']
|
|
107
|
-
self._data.select_ps(query=query, string_exact_match=string_exact_match, **kwargs)
|
|
104
|
+
self._plot_inputs.set_selection(kwargs)
|
|
105
|
+
self._ms_data.select_ps(query=query, string_exact_match=string_exact_match, **kwargs)
|
|
108
106
|
except KeyError as ke:
|
|
109
107
|
error = "ProcessingSet selection yielded empty ProcessingSet."
|
|
110
108
|
if not self._show_gui:
|
|
@@ -128,12 +126,10 @@ class MsRaster(MsPlot):
|
|
|
128
126
|
For explanation of parameters and examples, see:
|
|
129
127
|
https://xradio.readthedocs.io/en/latest/measurement_set/schema_and_api/measurement_set_api.html#xradio.measurement_set.MeasurementSetXdt.sel
|
|
130
128
|
'''
|
|
131
|
-
if self.
|
|
129
|
+
if self._ms_data and self._ms_data.is_valid():
|
|
132
130
|
try:
|
|
133
|
-
self._plot_inputs
|
|
134
|
-
self.
|
|
135
|
-
if 'data_group_name' in indexers_kwargs:
|
|
136
|
-
self._plot_inputs['data_group'] |= indexers_kwargs['data_group_name']
|
|
131
|
+
self._plot_inputs.set_selection(indexers_kwargs)
|
|
132
|
+
self._ms_data.select_ms(indexers=indexers, method=method, tolerance=tolerance, drop=drop, **indexers_kwargs)
|
|
137
133
|
except KeyError as ke:
|
|
138
134
|
error = str(ke).strip("\'")
|
|
139
135
|
if not self._show_gui:
|
|
@@ -179,9 +175,7 @@ class MsRaster(MsPlot):
|
|
|
179
175
|
|
|
180
176
|
If plot is successful, use show() or save() to view/save the plot.
|
|
181
177
|
'''
|
|
182
|
-
inputs = locals() # collect arguments into dict
|
|
183
|
-
if self._plot_inputs['selection']:
|
|
184
|
-
self._logger.info("Create raster plot with selection: %s", self._plot_inputs['selection'])
|
|
178
|
+
inputs = locals() # collect arguments into dict
|
|
185
179
|
|
|
186
180
|
start = time.time()
|
|
187
181
|
|
|
@@ -190,23 +184,28 @@ class MsRaster(MsPlot):
|
|
|
190
184
|
|
|
191
185
|
# Get data dimensions if valid MS is set to check input axes
|
|
192
186
|
if 'data_dims' in self._ms_info:
|
|
193
|
-
|
|
187
|
+
data_dims = self._ms_info['data_dims'] if 'data_dims' in self._ms_info else None
|
|
188
|
+
inputs['data_dims'] = data_dims
|
|
194
189
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
190
|
+
self._plot_inputs.set_inputs(inputs)
|
|
191
|
+
selection = self._plot_inputs.get_input('selection')
|
|
192
|
+
if selection:
|
|
193
|
+
self._logger.info("Create raster plot with selection: %s", selection)
|
|
194
|
+
|
|
195
|
+
# If previous plot was shown, unlink from data streams
|
|
196
|
+
super().unlink_plot_streams()
|
|
198
197
|
|
|
199
198
|
if not self._show_gui:
|
|
200
199
|
# Cannot plot if no MS
|
|
201
|
-
if not self.
|
|
200
|
+
if not self._ms_data or not self._ms_data.is_valid():
|
|
202
201
|
raise RuntimeError("Cannot plot MS: input MS path is invalid or missing.")
|
|
203
202
|
|
|
204
203
|
# Create raster plot and add to plot list
|
|
205
204
|
try:
|
|
206
|
-
if self._plot_inputs
|
|
207
|
-
self._do_iter_plot(
|
|
205
|
+
if self._plot_inputs.get_input('iter_axis'):
|
|
206
|
+
self._do_iter_plot()
|
|
208
207
|
else:
|
|
209
|
-
plot = self._do_plot(
|
|
208
|
+
plot = self._do_plot()
|
|
210
209
|
self._plots.append(plot)
|
|
211
210
|
except RuntimeError as e:
|
|
212
211
|
error = f"Plot failed: {str(e)}"
|
|
@@ -239,160 +238,155 @@ class MsRaster(MsPlot):
|
|
|
239
238
|
filename = f"{self._ms_info['basename']}_raster.png"
|
|
240
239
|
super().save(filename, fmt, width, height)
|
|
241
240
|
|
|
242
|
-
def
|
|
243
|
-
''' Set inputs from latest plot() call '''
|
|
244
|
-
for key, val in plot_inputs.items():
|
|
245
|
-
self._plot_inputs[key] = val
|
|
246
|
-
|
|
247
|
-
def _do_plot(self, plot_inputs, is_gui_plot=False):
|
|
241
|
+
def _do_plot(self, is_gui_plot=False):
|
|
248
242
|
''' Create plot using plot inputs '''
|
|
249
243
|
if not self._plot_init:
|
|
250
|
-
self._init_plot(
|
|
244
|
+
self._init_plot()
|
|
251
245
|
|
|
252
246
|
# Select vis_axis data to plot and update selection; returns xarray Dataset
|
|
253
|
-
raster_data = self.
|
|
247
|
+
raster_data = self._ms_data.get_raster_data(self._plot_inputs.get_inputs())
|
|
254
248
|
|
|
255
249
|
# Save plot data for plot location callbacks unless layout (location not supported)
|
|
256
|
-
if not self.
|
|
250
|
+
if not self._plot_inputs.is_layout():
|
|
251
|
+
x_axis = self._plot_inputs.get_input('x_axis')
|
|
252
|
+
y_axis = self._plot_inputs.get_input('y_axis')
|
|
257
253
|
if is_gui_plot:
|
|
258
|
-
self._gui_plot_data = set_index_coordinates(raster_data, (
|
|
254
|
+
self._gui_plot_data = set_index_coordinates(raster_data, (x_axis, y_axis))
|
|
259
255
|
else:
|
|
260
|
-
self._plot_data = set_index_coordinates(raster_data, (
|
|
256
|
+
self._plot_data = set_index_coordinates(raster_data, (x_axis, y_axis))
|
|
261
257
|
|
|
262
258
|
# Add params needed for plot: auto color range and ms name
|
|
263
|
-
self._set_auto_color_range(
|
|
259
|
+
self._set_auto_color_range() # set calculated limits if auto mode
|
|
264
260
|
ms_name = self._ms_info['basename'] # for title
|
|
261
|
+
plot_inputs = self._plot_inputs.get_inputs()
|
|
265
262
|
self._raster_plot.set_plot_params(raster_data, plot_inputs, ms_name)
|
|
266
263
|
|
|
267
264
|
# Show plot inputs in log
|
|
268
265
|
super()._set_plot_params(plot_inputs | self._raster_plot.get_plot_params()['style'])
|
|
269
|
-
|
|
266
|
+
plot_params = [f"{key}={value}" for key, value in self._plot_params.items()]
|
|
267
|
+
self._logger.info("MsRaster plot parameters: %s", ", ".join(plot_params))
|
|
270
268
|
|
|
271
269
|
# Make plot. Add data min/max if GUI is shown to update color limits range.
|
|
272
270
|
return self._raster_plot.raster_plot(raster_data, self._logger, self._show_gui)
|
|
273
271
|
|
|
274
|
-
def _do_iter_plot(self
|
|
272
|
+
def _do_iter_plot(self):
|
|
275
273
|
''' Create one plot per iteration value in iter_range which fits into subplots '''
|
|
276
274
|
# Default (0, 0) (first iteration only). Use (0, -1) for all iterations.
|
|
277
275
|
# If subplots is a grid, end iteration index is limited by the grid size.
|
|
278
276
|
# If subplots is a single plot, all iteration plots in the range can be saved using export_range in save().
|
|
279
|
-
iter_axis =
|
|
280
|
-
iter_range =
|
|
281
|
-
subplots =
|
|
282
|
-
|
|
283
|
-
iter_range = (0, 0) if iter_range is None else iter_range
|
|
284
|
-
start_idx, end_idx = iter_range
|
|
277
|
+
iter_axis = self._plot_inputs.get_input('iter_axis')
|
|
278
|
+
iter_range = self._plot_inputs.get_input('iter_range')
|
|
279
|
+
subplots = self._plot_inputs.get_input('subplots')
|
|
285
280
|
|
|
286
281
|
# Init plot before getting iter values
|
|
287
|
-
self._init_plot(
|
|
288
|
-
|
|
289
|
-
iter_values = self._data.get_dimension_values(iter_axis)
|
|
282
|
+
self._init_plot()
|
|
283
|
+
iter_values = self._ms_data.get_dimension_values(iter_axis)
|
|
290
284
|
n_iter = len(iter_values)
|
|
291
285
|
|
|
286
|
+
iter_range = (0, 0) if iter_range is None else iter_range
|
|
287
|
+
start_idx, end_idx = iter_range
|
|
288
|
+
auto_range = end_idx == -1
|
|
289
|
+
|
|
292
290
|
if start_idx >= n_iter:
|
|
293
291
|
raise IndexError(f"iter_range start {start_idx} is greater than number of iterations {n_iter}")
|
|
294
292
|
end_idx = n_iter if (end_idx == -1 or end_idx >= n_iter) else end_idx + 1
|
|
295
293
|
num_iter_plots = end_idx - start_idx
|
|
296
294
|
|
|
297
|
-
# Plot the minimum of iter range or subplots number of plots
|
|
295
|
+
# Plot the minimum of iter range or subplots number of plots.
|
|
296
|
+
# If subplots is single plot, plot all for save()
|
|
298
297
|
num_subplots = np.prod(subplots) if subplots else 1
|
|
299
298
|
num_iter_plots = min(num_iter_plots, num_subplots) if num_subplots > 1 else num_iter_plots
|
|
300
299
|
end_idx = start_idx + num_iter_plots
|
|
301
|
-
self._plot_inputs['iter_range'] = (start_idx, end_idx)
|
|
302
300
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
301
|
+
if auto_range:
|
|
302
|
+
# For listing plot inputs
|
|
303
|
+
start_idx = start_idx.item() if isinstance(start_idx, np.int64) else start_idx
|
|
304
|
+
end_idx = end_idx.item() if isinstance(end_idx, np.int64) else end_idx
|
|
305
|
+
self._plot_inputs.set_input('auto_iter_range', (start_idx, end_idx - 1))
|
|
306
306
|
|
|
307
307
|
for i in range(start_idx, end_idx):
|
|
308
308
|
# Select iteration value and make plot
|
|
309
309
|
value = iter_values[i]
|
|
310
310
|
self._logger.info("Plot %s iteration index %s value %s", iter_axis, i, value)
|
|
311
|
-
|
|
311
|
+
self._plot_inputs.set_selection({iter_axis: value})
|
|
312
312
|
try:
|
|
313
|
-
plot = self._do_plot(
|
|
313
|
+
plot = self._do_plot()
|
|
314
314
|
self._plots.append(plot)
|
|
315
315
|
except RuntimeError as e:
|
|
316
316
|
self._logger.info("Iteration plot for value %s failed: %s", str(value), str(e))
|
|
317
317
|
continue
|
|
318
318
|
|
|
319
|
-
def _init_plot(self
|
|
319
|
+
def _init_plot(self):
|
|
320
320
|
''' Apply automatic selection '''
|
|
321
321
|
# Remove previous auto selections
|
|
322
322
|
for key in ['dim_selection', 'auto_spw']:
|
|
323
|
-
|
|
324
|
-
del self._plot_inputs[key]
|
|
325
|
-
except KeyError:
|
|
326
|
-
pass
|
|
323
|
+
self._plot_inputs.remove_input(key)
|
|
327
324
|
|
|
328
325
|
# Automatically select data group and spw name if not user-selected
|
|
329
326
|
auto_selection = {}
|
|
330
|
-
if 'data_group'
|
|
327
|
+
if not self._plot_inputs.get_input('data_group'):
|
|
331
328
|
auto_selection['data_group_name'] = 'base'
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
329
|
+
self._plot_inputs.set_input('data_group', 'base')
|
|
330
|
+
|
|
331
|
+
data_group = self._plot_inputs.get_input('data_group')
|
|
332
|
+
spw_selection = self._plot_inputs.get_selection('spw_name')
|
|
333
|
+
if not spw_selection:
|
|
334
|
+
first_spw = self._ms_data.get_first_spw(data_group)
|
|
335
335
|
auto_selection['spw_name'] = first_spw
|
|
336
|
-
|
|
336
|
+
self._plot_inputs.set_input('auto_spw', first_spw) # keep separate from user selection
|
|
337
337
|
|
|
338
338
|
if auto_selection:
|
|
339
339
|
# Do selection and save to plot inputs
|
|
340
340
|
self._logger.info("Automatic selection of data group and/or spw: %s", auto_selection)
|
|
341
|
-
self.
|
|
341
|
+
self._ms_data.select_ps(query=None, string_exact_match=True, **auto_selection)
|
|
342
342
|
|
|
343
343
|
# Print data info for spw selection
|
|
344
|
-
self._logger.info("Plotting %s msv4 datasets.", self.
|
|
345
|
-
self._logger.info("Maximum dimensions for selected spw: %s", self.
|
|
344
|
+
self._logger.info("Plotting %s msv4 datasets.", self._ms_data.get_num_ms())
|
|
345
|
+
self._logger.info("Maximum dimensions for selected spw: %s", self._ms_data.get_max_data_dims())
|
|
346
346
|
self._plot_init = True
|
|
347
347
|
|
|
348
|
-
def
|
|
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
|
-
|
|
364
|
-
def _set_auto_color_range(self, plot_inputs):
|
|
348
|
+
def _set_auto_color_range(self):
|
|
365
349
|
''' Calculate stats for color limits for non-gui amplitude plots. '''
|
|
366
|
-
color_mode =
|
|
367
|
-
|
|
350
|
+
color_mode = self._plot_inputs.get_input('color_mode')
|
|
351
|
+
auto_color_limits = None
|
|
368
352
|
|
|
369
353
|
if color_mode == 'auto':
|
|
370
|
-
if
|
|
354
|
+
if self._plot_inputs.get_input('vis_axis') == 'amp' and not self._plot_inputs.get_input('aggregator'):
|
|
371
355
|
# For amplitude, limit colorbar range using stored per-spw ms stats
|
|
372
|
-
spw_name = self._plot_inputs
|
|
356
|
+
spw_name = self._plot_inputs.get_selection('spw_name')
|
|
357
|
+
if not spw_name:
|
|
358
|
+
spw_name = self._plot_inputs.get_input('auto_spw')
|
|
359
|
+
|
|
373
360
|
if spw_name in self._spw_color_limits:
|
|
374
|
-
|
|
361
|
+
auto_color_limits = self._spw_color_limits[spw_name]
|
|
375
362
|
else:
|
|
376
363
|
# Select spw name and data group only, no dimensions
|
|
377
|
-
data_group = self._plot_inputs
|
|
364
|
+
data_group = self._plot_inputs.get_input('data_group')
|
|
378
365
|
spw_data_selection = {'spw_name': spw_name, 'data_group_name': data_group}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
366
|
+
auto_color_limits = self._calc_amp_color_limits(spw_data_selection)
|
|
367
|
+
|
|
368
|
+
if auto_color_limits:
|
|
369
|
+
# Convert to float for listing plot inputs
|
|
370
|
+
start, end = auto_color_limits
|
|
371
|
+
start = start.item() if isinstance(start, np.float64) else start
|
|
372
|
+
end = end.item() if isinstance(end, np.float64) else end
|
|
373
|
+
auto_color_limits = (start, end)
|
|
374
|
+
self._spw_color_limits[spw_name] = auto_color_limits
|
|
375
|
+
self._plot_inputs.set_input('auto_color_range', auto_color_limits)
|
|
376
|
+
|
|
377
|
+
if auto_color_limits:
|
|
378
|
+
self._logger.info("Setting amplitude color range: (%.4f, %.4f).", auto_color_limits[0], auto_color_limits[1])
|
|
385
379
|
elif color_mode is None:
|
|
386
380
|
self._logger.info("Autoscale color range")
|
|
387
381
|
else:
|
|
388
|
-
self._logger.info("Using manual color range: %s",
|
|
382
|
+
self._logger.info("Using manual color range: %s", self._plot_inputs.get_input('color_range'))
|
|
389
383
|
|
|
390
384
|
def _calc_amp_color_limits(self, selection):
|
|
391
385
|
# Calculate colorbar limits from amplitude stats for unflagged data in selected spw
|
|
392
386
|
self._logger.info("Calculating stats for colorbar limits.")
|
|
393
387
|
start = time.time()
|
|
394
388
|
|
|
395
|
-
ms_stats = self.
|
|
389
|
+
ms_stats = self._ms_data.get_vis_stats(selection, 'amp')
|
|
396
390
|
self._spw_stats['spw_name'] = ms_stats
|
|
397
391
|
if not ms_stats:
|
|
398
392
|
return None # autoscale
|
|
@@ -417,7 +411,7 @@ class MsRaster(MsPlot):
|
|
|
417
411
|
if clear_plots:
|
|
418
412
|
super().clear_plots()
|
|
419
413
|
|
|
420
|
-
# Clear params
|
|
414
|
+
# Clear params for last plot
|
|
421
415
|
self._raster_plot.reset_plot_params()
|
|
422
416
|
|
|
423
417
|
# Unitialize plot to redo auto selections if needed
|
|
@@ -443,8 +437,8 @@ class MsRaster(MsPlot):
|
|
|
443
437
|
'update_plot': self._update_plot,
|
|
444
438
|
}
|
|
445
439
|
data_dims = self._ms_info['data_dims'] if 'data_dims' in self._ms_info else None
|
|
446
|
-
x_axis = self._plot_inputs
|
|
447
|
-
y_axis = self._plot_inputs
|
|
440
|
+
x_axis = self._plot_inputs.get_input('x_axis')
|
|
441
|
+
y_axis = self._plot_inputs.get_input('y_axis')
|
|
448
442
|
self._gui_layout = create_raster_gui(callbacks, data_dims, x_axis, y_axis)
|
|
449
443
|
self._gui_layout.show(title=self._app_name, threaded=True)
|
|
450
444
|
|
|
@@ -473,31 +467,20 @@ class MsRaster(MsPlot):
|
|
|
473
467
|
|
|
474
468
|
# User changed inputs without clicking Plot button, or callback for cursor/box.
|
|
475
469
|
if not do_plot and not self._first_gui_plot:
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
|
488
|
-
self._update_box_location(bounds)
|
|
489
|
-
self._last_box = bounds
|
|
490
|
-
|
|
470
|
+
# Locate callbacks
|
|
471
|
+
super()._locate_cursor(x, y, self._gui_plot_data, self._gui_layout[0])
|
|
472
|
+
super()._locate_points(data, self._gui_plot_data, self._gui_layout[0])
|
|
473
|
+
super()._locate_box(bounds, self._gui_plot_data, self._gui_layout[0])
|
|
491
474
|
# Not ready to update plot yet, return last plot.
|
|
492
|
-
return self.
|
|
475
|
+
return self._last_plot
|
|
493
476
|
|
|
494
477
|
# First plot for input ms, or user clicked Plot button for new inputs
|
|
495
|
-
if (self._set_ms(ms) or self._first_gui_plot) and self.
|
|
478
|
+
if (self._set_ms(ms) or self._first_gui_plot) and self._ms_data and self._ms_data.is_valid():
|
|
496
479
|
# New MS set and is valid
|
|
497
480
|
self._update_gui_axis_options()
|
|
498
481
|
|
|
499
482
|
# Add ms path to detect change and make new plot
|
|
500
|
-
self._plot_inputs
|
|
483
|
+
self._plot_inputs.set_input('ms', ms)
|
|
501
484
|
|
|
502
485
|
# Do new plot or resend last plot
|
|
503
486
|
self._reset_plot()
|
|
@@ -506,12 +489,14 @@ class MsRaster(MsPlot):
|
|
|
506
489
|
|
|
507
490
|
# Make plot if first plot or changed plot
|
|
508
491
|
style_inputs = self._raster_plot.get_plot_params()['style']
|
|
509
|
-
|
|
492
|
+
plot_inputs = self._plot_inputs.get_inputs()
|
|
493
|
+
if inputs_changed(plot_inputs, self._last_plot_inputs) or inputs_changed(style_inputs, self._last_style_inputs):
|
|
510
494
|
try:
|
|
511
495
|
# Check inputs from GUI then plot
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
496
|
+
data_dims = self._ms_info['data_dims']
|
|
497
|
+
self._plot_inputs.set_input('data_dims', data_dims)
|
|
498
|
+
self._plot_inputs.check_inputs()
|
|
499
|
+
if 'ps_selection' in self._gui_selection or 'ms_selection' in self._gui_selection:
|
|
515
500
|
self._do_gui_selection()
|
|
516
501
|
gui_plot = self._do_gui_plot()
|
|
517
502
|
except (ValueError, TypeError, KeyError, RuntimeError) as e:
|
|
@@ -520,25 +505,17 @@ class MsRaster(MsPlot):
|
|
|
520
505
|
gui_plot = self._empty_plot
|
|
521
506
|
else:
|
|
522
507
|
# Subparam values changed but not applied to plot
|
|
523
|
-
gui_plot = self.
|
|
508
|
+
gui_plot = self._last_plot
|
|
524
509
|
|
|
525
510
|
# Update plot inputs for gui
|
|
526
|
-
self._set_plot_params(
|
|
511
|
+
self._set_plot_params(plot_inputs | style_inputs)
|
|
527
512
|
|
|
528
513
|
# Save inputs to see if changed next time
|
|
529
|
-
self._last_plot_inputs =
|
|
514
|
+
self._last_plot_inputs = plot_inputs.copy()
|
|
530
515
|
self._last_style_inputs = style_inputs.copy()
|
|
531
|
-
self._last_plot_inputs['ps_selection'] = self._plot_inputs['ps_selection'].copy()
|
|
532
|
-
self._last_plot_inputs['ms_selection'] = self._plot_inputs['ms_selection'].copy()
|
|
533
516
|
|
|
534
517
|
# Save plot for return value if plot update not requested
|
|
535
|
-
self.
|
|
536
|
-
|
|
537
|
-
# Remove selection not from user
|
|
538
|
-
if self._first_gui_plot and not do_plot:
|
|
539
|
-
self._plot_inputs['ps_selection'].clear()
|
|
540
|
-
self._plot_inputs['ms_selection'].clear()
|
|
541
|
-
|
|
518
|
+
self._last_plot = gui_plot
|
|
542
519
|
self._first_gui_plot = False
|
|
543
520
|
|
|
544
521
|
# Add plot inputs to GUI, change plot button to outline, and stop spinner
|
|
@@ -551,35 +528,34 @@ class MsRaster(MsPlot):
|
|
|
551
528
|
|
|
552
529
|
def _do_gui_selection(self):
|
|
553
530
|
''' Apply selections selected in GUI '''
|
|
554
|
-
if self.
|
|
555
|
-
self.select_ps(**self.
|
|
556
|
-
if self.
|
|
557
|
-
self.select_ms(**self.
|
|
531
|
+
if self._gui_selection['ps_selection']:
|
|
532
|
+
self.select_ps(**self._gui_selection['ps_selection'])
|
|
533
|
+
if self._gui_selection['ms_selection']:
|
|
534
|
+
self.select_ms(**self._gui_selection['ms_selection'])
|
|
558
535
|
|
|
559
536
|
###
|
|
560
537
|
### Create plot for DynamicMap
|
|
561
538
|
###
|
|
562
539
|
def _do_gui_plot(self):
|
|
563
540
|
''' Create plot based on gui plot inputs '''
|
|
564
|
-
if self.
|
|
541
|
+
if self._ms_data and self._ms_data.is_valid():
|
|
565
542
|
try:
|
|
566
|
-
if self._plot_inputs
|
|
543
|
+
if self._plot_inputs.get_input('iter_axis'):
|
|
567
544
|
# Make iter plot (possibly with subplots layout)
|
|
568
|
-
self._do_iter_plot(
|
|
569
|
-
subplots = self._plot_inputs
|
|
545
|
+
self._do_iter_plot()
|
|
546
|
+
subplots = self._plot_inputs.get_input('subplots')
|
|
570
547
|
layout_plot, is_layout = super()._layout_plots(subplots)
|
|
571
|
-
|
|
572
548
|
if is_layout:
|
|
573
549
|
# Cannot show Layout in DynamicMap, show in new tab
|
|
574
550
|
super().show()
|
|
575
551
|
self._logger.info("Plot update complete")
|
|
576
|
-
return self.
|
|
552
|
+
return self._last_plot
|
|
577
553
|
# Overlay raster plot for DynamicMap
|
|
578
554
|
self._logger.info("Plot update complete")
|
|
579
555
|
return layout_plot
|
|
580
556
|
|
|
581
557
|
# Make single Overlay raster plot for DynamicMap
|
|
582
|
-
plot = self._do_plot(
|
|
558
|
+
plot = self._do_plot(True)
|
|
583
559
|
plot_params = self._raster_plot.get_plot_params()
|
|
584
560
|
|
|
585
561
|
# Update color limits in gui with data range
|
|
@@ -593,7 +569,7 @@ class MsRaster(MsPlot):
|
|
|
593
569
|
return plot.opts(
|
|
594
570
|
hv.opts.QuadMesh(
|
|
595
571
|
tools=['hover', 'box_select'],
|
|
596
|
-
selection_fill_alpha=0.
|
|
572
|
+
selection_fill_alpha=0.2, # dim selected areas of plot
|
|
597
573
|
nonselection_fill_alpha=1.0, # do not dim unselected areas of plot
|
|
598
574
|
)
|
|
599
575
|
)
|
|
@@ -705,7 +681,7 @@ class MsRaster(MsPlot):
|
|
|
705
681
|
# Update options for vis_axis selector
|
|
706
682
|
vis_axis_selector = axis_selectors.objects[2]
|
|
707
683
|
vis_axis_value = vis_axis_selector.value
|
|
708
|
-
if self.
|
|
684
|
+
if self._ms_data.get_correlated_data('base') == 'SPECTRUM':
|
|
709
685
|
vis_axis_selector.options = SPECTRUM_AXIS_OPTIONS
|
|
710
686
|
else:
|
|
711
687
|
vis_axis_selector.options = VIS_AXIS_OPTIONS
|
|
@@ -731,8 +707,8 @@ class MsRaster(MsPlot):
|
|
|
731
707
|
|
|
732
708
|
def _update_ps_selection_options(self, ps_selectors):
|
|
733
709
|
''' Set ProcessingSet gui options from ms summary '''
|
|
734
|
-
if self.
|
|
735
|
-
summary = self.
|
|
710
|
+
if self._ms_data and self._ms_data.is_valid():
|
|
711
|
+
summary = self._ms_data.get_summary()
|
|
736
712
|
if summary is None:
|
|
737
713
|
return
|
|
738
714
|
|
|
@@ -749,7 +725,7 @@ class MsRaster(MsPlot):
|
|
|
749
725
|
|
|
750
726
|
def _update_ms_selection_options(self, ms_selectors):
|
|
751
727
|
''' Set MeasurementSet gui options from ms data '''
|
|
752
|
-
if self.
|
|
728
|
+
if self._ms_data and self._ms_data.is_valid():
|
|
753
729
|
for selector in ms_selectors:
|
|
754
730
|
selection_key = MS_SELECTION_OPTIONS[selector.name] if selector.name in MS_SELECTION_OPTIONS else None
|
|
755
731
|
if selection_key:
|
|
@@ -782,7 +758,7 @@ class MsRaster(MsPlot):
|
|
|
782
758
|
''' Set up player with values when iter_axis is selected '''
|
|
783
759
|
iter_axis = None if iter_axis == 'None' else iter_axis
|
|
784
760
|
if iter_axis and self._gui_layout:
|
|
785
|
-
iter_values = self.
|
|
761
|
+
iter_values = self._ms_data.get_dimension_values(iter_axis)
|
|
786
762
|
if iter_values:
|
|
787
763
|
|
|
788
764
|
iter_selectors = self._get_selector('iter')
|
|
@@ -812,7 +788,7 @@ class MsRaster(MsPlot):
|
|
|
812
788
|
|
|
813
789
|
def _get_datetime_values(self, float_times):
|
|
814
790
|
''' Return list of float time values as list of datetime values for gui options '''
|
|
815
|
-
time_attrs = self.
|
|
791
|
+
time_attrs = self._ms_data.get_dimension_attrs('time')
|
|
816
792
|
datetime_values = []
|
|
817
793
|
try:
|
|
818
794
|
datetime_values = to_datetime(float_times, unit=time_attrs['units'], origin=time_attrs['format'])
|
|
@@ -835,105 +811,16 @@ class MsRaster(MsPlot):
|
|
|
835
811
|
button.button_style = 'solid' if plot_changed else 'outline'
|
|
836
812
|
|
|
837
813
|
def _show_plot_inputs(self):
|
|
838
|
-
''' Show inputs for raster plot in GUI tab '''
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
inputs_column.clear()
|
|
842
|
-
for param in self._plot_params:
|
|
843
|
-
str_pane = pn.pane.Str(param)
|
|
844
|
-
str_pane.margin = (0, 10)
|
|
845
|
-
inputs_column.append(str_pane)
|
|
846
|
-
|
|
847
|
-
def _update_cursor_location(self, x, y):
|
|
848
|
-
''' Show data values for cursor x,y position in cursor location box below plot '''
|
|
849
|
-
# Convert plot values to selection values to select plot data
|
|
850
|
-
x_axis = self._plot_inputs['x_axis']
|
|
851
|
-
y_axis = self._plot_inputs['y_axis']
|
|
852
|
-
cursor_position = {x_axis: x, y_axis: y}
|
|
853
|
-
cursor_location = locate_point(self._gui_plot_data, cursor_position, self._plot_inputs['vis_axis'])
|
|
854
|
-
|
|
855
|
-
cursor_location_box = self._gui_layout[0][0][1] # row[0] tabs[0] column[1]
|
|
856
|
-
cursor_location_box.clear() # pn.WidgetBox
|
|
857
|
-
location_layout = pn.Column(pn.widgets.StaticText(name="Cursor Location"))
|
|
858
|
-
location_row = self._layout_point_location(cursor_location)
|
|
859
|
-
# Add row of columns to column layout
|
|
860
|
-
location_layout.append(location_row)
|
|
861
|
-
# Add column layout to widget box
|
|
862
|
-
cursor_location_box.append(location_layout)
|
|
863
|
-
|
|
864
|
-
def _layout_point_location(self, text_list):
|
|
865
|
-
''' Layout list of StaticText in row of columns containing 3 rows '''
|
|
866
|
-
location_row = pn.Row()
|
|
867
|
-
location_col = pn.Column()
|
|
868
|
-
|
|
869
|
-
for static_text in text_list:
|
|
870
|
-
# 3 entries per column; append to row and start new column
|
|
871
|
-
if len(location_col.objects) == 3:
|
|
872
|
-
location_row.append(location_col)
|
|
873
|
-
location_col = pn.Column()
|
|
874
|
-
|
|
875
|
-
static_text.margin = (0, 10) # default (5, 10)
|
|
876
|
-
location_col.append(static_text)
|
|
877
|
-
|
|
878
|
-
# Add last column
|
|
879
|
-
location_row.append(location_col)
|
|
880
|
-
return location_row
|
|
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
|
-
|
|
906
|
-
def _update_box_location(self, bounds):
|
|
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))
|
|
814
|
+
''' Show inputs for raster plot in column in GUI tab '''
|
|
815
|
+
inputs_column = self._gui_layout[0][1]
|
|
816
|
+
super()._fill_inputs_column(inputs_column)
|
|
930
817
|
|
|
931
818
|
###
|
|
932
819
|
### Callbacks for widgets which update plot inputs
|
|
933
820
|
###
|
|
934
821
|
def _set_title(self, title):
|
|
935
822
|
''' Set title from gui text input '''
|
|
936
|
-
self._plot_inputs
|
|
823
|
+
self._plot_inputs.set_input('title', title)
|
|
937
824
|
self._update_plot_status(True) # Change plot button to solid
|
|
938
825
|
|
|
939
826
|
def _set_style_params(self, unflagged_cmap, flagged_cmap, show_colorbar, show_flagged_colorbar):
|
|
@@ -942,65 +829,59 @@ class MsRaster(MsPlot):
|
|
|
942
829
|
|
|
943
830
|
def _set_color_range(self, color_mode, color_range):
|
|
944
831
|
''' Set style params from gui '''
|
|
945
|
-
color_mode
|
|
946
|
-
color_mode = None if color_mode == 'No' else color_mode
|
|
947
|
-
self._plot_inputs['color_mode'] = color_mode
|
|
948
|
-
self._plot_inputs['color_range'] = color_range
|
|
832
|
+
self._plot_inputs.set_color_inputs(color_mode, color_range)
|
|
949
833
|
self._update_plot_status(True) # Change plot button to solid
|
|
950
834
|
|
|
951
835
|
def _set_axes(self, x_axis, y_axis, vis_axis):
|
|
952
|
-
''' Set plot axis
|
|
953
|
-
self._plot_inputs
|
|
954
|
-
self._plot_inputs['y_axis'] = y_axis
|
|
955
|
-
self._plot_inputs['vis_axis'] = vis_axis
|
|
836
|
+
''' Set plot axis inputs from gui '''
|
|
837
|
+
self._plot_inputs.set_axis_inputs(x_axis, y_axis, vis_axis)
|
|
956
838
|
self._update_plot_status(True) # Change plot button to solid
|
|
957
839
|
|
|
958
840
|
def _set_aggregation(self, aggregator, agg_axes):
|
|
959
841
|
''' Set aggregation params from gui '''
|
|
960
|
-
aggregator
|
|
961
|
-
self._plot_inputs['aggregator'] = aggregator
|
|
962
|
-
self._plot_inputs['agg_axis'] = agg_axes # ignored if aggregator not set
|
|
842
|
+
self._plot_inputs.set_aggregation_inputs(aggregator, agg_axes)
|
|
963
843
|
self._update_plot_status(True) # Change plot button to solid
|
|
964
844
|
|
|
965
845
|
# pylint: disable=too-many-arguments, too-many-positional-arguments
|
|
966
846
|
def _set_iteration(self, iter_axis, iter_value_type, iter_value, iter_start, iter_end, subplot_rows, subplot_columns):
|
|
967
847
|
''' Set iteration params from gui '''
|
|
968
848
|
iter_axis = None if iter_axis == 'None' else iter_axis
|
|
969
|
-
|
|
970
|
-
self._plot_inputs['subplots'] = (subplot_rows, subplot_columns)
|
|
971
|
-
|
|
849
|
+
iter_range = None
|
|
972
850
|
if iter_axis:
|
|
851
|
+
# Set iter_range
|
|
973
852
|
if iter_value_type == 'By Value':
|
|
974
853
|
# Use index of iter_value for tuple
|
|
975
|
-
if self.
|
|
976
|
-
iter_values = self.
|
|
854
|
+
if self._ms_data and self._ms_data.is_valid():
|
|
855
|
+
iter_values = self._ms_data.get_dimension_values(iter_axis)
|
|
977
856
|
iter_index = iter_values.index(iter_value)
|
|
978
|
-
|
|
857
|
+
iter_range = (iter_index, iter_index)
|
|
979
858
|
else:
|
|
980
859
|
# 'By Range': use range start and end values for tuple
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
860
|
+
iter_range = (iter_start, iter_end)
|
|
861
|
+
|
|
862
|
+
self._plot_inputs.set_iteration_inputs(iter_axis, iter_range, subplot_rows, subplot_columns)
|
|
984
863
|
self._update_plot_status(True) # Change plot button to solid
|
|
864
|
+
# pylint: enable=too-many-arguments, too-many-positional-arguments
|
|
985
865
|
|
|
866
|
+
# pylint: disable=too-many-arguments, too-many-positional-arguments, unused-argument
|
|
986
867
|
def _set_ps_selection(self, query, name, intents, scan_name, spw_name, field_name, source_name, line_name):
|
|
987
|
-
'''
|
|
868
|
+
''' Set ProcessingSet selection from gui using summary columns '''
|
|
988
869
|
inputs = locals()
|
|
989
870
|
ps_selection = {}
|
|
990
871
|
for key, val in inputs.items():
|
|
991
872
|
if key in PS_SELECTION_OPTIONS.values() and val:
|
|
992
873
|
ps_selection[key] = val
|
|
993
|
-
self.
|
|
874
|
+
self._gui_selection['ps_selection'] = ps_selection
|
|
994
875
|
self._update_plot_status(True) # Change plot button to solid
|
|
995
876
|
|
|
996
877
|
def _set_ms_selection(self, data_group, datetime, baseline, antenna1, antenna2, frequency, polarization):
|
|
997
|
-
'''
|
|
878
|
+
''' Set MeasurementSet selection from gui using data group, dimensions, and coordinates '''
|
|
998
879
|
inputs = locals()
|
|
999
880
|
inputs['time'] = inputs.pop('datetime')
|
|
1000
881
|
ms_selection = {}
|
|
1001
882
|
for key, val in inputs.items():
|
|
1002
883
|
if key in MS_SELECTION_OPTIONS.values() and val:
|
|
1003
884
|
ms_selection[key] = val
|
|
1004
|
-
self.
|
|
885
|
+
self._gui_selection['ms_selection'] = ms_selection
|
|
1005
886
|
self._update_plot_status(True) # Change plot button to solid
|
|
1006
887
|
# pylint: enable=too-many-arguments, too-many-positional-arguments, unused-argument
|