sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Apply the wrapparound longitude on other map projections that may be affected by this problem. A bug fix in previous commit has also be corrected: missing a factor 2 in θ_span calculation.
Date Sat, 21 Dec 2019 16:04:08 GMT
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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 8c761eb  Apply the wrapparound longitude on other map projections that may be affected
by this problem. A bug fix in previous commit has also be corrected: missing a factor 2 in
θ_span calculation.
8c761eb is described below

commit 8c761eb6834a637d15a0e4ec840d0ee2a0476146
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat Dec 21 17:02:06 2019 +0100

    Apply the wrapparound longitude on other map projections that may be affected by this
problem.
    A bug fix in previous commit has also be corrected: missing a factor 2 in θ_span calculation.
    
    This commit complete https://issues.apache.org/jira/browse/SIS-486
---
 .../operation/projection/AlbersEqualArea.java      | 31 +++++++++----------
 .../operation/projection/Initializer.java          | 25 +++++++++++++++
 .../projection/LambertConicConformal.java          | 28 +++++++++++------
 .../operation/projection/ObliqueStereographic.java | 36 ++++++++++++++--------
 .../operation/projection/SatelliteTracking.java    | 13 +++++++-
 .../projection/LambertConicConformalTest.java      |  9 ++++--
 .../org/apache/sis/internal/util/DoubleDouble.java |  8 ++---
 .../java/org/apache/sis/measure/Longitude.java     |  6 ++--
 .../apache/sis/internal/util/DoubleDoubleTest.java |  2 +-
 9 files changed, 109 insertions(+), 49 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
