You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2015/10/06 14:14:10 UTC
svn commit: r1707026 - in /sis/branches/JDK8/core/sis-referencing/src:
main/java/org/apache/sis/referencing/operation/projection/
test/java/org/apache/sis/referencing/operation/projection/
Author: desruisseaux
Date: Tue Oct 6 12:14:09 2015
New Revision: 1707026
URL: http://svn.apache.org/viewvc?rev=1707026&view=rev
Log:
Refactor TransverseMercator as a ConformalProjection subclass.
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java?rev=1707026&r1=1707025&r2=1707026&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java [UTF-8] Tue Oct 6 12:14:09 2015
@@ -36,11 +36,16 @@ import static java.lang.Math.*;
* (Source: §1.3.3 in IOGP Publication 373-7-2 – Geomatics Guidance Note number 7, part 2 – April 2015).
* Indeed, those two projections have some equation in commons which are provided in this base class.</p>
*
- * <p>The polar stereographic projection is not documented as a special case of Lambert Conic Conformal,
+ * <p>The Polar Stereographic projection is not documented as a special case of Lambert Conic Conformal,
* but the equations in the {@code PolarStereographic.transform(…)} and {@code inverseTransform(…)} methods
* appear to be the same with the <var>n</var> factor fixed to 1 or -1, so we leverage the code provided by
* this base class. This class hierarchy is only an implementation convenience and not part of public API.</p>
*
+ * <p>The Transverse Mercator projection is also conformal, but does not use the formulas provided in this class.
+ * It will instead compute the coefficients itself and use its own, more complex, formulas with those coefficients.
+ * However the formulas provided in this {@code ConformalProjection} class can be seen as a special case of
+ * Transverse Mercator formulas for <var>x</var> = 0.</p>
+ *
* <div class="note"><b>Reference:</b>
* “Lambert developed the regular Conformal Conic as the oblique aspect of a family containing the previously
* known polar Stereographic and regular Mercator projections. (…) If the standard parallels are symmetrical
@@ -59,6 +64,42 @@ abstract class ConformalProjection exten
private static final long serialVersionUID = 458860570536642265L;
/**
+ * {@code false} for using the original formulas as published by EPSG, or {@code true} for using formulas
+ * modified using trigonometric identities. The use of trigonometric identities is for reducing the amount
+ * of calls to the {@link Math#sin(double)} and similar methods. Some identities used are:
+ *
+ * <ul>
+ * <li>sin(2θ) = 2⋅sinθ⋅cosθ</li>
+ * <li>cos(2θ) = cos²θ - sin²θ</li>
+ * <li>sin(3θ) = (3 - 4⋅sin²θ)⋅sinθ</li>
+ * <li>cos(3θ) = (4⋅cos³θ) - 3⋅cosθ</li>
+ * <li>sin(4θ) = (4 - 8⋅sin²θ)⋅sinθ⋅cosθ</li>
+ * <li>cos(4θ) = (8⋅cos⁴θ) - (8⋅cos²θ) + 1</li>
+ * </ul>
+ *
+ * Hyperbolic formulas (used in Transverse Mercator projection):
+ *
+ * <ul>
+ * <li>sinh(2θ) = 2⋅sinhθ⋅coshθ</li>
+ * <li>cosh(2θ) = cosh²θ + sinh²θ = 2⋅cosh²θ - 1 = 1 + 2⋅sinh²θ</li>
+ * <li>sinh(3θ) = (3 + 4⋅sinh²θ)⋅sinhθ</li>
+ * <li>cosh(3θ) = ((4⋅cosh²θ) - 3)⋅coshθ</li>
+ * <li>sinh(4θ) = (1 + 2⋅sinh²θ)⋅4.sinhθ⋅coshθ
+ * = 4.cosh(2θ).sinhθ⋅coshθ</li>
+ * <li>cosh(4θ) = (8⋅cosh⁴θ) - (8⋅cosh²θ) + 1
+ * = 8⋅cosh²(θ) ⋅ (cosh²θ - 1) + 1
+ * = 8⋅cosh²(θ) ⋅ sinh²(θ) + 1
+ * = 2⋅sinh²(2θ) + 1</li>
+ * </ul>
+ *
+ * Note that since this boolean is static final, the compiler should exclude the code in the branch that is never
+ * executed (no need to comment-out that code).
+ *
+ * @see #identityEquals(double, double)
+ */
+ static final boolean ALLOW_TRIGONOMETRIC_IDENTITIES = true;
+
+ /**
* The threshold value of {@link #excentricity} at which we consider the accuracy of the
* series expansion insufficient. This threshold is determined empirically with the help
* of the {@code MercatorMethodComparison} class in the test directory.
@@ -72,34 +113,49 @@ abstract class ConformalProjection exten
static final double EXCENTRICITY_THRESHOLD = 0.16;
/**
- * Coefficients in the series expansion used by {@link #φ(double)}.
+ * {@code true} if the {@link #excentricity} value is greater than or equals to {@link #EXCENTRICITY_THRESHOLD},
+ * in which case the {@link #φ(double)} method will need to use an iterative method.
*
- * <p>Consider those fields as final. They are not only of the purpose of {@link #readObject(ObjectInputStream)}.</p>
+ * <p><strong>Consider this field as final!</strong>
+ * It is not final only for the purpose of {@link #readObject(ObjectInputStream)}.</p>
*/
- private transient double c2χ, c4χ, c6χ, c8χ;
+ private transient boolean useIterations;
/**
- * {@code true} if the {@link #excentricity} value is greater than or equals to {@link #EXCENTRICITY_THRESHOLD},
- * in which case the {@link #φ(double)} method will need to use an iterative method.
+ * Coefficients in the series expansion of the inverse projection,
+ * depending only on {@linkplain #excentricity excentricity} value.
+ * The series expansion is of the following form, where f(θ) is typically sin(θ):
+ *
+ * <blockquote>ci₂⋅f(2θ) + ci₄⋅f(4θ) + ci₆⋅f(6θ) + ci₈⋅f(8θ)</blockquote>
*
- * <p>Consider this field as final. It is not only of the purpose of {@link #readObject(ObjectInputStream)}.</p>
+ * This {@code ConformalProjection} class uses those coefficients in {@link #φ(double)}.
+ * However some subclasses may compute those coefficients differently and use them in a
+ * different series expansion (but for the same purpose).
+ *
+ * <p><strong>Consider those fields as final!</strong> They are not final only for sub-class
+ * constructors convenience and for the purpose of {@link #readObject(ObjectInputStream)}.</p>
+ *
+ * @see #computeCoefficients()
*/
- private transient boolean useIterations;
+ transient double ci2, ci4, ci6, ci8;
/**
* Creates a new normalized projection from the parameters computed by the given initializer.
*
+ * <p>It is sub-classes responsibility to invoke {@code super.computeCoefficients()} in their
+ * constructor when ready, or to compute the coefficients themselves.</p>
+ *
* @param initializer The initializer for computing map projection internal parameters.
*/
ConformalProjection(final Initializer initializer) {
super(initializer);
- initialize();
}
/**
- * Computes the transient fields after construction or deserialization.
+ * Computes the coefficients in the series expansions from the {@link #excentricitySquared} value.
+ * This method shall be invoked after {@code ConformalProjection} construction or deserialization.
*/
- private void initialize() {
+ void computeCoefficients() {
useIterations = (excentricity >= EXCENTRICITY_THRESHOLD);
final double e2 = excentricitySquared;
final double e4 = e2 * e2;
@@ -109,14 +165,20 @@ abstract class ConformalProjection exten
* For each line below, add the smallest values first in order to reduce rounding errors.
* The smallest values are the one using the excentricity raised to the highest power.
*/
- c2χ = 13/ 360.* e8 + 1/ 12.* e6 + 5/24.* e4 + e2/2;
- c4χ = 811/ 11520.* e8 + 29/240.* e6 + 7/48.* e4;
- c6χ = 81/ 1120.* e8 + 7/120.* e6;
- c8χ = 4279/161280.* e8;
+ ci2 = 13/ 360.* e8 + 1/ 12.* e6 + 5/24.* e4 + e2/2;
+ ci4 = 811/ 11520.* e8 + 29/240.* e6 + 7/48.* e4;
+ ci6 = 81/ 1120.* e8 + 7/120.* e6;
+ ci8 = 4279/161280.* e8;
+ /*
+ * When rewriting equations using trigonometric identities, some constants appear.
+ * For example sin(2θ) = 2⋅sinθ⋅cosθ, so we can factor out the 2 constant into the
+ * corresponding 'c' field.
+ */
if (ALLOW_TRIGONOMETRIC_IDENTITIES) {
- c4χ *= 2;
- c6χ *= 4;
- c8χ *= 8;
+ // Multiplication by powers of 2 does not bring any additional rounding error.
+ ci4 *= 2;
+ ci6 *= 4;
+ ci8 *= 8;
}
}
@@ -129,10 +191,10 @@ abstract class ConformalProjection exten
ConformalProjection(final ConformalProjection other) {
super(other);
useIterations = other.useIterations;
- c2χ = other.c2χ;
- c4χ = other.c4χ;
- c6χ = other.c6χ;
- c8χ = other.c8χ;
+ ci2 = other.ci2;
+ ci4 = other.ci4;
+ ci6 = other.ci6;
+ ci8 = other.ci8;
}
/**
@@ -140,6 +202,12 @@ abstract class ConformalProjection exten
* This formula is also part of other projections, since Mercator can be considered as a special case of
* Lambert Conic Conformal for instance.
*
+ * <div class="note"><b>Warning:</b>
+ * this method is valid only if the series expansion coefficients where computed by the
+ * {@link #computeCoefficients()} method defined in this class. This is not the case of
+ * {@link TransverseMercator} for instance. Note however that even in the later case,
+ * this method can still be seen as a special case of {@code TransverseMercator} formulas.</div>
+ *
* <p>This function is <em>almost</em> the converse of the {@link #expOfNorthing(double, double)} function.
* In a Mercator inverse projection, the value of the {@code expOfSouthing} argument is {@code exp(-y)}.</p>
*
@@ -181,10 +249,10 @@ abstract class ConformalProjection exten
* values, for reducing rounding errors due to IEEE 754 arithmetic.
*/
if (!ALLOW_TRIGONOMETRIC_IDENTITIES) {
- φ += c8χ * sin(8*φ)
- + c6χ * sin(6*φ)
- + c4χ * sin(4*φ)
- + c2χ * sin(2*φ);
+ φ += ci8 * sin(8*φ)
+ + ci6 * sin(6*φ)
+ + ci4 * sin(4*φ)
+ + ci2 * sin(2*φ);
} else {
/*
* Same formula than above, be rewriten using trigonometric identities in order to have only two
@@ -192,8 +260,8 @@ abstract class ConformalProjection exten
*/
final double sin_2φ = sin(2*φ);
final double sin2 = sin_2φ * sin_2φ;
- φ += ((c4χ + c8χ * (0.50 - sin2)) * cos(2*φ)
- + (c2χ + c6χ * (0.75 - sin2))) * sin_2φ;
+ φ += ((ci4 + ci8 * (0.50 - sin2)) * cos(2*φ)
+ + (ci2 + ci6 * (0.75 - sin2))) * sin_2φ;
}
/*
* Note: a previous version checked if the value of the smallest term c8χ⋅sin(8φ) was smaller than
@@ -317,6 +385,18 @@ abstract class ConformalProjection exten
*/
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
- initialize();
+ computeCoefficients();
+ }
+
+ /**
+ * Verifies if a trigonometric identity produced the expected value. This method is used in assertions only,
+ * for values close to the [-1 … +1] range. The tolerance threshold is approximatively 1.5E-12 (note that it
+ * still about 7000 time greater than {@code Math.ulp(1.0)}).
+ *
+ * @see #ALLOW_TRIGONOMETRIC_IDENTITIES
+ */
+ static boolean identityEquals(final double actual, final double expected) {
+ return !(abs(actual - expected) > // Use !(a > b) instead of (a <= b) in order to tolerate NaN.
+ (ANGULAR_TOLERANCE / 1000) * max(1, abs(expected))); // Increase tolerance for values outside the [-1 … +1] range.
}
}
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java?rev=1707026&r1=1707025&r2=1707026&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java [UTF-8] Tue Oct 6 12:14:09 2015
@@ -210,6 +210,7 @@ public class LambertConicConformal exten
@Workaround(library="JDK", version="1.7")
private LambertConicConformal(final Initializer initializer) {
super(initializer);
+ super.computeCoefficients();
double φ0 = initializer.getAndStore(((initializer.variant & 1) != 0) ? // Odd 'type' are SP1, even 'type' are SP2.
LambertConformal1SP.LATITUDE_OF_ORIGIN : LambertConformal2SP.LATITUDE_OF_FALSE_ORIGIN);
/*
@@ -367,7 +368,7 @@ public class LambertConicConformal exten
/**
* Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite> transforms
- * as a whole. The transform returned by this method except (<var>longitude</var>, <var>latitude</var>)
+ * as a whole. The transform returned by this method expects (<var>longitude</var>, <var>latitude</var>)
* coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
*
* <p>The non-linear part of the returned transform will be {@code this} transform, except if the ellipsoid
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java?rev=1707026&r1=1707025&r2=1707026&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java [UTF-8] Tue Oct 6 12:14:09 2015
@@ -201,6 +201,7 @@ public class Mercator extends ConformalP
@Workaround(library="JDK", version="1.7")
private Mercator(final Initializer initializer) {
super(initializer);
+ super.computeCoefficients();
this.variant = initializer.variant;
/*
* The "Longitude of natural origin" parameter is found in all Mercator projections and is mandatory.
@@ -302,7 +303,7 @@ public class Mercator extends ConformalP
/**
* Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite> transforms
- * as a whole. The transform returned by this method except (<var>longitude</var>, <var>latitude</var>)
+ * as a whole. The transform returned by this method expects (<var>longitude</var>, <var>latitude</var>)
* coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
*
* <p>The non-linear part of the returned transform will be {@code this} transform, except if the ellipsoid
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java?rev=1707026&r1=1707025&r2=1707026&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java [UTF-8] Tue Oct 6 12:14:09 2015
@@ -129,40 +129,6 @@ public abstract class NormalizedProjecti
private static final long serialVersionUID = 1969740225939106310L;
/**
- * {@code false} for using the original formulas as published by EPSG, or {@code true} for using formulas
- * modified using trigonometric identities. The use of trigonometric identities is for reducing the amount
- * of calls to the {@link Math#sin(double)} and similar methods. Some identities used are:
- *
- * <ul>
- * <li>sin(2θ) = 2⋅sinθ⋅cosθ</li>
- * <li>cos(2θ) = cos²θ - sin²θ</li>
- * <li>sin(3θ) = (3 - 4⋅sin²θ)⋅sinθ</li>
- * <li>cos(3θ) = (4⋅cos³θ) - 3⋅cosθ</li>
- * <li>sin(4θ) = (4 - 8⋅sin²θ)⋅sinθ⋅cosθ</li>
- * <li>cos(4θ) = (8⋅cos⁴θ) - (8⋅cos²θ) + 1</li>
- * </ul>
- *
- * Hyperbolic formulas:
- *
- * <ul>
- * <li>sinh(2θ) = 2⋅sinhθ⋅coshθ</li>
- * <li>cosh(2θ) = cosh²θ + sinh²θ = 2⋅cosh²θ - 1 = 1 + 2⋅sinh²θ</li>
- * <li>sinh(3θ) = (3 + 4⋅sinh²θ)⋅sinhθ</li>
- * <li>cosh(3θ) = ((4⋅cosh²θ) - 3)⋅coshθ</li>
- * <li>sinh(4θ) = (1 + 2⋅sinh²θ)⋅4.sinhθ⋅coshθ
- * = 4.cosh(2θ).sinhθ⋅coshθ</li>
- * <li>cosh(4θ) = (8⋅cosh⁴θ) - (8⋅cosh²θ) + 1
- * = 8⋅cosh²(θ) ⋅ (cosh²θ - 1) + 1
- * = 8⋅cosh²(θ) ⋅ sinh²(θ) + 1
- * = 2⋅sinh²(2θ) + 1</li>
- * </ul>
- *
- * Note that since this boolean is static final, the compiler should exclude the code in the branch that is never
- * executed (no need to comment-out that code).
- */
- static final boolean ALLOW_TRIGONOMETRIC_IDENTITIES = true;
-
- /**
* Maximum difference allowed when comparing longitudes or latitudes in radians.
* The current value takes the system-wide angular tolerance value (equivalent to
* about 1 cm on Earth) converted to radians.
@@ -490,7 +456,7 @@ public abstract class NormalizedProjecti
/**
* Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite> transforms
- * as a whole. The transform returned by this method except (<var>longitude</var>, <var>latitude</var>)
+ * as a whole. The transform returned by this method expects (<var>longitude</var>, <var>latitude</var>)
* coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
* Conversion to other units and {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes
* changes in axis order} are <strong>not</strong> managed by the returned transform.
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java?rev=1707026&r1=1707025&r2=1707026&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java [UTF-8] Tue Oct 6 12:14:09 2015
@@ -133,6 +133,7 @@ public class PolarStereographic extends
@Workaround(library="JDK", version="1.7")
private PolarStereographic(final Initializer initializer) {
super(initializer);
+ super.computeCoefficients();
final byte variant = initializer.variant;
/*
* "Standard parallel" and "Latitude of origin" should be mutually exclusive,
@@ -248,7 +249,7 @@ public class PolarStereographic extends
/**
* Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite> transforms
- * as a whole. The transform returned by this method except (<var>longitude</var>, <var>latitude</var>)
+ * as a whole. The transform returned by this method expects (<var>longitude</var>, <var>latitude</var>)
* coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
*
* <p>The non-linear part of the returned transform will be {@code this} transform, except if the ellipsoid
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java?rev=1707026&r1=1707025&r2=1707026&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] Tue Oct 6 12:14:09 2015
@@ -17,8 +17,6 @@
package org.apache.sis.referencing.operation.projection;
import java.util.EnumMap;
-import java.io.IOException;
-import java.io.ObjectInputStream;
import org.opengis.util.FactoryException;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.referencing.operation.MathTransform;
@@ -65,19 +63,26 @@ import static org.apache.sis.math.MathFu
* @see Mercator
* @see ObliqueMercator
*/
-public class TransverseMercator extends NormalizedProjection {
+public class TransverseMercator extends ConformalProjection {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = 8167193470189463354L;
/**
- * Internal coefficients for computation, depending only on value of excentricity.
- * Defined in §1.3.5.1 of IOGP Publication 373-7-2 – Geomatics Guidance Note number 7, part 2 – April 2015.
+ * Coefficients in the series expansion of the forward projection,
+ * depending only on {@linkplain #excentricity excentricity} value.
+ * The series expansion is of the following form:
*
- * <p>Consider those fields as final. They are not only of the purpose of {@link #readObject(ObjectInputStream)}.</p>
+ * <blockquote>cf₂⋅f(2θ) + cf₄⋅f(4θ) + cf₆⋅f(6θ) + cf₈⋅f(8θ)</blockquote>
+ *
+ * Those coefficients are named h₁, h₂, h₃ and h₄ in §1.3.5.1 of
+ * IOGP Publication 373-7-2 – Geomatics Guidance Note number 7, part 2 – April 2015.
+ *
+ * <p><strong>Consider those fields as final!</strong>
+ * They are not final only for the purpose of {@link #computeCoefficients()}.</p>
*/
- private transient double h1, h2, h3, h4, ih1, ih2, ih3, ih4;
+ private transient double cf2, cf4, cf6, cf8;
/**
* Creates a Transverse Mercator projection from the given parameters.
@@ -138,7 +143,7 @@ public class TransverseMercator extends
*/
final DoubleDouble t = initializer.axisLengthRatio(); // t = b/a
t.ratio_1m_1p(); // t = (1 - t) / (1 + t)
- initialize(t.doubleValue());
+ computeCoefficients(t.doubleValue());
/*
* Compute B = (1 + n²/4 + n⁴/64) / (1 + n)
*/
@@ -161,10 +166,10 @@ public class TransverseMercator extends
final double Q = asinh(tan(φ0)) - excentricity * atanh(excentricity * sin(φ0));
final double β = atan(sinh(Q));
final DoubleDouble M0 = new DoubleDouble();
- M0.value = h4 * sin(8*β)
- + h3 * sin(6*β)
- + h2 * sin(4*β)
- + h1 * sin(2*β)
+ M0.value = cf8 * sin(8*β)
+ + cf6 * sin(6*β)
+ + cf4 * sin(4*β)
+ + cf2 * sin(2*β)
+ β;
M0.multiply(B);
M0.negate();
@@ -188,21 +193,22 @@ public class TransverseMercator extends
/*
* When rewriting equations using trigonometric identities, some constants appear.
* For example sin(2θ) = 2⋅sinθ⋅cosθ, so we can factor out the 2 constant into the
- * corresponding 'h' field. Note: this factorization can only be performed after
+ * corresponding 'c' field. Note: this factorization can only be performed after
* the constructor finished to compute other constants.
*/
if (ALLOW_TRIGONOMETRIC_IDENTITIES) {
- h2 *= 4; ih2 *= 4;
- h3 *= 16; ih3 *= 16;
- h4 *= 64; ih4 *= 64;
+ // Multiplication by powers of 2 does not bring any additional rounding error.
+ cf4 *= 4; ci4 *= 4;
+ cf6 *= 16; ci6 *= 16;
+ cf8 *= 64; ci8 *= 64;
}
}
/**
- * Restores transient fields after deserialization.
+ * Automatically invoked after deserialization for restoring transient fields.
*/
- private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
- in.defaultReadObject();
+ @Override
+ final void computeCoefficients() {
/*
* Double-double precision is necessary for computing 'n', otherwise we have rounding errors
* in the 3 last digits. Note that we still have sometime a 1 ULP difference compared to the
@@ -212,11 +218,12 @@ public class TransverseMercator extends
t.subtract(excentricitySquared, 0);
t.sqrt();
t.ratio_1m_1p();
- initialize(t.doubleValue());
+ computeCoefficients(t.doubleValue());
if (ALLOW_TRIGONOMETRIC_IDENTITIES) {
- h2 *= 4; ih2 *= 4;
- h3 *= 16; ih3 *= 16;
- h4 *= 64; ih4 *= 64;
+ // Same scaling than in the constructor.
+ cf4 *= 4; ci4 *= 4;
+ cf6 *= 16; ci6 *= 16;
+ cf8 *= 64; ci8 *= 64;
}
}
@@ -238,26 +245,26 @@ public class TransverseMercator extends
*
* @param n The value of {@code f / (2-f)} where {@code f} is the flattening factor.
*/
- private void initialize(final double n) {
+ private void computeCoefficients(final double n) {
final double n2 = n * n;
final double n3 = n2 * n;
final double n4 = n2 * n2;
/*
- * Coefficients for direct projection.
+ * Coefficients for the forward projections.
* Add the smallest values first in order to reduce rounding errors.
*/
- h1 = ( 41. / 180)*n4 + ( 5. / 16)*n3 + (-2. / 3)*n2 + n/2;
- h2 = ( 557. / 1440)*n4 + (-3. / 5)*n3 + (13. / 48)*n2;
- h3 = ( -103. / 140)*n4 + (61. / 240)*n3;
- h4 = (49561. / 161280)*n4;
+ cf2 = ( 41. / 180)*n4 + ( 5. / 16)*n3 + (-2. / 3)*n2 + n/2;
+ cf4 = ( 557. / 1440)*n4 + (-3. / 5)*n3 + (13. / 48)*n2;
+ cf6 = ( -103. / 140)*n4 + (61. / 240)*n3;
+ cf8 = (49561. / 161280)*n4;
/*
- * Coefficients for inverse projection.
+ * Coefficients for the inverse projections.
* Add the smallest values first in order to reduce rounding errors.
*/
- ih1 = ( -1. / 360)*n4 + (37. / 96)*n3 + (-2. / 3)*n2 + n/2;
- ih2 = (-437. / 1440)*n4 + ( 1. / 15)*n3 + ( 1. / 48)*n2;
- ih3 = ( -37. / 840)*n4 + (17. / 480)*n3;
- ih4 = (4397. / 161280)*n4;
+ ci2 = ( -1. / 360)*n4 + (37. / 96)*n3 + (-2. / 3)*n2 + n/2;
+ ci4 = (-437. / 1440)*n4 + ( 1. / 15)*n3 + ( 1. / 48)*n2;
+ ci6 = ( -37. / 840)*n4 + (17. / 480)*n3;
+ ci8 = (4397. / 161280)*n4;
}
/**
@@ -265,19 +272,15 @@ public class TransverseMercator extends
*/
TransverseMercator(final TransverseMercator other) {
super(other);
- h1 = other. h1;
- h2 = other. h2;
- h3 = other. h3;
- h4 = other. h4;
- ih1 = other.ih1;
- ih2 = other.ih2;
- ih3 = other.ih3;
- ih4 = other.ih4;
+ cf2 = other.cf2;
+ cf4 = other.cf4;
+ cf6 = other.cf6;
+ cf8 = other.cf8;
}
/**
* Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite> transforms
- * as a whole. The transform returned by this method except (<var>longitude</var>, <var>latitude</var>)
+ * as a whole. The transform returned by this method expects (<var>longitude</var>, <var>latitude</var>)
* coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
*
* <p>The non-linear part of the returned transform will be {@code this} transform, except if the ellipsoid
@@ -309,8 +312,8 @@ public class TransverseMercator extends
final double[] dstPts, final int dstOff,
final boolean derivate) throws ProjectionException
{
- final double λ = srcPts[srcOff];
- final double φ = srcPts[srcOff + 1];
+ final double λ = srcPts[srcOff ];
+ final double φ = srcPts[srcOff+1];
final double sinλ = sin(λ);
final double ℯsinφ = sin(φ) * excentricity;
final double Q = asinh(tan(φ)) - atanh(ℯsinφ) * excentricity;
@@ -346,12 +349,12 @@ public class TransverseMercator extends
} else {
final double sin2 = sin_2ξ0 * sin_2ξ0;
final double cos2 = cos_2ξ0 * cos_2ξ0;
- sin_4ξ0 = sin_2ξ0 * cos_2ξ0; // sin(4⋅ξ₀) ÷ 2
- cos_4ξ0 = (cos2 - sin2) * 0.5; // cos(4⋅ξ₀) ÷ 2
- sin_6ξ0 = (0.75 - sin2) * sin_2ξ0; // sin(6⋅ξ₀) ÷ 4
- cos_6ξ0 = (cos2 - 0.75) * cos_2ξ0; // cos(6⋅ξ₀) ÷ 4
- sin_8ξ0 = sin_4ξ0 * cos_4ξ0; // sin(8⋅ξ₀) ÷ 8
- cos_8ξ0 = 0.125 - sin_4ξ0 * sin_4ξ0; // cos(8⋅ξ₀) ÷ 8
+ sin_4ξ0 = sin_2ξ0 * cos_2ξ0; assert identityEquals(sin_4ξ0, sin(4*ξ0) / 2) : ξ0;
+ cos_4ξ0 = (cos2 - sin2) * 0.5; assert identityEquals(cos_4ξ0, cos(4*ξ0) / 2) : ξ0;
+ sin_6ξ0 = (0.75 - sin2) * sin_2ξ0; assert identityEquals(sin_6ξ0, sin(6*ξ0) / 4) : ξ0;
+ cos_6ξ0 = (cos2 - 0.75) * cos_2ξ0; assert identityEquals(cos_6ξ0, cos(6*ξ0) / 4) : ξ0;
+ sin_8ξ0 = sin_4ξ0 * cos_4ξ0; assert identityEquals(sin_8ξ0, sin(8*ξ0) / 8) : ξ0;
+ cos_8ξ0 = 0.125 - sin_4ξ0 * sin_4ξ0; assert identityEquals(cos_8ξ0, cos(8*ξ0) / 8) : ξ0;
}
/*
* Compute sinh(2⋅ξ₀), sinh(4⋅ξ₀), sinh(6⋅ξ₀), sinh(8⋅ξ₀) and same for cosh, but using the following
@@ -372,12 +375,12 @@ public class TransverseMercator extends
} else {
final double sinh2 = sinh_2η0 * sinh_2η0;
final double cosh2 = cosh_2η0 * cosh_2η0;
- cosh_4η0 = (cosh2 + sinh2) * 0.5; // cosh(4⋅η₀) ÷ 2
- sinh_4η0 = cosh_2η0 * sinh_2η0; // sinh(4⋅η₀) ÷ 2
- cosh_6η0 = cosh_2η0 * (cosh2 - 0.75); // cosh(6⋅η₀) ÷ 4
- sinh_6η0 = sinh_2η0 * (sinh2 + 0.75); // sinh(6⋅η₀) ÷ 4
- cosh_8η0 = sinh_4η0 * sinh_4η0 + 0.125; // cosh(8⋅η₀) ÷ 8
- sinh_8η0 = sinh_4η0 * cosh_4η0; // sinh(8⋅η₀) ÷ 8
+ cosh_4η0 = (cosh2 + sinh2) * 0.5; assert identityEquals(cosh_4η0, cosh(4*η0) / 2) : η0;
+ sinh_4η0 = cosh_2η0 * sinh_2η0; assert identityEquals(sinh_4η0, sinh(4*η0) / 2) : η0;
+ cosh_6η0 = cosh_2η0 * (cosh2 - 0.75); assert identityEquals(cosh_6η0, cosh(6*η0) / 4) : η0;
+ sinh_6η0 = sinh_2η0 * (sinh2 + 0.75); assert identityEquals(sinh_6η0, sinh(6*η0) / 4) : η0;
+ cosh_8η0 = sinh_4η0 * sinh_4η0 + 0.125; assert identityEquals(cosh_8η0, cosh(8*η0) / 8) : η0;
+ sinh_8η0 = sinh_4η0 * cosh_4η0; assert identityEquals(sinh_8η0, sinh(8*η0) / 8) : η0;
}
/*
* Assuming that (λ, φ) ↦ Proj((λ, φ))
@@ -386,22 +389,22 @@ public class TransverseMercator extends
* => (λ, φ) ↦ (η(λ, φ), ξ(λ, φ)).
*/
//-- ξ(λ, φ)
- final double ξ = h4 * sin_8ξ0 * cosh_8η0
- + h3 * sin_6ξ0 * cosh_6η0
- + h2 * sin_4ξ0 * cosh_4η0
- + h1 * sin_2ξ0 * cosh_2η0
+ final double ξ = cf8 * sin_8ξ0 * cosh_8η0
+ + cf6 * sin_6ξ0 * cosh_6η0
+ + cf4 * sin_4ξ0 * cosh_4η0
+ + cf2 * sin_2ξ0 * cosh_2η0
+ ξ0;
//-- η(λ, φ)
- final double η = h4 * cos_8ξ0 * sinh_8η0
- + h3 * cos_6ξ0 * sinh_6η0
- + h2 * cos_4ξ0 * sinh_4η0
- + h1 * cos_2ξ0 * sinh_2η0
+ final double η = cf8 * cos_8ξ0 * sinh_8η0
+ + cf6 * cos_6ξ0 * sinh_6η0
+ + cf4 * cos_4ξ0 * sinh_4η0
+ + cf2 * cos_2ξ0 * sinh_2η0
+ η0;
if (dstPts != null) {
- dstPts[dstOff ] = η;
- dstPts[dstOff + 1] = ξ;
+ dstPts[dstOff ] = η;
+ dstPts[dstOff+1] = ξ;
}
if (!derivate) {
return null;
@@ -436,31 +439,31 @@ public class TransverseMercator extends
*/
//-- dξ(λ, φ) / dλ
final double dξ_dλ = dξ0_dλ
- + 2 * (h1 * (dξ0_dλ * cos_2ξ0 * cosh_2η0 + dη0_dλ * sinh_2η0 * sin_2ξ0)
- + 3 * h3 * (dξ0_dλ * cos_6ξ0 * cosh_6η0 + dη0_dλ * sinh_6η0 * sin_6ξ0)
- + 2 * (h2 * (dξ0_dλ * cos_4ξ0 * cosh_4η0 + dη0_dλ * sinh_4η0 * sin_4ξ0)
- + 2 * h4 * (dξ0_dλ * cos_8ξ0 * cosh_8η0 + dη0_dλ * sinh_8η0 * sin_8ξ0)));
+ + 2 * (cf2 * (dξ0_dλ * cos_2ξ0 * cosh_2η0 + dη0_dλ * sinh_2η0 * sin_2ξ0)
+ + 3 * cf6 * (dξ0_dλ * cos_6ξ0 * cosh_6η0 + dη0_dλ * sinh_6η0 * sin_6ξ0)
+ + 2 * (cf4 * (dξ0_dλ * cos_4ξ0 * cosh_4η0 + dη0_dλ * sinh_4η0 * sin_4ξ0)
+ + 2 * cf8 * (dξ0_dλ * cos_8ξ0 * cosh_8η0 + dη0_dλ * sinh_8η0 * sin_8ξ0)));
//-- dξ(λ, φ) / dφ
final double dξ_dφ = dξ0_dφ
- + 2 * (h1 * (dξ0_dφ * cos_2ξ0 * cosh_2η0 + dη0_dφ * sinh_2η0 * sin_2ξ0)
- + 3 * h3 * (dξ0_dφ * cos_6ξ0 * cosh_6η0 + dη0_dφ * sinh_6η0 * sin_6ξ0)
- + 2 * (h2 * (dξ0_dφ * cos_4ξ0 * cosh_4η0 + dη0_dφ * sinh_4η0 * sin_4ξ0)
- + 2 * h4 * (dξ0_dφ * cos_8ξ0 * cosh_8η0 + dη0_dφ * sinh_8η0 * sin_8ξ0)));
+ + 2 * (cf2 * (dξ0_dφ * cos_2ξ0 * cosh_2η0 + dη0_dφ * sinh_2η0 * sin_2ξ0)
+ + 3 * cf6 * (dξ0_dφ * cos_6ξ0 * cosh_6η0 + dη0_dφ * sinh_6η0 * sin_6ξ0)
+ + 2 * (cf4 * (dξ0_dφ * cos_4ξ0 * cosh_4η0 + dη0_dφ * sinh_4η0 * sin_4ξ0)
+ + 2 * cf8 * (dξ0_dφ * cos_8ξ0 * cosh_8η0 + dη0_dφ * sinh_8η0 * sin_8ξ0)));
//-- dη(λ, φ) / dλ
final double dη_dλ = dη0_dλ
- + 2 * (h1 * (dη0_dλ * cosh_2η0 * cos_2ξ0 - dξ0_dλ * sin_2ξ0 * sinh_2η0)
- + 3 * h3 * (dη0_dλ * cosh_6η0 * cos_6ξ0 - dξ0_dλ * sin_6ξ0 * sinh_6η0)
- + 2 * (h2 * (dη0_dλ * cosh_4η0 * cos_4ξ0 - dξ0_dλ * sin_4ξ0 * sinh_4η0)
- + 2 * h4 * (dη0_dλ * cosh_8η0 * cos_8ξ0 - dξ0_dλ * sin_8ξ0 * sinh_8η0)));
+ + 2 * (cf2 * (dη0_dλ * cosh_2η0 * cos_2ξ0 - dξ0_dλ * sin_2ξ0 * sinh_2η0)
+ + 3 * cf6 * (dη0_dλ * cosh_6η0 * cos_6ξ0 - dξ0_dλ * sin_6ξ0 * sinh_6η0)
+ + 2 * (cf4 * (dη0_dλ * cosh_4η0 * cos_4ξ0 - dξ0_dλ * sin_4ξ0 * sinh_4η0)
+ + 2 * cf8 * (dη0_dλ * cosh_8η0 * cos_8ξ0 - dξ0_dλ * sin_8ξ0 * sinh_8η0)));
//-- dη(λ, φ) / dφ
final double dη_dφ = dη0_dφ
- + 2 * (h1 * (dη0_dφ * cosh_2η0 * cos_2ξ0 - dξ0_dφ * sin_2ξ0 * sinh_2η0)
- + 3 * h3 * (dη0_dφ * cosh_6η0 * cos_6ξ0 - dξ0_dφ * sin_6ξ0 * sinh_6η0)
- + 2 * (h2 * (dη0_dφ * cosh_4η0 * cos_4ξ0 - dξ0_dφ * sin_4ξ0 * sinh_4η0)
- + 2 * h4 * (dη0_dφ * cosh_8η0 * cos_8ξ0 - dξ0_dφ * sin_8ξ0 * sinh_8η0)));
+ + 2 * (cf2 * (dη0_dφ * cosh_2η0 * cos_2ξ0 - dξ0_dφ * sin_2ξ0 * sinh_2η0)
+ + 3 * cf6 * (dη0_dφ * cosh_6η0 * cos_6ξ0 - dξ0_dφ * sin_6ξ0 * sinh_6η0)
+ + 2 * (cf4 * (dη0_dφ * cosh_4η0 * cos_4ξ0 - dξ0_dφ * sin_4ξ0 * sinh_4η0)
+ + 2 * cf8 * (dη0_dφ * cosh_8η0 * cos_8ξ0 - dξ0_dφ * sin_8ξ0 * sinh_8η0)));
return new Matrix2(dη_dλ, dη_dφ,
dξ_dλ, dξ_dφ);
@@ -476,8 +479,8 @@ public class TransverseMercator extends
final double[] dstPts, final int dstOff)
throws ProjectionException
{
- final double η = srcPts[srcOff];
- final double ξ = srcPts[srcOff + 1];
+ final double η = srcPts[srcOff ];
+ final double ξ = srcPts[srcOff+1];
/*
* Following calculation of sin_2ξ, sin_4ξ, etc. is basically a copy-and-paste of the code in transform(…).
* Its purpose is the same than for transform(…): reduce the amount of calls to Math.sin(double) and other
@@ -508,34 +511,34 @@ public class TransverseMercator extends
} else {
final double sin2 = sin_2ξ * sin_2ξ;
final double cos2 = cos_2ξ * cos_2ξ;
- sin_4ξ = sin_2ξ * cos_2ξ; // sin(4⋅ξ) ÷ 2
- cos_4ξ = (cos2 - sin2) * 0.5; // cos(4⋅ξ) ÷ 2
- sin_6ξ = (0.75 - sin2) * sin_2ξ; // sin(6⋅ξ) ÷ 4
- cos_6ξ = (cos2 - 0.75) * cos_2ξ; // cos(6⋅ξ) ÷ 4
- sin_8ξ = sin_4ξ * cos_4ξ; // sin(8⋅ξ) ÷ 8
- cos_8ξ = 0.125 - sin_4ξ * sin_4ξ; // cos(8⋅ξ) ÷ 8
+ sin_4ξ = sin_2ξ * cos_2ξ; assert identityEquals(sin_4ξ, sin(4*ξ) / 2) : ξ;
+ cos_4ξ = (cos2 - sin2) * 0.5; assert identityEquals(cos_4ξ, cos(4*ξ) / 2) : ξ;
+ sin_6ξ = (0.75 - sin2) * sin_2ξ; assert identityEquals(sin_6ξ, sin(6*ξ) / 4) : ξ;
+ cos_6ξ = (cos2 - 0.75) * cos_2ξ; assert identityEquals(cos_6ξ, cos(6*ξ) / 4) : ξ;
+ sin_8ξ = sin_4ξ * cos_4ξ; assert identityEquals(sin_8ξ, sin(8*ξ) / 8) : ξ;
+ cos_8ξ = 0.125 - sin_4ξ * sin_4ξ; assert identityEquals(cos_8ξ, cos(8*ξ) / 8) : ξ;
final double sinh2 = sinh_2η * sinh_2η;
final double cosh2 = cosh_2η * cosh_2η;
- cosh_4η = (cosh2 + sinh2) * 0.5; // cosh(4⋅η) ÷ 2
- sinh_4η = cosh_2η * sinh_2η; // sinh(4⋅η) ÷ 2
- cosh_6η = cosh_2η * (cosh2 - 0.75); // cosh(6⋅η) ÷ 4
- sinh_6η = sinh_2η * (sinh2 + 0.75); // sinh(6⋅η) ÷ 4
- cosh_8η = sinh_4η * sinh_4η + 0.125; // cosh(8⋅η) ÷ 8
- sinh_8η = sinh_4η * cosh_4η; // sinh(8⋅η) ÷ 8
+ cosh_4η = (cosh2 + sinh2) * 0.5; assert identityEquals(cosh_4η, cosh(4*η) / 2) : η;
+ sinh_4η = cosh_2η * sinh_2η; assert identityEquals(sinh_4η, sinh(4*η) / 2) : η;
+ cosh_6η = cosh_2η * (cosh2 - 0.75); assert identityEquals(cosh_6η, cosh(6*η) / 4) : η;
+ sinh_6η = sinh_2η * (sinh2 + 0.75); assert identityEquals(sinh_6η, sinh(6*η) / 4) : η;
+ cosh_8η = sinh_4η * sinh_4η + 0.125; assert identityEquals(cosh_8η, cosh(8*η) / 8) : η;
+ sinh_8η = sinh_4η * cosh_4η; assert identityEquals(sinh_8η, sinh(8*η) / 8) : η;
}
/*
* The actual inverse transform.
*/
- final double ξ0 = ξ - (ih4 * sin_8ξ * cosh_8η
- + ih3 * sin_6ξ * cosh_6η
- + ih2 * sin_4ξ * cosh_4η
- + ih1 * sin_2ξ * cosh_2η);
-
- final double η0 = η - (ih4 * cos_8ξ * sinh_8η
- + ih3 * cos_6ξ * sinh_6η
- + ih2 * cos_4ξ * sinh_4η
- + ih1 * cos_2ξ * sinh_2η);
+ final double ξ0 = ξ - (ci8 * sin_8ξ * cosh_8η
+ + ci6 * sin_6ξ * cosh_6η
+ + ci4 * sin_4ξ * cosh_4η
+ + ci2 * sin_2ξ * cosh_2η);
+
+ final double η0 = η - (ci8 * cos_8ξ * sinh_8η
+ + ci6 * cos_6ξ * sinh_6η
+ + ci4 * cos_4ξ * sinh_4η
+ + ci2 * cos_2ξ * sinh_2η);
final double β = asin(sin(ξ0) / cosh(η0));
final double Q = asinh(tan(β));
@@ -547,8 +550,8 @@ public class TransverseMercator extends
final double c = excentricity * atanh(excentricity * tanh(Qp));
Qp = Q + c;
if (abs(c - p) <= ITERATION_TOLERANCE) {
- dstPts[dstOff ] = asin(tanh(η0) / cos(β));
- dstPts[dstOff + 1] = atan(sinh(Qp));
+ dstPts[dstOff ] = asin(tanh(η0) / cos(β));
+ dstPts[dstOff+1] = atan(sinh(Qp));
return;
}
p = c;
@@ -590,8 +593,8 @@ public class TransverseMercator extends
final double[] dstPts, final int dstOff,
final boolean derivate) throws ProjectionException
{
- final double λ = srcPts[srcOff];
- final double φ = srcPts[srcOff + 1];
+ final double λ = srcPts[srcOff ];
+ final double φ = srcPts[srcOff+1];
final double sinλ = sin(λ);
final double cosλ = cos(λ);
final double sinφ = sin(φ);
Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java?rev=1707026&r1=1707025&r2=1707026&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java [UTF-8] Tue Oct 6 12:14:09 2015
@@ -37,7 +37,7 @@ import org.junit.Test;
import static java.lang.StrictMath.*;
import static java.lang.Double.*;
-import static org.junit.Assert.*;
+import static org.apache.sis.test.Assert.*;
/**
@@ -49,7 +49,7 @@ import static org.junit.Assert.*;
* @author Martin Desruisseaux (Geomatys)
* @author Rémi Maréchal (Geomatys)
* @since 0.6
- * @version 0.6
+ * @version 0.7
* @module
*/
@DependsOn(ConformalProjectionTest.class)
@@ -336,4 +336,23 @@ public final strictfp class LambertConic
tolerance = Formulas.LINEAR_TOLERANCE;
compareEllipticalWithSpherical(CoordinateDomain.GEOGRAPHIC_SAFE, 0);
}
+
+ /**
+ * Verifies that deserialized projections work as expected. This implies that deserialization
+ * recomputed the internal transient fields, especially the series expansion coefficients.
+ *
+ * @throws FactoryException if an error occurred while creating the map projection.
+ * @throws TransformException if an error occurred while projecting a coordinate.
+ */
+ @Test
+ @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[] target = new double[source.length];
+ transform.transform(source, 0, target, 0, 10);
+ transform = assertSerializedEquals(transform);
+ tolerance = Formulas.LINEAR_TOLERANCE;
+ verifyTransform(source, target);
+ }
}
Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java?rev=1707026&r1=1707025&r2=1707026&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java [UTF-8] Tue Oct 6 12:14:09 2015
@@ -74,6 +74,7 @@ final strictfp class NoOp extends Confor
super(new Initializer(new DefaultOperationMethod(
Collections.singletonMap(DefaultOperationMethod.NAME_KEY, parameters.getDescriptor().getName()),
2, 2, parameters.getDescriptor()), parameters, Collections.emptyMap(), (byte) 0));
+ super.computeCoefficients();
}
/**
Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java?rev=1707026&r1=1707025&r2=1707026&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java [UTF-8] Tue Oct 6 12:14:09 2015
@@ -24,9 +24,11 @@ import org.apache.sis.parameter.Paramete
import org.apache.sis.referencing.operation.transform.CoordinateDomain;
import org.apache.sis.test.DependsOnMethod;
import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestUtilities;
import org.junit.Test;
import static java.lang.StrictMath.toRadians;
+import static org.apache.sis.test.Assert.*;
/**
@@ -34,7 +36,7 @@ import static java.lang.StrictMath.toRad
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.6
- * @version 0.6
+ * @version 0.7
* @module
*/
@DependsOn(NormalizedProjectionTest.class)
@@ -138,4 +140,23 @@ public final strictfp class TransverseMe
verifyDerivative(toRadians(-3), toRadians(30));
verifyDerivative(toRadians(+6), toRadians(60));
}
+
+ /**
+ * Verifies that deserialized projections work as expected. This implies that deserialization
+ * recomputed the internal transient fields, especially the series expansion coefficients.
+ *
+ * @throws FactoryException if an error occurred while creating the map projection.
+ * @throws TransformException if an error occurred while projecting a coordinate.
+ */
+ @Test
+ @DependsOnMethod("testTransverseMercator")
+ public void testSerialization() throws FactoryException, TransformException {
+ createNormalizedProjection(true, 40);
+ final double[] source = CoordinateDomain.GEOGRAPHIC_RADIANS_HALF_λ.generateRandomInput(TestUtilities.createRandomNumberGenerator(), 2, 10);
+ final double[] target = new double[source.length];
+ transform.transform(source, 0, target, 0, 10);
+ transform = assertSerializedEquals(transform);
+ tolerance = Formulas.LINEAR_TOLERANCE;
+ verifyTransform(source, target);
+ }
}