vidavis 0.0.8__py3-none-any.whl → 0.0.9__py3-none-any.whl

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