index 7b90adb..88476ee 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
@@ -58,7 +58,7 @@ public class AlbersEqualArea extends EqualAreaProjection {
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = -3024658742514888646L;
+    private static final long serialVersionUID = -3466040922402982480L;
 
     /**
      * Internal coefficients for computation, depending only on eccentricity and values of
standards parallels.
@@ -82,12 +82,12 @@ public class AlbersEqualArea extends EqualAreaProjection {
     final double C;
 
     /**
-     * The valid range of θ = n⋅λ values, which is [−n⋅π … n⋅π]. We need to
ensure that θ values
-     * are inside that range before to use it in trigonometric functions.
+     * Size of the [−n⋅π … n⋅π] range, which is the valid range of  θ = n⋅λ
 values.
+     * We need to ensure that θ values are inside that range before to use it in trigonometric
functions.
      *
-     * <a href="https://issues.apache.org/jira/browse/SIS-486">SIS-486</a>
+     * @see Initializer#spanOfScaledLongitude(DoubleDouble)
      */
-    final double rangeOfθ;
+    final double θ_span;
 
     /**
      * Creates an Albers Equal Area projection from the given parameters.
@@ -172,8 +172,7 @@ public class AlbersEqualArea extends EqualAreaProjection {
         denormalize.convertBefore(0, rn, null); rn.negate();
         denormalize.convertBefore(1, rn, ρ0);   rn.inverseDivide(-1);
         normalize.convertAfter(0, rn, null);    // On this line, `rn` became `n`.
-        rn.multiply(DoubleDouble.createPi());
-        rangeOfθ = rn.doubleValue();
+        θ_span = Initializer.spanOfScaledLongitude(rn);
     }
 
     /**
@@ -181,9 +180,9 @@ public class AlbersEqualArea extends EqualAreaProjection {
      */
     AlbersEqualArea(final AlbersEqualArea other) {
         super(other);
-        nm       = other.nm;
-        C        = other.C;
-        rangeOfθ = other.rangeOfθ;
+        nm     = other.nm;
+        C      = other.C;
+        θ_span = other.θ_span;
     }
 
     /**
@@ -239,8 +238,8 @@ public class AlbersEqualArea extends EqualAreaProjection {
                             final boolean derivate) throws ProjectionException
     {
         // θ = n⋅λ  reduced to  [−n⋅π … n⋅π]  range.
-        final double θ = IEEEremainder(srcPts[srcOff], rangeOfθ);
-        final double φ = srcPts[srcOff+1];
+        final double θ = IEEEremainder(srcPts[srcOff], θ_span);
+        final double φ = srcPts[srcOff + 1];
         final double cosθ = cos(θ);
         final double sinθ = sin(θ);
         final double sinφ = sin(φ);
@@ -300,7 +299,7 @@ public class AlbersEqualArea extends EqualAreaProjection {
      *
      * @author  Martin Desruisseaux (Geomatys)
      * @author  Rémi Maréchal (Geomatys)
-     * @version 0.8
+     * @version 1.1
      * @since   0.8
      * @module
      */
@@ -308,7 +307,7 @@ public class AlbersEqualArea extends EqualAreaProjection {
         /**
          * For cross-version compatibility.
          */
-        private static final long serialVersionUID = 9090765015127854096L;
+        private static final long serialVersionUID = -7238296545347764989L;
 
         /**
          * Constructs a new map projection from the parameters of the given projection.
@@ -328,8 +327,8 @@ public class AlbersEqualArea extends EqualAreaProjection {
                                 final boolean derivate)
         {
             // θ = n⋅λ  reduced to  [−n⋅π … n⋅π]  range.
-            final double θ = IEEEremainder(srcPts[srcOff], rangeOfθ);
-            final double φ = srcPts[srcOff+1];
+            final double θ = IEEEremainder(srcPts[srcOff], θ_span);
+            final double φ = srcPts[srcOff + 1];
             final double cosθ = cos(θ);
             final double sinθ = sin(θ);
             final double sinφ = sin(φ);
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
index 804d082..9e88f90 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
@@ -379,4 +379,29 @@ final class Initializer {
         s.inverseDivide(cosφ);
         return s.doubleValue();
     }
+
+    /**
+     * Returns the size of the [−n⋅π … n⋅π] range, which is the valid range of
 θ = n⋅λ  values.
+     * This method is invoked by map projections that multiply the longitude values by some
scale factor before
+     * to use them in trigonometric functions. Usually we do not explicitly wraparound the
longitude values,
+     * because trigonometric functions do that automatically for us. However if the longitude
is multiplied
+     * by some factor before to be used in trigonometric functions, them that implicit wraparound
is not the
+     * one we expect. The map projection code needs to perform explicit wraparound in such
cases.
+     * Example:
+     *
+     * {@preformat java
+     *   double spanθ = spanOfScaledLongitude(n);       // Should be computed only once.
+     *   double θ = Math.IEEEremainder(λn, spanθ);      // λ without n is typically unknown.
+     * }
+     *
+     * @param  n  the factor by which longitude values are multiplied before use in trigonometry.
+     * @return size of the [−n⋅π … n⋅π] range, for use in {@link Math#IEEEremainder(double,
double)}.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/SIS-486">SIS-486</a>
+     */
+    static double spanOfScaledLongitude(final DoubleDouble n) {
+        final DoubleDouble r = DoubleDouble.createTwicePi();
+        r.multiply(n);
+        return abs(r.doubleValue());
+    }
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
index 090f0da..961eea2 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
@@ -60,7 +60,7 @@ import static org.apache.sis.math.MathFunctions.isPositive;
  * @author  André Gosselin (MPO)
  * @author  Rueben Schulz (UBC)
  * @author  Rémi Maréchal (Geomatys)
- * @version 0.8
+ * @version 1.1
  * @since   0.6
  * @module
  */
@@ -68,7 +68,7 @@ public class LambertConicConformal extends ConformalProjection {
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = 2067358524298002016L;
+    private static final long serialVersionUID = -8971522854919443706L;
 
     /**
      * Codes for variants of Lambert Conical Conformal projection. Those variants modify
the way the projections are
@@ -141,6 +141,14 @@ public class LambertConicConformal extends ConformalProjection {
     final double n;
 
     /**
+     * Size of the [−n⋅π … n⋅π] range, which is the valid range of  θ = n⋅λ
 values.
+     * We need to ensure that θ values are inside that range before to use it in trigonometric
functions.
+     *
+     * @see Initializer#spanOfScaledLongitude(DoubleDouble)
+     */
+    final double θ_span;
+
+    /**
      * Creates a Lambert projection from the given parameters.
      * The {@code method} argument can be the description of one of the following:
      *
@@ -339,6 +347,7 @@ public class LambertConicConformal extends ConformalProjection {
         normalize  .convertAfter(1, sφ, null);
         denormalize.convertBefore(0, F, null); F.negate();
         denormalize.convertBefore(1, F, rF);
+        θ_span = Initializer.spanOfScaledLongitude(sλ);
     }
 
     /**
@@ -346,7 +355,8 @@ public class LambertConicConformal extends ConformalProjection {
      */
     LambertConicConformal(final LambertConicConformal other) {
         super(other);
-        n = other.n;
+        n      = other.n;
+        θ_span = other.θ_span;
     }
 
     /**
@@ -406,8 +416,8 @@ public class LambertConicConformal extends ConformalProjection {
          * the first non-linear one moved to the "normalize" affine transform, and the linear
operations
          * applied after the last non-linear one moved to the "denormalize" affine transform.
          */
-        final double θ    = srcPts[srcOff  ];     // θ = λ⋅n  (ignoring longitude of
origin)
-        final double φ    = srcPts[srcOff+1];     // Sign may be reversed
+        final double θ    = IEEEremainder(srcPts[srcOff], θ_span);      // θ = λ⋅n
 (ignoring longitude of origin)
+        final double φ    = srcPts[srcOff + 1];                         // Sign may be reversed
         final double absφ = abs(φ);
         final double sinθ = sin(θ);
         final double cosθ = cos(θ);
@@ -490,7 +500,7 @@ public class LambertConicConformal extends ConformalProjection {
      * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
      * @author  André Gosselin (MPO)
      * @author  Rueben Schulz (UBC)
-     * @version 0.6
+     * @version 1.1
      * @since   0.6
      * @module
      */
@@ -498,7 +508,7 @@ public class LambertConicConformal extends ConformalProjection {
         /**
          * For cross-version compatibility.
          */
-        private static final long serialVersionUID = -7005092237343502956L;
+        private static final long serialVersionUID = -8077690516096472987L;
 
         /**
          * Constructs a new map projection from the parameters of the given projection.
@@ -517,8 +527,8 @@ public class LambertConicConformal extends ConformalProjection {
                                 final double[] dstPts, final int dstOff,
                                 final boolean derivate)
         {
-            final double θ    = srcPts[srcOff  ];       // θ = λ⋅n
-            final double φ    = srcPts[srcOff+1];       // Sign may be reversed
+            final double θ    = IEEEremainder(srcPts[srcOff], θ_span);      // θ = λ⋅n
 (ignoring longitude of origin)
+            final double φ    = srcPts[srcOff + 1];                         // Sign may
be reversed
             final double absφ = abs(φ);
             final double sinθ = sin(θ);
             final double cosθ = cos(θ);
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
index e4828f8..be81ee1 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
@@ -61,7 +61,7 @@ import static org.apache.sis.internal.referencing.provider.ObliqueStereographic.
  *
  * @author  Rémi Maréchal (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 1.1
  * @since   0.7
  * @module
  */
@@ -69,7 +69,7 @@ public class ObliqueStereographic extends NormalizedProjection {
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = -1454098847621943639L;
+    private static final long serialVersionUID = -1725537881127730658L;
 
     /**
      * Conformal latitude of origin (χ₀), together with its sine and cosine.
@@ -90,7 +90,15 @@ public class ObliqueStereographic extends NormalizedProjection {
      * More precisely <var>g</var> and <var>h</var> are used to compute
intermediate parameters <var>i</var>
      * and <var>j</var>, which are themselves used to compute conformal latitude
and longitude.
      */
-    final double g, h;
+    private final double g, h;
+
+    /**
+     * Size of the [−n⋅π … n⋅π] range, which is the valid range of  Λ = n⋅λ
 values.
+     * We need to ensure that Λ values are inside that range before to use it in trigonometric
functions.
+     *
+     * @see Initializer#spanOfScaledLongitude(DoubleDouble)
+     */
+    private final double θ_span;
 
     /**
      * Creates an Oblique Stereographic projection from the given parameters.
@@ -173,6 +181,7 @@ public class ObliqueStereographic extends NormalizedProjection {
         final double R2 = 2 * initializer.radiusOfConformalSphere(sinφ0);
         denormalize.convertBefore(0, R2, null);
         denormalize.convertBefore(1, R2, null);
+        θ_span = n * (2*PI);
     }
 
     /**
@@ -180,13 +189,14 @@ public class ObliqueStereographic extends NormalizedProjection {
      */
     ObliqueStereographic(final ObliqueStereographic other) {
         super(other);
-        χ0    = other.χ0;
-        sinχ0 = other.sinχ0;
-        cosχ0 = other.cosχ0;
-        c     = other.c;
-        n     = other.n;
-        g     = other.g;
-        h     = other.h;
+        χ0     = other.χ0;
+        sinχ0  = other.sinχ0;
+        cosχ0  = other.cosχ0;
+        c      = other.c;
+        n      = other.n;
+        g      = other.g;
+        h      = other.h;
+        θ_span = other.θ_span;
     }
 
     /**
@@ -256,8 +266,9 @@ public class ObliqueStereographic extends NormalizedProjection {
                             final double[] dstPts, final int dstOff,
                             final boolean derivate) throws ProjectionException
     {
-        final double Λ     = srcPts[srcOff  ];      // Λ = λ⋅n  (see below), ignoring
longitude of origin.
-        final double φ     = srcPts[srcOff+1];
+        // Λ = λ⋅n  (see below), ignoring longitude of origin.
+        final double Λ     = IEEEremainder(srcPts[srcOff], θ_span);
+        final double φ     = srcPts[srcOff + 1];
         final double sinφ  = sin(φ);
         final double ℯsinφ = eccentricity * sinφ;
         final double Sa    = (1 +  sinφ) / (1 -  sinφ);
@@ -407,6 +418,7 @@ public class ObliqueStereographic extends NormalizedProjection {
                                 final double[] dstPts, final int dstOff,
                                 final boolean derivate)
         {
+            // No need to enforce [−n⋅π … n⋅π] range here because n=1.
             final double λ = srcPts[srcOff  ];
             final double φ = srcPts[srcOff+1];
             /*
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/SatelliteTracking.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/SatelliteTracking.java
index a941896..1b9d68b 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/SatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/SatelliteTracking.java
@@ -69,7 +69,7 @@ public class SatelliteTracking extends NormalizedProjection {
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = 859940667477896653L;
+    private static final long serialVersionUID = -209787336760184649L;
 
     /**
      * Sines and cosines of inclination between the plane of the Earth's Equator and the
plane
@@ -99,6 +99,14 @@ public class SatelliteTracking extends NormalizedProjection {
     private final boolean isConic;
 
     /**
+     * Size of the [−n⋅π … n⋅π] range, which is the valid range of  θ = n⋅λ
 values.
+     * We need to ensure that θ values are inside that range before to use it in trigonometric
functions.
+     *
+     * @see Initializer#spanOfScaledLongitude(DoubleDouble)
+     */
+    private final double θ_span;
+
+    /**
      * Work around for RFE #4093999 in Sun's bug database ("Relax constraint on
      * placement of this()/super() call in constructors").
      */
@@ -194,6 +202,7 @@ public class SatelliteTracking extends NormalizedProjection {
             normalize  .convertAfter (0,  n,  null);
             denormalize.convertBefore(0, +ρf, null);
             denormalize.convertBefore(1, -ρf, ρ0);
+            θ_span = n * (2*PI);
         } else {
             /*
              * Cylindrical projection case. The equations are (ignoring R and λ₀):
@@ -205,6 +214,7 @@ public class SatelliteTracking extends NormalizedProjection {
              * The cosφ₁ (for x at dimension 0) and cosφ₁/F₁′ (for y at dimension
1) factors are computed
              * in advance and stored below. The remaining factor to compute in transform(…)
method is L.
              */
+            θ_span = 2*PI;
             n = s0 = Double.NaN;
             final double cotF = sqrt(cos2_φ1 - cos2_i) / (p2_on_p1*cos2_φ1 - cos_i);  
 // Cotangente of F₁.
             denormalize.convertBefore(0, cosφ1,      null);
@@ -298,6 +308,7 @@ public class SatelliteTracking extends NormalizedProjection {
         double x = srcPts[srcOff];
         double y = λt - p2_on_p1 * λpm;
         if (isConic) {
+            x = IEEEremainder(x, θ_span);
             λpm = n*y + s0;                     // Use this variable for a new purpose.
Needed for derivative.
             if ((Double.doubleToRawLongBits(λpm) ^ Double.doubleToRawLongBits(n)) < 0)
{
                 /*
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java
index 7ccb2c3..0edb4c7 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java
@@ -48,7 +48,7 @@ import static org.apache.sis.test.Assert.*;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Rémi Maréchal (Geomatys)
- * @version 0.8
+ * @version 1.1
  * @since   0.6
  * @module
  */
@@ -357,9 +357,12 @@ public final strictfp class LambertConicConformalTest extends MapProjectionTestC
     @DependsOnMethod("testLambertConicConformal1SP")
     public void testSerialization() throws FactoryException, TransformException {
         createNormalizedProjection(true, 40);
-        final double[] source = CoordinateDomain.GEOGRAPHIC_RADIANS_NORTH.generateRandomInput(TestUtilities.createRandomNumberGenerator(),
2, 10);
+        final double[] source = new double[] {
+            70*PI/180, 27*PI/180,
+            30*PI/180, 56*PI/180
+        };
         final double[] target = new double[source.length];
-        transform.transform(source, 0, target, 0, 10);
+        transform.transform(source, 0, target, 0, source.length / 2);
         transform = assertSerializedEquals(transform);
         tolerance = Formulas.LINEAR_TOLERANCE;
         verifyTransform(source, target);
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
index 0aa5324..b5a9b02 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
@@ -309,12 +309,12 @@ public final class DoubleDouble extends Number {
     }
 
     /**
-     * Returns a new {@code DoubleDouble} instance initialized to the PI value.
+     * Returns a new {@code DoubleDouble} instance initialized to the 2π value.
      *
-     * @return an instance initialized to the 3.14159265358979323846264338327950 value.
+     * @return an instance initialized to the 6.28318530717958647692528676655901 value.
      */
-    public static DoubleDouble createPi() {
-        return new DoubleDouble(3.14159265358979323846264338327950, 1.2246467991473532E-16);
+    public static DoubleDouble createTwicePi() {
+        return new DoubleDouble(6.28318530717958647692528676655901, 2.4492935982947064E-16);
     }
 
     /**
diff --git a/core/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java b/core/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java
index ec19f87..fe049be 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java
@@ -127,9 +127,9 @@ public final class Longitude extends Angle {
     }
 
     /**
-     * Returns the given longitude value normalized to the [{@linkplain #MIN_VALUE -180}
… {@linkplain #MAX_VALUE 180})°
-     * range (upper value is exclusive). If the given value is outside the longitude range,
then this method adds or
-     * subtracts a multiple of 360° in order to bring back the value to that range.
+     * Returns the given longitude value normalized to the [{@value #MIN_VALUE} … {@value
#MAX_VALUE})°
+     * range (upper value is exclusive). If the given value is outside the longitude range,
then this
+     * method adds or subtracts a multiple of 360° in order to bring back the value to that
range.
      *
      * <p>Special cases:</p>
      * <ul>
diff --git a/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
b/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
index c08fc24..ebdf365 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
@@ -444,7 +444,7 @@ public final strictfp class DoubleDoubleTest extends TestCase {
         for (int i=0; ; i++) {
             final DoubleDouble dd;
             switch (i) {
-                case 0:  dd = DoubleDouble.createPi();               break;
+                case 0:  dd = DoubleDouble.createTwicePi();          break;
                 case 1:  dd = DoubleDouble.createRadiansToDegrees(); break;
                 case 2:  dd = DoubleDouble.createDegreesToRadians(); break;
                 case 3:  dd = DoubleDouble.createSecondsToRadians(); break;


Mime
View raw message