cubevis 0.5.2__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.
Files changed (132) hide show
  1. cubevis/LICENSE.rst +500 -0
  2. cubevis/__icons__/20px/fast-backward.svg +13 -0
  3. cubevis/__icons__/20px/fast-forward.svg +13 -0
  4. cubevis/__icons__/20px/step-backward.svg +12 -0
  5. cubevis/__icons__/20px/step-forward.svg +12 -0
  6. cubevis/__icons__/add-chan.png +0 -0
  7. cubevis/__icons__/add-chan.svg +84 -0
  8. cubevis/__icons__/add-cube.png +0 -0
  9. cubevis/__icons__/add-cube.svg +186 -0
  10. cubevis/__icons__/drag.png +0 -0
  11. cubevis/__icons__/drag.svg +109 -0
  12. cubevis/__icons__/mask-selected.png +0 -0
  13. cubevis/__icons__/mask.png +0 -0
  14. cubevis/__icons__/mask.svg +1 -0
  15. cubevis/__icons__/new-layer-sm-selected.png +0 -0
  16. cubevis/__icons__/new-layer-sm-selected.svg +88 -0
  17. cubevis/__icons__/new-layer-sm.png +0 -0
  18. cubevis/__icons__/new-layer-sm.svg +15 -0
  19. cubevis/__icons__/reset.png +0 -0
  20. cubevis/__icons__/reset.svg +11 -0
  21. cubevis/__icons__/sub-chan.png +0 -0
  22. cubevis/__icons__/sub-chan.svg +71 -0
  23. cubevis/__icons__/sub-cube.png +0 -0
  24. cubevis/__icons__/sub-cube.svg +95 -0
  25. cubevis/__icons__/zoom-to-fit.png +0 -0
  26. cubevis/__icons__/zoom-to-fit.svg +21 -0
  27. cubevis/__init__.py +58 -0
  28. cubevis/__js__/bokeh-3.6.1.min.js +728 -0
  29. cubevis/__js__/bokeh-tables-3.6.1.min.js +119 -0
  30. cubevis/__js__/bokeh-widgets-3.6.1.min.js +141 -0
  31. cubevis/__js__/casalib.min.js +1 -0
  32. cubevis/__js__/cubevisjs.min.js +62 -0
  33. cubevis/__version__.py +1 -0
  34. cubevis/apps/__init__.py +44 -0
  35. cubevis/apps/_createmask.py +461 -0
  36. cubevis/apps/_createregion.py +513 -0
  37. cubevis/apps/_interactiveclean.py +3260 -0
  38. cubevis/apps/_interactiveclean_wrappers.py +130 -0
  39. cubevis/apps/_ms_raster.py +815 -0
  40. cubevis/apps/_plotants.py +286 -0
  41. cubevis/apps/_plotbandpass.py +7 -0
  42. cubevis/bokeh/__init__.py +29 -0
  43. cubevis/bokeh/annotations/__init__.py +1 -0
  44. cubevis/bokeh/annotations/_ev_poly_annotation.py +6 -0
  45. cubevis/bokeh/components/__init__.py +28 -0
  46. cubevis/bokeh/format/__init__.py +31 -0
  47. cubevis/bokeh/format/_time_ticks.py +44 -0
  48. cubevis/bokeh/format/_wcs_ticks.py +45 -0
  49. cubevis/bokeh/models/__init__.py +4 -0
  50. cubevis/bokeh/models/_edit_span.py +7 -0
  51. cubevis/bokeh/models/_ev_text_input.py +6 -0
  52. cubevis/bokeh/models/_tip.py +37 -0
  53. cubevis/bokeh/models/_tip_button.py +50 -0
  54. cubevis/bokeh/sources/__init__.py +35 -0
  55. cubevis/bokeh/sources/_data_pipe.py +258 -0
  56. cubevis/bokeh/sources/_image_data_source.py +83 -0
  57. cubevis/bokeh/sources/_image_pipe.py +581 -0
  58. cubevis/bokeh/sources/_spectra_data_source.py +55 -0
  59. cubevis/bokeh/sources/_updatable_data_source.py +189 -0
  60. cubevis/bokeh/state/__init__.py +34 -0
  61. cubevis/bokeh/state/_initialize.py +164 -0
  62. cubevis/bokeh/state/_javascript.py +53 -0
  63. cubevis/bokeh/state/_palette.py +58 -0
  64. cubevis/bokeh/state/_session.py +44 -0
  65. cubevis/bokeh/state/js/bokeh-2.4.1.min.js +596 -0
  66. cubevis/bokeh/state/js/bokeh-gl-2.4.1.min.js +74 -0
  67. cubevis/bokeh/state/js/bokeh-tables-2.4.1.min.js +132 -0
  68. cubevis/bokeh/state/js/bokeh-widgets-2.4.1.min.js +118 -0
  69. cubevis/bokeh/state/js/casaguijs-v0.0.4.0-b2.4.min.js +49 -0
  70. cubevis/bokeh/state/js/casaguijs-v0.0.5.0-b2.4.min.js +49 -0
  71. cubevis/bokeh/state/js/casaguijs-v0.0.6.0-b2.4.min.js +49 -0
  72. cubevis/bokeh/state/js/casalib-v0.0.1.min.js +1 -0
  73. cubevis/bokeh/tools/__init__.py +31 -0
  74. cubevis/bokeh/tools/_cbreset_tool.py +52 -0
  75. cubevis/bokeh/tools/_drag_tool.py +61 -0
  76. cubevis/bokeh/utils/__init__.py +35 -0
  77. cubevis/bokeh/utils/_axes_labels.py +94 -0
  78. cubevis/bokeh/utils/_svg_icon.py +136 -0
  79. cubevis/data/__init__.py +1 -0
  80. cubevis/data/casaimage/__init__.py +114 -0
  81. cubevis/data/measurement_set/__init__.py +7 -0
  82. cubevis/data/measurement_set/_ms_data.py +178 -0
  83. cubevis/data/measurement_set/processing_set/__init__.py +30 -0
  84. cubevis/data/measurement_set/processing_set/_ps_concat.py +98 -0
  85. cubevis/data/measurement_set/processing_set/_ps_coords.py +78 -0
  86. cubevis/data/measurement_set/processing_set/_ps_data.py +213 -0
  87. cubevis/data/measurement_set/processing_set/_ps_io.py +55 -0
  88. cubevis/data/measurement_set/processing_set/_ps_raster_data.py +154 -0
  89. cubevis/data/measurement_set/processing_set/_ps_select.py +91 -0
  90. cubevis/data/measurement_set/processing_set/_ps_stats.py +218 -0
  91. cubevis/data/measurement_set/processing_set/_xds_data.py +149 -0
  92. cubevis/plot/__init__.py +1 -0
  93. cubevis/plot/ms_plot/__init__.py +29 -0
  94. cubevis/plot/ms_plot/_ms_plot.py +242 -0
  95. cubevis/plot/ms_plot/_ms_plot_constants.py +22 -0
  96. cubevis/plot/ms_plot/_ms_plot_selectors.py +348 -0
  97. cubevis/plot/ms_plot/_raster_plot.py +292 -0
  98. cubevis/plot/ms_plot/_raster_plot_inputs.py +116 -0
  99. cubevis/plot/ms_plot/_xds_plot_axes.py +110 -0
  100. cubevis/private/__java__/xml-casa-assembly-1.86.jar +0 -0
  101. cubevis/private/_gclean.py +798 -0
  102. cubevis/private/casashell/createmask.py +332 -0
  103. cubevis/private/casashell/iclean.py +4432 -0
  104. cubevis/private/casatasks/__init__.py +140 -0
  105. cubevis/private/casatasks/createmask.py +86 -0
  106. cubevis/private/casatasks/createregion.py +83 -0
  107. cubevis/private/casatasks/iclean.py +1831 -0
  108. cubevis/readme.rst +16 -0
  109. cubevis/remote/__init__.py +10 -0
  110. cubevis/remote/_gclean.py +61 -0
  111. cubevis/remote/_local.py +287 -0
  112. cubevis/remote/_remote_kernel.py +80 -0
  113. cubevis/toolbox/__init__.py +32 -0
  114. cubevis/toolbox/_app_context.py +74 -0
  115. cubevis/toolbox/_cube.py +3457 -0
  116. cubevis/toolbox/_region_list.py +197 -0
  117. cubevis/utils/_ResourceManager.py +86 -0
  118. cubevis/utils/__init__.py +620 -0
  119. cubevis/utils/_contextmgrchain.py +84 -0
  120. cubevis/utils/_conversion.py +93 -0
  121. cubevis/utils/_copydoc.py +55 -0
  122. cubevis/utils/_docenum.py +25 -0
  123. cubevis/utils/_import_protected_module.py +35 -0
  124. cubevis/utils/_logging.py +85 -0
  125. cubevis/utils/_pkgs.py +77 -0
  126. cubevis/utils/_regions.py +40 -0
  127. cubevis/utils/_static.py +66 -0
  128. cubevis/utils/_tiles.py +167 -0
  129. cubevis-0.5.2.dist-info/METADATA +151 -0
  130. cubevis-0.5.2.dist-info/RECORD +132 -0
  131. cubevis-0.5.2.dist-info/WHEEL +4 -0
  132. cubevis-0.5.2.dist-info/licenses/LICENSE +504 -0
