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