This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit b677477aec0b6aba473ec838709ad4092b276312
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Sat Jan 29 16:02:27 2022 +0100

    Add tests for north pole rotation.
---
 .../referencing/provider/RotatedNorthPole.java     |   5 +
 .../referencing/provider/RotatedSouthPole.java     |   4 +
 .../operation/transform/RotatedPole.java           |   9 +-
 .../operation/transform/RotatedPoleTest.java       | 115 ++++++++++++++++++++-
 4 files changed, 126 insertions(+), 7 deletions(-)

diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RotatedNorthPole.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RotatedNorthPole.java
index e338bea..ba921ad 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RotatedNorthPole.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RotatedNorthPole.java
@@ -37,6 +37,11 @@ import org.apache.sis.measure.Units;
  * This is similar to the WMO Rotated Latitude/Longitude but rotating north 
pole instead of
  * south pole.
  *
+ * <h2>Comparison with UCAR library</h2>
+ * {@link ucar.unidata.geoloc.projection.RotatedPole} in UCAR netCDF library 
version 5.5.2
+ * gives results with an offset of 180° in longitude values compared to our 
implementation.
+ * See {@code RotatedPoleTest.testRotateNorthPoleOnGreenwich()} for more 
details.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.2
  *
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RotatedSouthPole.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RotatedSouthPole.java
index b485905..2f18264 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RotatedSouthPole.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RotatedSouthPole.java
@@ -36,6 +36,10 @@ import org.apache.sis.measure.Units;
  * The provider for the WMO <cite>Rotated Latitude/Longitude</cite> coordinate 
operation.
  * This is defined by the World Meteorological Organization (WMO) in GRIB2 
template 3.1.
  *
+ * <h2>Comparison with UCAR library</h2>
+ * This is consistent with {@link ucar.unidata.geoloc.projection.RotatedLatLon}
+ * in UCAR netCDF library version 5.5.2.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.2
  * @since   1.2
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/RotatedPole.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/RotatedPole.java
index 6e613fd..513d4cd 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/RotatedPole.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/RotatedPole.java
@@ -153,7 +153,9 @@ public class RotatedPole extends AbstractMathTransform2D 
implements Serializable
                     i = 3 - i;
                 }
                 double value = -((Number) ((ParameterValue<?>) 
values.get(i)).getValue()).doubleValue();
-                if (i == 0) value = IEEEremainder(value + 180, 360);
+                if (i == 0 && 
RotatedSouthPole.PARAMETERS.equals(forward.getDescriptor())) {
+                    value = IEEEremainder(value + 180, 360);
+                }
                 target.setValue(value);
                 return true;
             }
