xradio 0.0.51__tar.gz → 0.0.52__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 (84) hide show
  1. {xradio-0.0.51/src/xradio.egg-info → xradio-0.0.52}/PKG-INFO +4 -4
  2. {xradio-0.0.51 → xradio-0.0.52}/README.md +4 -4
  3. {xradio-0.0.51 → xradio-0.0.52}/pyproject.toml +1 -1
  4. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/_utils/dict_helpers.py +24 -0
  5. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/_casacore/xds_from_casacore.py +116 -69
  6. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/_casacore/xds_to_casacore.py +51 -32
  7. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/_fits/xds_from_fits.py +43 -52
  8. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/_zarr/common.py +1 -1
  9. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/_zarr/xds_from_zarr.py +37 -20
  10. xradio-0.0.52/src/xradio/image/_util/_zarr/xds_to_zarr.py +48 -0
  11. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/casacore.py +6 -3
  12. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/create_field_and_source_xds.py +9 -2
  13. {xradio-0.0.51 → xradio-0.0.52/src/xradio.egg-info}/PKG-INFO +4 -4
  14. xradio-0.0.51/src/xradio/image/_util/_zarr/xds_to_zarr.py +0 -49
  15. {xradio-0.0.51 → xradio-0.0.52}/LICENSE.txt +0 -0
  16. {xradio-0.0.51 → xradio-0.0.52}/MANIFEST.in +0 -0
  17. {xradio-0.0.51 → xradio-0.0.52}/setup.cfg +0 -0
  18. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/__init__.py +0 -0
  19. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/_utils/__init__.py +0 -0
  20. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/_utils/_casacore/tables.py +0 -0
  21. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/_utils/coord_math.py +0 -0
  22. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/_utils/list_and_array.py +0 -0
  23. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/_utils/schema.py +0 -0
  24. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/_utils/zarr/__init__.py +0 -0
  25. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/_utils/zarr/common.py +0 -0
  26. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/__init__.py +0 -0
  27. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/__init__.py +0 -0
  28. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/_casacore/__init__.py +0 -0
  29. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/_casacore/common.py +0 -0
  30. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/_zarr/zarr_low_level.py +0 -0
  31. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/common.py +0 -0
  32. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/fits.py +0 -0
  33. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/image_factory.py +0 -0
  34. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/_util/zarr.py +0 -0
  35. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/image/image.py +0 -0
  36. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/__init__.py +0 -0
  37. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/__init__.py +0 -0
  38. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/_tables/load.py +0 -0
  39. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/_tables/load_main_table.py +0 -0
  40. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/_tables/read.py +0 -0
  41. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/_tables/read_main_table.py +0 -0
  42. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/_tables/read_subtables.py +0 -0
  43. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/_tables/table_query.py +0 -0
  44. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/_tables/write.py +0 -0
  45. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/_tables/write_exp_api.py +0 -0
  46. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/chunks.py +0 -0
  47. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/conversion.py +0 -0
  48. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/create_antenna_xds.py +0 -0
  49. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/descr.py +0 -0
  50. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/msv2_msv3.py +0 -0
  51. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/msv2_to_msv4_meta.py +0 -0
  52. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py +0 -0
  53. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/msv4_sub_xdss.py +0 -0
  54. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/optimised_functions.py +0 -0
  55. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/partition_queries.py +0 -0
  56. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/partitions.py +0 -0
  57. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_msv2/subtables.py +0 -0
  58. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_utils/cds.py +0 -0
  59. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_utils/partition_attrs.py +0 -0
  60. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_utils/stokes_types.py +0 -0
  61. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_utils/xds_helper.py +0 -0
  62. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_zarr/encoding.py +0 -0
  63. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_zarr/read.py +0 -0
  64. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/_zarr/write.py +0 -0
  65. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/msv2.py +0 -0
  66. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/_utils/zarr.py +0 -0
  67. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/convert_msv2_to_processing_set.py +0 -0
  68. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/load_processing_set.py +0 -0
  69. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/measurement_set_xdt.py +0 -0
  70. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/open_processing_set.py +0 -0
  71. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/processing_set_xdt.py +0 -0
  72. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/measurement_set/schema.py +0 -0
  73. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/schema/__init__.py +0 -0
  74. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/schema/bases.py +0 -0
  75. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/schema/check.py +0 -0
  76. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/schema/dataclass.py +0 -0
  77. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/schema/metamodel.py +0 -0
  78. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/schema/typing.py +0 -0
  79. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/sphinx/__init__.py +0 -0
  80. {xradio-0.0.51 → xradio-0.0.52}/src/xradio/sphinx/schema_table.py +0 -0
  81. {xradio-0.0.51 → xradio-0.0.52}/src/xradio.egg-info/SOURCES.txt +0 -0
  82. {xradio-0.0.51 → xradio-0.0.52}/src/xradio.egg-info/dependency_links.txt +0 -0
  83. {xradio-0.0.51 → xradio-0.0.52}/src/xradio.egg-info/requires.txt +0 -0
  84. {xradio-0.0.51 → xradio-0.0.52}/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: 0.0.51
3
+ Version: 0.0.52
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
@@ -80,9 +80,9 @@ Dynamic: license-file
80
80
  Xarray Radio Astronomy Data IO is still in development.
81
81
 
