Ejemplo n.º 1
0
    def __init__(self, x, y, copy=True, **kwargs):

        if x is None or y is None:
            raise ValueError(
                'x and y are required to instantiate CartesianRepresentation')

        if not isinstance(x, self.attr_classes['x']):
            raise TypeError('x should be a {0}'.format(
                self.attr_classes['x'].__name__))

        if not isinstance(y, self.attr_classes['y']):
            raise TypeError('y should be a {0}'.format(
                self.attr_classes['y'].__name__))

        x = self.attr_classes['x'](x, copy=copy)
        y = self.attr_classes['y'](y, copy=copy)

        if not (x.unit.physical_type == y.unit.physical_type):
            raise u.UnitsError("x and y should have matching physical types")

        try:
            x, y = np.broadcast_arrays(x, y, subok=True)
        except ValueError:
            raise ValueError("Input parameters x and y cannot be broadcast")

        self._x = x
        self._y = y
        self._differentials = {}
Ejemplo n.º 2
0
    def radial_velocity(self, val):
        if val is not None:
            if not val.unit.is_equivalent(u.km / u.s):
                raise u.UnitsError("Radial velocity must be a velocity.")

        new_spectral_axis = self.spectral_axis.with_radial_velocity(val)
        self._spectral_axis = new_spectral_axis
Ejemplo n.º 3
0
    def rest_value(self, value):
        if not hasattr(value, 'unit') or not value.unit.is_equivalent(
                u.Hz, u.spectral()):
            raise u.UnitsError(
                "Rest value must be energy/wavelength/frequency equivalent.")

        self._rest_value = value
Ejemplo n.º 4
0
def _parse_dimension(dim):
    """
    Parses the radius and returns it in the format expected by UKIDSS.

    Parameters
    ----------
    dim : str, `~astropy.units.Quantity`

    Returns
    -------
    dim_in_min : float
        The value of the radius in arcminutes.
    """
    if isinstance(dim,
                  u.Quantity) and dim.unit in u.deg.find_equivalent_units():
        dim_in_min = dim.to(u.arcmin).value
    # otherwise must be an Angle or be specified in hours...
    else:
        try:
            new_dim = commons.parse_radius(dim).degree
            dim_in_min = u.Quantity(value=new_dim,
                                    unit=u.deg).to(u.arcmin).value
        except (u.UnitsError, coord.errors.UnitsError, AttributeError):
            raise u.UnitsError("Dimension not in proper units")
    return dim_in_min
Ejemplo n.º 5
0
    def __init__(self,
                 flux,
                 spectral_axis=None,
                 wcs=None,
                 uncertainty=None,
                 mask=None,
                 meta=None):
        # Check for quantity
        if not isinstance(flux, u.Quantity):
            raise u.UnitsError("Flux must be a `Quantity`.")

        if spectral_axis is not None:
            if not isinstance(spectral_axis, u.Quantity):
                raise u.UnitsError("Spectral axis must be a `Quantity`.")

            # Ensure that the input values are the same shape
            if not (flux.shape == spectral_axis.shape):
                raise ValueError(
                    "Shape of all data elements must be the same.")

        if uncertainty is not None and uncertainty.array.shape != flux.shape:
            raise ValueError("Uncertainty must be the same shape as flux and "
                             "spectral axis.")

        if mask is not None and mask.shape != flux.shape:
            raise ValueError("Mask must be the same shape as flux and "
                             "spectral axis.")

        # Convert uncertainties to standard deviations if not already defined
        # to be of some type
        if uncertainty is not None and not isinstance(uncertainty,
                                                      NDUncertainty):
            # If the uncertainties are not provided a unit, raise a warning
            # and use the flux units
            if not isinstance(uncertainty, u.Quantity):
                logging.warning("No unit associated with uncertainty, assuming"
                                "flux units of '{}'.".format(flux.unit))
                uncertainty = u.Quantity(uncertainty, unit=flux.unit)

            uncertainty = StdDevUncertainty(uncertainty)

        self._flux = flux
        self._spectral_axis = spectral_axis
        self._wcs = wcs
        self._uncertainty = uncertainty
        self._mask = mask
        self._meta = meta
