xradio 1.0.2__tar.gz → 1.1.1__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.
Files changed (83) hide show
  1. {xradio-1.0.2/src/xradio.egg-info → xradio-1.1.1}/PKG-INFO +65 -23
  2. {xradio-1.0.2 → xradio-1.1.1}/README.md +19 -10
  3. {xradio-1.0.2 → xradio-1.1.1}/pyproject.toml +42 -8
  4. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/_utils/_casacore/casacore_from_casatools.py +75 -9
  5. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/_utils/dict_helpers.py +38 -7
  6. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/_utils/list_and_array.py +26 -3
  7. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/_utils/schema.py +44 -0
  8. xradio-1.1.1/src/xradio/_utils/xarray_helpers.py +63 -0
  9. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/_utils/zarr/common.py +4 -2
  10. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/__init__.py +4 -2
  11. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/_casacore/common.py +2 -1
  12. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/_casacore/xds_from_casacore.py +144 -92
  13. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/_casacore/xds_to_casacore.py +118 -53
  14. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/_fits/xds_from_fits.py +125 -37
  15. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/_zarr/common.py +0 -1
  16. xradio-1.1.1/src/xradio/image/_util/casacore.py +303 -0
  17. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/common.py +10 -8
  18. xradio-1.1.1/src/xradio/image/_util/image_factory.py +709 -0
  19. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/image.py +72 -100
  20. xradio-1.1.1/src/xradio/image/image_xds.py +262 -0
  21. xradio-1.1.1/src/xradio/image/schema.py +85 -0
  22. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/__init__.py +5 -4
  23. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/_tables/read.py +4 -3
  24. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/conversion.py +6 -9
  25. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/create_field_and_source_xds.py +1 -0
  26. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_utils/interpolate.py +5 -0
  27. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_utils/partition_attrs.py +0 -1
  28. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/convert_msv2_to_processing_set.py +9 -9
  29. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/load_processing_set.py +2 -2
  30. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/measurement_set_xdt.py +83 -93
  31. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/open_processing_set.py +1 -1
  32. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/processing_set_xdt.py +33 -26
  33. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/schema/check.py +70 -19
  34. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/schema/common.py +0 -1
  35. xradio-1.1.1/src/xradio/testing/__init__.py +0 -0
  36. xradio-1.1.1/src/xradio/testing/_utils/__template__.py +58 -0
  37. xradio-1.1.1/src/xradio/testing/measurement_set/__init__.py +58 -0
  38. xradio-1.1.1/src/xradio/testing/measurement_set/checker.py +131 -0
  39. xradio-1.1.1/src/xradio/testing/measurement_set/io.py +22 -0
  40. xradio-1.1.1/src/xradio/testing/measurement_set/msv2_io.py +1854 -0
  41. {xradio-1.0.2 → xradio-1.1.1/src/xradio.egg-info}/PKG-INFO +65 -23
  42. {xradio-1.0.2 → xradio-1.1.1}/src/xradio.egg-info/SOURCES.txt +10 -1
  43. {xradio-1.0.2 → xradio-1.1.1}/src/xradio.egg-info/requires.txt +46 -10
  44. xradio-1.0.2/src/xradio/image/_util/casacore.py +0 -145
  45. xradio-1.0.2/src/xradio/image/_util/image_factory.py +0 -267
  46. {xradio-1.0.2 → xradio-1.1.1}/LICENSE.txt +0 -0
  47. {xradio-1.0.2 → xradio-1.1.1}/MANIFEST.in +0 -0
  48. {xradio-1.0.2 → xradio-1.1.1}/setup.cfg +0 -0
  49. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/__init__.py +0 -0
  50. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/_utils/__init__.py +0 -0
  51. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/_utils/_casacore/tables.py +0 -0
  52. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/_utils/coord_math.py +0 -0
  53. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/_utils/zarr/__init__.py +0 -0
  54. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/__init__.py +0 -0
  55. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/_casacore/__init__.py +0 -0
  56. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/_zarr/xds_from_zarr.py +0 -0
  57. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/_zarr/xds_to_zarr.py +0 -0
  58. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/_zarr/zarr_low_level.py +0 -0
  59. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/image/_util/zarr.py +0 -0
  60. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/__init__.py +0 -0
  61. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/__init__.py +0 -0
  62. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/_tables/read_main_table.py +0 -0
  63. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/_tables/table_query.py +0 -0
  64. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/create_antenna_xds.py +0 -0
  65. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/msv2_to_msv4_meta.py +0 -0
  66. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py +0 -0
  67. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/msv4_sub_xdss.py +1 -1
  68. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/optimised_functions.py +0 -0
  69. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/partition_queries.py +0 -0
  70. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_msv2/subtables.py +0 -0
  71. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_utils/stokes_types.py +0 -0
  72. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/_utils/_zarr/encoding.py +0 -0
  73. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/measurement_set/schema.py +0 -0
  74. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/schema/__init__.py +0 -0
  75. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/schema/bases.py +0 -0
  76. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/schema/dataclass.py +0 -0
  77. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/schema/export.py +0 -0
  78. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/schema/metamodel.py +0 -0
  79. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/schema/typing.py +0 -0
  80. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/sphinx/__init__.py +0 -0
  81. {xradio-1.0.2 → xradio-1.1.1}/src/xradio/sphinx/schema_table.py +0 -0
  82. {xradio-1.0.2 → xradio-1.1.1}/src/xradio.egg-info/dependency_links.txt +0 -0
  83. {xradio-1.0.2 → xradio-1.1.1}/src/xradio.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xradio
