Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring cleaning coordinate frames #457

Open
wants to merge 53 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
362dc95
Define and document the core CoordinateFrame API
Cadair Jun 15, 2023
bff8469
First pass at restructuring the pixel <> world API
Cadair Jun 19, 2023
e5b1746
Remove now unused methods
Cadair Jun 20, 2023
3a946d0
Rewrite coordinate systems tests for APE 14
Cadair Jun 20, 2023
8439a84
cleanup
Cadair Jul 3, 2023
76fed91
Revert changes to inverse w/r/t with_units
Cadair Oct 12, 2023
102ba58
Use is_high_level to detect input to inverse
Cadair Oct 12, 2023
9f22da6
Remove isnumerical
Cadair Oct 12, 2023
e745352
Remove old test
Cadair Oct 12, 2023
262a317
Remove unused imports
Cadair Oct 12, 2023
882c6b2
Fix doc build
Cadair Oct 12, 2023
8c48a7c
Delete reference_position
Cadair Oct 12, 2023
001eb07
lint
Cadair Oct 12, 2023
0cec3ee
Add a test for axes ordering with CelestialFrame
Cadair Oct 12, 2023
45fed68
Test different Units said Nadia 💥
Cadair Oct 12, 2023
7a8006a
First attempt at keeping a sorted and unsorted list of frame props
Cadair Oct 16, 2023
d9cae7d
make tests pass, ecept slicing
Nov 20, 2023
6618d1d
ensure units are units
Cadair Sep 25, 2024
0edcb37
Raise an error if with_units is used in numerical inverse
Cadair Sep 26, 2024
0657097
Refactor Frames to require _native_world_axis_object_components
Cadair Sep 26, 2024
9eb7df8
Apply suggestions from code review
Cadair Sep 26, 2024
758f127
Ensure we call values_to_high correctly
Cadair Sep 26, 2024
3a203d2
We don't need to unit convert in API
Cadair Sep 26, 2024
77c1c60
Fix CelestialFrame units
Cadair Sep 26, 2024
0404517
More roundtip test fixing
Cadair Sep 26, 2024
5300a7e
Fix roundtrip test by changing projection type
Cadair Sep 26, 2024
669f292
Test and polish more ordering
Cadair Sep 27, 2024
d32dd24
Make it so CompositeFrame follows the same ordering
Cadair Oct 3, 2024
33d1f7b
Test and fix RegionSelector doctest fail
Cadair Oct 3, 2024
3cb00bb
lint
Cadair Oct 3, 2024
12262de
Fix duplicated pass_env / passenv config in tox
Cadair Oct 3, 2024
b2e6d69
Some doc polish
Cadair Oct 3, 2024
bc2beb9
Fix rebase
Cadair Oct 17, 2024
876b94a
Add high level <> values converters to frames
Cadair Nov 20, 2024
991674a
Fix a apt bug
Cadair Nov 20, 2024
683fd74
Fix a naxes bug
Cadair Nov 20, 2024
8ea2ff4
Compat with astropy 7.0 for high_level methods
Cadair Nov 21, 2024
8c63751
Explicitly error if invalid values
Cadair Nov 21, 2024
ad850f7
Raise an explicit error if mixed high level types are passed
Cadair Nov 21, 2024
0810abe
Test intermediate high_level conversion
Cadair Nov 21, 2024
2d90bb0
Whoops
Cadair Nov 25, 2024
7df9fa8
Fix high level output of intermediate frames
Cadair Dec 3, 2024
cbdbe8a
More test fixes
Cadair Dec 4, 2024
1ac7aea
Move fixture to examples
Cadair Dec 4, 2024
1b7871b
Add changelog
Cadair Dec 4, 2024
c486dae
Merge remote-tracking branch 'upstream/master' into visp_wcs
Cadair Dec 17, 2024
25131a3
Fix api test for bbox
Cadair Dec 17, 2024
62af195
lint
Cadair Dec 17, 2024
21c4f7a
Merge branch 'master' into visp_wcs
WilliamJamieson Dec 18, 2024
f4a36f7
Merge branch 'master' into visp_wcs
WilliamJamieson Dec 18, 2024
8c31426
Fix tests
Cadair Dec 19, 2024
6e9e36a
Merge branch 'master' into visp_wcs
Cadair Dec 19, 2024
f97dcd8
Bugfix uncovered by JWST
WilliamJamieson Dec 19, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: test
on:
push:
branches:
- 'master'
- '*'
tags:
- '*'
pull_request:
Expand Down
10 changes: 10 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
0.22.0 (unreleased)
-------------------