Ejemplo n.º 6
0
    def from_geocentric(cls, x, y, z, unit=None):
        """
        Location on Earth, initialized from geocentric coordinates.

        Parameters
        ----------
        x, y, z : `~astropy.units.Quantity` or array-like
            Cartesian coordinates.  If not quantities, ``unit`` should be given.
        unit : `~astropy.units.UnitBase` object or None
            Physical unit of the coordinate values.  If ``x``, ``y``, and/or
            ``z`` are quantities, they will be converted to this unit.

        Raises
        ------
        astropy.units.UnitsError
            If the units on ``x``, ``y``, and ``z`` do not match or an invalid
            unit is given.
        ValueError
            If the shapes of ``x``, ``y``, and ``z`` do not match.
        TypeError
            If ``x`` is not a `~astropy.units.Quantity` and no unit is given.
        """
        if unit is None:
            try:
                unit = x.unit
            except AttributeError:
                raise TypeError("Geocentric coordinates should be Quantities "
                                "unless an explicit unit is given.")
        else:
            unit = u.Unit(unit)

        if unit.physical_type != 'length':
            raise u.UnitsError("Geocentric coordinates should be in "
                               "units of length.")

        try:
            x = u.Quantity(x, unit, copy=False)
            y = u.Quantity(y, unit, copy=False)
            z = u.Quantity(z, unit, copy=False)
        except u.UnitsError:
            raise u.UnitsError("Geocentric coordinate units should all be "
                               "consistent.")

        x, y, z = np.broadcast_arrays(x, y, z)
        struc = np.empty(x.shape, cls._location_dtype)
        struc['x'], struc['y'], struc['z'] = x, y, z
        return super().__new__(cls, struc, unit, copy=False)
Ejemplo n.º 7
0
    def _new_spectrum_with(self, data=None, wcs=None, mask=None, meta=None,
                           fill_value=None, spectral_unit=None, unit=None,
                           header=None, wcs_tolerance=None, **kwargs):

        data = self._data if data is None else data
        if unit is None and hasattr(data, 'unit'):
            if data.unit != self.unit:
                raise u.UnitsError("New data unit '{0}' does not"
                                   " match unit '{1}'.  You can"
                                   " override this by specifying the"
                                   " `unit` keyword."
                                   .format(data.unit, self.unit))
            unit = data.unit
        elif unit is None:
            unit = self.unit
        elif unit is not None:
            # convert string units to Units
            if not isinstance(unit, u.Unit):
                unit = u.Unit(unit)

            if hasattr(data, 'unit'):
                if u.Unit(unit) != data.unit:
                    raise u.UnitsError("The specified new cube unit '{0}' "
                                       "does not match the input unit '{1}'."
                                       .format(unit, data.unit))
            else:
                data = u.Quantity(data, unit=unit, copy=False)

        wcs = self._wcs if wcs is None else wcs
        mask = self._mask if mask is None else mask
        if meta is None:
            meta = {}
            meta.update(self._meta)
        if unit is not None:
            meta['BUNIT'] = unit.to_string(format='FITS')

        fill_value = self._fill_value if fill_value is None else fill_value
        spectral_unit = self._spectral_unit if spectral_unit is None else u.Unit(spectral_unit)

        spectrum = self.__class__(value=data, wcs=wcs, mask=mask, meta=meta,
                                  unit=unit, fill_value=fill_value,
                                  header=header or self._header,
                                  wcs_tolerance=wcs_tolerance or self._wcs_tolerance,
                                  **kwargs)
        spectrum._spectral_unit = spectral_unit

        return spectrum
Ejemplo n.º 8
0
def hcrs_to_icrs(hcrs_coo, icrs_frame):
    # this is just an origin translation so without a distance it cannot go ahead
    if isinstance(hcrs_coo.data, UnitSphericalRepresentation):
        raise u.UnitsError(
            _NEED_ORIGIN_HINT.format(hcrs_coo.__class__.__name__))

    return None, get_offset_sun_from_barycenter(
        hcrs_coo.obstime, include_velocity=bool(hcrs_coo.data.differentials))
Ejemplo n.º 9
0
 def __new__(cls, dm, unit=None, **kwargs):
     if unit is None:
         unit = getattr(dm, 'unit', cls._default_unit)
     self = super(DispersionMeasure, cls).__new__(cls, dm, unit, **kwargs)
     if not self.unit.is_equivalent(cls._default_unit):
         raise u.UnitsError("Dispersion measures should have units "
                            "equivalent to pc/cm^3")
     return self