3
- Version: 1.0.2
3
+ Version: 1.1.1
4
4
  Summary: Xarray Radio Astronomy Data IO
5
5
  Author-email: Jan-Willem Steeb <jsteeb@nrao.edu>, Federico Montesino Pouzols <pouzols@eso.edu>, Dave Mehringer <dmehring@nrao.edu>, Peter Wortmann <peter.wortmann@skao.int>
6
6
  License: BSD 3-Clause License
@@ -40,24 +40,48 @@ License: BSD 3-Clause License
40
40
  Requires-Python: <3.14,>=3.11
41
41
  Description-Content-Type: text/markdown
42
42
  License-File: LICENSE.txt
43
- Requires-Dist: astropy
44
- Requires-Dist: toolviper>=0.0.12
45
- Requires-Dist: numba>=0.57.0
46
- Requires-Dist: s3fs
47
- Requires-Dist: scipy
48
43
  Requires-Dist: xarray
49
- Requires-Dist: zarr<3,>=2
50
- Requires-Dist: pyarrow
51
- Requires-Dist: typeguard
52
- Requires-Dist: numcodecs<0.16
53
- Requires-Dist: psutil
44
+ Provides-Extra: zarr
45
+ Requires-Dist: astropy; extra == "zarr"
46
+ Requires-Dist: toolviper>=0.0.12; extra == "zarr"
47
+ Requires-Dist: s3fs; extra == "zarr"
48
+ Requires-Dist: scipy; extra == "zarr"
49
+ Requires-Dist: xarray; extra == "zarr"
50
+ Requires-Dist: zarr<3,>=2; extra == "zarr"
51
+ Requires-Dist: pyarrow; extra == "zarr"
52
+ Requires-Dist: psutil; extra == "zarr"
54
53
  Provides-Extra: test
54
+ Requires-Dist: astropy; extra == "test"
55
+ Requires-Dist: toolviper>=0.0.12; extra == "test"
56
+ Requires-Dist: s3fs; extra == "test"
57
+ Requires-Dist: scipy; extra == "test"
58
+ Requires-Dist: xarray; extra == "test"
59
+ Requires-Dist: zarr<3,>=2; extra == "test"
60
+ Requires-Dist: pyarrow; extra == "test"
61
+ Requires-Dist: psutil; extra == "test"
55
62
  Requires-Dist: pytest; extra == "test"
56
63
  Requires-Dist: pytest-cov; extra == "test"
57
64
  Requires-Dist: pytest-html; extra == "test"
58
- Provides-Extra: python-casacore
59
- Requires-Dist: python_casacore>=3.6.1; sys_platform != "darwin" and extra == "python-casacore"
65
+ Requires-Dist: python_casacore>=3.6.1; sys_platform != "darwin" and extra == "test"
66
+ Provides-Extra: casacore
67
+ Requires-Dist: astropy; extra == "casacore"
68
+ Requires-Dist: toolviper>=0.0.12; extra == "casacore"
69
+ Requires-Dist: s3fs; extra == "casacore"
70
+ Requires-Dist: scipy; extra == "casacore"
71
+ Requires-Dist: xarray; extra == "casacore"
72
+ Requires-Dist: zarr<3,>=2; extra == "casacore"
73
+ Requires-Dist: pyarrow; extra == "casacore"
74
+ Requires-Dist: psutil; extra == "casacore"
75
+ Requires-Dist: python_casacore>=3.6.1; sys_platform != "darwin" and extra == "casacore"
60
76
  Provides-Extra: interactive
