diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6481e0c..b32c49d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,8 +27,24 @@ jobs: distribution: corretto cache: maven + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install reference file generator requirements + run: | + pip install -r test-requirements.txt + + - name: Locate Python + id: locate-python + run: + echo "python-path=`which python`" >> "$GITHUB_OUTPUT" + - name: Execute tests run: mvn clean verify + env: + GWCS_JAVA_TESTS_PYTHON_PATH: ${{ steps.locate-python.outputs.python-path }} - name: Add code coverage PR comment uses: madrapps/jacoco-report@v1.7.2 diff --git a/pom.xml b/pom.xml index 4bd672c..4005e7c 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ 11 dev 1.18.36 - 0.1-alpha-8 + 0.1-beta-1 0.44.0 3.4.0 1.7.0 diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/AsdfNodeUtils.java b/src/main/java/edu/stsci/gwcs/asdf/converter/AsdfNodeUtils.java index 027dc12..01c4126 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/AsdfNodeUtils.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/AsdfNodeUtils.java @@ -1,5 +1,6 @@ package edu.stsci.gwcs.asdf.converter; +import org.asdfformat.asdf.ndarray.DoubleNdArray; import org.asdfformat.asdf.node.AsdfNode; import java.util.List; @@ -14,7 +15,17 @@ public static String[] readStringArray(final AsdfNode node, final String key) { } public static int[] readIntArray(final AsdfNode node, final String key) { - final List list = node.getList(key, Integer.class); + final AsdfNode child = node.get(key); + if (child.isNdArray()) { + final DoubleNdArray ndArray = child.asNdArray().asDoubleNdArray(); + final int length = ndArray.getShape().get(0); + final int[] result = new int[length]; + for (int i = 0; i < length; i++) { + result[i] = (int) ndArray.get(i); + } + return result; + } + final List list = child.asList(Integer.class); final int[] result = new int[list.size()]; for (int i = 0; i < result.length; i++) { result[i] = list.get(i); @@ -23,7 +34,17 @@ public static int[] readIntArray(final AsdfNode node, final String key) { } public static double[] readDoubleArray(final AsdfNode node, final String key) { - final List list = node.getList(key, Double.class); + final AsdfNode child = node.get(key); + if (child.isNdArray()) { + final DoubleNdArray ndArray = child.asNdArray().asDoubleNdArray(); + final int length = ndArray.getShape().get(0); + final double[] result = new double[length]; + for (int i = 0; i < length; i++) { + result[i] = ndArray.get(i); + } + return result; + } + final List list = child.asList(Double.class); final double[] result = new double[list.size()]; for (int i = 0; i < result.length; i++) { result[i] = list.get(i); diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/StepConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/StepConverter.java index dc4dd93..6bc2cb9 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/StepConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/StepConverter.java @@ -12,6 +12,7 @@ public class StepConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:gwcs/step-1.1.0", + "tag:stsci.edu:gwcs/step-1.2.0", "tag:stsci.edu:gwcs/step-1.3.0" ); diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/WcsConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/WcsConverter.java index f7c01ec..2557333 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/WcsConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/WcsConverter.java @@ -10,6 +10,7 @@ public class WcsConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:gwcs/wcs-1.2.0", + "tag:stsci.edu:gwcs/wcs-1.3.0", "tag:stsci.edu:gwcs/wcs-1.4.0" ); diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/frame/CelestialFrameConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/frame/CelestialFrameConverter.java index 244c5d9..5728d6c 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/frame/CelestialFrameConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/frame/CelestialFrameConverter.java @@ -11,6 +11,7 @@ public class CelestialFrameConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:gwcs/celestial_frame-1.0.0", + "tag:stsci.edu:gwcs/celestial_frame-1.1.0", "tag:stsci.edu:gwcs/celestial_frame-1.2.0" ); diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/frame/Frame2DConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/frame/Frame2DConverter.java index 263befc..704ce4c 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/frame/Frame2DConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/frame/Frame2DConverter.java @@ -11,6 +11,7 @@ public class Frame2DConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:gwcs/frame2d-1.0.0", + "tag:stsci.edu:gwcs/frame2d-1.1.0", "tag:stsci.edu:gwcs/frame2d-1.2.0" ); diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/ConstantConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/ConstantConverter.java index cb6a274..c30da26 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/ConstantConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/ConstantConverter.java @@ -11,7 +11,8 @@ public class ConstantConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/constant-1.4.0", - "tag:stsci.edu:asdf/transform/constant-1.5.0" + "tag:stsci.edu:asdf/transform/constant-1.5.0", + "tag:stsci.edu:asdf/transform/constant-1.6.0" ); public ConstantConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/IdentityConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/IdentityConverter.java index 5deeb00..d331671 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/IdentityConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/IdentityConverter.java @@ -11,7 +11,8 @@ public class IdentityConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/identity-1.2.0", - "tag:stsci.edu:asdf/transform/identity-1.3.0" + "tag:stsci.edu:asdf/transform/identity-1.3.0", + "tag:stsci.edu:asdf/transform/identity-1.4.0" ); public IdentityConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/RemapAxesConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/RemapAxesConverter.java index e90a994..70c4568 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/RemapAxesConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/RemapAxesConverter.java @@ -12,7 +12,8 @@ public class RemapAxesConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/remap_axes-1.3.0", - "tag:stsci.edu:asdf/transform/remap_axes-1.4.0" + "tag:stsci.edu:asdf/transform/remap_axes-1.4.0", + "tag:stsci.edu:asdf/transform/remap_axes-1.5.0" ); public RemapAxesConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/AddConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/AddConverter.java index 8cf4b72..5106118 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/AddConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/AddConverter.java @@ -9,7 +9,8 @@ public class AddConverter extends BinaryCompoundConverter { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/add-1.2.0", - "tag:stsci.edu:asdf/transform/add-1.3.0" + "tag:stsci.edu:asdf/transform/add-1.3.0", + "tag:stsci.edu:asdf/transform/add-1.4.0" ); public AddConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/ComposeConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/ComposeConverter.java index fc6b223..1578e16 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/ComposeConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/ComposeConverter.java @@ -9,7 +9,8 @@ public class ComposeConverter extends BinaryCompoundConverter { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/compose-1.2.0", - "tag:stsci.edu:asdf/transform/compose-1.3.0" + "tag:stsci.edu:asdf/transform/compose-1.3.0", + "tag:stsci.edu:asdf/transform/compose-1.4.0" ); public ComposeConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/ConcatenateConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/ConcatenateConverter.java index 5b2acef..b181bc0 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/ConcatenateConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/ConcatenateConverter.java @@ -9,7 +9,8 @@ public class ConcatenateConverter extends BinaryCompoundConverter { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/concatenate-1.2.0", - "tag:stsci.edu:asdf/transform/concatenate-1.3.0" + "tag:stsci.edu:asdf/transform/concatenate-1.3.0", + "tag:stsci.edu:asdf/transform/concatenate-1.4.0" ); public ConcatenateConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/DivideConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/DivideConverter.java index c8e94de..286959c 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/DivideConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/DivideConverter.java @@ -9,7 +9,8 @@ public class DivideConverter extends BinaryCompoundConverter { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/divide-1.2.0", - "tag:stsci.edu:asdf/transform/divide-1.3.0" + "tag:stsci.edu:asdf/transform/divide-1.3.0", + "tag:stsci.edu:asdf/transform/divide-1.4.0" ); public DivideConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/FixInputsConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/FixInputsConverter.java index 0bfac2f..17c13b9 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/FixInputsConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/FixInputsConverter.java @@ -1,20 +1,21 @@ package edu.stsci.gwcs.asdf.converter.transform.compound; import edu.stsci.gwcs.asdf.GwcsAsdfSupport; +import edu.stsci.gwcs.asdf.converter.AsdfNodeUtils; import edu.stsci.gwcs.asdf.converter.ConverterBase; import edu.stsci.gwcs.transform.Transform; import edu.stsci.gwcs.transform.compound.FixInputs; import org.asdfformat.asdf.node.AsdfNode; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Set; public class FixInputsConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/fix_inputs-1.2.0", - "tag:stsci.edu:asdf/transform/fix_inputs-1.3.0" + "tag:stsci.edu:asdf/transform/fix_inputs-1.3.0", + "tag:stsci.edu:asdf/transform/fix_inputs-1.4.0" ); public FixInputsConverter(final GwcsAsdfSupport support) { @@ -27,18 +28,18 @@ public Transform fromAsdfNode(final AsdfNode node) { final Transform delegate = support().deserializeTransform(forwardNode.get(0L)); final AsdfNode mappingNode = forwardNode.get(1L); - final List keys = mappingNode.getList("keys", Integer.class); - final List values = mappingNode.getList("values", Double.class); + final int[] keys = AsdfNodeUtils.readIntArray(mappingNode, "keys"); + final double[] values = AsdfNodeUtils.readDoubleArray(mappingNode, "values"); - if (keys.size() != values.size()) { + if (keys.length != values.length) { throw new IllegalArgumentException( "fix_inputs keys and values must have the same length, got " - + keys.size() + " keys and " + values.size() + " values"); + + keys.length + " keys and " + values.length + " values"); } final Map fixedInputs = new LinkedHashMap<>(); - for (int i = 0; i < keys.size(); i++) { - fixedInputs.put(keys.get(i), values.get(i)); + for (int i = 0; i < keys.length; i++) { + fixedInputs.put(keys[i], values[i]); } return new FixInputs(delegate, fixedInputs); diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/MultiplyConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/MultiplyConverter.java index 1092780..670a223 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/MultiplyConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/MultiplyConverter.java @@ -9,7 +9,8 @@ public class MultiplyConverter extends BinaryCompoundConverter { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/multiply-1.2.0", - "tag:stsci.edu:asdf/transform/multiply-1.3.0" + "tag:stsci.edu:asdf/transform/multiply-1.3.0", + "tag:stsci.edu:asdf/transform/multiply-1.4.0" ); public MultiplyConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/PowerConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/PowerConverter.java index 7d7072d..4c6b731 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/PowerConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/PowerConverter.java @@ -9,7 +9,8 @@ public class PowerConverter extends BinaryCompoundConverter { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/power-1.2.0", - "tag:stsci.edu:asdf/transform/power-1.3.0" + "tag:stsci.edu:asdf/transform/power-1.3.0", + "tag:stsci.edu:asdf/transform/power-1.4.0" ); public PowerConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/SubtractConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/SubtractConverter.java index d07c770..73c6fea 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/SubtractConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/compound/SubtractConverter.java @@ -9,7 +9,8 @@ public class SubtractConverter extends BinaryCompoundConverter { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/subtract-1.2.0", - "tag:stsci.edu:asdf/transform/subtract-1.3.0" + "tag:stsci.edu:asdf/transform/subtract-1.3.0", + "tag:stsci.edu:asdf/transform/subtract-1.4.0" ); public SubtractConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/AffineConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/AffineConverter.java index aa2b1b4..8e68028 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/AffineConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/AffineConverter.java @@ -12,7 +12,8 @@ public class AffineConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/affine-1.3.0", - "tag:stsci.edu:asdf/transform/affine-1.4.0" + "tag:stsci.edu:asdf/transform/affine-1.4.0", + "tag:stsci.edu:asdf/transform/affine-1.5.0" ); public AffineConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/ScaleConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/ScaleConverter.java index 40a9a74..2f97433 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/ScaleConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/ScaleConverter.java @@ -11,7 +11,8 @@ public class ScaleConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/scale-1.2.0", - "tag:stsci.edu:asdf/transform/scale-1.3.0" + "tag:stsci.edu:asdf/transform/scale-1.3.0", + "tag:stsci.edu:asdf/transform/scale-1.4.0" ); public ScaleConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/ShiftConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/ShiftConverter.java index 9818962..b99e75c 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/ShiftConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/functional/ShiftConverter.java @@ -11,7 +11,8 @@ public class ShiftConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/shift-1.2.0", - "tag:stsci.edu:asdf/transform/shift-1.3.0" + "tag:stsci.edu:asdf/transform/shift-1.3.0", + "tag:stsci.edu:asdf/transform/shift-1.4.0" ); public ShiftConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/geometry/DirectionCosinesConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/geometry/DirectionCosinesConverter.java index 06812df..7a304f0 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/geometry/DirectionCosinesConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/geometry/DirectionCosinesConverter.java @@ -12,6 +12,8 @@ public class DirectionCosinesConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:gwcs/direction_cosines-1.0.0", + "tag:stsci.edu:gwcs/direction_cosines-1.1.0", + "tag:stsci.edu:gwcs/direction_cosines-1.2.0", "tag:stsci.edu:gwcs/direction_cosines-1.3.0" ); diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/geometry/SphericalCartesianConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/geometry/SphericalCartesianConverter.java index 85a0b03..5233d1b 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/geometry/SphericalCartesianConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/geometry/SphericalCartesianConverter.java @@ -12,6 +12,8 @@ public class SphericalCartesianConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:gwcs/spherical_cartesian-1.0.0", + "tag:stsci.edu:gwcs/spherical_cartesian-1.1.0", + "tag:stsci.edu:gwcs/spherical_cartesian-1.2.0", "tag:stsci.edu:gwcs/spherical_cartesian-1.3.0" ); diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/polynomial/PolynomialConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/polynomial/PolynomialConverter.java index ce981c2..2f11380 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/polynomial/PolynomialConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/polynomial/PolynomialConverter.java @@ -13,7 +13,8 @@ public class PolynomialConverter extends ConverterBase { private static final Set TAGS = Set.of( - "tag:stsci.edu:asdf/transform/polynomial-1.2.0" + "tag:stsci.edu:asdf/transform/polynomial-1.2.0", + "tag:stsci.edu:asdf/transform/polynomial-1.3.0" ); public PolynomialConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/projection/ProjectionConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/projection/ProjectionConverter.java index 8197fa9..9902497 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/projection/ProjectionConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/projection/ProjectionConverter.java @@ -42,58 +42,93 @@ public class ProjectionConverter extends ConverterBase { private static final Set TAGS = Set.of( TAG_PREFIX + "gnomonic-1.2.0", TAG_PREFIX + "gnomonic-1.3.0", + TAG_PREFIX + "gnomonic-1.4.0", TAG_PREFIX + "stereographic-1.2.0", TAG_PREFIX + "stereographic-1.3.0", + TAG_PREFIX + "stereographic-1.4.0", TAG_PREFIX + "zenithal_equal_area-1.2.0", TAG_PREFIX + "zenithal_equal_area-1.3.0", + TAG_PREFIX + "zenithal_equal_area-1.4.0", TAG_PREFIX + "zenithal_equidistant-1.2.0", TAG_PREFIX + "zenithal_equidistant-1.3.0", + TAG_PREFIX + "zenithal_equidistant-1.4.0", TAG_PREFIX + "zenithal_perspective-1.2.0", TAG_PREFIX + "zenithal_perspective-1.3.0", + TAG_PREFIX + "zenithal_perspective-1.4.0", + TAG_PREFIX + "zenithal_perspective-1.5.0", TAG_PREFIX + "slant_zenithal_perspective-1.2.0", TAG_PREFIX + "slant_zenithal_perspective-1.3.0", + TAG_PREFIX + "slant_zenithal_perspective-1.4.0", TAG_PREFIX + "slant_orthographic-1.2.0", TAG_PREFIX + "slant_orthographic-1.3.0", + TAG_PREFIX + "slant_orthographic-1.4.0", TAG_PREFIX + "airy-1.2.0", TAG_PREFIX + "airy-1.3.0", + TAG_PREFIX + "airy-1.4.0", TAG_PREFIX + "conic_perspective-1.2.0", TAG_PREFIX + "conic_perspective-1.3.0", + TAG_PREFIX + "conic_perspective-1.4.0", + TAG_PREFIX + "conic_perspective-1.5.0", TAG_PREFIX + "conic_equal_area-1.2.0", TAG_PREFIX + "conic_equal_area-1.3.0", + TAG_PREFIX + "conic_equal_area-1.4.0", + TAG_PREFIX + "conic_equal_area-1.5.0", TAG_PREFIX + "conic_equidistant-1.2.0", TAG_PREFIX + "conic_equidistant-1.3.0", + TAG_PREFIX + "conic_equidistant-1.4.0", + TAG_PREFIX + "conic_equidistant-1.5.0", TAG_PREFIX + "conic_orthomorphic-1.2.0", TAG_PREFIX + "conic_orthomorphic-1.3.0", + TAG_PREFIX + "conic_orthomorphic-1.4.0", + TAG_PREFIX + "conic_orthomorphic-1.5.0", TAG_PREFIX + "cylindrical_perspective-1.2.0", TAG_PREFIX + "cylindrical_perspective-1.3.0", + TAG_PREFIX + "cylindrical_perspective-1.4.0", + TAG_PREFIX + "cylindrical_perspective-1.5.0", TAG_PREFIX + "cylindrical_equal_area-1.2.0", TAG_PREFIX + "cylindrical_equal_area-1.3.0", + TAG_PREFIX + "cylindrical_equal_area-1.4.0", + TAG_PREFIX + "cylindrical_equal_area-1.5.0", TAG_PREFIX + "mercator-1.2.0", TAG_PREFIX + "mercator-1.3.0", + TAG_PREFIX + "mercator-1.4.0", TAG_PREFIX + "plate_carree-1.2.0", TAG_PREFIX + "plate_carree-1.3.0", + TAG_PREFIX + "plate_carree-1.4.0", TAG_PREFIX + "bonne_equal_area-1.2.0", TAG_PREFIX + "bonne_equal_area-1.3.0", + TAG_PREFIX + "bonne_equal_area-1.4.0", + TAG_PREFIX + "bonne_equal_area-1.5.0", TAG_PREFIX + "polyconic-1.2.0", TAG_PREFIX + "polyconic-1.3.0", + TAG_PREFIX + "polyconic-1.4.0", TAG_PREFIX + "hammer_aitoff-1.2.0", TAG_PREFIX + "hammer_aitoff-1.3.0", - TAG_PREFIX + "mollweide-1.2.0", - TAG_PREFIX + "mollweide-1.3.0", + TAG_PREFIX + "hammer_aitoff-1.4.0", + TAG_PREFIX + "molleweide-1.2.0", + TAG_PREFIX + "molleweide-1.3.0", + TAG_PREFIX + "molleweide-1.4.0", TAG_PREFIX + "parabolic-1.2.0", TAG_PREFIX + "parabolic-1.3.0", + TAG_PREFIX + "parabolic-1.4.0", TAG_PREFIX + "sanson_flamsteed-1.2.0", TAG_PREFIX + "sanson_flamsteed-1.3.0", + TAG_PREFIX + "sanson_flamsteed-1.4.0", TAG_PREFIX + "healpix-1.2.0", TAG_PREFIX + "healpix-1.3.0", + TAG_PREFIX + "healpix-1.4.0", TAG_PREFIX + "healpix_polar-1.2.0", TAG_PREFIX + "healpix_polar-1.3.0", + TAG_PREFIX + "healpix_polar-1.4.0", TAG_PREFIX + "quad_spherical_cube-1.2.0", TAG_PREFIX + "quad_spherical_cube-1.3.0", + TAG_PREFIX + "quad_spherical_cube-1.4.0", TAG_PREFIX + "cobe_quad_spherical_cube-1.2.0", TAG_PREFIX + "cobe_quad_spherical_cube-1.3.0", + TAG_PREFIX + "cobe_quad_spherical_cube-1.4.0", TAG_PREFIX + "tangential_spherical_cube-1.2.0", - TAG_PREFIX + "tangential_spherical_cube-1.3.0" + TAG_PREFIX + "tangential_spherical_cube-1.3.0", + TAG_PREFIX + "tangential_spherical_cube-1.4.0" ); public ProjectionConverter(final GwcsAsdfSupport support) { @@ -181,7 +216,7 @@ private static Projection createProjection(final String name, final AsdfNode nod return new Polyconic(direction); case "hammer_aitoff": return new HammerAitoff(direction); - case "mollweide": + case "molleweide": return new Mollweide(direction); case "parabolic": return new Parabolic(direction); diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/Rotate3DConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/Rotate3DConverter.java index 7b56e0f..062cc2c 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/Rotate3DConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/Rotate3DConverter.java @@ -13,7 +13,8 @@ public class Rotate3DConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/rotate3d-1.3.0", - "tag:stsci.edu:asdf/transform/rotate3d-1.4.0" + "tag:stsci.edu:asdf/transform/rotate3d-1.4.0", + "tag:stsci.edu:asdf/transform/rotate3d-1.5.0" ); public Rotate3DConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/RotateSequence3DConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/RotateSequence3DConverter.java index f2ebc9c..db1127e 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/RotateSequence3DConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/RotateSequence3DConverter.java @@ -1,17 +1,18 @@ package edu.stsci.gwcs.asdf.converter.transform.rotation; import edu.stsci.gwcs.asdf.GwcsAsdfSupport; +import edu.stsci.gwcs.asdf.converter.AsdfNodeUtils; import edu.stsci.gwcs.asdf.converter.ConverterBase; import edu.stsci.gwcs.transform.Transform; import edu.stsci.gwcs.transform.rotation.RotateSequence3D; import org.asdfformat.asdf.node.AsdfNode; -import java.util.List; import java.util.Set; public class RotateSequence3DConverter extends ConverterBase { private static final Set TAGS = Set.of( - "tag:stsci.edu:asdf/transform/rotate_sequence_3d-1.1.0" + "tag:stsci.edu:asdf/transform/rotate_sequence_3d-1.1.0", + "tag:stsci.edu:asdf/transform/rotate_sequence_3d-1.2.0" ); public RotateSequence3DConverter(final GwcsAsdfSupport support) { @@ -25,11 +26,7 @@ public Transform fromAsdfNode(final AsdfNode node) { throw new IllegalArgumentException("Unsupported rotation_type: " + rotationType); } - final List anglesList = node.getList("angles", Double.class); - final double[] angles = new double[anglesList.size()]; - for (int i = 0; i < angles.length; i++) { - angles[i] = anglesList.get(i); - } + final double[] angles = AsdfNodeUtils.readDoubleArray(node, "angles"); final String axesOrder = node.getString("axes_order"); return new RotateSequence3D(angles, axesOrder); } diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/Rotation2DConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/Rotation2DConverter.java index ea5f860..81afe9a 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/Rotation2DConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/rotation/Rotation2DConverter.java @@ -11,7 +11,8 @@ public class Rotation2DConverter extends ConverterBase { private static final Set TAGS = Set.of( "tag:stsci.edu:asdf/transform/rotate2d-1.3.0", - "tag:stsci.edu:asdf/transform/rotate2d-1.4.0" + "tag:stsci.edu:asdf/transform/rotate2d-1.4.0", + "tag:stsci.edu:asdf/transform/rotate2d-1.5.0" ); public Rotation2DConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/selector/LabelMapperDictConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/selector/LabelMapperDictConverter.java index 0df962f..b95862a 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/selector/LabelMapperDictConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/selector/LabelMapperDictConverter.java @@ -13,7 +13,10 @@ public class LabelMapperDictConverter extends ConverterBase { private static final Set TAGS = Set.of( - "tag:stsci.edu:gwcs/label_mapper-1.0.0" + "tag:stsci.edu:gwcs/label_mapper-1.0.0", + "tag:stsci.edu:gwcs/label_mapper-1.1.0", + "tag:stsci.edu:gwcs/label_mapper-1.2.0", + "tag:stsci.edu:gwcs/label_mapper-1.3.0" ); public LabelMapperDictConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/selector/RegionsSelectorConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/selector/RegionsSelectorConverter.java index c1e92f9..42f047a 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/selector/RegionsSelectorConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/selector/RegionsSelectorConverter.java @@ -13,7 +13,10 @@ public class RegionsSelectorConverter extends ConverterBase { private static final Set TAGS = Set.of( - "tag:stsci.edu:gwcs/regions_selector-1.0.0" + "tag:stsci.edu:gwcs/regions_selector-1.0.0", + "tag:stsci.edu:gwcs/regions_selector-1.1.0", + "tag:stsci.edu:gwcs/regions_selector-1.2.0", + "tag:stsci.edu:gwcs/regions_selector-1.3.0" ); public RegionsSelectorConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/GratingEquationConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/GratingEquationConverter.java index a70aa9e..056d9eb 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/GratingEquationConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/GratingEquationConverter.java @@ -11,7 +11,10 @@ public class GratingEquationConverter extends ConverterBase { private static final Set TAGS = Set.of( - "tag:stsci.edu:gwcs/grating_equation-1.0.0" + "tag:stsci.edu:gwcs/grating_equation-1.0.0", + "tag:stsci.edu:gwcs/grating_equation-1.1.0", + "tag:stsci.edu:gwcs/grating_equation-1.2.0", + "tag:stsci.edu:gwcs/grating_equation-1.3.0" ); public GratingEquationConverter(final GwcsAsdfSupport support) { @@ -21,7 +24,7 @@ public GratingEquationConverter(final GwcsAsdfSupport support) { @Override public Transform fromAsdfNode(final AsdfNode node) { final double grooveDensity = node.getDouble("groove_density"); - final int order = node.getInt("order"); + final int order = (int) node.getDouble("order"); final String output = node.getString("output"); final Transform transform; diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierGlassConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierGlassConverter.java index e5bd632..914e023 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierGlassConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierGlassConverter.java @@ -11,7 +11,10 @@ public class SellmeierGlassConverter extends ConverterBase { private static final Set TAGS = Set.of( - "tag:stsci.edu:gwcs/sellmeier_glass-1.0.0" + "tag:stsci.edu:gwcs/sellmeier_glass-1.0.0", + "tag:stsci.edu:gwcs/sellmeier_glass-1.1.0", + "tag:stsci.edu:gwcs/sellmeier_glass-1.2.0", + "tag:stsci.edu:gwcs/sellmeier_glass-1.3.0" ); public SellmeierGlassConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierZemaxConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierZemaxConverter.java index f0ccacb..a0d6cbc 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierZemaxConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierZemaxConverter.java @@ -11,7 +11,10 @@ public class SellmeierZemaxConverter extends ConverterBase { private static final Set TAGS = Set.of( - "tag:stsci.edu:gwcs/sellmeier_zemax-1.0.0" + "tag:stsci.edu:gwcs/sellmeier_zemax-1.0.0", + "tag:stsci.edu:gwcs/sellmeier_zemax-1.1.0", + "tag:stsci.edu:gwcs/sellmeier_zemax-1.2.0", + "tag:stsci.edu:gwcs/sellmeier_zemax-1.3.0" ); public SellmeierZemaxConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/Snell3DConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/Snell3DConverter.java index f756c79..dc495e7 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/Snell3DConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/Snell3DConverter.java @@ -10,7 +10,10 @@ public class Snell3DConverter extends ConverterBase { private static final Set TAGS = Set.of( - "tag:stsci.edu:gwcs/snell3d-1.0.0" + "tag:stsci.edu:gwcs/snell3d-1.0.0", + "tag:stsci.edu:gwcs/snell3d-1.1.0", + "tag:stsci.edu:gwcs/snell3d-1.2.0", + "tag:stsci.edu:gwcs/snell3d-1.3.0" ); public Snell3DConverter(final GwcsAsdfSupport support) { diff --git a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/tabular/Tabular1DConverter.java b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/tabular/Tabular1DConverter.java index 0a2e3a2..3ae953e 100644 --- a/src/main/java/edu/stsci/gwcs/asdf/converter/transform/tabular/Tabular1DConverter.java +++ b/src/main/java/edu/stsci/gwcs/asdf/converter/transform/tabular/Tabular1DConverter.java @@ -14,7 +14,9 @@ public class Tabular1DConverter extends ConverterBase { private static final Set TAGS = Set.of( - "tag:stsci.edu:asdf/transform/tabular-1.2.0" + "tag:stsci.edu:asdf/transform/tabular-1.2.0", + "tag:stsci.edu:asdf/transform/tabular-1.3.0", + "tag:stsci.edu:asdf/transform/tabular-1.4.0" ); public Tabular1DConverter(final GwcsAsdfSupport support) { diff --git a/src/test/java/edu/stsci/gwcs/asdf/GwcsAsdfSupportTest.java b/src/test/java/edu/stsci/gwcs/asdf/GwcsAsdfSupportTest.java index c139274..8d48ba8 100644 --- a/src/test/java/edu/stsci/gwcs/asdf/GwcsAsdfSupportTest.java +++ b/src/test/java/edu/stsci/gwcs/asdf/GwcsAsdfSupportTest.java @@ -212,8 +212,11 @@ void deserializeWcsWithPixelShape() { when(wcsNode.getTag()).thenReturn("tag:stsci.edu:gwcs/wcs-1.4.0"); when(wcsNode.getString("name")).thenReturn("test_wcs"); when(wcsNode.get("steps")).thenReturn(stepsNode); - when(wcsNode.getOptional("pixel_shape")).thenReturn(Optional.of(mock(AsdfNode.class))); - when(wcsNode.getList("pixel_shape", Integer.class)).thenReturn(List.of(4088, 4088)); + final AsdfNode pixelShapeNode = mock(AsdfNode.class); + when(pixelShapeNode.isNdArray()).thenReturn(false); + when(pixelShapeNode.asList(Integer.class)).thenReturn(List.of(4088, 4088)); + when(wcsNode.getOptional("pixel_shape")).thenReturn(Optional.of(pixelShapeNode)); + when(wcsNode.get("pixel_shape")).thenReturn(pixelShapeNode); final Wcs wcs = support.deserializeWcs(wcsNode); @@ -270,7 +273,10 @@ private AsdfNode mockFrame2DNode(final String name, final String[] axisNames) { when(node.getTag()).thenReturn("tag:stsci.edu:gwcs/frame2d-1.0.0"); when(node.getString("name")).thenReturn(name); when(node.getList("axes_names", String.class)).thenReturn(List.of(axisNames)); - when(node.getList("axes_order", Integer.class)).thenReturn(List.of(0, 1)); + final AsdfNode axisOrderNode = mock(AsdfNode.class); + when(axisOrderNode.isNdArray()).thenReturn(false); + when(axisOrderNode.asList(Integer.class)).thenReturn(List.of(0, 1)); + when(node.get("axes_order")).thenReturn(axisOrderNode); when(node.getList("axis_physical_types", String.class)).thenReturn(List.of("custom:x", "custom:y")); when(node.getList("unit", String.class)).thenReturn(List.of("pixel", "pixel")); return node; diff --git a/src/test/java/edu/stsci/gwcs/asdf/converter/AsdfNodeUtilsTest.java b/src/test/java/edu/stsci/gwcs/asdf/converter/AsdfNodeUtilsTest.java index b933b7b..855b4e9 100644 --- a/src/test/java/edu/stsci/gwcs/asdf/converter/AsdfNodeUtilsTest.java +++ b/src/test/java/edu/stsci/gwcs/asdf/converter/AsdfNodeUtilsTest.java @@ -31,7 +31,10 @@ void readStringArrayPreservesOrder() { @Test void readIntArrayReturnsEmptyArrayForEmptyList() { final AsdfNode node = mock(AsdfNode.class); - when(node.getList("order", Integer.class)).thenReturn(Collections.emptyList()); + final AsdfNode child = mock(AsdfNode.class); + when(node.get("order")).thenReturn(child); + when(child.isNdArray()).thenReturn(false); + when(child.asList(Integer.class)).thenReturn(Collections.emptyList()); final int[] result = AsdfNodeUtils.readIntArray(node, "order"); assertEquals(0, result.length); @@ -40,7 +43,10 @@ void readIntArrayReturnsEmptyArrayForEmptyList() { @Test void readIntArrayPreservesOrder() { final AsdfNode node = mock(AsdfNode.class); - when(node.getList("order", Integer.class)).thenReturn(List.of(2, 0, 1)); + final AsdfNode child = mock(AsdfNode.class); + when(node.get("order")).thenReturn(child); + when(child.isNdArray()).thenReturn(false); + when(child.asList(Integer.class)).thenReturn(List.of(2, 0, 1)); final int[] result = AsdfNodeUtils.readIntArray(node, "order"); assertArrayEquals(new int[]{2, 0, 1}, result); diff --git a/src/test/java/edu/stsci/gwcs/asdf/converter/frame/Frame2DConverterTest.java b/src/test/java/edu/stsci/gwcs/asdf/converter/frame/Frame2DConverterTest.java index a710d98..d30de2e 100644 --- a/src/test/java/edu/stsci/gwcs/asdf/converter/frame/Frame2DConverterTest.java +++ b/src/test/java/edu/stsci/gwcs/asdf/converter/frame/Frame2DConverterTest.java @@ -62,7 +62,10 @@ public static AsdfNode mockFrame2DNode(final String tag, when(node.getTag()).thenReturn(tag); when(node.getString("name")).thenReturn(name); when(node.getList("axes_names", String.class)).thenReturn(axisNames); - when(node.getList("axes_order", Integer.class)).thenReturn(axisOrder); + final AsdfNode axisOrderNode = mock(AsdfNode.class); + when(axisOrderNode.isNdArray()).thenReturn(false); + when(axisOrderNode.asList(Integer.class)).thenReturn(axisOrder); + when(node.get("axes_order")).thenReturn(axisOrderNode); when(node.getList("axis_physical_types", String.class)).thenReturn(axisPhysicalTypes); when(node.getList("unit", String.class)).thenReturn(units); return node; diff --git a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/RemapAxesConverterTest.java b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/RemapAxesConverterTest.java index 82265a3..52a6f36 100644 --- a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/RemapAxesConverterTest.java +++ b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/RemapAxesConverterTest.java @@ -13,11 +13,21 @@ import static org.mockito.Mockito.*; class RemapAxesConverterTest { + + private static AsdfNode mockIntListNode(final List values) { + final AsdfNode child = mock(AsdfNode.class); + when(child.isNdArray()).thenReturn(false); + when(child.asList(Integer.class)).thenReturn(values); + return child; + } + @Test void deserializeRemapAxesWithNInputs() { + final AsdfNode mappingNode = mockIntListNode(List.of(1, 0, 1)); + final AsdfNode node = mock(AsdfNode.class); when(node.getTag()).thenReturn("tag:stsci.edu:asdf/transform/remap_axes-1.3.0"); - when(node.getList("mapping", Integer.class)).thenReturn(List.of(1, 0, 1)); + when(node.get("mapping")).thenReturn(mappingNode); final AsdfNode nInputsNode = mock(AsdfNode.class); when(nInputsNode.asInt()).thenReturn(3); @@ -37,9 +47,11 @@ void deserializeRemapAxesWithNInputs() { @Test void deserializeRemapAxesWithoutNInputs() { + final AsdfNode mappingNode = mockIntListNode(List.of(1, 0)); + final AsdfNode node = mock(AsdfNode.class); when(node.getTag()).thenReturn("tag:stsci.edu:asdf/transform/remap_axes-1.4.0"); - when(node.getList("mapping", Integer.class)).thenReturn(List.of(1, 0)); + when(node.get("mapping")).thenReturn(mappingNode); when(node.getOptional("n_inputs")).thenReturn(Optional.empty()); when(node.getOptional("name")).thenReturn(Optional.empty()); when(node.getOptional("inputs")).thenReturn(Optional.empty()); diff --git a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/compound/CompoundConverterTest.java b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/compound/CompoundConverterTest.java index 7dfffa4..8c0e909 100644 --- a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/compound/CompoundConverterTest.java +++ b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/compound/CompoundConverterTest.java @@ -194,8 +194,14 @@ void deserializeFixInputs() { when(identityNode.getOptional("bounding_box")).thenReturn(Optional.empty()); final AsdfNode mappingNode = mock(AsdfNode.class); - when(mappingNode.getList("keys", Integer.class)).thenReturn(List.of(1)); - when(mappingNode.getList("values", Double.class)).thenReturn(List.of(99.0)); + final AsdfNode keysNode = mock(AsdfNode.class); + when(keysNode.isNdArray()).thenReturn(false); + when(keysNode.asList(Integer.class)).thenReturn(List.of(1)); + when(mappingNode.get("keys")).thenReturn(keysNode); + final AsdfNode valuesNode = mock(AsdfNode.class); + when(valuesNode.isNdArray()).thenReturn(false); + when(valuesNode.asList(Double.class)).thenReturn(List.of(99.0)); + when(mappingNode.get("values")).thenReturn(valuesNode); final AsdfNode forwardNode = mock(AsdfNode.class); when(forwardNode.get(0L)).thenReturn(identityNode); @@ -232,8 +238,14 @@ void deserializeFixInputsMultipleFixedInputs() { when(identityNode.getOptional("bounding_box")).thenReturn(Optional.empty()); final AsdfNode mappingNode = mock(AsdfNode.class); - when(mappingNode.getList("keys", Integer.class)).thenReturn(List.of(0, 2)); - when(mappingNode.getList("values", Double.class)).thenReturn(List.of(5.0, 15.0)); + final AsdfNode keysNode = mock(AsdfNode.class); + when(keysNode.isNdArray()).thenReturn(false); + when(keysNode.asList(Integer.class)).thenReturn(List.of(0, 2)); + when(mappingNode.get("keys")).thenReturn(keysNode); + final AsdfNode valuesNode = mock(AsdfNode.class); + when(valuesNode.isNdArray()).thenReturn(false); + when(valuesNode.asList(Double.class)).thenReturn(List.of(5.0, 15.0)); + when(mappingNode.get("values")).thenReturn(valuesNode); final AsdfNode forwardNode = mock(AsdfNode.class); when(forwardNode.get(0L)).thenReturn(identityNode); @@ -528,8 +540,14 @@ void deserializeFixInputsKeyValueSizeMismatchThrows() { when(identityNode.getOptional("bounding_box")).thenReturn(Optional.empty()); final AsdfNode mappingNode = mock(AsdfNode.class); - when(mappingNode.getList("keys", Integer.class)).thenReturn(List.of(0, 1)); - when(mappingNode.getList("values", Double.class)).thenReturn(List.of(99.0)); + final AsdfNode keysNode = mock(AsdfNode.class); + when(keysNode.isNdArray()).thenReturn(false); + when(keysNode.asList(Integer.class)).thenReturn(List.of(0, 1)); + when(mappingNode.get("keys")).thenReturn(keysNode); + final AsdfNode valuesNode = mock(AsdfNode.class); + when(valuesNode.isNdArray()).thenReturn(false); + when(valuesNode.asList(Double.class)).thenReturn(List.of(99.0)); + when(mappingNode.get("values")).thenReturn(valuesNode); final AsdfNode forwardNode = mock(AsdfNode.class); when(forwardNode.get(0L)).thenReturn(identityNode); diff --git a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/projection/ProjectionConverterTest.java b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/projection/ProjectionConverterTest.java index 5917719..b20d57f 100644 --- a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/projection/ProjectionConverterTest.java +++ b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/projection/ProjectionConverterTest.java @@ -342,7 +342,7 @@ void deserializeHammerAitoff() { @Test void deserializeMollweide() { - final AsdfNode node = projectionNode("mollweide-1.3.0", "pix2sky"); + final AsdfNode node = projectionNode("molleweide-1.3.0", "pix2sky"); assertInstanceOf(Mollweide.class, support.deserializeTransform(node)); } diff --git a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/rotation/RotateSequence3DConverterTest.java b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/rotation/RotateSequence3DConverterTest.java index d13a6ae..30db738 100644 --- a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/rotation/RotateSequence3DConverterTest.java +++ b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/rotation/RotateSequence3DConverterTest.java @@ -15,10 +15,14 @@ class RotateSequence3DConverterTest { @Test void deserializeRotateSequence3D() { + final AsdfNode anglesNode = mock(AsdfNode.class); + when(anglesNode.isNdArray()).thenReturn(false); + when(anglesNode.asList(Double.class)).thenReturn(List.of(90.0, 0.0, 0.0)); + final AsdfNode node = mock(AsdfNode.class); when(node.getTag()).thenReturn("tag:stsci.edu:asdf/transform/rotate_sequence_3d-1.1.0"); when(node.getString("rotation_type")).thenReturn("cartesian"); - when(node.getList("angles", Double.class)).thenReturn(List.of(90.0, 0.0, 0.0)); + when(node.get("angles")).thenReturn(anglesNode); when(node.getString("axes_order")).thenReturn("zyx"); when(node.getOptional("name")).thenReturn(Optional.empty()); when(node.getOptional("inputs")).thenReturn(Optional.empty()); @@ -41,10 +45,14 @@ void deserializeRotateSequence3D() { @Test void deserializeIdentityRotation() { + final AsdfNode anglesNode = mock(AsdfNode.class); + when(anglesNode.isNdArray()).thenReturn(false); + when(anglesNode.asList(Double.class)).thenReturn(List.of(0.0, 0.0, 0.0)); + final AsdfNode node = mock(AsdfNode.class); when(node.getTag()).thenReturn("tag:stsci.edu:asdf/transform/rotate_sequence_3d-1.1.0"); when(node.getString("rotation_type")).thenReturn("cartesian"); - when(node.getList("angles", Double.class)).thenReturn(List.of(0.0, 0.0, 0.0)); + when(node.get("angles")).thenReturn(anglesNode); when(node.getString("axes_order")).thenReturn("xyz"); when(node.getOptional("name")).thenReturn(Optional.empty()); when(node.getOptional("inputs")).thenReturn(Optional.empty()); diff --git a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/GratingEquationConverterTest.java b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/GratingEquationConverterTest.java index aba76e6..0299909 100644 --- a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/GratingEquationConverterTest.java +++ b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/GratingEquationConverterTest.java @@ -18,7 +18,7 @@ void deserializeAnglesFromGratingEquation3D() { final AsdfNode node = mock(AsdfNode.class); when(node.getTag()).thenReturn("tag:stsci.edu:gwcs/grating_equation-1.0.0"); when(node.getDouble("groove_density")).thenReturn(100.0); - when(node.getInt("order")).thenReturn(1); + when(node.getDouble("order")).thenReturn(1.0); when(node.getString("output")).thenReturn("angle"); when(node.getOptional("name")).thenReturn(Optional.empty()); when(node.getOptional("inputs")).thenReturn(Optional.empty()); @@ -37,7 +37,7 @@ void deserializeWavelengthFromGratingEquation() { final AsdfNode node = mock(AsdfNode.class); when(node.getTag()).thenReturn("tag:stsci.edu:gwcs/grating_equation-1.0.0"); when(node.getDouble("groove_density")).thenReturn(100.0); - when(node.getInt("order")).thenReturn(1); + when(node.getDouble("order")).thenReturn(1.0); when(node.getString("output")).thenReturn("wavelength"); when(node.getOptional("name")).thenReturn(Optional.empty()); when(node.getOptional("inputs")).thenReturn(Optional.empty()); @@ -56,7 +56,7 @@ void anglesFromGratingEquationEvaluatesCorrectly() { final AsdfNode node = mock(AsdfNode.class); when(node.getTag()).thenReturn("tag:stsci.edu:gwcs/grating_equation-1.0.0"); when(node.getDouble("groove_density")).thenReturn(100.0); - when(node.getInt("order")).thenReturn(1); + when(node.getDouble("order")).thenReturn(1.0); when(node.getString("output")).thenReturn("angle"); when(node.getOptional("name")).thenReturn(Optional.empty()); when(node.getOptional("inputs")).thenReturn(Optional.empty()); @@ -77,7 +77,7 @@ void unknownOutputThrows() { final AsdfNode node = mock(AsdfNode.class); when(node.getTag()).thenReturn("tag:stsci.edu:gwcs/grating_equation-1.0.0"); when(node.getDouble("groove_density")).thenReturn(100.0); - when(node.getInt("order")).thenReturn(1); + when(node.getDouble("order")).thenReturn(1.0); when(node.getString("output")).thenReturn("invalid"); when(node.getOptional("name")).thenReturn(Optional.empty()); when(node.getOptional("inputs")).thenReturn(Optional.empty()); diff --git a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierGlassConverterTest.java b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierGlassConverterTest.java index 6053c35..840b08f 100644 --- a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierGlassConverterTest.java +++ b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierGlassConverterTest.java @@ -13,12 +13,23 @@ import static org.mockito.Mockito.*; class SellmeierGlassConverterTest { + + private static AsdfNode mockDoubleListNode(final List values) { + final AsdfNode child = mock(AsdfNode.class); + when(child.isNdArray()).thenReturn(false); + when(child.asList(Double.class)).thenReturn(values); + return child; + } + @Test void deserializeSellmeierGlass() { + final AsdfNode bCoefNode = mockDoubleListNode(List.of(0.6961663, 0.4079426, 0.8974794)); + final AsdfNode cCoefNode = mockDoubleListNode(List.of(0.0046914826, 0.013512063, 97.934003)); + final AsdfNode node = mock(AsdfNode.class); when(node.getTag()).thenReturn("tag:stsci.edu:gwcs/sellmeier_glass-1.0.0"); - when(node.getList("B_coef", Double.class)).thenReturn(List.of(0.6961663, 0.4079426, 0.8974794)); - when(node.getList("C_coef", Double.class)).thenReturn(List.of(0.0046914826, 0.013512063, 97.934003)); + when(node.get("B_coef")).thenReturn(bCoefNode); + when(node.get("C_coef")).thenReturn(cCoefNode); when(node.getOptional("name")).thenReturn(Optional.empty()); when(node.getOptional("inputs")).thenReturn(Optional.empty()); when(node.getOptional("outputs")).thenReturn(Optional.empty()); @@ -33,10 +44,13 @@ void deserializeSellmeierGlass() { @Test void sellmeierGlassEvaluatesCorrectly() { + final AsdfNode bCoefNode = mockDoubleListNode(List.of(0.6961663, 0.4079426, 0.8974794)); + final AsdfNode cCoefNode = mockDoubleListNode(List.of(0.0046914826, 0.013512063, 97.934003)); + final AsdfNode node = mock(AsdfNode.class); when(node.getTag()).thenReturn("tag:stsci.edu:gwcs/sellmeier_glass-1.0.0"); - when(node.getList("B_coef", Double.class)).thenReturn(List.of(0.6961663, 0.4079426, 0.8974794)); - when(node.getList("C_coef", Double.class)).thenReturn(List.of(0.0046914826, 0.013512063, 97.934003)); + when(node.get("B_coef")).thenReturn(bCoefNode); + when(node.get("C_coef")).thenReturn(cCoefNode); when(node.getOptional("name")).thenReturn(Optional.empty()); when(node.getOptional("inputs")).thenReturn(Optional.empty()); when(node.getOptional("outputs")).thenReturn(Optional.empty()); diff --git a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierZemaxConverterTest.java b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierZemaxConverterTest.java index a98bc8f..498cd34 100644 --- a/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierZemaxConverterTest.java +++ b/src/test/java/edu/stsci/gwcs/asdf/converter/transform/spectroscopy/SellmeierZemaxConverterTest.java @@ -13,21 +13,39 @@ import static org.mockito.Mockito.*; class SellmeierZemaxConverterTest { - @Test - void deserializeSellmeierZemax() { + + private static AsdfNode mockDoubleListNode(final List values) { + final AsdfNode child = mock(AsdfNode.class); + when(child.isNdArray()).thenReturn(false); + when(child.asList(Double.class)).thenReturn(values); + return child; + } + + private AsdfNode mockSellmeierZemaxNode(final String tag, final double temperature) { + final AsdfNode bCoefNode = mockDoubleListNode(List.of(0.6961663, 0.4079426, 0.8974794)); + final AsdfNode cCoefNode = mockDoubleListNode(List.of(0.0046914826, 0.013512063, 97.934003)); + final AsdfNode dCoefNode = mockDoubleListNode(List.of(1.0e-5, 2.0e-5, 3.0e-5)); + final AsdfNode eCoefNode = mockDoubleListNode(List.of(4.0e-5, 5.0e-5, 0.15)); + final AsdfNode node = mock(AsdfNode.class); - when(node.getTag()).thenReturn("tag:stsci.edu:gwcs/sellmeier_zemax-1.0.0"); - when(node.getDouble("temperature")).thenReturn(296.15); + when(node.getTag()).thenReturn(tag); + when(node.getDouble("temperature")).thenReturn(temperature); when(node.getDouble("ref_temperature")).thenReturn(293.15); when(node.getDouble("ref_pressure")).thenReturn(1.0); when(node.getDouble("pressure")).thenReturn(1.0); - when(node.getList("B_coef", Double.class)).thenReturn(List.of(0.6961663, 0.4079426, 0.8974794)); - when(node.getList("C_coef", Double.class)).thenReturn(List.of(0.0046914826, 0.013512063, 97.934003)); - when(node.getList("D_coef", Double.class)).thenReturn(List.of(1.0e-5, 2.0e-5, 3.0e-5)); - when(node.getList("E_coef", Double.class)).thenReturn(List.of(4.0e-5, 5.0e-5, 0.15)); + when(node.get("B_coef")).thenReturn(bCoefNode); + when(node.get("C_coef")).thenReturn(cCoefNode); + when(node.get("D_coef")).thenReturn(dCoefNode); + when(node.get("E_coef")).thenReturn(eCoefNode); when(node.getOptional("name")).thenReturn(Optional.empty()); when(node.getOptional("inputs")).thenReturn(Optional.empty()); when(node.getOptional("outputs")).thenReturn(Optional.empty()); + return node; + } + + @Test + void deserializeSellmeierZemax() { + final AsdfNode node = mockSellmeierZemaxNode("tag:stsci.edu:gwcs/sellmeier_zemax-1.0.0", 296.15); final GwcsAsdfSupport support = new GwcsAsdfSupport(); final Transform transform = support.deserializeTransform(node); @@ -39,20 +57,7 @@ void deserializeSellmeierZemax() { final double[] result = transform.evaluate(0.5893); assertTrue(result[0] > 1.0, "Refractive index should be > 1"); - final AsdfNode refNode = mock(AsdfNode.class); - when(refNode.getTag()).thenReturn("tag:stsci.edu:gwcs/sellmeier_zemax-1.0.0"); - when(refNode.getDouble("temperature")).thenReturn(293.15); - when(refNode.getDouble("ref_temperature")).thenReturn(293.15); - when(refNode.getDouble("ref_pressure")).thenReturn(1.0); - when(refNode.getDouble("pressure")).thenReturn(1.0); - when(refNode.getList("B_coef", Double.class)).thenReturn(List.of(0.6961663, 0.4079426, 0.8974794)); - when(refNode.getList("C_coef", Double.class)).thenReturn(List.of(0.0046914826, 0.013512063, 97.934003)); - when(refNode.getList("D_coef", Double.class)).thenReturn(List.of(1.0e-5, 2.0e-5, 3.0e-5)); - when(refNode.getList("E_coef", Double.class)).thenReturn(List.of(4.0e-5, 5.0e-5, 0.15)); - when(refNode.getOptional("name")).thenReturn(Optional.empty()); - when(refNode.getOptional("inputs")).thenReturn(Optional.empty()); - when(refNode.getOptional("outputs")).thenReturn(Optional.empty()); - + final AsdfNode refNode = mockSellmeierZemaxNode("tag:stsci.edu:gwcs/sellmeier_zemax-1.0.0", 293.15); final Transform refTransform = support.deserializeTransform(refNode); final double[] refResult = refTransform.evaluate(0.5893); diff --git a/src/test/java/edu/stsci/gwcs/testing/GwcsReferenceFileType.java b/src/test/java/edu/stsci/gwcs/testing/GwcsReferenceFileType.java new file mode 100644 index 0000000..384d8c3 --- /dev/null +++ b/src/test/java/edu/stsci/gwcs/testing/GwcsReferenceFileType.java @@ -0,0 +1,32 @@ +package edu.stsci.gwcs.testing; + +import java.io.InputStream; + +public enum GwcsReferenceFileType implements ReferenceFile { + FUNCTIONAL, + POLYNOMIAL, + TABULAR, + COMPOUND, + ROTATION, + GEOMETRY, + PROJECTION, + SPECTROSCOPY, + FITS_WCS, + WCS_IMAGING, + WCS_CAL, + WCS_SPECTROSCOPY, + ROMAN_WCS, + ; + + @Override + public String getName() { + return name(); + } + + @Override + public InputStream openScript() { + return GwcsReferenceFileType.class.getResourceAsStream( + String.format("/reference-file-scripts/%s.py", name().toLowerCase()) + ); + } +} diff --git a/src/test/java/edu/stsci/gwcs/testing/ReferenceFile.java b/src/test/java/edu/stsci/gwcs/testing/ReferenceFile.java new file mode 100644 index 0000000..5bc9d4a --- /dev/null +++ b/src/test/java/edu/stsci/gwcs/testing/ReferenceFile.java @@ -0,0 +1,9 @@ +package edu.stsci.gwcs.testing; + +import java.io.IOException; +import java.io.InputStream; + +public interface ReferenceFile { + String getName(); + InputStream openScript() throws IOException; +} diff --git a/src/test/java/edu/stsci/gwcs/testing/ReferenceFileUtils.java b/src/test/java/edu/stsci/gwcs/testing/ReferenceFileUtils.java new file mode 100644 index 0000000..900f7b9 --- /dev/null +++ b/src/test/java/edu/stsci/gwcs/testing/ReferenceFileUtils.java @@ -0,0 +1,95 @@ +package edu.stsci.gwcs.testing; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class ReferenceFileUtils { + private static final String PYTHON_PATH = System.getenv("GWCS_JAVA_TESTS_PYTHON_PATH"); + private static final Path GENERATOR_SCRIPT_PATH = extractGeneratorScript(); + + private static final Map CACHE = new HashMap<>(); + + private static Path extractGeneratorScript() { + try { + final Path path = Files.createTempFile("reference_file_generator_", ".py"); + path.toFile().deleteOnExit(); + + try ( + final InputStream input = Optional.ofNullable( + ReferenceFileUtils.class.getResourceAsStream("/testing/reference_file_generator.py")) + .orElseThrow(() -> new RuntimeException("Missing testing/reference_file_generator.py")); + final OutputStream output = Files.newOutputStream(path, StandardOpenOption.CREATE) + ) { + input.transferTo(output); + } + + return path; + } catch (final IOException e) { + throw new RuntimeException("Failed to extract generator script", e); + } + } + + public static Path getPath(final ReferenceFile referenceFile) { + final String key = referenceFile.getName(); + + if (!CACHE.containsKey(key)) { + CACHE.put(key, generateTestFile(referenceFile)); + } + + return CACHE.get(key); + } + + private static Path generateTestFile(final ReferenceFile referenceFile) { + assumeTrue( + Optional.ofNullable(PYTHON_PATH) + .filter(p -> !p.isEmpty()) + .map(p -> Files.exists(Paths.get(p))) + .orElse(false), + "GWCS_JAVA_TESTS_PYTHON_PATH missing or unset" + ); + + try { + final InputStream scriptInput = referenceFile.openScript(); + assumeTrue(scriptInput != null, + "Reference script not found for " + referenceFile.getName()); + + final Path outputPath = Files.createTempFile(referenceFile.getName() + "-", ".asdf"); + outputPath.toFile().deleteOnExit(); + + final Process process = new ProcessBuilder(PYTHON_PATH, GENERATOR_SCRIPT_PATH.toString()).start(); + + try (scriptInput; final OutputStream processInput = process.getOutputStream()) { + scriptInput.transferTo(processInput); + } + + try ( + final InputStream processOutput = process.getInputStream(); + final OutputStream fileOutput = Files.newOutputStream(outputPath, StandardOpenOption.CREATE) + ) { + processOutput.transferTo(fileOutput); + } + + final int exitCode = process.waitFor(); + if (exitCode != 0) { + final String stderr = new String(process.getErrorStream().readAllBytes()); + throw new RuntimeException( + "Python generator failed for " + referenceFile.getName() + + " (exit code " + exitCode + "): " + stderr); + } + + return outputPath; + } catch (final IOException | InterruptedException e) { + throw new RuntimeException("Failed to generate reference file for " + referenceFile.getName(), e); + } + } +} diff --git a/src/test/java/edu/stsci/gwcs/testing/TransformReferenceTest.java b/src/test/java/edu/stsci/gwcs/testing/TransformReferenceTest.java new file mode 100644 index 0000000..2691903 --- /dev/null +++ b/src/test/java/edu/stsci/gwcs/testing/TransformReferenceTest.java @@ -0,0 +1,168 @@ +package edu.stsci.gwcs.testing; + +import edu.stsci.gwcs.Wcs; +import edu.stsci.gwcs.asdf.GwcsAsdfSupport; +import edu.stsci.gwcs.transform.Transform; +import org.asdfformat.asdf.Asdf; +import org.asdfformat.asdf.AsdfFile; +import org.asdfformat.asdf.node.AsdfNode; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.io.IOException; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +@Tag("reference-tests") +public class TransformReferenceTest { + + @ParameterizedTest(name = "{0}") + @EnumSource(GwcsReferenceFileType.class) + void referenceTest(final GwcsReferenceFileType referenceFileType) throws IOException { + final Path path = ReferenceFileUtils.getPath(referenceFileType); + + try (final AsdfFile asdfFile = Asdf.open(path)) { + final AsdfNode testCases = asdfFile.getTree().get("test_cases"); + + for (int i = 0; i < testCases.size(); i++) { + final AsdfNode testCase = testCases.get((long) i); + final String testCaseName = testCase.getString("name"); + final String label = referenceFileType.getName() + "/" + testCaseName; + + final double tolerance = testCase.getOptional("tolerance") + .map(AsdfNode::asDouble) + .orElse(TestingUtils.DOUBLE_TOLERANCE); + + if (testCase.getOptional("fixture_path").isPresent()) { + verifyFixtureWcs(testCase, label, tolerance); + } else if (testCase.getOptional("wcs").isPresent()) { + verifyWcs(testCase, label, tolerance); + } else { + verifyTransform(testCase, label, tolerance); + } + } + } + } + + private void verifyTransform(final AsdfNode testCase, final String label, + final double tolerance) { + final Transform transform = GwcsAsdfSupport.instance().deserializeTransform( + testCase.get("transform")); + + verifyForward(transform, testCase, label, tolerance); + + if (testCase.getOptional("has_inverse").map(AsdfNode::asBoolean).orElse(false)) { + final Transform inverse = transform.getInverse(); + verifyInverse(inverse, testCase, label, tolerance); + } + } + + private void verifyWcs(final AsdfNode testCase, final String label, + final double tolerance) { + final Wcs wcs = GwcsAsdfSupport.instance().deserializeWcs(testCase.get("wcs")); + + verifyForwardWcs(wcs, testCase, label, tolerance); + + if (testCase.getOptional("has_inverse").map(AsdfNode::asBoolean).orElse(false)) { + verifyInverseWcs(wcs, testCase, label, tolerance); + } + } + + private void verifyFixtureWcs(final AsdfNode testCase, final String label, + final double tolerance) throws IOException { + final String fixturePath = testCase.getString("fixture_path"); + final Path fixtureFile = Path.of("src/test/resources/" + fixturePath); + + final Wcs wcs; + try (final AsdfFile fixtureAsdf = Asdf.open(fixtureFile)) { + final AsdfNode wcsNode = fixtureAsdf.getTree().get("roman").get("meta").get("wcs"); + wcs = GwcsAsdfSupport.instance().deserializeWcs(wcsNode); + } + + verifyForwardWcs(wcs, testCase, label, tolerance); + + if (testCase.getOptional("has_inverse").map(AsdfNode::asBoolean).orElse(false)) { + verifyInverseWcs(wcs, testCase, label, tolerance); + } + } + + private void verifyForward(final Transform transform, final AsdfNode testCase, + final String label, final double tolerance) { + final double[][] inputs = readNdArray2D(testCase.get("forward_inputs")); + final double[][] expectedOutputs = readNdArray2D(testCase.get("forward_outputs")); + + for (int i = 0; i < inputs.length; i++) { + final double[] actual = transform.evaluate(inputs[i]); + assertArrayEquals( + expectedOutputs[i], actual, tolerance, + String.format("%s forward[%d]: input=%s", label, i, formatArray(inputs[i])) + ); + } + } + + private void verifyInverse(final Transform inverse, final AsdfNode testCase, + final String label, final double tolerance) { + final double[][] inputs = readNdArray2D(testCase.get("inverse_inputs")); + final double[][] expectedOutputs = readNdArray2D(testCase.get("inverse_outputs")); + + for (int i = 0; i < inputs.length; i++) { + final double[] actual = inverse.evaluate(inputs[i]); + assertArrayEquals( + expectedOutputs[i], actual, tolerance, + String.format("%s inverse[%d]: input=%s", label, i, formatArray(inputs[i])) + ); + } + } + + private void verifyForwardWcs(final Wcs wcs, final AsdfNode testCase, + final String label, final double tolerance) { + final double[][] inputs = readNdArray2D(testCase.get("forward_inputs")); + final double[][] expectedOutputs = readNdArray2D(testCase.get("forward_outputs")); + + for (int i = 0; i < inputs.length; i++) { + final double[] actual = wcs.evaluate(inputs[i]); + assertArrayEquals( + expectedOutputs[i], actual, tolerance, + String.format("%s forward[%d]: input=%s", label, i, formatArray(inputs[i])) + ); + } + } + + private void verifyInverseWcs(final Wcs wcs, final AsdfNode testCase, + final String label, final double tolerance) { + final double[][] inputs = readNdArray2D(testCase.get("inverse_inputs")); + final double[][] expectedOutputs = readNdArray2D(testCase.get("inverse_outputs")); + + for (int i = 0; i < inputs.length; i++) { + final double[] actual = wcs.evaluateInverse(inputs[i]); + assertArrayEquals( + expectedOutputs[i], actual, tolerance, + String.format("%s inverse[%d]: input=%s", label, i, formatArray(inputs[i])) + ); + } + } + + private static double[][] readNdArray2D(final AsdfNode node) { + final var ndArray = node.asNdArray().asDoubleNdArray(); + final int rows = ndArray.getShape().get(0); + final int cols = ndArray.getShape().get(1); + final double[][] result = new double[rows][cols]; + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + result[r][c] = ndArray.get(r, c); + } + } + return result; + } + + private static String formatArray(final double[] values) { + final StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < values.length; i++) { + if (i > 0) sb.append(", "); + sb.append(values[i]); + } + return sb.append("]").toString(); + } +} diff --git a/src/test/resources/reference-file-scripts/compound.py b/src/test/resources/reference-file-scripts/compound.py new file mode 100644 index 0000000..a2b9b54 --- /dev/null +++ b/src/test/resources/reference-file-scripts/compound.py @@ -0,0 +1,99 @@ +compose = models.Shift(2.0) | models.Scale(3.0) +concat = models.Shift(1.0) & models.Shift(2.0) +add = models.Shift(1.0) + models.Scale(2.0) +subtract = models.Shift(1.0) - models.Scale(2.0) +multiply = models.Shift(1.0) * models.Scale(2.0) +divide = models.Shift(1.0) / models.Scale(2.0) +power = models.Shift(1.0) ** models.Scale(2.0) +fix = models.fix_inputs( + models.AffineTransformation2D( + matrix=np.array([[2.0, 0.0], [0.0, 3.0]]), + translation=np.array([1.0, 2.0]), + ), + {0: 5.0}, +) + +inputs_1d = np.array([[1.0], [2.5], [-3.0], [0.5]]) +inputs_2d = np.array([[1.0, 2.0], [3.0, 4.0], [-1.0, -2.0], [0.0, 0.0]]) + +compose_outputs = np.array([[compose(x[0])] for x in inputs_1d]) +concat_outputs = np.array([list(concat(x[0], x[1])) for x in inputs_2d]) +add_outputs = np.array([[add(x[0])] for x in inputs_1d]) +subtract_outputs = np.array([[subtract(x[0])] for x in inputs_1d]) +multiply_outputs = np.array([[multiply(x[0])] for x in inputs_1d]) +divide_outputs = np.array([[divide(x[0])] for x in inputs_1d]) +power_outputs = np.array([[power(x[0])] for x in inputs_1d]) +fix_outputs = np.array([list(fix(x[0])) for x in inputs_1d]) + +compose_inv = compose.inverse +compose_inv_inputs = compose_outputs.copy() +compose_inv_outputs = np.array([[compose_inv(x[0])] for x in compose_inv_inputs]) + +concat_inv = concat.inverse +concat_inv_inputs = concat_outputs.copy() +concat_inv_outputs = np.array( + [list(concat_inv(x[0], x[1])) for x in concat_inv_inputs] +) + +af["test_cases"] = [ + { + "name": "compose", + "transform": compose, + "forward_inputs": inputs_1d, + "forward_outputs": compose_outputs, + "has_inverse": True, + "inverse_inputs": compose_inv_inputs, + "inverse_outputs": compose_inv_outputs, + }, + { + "name": "concatenate", + "transform": concat, + "forward_inputs": inputs_2d, + "forward_outputs": concat_outputs, + "has_inverse": True, + "inverse_inputs": concat_inv_inputs, + "inverse_outputs": concat_inv_outputs, + }, + { + "name": "add", + "transform": add, + "forward_inputs": inputs_1d, + "forward_outputs": add_outputs, + "has_inverse": False, + }, + { + "name": "subtract", + "transform": subtract, + "forward_inputs": inputs_1d, + "forward_outputs": subtract_outputs, + "has_inverse": False, + }, + { + "name": "multiply", + "transform": multiply, + "forward_inputs": inputs_1d, + "forward_outputs": multiply_outputs, + "has_inverse": False, + }, + { + "name": "divide", + "transform": divide, + "forward_inputs": inputs_1d, + "forward_outputs": divide_outputs, + "has_inverse": False, + }, + { + "name": "power", + "transform": power, + "forward_inputs": inputs_1d, + "forward_outputs": power_outputs, + "has_inverse": False, + }, + { + "name": "fix_inputs", + "transform": fix, + "forward_inputs": inputs_1d, + "forward_outputs": fix_outputs, + "has_inverse": False, + }, +] diff --git a/src/test/resources/reference-file-scripts/fits_wcs.py b/src/test/resources/reference-file-scripts/fits_wcs.py new file mode 100644 index 0000000..93a53a4 --- /dev/null +++ b/src/test/resources/reference-file-scripts/fits_wcs.py @@ -0,0 +1,42 @@ +crpix = np.array([512.0, 512.0]) +crval = np.array([180.0, 45.0]) +cdelt = np.array([-1e-4, 1e-4]) +pc = np.array([[1.0, 0.0], [0.0, 1.0]]) +cd = np.outer(cdelt, np.array([1.0, 1.0])) * pc + +shift_x = models.Shift(-crpix[0]) +shift_y = models.Shift(-crpix[1]) +affine = models.AffineTransformation2D(matrix=cd) +tan = models.Pix2Sky_TAN() +rot = models.RotateNative2Celestial(crval[0], crval[1], 180.0) + +transform = (shift_x & shift_y) | affine | tan | rot + +forward_inputs = np.array([ + [512.0, 512.0], + [0.0, 0.0], + [1023.0, 1023.0], + [256.0, 768.0], +]) +forward_outputs = np.array( + [list(transform(row[0], row[1])) for row in forward_inputs] +) + +inv = transform.inverse +inverse_inputs = forward_outputs.copy() +inverse_outputs = np.array( + [list(inv(row[0], row[1])) for row in inverse_inputs] +) + +af["test_cases"] = [ + { + "name": "fits_wcs_imaging", + "transform": transform, + "forward_inputs": forward_inputs, + "forward_outputs": forward_outputs, + "has_inverse": True, + "inverse_inputs": inverse_inputs, + "inverse_outputs": inverse_outputs, + "tolerance": 1e-5, + }, +] diff --git a/src/test/resources/reference-file-scripts/functional.py b/src/test/resources/reference-file-scripts/functional.py new file mode 100644 index 0000000..a4b44bd --- /dev/null +++ b/src/test/resources/reference-file-scripts/functional.py @@ -0,0 +1,55 @@ +shift = models.Shift(3.0) +scale = models.Scale(2.5) +affine = models.AffineTransformation2D( + matrix=np.array([[0.9, -0.1], [0.2, 1.1]]), + translation=np.array([5.0, -3.0]), +) + +shift_inputs = np.array([[1.0], [2.5], [-3.0], [0.0]]) +shift_outputs = np.array([[x[0] + 3.0] for x in shift_inputs]) + +scale_inputs = np.array([[1.0], [2.5], [-3.0], [0.0]]) +scale_outputs = np.array([[x[0] * 2.5] for x in scale_inputs]) + +affine_inputs = np.array([[1.0, 2.0], [0.0, 0.0], [-1.5, 3.5], [10.0, -5.0]]) +affine_outputs = np.array([affine(x[0], x[1]) for x in affine_inputs]) + +shift_inv_inputs = shift_outputs.copy() +shift_inv_outputs = shift_inputs.copy() + +scale_inv_inputs = scale_outputs.copy() +scale_inv_outputs = scale_inputs.copy() + +affine_inv = affine.inverse +affine_inv_inputs = affine_outputs.copy() +affine_inv_outputs = np.array([affine_inv(x[0], x[1]) for x in affine_inv_inputs]) + +af["test_cases"] = [ + { + "name": "shift", + "transform": shift, + "forward_inputs": shift_inputs, + "forward_outputs": shift_outputs, + "has_inverse": True, + "inverse_inputs": shift_inv_inputs, + "inverse_outputs": shift_inv_outputs, + }, + { + "name": "scale", + "transform": scale, + "forward_inputs": scale_inputs, + "forward_outputs": scale_outputs, + "has_inverse": True, + "inverse_inputs": scale_inv_inputs, + "inverse_outputs": scale_inv_outputs, + }, + { + "name": "affine", + "transform": affine, + "forward_inputs": affine_inputs, + "forward_outputs": affine_outputs, + "has_inverse": True, + "inverse_inputs": affine_inv_inputs, + "inverse_outputs": affine_inv_outputs, + }, +] diff --git a/src/test/resources/reference-file-scripts/geometry.py b/src/test/resources/reference-file-scripts/geometry.py new file mode 100644 index 0000000..d3d0173 --- /dev/null +++ b/src/test/resources/reference-file-scripts/geometry.py @@ -0,0 +1,99 @@ +from gwcs.geometry import ( + CartesianToSpherical, + FromDirectionCosines, + SphericalToCartesian, + ToDirectionCosines, +) + +s2c = SphericalToCartesian(wrap_lon_at=180) +c2s = CartesianToSpherical(wrap_lon_at=180) +to_dc = ToDirectionCosines() +from_dc = FromDirectionCosines() + +s2c_inputs = np.array([[45.0, 30.0], [-60.0, 15.0], [120.0, -45.0], [0.0, 0.0]]) +s2c_outputs = np.array( + [list(s2c(row[0], row[1])) for row in s2c_inputs] +) + +c2s_inputs = np.array([ + [0.6123724, 0.6123724, 0.5], + [-0.3535534, 0.6123724, 0.7071068], + [0.0, -0.7071068, 0.7071068], + [1.0, 0.0, 0.0], +]) +c2s_outputs = np.array( + [list(c2s(row[0], row[1], row[2])) for row in c2s_inputs] +) + +to_dc_inputs = np.array([[0.5, 0.3, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [-0.5, 0.5, 0.0]]) +to_dc_outputs = np.array( + [list(to_dc(row[0], row[1], row[2])) for row in to_dc_inputs] +) + +from_dc_inputs = to_dc_outputs.copy() +from_dc_outputs = np.array( + [list(from_dc(row[0], row[1], row[2], row[3])) for row in from_dc_inputs] +) + +s2c_inv = s2c.inverse +s2c_inv_inputs = s2c_outputs.copy() +s2c_inv_outputs = np.array( + [list(s2c_inv(row[0], row[1], row[2])) for row in s2c_inv_inputs] +) + +c2s_inv = c2s.inverse +c2s_inv_inputs = c2s_outputs.copy() +c2s_inv_outputs = np.array( + [list(c2s_inv(row[0], row[1])) for row in c2s_inv_inputs] +) + +to_dc_inv = to_dc.inverse +to_dc_inv_inputs = to_dc_outputs.copy() +to_dc_inv_outputs = np.array( + [list(to_dc_inv(row[0], row[1], row[2], row[3])) for row in to_dc_inv_inputs] +) + +from_dc_inv = from_dc.inverse +from_dc_inv_inputs = from_dc_outputs.copy() +from_dc_inv_outputs = np.array( + [list(from_dc_inv(row[0], row[1], row[2])) for row in from_dc_inv_inputs] +) + +af["test_cases"] = [ + { + "name": "spherical_to_cartesian", + "transform": s2c, + "forward_inputs": s2c_inputs, + "forward_outputs": s2c_outputs, + "has_inverse": True, + "inverse_inputs": s2c_inv_inputs, + "inverse_outputs": s2c_inv_outputs, + }, + { + "name": "cartesian_to_spherical", + "transform": c2s, + "forward_inputs": c2s_inputs, + "forward_outputs": c2s_outputs, + "has_inverse": True, + "inverse_inputs": c2s_inv_inputs, + "inverse_outputs": c2s_inv_outputs, + }, + { + "name": "to_direction_cosines", + "transform": to_dc, + "forward_inputs": to_dc_inputs, + "forward_outputs": to_dc_outputs, + "has_inverse": True, + "inverse_inputs": to_dc_inv_inputs, + "inverse_outputs": to_dc_inv_outputs, + }, + { + "name": "from_direction_cosines", + "transform": from_dc, + "forward_inputs": from_dc_inputs, + "forward_outputs": from_dc_outputs, + "has_inverse": True, + "inverse_inputs": from_dc_inv_inputs, + "inverse_outputs": from_dc_inv_outputs, + }, +] diff --git a/src/test/resources/reference-file-scripts/polynomial.py b/src/test/resources/reference-file-scripts/polynomial.py new file mode 100644 index 0000000..27c6e35 --- /dev/null +++ b/src/test/resources/reference-file-scripts/polynomial.py @@ -0,0 +1,29 @@ +poly1d = models.Polynomial1D( + degree=3, c0=1.0, c1=-2.0, c2=0.5, c3=0.1, domain=[-5, 5], window=[-1, 1] +) +poly2d = models.Polynomial2D( + degree=2, c0_0=1.0, c1_0=2.0, c0_1=-1.0, c2_0=0.3, c1_1=0.5, c0_2=-0.2 +) + +poly1d_inputs = np.array([[0.0], [2.5], [-3.0], [4.5]]) +poly1d_outputs = np.array([[poly1d(row[0])] for row in poly1d_inputs]) + +poly2d_inputs = np.array([[1.0, 2.0], [0.0, 0.0], [-1.5, 3.5], [2.0, -1.0]]) +poly2d_outputs = np.array([[poly2d(row[0], row[1])] for row in poly2d_inputs]) + +af["test_cases"] = [ + { + "name": "polynomial1d", + "transform": poly1d, + "forward_inputs": poly1d_inputs, + "forward_outputs": poly1d_outputs, + "has_inverse": False, + }, + { + "name": "polynomial2d", + "transform": poly2d, + "forward_inputs": poly2d_inputs, + "forward_outputs": poly2d_outputs, + "has_inverse": False, + }, +] diff --git a/src/test/resources/reference-file-scripts/projection.py b/src/test/resources/reference-file-scripts/projection.py new file mode 100644 index 0000000..fade5f1 --- /dev/null +++ b/src/test/resources/reference-file-scripts/projection.py @@ -0,0 +1,85 @@ +gnomonic = models.Pix2Sky_TAN() +stereographic = models.Pix2Sky_STG() +mollweide = models.Pix2Sky_MOL() +healpix = models.Pix2Sky_HPX() + +gnomonic_inputs = np.array([[0.0, 0.0], [5.0, 5.0], [-3.0, 7.0], [10.0, -5.0]]) +gnomonic_outputs = np.array([list(gnomonic(row[0], row[1])) for row in gnomonic_inputs]) + +stereographic_inputs = np.array([[0.0, 0.0], [10.0, 10.0], [-5.0, 15.0], [20.0, -10.0]]) +stereographic_outputs = np.array( + [list(stereographic(row[0], row[1])) for row in stereographic_inputs] +) + +mollweide_inputs = np.array([[0.0, 0.0], [10.0, 5.0], [-15.0, -8.0], [5.0, 12.0]]) +mollweide_outputs = np.array( + [list(mollweide(row[0], row[1])) for row in mollweide_inputs] +) + +healpix_inputs = np.array([[0.0, 0.0], [10.0, 10.0], [-10.0, 20.0], [5.0, -15.0]]) +healpix_outputs = np.array( + [list(healpix(row[0], row[1])) for row in healpix_inputs] +) + +gnomonic_inv = gnomonic.inverse +gnomonic_inv_inputs = gnomonic_outputs.copy() +gnomonic_inv_outputs = np.array( + [list(gnomonic_inv(row[0], row[1])) for row in gnomonic_inv_inputs] +) + +stereographic_inv = stereographic.inverse +stereographic_inv_inputs = stereographic_outputs.copy() +stereographic_inv_outputs = np.array( + [list(stereographic_inv(row[0], row[1])) for row in stereographic_inv_inputs] +) + +mollweide_inv = mollweide.inverse +mollweide_inv_inputs = mollweide_outputs.copy() +mollweide_inv_outputs = np.array( + [list(mollweide_inv(row[0], row[1])) for row in mollweide_inv_inputs] +) + +healpix_inv = healpix.inverse +healpix_inv_inputs = healpix_outputs.copy() +healpix_inv_outputs = np.array( + [list(healpix_inv(row[0], row[1])) for row in healpix_inv_inputs] +) + +af["test_cases"] = [ + { + "name": "gnomonic", + "transform": gnomonic, + "forward_inputs": gnomonic_inputs, + "forward_outputs": gnomonic_outputs, + "has_inverse": True, + "inverse_inputs": gnomonic_inv_inputs, + "inverse_outputs": gnomonic_inv_outputs, + }, + { + "name": "stereographic", + "transform": stereographic, + "forward_inputs": stereographic_inputs, + "forward_outputs": stereographic_outputs, + "has_inverse": True, + "inverse_inputs": stereographic_inv_inputs, + "inverse_outputs": stereographic_inv_outputs, + }, + { + "name": "mollweide", + "transform": mollweide, + "forward_inputs": mollweide_inputs, + "forward_outputs": mollweide_outputs, + "has_inverse": True, + "inverse_inputs": mollweide_inv_inputs, + "inverse_outputs": mollweide_inv_outputs, + }, + { + "name": "healpix", + "transform": healpix, + "forward_inputs": healpix_inputs, + "forward_outputs": healpix_outputs, + "has_inverse": True, + "inverse_inputs": healpix_inv_inputs, + "inverse_outputs": healpix_inv_outputs, + }, +] diff --git a/src/test/resources/reference-file-scripts/roman_wcs.py b/src/test/resources/reference-file-scripts/roman_wcs.py new file mode 100644 index 0000000..da0d8b2 --- /dev/null +++ b/src/test/resources/reference-file-scripts/roman_wcs.py @@ -0,0 +1,76 @@ +import os +import asdf + +script_dir = os.path.dirname(os.path.abspath(__file__)) if '__file__' in dir() else '.' +resource_dir = os.path.join(script_dir, '..', 'roman-reference-files') +if not os.path.isdir(resource_dir): + resource_dir = os.path.join('src', 'test', 'resources', 'roman-reference-files') + +test_cases = [] + +CAL_FILENAME = 'r0034001001001001001_0001_wfi01_f062_cal_wcs_only.asdf' +COADD_FILENAME = 'r0000101001001001001_p_v01001001001001_045p86x47y51_f158_coadd_wcs_only.asdf' + +cal_path = os.path.join(resource_dir, CAL_FILENAME) +if os.path.exists(cal_path): + with asdf.open(cal_path) as cal_asdf: + cal_wcs = cal_asdf['roman']['meta']['wcs'] + + cal_inputs = np.array([ + [1024.0, 1024.0], + [0.0, 0.0], + [2047.0, 2047.0], + [512.0, 1536.0], + ]) + cal_outputs = np.array([list(cal_wcs(row[0], row[1])) for row in cal_inputs]) + + cal_inv = cal_wcs.backward_transform + cal_inv_inputs = cal_outputs.copy() + cal_inv_outputs = np.array( + [list(cal_inv(row[0], row[1])) for row in cal_inv_inputs] + ) + + test_cases.append({ + "name": "roman_cal", + "fixture_path": "roman-reference-files/" + CAL_FILENAME, + "forward_inputs": cal_inputs, + "forward_outputs": cal_outputs, + "has_inverse": True, + "inverse_inputs": cal_inv_inputs, + "inverse_outputs": cal_inv_outputs, + "tolerance": 1e-10, + }) + +coadd_path = os.path.join(resource_dir, COADD_FILENAME) +if os.path.exists(coadd_path): + with asdf.open(coadd_path) as coadd_asdf: + coadd_wcs = coadd_asdf['roman']['meta']['wcs'] + + coadd_inputs = np.array([ + [512.0, 512.0], + [0.0, 0.0], + [1023.0, 1023.0], + [256.0, 768.0], + ]) + coadd_outputs = np.array( + [list(coadd_wcs(row[0], row[1])) for row in coadd_inputs] + ) + + coadd_inv = coadd_wcs.backward_transform + coadd_inv_inputs = coadd_outputs.copy() + coadd_inv_outputs = np.array( + [list(coadd_inv(row[0], row[1])) for row in coadd_inv_inputs] + ) + + test_cases.append({ + "name": "roman_coadd", + "fixture_path": "roman-reference-files/" + COADD_FILENAME, + "forward_inputs": coadd_inputs, + "forward_outputs": coadd_outputs, + "has_inverse": True, + "inverse_inputs": coadd_inv_inputs, + "inverse_outputs": coadd_inv_outputs, + "tolerance": 1e-10, + }) + +af["test_cases"] = test_cases diff --git a/src/test/resources/reference-file-scripts/rotation.py b/src/test/resources/reference-file-scripts/rotation.py new file mode 100644 index 0000000..508ef72 --- /dev/null +++ b/src/test/resources/reference-file-scripts/rotation.py @@ -0,0 +1,114 @@ +rotation2d = models.Rotation2D(angle=35.0) + +euler_angle = models.EulerAngleRotation(23.0, 45.0, 67.0, axes_order='xyz') + +rotate_seq = models.RotationSequence3D(angles=[10.0, 20.0, 30.0], axes_order='xyz') + +native2celestial = models.RotateNative2Celestial(lon=45.0, lat=60.0, lon_pole=180.0) + +celestial2native = models.RotateCelestial2Native(lon=45.0, lat=60.0, lon_pole=180.0) + +rotation2d_inputs = np.array([[1.0, 0.0], [0.0, 1.0], [3.0, 4.0], [-2.0, 1.5]]) +rotation2d_outputs = np.array( + [list(rotation2d(row[0], row[1])) for row in rotation2d_inputs] +) + +euler_inputs = np.array([[10.0, 20.0], [30.0, 40.0], [-15.0, 25.0], [45.0, -10.0]]) +euler_outputs = np.array( + [list(euler_angle(row[0], row[1])) for row in euler_inputs] +) + +rotate_seq_inputs = np.array( + [[10.0, 20.0, 30.0], [30.0, 40.0, 50.0], [-15.0, 25.0, 10.0], [45.0, -10.0, 20.0]] +) +rotate_seq_outputs = np.array( + [list(rotate_seq(row[0], row[1], row[2])) for row in rotate_seq_inputs] +) + +n2c_inputs = np.array([[0.0, 0.0], [10.0, 20.0], [-15.0, 30.0], [45.0, -10.0]]) +n2c_outputs = np.array( + [list(native2celestial(row[0], row[1])) for row in n2c_inputs] +) + +c2n_inputs = np.array([[0.0, 0.0], [10.0, 20.0], [-15.0, 30.0], [45.0, -10.0]]) +c2n_outputs = np.array( + [list(celestial2native(row[0], row[1])) for row in c2n_inputs] +) + +rotation2d_inv = rotation2d.inverse +rotation2d_inv_inputs = rotation2d_outputs.copy() +rotation2d_inv_outputs = np.array( + [list(rotation2d_inv(row[0], row[1])) for row in rotation2d_inv_inputs] +) + +euler_inv = euler_angle.inverse +euler_inv_inputs = euler_outputs.copy() +euler_inv_outputs = np.array( + [list(euler_inv(row[0], row[1])) for row in euler_inv_inputs] +) + +rotate_seq_inv = rotate_seq.inverse +rotate_seq_inv_inputs = rotate_seq_outputs.copy() +rotate_seq_inv_outputs = np.array( + [list(rotate_seq_inv(row[0], row[1], row[2])) for row in rotate_seq_inv_inputs] +) + +n2c_inv = native2celestial.inverse +n2c_inv_inputs = n2c_outputs.copy() +n2c_inv_outputs = np.array( + [list(n2c_inv(row[0], row[1])) for row in n2c_inv_inputs] +) + +c2n_inv = celestial2native.inverse +c2n_inv_inputs = c2n_outputs.copy() +c2n_inv_outputs = np.array( + [list(c2n_inv(row[0], row[1])) for row in c2n_inv_inputs] +) + +af["test_cases"] = [ + { + "name": "rotation2d", + "transform": rotation2d, + "forward_inputs": rotation2d_inputs, + "forward_outputs": rotation2d_outputs, + "has_inverse": True, + "inverse_inputs": rotation2d_inv_inputs, + "inverse_outputs": rotation2d_inv_outputs, + }, + { + "name": "euler_angle_rotation", + "transform": euler_angle, + "forward_inputs": euler_inputs, + "forward_outputs": euler_outputs, + "has_inverse": True, + "inverse_inputs": euler_inv_inputs, + "inverse_outputs": euler_inv_outputs, + }, + { + "name": "rotate_sequence_3d", + "transform": rotate_seq, + "forward_inputs": rotate_seq_inputs, + "forward_outputs": rotate_seq_outputs, + "has_inverse": True, + "inverse_inputs": rotate_seq_inv_inputs, + "inverse_outputs": rotate_seq_inv_outputs, + }, + { + "name": "rotate_native2celestial", + "transform": native2celestial, + "forward_inputs": n2c_inputs, + "forward_outputs": n2c_outputs, + "has_inverse": True, + "inverse_inputs": n2c_inv_inputs, + "inverse_outputs": n2c_inv_outputs, + }, + { + "name": "rotate_celestial2native", + "transform": celestial2native, + "forward_inputs": c2n_inputs, + "forward_outputs": c2n_outputs, + "has_inverse": True, + "inverse_inputs": c2n_inv_inputs, + "inverse_outputs": c2n_inv_outputs, + }, +] diff --git a/src/test/resources/reference-file-scripts/spectroscopy.py b/src/test/resources/reference-file-scripts/spectroscopy.py new file mode 100644 index 0000000..30dbb45 --- /dev/null +++ b/src/test/resources/reference-file-scripts/spectroscopy.py @@ -0,0 +1,103 @@ +from gwcs import spectroscopy as sp + +sellmeier_glass = sp.SellmeierGlass( + B_coef=[1.03961212, 0.23179234, 1.01046945], + C_coef=[6.00069867e-3, 2.00179144e-2, 1.03560653e2], +) + +glass_inputs = np.array([[0.5], [0.6], [0.8], [1.0]]) +glass_outputs = np.array([[sellmeier_glass(row[0])] for row in glass_inputs]) + +sellmeier_zemax = sp.SellmeierZemax( + temperature=22.0, + ref_temperature=20.0, + ref_pressure=101325.0, + pressure=101325.0, + B_coef=[1.03961212, 0.23179234, 1.01046945], + C_coef=[6.00069867e-3, 2.00179144e-2, 1.03560653e2], + D_coef=[1.86e-6, 1.31e-8, -1.37e-11], + E_coef=[4.34e-7, 1.15e-9, 0.17], +) + +zemax_inputs = np.array([[0.5], [0.6], [0.8], [1.0]]) +zemax_outputs = np.array( + [[sellmeier_zemax(np.array([row[0]]))[0]] for row in zemax_inputs] +) + +snell3d = sp.Snell3D() + +snell_inputs = np.array([ + [1.5, 0.3, 0.4, 0.0], + [1.0, 0.3, 0.4, 0.0], + [1.2, 0.1, 0.2, 0.0], + [2.0, 0.5, 0.3, 0.0], +]) +snell_outputs = np.array( + [list(snell3d(row[0], row[1], row[2], row[3])) for row in snell_inputs] +) + +angles_from_grating = sp.AnglesFromGratingEquation3D( + groove_density=2700.0, spectral_order=-1, +) + +angles_inputs = np.array([ + [2e-6, 0.1, 0.1], + [1e-6, 0.05, 0.05], + [5e-7, 0.2, 0.1], + [3e-6, 0.01, 0.02], +]) +angles_outputs = np.array( + [list(angles_from_grating(row[0], row[1], row[2])) for row in angles_inputs] +) + +wavelength_from_grating = sp.WavelengthFromGratingEquation( + groove_density=2700.0, spectral_order=-1, +) + +wl_inputs = np.array([ + [0.1, 0.2], + [0.05, 0.15], + [0.3, 0.1], + [0.01, 0.02], +]) +wl_outputs = np.array( + [[wavelength_from_grating(row[0], row[1])] for row in wl_inputs] +) + +af["test_cases"] = [ + { + "name": "sellmeier_glass", + "transform": sellmeier_glass, + "forward_inputs": glass_inputs, + "forward_outputs": glass_outputs, + "has_inverse": False, + }, + { + "name": "sellmeier_zemax", + "transform": sellmeier_zemax, + "forward_inputs": zemax_inputs, + "forward_outputs": zemax_outputs, + "has_inverse": False, + }, + { + "name": "snell3d", + "transform": snell3d, + "forward_inputs": snell_inputs, + "forward_outputs": snell_outputs, + "has_inverse": False, + }, + { + "name": "angles_from_grating_equation_3d", + "transform": angles_from_grating, + "forward_inputs": angles_inputs, + "forward_outputs": angles_outputs, + "has_inverse": False, + }, + { + "name": "wavelength_from_grating_equation", + "transform": wavelength_from_grating, + "forward_inputs": wl_inputs, + "forward_outputs": wl_outputs, + "has_inverse": False, + }, +] diff --git a/src/test/resources/reference-file-scripts/tabular.py b/src/test/resources/reference-file-scripts/tabular.py new file mode 100644 index 0000000..e651221 --- /dev/null +++ b/src/test/resources/reference-file-scripts/tabular.py @@ -0,0 +1,27 @@ +tabular1d = models.Tabular1D( + points=np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0]), + lookup_table=np.array([0.0, 2.1, 3.8, 6.2, 7.9, 10.5]), + method="linear", + bounds_error=True, +) + +tabular1d_inputs = np.array([[0.5], [1.7], [2.3], [4.2]]) +tabular1d_outputs = np.array([[tabular1d(row[0])] for row in tabular1d_inputs]) + +tabular1d_inv = tabular1d.inverse +tabular1d_inv_inputs = tabular1d_outputs.copy() +tabular1d_inv_outputs = np.array( + [[tabular1d_inv(row[0])] for row in tabular1d_inv_inputs] +) + +af["test_cases"] = [ + { + "name": "tabular1d", + "transform": tabular1d, + "forward_inputs": tabular1d_inputs, + "forward_outputs": tabular1d_outputs, + "has_inverse": True, + "inverse_inputs": tabular1d_inv_inputs, + "inverse_outputs": tabular1d_inv_outputs, + }, +] diff --git a/src/test/resources/reference-file-scripts/wcs_cal.py b/src/test/resources/reference-file-scripts/wcs_cal.py new file mode 100644 index 0000000..88972e6 --- /dev/null +++ b/src/test/resources/reference-file-scripts/wcs_cal.py @@ -0,0 +1,67 @@ +detector = cf.Frame2D(name="detector", axes_order=(0, 1)) +v2v3 = cf.Frame2D(name="v2v3", axes_order=(0, 1)) +v2v3vacorr = cf.Frame2D(name="v2v3vacorr", axes_order=(0, 1)) +v2v3corr = cf.Frame2D(name="v2v3corr", axes_order=(0, 1)) +sky = cf.CelestialFrame(reference_frame=coord.ICRS(), axes_order=(0, 1)) + +distortion = ( + (models.Shift(-1024.0) & models.Shift(-1024.0)) + | models.AffineTransformation2D( + matrix=np.array([[0.11, 0.005], [-0.005, 0.11]]), + translation=np.array([0.0, 0.0]), + ) +) + +velocity_correction = models.AffineTransformation2D( + matrix=np.array([[1.001, 0.0], [0.0, 0.999]]), + translation=np.array([0.0, 0.0]), +) + +tpc_correction = models.AffineTransformation2D( + matrix=np.array([[1.0, 0.002], [-0.002, 1.0]]), + translation=np.array([0.1, -0.05]), +) + +tan = models.Pix2Sky_TAN() +celestial_rotation = models.RotateNative2Celestial( + lon=53.16, lat=-27.79, lon_pole=180.0 +) +to_sky = ( + (models.Scale(1.0 / 3600.0) & models.Scale(1.0 / 3600.0)) + | tan + | celestial_rotation +) + +pipeline = [ + (detector, distortion), + (v2v3, velocity_correction), + (v2v3vacorr, tpc_correction), + (v2v3corr, to_sky), + (sky, None), +] +w = gwcs_wcs.WCS(pipeline) + +inputs = np.array([ + [1024.0, 1024.0], + [512.0, 512.0], + [100.0, 200.0], + [1500.0, 1800.0], +]) +outputs = np.array([list(w(row[0], row[1])) for row in inputs]) + +inv = w.backward_transform +inv_inputs = outputs.copy() +inv_outputs = np.array([list(inv(row[0], row[1])) for row in inv_inputs]) + +af["test_cases"] = [ + { + "name": "cal_pipeline", + "wcs": w, + "forward_inputs": inputs, + "forward_outputs": outputs, + "has_inverse": True, + "inverse_inputs": inv_inputs, + "inverse_outputs": inv_outputs, + "tolerance": 1e-9, + }, +] diff --git a/src/test/resources/reference-file-scripts/wcs_imaging.py b/src/test/resources/reference-file-scripts/wcs_imaging.py new file mode 100644 index 0000000..e6cfcce --- /dev/null +++ b/src/test/resources/reference-file-scripts/wcs_imaging.py @@ -0,0 +1,28 @@ +detector = cf.Frame2D(name="detector", axes_order=(0, 1)) +sky = cf.CelestialFrame(reference_frame=coord.ICRS(), axes_order=(0, 1)) + +shift = models.Shift(1.0) & models.Shift(2.0) +scale = models.Scale(0.1) & models.Scale(0.1) +transform = shift | scale + +pipeline = [(detector, transform), (sky, None)] +w = gwcs_wcs.WCS(pipeline) + +inputs = np.array([[0.0, 0.0], [10.0, 20.0], [50.0, 50.0], [100.0, 100.0]]) +outputs = np.array([list(w(row[0], row[1])) for row in inputs]) + +inv = w.backward_transform +inv_inputs = outputs.copy() +inv_outputs = np.array([list(inv(row[0], row[1])) for row in inv_inputs]) + +af["test_cases"] = [ + { + "name": "simple_imaging", + "wcs": w, + "forward_inputs": inputs, + "forward_outputs": outputs, + "has_inverse": True, + "inverse_inputs": inv_inputs, + "inverse_outputs": inv_outputs, + }, +] diff --git a/src/test/resources/reference-file-scripts/wcs_spectroscopy.py b/src/test/resources/reference-file-scripts/wcs_spectroscopy.py new file mode 100644 index 0000000..a33e3a8 --- /dev/null +++ b/src/test/resources/reference-file-scripts/wcs_spectroscopy.py @@ -0,0 +1,44 @@ +from gwcs import spectroscopy as sp + +grating = sp.WavelengthFromGratingEquation(groove_density=2700.0, spectral_order=-1) +glass = sp.SellmeierGlass( + B_coef=[1.03961212, 0.23179234, 1.01046945], + C_coef=[6.00069867e-3, 2.00179144e-2, 1.03560653e2], +) + +grating_inputs = np.array([ + [0.1, 0.2], + [0.05, 0.15], + [-0.1, 0.3], + [0.15, -0.1], +]) +grating_outputs = np.array( + [[grating(row[0], row[1])] for row in grating_inputs] +) + +glass_inputs = np.array([ + [0.5], + [0.6], + [0.7], + [0.8], +]) +glass_outputs = np.array( + [[glass(row[0])] for row in glass_inputs] +) + +af["test_cases"] = [ + { + "name": "wavelength_from_grating_equation", + "transform": grating, + "forward_inputs": grating_inputs, + "forward_outputs": grating_outputs, + "has_inverse": False, + }, + { + "name": "sellmeier_glass", + "transform": glass, + "forward_inputs": glass_inputs, + "forward_outputs": glass_outputs, + "has_inverse": False, + }, +] diff --git a/src/test/resources/testing/reference_file_generator.py b/src/test/resources/testing/reference_file_generator.py new file mode 100644 index 0000000..396d1b5 --- /dev/null +++ b/src/test/resources/testing/reference_file_generator.py @@ -0,0 +1,35 @@ +import sys +from io import BytesIO + +import asdf +import numpy as np +from astropy import coordinates as coord +from astropy.modeling import models + +import gwcs.coordinate_frames as cf +import gwcs.wcs as gwcs_wcs + + +def main(): + af = asdf.AsdfFile() + + script = sys.stdin.read() + env = { + "af": af, + "np": np, + "models": models, + "cf": cf, + "gwcs_wcs": gwcs_wcs, + "coord": coord, + } + exec(script, env) + + buffer = BytesIO() + af.write_to(buffer) + + buffer.seek(0) + sys.stdout.buffer.write(buffer.read()) + + +if __name__ == "__main__": + main() diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..0f2743d --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,3 @@ +gwcs +astropy +numpy