This notebook showcases a rough draft of how we are to proceed in the creation of coordinate frames in the proposed sunpy.coordinates
package.
As was discussed in the previous notebook, the Astropy Coordinates API is central to the re-implementation project.
This document draws from packages like astropy.coordinates.builtin_frames
and astropy.coordinates.baseframe
. I have tried to keep the example as easy to understand as possible.
In the first cell, I have created an example set of two frame classes which derive from BaseCoordinateFrame
. The attributes of the frame classes have been described, in the first frame class. The frame classes are followed by two methods which are used by the astropy.coordinates
package to perform transforms from one frame to another. They are preceded by a function decorator which registers the transform type, transform source and transform target in the Astropy transformation registry. In the last cell, I wrap around the BaseFrameCoordinate
objects with SkyCoord
. Then I perform some transforms.
For information on the reasoning behind the coordinates API, please see APE5.
# This notebook describes the use of frames.
# I will create two basic reference frames to
# play with, to demonstrate how we will be doing
# the same in the project.
from astropy.coordinates.baseframe import BaseCoordinateFrame as BCF
from astropy.coordinates.baseframe import frame_transform_graph
from astropy.coordinates import CartesianRepresentation
from astropy.utils.compat.odict import OrderedDict
from astropy.coordinates.transformations import FunctionTransform
from astropy import units as u
import numpy as np
class FrameFirst(BCF):
# The preferred representation that this frame will adhere to.
# Other representations include Cartesian, UnitSpherical,
# Cylindrical.
#preferred_representation = SphericalRepresentation
preferred_representation = CartesianRepresentation
# The preferred attribute names mapping.
# This is a dict that maps attributes of this class
# to those in the preferred representation class.
# Thus right ascension (ra) is mapped to longitude,
# declination (dec) is mapped to latitude, and distance
# is mapped to distance.
#preferred_attr_names = OrderedDict([('ra', 'lon'), ('dec', 'lat'),
# ('distance', 'distance')])
preferred_attr_names = OrderedDict([('X', 'x'), ('Y', 'y'),
('Z', 'z')])
# The preferred units. Understandably, both right ascension
# and declination have been specified in degrees.
#preferred_attr_units = {'ra': u.degree, 'dec': u.degree}
preferred_attr_units = {'X': u.m, 'Y': u.m,
'Z': u.m}
# The frame attribute names.
# Specifies the additional attributes needed to specify the
# frame and their default values.
frame_attr_names = {'i can has coord': True}
# time_attr_names = ('equinox', 'obstime')
# Sequence of attribute names that must be astropy.time.Time
# objects. When provided as keywords in the initializer, they
# will be converted if possible to the appropriate Time objects.
# I have used CartesianRepresentation for ease of transformation.
class FrameSecond(BCF):
preferred_representation = CartesianRepresentation
preferred_attr_names = OrderedDict([('X', 'x'), ('Y', 'y'),
('Z', 'z')])
preferred_attr_units = {'X': u.m, 'Y': u.m,
'Z': u.m}
frame_attr_names = {}
# Function decorator. The function automatically inserts the
# given transform into the graph/registry.
@frame_transform_graph.transform(FunctionTransform, FrameFirst, FrameSecond)
def ffirst_to_fsecond(ffirstcoord, fsecondframe):
# Normally done through matrices & stuff, but this is
# just a test run.
x = ffirstcoord.cartesian.x * 200
y = ffirstcoord.cartesian.y / 100
z = ffirstcoord.cartesian.z * 2
representation = CartesianRepresentation(x,y,z)
return FrameSecond(representation)
@frame_transform_graph.transform(FunctionTransform, FrameSecond, FrameFirst)
def fsecond_to_ffirst(fseccoord, ffirstframe):
x = fseccoord.cartesian.x / 200
y = fseccoord.cartesian.y * 100
z = fseccoord.cartesian.z / 2
representation = CartesianRepresentation(x,y,z)
return FrameFirst(representation)
from astropy.coordinates import SkyCoord
sc1 = FrameFirst(15*u.m, 20*u.m, 2*u.m)
print(sc1)
sc2 = sc1.transform_to(FrameSecond)
print(sc2)
sc3 = sc2.transform_to(FrameFirst)
print(sc3)
<FrameFirst Coordinate: i can has coord=True, X=15.0 m, Y=20.0 m, Z=2.0 m> <FrameSecond Coordinate: X=3000.0 m, Y=0.2 m, Z=4.0 m> <FrameFirst Coordinate: i can has coord=True, X=15.0 m, Y=20.0 m, Z=2.0 m>