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 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c6c6957
Define and document the core CoordinateFrame API
Cadair Jun 15, 2023
add480e
First pass at restructuring the pixel <> world API
Cadair Jun 19, 2023
f90d17a
Remove now unused methods
Cadair Jun 20, 2023
1ac43b8
Rewrite coordinate systems tests for APE 14
Cadair Jun 20, 2023
b2d7d8c
cleanup
Cadair Jul 3, 2023
c1fe2bd
Revert changes to inverse w/r/t with_units
Cadair Oct 12, 2023
dbcbf74
Use is_high_level to detect input to inverse
Cadair Oct 12, 2023
52e227d
Remove isnumerical
Cadair Oct 12, 2023
8e4b9d9
Remove old test
Cadair Oct 12, 2023
fd8584a
Remove unused imports
Cadair Oct 12, 2023
980554b
Fix doc build
Cadair Oct 12, 2023
d12e7df
Delete reference_position
Cadair Oct 12, 2023
4e19dda
lint
Cadair Oct 12, 2023
84598b8
Add a test for axes ordering with CelestialFrame
Cadair Oct 12, 2023
ddac585
Test different Units said Nadia 💥
Cadair Oct 12, 2023
9fc1d89
First attempt at keeping a sorted and unsorted list of frame props
Cadair Oct 16, 2023
a3fe415
make tests pass, ecept slicing
Nov 20, 2023
2cdaf7b
ensure units are units
Cadair Sep 25, 2024
ccbc5e9
Raise an error if with_units is used in numerical inverse
Cadair Sep 26, 2024
18bb8ef
Refactor Frames to require _native_world_axis_object_components
Cadair Sep 26, 2024
07fe5ec
Apply suggestions from code review
Cadair Sep 26, 2024
9292790
Ensure we call values_to_high correctly
Cadair Sep 26, 2024
17de217
We don't need to unit convert in API
Cadair Sep 26, 2024
a5d7642
Fix units in waoc
Cadair Sep 26, 2024
95c0773
Merge branch 'fix_celestial_waoc' into visp_wcs
Cadair Sep 26, 2024
760a636
Fix CelestialFrame units
Cadair Sep 26, 2024
54a3936
More roundtip test fixing
Cadair Sep 26, 2024
5b189e7
Fix roundtrip test by changing projection type
Cadair Sep 26, 2024
8264b7e
Test and polish more ordering
Cadair Sep 27, 2024
beb24d3
Make it so CompositeFrame follows the same ordering
Cadair Oct 3, 2024
ea5d3e0
Test and fix RegionSelector doctest fail
Cadair Oct 3, 2024
38bdf09
lint
Cadair Oct 3, 2024
98a1508
Fix duplicated pass_env / passenv config in tox
Cadair Oct 3, 2024
c037a05
Some doc polish
Cadair Oct 3, 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: 'CI'
on:
push:
branches:
- 'master'
- '*'
tags:
- '*'
pull_request:
Expand Down
5 changes: 0 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,6 @@ 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:

Expand Down
95 changes: 12 additions & 83 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

__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 @@ -78,19 +77,14 @@ def _remove_quantity_output(self, result, frame):
if self.output_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 +98,9 @@ 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)
if self.forward_transform.uses_quantity:
pixel_arrays = self._add_units_input(pixel_arrays, self.input_frame)
result = self._call_forward(*pixel_arrays)

return self._remove_quantity_output(result, self.output_frame)

Expand All @@ -132,9 +127,10 @@ 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)
if self.backward_transform.uses_quantity:
world_arrays = self._add_units_input(world_arrays, 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 @@ -259,78 +255,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 @@ -138,19 +138,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