Author: desruisseaux
Date: Wed Mar 25 14:47:22 2015
New Revision: 1669129
URL: http://svn.apache.org/r1669129
Log:
Referencing: clarified what the formulas mean.
Modified:
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/UnitaryProjection.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/UnitaryProjectionTest.java
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=1669129&r1=1669128&r2=1669129&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] Wed Mar 25 14:47:22 2015
@@ -121,10 +121,9 @@ public class Mercator extends UnitaryPro
final double[] dstPts, final int dstOff,
final boolean derivate) throws ProjectionException
{
- final double λ = srcPts[srcOff];
- final double φ = srcPts[srcOff + 1];
- final double sinφ = sin(φ);
- final double ℯsinφ = excentricity * sinφ;
+ final double λ = srcPts[srcOff];
+ final double φ = srcPts[srcOff + 1];
+ final double sinφ = sin(φ);
/*
* Projection of zero is zero. However the formulas below have a
slight rounding error
* which produce values close to 1E-10, so we will avoid them when
y=0. In addition of
@@ -133,29 +132,19 @@ public class Mercator extends UnitaryPro
final double y;
if (sinφ == 0) {
y = φ;
+ } else if (φ == PI/2) { // Really exact comparison, no epsilon
here.
+ y = POSITIVE_INFINITY; // See javadoc of Spherical inner class
for rational.
} else {
- // See the javadoc of the Spherical inner class for a note
- // about why we perform explicit checks for the pole cases.
- final double absφ = abs(φ);
- if (absφ < PI/2) {
- y = log(exp_y(φ, ℯsinφ)); // Snyder (7-7)
- } else {
- y = copySign(absφ <= (PI/2 + ANGLE_TOLERANCE) ?
POSITIVE_INFINITY : NaN, φ);
- }
+ y = log(expOfNorthing(φ, excentricity * sinφ)); // Snyder (7-7)
}
if (dstPts != null) {
dstPts[dstOff] = λ;
dstPts[dstOff+1] = y;
}
- if (!derivate) {
- return null;
- }
/*
* End of map projection. Now compute the derivative, if requested.
*/
- final double cosφ = cos(φ);
- final double t = (1 - sinφ) / cosφ;
- return new Matrix2(1, 0, 0, 0.5*(t + 1/t) - excentricitySquared*cosφ /
(1 - ℯsinφ*ℯsinφ));
+ return derivate ? new Matrix2(1, 0, 0, dy_dφ(sinφ, cos(φ))) : null;
}
/**
@@ -181,13 +170,10 @@ public class Mercator extends UnitaryPro
final double φ = dstPts[dstOff += 2]; // Same as srcPts[srcOff
+ 1].
final double y;
if (φ != 0) {
- // See the javadoc of the Spherical inner class for a note
- // about why we perform explicit checks for the pole cases.
- final double absφ = abs(φ);
- if (absφ < PI/2) {
- y = log(exp_y(φ, excentricity * sin(φ)));
+ if (φ == PI/2) { // Really exact comparison, no
epsilon here.
+ y = POSITIVE_INFINITY; // See javadoc of Spherical
inner class for rational.
} else {
- y = copySign(absφ <= (PI/2 + ANGLE_TOLERANCE) ?
POSITIVE_INFINITY : NaN, φ);
+ y = log(expOfNorthing(φ, excentricity * sin(φ)));
}
dstPts[dstOff] = y;
}
@@ -216,12 +202,20 @@ public class Mercator extends UnitaryPro
* Provides the transform equations for the spherical case of the Mercator
projection.
*
* <div class="note"><b>Implementation note:</b>
- * this class contains explicit checks for latitude values at poles. If
floating point arithmetic had infinite
- * precision, those checks would not be necessary since the formulas lead
naturally to infinite values at poles,
+ * this class contains an explicit check for latitude values at a pole. If
floating point arithmetic had infinite
+ * precision, such checks would not be necessary since the formulas lead
naturally to infinite values at poles,
* which is the correct answer. In practice the infinite value emerges by
itself at only one pole, and the other
- * one produces a high value (approximatively 1E+16). This is because
there is no accurate representation of π/2
- * in base 2, and consequently {@code tan(π/2)} does not returns the
infinite value.
- * </div>
+ * one produces a high value (approximatively 1E+16). This is because
there is no accurate representation of π/2,
+ * and consequently {@code tan(π/2)} does not return the infinite value.
We workaround this issue with an explicit
+ * check for φ = π/2 only. Note that:
+ *
+ * <ul>
+ * <li>We check for strict equality, not a comparison with a tolerance
threshold, because the arithmetic is not
+ * broken for values close to pole. We check π/2 because this is the
result of converting 90°N to radians,
+ * and we presume that the user really wanted to said 90°N. But for
all other values we can let the math do
+ * their "natural" work.</li>
+ * <li>There is no need to check for φ = -π/2 as our arithmetic already
produces negative infinity.</li>
+ * </ul></div>
*
* @author Martin Desruisseaux (MPO, IRD, Geomatys)
* @author Rueben Schulz (UBC)
@@ -279,14 +273,10 @@ public class Mercator extends UnitaryPro
final double y;
if (φ == 0) {
y = φ;
+ } else if (φ == PI/2) { // Really exact comparison, no
epsilon here.
+ y = POSITIVE_INFINITY; // See class javadoc for
rational.
} else {
- // See class javadoc for a note about explicit check for poles.
- final double a = abs(φ);
- if (a < PI/2) {
- y = log(tan(PI/4 + 0.5*φ)); // Snyder (7-2)
- } else {
- y = copySign(a <= (PI/2 + ANGLE_TOLERANCE) ?
POSITIVE_INFINITY : NaN, φ);
- }
+ y = log(tan(PI/4 + 0.5*φ)); // Part of Snyder (7-2)
}
Matrix derivative = null;
if (derivate) {
@@ -321,13 +311,13 @@ public class Mercator extends UnitaryPro
} else {
dstOff--;
while (--numPts >= 0) {
- double y = dstPts[dstOff += 2]; // Same as
srcPts[srcOff …].
- if (y != 0) {
- final double a = abs(y);
- if (a < PI/2) {
- y = log(tan(PI/4 + 0.5*y)); // Snyder (7-2)
+ final double φ = dstPts[dstOff += 2]; // Same as
srcPts[srcOff + 1].
+ final double y;
+ if (φ != 0) {
+ if (φ == PI/2) { // Really exact
comparison, no epsilon here.
+ y = POSITIVE_INFINITY; // See class
javadoc for rational.
} else {
- y = copySign(a <= (PI/2 + ANGLE_TOLERANCE) ?
POSITIVE_INFINITY : NaN, y);
+ y = log(tan(PI/4 + 0.5*φ)); // Part of Snyder
(7-2)
}
dstPts[dstOff] = y;
}
@@ -345,7 +335,7 @@ public class Mercator extends UnitaryPro
{
double x = srcPts[srcOff ];
double y = srcPts[srcOff+1];
- y = PI/2 - 2 * atan(exp(-y)); // Snyder (7-4)
+ y = PI/2 - 2 * atan(exp(-y)); // Part of Snyder (7-4)
assert pseudo || checkInverseTransform(srcPts, srcOff, dstPts,
dstOff, x, y);
dstPts[dstOff ] = x;
dstPts[dstOff+1] = y;
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/UnitaryProjection.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/UnitaryProjection.java?rev=1669129&r1=1669128&r2=1669129&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/UnitaryProjection.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/UnitaryProjection.java
[UTF-8] Wed Mar 25 14:47:22 2015
@@ -90,11 +90,6 @@ public abstract class UnitaryProjection
private static final long serialVersionUID = 1969740225939106310L;
/**
- * Tolerance in the correctness of argument values provided to the
mathematical functions defined in this class.
- */
- private static final double ARGUMENT_TOLERANCE = 1E-15;
-
- /**
* Maximum difference allowed when comparing longitudes or latitudes in
radians.
* A tolerance of 1E-6 is about 0.2 second of arcs, which is about 6
kilometers
* (computed from the standard length of nautical mile).
@@ -445,7 +440,7 @@ public abstract class UnitaryProjection
}
/**
- * Computes part of the Mercator projection for the given latitude. This
formulas is also part of
+ * Computes part of the Mercator projection for the given latitude. This
formula is also part of
* Lambert Conic Conformal projection, since Mercator can be considered as
a special case of that
* Lambert projection with the equator as the single standard parallel.
*
@@ -455,21 +450,21 @@ public abstract class UnitaryProjection
* <div class="section">Properties</div>
* This function has the following identity (ignoring rounding errors):
*
- * <blockquote>exp_y(φ) = 1/exp_y(-φ)</blockquote>
+ * <blockquote>expOfNorthing(-φ) = 1 / expOfNorthing(φ)</blockquote>
*
* This function has a periodicity of 2π.
* The result is always a positive value when φ is valid (more on it
below).
* More specifically its behavior at some particular points is:
*
* <ul>
- * <li>If φ is NaN or infinite, then the result is NaN.</li>
- * <li>If φ is -π/2, then the result is close to 0.</li>
- * <li>If φ is 0, then the result is close to 1.</li>
- * <li>If φ is +π/2, then the result tends toward positive infinity.
- * The actual result is not infinity however, but some large value
like 1E+10.</li>
+ * <li>expOfNorthing(-π/2) = 0</li>
+ * <li>expOfNorthing( 0 ) = 1</li>
+ * <li>expOfNorthing(+π/2) → ∞ (actually some large value like
1.633E+16)</li>
+ * <li>expOfNorthing( ∞ ) = NaN</li>
+ * <li>expOfNorthing(NaN) = NaN</li>
* <li>If φ, after removal of any 2π periodicity, still outside the
[-π/2 … π/2] range,
* then the result is a negative number. If the caller is going to
compute the logarithm
- * of the returned value as in the Mercator projection, he will get
NaN.</li>
+ * of the returned value as in the Mercator projection, (s)he will
get NaN.</li>
* </ul>
*
* <div class="section">Relationship with Snyder</div>
@@ -478,63 +473,78 @@ public abstract class UnitaryProjection
* <ul>
* <li>(7-7) in the <cite>Mercator projection</cite> chapter.</li>
* <li>Reciprocal of (9-13) in the <cite>Oblique Mercator
projection</cite> chapter.</li>
- * <li>Reciprocal of (15-9) in the <cite>Lambert Conformal Conic
projection</cite> chapter.
- * Note: tan(π/4 - φ/2) = 1/tan(π/4 + φ/2).</li>
+ * <li>Reciprocal of (15-9) in the <cite>Lambert Conformal Conic
projection</cite> chapter.</li>
* </ul>
*
* @param φ The latitude in radians.
* @param ℯsinφ The sine of the φ argument multiplied by {@link
#excentricity}.
* @return {@code Math.exp} of the Mercator projection of the given
latitude.
+ *
+ * @see #φ(double)
+ * @see #dy_dφ(double, double)
*/
- final double exp_y(final double φ, final double ℯsinφ) {
+ final double expOfNorthing(final double φ, final double ℯsinφ) {
+ /*
+ * Note: tan(π/4 - φ/2) = 1 / tan(π/4 + φ/2)
+ */
return tan(PI/4 + 0.5*φ) * pow((1 - ℯsinφ) / (1 + ℯsinφ),
0.5*excentricity);
}
/**
- * Iteratively solve equation (7-9) from Snyder (<cite>Mercator
projection</cite> chapter).
- * This is <em>almost</em> the converse of the above {@link #exp_y(double,
double)} function.
- * In a Mercator inverse projection, the value of {@code t} argument is
{@code exp(-y)}.
+ * Computes the latitude for a value closely related to the <var>y</var>
value of a Mercator projection.
+ * This formula is also part of other projections, since Mercator can be
considered as a special case of
+ * Lambert Conic Conformal for instance.
+ *
+ * <p>This function is <em>almost</em> the converse of the above {@link
#expOfNorthing(double, double)} function.
+ * In a Mercator inverse projection, the value of the {@code
expOfSouthing} argument is {@code exp(-y)}.</p>
*
* <p>The input should be a positive number, otherwise the result will be
either outside
* the [-π/2 … π/2] range, or will be NaN. Its behavior at some particular
points is:</p>
*
* <ul>
- * <li>If {@code t} is zero, then the result is close to π/2.</li>
- * <li>If {@code t} is 1, then the result is close to zero.</li>
- * <li>If {@code t} is positive infinity, then the result is close to
-π/2.</li>
+ * <li>φ(0) = π/2</li>
+ * <li>φ(1) = 0</li>
+ * <li>φ(∞) = -π/2.</li>
* </ul>
*
- * @param t The <em>reciprocal</em> of the value returned by {@link
#exp_y(double)}.
+ * @param expOfSouthing The <em>reciprocal</em> of the value returned by
{@link #expOfNorthing}.
* @return The latitude in radians.
* @throws ProjectionException if the iteration does not converge.
+ *
+ * @see #expOfNorthing(double, double)
+ * @see #dy_dφ(double, double)
*/
- final double φ(final double t) throws ProjectionException {
+ final double φ(final double expOfSouthing) throws ProjectionException {
final double hℯ = 0.5 * excentricity;
- double φ = (PI/2) - 2*atan(t); // Snyder (7-11)
- for (int i=0; i<MAXIMUM_ITERATIONS; i++) {
+ double φ = (PI/2) - 2*atan(expOfSouthing); // Snyder (7-11)
+ for (int i=0; i<MAXIMUM_ITERATIONS; i++) { // Iteratively
solve equation (7-9) from Snyder
final double ℯsinφ = excentricity * sin(φ);
- final double Δφ = abs(φ - (φ = PI/2 - 2*atan(t * pow((1 -
ℯsinφ)/(1 + ℯsinφ), hℯ))));
+ final double Δφ = abs(φ - (φ = PI/2 - 2*atan(expOfSouthing *
pow((1 - ℯsinφ)/(1 + ℯsinφ), hℯ))));
if (Δφ <= ITERATION_TOLERANCE) {
return φ;
}
}
- if (isNaN(t)) {
+ if (isNaN(expOfSouthing)) {
return NaN;
}
throw new ProjectionException(Errors.Keys.NoConvergence);
}
/**
- * Gets the partial derivative of the {@link #t(double, double)} method
divided by {@code t}.
- * Callers must multiply the return value by {@code t} in order to get the
actual value.
+ * Computes the partial derivative of a Mercator projection at the given
latitude. This formula is also part of
+ * other projections, since Mercator can be considered as a special case
of Lambert Conic Conformal for instance.
+ *
+ * <p>In order to get the derivative of the {@link #expOfNorthing(double,
double)} function, call can multiply
+ * the returned value by by {@code expOfNorthing}.</p>
*
- * @param φ The latitude.
* @param sinφ the sine of latitude.
* @param cosφ The cosine of latitude.
- * @return The {@code t(φ, sinφ)} derivative at the specified latitude.
+ * @return The partial derivative of a Mercator projection at the given
latitude.
+ *
+ * @see #expOfNorthing(double, double)
+ * @see #φ(double)
*/
- final double dt_dφ(final double φ, final double sinφ, final double cosφ) {
- final double t = (1 - sinφ) / cosφ;
- return excentricitySquared*cosφ / (1 - excentricitySquared *
(sinφ*sinφ)) - 0.5*(t + 1/t);
+ final double dy_dφ(final double sinφ, final double cosφ) {
+ return (1 / cosφ) - excentricitySquared * cosφ / (1 -
excentricitySquared * (sinφ*sinφ));
}
}
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java?rev=1669129&r1=1669128&r2=1669129&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
[UTF-8] Wed Mar 25 14:47:22 2015
@@ -68,7 +68,8 @@ import static org.apache.sis.util.Argume
* class from the {@linkplain
org.apache.sis.referencing.operation.projection map projection package}.
* Note that different {@code MathTransformProvider}s may instantiate the
same map projection class.
* For example both <cite>"Mercator (variant A)"</cite> and
<cite>"Mercator (variant B)"</cite> operation methods
- * instantiate the same {@link
org.apache.sis.referencing.operation.Mercator} class, but with different
descriptors.</li>
+ * instantiate the same {@link
org.apache.sis.referencing.operation.projection.Mercator} class,
+ * but with different descriptors.</li>
*
* <li>The map projection constructor fetches all parameters that it needs
from the user-supplied
* {@link ParameterValueGroup}, initializes the projection, then saves the
parameter values that
Modified:
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/UnitaryProjectionTest.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/UnitaryProjectionTest.java?rev=1669129&r1=1669128&r2=1669129&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/UnitaryProjectionTest.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/UnitaryProjectionTest.java
[UTF-8] Wed Mar 25 14:47:22 2015
@@ -16,8 +16,11 @@
*/
package org.apache.sis.referencing.operation.projection;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.test.referencing.TransformTestCase;
+import org.apache.sis.referencing.operation.transform.AbstractMathTransform1D;
import org.apache.sis.test.DependsOnMethod;
-import org.apache.sis.test.TestCase;
+import org.apache.sis.test.TestUtilities;
import org.junit.Test;
import static java.lang.Double.*;
@@ -33,7 +36,7 @@ import static org.junit.Assert.*;
* @version 0.6
* @module
*/
-public final strictfp class UnitaryProjectionTest extends TestCase {
+public final strictfp class UnitaryProjectionTest extends TransformTestCase {
/**
* Tolerance level for comparing floating point numbers.
*/
@@ -44,32 +47,38 @@ public final strictfp class UnitaryProje
* case, truncated to nearest integer. This is not a real infinity because
there is no exact
* representation of π/2 in base 2, so tan(π/2) is not positive infinity.
*/
- static final int LN_INFINITY = 37;
+ private static final int LN_INFINITY = 37;
/**
- * The projection to test.
+ * Computes {@link UnitaryProjection#expOfNorthing(double, double)} for
the given latitude.
+ *
+ * @param projection The projection on which to invoke {@code
expOfNorthing(…)}.
+ * @param φ The latitude in radians.
+ * @return {@code Math.exp} of the Mercator projection of the given
latitude.
*/
- private UnitaryProjection projection;
+ static double expOfNorthing(final UnitaryProjection projection, final
double φ) {
+ return projection.expOfNorthing(φ, projection.excentricity * sin(φ));
+ }
/**
- * Computes {@link UnitaryProjection#exp_y(double, double)} for the given
latitude.
+ * Computes {@link UnitaryProjection#expOfNorthing(double, double)} for
the given latitude.
*
* @param φ The latitude in radians.
* @return {@code Math.exp} of the Mercator projection of the given
latitude.
*/
- private double exp_y(final double φ) {
- return projection.exp_y(φ, projection.excentricity * sin(φ));
+ private double expOfNorthing(final double φ) {
+ return expOfNorthing((UnitaryProjection) transform, φ);
}
/**
* Computes {@link UnitaryProjection#φ(double)}.
*
- * @param t The reciprocal of the value returned by {@link
#exp_y(double)}.
+ * @param expOfSouthing The reciprocal of the value returned by {@link
#expOfNorthing(double)}.
* @return The latitude in radians.
* @throws ProjectionException if the iteration does not converge.
*/
- private double φ(final double t) throws ProjectionException {
- return projection.φ(t);
+ private double φ(final double expOfSouthing) throws ProjectionException {
+ return ((UnitaryProjection) transform).φ(expOfSouthing);
}
/**
@@ -117,7 +126,7 @@ public final strictfp class UnitaryProje
}
/**
- * Tests the {@link UnitaryProjection#exp_y(double, double)} function.
+ * Tests the {@link UnitaryProjection#expOfNorthing(double, double)}
function.
*
* {@preformat text
* Forward: y = -log(t(φ))
@@ -125,37 +134,38 @@ public final strictfp class UnitaryProje
* }
*/
@Test
- public void testExp_y() {
- projection = new NoOp(false); // Spherical case
- doTestExp_y();
- projection = new NoOp(true); // Ellipsoidal case
- doTestExp_y();
+ public void testExpOfNorthing() {
+ transform = new NoOp(false); // Spherical case
+ tolerance = TOLERANCE;
+ doTestExpOfNorthing();
+ transform = new NoOp(true); // Ellipsoidal case
+ doTestExpOfNorthing();
}
/**
- * Implementation of {@link #testExp_y()}.
+ * Implementation of {@link #testExpOfNorthing()}.
* The {@link #projection} field must have been set before this method is
called.
*/
- private void doTestExp_y() {
- assertEquals("Function contract", NaN, exp_y(NaN),
TOLERANCE);
- assertEquals("Function contract", NaN, exp_y(POSITIVE_INFINITY),
TOLERANCE);
- assertEquals("Function contract", NaN, exp_y(NEGATIVE_INFINITY),
TOLERANCE);
- assertEquals("Function contract", 1, exp_y(0),
TOLERANCE);
- assertEquals("Function contract", 0, exp_y(-PI/2),
TOLERANCE);
- assertTrue ("Function contract", exp_y(+PI/2) >
1E+16);
- assertTrue ("Out of bounds", exp_y(-PI/2 - 0.1) < 0);
- assertTrue ("Out of bounds", exp_y(+PI/2 + 0.1) < 0);
- assertEquals("Out of bounds", -1, exp_y(-PI),
TOLERANCE);
- assertTrue ("Out of bounds", exp_y(-PI*3/2) <
-1E+16);
- assertEquals("Function periodicity", 1, exp_y(-2*PI),
TOLERANCE);
- assertEquals("Function periodicity", 0, exp_y(-PI*5/2),
TOLERANCE);
+ private void doTestExpOfNorthing() {
+ assertEquals("Function contract", NaN, expOfNorthing(NaN),
tolerance);
+ assertEquals("Function contract", NaN,
expOfNorthing(POSITIVE_INFINITY), tolerance);
+ assertEquals("Function contract", NaN,
expOfNorthing(NEGATIVE_INFINITY), tolerance);
+ assertEquals("Function contract", 1, expOfNorthing(0),
tolerance);
+ assertEquals("Function contract", 0, expOfNorthing(-PI/2),
tolerance);
+ assertTrue ("Function contract", expOfNorthing(+PI/2)
> 1E+16);
+ assertTrue ("Out of bounds", expOfNorthing(-PI/2 - 0.1)
< 0);
+ assertTrue ("Out of bounds", expOfNorthing(+PI/2 + 0.1)
< 0);
+ assertEquals("Out of bounds", -1, expOfNorthing(-PI),
tolerance);
+ assertTrue ("Out of bounds", expOfNorthing(-PI*3/2)
< -1E+16);
+ assertEquals("Function periodicity", 1, expOfNorthing(-2*PI),
tolerance);
+ assertEquals("Function periodicity", 0, expOfNorthing(-PI*5/2),
tolerance);
/*
* Use in a way close to (but not identical)
* to the way the Mercator projection need it.
*/
- assertEquals("Forward 0°N", 0, log(exp_y(0)),
TOLERANCE);
- assertEquals("Forward 90°S", NEGATIVE_INFINITY, log(exp_y(-PI/2)),
TOLERANCE);
- assertTrue ("Forward 90°N", LN_INFINITY < log(exp_y(+PI/2)));
+ assertEquals("Forward 0°N", 0, log(expOfNorthing(0)),
tolerance);
+ assertEquals("Forward 90°S", NEGATIVE_INFINITY,
log(expOfNorthing(-PI/2)), tolerance);
+ assertTrue ("Forward 90°N", LN_INFINITY <
log(expOfNorthing(+PI/2)));
}
/**
@@ -167,19 +177,21 @@ public final strictfp class UnitaryProje
* @throws ProjectionException Should never happen.
*/
@Test
- @DependsOnMethod("testExp_y")
+ @DependsOnMethod("testExpOfNorthing")
public void test_φ() throws ProjectionException {
- projection = new NoOp(false); // Spherical case
- doTest_φ(TOLERANCE);
- projection = new NoOp(true); // Ellipsoidal case
- doTest_φ(UnitaryProjection.ITERATION_TOLERANCE);
+ transform = new NoOp(false); // Spherical case
+ tolerance = TOLERANCE;
+ doTest_φ();
+ transform = new NoOp(true); // Ellipsoidal case
+ tolerance = UnitaryProjection.ITERATION_TOLERANCE;
+ doTest_φ();
}
/**
* Implementation of {@link #test_φ()}.
* The {@link #projection} field must have been set before this method is
called.
*/
- private void doTest_φ(final double tolerance) throws ProjectionException {
+ private void doTest_φ() throws ProjectionException {
assertEquals("Function contract", NaN, φ(NaN),
tolerance);
assertEquals("Function contract", PI/2, φ(0),
tolerance);
assertEquals("Function contract", PI/2, φ(MIN_VALUE),
tolerance);
@@ -195,7 +207,7 @@ public final strictfp class UnitaryProje
*/
for (int i=-90; i<=270; i+=5) {
final double φ = toRadians(i);
- final double t = 1 / exp_y(φ);
+ final double t = 1 / expOfNorthing(φ);
final double back = toDegrees(φ(t));
if (i <= 90) {
assertTrue("φ(t) in valid range should be positive.", t >= 0);
@@ -205,4 +217,47 @@ public final strictfp class UnitaryProje
assertEquals("Inverse function doesn't match.", i, back,
tolerance);
}
}
+
+ /**
+ * Tests the {@link UnitaryProjection#dy_dφ(double, double)} method.
+ *
+ * @throws TransformException Should never happen.
+ */
+ @Test
+ @DependsOnMethod("testExpOfNorthing")
+ public void test_dN_dφ() throws TransformException {
+ tolerance = 1E-7;
+ doTest_dN_dφ(new NoOp(false)); // Spherical case
+ doTest_dN_dφ(new NoOp(true)); // Ellipsoidal case
+ }
+
+ /**
+ * Implementation of {@link #test_dN_dφ()}.
+ * The {@link #projection} field must have been set before this method is
called.
+ */
+ private void doTest_dN_dφ(final NoOp projection) throws TransformException
{
+ transform = new AbstractMathTransform1D() {
+ @Override public double transform(final double φ) {
+ return expOfNorthing(projection, φ);
+ }
+ @Override public double derivative(final double φ) {
+ final double sinφ = sin(φ);
+ return projection.dy_dφ(sinφ, cos(φ)) *
expOfNorthing(projection, φ);
+ }
+ };
+ verifyInDomain(-89 * (PI/180), 89 * (PI/180)); // Verify from 85°S to
85°N.
+ }
+
+ /**
+ * Convenience method invoking {@link TransformTestCase#verifyInDomain}
for an 1D transform.
+ */
+ private void verifyInDomain(final double min, final double max) throws
TransformException {
+ derivativeDeltas = new double[] {2E-8};
+ isInverseTransformSupported = false;
+ verifyInDomain(
+ new double[] {min}, // Minimal value to test.
+ new double[] {max}, // Maximal value to test.
+ new int[] {100}, // Number of points to test.
+ TestUtilities.createRandomNumberGenerator());
+ }
}