From 81649b609cf11f420f3a1a71715657f6ec5f93be Mon Sep 17 00:00:00 2001 From: Nadia Dencheva Date: Sat, 27 Nov 2021 18:06:47 -0500 Subject: [PATCH 01/12] start dev --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7a9faf69..568f86f3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,12 @@ +0.17.2 (Unreleased) +------------------- +Bug Fixes +^^^^^^^^^ + +New Features +^^^^^^^^^^^^ + + 0.17.1 (2021-11-27) ------------------- From dd341fabe5ace352597c56c8bd4083b7cced9b17 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Thu, 2 Dec 2021 09:17:57 -0500 Subject: [PATCH 02/12] DOC: Fix code typo in example --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 59f58695..99a8ff17 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -254,7 +254,7 @@ Save a WCS object as an ASDF extension in a FITS file 1 SCI 1 ImageHDU 71 (600, 550) float32 >>> tree = {"sci": hdul[1].data, ... "wcs": wcsobj} - >>> fa = fits.embed.AsdfInFits(hdul, tree) + >>> fa = fits_embed.AsdfInFits(hdul, tree) >>> fa.write_to("imaging_with_wcs_in_asdf.fits") >>> fits.info("imaging_with_wcs_in_asdf.fits") Filename: example_with_wcs.asdf From c7474294b14f8f2a34a2aeb67ff45b7b12cde12e Mon Sep 17 00:00:00 2001 From: Mihai Cara Date: Tue, 16 Nov 2021 23:54:18 -0500 Subject: [PATCH 03/12] Update region.py --- CHANGES.rst | 3 ++ gwcs/region.py | 103 ++++++++++++++++++++++++++++++-------- gwcs/tests/test_region.py | 9 ++-- 3 files changed, 91 insertions(+), 24 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 568f86f3..f7f5614c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -31,6 +31,9 @@ Bug Fixes - Use ``CD`` formalism in ``WCS.to_fits_sip()``. [#380] +- Updated code in ``region.py`` with latest improvements and bug fixes + from ``stsci.skypac.regions.py`` [#382] + New Features ^^^^^^^^^^^^ - ``wcs_from_points`` now includes fitting for the inverse transform. [#349] diff --git a/gwcs/region.py b/gwcs/region.py index d257a43d..29665505 100644 --- a/gwcs/region.py +++ b/gwcs/region.py @@ -1,8 +1,19 @@ +""" +Polygon filling algorithm. + +""" # Licensed under a 3-clause BSD style license - see LICENSE.rst + +# NOTE: Algorithm description can be found, e.g., here: +# http://www.cs.rit.edu/~icss571/filling/how_to.html +# http://www.cs.uic.edu/~jbell/CourseNotes/ComputerGraphics/PolygonFilling.html + import abc from collections import OrderedDict import numpy as np +__all__ = ['Region', 'Edge', 'Polygon'] + class Region(metaclass=abc.ABCMeta): @@ -81,9 +92,30 @@ def __init__(self, rid, vertices, coord_frame="detector"): "a list of minimum 4 tuples (x,y)") super(Polygon, self).__init__(rid, coord_frame) - self._vertices = np.asarray(vertices) + # self._shiftx & self._shifty are introduced to shift the bottom-left + # corner of the polygon's bounding box to (0,0) as a (hopefully + # temporary) workaround to a limitation of the original code that the + # polygon must be completely contained in the image. It seems that the + # code works fine if we make sure that the bottom-left corner of the + # polygon's bounding box has non-negative coordinates. + self._shiftx = 0 + self._shifty = 0 + for vertex in vertices: + x, y = vertex + if x < self._shiftx: + self._shiftx = x + if y < self._shifty: + self._shifty = y + v = [(i - self._shiftx, j - self._shifty) for i, j in vertices] + + # convert to integer coordinates: + self._vertices = np.asarray(list(map(_round_vertex, v))) + self._shiftx = int(round(self._shiftx)) + self._shifty = int(round(self._shifty)) + self._bbox = self._get_bounding_box() - self._scan_line_range = list(range(self._bbox[1], self._bbox[3] + self._bbox[1] + 1)) + self._scan_line_range = \ + list(range(self._bbox[1], self._bbox[3] + self._bbox[1] + 1)) # constructs a Global Edge Table (GET) in bbox coordinates self._GET = self._construct_ordered_GET() @@ -115,7 +147,10 @@ def _construct_ordered_GET(self): ymin = np.asarray([e._ymin for e in edges]) for i in self._scan_line_range: ymin_ind = (ymin == i).nonzero()[0] - if ymin_ind.any(): + # a hack for incomplete filling .any() fails if 0 is in ymin_ind + # if ymin_ind.any(): + yminindlen, = ymin_ind.shape + if yminindlen: GET[i] = [edges[ymin_ind[0]]] for j in ymin_ind[1:]: GET[i].append(edges[j]) @@ -125,9 +160,8 @@ def get_edges(self): """ Create a list of Edge objects from vertices """ - return [Edge(name='E{}'.format(i - 1), start=self._vertices[i - 1], stop=self._vertices[i]) - for i in range(1, len(self._vertices)) - ] + return [Edge(name=f'E{i - 1}', start=self._vertices[i - 1], stop=self._vertices[i]) + for i in range(1, len(self._vertices))] def scan(self, data): """ @@ -157,21 +191,45 @@ def scan(self, data): # 2. Currently it uses intersection of the scan line with edges. If this is # too slow it should use the 1/m increment (replace 3 above) (or the increment # should be removed from the GET entry). - if self._bbox[2] <= 0: - return data + + # see comments in the __init__ function for the reason of introducing + # polygon shifts (self._shiftx & self._shifty). Here we need to shift + # it back. + + (ny, nx) = data.shape y = np.min(list(self._GET.keys())) + AET = [] scline = self._scan_line_range[-1] + while y <= scline: - AET = self.update_AET(y, AET) + + if y < scline: + AET = self.update_AET(y, AET) + + if self._bbox[2] <= 0: + y += 1 + continue + scan_line = Edge('scan_line', start=[self._bbox[0], y], stop=[self._bbox[0] + self._bbox[2], y]) - x = [np.ceil(e.compute_AET_entry(scan_line)[1]) for e in AET if e is not None] - xnew = np.asarray(np.sort(x), dtype=int) + x = [int(np.ceil(e.compute_AET_entry(scan_line)[1])) + for e in AET if e is not None] + xnew = np.sort(x) + ysh = y + self._shifty + + if ysh < 0 or ysh >= ny: + y += 1 + continue + for i, j in zip(xnew[::2], xnew[1::2]): - data[y][i:j + 1] = self._rid - y = y + 1 + xstart = max(0, i + self._shiftx) + xend = min(j + self._shiftx, nx - 1) + data[ysh][xstart:xend + 1] = self._rid + + y += 1 + return data def update_AET(self, y, AET): @@ -314,16 +372,21 @@ def intersection(self, edge): u = self._stop - self._start v = edge._stop - edge._start w = self._start - edge._start - eps = 1e2 * np.finfo(float).eps - if np.allclose(np.cross(u, v), 0, rtol=0, atol=eps): - return np.array(self._start) D = np.cross(u, v) + + if np.allclose(np.cross(u, v), 0, rtol=0, + atol=1e2 * np.finfo(float).eps): + return np.array(self._start) + return np.cross(v, w) / D * u + self._start def is_parallel(self, edge): u = self._stop - self._start v = edge._stop - edge._start - if np.cross(u, v): - return False - else: - return True + return np.allclose(np.cross(u, v), 0, rtol=0, + atol=1e2 * np.finfo(float).eps) + + +def _round_vertex(v): + x, y = v + return (int(round(x)), int(round(y))) diff --git a/gwcs/tests/test_region.py b/gwcs/tests/test_region.py index 05b6bb0c..5fe69f90 100644 --- a/gwcs/tests/test_region.py +++ b/gwcs/tests/test_region.py @@ -40,7 +40,7 @@ def test_LabelMapperArray_from_vertices_string(): # These tests below check the scanning algorithm for two shapes def polygon1(shape=(9, 9)): - ar = np.zeros(shape) + ar = np.zeros(shape, dtype=int) ar[1, 2] = 1 ar[2][2:4] = 1 ar[3][1:4] = 1 @@ -48,12 +48,12 @@ def polygon1(shape=(9, 9)): ar[5][1:4] = 1 ar[6][2:7] = 1 ar[7][3:6] = 1 - # ar[8][3:4] =1 ##need to include this in the future if padding top and left + ar[8][3:4] = 1 return ar def two_polygons(): - ar = np.zeros((301, 301)) + ar = np.zeros((301, 301), dtype=int) ar[1, 2] = 1 ar[2][2:4] = 1 ar[3][1:4] = 1 @@ -61,7 +61,8 @@ def two_polygons(): ar[5][1:4] = 1 ar[6][2:7] = 1 ar[7][3:6] = 1 - ar[:30, 10:31] = 2 + ar[8][3:4] = 1 + ar[:31, 10:31] = 2 return ar From c0d31ca7fb55fc93171cbe07eaefb6a2ede2e61f Mon Sep 17 00:00:00 2001 From: Nadia Dencheva Date: Sat, 4 Dec 2021 17:13:51 -0500 Subject: [PATCH 04/12] add a change log --- CHANGES.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index f7f5614c..f46f9e7c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,9 +3,15 @@ Bug Fixes ^^^^^^^^^ +- Updated code in ``region.py`` with latest improvements and bug fixes + from ``stsci.skypac.regions.py`` [#382] + + New Features ^^^^^^^^^^^^ +- Moved schemas to standalone package ``asdf-wcs-schemas``. + Reqorked the serialization code to use ASDF converters. [#371] 0.17.1 (2021-11-27) ------------------- @@ -31,8 +37,6 @@ Bug Fixes - Use ``CD`` formalism in ``WCS.to_fits_sip()``. [#380] -- Updated code in ``region.py`` with latest improvements and bug fixes - from ``stsci.skypac.regions.py`` [#382] New Features ^^^^^^^^^^^^ From 869a1ece580e5520604e77961de034bd75522dc0 Mon Sep 17 00:00:00 2001 From: "Zane.Geiger" Date: Mon, 6 Dec 2021 11:41:27 -0500 Subject: [PATCH 05/12] Use new publish-to-pypi.yml to build & publish wheels --- .github/workflows/publish-to-pypi.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/publish-to-pypi.yml diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml new file mode 100644 index 00000000..8f96ce9d --- /dev/null +++ b/.github/workflows/publish-to-pypi.yml @@ -0,0 +1,16 @@ +name: Publish to PyPI + +on: + release: + types: [released] + +jobs: + publish: + uses: spacetelescope/action-publish_to_pypi/.github/workflows/workflow.yml@master + with: + test: false + build_platform_wheels: false # Set to true if your package contains a C extension + secrets: + user: ${{ secrets.PYPI_USERNAME_STSCI_MAINTAINER }} + password: ${{ secrets.PYPI_PASSWORD_STSCI_MAINTAINER }} # WARNING: Do not hardcode secret values here! If you want to use a different user or password, you can override this secret by creating one with the same name in your Github repository settings. + test_password: ${{ secrets.PYPI_PASSWORD_STSCI_MAINTAINER_TEST }} From 258d31af8a4474eff20aab6ced87c665527cae46 Mon Sep 17 00:00:00 2001 From: Mihai Cara Date: Tue, 27 Jul 2021 10:52:21 -0400 Subject: [PATCH 06/12] Convert to asdf Converters --- CHANGES.rst | 2 +- docs/gwcs/schemas/index.rst | 33 -- docs/gwcs/wcs_validation.rst | 16 +- docs/index.rst | 1 - gwcs/converters/__init__.py | 2 + .../geometry.py} | 44 ++- .../selector.py} | 74 ++--- gwcs/converters/spectroscopy.py | 104 +++++++ .../{schemas => converters/tests}/__init__.py | 0 gwcs/converters/tests/test_selector.py | 126 ++++++++ .../tests/test_transforms.py | 8 +- gwcs/{tags => converters}/tests/test_wcs.py | 96 ++++-- gwcs/converters/wcs.py | 210 +++++++++++++ gwcs/extension.py | 84 +++-- gwcs/geometry.py | 1 - .../stsci.edu/gwcs/celestial_frame-1.0.0.yaml | 25 -- .../stsci.edu/gwcs/composite_frame-1.0.0.yaml | 20 -- .../gwcs/direction_cosines-1.0.0.yaml | 38 --- .../gwcs/direction_cosines-1.1.0.yaml | 38 --- gwcs/schemas/stsci.edu/gwcs/frame-1.0.0.yaml | 82 ----- .../schemas/stsci.edu/gwcs/frame2d-1.0.0.yaml | 36 --- .../gwcs/grating_equation-1.0.0.yaml | 52 ---- .../gwcs/grating_equation-1.1.0.yaml | 52 ---- .../stsci.edu/gwcs/label_mapper-1.0.0.yaml | 130 -------- .../stsci.edu/gwcs/label_mapper-1.1.0.yaml | 130 -------- .../gwcs/regions_selector-1.0.0.yaml | 104 ------- .../gwcs/regions_selector-1.1.0.yaml | 104 ------- .../stsci.edu/gwcs/sellmeier_glass-1.0.0.yaml | 38 --- .../stsci.edu/gwcs/sellmeier_glass-1.1.0.yaml | 38 --- .../stsci.edu/gwcs/sellmeier_zemax-1.0.0.yaml | 72 ----- .../stsci.edu/gwcs/sellmeier_zemax-1.1.0.yaml | 72 ----- .../schemas/stsci.edu/gwcs/snell3d-1.0.0.yaml | 13 - .../schemas/stsci.edu/gwcs/snell3d-1.1.0.yaml | 13 - .../stsci.edu/gwcs/spectral_frame-1.0.0.yaml | 30 -- .../gwcs/spherical_cartesian-1.0.0.yaml | 44 --- .../gwcs/spherical_cartesian-1.1.0.yaml | 44 --- gwcs/schemas/stsci.edu/gwcs/step-1.0.0.yaml | 32 -- gwcs/schemas/stsci.edu/gwcs/step-1.1.0.yaml | 32 -- .../stsci.edu/gwcs/stokes_frame-1.0.0.yaml | 22 -- .../stsci.edu/gwcs/temporal_frame-1.0.0.yaml | 56 ---- gwcs/schemas/stsci.edu/gwcs/wcs-1.0.0.yaml | 33 -- gwcs/schemas/stsci.edu/gwcs/wcs-1.1.0.yaml | 33 -- gwcs/spectroscopy.py | 8 +- gwcs/tags/__init__.py | 9 - gwcs/tags/spectroscopy_models.py | 123 -------- gwcs/tags/tests/__init__.py | 0 gwcs/tags/tests/test_selector.py | 74 ----- gwcs/tags/wcs.py | 292 ------------------ gwcs/tests/test_geometry.py | 6 +- gwcs/wcs.py | 2 +- setup.cfg | 12 +- setup.py | 29 -- 52 files changed, 640 insertions(+), 2099 deletions(-) delete mode 100644 docs/gwcs/schemas/index.rst create mode 100644 gwcs/converters/__init__.py rename gwcs/{tags/geometry_models.py => converters/geometry.py} (58%) rename gwcs/{tags/selectortags.py => converters/selector.py} (62%) create mode 100644 gwcs/converters/spectroscopy.py rename gwcs/{schemas => converters/tests}/__init__.py (100%) create mode 100644 gwcs/converters/tests/test_selector.py rename gwcs/{tags => converters}/tests/test_transforms.py (90%) rename gwcs/{tags => converters}/tests/test_wcs.py (65%) create mode 100644 gwcs/converters/wcs.py delete mode 100644 gwcs/schemas/stsci.edu/gwcs/celestial_frame-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/composite_frame-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/direction_cosines-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/direction_cosines-1.1.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/frame-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/frame2d-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/grating_equation-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/grating_equation-1.1.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/label_mapper-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/label_mapper-1.1.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/regions_selector-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/regions_selector-1.1.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/sellmeier_glass-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/sellmeier_glass-1.1.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/sellmeier_zemax-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/sellmeier_zemax-1.1.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/snell3d-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/snell3d-1.1.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/spectral_frame-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/spherical_cartesian-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/spherical_cartesian-1.1.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/step-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/step-1.1.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/stokes_frame-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/temporal_frame-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/wcs-1.0.0.yaml delete mode 100644 gwcs/schemas/stsci.edu/gwcs/wcs-1.1.0.yaml delete mode 100644 gwcs/tags/__init__.py delete mode 100644 gwcs/tags/spectroscopy_models.py delete mode 100644 gwcs/tags/tests/__init__.py delete mode 100644 gwcs/tags/tests/test_selector.py delete mode 100644 gwcs/tags/wcs.py diff --git a/CHANGES.rst b/CHANGES.rst index f46f9e7c..ea86df8c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,7 +11,7 @@ New Features ^^^^^^^^^^^^ - Moved schemas to standalone package ``asdf-wcs-schemas``. - Reqorked the serialization code to use ASDF converters. [#371] + Reworked the serialization code to use ASDF converters. [#388] 0.17.1 (2021-11-27) ------------------- diff --git a/docs/gwcs/schemas/index.rst b/docs/gwcs/schemas/index.rst deleted file mode 100644 index e0aca7cd..00000000 --- a/docs/gwcs/schemas/index.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _gwcs-schemas: - -GWCS Schema Definitions -======================= - -WCS object ----------- - -.. asdf-autoschemas:: - - wcs-1.0.0 - step-1.0.0 - - -Coordinate Frames ------------------ - -.. asdf-autoschemas:: - - celestial_frame-1.0.0 - frame-1.0.0 - spectral_frame-1.0.0 - frame2d-1.0.0 - temporal_frame-1.0.0 - composite_frame-1.0.0 - -Transforms ----------- - -.. asdf-autoschemas:: - - label_mapper-1.0.0 - regions_selector-1.0.0 diff --git a/docs/gwcs/wcs_validation.rst b/docs/gwcs/wcs_validation.rst index b02cb558..6ecc22b3 100644 --- a/docs/gwcs/wcs_validation.rst +++ b/docs/gwcs/wcs_validation.rst @@ -7,14 +7,18 @@ WCS validation The WCS is validated when an object is read in or written to a file. However, this happens transparently to the end user and knowing the details of the validation machinery is not necessary to use or -construct a WCS object. +construct a WCS object. GWCS uses the `Advanced Scientific Data Format `__ (ASDF) to validate the transforms, coordinate frames and the overall WCS object structure. ASDF makes use of abstract data type -definitions called ``schemas``. The serialization and deserialization happens in classes, -referred to as ``tags``. Most of the transform schemas live in the ``asdf-standard`` package while most of the transform tags live in ``astropy``. :ref:`gwcs-schemas` are available for the WCS object, coordinate frames and some WCS specific transforms. - -Packages using GWCS may create their own transforms and schemas and register them as an ``Asdf Extension``. If those are of general use, it is recommended they be included in astropy. - +definitions called ``schemas``. The serialization and deserialization happens +in classes, referred to as ``tags``. Most of the transform schemas live in the +``asdf-standard`` package while most of the transform tags live in ``astropy``. +`gwcs schemas `_ are +available for the WCS object, coordinate frames and some WCS specific +transforms. +Packages using GWCS may create their own transforms and schemas and register +them as an ``Asdf Extension``. If those are of general use, it is recommended +they be included in astropy. diff --git a/docs/index.rst b/docs/index.rst index 99a8ff17..031660fc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -306,7 +306,6 @@ Using `gwcs` gwcs/wcstools.rst gwcs/pure_asdf.rst gwcs/wcs_validation.rst - gwcs/schemas/index.rst gwcs/points_to_wcs.rst diff --git a/gwcs/converters/__init__.py b/gwcs/converters/__init__.py new file mode 100644 index 00000000..ccbc6cb9 --- /dev/null +++ b/gwcs/converters/__init__.py @@ -0,0 +1,2 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +# -*- coding: utf-8 -*- diff --git a/gwcs/tags/geometry_models.py b/gwcs/converters/geometry.py similarity index 58% rename from gwcs/tags/geometry_models.py rename to gwcs/converters/geometry.py index acffd29e..e625e13e 100644 --- a/gwcs/tags/geometry_models.py +++ b/gwcs/converters/geometry.py @@ -1,22 +1,20 @@ """ ASDF tags for geometry related models. + """ -from asdf import yamlutil -from ..gwcs_types import GWCSTransformType -from .. geometry import (ToDirectionCosines, FromDirectionCosines, - SphericalToCartesian, CartesianToSpherical) +from asdf_astropy.converters.transform.core import TransformConverterBase -__all__ = ['DirectionCosinesType', 'SphericalCartesianType'] +__all__ = ['DirectionCosinesConverter', 'SphericalCartesianConverter'] -class DirectionCosinesType(GWCSTransformType): - name = "direction_cosines" - types = [ToDirectionCosines, FromDirectionCosines] - version = "1.1.0" +class DirectionCosinesConverter(TransformConverterBase): + tags = ["tag:stsci.edu:gwcs/direction_cosines-*"] + types = ["gwcs.geometry.ToDirectionCosines", + "gwcs.geometry.FromDirectionCosines"] - @classmethod - def from_tree_transform(cls, node, ctx): + def from_yaml_tree_transform(self, node, tag, ctx): + from ..geometry import ToDirectionCosines, FromDirectionCosines transform_type = node['transform_type'] if transform_type == 'to_direction_cosines': return ToDirectionCosines() @@ -25,8 +23,8 @@ def from_tree_transform(cls, node, ctx): else: raise TypeError(f"Unknown model_type {transform_type}") - @classmethod - def to_tree_transform(cls, model, ctx): + def to_yaml_tree_transform(self, model, tag, ctx): + from ..geometry import ToDirectionCosines, FromDirectionCosines if isinstance(model, FromDirectionCosines): transform_type = 'from_direction_cosines' elif isinstance(model, ToDirectionCosines): @@ -34,16 +32,16 @@ def to_tree_transform(cls, model, ctx): else: raise TypeError(f"Model of type {model.__class__} is not supported.") node = {'transform_type': transform_type} - return yamlutil.custom_tree_to_tagged_tree(node, ctx) + return node -class SphericalCartesianType(GWCSTransformType): - name = "spherical_cartesian" - types = [SphericalToCartesian, CartesianToSpherical] - version = "1.1.0" +class SphericalCartesianConverter(TransformConverterBase): + tags = ["tag:stsci.edu:gwcs/spherical_cartesian-*"] + types = ["gwcs.geometry.SphericalToCartesian", + "gwcs.geometry.CartesianToSpherical"] - @classmethod - def from_tree_transform(cls, node, ctx): + def from_yaml_tree_transform(self, node, tag, ctx): + from ..geometry import SphericalToCartesian, CartesianToSpherical transform_type = node['transform_type'] wrap_lon_at = node['wrap_lon_at'] if transform_type == 'spherical_to_cartesian': @@ -53,8 +51,8 @@ def from_tree_transform(cls, node, ctx): else: raise TypeError(f"Unknown model_type {transform_type}") - @classmethod - def to_tree_transform(cls, model, ctx): + def to_yaml_tree_transform(self, model, tag, ctx): + from ..geometry import SphericalToCartesian, CartesianToSpherical if isinstance(model, SphericalToCartesian): transform_type = 'spherical_to_cartesian' elif isinstance(model, CartesianToSpherical): @@ -66,4 +64,4 @@ def to_tree_transform(cls, model, ctx): 'transform_type': transform_type, 'wrap_lon_at': model.wrap_lon_at } - return yamlutil.custom_tree_to_tagged_tree(node, ctx) + return node diff --git a/gwcs/tags/selectortags.py b/gwcs/converters/selector.py similarity index 62% rename from gwcs/tags/selectortags.py rename to gwcs/converters/selector.py index 2c472909..104a6f9a 100644 --- a/gwcs/tags/selectortags.py +++ b/gwcs/converters/selector.py @@ -3,35 +3,31 @@ from collections import OrderedDict import numpy as np -from numpy.testing import assert_array_equal from astropy.modeling import models from astropy.modeling.core import Model from astropy.utils.misc import isiterable -from asdf import yamlutil from asdf.tags.core.ndarray import NDArrayType -from ..gwcs_types import GWCSTransformType +from asdf_astropy.converters.transform.core import TransformConverterBase -from ..selector import * +__all__ = ['LabelMapperConverter', 'RegionsSelectorConverter'] -__all__ = ['LabelMapperType', 'RegionsSelectorType'] +class LabelMapperConverter(TransformConverterBase): + tags = ["tag:stsci.edu:gwcs/label_mapper-*"] + types = ["gwcs.selector.LabelMapperArray", "gwcs.selector.LabelMapperDict", + "gwcs.selector.LabelMapperRange", "gwcs.selector.LabelMapper"] - -class LabelMapperType(GWCSTransformType): - name = "label_mapper" - types = [LabelMapperArray, LabelMapperDict, LabelMapperRange, LabelMapper] - version = "1.1.0" - - @classmethod - def from_tree_transform(cls, node, ctx): + def from_yaml_tree_transform(self, node, tag, ctx): + from ..selector import (LabelMapperArray, LabelMapperDict, + LabelMapperRange, LabelMapper) inputs_mapping = node.get('inputs_mapping', None) if inputs_mapping is not None and not isinstance(inputs_mapping, models.Mapping): raise TypeError("inputs_mapping must be an instance" "of astropy.modeling.models.Mapping.") mapper = node['mapper'] - atol = node.get('atol', 10**-8) + atol = node.get('atol', 10e-8) no_label = node.get('no_label', np.nan) if isinstance(mapper, NDArrayType): @@ -56,8 +52,9 @@ def from_tree_transform(cls, node, ctx): dict_mapper = dict(zip(labels, transforms)) return LabelMapperDict(inputs, dict_mapper, inputs_mapping, atol=atol) - @classmethod - def to_tree_transform(cls, model, ctx): + def to_yaml_tree_transform(self, model, tag, ctx): + from ..selector import (LabelMapperArray, LabelMapperDict, + LabelMapperRange, LabelMapper) node = OrderedDict() node['no_label'] = model.no_label if model.inputs_mapping is not None: @@ -86,31 +83,15 @@ def to_tree_transform(cls, model, ctx): else: raise TypeError("Unrecognized type of LabelMapper - {0}".format(model)) - return yamlutil.custom_tree_to_tagged_tree(node, ctx) - - @classmethod - def assert_equal(cls, a, b): - # TODO: If models become comparable themselves, remove this. - assert (a.__class__ == b.__class__) # nosec - if isinstance(a.mapper, dict): - assert(a.mapper.__class__ == b.mapper.__class__) # nosec - assert(all(np.in1d(list(a.mapper), list(b.mapper)))) # nosec - for k in a.mapper: - assert (a.mapper[k].__class__ == b.mapper[k].__class__) # nosec - assert(all(a.mapper[k].parameters == b.mapper[k].parameters)) # nosec - assert (a.inputs == b.inputs) # nosec - assert (a.inputs_mapping.mapping == b.inputs_mapping.mapping) # nosec - else: - assert_array_equal(a.mapper, b.mapper) + return node -class RegionsSelectorType(GWCSTransformType): - name = "regions_selector" - types = [RegionsSelector] - version = "1.1.0" +class RegionsSelectorConverter(TransformConverterBase): + tags = ["tag:stsci.edu:gwcs/regions_selector-*"] + types = ["gwcs.selector.RegionsSelector"] - @classmethod - def from_tree_transform(cls, node, ctx): + def from_yaml_tree_transform(self, node, tag, ctx): + from ..selector import RegionsSelector inputs = node['inputs'] outputs = node['outputs'] label_mapper = node['label_mapper'] @@ -120,8 +101,7 @@ def from_tree_transform(cls, node, ctx): return RegionsSelector(inputs, outputs, sel, label_mapper, undefined_transform_value) - @classmethod - def to_tree_transform(cls, model, ctx): + def to_yaml_tree_transform(self, model, tag, ctx): selector = OrderedDict() node = OrderedDict() labels = list(model.selector) @@ -135,16 +115,4 @@ def to_tree_transform(cls, model, ctx): node['selector'] = selector node['label_mapper'] = model.label_mapper node['undefined_transform_value'] = model.undefined_transform_value - return yamlutil.custom_tree_to_tagged_tree(node, ctx) - - @classmethod - def assert_equal(cls, a, b): - # TODO: If models become comparable themselves, remove this. - assert (a.__class__ == b.__class__) # nosec - LabelMapperType.assert_equal(a.label_mapper, b.label_mapper) - assert_array_equal(a.inputs, b.inputs) - assert_array_equal(a.outputs, b.outputs) - assert_array_equal(a.selector.keys(), b.selector.keys()) - for key in a.selector: - assert_array_equal(a.selector[key].parameters, b.selector[key].parameters) - assert_array_equal(a.undefined_transform_value, b.undefined_transform_value) + return node diff --git a/gwcs/converters/spectroscopy.py b/gwcs/converters/spectroscopy.py new file mode 100644 index 00000000..8b762503 --- /dev/null +++ b/gwcs/converters/spectroscopy.py @@ -0,0 +1,104 @@ +""" +ASDF tags for spectroscopy related models. + +""" +from astropy import units as u +from asdf_astropy.converters.transform.core import ( + TransformConverterBase, parameter_to_value +) + + +__all__ = ['GratingEquationConverter', 'SellmeierGlassConverter', + 'SellmeierZemaxConverter', 'Snell3DConverter'] + + +class SellmeierGlassConverter(TransformConverterBase): + tags = ["tag:stsci.edu:gwcs/sellmeier_glass-*"] + types = ["gwcs.spectroscopy.SellmeierGlass"] + + def from_yaml_tree_transform(self, node, tag, ctx): + from ..spectroscopy import SellmeierGlass + return SellmeierGlass(node['B_coef'], node['C_coef']) + + def to_yaml_tree_transform(self, model, tag, ctx): + node = {'B_coef': parameter_to_value(model.B_coef), + 'C_coef': parameter_to_value(model.C_coef)} + return node + + +class SellmeierZemaxConverter(TransformConverterBase): + tags = ["tag:stsci.edu:gwcs/sellmeier_zemax-*"] + types = ["gwcs.spectroscopy.SellmeierZemax"] + + def from_yaml_tree_transform(self, node, tag, ctx): + from ..spectroscopy import SellmeierZemax + return SellmeierZemax(node['temperature'], node['ref_temperature'], + node['ref_pressure'], node['pressure'], + node['B_coef'], node['C_coef'], node['D_coef'], + node['E_coef']) + + def to_yaml_tree_transform(self, model, tag, ctx): + node = {'B_coef': parameter_to_value(model.B_coef), + 'C_coef': parameter_to_value(model.C_coef), + 'D_coef': parameter_to_value(model.D_coef), + 'E_coef': parameter_to_value(model.E_coef), + 'temperature': parameter_to_value(model.temperature), + 'ref_temperature': parameter_to_value(model.ref_temperature), + 'pressure': parameter_to_value(model.pressure), + 'ref_pressure': parameter_to_value(model.ref_pressure)} + return node + + +class Snell3DConverter(TransformConverterBase): + tags = ["tag:stsci.edu:gwcs/snell3d-*"] + types = ["gwcs.spectroscopy.Snell3D"] + + def from_yaml_tree_transform(self, node, tag, ctx): + from ..spectroscopy import Snell3D + return Snell3D() + + def to_yaml_tree_transform(self, model, tag, ctx): + return {} + + +class GratingEquationConverter(TransformConverterBase): + tags = ["tag:stsci.edu:gwcs/grating_equation-*"] + types = ["gwcs.spectroscopy.AnglesFromGratingEquation3D", + "gwcs.spectroscopy.WavelengthFromGratingEquation"] + + def from_yaml_tree_transform(self, node, tag, ctx): + from ..spectroscopy import (AnglesFromGratingEquation3D, + WavelengthFromGratingEquation) + groove_density = node['groove_density'] + order = node['order'] + output = node['output'] + if output == "wavelength": + model = WavelengthFromGratingEquation(groove_density=groove_density, + spectral_order=order) + elif output == "angle": + model = AnglesFromGratingEquation3D(groove_density=groove_density, + spectral_order=order) + else: + raise ValueError("Can't create a GratingEquation model with " + "output {0}".format(output)) + return model + + def to_yaml_tree_transform(self, model, tag, ctx): + from ..spectroscopy import (AnglesFromGratingEquation3D, + WavelengthFromGratingEquation) + if model.groove_density.unit is not None: + groove_density = u.Quantity(model.groove_density.value, + unit=model.groove_density.unit) + else: + groove_density = model.groove_density.value + node = {'order': model.spectral_order.value, + 'groove_density': groove_density + } + if isinstance(model, AnglesFromGratingEquation3D): + node['output'] = 'angle' + elif isinstance(model, WavelengthFromGratingEquation): + node['output'] = 'wavelength' + else: + raise TypeError("Can't serialize an instance of {0}" + .format(model.__class__.__name__)) + return node diff --git a/gwcs/schemas/__init__.py b/gwcs/converters/tests/__init__.py similarity index 100% rename from gwcs/schemas/__init__.py rename to gwcs/converters/tests/__init__.py diff --git a/gwcs/converters/tests/test_selector.py b/gwcs/converters/tests/test_selector.py new file mode 100644 index 00000000..a48b4a96 --- /dev/null +++ b/gwcs/converters/tests/test_selector.py @@ -0,0 +1,126 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +# -*- coding: utf-8 -*- +import numpy as np +from numpy.testing import assert_array_equal +from astropy.modeling.models import Mapping, Shift, Scale, Polynomial2D +import asdf + +from ...tests.test_region import create_scalar_mapper +from ... import selector + + +def _assert_mapper_equal(a, b): + __tracebackhide__ = True + + if a is None: + return + + assert type(a) is type(b) + + if isinstance(a.mapper, dict): + assert(a.mapper.__class__ == b.mapper.__class__) # nosec + assert(all(np.in1d(list(a.mapper), list(b.mapper)))) # nosec + for k in a.mapper: + assert (a.mapper[k].__class__ == b.mapper[k].__class__) # nosec + assert(all(a.mapper[k].parameters == b.mapper[k].parameters)) # nosec + assert (a.inputs == b.inputs) # nosec + assert (a.inputs_mapping.mapping == b.inputs_mapping.mapping) # nosec + else: + assert_array_equal(a.mapper, b.mapper) + + +def _assert_selector_equal(a, b): + __tracebackhide__ = True + + if a is None: + return + + if isinstance(a, selector.RegionsSelector): + assert type(a) is type(b) + _assert_mapper_equal(a.label_mapper, b.label_mapper) + assert_array_equal(a.inputs, b.inputs) + assert_array_equal(a.outputs, b.outputs) + assert_array_equal(a.selector.keys(), b.selector.keys()) + for key in a.selector: + assert_array_equal(a.selector[key].parameters, b.selector[key].parameters) + assert_array_equal(a.undefined_transform_value, b.undefined_transform_value) + + +def assert_selector_roundtrip(s, tmpdir, version=None): + """ + Assert that a selector can be written to an ASDF file and read back + in without losing any of its essential properties. + """ + path = str(tmpdir / "test.asdf") + + with asdf.AsdfFile({"selector": s}, version=version) as af: + af.write_to(path) + + with asdf.open(path) as af: + rs = af["selector"] + if isinstance(s, selector.RegionsSelector): + _assert_selector_equal(s, rs) + elif isinstance(s, selector._LabelMapper): + _assert_mapper_equal(s, rs) + else: + assert False + + +def test_regions_selector(tmpdir): + m1 = Mapping([0, 1, 1]) | Shift(1) & Shift(2) & Shift(3) + m2 = Mapping([0, 1, 1]) | Scale(2) & Scale(3) & Scale(3) + sel = {1: m1, 2: m2} + a = np.zeros((5, 6), dtype=np.int32) + a[:, 1:3] = 1 + a[:, 4:5] = 2 + mask = selector.LabelMapperArray(a) + rs = selector.RegionsSelector(inputs=('x', 'y'), outputs=('ra', 'dec', 'lam'), + selector=sel, label_mapper=mask) + assert_selector_roundtrip(rs, tmpdir) + + +def test_LabelMapperArray_str(tmpdir): + a = np.array([["label1", "", "label2"], + ["label1", "", ""], + ["label1", "label2", "label2"]]) + mask = selector.LabelMapperArray(a) + assert_selector_roundtrip(mask, tmpdir) + + +def test_labelMapperArray_int(tmpdir): + + a = np.array([[1, 0, 2], + [1, 0, 0], + [1, 2, 2]]) + mask = selector.LabelMapperArray(a) + assert_selector_roundtrip(mask, tmpdir) + + +def test_LabelMapperDict(tmpdir): + dmapper = create_scalar_mapper() + sel = selector.LabelMapperDict(('x', 'y'), dmapper, + inputs_mapping=Mapping((0,), n_inputs=2), atol=1e-3) + assert_selector_roundtrip(sel, tmpdir) + + +def test_LabelMapperRange(tmpdir): + m = [] + for i in np.arange(9) * .1: + c0_0, c1_0, c0_1, c1_1 = np.ones((4,)) * i + m.append(Polynomial2D(2, c0_0=c0_0, + c1_0=c1_0, c0_1=c0_1, c1_1=c1_1)) + keys = np.array([[4.88, 5.64], + [5.75, 6.5], + [6.67, 7.47], + [7.7, 8.63], + [8.83, 9.96], + [10.19, 11.49], + [11.77, 13.28], + [13.33, 15.34], + [15.56, 18.09]]) + rmapper = {} + for k, v in zip(keys, m): + rmapper[tuple(k)] = v + sel = selector.LabelMapperRange(('x', 'y'), rmapper, + inputs_mapping=Mapping((0,), n_inputs=2)) + assert_selector_roundtrip(sel, tmpdir) diff --git a/gwcs/tags/tests/test_transforms.py b/gwcs/converters/tests/test_transforms.py similarity index 90% rename from gwcs/tags/tests/test_transforms.py rename to gwcs/converters/tests/test_transforms.py index 49a0c9b9..b550b590 100644 --- a/gwcs/tags/tests/test_transforms.py +++ b/gwcs/converters/tests/test_transforms.py @@ -2,11 +2,10 @@ # -*- coding: utf-8 -*- import pytest -import numpy as np - from astropy.modeling.models import Identity from astropy import units as u -from asdf.tests import helpers +from asdf_astropy.converters.transform.tests.test_transform import ( + assert_model_roundtrip) from ... import spectroscopy as sp from ... import geometry @@ -39,5 +38,4 @@ @pytest.mark.parametrize(('model'), transforms) def test_transforms(tmpdir, model): - tree = {'model': model} - helpers.assert_roundtrip_tree(tree, tmpdir) + assert_model_roundtrip(model, tmpdir) diff --git a/gwcs/tags/tests/test_wcs.py b/gwcs/converters/tests/test_wcs.py similarity index 65% rename from gwcs/tags/tests/test_wcs.py rename to gwcs/converters/tests/test_wcs.py index d41d07d1..cf1be614 100644 --- a/gwcs/tags/tests/test_wcs.py +++ b/gwcs/converters/tests/test_wcs.py @@ -6,34 +6,80 @@ astropy = pytest.importorskip('astropy', minversion='3.0') -from astropy.modeling import models -from astropy import coordinates as coord -from astropy import units as u -from astropy import time +from astropy.modeling import models # noqa: E402 +from astropy import coordinates as coord # noqa: E402 +from astropy import units as u # noqa: E402 +from astropy import time # noqa: E402 -import asdf -from asdf.tests import helpers +import asdf # noqa: E402 +from asdf_astropy.converters.transform.tests.test_transform import ( # noqa: E402 + assert_models_equal) -from ... import coordinate_frames as cf -from ... import wcs +from ... import coordinate_frames as cf # noqa: E402 +from ... import wcs # noqa: E402 + + +def _assert_frame_equal(a, b): + __tracebackhide__ = True + + assert type(a) is type(b) + + if a is None: + return + + if not isinstance(a, cf.CoordinateFrame): + return a == b + + assert a.name == b.name # nosec + assert a.axes_order == b.axes_order # nosec + assert a.axes_names == b.axes_names # nosec + assert a.unit == b.unit # nosec + assert a.reference_frame == b.reference_frame # nosec + + +def assert_frame_roundtrip(frame, tmpdir, version=None): + """ + Assert that a frame can be written to an ASDF file and read back + in without losing any of its essential properties. + """ + path = str(tmpdir / "test.asdf") + + with asdf.AsdfFile({"frame": frame}, version=version) as af: + af.write_to(path) + + with asdf.open(path) as af: + _assert_frame_equal(frame, af["frame"]) + + +def _assert_wcs_equal(a, b): + assert a.name == b.name # nosec + assert len(a.available_frames) == len(b.available_frames) # nosec + for a_step, b_step in zip(a.pipeline, b.pipeline): + _assert_frame_equal(a_step.frame, b_step.frame) + assert_models_equal(a_step.transform, b_step.transform) + + +def assert_wcs_roundtrip(wcs, tmpdir, version=None): + path = str(tmpdir / "test.asdf") + + with asdf.AsdfFile({"wcs": wcs}, version=version) as af: + af.write_to(path) + + with asdf.open(path) as af: + _assert_wcs_equal(wcs, af["wcs"]) def test_create_wcs(tmpdir): m1 = models.Shift(12.4) & models.Shift(-2) - m2 = models.Scale(2) & models.Scale(-2) icrs = cf.CelestialFrame(name='icrs', reference_frame=coord.ICRS()) det = cf.Frame2D(name='detector', axes_order=(0, 1)) gw1 = wcs.WCS(output_frame='icrs', input_frame='detector', forward_transform=m1) gw2 = wcs.WCS(output_frame='icrs', forward_transform=m1) gw3 = wcs.WCS(output_frame=icrs, input_frame=det, forward_transform=m1) - tree = { - 'gw1': gw1, - 'gw2': gw2, - 'gw3': gw3 - } - - helpers.assert_roundtrip_tree(tree, tmpdir) + assert_wcs_roundtrip(gw1, tmpdir) + assert_wcs_roundtrip(gw2, tmpdir) + assert_wcs_roundtrip(gw3, tmpdir) def test_composite_frame(tmpdir): @@ -49,13 +95,10 @@ def test_composite_frame(tmpdir): comp2 = cf.CompositeFrame([cel2, spec2]) comp = cf.CompositeFrame([comp1, cf.SpectralFrame(axes_order=(3, ), unit=(u.m, ))]) - tree = { - 'comp1': comp1, - 'comp2': comp2, - 'comp': comp - } + assert_frame_roundtrip(comp, tmpdir) + assert_frame_roundtrip(comp1, tmpdir) + assert_frame_roundtrip(comp2, tmpdir) - helpers.assert_roundtrip_tree(tree, tmpdir) def create_test_frames(): """Creates an array of frames to be used for testing.""" @@ -124,12 +167,9 @@ def create_test_frames(): def test_frames(tmpdir): - - tree = { - 'frames': create_test_frames() - } - - helpers.assert_roundtrip_tree(tree, tmpdir) + frames = create_test_frames() + for f in frames: + assert_frame_roundtrip(f, tmpdir) def test_references(tmpdir): diff --git a/gwcs/converters/wcs.py b/gwcs/converters/wcs.py new file mode 100644 index 00000000..b12d3c5c --- /dev/null +++ b/gwcs/converters/wcs.py @@ -0,0 +1,210 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +# -*- coding: utf-8 -*- + +from asdf.extension import Converter + + +_REQUIRES = ['astropy'] + + +__all__ = ["WCSConverter", "CelestialFrameConverter", "CompositeFrameConverter", + "FrameConverter", "SpectralFrameConverter", "StepConverter", + "TemporalFrameConverter", "StokesFrameConverter"] + + +class WCSConverter(Converter): + tags = ["tag:stsci.edu:gwcs/wcs-*"] + types = ["gwcs.wcs.WCS"] + + def from_yaml_tree(self, node, tag, ctx): + from ..wcs import WCS + name = node['name'] + steps = [(x.frame, x.transform) for x in node['steps']] + return WCS(steps, name=name) + + def to_yaml_tree(self, gwcsobj, tag, ctx): + return { + 'name': gwcsobj.name, + 'steps': gwcsobj.pipeline + } + + +class StepConverter(Converter): + tags = ["tag:stsci.edu:gwcs/step-*"] + types = ["gwcs.wcs.Step"] + + def from_yaml_tree(self, node, tag, ctx): + from ..wcs import Step + return Step(frame=node['frame'], transform=node.get('transform', None)) + + def to_yaml_tree(self, step, tag, ctx): + return { + 'frame': step.frame, + 'transform': step.transform + } + + +class FrameConverter(Converter): + tags = ["tag:stsci.edu:gwcs/frame-*"] + types = ["gwcs.coordinate_frames.CoordinateFrame"] + + def _from_yaml_tree(self, node, tag, ctx): + kwargs = {'name': node['name']} + + if 'axes_type' in node and 'naxes' in node: + kwargs.update({ + 'axes_type': node['axes_type'], + 'naxes': node['naxes']}) + + if 'axes_names' in node: + kwargs['axes_names'] = node['axes_names'] + + if 'reference_frame' in node: + kwargs['reference_frame'] = node['reference_frame'] + + if 'axes_order' in node: + kwargs['axes_order'] = tuple(node['axes_order']) + + if 'unit' in node: + kwargs['unit'] = tuple(node['unit']) + + if 'axis_physical_types' in node: + kwargs['axis_physical_types'] = tuple(node['axis_physical_types']) + + return kwargs + + def _to_yaml_tree(self, frame, tag, ctx): + from ..coordinate_frames import CoordinateFrame + + node = {} + + node['name'] = frame.name + + # We want to check that it is exactly this type and not a subclass + if type(frame) is CoordinateFrame: + node['axes_type'] = frame.axes_type + node['naxes'] = frame.naxes + + if frame.axes_order is not None: + node['axes_order'] = list(frame.axes_order) + + if frame.axes_names is not None: + node['axes_names'] = list(frame.axes_names) + + if frame.reference_frame is not None: + node['reference_frame'] = frame.reference_frame + + if frame.unit is not None: + node['unit'] = list(frame.unit) + + if frame.axis_physical_types is not None: + node['axis_physical_types'] = list(frame.axis_physical_types) + + return node + + def from_yaml_tree(self, node, tag, ctx): + from ..coordinate_frames import CoordinateFrame + node = self._from_yaml_tree(node, tag, ctx) + return CoordinateFrame(**node) + + def to_yaml_tree(self, frame, tag, ctx): + return self._to_yaml_tree(frame, tag, ctx) + + +class Frame2DConverter(FrameConverter): + tags = ["tag:stsci.edu:gwcs/frame2d-*"] + types = ["gwcs.coordinate_frames.Frame2D"] + + def from_yaml_tree(self, node, tag, ctx): + from ..coordinate_frames import Frame2D + node = self._from_yaml_tree(node, tag, ctx) + return Frame2D(**node) + + +class CelestialFrameConverter(FrameConverter): + tags = ["tag:stsci.edu:gwcs/celestial_frame-*"] + types = ["gwcs.coordinate_frames.CelestialFrame"] + + def from_yaml_tree(self, node, tag, ctx): + from ..coordinate_frames import CelestialFrame + node = self._from_yaml_tree(node, tag, ctx) + return CelestialFrame(**node) + + def to_yaml_tree(self, frame, tag, ctx): + return self._to_yaml_tree(frame, tag, ctx) + + +class SpectralFrameConverter(FrameConverter): + tags = ["tag:stsci.edu:gwcs/spectral_frame-*"] + types = ["gwcs.coordinate_frames.SpectralFrame"] + + 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-*"] + types = ["gwcs.coordinate_frames.CompositeFrame"] + + def from_yaml_tree(self, node, tag, ctx): + from ..coordinate_frames import CompositeFrame + if len(node) != 2: + raise ValueError("CompositeFrame has extra properties") + + name = node['name'] + frames = node['frames'] + + return CompositeFrame(frames, name) + + def to_yaml_tree(self, frame, tag, ctx): + return { + 'name': frame.name, + 'frames': frame.frames + } + + +class TemporalFrameConverter(FrameConverter): + tags = ["tag:stsci.edu:gwcs/temporal_frame-*"] + types = ["gwcs.coordinate_frames.TemporalFrame"] + + + def from_yaml_tree(self, node, tag, ctx): + from ..coordinate_frames import TemporalFrame + node = self._from_yaml_tree(node, tag, ctx) + return TemporalFrame(**node) + + def to_yaml_tree(self, frame, tag, ctx): + return self._to_yaml_tree(frame, tag, ctx) + + +class StokesFrameConverter(FrameConverter): + tags = ["tag:stsci.edu:gwcs/stokes_frame-*"] + types = ["gwcs.coordinate_frames.StokesFrame"] + + def from_yaml_tree(self, node, tag, ctx): + from ..coordinate_frames import StokesFrame + node = self._from_yaml_tree(node, tag, ctx) + return StokesFrame(**node) + + def to_yaml_tree(self, frame, tag, ctx): + node = {} + + node['name'] = frame.name + if frame.axes_order: + node['axes_order'] = list(frame.axes_order) + + return node diff --git a/gwcs/extension.py b/gwcs/extension.py index 7ddabdea..64d42cbc 100644 --- a/gwcs/extension.py +++ b/gwcs/extension.py @@ -1,34 +1,64 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -import os -from asdf import util -from asdf.extension import BuiltinExtension -from .tags.wcs import * # noqa -from .tags.selectortags import * # noqa -from .tags.spectroscopy_models import * # noqa -from .tags.geometry_models import * # noqa +from asdf.extension import ManifestExtension +from .converters.wcs import ( + CelestialFrameConverter, CompositeFrameConverter, FrameConverter, + Frame2DConverter, SpectralFrameConverter, StepConverter, + StokesFrameConverter, TemporalFrameConverter, WCSConverter, +) +from .converters.selector import ( + LabelMapperConverter, RegionsSelectorConverter +) +from .converters.spectroscopy import ( + GratingEquationConverter, SellmeierGlassConverter, SellmeierZemaxConverter, + Snell3DConverter +) +from .converters.geometry import ( + DirectionCosinesConverter, SphericalCartesianConverter +) -# Make sure that all tag implementations are imported by the time we create -# the extension class so that _gwcs_types is populated correctly. -from .tags import * # noqa -from .gwcs_types import _gwcs_types +WCS_MODEL_CONVERTERS = [ + CelestialFrameConverter(), + CompositeFrameConverter(), + FrameConverter(), + Frame2DConverter(), + SpectralFrameConverter(), + StepConverter(), + StokesFrameConverter(), + TemporalFrameConverter(), + WCSConverter(), + LabelMapperConverter(), + RegionsSelectorConverter(), + GratingEquationConverter(), + SellmeierGlassConverter(), + SellmeierZemaxConverter(), + Snell3DConverter(), + DirectionCosinesConverter(), + SphericalCartesianConverter(), +] -SCHEMA_PATH = os.path.abspath( - os.path.join(os.path.dirname(__file__), 'schemas')) +# The order here is important; asdf will prefer to use extensions +# that occur earlier in the list. +WCS_MANIFEST_URIS = [ + "asdf://stsci.edu/schemas/gwcs/manifests/gwcs-schemas-1.0", +] -class GWCSExtension(BuiltinExtension): - @property - def types(self): - return _gwcs_types +TRANSFORM_EXTENSIONS = [ + ManifestExtension.from_uri( + uri, + legacy_class_names=["gwcs.extension.GWCSExtension"], + converters=WCS_MODEL_CONVERTERS, + ) + for uri in WCS_MANIFEST_URIS +] - @property - def tag_mapping(self): - return [('tag:stsci.edu:gwcs', - 'http://stsci.edu/schemas/gwcs{tag_suffix}')] - - @property - def url_mapping(self): - return [('http://stsci.edu/schemas/gwcs', - util.filepath_to_url(os.path.join(SCHEMA_PATH, "stsci.edu")) + - '/gwcs{url_suffix}.yaml')] +def get_extensions(): + """ + Get the gwcs.converters extension. + This method is registered with the asdf.extensions entry point. + Returns + ------- + list of asdf.extension.Extension + """ + return TRANSFORM_EXTENSIONS diff --git a/gwcs/geometry.py b/gwcs/geometry.py index 48f41594..0cd61f3b 100644 --- a/gwcs/geometry.py +++ b/gwcs/geometry.py @@ -53,7 +53,6 @@ def __init__(self, **kwargs): self.outputs = ('x', 'y', 'z') def evaluate(self, cosa, cosb, cosc, length): - return cosa * length, cosb * length, cosc * length def inverse(self): diff --git a/gwcs/schemas/stsci.edu/gwcs/celestial_frame-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/celestial_frame-1.0.0.yaml deleted file mode 100644 index 6a907929..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/celestial_frame-1.0.0.yaml +++ /dev/null @@ -1,25 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/celestial_frame-1.0.0" -tag: "tag:stsci.edu:gwcs/celestial_frame-1.0.0" - -title: > - Represents a celestial frame. - -allOf: - - type: object - properties: - axes_names: - minItems: 2 - maxItems: 3 - - axes_order: - minItems: 2 - maxItems: 3 - - unit: - minItems: 2 - maxItems: 3 - - - $ref: frame-1.0.0 diff --git a/gwcs/schemas/stsci.edu/gwcs/composite_frame-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/composite_frame-1.0.0.yaml deleted file mode 100644 index 94a63717..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/composite_frame-1.0.0.yaml +++ /dev/null @@ -1,20 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/composite_frame-1.0.0" -tag: "tag:stsci.edu:gwcs/composite_frame-1.0.0" - -title: > - Represents a set of frames. -allOf: - - type: object - properties: - name: - description: - Name of composite frame. - type: string - - frames: - description: - List of frames in the composite frame. - type: array diff --git a/gwcs/schemas/stsci.edu/gwcs/direction_cosines-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/direction_cosines-1.0.0.yaml deleted file mode 100644 index 22e663d8..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/direction_cosines-1.0.0.yaml +++ /dev/null @@ -1,38 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/direction_cosines-1.0.0" -tag: "tag:stsci.edu:gwcs/direction_cosines-1.0.0" - -title: > - Convert coordinates between vector and direction cosine form. - -description: | - This schema is for transforms which convert to and from direction cosines. - -examples: - - - - Convert direction cosines to vectors. - - - | - - ! - transform_type: from_direction_cosines - - - - - Convert vectors to directional cosines. - - - | - ! - transform_type: to_direction_cosines - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - - object: - properties: - transform_type: - description: | - The type of transform/class to initialize. - type: string - enum: [to_direction_cosines, from_direction_cosines] - required: [transform_type] diff --git a/gwcs/schemas/stsci.edu/gwcs/direction_cosines-1.1.0.yaml b/gwcs/schemas/stsci.edu/gwcs/direction_cosines-1.1.0.yaml deleted file mode 100644 index 26f50a25..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/direction_cosines-1.1.0.yaml +++ /dev/null @@ -1,38 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/direction_cosines-1.1.0" -tag: "tag:stsci.edu:gwcs/direction_cosines-1.1.0" - -title: > - Convert coordinates between vector and direction cosine form. - -description: | - This schema is for transforms which convert to and from direction cosines. - -examples: - - - - Convert direction cosines to vectors. - - - | - - ! - transform_type: from_direction_cosines - - - - - Convert vectors to directional cosines. - - - | - ! - transform_type: to_direction_cosines - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - - object: - properties: - transform_type: - description: | - The type of transform/class to initialize. - type: string - enum: [to_direction_cosines, from_direction_cosines] - required: [transform_type] diff --git a/gwcs/schemas/stsci.edu/gwcs/frame-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/frame-1.0.0.yaml deleted file mode 100644 index 2544dcd9..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/frame-1.0.0.yaml +++ /dev/null @@ -1,82 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/frame-1.0.0" -title: | - The base class of all coordinate frames. - -description: | - These objects are designed to be nested in arbitrary ways to build up - transformation pipelines out of a number of low-level pieces. - -examples: - - - - | - A celestial frame in the ICRS reference frame. - - | - ! - axes_names: [lon, lat] - name: CelestialFrame - reference_frame: ! - frame_attributes: {} - unit: [!unit/unit-1.0.0 deg, !unit/unit-1.0.0 deg] - - - - - | - A pixel frame in three dimensions - - | - ! - axes_names: [raster position, slit position, wavelength] - axes_order: [0, 1, 2] - axes_type: [SPATIAL, SPATIAL, SPECTRAL] - name: pixel - naxes: 3 - unit: [!unit/unit-1.0.0 pixel, !unit/unit-1.0.0 pixel, !unit/unit-1.0.0 pixel] - -type: object -properties: - name: - description: | - A user-friendly name for the frame. - type: string - - axes_order: - description: | - The order of the axes. - type: array - items: - type: integer - - axes_names: - description: | - The name of each axis in this frame. - type: array - items: - anyOf: - - type: string - - type: 'null' - - reference_frame: - description: | - The reference frame. - $ref: "tag:astropy.org:astropy/coordinates/frames/baseframe-1.0.0" - - unit: - description: | - Units for each axis. - type: array - items: - $ref: "tag:stsci.edu:asdf/unit/unit-1.0.0" - - axis_physical_types: - description: | - An iterable of strings describing the physical type for each world axis. - These should be names from the VO UCD1+ controlled Vocabulary - (http://www.ivoa.net/documents/latest/UCDlist.html). - type: array - items: - type: - string - -required: [name] -additionalProperties: true diff --git a/gwcs/schemas/stsci.edu/gwcs/frame2d-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/frame2d-1.0.0.yaml deleted file mode 100644 index a351e181..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/frame2d-1.0.0.yaml +++ /dev/null @@ -1,36 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/frame2d-1.0.0" -tag: "tag:stsci.edu:gwcs/frame2d-1.0.0" - -title: > - Represents a 2D frame. - -examples: - - - - | - A two dimensional spatial frame - - | - ! - axes_names: [lon, lat] - name: Frame2D - unit: [!unit/unit-1.0.0 pixel, !unit/unit-1.0.0 pixel] - - -allOf: - - type: object - properties: - axes_names: - minItems: 2 - maxItems: 2 - - axes_order: - minItems: 2 - maxItems: 2 - - unit: - minItems: 2 - maxItems: 2 - - - $ref: frame-1.0.0 diff --git a/gwcs/schemas/stsci.edu/gwcs/grating_equation-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/grating_equation-1.0.0.yaml deleted file mode 100644 index 8c46892f..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/grating_equation-1.0.0.yaml +++ /dev/null @@ -1,52 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/grating_equation-1.0.0" -tag: "tag:stsci.edu:gwcs/grating_equation-1.0.0" - -title: > - A grating equation model. -description: | - Supports two models: - - Given incident angle and wavelength compute the refraction/difraction angle. - - Given an incident angle and a refraction angle compute the wavelength. - -examples: - - - - AnglesFromGratingEquation3D model. - - - | - ! - groove_density: 2700.0 - order: 2.0 - output: angle - - - - - WavelengthFromGratingEquation model. - - - | - ! - groove_density: 2700.0 - order: 2.0 - output: wavelength - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - - type: object - properties: - groove_density: - description: | - The groove density of the grating - anyOf: - - type: number - - $ref: "tag:stsci.edu:asdf/unit/quantity-1.1.0" - order: - description: | - Spectral order - type: number - output: - type: string - description: | - indicates which quantity the grating equation is solved for. - enum: [wavelength, angle] - required: [groove_density, order, output] diff --git a/gwcs/schemas/stsci.edu/gwcs/grating_equation-1.1.0.yaml b/gwcs/schemas/stsci.edu/gwcs/grating_equation-1.1.0.yaml deleted file mode 100644 index 3bc22e43..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/grating_equation-1.1.0.yaml +++ /dev/null @@ -1,52 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/grating_equation-1.1.0" -tag: "tag:stsci.edu:gwcs/grating_equation-1.1.0" - -title: > - A grating equation model. -description: | - Supports two models: - - Given incident angle and wavelength compute the refraction/difraction angle. - - Given an incident angle and a refraction angle compute the wavelength. - -examples: - - - - AnglesFromGratingEquation3D model. - - - | - ! - groove_density: 2700.0 - order: 2.0 - output: angle - - - - - WavelengthFromGratingEquation model. - - - | - ! - groove_density: 2700.0 - order: 2.0 - output: wavelength - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - - type: object - properties: - groove_density: - description: | - The groove density of the grating - anyOf: - - type: number - - $ref: "tag:stsci.edu:asdf/unit/quantity-1.1.0" - order: - description: | - Spectral order - type: number - output: - type: string - description: | - indicates which quantity the grating equation is solved for. - enum: [wavelength, angle] - required: [groove_density, order, output] diff --git a/gwcs/schemas/stsci.edu/gwcs/label_mapper-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/label_mapper-1.0.0.yaml deleted file mode 100644 index e106f2b4..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/label_mapper-1.0.0.yaml +++ /dev/null @@ -1,130 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/label_mapper-1.0.0" -tag: "tag:stsci.edu:gwcs/label_mapper-1.0.0" -title: > - Represents a mapping from a coordinate value to a label. -description: | - A label mapper instance maps inputs to a label. It is used together - with - [regions_selector](ref:regions_selector-1.0.0). The - [label_mapper](ref:label_mapper-1.0.0) - returns the label corresponding to given inputs. The - [regions_selector](ref:regions_selector-1.0.0) - returns the transform corresponding to this label. This maps inputs - (e.g. pixels on a detector) to transforms uniquely. - -examples: - - - - Map array indices are to labels. - - - | - ! - mapper: !core/ndarray-1.0.0 - data: - - [1, 0, 2] - - [1, 0, 2] - - [1, 0, 2] - datatype: int64 - shape: [3, 3] - no_label: 0 - - - - - Map numbers dictionary to transforms which return labels. - - - | - ! - atol: 1.0e-08 - inputs: [x, y] - inputs_mapping: !transform/remap_axes-1.1.0 - mapping: [0] - n_inputs: 2 - mapper: !!omap - - !!omap - labels: [-1.67833272, -1.9580548, -1.118888] - - !!omap - models: - - !transform/shift-1.1.0 {offset: 6.0} - - !transform/shift-1.1.0 {offset: 2.0} - - !transform/shift-1.1.0 {offset: 4.0} - no_label: 0 - - - - - Map a number within a range of numbers to transforms which return labels. - - - | - ! - mapper: !!omap - - !!omap - labels: - - [3.2, 4.1] - - [2.67, 2.98] - - [1.95, 2.3] - - !!omap - models: - - !transform/shift-1.1.0 {offset: 6.0} - - !transform/shift-1.1.0 {offset: 2.0} - - !transform/shift-1.1.0 {offset: 4.0} - inputs: [x, y] - inputs_mapping: !transform/remap_axes-1.1.0 - mapping: [0] - n_inputs: 2 - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - - type: object - properties: - mapper: - description: | - A mapping of inputs to labels. - In the general case this is a `astropy.modeling.core.Model`. - - It could be a numpy array with the shape of the detector/observation. - Pixel values are of type integer or string and represent - region labels. Pixels which are not within any region have value ``no_label``. - - It could be a dictionary which maps tuples to labels or floating point numbers to labels. - - anyOf: - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - - type: object - properties: - labels: - type: array - items: - anyOf: - - type: number - - type: array - items: - type: number - minLength: 2 - maxLength: 2 - models: - type: array - items: - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - - inputs: - type: array - items: - type: string - description: | - Names of inputs. - inputs_mapping: - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - description: | - [mapping](https://asdf-standard.readthedocs.io/en/latest/generated/stsci.edu/asdf/transform/remap_axes-1.1.0.html) - atol: - type: number - description: | - absolute tolerance to compare keys in mapper. - no_label: - description: | - Fill in value for missing output. - anyOf: - - type: number - - type: string - - required: [mapper] diff --git a/gwcs/schemas/stsci.edu/gwcs/label_mapper-1.1.0.yaml b/gwcs/schemas/stsci.edu/gwcs/label_mapper-1.1.0.yaml deleted file mode 100644 index 1f7a2bce..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/label_mapper-1.1.0.yaml +++ /dev/null @@ -1,130 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/label_mapper-1.1.0" -tag: "tag:stsci.edu:gwcs/label_mapper-1.1.0" -title: > - Represents a mapping from a coordinate value to a label. -description: | - A label mapper instance maps inputs to a label. It is used together - with - [regions_selector](ref:regions_selector-1.1.0). The - [label_mapper](ref:label_mapper-1.1.0) - returns the label corresponding to given inputs. The - [regions_selector](ref:regions_selector-1.1.0) - returns the transform corresponding to this label. This maps inputs - (e.g. pixels on a detector) to transforms uniquely. - -examples: - - - - Map array indices are to labels. - - - | - ! - mapper: !core/ndarray-1.0.0 - data: - - [1, 0, 2] - - [1, 0, 2] - - [1, 0, 2] - datatype: int64 - shape: [3, 3] - no_label: 0 - - - - - Map numbers dictionary to transforms which return labels. - - - | - ! - atol: 1.0e-08 - inputs: [x, y] - inputs_mapping: !transform/remap_axes-1.3.0 - mapping: [0] - n_inputs: 2 - mapper: !!omap - - !!omap - labels: [-1.67833272, -1.9580548, -1.118888] - - !!omap - models: - - !transform/shift-1.2.0 {offset: 6.0} - - !transform/shift-1.2.0 {offset: 2.0} - - !transform/shift-1.2.0 {offset: 4.0} - no_label: 0 - - - - - Map a number within a range of numbers to transforms which return labels. - - - | - ! - mapper: !!omap - - !!omap - labels: - - [3.2, 4.1] - - [2.67, 2.98] - - [1.95, 2.3] - - !!omap - models: - - !transform/shift-1.2.0 {offset: 6.0} - - !transform/shift-1.2.0 {offset: 2.0} - - !transform/shift-1.2.0 {offset: 4.0} - inputs: [x, y] - inputs_mapping: !transform/remap_axes-1.3.0 - mapping: [0] - n_inputs: 2 - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - - type: object - properties: - mapper: - description: | - A mapping of inputs to labels. - In the general case this is a `astropy.modeling.core.Model`. - - It could be a numpy array with the shape of the detector/observation. - Pixel values are of type integer or string and represent - region labels. Pixels which are not within any region have value ``no_label``. - - It could be a dictionary which maps tuples to labels or floating point numbers to labels. - - anyOf: - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - - type: object - properties: - labels: - type: array - items: - anyOf: - - type: number - - type: array - items: - type: number - minLength: 2 - maxLength: 2 - models: - type: array - items: - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - - inputs: - type: array - items: - type: string - description: | - Names of inputs. - inputs_mapping: - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - description: | - [mapping](https://asdf-standard.readthedocs.io/en/latest/generated/stsci.edu/asdf/transform/remap_axes-1.3.0.html) - atol: - type: number - description: | - absolute tolerance to compare keys in mapper. - no_label: - description: | - Fill in value for missing output. - anyOf: - - type: number - - type: string - - required: [mapper] diff --git a/gwcs/schemas/stsci.edu/gwcs/regions_selector-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/regions_selector-1.0.0.yaml deleted file mode 100644 index 55ef836d..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/regions_selector-1.0.0.yaml +++ /dev/null @@ -1,104 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/regions_selector-1.0.0" -tag: "tag:stsci.edu:gwcs/regions_selector-1.0.0" -title: > - Represents a discontinuous transform. -description: | - Maps regions to transgorms and evaluates the transforms with the corresponding inputs. - -examples: - - - - Create a regions_selector schema for 2 regions, labeled "1" and "2". - - | - ! - inputs: [x, y] - label_mapper: ! - mapper: !core/ndarray-1.0.0 - datatype: int8 - data: - - [0, 1, 1, 0, 2, 0] - - [0, 1, 1, 0, 2, 0] - - [0, 1, 1, 0, 2, 0] - - [0, 1, 1, 0, 2, 0] - - [0, 1, 1, 0, 2, 0] - datatype: int64 - shape: [5, 6] - no_label: 0 - outputs: [ra, dec, lam] - selector: !!omap - - !!omap - labels: [1, 2] - - !!omap - transforms: - - !transform/compose-1.1.0 - forward: - - !transform/remap_axes-1.1.0 - mapping: [0, 1, 1] - - !transform/concatenate-1.1.0 - forward: - - !transform/concatenate-1.1.0 - forward: - - !transform/shift-1.1.0 {offset: 1.0} - - !transform/shift-1.1.0 {offset: 2.0} - - !transform/shift-1.1.0 {offset: 3.0} - - !transform/compose-1.1.0 - forward: - - !transform/remap_axes-1.1.0 - mapping: [0, 1, 1] - - !transform/concatenate-1.1.0 - forward: - - !transform/concatenate-1.1.0 - forward: - - !transform/scale-1.1.0 {factor: 2.0} - - !transform/scale-1.1.0 {factor: 3.0} - - !transform/scale-1.1.0 {factor: 3.0} - undefined_transform_value: .nan - - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - - type: object - properties: - label_mapper: - description: | - An instance of - [label_mapper-1.1.0](ref:label_mapper-1.0.0) - $ref: "./label_mapper-1.0.0" - inputs: - description: | - Names of inputs. - type: array - items: - type: string - outputs: - description: | - Names of outputs. - type: array - items: - type: string - selector: - description: | - A mapping of regions to trransforms. - type: object - properties: - labels: - description: | - An array of unique region labels. - type: array - items: - type: - - integer - - string - transforms: - description: | - A transform for each region. The order should match the order of labels. - type: array - items: - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - undefined_transform_value: - description: | - Value to be returned if there's no transform defined for the inputs. - type: number - required: [label_mapper, inputs, outputs, selector] diff --git a/gwcs/schemas/stsci.edu/gwcs/regions_selector-1.1.0.yaml b/gwcs/schemas/stsci.edu/gwcs/regions_selector-1.1.0.yaml deleted file mode 100644 index 59471004..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/regions_selector-1.1.0.yaml +++ /dev/null @@ -1,104 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/regions_selector-1.1.0" -tag: "tag:stsci.edu:gwcs/regions_selector-1.1.0" -title: > - Represents a discontinuous transform. -description: | - Maps regions to transgorms and evaluates the transforms with the corresponding inputs. - -examples: - - - - Create a regions_selector schema for 2 regions, labeled "1" and "2". - - | - ! - inputs: [x, y] - label_mapper: ! - mapper: !core/ndarray-1.0.0 - datatype: int8 - data: - - [0, 1, 1, 0, 2, 0] - - [0, 1, 1, 0, 2, 0] - - [0, 1, 1, 0, 2, 0] - - [0, 1, 1, 0, 2, 0] - - [0, 1, 1, 0, 2, 0] - datatype: int64 - shape: [5, 6] - no_label: 0 - outputs: [ra, dec, lam] - selector: !!omap - - !!omap - labels: [1, 2] - - !!omap - transforms: - - !transform/compose-1.2.0 - forward: - - !transform/remap_axes-1.3.0 - mapping: [0, 1, 1] - - !transform/concatenate-1.2.0 - forward: - - !transform/concatenate-1.2.0 - forward: - - !transform/shift-1.2.0 {offset: 1.0} - - !transform/shift-1.2.0 {offset: 2.0} - - !transform/shift-1.2.0 {offset: 3.0} - - !transform/compose-1.2.0 - forward: - - !transform/remap_axes-1.3.0 - mapping: [0, 1, 1] - - !transform/concatenate-1.2.0 - forward: - - !transform/concatenate-1.2.0 - forward: - - !transform/scale-1.2.0 {factor: 2.0} - - !transform/scale-1.2.0 {factor: 3.0} - - !transform/scale-1.2.0 {factor: 3.0} - undefined_transform_value: .nan - - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - - type: object - properties: - label_mapper: - description: | - An instance of - [label_mapper-1.1.0](ref:label_mapper-1.1.0) - $ref: "./label_mapper-1.1.0" - inputs: - description: | - Names of inputs. - type: array - items: - type: string - outputs: - description: | - Names of outputs. - type: array - items: - type: string - selector: - description: | - A mapping of regions to trransforms. - type: object - properties: - labels: - description: | - An array of unique region labels. - type: array - items: - type: - - integer - - string - transforms: - description: | - A transform for each region. The order should match the order of labels. - type: array - items: - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - undefined_transform_value: - description: | - Value to be returned if there's no transform defined for the inputs. - type: number - required: [label_mapper, inputs, outputs, selector] diff --git a/gwcs/schemas/stsci.edu/gwcs/sellmeier_glass-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/sellmeier_glass-1.0.0.yaml deleted file mode 100644 index fc19df21..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/sellmeier_glass-1.0.0.yaml +++ /dev/null @@ -1,38 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/sellmeier_glass-1.0.0" -tag: "tag:stsci.edu:gwcs/sellmeier_glass-1.0.0" -title: Sellmeier equation for glass - -description: | - Sellmeier equation for glass. - - $$ n(\\lambda)^2 = 1 + \\frac{(B1 * \\lambda^2 )}{(\\lambda^2 - C1)} + - \\frac{(B2 * \\lambda^2 )}{(\\lambda^2 - C2)} + - \\frac{(B3 * \\lambda^2 )}{(\\lambda^2 - C3)} $$ - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - - type: object - properties: - B_coef: - description: | - B coefficients in Sellmeier equation. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 - C_coef: - description: | - C coefficients in Sellmeier equation. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 diff --git a/gwcs/schemas/stsci.edu/gwcs/sellmeier_glass-1.1.0.yaml b/gwcs/schemas/stsci.edu/gwcs/sellmeier_glass-1.1.0.yaml deleted file mode 100644 index f6e1d476..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/sellmeier_glass-1.1.0.yaml +++ /dev/null @@ -1,38 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/sellmeier_glass-1.1.0" -tag: "tag:stsci.edu:gwcs/sellmeier_glass-1.1.0" -title: Sellmeier equation for glass - -description: | - Sellmeier equation for glass. - - $$ n(\\lambda)^2 = 1 + \\frac{(B1 * \\lambda^2 )}{(\\lambda^2 - C1)} + - \\frac{(B2 * \\lambda^2 )}{(\\lambda^2 - C2)} + - \\frac{(B3 * \\lambda^2 )}{(\\lambda^2 - C3)} $$ - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - - type: object - properties: - B_coef: - description: | - B coefficients in Sellmeier equation. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 - C_coef: - description: | - C coefficients in Sellmeier equation. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 diff --git a/gwcs/schemas/stsci.edu/gwcs/sellmeier_zemax-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/sellmeier_zemax-1.0.0.yaml deleted file mode 100644 index da761723..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/sellmeier_zemax-1.0.0.yaml +++ /dev/null @@ -1,72 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/sellmeier_zemax-1.0.0" -tag: "tag:stsci.edu:gwcs/sellmeier_zemax-1.0.0" -title: Sellmeier equation for glass used by Zemax - -description: | - Sellmeier equation for glass used by Zemax - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - - type: object - properties: - B_coef: - description: | - B coefficients in Sellmeier equation. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 - C_coef: - description: | - C coefficients in Sellmeier equation. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 - D_coef: - description: | - Thermal D coefficients of the glass. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 - E_coef: - description: | - Thermal E coefficients of the glass. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 - ref_temperature: - description: | - Reference temperature of the glass in Kelvin. - type: number - ref_pressure: - description: | - Reference pressure of the glass in ATM. - type: number - temperature: - description: | - System temperature in Kelvin. - type: number - pressure: - description: | - System pressure in ATM. - type: number - required: [B_coef, C_coef, D_coef, E_coef, ref_temperature, - ref_pressure, temperature, pressure] diff --git a/gwcs/schemas/stsci.edu/gwcs/sellmeier_zemax-1.1.0.yaml b/gwcs/schemas/stsci.edu/gwcs/sellmeier_zemax-1.1.0.yaml deleted file mode 100644 index 27f39363..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/sellmeier_zemax-1.1.0.yaml +++ /dev/null @@ -1,72 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/sellmeier_zemax-1.1.0" -tag: "tag:stsci.edu:gwcs/sellmeier_zemax-1.1.0" -title: Sellmeier equation for glass used by Zemax - -description: | - Sellmeier equation for glass used by Zemax - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - - type: object - properties: - B_coef: - description: | - B coefficients in Sellmeier equation. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 - C_coef: - description: | - C coefficients in Sellmeier equation. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 - D_coef: - description: | - Thermal D coefficients of the glass. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 - E_coef: - description: | - Thermal E coefficients of the glass. - anyOf: - - type: array - - $ref: "tag:stsci.edu:asdf/core/ndarray-1.0.0" - items: - type: number - minItems: 3 - maxItems: 3 - ref_temperature: - description: | - Reference temperature of the glass in Kelvin. - type: number - ref_pressure: - description: | - Reference pressure of the glass in ATM. - type: number - temperature: - description: | - System temperature in Kelvin. - type: number - pressure: - description: | - System pressure in ATM. - type: number - required: [B_coef, C_coef, D_coef, E_coef, ref_temperature, - ref_pressure, temperature, pressure] diff --git a/gwcs/schemas/stsci.edu/gwcs/snell3d-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/snell3d-1.0.0.yaml deleted file mode 100644 index 570b3bcc..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/snell3d-1.0.0.yaml +++ /dev/null @@ -1,13 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/snell3d-1.0.0" -tag: "tag:stsci.edu:gwcs/snell3d-1.0.0" -title: Snell Law in 3D space - -description: | - Snell Law in 3D. - Inputs are index of refraction and direction cosines. - Outputs are direction cosines. - -$ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" diff --git a/gwcs/schemas/stsci.edu/gwcs/snell3d-1.1.0.yaml b/gwcs/schemas/stsci.edu/gwcs/snell3d-1.1.0.yaml deleted file mode 100644 index 741b1821..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/snell3d-1.1.0.yaml +++ /dev/null @@ -1,13 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/snell3d-1.1.0" -tag: "tag:stsci.edu:gwcs/snell3d-1.1.0" -title: Snell Law in 3D space - -description: | - Snell Law in 3D. - Inputs are index of refraction and direction cosines. - Outputs are direction cosines. - -$ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" diff --git a/gwcs/schemas/stsci.edu/gwcs/spectral_frame-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/spectral_frame-1.0.0.yaml deleted file mode 100644 index 110f16df..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/spectral_frame-1.0.0.yaml +++ /dev/null @@ -1,30 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/spectral_frame-1.0.0" -tag: "tag:stsci.edu:gwcs/spectral_frame-1.0.0" - -title: > - Represents a spectral frame. -allOf: - - type: object - properties: - reference_position: - description: | - The position of the reference frame. - enum: [geocenter, barycenter, heliocenter] - default: geocenter - - axes_names: - minItems: 1 - maxItems: 1 - - axes_order: - minItems: 1 - maxItems: 1 - - unit: - minItems: 1 - maxItems: 1 - - - $ref: frame-1.0.0 diff --git a/gwcs/schemas/stsci.edu/gwcs/spherical_cartesian-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/spherical_cartesian-1.0.0.yaml deleted file mode 100644 index 61f959eb..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/spherical_cartesian-1.0.0.yaml +++ /dev/null @@ -1,44 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/spherical_cartesian-1.0.0" -tag: "tag:stsci.edu:gwcs/spherical_cartesian-1.0.0" - -title: > - Convert coordinates between spherical and Cartesian coordinates. - -description: | - This schema is for transforms which convert between spherical coordinates - (on the unit sphere) and Cartesian coordinates. - -examples: - - - - Convert spherical coordinates to Cartesian coordinates. - - - | - - ! - transform_type: spherical_to_cartesian - - - - - Convert Cartesian coordinates to spherical coordinates. - - - | - ! - transform_type: cartesian_to_spherical - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - - object: - properties: - wrap_lon_at: - description: Angle at which to wrap the longitude angle. - type: integer - enum: [180, 360] - default: 360 - transform_type: - description: The type of transform/class to initialize. - type: string - enum: [spherical_to_cartesian, cartesian_to_spherical] - - required: [transform_type, wrap_lon_at] diff --git a/gwcs/schemas/stsci.edu/gwcs/spherical_cartesian-1.1.0.yaml b/gwcs/schemas/stsci.edu/gwcs/spherical_cartesian-1.1.0.yaml deleted file mode 100644 index 8851769a..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/spherical_cartesian-1.1.0.yaml +++ /dev/null @@ -1,44 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/spherical_cartesian-1.1.0" -tag: "tag:stsci.edu:gwcs/spherical_cartesian-1.1.0" - -title: > - Convert coordinates between spherical and Cartesian coordinates. - -description: | - This schema is for transforms which convert between spherical coordinates - (on the unit sphere) and Cartesian coordinates. - -examples: - - - - Convert spherical coordinates to Cartesian coordinates. - - - | - - ! - transform_type: spherical_to_cartesian - - - - - Convert Cartesian coordinates to spherical coordinates. - - - | - ! - transform_type: cartesian_to_spherical - -allOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - - object: - properties: - wrap_lon_at: - description: Angle at which to wrap the longitude angle. - type: integer - enum: [180, 360] - default: 360 - transform_type: - description: The type of transform/class to initialize. - type: string - enum: [spherical_to_cartesian, cartesian_to_spherical] - - required: [transform_type, wrap_lon_at] diff --git a/gwcs/schemas/stsci.edu/gwcs/step-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/step-1.0.0.yaml deleted file mode 100644 index be468c42..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/step-1.0.0.yaml +++ /dev/null @@ -1,32 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/step-1.0.0" -tag: "tag:stsci.edu:gwcs/step-1.0.0" - -title: > - Describes a single step of a WCS transform pipeline. -description: > -examples: [] - -type: object -properties: - frame: - description: | - The frame of the inputs to the transform. - anyOf: - - type: string - - $ref: frame-1.0.0 - - transform: - description: | - The transform from this step to the next one. The - last step in a WCS should not have a transform, but - exists only to describe the frames and units of the - final output axes. - anyOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.1.0" - - type: 'null' - default: null - -required: [frame] diff --git a/gwcs/schemas/stsci.edu/gwcs/step-1.1.0.yaml b/gwcs/schemas/stsci.edu/gwcs/step-1.1.0.yaml deleted file mode 100644 index 2af61785..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/step-1.1.0.yaml +++ /dev/null @@ -1,32 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/step-1.1.0" -tag: "tag:stsci.edu:gwcs/step-1.1.0" - -title: > - Describes a single step of a WCS transform pipeline. -description: > -examples: [] - -type: object -properties: - frame: - description: | - The frame of the inputs to the transform. - anyOf: - - type: string - - $ref: frame-1.0.0 - - transform: - description: | - The transform from this step to the next one. The - last step in a WCS should not have a transform, but - exists only to describe the frames and units of the - final output axes. - anyOf: - - $ref: "tag:stsci.edu:asdf/transform/transform-1.2.0" - - type: 'null' - default: null - -required: [frame] diff --git a/gwcs/schemas/stsci.edu/gwcs/stokes_frame-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/stokes_frame-1.0.0.yaml deleted file mode 100644 index 342d4c73..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/stokes_frame-1.0.0.yaml +++ /dev/null @@ -1,22 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/stokes_frame-1.0.0" -tag: "tag:stsci.edu:gwcs/stokes_frame-1.0.0" - -title: > - Represents a stokes frame - -type: object -properties: - name: - description: | - A user-friendly name for the frame. - type: string - - axes_order: - description: | - The order of the axes. - type: array - items: - type: integer diff --git a/gwcs/schemas/stsci.edu/gwcs/temporal_frame-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/temporal_frame-1.0.0.yaml deleted file mode 100644 index 10dec983..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/temporal_frame-1.0.0.yaml +++ /dev/null @@ -1,56 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/temporal_frame-1.0.0" -tag: "tag:stsci.edu:gwcs/temporal_frame-1.0.0" - -title: > - Represents a temporal frame. - - -type: object -properties: - name: - description: | - A user-friendly name for the frame. - type: string - - axes_order: - description: | - The order of the axes. - type: array - items: - type: integer - - axes_names: - description: | - The name of each axis in this frame. - type: array - items: - anyOf: - - type: string - - type: 'null' - - reference_frame: - description: | - The reference frame. - $ref: "tag:stsci.edu:asdf/time/time-1.1.0" - - unit: - description: | - Units for each axis. - type: array - items: - $ref: "tag:stsci.edu:asdf/unit/unit-1.0.0" - - axis_physical_types: - description: | - An iterable of strings describing the physical type for each world axis. - These should be names from the VO UCD1+ controlled Vocabulary - (http://www.ivoa.net/documents/latest/UCDlist.html). - type: array - items: - type: - string - -required: [name] diff --git a/gwcs/schemas/stsci.edu/gwcs/wcs-1.0.0.yaml b/gwcs/schemas/stsci.edu/gwcs/wcs-1.0.0.yaml deleted file mode 100644 index 12bcecd1..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/wcs-1.0.0.yaml +++ /dev/null @@ -1,33 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/wcs-1.0.0" -tag: "tag:stsci.edu:gwcs/wcs-1.0.0" - -title: > - A system for describing generalized world coordinate transformations. -description: > - ASDF WCS is a way of specifying transformations (usually from - detector space to world coordinate space and back) by using the - transformations in the `transform-schema` module. -type: object -properties: - name: - description: | - A descriptive name for this WCS. - type: string - - steps: - description: | - A list of steps in the forward transformation from detector to - world coordinates. - The inverse transformation is determined automatically by - reversing this list, and inverting each of the individual - transforms according to the rules described in - [inverse](https://asdf-standard.readthedocs.io/en/latest/generated/stsci.edu/asdf/transform/transform-1.1.0.html#inverse). - type: array - items: - $ref: step-1.0.0 - -required: [name, steps] -additionalProperties: true diff --git a/gwcs/schemas/stsci.edu/gwcs/wcs-1.1.0.yaml b/gwcs/schemas/stsci.edu/gwcs/wcs-1.1.0.yaml deleted file mode 100644 index b61de8a6..00000000 --- a/gwcs/schemas/stsci.edu/gwcs/wcs-1.1.0.yaml +++ /dev/null @@ -1,33 +0,0 @@ -%YAML 1.1 ---- -$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" -id: "http://stsci.edu/schemas/gwcs/wcs-1.1.0" -tag: "tag:stsci.edu:gwcs/wcs-1.1.0" - -title: > - A system for describing generalized world coordinate transformations. -description: > - ASDF WCS is a way of specifying transformations (usually from - detector space to world coordinate space and back) by using the - transformations in the `transform-schema` module. -type: object -properties: - name: - description: | - A descriptive name for this WCS. - type: string - - steps: - description: | - A list of steps in the forward transformation from detector to - world coordinates. - The inverse transformation is determined automatically by - reversing this list, and inverting each of the individual - transforms according to the rules described in - [inverse](https://asdf-standard.readthedocs.io/en/latest/generated/stsci.edu/asdf/transform/transform-1.2.0.html#inverse). - type: array - items: - $ref: step-1.1.0 - -required: [name, steps] -additionalProperties: true diff --git a/gwcs/spectroscopy.py b/gwcs/spectroscopy.py index dd65c446..74bb1226 100644 --- a/gwcs/spectroscopy.py +++ b/gwcs/spectroscopy.py @@ -140,9 +140,11 @@ def evaluate(self, wavelength, alpha_in, beta_in, def input_units(self): if self.groove_density.unit is None: return None - return {'wavelength': 1 / self.groove_density.unit, - 'alpha_in': u.Unit(1), - 'beta_in': u.Unit(1)} + return { + 'wavelength': 1 / self.groove_density.unit, + 'alpha_in': u.Unit(1), + 'beta_in': u.Unit(1) + } class Snell3D(Model): diff --git a/gwcs/tags/__init__.py b/gwcs/tags/__init__.py deleted file mode 100644 index 08fea9c4..00000000 --- a/gwcs/tags/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -# -*- coding: utf-8 -*- -from astropy import units as u - - -def _parameter_to_value(param): - if param.unit is not None: - return u.Quantity(param) - return param.value diff --git a/gwcs/tags/spectroscopy_models.py b/gwcs/tags/spectroscopy_models.py deleted file mode 100644 index 3ffe424a..00000000 --- a/gwcs/tags/spectroscopy_models.py +++ /dev/null @@ -1,123 +0,0 @@ -""" -ASDF tags for spectroscopy related models. -""" - -from numpy.testing import assert_allclose - -from astropy import units as u -from astropy.units import allclose -from asdf import yamlutil - -from ..gwcs_types import GWCSTransformType -from .. spectroscopy import * -from . import _parameter_to_value - - -__all__ = ['GratingEquationType', 'SellmeierGlassType', - 'SellmeierZemaxType', 'Snell3D'] - - -class SellmeierGlassType(GWCSTransformType): - name = "sellmeier_glass" - types = [SellmeierGlass] - version = "1.1.0" - - @classmethod - def from_tree_transform(cls, node, ctx): - return SellmeierGlass(node['B_coef'], node['C_coef']) - - @classmethod - def to_tree_transform(cls, model, ctx): - node = {'B_coef': _parameter_to_value(model.B_coef), - 'C_coef': _parameter_to_value(model.C_coef)} - return yamlutil.custom_tree_to_tagged_tree(node, ctx) - - -class SellmeierZemaxType(GWCSTransformType): - name = "sellmeier_zemax" - types = [SellmeierZemax] - version = "1.1.0" - - @classmethod - def from_tree_transform(cls, node, ctx): - return SellmeierZemax(node['temperature'], node['ref_temperature'], - node['ref_pressure'], node['pressure'], - node['B_coef'], node['C_coef'], node['D_coef'], - node['E_coef']) - - @classmethod - def to_tree_transform(cls, model, ctx): - node = {'B_coef': _parameter_to_value(model.B_coef), - 'C_coef': _parameter_to_value(model.C_coef), - 'D_coef': _parameter_to_value(model.D_coef), - 'E_coef': _parameter_to_value(model.E_coef), - 'temperature': _parameter_to_value(model.temperature), - 'ref_temperature': _parameter_to_value(model.ref_temperature), - 'pressure': _parameter_to_value(model.pressure), - 'ref_pressure': _parameter_to_value(model.ref_pressure)} - return yamlutil.custom_tree_to_tagged_tree(node, ctx) - - -class Snell3DType(GWCSTransformType): - name = "snell3d" - types = [Snell3D] - version = "1.1.0" - - @classmethod - def from_tree_transform(cls, node, ctx): - return Snell3D() - - @classmethod - def to_tree_transform(cls, model, ctx): - return yamlutil.custom_tree_to_tagged_tree({}, ctx) - - -class GratingEquationType(GWCSTransformType): - name = "grating_equation" - version = '1.1.0' - types = [AnglesFromGratingEquation3D, - WavelengthFromGratingEquation] - - @classmethod - def from_tree_transform(cls, node, ctx): - groove_density = node['groove_density'] - order = node['order'] - output = node['output'] - if output == "wavelength": - model = WavelengthFromGratingEquation(groove_density=groove_density, - spectral_order=order) - elif output == "angle": - model = AnglesFromGratingEquation3D(groove_density=groove_density, - spectral_order=order) - else: - raise ValueError("Can't create a GratingEquation model with " - "output {0}".format(output)) - return model - - @classmethod - def to_tree_transform(cls, model, ctx): - if model.groove_density.unit is not None: - groove_density = u.Quantity(model.groove_density.value, - unit=model.groove_density.unit) - else: - groove_density = model.groove_density.value - node = {'order': model.spectral_order.value, - 'groove_density': groove_density - } - if isinstance(model, AnglesFromGratingEquation3D): - node['output'] = 'angle' - elif isinstance(model, WavelengthFromGratingEquation): - node['output'] = 'wavelength' - else: - raise TypeError("Can't serialize an instance of {0}" - .format(model.__class__.__name__)) - return yamlutil.custom_tree_to_tagged_tree(node, ctx) - - @classmethod - def assert_equal(cls, a, b): - if isinstance(a, AnglesFromGratingEquation3D): - assert isinstance(b, AnglesFromGratingEquation3D) # nosec - elif isinstance(a, WavelengthFromGratingEquation): - assert isinstance(b, WavelengthFromGratingEquation) # nosec - allclose(a.groove_density, b.groove_density) # nosec - assert a.spectral_order.value == b.spectral_order.value # nosec diff --git a/gwcs/tags/tests/__init__.py b/gwcs/tags/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/gwcs/tags/tests/test_selector.py b/gwcs/tags/tests/test_selector.py deleted file mode 100644 index 2cca57d5..00000000 --- a/gwcs/tags/tests/test_selector.py +++ /dev/null @@ -1,74 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -# -*- coding: utf-8 -*- -import numpy as np - -from astropy.modeling.models import Mapping, Shift, Scale, Polynomial2D -from ... import selector -from asdf.tests import helpers -from ...tests.test_region import create_scalar_mapper -from ...extension import GWCSExtension - - -def test_regions_selector(tmpdir): - m1 = Mapping([0, 1, 1]) | Shift(1) & Shift(2) & Shift(3) - m2 = Mapping([0, 1, 1]) | Scale(2) & Scale(3) & Scale(3) - sel = {1: m1, 2: m2} - a = np.zeros((5, 6), dtype=np.int32) - a[:, 1:3] = 1 - a[:, 4:5] = 2 - mask = selector.LabelMapperArray(a) - rs = selector.RegionsSelector(inputs=('x', 'y'), outputs=('ra', 'dec', 'lam'), - selector=sel, label_mapper=mask) - tree = {'model': rs} - helpers.assert_roundtrip_tree(tree, tmpdir, extensions=GWCSExtension()) - - -def test_LabelMapperArray_str(tmpdir): - a = np.array([["label1", "", "label2"], - ["label1", "", ""], - ["label1", "label2", "label2"]]) - mask = selector.LabelMapperArray(a) - tree = {'model': mask} - helpers.assert_roundtrip_tree(tree, tmpdir, extensions=GWCSExtension()) - - -def test_labelMapperArray_int(tmpdir): - - a = np.array([[1, 0, 2], - [1, 0, 0], - [1, 2, 2]]) - mask = selector.LabelMapperArray(a) - tree = {'model': mask} - helpers.assert_roundtrip_tree(tree, tmpdir, extensions=GWCSExtension()) - - -def test_LabelMapperDict(tmpdir): - dmapper = create_scalar_mapper() - sel = selector.LabelMapperDict(('x', 'y'), dmapper, - inputs_mapping=Mapping((0,), n_inputs=2), atol=1e-3) - tree = {'model': sel} - helpers.assert_roundtrip_tree(tree, tmpdir, extensions=GWCSExtension()) - - -def test_LabelMapperRange(tmpdir): - m = [] - for i in np.arange(9) * .1: - c0_0, c1_0, c0_1, c1_1 = np.ones((4,)) * i - m.append(Polynomial2D(2, c0_0=c0_0, - c1_0=c1_0, c0_1=c0_1, c1_1=c1_1)) - keys = np.array([[4.88, 5.64], - [5.75, 6.5], - [6.67, 7.47], - [7.7, 8.63], - [8.83, 9.96], - [10.19, 11.49], - [11.77, 13.28], - [13.33, 15.34], - [15.56, 18.09]]) - rmapper = {} - for k, v in zip(keys, m): - rmapper[tuple(k)] = v - sel = selector.LabelMapperRange(('x', 'y'), rmapper, - inputs_mapping=Mapping((0,), n_inputs=2)) - tree = {'model': sel} - helpers.assert_roundtrip_tree(tree, tmpdir, extensions=GWCSExtension()) diff --git a/gwcs/tags/wcs.py b/gwcs/tags/wcs.py deleted file mode 100644 index d2098014..00000000 --- a/gwcs/tags/wcs.py +++ /dev/null @@ -1,292 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -# -*- coding: utf-8 -*- -import astropy.time - -from asdf import yamlutil -from ..gwcs_types import GWCSType -from ..coordinate_frames import (Frame2D, CoordinateFrame, CelestialFrame, - SpectralFrame, TemporalFrame, CompositeFrame, - StokesFrame) -from ..wcs import WCS, Step - - -_REQUIRES = ['astropy'] - - -__all__ = ["WCSType", "CelestialFrameType", "CompositeFrameType", "FrameType", - "SpectralFrameType", "StepType", "TemporalFrameType", "StokesFrameType"] - - -class WCSType(GWCSType): - name = "wcs" - requires = _REQUIRES - types = [WCS] - version = '1.1.0' - - @classmethod - def from_tree(cls, node, ctx): - name = node['name'] - steps = [(x.frame, x.transform) for x in node['steps']] - return WCS(steps, name=name) - - @classmethod - def to_tree(cls, gwcsobj, ctx): - return {'name': gwcsobj.name, - 'steps': gwcsobj.pipeline - } - - @classmethod - def assert_equal(cls, old, new): - from asdf.tests import helpers - assert old.name == new.name # nosec - assert len(old.available_frames) == len(new.available_frames) # nosec - for old_step, new_step in zip( - old.pipeline, new.pipeline): - helpers.assert_tree_match(old_step.frame, new_step.frame) - helpers.assert_tree_match(old_step.transform, new_step.transform) - - -class StepType(dict, GWCSType): - name = "step" - requires = _REQUIRES - version = '1.1.0' - types = [Step] - - @classmethod - def from_tree(cls, node, ctx): - return Step(frame=node['frame'], transform=node.get('transform', None)) - - @classmethod - def to_tree(cls, step, ctx): - return {'frame': step.frame, - 'transform': step.transform} - - -class FrameType(GWCSType): - name = "frame" - requires = _REQUIRES - types = [CoordinateFrame] - version = '1.0.0' - - @classmethod - def _from_tree(cls, node, ctx): - kwargs = {'name': node['name']} - - if 'axes_type' in node and 'naxes' in node: - kwargs.update({ - 'axes_type': node['axes_type'], - 'naxes': node['naxes']}) - - if 'axes_names' in node: - kwargs['axes_names'] = node['axes_names'] - - if 'reference_frame' in node: - kwargs['reference_frame'] = yamlutil.tagged_tree_to_custom_tree( - node['reference_frame'], ctx) - - if 'axes_order' in node: - kwargs['axes_order'] = tuple(node['axes_order']) - - if 'unit' in node: - kwargs['unit'] = tuple( - yamlutil.tagged_tree_to_custom_tree(node['unit'], ctx)) - - if 'axis_physical_types' in node: - kwargs['axis_physical_types'] = tuple(node['axis_physical_types']) - - return kwargs - - @classmethod - def _to_tree(cls, frame, ctx): - - node = {} - - node['name'] = frame.name - - # We want to check that it is exactly this type and not a subclass - if type(frame) is CoordinateFrame: - node['axes_type'] = frame.axes_type - node['naxes'] = frame.naxes - - if frame.axes_order is not None: - node['axes_order'] = list(frame.axes_order) - - if frame.axes_names is not None: - node['axes_names'] = list(frame.axes_names) - - if frame.reference_frame is not None: - node['reference_frame'] = yamlutil.custom_tree_to_tagged_tree( - frame.reference_frame, ctx) - - if frame.unit is not None: - node['unit'] = yamlutil.custom_tree_to_tagged_tree( - list(frame.unit), ctx) - - if frame.axis_physical_types is not None: - node['axis_physical_types'] = list(frame.axis_physical_types) - - return node - - @classmethod - def _assert_equal(cls, old, new): - from asdf.tests import helpers - assert old.name == new.name # nosec - assert old.axes_order == new.axes_order # nosec - assert old.axes_names == new.axes_names # nosec - assert type(old.reference_frame) is type(new.reference_frame) # nosec - assert old.unit == new.unit # nosec - - if old.reference_frame is not None: - for name in old.reference_frame.get_frame_attr_names().keys(): - helpers.assert_tree_match( - getattr(old.reference_frame, name), - getattr(new.reference_frame, name)) - - @classmethod - def assert_equal(cls, old, new): - cls._assert_equal(old, new) - - @classmethod - def from_tree(cls, node, ctx): - node = cls._from_tree(node, ctx) - return CoordinateFrame(**node) - - @classmethod - def to_tree(cls, frame, ctx): - return cls._to_tree(frame, ctx) - - -class Frame2DType(FrameType): - name = "frame2d" - types = [Frame2D] - - @classmethod - def from_tree(cls, node, ctx): - node = cls._from_tree(node, ctx) - return Frame2D(**node) - - -class CelestialFrameType(FrameType): - name = "celestial_frame" - types = [CelestialFrame] - - @classmethod - def from_tree(cls, node, ctx): - node = cls._from_tree(node, ctx) - return CelestialFrame(**node) - - @classmethod - def to_tree(cls, frame, ctx): - return cls._to_tree(frame, ctx) - - @classmethod - def assert_equal(cls, old, new): - cls._assert_equal(old, new) - - assert old.reference_position == new.reference_position # nosec - - -class SpectralFrameType(FrameType): - name = "spectral_frame" - types = [SpectralFrame] - version = "1.0.0" - - @classmethod - def from_tree(cls, node, ctx): - node = cls._from_tree(node, ctx) - - if 'reference_position' in node: - node['reference_position'] = node['reference_position'].upper() - - return SpectralFrame(**node) - - @classmethod - def to_tree(cls, frame, ctx): - node = cls._to_tree(frame, ctx) - - if frame.reference_position is not None: - node['reference_position'] = frame.reference_position.lower() - - return node - - -class CompositeFrameType(FrameType): - name = "composite_frame" - types = [CompositeFrame] - - @classmethod - def from_tree(cls, node, ctx): - if len(node) != 2: - raise ValueError("CompositeFrame has extra properties") - - name = node['name'] - frames = node['frames'] - - return CompositeFrame(frames, name) - - @classmethod - def to_tree(cls, frame, ctx): - return { - 'name': frame.name, - 'frames': yamlutil.custom_tree_to_tagged_tree(frame.frames, ctx) - } - - @classmethod - def assert_equal(cls, old, new): - from asdf.tests import helpers - assert old.name == new.name # nosec - for old_frame, new_frame in zip(old.frames, new.frames): - helpers.assert_tree_match(old_frame, new_frame) - - -class TemporalFrameType(FrameType): - name = "temporal_frame" - requires = _REQUIRES - types = [TemporalFrame] - version = '1.0.0' - - - @classmethod - def from_tree(cls, node, ctx): - node = cls._from_tree(node, ctx) - return TemporalFrame(**node) - - @classmethod - def to_tree(cls, frame, ctx): - return cls._to_tree(frame, ctx) - - @classmethod - def assert_equal(cls, old, new): - assert old.name == new.name # nosec - assert old.axes_order == new.axes_order # nosec - assert old.axes_names == new.axes_names # nosec - assert old.unit == new.unit # nosec - - assert old.reference_frame == new.reference_frame # nosec - - -class StokesFrameType(FrameType): - name = "stokes_frame" - types = [StokesFrame] - - @classmethod - def from_tree(cls, node, ctx): - node = cls._from_tree(node, ctx) - return StokesFrame(**node) - - @classmethod - def _to_tree(cls, frame, ctx): - - node = {} - - node['name'] = frame.name - if frame.axes_order: - node['axes_order'] = list(frame.axes_order) - - return node - - @classmethod - def assert_equal(cls, old, new): - from asdf.tests import helpers - assert old.name == new.name # nosec - assert old.axes_order == new.axes_order # nosec diff --git a/gwcs/tests/test_geometry.py b/gwcs/tests/test_geometry.py index f395f3ef..330c2834 100644 --- a/gwcs/tests/test_geometry.py +++ b/gwcs/tests/test_geometry.py @@ -5,9 +5,10 @@ import pytest import asdf -from asdf.tests.helpers import assert_roundtrip_tree import numpy as np from astropy import units as u +from asdf_astropy.converters.transform.tests.test_transform import ( + assert_model_roundtrip) from .. import geometry @@ -185,7 +186,8 @@ def test_cartesian_spherical_asdf(tmpdir): c2s0 = geometry.CartesianToSpherical(wrap_lon_at=180) # asdf round-trip test: - assert_roundtrip_tree({'c2s': c2s0, 's2c': s2c0}, tmpdir) + assert_model_roundtrip(c2s0, tmpdir) + assert_model_roundtrip(s2c0, tmpdir) # create file object f = asdf.AsdfFile({'c2s': c2s0, 's2c': s2c0}) diff --git a/gwcs/wcs.py b/gwcs/wcs.py index e91763e6..4796c791 100644 --- a/gwcs/wcs.py +++ b/gwcs/wcs.py @@ -30,7 +30,7 @@ new_bbox = False -__all__ = ['WCS', 'NoConvergence'] +__all__ = ['WCS', 'Step', 'NoConvergence'] _ITER_INV_KWARGS = ['tolerance', 'maxiter', 'adaptive', 'detect_divergence', 'quiet'] diff --git a/setup.cfg b/setup.cfg index e3b62d64..f670f66d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,10 +19,12 @@ setup_requires = setuptools_scm install_requires = - asdf + asdf >= 2.8.1 astropy >= 4.1 numpy scipy + asdf_wcs_schemas + asdf-astropy @ git+https://github.com/astropy/asdf-astropy [options.extras_require] docs = @@ -40,8 +42,8 @@ test = codecov [options.entry_points] -asdf_extensions = - gwcs = gwcs.extension:GWCSExtension +asdf.extensions = + gwcs = gwcs.extension:get_extensions [flake8] @@ -68,8 +70,8 @@ minversion = 4.6 #testpaths = gwcs docs norecursedirs = build docs/_build .tox doctest_plus = enabled -asdf_schema_tests_enabled = true -asdf_schema_root = gwcs/schemas +#asdf_schema_tests_enabled = true +#asdf_schema_root = gwcs/schemas addopts = --doctest-rst filterwarnings = ignore:Models in math_functions:astropy.utils.exceptions.AstropyUserWarning diff --git a/setup.py b/setup.py index 43305683..6825a8ce 100755 --- a/setup.py +++ b/setup.py @@ -1,38 +1,9 @@ #!/usr/bin/env python # Licensed under a 3-clause BSD style license - see LICENSE.rst -import os - from setuptools import setup, find_packages -from configparser import ConfigParser - - -conf = ConfigParser() -conf.read(['setup.cfg']) -metadata = dict(conf.items('metadata')) - -PACKAGENAME = metadata.get('name', 'packagename') - -def get_package_data(): - # Installs the schema files - schemas = [] - root = os.path.join(PACKAGENAME, 'schemas') - for node, dirs, files in os.walk(root): - for fname in files: - if fname.endswith('.yaml'): - schemas.append( - os.path.relpath(os.path.join(node, fname), root)) - # In the package directory, install to the subdirectory 'schemas' - schemas = [os.path.join('schemas', s) for s in schemas] - return schemas - - -schemas = get_package_data() -PACKAGE_DATA ={'gwcs':schemas} - setup(use_scm_version=True, setup_requires=['setuptools_scm'], packages=find_packages(), - package_data=PACKAGE_DATA, ) From c5992178da5fa591483d5aa2856d85f555205fe2 Mon Sep 17 00:00:00 2001 From: Mihai Cara Date: Thu, 30 Sep 2021 22:33:50 -0400 Subject: [PATCH 07/12] Address reviewer's comments --- docs/gwcs/wcs_validation.rst | 21 +++++++++++---------- gwcs/converters/selector.py | 5 ++--- gwcs/converters/wcs.py | 14 +------------- setup.cfg | 3 --- 4 files changed, 14 insertions(+), 29 deletions(-) diff --git a/docs/gwcs/wcs_validation.rst b/docs/gwcs/wcs_validation.rst index 6ecc22b3..b8ebd537 100644 --- a/docs/gwcs/wcs_validation.rst +++ b/docs/gwcs/wcs_validation.rst @@ -9,16 +9,17 @@ However, this happens transparently to the end user and knowing the details of the validation machinery is not necessary to use or construct a WCS object. -GWCS uses the `Advanced Scientific Data Format `__ (ASDF) -to validate the transforms, coordinate frames and the overall WCS object structure. -ASDF makes use of abstract data type -definitions called ``schemas``. The serialization and deserialization happens -in classes, referred to as ``tags``. Most of the transform schemas live in the -``asdf-standard`` package while most of the transform tags live in ``astropy``. -`gwcs schemas `_ are -available for the WCS object, coordinate frames and some WCS specific -transforms. +GWCS uses the +`Advanced Scientific Data Format `_ +(ASDF) to serialize and deserialize GWCS objects (including transformations +and frames) and to provide validation that the serialization is correct. +ASDF makes use of abstract data type definitions called ``schemas``. +The serialization and deserialization happens in classes, referred to as +``converters`` defined in ``gwcs.converters.*`` modules. Most of the schemas +available for the WCS object, coordinate frames and some WCS specific transforms +live in the +`asdf-wcs-schemas package `_. Packages using GWCS may create their own transforms and schemas and register them as an ``Asdf Extension``. If those are of general use, it is recommended -they be included in astropy. +they be included in `asdf-astropy `_. diff --git a/gwcs/converters/selector.py b/gwcs/converters/selector.py index 104a6f9a..bd543dba 100644 --- a/gwcs/converters/selector.py +++ b/gwcs/converters/selector.py @@ -27,13 +27,12 @@ def from_yaml_tree_transform(self, node, tag, ctx): raise TypeError("inputs_mapping must be an instance" "of astropy.modeling.models.Mapping.") mapper = node['mapper'] - atol = node.get('atol', 10e-8) + atol = node.get('atol', 1e-8) no_label = node.get('no_label', np.nan) if isinstance(mapper, NDArrayType): if mapper.ndim != 2: - raise NotImplementedError( - "GWCS currently only supports 2x2 masks ") + raise NotImplementedError("GWCS currently only supports 2D masks.") return LabelMapperArray(mapper, inputs_mapping) elif isinstance(mapper, Model): inputs = node.get('inputs') diff --git a/gwcs/converters/wcs.py b/gwcs/converters/wcs.py index b12d3c5c..c806df6d 100644 --- a/gwcs/converters/wcs.py +++ b/gwcs/converters/wcs.py @@ -4,9 +4,6 @@ from asdf.extension import Converter -_REQUIRES = ['astropy'] - - __all__ = ["WCSConverter", "CelestialFrameConverter", "CompositeFrameConverter", "FrameConverter", "SpectralFrameConverter", "StepConverter", "TemporalFrameConverter", "StokesFrameConverter"] @@ -18,9 +15,7 @@ class WCSConverter(Converter): def from_yaml_tree(self, node, tag, ctx): from ..wcs import WCS - name = node['name'] - steps = [(x.frame, x.transform) for x in node['steps']] - return WCS(steps, name=name) + return WCS(node['steps'], name=node['name']) def to_yaml_tree(self, gwcsobj, tag, ctx): return { @@ -130,9 +125,6 @@ def from_yaml_tree(self, node, tag, ctx): node = self._from_yaml_tree(node, tag, ctx) return CelestialFrame(**node) - def to_yaml_tree(self, frame, tag, ctx): - return self._to_yaml_tree(frame, tag, ctx) - class SpectralFrameConverter(FrameConverter): tags = ["tag:stsci.edu:gwcs/spectral_frame-*"] @@ -181,15 +173,11 @@ class TemporalFrameConverter(FrameConverter): tags = ["tag:stsci.edu:gwcs/temporal_frame-*"] types = ["gwcs.coordinate_frames.TemporalFrame"] - def from_yaml_tree(self, node, tag, ctx): from ..coordinate_frames import TemporalFrame node = self._from_yaml_tree(node, tag, ctx) return TemporalFrame(**node) - def to_yaml_tree(self, frame, tag, ctx): - return self._to_yaml_tree(frame, tag, ctx) - class StokesFrameConverter(FrameConverter): tags = ["tag:stsci.edu:gwcs/stokes_frame-*"] diff --git a/setup.cfg b/setup.cfg index f670f66d..2cdc9b10 100644 --- a/setup.cfg +++ b/setup.cfg @@ -67,11 +67,8 @@ show-response = 1 [tool:pytest] minversion = 4.6 -#testpaths = gwcs docs norecursedirs = build docs/_build .tox doctest_plus = enabled -#asdf_schema_tests_enabled = true -#asdf_schema_root = gwcs/schemas addopts = --doctest-rst filterwarnings = ignore:Models in math_functions:astropy.utils.exceptions.AstropyUserWarning From 253d17c541b29a7acc9bdd41420569e756866c8a Mon Sep 17 00:00:00 2001 From: Mihai Cara Date: Thu, 2 Dec 2021 03:54:26 -0500 Subject: [PATCH 08/12] Remove git tag from asdf-astropy in setup.cfg --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 2cdc9b10..0f254960 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,7 +24,7 @@ install_requires = numpy scipy asdf_wcs_schemas - asdf-astropy @ git+https://github.com/astropy/asdf-astropy + asdf-astropy [options.extras_require] docs = From a76ba5d1ed24fd7d57d33f6f15e4155cdc613963 Mon Sep 17 00:00:00 2001 From: Mihai Cara Date: Fri, 3 Dec 2021 10:58:54 -0500 Subject: [PATCH 09/12] Change manifest URI authority to asdf-format.org --- gwcs/extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gwcs/extension.py b/gwcs/extension.py index 64d42cbc..a1a66234 100644 --- a/gwcs/extension.py +++ b/gwcs/extension.py @@ -40,7 +40,7 @@ # The order here is important; asdf will prefer to use extensions # that occur earlier in the list. WCS_MANIFEST_URIS = [ - "asdf://stsci.edu/schemas/gwcs/manifests/gwcs-schemas-1.0", + "asdf://asdf-format.org/astronomy/gwcs/manifests/gwcs-1.0.0", ] From 520e9a5455619778474560539a2947a02f8d8fa0 Mon Sep 17 00:00:00 2001 From: William Jamieson Date: Wed, 8 Dec 2021 13:25:08 -0500 Subject: [PATCH 10/12] Removed python 3.6 and python 3.7 from CI --- .github/workflows/ci.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3eb73b2..ab5566b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,16 +33,6 @@ jobs: python-version: 3.9 toxenv: py39 - - name: Python 3.7 - runs-on: ubuntu-latest - python-version: 3.7 - toxenv: py37 - - - name: Python 3.6 - runs-on: ubuntu-latest - python-version: 3.6 - toxenv: py36 - - name: Python 3.8 with coverage runs-on: ubuntu-latest python-version: 3.8 From d03d75cc24c65e39443918b9b20edfaff54d315b Mon Sep 17 00:00:00 2001 From: William Jamieson Date: Fri, 17 Dec 2021 09:25:39 -0500 Subject: [PATCH 11/12] Initial changes needed to get this to work with new bounding_box Clean up of bounding box refactor changes. Add backwards compatibility for old bounding box Added compound bounding box support Enabled order for compound_bounding_box Added back `_get_axes_indices` it was removed by accident. Removed `_user_compound_bounding_box` remnant. Added compound bounding box tests Added suggested test assert by nden. Updated changelog. --- CHANGES.rst | 4 ++- gwcs/tests/test_wcs.py | 56 ++++++++++++++++++++++++++++++++++++++++++ gwcs/wcs.py | 17 ++++++++++++- 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index ea86df8c..f81748b6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,8 @@ Bug Fixes - Updated code in ``region.py`` with latest improvements and bug fixes from ``stsci.skypac.regions.py`` [#382] +- Enabled ``CompoundBoundingBox`` support for wcs. [#375] + New Features ^^^^^^^^^^^^ @@ -52,7 +54,7 @@ API Changes - Modified interface to ``wcs_from_points`` function to better match analogous function in astropy. [#349] -- ``Model._BoundingBox`` was renamed to ``Model.ModelBoundingBox`. [#376, #377] +- ``Model._BoundingBox`` was renamed to ``Model.ModelBoundingBox``. [#376, #377] 0.16.1 (2020-12-20) ------------------- diff --git a/gwcs/tests/test_wcs.py b/gwcs/tests/test_wcs.py index 83ec6da6..785d1dad 100644 --- a/gwcs/tests/test_wcs.py +++ b/gwcs/tests/test_wcs.py @@ -283,6 +283,62 @@ def test_bounding_box(): assert_allclose(w(-1*u.pix, -1*u.pix), (np.nan, np.nan)) +def test_compound_bounding_box(): + trans3 = models.Shift(10) & models.Scale(2) & models.Shift(-1) + pipeline = [('detector', trans3), ('sky', None)] + w = wcs.WCS(pipeline) + cbb = { + 1: ((-1, 10), (6, 15)), + 2: ((-1, 5), (3, 17)), + 3: ((-3, 7), (1, 27)), + } + if new_bbox: + # Test attaching a valid bounding box (ignoring input 'x') + w.attach_compound_bounding_box(cbb, [('x',)]) + from astropy.modeling.bounding_box import CompoundBoundingBox + cbb = CompoundBoundingBox.validate(trans3, cbb, selector_args=[('x',)], order='F') + assert w.bounding_box == cbb + assert w.bounding_box is trans3.bounding_box + + # Test evaluating + assert_allclose(w(13, 2, 1), (np.nan, np.nan, np.nan)) + assert_allclose(w(13, 2, 2), (np.nan, np.nan, np.nan)) + assert_allclose(w(13, 0, 3), (np.nan, np.nan, np.nan)) + # No bounding box for selector + with pytest.raises(RuntimeError): + w(13, 13, 4) + + # Test attaching a invalid bounding box (not ignoring input 'x') + with pytest.raises(ValueError): + w.attach_compound_bounding_box(cbb, [('x', False)]) + else: + with pytest.raises(NotImplementedError) as err: + w.attach_compound_bounding_box(cbb, [('x',)]) + assert str(err.value) == 'Compound bounding box is not supported for your version of astropy' + + # Test that bounding_box with quantities can be assigned and evaluates + trans = models.Shift(10 * u .pix) & models.Shift(2 * u.pix) + pipeline = [('detector', trans), ('sky', None)] + w = wcs.WCS(pipeline) + cbb = { + 1 * u.pix: (1 * u.pix, 5 * u.pix), + 2 * u.pix: (2 * u.pix, 6 * u.pix) + } + if new_bbox: + w.attach_compound_bounding_box(cbb, [('x1',)]) + + from astropy.modeling.bounding_box import CompoundBoundingBox + cbb = CompoundBoundingBox.validate(trans, cbb, selector_args=[('x1',)], order='F') + assert w.bounding_box == cbb + assert w.bounding_box is trans.bounding_box + + assert_allclose(w(-1*u.pix, 1*u.pix), (np.nan, np.nan)) + assert_allclose(w(7*u.pix, 2*u.pix), (np.nan, np.nan)) + else: + with pytest.raises(NotImplementedError) as err: + w.attach_compound_bounding_box(cbb, [('x1',)]) + + def test_grid_from_bounding_box(): bb = ((-1, 9.9), (6.5, 15)) x, y = grid_from_bounding_box(bb, step=[.1, .5], center=False) diff --git a/gwcs/wcs.py b/gwcs/wcs.py index 4796c791..80f7fd8b 100644 --- a/gwcs/wcs.py +++ b/gwcs/wcs.py @@ -24,6 +24,7 @@ try: from astropy.modeling.bounding_box import ModelBoundingBox as Bbox + from astropy.modeling.bounding_box import CompoundBoundingBox new_bbox = True except ImportError: from astropy.modeling.utils import _BoundingBox as Bbox @@ -1313,6 +1314,7 @@ def bounding_box(self): Return the range of acceptable values for each input axis. The order of the axes is `~gwcs.coordinate_frames.CoordinateFrame.axes_order`. """ + frames = self.available_frames transform_0 = self.get_transform(frames[0], frames[1]) try: @@ -1353,7 +1355,10 @@ def bounding_box(self, value): try: # Make sure the dimensions of the new bbox are correct. if new_bbox: - bbox = Bbox.validate(transform_0, value, order='F') + if isinstance(value, CompoundBoundingBox): + bbox = CompoundBoundingBox.validate(transform_0, value, order='F') + else: + bbox = Bbox.validate(transform_0, value, order='F') else: Bbox.validate(transform_0, value) except Exception: @@ -1373,6 +1378,16 @@ def bounding_box(self, value): self.set_transform(frames[0], frames[1], transform_0) + def attach_compound_bounding_box(self, cbbox, selector_args): + if new_bbox: + frames = self.available_frames + transform_0 = self.get_transform(frames[0], frames[1]) + + self.bounding_box = CompoundBoundingBox.validate(transform_0, cbbox, selector_args=selector_args, + order='F') + else: + raise NotImplementedError('Compound bounding box is not supported for your version of astropy') + def _get_axes_indices(self): try: axes_ind = np.argsort(self.input_frame.axes_order) From 8e57a1bff0b66021278a4c7aa83bbb140f479cb1 Mon Sep 17 00:00:00 2001 From: Nadia Dencheva Date: Wed, 22 Dec 2021 16:29:04 -0500 Subject: [PATCH 12/12] update changelog for release --- CHANGES.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index f81748b6..08e07a7f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,4 @@ -0.17.2 (Unreleased) +0.18.0 (2021-12-22) ------------------- Bug Fixes ^^^^^^^^^ @@ -6,12 +6,12 @@ Bug Fixes - Updated code in ``region.py`` with latest improvements and bug fixes from ``stsci.skypac.regions.py`` [#382] -- Enabled ``CompoundBoundingBox`` support for wcs. [#375] - New Features ^^^^^^^^^^^^ +- Enabled ``CompoundBoundingBox`` support for wcs. [#375] + - Moved schemas to standalone package ``asdf-wcs-schemas``. Reworked the serialization code to use ASDF converters. [#388]