Ejemplo n.º 10
0
 def with_observer_stationary_relative_to(self, frame,
                                          velocity=None,
                                          preserve_observer_frame=False):
     if self.unit is u.pixel:
         raise u.UnitsError("Cannot transform spectral coordinates in pixel units")
     super().with_observer_stationary_relative_to(frame,
                                                  velocity=velocity,
                                                  preserve_observer_frame=preserve_observer_frame)
Ejemplo n.º 11
0
 def with_radial_velocity_shift(self,
                                target_shift=None,
                                observer_shift=None):
     if self.unit is u.pixel:
         raise u.UnitsError(
             "Cannot transform spectral coordinates in pixel units")
     return super().with_radial_velocity_shift(
         target_shift=target_shift, observer_shift=observer_shift)
Ejemplo n.º 12
0
def units_filter(quantity, unit):
    """
    Convert quantity to given units and extract value
    """
    if not isinstance(quantity, u.Quantity):
        raise u.UnitsError(
            f'Value must be a quantity with units compatible with {unit}')
    return quantity.to(unit).value
Ejemplo n.º 13
0
    def subtract_dark(self,
                      image,
                      dark,
                      scale=False,
                      exposure_time=None,
                      exposure_unit=None):
        """
        Subtract dark current from an image.
        Parameters
        ----------
        image : `~astropy.nddata.CCDData`
            Image from which dark will be subtracted.
        dark : `~astropy.nddata.CCDData`
            Dark image.
        exposure_time : str or `~ccdproc.Keyword` or None, optional
            Name of key in image metadata that contains exposure time.
            Default is ``None``.
        exposure_unit : `~astropy.units.Unit` or None, optional
            Unit of the exposure time if the value in the meta data does not
            include a unit.
            Default is ``None``.
        scale: bool, optional
            If True, scale the dark frame by the exposure times.
            Default is ``False``.
        {log}
        Returns
        -------
        result : `~astropy.nddata.CCDData`
            Dark-subtracted image.
        """

        self.log.debug('Subtracting dark...')
        result = image.copy()
        try:
            # if dark current is linear, then this first step scales the provided
            # dark to match the exposure time
            if scale:
                dark_scaled = dark.copy()

                data_exposure = image.header[exposure_time]
                dark_exposure = dark.header[exposure_time]
                # data_exposure and dark_exposure are both quantities,
                # so we can just have subtract do the scaling
                dark_scaled = dark_scaled.multiply(data_exposure /
                                                   dark_exposure)
                result.data = image.data - dark_scaled.data
            else:
                result.data = image.data - dark.data
        except (u.UnitsError, u.UnitConversionError, ValueError) as e:

            # Make the error message a little more explicit than what is returned
            # by default.
            raise u.UnitsError("Unit '{}' of the uncalibrated image does not "
                               "match unit '{}' of the calibration "
                               "image".format(image.unit, dark.unit))

        self.log.debug('Subtracted dark.')
        return result
Ejemplo n.º 14
0
    def __new__(cls,
                value,
                unit=None,
                observer=None,
                target=None,
                radial_velocity=None,
                redshift=None,
                **kwargs):

        obj = super().__new__(cls, value, unit=unit, **kwargs)

        # There are two main modes of operation in this class. Either the
        # observer and target are both defined, in which case the radial
        # velocity and redshift are automatically computed from these, or
        # only one of the observer and target are specified, along with a
        # manually specified radial velocity or redshift. So if a target and
        # observer are both specified, we can't also accept a radial velocity
        # or redshift.
        if target is not None and observer is not None:
            if radial_velocity is not None or redshift is not None:
                raise ValueError(
                    "Cannot specify radial velocity or redshift if both "
                    "target and observer are specified")

        # We only deal with redshifts here and in the redshift property.
        # Otherwise internally we always deal with velocities.
        if redshift is not None:
            if radial_velocity is not None:
                raise ValueError(
                    "Cannot set both a radial velocity and redshift")
            redshift = u.Quantity(redshift)
            # For now, we can't specify redshift=u.one in quantity_input above
            # and have it work with plain floats, but if that is fixed, for
            # example as in https://github.com/astropy/astropy/pull/10232, we
            # can remove the check here and add redshift=u.one to the decorator
            if not redshift.unit.is_equivalent(u.one):
                raise u.UnitsError('redshift should be dimensionless')
            radial_velocity = _redshift_to_velocity(redshift)

        # If we're initializing from an existing SpectralCoord, keep any
        # parameters that aren't being overridden
        if observer is None:
            observer = getattr(value, 'observer', None)
        if target is None:
            target = getattr(value, 'target', None)

        # As mentioned above, we should only specify the radial velocity
        # manually if either or both the observer and target are not
        # specified.
        if observer is None or target is None:
            if radial_velocity is None:
                radial_velocity = getattr(value, 'radial_velocity', None)

        obj._radial_velocity = radial_velocity
        obj._observer = cls._validate_coordinate(observer, label='observer')
        obj._target = cls._validate_coordinate(target, label='target')

        return obj
