Author: desruisseaux
Date: Sun Mar 22 22:10:23 2015
New Revision: 1668491

URL: http://svn.apache.org/r1668491
Log:
Referencing: partial port of UnitaryProjection, which is the base class of code 
performing the map projection on a ellipsoid having a semi-major axis length of 
1.
Note: the class name may be changed. For example maybe "NormalizedProjection" 
would be more understandable. We will submit the question on the mailing list 
after we ported the Mercator projection.

Added:
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java
   (with props)
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/UnitaryProjection.java
   (with props)
    
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/
    
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/UnitaryProjectionTest.java
   (with props)
Modified:
    
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
    
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties

Added: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java?rev=1668491&view=auto
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java
 (added)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java
 [UTF-8] Sun Mar 22 22:10:23 2015
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.projection;
+
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * Thrown by {@link UnitaryProjection} when a map projection failed.
+ *
+ * @author  André Gosselin (MPO)
+ * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+public class ProjectionException extends TransformException {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = 3031350727691500915L;
+
+    /**
+     * Constructs a new exception with no detail message.
+     */
+    public ProjectionException() {
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message.
+     *
+     * @param message The details message, or {@code null} if none.
+     */
+    public ProjectionException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new exception with the specified cause.
+     * The details message is copied from the cause.
+     *
+     * @param cause The cause, or {@code null} if none.
+     */
+    public ProjectionException(final Throwable cause) {
+        super(cause.getLocalizedMessage(), cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message and cause.
+     *
+     * @param message The details message, or {@code null} if none.
+     * @param cause   The cause, or {@code null} if none.
+     */
+    public ProjectionException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message.
+     *
+     * @param code One of the constants suitable for {@link 
Errors#format(short)}.
+     */
+    ProjectionException(final short code) {
+        this(Errors.format(code));
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message.
+     *
+     * @param code One of the constants suitable for {@link 
Errors#format(short)}.
+     * @param value An argument value to be formatted.
+     */
+    ProjectionException(final short code, final Object value) {
+        this(Errors.format(code, value));
+    }
+}

Propchange: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: 
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=1668491&view=auto
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/UnitaryProjection.java
 (added)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/UnitaryProjection.java
 [UTF-8] Sun Mar 22 22:10:23 2015
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.projection;
+
+import java.io.Serializable;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.MathTransform2D;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.operation.matrix.Matrices;
+import org.apache.sis.referencing.operation.transform.AbstractMathTransform2D;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
+import org.apache.sis.internal.referencing.provider.MapProjection;
+
+import static java.lang.Math.*;
+import static java.lang.Double.*;
+import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
+
+
+/**
+ * Base class for conversion services between ellipsoidal and cartographic 
projections.
+ * This conversion works on a normalized spaces, where angles are expressed in 
radians and
+ * computations are performed for a sphere having a semi-major axis of 1. More 
specifically:
+ *
+ * <ul class="verbose">
+ *   <li>On input, the {@link #transform(double[], int, double[], int, 
boolean) transform} method
+ *   expects (<var>longitude</var>, <var>latitude</var>) angles in 
<strong>radians</strong>.
+ *   Longitudes have the <cite>central meridian</cite> removed before the 
transform method is invoked.
+ *   The conversion from degrees to radians and the longitude rotation are 
applied by the
+ *   {@linkplain ContextualParameters#normalization(boolean) normalize} affine 
transform.</li>
+ *
+ *   <li>On output, the {@link #transform(double[],int,double[],int,boolean) 
transform} method returns
+ *   (<var>easting</var>, <var>northing</var>) values on a sphere or ellipse 
having a semi-major axis length of 1.
+ *   The multiplication by the scale factor and the false easting/northing 
offsets are applied by the
+ *   {@linkplain ContextualParameters#normalization(boolean) denormalize} 
affine transform.</li>
+ * </ul>
+ *
+ * {@code UnitaryProjection} does not store the above cited parameters 
(central meridian, scale factor, <i>etc.</i>)
+ * on intend, in order to make clear that those parameters are not used by 
subclasses.
+ * The ability to recognize two {@code UnitaryProjection}s as {@linkplain 
#equals(Object, ComparisonMode) equivalent}
+ * without consideration for the scale factor (among other) allow more 
efficient concatenation in some cases
+ * (typically some combinations of inverse projection followed by a direct 
projection).
+ *
+ * <p>All angles (either fields, method parameters or return values) in this 
class and subclasses are
+ * in radians. This is the opposite of {@link Parameters} where all angles are 
in decimal degrees.</p>
+ *
+ * <div class="section">Serialization</div>
+ * Serialization of this class is appropriate for short-term storage or RMI 
use, but may not be compatible
+ * with future versions. For long term storage, WKT (Well Know Text) or XML 
are more appropriate.
+ *
+ * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
+ * @author  André Gosselin (MPO)
+ * @author  Rueben Schulz (UBC)
+ * @author  Rémi Maréchal (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ *
+ * @see <a href="http://mathworld.wolfram.com/MapProjection.html";>Map 
projections on MathWorld</a>
+ */
+public abstract class UnitaryProjection extends AbstractMathTransform2D 
implements Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    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).
+     *
+     * <p>Some formulas use this tolerance value for testing sines or cosines 
of an angle.
+     * In the sine case, this is justified because sin(θ) ≅ θ when θ is small.
+     * Similar reasoning applies to cosine with cos(θ) ≅ θ + π/2 when θ is 
small.</p>
+     */
+    static final double ANGLE_TOLERANCE = 1E-6;
+
+    /**
+     * Difference allowed in iterative computations. A value of 1E-10 causes 
the {@link #φ(double)} function
+     * to compute the latitude at a precision of 1E-10 radians, which is 
slightly smaller than one millimetre.
+     */
+    static final double ITERATION_TOLERANCE = 1E-10;
+
+    /**
+     * Maximum number of iterations for iterative computations.
+     */
+    static final int MAXIMUM_ITERATIONS = 15;
+
+    /**
+     * Maximum difference allowed when comparing real numbers (other cases).
+     */
+    static final double EPSILON = 1E-7;
+
+    /**
+     * The parameters used for creating this projection. They are used for 
formatting <cite>Well Known Text</cite> (WKT)
+     * and error messages. Subclasses shall not use the values defined in this 
object for computation purpose, except at
+     * construction time.
+     */
+    protected final ContextualParameters parameters;
+
+    /**
+     * Ellipsoid excentricity, equal to <code>sqrt({@linkplain 
#excentricitySquared})</code>.
+     * Value 0 means that the ellipsoid is spherical.
+     */
+    protected final double excentricity;
+
+    /**
+     * The square of excentricity: e² = (a²-b²)/a² where
+     * <var>e</var> is the {@linkplain #excentricity excentricity},
+     * <var>a</var> is the <cite>semi-major</cite> axis length and
+     * <var>b</var> is the <cite>semi-minor</cite> axis length.
+     */
+    protected final double excentricitySquared;
+
+    /**
+     * The inverse of this map projection.
+     */
+    private final MathTransform2D inverse;
+
+    /**
+     * Constructs a new map projection from the supplied parameters.
+     *
+     * @param method Description of the map projection parameters.
+     * @param parameters The parameters of the projection to be created.
+     */
+    protected UnitaryProjection(final OperationMethod method, final Parameters 
parameters) {
+        this.parameters = new ContextualParameters(method);
+        ensureNonNull("parameters", parameters);
+        final double a = parameters.doubleValue(MapProjection.SEMI_MAJOR);
+        final double b = parameters.doubleValue(MapProjection.SEMI_MINOR);
+        excentricitySquared = 1.0 - (b*b) / (a*a);
+        excentricity = sqrt(excentricitySquared);
+        inverse = new Inverse();
+    }
+
+    /**
+     * Converts a single coordinate in {@code srcPts} at the given offset and 
stores the result
+     * in {@code dstPts} at the given offset. In addition, opportunistically 
computes the
+     * transform derivative if requested.
+     *
+     * <div class="section">Normalization</div>
+     * <p>The input ordinates are (<var>λ</var>,<var>φ</var>) (the variable 
names for <var>longitude</var> and
+     * <var>latitude</var> respectively) angles in radians.
+     * Input coordinate shall have the <cite>central meridian</cite> removed 
from the longitude by the caller
+     * before this method is invoked. After this method is invoked, the caller 
will need to multiply the output
+     * coordinate by the global <cite>scale factor</cite>
+     * and apply the (<cite>false easting</cite>, <cite>false northing</cite>) 
offset.
+     * This means that projections that implement this method are performed on 
a sphere or ellipse
+     * having a semi-major axis length of 1.</p>
+     *
+     * <div class="note"><b>Note:</b> in <a 
href="http://trac.osgeo.org/proj/";>PROJ.4</a>, the same standardization,
+     * described above, is handled by {@code pj_fwd.c}.</div>
+     *
+     * <div class="section">Argument checks</div>
+     * The input longitude and latitude are usually (but not always) in the 
range [-π … π] and [-π/2 … π/2] respectively.
+     * However values outside those ranges are accepted on the assumption that 
most implementations use those values
+     * only in trigonometric functions like {@linkplain Math#sin(double) sine} 
and {@linkplain Math#cos(double) cosine}.
+     * If this assumption is not applicable to a particular subclass, then it 
is implementor's responsibility to check
+     * the range.
+     *
+     * @param srcPts   The array containing the source point coordinate, as 
(<var>longitude</var>, <var>latitude</var>)
+     *                 angles in <strong>radians</strong>.
+     * @param srcOff   The offset of the single coordinate to be converted in 
the source array.
+     * @param dstPts   The array into which the converted coordinate is 
returned (may be the same than {@code srcPts}).
+     *                 Ordinates will be expressed in a dimensionless unit, as 
a linear distance on a unit sphere or ellipse.
+     * @param dstOff   The offset of the location of the converted coordinate 
that is stored in the destination array.
+     * @param derivate {@code true} for computing the derivative, or {@code 
false} if not needed.
+     * @return The matrix of the projection derivative at the given source 
position,
+     *         or {@code null} if the {@code derivate} argument is {@code 
false}.
+     * @throws ProjectionException if the coordinate can not be converted.
+     */
+    @Override
+    public abstract Matrix transform(double[] srcPts, int srcOff, double[] 
dstPts, int dstOff, boolean derivate)
+            throws ProjectionException;
+
+    /**
+     * Inverse converts the single coordinate in {@code srcPts} at the given 
offset and stores the result in
+     * {@code ptDst} at the given offset. The output ordinates are 
(<var>longitude</var>, <var>latitude</var>)
+     * angles in radians, usually (but not necessarily) in the range [-π … π] 
and [-π/2 … π/2] respectively.
+     *
+     * <div class="section">Normalization</div>
+     * <p>Input coordinate shall have the (<cite>false easting</cite>, 
<cite>false northing</cite>) removed
+     * by the caller and the result divided by the global <cite>scale 
factor</cite> before this method is invoked.
+     * After this method is invoked, the caller will need to add the 
<cite>central meridian</cite> to the longitude
+     * in the output coordinate. This means that projections that implement 
this method are performed on a sphere
+     * or ellipse having a semi-major axis of 1.</p>
+     *
+     * <div class="note"><b>Note:</b> in <a 
href="http://trac.osgeo.org/proj/";>PROJ.4</a>, the same standardization,
+     * described above, is handled by {@code pj_inv.c}.</div>
+     *
+     * @param srcPts The array containing the source point coordinate, as 
linear distance on a unit sphere or ellipse.
+     * @param srcOff The offset of the point to be converted in the source 
array.
+     * @param dstPts The array into which the converted point coordinate is 
returned (may be the same than {@code srcPts}).
+     *               Ordinates will be (<var>longitude</var>, 
<var>latitude</var>) angles in <strong>radians</strong>.
+     * @param dstOff The offset of the location of the converted point that is 
stored in the destination array.
+     * @throws ProjectionException if the point can't be converted.
+     */
+    protected abstract void inverseTransform(double[] srcPts, int srcOff, 
double[] dstPts, int dstOff)
+            throws ProjectionException;
+
+    /**
+     * Returns the inverse of this map projection.
+     * Subclasses do not need to override this method, as they should override
+     * {@link #inverseTransform(double[], int, double[], int) 
inverseTransform(…)} instead.
+     *
+     * @return The inverse of this map projection.
+     */
+    @Override
+    public MathTransform2D inverse() {
+        return inverse;
+    }
+
+    /**
+     * Inverse of a normalized map projection.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.6
+     * @version 0.6
+     * @module
+     */
+    private final class Inverse extends AbstractMathTransform2D.Inverse {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = -9138242780765956870L;
+
+        /**
+         * Default constructor.
+         */
+        public Inverse() {
+            UnitaryProjection.this.super();
+        }
+
+        /**
+         * Inverse transforms the specified {@code srcPts} and stores the 
result in {@code dstPts}.
+         * If the derivative has been requested, then this method will 
delegate the derivative
+         * calculation to the enclosing class and inverts the resulting matrix.
+         */
+        @Override
+        public Matrix transform(final double[] srcPts, final int srcOff,
+                                      double[] dstPts,       int dstOff,
+                                final boolean derivate) throws 
TransformException
+        {
+            if (!derivate) {
+                inverseTransform(srcPts, srcOff, dstPts, dstOff);
+                return null;
+            } else {
+                if (dstPts == null) {
+                    dstPts = new double[2];
+                    dstOff = 0;
+                }
+                inverseTransform(srcPts, srcOff, dstPts, dstOff);
+                return 
Matrices.inverse(UnitaryProjection.this.transform(dstPts, dstOff, null, 0, 
true));
+            }
+        }
+    }
+
+
+
+
+    
//////////////////////////////////////////////////////////////////////////////////////////
+    ////////                                                                   
       ////////
+    ////////                           FORMULAS FROM SNYDER                    
       ////////
+    ////////                                                                   
       ////////
+    
//////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Computes functions (9-13) and (15-9) from Snyder.
+     * This function is part of the following chapters:
+     *
+     * <ul>
+     *   <li>(9-13) in the <cite>Oblique Mercator projection</cite> 
chapter.</li>
+     *   <li>(15-9) in the <cite>Lambert Conformal Conic projection</cite> 
chapter.</li>
+     *   <li>Negative of part of (7-7) in the <cite>Mercator projection</cite> 
chapter.
+     *       In the case of Mercator projection, this is {@code exp(-y)} where 
<var>y</var>
+     *       is the northing on the unit ellipse.</li>
+     * </ul>
+     *
+     * This function is the converse of {@link #φ(double)}.
+     *
+     * <div class="section">Properties</div>
+     * 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>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>
+     * </ul>
+     *
+     * <div class="section">Relationship with other formulas</div>
+     * Function (3-1) from Snyder gives the <cite>conformal latitude</cite> χ 
as (Adam, 1921, p.18, 84):
+     *
+     * <blockquote>
+     * χ = 2∙arctan( tan(π/4 + φ/2) ∙ [(1 - ℯ∙sinφ)/(1 + ℯ∙sinφ)]^(ℯ/2) )  -  
π/2
+     * </blockquote>
+     *
+     * This method can compute the part inside the {@code arctan(…)} 
expression as below
+     * (note the negative sign in front of φ):
+     *
+     * <blockquote>
+     * χ = 2∙arctan( t(-φ, sinφ) )  -  π/2
+     * </blockquote>
+     *
+     * @param  φ    The latitude in radians.
+     * @param  sinφ The sine of the φ argument (often already computed by the 
caller).
+     * @return Function 9-13 or 15-9, or the negative of function 7-7 from 
Snyder.
+     */
+    final double t(final double φ, double sinφ) {
+        sinφ *= excentricity;
+        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 the converse of the above {@link #t(double, double)} function.
+     * 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:
+     *
+     * <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>
+     * </ul>
+     *
+     * @param  t The value returned by {@link #t(double, double)}.
+     * @return The latitude in radians.
+     * @throws ProjectionException if the iteration does not converge.
+     */
+    final double φ(final double t) throws ProjectionException {
+        final double hℯ = 0.5 * excentricity;
+        double φ = (PI/2) - 2*atan(t);
+        for (int i=0; i<MAXIMUM_ITERATIONS; i++) {
+            final double ℯsinφ = excentricity * sin(φ);
+            final double Δφ = abs(φ - (φ = PI/2 - 2*atan(t * pow((1 - 
ℯsinφ)/(1 + ℯsinφ), hℯ))));
+            if (Δφ <= ITERATION_TOLERANCE) {
+                return φ;
+            }
+        }
+        if (isNaN(t)) {
+            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.
+     *
+     * @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.
+     */
+    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);
+    }
+}

Propchange: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/UnitaryProjection.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/UnitaryProjection.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: 
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=1668491&view=auto
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/UnitaryProjectionTest.java
 (added)
+++ 
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/UnitaryProjectionTest.java
 [UTF-8] Sun Mar 22 22:10:23 2015
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.projection;
+
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static java.lang.Double.*;
+import static java.lang.StrictMath.*;
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link UnitaryProjection} class. This class uses {@link Mercator}
+ * for testing purpose, because it is the simplest non-trivial projection.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+public final strictfp class UnitaryProjectionTest extends TestCase {
+    /**
+     * Tolerance level for comparing floating point numbers.
+     */
+    private static final double TOLERANCE = 1E-12;
+
+    /**
+     * Natural logarithm of the pseudo-infinity as returned by Mercator 
formulas in the spherical
+     * 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;
+
+    /**
+     * The projection to test.
+     */
+    private UnitaryProjection projection;
+
+    /**
+     * Computes {@link UnitaryProjection#t(double, double)} for the given 
latitude.
+     *
+     * @param  φ The latitude in radians.
+     * @return Function 9-13 or 15-9, or the negative of function 7-7 from 
Snyder.
+     */
+    private double t(final double φ) {
+        return projection.t(φ, sin(φ));
+    }
+
+    /**
+     * Computes {@link UnitaryProjection#φ(double)}.
+     *
+     * @param  t The value returned by {@link #t(double)}.
+     * @return The latitude in radians.
+     * @throws ProjectionException if the iteration does not converge.
+     */
+    private double φ(final double t) throws ProjectionException {
+        return projection.φ(t);
+    }
+
+    /**
+     * Tests the value documented in the javadoc. Those value may be freely 
changed;
+     * those tests exist only to increase the chances that the documented 
values are right.
+     */
+    @Test
+    public void testDocumentation() {
+        double minutes = toDegrees(UnitaryProjection.ANGLE_TOLERANCE) * 60;
+        assertEquals("Documentation said 0.2″ precision.", 0.2, minutes*60, 
0.1);
+        assertEquals("Documentation said 6 km precision.", 6, minutes*1852, 
0.5);
+
+        minutes = toDegrees(UnitaryProjection.ITERATION_TOLERANCE) * 60;
+        assertEquals("Documentation said 1 mm precision.", 0.001, 
minutes*1852, 0.0005);
+    }
+
+    /**
+     * Tests a few formulas used by the Mercator projection in the spherical 
case.
+     * This is a little bit more a Java test than an Apache SIS test (or to be 
more
+     * accurate, a test of our understanding of the {@code java.lang.Math} 
library).
+     *
+     * {@preformat text
+     *   Forward:  y = log(tan(π/4 + φ/2))
+     *   Inverse:  φ = π/2 - 2*atan(exp(-y))
+     * }
+     */
+    @Test
+    public void testMath() {
+        assertEquals("Forward 0°N",      0, log(tan(PI/4)),                   
TOLERANCE);
+        assertEquals("Inverse 0 m",      0, PI/2 - 2*atan(exp(0)),            
TOLERANCE);
+        assertEquals("Forward 90°S",     NEGATIVE_INFINITY, log(tan(0)),      
TOLERANCE);
+        assertEquals("Forward (90+ε)°S", NaN,  log(tan(-nextUp(0))),          
TOLERANCE);
+        assertEquals("Inverse -∞",       PI/2, atan(exp(-NEGATIVE_INFINITY)), 
TOLERANCE);
+        assertEquals("Inverse -∞ appr.", PI/2, atan(exp(LN_INFINITY + 1)),    
TOLERANCE);
+        /*
+         * tan(PI/2) do not produces positive infinity as we would expect, 
because there is no
+         * exact representation of PI in base 2.  Experiments show that we get 
some high value
+         * instead (1.633E+16 on my machine, having a logarithm of 37.332).
+         */
+        assertTrue  ("Forward 90°N",     1E+16 < tan(PI/2));
+        assertTrue  ("Forward 90°N",     LN_INFINITY < log(tan(PI/2)));
+        assertEquals("Forward (90+ε)°N", NaN, log(tan(nextUp(PI/2))),      
TOLERANCE);
+        assertEquals("Inverse +∞",       0, atan(exp(NEGATIVE_INFINITY)),  
TOLERANCE);
+        assertEquals("Inverse +∞ appr.", 0, atan(exp(-(LN_INFINITY + 1))), 
TOLERANCE);
+    }
+
+    /**
+     * Tests the {@link UnitaryProjection#t(double, double)} function.
+     * This is also a test of the forward Mercator projection in the 
ellipsoidal case.
+     *
+     * {@preformat text
+     *   Forward:  y = -log(tsfn(φ))
+     *   Inverse:  φ = cphi2(exp(-y))
+     * }
+     */
+    @Test
+    @org.junit.Ignore("Pending port of the Mercator projection.")
+    public void test_t() {
+        projection = null; // TODO: set spherical case here.
+        doTest_t();
+        projection = null; // TODO: set ellipsoidal case here.
+        doTest_t();
+    }
+
+    /**
+     * Implementation of {@link #test_t()}.
+     * The {@link #projection} field must have been set before this method is 
called.
+     */
+    private void doTest_t() {
+        assertEquals("Function contract",  NaN, t(NaN),               
TOLERANCE);
+        assertEquals("Function contract",  NaN, t(POSITIVE_INFINITY), 
TOLERANCE);
+        assertEquals("Function contract",  NaN, t(NEGATIVE_INFINITY), 
TOLERANCE);
+        assertEquals("Function contract",    1, t(0),                 
TOLERANCE);
+        assertEquals("Function contract",    0, t(+PI/2),             
TOLERANCE);
+        assertTrue  ("Function contract",       t(-PI/2)              > 1E+16);
+        assertTrue  ("Out of bounds",           t(+PI/2 + 0.1)        < 0);
+        assertTrue  ("Out of bounds",           t(-PI/2 - 0.1)        < 0);
+        assertEquals("Out of bounds",       -1, t(PI),                
TOLERANCE);
+        assertTrue  ("Out of bounds",           t(PI*3/2)             < 
-1E+16);
+        assertEquals("Function periodicity", 1, t(2*PI),              
TOLERANCE);
+        assertEquals("Function periodicity", 0, t(PI*5/2),            
TOLERANCE);
+        assertEquals("Forward 0°N",  0,                 -log(t(0)),     
TOLERANCE);
+        assertEquals("Forward 90°N", POSITIVE_INFINITY, -log(t(+PI/2)), 
TOLERANCE);
+        assertTrue  ("Forward 90°S", -LN_INFINITY >     -log(t(-PI/2)));
+    }
+
+    /**
+     * Tests the {@link UnitaryProjection#φ(double)} function. We expect it to 
be the converse of the
+     * {@link UnitaryProjection#t(double, double)} function. In theory only 
the range [-90° … +90°]
+     * needs to be tested. However the function still consistent in the range 
[-90° … +270°]
+     * so we test that range for tracking this fact.
+     *
+     * @throws ProjectionException Should never happen.
+     */
+    @Test
+    @DependsOnMethod("test_t")
+    @org.junit.Ignore("Pending port of the Mercator projection.")
+    public void test_φ() throws ProjectionException {
+        projection = null; // TODO: set spherical case here.
+        doTest_φ(TOLERANCE);
+        projection = null; // TODO: set ellipsoidal case here.
+        doTest_φ(UnitaryProjection.ITERATION_TOLERANCE);
+    }
+
+    /**
+     * 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 {
+        assertEquals("Function contract",  NaN,  φ(NaN),               
tolerance);
+        assertEquals("Function contract",  PI/2, φ(0),                 
tolerance);
+        assertEquals("Function contract",  PI/2, φ(MIN_VALUE),         
tolerance);
+        assertEquals("Function contract",  0,    φ(1),                 
tolerance);
+        assertEquals("Function contract", -PI/2, φ(MAX_VALUE),         
tolerance);
+        assertEquals("Function contract", -PI/2, φ(POSITIVE_INFINITY), 
tolerance);
+        assertEquals("Out of bounds",   PI+PI/2, φ(NEGATIVE_INFINITY), 
tolerance);
+        assertEquals("Out of bounds",   PI+PI/2, φ(-MAX_VALUE),        
tolerance);
+        assertEquals("Out of bounds",   PI,      φ(-1),                
tolerance);
+        assertEquals("Almost f. contract", PI/2, φ(-MIN_VALUE),        
tolerance);
+        /*
+         * Using t(φ) as a reference.
+         */
+        for (int i=-90; i<=270; i+=5) {
+            final double φ   = toRadians(i);
+            final double t    = t(φ);
+            final double back = toDegrees(φ(t));
+            if (i <= 90) {
+                assertTrue("tsfn in valid range should be positive.", t >= 0);
+            } else {
+                assertTrue("tsfn in invalid range should be negative.", t < 0);
+            }
+            assertEquals("Inverse function doesn't match.", i, back, 
tolerance);
+        }
+    }
+}

Propchange: 
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/UnitaryProjectionTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/UnitaryProjectionTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: 
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1668491&r1=1668490&r2=1668491&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
 [UTF-8] Sun Mar 22 22:10:23 2015
@@ -32,8 +32,21 @@ import org.junit.BeforeClass;
 @Suite.SuiteClasses({
     org.apache.sis.internal.referencing.FormulasTest.class,
     org.apache.sis.internal.referencing.j2d.ShapeUtilitiesTest.class,
+    org.apache.sis.internal.referencing.AxisDirectionsTest.class,
+    org.apache.sis.internal.referencing.VerticalDatumTypesTest.class,
+    org.apache.sis.internal.referencing.PositionalAccuracyConstantTest.class,
+    org.apache.sis.internal.referencing.ReferencingUtilitiesTest.class,
+    org.apache.sis.internal.jaxb.referencing.CodeTest.class,
+    org.apache.sis.internal.jaxb.referencing.SecondDefiningParameterTest.class,
 
-    // Test matrix early because they may be used in about every SIS corners.
+    // Identification of objects, needed by large parts of sis-referencing.
+    org.apache.sis.referencing.NamedIdentifierTest.class,
+    org.apache.sis.referencing.IdentifiedObjectsTest.class,
+    org.apache.sis.referencing.AbstractIdentifiedObjectTest.class,
+    org.apache.sis.referencing.AbstractReferenceSystemTest.class,
+    org.apache.sis.referencing.BuilderTest.class,
+
+    // Test matrices early because they may be used in about every SIS corners.
     org.apache.sis.referencing.operation.matrix.GeneralMatrixTest.class,
     org.apache.sis.referencing.operation.matrix.SolverTest.class,
     org.apache.sis.referencing.operation.matrix.Matrix1Test.class,
@@ -43,30 +56,8 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.matrix.NonSquareMatrixTest.class, // 
Expected to be last MatrixTestCase - see javadoc.
     org.apache.sis.referencing.operation.matrix.MatricesTest.class,
     org.apache.sis.referencing.operation.matrix.AffineTransforms2DTest.class,
-    org.apache.sis.referencing.operation.transform.CoordinateDomainTest.class,
-    org.apache.sis.referencing.operation.transform.IterationStrategyTest.class,
-    
org.apache.sis.referencing.operation.transform.AbstractMathTransformTest.class,
-    
org.apache.sis.referencing.operation.transform.ProjectiveTransformTest.class,
-    org.apache.sis.referencing.operation.transform.LinearTransformTest.class,
-    
org.apache.sis.referencing.operation.transform.ExponentialTransform1DTest.class,
-    
org.apache.sis.referencing.operation.transform.LogarithmicTransform1DTest.class,
-    org.apache.sis.referencing.operation.transform.CopyTransformTest.class,
-    
org.apache.sis.referencing.operation.transform.PassThroughTransformTest.class,
-    
org.apache.sis.referencing.operation.transform.ConcatenatedTransformTest.class,
-    org.apache.sis.referencing.operation.transform.TransferFunctionTest.class,
-    org.apache.sis.referencing.operation.transform.MathTransformsTest.class,
 
-    org.apache.sis.internal.referencing.VerticalDatumTypesTest.class,
-    org.apache.sis.internal.referencing.AxisDirectionsTest.class,
-    org.apache.sis.internal.referencing.PositionalAccuracyConstantTest.class,
-    org.apache.sis.internal.referencing.ReferencingUtilitiesTest.class,
-    org.apache.sis.internal.jaxb.referencing.CodeTest.class,
-    org.apache.sis.internal.jaxb.referencing.SecondDefiningParameterTest.class,
-    org.apache.sis.referencing.IdentifiedObjectsTest.class,
-    org.apache.sis.referencing.NamedIdentifierTest.class,
-    org.apache.sis.referencing.AbstractIdentifiedObjectTest.class,
-    org.apache.sis.referencing.AbstractReferenceSystemTest.class,
-    org.apache.sis.referencing.BuilderTest.class,
+    // Parameter are needed for math transforms and map projections.
     org.apache.sis.parameter.DefaultParameterDescriptorTest.class,
     org.apache.sis.parameter.DefaultParameterDescriptorGroupTest.class,
     org.apache.sis.parameter.DefaultParameterValueTest.class,
@@ -80,8 +71,26 @@ import org.junit.BeforeClass;
     org.apache.sis.parameter.TensorValuesTest.class,
     org.apache.sis.parameter.MapProjectionParametersTest.class,
 
+    // Test transforms other than map projections.
+    org.apache.sis.referencing.operation.transform.CoordinateDomainTest.class,
+    org.apache.sis.referencing.operation.transform.IterationStrategyTest.class,
+    
org.apache.sis.referencing.operation.transform.AbstractMathTransformTest.class,
+    
org.apache.sis.referencing.operation.transform.ProjectiveTransformTest.class,
+    org.apache.sis.referencing.operation.transform.LinearTransformTest.class,
+    
org.apache.sis.referencing.operation.transform.ExponentialTransform1DTest.class,
+    
org.apache.sis.referencing.operation.transform.LogarithmicTransform1DTest.class,
+    org.apache.sis.referencing.operation.transform.CopyTransformTest.class,
+    
org.apache.sis.referencing.operation.transform.PassThroughTransformTest.class,
+    
org.apache.sis.referencing.operation.transform.ConcatenatedTransformTest.class,
+    org.apache.sis.referencing.operation.transform.TransferFunctionTest.class,
+    org.apache.sis.referencing.operation.transform.MathTransformsTest.class,
+
+    // Test map projections.
     org.apache.sis.referencing.operation.DefaultFormulaTest.class,
     org.apache.sis.referencing.operation.DefaultOperationMethodTest.class,
+    
org.apache.sis.referencing.operation.projection.UnitaryProjectionTest.class,
+
+    // Registration of map projections and other math transforms.
     org.apache.sis.internal.referencing.provider.AffineTest.class,
     org.apache.sis.internal.referencing.provider.LongitudeRotationTest.class,
     org.apache.sis.internal.referencing.provider.MapProjectionTest.class,
@@ -90,6 +99,7 @@ import org.junit.BeforeClass;
     
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactoryTest.class,
     org.apache.sis.internal.referencing.OperationMethodsTest.class,
 
+    // Coordinate Reference System components.
     org.apache.sis.referencing.datum.BursaWolfParametersTest.class,
     org.apache.sis.referencing.datum.TimeDependentBWPTest.class,
     org.apache.sis.referencing.datum.DefaultEllipsoidTest.class,

Modified: 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1668491&r1=1668490&r2=1668491&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
 [UTF-8] Sun Mar 22 22:10:23 2015
@@ -580,6 +580,11 @@ public final class Errors extends Indexe
         public static final short NegativeArrayLength_1 = 68;
 
         /**
+         * No convergence.
+         */
+        public static final short NoConvergence = 181;
+
+        /**
          * No convergence for points {0} and {1}.
          */
         public static final short NoConvergenceForPoints_2 = 69;

Modified: 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1668491&r1=1668490&r2=1668491&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
 [ISO-8859-1] (original)
+++ 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
 [ISO-8859-1] Sun Mar 22 22:10:23 2015
@@ -126,6 +126,7 @@ MissingSpatioTemporalDimension_1  = No s
 MutuallyExclusiveOptions_2        = Options \u201c{0}\u201d and 
\u201c{1}\u201d are mutually exclusive.
 NegativeArgument_2                = Argument \u2018{0}\u2019 shall not be 
negative. The given value was {1}.
 NegativeArrayLength_1             = Can not create a \u201c{0}\u201d array of 
negative length.
+NoConvergence                     = No convergence.
 NoConvergenceForPoints_2          = No convergence for points {0} and {1}.
 NodeChildOfItself_1               = Node \u201c{0}\u201d can not be a child of 
itself.
 NodeHasAnotherParent_1            = Node \u201c{0}\u201d already has another 
parent.

Modified: 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1668491&r1=1668490&r2=1668491&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
 [ISO-8859-1] (original)
+++ 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
 [ISO-8859-1] Sun Mar 22 22:10:23 2015
@@ -116,6 +116,7 @@ MissingSpatioTemporalDimension_1  = Aucu
 MutuallyExclusiveOptions_2        = Les options \u00ab\u202f{0}\u202f\u00bb et 
\u00ab\u202f{1}\u202f\u00bb sont mutuellement exclusives.
 NegativeArgument_2                = L\u2019argument \u2018{0}\u2019 ne doit 
pas \u00eatre n\u00e9gatif. La valeur donn\u00e9e \u00e9tait {1}.
 NegativeArrayLength_1             = Ne peut pas cr\u00e9er un tableau 
\u00ab\u202f{0}\u202f\u00bb de longueur n\u00e9gative.
+NoConvergence                     = Le calcul ne converge pas.
 NoConvergenceForPoints_2          = Le calcul ne converge pas pour les points 
{0} et {1}.
 NodeChildOfItself_1               = Le n\u0153ud \u00ab\u202f{0}\u202f\u00bb 
ne peut pas \u00eatre un enfant de lui-m\u00eame.
 NodeHasAnotherParent_1            = Le n\u0153ud \u00ab\u202f{0}\u202f\u00bb a 
d\u00e9j\u00e0 un autre parent.


Reply via email to