xradio 0.0.50__py3-none-any.whl → 0.0.52__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.
@@ -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
xradio/_utils/schema.py CHANGED
@@ -190,14 +190,14 @@ casacore_to_msv4_measure_type = {
190
190
  "LSRK": "lsrk",
191
191
  "LSRD": "lsrd",
192
192
  "BARY": "BARY",
193
- "GEO": "GEO",
193
+ "GEO": "gcrs",
194
194
  "TOPO": "TOPO",
195
195
  }, # The frames/observer we are not sure if/how to translate to astropy are uppercase
196
196
  },
197
197
  "position": {
198
198
  "type": "location",
199
199
  "Ref": "frame",
200
- "Ref_map": {"ITRF": "GRS80"},
200
+ "Ref_map": {"ITRF": "ITRS"},
201
201
  },
202
202
  "uvw": {
203
203
  "type": "uvw",
@@ -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_"