diff --git a/src/xradio/_utils/_casacore/casatools_to_casacore.py b/src/xradio/_utils/_casacore/casacore_from_casatools.py similarity index 55% rename from src/xradio/_utils/_casacore/casatools_to_casacore.py rename to src/xradio/_utils/_casacore/casacore_from_casatools.py index c9fcc00d..d6cd0e46 100644 --- a/src/xradio/_utils/_casacore/casatools_to_casacore.py +++ b/src/xradio/_utils/_casacore/casacore_from_casatools.py @@ -11,7 +11,7 @@ import inspect import shutil from functools import wraps -from typing import Any, Dict, List, Union +from typing import Any, Dict, List, Union, Sequence import os import casatools @@ -248,8 +248,62 @@ def getcolshapestring(self, *args, **kwargs): ret = super().getcolshapestring(*args, **kwargs) return [str(list(reversed(ast.literal_eval(shape)))) for shape in ret] + def getcellslice(self, columnname, rownr, blc, trc, incr=1): + """Retrieve a sliced portion of a cell from a specified column. + This method extracts a subarray from a cell within a table column, + given the bottom-left corner (BLC) and top-right corner (TRC) indices. + It also supports an optional increment (`incr`) to control step size. + + Parameters + ---------- + columnname : str + The name of the column from which to extract data. + rownr : int or Sequence[int] + The row number(s) from which to extract data. If a sequence is provided, + it is reversed before processing. + blc : Sequence[int] + The bottom-left corner indices of the slice. + trc : Sequence[int] + The top-right corner indices of the slice. + incr : int or Sequence[int], optional + Step size for slicing. If a sequence is provided, it is reversed. + If a single integer is given, it is expanded to match `blc` dimensions. + Defaults to 1. + + Returns + ------- + Any + The extracted slice from the specified column and row(s). + + Notes + ----- + - If `rownr` is a sequence, it is reversed before processing. + - The `blc`, `trc`, and `incr` parameters are converted to lists of integers. + - Calls the superclass method `getcellslice` for actual data retrieval. + """ + + if isinstance(rownr, Sequence): + rownr = rownr[::-1] + else: + rownr = [rownr]*len(blc) + rownr = 0 + if isinstance(blc, Sequence): + blc = list(map(int, blc[::-1])) + if isinstance(trc, Sequence): + trc = list(map(int, trc[::-1])) + if isinstance(incr, Sequence): + incr = incr[::-1] + else: + incr = [incr]*len(blc) + ret = super().getcellslice(columnname=columnname, rownr=rownr, blc=blc, trc=trc) + return ret + + +@wrap_class_methods class image(casatools.image): + """A Wrapper class around `casatools.image` that provides python-casacore-like methods.""" + def __init__( self, imagename, @@ -265,7 +319,224 @@ def __init__( tileshape=(), ): super().__init__() - self.open(*arg, **kwargs) + self._imagename = imagename + if shape is None: + # self.open(*arg, **kwargs) + self.open(imagename) + else: + if value is None: + self.newimagefromshape(outfile, shape=shape) + else: + self.newimagefromarray(outfile, pixels=self.makearray(value, shape)) + + def getdata(self, blc=None, trc=None, inc=None): + """Retrieve image data as a chunk. + + Parameters + ---------- + blc : list of int, optional + Bottom-left corner of the region to extract. Defaults to `[-1]` (entire image). + trc : list of int, optional + Top-right corner of the region to extract. Defaults to `[-1]` (entire image). + inc : list of int, optional + Step size for slicing. Defaults to `[1]`. + + Returns + ------- + numpy.ndarray + The extracted data chunk. + """ + + if blc is None: + blc = [-1] + if trc is None: + trc = [-1] + if inc is None: + inc = [1] + return super().getchunk(blc, trc, inc) + + def shape(self): + """Get the shape of the image. + + Returns + ------- + list of int + The shape of the image, with axes reversed for consistency. + """ + return list(map(int, super().shape()[::-1])) + + def coordinates(self): + """Get the coordinate system of the image. + + Returns + ------- + casatools.coordinatesystem + The coordinate system associated with the image. + """ + return coordinatesystem(self) + + def unit(self): + """Get the brightness unit of the image. + + Returns + ------- + str + The brightness unit of the image. + """ + return self.brightnessunit() + + def info(self): + """Retrieve image metadata including coordinates, misc info, and beam information. + + Returns + ------- + dict + Dictionary containing: + - 'imageinfo': Flattened image summary. + - 'coordinates': Coordinate system as a dictionary. + - 'miscinfo': Miscellaneous metadata. + """ + imageinfo = self.summary(list=False) + imageinfo = self._flatten_multibeam(imageinfo) + # table.getdesc() + + return {'imageinfo': imageinfo, + 'coordinates': self.coordsys().torecord(), + 'miscinfo': self.miscinfo()} + + def datatype(self): + return self.pixeltype() + + def _flatten_multibeam(self, imageinfo): + """Flatten the per-plane beam information in the image metadata. + + This method restructures the `perplanebeams` field in `imageinfo` + to make it more accessible by flattening the nested structure. + + Parameters + ---------- + imageinfo : dict + The image metadata containing per-plane beam information. + + Returns + ------- + dict + Updated `imageinfo` dictionary with flattened per-plane beam data. + """ + if 'perplanebeams' in imageinfo: + perplanebeams = imageinfo['perplanebeams']['beams'] + perplanebeams_flat = {} + nchan = imageinfo['perplanebeams']['nChannels'] + npol = imageinfo['perplanebeams']['nStokes'] + + for c in range(nchan): + for p in range(npol): + k = nchan * p + c + perplanebeams_flat["*" + str(k)] = perplanebeams['*'+str(c)]['*'+str(p)] + imageinfo['perplanebeams'].pop('beams', None) + imageinfo['perplanebeams'].update(perplanebeams_flat) + + return imageinfo + + +class coordinatesystem(casatools.coordsys): + """A wrapper around `casatools.coordsys` that provides python-casacore like methods""" + + def __init__(self, image=None): + self._image = image + if image is None: + self._cs = casatools.coordsys() + else: + self._cs = image.coordsys() + + def get_axes(self): + """Retrieve the names of the coordinate axes. + + Returns + ------- + list of str or list of lists + A list containing the names of each axis, grouped by coordinate type. + Spectral axes are returned as a single string instead of a list. + """ + axes = [] + axis_names = self._cs.names() + for axis_type in self.get_names(): + axis_inds = self._cs.findcoordinate(axis_type).get('pixel') + axes_list = [axis_names[idx] for idx in axis_inds[::-1]] + if axis_type == 'spectral': + axes_list = axes_list[0] + axes.append(axes_list) + return axes + + def get_referencepixel(self): + """Get the reference pixel coordinates. + + Returns + ------- + list of float + The numeric reference pixel values, with axes reversed. + """ + return self._cs.referencepixel()['numeric'][::-1] + + def get_referencevalue(self): + """Get the reference value at the reference pixel. + + Returns + ------- + list of float + The numeric reference values, with axes reversed. + """ + return self._cs.referencevalue()['numeric'][::-1] + + def get_increment(self): + """Get the coordinate increments per pixel. + + Returns + ------- + list of float + The coordinate increment values, with axes reversed. + """ + return self._cs.increment()['numeric'][::-1] + + def get_unit(self): + """Get the units of the coordinate axes. + + Returns + ------- + list of str + The units of each axis, with axes reversed. + """ + return self._cs.units()[::-1] + + def get_names(self): + """Get the coordinate type names in lowercase. + + Returns + ------- + list of str + The coordinate type names, with axes reversed. + """ + return list(map(str.lower, self._cs.coordinatetype()[::-1])) + + def dict(self): + """Convert the coordinate system to a dictionary representation. + + Returns + ------- + dict + The coordinate system in CASA's dictionary format. + """ + return self._cs.torecord() + + +class directioncoordinate(coordinatesystem): + + def __init__(self, rec): + super().__init__() + self._rec = rec + + def get_projection(self): + return self._rec['projection'] @wrap_class_methods diff --git a/src/xradio/_utils/_casacore/tables.py b/src/xradio/_utils/_casacore/tables.py index edd1a236..fda90237 100644 --- a/src/xradio/_utils/_casacore/tables.py +++ b/src/xradio/_utils/_casacore/tables.py @@ -1,7 +1,7 @@ try: from casacore import tables except ImportError: - from . import casatools_to_casacore as tables + from . import casacore_from_casatools as tables from contextlib import contextmanager from typing import Dict, Generator diff --git a/src/xradio/image/_util/_casacore/common.py b/src/xradio/image/_util/_casacore/common.py index e9763860..691ee256 100644 --- a/src/xradio/image/_util/_casacore/common.py +++ b/src/xradio/image/_util/_casacore/common.py @@ -1,4 +1,8 @@ -from casacore import images +try: + from casacore import images +except ImportError: + from ...._utils._casacore import casacore_from_casatools as images + from contextlib import contextmanager import numpy as np from typing import Dict, Generator, List, Union diff --git a/src/xradio/image/_util/_casacore/xds_from_casacore.py b/src/xradio/image/_util/_casacore/xds_from_casacore.py index 83dca34d..ee5ed437 100644 --- a/src/xradio/image/_util/_casacore/xds_from_casacore.py +++ b/src/xradio/image/_util/_casacore/xds_from_casacore.py @@ -11,9 +11,10 @@ from astropy import units as u try: from casacore import tables + from casacore.images import coordinates except ImportError: - from ...._utils._casacore import casatools_to_casacore as tables -from casacore.images import coordinates + from ...._utils._casacore import casacore_from_casatools as tables + from ...._utils._casacore import casacore_from_casatools as coordinates from .common import ( _active_mask, @@ -312,6 +313,9 @@ def _casa_image_to_xds_coords( "note": attr_note[c], } if do_sky_coords: + #from pprint import pprint + #print('--->',coord_dict) + #pprint(coord_dict) for k in coord_dict.keys(): if k.startswith("direction"): dc = coordinates.directioncoordinate(coord_dict[k]) diff --git a/src/xradio/image/_util/_casacore/xds_to_casacore.py b/src/xradio/image/_util/_casacore/xds_to_casacore.py index fa194d96..618c626c 100644 --- a/src/xradio/image/_util/_casacore/xds_to_casacore.py +++ b/src/xradio/image/_util/_casacore/xds_to_casacore.py @@ -8,7 +8,7 @@ try: from casacore import tables except ImportError: - from ...._utils._casacore import casatools_to_casacore as tables + from ...._utils._casacore import casacore_from_casatools as tables from .common import _active_mask, _create_new_image, _object_name, _pointing_center from ..common import _aperture_or_sky, _compute_sky_reference_pixel, _doppler_types diff --git a/src/xradio/image/_util/_fits/xds_from_fits.py b/src/xradio/image/_util/_fits/xds_from_fits.py index edf638bd..27786ca1 100644 --- a/src/xradio/image/_util/_fits/xds_from_fits.py +++ b/src/xradio/image/_util/_fits/xds_from_fits.py @@ -1,29 +1,25 @@ +import copy +import re +from typing import Union + import astropy as ap +import dask +import dask.array as da +import numpy as np +import xarray as xr from astropy import units as u from astropy.io import fits from astropy.time import Time -from ..common import ( - _compute_linear_world_values, - _compute_velocity_values, - _compute_world_sph_dims, - _convert_beam_to_rad, - _default_freq_info, - _doppler_types, - _freq_from_vel, - _get_unit, - _get_xds_dim_order, - _image_type, - _l_m_attr_notes, -) + from xradio._utils.coord_math import _deg_to_rad from xradio._utils.dict_helpers import make_quantity -import copy -import dask -import dask.array as da -import numpy as np -import re -from typing import Union -import xarray as xr + +from ....measurement_set._utils._utils.stokes_types import stokes_types +from ..common import (_compute_linear_world_values, _compute_velocity_values, + _compute_world_sph_dims, _convert_beam_to_rad, + _default_freq_info, _doppler_types, _freq_from_vel, + _get_unit, _get_xds_dim_order, _image_type, + _l_m_attr_notes) def _fits_image_to_xds( @@ -506,32 +502,7 @@ def _get_time_values(helpers): def _get_pol_values(helpers): - # as mapped in casacore Stokes.h - stokes_map = [ - "Undefined", - "I", - "Q", - "U", - "V", - "RR", - "RL", - "LR", - "LL", - "XX", - "XY", - "YX", - "YY", - "RX", - "RY", - "LX", - "LY", - "XR", - "XL", - "YR", - "YL", - "PP", - "PQ", - ] + idx = helpers["ctype"].index("STOKES") if idx >= 0: vals = [] @@ -541,7 +512,7 @@ def _get_pol_values(helpers): stokes_start_idx = crval - cdelt * crpix for i in range(helpers["shape"][idx]): stokes_idx = (stokes_start_idx + i) * cdelt - vals.append(stokes_map[stokes_idx]) + vals.append(stokes_types[stokes_idx]) return vals else: return ["I"] diff --git a/src/xradio/image/_util/casacore.py b/src/xradio/image/_util/casacore.py index 33d4a042..8358102a 100644 --- a/src/xradio/image/_util/casacore.py +++ b/src/xradio/image/_util/casacore.py @@ -13,7 +13,7 @@ try: from casacore import tables except ImportError: - from ..._utils._casacore import casatools_to_casacore as tables + from ..._utils._casacore import casacore_from_casatools as tables from ._casacore.common import _open_image_ro from ._casacore.xds_from_casacore import ( diff --git a/src/xradio/measurement_set/_utils/_msv2/_tables/load.py b/src/xradio/measurement_set/_utils/_msv2/_tables/load.py index 57fa2404..42d080c7 100644 --- a/src/xradio/measurement_set/_utils/_msv2/_tables/load.py +++ b/src/xradio/measurement_set/_utils/_msv2/_tables/load.py @@ -5,7 +5,7 @@ try: from casacore import tables except ImportError: - from ....._utils._casacore.casatools_to_casacore import tables + from ....._utils._casacore.casacore_from_casatools import tables from ....._utils.common import get_pad_value diff --git a/src/xradio/measurement_set/_utils/_msv2/_tables/load_main_table.py b/src/xradio/measurement_set/_utils/_msv2/_tables/load_main_table.py index fb06d8c3..5feba217 100644 --- a/src/xradio/measurement_set/_utils/_msv2/_tables/load_main_table.py +++ b/src/xradio/measurement_set/_utils/_msv2/_tables/load_main_table.py @@ -8,7 +8,7 @@ try: from casacore import tables except ImportError: - from ....._utils._casacore import casatools_to_casacore as tables + from ....._utils._casacore import casacore_from_casatools as tables from .load import load_col_chunk diff --git a/src/xradio/measurement_set/_utils/_msv2/_tables/read.py b/src/xradio/measurement_set/_utils/_msv2/_tables/read.py index a56dcc52..892c53b7 100644 --- a/src/xradio/measurement_set/_utils/_msv2/_tables/read.py +++ b/src/xradio/measurement_set/_utils/_msv2/_tables/read.py @@ -12,7 +12,7 @@ try: from casacore import tables except ImportError: - from ....._utils._casacore import casatools_to_casacore as tables + from ....._utils._casacore import casacore_from_casatools as tables from .table_query import open_query, open_table_ro from xradio._utils.list_and_array import get_pad_value diff --git a/src/xradio/measurement_set/_utils/_msv2/_tables/read_main_table.py b/src/xradio/measurement_set/_utils/_msv2/_tables/read_main_table.py index ce6283a8..0656a72c 100644 --- a/src/xradio/measurement_set/_utils/_msv2/_tables/read_main_table.py +++ b/src/xradio/measurement_set/_utils/_msv2/_tables/read_main_table.py @@ -9,7 +9,7 @@ try: from casacore import tables except ImportError: - from ....._utils._casacore import casatools_to_casacore as tables + from ....._utils._casacore import casacore_from_casatools as tables from .read import ( read_flat_col_chunk, diff --git a/src/xradio/measurement_set/_utils/_msv2/_tables/read_subtables.py b/src/xradio/measurement_set/_utils/_msv2/_tables/read_subtables.py index 08e321f6..dd60795a 100644 --- a/src/xradio/measurement_set/_utils/_msv2/_tables/read_subtables.py +++ b/src/xradio/measurement_set/_utils/_msv2/_tables/read_subtables.py @@ -10,7 +10,7 @@ try: from casacore import tables except ImportError: - from ....._utils._casacore import casatools_to_casacore as tables + from ....._utils._casacore import casacore_from_casatools as tables from .table_query import open_query, open_table_ro from .read import ( diff --git a/src/xradio/measurement_set/_utils/_msv2/_tables/table_query.py b/src/xradio/measurement_set/_utils/_msv2/_tables/table_query.py index 9d3002df..ae2390ee 100644 --- a/src/xradio/measurement_set/_utils/_msv2/_tables/table_query.py +++ b/src/xradio/measurement_set/_utils/_msv2/_tables/table_query.py @@ -4,7 +4,7 @@ try: from casacore import tables except ImportError: - from ....._utils._casacore import casatools_to_casacore as tables + from ....._utils._casacore import casacore_from_casatools as tables @contextmanager def open_table_ro(infile: str) -> Generator[tables.table, None, None]: diff --git a/src/xradio/measurement_set/_utils/_msv2/_tables/write.py b/src/xradio/measurement_set/_utils/_msv2/_tables/write.py index bea76d20..cc0464d8 100644 --- a/src/xradio/measurement_set/_utils/_msv2/_tables/write.py +++ b/src/xradio/measurement_set/_utils/_msv2/_tables/write.py @@ -7,7 +7,7 @@ try: from casacore import tables except ImportError: - from ....._utils._casacore import casatools_to_casacore as tables + from ....._utils._casacore import casacore_from_casatools as tables def revert_time(datetimes: np.ndarray) -> np.ndarray: """ diff --git a/src/xradio/measurement_set/_utils/_msv2/_tables/write_exp_api.py b/src/xradio/measurement_set/_utils/_msv2/_tables/write_exp_api.py index 9345b234..a35bcfe2 100644 --- a/src/xradio/measurement_set/_utils/_msv2/_tables/write_exp_api.py +++ b/src/xradio/measurement_set/_utils/_msv2/_tables/write_exp_api.py @@ -12,7 +12,7 @@ try: from casacore import tables except ImportError: - from ....._utils._casacore import casatools_to_casacore as tables + from ....._utils._casacore import casacore_from_casatools as tables # TODO: this should be consolidated with the equivalent in read_main_table, diff --git a/src/xradio/measurement_set/_utils/_msv2/conversion.py b/src/xradio/measurement_set/_utils/_msv2/conversion.py index cb0e5646..34286c5c 100644 --- a/src/xradio/measurement_set/_utils/_msv2/conversion.py +++ b/src/xradio/measurement_set/_utils/_msv2/conversion.py @@ -14,7 +14,7 @@ try: from casacore import tables except ImportError: - from ...._utils._casacore import casatools_to_casacore as tables + from ...._utils._casacore import casacore_from_casatools as tables from xradio.measurement_set._utils._msv2.msv4_sub_xdss import ( create_pointing_xds, diff --git a/src/xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py b/src/xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py index f6e3b9be..7dfe2ec6 100644 --- a/src/xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py +++ b/src/xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py @@ -4,7 +4,7 @@ try: from casacore import tables except ImportError: - from ...._utils._casacore import casatools_to_casacore as tables + from ...._utils._casacore import casacore_from_casatools as tables import toolviper.utils.logger as logger diff --git a/src/xradio/measurement_set/_utils/_msv2/partition_queries.py b/src/xradio/measurement_set/_utils/_msv2/partition_queries.py index 23e323eb..ce9955a9 100644 --- a/src/xradio/measurement_set/_utils/_msv2/partition_queries.py +++ b/src/xradio/measurement_set/_utils/_msv2/partition_queries.py @@ -9,7 +9,7 @@ try: from casacore import tables except ImportError: - from ...._utils._casacore import casatools_to_casacore as tables + from ...._utils._casacore import casacore_from_casatools as tables from ._tables.table_query import open_table_ro, open_query from ._tables.read import table_exists