vidavis 0.0.5__tar.gz → 0.0.7__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.5 → vidavis-0.0.7}/PKG-INFO +3 -25
- {vidavis-0.0.5 → vidavis-0.0.7}/pyproject.toml +3 -1
- {vidavis-0.0.5 → vidavis-0.0.7}/readme.rst +2 -24
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/apps/_ms_raster.py +142 -105
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_ms_plot.py +94 -70
- vidavis-0.0.7/src/vidavis/plot/ms_plot/_raster_plot_gui.py +91 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/LICENSE +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/LICENSE.rst +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/__init__.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/apps/__init__.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/bokeh/__init__.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/bokeh/_palette.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/__init__.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/__init__.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/_ms_data.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/__init__.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_concat.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_coords.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_data.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_io.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_raster_data.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_select.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_stats.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_xds_data.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/plot/__init__.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/__init__.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_ms_plot_constants.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_ms_plot_selectors.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_raster_plot.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_raster_plot_inputs.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_time_ticks.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/plot/ms_plot/_xds_plot_axes.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/toolbox/__init__.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/toolbox/_app_context.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/toolbox/_logging.py +0 -0
- {vidavis-0.0.5 → vidavis-0.0.7}/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.7
|
|
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>
|
|
@@ -43,9 +43,6 @@ Requirements
|
|
|
43
43
|
- Optionally `python-casacore <https://pypi.org/project/python-casacore/>`_ or
|
|
44
44
|
`casatools <https://pypi.org/project/casatools/>`_ for MSv2 conversion
|
|
45
45
|
|
|
46
|
-
- Optionally `Selenium <https://www.selenium.dev/documentation/en/>`_ along with
|
|
47
|
-
a web driver to export to file using ``save()``
|
|
48
|
-
|
|
49
46
|
Install
|
|
50
47
|
```````
|
|
51
48
|
|
|
@@ -54,33 +51,14 @@ Install
|
|
|
54
51
|
MSv2 Conversion
|
|
55
52
|
^^^^^^^^^^^^^^^
|
|
56
53
|
|
|
57
|
-
To enable conversion from MSv2 to MSv4 with **python-casacore**
|
|
54
|
+
To enable conversion from MSv2 to MSv4 with **python-casacore** for Linux only:
|
|
58
55
|
|
|
59
56
|
- :code:`pip install "xradio[python-casacore]"`
|
|
60
57
|
|
|
61
|
-
On macOS it is required to pre-install
|
|
58
|
+
On macOS it is required to pre-install **python-casacore**:
|
|
62
59
|
|
|
63
60
|
- :code:`conda install -c conda-forge python-casacore`
|
|
64
61
|
|
|
65
|
-
Exporting Plots
|
|
66
|
-
^^^^^^^^^^^^^^^
|
|
67
|
-
|
|
68
|
-
To enable exporting plots to file without showing the plot, using preferred web
|
|
69
|
-
driver:
|
|
70
|
-
|
|
71
|
-
**Selenium** with **geckodriver** and **Firefox** (to ensure compatible versions):
|
|
72
|
-
|
|
73
|
-
- :code:`conda install -c conda-forge selenium firefox geckodriver`
|
|
74
|
-
|
|
75
|
-
**Selenium** with **ChromeDriver** (Chrome), with the executable
|
|
76
|
-
**chromedriver** in your PATH:
|
|
77
|
-
|
|
78
|
-
- :code:`conda install -c conda-forge selenium python-chromedriver-binary`
|
|
79
|
-
|
|
80
|
-
or:
|
|
81
|
-
|
|
82
|
-
- :code:`pip install selenium chromedriver-binary`
|
|
83
|
-
|
|
84
62
|
Simple MsRaster Usage Example
|
|
85
63
|
`````````````````````````````
|
|
86
64
|
|
|
@@ -8,12 +8,14 @@ authors = [
|
|
|
8
8
|
]
|
|
9
9
|
dependencies = [
|
|
10
10
|
"bokeh==3.6.1",
|
|
11
|
+
"matplotlib",
|
|
11
12
|
"hvplot",
|
|
12
13
|
"graphviper",
|
|
14
|
+
"selenium",
|
|
13
15
|
]
|
|
14
16
|
requires-python = ">=3.11, <3.14"
|
|
15
17
|
readme = "readme.rst"
|
|
16
|
-
version = "0.0.
|
|
18
|
+
version = "0.0.7"
|
|
17
19
|
|
|
18
20
|
[project.license]
|
|
19
21
|
text = "LGPL"
|
|
@@ -34,9 +34,6 @@ Requirements
|
|
|
34
34
|
- Optionally `python-casacore <https://pypi.org/project/python-casacore/>`_ or
|
|
35
35
|
`casatools <https://pypi.org/project/casatools/>`_ for MSv2 conversion
|
|
36
36
|
|
|
37
|
-
- Optionally `Selenium <https://www.selenium.dev/documentation/en/>`_ along with
|
|
38
|
-
a web driver to export to file using ``save()``
|
|
39
|
-
|
|
40
37
|
Install
|
|
41
38
|
```````
|
|
42
39
|
|
|
@@ -45,33 +42,14 @@ Install
|
|
|
45
42
|
MSv2 Conversion
|
|
46
43
|
^^^^^^^^^^^^^^^
|
|
47
44
|
|
|
48
|
-
To enable conversion from MSv2 to MSv4 with **python-casacore**
|
|
45
|
+
To enable conversion from MSv2 to MSv4 with **python-casacore** for Linux only:
|
|
49
46
|
|
|
50
47
|
- :code:`pip install "xradio[python-casacore]"`
|
|
51
48
|
|
|
52
|
-
On macOS it is required to pre-install
|
|
49
|
+
On macOS it is required to pre-install **python-casacore**:
|
|
53
50
|
|
|
54
51
|
- :code:`conda install -c conda-forge python-casacore`
|
|
55
52
|
|
|
56
|
-
Exporting Plots
|
|
57
|
-
^^^^^^^^^^^^^^^
|
|
58
|
-
|
|
59
|
-
To enable exporting plots to file without showing the plot, using preferred web
|
|
60
|
-
driver:
|
|
61
|
-
|
|
62
|
-
**Selenium** with **geckodriver** and **Firefox** (to ensure compatible versions):
|
|
63
|
-
|
|
64
|
-
- :code:`conda install -c conda-forge selenium firefox geckodriver`
|
|
65
|
-
|
|
66
|
-
**Selenium** with **ChromeDriver** (Chrome), with the executable
|
|
67
|
-
**chromedriver** in your PATH:
|
|
68
|
-
|
|
69
|
-
- :code:`conda install -c conda-forge selenium python-chromedriver-binary`
|
|
70
|
-
|
|
71
|
-
or:
|
|
72
|
-
|
|
73
|
-
- :code:`pip install selenium chromedriver-binary`
|
|
74
|
-
|
|
75
53
|
Simple MsRaster Usage Example
|
|
76
54
|
`````````````````````````````
|
|
77
55
|
|
|
@@ -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']:
|
|
@@ -244,10 +231,13 @@ class MsRaster(MsPlot):
|
|
|
244
231
|
Args:
|
|
245
232
|
filename (str): Name of file to save. Default '': the plot will be saved as {ms}_raster.{ext}.
|
|
246
233
|
If fmt is not set for extension, plot will be saved as .png.
|
|
247
|
-
fmt (str): Format of file to save ('png', '
|
|
234
|
+
fmt (str): Format of file to save ('png', 'svg', or 'html').
|
|
248
235
|
Default 'auto': inferred from filename extension.
|
|
249
|
-
width (int): width of exported plot.
|
|
250
|
-
height (int): height of exported plot.
|
|
236
|
+
width (int): width of exported plot in pixels.
|
|
237
|
+
height (int): height of exported plot in pixels.
|
|
238
|
+
|
|
239
|
+
If subplots defines a grid layout, width and height describe the size of each plot in the layout.
|
|
240
|
+
The layout plot size will be (width * columns, height * rows) pixels.
|
|
251
241
|
|
|
252
242
|
If iteration plots were created:
|
|
253
243
|
If subplots is a grid, the layout plot will be saved to a single file.
|
|
@@ -270,6 +260,7 @@ class MsRaster(MsPlot):
|
|
|
270
260
|
|
|
271
261
|
# Select vis_axis data to plot and update selection; returns xarray Dataset
|
|
272
262
|
raster_data = self._data.get_raster_data(plot_inputs)
|
|
263
|
+
self._plot_data = raster_data
|
|
273
264
|
|
|
274
265
|
# Add params needed for plot: auto color range and ms name
|
|
275
266
|
self._set_auto_color_range(plot_inputs) # set calculated limits if auto mode
|
|
@@ -423,85 +414,34 @@ class MsRaster(MsPlot):
|
|
|
423
414
|
### -----------------------------------------------------------------------
|
|
424
415
|
def _launch_gui(self):
|
|
425
416
|
''' Use Holoviz Panel to create a dashboard for plot inputs. '''
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
|
436
432
|
x_axis = self._plot_inputs['x_axis']
|
|
437
433
|
y_axis = self._plot_inputs['y_axis']
|
|
438
|
-
|
|
439
|
-
axis_selectors = axis_selector(x_axis, y_axis, data_dims, True, self._set_axes)
|
|
440
|
-
|
|
441
|
-
# Select from ProcessingSet and MeasurementSet
|
|
442
|
-
selection_selectors = selection_selector(self._set_ps_selection, self._set_ms_selection)
|
|
443
|
-
|
|
444
|
-
# Generic axis options, updated when ms is set
|
|
445
|
-
axis_options = data_dims if data_dims else []
|
|
446
|
-
|
|
447
|
-
# Select aggregator and axes to aggregate
|
|
448
|
-
agg_selectors = aggregation_selector(axis_options, self._set_aggregation)
|
|
449
|
-
|
|
450
|
-
# Select iter_axis and iter value or range
|
|
451
|
-
iter_selectors = iteration_selector(axis_options, self._set_iter_values, self._set_iteration)
|
|
452
|
-
|
|
453
|
-
# Put user input widgets in accordion with only one card active at a time
|
|
454
|
-
selectors = pn.Accordion(
|
|
455
|
-
("Select file", file_selectors), # [0]
|
|
456
|
-
("Plot style", style_selectors), # [1]
|
|
457
|
-
("Data Selection", selection_selectors), # [2]
|
|
458
|
-
("Plot axes", axis_selectors), # [3]
|
|
459
|
-
("Aggregation", agg_selectors), # [4]
|
|
460
|
-
("Iteration", iter_selectors), # [5]
|
|
461
|
-
("Plot title", title_input), # [6]
|
|
462
|
-
)
|
|
463
|
-
selectors.toggle = True
|
|
464
|
-
|
|
465
|
-
# Plot button and spinner while plotting
|
|
466
|
-
init_plot = plot_starter(self._update_plot_spinner)
|
|
467
|
-
|
|
468
|
-
# Connect plot to filename and selector widgets
|
|
469
|
-
dmap = hv.DynamicMap(
|
|
470
|
-
pn.bind(
|
|
471
|
-
self._update_plot,
|
|
472
|
-
ms=file_selectors[0][0],
|
|
473
|
-
do_plot=init_plot[0],
|
|
474
|
-
),
|
|
475
|
-
)
|
|
476
|
-
|
|
477
|
-
# Layout plot and input widgets in a row
|
|
478
|
-
self._gui_layout = pn.Row(
|
|
479
|
-
pn.Tabs( # [0]
|
|
480
|
-
('Plot', dmap), # [0]
|
|
481
|
-
('Plot Inputs', pn.Column()), # [1]
|
|
482
|
-
sizing_mode='stretch_width',
|
|
483
|
-
),
|
|
484
|
-
pn.Spacer(width=10), # [1]
|
|
485
|
-
pn.Column( # [2]
|
|
486
|
-
pn.Spacer(height=25), # [0]
|
|
487
|
-
selectors, # [1]
|
|
488
|
-
init_plot, # [2]
|
|
489
|
-
width_policy='min',
|
|
490
|
-
width=400,
|
|
491
|
-
sizing_mode='stretch_height',
|
|
492
|
-
),
|
|
493
|
-
sizing_mode='stretch_height',
|
|
494
|
-
)
|
|
495
|
-
|
|
434
|
+
self._gui_layout = create_raster_gui(callbacks, data_dims, x_axis, y_axis)
|
|
496
435
|
# Show gui
|
|
497
|
-
# print("gui layout:", self._gui_layout) # for debugging
|
|
498
436
|
self._gui_layout.show(title=self._app_name, threaded=True)
|
|
499
437
|
|
|
500
438
|
###
|
|
501
439
|
### Main callback to create plot if inputs changed
|
|
502
440
|
###
|
|
503
|
-
def _update_plot(self, ms, do_plot):
|
|
504
|
-
''' 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
|
+
'''
|
|
505
445
|
if self._toast:
|
|
506
446
|
self._toast.destroy()
|
|
507
447
|
|
|
@@ -510,12 +450,18 @@ class MsRaster(MsPlot):
|
|
|
510
450
|
# Launched GUI with no MS
|
|
511
451
|
return self._empty_plot
|
|
512
452
|
|
|
513
|
-
# 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.
|
|
514
455
|
first_plot = not self._last_gui_plot
|
|
515
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)
|
|
516
461
|
# Not ready to update plot yet, return last plot.
|
|
517
462
|
return self._last_gui_plot
|
|
518
463
|
|
|
464
|
+
# Callback is for initial plot for input ms, or user clicked Plot button
|
|
519
465
|
if (self._set_ms(ms) or first_plot) and self._data and self._data.is_valid():
|
|
520
466
|
# New MS set and is valid
|
|
521
467
|
self._update_gui_axis_options()
|
|
@@ -811,7 +757,6 @@ class MsRaster(MsPlot):
|
|
|
811
757
|
#filename_input.width = len(filename[-1])
|
|
812
758
|
filename_input.value = filename[-1]
|
|
813
759
|
|
|
814
|
-
|
|
815
760
|
def _set_iter_values(self, iter_axis):
|
|
816
761
|
''' Set up player with values when iter_axis is selected '''
|
|
817
762
|
iter_axis = None if iter_axis == 'None' else iter_axis
|
|
@@ -876,6 +821,98 @@ class MsRaster(MsPlot):
|
|
|
876
821
|
for param in self._plot_params:
|
|
877
822
|
inputs_column.append(pn.pane.Str(param))
|
|
878
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':
|
|
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
|
|
860
|
+
|
|
861
|
+
# Set complex component name for visibilities
|
|
862
|
+
if 'VISIBILITY' in values:
|
|
863
|
+
values[self._plot_inputs['vis_axis'].capitalize()] = values.pop('VISIBILITY')
|
|
864
|
+
return values, units
|
|
865
|
+
|
|
866
|
+
def _update_cursor_box(self, location_values, units):
|
|
867
|
+
''' Update cursor location widget box with info in dict '''
|
|
868
|
+
cursor_location_box = self._gui_layout[0][0][1]
|
|
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)
|
|
900
|
+
|
|
901
|
+
def _get_xda_val_unit(self, xda):
|
|
902
|
+
''' Get value and unit as str not list '''
|
|
903
|
+
# Value
|
|
904
|
+
val = xda.values
|
|
905
|
+
if isinstance(val, np.ndarray) and val.size == 1:
|
|
906
|
+
val = val.item()
|
|
907
|
+
# Unit
|
|
908
|
+
try:
|
|
909
|
+
unit = xda.attrs['units']
|
|
910
|
+
unit = unit[0] if (isinstance(unit, list) and len(unit) == 1) else unit
|
|
911
|
+
unit = '' if unit == 'unkown' else unit
|
|
912
|
+
except KeyError:
|
|
913
|
+
unit = ''
|
|
914
|
+
return val, unit
|
|
915
|
+
|
|
879
916
|
###
|
|
880
917
|
### Callbacks for widgets which update plot inputs
|
|
881
918
|
###
|
|
@@ -5,11 +5,13 @@ Base class for ms plots
|
|
|
5
5
|
import os
|
|
6
6
|
import time
|
|
7
7
|
|
|
8
|
-
from bokeh.
|
|
8
|
+
from bokeh.io import export_png, export_svg
|
|
9
|
+
from bokeh.plotting import save, show
|
|
9
10
|
import hvplot
|
|
10
11
|
import holoviews as hv
|
|
11
12
|
import numpy as np
|
|
12
13
|
import panel as pn
|
|
14
|
+
from selenium import webdriver
|
|
13
15
|
|
|
14
16
|
try:
|
|
15
17
|
from toolviper.utils.logger import get_logger, setup_logger
|
|
@@ -57,6 +59,9 @@ class MsPlot:
|
|
|
57
59
|
self._plots_locked = False
|
|
58
60
|
self._plots = []
|
|
59
61
|
|
|
62
|
+
# Initialize gui
|
|
63
|
+
self._gui_layout = None
|
|
64
|
+
|
|
60
65
|
# Set data (if ms)
|
|
61
66
|
self._data = None
|
|
62
67
|
self._ms_info = {}
|
|
@@ -140,27 +145,21 @@ class MsPlot:
|
|
|
140
145
|
self._plots_locked = True
|
|
141
146
|
|
|
142
147
|
# Single plot or combine plots into layout using subplots (rows, columns)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
# Render to bokeh figure
|
|
148
|
-
if is_layout:
|
|
149
|
-
# Show plots in columns
|
|
150
|
-
plot = hv.render(layout_plot.cols(subplots[1]))
|
|
151
|
-
else:
|
|
152
|
-
# Show single plot
|
|
153
|
-
plot = hv.render(layout_plot)
|
|
148
|
+
layout_plot = self._layout_plots(self._plot_inputs['subplots'])
|
|
149
|
+
|
|
150
|
+
# Render plot as Bokeh Figure or GridPlot so can show() in script without tying up thread
|
|
151
|
+
bokeh_fig = hv.render(layout_plot)
|
|
154
152
|
|
|
155
153
|
self._plots_locked = False
|
|
156
154
|
if self._plot_params:
|
|
155
|
+
# Show plot and plot inputs in tabs
|
|
157
156
|
column = pn.Column()
|
|
158
157
|
for param in self._plot_params:
|
|
159
158
|
column.append(pn.pane.Str(param))
|
|
160
|
-
tabs = pn.Tabs(('Plot',
|
|
159
|
+
tabs = pn.Tabs(('Plot', bokeh_fig), ('Plot Inputs', column))
|
|
161
160
|
tabs.show(title=self._app_name, threaded=True)
|
|
162
161
|
else:
|
|
163
|
-
show(
|
|
162
|
+
show(bokeh_fig)
|
|
164
163
|
|
|
165
164
|
def save(self, filename='ms_plot.png', fmt='auto', width=900, height=600):
|
|
166
165
|
'''
|
|
@@ -175,78 +174,103 @@ class MsPlot:
|
|
|
175
174
|
|
|
176
175
|
start_time = time.time()
|
|
177
176
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
subplots = self._plot_inputs['subplots']
|
|
181
|
-
layout_plot, is_layout = self._layout_plots(subplots)
|
|
177
|
+
name, ext = os.path.splitext(filename)
|
|
178
|
+
fmt = ext[1:] if fmt=='auto' else fmt
|
|
182
179
|
|
|
183
|
-
if
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
self._logger.info("Saved plot to %s.", filename)
|
|
187
|
-
else:
|
|
188
|
-
# Save plots individually, with index appended if exprange='all' and multiple plots.
|
|
189
|
-
if self._plot_inputs['iter_axis'] is None:
|
|
190
|
-
hvplot.save(layout_plot.opts(width=width, height=height), filename=filename, fmt=fmt)
|
|
191
|
-
self._logger.info("Saved plot to %s.", filename)
|
|
192
|
-
else:
|
|
193
|
-
name, ext = os.path.splitext(filename)
|
|
194
|
-
iter_range = self._plot_inputs['iter_range'] # None or (start, end)
|
|
195
|
-
plot_idx = 0 if iter_range is None else iter_range[0]
|
|
196
|
-
|
|
197
|
-
for plot in self._plots:
|
|
198
|
-
exportname = f"{name}_{plot_idx}{ext}"
|
|
199
|
-
hvplot.save(plot.opts(width=width, height=height), filename=exportname, fmt=fmt)
|
|
200
|
-
self._logger.info("Saved plot to %s.", exportname)
|
|
201
|
-
plot_idx += 1
|
|
180
|
+
# Combine plots into layout using subplots (rows, columns) if not single plot.
|
|
181
|
+
# Set fixed size for export.
|
|
182
|
+
layout_plot = self._layout_plots(self._plot_inputs['subplots'], (width, height))
|
|
202
183
|
|
|
184
|
+
if not isinstance(layout_plot, hv.Layout) and self._plot_inputs['iter_axis']:
|
|
185
|
+
# Save iterated plots individually, with index appended to filename
|
|
186
|
+
plot_idx = 0 if self._plot_inputs['iter_range'] is None else self._plot_inputs['iter_range'][0]
|
|
187
|
+
for plot in self._plots:
|
|
188
|
+
exportname = f"{name}_{plot_idx}{ext}"
|
|
189
|
+
self._save_plot(plot, exportname, fmt)
|
|
190
|
+
plot_idx += 1
|
|
191
|
+
else:
|
|
192
|
+
self._save_plot(layout_plot, filename, fmt)
|
|
203
193
|
self._logger.debug("Save elapsed time: %.2fs.", time.time() - start_time)
|
|
204
194
|
|
|
205
|
-
def _layout_plots(self, subplots):
|
|
195
|
+
def _layout_plots(self, subplots, fixed_size=None):
|
|
196
|
+
''' Combine plots in a layout, using fixed size for the layout if given '''
|
|
206
197
|
subplots = (1, 1) if subplots is None else subplots
|
|
207
|
-
num_plots = len(self._plots)
|
|
208
|
-
|
|
198
|
+
num_plots = min(len(self._plots), np.prod(subplots))
|
|
199
|
+
plot_width = fixed_size[0] if fixed_size else None
|
|
200
|
+
plot_height = fixed_size[1] if fixed_size else None
|
|
209
201
|
|
|
210
|
-
if
|
|
211
|
-
|
|
202
|
+
if num_plots == 1:
|
|
203
|
+
# Single plot, not layout
|
|
204
|
+
plot = self._plots[0]
|
|
205
|
+
if fixed_size:
|
|
206
|
+
plot = plot.opts(responsive=False, width=plot_width, height=plot_height, clone=True)
|
|
207
|
+
return plot
|
|
212
208
|
|
|
213
209
|
# Set plots in layout
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
for i in range(num_layout_plots):
|
|
210
|
+
layout_plot = None
|
|
211
|
+
for i in range(num_plots):
|
|
217
212
|
plot = self._plots[i]
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
213
|
+
if fixed_size:
|
|
214
|
+
plot = plot.opts(responsive=False, width=plot_width, height=plot_height, clone=True)
|
|
215
|
+
layout_plot = plot if layout_plot is None else layout_plot + plot
|
|
216
|
+
|
|
217
|
+
# Layout in columns
|
|
218
|
+
return layout_plot.cols(subplots[1])
|
|
219
|
+
|
|
220
|
+
def _save_plot(self, plot, filename, fmt):
|
|
221
|
+
''' Save plot using hvplot, else bokeh '''
|
|
222
|
+
# Remove toolbar unless html
|
|
223
|
+
toolbar = 'right' if fmt=='html' else None
|
|
224
|
+
plot = plot.opts(toolbar=toolbar, clone=True)
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
hvplot.save(plot, filename=filename, fmt=fmt)
|
|
228
|
+
except (Exception, RuntimeError) as exc:
|
|
229
|
+
# Fails if hvplot cannot find web driver or fmt is svg.
|
|
230
|
+
# Render a Bokeh Figure or GridPlot, create webdriver, then use Bokeh to export.
|
|
231
|
+
fig = hv.render(plot)
|
|
232
|
+
if fmt=='html':
|
|
233
|
+
save(fig, filename)
|
|
234
|
+
elif fmt in ['png', 'svg']:
|
|
235
|
+
# Use Chrome web driver
|
|
236
|
+
service = webdriver.ChromeService()
|
|
237
|
+
options = webdriver.ChromeOptions()
|
|
238
|
+
options.add_argument('--headless')
|
|
239
|
+
options.add_argument('--no-sandbox')
|
|
240
|
+
|
|
241
|
+
with webdriver.Chrome(service=service, options=options) as driver:
|
|
242
|
+
if fmt=='png':
|
|
243
|
+
export_png(fig, filename=filename, webdriver=driver)
|
|
244
|
+
elif fmt=='svg':
|
|
245
|
+
export_svg(fig, filename=filename, webdriver=driver)
|
|
246
|
+
else:
|
|
247
|
+
raise ValueError(f"Invalid fmt or filename extension {fmt} for save()") from exc
|
|
248
|
+
self._logger.info("Saved plot to %s.", filename)
|
|
223
249
|
|
|
224
250
|
def _set_ms(self, ms_path):
|
|
225
251
|
''' Set MsData and update ms info for input ms filepath (MSv2 or zarr), if set.
|
|
226
|
-
Return whether ms changed (false if
|
|
252
|
+
Return whether ms changed (false if ms_path is None, not set yet), even if error. '''
|
|
227
253
|
self._ms_info['ms'] = ms_path
|
|
228
254
|
ms_error = ""
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
255
|
+
if not ms_path or (self._data and self._data.is_ms_path(ms_path)):
|
|
256
|
+
return False
|
|
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
|
|
246
271
|
if ms_error:
|
|
247
272
|
self._notify(ms_error, 'error', 0)
|
|
248
|
-
|
|
249
|
-
return ms_changed
|
|
273
|
+
return True
|
|
250
274
|
|
|
251
275
|
def _notify(self, message, level, duration=3000):
|
|
252
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.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_concat.py
RENAMED
|
File without changes
|
{vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_coords.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_raster_data.py
RENAMED
|
File without changes
|
{vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_select.py
RENAMED
|
File without changes
|
{vidavis-0.0.5 → vidavis-0.0.7}/src/vidavis/data/measurement_set/processing_set/_ps_stats.py
RENAMED
|
File without changes
|
{vidavis-0.0.5 → vidavis-0.0.7}/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
|