Ejemplo n.º 15
0
 def __new__(cls, dm, unit=None, dtype=None, copy=True):
     if unit is None:
         unit = getattr(dm, 'unit', cls._default_unit)
     self = super(DispersionMeasure, cls).__new__(cls, dm, unit,
                                                  dtype=dtype, copy=copy)
     if not self.unit.is_equivalent(cls._default_unit):
         raise u.UnitsError("Dispersion measures should have units of "
                            "pc/cm^3")
     return self
Ejemplo n.º 16
0
def _check_units(inputs):
    """Check for consistent units on data, error, and background."""
    has_unit = [hasattr(x, 'unit') for x in inputs]
    if any(has_unit) and not all(has_unit):
        raise ValueError('If any of data, error, or background has units, '
                         'then they all must all have units.')
    if all(has_unit):
        if any([inputs[0].unit != getattr(x, 'unit') for x in inputs[1:]]):
            raise u.UnitsError(
                'data, error, and background units do not match.')
Ejemplo n.º 17
0
 def ylim(self, range):
     if isinstance(range[0], u.Quantity) is isinstance(range[1], u.Quantity):
         if isinstance(range[0], u.Quantity):
             if not range[0].unit.is_equivalent(range[1].unit):
                 raise u.UnitsError(f'The units of ymin ({range[0].unit}) are '
                                    f'not compatible with the units of ymax ({range[1].unit})')
     else:
         raise ValueError('Either both or neither limit has to be specified '
                          'as a Quantity')
     self._ylim = range
Ejemplo n.º 18
0
    def __init__(self, wn, od, **kwargs):
        """
        AbsorptionSpectrum(wn,od,**kwargs)

        Constructor for the `AbsorptionSpectrum` class.

        Parameters
        ----------
        wn : `astropy.units.Quantity`
            The absorption spectrum frequency data. Unlike `BaseSpectrum`,
            the initialisation of `AbsorptionSpectrum` requires this to be
            in the specific units of reciprocal wavenumber. However, if it is
            in a quantity convertable to kayser, conversion will be attempted
            while a warning is given to notify the user of this.
        od : `astropy.units.Quantity`
            The absorption spectrum optical depth data. Unlike `BaseSpectrum`,
            the initialisation of `AbsorptionSpectrum` requires this to be
            in the specific units of optical depth units (from
            `omnifit.utils.unit_od`).
        **kwargs : Arguments, optional
            Additional initialisation arguments can be passed to `BaseSpectrum`
            using this. Note that x and y are defined using the other
            initialisation parameters of `AbsorptionSpectrum`.
        """
        if type(wn) != u.quantity.Quantity:
            raise u.UnitsError('Input wn is not an astropy quantity.')
        if wn.unit != u.kayser:
            warnings.warn('Input wn is not in kayser units. Converting...',
                          RuntimeWarning)
            with u.set_enabled_equivalencies(u.equivalencies.spectral()):
                wn = wn.to(u.kayser)
        if type(od) != u.quantity.Quantity:
            raise u.UnitsError('Input od is not an astropy quantity.')
        if od.unit != utils.unit_od:
            raise u.UnitsError('Input od is not in optical depth units.')
        if len(wn) != len(od):
            raise RuntimeError('Input arrays have different sizes.')
        self.wn = wn
        with u.set_enabled_equivalencies(u.equivalencies.spectral()):
            self.wl = self.wn.to(u.micron)
        self.od = od
        BaseSpectrum.__init__(self, self.wn, self.od, **kwargs)