82
82
  [![Python 3.11 3.12 3.13](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/downloads/release/python-3130/)
83
- ![Linux Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml/badge.svg?branch=main)
84
- ![macOS Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml/badge.svg?branch=main)
85
- ![ipynb Tests](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml/badge.svg?branch=main)
83
+ [![Linux Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml/badge.svg?branch=main)](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml?query=branch%3Amain)
84
+ [![macOS Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml/badge.svg?branch=main)](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml?query=branch%3Amain)
85
+ [![ipynb Tests](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml/badge.svg?branch=main)](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml?query=branch%3Amain)
86
86
  [![Coverage](https://codecov.io/gh/casangi/xradio/branch/main/graph/badge.svg)](https://codecov.io/gh/casangi/xradio/branch/main/xradio)
87
87
  [![Documentation Status](https://readthedocs.org/projects/xradio/badge/?version=latest)](https://xradio.readthedocs.io)
88
88
  [![Version Status](https://img.shields.io/pypi/v/xradio.svg)](https://pypi.python.org/pypi/xradio/)
@@ -2,9 +2,9 @@
2
2
  Xarray Radio Astronomy Data IO is still in development.
3
3
 
4
4
  [![Python 3.11 3.12 3.13](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/downloads/release/python-3130/)
5
- ![Linux Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml/badge.svg?branch=main)
6
- ![macOS Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml/badge.svg?branch=main)
7
- ![ipynb Tests](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml/badge.svg?branch=main)
5
+ [![Linux Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml/badge.svg?branch=main)](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml?query=branch%3Amain)
6
+ [![macOS Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml/badge.svg?branch=main)](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml?query=branch%3Amain)
7
+ [![ipynb Tests](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml/badge.svg?branch=main)](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml?query=branch%3Amain)
8
8
  [![Coverage](https://codecov.io/gh/casangi/xradio/branch/main/graph/badge.svg)](https://codecov.io/gh/casangi/xradio/branch/main/xradio)
9
9
  [![Documentation Status](https://readthedocs.org/projects/xradio/badge/?version=latest)](https://xradio.readthedocs.io)
10
10
  [![Version Status](https://img.shields.io/pypi/v/xradio.svg)](https://pypi.python.org/pypi/xradio/)
@@ -24,4 +24,4 @@ pip install xradio
24
24
  This will also install the minimal dependencies for XRADIO. To install the minimal dependencies and the interactive components (JupyterLab) use:
25
25
  ```sh
26
26
  pip install "xradio[interactive]"
27
- ```
27
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "xradio"
3
- version = "0.0.51"
3
+ version = "0.0.52"
4
4
  description = " Xarray Radio Astronomy Data IO"
5
5
  authors = [
6
6
  {name = "Jan-Willem Steeb", email="jsteeb@nrao.edu"},
@@ -43,6 +43,30 @@ def make_time_measure_attrs(units=["s"], scale="utc", time_format="mjd") -> dict
43
43
  return {"units": u, "scale": scale, "format": time_format, "type": "time"}
44
44
 
45
45
 
46
+ def make_time_measure_dict(data, units=["s"], scale="utc", time_format="mjd") -> dict:
47
+ """
48
+ create a time measure dictionary given value and units
49
+ Parameters
50
+ ----------
51
+ value : numeric or array of numerics
52
+ Time value
53
+ units: str
54
+ Time units
55
+ scale: str
56
+ Time scale
57
+ time_format: str
58
+ Time format
59
+ Returns
60
+ -------
61
+ dict
62
+ """
63
+ x = {}
64
+ x["attrs"] = make_time_measure_attrs(units, scale, time_format)
65
+ x["data"] = data
66
+ x["dims"] = []
67
+ return x
68
+
69
+
46
70
  def make_time_coord_attrs(units=["s"], scale="utc", time_format="mjd") -> dict:
47
71
  """
48
72
  create a time measure dictionary given value and units
@@ -10,7 +10,7 @@ import numpy as np
10
10
  import xarray as xr
11
11
  from astropy import units as u
12
12
  from casacore import tables
13
- from casacore.images import coordinates
13
+ from casacore.images import coordinates, image as casa_image
14
14
 
15
15
  from .common import (
16
16
  _active_mask,
@@ -32,10 +32,11 @@ from ..common import (
32
32
  from ...._utils._casacore.tables import extract_table_attributes, open_table_ro
33
33
  from xradio._utils.coord_math import _deg_to_rad
34
34
  from xradio._utils.dict_helpers import (
35
- make_quantity,
35
+ _casacore_q_to_xradio_q,
36
36
  make_frequency_reference_dict,
37
+ make_quantity,
37
38
  make_skycoord_dict,
38
- _casacore_q_to_xradio_q,
39
+ make_time_measure_dict,
39
40
  )
40
41
 
41
42
 
@@ -89,19 +90,126 @@ def _add_mask(
89
90
  return xds
90
91
 
91
92
 
93
+ def _casa_image_to_xds_image_attrs(image: casa_image, history: bool = True) -> dict:
94
+ """
95
+ get the image attributes from the casacoreimage object
96
+ """
97
+ meta_dict = image.info()
98
+ coord_dict = copy.deepcopy(meta_dict["coordinates"])
99
+ attrs = {}
100
+ attrs[_image_type] = image.info()["imageinfo"]["imagetype"]
101
+ attrs["units"] = image.unit()
102
+ attrs["telescope"] = {}
103
+ telescope = attrs["telescope"]
104
+ for k in ("observer", "obsdate", "telescope", "telescopeposition"):
105
+ if k.startswith("telescope"):
106
+ if k == "telescope":
107
+ telescope["name"] = coord_dict[k]
108
+ elif k in coord_dict:
109
+ casa_pos = coord_dict[k]
110
+ location = {}
111
+ loc_attrs = {}
112
+ loc_attrs["type"] = "location"
113
+ loc_attrs["frame"] = casa_pos["refer"]
114
+ """
115
+ if casa_pos["refer"] == "ITRF":
116
+ loc_attrs["ellipsoid"] = "GRS80"
117
+ """
118
+ loc_attrs["units"] = [
119
+ casa_pos["m0"]["unit"],
120
+ casa_pos["m1"]["unit"],
121
+ casa_pos["m2"]["unit"],
122
+ ]
123
+ loc_attrs["coordinate_system"] = "geocentric"
124
+ loc_attrs["origin_object_name"] = "earth"
125
+ location["attrs"] = loc_attrs
126
+ location["data"] = [
127
+ casa_pos["m0"]["value"],
128
+ casa_pos["m1"]["value"],
129
+ casa_pos["m2"]["value"],
130
+ ]
131
+ telescope["location"] = location
132
+ """
133
+ del (
134
+ telescope["position"]["refer"],
135
+ telescope["position"]["m0"],
136
+ telescope["position"]["m1"],
137
+ telescope["position"]["m2"],
138
+ )
139
+ """
140
+ elif k == "obsdate":
141
+ obsdate = coord_dict[k]
142
+ """
143
+ o_attrs = {"type": "time"}
144
+ o_attrs["scale"] = coord_dict[k]["refer"]
145
+ myu = coord_dict[k]["m0"]["unit"]
146
+ o_attrs["units"] = myu if isinstance(myu, list) else [myu]
147
+ o_attrs["format"] = _get_time_format(m0["value"], m0["unit"])
148
+ o_date = {}
149
+ o_date["attrs"] = o_attrs
150
+ """
151
+ m0 = obsdate["m0"]
152
+ attrs["obsdate"] = make_time_measure_dict(
153
+ data=m0["value"],
154
+ units=m0["unit"],
155
+ scale=obsdate["refer"],
156
+ time_format=_get_time_format(m0["value"], m0["unit"]),
157
+ )
158
+ else:
159
+ attrs[k] = coord_dict[k] if k in coord_dict else ""
160
+ dir_key = next((k for k in coord_dict if k.startswith("direction")), None)
161
+ if dir_key:
162
+ frame, eqnx = _convert_direction_system(coord_dict[dir_key]["system"], "system")
163
+ else:
164
+ frame = "icrs"
165
+ logger.warning(
166
+ "No direction coordinate found from which "
167
+ "to get pointing center frame. Assuming ICRS"
168
+ )
169
+ # it looks like the pointing center is always in radians in a casa image coord system dict
170
+ # note that in a casa image, the pointing center does not explicitly have a reference frame
171
+ # associated with it.
172
+ # point_center = coord_dict["pointingcenter"]
173
+ attrs[_pointing_center] = make_skycoord_dict(
174
+ coord_dict["pointingcenter"]["value"].tolist(), ["rad", "rad"], frame
175
+ )
176
+ imageinfo = meta_dict["imageinfo"]
177
+ obj = "objectname"
178
+ attrs[_object_name] = imageinfo[obj] if obj in imageinfo else ""
179
+ attrs["user"] = meta_dict["miscinfo"]
180
+ defmask = "Image_defaultmask"
181
+ with open_table_ro(image.name()) as casa_table:
182
+ # the actual mask is a data var and data var names are all caps by convention
183
+ attrs[_active_mask] = (
184
+ casa_table.getkeyword(defmask).upper()
185
+ if defmask in casa_table.keywordnames()
186
+ else None
187
+ )
188
+ attrs["description"] = None
189
+ # if also loading history, put it as another xds in the image attrs
190
+ if history:
191
+ htable = os.sep.join([os.path.abspath(image.name()), "logtable"])
192
+ if os.path.isdir(htable):
193
+ attrs["history"] = read_generic_table(htable)
194
+ else:
195
+ logger.warning(
196
+ f"Unable to find history table {htable}. History will not be included"
197
+ )
198
+ return attrs
199
+
200
+
92
201
  def _add_sky_or_aperture(
93
202
  xds: xr.Dataset,
94
203
  ary: Union[np.ndarray, da.array],
95
204
  dimorder: list,
96
205
  img_full_path: str,
97
206
  has_sph_dims: bool,
207
+ history: bool,
98
208
  ) -> xr.Dataset:
99
209
  xda = xr.DataArray(ary, dims=dimorder).astype(ary.dtype)
100
210
  with _open_image_ro(img_full_path) as casa_image:
101
- image_type = casa_image.info()["imageinfo"]["imagetype"]
102
- unit = casa_image.unit()
103
- xda.attrs[_image_type] = image_type
104
- xda.attrs["units"] = unit
211
+ xda.attrs = _casa_image_to_xds_image_attrs(casa_image, history)
212
+ # xds.attrs = attrs
105
213
  name = "SKY" if has_sph_dims else "APERTURE"
106
214
  xda = xda.rename(name)
107
215
  xds[xda.name] = xda
@@ -143,7 +251,7 @@ def _add_vel_attrs(xds: xr.Dataset, coord_dict: dict) -> xr.Dataset:
143
251
  return xds
144
252
 
145
253
 
146
- def _casa_image_to_xds_attrs(img_full_path: str, history: bool = True) -> dict:
254
+ def _casa_image_to_xds_attrs(img_full_path: str) -> dict:
147
255
  """
148
256
  Get the xds level attribut/es as a python dictionary
149
257
  """
@@ -187,67 +295,6 @@ def _casa_image_to_xds_attrs(img_full_path: str, history: bool = True) -> dict:
187
295
  if j in coord_dir_dict:
188
296
  dir_dict[j] = coord_dir_dict[j]
189
297
  attrs["direction"] = dir_dict
190
- attrs["telescope"] = {}
191
- telescope = attrs["telescope"]
192
- attrs["obsdate"] = {"type": "time"}
193
- obsdate = attrs["obsdate"]
194
- attrs[_pointing_center] = coord_dict["pointingcenter"].copy()
195
- for k in ("observer", "obsdate", "telescope", "telescopeposition"):
196
- if k.startswith("telescope"):
197
- if k == "telescope":
198
- telescope["name"] = coord_dict[k]
199
- elif k in coord_dict:
200
- telescope["position"] = coord_dict[k]
201
- telescope["position"]["ellipsoid"] = telescope["position"]["refer"]
202
- if telescope["position"]["refer"] == "ITRF":
203
- telescope["position"]["ellipsoid"] = "GRS80"
204
- telescope["position"]["units"] = [
205
- telescope["position"]["m0"]["unit"],
206
- telescope["position"]["m1"]["unit"],
207
- telescope["position"]["m2"]["unit"],
208
- ]
209
- telescope["position"]["value"] = [
210
- telescope["position"]["m0"]["value"],
211
- telescope["position"]["m1"]["value"],
212
- telescope["position"]["m2"]["value"],
213
- ]
214
- del (
215
- telescope["position"]["refer"],
216
- telescope["position"]["m0"],
217
- telescope["position"]["m1"],
218
- telescope["position"]["m2"],
219
- )
220
- elif k == "obsdate":
221
- obsdate["scale"] = coord_dict[k]["refer"]
222
- myu = coord_dict[k]["m0"]["unit"]
223
- obsdate["units"] = myu if isinstance(myu, list) else [myu]
224
- obsdate["value"] = coord_dict[k]["m0"]["value"]
225
- obsdate["format"] = _get_time_format(obsdate["value"], obsdate["units"])
226
- obsdate["type"] = "time"
227
- else:
228
- attrs[k] = coord_dict[k] if k in coord_dict else ""
229
- imageinfo = meta_dict["imageinfo"]
230
- obj = "objectname"
231
- attrs[_object_name] = imageinfo[obj] if obj in imageinfo else ""
232
- attrs["user"] = meta_dict["miscinfo"]
233
- defmask = "Image_defaultmask"
234
- with open_table_ro(img_full_path) as casa_table:
235
- # the actual mask is a data var and data var names are all caps by convention
236
- attrs[_active_mask] = (
237
- casa_table.getkeyword(defmask).upper()
238
- if defmask in casa_table.keywordnames()
239
- else None
240
- )
241
- attrs["description"] = None
242
- # if also loading history, put it as another xds in the attrs
243
- if history:
244
- htable = os.sep.join([img_full_path, "logtable"])
245
- if os.path.isdir(htable):
246
- attrs["history"] = read_generic_table(htable)
247
- else:
248
- logger.warning(
249
- f"Unable to find history table {htable}. History will not be included"
250
- )
251
298
  return copy.deepcopy(attrs)
252
299
 
253
300
 
@@ -19,12 +19,16 @@ def _compute_direction_dict(xds: xr.Dataset) -> dict:
19
19
  """
20
20
  direction = {}
21
21
  xds_dir = xds.attrs["direction"]
22
+ direction["_axes_sizes"] = np.array(
23
+ [xds.sizes[dim] for dim in ("l", "m")], dtype=np.int32
24
+ )
25
+ direction["_image_axes"] = np.array([2, 3], dtype=np.int32)
22
26
  direction["system"] = xds_dir["reference"]["attrs"]["equinox"].upper()
23
27
  if direction["system"] == "J2000.0":
24
28
  direction["system"] = "J2000"
25
29
  direction["projection"] = xds_dir["projection"]
26
30
  direction["projection_parameters"] = xds_dir["projection_parameters"]
27
- direction["units"] = np.array(xds_dir["reference"]["attrs"]["units"], dtype="<U16")
31
+ direction["units"] = xds_dir["reference"]["attrs"]["units"]
28
32
  direction["crval"] = np.array(xds_dir["reference"]["data"])
29
33
  direction["cdelt"] = np.array((xds.l[1] - xds.l[0], xds.m[1] - xds.m[0]))
30
34
  direction["crpix"] = _compute_sky_reference_pixel(xds)
@@ -34,9 +38,9 @@ def _compute_direction_dict(xds: xr.Dataset) -> dict:
34
38
  for s in ["longpole", "latpole"]:
35
39
  m = "lonpole" if s == "longpole" else s
36
40
  # lonpole, latpole are numerical values in degrees in casa images
37
- direction[s] = Angle(
38
- str(xds_dir[m]["data"]) + xds_dir[m]["attrs"]["units"][0]
39
- ).deg
41
+ direction[s] = float(
42
+ Angle(str(xds_dir[m]["data"]) + xds_dir[m]["attrs"]["units"][0]).deg
43
+ )
40
44
  return direction
41
45
 
42
46
 
@@ -69,6 +73,8 @@ def _compute_spectral_dict(xds: xr.Dataset) -> dict:
69
73
  for a CASA image coordinate system
70
74
  """
71
75
  spec = {}
76
+ spec["_axes_sizes"] = np.array([xds.sizes["frequency"]], dtype=np.int32)
77
+ spec["_image_axes"] = np.array([0], dtype=np.int32)
72
78
  spec["formatUnit"] = ""
73
79
  spec["name"] = "Frequency"
74
80
  # spec["nativeType"] = _native_types.index(xds.frequency.attrs["native_type"])
@@ -76,7 +82,7 @@ def _compute_spectral_dict(xds: xr.Dataset) -> dict:
76
82
  spec["nativeType"] = 0
77
83
  spec["restfreq"] = xds.frequency.attrs["rest_frequency"]["data"]
78
84
  # spec["restfreqs"] = copy.deepcopy(xds.frequency.attrs["restfreqs"]["value"])
79
- spec["restfreqs"] = [spec["restfreq"]]
85
+ spec["restfreqs"] = np.array([spec["restfreq"]])
80
86
  spec["system"] = xds.frequency.attrs["reference_value"]["attrs"]["observer"].upper()
81
87
  u = xds.frequency.attrs["reference_value"]["attrs"]["units"]
82
88
  spec["unit"] = u if isinstance(u, str) else u[0]
@@ -88,53 +94,65 @@ def _compute_spectral_dict(xds: xr.Dataset) -> dict:
88
94
  wcs = {}
89
95
  wcs["ctype"] = "FREQ"
90
96
  wcs["pc"] = 1.0
91
- wcs["crval"] = xds.frequency.attrs["reference_value"]["data"]
92
- wcs["cdelt"] = xds.frequency.values[1] - xds.frequency.values[0]
93
- wcs["crpix"] = (wcs["crval"] - xds.frequency.values[0]) / wcs["cdelt"]
97
+ wcs["crval"] = float(xds.frequency.attrs["reference_value"]["data"])
98
+ wcs["cdelt"] = float(xds.frequency.values[1] - xds.frequency.values[0])
99
+ wcs["crpix"] = float((wcs["crval"] - xds.frequency.values[0]) / wcs["cdelt"])
94
100
  spec["wcs"] = wcs
95
101
  return spec
96
102
 
97
103
 
98
104
  def _coord_dict_from_xds(xds: xr.Dataset) -> dict:
99
105
  coord = {}
100
- coord["telescope"] = xds.attrs["telescope"]["name"]
101
- coord["observer"] = xds.attrs["observer"]
106
+ sky_ap = "SKY" if "SKY" in xds.data_vars else "APERTURE"
107
+ if "telescope" in xds[sky_ap].attrs:
108
+ tel = xds[sky_ap].attrs["telescope"]
109
+ if "name" in tel:
110
+ coord["telescope"] = xds[sky_ap].attrs["telescope"]["name"]
111
+ if "location" in tel:
112
+ xds_telloc = tel["location"]
113
+ telloc = {}
114
+ telloc["refer"] = xds_telloc["attrs"]["frame"]
115
+ if telloc["refer"] == "GRS80":
116
+ telloc["refer"] = "ITRF"
117
+ for i in range(3):
118
+ telloc[f"m{i}"] = {
119
+ "unit": xds_telloc["attrs"]["units"][i],
120
+ "value": xds_telloc["data"][i],
121
+ }
122
+ telloc["type"] = "position"
123
+ coord["telescopeposition"] = telloc
124
+ if "observer" in xds[sky_ap].attrs:
125
+ coord["observer"] = xds[sky_ap].attrs["observer"]
102
126
  obsdate = {}
103
127
  obsdate["refer"] = xds.coords["time"].attrs["scale"]
104
128
  obsdate["type"] = "epoch"
105
129
  obsdate["m0"] = {}
106
130
  obsdate["m0"]["unit"] = xds.coords["time"].attrs["units"][0]
107
- obsdate["m0"]["value"] = xds.coords["time"].values[0]
131
+ obsdate["m0"]["value"] = float(xds.coords["time"].values[0])
108
132
  coord["obsdate"] = obsdate
109
- coord["pointingcenter"] = xds.attrs[_pointing_center].copy()
110
- if "position" in xds.attrs["telescope"]:
111
- telpos = {}
112
- telpos["refer"] = xds.attrs["telescope"]["position"]["ellipsoid"]
113
- if xds.attrs["telescope"]["position"]["ellipsoid"] == "GRS80":
114
- telpos["refer"] = "ITRF"
115
- for i in range(3):
116
- telpos[f"m{i}"] = {
117
- "unit": xds.attrs["telescope"]["position"]["units"][i],
118
- "value": xds.attrs["telescope"]["position"]["value"][i],
119
- }
120
- telpos["type"] = "position"
121
- coord["telescopeposition"] = telpos
133
+ if _pointing_center in xds[sky_ap].attrs:
134
+ coord["pointingcenter"] = {
135
+ "initial": True,
136
+ "value": xds[sky_ap].attrs[_pointing_center]["data"],
137
+ }
122
138
  if "l" in xds.coords:
123
139
  coord["direction0"] = _compute_direction_dict(xds)
124
140
  else:
125
141
  coord["linear0"] = _compute_linear_dict(xds)
126
142
  coord["stokes1"] = {
127
- "axes": np.array(["Stokes"], dtype="<U16"),
143
+ "_axes_sizes": np.array([xds.sizes["polarization"]], dtype=np.int32),
144
+ "_image_axes": np.array([1], dtype=np.int32),
145
+ "axes": ["Stokes"],
128
146
  "cdelt": np.array([1.0]),
129
147
  "crpix": np.array([0.0]),
130
148
  "crval": np.array([1.0]),
131
149
  "pc": np.array([[1.0]]),
132
- "stokes": np.array(xds.polarization.values, dtype="<U16"),
150
+ "stokes": xds.polarization.values.tolist(),
133
151
  }
134
152
  coord["spectral2"] = _compute_spectral_dict(xds)
135
- coord["pixelmap0"] = np.array([0, 1])
136
- coord["pixelmap1"] = np.array([2])
137
- coord["pixelmap2"] = np.array([3])
153
+ coord["pixelmap0"] = np.array([0, 1], dtype=np.int32)
154
+ coord["pixelmap1"] = np.array([2], dtype=np.int32)
155
+ coord["pixelmap2"] = np.array([3], dtype=np.int32)
138
156
  coord["pixelreplace0"] = np.array([0.0, 0.0])
139
157
  coord["pixelreplace1"] = np.array([0.0])
140
158
  coord["pixelreplace2"] = np.array([0.0])
@@ -144,7 +162,6 @@ def _coord_dict_from_xds(xds: xr.Dataset) -> dict:
144
162
  # this probbably needs some verification
145
163
  coord["worldreplace0"] = [0.0, 0.0]
146
164
  coord["worldreplace1"] = np.array(coord["stokes1"]["crval"])
147
- # print("spectral", coord["spectral2"])
148
165
  coord["worldreplace2"] = np.array(coord["spectral2"]["wcs"]["crval"])
149
166
  return coord
150
167
 
@@ -181,7 +198,7 @@ def _imageinfo_dict_from_xds(xds: xr.Dataset) -> dict:
181
198
  ii["image_type"] = (
182
199
  xds[ap_sky].attrs["image_type"] if "image_type" in xds[ap_sky].attrs else ""
183
200
  )
184
- ii["objectname"] = xds.attrs[_object_name]
201
+ ii["objectname"] = xds[ap_sky].attrs[_object_name]
185
202
  if "BEAM" in xds.data_vars:
186
203
  # multi beam
187
204
  pp = {}
@@ -231,7 +248,9 @@ def _write_casa_data(xds: xr.Dataset, image_full_path: str) -> None:
231
248
  else ("frequency", "polarization", "v", "u")
232
249
  )
233
250
  casa_image_shape = xds[sky_ap].isel(time=0).transpose(*trans_coords).shape[::-1]
234
- active_mask = xds.attrs["active_mask"] if _active_mask in xds.attrs else ""
251
+ active_mask = (
252
+ xds[sky_ap].attrs["active_mask"] if _active_mask in xds[sky_ap].attrs else ""
253
+ )
235
254
  masks = []
236
255
  masks_rec = {}
237
256
  mask_rec = {
@@ -17,9 +17,10 @@ from ..common import (
17
17
  )
18
18
  from xradio._utils.coord_math import _deg_to_rad
19
19
  from xradio._utils.dict_helpers import (
20
- make_quantity,
21
20
  make_frequency_reference_dict,
21
+ make_quantity,
22
22
  make_skycoord_dict,
23
+ make_time_measure_dict,
23
24
  )
24
25
  import copy
25
26
  import dask
@@ -49,7 +50,7 @@ def _fits_image_to_xds(
49
50
  sphr_dims = helpers["sphr_dims"]
50
51
  ary = _read_image_array(img_full_path, chunks, helpers, verbose)
51
52
  dim_order = _get_xds_dim_order(sphr_dims)
52
- xds = _add_sky_or_aperture(xds, ary, dim_order, helpers, sphr_dims)
53
+ xds = _add_sky_or_aperture(xds, ary, dim_order, header, helpers, sphr_dims)
53
54
  xds.attrs = attrs
54
55
  xds = _add_coord_attrs(xds, helpers)
55
56
  if helpers["has_multibeam"]:
@@ -70,13 +71,7 @@ def _add_coord_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
70
71
 
71
72
  def _add_time_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
72
73
  time_coord = xds.coords["time"]
73
- meta = copy.deepcopy(helpers["obsdate"])
74
- del meta["value"]
75
- # meta["units"] = [ meta["units"] ]
76
- # meta['format'] = 'MJD'
77
- # meta['time_scale'] = meta['refer']
78
- # del meta['refer']
79
- time_coord.attrs = meta
74
+ time_coord.attrs = copy.deepcopy(helpers["obsdate"]["attrs"])
80
75
  xds.assign_coords(time=time_coord)
81
76
  return xds
82
77
 
@@ -176,15 +171,6 @@ def _xds_direction_attrs_from_header(helpers: dict, header) -> dict:
176
171
  direction["reference"] = make_skycoord_dict(
177
172
  [0.0, 0.0], units=["rad", "rad"], frame=ref_sys
178
173
  )
179
- """
180
- direction["reference"] = {
181
- "type": "sky_coord",
182
- "frame": ref_sys,
183
- "equinox": ref_eqx,
184
- "units": ["rad", "rad"],
185
- "value": [0.0, 0.0],
186
- }
187
- """
188
174
  dir_axes = helpers["dir_axes"]
189
175
  ddata = []
190
176
  dunits = []
@@ -261,18 +247,21 @@ def _get_telescope_metadata(helpers: dict, header) -> dict:
261
247
  r = np.sqrt(np.sum(xyz * xyz))
262
248
  lat = np.arcsin(z / r)
263
249
  long = np.arctan2(y, x)
264
- tel["position"] = {
265
- "type": "position",
266
- # I haven't seen a FITS keyword for reference frame of telescope posiiton
267
- "ellipsoid": "GRS80",
268
- "units": ["rad", "rad", "m"],
269
- "value": np.array([long, lat, r]),
250
+ tel["location"] = {
251
+ "attrs": {
252
+ "coordinate_system": "geocentric",
253
+ # I haven't seen a FITS keyword for reference frame of telescope posiiton
254
+ "frame": "ITRF",
255
+ "origin_object_name": "earth",
256
+ "type": "location",
257
+ "units": ["rad", "rad", "m"],
258
+ },
259
+ "data": np.array([long, lat, r]),
270
260
  }
271
- helpers["tel_pos"] = tel["position"]
272
261
  return tel
273
262
 
274
263
 
275
- def _pointing_center_to_metadata(helpers: dict, header) -> dict:
264
+ def _compute_pointing_center(helpers: dict, header) -> dict:
276
265
  # Neither helpers or header is modified
277
266
  t_axes = helpers["t_axes"]
278
267
  long_unit = header[f"CUNIT{t_axes[0]}"]
@@ -285,7 +274,9 @@ def _pointing_center_to_metadata(helpers: dict, header) -> dict:
285
274
  pc_lat = float(header[f"CRVAL{t_axes[1]}"]) * unit[1]
286
275
  pc_long = pc_long.to(u.rad).value
287
276
  pc_lat = pc_lat.to(u.rad).value
288
- return {"value": np.array([pc_long, pc_lat]), "initial": True}
277
+ return make_skycoord_dict(
278
+ [pc_long, pc_lat], units=["rad", "rad"], frame=helpers["ref_sys"]
279
+ )
289
280
 
290
281
 
291
282
  def _user_attrs_from_header(header) -> dict:
@@ -382,7 +373,7 @@ def _create_dim_map(helpers: dict, header) -> dict:
382
373
  return dim_map
383
374
 
384
375
 
385
- def _fits_header_to_xds_attrs(hdulist: fits.hdu.hdulist.HDUList) -> dict:
376
+ def _fits_header_to_xds_attrs(hdulist: fits.hdu.hdulist.HDUList) -> tuple:
386
377
  primary = None
387
378
  beams = None
388
379
  for hdu in hdulist:
@@ -418,9 +409,7 @@ def _fits_header_to_xds_attrs(hdulist: fits.hdu.hdulist.HDUList) -> dict:
418
409
  if dir_axes is not None:
419
410
  attrs["direction"] = _xds_direction_attrs_from_header(helpers, header)
420
411
  # FIXME read fits data in chunks in case all data too large to hold in memory
421
- has_mask = da.any(da.isnan(primary.data)).compute()
422
- attrs["active_mask"] = "MASK0" if has_mask else None
423
- helpers["has_mask"] = has_mask
412
+ helpers["has_mask"] = da.any(da.isnan(primary.data)).compute()
424
413
  beam = _beam_attr_from_header(helpers, header)
425
414
  if beam != "mb":
426
415
  helpers["beam"] = beam
@@ -432,24 +421,15 @@ def _fits_header_to_xds_attrs(hdulist: fits.hdu.hdulist.HDUList) -> dict:
432
421
  helpers["dtype"] = "float64"
433
422
  else:
434
423
  raise RuntimeError(f'Unhandled data type {header["BITPIX"]}')
435
- helpers["btype"] = header["BTYPE"] if "BTYPE" in header else None
436
- helpers["bunit"] = header["BUNIT"] if "BUNIT" in header else None
437
- attrs["object_name"] = header["OBJECT"] if "OBJECT" in header else None
438
- obsdate = {}
439
- obsdate["type"] = "time"
440
- obsdate["value"] = Time(header["DATE-OBS"], format="isot").mjd
441
- obsdate["units"] = ["d"]
442
- obsdate["scale"] = header["TIMESYS"]
443
- obsdate["format"] = "MJD"
444
- attrs["obsdate"] = obsdate
445
- helpers["obsdate"] = obsdate
446
- attrs["observer"] = header["OBSERVER"]
447
- attrs["pointing_center"] = _pointing_center_to_metadata(helpers, header)
448
- attrs["description"] = None
449
- attrs["telescope"] = _get_telescope_metadata(helpers, header)
424
+ helpers["obsdate"] = make_time_measure_dict(
425
+ data=Time(header["DATE-OBS"], format="isot").mjd,
426
+ units=["d"],
427
+ scale=header["TIMESYS"],
428
+ time_format="MJD",
429
+ )
430
+
450
431
  # TODO complete _make_history_xds when spec has been finalized
451
432
  # attrs['history'] = _make_history_xds(header)
452
- attrs["user"] = _user_attrs_from_header(header)
453
433
  return attrs, helpers, header
454
434
 
455
435
 
@@ -532,7 +512,7 @@ def _create_coords(
532
512
 
533
513
 
534
514
  def _get_time_values(helpers):
535
- return [helpers["obsdate"]["value"]]
515
+ return [helpers["obsdate"]["data"]]
536
516
 
537
517
 
538
518
  def _get_pol_values(helpers):
@@ -710,14 +690,22 @@ def _add_sky_or_aperture(
710
690
  xds: xr.Dataset,
711
691
  ary: Union[np.ndarray, da.array],
712
692
  dim_order: list,
693
+ header,
713
694
  helpers: dict,
714
695
  has_sph_dims: bool,
715
696
  ) -> xr.Dataset:
716
697
  xda = xr.DataArray(ary, dims=dim_order)
717
- image_type = helpers["btype"]
718
- unit = helpers["bunit"]
719
- xda.attrs[_image_type] = image_type
720
- xda.attrs["units"] = unit
698
+ for h, a in zip(
699
+ ["BUNIT", "BTYPE", "OBJECT", "OBSERVER"],
700
+ ["units", _image_type, "object_name", "observer"],
701
+ ):
702
+ if h in header:
703
+ xda.attrs[a] = header[h]
704
+ xda.attrs["obsdate"] = helpers["obsdate"].copy()
705
+ xda.attrs["pointing_center"] = _compute_pointing_center(helpers, header)
706
+ xda.attrs["telescope"] = _get_telescope_metadata(helpers, header)
707
+ xda.attrs["description"] = None
708
+ xda.attrs["user"] = _user_attrs_from_header(header)
721
709
  name = "SKY" if has_sph_dims else "APERTURE"
722
710
  xda = xda.rename(name)
723
711
  xds[xda.name] = xda
@@ -727,6 +715,9 @@ def _add_sky_or_aperture(
727
715
  mask.attrs = {}
728
716
  mask = mask.rename("MASK0")
729
717
  xds["MASK0"] = mask
718
+ xda.attrs["active_mask"] = "MASK0"
719
+ xda = xda.rename(name)
720
+ xds[xda.name] = xda
730
721
  return xds
731
722
 
732
723
 
@@ -13,4 +13,4 @@ _np_types = {
13
13
  "int64": np.int64,
14
14
  }
15
15
 
16
- _top_level_sub_xds = "_attrs_xds"
16
+ _top_level_sub_xds = "_attrs_xds_"
@@ -12,9 +12,9 @@ from xradio._utils.zarr.common import _get_file_system_and_items
12
12
 
13
13
 
14
14
  def _read_zarr(
15
- zarr_store: str, output: dict, selection: dict = {}
15
+ zarr_store: str, id_dict: dict, selection: dict = {}
16
16
  ) -> (xr.Dataset, bool):
17
- # supported key/values in output are:
17
+ # supported key/values in id_dict are:
18
18
  # "dv"
19
19
  # what data variables should be returned as.
20
20
  # "numpy": numpy arrays
@@ -29,27 +29,27 @@ def _read_zarr(
29
29
  do_dask = False
30
30
  do_numpy = False
31
31
  do_np_coords = False
32
- if "dv" in output:
33
- dv = output["dv"]
32
+ if "dv" in id_dict:
33
+ dv = id_dict["dv"]
34
34
  if dv in ["dask", "numpy"]:
35
35
  do_dask = dv == "dask"
36
36
  do_numpy = not do_dask
37
37
  else:
38
38
  raise ValueError(
39
- f"Unsupported value {output[dv]} for output[dv]. "
39
+ f"Unsupported value {id_dict[dv]} for id_dict[dv]. "
40
40
  "Supported values are 'dask' and 'numpy'"
41
41
  )
42
- if "coords" in output:
43
- c = output["coords"]
42
+ if "coords" in id_dict:
43
+ c = id_dict["coords"]
44
44
  if c == "numpy":
45
45
  do_np_coords = True
46
46
  else:
47
47
  raise ValueError(
48
- f"Unexpected value {c} for output[coords]. "
48
+ f"Unexpected value {c} for id_dict[coords]. "
49
49
  "The supported value is 'numpy'"
50
50
  )
51
51
  # do not pass selection, because that is only for the top level data vars
52
- xds = _decode(xds, zarr_store, output)
52
+ xds = _decode(xds, zarr_store, id_dict)
53
53
  if do_np_coords:
54
54
  xds = _coords_to_numpy(xds)
55
55
  if do_dask:
@@ -59,11 +59,9 @@ def _read_zarr(
59
59
  return xds
60
60
 
61
61
 
62
- def _decode(xds: xr.Dataset, zarr_store: str, output: dict) -> (xr.Dataset, bool):
62
+ def _decode(xds: xr.Dataset, zarr_store: str, id_dict: dict) -> xr.Dataset:
63
63
  xds.attrs = _decode_dict(xds.attrs, "")
64
- sub_xdses = _decode_sub_xdses(zarr_store, output)
65
- for k, v in sub_xdses.items():
66
- xds.attrs[k] = v
64
+ _decode_sub_xdses(xds, zarr_store, id_dict)
67
65
  return xds
68
66
 
69
67
 
@@ -85,26 +83,45 @@ def _decode_dict(my_dict: dict, top_key: str) -> dict:
85
83
  return my_dict
86
84
 
87
85
 
88
- def _decode_sub_xdses(zarr_store: str, output: dict) -> dict:
89
- sub_xdses = {}
86
+ def _decode_sub_xdses(xarrayObj, top_dir: str, id_dict: dict) -> None:
87
+ # FIXME this also needs to support S3
88
+ # determine immediate subdirs of zarr_store
89
+ entries = os.scandir(top_dir)
90
+ for d in entries:
91
+ path = os.path.join(top_dir, d.name)
92
+ if os.path.isdir(path):
93
+ if d.name.startswith(_top_level_sub_xds):
94
+ ky = d.name[len(_top_level_sub_xds) :]
95
+ xarrayObj.attrs[ky] = _read_zarr(path, id_dict)
96
+ # TODO if attrs that are xdses have attrs that are xdses ...
97
+ else:
98
+ # descend into the directory
99
+ _decode_sub_xdses(xarrayObj[d.name], path, id_dict)
90
100
 
91
- fs, store_contents = _get_file_system_and_items(zarr_store)
92
101
 
102
+ """
103
+ def _decode_sub_xdses(zarr_store: str, id_dict: dict) -> dict:
104
+ sub_xdses = {}
105
+ fs, store_contents = _get_file_system_and_items(zarr_store)
93
106
  if isinstance(fs, s3fs.core.S3FileSystem):
94
107
  # could we just use the items as returned from the helper function..?
95
108
  store_tree = fs.walk(zarr_store, topdown=True)
109
+ # Q: what is prepend_s3 used for? In this version it is defined but not used.
96
110
  prepend_s3 = "s3://"
97
111
  else:
98
112
  store_tree = os.walk(zarr_store, topdown=True)
99
113
  prepend_s3 = ""
100
-
101
114
  for root, dirs, files in store_tree:
115
+ relpath = os.path.relpath(root, zarr_store)
116
+ print("rpath", relpath)
102
117
  for d in dirs:
103
118
  if d.startswith(_top_level_sub_xds):
104
- xds = _read_zarr(os.sep.join([root, d]), output)
119
+ xds = _read_zarr(os.sep.join([root, d]), id_dict)
105
120
  # for k, v in xds.data_vars.items():
106
121
  # xds = xds.drop_vars([k]).assign({k: v.compute()})
107
- ky = d[len(_top_level_sub_xds) + 1 :]
122
+ ky = d[len(_top_level_sub_xds) :]
108
123
  sub_xdses[ky] = xds
109
-
124
+ print(f"Sub xdses: {sub_xdses.keys()}")
125
+ print("return")
110
126
  return sub_xdses
127
+ """
@@ -0,0 +1,48 @@
1
+ import logging
2
+ import numpy as np
3
+ import xarray as xr
4
+ import os
5
+ from .common import _np_types, _top_level_sub_xds
6
+
7
+
8
+ def _write_zarr(xds: xr.Dataset, zarr_store: str):
9
+ xds_copy = xds.copy(deep=True)
10
+ sub_xds_dict = _encode(xds_copy, zarr_store)
11
+ z_obj = xds_copy.to_zarr(store=zarr_store, compute=True)
12
+ if sub_xds_dict:
13
+ _write_sub_xdses(sub_xds_dict)
14
+
15
+
16
+ def _encode(xds: xr.Dataset, top_path: str) -> dict:
17
+ # encode attrs
18
+ sub_xds_dict = {}
19
+ _encode_dict(xds.attrs, top_path, sub_xds_dict)
20
+ for dv in xds.data_vars:
21
+ _encode_dict(xds[dv].attrs, os.sep.join([top_path, dv]), sub_xds_dict)
22
+ logging.debug(f"Encoded sub_xds_dict: {sub_xds_dict}")
23
+ return sub_xds_dict
24
+
25
+
26
+ def _encode_dict(my_dict: dict, top_path: str, sub_xds_dict) -> tuple:
27
+ del_keys = []
28
+ for k, v in my_dict.items():
29
+ if isinstance(v, dict):
30
+ z = os.sep.join([top_path, k])
31
+ _encode_dict(v, z, sub_xds_dict)
32
+ elif isinstance(v, np.ndarray):
33
+ my_dict[k] = {}
34
+ my_dict[k]["_type"] = "numpy.ndarray"
35
+ my_dict[k]["_value"] = v.tolist()
36
+ my_dict[k]["_dtype"] = str(v.dtype)
37
+ elif isinstance(v, xr.Dataset):
38
+ sub_xds_dict[os.sep.join([top_path, f"{_top_level_sub_xds}{k}"])] = v.copy(
39
+ deep=True
40
+ )
41
+ del_keys.append(k)
42
+ for k in del_keys:
43
+ del my_dict[k]
44
+
45
+
46
+ def _write_sub_xdses(sub_xds: dict):
47
+ for k, v in sub_xds.items():
48
+ z_obj = v.to_zarr(store=k, compute=True)
@@ -48,7 +48,9 @@ def _load_casa_image_block(infile: str, block_des: dict, do_sky_coords) -> xr.Da
48
48
  block = _get_persistent_block(
49
49
  image_full_path, shapes, starts, dimorder, transpose_list, new_axes
50
50
  )
51
- xds = _add_sky_or_aperture(xds, block, dimorder, image_full_path, ret["sphr_dims"])
51
+ xds = _add_sky_or_aperture(
52
+ xds, block, dimorder, image_full_path, ret["sphr_dims"], True
53
+ )
52
54
  mymasks = _get_mask_names(image_full_path)
53
55
  for m in mymasks:
54
56
  full_path = os.sep.join([image_full_path, m])
@@ -57,7 +59,7 @@ def _load_casa_image_block(infile: str, block_des: dict, do_sky_coords) -> xr.Da
57
59
  )
58
60
  # data vars are all caps by convention
59
61
  xds = _add_mask(xds, m.upper(), block, dimorder)
60
- xds.attrs = _casa_image_to_xds_attrs(image_full_path, True)
62
+ xds.attrs = _casa_image_to_xds_attrs(image_full_path)
61
63
  mb = _multibeam_array(xds, image_full_path, False)
62
64
  if mb is not None:
63
65
  selectors = {}
@@ -86,6 +88,7 @@ def _read_casa_image(
86
88
  dimorder,
87
89
  img_full_path,
88
90
  ret["sphr_dims"],
91
+ history,
89
92
  )
90
93
  if masks:
91
94
  mymasks = _get_mask_names(img_full_path)
@@ -93,7 +96,7 @@ def _read_casa_image(
93
96
  ary = _read_image_array(img_full_path, chunks, mask=m, verbose=verbose)
94
97
  # data var names are all caps by convention
95
98
  xds = _add_mask(xds, m.upper(), ary, dimorder)
96
- xds.attrs = _casa_image_to_xds_attrs(img_full_path, history)
99
+ xds.attrs = _casa_image_to_xds_attrs(img_full_path)
97
100
  mb = _multibeam_array(xds, img_full_path, True)
98
101
  if mb is not None:
99
102
  xds["BEAM"] = mb
@@ -17,7 +17,8 @@ from xradio.measurement_set._utils._msv2._tables.read import (
17
17
  make_taql_where_between_min_max,
18
18
  load_generic_table,
19
19
  )
20
- from xradio._utils.list_and_array import cast_to_str
20
+ from xradio._utils.list_and_array import cast_to_str, get_pad_value
21
+
21
22
  from xradio._utils.coord_math import (
22
23
  convert_to_si_units,
23
24
  add_position_offsets,
@@ -547,10 +548,16 @@ def pad_missing_sources(
547
548
  for source_id in unique_source_ids
548
549
  if source_id not in source_xds.coords["SOURCE_ID"]
549
550
  ]
551
+ if len(missing_source_ids) < 1:
552
+ return source_xds
550
553
 
551
554
  # would like to use the new-ish xr.pad, but it creates issues with indices/coords and is
552
555
  # also not free of overheads, as it for example changes all numeric types to float64
553
- missing_source_xds = xr.full_like(source_xds.isel(SOURCE_ID=0), fill_value=np.nan)
556
+ fill_value = {
557
+ var_name: get_pad_value(var.dtype)
558
+ for var_name, var in source_xds.data_vars.items()
559
+ }
560
+ missing_source_xds = xr.full_like(source_xds.isel(SOURCE_ID=0), fill_value)
554
561
  pad_str = "Unknown"
555
562
  pad_str_type = "<U9"
556
563
  for var in missing_source_xds.data_vars:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xradio
3
- Version: 0.0.51
3
+ Version: 0.0.52
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
@@ -80,9 +80,9 @@ Dynamic: license-file
80
80
  Xarray Radio Astronomy Data IO is still in development.
81
81
 
82
82
  [![Python 3.11 3.12 3.13](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/downloads/release/python-3130/)
83
- ![Linux Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml/badge.svg?branch=main)
84
- ![macOS Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml/badge.svg?branch=main)
85
- ![ipynb Tests](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml/badge.svg?branch=main)
83
+ [![Linux Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml/badge.svg?branch=main)](https://github.com/casangi/xradio/actions/workflows/python-testing-linux.yml?query=branch%3Amain)
84
+ [![macOS Tests](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml/badge.svg?branch=main)](https://github.com/casangi/xradio/actions/workflows/python-testing-macos.yml?query=branch%3Amain)
85
+ [![ipynb Tests](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml/badge.svg?branch=main)](https://github.com/casangi/xradio/actions/workflows/run-ipynb.yml?query=branch%3Amain)
86
86
  [![Coverage](https://codecov.io/gh/casangi/xradio/branch/main/graph/badge.svg)](https://codecov.io/gh/casangi/xradio/branch/main/xradio)
87
87
  [![Documentation Status](https://readthedocs.org/projects/xradio/badge/?version=latest)](https://xradio.readthedocs.io)
88
88
  [![Version Status](https://img.shields.io/pypi/v/xradio.svg)](https://pypi.python.org/pypi/xradio/)
@@ -1,49 +0,0 @@
1
- import numpy as np
2
- import xarray as xr
3
- import os
4
- from .common import _np_types, _top_level_sub_xds
5
-
6
-
7
- def _write_zarr(xds: xr.Dataset, zarr_store: str):
8
- xds_copy = xds.copy(deep=True)
9
- xds_copy, xds_dict = _encode(xds_copy)
10
- z_obj = xds_copy.to_zarr(store=zarr_store, compute=True)
11
- _write_sub_xdses(zarr_store, xds_dict, _top_level_sub_xds)
12
-
13
-
14
- def _encode(xds: xr.Dataset):
15
- # encode attrs
16
- xds.attrs, xds_dict = _encode_dict(xds.attrs)
17
- return xds, xds_dict
18
-
19
-
20
- def _encode_dict(my_dict: dict, top_key="") -> tuple:
21
- xds_dict = {}
22
- del_keys = []
23
- for k, v in my_dict.items():
24
- if isinstance(v, dict):
25
- z = os.sep.join([top_key, k]) if top_key else k
26
- my_dict[k], ret_xds_dict = _encode_dict(v, z)
27
- if ret_xds_dict:
28
- xds_dict[k] = ret_xds_dict
29
- elif isinstance(v, np.ndarray):
30
- my_dict[k] = {}
31
- my_dict[k]["_type"] = "numpy.ndarray"
32
- my_dict[k]["_value"] = v.tolist()
33
- my_dict[k]["_dtype"] = str(v.dtype)
34
- elif isinstance(v, xr.Dataset):
35
- xds_dict[k] = v.copy(deep=True)
36
- del_keys.append(k)
37
- for k in del_keys:
38
- del my_dict[k]
39
- return my_dict, xds_dict
40
-
41
-
42
- def _write_sub_xdses(zarr_store: str, xds_dict: dict, path: str):
43
- for k, v in xds_dict.items():
44
- my_path = f"{path}_{k}" if path else f"{k}"
45
- if isinstance(v, dict):
46
- _write_sub_xdses(zarr_store, xds_dict[k], my_path)
47
- elif isinstance(v, xr.Dataset):
48
- zs = os.sep.join([zarr_store, my_path])
49
- z_obj = v.to_zarr(store=zs, compute=True)
File without changes
File without changes
File without changes
File without changes