-
-
Notifications
You must be signed in to change notification settings - Fork 18
Generalized WCS #10
base: master
Are you sure you want to change the base?
Generalized WCS #10
Changes from all commits
6dec200
bc39415
77e80c9
21b0d36
b06e01e
8878e42
2fa82bb
72d7e9f
6605d14
ca7c848
16b99b2
9824aa6
016ed81
c2c4ee9
49c51bb
2056192
d4ae4ec
f9bb445
36f9011
6dd51ac
680acb0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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. |
| 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 | ||
| """ | ||
|
|
||
| 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", '...'] | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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).
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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,
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 = | ||
|
|
||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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...
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In common parlance, a "coordinate system" is surely any collection of axes David On 7 February 2014 07:11, Erik Tollerud notifications@github.com wrote:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
|
||
| 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. |
| 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 |
There was a problem hiding this comment.
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?