Skip to content

Commit

Permalink
Remove aperture deprecations
Browse files Browse the repository at this point in the history
  • Loading branch information
larrybradley committed Jul 27, 2022
1 parent e5c34fc commit 799e0b0
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 166 deletions.
107 changes: 37 additions & 70 deletions photutils/aperture/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@
validation.
"""

import warnings

from astropy.coordinates import SkyCoord
import astropy.units as u
from astropy.utils.exceptions import AstropyDeprecationWarning
import numpy as np

__all__ = ['ApertureAttribute', 'PixelPositions', 'SkyCoordPositions',
'PositiveScalar', 'ScalarAngle', 'ScalarAngleOrValue',
'ScalarAngleOrPixel']
'PositiveScalar', 'ScalarAngle', 'ScalarAngleOrValue']


class ApertureAttribute:
Expand Down Expand Up @@ -60,49 +56,31 @@ class PixelPositions(ApertureAttribute):
"""
Validate and set positions for pixel-based apertures.
In all cases, pixel positions are converted to a 2D `~numpy.ndarray`
(without units).
Pixel positions are converted to a 2D `~numpy.ndarray`.
"""

def __set__(self, instance, value):
# This is needed for zip to work seamlessly in Python 3
# (e.g., positions = zip(xpos, ypos))
# this is needed for zip (e.g., positions = zip(xpos, ypos))
if isinstance(value, zip):
value = tuple(value)

value = np.asanyarray(value).astype(float) # np.ndarray
self._validate(value)

if isinstance(value, u.Quantity):
# deprecated in version 1.4.0
warnings.warn('Inputing positions as a Quantity is deprecated '
'and will be removed in a future version.',
AstropyDeprecationWarning)

if value.unit != u.pixel:
raise ValueError('Input positions must have pixel units')
value = value.value

if value.ndim == 2 and value.shape[1] != 2 and value.shape[0] == 2:
raise ValueError('Input positions must be an (x, y) pixel '
'position or a list or array of (x, y) pixel '
'positions, e.g., [(x1, y1), (x2, y2), '
'(x3, y3)].')

instance.__dict__[self.name] = value

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 isinstance(value, u.Quantity):
raise TypeError(f'{self.name!r} must not be a Quantity')

if np.any(~np.isfinite(value)):
raise ValueError(f'{self.name} must not contain any non-finite '
raise ValueError(f'{self.name!r} 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:
if value.ndim > 2 and value.shape[1] != 2:
raise TypeError(f'{self.name!r} must be a (x, y) pixel position '
'or a list or array of (x, y) pixel positions.')
'or a list or array of (x, y) pixel positions, '
'e.g., [(x1, y1), (x2, y2), (x3, y3)]')


class SkyCoordPositions(ApertureAttribute):
Expand All @@ -127,8 +105,9 @@ def _validate(self, value):

class ScalarAngle(ApertureAttribute):
"""
Check that value is a scalar angle, either as an astropy Angle or
Quantity with angular units.
Check that value is a scalar angle, either as a
`~astropy.coordinates.Angle` or `~astropy.units.Quantity` with
angular units.
"""

def _validate(self, value):
Expand All @@ -142,6 +121,27 @@ def _validate(self, value):
raise TypeError(f'{self.name!r} must be a scalar angle')


class PositiveScalarAngle(ApertureAttribute):
"""
Check that value is a positive scalar angle, either as a
`~astropy.coordinates.Angle` or `~astropy.units.Quantity` with
angular units.
"""

def _validate(self, value):
if value <= 0:
raise ValueError(f'{self.name!r} must be greater than zero')

if isinstance(value, u.Quantity):
if not value.isscalar:
raise ValueError(f'{self.name!r} must be a scalar')

if not value.unit.physical_type == 'angle':
raise ValueError(f'{self.name!r} must have angular units')
else:
raise TypeError(f'{self.name!r} must be a scalar angle')


class ScalarAngleOrValue(ApertureAttribute):
"""
Check that value is a scalar angle, either as a
Expand All @@ -164,42 +164,9 @@ def _validate(self, value):
if not value.isscalar:
raise ValueError(f'{self.name!r} must be a scalar')

if not (value.unit.physical_type == 'angle'
or value.unit == u.pixel):
raise ValueError(f'{self.name!r} must have angular or pixel '
'units')
if not value.unit.physical_type == 'angle':
raise ValueError(f'{self.name!r} must have angular units')
else:
if not np.isscalar(value):
raise TypeError(f'{self.name!r} must be a scalar float in '
'radians')


class ScalarAngleOrPixel(ApertureAttribute):
"""
Check that value is a scalar angle, either as a
`~astropy.coordinates.Angle` or `~astropy.units.Quantity` with
angular units, or a scalar `~astropy.units.Quantity` in pixel units.
The value must be strictly positive (> 0).
"""

def _validate(self, value):
if isinstance(value, u.Quantity):
if not value.isscalar:
raise ValueError(f'{self.name!r} must be a scalar')

