Author: desruisseaux
Date: Tue Mar 24 14:46:32 2015
New Revision: 1668913

URL: http://svn.apache.org/r1668913
Log:
Referencing: initial and incomplete port of Mercator projection.
Missing the initialization in the constructor for now.

Added:
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
   (with props)

Added: 
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=1668913&view=auto
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
 (added)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
 [UTF-8] Tue Mar 24 14:46:32 2015
@@ -0,0 +1,377 @@
+/*
+ * 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.parameter.ParameterDescriptorGroup;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransform2D;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.internal.referencing.provider.Mercator1SP;
+import org.apache.sis.internal.referencing.provider.Mercator2SP;
+import org.apache.sis.internal.referencing.provider.PseudoMercator;
+import org.apache.sis.internal.referencing.provider.MillerCylindrical;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
+import org.apache.sis.parameter.Parameters;
+
+import static java.lang.Math.*;
+import static java.lang.Double.*;
+
+
+/**
+ * <cite>Mercator Cylindrical</cite> projection (EPSG codes 9804, 9805, 1026, 
1024, <span class="deprecated">9841</span>).
+ * See the <a 
href="http://mathworld.wolfram.com/MercatorProjection.html";>Mercator projection 
on MathWorld</a> for an overview.
+ *
+ * <div class="section">Description</div>
+ * The parallels and the meridians are straight lines and cross at right 
angles; this projection thus produces
+ * rectangular charts. The scale is true along the equator (by default) or 
along two parallels equidistant of the
+ * equator (if a scale factor other than 1 is used).
+ *
+ * <p>This projection is used to represent areas close to the equator. It is 
also often used for maritime navigation
+ * because all the straight lines on the chart are <cite>loxodrome</cite> 
lines, i.e. a ship following this line would
+ * keep a constant azimuth on its compass.</p>
+ *
+ * <p>This implementation handles both the 1 and 2 standard parallel cases.
+ * For <cite>Mercator (variant A)</cite> (EPSG code 9804), the line of contact 
is the equator.
+ * For <cite>Mercator (variant B)</cite> (EPSG code 9805) lines of contact are 
symmetrical about the equator.</p>
+ *
+ * <div class="section">Behavior at poles</div>
+ * The projection of 90°N gives {@linkplain Double#POSITIVE_INFINITY positive 
infinity}.
+ * The projection of 90°S gives {@linkplain Double#NEGATIVE_INFINITY negative 
infinity}.
+ * Projection of a latitude outside the [-90-ε … 90+ε]° range produces 
{@linkplain Double#NaN NaN}.
+ *
+ * <div class="section">References</div>
+ * <ul>
+ *   <li>John P. Snyder (Map Projections - A Working Manual,<br>
+ *       U.S. Geological Survey Professional Paper 1395, 1987)</li>
+ *   <li>"Coordinate Conversions and Transformations including Formulas",<br>
+ *       EPSG Guidance Note Number 7, Version 19.</li>
+ * </ul>
+ *
+ * @author  André Gosselin (MPO)
+ * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
+ * @author  Rueben Schulz (UBC)
+ * @author  Simon Reynard (Geomatys)
+ * @author  Rémi Maréchal (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ *
+ * @see TransverseMercator
+ * @see ObliqueMercator
+ */
+public class Mercator extends UnitaryProjection {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 2564172914329253286L;
+
+    /**
+     * Creates a Mercator projection from the given parameters.
+     * The {@code method} argument can be the description of one of the 
following:
+     *
+     * <ul>
+     *   <li><cite>Mercator (variant A)</cite>, also known as <cite>Mercator 
(1SP)</cite>.</li>
+     *   <li><cite>Mercator (variant B)</cite>, also known as <cite>Mercator 
(2SP)</cite>.</li>
+     * </ul>
+     *
+     * @param method Description of the projection parameters.
+     * @param values The parameter values of the projection to create.
+     */
+    protected Mercator(final OperationMethod method, final Parameters values) {
+        super(method, values);
+    }
+
+    /**
+     * Returns the parameter descriptors for this unitary projection. Note 
that the returned descriptor is about
+     * the unitary projection, not the full one. Consequently the default 
implementation returns the descriptor
+     * of <cite>Mercator (variant A)</cite> in all cases except for the 
pseudo-Mercator projection, because the
+     * <cite>Mercator (variant B)</cite> case is implemented as the variant A 
with a different scale factor.
+     *
+     * @return The <cite>Mercator (variant A)</cite> or <cite>Popular 
Visualisation Pseudo Mercator</cite>
+     *         parameter descriptor.
+     */
+    @Override
+    public ParameterDescriptorGroup getParameterDescriptors() {
+        return Mercator1SP.PARAMETERS;
+    }
+
+    // No need to override getParameterValues() because no additional
+    // parameter are significant to a unitary Mercator projection.
+
+
+    /**
+     * Converts the specified (<var>λ</var>,<var>φ</var>) coordinate (units in 
radians)
+     * and stores the result in {@code dstPts} (linear distance on a unit 
sphere). In addition,
+     * opportunistically computes the projection derivative if {@code 
derivate} is {@code true}.
+     *
+     * @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 Matrix transform(final double[] srcPts, final int srcOff,
+                            final double[] dstPts, final int dstOff,
+                            final boolean derivate) throws ProjectionException
+    {
+        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
+         * avoiding rounding error, this also preserve the sign (positive vs 
negative zero).
+         */
+        final double y;
+        if (sinφ == 0) {
+            y = φ;
+        } else {
+            // See the javadoc of the Spherical inner class for a note
+            // about why we perform explicit checks for the pole cases.
+            final double a = abs(φ);
+            if (a < PI/2) {
+                y = -log(t(φ, sinφ));
+            } else {
+                y = copySign(a <= (PI/2 + ANGLE_TOLERANCE) ? POSITIVE_INFINITY 
: NaN, φ);
+            }
+        }
+        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 ℯsinφ = sinφ * excentricity;
+        final double t     = (1 - sinφ) / cosφ;
+        return new Matrix2(1, 0, 0, 0.5*(t + 1/t) - excentricitySquared*cosφ / 
(1 - ℯsinφ*ℯsinφ));
+    }
+
+    /**
+     * Converts a list of coordinate point ordinal values.
+     *
+     * <div class="note"><b>Note:</b>
+     * We override the super-class method only as an optimization in the 
special case where the target coordinates
+     * are written at the same locations than the source coordinates. In such 
case, we can take advantage of the
+     * fact that the λ values are not modified by the unitary Mercator 
projection.</div>
+     *
+     * @throws TransformException if a point can not be transformed.
+     */
+    @Override
+    public void transform(final double[] srcPts, int srcOff,
+                          final double[] dstPts, int dstOff, int numPts)
+            throws TransformException
+    {
+        if (srcPts != dstPts || srcOff != dstOff) {
+            super.transform(srcPts, srcOff, dstPts, dstOff, numPts);
+        } else {
+            dstOff--;
+            while (--numPts >= 0) {
+                double y = dstPts[dstOff += 2]; // Same as srcPts[srcOff + 1].
+                if (y != 0) {
+                    // See the javadoc of the Spherical inner class for a note
+                    // about why we perform explicit checks for the pole cases.
+                    final double a = abs(y);
+                    if (a < PI/2) {
+                        y = -log(t(y, sin(y)));
+                    } else {
+                        y = copySign(a <= (PI/2 + ANGLE_TOLERANCE) ? 
POSITIVE_INFINITY : NaN, y);
+                    }
+                    dstPts[dstOff] = y;
+                }
+            }
+        }
+    }
+
+    /**
+     * Transforms the specified (<var>x</var>,<var>y</var>) coordinates
+     * and stores the result in {@code dstPts} (angles in radians).
+     *
+     * @throws ProjectionException if the point can not be converted.
+     */
+    @Override
+    protected void inverseTransform(final double[] srcPts, final int srcOff,
+                                    final double[] dstPts, final int dstOff)
+            throws ProjectionException
+    {
+        final double y   = srcPts[srcOff+1];    // Must be before writing x.
+        dstPts[dstOff  ] = srcPts[srcOff  ];    // Must be before writing y.
+        dstPts[dstOff+1] = φ(exp(-y));
+    }
+
+
+    /**
+     * 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,
+     * 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.
+     *
+     * <p>The {@code Spherical} formula has the opposite behavior than {@link 
Mercator} regarding which pole returns
+     * the infinite value. In {@code Spherical}, this is the South pole. In 
{@code Mercator} (ellipsoidal case), this
+     * is the North pole. Using explicit checks allow us to enforce the same 
behavior for the two implementations.</p>
+     * </div>
+     *
+     * @author Martin Desruisseaux (MPO, IRD, Geomatys)
+     * @author Rueben Schulz (UBC)
+     * @module
+     */
+    static final class Spherical extends Mercator {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = 2383414176395616561L;
+
+        /**
+         * {@code true} if we are in the "Pseudo Mercator" case.
+         */
+        private final boolean pseudo;
+
+        /**
+         * Constructs a new map projection from the supplied parameters.
+         *
+         * @param method Description of the projection parameters.
+         * @param values The parameter values of the projection to create.
+         * @param pseudo {@code true} if we are in the "Pseudo Mercator" case.
+         */
+        Spherical(final OperationMethod method, final Parameters values, final 
boolean pseudo) {
+            super(method, values);
+            this.pseudo = pseudo;
+            if (!pseudo) {
+                ensureSpherical();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public ParameterDescriptorGroup getParameterDescriptors() {
+            return pseudo ? PseudoMercator.PARAMETERS : 
super.getParameterDescriptors();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Matrix transform(final double[] srcPts, final int srcOff,
+                                final double[] dstPts, final int dstOff,
+                                final boolean derivate) throws 
ProjectionException
+        {
+            final double λ = srcPts[srcOff  ];
+            final double φ = srcPts[srcOff+1];
+            /*
+             * 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
+             * avoiding rounding error, this also preserve the sign (positive 
vs negative zero).
+             */
+            final double y;
+            if (φ == 0) {
+                y = φ;
+            } 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*φ));
+                } else {
+                    y = copySign(a <= (PI/2 + ANGLE_TOLERANCE) ? 
POSITIVE_INFINITY : NaN, φ);
+                }
+            }
+            Matrix derivative = null;
+            if (derivate) {
+                derivative = new Matrix2(1, 0, 0, 1/cos(φ));
+            }
+            /*
+             * Following part is common to all spherical projections: verify, 
store and return.
+             */
+            assert pseudo || (Assertions.checkDerivative(derivative, 
super.transform(srcPts, srcOff, dstPts, dstOff, derivate))
+                    && Assertions.checkTransform(dstPts, dstOff, λ, y)); // 
dstPts = result from ellipsoidal formulas.
+            if (dstPts != null) {
+                dstPts[dstOff  ] = λ;
+                dstPts[dstOff+1] = y;
+            }
+            return derivative;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * <div class="note"><b>Note:</b>
+         * This method must be overridden because the {@link Mercator} class 
overrides the {@link UnitaryProjection}
+         * default implementation.</div>
+         */
+        @Override
+        public void transform(final double[] srcPts, int srcOff,
+                              final double[] dstPts, int dstOff, int numPts)
+                throws TransformException
+        {
+            if (srcPts != dstPts || srcOff != dstOff) {
+                super.transform(srcPts, srcOff, dstPts, dstOff, numPts);
+            } 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));
+                        } else {
+                            y = copySign(a <= (PI/2 + ANGLE_TOLERANCE) ? 
POSITIVE_INFINITY : NaN, y);
+                        }
+                        dstPts[dstOff] = y;
+                    }
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        protected void inverseTransform(final double[] srcPts, final int 
srcOff,
+                                        final double[] dstPts, final int 
dstOff)
+                throws ProjectionException
+        {
+            double x = srcPts[srcOff  ];
+            double y = srcPts[srcOff+1];
+            y = PI/2 - 2 * atan(exp(-y));
+            assert pseudo || checkInverseTransform(srcPts, srcOff, dstPts, 
dstOff, x, y);
+            dstPts[dstOff  ] = x;
+            dstPts[dstOff+1] = y;
+        }
+
+        /**
+         * Computes using ellipsoidal formulas and compare with the
+         * result from spherical formulas. Used in assertions only.
+         */
+        private boolean checkInverseTransform(final double[] srcPts, final int 
srcOff,
+                                              final double[] dstPts, final int 
dstOff,
+                                              final double λ, final double φ)
+                throws ProjectionException
+        {
+            super.inverseTransform(srcPts, srcOff, dstPts, dstOff);
+            return Assertions.checkInverseTransform(dstPts, dstOff, λ, φ);
+        }
+    }
+}

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

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


Reply via email to