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.