Skip to content
This repository was archived by the owner on May 26, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions generalized_wcs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Proposal for a WCS package

*wcs_api.md* contains the main class.

*coordinate_systems.md* defines the coordinate systems.

*selector.md* contains a definition of a SelectorModel (a subclass of `astropy.modeling.Model`, to be used when a WCS requires more than one transform based on a selection criteria, e.g. for IFUs.

*region_api.md* explains the role of regions in WCS.

*example.md* shows how an instrument scientist would use the API.
207 changes: 207 additions & 0 deletions generalized_wcs/coordinate_systems_api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
Coordinate Systems
------------------

The proposed API for coordinate systems is based on the
[STC document](http://www.ivoa.net/documents/PR/STC/STC-20050315.html)
and [WCS Paper III](http://fits.gsfc.nasa.gov/fits_wcs.html)
and is a high level description of coordinate system classes.

[Prototype implementation (incomplete)] ( https://github.com/nden/code-experiments/blob/master/generalized_wcs_api/prototype/coordinate_systems.py)

Note: Some of this may be replaced (or merged) with the high level coordinate class
being worked on although it may be that it is only a space coordinate class.

A coordinate system consists of one or more coordinate frames which
describe a reference position and a reference frame.

The base class of all frames is `CoordinateFrame`. It is kept intentionally
very simple so that it can be easily extended.

class CoordinateFrame(object):
"""
Base class for CoordinateFrames

Parameters
----------
num_axes : int
number of axes
ref_system : str
reference system (see subclasses)
ref_pos : str or ``ReferencePosition``
reference position or standard of rest
units : list of units
unit for each axis
axes_names : list of str
names of the axes in this frame
name : str
name (alias) for this frame
"""
def __init__(self, num_axes, ref_system=None, ref_pos=None, units=None, axes_names=None, name=None):
""" Initialize a frame"""

def transform_to(self, other):
"""
Transform from the current reference system to other
"""

The following basic frames are defined as subclasses of `CoordinateFrame`:

class TimeFrame(CoordinateFrame):
"""
Time Frame

Parameters
----------
scale : str
time scale, one of ``standard_time_scale``
format : str
time representation
obslat : float or ``astropy.coordinates.Latitude``
observer's latitude
obslon : float or ``astropy.coordinates.Longitude``
observer's longitude
units : str or ``astropy.units.Unit``
unit for each axis
axes_names : list of str
names of the axes in this frame
"""

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you also need something to indicate how the time is represented - is it an MJD, a Julian Epoch, A Besselian epoch, or what?

standard_time_scale = time.Time.SCALES

time_format = time.Time.FORMATS

def __init__(self, scale, format, obslat=0., obslon=0., units="s", axes_names="Time", name="Time"):


def transform_to(self, val, tosys, val2=None, precision=None, in_subfmt=None,
out_subfmt=None, copy=False):
"""Transforms to a different scale/representation"""

class SkyFrame(CoordinateFrame):
"""
Sky Frame Representation

Parameters
----------
reference_system : str
one of standard_ref_frame
reference_position : str, ReferencePosition instance
obstime : time.Time instance
observation time
equinox : time.Time
equinox
##TODO? List of supported projections per frame
projection : str
projection type
axes_names : iterable or str
axes labels
units : str or units.Unit instance or iterable of those
units on axes
distance : Quantity
distance from origin
see ``~astropy.coordinates.distance`
obslat : float or ~astropy.coordinates.Latitute`
observer's latitude
obslon : float or ~astropy.coordinates.Longitude
observer's longitude
"""
standart_ref_frame = ["FK4", "FK5", "ICRS", '...']

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need the observer location for things like horizon coordinates. Plus an equinox for FK4/FK5/Ecliptic, etc. Plus an epoch of observation to allow conversion between systems that move with respect to each other (like FK4 and FK5)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there is a subtlety to do with the "transform_to" method - since in general sky coordinates systems may move with respect to each other over time, you need something to indicate which reference frame is to be considered "fixed". For instance, say you have two skyframes both representing Helioecliptic longitude and latitude (i.e. the Sun defines zero longitude) but they have different epoch of observations. If you use "transform_to" to transform a position from one SkyFrame to the other, does the position change? Since both SkyFrames represent HelioEcliptic, you could argue that the position should not change. But on the other hand, that position will change when viewed against the background of distant stars (i.e. if you convert the position from the first skyframe, to ICRS, and then convert back to the second SkyFrame). The AST library addresses this issue by means of the "AlignSystem" attribute attached to the base Frame class (see http://starlink.jach.hawaii.edu/docs/sun211.htx/node454.html).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dsberry Thanks for the link. There's a lot to think about and revise. Thanks for all comments.

def __init__(self, reference_system, reference_position, obstime, equinox=None,
projection="", axes_names=["", ""], units=["",""], distance=None,
obslat=None, obslon=None, name=None):

def transform_to(self, lat, lon, other, distance=None):
"""
Transform from the current reference system to other.
"""


class SpectralFrame(CoordinateFrame):
"""
Represents Spectral Frame

Parameters
----------
reference_frame : str
one of spectral_ref_frame
reference_position : ``ReferencePosition``
obstime : time.Time instance
units : str or units.Unit instance
units for the spectral frame
axes_names : str
spectral axis name
If None, reference_system is used
rest_freq : float or Quantity (default None)
rest frequency
obslat : float or instance of `~astropy.coordinates.Latitude`
observer's latitude
obslon : float or instanceof `~astropy.coordinates.Longitude`
observer's longitude
"""

spectral_ref_frame = ["WAVE", "FREQ", "ENER", "WAVEN", "AWAV", "VRAD",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment here that also applies anywhere this sort of thing is used: these sort of all-caps abbreviations I think are a bad idea. I know historically they come from situations where bytes were more precious, but here we can make a break with the past and instead use something more readable. So for example, spectral_ref_frame might instead be: ['wavelength', 'frequency', 'energy', 'wave number', 'air wavelength', 'radio velocity', 'optical velocity', 'optical z', 'relativistic v', 'relativistic beta']. (If these are actually needed to be attributes in some cases, replace the spaces with underscores)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These came from WCS Paper III. While I'm not opposed to changing them we have to consider that they are in common use now and will have to support them in some way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I agree we have to accept them as FITS/WCS header keywords, but we do not have to actually encourage their use in code. In fact, I want to make every effort to discouraging it - this isn't fortran, so we're not limited to 72 characters any more 😉

This reflects me general attitude that we are not aiming to reproduce WCS, but rather make a better and more pythonic take on WCS.

"VOPT", "ZOPT", "VELO", "BETA"]

standard_reference_position = ["GEOCENTER", "BARYCENTER", "HELIOCENTER",
"TOPOCENTER", "LSR", "LSRK", "LSRD",
"GALACTIC_CENTER", "MOON", "LOCAL_GROUP_CENTER"]

def __init__(self, reference_system, reference_position, units, axes_names=None,
obstime=None, rest_freq=None, obslat=None, obslon=None, name=None):

def transform_to(self, coord, other):
""" Transforms to other spectral coordinate system"""

class CartesianFrame(CoordinateFrame):
def __init__(self, num_axes, reference_position, units, projection=None, name=None, axes_names=None):
##TODO: reference position
super(CartesianFrame, self).__init__(num_axes, ref_pos=reference_position, units=units,
axes_names=axes_names, name=name)

def transform_to(self, other, *args):
#use affine transform


class DetectorFrame(CartesianFrame):
def __init__(self, reference_pixel, units=[u.pixel, u.pixel], name=None, reference_position="Local",
axes_names=None):
super(DetectorFrame, self).__init__(2, reference_position=reference_position, units=units, name=name, axes_names=axes_names)
self._reference_pixel =


Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There needs to be a standard of rest in here. LSRK, LSRD, Topo, Helio, Barycentric, etc. They are all listed in FITS Paper 3 and are critical for mm/submm observing. Technically planet frames also have to be there...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See ReferencePosition below, which is basically Table 1 in the STC document. Is standart_of_rest better name?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Right so is that what STC calls the standard of rest? I had assumed Reference Position was the point on the sky that the spectrum referred to. AST calls that RefPos. You need to know which direction you are looking in so that you can work out where that is relative to the standard of rest. So if ReferencePosition is the standard of rest where do you specify the position on the sky of the spectrum itself?

A `CoordinateSystem` has one or more `CoordinateFrame` objects:

class CoordinateSystem(object):
"""
A coordinate system has one or more frames.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're getting this use of "coordinate system" from the STC, right? This is yet another example of "coordinate system" being an overloaded term: the STC (I believe) means it to be something like "multiple coordinate frames mixed together to create a higher dimensional space" - that is a "system of coordinates" in the same sense of the word as a "system of equations". But that's not the common usage of "coordinate system" meaning something like equatorial J2000 ir whatever. So I think we would want to try to use different terminology here. (some possible, admittedly not great, ideas: MultiFrame, SystemOfFrames, or MultiDimCoordinates)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In common parlance, a "coordinate system" is surely any collection of axes
that can be used to identify points in some specified domain. I would have
thought this definition applies to all domains of any type or
dimensionality. So sure - you could use equatorial J2000 RA and Dec axes to
form a "coordinate system" to describe point on the 2D celestial sphere.
But it would be just as valid to describe a set of (ra,dec,frequency) axes
as a "coordinate system" that describes positions within the domain of a a
spectral cube.

David

On 7 February 2014 07:11, Erik Tollerud notifications@github.com wrote:

In generalized_wcs/coordinate_systems_api.md:

  •    def transform_to(self, other, *args):
    
  •        #use affine transform
    
  • class DetectorFrame(CartesianFrame):
  •    def **init**(self, reference_pixel, units=[u.pixel, u.pixel], name=None, reference_position="Local",
    
  •                 axes_names=None):
    
  •        super(DetectorFrame, self).**init**(2, reference_position=reference_position, units=units, name=name, axes_names=axes_names)
    
  •        self._reference_pixel =
    
    +A CoordinateSystem has one or more CoordinateFrame objects:
    +
  • class CoordinateSystem(object):
  •    """
    
  •    A coordinate system has one or more frames.
    

You're getting this use of "coordinate system" from the STC, right? This
is yet another example of "coordinate system" being an overloaded term: the
STC (I believe) means it to be something like "multiple coordinate frames
mixed together to create a higher dimensional space" - that is a "system of
coordinates" in the same sense of the word as a "system of equations". But
that's not the common usage of "coordinate system" meaning something
like equatorial J2000 ir whatever. So I think we would want to try to use
different terminology here. (some possible, admittedly not great, ideas:
MultiFrame, SystemOfFrames, or MultiDimCoordinates)

Reply to this email directly or view it on GitHubhttps://github.com//pull/10/files#r9531363
.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with David here. I'm not sure what the common usage of "coordinate system" is among astronomers, it must be field dependent. But I'm pretty sure "coordinate system" is used with spectral cubes as well as 1D spectra. The "coordinate system" in "WCS" is not meant to be limited to space only.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dsberry @nden : I'm saying that in common parlance, "coordinate system" does not always mean the same thing. If I go up and down the hallway and ask different astronomers what "coordinate systems" are (I actually just did that experiment), I get answers ranging "You mean like Cartesian or polar?" to "The way you describe objects in a catalog" to "It depends on the context".

Put more in the context of this proposal's language, some people of "coordinate system" as meaning the frame, while others think of it as a collection of frames (what you mean here), while yet others are thinking of specific points in a frame ("this catalog are in the ICRS coordinate system").

All I'm saying is that not worth worrying about which is "right", but rather just pick a phrasing that doesn't have this language ambiguity. That said, it may just be there's no other better word for it...


Parameters
----------
name : string
a user defined name
frames : list
list of frames [Time, Sky, Spectral]
"""
def __init__(self, frames, name="CompositeSystem"):
self.frames = frames

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is this a way to define multi-dimensional coordinate Frames such as you may use for a spectral cube? Why does the list of frames not include CoordinateFrame and CoordinateSystem? That is, you may want to include basic CoordinateFrames in your multi-dimensional system, and you may want to combine composite systems together to form a composite system with higher dimensionality.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, this is too restrictive.Will change it.


class ReferencePosition(object):
"""
Encapsulates a reference position for a CoordFrame

Table 1 in the STC document.

"""
standart_reference_positions = ["GEOCENTER", "BARYCENTER", "HELIOCENTER",
"TOPOCENTER", "LSR", "LSRK", "LSRD",
"GALACTIC_CENTER", "MOON", "LOCAL_GROUP_CENTER",
"EMBARYCENTER", "RELOCATABLE", "UNKNOWNRefPos"]

def __init__(self, frame, name):
if self._validate(frame, name):
self._name = name
else:
raise ReferencePositionError

7 changes: 7 additions & 0 deletions generalized_wcs/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
The goal is to provide a framework for WCS transformations which is general and flexible enough that it is easy to extend by adding new transformations or coordinate classes.

The WCS class is represented as a directed graph whose nodes are coordinate systems and edges are transformations
between them. This allows one to specify multiple coordinate systems and the transformations between them.
(The [prototype](https://github.com/nden/code-experiments/blob/master/generalized_wcs_api/prototype/wcs.py) uses [networkx](http://networkx.github.io).)

Further advantage of this approach is that this can be extended by the transformations in the celestial coordinates package `astropy.coordinates` which is also represented by a graph and thus providing access to its claasses and transformations.
99 changes: 99 additions & 0 deletions generalized_wcs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
These are examples of how an instrument scientist can use the package to construct a WCS for an instrument. All examples assume the WCS information was already read in from somewhere (either a FITS header or a JSON file). This does not deal with WCS serialization issues.


JSON files used in the examples:

[dist_info](https://github.com/nden/code-experiments/blob/master/generalized_wcs_api/prototype/jwst_example/reference_files/spec_regions.json) - coonstains models for distortion corrections


[spec_info](https://github.com/nden/code-experiments/blob/master/generalized_wcs_api/prototype/jwst_example/reference_files/spec_wcs.json) - contsains spectral models


[regions_def](https://github.com/nden/code-experiments/blob/master/generalized_wcs_api/prototype/jwst_example/reference_files/regions_miri.json) - constains definitions of IFU regions as polygon vertices in pixel space

[wcs_regions](https://github.com/nden/code-experiments/blob/master/generalized_wcs_api/prototype/jwst_example/reference_files/wcs_regions.json) - contains basic WCS for each IFU region as offsets from a primary region. The WCS for the primary region is in the FITS header

[spec_regions](https://github.com/nden/code-experiments/blob/master/generalized_wcs_api/prototype/jwst_example/reference_files/spec_regions.json) - contains spectral models for all regions



class ImagingWCS(WCS):
"""
wcs_info is a dict of basic FITS WCS keywords from the FITS header

"""
def __init__(wcs_info, distortion_info):
dist = self.create_distortion_transformstion(distortion_info)
linear_trans = self.create_linear_wcs_transformation(wcs_info)
coord_sys = self.create_coordinate_system(wcs_info)
foc2sky = self.create_foc2sky(wcs_info)
trans = modeling.SerialCompositeModel([dist, linear_trans, foc2sky_trans])
super(ImagingWCS, self).__init__(trans, coord_sys)

def create_linear_wcs_transformation(self, wcs_info):
"""Creates a composite transformation of a shift, rotation and scaling"""
return linear_trans

def create_distortion_transformation(self, dist_info):
"""
Creates a distortion transformation.

An example of a JSON representation.
"""
return dist_trans


def create_foc2sky(self, wcs_info):
""" Create a transformation from focal plane to sky - deprojection + celestial rotation"""
return foc2sky

def create_coordinate_system(self, wcs_info):
"""Construts an instance of wsc.CoordinateSystem"""
return coord_sys


Spectral WCS:

class SpectralWCS(WCS):

def __init__(self, spec_info):
trans = self.create_spectral_transform(spec_info)
coord_sys = self.create_coordinate_system(wcs_info)
super(SpectralWCS, self).__init__(trans, coord_sys)

def create_coordinate_system(self, wcs_info):
"""Construts an instance of wsc.CoordinateSystem"""
return coord_sys

def create_spectral_transform(spec_info):
""" Constructs a transform from teh model(s) in sepc_info"""
return spec_model

An examplel of a composite spectral transform

class CompositeSpectralTransform(object):

def __init__(self, wcs_info, spec_info, dist_info=None):
self.spatial_transform = ImagingTransform(wcs_info, dist_info)
self.spectral_transform = SpectralTransform(spec_info)

def __call__(self, x, y):
alpha, delta = self.spatial_transform(x, y)
wave = self.spectral_transform(x)
return alpha, delta, wave


IFU Example:

class IFUWCS(WCS):

def __init__(self, regions_def, wcs_info, wcs_regions, spec_regions, dist_info):
trans = selector.RegionsSelector(mask_shape, regions_def, wcs_info, wcs_regions, spec_regions, dist_info )
coord_sys = self.create_coordinate_system(wcs_info)
super(IFUWCS, self).__init__(trans, coord_sys)

def create_coordinate_system(self, wcs_info):
"""
Construts an instance of wcs.CoordinateSystem which in this case is a composite systems of a SkyFrame and SpectralFrame.
"""
return coord_sys
Loading