@@ -0,0 +1,258 @@
1
+ ########################################################################
2
+ #
3
+ # Copyright (C) 2021, 2023
4
+ # Associated Universities, Inc. Washington DC, USA.
5
+ #
6
+ # This script is free software; you can redistribute it and/or modify it
7
+ # under the terms of the GNU Library General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or (at your
9
+ # option) any later version.
10
+ #
11
+ # This library is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14
+ # License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Library General Public License
17
+ # along with this library; if not, write to the Free Software Foundation,
18
+ # Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
19
+ #
20
+ # Correspondence concerning AIPS++ should be adressed as follows:
21
+ # Internet email: casa-feedback@nrao.edu.
22
+ # Postal address: AIPS++ Project Office
23
+ # National Radio Astronomy Observatory
24
+ # 520 Edgemont Road
25
+ # Charlottesville, VA 22903-2475 USA
26
+ #
27
+ ########################################################################
28
+ '''Implementation of a general purpose event manager between Python and
29
+ JavaScript via ``websockets``. This provides a mechanism like the
30
+ ``ImagePipe``. The difference is that ``ImagePipe`` is tuned for use
31
+ with CASA images but ``DataPipe`` can have generic messages.'''
32
+
33
+ import inspect
34
+ import threading
35
+ import asyncio
36
+ import traceback
37
+
38
+ from bokeh.models.sources import DataSource
39
+ from bokeh.util.compiler import TypeScript
40
+ from bokeh.core.properties import Tuple, String, Int, Instance, Nullable
41
+ from bokeh.models.callbacks import Callback
42
+
43
+ from ...utils import serialize, deserialize
44
+ from ..state import casalib_url, cubevisjs_url
45
+
46
+ class DataPipe(DataSource):
47
+ """This class allows for communication between Python and the JavaScript implementation
48
+ running in a browser. It allows Python code to send a message to JavaScript and register
49
+ a callback which will receive the result. JavaScript code can do the same to request
50
+ information from Python. Generally messages are expected to be dictionaries in the
51
+ Python domain and objects in the JavaScript domain. UUIDs are used to keep messages in
52
+ sync, and messages sent with the same UUID are queued until the currently pending
53
+ message reply is recieved. A class can use multipe UUIDs to control queuing behavior.
54
+
55
+ Attributes
56
+ ----------
57
+ address: tuple of string and int
58
+ the string is the IP address for the network that should be used and the
59
+ integer is the port number, see ``cubevis.utils.find_ws_address``
60
+ init_script: JavaScript
61
+ this javascript is run when this DataPipe object is initialized. init_script
62
+ is used to run caller JavaScript which needs to be run at initialization time.
63
+ This is optional and does not need to be set.
64
+ """
65
+
66
+ init_script = Nullable(Instance(Callback), help="""
67
+ JavaScript to be run during initialization of an instance of an DataPipe object.
68
+ """)
69
+
70
+ address = Tuple( String, Int, help="two integer sequence representing the address and port to use for the websocket" )
71
+
72
+ __javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
73
+
74
+ def __init__( self, *args, abort=None, **kwargs ):
75
+ super( ).__init__( *args, **kwargs )
76
+ self.__send_queue = { }
77
+ self.__pending = { }
78
+ self.__incoming_callbacks = { }
79
+ self.__websocket = None
80
+ self.__lock = threading.Lock( )
81
+ self.__session = None
82
+ self.__abort = abort
83
+
84
+ if self.__abort is not None and not callable(self.__abort):
85
+ raise RuntimeError(f'abort function must be callable ({type(self.__abort)} is not)')
86
+
87
+ def __enqueue_send( self, ident, msg, callback ):
88
+ ### it is assumed that this is called AFTER the lock has been aquired
89
+ if ident in self.__send_queue:
90
+ self.__send_queue[ident].insert(0, { 'cb': callback, 'msg': msg })
91
+ else:
92
+ self.__send_queue[ident] = [ { 'cb': callback, 'msg': msg } ]
93
+ def __dequeue_send( self, ident ):
94
+ ### it is assumed that this is called AFTER the lock has been aquired
95
+ if ident in self.__send_queue and self.__send_queue[ident]:
96
+ return self.__send_queue[ident].pop( )
97
+ return None
98
+ async def __put_pending( self, ident, callback ):
99
+ ### it is assumed that this is called AFTER the lock has been aquired
100
+ ## info about request sent to javascript waits in this queue
101
+ ## until the javascript reply is received...
102
+ if ident in self.__pending:
103
+ if self.__websocket is not None:
104
+ await self.__websocket.send( { 'id': '', 'message': 'queueing callback, but already one callback waiting', 'direction': 'error' } )
105
+ else:
106
+ self.__pending[ident] = callback
107
+
108
+ def __get_pending( self, ident ):
109
+ ### it is assumed that this is called AFTER the lock has been aquired
110
+ ## when reply arrives for a request to javascript
111
+ ## the callback is retrieved from the waiting queue
112
+ if ident in self.__pending:
113
+ result = self.__pending[ident]
114
+ del self.__pending[ident]
115
+ return result
116
+ return None
117
+
118
+ def register( self, ident, callback ):
119
+ """Register a callback to handle all requests coming from JavaScript. The
120
+ callback will be called whenever a request arrives.
121
+
122
+ Parameters
123
+ ----------
124
+ ident: string
125
+ UUID which is associated with the messages that should be delivered
126
+ to this callback
127
+ callback: (string) => dictionary
128
+ Callback which receives the message from Python as its sole parameter.
129
+ The return value of this callback is delivered to the JavaScript code
130
+ as the ''reply'' to to JavaScript in response to the ''request'' contained
131
+ in the message.
132
+ """
133
+ with self.__lock:
134
+ self.__incoming_callbacks[ident] = callback
135
+
136
+ async def send( self, ident, message, callback ):
137
+ """Send a `message` to JavaScript identified by `ident`. Once the reply is
138
+ received, the `callback` will be called with the reply message.
139
+
140
+ Parameters
141
+ ----------
142
+ ident: string
143
+ UUID to associate with this message. It is used to keep track of the callback
144
+ that should be called when the reply is received.
145
+ message: dictionary
146
+ This dictionary contains the request for the JavaScript code.
147
+ callback: (string) => void
148
+ Callback which receives the message that JavaScript generates in response to
149
+ the request contained in the `message` parameter.
150
+ """
151
+ with self.__lock:
152
+ if self.__websocket is not None:
153
+ msg = { 'id': ident, 'message': message, 'direction': 'p2j' }
154
+ if ident in self.__pending:
155
+ self.__enqueue_send( ident, msg, callback )
156
+ else:
157
+ if ident in self.__send_queue and self.__send_queue[ident]:
158
+ self.__enqueue_send( ident, msg, callback )
159
+ existing = self.__dequeue_send(ident)
160
+ self.__put_pending(ident, existing['cb'])
161
+ await self.__websocket.send(serialize( existing['msg'] ))
162
+ else:
163
+ await self.__put_pending(ident, callback)
164
+ await self.__websocket.send(serialize( msg ))
165
+
166
+ async def process_messages( self, websocket ):
167
+ """Process messages related to image display updates.
168
+
169
+ Parameters
170
+ ----------
171
+ websocket: websocket object
172
+ Websocket serve function passes in the websocket object to use.
173
+ path:
174
+ Websocket serve provides a second parameter.
175
+ """
176
+ try:
177
+ self.__websocket = websocket
178
+ async for message in websocket:
179
+ msg = deserialize(message)
180
+ if 'session' not in msg:
181
+ await self.__websocket.close( )
182
+ err = RuntimeError(f'session not in: {msg}')
183
+ if self.__abort is not None:
184
+ self.__abort( err )
185
+ else:
186
+ raise err
187
+ return
188
+ elif self.__session != None and self.__session != msg['session']:
189
+ await self.__websocket.close( )
190
+ err = RuntimeError(f"session corruption: {msg['session']} does not equal {self.__session}")
191
+ if self.__abort is not None:
192
+ self.__abort( err )
193
+ else:
194
+ raise err
195
+ return
196
+
197
+ with self.__lock:
198
+ ###
199
+ ### initialize session identifier
200
+ ###
201
+ if self.__session == None:
202
+ self.__session = msg['session']
203
+ if msg['direction'] == 'p2j':
204
+ cb = self.__get_pending(msg['id'])
205
+ outgo = self.__dequeue_send(msg['id'])
206
+ if outgo is not None:
207
+ await websocket.send(serialize(outgo['msg']))
208
+ await self.__put_pending(msg['id'],outgo['cb'])
209
+ if cb is not None:
210
+ if inspect.isawaitable(cb):
211
+ await cb(msg['message'])
212
+ else:
213
+ cb(msg['message'])
214
+ elif msg['id'] != 'initialize':
215
+ ###
216
+ ### no cb/reply for initialization of session
217
+ ###
218
+ if msg['id'] not in self.__incoming_callbacks:
219
+ raise RuntimeError(f'incoming js request with no callback: {msg}')
220
+ result = self.__incoming_callbacks[msg['id']](msg['message'])
221
+ if inspect.isawaitable(result):
222
+ try:
223
+ return_message = "an exception occurred in creating the respone"
224
+ return_message = await result
225
+ await self.__websocket.send( serialize( { 'id': msg['id'],
226
+ 'message': return_message,
227
+ 'direction': msg['direction'] } ) )
228
+ except Exception as e:
229
+ trace_back = traceback.format_exc().replace('\n','\n ')
230
+ print('************************************************************************************************************************')
231
+ print( f'''EXCEPTION ENCOUNTERED: {repr(e)}''' )
232
+ print( f''' MESSAGE: {repr(msg)}''' )
233
+ print( f''' STACK TRACE: {trace_back}''' )
234
+ print('************************************************************************************************************************')
235
+ await self.__websocket.send( serialize( { 'id': msg['id'],
236
+ 'message': { 'error': "exception encountered",
237
+ 'errant': str(return_message),
238
+ 'exception': repr(e) },
239
+ 'direction': str(msg['direction']) } ) )
240
+ else:
241
+ try:
242
+ await self.__websocket.send( serialize( { 'id': msg['id'],
243
+ 'message': result,
244
+ 'direction': msg['direction'] } ) )
245
+ except Exception as e:
246
+ trace_back = traceback.format_exc().replace('\n','\n ')
247
+ print('************************************************************************************************************************')
248
+ print( f'''EXCEPTION ENCOUNTERED: {repr(e)}''' )
249
+ print( f''' MESSAGE: {repr(msg)}''' )
250
+ print( f''' STACK TRACE: {trace_back}''' )
251
+ print('************************************************************************************************************************')
252
+ await self.__websocket.send( serialize( { 'id': msg['id'],
253
+ 'message': { 'error': "exception encountered",
254
+ 'errant': str(result),
255
+ 'exception': repr(e) },
256
+ 'direction': str(msg['direction']) } ) )
257
+ finally:
258
+ self.__websocket = None
@@ -0,0 +1,83 @@
1
+ ########################################################################
2
+ #
3
+ # Copyright (C) 2021
4
+ # Associated Universities, Inc. Washington DC, USA.
5
+ #
6
+ # This script is free software; you can redistribute it and/or modify it
7
+ # under the terms of the GNU Library General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or (at your
9
+ # option) any later version.
10
+ #
11
+ # This library is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14
+ # License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Library General Public License
17
+ # along with this library; if not, write to the Free Software Foundation,
18
+ # Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
19
+ #
20
+ # Correspondence concerning AIPS++ should be adressed as follows:
21
+ # Internet email: casa-feedback@nrao.edu.
22
+ # Postal address: AIPS++ Project Office
23
+ # National Radio Astronomy Observatory
24
+ # 520 Edgemont Road
25
+ # Charlottesville, VA 22903-2475 USA
26
+ #
27
+ ########################################################################
28
+ '''Specialization of the Bokeh ``DataSource`` for image cube data.'''
29
+
30
+ import numpy as np
31
+ from bokeh.plotting import ColumnDataSource
32
+ from bokeh.util.compiler import TypeScript
33
+ from bokeh.core.properties import Instance, Tuple, Int, Nullable
34
+ from bokeh.models.callbacks import Callback
35
+ from ._image_pipe import ImagePipe
36
+ from ..state import casalib_url, cubevisjs_url
37
+
38
+ class ImageDataSource(ColumnDataSource):
39
+ """Implementation of a ``ColumnDataSource`` customized for planes from
40
+ `CASA`/`CNGI` image cubes. This is designed to use an `ImagePipe` to
41
+ update the image channel/plane displayed in a browser, app or notebook
42
+ with `bokeh`.
43
+
44
+ Attributes
45
+ ----------
46
+ image_source: ImagePipe
47
+ the conduit for updating the channel/plane from the image cube
48
+ """
49
+
50
+ init_script = Nullable(Instance(Callback), help="""
51
+ JavaScript to be run during initialization of an instance of an ImageDataSource object.
52
+ """)
53
+
54
+ image_source = Instance(ImagePipe)
55
+ _mask_contour_source = Nullable(Instance(ColumnDataSource), help='''
56
+ Data source for updating contour polygons. This data source is directly updated
57
+ inside JavaScript.
58
+ ''')
59
+ num_chans = Tuple( Int, Int, help="[ num-stokes-planes, num-channels ]" )
60
+ cur_chan = Tuple( Int, Int, help="[ num-stokes-planes, num-channels ]" )
61
+
62
+ __javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
63
+
64
+ def __init__( self, *args, **kwargs ):
65
+ super( ).__init__( *args, **kwargs )
66
+ mask0 = self.image_source.mask0( [0,0] )
67
+ self.data = { 'img': [ self.image_source.channel( [0,0], np.uint8 ) ],
68
+ 'msk0': [ mask0 if mask0 is not None else None ] }
69
+ if self.image_source.have_mask( ):
70
+ self.data['msk'] = [ self.image_source.mask( [0,0] ) ]
71
+ self.num_chans = list(self.image_source.shape[-2:])
72
+ self.cur_chan = [ 0, 0 ]
73
+
74
+ def mask_contour_source( self, data ):
75
+ if not self._mask_contour_source:
76
+ self._mask_contour_source = ColumnDataSource( data=data )
77
+ return self._mask_contour_source
78
+
79
+ def pixel_value( self, chan, index ):
80
+ return self.image_source.pixel_value( chan, index )
81
+
82
+ def stokes_labels( self ):
83
+ return self.image_source.stokes_labels( )