Ejemplo n.º 19
0
    def closest_spectral_channel(self, value, rest_frequency=None):
        """
        Find the index of the closest spectral channel to the specified
        spectral coordinate.

        Parameters
        ----------
        value : :class:`~astropy.units.Quantity`
            The value of the spectral coordinate to search for.
        rest_frequency : :class:`~astropy.units.Quantity`
            The rest frequency for any Doppler conversions
        """

        # TODO: we have to not compute this every time
        spectral_axis = self.spectral_axis

        try:
            value = value.to(spectral_axis.unit, equivalencies=u.spectral())
        except u.UnitsError:
            if value.unit.is_equivalent(spectral_axis.unit,
                                        equivalencies=u.doppler_radio(None)):
                if rest_frequency is None:
                    raise u.UnitsError(
                        "{0} cannot be converted to {1} without a "
                        "rest frequency".format(value.unit,
                                                spectral_axis.unit))
                else:
                    try:
                        value = value.to(
                            spectral_axis.unit,
                            equivalencies=u.doppler_radio(rest_frequency))
                    except u.UnitsError:
                        raise u.UnitsError(
                            "{0} cannot be converted to {1}".format(
                                value.unit, spectral_axis.unit))
            else:
                raise u.UnitsError(
                    "'value' should be in frequency equivalent or velocity units (got {0})"
                    .format(value.unit))

        # TODO: optimize the next line - just brute force for now
        return np.argmin(np.abs(spectral_axis - value))
Ejemplo n.º 20
0
 def in_time_interval(self, time: u.Quantity):
     """
     Return `True` if the time is between `time_start` and
     `time_max`, and `False` otherwise.  If `time` is not a valid
     time, then raise a `~astropy.units.UnitsError`.
     """
     if not isinstance(time, u.Quantity):
         raise TypeError
     if not time.unit.physical_type == 'time':
         raise u.UnitsError(f"{time} is not a valid time.")
     return self.time_start <= time <= self.time_max
Ejemplo n.º 21
0
 def _parameter_units_for_data_units(self, inputs_unit, outputs_unit):
     # Note that here we need to make sure that x and y are in the same
     # units otherwise this can lead to issues since rotation is not well
     # defined.
     if inputs_unit['x'] != inputs_unit['y']:
         raise u.UnitsError("Units of 'x' and 'y' inputs should match")
     return OrderedDict([('x_mean', inputs_unit['x']),
                         ('y_mean', inputs_unit['x']),
                         ('stddev', inputs_unit['x']),
                         ('ratio', u.dimensionless_unscaled),
                         ('amplitude', outputs_unit['z'])])
Ejemplo n.º 22
0
 def distance(self, new_distance):
     if not isinstance(new_distance, u.Quantity):
         self._distance = new_distance * 1000. * u.pc
     else:
         if new_distance.unit.physical_type != 'distance':
             TypeError('Wrong type of new_distance!')
         if (new_distance.unit == "pc") or (new_distance.unit == "kpc"):
             self._distance = new_distance
         else:
             raise u.UnitsError(
                 'Allowed units for Lens distance are "pc" or "kpc"')
Ejemplo n.º 23
0
 def distance(self, new_distance):
     if new_distance is None:
         self._distance = new_distance
     else:
         if not isinstance(new_distance, u.Quantity):
             self._distance = new_distance * 1000. * u.pc
         else:
             if (new_distance.unit == "pc") or (new_distance.unit == "kpc"):
                 self._distance = new_distance
             else:
                 raise u.UnitsError(
                     'Allowed units for Source distance are "pc" or "kpc"')
Ejemplo n.º 24
0
 def _tuple_to_float(angle, unit):
     """
     Converts an angle represented as a 3-tuple or 2-tuple into a floating
     point number in the given unit.
     """
     # TODO: Numpy array of tuples?
     if unit == u.hourangle:
         return util.hms_to_hours(*angle)
     elif unit == u.degree:
         return util.dms_to_degrees(*angle)
     else:
         raise u.UnitsError(f"Can not parse '{angle}' as unit '{unit}'")