@@ -340,8 +342,9 @@ public class RotatedPole extends AbstractMathTransform2D 
implements Serializable
     public boolean isIdentity() {
         return sinφp == -1;
         /*
-         * We do not test `cosφp` because that value may be small but 
non-zero, especially since there is no exact
-         * representation of `cos(π/2)`. Testing `sinφp == -1` is a way to 
allow for a small tolerance around π/2.
+         * We do not test `cosφp` because that value can be small but not 
zero, because
+         * there is no way to compute exactly `cos(π/2)` with `Math.PI` 
approximation.
+         * Testing `sinφp == -1` is a way to allow for a small tolerance 
around π/2.
          * This policy is also needed for consistency with 
`RotatedPole(RotatedPole)` implementation.
          */
     }
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/RotatedPoleTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/RotatedPoleTest.java
index 6764670..7072333 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/RotatedPoleTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/RotatedPoleTest.java
@@ -59,9 +59,22 @@ public final strictfp class RotatedPoleTest extends 
MathTransformTestCase {
     }
 
     /**
+     * Creates a new transform which should be the inverse of current 
transform according
+     * the parameters declared in {@link RotatedPole#context}. This is the 
same work than
+     * {@link #inverseSouthPoleTransform()} but for the other transform.
+     */
+    private void inverseNorthPoleTransform() throws FactoryException, 
TransformException {
+        final ParameterValueGroup pg = ((Parameterized) 
transform.inverse()).getParameterValues();
+        transform = RotatedPole.rotateSouthPole(factory(),
+                pg.parameter("grid_north_pole_latitude") .doubleValue(),
+                pg.parameter("grid_north_pole_longitude").doubleValue(),
+                pg.parameter("north_pole_grid_longitude").doubleValue());
+    }
+
+    /**
      * Tests a rotation of south pole with the new pole on Greenwich.
-     * The {@link ucar.unidata.geoloc.LatLonPoint} class has been used
-     * as a reference implementation for computing the expected values.
+     * The {@link ucar.unidata.geoloc.projection.RotatedLatLon} class
+     * has been used as a reference implementation for expected values.
      *
      * @throws FactoryException if the transform can not be created.
      * @throws TransformException if an error occurred while transforming a 
point.
@@ -88,8 +101,8 @@ public final strictfp class RotatedPoleTest extends 
MathTransformTestCase {
 
     /**
      * Tests a rotation of south pole with the pole on arbitrary meridian.
-     * The {@link ucar.unidata.geoloc.LatLonPoint} class has been used as
-     * a reference implementation for computing the expected values.
+     * The {@link ucar.unidata.geoloc.projection.RotatedLatLon} class has
+     * been used as a reference implementation for expected values.
      *
      * @throws FactoryException if the transform can not be created.
      * @throws TransformException if an error occurred while transforming a 
point.
@@ -114,4 +127,98 @@ public final strictfp class RotatedPoleTest extends 
MathTransformTestCase {
         inverseSouthPoleTransform();
         verifyTransform(expected, coordinates);
     }
+
+    /**
+     * Tries rotating a pole to opposite hemisphere.
+     *
+     * @throws FactoryException if the transform can not be created.
+     * @throws TransformException if an error occurred while transforming a 
point.
+     */
+    @Test
+    public void testRotateToOppositeHemisphere() throws FactoryException, 
TransformException {
+        transform = RotatedPole.rotateSouthPole(factory(), 50, 20, 10);
+        tolerance = Formulas.ANGULAR_TOLERANCE;
+        isDerivativeSupported = false;
+        final double[] coordinates = {      // (λ,φ) coordinates to convert.
+             20, 51,
+             80, 44,
+            -30, 89
+        };
+        final double[] expected = {         // (λ,φ) coordinates after 
conversion.
+             -10.000000000, -89.000000000,
+              64.651211252, -49.758697265,
+             -11.207848626, -50.636582758
+        };
+        verifyTransform(coordinates, expected);
+        inverseSouthPoleTransform();
+        verifyTransform(expected, coordinates);
+    }
+
+    /**
+     * Tests a rotation of north pole with the new pole on Greenwich.
+     *
+     * <h4>Comparison with UCAR library</h4>
+     * {@link ucar.unidata.geoloc.projection.RotatedPole} in UCAR netCDF 
library version 5.5.2
+     * gives results with an offset of 180° in longitude values compared to 
our implementation.
+     * But geometrical reasoning suggests that our implementation is correct: 
if we rotate the
+     * pole to 60°N, then latitude of 54°N on Greenwich meridian become only 
6° below new pole,
+     * i.e. 84°N but still on the same meridian (Greenwich) because we did not 
cross the pole.
+     *
+     * @throws FactoryException if the transform can not be created.
+     * @throws TransformException if an error occurred while transforming a 
point.
+     */
+    @Test
+    public void testRotateNorthPoleOnGreenwich() throws FactoryException, 
TransformException {
+        transform = RotatedPole.rotateNorthPole(factory(), 60, 0, 0);
+        tolerance = Formulas.ANGULAR_TOLERANCE;
+        isDerivativeSupported = false;
+        final double[] coordinates = {      // (λ,φ) coordinates to convert.
+             0, 54,
+            20, 62,
+           -30, 89
+        };
+        final double[] expected = {         // (λ,φ) coordinates after 
conversion.
+               0.000000000, 84.000000000,
+             110.307140436, 80.141810970,
+            -178.973119126, 60.862133738
+        };
+        verifyTransform(coordinates, expected);
+        inverseNorthPoleTransform();
+        verifyTransform(expected, coordinates);
+    }
+
+    /**
+     * Tests a rotation of north pole with the pole on arbitrary meridian.
+     * Result can be compared with PROJ using the following command, where
+     * {@code coords.txt} is a file containing input coordinates in (λ,φ)
+     * order and the output is in (φ,λ) order.
+     *
+     * {@preformat shell
+     *   cs2cs -I -E -f %g "EPSG:4326" +to +type=crs +proj=ob_tran 
+o_proj=longlat +datum=WGS84 +no_defs \
+     *         +o_lat_p=70 +o_lon_p=40 +lon_0=0 coords.txt
+     * }
+     *
+     * @throws FactoryException if the transform can not be created.
+     * @throws TransformException if an error occurred while transforming a 
point.
+     */
+    @Test
+    @DependsOnMethod("testRotateNorthPoleOnGreenwich")
+    public void testRotateNorthPole() throws FactoryException, 
TransformException {
+        transform = RotatedPole.rotateNorthPole(factory(), 70, 40, 0);
+        tolerance = Formulas.ANGULAR_TOLERANCE;
+        isDerivativeSupported = false;
+        final double[] coordinates = {      // (λ,φ) coordinates to convert.
+             0, 54,
+            20, 62,
+           -30, 89
+        };
+        final double[] expected = {         // (λ,φ) coordinates after 
conversion.
+             -68.817428350, 66.096411904,
+             -54.967324181, 78.691210976,
+            -177.208632734, 70.320491507
+        };
+        verifyTransform(coordinates, expected);
+        inverseNorthPoleTransform();
+        verifyTransform(expected, coordinates);
+    }
 }

Reply via email to