if not (value.unit.physical_type == 'angle'
or value.unit == u.pixel):
raise ValueError(f'{self.name!r} must have angular or pixel '
'units')

if value.unit == u.pixel:
warnings.warn('Inputing sky aperture quantities in pixel '
'units is deprecated and will be removed in '
'a future version.', AstropyDeprecationWarning)

if not value > 0:
raise ValueError(f'{self.name!r} must be strictly positive')
else:
raise TypeError(f'{self.name!r} must be a scalar angle or pixel '
'Quantity')
raise TypeError(f'If not an angle Quantity, {self.name!r} '
'must be a scalar float in radians')
23 changes: 7 additions & 16 deletions photutils/aperture/circle.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import numpy as np

from .attributes import (PixelPositions, PositiveScalar, SkyCoordPositions,
ScalarAngleOrPixel)
PositiveScalarAngle)
from .core import PixelAperture, SkyAperture
from .mask import ApertureMask
from ..geometry import circular_overlap_grid
Expand Down Expand Up @@ -116,9 +116,6 @@ class CircularAperture(CircularMaskMixin, PixelAperture):
* single ``(x, y)`` pair as a tuple, list, or `~numpy.ndarray`
* tuple, list, or `~numpy.ndarray` of ``(x, y)`` pairs
* `~astropy.units.Quantity` instance of ``(x, y)`` pairs in
pixel units (this is Deprecated and will be removed in a
future version)
r : float
The radius of the circle in pixels.
Expand Down Expand Up @@ -233,9 +230,6 @@ class CircularAnnulus(CircularMaskMixin, PixelAperture):
* single ``(x, y)`` pair as a tuple, list, or `~numpy.ndarray`
* tuple, list, or `~numpy.ndarray` of ``(x, y)`` pairs
* `~astropy.units.Quantity` instance of ``(x, y)`` pairs in
pixel units (this is Deprecated and will be removed in a
future version)
r_in : float
The inner radius of the circular annulus in pixels.
Expand Down Expand Up @@ -362,8 +356,7 @@ class SkyCircularAperture(SkyAperture):
either scalar coordinates or an array of coordinates.
r : scalar `~astropy.units.Quantity`
The radius of the circle in angular units. Pixel units are now
deprecated.
The radius of the circle in angular units.
Examples
--------
Expand All @@ -376,7 +369,7 @@ class SkyCircularAperture(SkyAperture):

_params = ('positions', 'r',)
positions = SkyCoordPositions('The center position(s) in sky coordinates.')
r = ScalarAngleOrPixel('The radius in angular units.')
r = PositiveScalarAngle('The radius in angular units.')

def __init__(self, positions, r):
self.positions = positions
Expand Down Expand Up @@ -417,12 +410,10 @@ class SkyCircularAnnulus(SkyAperture):
either scalar coordinates or an array of coordinates.
r_in : scalar `~astropy.units.Quantity`
The inner radius of the circular annulus in angular units. Pixel
units are now deprecated.
The inner radius of the circular annulus in angular units.
r_out : scalar `~astropy.units.Quantity`
The outer radius of the circular annulus in angular units. Pixel
units are now deprecated.
The outer radius of the circular annulus in angular units.
Examples
--------
Expand All @@ -435,8 +426,8 @@ class SkyCircularAnnulus(SkyAperture):

_params = ('positions', 'r_in', 'r_out')
positions = SkyCoordPositions('The center position(s) in sky coordinates.')
r_in = ScalarAngleOrPixel('The inner radius in angular units.')
r_out = ScalarAngleOrPixel('The outer radius in angular units.')
r_in = PositiveScalarAngle('The inner radius in angular units.')
r_out = PositiveScalarAngle('The outer radius in angular units.')

def __init__(self, positions, r_in, r_out):
if r_in.unit.physical_type != r_out.unit.physical_type:
Expand Down
55 changes: 15 additions & 40 deletions photutils/aperture/ellipse.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import numpy as np

