Source code for gwcs.coordinate_frames._core

import numbers

import numpy as np
from astropy import units as u
from astropy.wcs.wcsapi.high_level_api import (
    high_level_objects_to_values,
    values_to_high_level_objects,
)

from ._base import BaseCoordinateFrame
from ._properties import FrameProperties

__all__ = ["CoordinateFrame"]


[docs] class CoordinateFrame(BaseCoordinateFrame): """ Base class for Coordinate Frames. Parameters ---------- naxes : int Number of axes. axes_type : str One of ["SPATIAL", "SPECTRAL", "TIME"] axes_order : tuple of int A dimension in the input data that corresponds to this axis. reference_frame : astropy.coordinates.builtin_frames Reference frame (usually used with output_frame to convert to world coordinate objects). unit : list of astropy.units.Unit Unit for each axis. axes_names : list Names of the axes in this frame. name : str Name of this frame. """ def __init__( self, naxes, axes_type, axes_order, reference_frame=None, unit=None, axes_names=None, name=None, axis_physical_types=None, ): self._naxes = naxes self._axes_order = tuple(axes_order) self._reference_frame = reference_frame if name is None: self._name = self.__class__.__name__ else: self._name = name if len(self._axes_order) != naxes: msg = "Length of axes_order does not match number of axes." raise ValueError(msg) if isinstance(axes_type, str): axes_type = (axes_type,) self._prop = FrameProperties( naxes, axes_type, unit, axes_names, axis_physical_types or self._default_axis_physical_types(axes_type), ) super().__init__() def _default_axis_physical_types(self, axes_type): """ The default physical types to use for this frame if none are specified by the user. """ return tuple(f"custom:{t}" for t in axes_type) def __repr__(self): fmt = ( f'<{self.__class__.__name__}(name="{self.name}", unit={self.unit}, ' f"axes_names={self.axes_names}, axes_order={self.axes_order}" ) if self.reference_frame is not None: fmt += f", reference_frame={self.reference_frame}" fmt += ")>" return fmt def __str__(self): if self._name is not None: return self._name return self.__class__.__name__ def _sort_property(self, prop): sorted_prop = sorted( zip(prop, self.axes_order, strict=False), key=lambda x: x[1] ) return tuple([t[0] for t in sorted_prop]) @property def name(self): """A custom name of this frame.""" return self._name @name.setter def name(self, val): """A custom name of this frame.""" self._name = val @property def naxes(self): """The number of axes in this frame.""" return self._naxes @property def unit(self): """The unit of this frame.""" return self._sort_property(self._prop.unit) @property def axes_names(self): """Names of axes in the frame.""" return self._sort_property(self._prop.axes_names) @property def axes_order(self): """A tuple of indices which map inputs to axes.""" return self._axes_order @property def reference_frame(self): """Reference frame, used to convert to world coordinate objects.""" return self._reference_frame @property def axes_type(self): """Type of this frame : 'SPATIAL', 'SPECTRAL', 'TIME'.""" return self._sort_property(self._prop.axes_type) @property def axis_physical_types(self): """ The axis physical types for this frame. These physical types are the types in frame order, not transform order. """ return self._sort_property(self._prop.axis_physical_types) @property def world_axis_object_classes(self): return { f"{at}{i}" if i != 0 else at: (u.Quantity, (), {"unit": unit}) for i, (at, unit) in enumerate(zip(self.axes_type, self.unit, strict=False)) } @property def _native_world_axis_object_components(self): return [ (f"{at}{i}" if i != 0 else at, 0, "value") for i, at in enumerate(self._prop.axes_type) ] @property def serialized_classes(self): """ This property is used by the low level WCS API in Astropy. By providing it we can duck type as a low level WCS object. """ return False
[docs] def to_high_level_coordinates(self, *values): """ Convert "values" to high level coordinate objects described by this frame. "values" are the coordinates in array or scalar form, and high level objects are things such as ``SkyCoord`` or ``Quantity``. See :ref:`wcsapi` for details. Parameters ---------- values : `numbers.Number`, `numpy.ndarray`, or `~astropy.units.Quantity` ``naxis`` number of coordinates as scalars or arrays. Returns ------- high_level_coordinates One (or more) high level object describing the coordinate. """ # We allow Quantity-like objects here which values_to_high_level_objects # does not. values = [ v.to_value(unit) if hasattr(v, "to_value") else v for v, unit in zip(values, self.unit, strict=False) ] if not all( isinstance(v, numbers.Number) or type(v) is np.ndarray for v in values ): msg = "All values should be a scalar number or a numpy array." raise TypeError(msg) high_level = values_to_high_level_objects(*values, low_level_wcs=self) if len(high_level) == 1: high_level = high_level[0] return high_level
[docs] def from_high_level_coordinates(self, *high_level_coords): """ Convert high level coordinate objects to "values" as described by this frame. "values" are the coordinates in array or scalar form, and high level objects are things such as ``SkyCoord`` or ``Quantity``. See :ref:`wcsapi` for details. Parameters ---------- high_level_coordinates One (or more) high level object describing the coordinate. Returns ------- values : `numbers.Number` or `numpy.ndarray` ``naxis`` number of coordinates as scalars or arrays. """ values = high_level_objects_to_values(*high_level_coords, low_level_wcs=self) if len(values) == 1: values = values[0] return values