Ejemplo n.º 25
0
    def _validate(self, value):
        if isinstance(value, u.Quantity) and value.unit != u.pixel:
            raise u.UnitsError(f'{self.name} must be in pixel units')

        if np.any(~np.isfinite(value)):
            raise ValueError(f'{self.name} must not contain any non-finite '
                             '(e.g., NaN or inf) positions')

        value = np.atleast_2d(value)
        if (value.shape[1] != 2 and value.shape[0] != 2) or value.ndim > 2:
            raise TypeError(f'{self.name} must be a (x, y) pixel position '
                            'or a list or array of (x, y) pixel positions.')
Ejemplo n.º 26
0
 def _compare_units(self, other, xy):
     """
 Check units match another spectrum or kind of unit
 """
     if isinstance(other, (str, u.UnitBase)):
         #check specific unit
         if xy == 'x':
             if self.x_unit != u.Unit(other):
                 raise u.UnitsError("x_units differ")
         elif xy == 'y':
             if self.y_unit != u.Unit(other):
                 raise u.UnitsError("y_units differ")
         else:
             raise ValueError("xy not 'x' or 'y'")
     elif isinstance(other, u.Quantity):
         _compare_units(self, other.unit, xy)
     elif isinstance(other, Spectrum):
         #compare two spectra
         if xy == 'x':
             if self.x_unit != other.x_unit:
                 raise u.UnitsError("x_units differ")
         elif xy == 'y':
             if self.y_unit != other.y_unit:
                 raise u.UnitsError("y_units differ")
         elif xy == 'xy':
             if self.x_unit != other.x_unit:
                 raise u.UnitsError("x_units differ")
             if self.y_unit != other.y_unit:
                 raise u.UnitsError("y_units differ")
         else:
             raise ValueError("xy not 'x', 'y', or 'xy'")
     else:
         raise TypeError(
             "other was not Spectrum or interpretable as a unit")
Ejemplo n.º 27
0
def _parse_dimension(dim):
    if (isinstance(dim, u.Quantity) and
            dim.unit in u.deg.find_equivalent_units()):
        if dim.unit not in ['arcsec', 'arcmin', 'deg']:
            dim = dim.to(u.degree)
    # otherwise must be an Angle or be specified in hours...
    else:
        try:
            new_dim = coord.Angle(dim)
            dim = u.Quantity(new_dim.degree, u.Unit('degree'))
        except (u.UnitsError, coord.errors.UnitsError, AttributeError):
            raise u.UnitsError("Dimension not in proper units")
    return dim
    def subimage(self, xlo='min', xhi='max', ylo='min', yhi='max'):
        """
        Extract a region spatially.

        Parameters
        ----------
        [xy]lo/[xy]hi : int or `astropy.units.Quantity` or `min`/`max`
            The endpoints to extract.  If given as a quantity, will be
            interpreted as World coordinates.  If given as a string or
            int, will be interpreted as pixel coordinates.
        """

        self._raise_wcs_no_celestial()

        limit_dict = {
            'xlo': 0 if xlo == 'min' else xlo,
            'ylo': 0 if ylo == 'min' else ylo,
            'xhi': self.shape[1] if xhi == 'max' else xhi,
            'yhi': self.shape[0] if yhi == 'max' else yhi
        }
        dims = {'x': 1, 'y': 0}

        for val in (xlo, ylo, xhi, yhi):
            if hasattr(val, 'unit') and not val.unit.is_equivalent(u.degree):
                raise u.UnitsError("The X and Y slices must be specified in "
                                   "degree-equivalent units.")

        for lim in limit_dict:
            limval = limit_dict[lim]
            if hasattr(limval, 'unit'):
                dim = dims[lim[0]]
                sl = [slice(0, 1)]
                sl.insert(dim, slice(None))
                spine = self.world[sl][dim]
                val = np.argmin(np.abs(limval - spine))
                if limval > spine.max() or limval < spine.min():
                    pass
                    # log.warn("The limit {0} is out of bounds."
                    #          "  Using min/max instead.".format(lim))
                if lim[1:] == 'hi':
                    # End-inclusive indexing: need to add one for the high
                    # slice
                    limit_dict[lim] = val + 1
                else:
                    limit_dict[lim] = val

        slices = [
            slice(limit_dict[xx + 'lo'], limit_dict[xx + 'hi']) for xx in 'yx'
        ]

        return self[slices]
