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());
+    }
 }


Reply via email to