from .attributes import (ScalarAngle, PixelPositions, PositiveScalar,
SkyCoordPositions, ScalarAngleOrPixel,
ScalarAngleOrValue)
SkyCoordPositions, ScalarAngleOrValue,
PositiveScalarAngle)
from .core import PixelAperture, SkyAperture
from .mask import ApertureMask
from ..geometry import elliptical_overlap_grid
Expand Down Expand Up @@ -138,9 +138,6 @@ class EllipticalAperture(EllipticalMaskMixin, PixelAperture):
* single ``(x, y)`` pair as a tuple, list, or `~numpy.ndarray`
* tuple, list, or `~numpy.ndarray` of ``(x, y)`` pairs
* `~astropy.units.Quantity` instance of ``(x, y)`` pairs in
pixel units (this is Deprecated and will be removed in a
future version)
a : float
The semimajor axis of the ellipse in pixels.
Expand Down Expand Up @@ -276,9 +273,6 @@ class EllipticalAnnulus(EllipticalMaskMixin, PixelAperture):
* single ``(x, y)`` pair as a tuple, list, or `~numpy.ndarray`
* tuple, list, or `~numpy.ndarray` of ``(x, y)`` pairs
* `~astropy.units.Quantity` instance of ``(x, y)`` pairs in
pixel units (this is Deprecated and will be removed in a
future version)
a_in : float
The inner semimajor axis of the elliptical annulus in pixels.
Expand Down Expand Up @@ -445,12 +439,10 @@ class SkyEllipticalAperture(SkyAperture):
either scalar coordinates or an array of coordinates.
a : scalar `~astropy.units.Quantity`
The semimajor axis of the ellipse in angular units. Pixel units
are now deprecated.
The semimajor axis of the ellipse in angular units.
b : scalar `~astropy.units.Quantity`
The semiminor axis of the ellipse in angular units. Pixel units
are now deprecated.
The semiminor axis of the ellipse in angular units.
theta : scalar `~astropy.units.Quantity`, optional
The position angle (in angular units) of the ellipse semimajor
Expand All @@ -468,16 +460,12 @@ class SkyEllipticalAperture(SkyAperture):

_params = ('positions', 'a', 'b', 'theta')
positions = SkyCoordPositions('The center position(s) in sky coordinates.')
a = ScalarAngleOrPixel('The semimajor axis in angular units.')
b = ScalarAngleOrPixel('The semiminor axis in angular units.')
a = PositiveScalarAngle('The semimajor axis in angular units.')
b = PositiveScalarAngle('The semiminor axis in angular units.')
theta = ScalarAngle('The position angle in angular units of the ellipse '
'semimajor axis.')

def __init__(self, positions, a, b, theta=0. * u.deg):
if a.unit.physical_type != b.unit.physical_type:
raise ValueError('a and b should either both be angles '
'or in pixels')

self.positions = positions
self.a = a
self.b = b
Expand Down Expand Up @@ -518,21 +506,17 @@ class SkyEllipticalAnnulus(SkyAperture):
either scalar coordinates or an array of coordinates.
a_in : scalar `~astropy.units.Quantity`
The inner semimajor axis in angular units. Pixel units are now
deprecated.
The inner semimajor axis in angular units.
a_out : scalar `~astropy.units.Quantity`
The outer semimajor axis in angular units. Pixel units are now
deprecated.
The outer semimajor axis in angular units.
b_out : scalar `~astropy.units.Quantity`
The outer semiminor axis in angular units. Pixel units are now
deprecated.
The outer semiminor axis in angular units.
b_in : `None` or scalar `~astropy.units.Quantity`
The inner semiminor axis in angular units. Pixel units are
now deprecated. If `None`, then the inner semiminor axis is
calculated as:
The inner semiminor axis in angular units. If `None`, then the
inner semiminor axis is calculated as:
.. math:: b_{in} = b_{out}
\left(\frac{a_{in}}{a_{out}}\right)
Expand All @@ -554,21 +538,15 @@ class SkyEllipticalAnnulus(SkyAperture):

_params = ('positions', 'a_in', 'a_out', 'b_in', 'b_out', 'theta')
positions = SkyCoordPositions('The center position(s) in sky coordinates.')
a_in = ScalarAngleOrPixel('The inner semimajor axis in angular units.')
a_out = ScalarAngleOrPixel('The outer semimajor axis in angular units.')
b_in = ScalarAngleOrPixel('The inner semiminor axis in angular units.')
b_out = ScalarAngleOrPixel('The outer semiminor axis in angular units.')
a_in = PositiveScalarAngle('The inner semimajor axis in angular units.')
a_out = PositiveScalarAngle('The outer semimajor axis in angular units.')
b_in = PositiveScalarAngle('The inner semiminor axis in angular units.')
b_out = PositiveScalarAngle('The outer semiminor axis in angular units.')
theta = ScalarAngle('The position angle in angular units of the ellipse '
'semimajor axis.')

def __init__(self, positions, a_in, a_out, b_out, b_in=None,
theta=0. * u.deg):
if a_in.unit.physical_type != a_out.unit.physical_type:
raise ValueError('a_in and a_out should either both be angles '
'or in pixels')
if a_out.unit.physical_type != b_out.unit.physical_type:
raise ValueError('a_out and b_out should either both be angles '
'or in pixels')
if not a_out > a_in:
raise ValueError('"a_out" must be greater than "a_in".')

Expand All @@ -580,9 +558,6 @@ def __init__(self, positions, a_in, a_out, b_out, b_in=None,
if b_in is None:
b_in = self.b_out * self.a_in / self.a_out
else:
if b_in.unit.physical_type != b_out.unit.physical_type:
raise ValueError('b_in and b_out should either both be '
'angles or in pixels')
if not b_out > b_in:
raise ValueError('"b_out" must be greater than "b_in".')
self.b_in = b_in
Expand Down
Loading

0 comments on commit 799e0b0

Please sign in to comment.