77
+ Requires-Dist: astropy; extra == "interactive"
78
+ Requires-Dist: toolviper>=0.0.12; extra == "interactive"
79
+ Requires-Dist: s3fs; extra == "interactive"
80
+ Requires-Dist: scipy; extra == "interactive"
81
+ Requires-Dist: xarray; extra == "interactive"
82
+ Requires-Dist: zarr<3,>=2; extra == "interactive"
83
+ Requires-Dist: pyarrow; extra == "interactive"
84
+ Requires-Dist: psutil; extra == "interactive"
61
85
  Requires-Dist: matplotlib; extra == "interactive"
62
86
  Requires-Dist: prettytable; extra == "interactive"
63
87
  Requires-Dist: jupyterlab; extra == "interactive"
@@ -74,7 +98,16 @@ Requires-Dist: sphinx-autosummary-accessors; extra == "docs"
74
98
  Requires-Dist: sphinx_rtd_theme; extra == "docs"
75
99
  Requires-Dist: twine; extra == "docs"
76
100
  Requires-Dist: pandoc; extra == "docs"
101
+ Requires-Dist: toolviper; extra == "docs"
77
102
  Provides-Extra: all
103
+ Requires-Dist: astropy; extra == "all"
104
+ Requires-Dist: toolviper>=0.0.12; extra == "all"
105
+ Requires-Dist: s3fs; extra == "all"
106
+ Requires-Dist: scipy; extra == "all"
107
+ Requires-Dist: xarray; extra == "all"
108
+ Requires-Dist: zarr<3,>=2; extra == "all"
109
+ Requires-Dist: pyarrow; extra == "all"
110
+ Requires-Dist: psutil; extra == "all"
78
111
  Requires-Dist: pytest; extra == "all"
79
112
  Requires-Dist: pytest-cov; extra == "all"
80
113
  Requires-Dist: pytest-html; extra == "all"
@@ -119,28 +152,33 @@ XRADIO can now be installed using:
119
152
  ```sh
120
153
  pip install xradio
121
154
  ```
122
- This will also install the minimal dependencies for XRADIO.
155
+ This installs only the minimal dependencies for XRADIO, which allow you to use the schema checker and export schemas to JSON. **Note that if only the minimal dependencies are installed, the functionality to open data stored using zarr and to convert MSv2 to MSv4 will not be available.**
123
156
 
124
- Note that if only the minimal dependencies are installed, the functionality to convert MSv2 to MSv4 will not be available.
125
- This requires installing `python-casacore` (also included in the `all` group, see below), or alternatively the
126
- `casatools` backend, as explained in the [casatools I/O backend guide](docs/source/measurement_set/guides/backends.md).
157
+ To install the zarr backend use:
158
+ ```sh
159
+ pip install "xradio[zarr]"
160
+ ```
161
+ This allows for opening data stored using zarr.
127
162
 
128
- To install the minimal dependencies and the interactive components (JupyterLab) use:
163
+ To install the zarr backend and the interactive components (JupyterLab) use:
129
164
  ```sh
130
165
  pip install "xradio[interactive]"
131
166
  ```
132
167
 
133
- To enable conversion from MSv2 to MSv4 use (this only works for Linux):
168
+ To install the casacore backend along with the zarr backend which enables conversion from MSv2 to MSv4 use (this only works for Linux):
134
169
  ```sh
135
- pip install "xradio[python-casacore]"
170
+ pip install "xradio[casacore]"
136
171
  ```
137
- To be able to run tests:
172
+
173
+ To installs all the needed packages to run the unit tests:
138
174
  ```sh
139
175
  pip install "xradio[test]"
140
176
  ```
177
+ This also installs the zarr backend and the casacore backend on Linux. Note the tests will fail on MacOS if python-casacore is not installed separately using conda.
178
+
141
179
  Multiple-dependencies can be installed using:
142
180
  ```sh
143
- pip install "xradio[interactive,python-casacore,test]"
181
+ pip install "xradio[interactive,casacore,test]"
144
182
  ```
