Skip to content
Closed
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
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

- drop support for numpy 1.25. [#285]
- require asdf 3.3 and drop support for astropy 5.2 [#297]
- Add support for ``astropy.wcs.wcsapi.HighLevelWCSWrapper``. [#296]

0.8.0 (2025-06-11)
------------------
Expand Down
14 changes: 14 additions & 0 deletions asdf_astropy/converters/wcs/highlevelwrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from asdf.extension import Converter


class HighLevelWCSWrapperConverter(Converter):
tags = ("tag:astropy.org:astropy/wcs/highlevelwcswrapper-*",)
types = ("astropy.wcs.wcsapi.high_level_wcs_wrapper.HighLevelWCSWrapper",)

def from_yaml_tree(self, node, tag, ctx):
from astropy.wcs.wcsapi import HighLevelWCSWrapper

return HighLevelWCSWrapper(node["wcs"])

def to_yaml_tree(self, hlwcs, tag, ctx):
return {"wcs": hlwcs.low_level_wcs}
31 changes: 31 additions & 0 deletions asdf_astropy/converters/wcs/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import pytest
from astropy.wcs import WCS
from pytest_lazy_fixtures import lf


@pytest.fixture
def gwcs_4d_identity_units():
examples = pytest.importorskip("gwcs.examples")
return examples.gwcs_4d_identity_units()


@pytest.fixture
def astropy_wcs_4d():
wcs = WCS(naxis=4)
wcs.wcs.ctype = "RA---CAR", "DEC--CAR", "FREQ", "TIME"
wcs.wcs.cunit = "deg", "deg", "Hz", "s"
wcs.wcs.cdelt = -2.0, 2.0, 3.0e9, 1
wcs.wcs.crval = 4.0, 0.0, 4.0e9, 3
wcs.wcs.crpix = 6.0, 7.0, 11.0, 11.0
wcs.wcs.cname = "Right Ascension", "Declination", "Frequency", "Time"
return wcs


@pytest.fixture(
params=[
lf("gwcs_4d_identity_units"),
lf("astropy_wcs_4d"),
],
)
def all_4d_wcses(request):
return request.param
31 changes: 31 additions & 0 deletions asdf_astropy/converters/wcs/tests/test_highlevelwcswrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import asdf
import pytest
from astropy.wcs import WCS
from astropy.wcs.wcsapi import HighLevelWCSWrapper
from astropy.wcs.wcsapi.wrappers.sliced_wcs import SlicedLowLevelWCS
from pytest_lazy_fixtures import lf

from asdf_astropy.testing.helpers import assert_wcs_equal


@pytest.fixture
def wrapped_wcses(all_4d_wcses):
Copy link
Member

Choose a reason for hiding this comment

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

I'm kinda keen to test gwcs inputs here.

return HighLevelWCSWrapper(all_4d_wcses)


@pytest.fixture
def wrapped_sliced_wcs(astropy_wcs_4d):
return HighLevelWCSWrapper(SlicedLowLevelWCS(astropy_wcs_4d, 1))


@pytest.mark.parametrize("hl_wcs", [lf("wrapped_wcses"), lf("wrapped_sliced_wcs")])
def test_hllwcs_serialization(hl_wcs, tmp_path):
file_path = tmp_path / "test_highlevelwcswrapper.asdf"
with asdf.AsdfFile() as af:
af["hl_wcs"] = hl_wcs
af.write_to(file_path, version="1.6.0")

with asdf.open(file_path) as af:
loaded_hl_wcs = af["hl_wcs"]
if isinstance(loaded_hl_wcs, WCS):
assert_wcs_equal(hl_wcs._low_level_wcs, loaded_hl_wcs._low_level_wcs)
38 changes: 22 additions & 16 deletions asdf_astropy/converters/wcs/tests/test_slicedwcs.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
import asdf
import numpy as np
import pytest
from astropy.wcs import WCS
from astropy.wcs.wcsapi.wrappers.sliced_wcs import SlicedLowLevelWCS
from pytest_lazy_fixtures import lf

from asdf_astropy.testing.helpers import assert_wcs_equal


def create_wcs():
wcs = WCS(naxis=4)
wcs.wcs.ctype = "RA---CAR", "DEC--CAR", "FREQ", "TIME"
wcs.wcs.cunit = "deg", "deg", "Hz", "s"
wcs.wcs.cdelt = -2.0, 2.0, 3.0e9, 1
wcs.wcs.crval = 4.0, 0.0, 4.0e9, 3
wcs.wcs.crpix = 6.0, 7.0, 11.0, 11.0
wcs.wcs.cname = "Right Ascension", "Declination", "Frequency", "Time"
@pytest.fixture(
params=[
1,
[slice(None), slice(None), slice(None), 10],
[slice(3), slice(None), slice(None), 10],
[Ellipsis, slice(5, 10)],
np.s_[:, 2, 3, :],
],
)
def slices(request):
return request.param

wcs0 = SlicedLowLevelWCS(wcs, 1)
wcs1 = SlicedLowLevelWCS(wcs, [slice(None), slice(None), slice(None), 10])
wcs3 = SlicedLowLevelWCS(SlicedLowLevelWCS(wcs, slice(None)), [slice(3), slice(None), slice(None), 10])
wcs_ellipsis = SlicedLowLevelWCS(wcs, [Ellipsis, slice(5, 10)])
wcs2 = SlicedLowLevelWCS(wcs, np.s_[:, 2, 3, :])
return [wcs0, wcs1, wcs2, wcs_ellipsis, wcs3]

@pytest.fixture
def sliced_wcses(astropy_wcs_4d, slices):
return SlicedLowLevelWCS(astropy_wcs_4d, slices)

@pytest.mark.parametrize("sl_wcs", create_wcs())

@pytest.fixture
def nested_sliced_wcs(astropy_wcs_4d):
return SlicedLowLevelWCS(SlicedLowLevelWCS(astropy_wcs_4d, slice(None)), [slice(3), slice(None), slice(None), 10])


@pytest.mark.parametrize("sl_wcs", [lf("sliced_wcses"), lf("nested_sliced_wcs")])
def test_sliced_wcs_serialization(sl_wcs, tmp_path):
file_path = tmp_path / "test_slicedwcs.asdf"
with asdf.AsdfFile() as af:
Expand Down
3 changes: 3 additions & 0 deletions asdf_astropy/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from .converters.unit.magunit import MagUnitConverter
from .converters.unit.quantity import QuantityConverter
from .converters.unit.unit import UnitConverter
from .converters.wcs.highlevelwrapper import HighLevelWCSWrapperConverter
from .converters.wcs.slicedwcs import SlicedWCSConverter
from .converters.wcs.wcs import WCSConverter

Expand Down Expand Up @@ -522,6 +523,7 @@
UncertaintyConverter(),
WCSConverter(),
SlicedWCSConverter(),
HighLevelWCSWrapperConverter(),
]

_COORDINATES_MANIFEST_URIS = [
Expand All @@ -540,6 +542,7 @@


_ASTROPY_EXTENSION_MANIFEST_URIS = [
"asdf://astropy.org/astropy/manifests/astropy-1.4.0",
"asdf://astropy.org/astropy/manifests/astropy-1.3.0",
"asdf://astropy.org/astropy/manifests/astropy-1.2.0",
"asdf://astropy.org/astropy/manifests/astropy-1.1.0",
Expand Down
79 changes: 79 additions & 0 deletions asdf_astropy/resources/manifests/astropy-1.4.0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
id: asdf://astropy.org/astropy/manifests/astropy-1.4.0
extension_uri: asdf://astropy.org/astropy/extensions/astropy-1.4.0
title: Astropy extension 1.4.0
description: |-
A set of tags for serializing astropy objects. This does not include most
model classes, which are handled by an implementation of the ASDF
transform extension.
asdf_standard_requirement:
gte: 1.6.0
tags:
- tag_uri: tag:astropy.org:astropy/time/timedelta-1.1.0
schema_uri: http://astropy.org/schemas/astropy/time/timedelta-1.1.0
title: Represents an instance of TimeDelta from astropy
description: |-
Represents the time difference between two times.
- tag_uri: tag:astropy.org:astropy/fits/fits-1.1.0
schema_uri: http://astropy.org/schemas/astropy/fits/fits-1.1.0
title: A FITS file inside of an ASDF file.
description: |-
This schema is useful for distributing ASDF files that can
automatically be converted to FITS files by specifying the exact
content of the resulting FITS file.

Not all kinds of data in FITS are directly representable in ASDF.
For example, applying an offset and scale to the data using the
`BZERO` and `BSCALE` keywords. In these cases, it will not be
possible to store the data in the native format from FITS and also
be accessible in its proper form in the ASDF file.

Only image and binary table extensions are supported.
- tag_uri: tag:astropy.org:astropy/table/table-1.2.0
schema_uri: http://astropy.org/schemas/astropy/table/table-1.2.0
title: A table.
description: |-
A table is represented as a list of columns, where each entry is a
[column](ref:http://stsci.edu/schemas/asdf/table/column-1.1.0)
object, containing the data and some additional information.

The data itself may be stored inline as text, or in binary in either
row- or column-major order by use of the `strides` property on the
individual column arrays.

Each column in the table must have the same first (slowest moving)
dimension.
- tag_uri: tag:astropy.org:astropy/transform/units_mapping-1.1.0
schema_uri: http://astropy.org/schemas/astropy/transform/units_mapping-1.1.0
title: Mapper that operates on the units of the input.
description: |-
This transform operates on the units of the input, first converting to
the expected input units, then assigning replacement output units without
further conversion.
- tag_uri: tag:astropy.org:astropy/table/ndarraymixin-1.0.0
schema_uri: http://astropy.org/schemas/astropy/table/ndarraymixin-1.0.0
title: NdarrayMixin column.
description: |-
Represents an astropy.table.NdarrayMixin instance.
- tag_uri: tag:astropy.org:astropy/wcs/slicedwcs-1.0.0
schema_uri: http://astropy.org/schemas/astropy/wcs/slicedwcs-1.0.0
title: Represents an instance of SlicedLowLevelWCS
description: |-
The SlicedLowLevelWCS class is a wrapper class for WCS that applies slices
to the WCS, allowing certain pixel and world dimensions to be retained or
dropped.

It manages the slicing and coordinate transformations while preserving
the underlying WCS object.
- tag_uri: tag:astropy.org:astropy/wcs/wcs-1.0.0
schema_uri: http://astropy.org/schemas/astropy/wcs/wcs-1.0.0
title: FITS WCS (World Coordinate System) Converter
description: |-
Represents the FITS WCS object, the HDUlist of the FITS header is preserved
during serialization and during deserialization the WCS object is recreated
from the HDUlist.
- tag_uri: tag:astropy.org:astropy/wcs/highlevelwcswrapper-1.0.0
schema_uri: http://astropy.org/schemas/astropy/wcs/highlevelwcswrapper-1.0.0
title: Represents an instance of HighLevelWCSWrapper
description: |-
Wrapper class that can take any astropy.wcs.wcsapi.BaseLowLevelWCS
object and expose the high-level WCS API.
29 changes: 29 additions & 0 deletions asdf_astropy/resources/schemas/wcs/highlevelwcswrapper-1.0.0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
%YAML 1.1
---
$schema: "http://stsci.edu/schemas/yaml-schema/draft-01"
id: "http://astropy.org/schemas/astropy/wcs/highlevelwcswrapper-1.0.0"

title: Represents a HighLevelWCSWrapper object

description: >-
Wrapper class that can take any astropy.wcs.wcsapi.BaseLowLevelWCS
object and expose the high-level WCS API.

allOf:
- type: object
properties:
wcs:
anyOf:
# Known WCS objects
- tag: "tag:astropy.org:astropy/wcs/wcs-1*"
- tag: "tag:stsci.edu:gwcs/wcs-*"
- tag: "tag:astropy.org:astropy/wcs/slicedwcs-*"
- tag: "tag:sunpy.org:ndcube/resampledwcs-*"
- tag: "tag:sunpy.org:ndcube/ndcubesequence-*"
- tag: "tag:sunpy.org:ndcube/reorderedwcs-*"
- tag: "tag:sunpy.org:ndcube/compoundwcs-*"
# Allow anything else because any APE-14 compliant object is valid
- {}


required: ["wcs"]
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ test = [
"coverage",
"pytest-astropy",
"pytest-asdf-plugin",
"pytest-lazy-fixtures",
"pytest",
"gwcs",
"scipy", # indirect requirement via astropy
]
[project.urls]
Expand Down
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ commands_pre=
devdeps: pip install -U --pre -i https://pypi.anaconda.org/liberfa/simple pyerfa
devdeps: pip install -U --pre -i https://pypi.anaconda.org/astropy/simple astropy

# gwcs is incompatible with our oldest deps
oldestdeps: pip uninstall -y gwcs
# Generate `requiremments-min.txt`
oldestdeps: minimum_dependencies asdf-astropy --filename {envtmpdir}/requirements-min.txt
# Force install everything from `requirements-min.txt`
Expand Down
Loading