- Coordinate frames now have a "native" order and then are sorted based on ``axes_order``. [#457]

- ``WCS.numerical_inverse`` no longer accepts high level objects (``with_units=`` is not supported) use ``WCS.inverse``. [#457]

- ``CoordinateFrame.coordinates`` has been replaced by ``CoordinateFrame.to_high_level_coordinates`` [#457]

- ``CoordinateFrame.to_quantity`` has been replaced by ``CoordinateFrame.from_high_level_coordinates``. [#457]

- Inputs to ``CelestialFrame``, such as ``axes_names`` are now explicitly in lon, lat order and will re sorted based on ``axes_order=``. [#457]

- Replace usages of ``copy_arrays`` with ``memmap`` [#503]

- Fix an issue with units in ``wcs_from_points``. [#507]
Expand Down
10 changes: 2 additions & 8 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ To install the latest release::

pip install gwcs

The latest release of GWCS is also available as part of `astroconda <https://github.com/astroconda/astroconda>`__.
The latest release of GWCS is also available as a conda package via `conda-forge <https://github.com/conda-forge/gwcs-feedstock>`__.


.. _getting-started:
Expand Down Expand Up @@ -240,13 +240,7 @@ To convert a pixel (x, y) = (1, 2) to sky coordinates, call the WCS object as a
The :meth:`~gwcs.wcs.WCS.invert` method evaluates the :meth:`~gwcs.wcs.WCS.backward_transform`
if available, otherwise applies an iterative method to calculate the reverse coordinates.

.. doctest-skip::

>>> wcsobj.invert(*sky)
(0.9999999996185807, 1.999999999186798)

GWCS supports the common WCS interface which defines several methods
to work with high level Astropy objects:
GWCS supports the :ref:`wcsapi` which defines several methods to work with high level Astropy objects:

.. doctest-skip::

Expand Down
105 changes: 11 additions & 94 deletions gwcs/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@

"""

from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS
from astropy.wcs.wcsapi import BaseLowLevelWCS, HighLevelWCSMixin
from astropy.modeling import separable
import astropy.units as u

from . import utils
from . import coordinate_frames as cf
from gwcs import utils

__all__ = ["GWCSAPIMixin"]


class GWCSAPIMixin(BaseHighLevelWCS, BaseLowLevelWCS):
class GWCSAPIMixin(BaseLowLevelWCS, HighLevelWCSMixin):
"""
A mix-in class that is intended to be inherited by the
:class:`~gwcs.wcs.WCS` class and provides the low- and high-level
Expand Down Expand Up @@ -52,14 +51,7 @@ def world_axis_physical_types(self):
arbitrary string. Alternatively, if the physical type is
unknown/undefined, an element can be `None`.
"""
# A CompositeFrame orders the output correctly based on axes_order.
if isinstance(self.output_frame, cf.CompositeFrame):
return self.output_frame.axis_physical_types

# If we don't have a CompositeFrame, where this is taken care of for us,
# we need to make sure we re-order the output to match the transform.
# The underlying frames don't reorder themselves because axes_order is global.
return tuple(self.output_frame.axis_physical_types[i] for i in self.output_frame.axes_order)
return self.output_frame.axis_physical_types

@property
def world_axis_units(self):
Expand All @@ -75,22 +67,17 @@ def world_axis_units(self):

def _remove_quantity_output(self, result, frame):
if self.forward_transform.uses_quantity:
if self.output_frame.naxes == 1:
if frame.naxes == 1:
result = [result]

result = tuple(r.to_value(unit) for r, unit in zip(result, frame.unit))
result = tuple(r.to_value(unit) if isinstance(r, u.Quantity) else r
for r, unit in zip(result, frame.unit))

# If we only have one output axes, we shouldn't return a tuple.
if self.output_frame.naxes == 1 and isinstance(result, tuple):
return result[0]
return result

def _add_units_input(self, arrays, transform, frame):
Cadair marked this conversation as resolved.
Show resolved Hide resolved
if transform.uses_quantity:
return tuple(u.Quantity(array, unit) for array, unit in zip(arrays, frame.unit))

return arrays

def pixel_to_world_values(self, *pixel_arrays):
"""
Convert pixel coordinates to world coordinates.
Expand All @@ -104,8 +91,7 @@ def pixel_to_world_values(self, *pixel_arrays):
order, where for an image, ``x`` is the horizontal coordinate and ``y``
is the vertical coordinate.
"""
pixel_arrays = self._add_units_input(pixel_arrays, self.forward_transform, self.input_frame)
result = self(*pixel_arrays, with_units=False)
result = self._call_forward(*pixel_arrays)

return self._remove_quantity_output(result, self.output_frame)

Expand All @@ -132,9 +118,7 @@ def world_to_pixel_values(self, *world_arrays):
be returned in the ``(x, y)`` order, where for an image, ``x`` is the
horizontal coordinate and ``y`` is the vertical coordinate.
"""
world_arrays = self._add_units_input(world_arrays, self.backward_transform, self.output_frame)

result = self.invert(*world_arrays, with_units=False)
result = self._call_backward(*world_arrays)

return self._remove_quantity_output(result, self.input_frame)

Expand Down Expand Up @@ -263,78 +247,11 @@ def serialized_classes(self):

@property
def world_axis_object_classes(self):
return self.output_frame._world_axis_object_classes
return self.output_frame.world_axis_object_classes

@property
def world_axis_object_components(self):
return self.output_frame._world_axis_object_components

# High level APE 14 API
Cadair marked this conversation as resolved.
Show resolved Hide resolved

@property
def low_level_wcs(self):
"""
Returns a reference to the underlying low-level WCS object.
"""
return self

def _sanitize_pixel_inputs(self, *pixel_arrays):
pixels = []
if self.forward_transform.uses_quantity:
for i, pixel in enumerate(pixel_arrays):
if not isinstance(pixel, u.Quantity):
pixel = u.Quantity(value=pixel, unit=self.input_frame.unit[i])
pixels.append(pixel)
else:
for i, pixel in enumerate(pixel_arrays):
if isinstance(pixel, u.Quantity):
if pixel.unit != self.input_frame.unit[i]:
raise ValueError('Quantity input does not match the '
'input_frame unit.')
pixel = pixel.value
pixels.append(pixel)

return pixels

def pixel_to_world(self, *pixel_arrays):
"""
Convert pixel values to world coordinates.
"""
pixels = self._sanitize_pixel_inputs(*pixel_arrays)
return self(*pixels, with_units=True)

def array_index_to_world(self, *index_arrays):
"""
Convert array indices to world coordinates (represented by Astropy
objects).
"""
pixel_arrays = index_arrays[::-1]
pixels = self._sanitize_pixel_inputs(*pixel_arrays)
return self(*pixels, with_units=True)

def world_to_pixel(self, *world_objects):
"""
Convert world coordinates to pixel values.
"""
result = self.invert(*world_objects, with_units=True)

if self.input_frame.naxes > 1:
first_res = result[0]
if not utils.isnumerical(first_res):
result = [i.value for i in result]
else:
if not utils.isnumerical(result):
result = result.value

return result

def world_to_array_index(self, *world_objects):
"""
Convert world coordinates (represented by Astropy objects) to array
indices.
"""
result = self.invert(*world_objects, with_units=True)[::-1]
return tuple([utils._toindex(r) for r in result])
return self.output_frame.world_axis_object_components

@property
def pixel_axis_names(self):
Expand Down
11 changes: 0 additions & 11 deletions gwcs/converters/wcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,8 @@ def from_yaml_tree(self, node, tag, ctx):
from ..coordinate_frames import SpectralFrame
node = self._from_yaml_tree(node, tag, ctx)

if 'reference_position' in node:
node['reference_position'] = node['reference_position'].upper()

return SpectralFrame(**node)

def to_yaml_tree(self, frame, tag, ctx):
node = self._to_yaml_tree(frame, tag, ctx)

if frame.reference_position is not None:
node['reference_position'] = frame.reference_position.lower()

return node


class CompositeFrameConverter(FrameConverter):
tags = ["tag:stsci.edu:gwcs/composite_frame-*"]
Expand Down
Loading
Loading