145
183
 
146
184
  To install a more complete set of dependencies:
@@ -148,4 +186,8 @@ To install a more complete set of dependencies:
148
186
  pip install "xradio[all]"
149
187
  ```
150
188
  This will include the dependencies required to run the interactive Jupyter notebooks, run tests, build documentation,
151
- and python-casacore to enable MSv2=>MSv4 functionality.
189
+ and python-casacore to enable MSv2=>MSv4 functionality on Linux.
190
+
191
+ Instruction of how to setup a developer environment can be found at [Development](https://xradio.readthedocs.io/en/latest/development.html).
192
+
193
+ Instruction of how to setup a developer environment using casatools instead of python-casacore can be found at [casatools I/O backend guide](docs/source/measurement_set/guides/backends.md).
@@ -21,28 +21,33 @@ XRADIO can now be installed using:
21
21
  ```sh
22
22
  pip install xradio
23
23
  ```
24
- This will also install the minimal dependencies for XRADIO.
24
+ This installs only the minimal dependencies for XRADIO, which allow you to use the schema checker and export schemas to JSON. **Note that if only the minimal dependencies are installed, the functionality to open data stored using zarr and to convert MSv2 to MSv4 will not be available.**
25
25
 
26
- Note that if only the minimal dependencies are installed, the functionality to convert MSv2 to MSv4 will not be available.
27
- This requires installing `python-casacore` (also included in the `all` group, see below), or alternatively the
28
- `casatools` backend, as explained in the [casatools I/O backend guide](docs/source/measurement_set/guides/backends.md).
26
+ To install the zarr backend use:
27
+ ```sh
28
+ pip install "xradio[zarr]"
29
+ ```
30
+ This allows for opening data stored using zarr.
29
31
 
30
- To install the minimal dependencies and the interactive components (JupyterLab) use:
32
+ To install the zarr backend and the interactive components (JupyterLab) use:
31
33
  ```sh
32
34
  pip install "xradio[interactive]"
33
35
  ```
34
36
 
35
- To enable conversion from MSv2 to MSv4 use (this only works for Linux):
37
+ To install the casacore backend along with the zarr backend which enables conversion from MSv2 to MSv4 use (this only works for Linux):
36
38
  ```sh
37
- pip install "xradio[python-casacore]"
39
+ pip install "xradio[casacore]"
38
40
  ```
39
- To be able to run tests:
41
+
42
+ To installs all the needed packages to run the unit tests:
40
43
  ```sh
41
44
  pip install "xradio[test]"
42
45
  ```
46
+ This also installs the zarr backend and the casacore backend on Linux. Note the tests will fail on MacOS if python-casacore is not installed separately using conda.
47
+
43
48
  Multiple-dependencies can be installed using:
44
49
  ```sh
45
- pip install "xradio[interactive,python-casacore,test]"
50
+ pip install "xradio[interactive,casacore,test]"
46
51
  ```
47
52
 
48
53
  To install a more complete set of dependencies:
@@ -50,4 +55,8 @@ To install a more complete set of dependencies:
50
55
  pip install "xradio[all]"
51
56
  ```
52
57
  This will include the dependencies required to run the interactive Jupyter notebooks, run tests, build documentation,
