Source code for autowisp.astrometry.anmatch_transformation
"""Define a class to apply sky-to-frame transformations found by anmatch."""
import os.path
import numpy
from autowisp.astrometry.map_projections import gnomonic_projection
[docs]
class AnmatchTransformation:
"""A clas that applies transformations generated by anmatch."""
[docs]
def __init__(self, trans_file=None):
"""
Parse the transformation to apply from the given file.
Args:
trans_file: The transformation file contaning the transformation
to apply. If this is the name of an existing file, it is opened
and parsed, otherwise this should be a sequence of the lines
contained in the transfomation file.
Returns:
None
"""
self.coefficients = {"x": [], "y": []}
if trans_file is not None:
if os.path.exists(trans_file):
with open(trans_file, encoding="ascii") as trans_lines:
self.read_anmatch_transformation(trans_lines)
else:
self.read_anmatch_transformation(trans_file)
[docs]
def read_anmatch_transformation(self, trans_lines):
"""
Parse the transformation to apply from the given file.
Args:
trans_lines: A sequence of the individual lines of the trans file
defining the transformation to apply.
Returns:
None
"""
for line in trans_lines:
if line[0] == "#":
if line[1:].strip().startswith("2MASS:"):
# ra is perfectly reasonable for sky position
# pylint: disable=invalid-name
ra, dec = map(float, line[1:].strip().split()[1:3])
# pylint: enable=invalid-name
self.center = {"RA": ra, "Dec": dec}
else:
key, value = map(str.strip, line.split("="))
if key == "type":
assert value == "polynomial"
elif key == "order":
self.order = int(value)
elif key == "offset":
assert value == "0, 0"
elif key == "scale":
assert value == "1"
elif key == "basisshift":
assert value == "0, 0"
elif (
key[0] == "d" and key[1] in ["x", "y"] and key[2:] == "fit"
):
self.coefficients[key[1]] = [
float(word.strip()) for word in value.split(",")
]
assert (
len(self.coefficients["x"])
== (self.order + 1) * (self.order + 2) // 2
)
assert (
len(self.coefficients["y"])
== (self.order + 1) * (self.order + 2) // 2
)
[docs]
def evaluate_polynomial(self, projected, transformed):
"""
Apply the currently defined polynomial transfomation to (xi, eta).
Args:
projected: The projected (c.f. gnomonic_projection()) coordinates
to transform. Should have `'xi'` and `'eta'` keys containing the
projected coordinates to transform in degrees.
transformed: A numpy array with `'x'` and `'y'` fields to fill
with the transformed coordinates (in pixels).
Returns:
None
"""
transformed["x"] = transformed["y"] = 0.0
xi_to_order = 1.0
order_factorial = 1.0
coef_index = 0
for order in range(self.order + 1):
xi_term = numpy.copy(xi_to_order)
eta_term = 1.0
xi_factor = order_factorial
eta_factor = 1.0
for eta_power in range(order + 1):
for out_var in ["x", "y"]:
transformed[out_var] += (
self.coefficients[out_var][coef_index]
* xi_term
* eta_term
/ xi_factor
/ eta_factor
)
coef_index += 1
if eta_power < order:
xi_term /= projected["xi"]
eta_term *= projected["eta"]
xi_factor /= order - eta_power
eta_factor *= eta_power + 1
order_factorial *= order + 1
xi_to_order *= projected["xi"]
[docs]
def __call__(self, sources, save_intermediate=False):
"""
Return the x and y coordinates to which the given (ra, dec) project to.
Args:
sources: The sky position to project to frame coordinates. Should
have keys named `'RA'` and `'Dec'` in degrees.
save_intermediate: Should the intermediate coordinates be saved.
Those are the coordinates after apllying the sky projection and
before applying the polynomial transformation.
Returns:
numpy.array(dtype=[('x', float), ('y', float)]):
The position in pixels relative to the lower left frame of the
corner to which the given sky position projects under the
currently defined transformation, as a numpy array with fields
named `'x'` and `'y'`. If `save_intermediate` is True, the
result also contains fields called `'xi'` and `'eta'` containing
the intermediate coordinates."""
try:
projected_shape = sources["RA"].shape
except TypeError:
projected_shape = (1,)
# pylint false positive
# pylint: disable=no-member
projected_dtype = [("x", numpy.float64), ("y", numpy.float64)]
intermediate_dtype = [("xi", numpy.float64), ("eta", numpy.float64)]
# pylint: enable=no-member
if save_intermediate:
projected_dtype.extend(intermediate_dtype)
projected = numpy.empty(projected_shape, dtype=projected_dtype)
intermediate = (
projected
if save_intermediate
else numpy.empty(projected_shape, intermediate_dtype)
)
gnomonic_projection(sources, intermediate, **self.center)
self.evaluate_polynomial(intermediate, projected)
return projected