Ejemplo n.º 29
0
def match_and_remove_unit(var, var_to_match):
    '''
    Make sure that `var` has the same unit as `var_to_match`, if any,
    and then remove the unit. If `var_to_match` has no unit, the value of
    `var` is returned, without its unit if it has one.

    Parameters
    ----------
    var: any type
        the variable to be tested
    var_to_match: any type
        the variable whose unit must be matched

    Returns
    -------
    value
         the value of `var` (without unit attached) after the unit has been
         changed to be the same as `var_to_match`

    Raises
    ------
    u.UnitsError
         if `var` cannot be converted to `var_to_match` using
         the standard astropy method `var`.to()

    Examples
    --------
    >>> a = 42*u.km
    >>> b = 42*u.m
    >>> match_and_remove_unit(a,b)
    42000.0

    >>> a = 42
    >>> c = 3.1415
    >>> match_and_remove_unit(a,c)
    42

    '''
    if isinstance(var, u.Quantity) and isinstance(var_to_match, u.Quantity):
        # Catch the errors here to have nicer tracebacks, otherwise
        # we end up deep into astropy libraries.
        try:
            return var.to(var_to_match.unit).value
        except Exception as e:
            raise u.UnitsError(str(e))

    elif isinstance(var, u.Quantity):
        return var.value
    else:
        return var
Ejemplo n.º 30
0
def prep_plot_kwargs(naxis, wcs, plot_axes, axes_coordinates, axes_units):
    """
    Prepare the kwargs for the plotting functions.

    This function accepts things in array order and returns things in WCS order.
    """
    # If plot_axes, axes_coordinates, axes_units are not None and not lists,
    # convert to lists for consistent indexing behaviour.
    if (not isinstance(plot_axes, (tuple, list))) and (plot_axes is not None):
        plot_axes = [plot_axes]
    if (not isinstance(axes_coordinates, (tuple, list))) and (axes_coordinates is not None):
        axes_coordinates = [axes_coordinates]
    if (not isinstance(axes_units, (tuple, list))) and (axes_units is not None):
        axes_units = [axes_units]
    # Set default value of plot_axes if not set by user.
    if plot_axes is None:
        plot_axes = [..., 'y', 'x']

    # We flip the plot axes here so they are in the right order for WCSAxes
    plot_axes = plot_axes[::-1]

    plot_axes = _expand_ellipsis(naxis, plot_axes)
    if 'x' not in plot_axes:
        raise ValueError("'x' must be in plot_axes.")

    if axes_coordinates is not None:
        axes_coordinates = _expand_ellipsis_axis_coordinates(axes_coordinates, wcs.world_axis_physical_types)
        # Ensure all elements in axes_coordinates are of correct types.
        ax_coord_types = (str, type(None))
        for axis_coordinate in axes_coordinates:
            if isinstance(axis_coordinate, str):
                # coordinates can be accessed by either name or type
                if axis_coordinate not in set(wcs.world_axis_physical_types).union(set(wcs.world_axis_names)):
                    raise ValueError(f"{axis_coordinate} is not one of this cubes world axis physical types.")
            if not isinstance(axis_coordinate, ax_coord_types):
                raise TypeError(f"axes_coordinates must be one of {ax_coord_types} or list of those, not {type(axis_coordinate)}.")

    if axes_units is not None:
        axes_units = _expand_ellipsis(wcs.world_n_dim, axes_units)
        if len(axes_units) != wcs.world_n_dim:
            raise ValueError(f"The length of the axes_units argument must be {wcs.world_n_dim}.")
        # Convert all non-None elements to astropy units
        axes_units = list(map(lambda x: u.Unit(x) if x is not None else None, axes_units))[::-1]
        for i, axis_unit in enumerate(axes_units):
            wau = wcs.world_axis_units[i]
            if axis_unit is not None and not axis_unit.is_equivalent(wau):
                raise u.UnitsError(
                    f"Specified axis unit '{axis_unit}' is not convertible to world axis unit '{wau}'")

    return plot_axes, axes_coordinates, axes_units