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.
- cubevis/LICENSE.rst +500 -0
- cubevis/__icons__/20px/fast-backward.svg +13 -0
- cubevis/__icons__/20px/fast-forward.svg +13 -0
- cubevis/__icons__/20px/step-backward.svg +12 -0
- cubevis/__icons__/20px/step-forward.svg +12 -0
- cubevis/__icons__/add-chan.png +0 -0
- cubevis/__icons__/add-chan.svg +84 -0
- cubevis/__icons__/add-cube.png +0 -0
- cubevis/__icons__/add-cube.svg +186 -0
- cubevis/__icons__/drag.png +0 -0
- cubevis/__icons__/drag.svg +109 -0
- cubevis/__icons__/mask-selected.png +0 -0
- cubevis/__icons__/mask.png +0 -0
- cubevis/__icons__/mask.svg +1 -0
- cubevis/__icons__/new-layer-sm-selected.png +0 -0
- cubevis/__icons__/new-layer-sm-selected.svg +88 -0
- cubevis/__icons__/new-layer-sm.png +0 -0
- cubevis/__icons__/new-layer-sm.svg +15 -0
- cubevis/__icons__/reset.png +0 -0
- cubevis/__icons__/reset.svg +11 -0
- cubevis/__icons__/sub-chan.png +0 -0
- cubevis/__icons__/sub-chan.svg +71 -0
- cubevis/__icons__/sub-cube.png +0 -0
- cubevis/__icons__/sub-cube.svg +95 -0
- cubevis/__icons__/zoom-to-fit.png +0 -0
- cubevis/__icons__/zoom-to-fit.svg +21 -0
- cubevis/__init__.py +58 -0
- cubevis/__js__/bokeh-3.6.1.min.js +728 -0
- cubevis/__js__/bokeh-tables-3.6.1.min.js +119 -0
- cubevis/__js__/bokeh-widgets-3.6.1.min.js +141 -0
- cubevis/__js__/casalib.min.js +1 -0
- cubevis/__js__/cubevisjs.min.js +62 -0
- cubevis/__version__.py +1 -0
- cubevis/apps/__init__.py +44 -0
- cubevis/apps/_createmask.py +461 -0
- cubevis/apps/_createregion.py +513 -0
- cubevis/apps/_interactiveclean.py +3260 -0
- cubevis/apps/_interactiveclean_wrappers.py +130 -0
- cubevis/apps/_ms_raster.py +815 -0
- cubevis/apps/_plotants.py +286 -0
- cubevis/apps/_plotbandpass.py +7 -0
- cubevis/bokeh/__init__.py +29 -0
- cubevis/bokeh/annotations/__init__.py +1 -0
- cubevis/bokeh/annotations/_ev_poly_annotation.py +6 -0
- cubevis/bokeh/components/__init__.py +28 -0
- cubevis/bokeh/format/__init__.py +31 -0
- cubevis/bokeh/format/_time_ticks.py +44 -0
- cubevis/bokeh/format/_wcs_ticks.py +45 -0
- cubevis/bokeh/models/__init__.py +4 -0
- cubevis/bokeh/models/_edit_span.py +7 -0
- cubevis/bokeh/models/_ev_text_input.py +6 -0
- cubevis/bokeh/models/_tip.py +37 -0
- cubevis/bokeh/models/_tip_button.py +50 -0
- cubevis/bokeh/sources/__init__.py +35 -0
- cubevis/bokeh/sources/_data_pipe.py +258 -0
- cubevis/bokeh/sources/_image_data_source.py +83 -0
- cubevis/bokeh/sources/_image_pipe.py +581 -0
- cubevis/bokeh/sources/_spectra_data_source.py +55 -0
- cubevis/bokeh/sources/_updatable_data_source.py +189 -0
- cubevis/bokeh/state/__init__.py +34 -0
- cubevis/bokeh/state/_initialize.py +164 -0
- cubevis/bokeh/state/_javascript.py +53 -0
- cubevis/bokeh/state/_palette.py +58 -0
- cubevis/bokeh/state/_session.py +44 -0
- cubevis/bokeh/state/js/bokeh-2.4.1.min.js +596 -0
- cubevis/bokeh/state/js/bokeh-gl-2.4.1.min.js +74 -0
- cubevis/bokeh/state/js/bokeh-tables-2.4.1.min.js +132 -0
- cubevis/bokeh/state/js/bokeh-widgets-2.4.1.min.js +118 -0
- cubevis/bokeh/state/js/casaguijs-v0.0.4.0-b2.4.min.js +49 -0
- cubevis/bokeh/state/js/casaguijs-v0.0.5.0-b2.4.min.js +49 -0
- cubevis/bokeh/state/js/casaguijs-v0.0.6.0-b2.4.min.js +49 -0
- cubevis/bokeh/state/js/casalib-v0.0.1.min.js +1 -0
- cubevis/bokeh/tools/__init__.py +31 -0
- cubevis/bokeh/tools/_cbreset_tool.py +52 -0
- cubevis/bokeh/tools/_drag_tool.py +61 -0
- cubevis/bokeh/utils/__init__.py +35 -0
- cubevis/bokeh/utils/_axes_labels.py +94 -0
- cubevis/bokeh/utils/_svg_icon.py +136 -0
- cubevis/data/__init__.py +1 -0
- cubevis/data/casaimage/__init__.py +114 -0
- cubevis/data/measurement_set/__init__.py +7 -0
- cubevis/data/measurement_set/_ms_data.py +178 -0
- cubevis/data/measurement_set/processing_set/__init__.py +30 -0
- cubevis/data/measurement_set/processing_set/_ps_concat.py +98 -0
- cubevis/data/measurement_set/processing_set/_ps_coords.py +78 -0
- cubevis/data/measurement_set/processing_set/_ps_data.py +213 -0
- cubevis/data/measurement_set/processing_set/_ps_io.py +55 -0
- cubevis/data/measurement_set/processing_set/_ps_raster_data.py +154 -0
- cubevis/data/measurement_set/processing_set/_ps_select.py +91 -0
- cubevis/data/measurement_set/processing_set/_ps_stats.py +218 -0
- cubevis/data/measurement_set/processing_set/_xds_data.py +149 -0
- cubevis/plot/__init__.py +1 -0
- cubevis/plot/ms_plot/__init__.py +29 -0
- cubevis/plot/ms_plot/_ms_plot.py +242 -0
- cubevis/plot/ms_plot/_ms_plot_constants.py +22 -0
- cubevis/plot/ms_plot/_ms_plot_selectors.py +348 -0
- cubevis/plot/ms_plot/_raster_plot.py +292 -0
- cubevis/plot/ms_plot/_raster_plot_inputs.py +116 -0
- cubevis/plot/ms_plot/_xds_plot_axes.py +110 -0
- cubevis/private/__java__/xml-casa-assembly-1.86.jar +0 -0
- cubevis/private/_gclean.py +798 -0
- cubevis/private/casashell/createmask.py +332 -0
- cubevis/private/casashell/iclean.py +4432 -0
- cubevis/private/casatasks/__init__.py +140 -0
- cubevis/private/casatasks/createmask.py +86 -0
- cubevis/private/casatasks/createregion.py +83 -0
- cubevis/private/casatasks/iclean.py +1831 -0
- cubevis/readme.rst +16 -0
- cubevis/remote/__init__.py +10 -0
- cubevis/remote/_gclean.py +61 -0
- cubevis/remote/_local.py +287 -0
- cubevis/remote/_remote_kernel.py +80 -0
- cubevis/toolbox/__init__.py +32 -0
- cubevis/toolbox/_app_context.py +74 -0
- cubevis/toolbox/_cube.py +3457 -0
- cubevis/toolbox/_region_list.py +197 -0
- cubevis/utils/_ResourceManager.py +86 -0
- cubevis/utils/__init__.py +620 -0
- cubevis/utils/_contextmgrchain.py +84 -0
- cubevis/utils/_conversion.py +93 -0
- cubevis/utils/_copydoc.py +55 -0
- cubevis/utils/_docenum.py +25 -0
- cubevis/utils/_import_protected_module.py +35 -0
- cubevis/utils/_logging.py +85 -0
- cubevis/utils/_pkgs.py +77 -0
- cubevis/utils/_regions.py +40 -0
- cubevis/utils/_static.py +66 -0
- cubevis/utils/_tiles.py +167 -0
- cubevis-0.5.2.dist-info/METADATA +151 -0
- cubevis-0.5.2.dist-info/RECORD +132 -0
- cubevis-0.5.2.dist-info/WHEEL +4 -0
- cubevis-0.5.2.dist-info/licenses/LICENSE +504 -0
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
'''Contains for conversion of data passed between Python and JavaScript
|
|
29
|
+
via websockets'''
|
|
30
|
+
|
|
31
|
+
import json
|
|
32
|
+
import numpy as np
|
|
33
|
+
from bokeh.util.serialization import transform_array
|
|
34
|
+
from bokeh.core.serialization import Serializer, Deserializer
|
|
35
|
+
from bokeh.core.json_encoder import serialize_json
|
|
36
|
+
from ._static import static_vars
|
|
37
|
+
|
|
38
|
+
def strip_arrays( val ):
|
|
39
|
+
'''convert all numpy arrays contained within val to lists
|
|
40
|
+
'''
|
|
41
|
+
if isinstance( val, dict ):
|
|
42
|
+
result = { }
|
|
43
|
+
for k, v in val.items( ):
|
|
44
|
+
result[k] = strip_arrays(v)
|
|
45
|
+
return result
|
|
46
|
+
if isinstance( val, np.ndarray ):
|
|
47
|
+
return val.tolist( )
|
|
48
|
+
if isinstance( val, range ):
|
|
49
|
+
return list(val)
|
|
50
|
+
return val
|
|
51
|
+
|
|
52
|
+
@static_vars( encoder=Serializer(deferred=False) )
|
|
53
|
+
def serialize( val ):
|
|
54
|
+
'''convert python values to a string that can be sent via websockets
|
|
55
|
+
'''
|
|
56
|
+
return serialize_json(serialize.encoder.serialize(val))
|
|
57
|
+
|
|
58
|
+
@static_vars( decoder=Deserializer( ) )
|
|
59
|
+
def deserialize( val ):
|
|
60
|
+
'''convert an encoded value received from websockets
|
|
61
|
+
'''
|
|
62
|
+
value = json.loads(val)
|
|
63
|
+
return deserialize.decoder.deserialize(value)
|
|
64
|
+
|
|
65
|
+
def pack_arrays( val ):
|
|
66
|
+
"""Convert `numpy` N dimensional arrays stored within a dictionary to
|
|
67
|
+
a format that can be converted into the multi-dimensional arrays that
|
|
68
|
+
are usable for Bokeh data.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
val: value
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
value
|
|
77
|
+
return value is identical to `val` parameter except that any
|
|
78
|
+
N dimensional `numpy` arrays are converted to Bokeh compatible
|
|
79
|
+
format
|
|
80
|
+
"""
|
|
81
|
+
if isinstance( val, dict ):
|
|
82
|
+
result = { }
|
|
83
|
+
for k, v in val.items( ):
|
|
84
|
+
result[k] = pack_arrays(v)
|
|
85
|
+
return result
|
|
86
|
+
if isinstance( val, np.ndarray ):
|
|
87
|
+
if isinstance(val, np.ma.MaskedArray):
|
|
88
|
+
return transform_array(val.filled(0))
|
|
89
|
+
else:
|
|
90
|
+
return transform_array(val)
|
|
91
|
+
if isinstance( val, range ):
|
|
92
|
+
return list(val)
|
|
93
|
+
return val
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
########################################################################
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2024
|
|
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
|
+
'''Reuse doc string'''
|
|
29
|
+
import os
|
|
30
|
+
|
|
31
|
+
#--------------------------------------------------
|
|
32
|
+
# ParamSpec is available with Python >= 3.10
|
|
33
|
+
#--------------------------------------------------
|
|
34
|
+
#from typing import Callable, TypeVar, ParamSpec
|
|
35
|
+
#
|
|
36
|
+
#P = ParamSpec("P")
|
|
37
|
+
#T = TypeVar("T")
|
|
38
|
+
#
|
|
39
|
+
#def copydoc(src: Callable[P, T]):
|
|
40
|
+
# """Copy documentation from src"""
|
|
41
|
+
#
|
|
42
|
+
# def decorator(dest: Callable) -> Callable[P, T]:
|
|
43
|
+
# dest.__doc__ = src.__doc__
|
|
44
|
+
# return dest
|
|
45
|
+
#
|
|
46
|
+
# return decorator
|
|
47
|
+
|
|
48
|
+
def copydoc(src):
|
|
49
|
+
"""Copy documentation from src"""
|
|
50
|
+
|
|
51
|
+
def decorator(dest):
|
|
52
|
+
dest.__doc__ = src.__doc__
|
|
53
|
+
return dest
|
|
54
|
+
|
|
55
|
+
return decorator
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
class DocEnum(Enum):
|
|
4
|
+
'''Allows for documenting individual enum values::
|
|
5
|
+
|
|
6
|
+
In [6]: class MaskMode(DocEnum):
|
|
7
|
+
...: 'Different masking modes available in addition to a user supplied mask'
|
|
8
|
+
...: PB = 1, 'primary beam mask'
|
|
9
|
+
...: AUTOMT = 2, 'multi-threshold auto masking'
|
|
10
|
+
...:
|
|
11
|
+
|
|
12
|
+
In [7]: MaskMode.PB?
|
|
13
|
+
Type: MaskMode
|
|
14
|
+
String form: MaskMode.PB
|
|
15
|
+
Docstring: primary beam mask
|
|
16
|
+
Class docstring: Different masking modes available in addition to a user supplied mask
|
|
17
|
+
|
|
18
|
+
In [8]:
|
|
19
|
+
'''
|
|
20
|
+
def __new__(cls, value, doc=None):
|
|
21
|
+
self = object.__new__(cls) # calling super().__new__(value) here would fail
|
|
22
|
+
self._value_ = value
|
|
23
|
+
if doc is not None:
|
|
24
|
+
self.__doc__ = doc
|
|
25
|
+
return self
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import importlib
|
|
3
|
+
import sys
|
|
4
|
+
import types
|
|
5
|
+
|
|
6
|
+
def ImportProtectedModule( name, file_mapping ):
|
|
7
|
+
|
|
8
|
+
class module_import(types.ModuleType):
|
|
9
|
+
"""Import objects, functions and classes for export while avoiding requiring user to
|
|
10
|
+
provide the sub-package name for the file contining the imported object, for example
|
|
11
|
+
allowing:
|
|
12
|
+
from cubevis.apps import iclean
|
|
13
|
+
|
|
14
|
+
instead of:
|
|
15
|
+
from cubevis.app._interactive_clean import iclean
|
|
16
|
+
|
|
17
|
+
while also not importing '_interactive_clean' (inside of __init__.py) regardless of
|
|
18
|
+
whether the user actually import 'iclean' or not. This is done to allow for some
|
|
19
|
+
dependencies to not be available but for the imports to still work as long as
|
|
20
|
+
the dependencies which are needed for the imported classes are available.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
_module = name
|
|
24
|
+
_mapping = file_mapping
|
|
25
|
+
|
|
26
|
+
def __getattr__(self, name):
|
|
27
|
+
if name in self._mapping:
|
|
28
|
+
m = importlib.import_module(self._mapping[name], self._module) # import submodule
|
|
29
|
+
o = getattr(m, name) # find the required member
|
|
30
|
+
setattr(sys.modules[self._module], name, o) # bind it into the package
|
|
31
|
+
return o
|
|
32
|
+
else:
|
|
33
|
+
raise AttributeError(f'module {__name__} has no attribute {name}')
|
|
34
|
+
|
|
35
|
+
return module_import
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
""" Use the python logging interface for cubevis logs.
|
|
2
|
+
If casalog is available, then send log messages to it.
|
|
3
|
+
Otherwise, use the base python logging mechanisms.
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from casatasks import casalog
|
|
9
|
+
__casalog_avail = True
|
|
10
|
+
except ImportError:
|
|
11
|
+
__casalog_avail = False
|
|
12
|
+
|
|
13
|
+
class _Logging:
|
|
14
|
+
# singleton instance
|
|
15
|
+
logger = None
|
|
16
|
+
log_handler = None
|
|
17
|
+
|
|
18
|
+
class CasalogHandler(logging.Handler):
|
|
19
|
+
""" Logs to casalog, as appropriate """
|
|
20
|
+
# map from python logging levels to casalog priorities
|
|
21
|
+
level_to_priority = {
|
|
22
|
+
"DEBUG": "DEBUG",
|
|
23
|
+
"INFO": "INFO",
|
|
24
|
+
"WARNING": "WARN",
|
|
25
|
+
"ERROR": "SEVERE",
|
|
26
|
+
"CRITICAL": "SEVERE"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def _level_to_levelname(self, level):
|
|
30
|
+
if (level >= 50):
|
|
31
|
+
return "CRITICAL"
|
|
32
|
+
if (level >= 40):
|
|
33
|
+
return "ERROR"
|
|
34
|
+
if (level >= 30):
|
|
35
|
+
return "WARNING"
|
|
36
|
+
if (level >= 20):
|
|
37
|
+
return "INFO"
|
|
38
|
+
return "DEBUG"
|
|
39
|
+
|
|
40
|
+
def setLevel(self, level_or_levelname):
|
|
41
|
+
super().setLevel(level_or_levelname)
|
|
42
|
+
if type(level_or_levelname) != str:
|
|
43
|
+
levelname = self._level_to_levelname(level_or_levelname)
|
|
44
|
+
else:
|
|
45
|
+
levelname = level_or_levelname
|
|
46
|
+
casalog.filter(CasalogHandler.level_to_priority[levelname])
|
|
47
|
+
|
|
48
|
+
def emit(self, record):
|
|
49
|
+
# promote log level for casalog
|
|
50
|
+
levelname = self._level_to_levelname(_Logging.logger.getEffectiveLevel())
|
|
51
|
+
if levelname == "DEBUG":
|
|
52
|
+
casalog.filter(CasalogHandler.level_to_priority["DEBUG"])
|
|
53
|
+
|
|
54
|
+
# get log properties
|
|
55
|
+
origin = (record.filename) if (record.filename != None) else ("")
|
|
56
|
+
casalog.origin(origin)
|
|
57
|
+
priority = CasalogHandler.level_to_priority[record.levelname]
|
|
58
|
+
self.format(record) # populates record.message
|
|
59
|
+
|
|
60
|
+
# emit the log
|
|
61
|
+
casalog.post(message=record.message, priority=priority, origin=record.funcName)
|
|
62
|
+
|
|
63
|
+
class CasalogLogger(logging.getLoggerClass()):
|
|
64
|
+
def setLevel(self, level):
|
|
65
|
+
super().setLevel(level)
|
|
66
|
+
_Logging.log_handler.setLevel(level)
|
|
67
|
+
|
|
68
|
+
def get_logger():
|
|
69
|
+
""" Returns the singleton logger instance. """
|
|
70
|
+
if _Logging.logger != None:
|
|
71
|
+
return _Logging.logger
|
|
72
|
+
|
|
73
|
+
if __casalog_avail:
|
|
74
|
+
_Logging.log_handler = CasalogHandler()
|
|
75
|
+
|
|
76
|
+
curr_logger_class = logging.getLoggerClass()
|
|
77
|
+
logging.setLoggerClass(CasalogLogger)
|
|
78
|
+
_Logging.logger = logging.getLogger(__name__)
|
|
79
|
+
logging.setLoggerClass(curr_logger_class)
|
|
80
|
+
|
|
81
|
+
_Logging.logger.addHandler(_Logging.log_handler)
|
|
82
|
+
_Logging.logger.propagate = False
|
|
83
|
+
else:
|
|
84
|
+
_Logging.logger = logging.getLogger(__name__)
|
|
85
|
+
return _Logging.logger
|
cubevis/utils/_pkgs.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import importlib.util
|
|
4
|
+
from os.path import join, exists, realpath
|
|
5
|
+
|
|
6
|
+
def find_pkg( name, extra_dirs=[] ):
|
|
7
|
+
"""
|
|
8
|
+
Returns list of paths which contain the ``name`` pkg. It is assumed that the
|
|
9
|
+
``name`` will be the name of a directory (containing the package) or that
|
|
10
|
+
``name.py(c)`` will be a file that exists. This does not attempt to import the
|
|
11
|
+
package.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
name: string
|
|
16
|
+
the name of the package in python "dot" notation to be searched for
|
|
17
|
+
extra_dirs: list of strings
|
|
18
|
+
directories to be searched in addition to those included in ``sys.path``
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
list of import specifications
|
|
23
|
+
specifications of ``name`` found by search ``sys.path`` and ``extra_dirs``
|
|
24
|
+
"""
|
|
25
|
+
result = [ ]
|
|
26
|
+
filename = (name.split('.'))
|
|
27
|
+
for dir in sys.path[:] + extra_dirs:
|
|
28
|
+
path = join( dir, *filename )
|
|
29
|
+
if exists(path):
|
|
30
|
+
spec = importlib.util.spec_from_file_location(name,path)
|
|
31
|
+
if spec:
|
|
32
|
+
result.append(spec)
|
|
33
|
+
else:
|
|
34
|
+
path = f"{path}.py"
|
|
35
|
+
if exists(path):
|
|
36
|
+
spec = importlib.util.spec_from_file_location(name,path)
|
|
37
|
+
if spec:
|
|
38
|
+
result.append(spec)
|
|
39
|
+
else:
|
|
40
|
+
path = f"{path}c"
|
|
41
|
+
if exists(path):
|
|
42
|
+
spec = importlib.util.spec_from_file_location(name,path)
|
|
43
|
+
if spec:
|
|
44
|
+
result.append(spec)
|
|
45
|
+
return result
|
|
46
|
+
|
|
47
|
+
def load_pkg( spec ):
|
|
48
|
+
"""
|
|
49
|
+
Loads and returns the module specified by ``spec`` (e.g. as returned from ``find_pkg``)
|
|
50
|
+
If ``spec.name`` does not already exist in ``sys.modules``, it is added.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
spec: ModuleSpec
|
|
55
|
+
the module to be loaded
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
module
|
|
60
|
+
the loaded module
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
import _frozen_importlib
|
|
64
|
+
if not isinstance(spec,_frozen_importlib.ModuleSpec):
|
|
65
|
+
raise ImportError(f"Expected ModuleSpec instead of {type(spec)}")
|
|
66
|
+
|
|
67
|
+
pkg = importlib.util.module_from_spec(spec)
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
spec.loader.exec_module(pkg)
|
|
71
|
+
except:
|
|
72
|
+
raise ImportError(f"Could not load {spec.origin}")
|
|
73
|
+
|
|
74
|
+
if spec.name not in sys.modules:
|
|
75
|
+
sys.modules[spec.name] = pkg
|
|
76
|
+
|
|
77
|
+
return pkg
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
from itertools import product
|
|
3
|
+
from matplotlib.path import Path
|
|
4
|
+
from math import floor,ceil
|
|
5
|
+
|
|
6
|
+
def polygon_indexes( xs, ys, shape ):
|
|
7
|
+
'''
|
|
8
|
+
Returns indexes for a 2D array of the given ``shape`` which
|
|
9
|
+
lie within the polygon specified by ``xs`` and ``ys``.
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
xs: list of numbers
|
|
14
|
+
the X coordinates for the vertices of the polygon
|
|
15
|
+
ys: list of numbers
|
|
16
|
+
the Y coordinates for the vertices of the polygon
|
|
17
|
+
shape: ( int, int )
|
|
18
|
+
the shape of the plane in which the polygon is found
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
generator of tuples:
|
|
23
|
+
the stream of tuples that is returned will be the indexes
|
|
24
|
+
of the elements which lie within the polygon
|
|
25
|
+
'''
|
|
26
|
+
assert len(shape) == 2, 'contains only works for 2D shapes, so "shape" should have length equal to two'
|
|
27
|
+
assert len(xs) == len(ys), 'to specify a polygon the number of X values must equal the number of Y values'
|
|
28
|
+
|
|
29
|
+
if len(xs) == 4 and len(ys) == 4 :
|
|
30
|
+
uniqx = sorted(set(xs))
|
|
31
|
+
uniqy = sorted(set(ys))
|
|
32
|
+
if len(uniqx) == 2 and len(uniqy) == 2:
|
|
33
|
+
### we have a proper box, the Path.contains_point implementation seems to
|
|
34
|
+
### err slightly with very small regions...
|
|
35
|
+
return product(range(max(floor(uniqx[0]),0),min(ceil(uniqx[1]),shape[0]-1)),range(max(floor(uniqy[0]),0),min(ceil(uniqy[1]),shape[1]-1)))
|
|
36
|
+
|
|
37
|
+
path = Path(list(zip(xs,ys)))
|
|
38
|
+
xmin, xmax = max([0,min(xs)-1]), max(xs)+1
|
|
39
|
+
ymin, ymax = max([0,min(ys)-1]), max(ys)+1
|
|
40
|
+
return filter( path.contains_point, product(range(max(floor(xmin),0),min(ceil(xmax),shape[0]-1)), range(max(floor(ymin),0),min(ceil(ymax),shape[1]-1))) )
|
cubevis/utils/_static.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
'''Contains functions for attaching static value as attributes of functions'''
|
|
29
|
+
|
|
30
|
+
def static_vars(**kwargs):
|
|
31
|
+
'''Initialize static function variables to for use within a function.
|
|
32
|
+
|
|
33
|
+
This function is used as a decorator which allows for the initialization of
|
|
34
|
+
static local variables for use within a function. It is used like:
|
|
35
|
+
|
|
36
|
+
@static_vars(counter=0)
|
|
37
|
+
def foo():
|
|
38
|
+
foo.counter += 1
|
|
39
|
+
print "Counter is %d" % foo.counter
|
|
40
|
+
|
|
41
|
+
This is used from:
|
|
42
|
+
https://stackoverflow.com/questions/279561/what-is-the-python-equivalent-of-static-variables-inside-a-function?rq=1
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
Initialized static local variables.
|
|
47
|
+
'''
|
|
48
|
+
|
|
49
|
+
def decorate(func):
|
|
50
|
+
for k, v in kwargs.items():
|
|
51
|
+
setattr(func, k, v)
|
|
52
|
+
return func
|
|
53
|
+
|
|
54
|
+
return decorate
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def static_dir(func):
|
|
58
|
+
'''return a list of static variables associated with ``func``
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
func: function
|
|
63
|
+
function with static variables
|
|
64
|
+
'''
|
|
65
|
+
return [a for a in dir(func) if a[0] != '_']
|
|
66
|
+
|
cubevis/utils/_tiles.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
########################################################################
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 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
|
+
'''tiling utilities'''
|
|
29
|
+
|
|
30
|
+
from math import ceil, log
|
|
31
|
+
|
|
32
|
+
class TMSTiles(object):
|
|
33
|
+
###
|
|
34
|
+
### This class is based upon https://github.com/OSGeo/gdal/blob/master/swig/python/gdal-utils/osgeo_utils/gdal2tiles.py
|
|
35
|
+
### written by: Klokan Petr Pridal (klokan at klokan dot cz)
|
|
36
|
+
### Even Rouault (even dot rouault at spatialys.com)
|
|
37
|
+
### Idan Miara (idan@miara.com)
|
|
38
|
+
### It is a small subset of the original source code, and it is highly modified to only
|
|
39
|
+
### handle raster profile tiling. The original source code has many useful details and
|
|
40
|
+
### supports mercator and geodetic data. The subset of gdal2tiles.py used here is less
|
|
41
|
+
### than 3% of the original file (42 lines out of 1697). While this is not considered
|
|
42
|
+
### a substantial portion, it was invaluable for understanding the TMS tile layout.
|
|
43
|
+
###
|
|
44
|
+
def __init__( self, dimension, title='' ):
|
|
45
|
+
'''
|
|
46
|
+
Construct a tiling object which will provide the coordinates of tiles, the size to
|
|
47
|
+
be read, the size of the scaled tile and the write offset within the :code:`tile_size`
|
|
48
|
+
output tile.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
dimension: list or tuple
|
|
53
|
+
should contain at least two elements (X,Y)
|
|
54
|
+
'''
|
|
55
|
+
|
|
56
|
+
def calculate_indexes( tz, result={ } ):
|
|
57
|
+
|
|
58
|
+
ti = 0
|
|
59
|
+
tile_details = []
|
|
60
|
+
tminx, tminy, tmaxx, tmaxy = self.tminmax[tz]
|
|
61
|
+
|
|
62
|
+
for ty in range(tmaxy, tminy - 1, -1):
|
|
63
|
+
for tx in range(tminx, tmaxx + 1):
|
|
64
|
+
ti += 1
|
|
65
|
+
ytile = ty
|
|
66
|
+
index = ( tz, tx, ty )
|
|
67
|
+
|
|
68
|
+
tsize = int(self.tsize[tz]) # tile_size in raster coordinates for actual zoom
|
|
69
|
+
xsize = self.__dimension[0]
|
|
70
|
+
ysize = self.__dimension[1]
|
|
71
|
+
querysize = self.__tile_size
|
|
72
|
+
|
|
73
|
+
rx = tx * tsize
|
|
74
|
+
rxsize = 0
|
|
75
|
+
if tx == tmaxx:
|
|
76
|
+
rxsize = xsize % tsize
|
|
77
|
+
if rxsize == 0:
|
|
78
|
+
rxsize = tsize
|
|
79
|
+
|
|
80
|
+
ry = ty * tsize
|
|
81
|
+
rysize = 0
|
|
82
|
+
if ty == tmaxy:
|
|
83
|
+
rysize = ysize % tsize
|
|
84
|
+
if rysize == 0:
|
|
85
|
+
rysize = tsize
|
|
86
|
+
|
|
87
|
+
wx, wy = 0, 0
|
|
88
|
+
wxsize = int(rxsize / float(tsize) * self.__tile_size)
|
|
89
|
+
wysize = int(rysize / float(tsize) * self.__tile_size)
|
|
90
|
+
|
|
91
|
+
ry = ysize - (ty * tsize) - rysize
|
|
92
|
+
if wysize != self.__tile_size:
|
|
93
|
+
wy = self.__tile_size - wysize
|
|
94
|
+
|
|
95
|
+
result[index] = dict( src=dict( idx=(rx, ry), dim=(rxsize, rysize),
|
|
96
|
+
t=(( 0, 0 if rysize == tsize else tsize - rysize ), tsize ) ),
|
|
97
|
+
dst=dict( idx=(wx, wy), dim=(wxsize, wysize) ) )
|
|
98
|
+
return result
|
|
99
|
+
|
|
100
|
+
self.__title = title
|
|
101
|
+
self.__tile_size = 256
|
|
102
|
+
self.__dimension = dimension
|
|
103
|
+
self.__zoom = (0, max( 0, int( max( ceil(log(dimension[0] / float(self.__tile_size), 2 )),
|
|
104
|
+
ceil( log(dimension[1] / float(self.__tile_size), 2 )) ) ) ) )
|
|
105
|
+
native_zoom = self.__zoom[1]
|
|
106
|
+
|
|
107
|
+
# Generate table with min max tile coordinates for all zoomlevels
|
|
108
|
+
self.tminmax = list(range(self.__zoom[0], self.__zoom[1] + 1))
|
|
109
|
+
self.tsize = list(range(self.__zoom[0], self.__zoom[1] + 1))
|
|
110
|
+
for tz in range(self.__zoom[0], self.__zoom[1] + 1):
|
|
111
|
+
tsize = 2.0**(native_zoom - tz) * self.__tile_size
|
|
112
|
+
tminx, tminy = 0, 0
|
|
113
|
+
tmaxx = int(ceil(dimension[0] / tsize)) - 1
|
|
114
|
+
tmaxy = int(ceil(dimension[1] / tsize)) - 1
|
|
115
|
+
self.tsize[tz] = ceil(tsize)
|
|
116
|
+
self.tminmax[tz] = (tminx, tminy, tmaxx, tmaxy)
|
|
117
|
+
|
|
118
|
+
self.__units_per_pixel = { }
|
|
119
|
+
for z in range(self.__zoom[0], self.__zoom[1] + 1):
|
|
120
|
+
self.__units_per_pixel[z] = 2**(self.__zoom[1] - z)
|
|
121
|
+
|
|
122
|
+
self.__tile_details = { }
|
|
123
|
+
for z in range(self.__zoom[0], self.__zoom[1] + 1):
|
|
124
|
+
calculate_indexes( z, self.__tile_details )
|
|
125
|
+
|
|
126
|
+
def profile( self ):
|
|
127
|
+
return "raster"
|
|
128
|
+
|
|
129
|
+
def dim( self ):
|
|
130
|
+
return self.__dimension
|
|
131
|
+
|
|
132
|
+
def tile_size( self ):
|
|
133
|
+
return self.__tile_size
|
|
134
|
+
|
|
135
|
+
def tile( self, z, x=None, y=None ):
|
|
136
|
+
try:
|
|
137
|
+
if (type(z) == tuple or type(z) == list) and len(z) == 3 and x is None and y is None:
|
|
138
|
+
return self.__tile_details[ (int(z[0]),int(z[1]),int(z[2])) ]
|
|
139
|
+
elif type(x) is not None and type(y) is not None:
|
|
140
|
+
return self.__tile_details[ (int(z),int(x),int(y)) ]
|
|
141
|
+
else:
|
|
142
|
+
raise RuntimeError( 'tile requires 3 integer parameters or 1 tuple[3] parameter' )
|
|
143
|
+
except KeyError:
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
def zoom_levels( self, reverse=False ):
|
|
147
|
+
return sorted( list(self.__units_per_pixel.keys( )), reverse=reverse )
|
|
148
|
+
|
|
149
|
+
def units_per_pixel( self, zoom_level ):
|
|
150
|
+
if zoom_level not in self.__units_per_pixel:
|
|
151
|
+
raise RuntimeError(f'''{zoom_level} is not an existing zoom level''')
|
|
152
|
+
return self.__units_per_pixel[zoom_level]
|
|
153
|
+
|
|
154
|
+
def __str__( self ):
|
|
155
|
+
return f'''<?xml version="1.0" encoding="utf-8"?>
|
|
156
|
+
<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">
|
|
157
|
+
<Title>{self.__title}</Title>
|
|
158
|
+
<Abstract></Abstract>
|
|
159
|
+
<SRS></SRS>
|
|
160
|
+
<BoundingBox minx="0.0000" miny="0.0000" maxx="{self.__dimension[0]}.0000" maxy="{self.__dimension[1]}.0000"/>
|
|
161
|
+
<Origin x="0.0000" y="0.0000"/>
|
|
162
|
+
<TileFormat width="256" height="256" mime-type="image/png" extension="png"/>
|
|
163
|
+
<TileSets profile="raster">''' + "\n" + '\n'.join( map( lambda z: f''' <TileSet href="{z}" units-per-pixel="{self.units_per_pixel(z)}.0000" order="{z}"/>''',
|
|
164
|
+
self.zoom_levels( ) ) ) + f'''
|
|
165
|
+
</TileSets>
|
|
166
|
+
</TileMap>
|
|
167
|
+
'''
|