Cherry-pick the https://issues.apache.org/jira/browse/SIS-486 fix from geoapi-4.0 branch.
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 fe17827..92f1538 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
@@ -50,7 +50,7 @@
*
* @author Martin Desruisseaux (Geomatys)
* @author Rémi Maréchal (Geomatys)
- * @version 0.8
+ * @version 1.1
* @since 0.8
* @module
*/
@@ -58,7 +58,7 @@
/**
* 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,6 +82,15 @@
final double C;
/**
+ * A bound of the [−n⋅π … n⋅π] range, which is the valid range of θ = n⋅λ values.
+ * Some (not all) θ values need to be shifted inside that range before to use them
+ * in trigonometric functions.
+ *
+ * @see Initializer#boundOfScaledLongitude(DoubleDouble)
+ */
+ final double θ_bound;
+
+ /**
* Creates an Albers Equal Area projection from the given parameters.
*
* @param method description of the projection parameters.
@@ -163,7 +172,8 @@
final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
denormalize.convertBefore(0, rn, null); rn.negate();
denormalize.convertBefore(1, rn, ρ0); rn.inverseDivide(-1);
- normalize.convertAfter(0, rn, null);
+ normalize.convertAfter(0, rn, null); // On this line, `rn` became `n`.
+ θ_bound = initializer.boundOfScaledLongitude(rn);
}
/**
@@ -171,8 +181,9 @@
*/
AlbersEqualArea(final AlbersEqualArea other) {
super(other);
- nm = other.nm;
- C = other.C;
+ nm = other.nm;
+ C = other.C;
+ θ_bound = other.θ_bound;
}
/**
@@ -227,8 +238,9 @@
final double[] dstPts, final int dstOff,
final boolean derivate) throws ProjectionException
{
- final double θ = srcPts[srcOff ]; // θ = n⋅λ
- final double φ = srcPts[srcOff+1];
+ // θ = n⋅λ reduced to [−n⋅π … n⋅π] range.
+ final double θ = wraparoundScaledLongitude(srcPts[srcOff], θ_bound);
+ final double φ = srcPts[srcOff + 1];
final double cosθ = cos(θ);
final double sinθ = sin(θ);
final double sinφ = sin(φ);
@@ -288,7 +300,7 @@
*
* @author Martin Desruisseaux (Geomatys)
* @author Rémi Maréchal (Geomatys)
- * @version 0.8
+ * @version 1.1
* @since 0.8
* @module
*/
@@ -296,7 +308,7 @@
/**
* 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.
@@ -315,8 +327,9 @@
final double[] dstPts, final int dstOff,
final boolean derivate)
{
- final double θ = srcPts[srcOff]; // θ = n⋅λ
- final double φ = srcPts[srcOff+1];
+ // θ = n⋅λ reduced to [−n⋅π … n⋅π] range.
+ final double θ = wraparoundScaledLongitude(srcPts[srcOff], θ_bound);
+ 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..ff0ac5c 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
@@ -88,6 +88,11 @@
final DoubleDouble eccentricitySquared;
/**
+ * Sign of central meridian: -1 if negative, 0 if zero, +1 if positive.
+ */
+ private final byte signum_λ0;
+
+ /**
* Map projection variant.
* Values from 0 to 127 inclusive are convenience values at the discretion of {@link NormalizedProjection} subclasses.
* Values from 128 to 255 inclusive are values handled in a special way by {@link Initializer} constructor.
@@ -143,6 +148,8 @@
final double fn = getAndStore(roles.get(ParameterRole.FALSE_NORTHING))
- getAndStore(roles.get(ParameterRole.FALSE_SOUTHING));
+ signum_λ0 = (λ0 > 0) ? (byte) +1 :
+ (λ0 < 0) ? (byte) -1 : 0;
eccentricitySquared = new DoubleDouble();
DoubleDouble k = DoubleDouble.createAndGuessError(a); // The value by which to multiply all results of normalized projection.
if (a != b) {
@@ -379,4 +386,39 @@
s.inverseDivide(cosφ);
return s.doubleValue();
}
+
+ /**
+ * Returns a bound 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, then that implicit wraparound is not the
+ * one we expect. The map projection code needs to perform explicit wraparound in such cases.
+ *
+ * @param n the factor by which longitude values are multiplied before use in trigonometry.
+ * @return a bound of the [−n⋅π … n⋅π] range.
+ *
+ * @see NormalizedProjection#wraparoundScaledLongitude(double, double)
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-486">SIS-486</a>
+ */
+ final double boundOfScaledLongitude(final double n) {
+ return boundOfScaledLongitude(new DoubleDouble(n));
+ }
+
+ /**
+ * Same as {@link #boundOfScaledLongitude(double)} with opportunistic use of double-double precision.
+ * This is used when than object is available anyway.
+ *
+ * @param n the factor by which longitude values are multiplied before use in trigonometry.
+ * @return a bound of the [−n⋅π … n⋅π] range.
+ */
+ final double boundOfScaledLongitude(final DoubleDouble n) {
+ if (signum_λ0 == 0 || n.doubleValue() >= 1) {
+ return Double.NaN; // Do not apply any wraparound.
+ }
+ final DoubleDouble r = DoubleDouble.createPi();
+ r.multiply(n);
+ final double θ_bound = abs(r.doubleValue());
+ return (signum_λ0 < 0) ? θ_bound : -θ_bound;
+ }
}
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..91209c3 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 @@
* @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 @@
/**
* 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,15 @@
final double n;
/**
+ * A bound of the [−n⋅π … n⋅π] range, which is the valid range of θ = n⋅λ values.
+ * Some (not all) θ values need to be shifted inside that range before to use them
+ * in trigonometric functions.
+ *
+ * @see Initializer#boundOfScaledLongitude(DoubleDouble)
+ */
+ final double θ_bound;
+
+ /**
* Creates a Lambert projection from the given parameters.
* The {@code method} argument can be the description of one of the following:
*
@@ -339,6 +348,7 @@
normalize .convertAfter(1, sφ, null);
denormalize.convertBefore(0, F, null); F.negate();
denormalize.convertBefore(1, F, rF);
+ θ_bound = initializer.boundOfScaledLongitude(sλ);
}
/**
@@ -346,7 +356,8 @@
*/
LambertConicConformal(final LambertConicConformal other) {
super(other);
- n = other.n;
+ n = other.n;
+ θ_bound = other.θ_bound;
}
/**
@@ -406,8 +417,8 @@
* 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 θ = wraparoundScaledLongitude(srcPts[srcOff], θ_bound); // θ = Δλ⋅n
+ final double φ = srcPts[srcOff + 1]; // Sign may be reversed
final double absφ = abs(φ);
final double sinθ = sin(θ);
final double cosθ = cos(θ);
@@ -490,7 +501,7 @@
* @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 +509,7 @@
/**
* 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 +528,8 @@
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 θ = wraparoundScaledLongitude(srcPts[srcOff], θ_bound); // θ = Δλ⋅n
+ 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/NormalizedProjection.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
index 5d127cc..761fc01 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
@@ -126,7 +126,7 @@
* @author André Gosselin (MPO)
* @author Rueben Schulz (UBC)
* @author Rémi Maréchal (Geomatys)
- * @version 0.8
+ * @version 1.1
*
* @see ContextualParameters
* @see <a href="http://mathworld.wolfram.com/MapProjection.html">Map projections on MathWorld</a>
@@ -647,6 +647,40 @@
}
/**
+ * If the given scaled longitude θ=n⋅λ is outside the [−n⋅π … n⋅π] range, maybe shifts θ to that range.
+ * This method intentionally does <strong>not</strong> force θ to be inside that range in all cases.
+ * We avoid explicit wraparounds as much as possible (as opposed to implicit wraparounds performed by
+ * trigonometric functions) because they tend to introduce discontinuities. We perform wraparounds only
+ * when necessary for the problem of area spanning the anti-meridian (±180°).
+ *
+ * <div class="note"><b>Example:</b>
+ * a CRS for Alaska may have the central meridian at λ₀=−154° of longitude. If the point to project is
+ * at λ=177° of longitude, calculations will be performed with Δλ=331° while the correct value that we
+ * need to use is Δλ=−29°.</div>
+ *
+ * In order to avoid wraparound operations as much as possible, we test only the bound where anti-meridian
+ * problem may happen; no wraparound will be applied for the opposite bound. Furthermore we add or subtract
+ * 360° only once. Even if the point did many turns around the Earth, the 360° shift will still be applied
+ * at most once. The desire to apply the minimal amount of shifts is the reason why we do not use
+ * {@link Math#IEEEremainder(double, double)}.
+ *
+ * @param θ the scaled longitude value θ=n⋅λ where <var>n</var> is a projection-dependent factor.
+ * @param θ_bound minimal (if negative) or maximal (if positive) value of θ before to apply the shift.
+ * This is computed by <code>{@linkplain Initializer#boundOfScaledLongitude(double)
+ * Initializer.boundOfScaledLongitude}(n)</code>
+ * @return θ or shifted θ.
+ *
+ * @see Initializer#boundOfScaledLongitude(double)
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-486">SIS-486</a>
+ */
+ static double wraparoundScaledLongitude(double θ, final double θ_bound) {
+ if (θ_bound < 0 ? θ < θ_bound : θ > θ_bound) {
+ θ -= 2*θ_bound;
+ }
+ return θ;
+ }
+
+ /**
* Converts a single coordinate in {@code srcPts} at the given offset and stores the result
* in {@code dstPts} at the given offset. In addition, opportunistically computes the
* transform derivative if requested.
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..64adcd0 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 @@
*
* @author Rémi Maréchal (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 1.1
* @since 0.7
* @module
*/
@@ -69,7 +69,7 @@
/**
* 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,16 @@
* 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;
+
+ /**
+ * A bound of the [−n⋅π … n⋅π] range, which is the valid range of θ = n⋅λ values.
+ * Some (not all) θ values need to be shifted inside that range before to use them
+ * in trigonometric functions.
+ *
+ * @see Initializer#boundOfScaledLongitude(DoubleDouble)
+ */
+ private final double θ_bound;
/**
* Creates an Oblique Stereographic projection from the given parameters.
@@ -173,6 +182,7 @@
final double R2 = 2 * initializer.radiusOfConformalSphere(sinφ0);
denormalize.convertBefore(0, R2, null);
denormalize.convertBefore(1, R2, null);
+ θ_bound = initializer.boundOfScaledLongitude(n);
}
/**
@@ -180,13 +190,14 @@
*/
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;
+ θ_bound = other.θ_bound;
}
/**
@@ -256,8 +267,9 @@
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 Λ = wraparoundScaledLongitude(srcPts[srcOff], θ_bound);
+ final double φ = srcPts[srcOff + 1];
final double sinφ = sin(φ);
final double ℯsinφ = eccentricity * sinφ;
final double Sa = (1 + sinφ) / (1 - sinφ);
@@ -407,6 +419,7 @@
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..430e92f 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 @@
/**
* 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,15 @@
private final boolean isConic;
/**
+ * A bound of the [−n⋅π … n⋅π] range, which is the valid range of θ = n⋅λ values.
+ * Some (not all) θ values need to be shifted inside that range before to use them
+ * in trigonometric functions.
+ *
+ * @see Initializer#boundOfScaledLongitude(DoubleDouble)
+ */
+ private final double θ_bound;
+
+ /**
* Work around for RFE #4093999 in Sun's bug database ("Relax constraint on
* placement of this()/super() call in constructors").
*/
@@ -194,6 +203,7 @@
normalize .convertAfter (0, n, null);
denormalize.convertBefore(0, +ρf, null);
denormalize.convertBefore(1, -ρf, ρ0);
+ θ_bound = initializer.boundOfScaledLongitude(n);
} else {
/*
* Cylindrical projection case. The equations are (ignoring R and λ₀):
@@ -205,7 +215,7 @@
* 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.
*/
- n = s0 = Double.NaN;
+ n = s0 = θ_bound = 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);
denormalize.convertBefore(1, cosφ1*cotF, null);
@@ -298,6 +308,7 @@
double x = srcPts[srcOff];
double y = λt - p2_on_p1 * λpm;
if (isConic) {
+ x = wraparoundScaledLongitude(x, θ_bound);
λ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/AlbersEqualAreaTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/AlbersEqualAreaTest.java
index a30c1cc..db87c4b 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/AlbersEqualAreaTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/AlbersEqualAreaTest.java
@@ -39,7 +39,7 @@
*
* @author Martin Desruisseaux (Geomatys)
* @author Rémi Maréchal (Geomatys)
- * @version 0.8
+ * @version 1.1
* @since 0.8
* @module
*/
@@ -248,4 +248,51 @@
new int[] { 5, 5}, // Number of points to test
TestUtilities.createRandomNumberGenerator());
}
+
+ /**
+ * Tests the projection of point where the difference between the given longitude value and central meridian
+ * is close to 360°. In most map other map projection implementations, we rely on range reductions performed
+ * automatically by trigonometric functions. However we can not rely on that effect in the particular case of
+ * {@link AlbersEqualArea} because the longitude is pre-multiplied by a <var>n</var> factor before to be used
+ * in trigonometric functions. The range reduction must be performed explicitly in map projection code.
+ *
+ * <p>The math transform tested here is:</p>
+ * {@preformat wkt
+ * Param_MT["Albers Equal Area",
+ * Parameter["semi_major", 6378206.4, Unit["metre"]],
+ * Parameter["semi_minor", 6356583.8, Unit["metre"]],
+ * Parameter["Latitude of false origin", 50, Unit["degree"]],
+ * Parameter["Longitude of false origin", -154, Unit["degree"]],
+ * Parameter["Latitude of 1st standard parallel", 55, Unit["degree"]],
+ * Parameter["Latitude of 2nd standard parallel", 65, Unit["degree"]]]
+ * }
+ *
+ * @throws FactoryException if an error occurred while creating the map projection.
+ * @throws TransformException if an error occurred while projecting a point.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-486">SIS-486</a>
+ */
+ @Test
+ public void testLongitudeWraparound() throws FactoryException, TransformException {
+ createCompleteProjection(new org.apache.sis.internal.referencing.provider.AlbersEqualArea(),
+ 6378206.4, // Semi-major axis length
+ 6356583.8, // Semi-minor axis length
+ -154, // Central meridian
+ 50, // Latitude of origin
+ 55, // Standard parallel 1
+ 65, // Standard parallel 2
+ NaN, // Scale factor (none)
+ NaN, // False easting (none)
+ NaN); // False northing (none)
+
+ tolerance = Formulas.LINEAR_TOLERANCE;
+ /*
+ * Skip inverse transform because the 176.003° become -183.997°. It is not the purpose
+ * of this test to verify longitude wraparound in inverse projection (we do not expect
+ * such wraparound to be applied).
+ */
+ isInverseTransformSupported = false;
+ verifyTransform(new double[] {176.00296518775082, 52.00158201757688},
+ new double[] {-2000419.117, 680784.426});
+ }
}
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 31155dd..059dfd3 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
@@ -51,7 +51,7 @@
*
* @author Martin Desruisseaux (Geomatys)
* @author Rémi Maréchal (Geomatys)
- * @version 0.8
+ * @version 1.1
* @since 0.6
* @module
*/
@@ -352,9 +352,12 @@
@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 65498a5..b30d0cd 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
@@ -56,7 +56,7 @@
* </ul>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
*
* @see <a href="http://en.wikipedia.org/wiki/Double-double_%28arithmetic%29#Double-double_arithmetic">Wikipedia: Double-double arithmetic</a>
*
@@ -309,6 +309,15 @@
}
/**
+ * Returns a new {@code DoubleDouble} instance initialized to the π value.
+ *
+ * @return an instance initialized to the 3.14159265358979323846264338327950 value.
+ */
+ public static DoubleDouble createPi() {
+ return new DoubleDouble(3.14159265358979323846264338327950, 1.2246467991473532E-16);
+ }
+
+ /**
* Returns a new {@code DoubleDouble} instance initialized to the conversion factor
* from radians to angular degrees.
*
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 @@
}
/**
- * 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 0401fc8..c08fc24 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
@@ -37,7 +37,7 @@
* Those tests need {@link DoubleDouble#DISABLED} to be set to {@code false}.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
* @since 0.4
* @module
*/
@@ -444,9 +444,10 @@
for (int i=0; ; i++) {
final DoubleDouble dd;
switch (i) {
- case 0: dd = DoubleDouble.createRadiansToDegrees(); break;
- case 1: dd = DoubleDouble.createDegreesToRadians(); break;
- case 2: dd = DoubleDouble.createSecondsToRadians(); break;
+ case 0: dd = DoubleDouble.createPi(); break;
+ case 1: dd = DoubleDouble.createRadiansToDegrees(); break;
+ case 2: dd = DoubleDouble.createDegreesToRadians(); break;
+ case 3: dd = DoubleDouble.createSecondsToRadians(); break;
default: return; // Test done.
}
assertEquals(DoubleDouble.errorForWellKnownValue(dd.value), dd.error, STRICT);