53
- and python-casacore to enable MSv2=>MSv4 functionality.
58
+ and python-casacore to enable MSv2=>MSv4 functionality on Linux.
59
+
60
+ Instruction of how to setup a developer environment can be found at [Development](https://xradio.readthedocs.io/en/latest/development.html).
61
+
62
+ Instruction of how to setup a developer environment using casatools instead of python-casacore can be found at [casatools I/O backend guide](docs/source/measurement_set/guides/backends.md).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "xradio"
3
- version = "v1.0.2"
3
+ version = "v1.1.1"
4
4
  description = " Xarray Radio Astronomy Data IO"
5
5
  authors = [
6
6
  {name = "Jan-Willem Steeb", email="jsteeb@nrao.edu"},
@@ -13,36 +13,61 @@ readme = "README.md"
13
13
  requires-python = ">= 3.11, < 3.14"
14
14
 
15
15
  dependencies = [
16
+ 'xarray'
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ zarr = [
16
21
  'astropy',
17
22
  'toolviper>=0.0.12',
18
- 'numba>=0.57.0',
19
23
  's3fs',
20
24
  'scipy',
21
25
  'xarray',
22
26
  'zarr>=2,<3',
23
27
  'pyarrow',
24
- 'typeguard',
25
- 'numcodecs<0.16',
26
28
  'psutil' # psutil is needed so large FITS images are not loaded into memory
27
29
  ]
28
-
29
30
  # toolviper has the following key dependencies also directly used by XRADIO (https://github.com/casangi/toolviper/blob/main/pyproject.toml):
30
31
  # 'numpy',
31
32
  # 'dask',
32
33
  # 'distributed',
33
34
 
34
- [project.optional-dependencies]
35
35
  test = [
36
+ 'astropy',
37
+ 'toolviper>=0.0.12',
38
+ 's3fs',
39
+ 'scipy',
40
+ 'xarray',
41
+ 'zarr>=2,<3',
42
+ 'pyarrow',
43
+ 'psutil',
36
44
  'pytest',
37
45
  'pytest-cov',
38
46
  'pytest-html',
47
+ 'python_casacore>=3.6.1; sys_platform != "darwin"'
39
48
  ]
40
49
 
41
- python_casacore = [
50
+ casacore = [
51
+ 'astropy',
52
+ 'toolviper>=0.0.12',
53
+ 's3fs',
54
+ 'scipy',
55
+ 'xarray',
56
+ 'zarr>=2,<3',
57
+ 'pyarrow',
58
+ 'psutil',
42
59
  'python_casacore>=3.6.1; sys_platform != "darwin"'
43
60
  ]
44
61
 
45
62
  interactive = [
63
+ 'astropy',
64
+ 'toolviper>=0.0.12',
65
+ 's3fs',
66
+ 'scipy',
67
+ 'xarray',
68
+ 'zarr>=2,<3',
69
+ 'pyarrow',
70
+ 'psutil',
46
71
  'matplotlib',
47
72
  'prettytable',
48
73
  'jupyterlab',
@@ -60,10 +85,19 @@ docs = [
60
85
  'sphinx-autosummary-accessors',
61
86
  'sphinx_rtd_theme',
62
87
  'twine',
63
- 'pandoc'
88
+ 'pandoc',
89
+ 'toolviper',
64
90
  ]
65
91
 
66
92
  all = [
93
+ 'astropy',
94
+ 'toolviper>=0.0.12',
95
+ 's3fs',
96
+ 'scipy',
97
+ 'xarray',
98
+ 'zarr>=2,<3',
99
+ 'pyarrow',
100
+ 'psutil',
67
101
  'pytest',
68
102
  'pytest-cov',
69
103
  'pytest-html',
@@ -29,6 +29,24 @@ except ModuleNotFoundError as exc:
29
29
  f"{exc}"
30
30
  )
31
31
 
32
+ # Valid casacore ImageInfo enum values
33
+ # Reference: https://casacore.github.io/casacore/classcasacore_1_1ImageInfo.html
34
+ _VALID_IMAGE_TYPES = (
35
+ "Undefined",
36
+ "Intensity",
37
+ "Beam",
38
+ "ColumnDensity",
39
+ "DepolarizationRatio",
40
+ "KineticTemperature",
41
+ "MagneticField",
42
+ "OpticalDepth",
43
+ "RotationMeasure",
44
+ "RotationalTemperature",
45
+ "SpectralIndex",
46
+ "Velocity",
47
+ "VelocityDispersion",
48
+ )
49
+
32
50
  import numpy as np
33
51
  import toolviper.utils.logger as logger
34
52
 
@@ -166,6 +184,32 @@ def wrap_class_methods(cls: type) -> type:
166
184
  return cls
167
185
 
168
186
 
187
+ def _validate_image_type(value: str) -> str:
188
+ """Validate and normalize an image type string.
189
+
190
+ Parameters
191
+ ----------
192
+ value
193
+ The image type string to validate.
194
+
195
+ Returns
196
+ -------
197
+ str
198
+ A valid casacore ImageInfo enum value with proper capitalization.
199
+ Returns 'Intensity' if the input is not a valid type.
200
+
201
+ Notes
202
+ -----
203
+ Validation is case-insensitive. The returned string uses the
204
+ canonical capitalization from the casacore ImageInfo enum.
205
+ """
206
+ value_lower = value.lower()
207
+ for valid_type in _VALID_IMAGE_TYPES:
208
+ if valid_type.lower() == value_lower:
209
+ return valid_type
210
+ return "Intensity"
211
+
212
+
169
213
  @wrap_class_methods
170
214
  class table(casatools.table):
171
215
  """A wrapper for the casatools table object.
@@ -513,7 +557,7 @@ class image(casatools.image):
513
557
  self,
514
558
  imagename,
515
559
  axis=0,
516
- maskname="mask0",
560
+ maskname="MASK_0",
517
561
  images=(),
518
562
  values=None,
519
563
  coordsys=None,
@@ -702,18 +746,36 @@ class image(casatools.image):
702
746
  def imageinfo(self) -> dict:
703
747
  """Retrieve metadata from the image table.
704
748
 
705
- This method accesses the image table associated with the image name
706
- and attempts to retrieve information stored under the 'imageinfo'
707
- keyword. If the 'imageinfo' keyword is not found in the table,
708
- a default dictionary containing basic information is returned.
749
+ Accesses the image table and retrieves information stored under the
750
+ 'imageinfo' keyword. The 'imagetype' value is validated against
751
+ casacore's ImageInfo enumeration values to mimic python-casacore
752
+ `image.imageinfo()` behavior.
709
753
 
710
754
  Returns
711
755
  -------
712
756
  dict
713
- A dictionary containing image metadata. This is either the
714
- value associated with the 'imageinfo' keyword in the table,
715
- or a default dictionary {'imagetype': 'Intensity',
716
- 'objectname': ''} if the keyword is absent.
757
+ Image metadata dictionary containing:
758
+
759
+ - **imagetype** : str
760
+ Type of the image, validated against casacore ImageInfo enum.
761
+ Defaults to 'Intensity' if invalid or missing.
762
+ - **objectname** : str
763
+ Name of the observed object.
764
+
765
+ Notes
766
+ -----
767
+ image.info()['imageinfo'] and image.imageinfo() from python-casacore
768
+ always returns "imagetype" in a predefined enum value. When the "imageinfo"
769
+ keyword is missing from the image table, or a non-standard value of "imagetype"
770
+ (e.g. 'sky') was written into that keyword, image.info() will just return
771
+ "Intensity" as the imagetype.
772
+
773
+ Examples
774
+ --------
775
+ >>> img = image('my_image.im')
776
+ >>> info = img.imageinfo()
777
+ >>> info['imagetype']
778
+ 'Intensity'
717
779
  """
718
780
  with table(self._imagename) as tb:
719
781
  if "imageinfo" in tb.keywordnames():
@@ -721,6 +783,10 @@ class image(casatools.image):
721
783
  else:
722
784
  image_metadata = {"imagetype": "Intensity", "objectname": ""}
723
785
 
786
+ image_metadata["imagetype"] = _validate_image_type(
787
+ image_metadata.get("imagetype", "Intensity")
788
+ )
789
+
724
790
  return image_metadata
725
791
 
726
792
  def datatype(self):
@@ -1,3 +1,6 @@
1
+ from xradio._utils.list_and_array import to_python_type
2
+
3
+
1
4
  def make_quantity(value, units: str, dims: list = []) -> dict:
2
5
  """
3
6
  create a quantity dictionary given value and units
@@ -13,7 +16,11 @@ def make_quantity(value, units: str, dims: list = []) -> dict:
13
16
  -------
14
17
  dict
15
18
  """
16
- return {"data": value, "dims": dims, "attrs": make_quantity_attrs(units)}
19
+ return {
20
+ "data": to_python_type(value),
21
+ "dims": dims,
22
+ "attrs": make_quantity_attrs(units),
23
+ }
17
24
 
18
25
 
19
26
  def ensure_units_are_consistent(units):
@@ -67,7 +74,7 @@ def make_spectral_coord_reference_dict(
67
74
  u,
68
75
  observer.lower() if observer not in ["TOPO", "BARY", "REST"] else observer,
69
76
  ),
70
- "data": value,
77
+ "data": to_python_type(value),
71
78
  "dims": [],
72
79
  }
73
80
 
@@ -98,14 +105,38 @@ def make_skycoord_dict(data: list[float], units: str, frame: str) -> dict:
98
105
  "type": "sky_coord",
99
106
  "units": ensure_units_are_consistent(units),
100
107
  },
101
- "data": data,
102
- "dims": ["l", "m"],
108
+ "data": to_python_type(data),
109
+ "dims": "sky_dir_label",
110
+ "coords": {"sky_dir_label": {"data": ["ra", "dec"], "dims": "sky_dir_label"}},
111
+ }
112
+
113
+
114
+ def make_direction_location_dict(data: list[float], units: str, frame: str) -> dict:
115
+ return {
116
+ "attrs": {
117
+ "frame": frame.upper(),
118
+ "type": "location",
119
+ "units": ensure_units_are_consistent(units),
120
+ },
121
+ "data": to_python_type(data),
122
+ "dims": "ellipsoid_dir_label",
123
+ "coords": {
124
+ "ellipsoid_dir_label": {
125
+ "data": ["lon", "lat"],
126
+ "dims": "ellipsoid_dir_label",
127
+ }
128
+ },
103
129
  }
104
130
 
105
131
 
106
132
  def make_time_measure_attrs(units="s", scale="utc", time_format="mjd") -> dict:
107
133
  u = ensure_units_are_consistent(units)
108
- return {"units": u, "scale": scale, "format": time_format, "type": "time"}
134
+ return {
135
+ "units": u,
136
+ "scale": scale.lower(),
137
+ "format": time_format.lower(),
138
+ "type": "time",
139
+ }
109
140
 
110
141
 
111
142
  def make_time_measure_dict(data, units="s", scale="utc", time_format="mjd") -> dict:
@@ -127,7 +158,7 @@ def make_time_measure_dict(data, units="s", scale="utc", time_format="mjd") -> d
127
158
  """
128
159
  x = {}
129
160
  x["attrs"] = make_time_measure_attrs(units, scale, time_format)
130
- x["data"] = data
161
+ x["data"] = to_python_type(data)
131
162
  x["dims"] = []
132
163
  return x
133
164
 
@@ -149,7 +180,7 @@ def make_time_coord_attrs(units="s", scale="utc", time_format="mjd") -> dict:
149
180
  -------
150
181
  dict
151
182
  """
152
- x = make_time_measure_attrs(units, scale, time_format)
183
+ x = make_time_measure_attrs(units, scale.lower(), time_format.lower())
153
184
  del x["type"]
154
185
  return x
155
186
 
@@ -53,11 +53,34 @@ def get_pad_value(col_dtype: np.dtype) -> object:
53
53
  )
54
54
 
55
55
 
56
+ def to_python_type(x):
57
+ """
58
+ Convert any NumPy scalar, array, or nested structure to native Python types.
59
+
60
+ - np.float32, np.float64 → float
61
+ - np.int32, np.int64 → int
62
+ - np.bool_ → bool
63
+ - np.ndarray → list of Python-native types
64
+ - nested containers (list/tuple/dict) are handled recursively
65
+ """
66
+ if isinstance(x, np.generic): # covers all numpy scalar types
67
+ return x.item()
68
+ elif isinstance(x, np.ndarray):
69
+ return x.tolist()
70
+ elif isinstance(x, (list, tuple)):
71
+ return type(x)(to_python_type(v) for v in x)
72
+ elif isinstance(x, dict):
73
+ return {k: to_python_type(v) for k, v in x.items()}
74
+ else:
75
+ return x
76
+
77
+
56
78
  def to_list(x):
57
79
  if isinstance(x, np.ndarray):
58
- if x.ndim == 0:
59
- return [x.item()]
60
- return list(x) # needed for json serialization
80
+ z = x.astype(float)
81
+ if z.ndim == 0:
82
+ return [z.item()]
83
+ return list(z) # needed for json serialization
61
84
  elif isinstance(x, list):
62
85
  return x
63
86
  return [x]
@@ -224,3 +224,47 @@ casa_frequency_frames = [
224
224
  ]
225
225
 
226
226
  casa_frequency_frames_codes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 64]
227
+
228
+
229
+ def get_data_group_keys(schema_name: str) -> list[tuple[str, bool]]:
230
+ """Return (name, is_optional) pairs for data group keys for the given schema.
231
+
232
+ Parameters
233
+ ----------
234
+ schema_name : str
235
+ The name of the schema to retrieve data group keys for. Supported values are "msv4" and "image".
236
+
237
+ Returns
238
+ -------
239
+ list[tuple[str, bool]]
240
+ A list of tuples (key_name, is_optional) for the specified schema.
241
+
242
+ Raises
243
+ ------
244
+ ValueError
245
+ If the schema name is unknown.
246
+ """
247
+ from typing import get_type_hints, get_origin, get_args, Union
248
+
249
+ if schema_name == "msv4":
250
+ from xradio.measurement_set.schema import DataGroupDict
251
+
252
+ cls = DataGroupDict
253
+ elif schema_name == "image":
254
+ from xradio.image.schema import DataGroupDict
255
+
256
+ cls = DataGroupDict
257
+ else:
258
+ raise ValueError(f"Unknown schema name: {schema_name}")
259
+
260
+ annotations = get_type_hints(cls)
261
+ keys_with_optional = {}
262
+ for name, anno in annotations.items():
263
+ origin = get_origin(anno)
264
+ is_optional = False
265
+ if origin is Union:
266
+ args = get_args(anno)
267
+ is_optional = type(None) in args
268
+ keys_with_optional[name] = is_optional
269
+
270
+ return keys_with_optional
@@ -0,0 +1,63 @@
1
+ import xarray as xr
2
+ from xradio._utils.schema import get_data_group_keys
3
+ from collections.abc import Mapping, Iterable
4
+ from typing import Any, Union
5
+
6
+
7
+ def get_data_group_name(
8
+ xdx: Union[xr.Dataset, xr.DataTree], data_group_name: str = None
9
+ ) -> str:
10
+
11
+ if data_group_name is None:
12
+ if "base" in xdx.attrs["data_groups"]:
13
+ data_group_name = "base"
14
+ else:
15
+ data_group_name = list(xdx.attrs["data_groups"].keys())[0]
16
+
17
+ return data_group_name
18
+
19
+
20
+ def create_new_data_group(
21
+ xdx: Union[xr.Dataset, xr.DataTree],
22
+ schema_name: str,
23
+ new_data_group_name: str,
24
+ data_group: dict,
25
+ data_group_dv_shared_with: str = None,
26
+ ) -> xr.DataTree:
27
+ """Adds a data group to Xarray Data Structure (Dataset or DataTree).
28
+
29
+ Parameters
30
+ ----------
31
+ new_data_group_name : str
32
+ The name of the new data group to add.
33
+ data_group : dict
34
+ A dictionary containing the data group variables and their attributes.
35
+ data_group_dv_shared_with : str, optional
36
+ The name of the data group to share data variables with, by default "base"
37
+
38
+ Returns
39
+ -------
40
+ xr.DataTree
41
+ MSv4 DataTree with the new group added
42
+ """
43
+
44
+ data_group_dv_shared_with = get_data_group_name(xdx, data_group_dv_shared_with)
45
+
46
+ default_data_group = xdx.attrs["data_groups"][data_group_dv_shared_with]
47
+
48
+ new_data_group = {}
49
+
50
+ data_group_keys = get_data_group_keys(schema_name)
51
+
52
+ for key, optional in data_group_keys.items():
53
+ if key in data_group:
54
+ new_data_group[key] = data_group[key]
55
+ else:
56
+ if key not in default_data_group and not optional:
57
+ raise ValueError(
58
+ f"Data group key '{key}' is required but not provided and not present in shared data group '{data_group_dv_shared_with}'."
59
+ )
60
+ elif key in default_data_group:
61
+ new_data_group[key] = default_data_group[key]
62
+
63
+ return new_data_group_name, new_data_group
@@ -1,7 +1,5 @@
1
1
  import xarray as xr
2
- import s3fs
3
2
  import os
4
- from botocore.exceptions import NoCredentialsError
5
3
 
6
4
  # from xradio.vis._vis_utils._ms.msv2_to_msv4_meta import (
7
5
  # column_description_casacore_to_msv4_measure,
@@ -10,6 +8,9 @@ from botocore.exceptions import NoCredentialsError
10
8
 
11
9
  def _get_file_system_and_items(ps_store: str):
12
10
 
11
+ import s3fs
12
+ from botocore.exceptions import NoCredentialsError
13
+
13
14
  # default to assuming the data are accessible on local file system
14
15
  if os.path.isdir(ps_store):
15
16
  # handle a common shell convention
@@ -74,6 +75,7 @@ def _open_dataset(
74
75
  """
75
76
 
76
77
  import dask
78
+ import s3fs
77
79
 
78
80
  if isinstance(file_system, s3fs.core.S3FileSystem):
79
81
  mapping = s3fs.S3Map(root=store, s3=file_system, check=False)