__author__ = "giacomov"
import collections
from astropy import coordinates
from astromodels.core.parameter import Parameter
from astromodels.core.tree import Node
[docs]
class WrongCoordinatePair(ValueError):
pass
[docs]
class IllegalCoordinateValue(ValueError):
pass
[docs]
class WrongCoordinateSystem(ValueError):
pass
[docs]
class SkyDirection(Node):
"""
This is essentially a wrapper around astropy.coordinates.SkyCoord with a possibility for
being serialized and deserialized with YAML.
"""
def __init__(self, ra=None, dec=None, l=None, b=None, equinox="J2000"):
"""
:param ra: Right Ascension in degrees
:param dec: Declination in degrees
:param l: Galactic latitude in degrees
:param b: Galactic longitude in degrees
:param equinox: string
:return:
"""
self._equinox = equinox
# Create the node
Node.__init__(self, "position")
# Check that we have the right pairs of coordinates
if ra is not None and dec is not None:
# This goes against duck typing, but it is needed to provide a means of initiating this class
# with either Parameter instances or just floats
# Try to transform it to float, if it works than we transform it to a parameter
ra = self._get_parameter_from_input(ra, 0, 360, "ra", "Right Ascension")
dec = self._get_parameter_from_input(dec, -90, 90, "dec", "Declination")
self._coord_type = "equatorial"
self._add_child(ra)
self._add_child(dec)
elif l is not None and b is not None:
# This goes against duck typing, but it is needed to provide a means of initiating this class
# with either Parameter instances or just floats
# Try to transform it to float, if it works than we transform it to a parameter
l = self._get_parameter_from_input(l, 0, 360, "l", "Galactic longitude")
b = self._get_parameter_from_input(b, -90, 90, "b", "Galactic latitude")
self._coord_type = "galactic"
self._add_child(l)
self._add_child(b)
else:
raise WrongCoordinatePair("You have to specify either (ra, dec) or (l, b).")
@staticmethod
def _get_parameter_from_input(number_or_parameter, minimum, maximum, what, desc):
# Try to transform it to float, if it works than we transform it to a parameter
try:
number_or_parameter = float(number_or_parameter)
except TypeError:
assert isinstance(number_or_parameter, Parameter), (
"%s must be either a number or a " "parameter instance" % what
)
# So this is a Parameter instance already. Enforce that it has the right maximum and minimum
parameter = number_or_parameter
assert (
parameter.min_value >= minimum
), "%s must have a minimum greater than or equal to %s" % (
what,
minimum,
)
assert (
parameter.max_value <= maximum
), "%s must have a maximum less than or equal to %s" % (
what,
maximum,
)
else:
# This was a float. Enforce that it has a legal value
assert (
minimum <= number_or_parameter <= maximum
), "%s cannot have a value of %s, " "it must be %s <= %s <= %s" % (
what,
number_or_parameter,
minimum,
what,
maximum,
)
parameter = Parameter(
what,
number_or_parameter,
desc=desc,
min_value=minimum,
max_value=maximum,
unit="deg",
free=False,
)
return parameter
[docs]
def get_ra(self):
"""
Get R.A. corresponding to the current position (ICRS, J2000)
:return: Right Ascension
"""
try:
return self.ra.value
except AttributeError:
# Transform from L,B to R.A., Dec
return self.sky_coord.transform_to("icrs").ra.value
[docs]
def get_dec(self):
"""
Get Dec. corresponding to the current position (ICRS, J2000)
:return: Declination
"""
try:
return self.dec.value
except AttributeError:
# Transform from L,B to R.A., Dec
return self.sky_coord.transform_to("icrs").dec.value
[docs]
def get_l(self):
"""
Get Galactic Longitude (l) corresponding to the current position
:return: Galactic Longitude
"""
try:
return self.l.value
except AttributeError:
# Transform from L,B to R.A., Dec
return self.sky_coord.transform_to("galactic").l.value
[docs]
def get_b(self):
"""
Get Galactic latitude (b) corresponding to the current position
:return: Latitude
"""
try:
return self.b.value
except AttributeError:
# Transform from L,B to R.A., Dec
return self.sky_coord.transform_to("galactic").b.value
def _get_sky_coord(self):
if self._coord_type == "galactic":
l = self.l.value
b = self.b.value
return coordinates.SkyCoord(
l=l, b=b, frame="galactic", equinox=self._equinox, unit="deg"
)
else:
ra = self.ra.value
dec = self.dec.value
return coordinates.SkyCoord(
ra=ra, dec=dec, frame="icrs", equinox=self._equinox, unit="deg"
)
@property
def sky_coord(self):
"""
Return an instance of astropy.coordinates.SkyCoord which can be used to make all transformations supported
by it
:return: astropy.coordinates.SkyCoord
"""
return self._get_sky_coord()
@property
def parameters(self):
"""
Get the dictionary of parameters (either ra,dec or l,b)
:return: dictionary of parameters
"""
if self._coord_type == "galactic":
return collections.OrderedDict((("l", self.l), ("b", self.b)))
else:
return collections.OrderedDict((("ra", self.ra), ("dec", self.dec)))
@property
def equinox(self):
"""
Returns the equinox for the coordinates.
:return:
"""
return self._equinox
[docs]
def to_dict(self, minimal=False):
data = collections.OrderedDict()
if self._coord_type == "equatorial":
data["ra"] = self.ra.to_dict(minimal)
data["dec"] = self.dec.to_dict(minimal)
data["equinox"] = self._equinox
else:
data["l"] = self.l.to_dict(minimal)
data["b"] = self.b.to_dict(minimal)
data["equinox"] = self._equinox
return data
[docs]
def fix(self):
"""
Fix the parameters with the coordinates (either ra,dec or l,b depending on how the class
has been instanced)
"""
if self._coord_type == "equatorial":
self.ra.fix = True
self.dec.fix = True
else:
self.l.fix = True
self.b.fix = True
[docs]
def free(self):
"""
Free the parameters with the coordinates (either ra,dec or l,b depending on how the class
has been instanced)
"""
if self._coord_type == "equatorial":
self.ra.fix = False
self.dec.fix = False
else:
self.l.fix = False
self.b.fix = False
[docs]
@classmethod
def from_dict(cls, data):
return cls(**data)
def _repr__base(self, rich_output):
if self._coord_type == "equatorial":
representation = "Sky direction (R.A., Dec.) = (%.5f, %.5f) (%s)" % (
self.ra.value,
self.dec.value,
self.equinox,
)
else:
representation = "Sky direction (l, b) = (%.5f, %.5f) (%s)" % (
self.l.value,
self.b.value,
self.equinox,
)
return representation