You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2016/07/15 17:01:29 UTC

svn commit: r1752855 - in /sis/branches/JDK8/core/sis-referencing/src: main/java/org/apache/sis/internal/referencing/provider/ main/java/org/apache/sis/referencing/operation/projection/ main/resources/META-INF/services/ test/java/org/apache/sis/interna...

Author: desruisseaux
Date: Fri Jul 15 17:01:29 2016
New Revision: 1752855

URL: http://svn.apache.org/viewvc?rev=1752855&view=rev
Log:
Initial commit of CylindricalEqualArea projection.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CylindricalEqualArea.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalEqualAreaTest.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CylindricalEqualArea.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CylindricalEqualArea.java?rev=1752855&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CylindricalEqualArea.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CylindricalEqualArea.java [UTF-8] Fri Jul 15 17:01:29 2016
@@ -0,0 +1,120 @@
+/*
+ * 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.internal.referencing.provider;
+
+import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.referencing.operation.CylindricalProjection;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.operation.projection.NormalizedProjection;
+
+
+/**
+ * The provider for <cite>"Cylindrical Equal Area"</cite> projection.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ *
+ * @see <a href="http://www.remotesensing.org/geotiff/proj_list/cylindrical_equal_area.html">Cylindrical Equal Area on RemoteSensing.org</a>
+ */
+@XmlTransient
+public final class CylindricalEqualArea extends MapProjection {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -672278344635217838L;
+
+    /**
+     * The operation parameter descriptor for the <cite>Latitude of 1st standard parallel</cite> (\u03c6\u2081) parameter value.
+     * Valid values range is (-90 \u2026 90)� and default value is 0�.
+     */
+    public static final ParameterDescriptor<Double> STANDARD_PARALLEL;
+
+    /**
+     * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (\u03bb\u2080) parameter value.
+     * Valid values range is [-180 \u2026 180]� and default value is 0�.
+     */
+    public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN;
+
+    /**
+     * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
+     * Valid values range is unrestricted and default value is 0 metre.
+     */
+    public static final ParameterDescriptor<Double> FALSE_EASTING;
+
+    /**
+     * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
+     * Valid values range is unrestricted and default value is 0 metre.
+     */
+    public static final ParameterDescriptor<Double> FALSE_NORTHING;
+
+    /**
+     * The group of all parameters expected by this coordinate operation.
+     */
+    static final ParameterDescriptorGroup PARAMETERS;
+    static {
+        final ParameterBuilder builder = builder();
+        STANDARD_PARALLEL   = createLatitude (exceptEPSG(Equirectangular.STANDARD_PARALLEL, builder), false);
+        LONGITUDE_OF_ORIGIN = createLongitude(exceptEPSG(Mercator1SP.LONGITUDE_OF_ORIGIN,   builder));
+        FALSE_EASTING       = createShift    (exceptEPSG(Equirectangular.FALSE_EASTING,     builder));
+        FALSE_NORTHING      = createShift    (exceptEPSG(Equirectangular.FALSE_NORTHING,    builder));
+        PARAMETERS = builder
+                .addName(Citations.OGC,     "Cylindrical_Equal_Area")
+                .addName(Citations.ESRI,    "Cylindrical_Equal_Area")
+                .addName(Citations.GEOTIFF, "CT_CylindricalEqualArea")
+                .addName(Citations.PROJ4,   "cea")
+                .addIdentifier(Citations.GEOTIFF, "28")
+                .createGroupForMapProjection(
+                        STANDARD_PARALLEL,
+                        LONGITUDE_OF_ORIGIN,
+                        Mercator2SP.SCALE_FACTOR,           // Not formally a CylindricalEqualArea parameter.
+                        FALSE_EASTING,
+                        FALSE_NORTHING);
+    }
+
+    /**
+     * Constructs a new provider.
+     */
+    public CylindricalEqualArea() {
+        super(PARAMETERS);
+    }
+
+    /**
+     * Returns the operation type for this map projection.
+     *
+     * @return {@code CylindricalProjection.class}
+     */
+    @Override
+    public final Class<CylindricalProjection> getOperationType() {
+        return CylindricalProjection.class;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return The map projection created from the given parameter values.
+     */
+    @Override
+    protected NormalizedProjection createProjection(final Parameters parameters) {
+        return new org.apache.sis.referencing.operation.projection.CylindricalEqualArea(this, parameters);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CylindricalEqualArea.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CylindricalEqualArea.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/CylindricalEqualArea.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java?rev=1752855&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java [UTF-8] Fri Jul 15 17:01:29 2016
@@ -0,0 +1,344 @@
+/*
+ * 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.util.EnumMap;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+import org.apache.sis.internal.referencing.provider.Mercator1SP;
+import org.apache.sis.internal.util.DoubleDouble;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
+import org.apache.sis.util.Workaround;
+
+import static java.lang.Math.*;
+import static org.apache.sis.internal.util.DoubleDouble.verbatim;
+import static org.apache.sis.internal.referencing.provider.CylindricalEqualArea.*;
+
+
+/**
+ * <cite>Cylindrical Equal Area</cite> projection.
+ * This is the simplest equal-area projection.
+ * This projection has various names depending on its standard parallel:
+ *
+ * <table class="sis">
+ *   <caption>Non-exhaustive list of variants</caption>
+ *   <tr><th>Name</th>                              <th>Standard parallel</th></tr>
+ *   <tr><td>Lambert cylindrical equal-area</td>    <td>0�</td></tr>
+ *   <tr><td>Behrmann cylindrical equal-area</td>   <td>30�</td></tr>
+ *   <tr><td>Gall orthographic</td>                 <td>45�</td></tr>
+ *   <tr><td>Balthasart</td>                        <td>50�</td></tr>
+ * </table>
+ *
+ * <div class="section">Description</div>
+ * The parallels and the meridians are straight lines and cross at right angles.
+ * The scale is true along standard parallels, but distortion increase greatly at other locations.
+ * Distortions are so great that there is little use of this projection for world mapping purposes.
+ * However this projection may be useful for computing areas.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+public class CylindricalEqualArea extends EqualAreaProjection {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 8840395516658904421L;
+
+    /**
+     * Value of {@link #qm(double)} function (part of Snyder equation (3-12)) at pole (sin\u03c6 = 1).
+     *
+     * @see #computeCoefficients()
+     */
+    private transient double qmPolar;
+
+    /**
+     * Creates a Cylindrical Equal Area projection from the given parameters.
+     *
+     * @param method     Description of the projection parameters.
+     * @param parameters The parameter values of the projection to create.
+     */
+    public CylindricalEqualArea(final OperationMethod method, final Parameters parameters) {
+        this(initializer(method, parameters));
+    }
+
+    /**
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in constructors").
+     */
+    @SuppressWarnings("fallthrough")
+    @Workaround(library="JDK", version="1.7")
+    private static Initializer initializer(final OperationMethod method, final Parameters parameters) {
+        final EnumMap<ParameterRole, ParameterDescriptor<Double>> roles = new EnumMap<>(ParameterRole.class);
+        /*
+         * "Longitude of origin" and "scale factor" are intentionally omitted from this map because they will
+         * be handled in a special way. See comments in Mercator.initializer(\u2026) method for more details.
+         */
+        roles.put(ParameterRole.FALSE_EASTING,  FALSE_EASTING);
+        roles.put(ParameterRole.FALSE_NORTHING, FALSE_NORTHING);
+        return new Initializer(method, parameters, roles, (byte) 0);
+    }
+
+    /**
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in constructors").
+     */
+    @Workaround(library="JDK", version="1.7")
+    private CylindricalEqualArea(final Initializer initializer) {
+        super(initializer);
+        final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
+        /*
+         * The longitude of origin is normally subtracted in the 'normalize' matrix. But in the particular of case
+         * of this map projection we can apply -\u03bb\u2080 on any matrix.  So we apply that operation on 'denormalize' for
+         * consistency with the Mercator projection and for increasing the chances to have cancellation when
+         * multiplying matrices together.
+         */
+        final double \u03bb0 = initializer.getAndStore(LONGITUDE_OF_ORIGIN);
+        if (\u03bb0 != 0) {
+            final DoubleDouble offset = DoubleDouble.createDegreesToRadians();
+            offset.multiply(-\u03bb0);
+            denormalize.convertBefore(0, null, offset);
+        }
+        /*
+         * Compute the scale factor as k\u2080 = cos\u03c6\u2081/\u221a(1 - \u212f�\u22c5sin�\u03c6\u2081), multiplied by user-specified scale factor if any.
+         * Explicit scale factor is not formally a Cylindrical Equal Area parameter (it is rather computed from \u03c6\u2081),
+         * but we nevertheless support it.
+         */
+        final double \u03c61 = toRadians(initializer.getAndStore(STANDARD_PARALLEL));
+        final DoubleDouble k0 = verbatim(initializer.scaleAt\u03c6(sin(\u03c61), cos(\u03c61)));
+        k0.multiply(initializer.getAndStore(Mercator1SP.SCALE_FACTOR));
+        /*
+         * In most Apache SIS map projection implementations, the scale factor is handled by the super-class by
+         * specifying a ParameterRole.SCALE_FACTOR. However in the case of this CylindricalEqualArea we rather
+         * handle the scale factor ourselves, because we do not perform the same multiplication on both axes:
+         *
+         *      x shall be multiplied by k\u2080
+         *      y shall be divided by k\u2080
+         *
+         * Furthermore we also multiply y by (1-\u212f�)/2 for avoiding the need to recompute this constant during
+         * the projection of every point.
+         */
+        final DoubleDouble ik = new DoubleDouble(1, 0);
+        ik.subtract(initializer.eccentricitySquared);
+        ik.multiply(0.5, 0);                 // This line need to be cancelled when using spherical formulas.
+        ik.divide(k0);
+        denormalize.convertAfter(0, k0, null);
+        denormalize.convertAfter(1, ik, null);
+        computeCoefficients();
+    }
+
+    /**
+     * Invoked at construction time or on deserialization for computing the transient fields.
+     */
+    @Override
+    final void computeCoefficients() {
+        super.computeCoefficients();
+        qmPolar = qm(1);
+    }
+
+    /**
+     * Creates a new projection initialized to the same parameters than the given one.
+     */
+    CylindricalEqualArea(final CylindricalEqualArea other) {
+        super(other);
+        qmPolar = other.qmPolar;
+    }
+
+    /**
+     * Returns the sequence of <cite>normalization</cite> \u2192 {@code this} \u2192 <cite>denormalization</cite> transforms
+     * as a whole. The transform returned by this method expects (<var>longitude</var>, <var>latitude</var>)
+     * coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
+     *
+     * <p>The non-linear part of the returned transform will be {@code this} transform, except if the ellipsoid
+     * is spherical. In the later case, {@code this} transform will be replaced by a simplified implementation.</p>
+     *
+     * @param  factory The factory to use for creating the transform.
+     * @return The map projection from (\u03bb,\u03c6) to (<var>x</var>,<var>y</var>) coordinates.
+     * @throws FactoryException if an error occurred while creating a transform.
+     */
+    @Override
+    public MathTransform createMapProjection(final MathTransformFactory factory) throws FactoryException {
+        CylindricalEqualArea kernel = this;
+        if (eccentricity == 0) {
+            kernel = new Spherical(this);
+        }
+        return context.completeTransform(factory, kernel);
+    }
+
+    /**
+     * Converts the specified (\u03bb,\u03c6) 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 \u03c6    = srcPts[srcOff+1];
+        final double sin\u03c6 = sin(\u03c6);
+        if (dstPts != null) {
+            dstPts[dstOff  ] = srcPts[srcOff];  // Multiplication by k\u2080 will be applied by the denormalization matrix.
+            dstPts[dstOff+1] = qm(sin\u03c6);        // Multiplication by (1-\u212f�)/(2k\u2080) will be applied by the denormalization matrix.
+        }
+        /*
+         * End of map projection. Now compute the derivative, if requested.
+         */
+        return derivate ? new Matrix2(1, 0, 0, 2*dqm_d\u03c6(sin\u03c6, cos(\u03c6))) : null;
+    }
+
+    /**
+     * 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 \u03bb values are not modified by the normalized Cylindrical Equal Area projection.</div>
+     *
+     * @throws TransformException if a point can not be converted.
+     */
+    @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) {
+                final double \u03c6 = dstPts[dstOff += 2];                   // Same as srcPts[srcOff + 1].
+                dstPts[dstOff] = qm(sin(\u03c6));                            // Part of Synder equation (10-15)
+            }
+        }
+    }
+
+    /**
+     * Converts 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] = \u03c6(y / qmPolar);
+        /*
+         * Equation 10-26 of Synder gives \u03b2 = asin(2y\u22c5k\u2080/(a\u22c5qPolar)).
+         * In our case it simplifies to sin\u03b2 = (y/qmPolar) because:
+         *
+         *   - y is already multiplied by 2k\u2080/a because of the denormalization matrix
+         *   - the missing (1-\u212f�) term in qmPolar (compared to qPolar) is in the denormalization matrix.
+         *   - taking the arc sine of \u03b2 is left to \u03c6(double) function.
+         */
+    }
+
+
+    /**
+     * Provides the transform equations for the spherical case of the Cylindrical Equal Area projection.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.8
+     * @version 0.8
+     * @module
+     */
+    static final class Spherical extends CylindricalEqualArea {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = 1063449347697947732L;
+
+        /**
+         * Constructs a new map projection from the parameters of the given projection.
+         *
+         * @param other The other projection (usually ellipsoidal) from which to copy the parameters.
+         */
+        Spherical(final CylindricalEqualArea other) {
+            super(other);
+            context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION).convertAfter(1, 2, null);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Matrix transform(final double[] srcPts, final int srcOff,
+                                final double[] dstPts, final int dstOff,
+                                final boolean derivate) throws ProjectionException
+        {
+            final double \u03c6 = srcPts[srcOff+1];
+            if (dstPts != null) {
+                dstPts[dstOff  ] = srcPts[srcOff];
+                dstPts[dstOff+1] = sin(\u03c6);
+            }
+            return derivate ? new Matrix2(1, 0, 0, cos(\u03c6)) : null;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * <div class="note"><b>Note:</b>
+         * This method must be overridden because the {@link Mercator} class overrides the {@link NormalizedProjection}
+         * 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) {
+                    final double \u03c6 = dstPts[dstOff += 2];           // Same as srcPts[srcOff + 1].
+                    dstPts[dstOff] = sin(\u03c6);
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @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] = asin(y);
+        }
+    }
+}

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

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.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/EqualAreaProjection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java?rev=1752855&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java [UTF-8] Fri Jul 15 17:01:29 2016
@@ -0,0 +1,209 @@
+/*
+ * 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.IOException;
+import java.io.ObjectInputStream;
+
+import static java.lang.Math.*;
+import static org.apache.sis.math.MathFunctions.atanh;
+
+
+/**
+ * Provides formulas common to Equal Area projections.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+abstract class EqualAreaProjection extends NormalizedProjection {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -6175270149094989517L;
+
+    /**
+     * {@code false} for using the original formulas as published by Synder, or {@code true} for using formulas
+     * modified using trigonometric identities. The use of trigonometric identities is for reducing the amount
+     * of calls to the {@link Math#sin(double)} and similar methods. Some identities used are:
+     *
+     * <ul>
+     *   <li>sin(2\u03b2) = 2\u22c5sin\u03b2\u22c5cos\u03b2</li>
+     *   <li>sin(4\u03b2) = (2 - 4\u22c5sin�\u03b2)\u22c5sin(2\u03b2)</li>
+     *   <li>sin(8\u03b2) = 4\u22c5sin(2\u03b2)\u22c5(cos�\u03b2 - sin�\u03b2)\u22c5(8\u22c5cos\u2074\u03b2 - 8\u22c5cos�\u03b2 + 1)</li>
+     * </ul>
+     *
+     * Note that since this boolean is static final, the compiler should exclude the code in the branch that is never
+     * executed (no need to comment-out that code).
+     */
+    private static final boolean ALLOW_TRIGONOMETRIC_IDENTITIES = false;
+
+    /**
+     * Coefficients in the series expansion of the inverse projection,
+     * depending only on {@linkplain #eccentricity eccentricity} value.
+     * The series expansion is of the following form:
+     *
+     *     <blockquote>\u03c6 = ci\u2082\u22c5sin(2\u03b2) + ci\u2084\u22c5sin(4\u03b2) + ci\u2088\u22c5sin(8\u03b2)</blockquote>
+     *
+     * This {@code EqualAreaProjection} class uses those coefficients in {@link #\u03c6(double)}.
+     *
+     * <p><strong>Consider those fields as final!</strong> They are not final only for sub-class
+     * constructors convenience and for the purpose of {@link #readObject(ObjectInputStream)}.</p>
+     *
+     * @see #computeCoefficients()
+     */
+    private transient double ci2, ci4, ci8;
+
+    /**
+     * Creates a new normalized projection from the parameters computed by the given initializer.
+     *
+     * @param initializer The initializer for computing map projection internal parameters.
+     */
+    EqualAreaProjection(final Initializer initializer) {
+        super(initializer);
+    }
+
+    /**
+     * Computes the coefficients in the series expansions from the {@link #eccentricitySquared} value.
+     * This method shall be invoked after {@code EqualAreaProjection} construction or deserialization.
+     */
+    void computeCoefficients() {
+        final double e2 = eccentricitySquared;
+        final double e4 = e2 * e2;
+        final double e6 = e2 * e4;
+        ci2  =  517/5040.  * e6  +  31/180. * e4  +  1/3. * e2;
+        ci4  =  251/3780.  * e6  +  23/360. * e4;
+        ci8  =  761/45360. * e6;
+        /*
+         * When rewriting equations using trigonometric identities, some constants appear.
+         * For example sin(2\u03b2) = 2\u22c5sin\u03b2\u22c5cos\u03b2, so we can factor out the 2 constant into the
+         * into the corresponding 'c' field.
+         */
+        if (ALLOW_TRIGONOMETRIC_IDENTITIES) {
+            // Multiplication by powers of 2 does not bring any additional rounding error.
+            ci2 *=  2;
+            ci4 *=  8;
+            ci8 *= 64;
+        }
+    }
+
+    /**
+     * Creates a new projection initialized to the values of the given one. This constructor may be invoked after
+     * we determined that the default implementation can be replaced by an other one, for example using spherical
+     * formulas instead than the ellipsoidal ones. This constructor allows to transfer all parameters to the new
+     * instance without recomputing them.
+     */
+    EqualAreaProjection(final EqualAreaProjection other) {
+        super(other);
+        ci2 = other.ci2;
+        ci4 = other.ci4;
+        ci8 = other.ci8;
+    }
+
+    /**
+     * Calculates <strong>part</strong> of <var>q</var> from Snyder equation (3-12).
+     * In order to get the <var>q</var> function, this method output must be multiplied
+     * by <code>(1 - {@linkplain #eccentricitySquared})</code>.
+     *
+     * <p>This equation has the following properties:</p>
+     *
+     * <ul>
+     *   <li>Input in the [-1 \u2026 +1] range</li>
+     *   <li>Output multiplied by {@code (1 - \u212f�)} in the [-2 \u2026 +2] range</li>
+     *   <li>Output of the same sign than input</li>
+     *   <li>q(-sin\u03c6) = -q(sin\u03c6)</li>
+     *   <li>q(0) = 0</li>
+     * </ul>
+     *
+     * In the spherical case, <var>q</var> = 2\u22c5sin\u03c6. It is caller responsibility to ensure that this
+     * method is not invoked in the spherical case, since this implementation does not work in such case.
+     *
+     * @param  sin\u03c6 sine of the latitude <var>q</var> is calculated for.
+     * @return <var>q</var> from Snyder equation (3-12).
+     */
+    final double qm(final double sin\u03c6) {
+        final double \u212fsin\u03c6 = eccentricity * sin\u03c6;
+        return sin\u03c6 / (1 - \u212fsin\u03c6*\u212fsin\u03c6) + atanh(\u212fsin\u03c6) / eccentricity;
+    }
+
+    /**
+     * Gets the derivative of the {@link #qm(double)} method.
+     * Callers must multiply the returned value by <code>(1 - {@linkplain #eccentricitySquared})</code>
+     * in order to get the derivative of Snyder equation (3-12).
+     *
+     * @param  sin\u03c6  the sine of latitude.
+     * @param  cos\u03c6  the cosines of latitude.
+     * @return the {@code qm} derivative at the specified latitude.
+     */
+    final double dqm_d\u03c6(final double sin\u03c6, final double cos\u03c6) {
+        final double \u212fsin\u03c62 = eccentricitySquared * (sin\u03c6*sin\u03c6);
+        return (cos\u03c6 / (1 - \u212fsin\u03c62)) * (1 + ((1 + \u212fsin\u03c62) / (1 - \u212fsin\u03c62)));
+    }
+
+    /**
+     * Computes the latitude using equation 3-18 from Synder.
+     *
+     * @param  sin\u03b2 see Synder equation 10-26.
+     * @return the latitude in radians.
+     */
+    final double \u03c6(final double sin\u03b2) {
+        final double \u03b2 = asin(sin\u03b2);
+        if (!ALLOW_TRIGONOMETRIC_IDENTITIES) {
+            return ci8 * sin(8*\u03b2)
+                 + ci4 * sin(4*\u03b2)
+                 + ci2 * sin(2*\u03b2)
+                 + \u03b2;                                                               // Synder 3-18
+        } else {
+            /*
+             * Same formula than above, but rewriten using trigonometric identities in order to avoid
+             * multiple calls to sin(double) method. The cost is only one sqrt(double) method call.
+             */
+            final double sin2_\u03b2 = sin\u03b2*sin\u03b2;                                        // = sin�\u03b2
+            final double cos2_\u03b2 = 1 - sin2_\u03b2;                                       // = cos�\u03b2
+            final double t2\u03b2 = sin\u03b2 * sqrt(cos2_\u03b2);                                 // = sin(2\u03b2) /   2
+            final double t4\u03b2 = 0.5 - sin2_\u03b2;                                        // = sin(4\u03b2) / ( 4\u22c5sin(2\u03b2))
+            final double t8\u03b2 = (cos2_\u03b2 - sin2_\u03b2)*(cos2_\u03b2*cos2_\u03b2 - cos2_\u03b2 + 1./8);   // = sin(8\u03b2) / (32\u22c5sin(2\u03b2))
+
+            assert identityEquals(t2\u03b2, sin(2*\u03b2) / ( 2      ));
+            assert identityEquals(t4\u03b2, sin(4*\u03b2) / ( 8 * t2\u03b2));
+            assert identityEquals(t8\u03b2, sin(8*\u03b2) / (64 * t2\u03b2));
+
+            return (ci8*t8\u03b2  +  ci4*t4\u03b2  +  ci2) * t2\u03b2  +  \u03b2;
+        }
+    }
+
+    /**
+     * Verifies if a trigonometric identity produced the expected value. This method is used in assertions only.
+     * The tolerance threshold is approximatively 1.5E-12 (note that it still about 7000 time greater than
+     * {@code Math.ulp(1.0)}).
+     *
+     * @see #ALLOW_TRIGONOMETRIC_IDENTITIES
+     */
+    private static boolean identityEquals(final double actual, final double expected) {
+        // Use !(a > b) instead of (a <= b) in order to tolerate NaN.
+        return !(abs(actual - expected) > (ANGULAR_TOLERANCE / 1000));
+    }
+
+    /**
+     * Restores transient fields after deserialization.
+     */
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        computeCoefficients();
+    }
+}

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

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

Modified: sis/branches/JDK8/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod?rev=1752855&r1=1752854&r2=1752855&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod [UTF-8] Fri Jul 15 17:01:29 2016
@@ -27,6 +27,7 @@ org.apache.sis.internal.referencing.prov
 org.apache.sis.internal.referencing.provider.PseudoMercator
 org.apache.sis.internal.referencing.provider.RegionalMercator
 org.apache.sis.internal.referencing.provider.MillerCylindrical
+org.apache.sis.internal.referencing.provider.CylindricalEqualArea
 org.apache.sis.internal.referencing.provider.LambertConformal1SP
 org.apache.sis.internal.referencing.provider.LambertConformal2SP
 org.apache.sis.internal.referencing.provider.LambertConformalWest

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java?rev=1752855&r1=1752854&r2=1752855&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java [UTF-8] Fri Jul 15 17:01:29 2016
@@ -79,6 +79,7 @@ public final strictfp class ProvidersTes
             PseudoMercator.class,
             RegionalMercator.class,
             MillerCylindrical.class,
+            CylindricalEqualArea.class,
             LambertConformal1SP.class,
             LambertConformal2SP.class,
             LambertConformalWest.class,

Added: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalEqualAreaTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalEqualAreaTest.java?rev=1752855&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalEqualAreaTest.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalEqualAreaTest.java [UTF-8] Fri Jul 15 17:01:29 2016
@@ -0,0 +1,82 @@
+/*
+ * 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.util.FactoryException;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.test.ToleranceModifier;
+import org.apache.sis.internal.referencing.Formulas;
+import org.junit.Test;
+
+
+/**
+ * Tests the {@link CylindricalEqualArea} class.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+public final strictfp class CylindricalEqualAreaTest extends MapProjectionTestCase {
+    /**
+     * Creates a map projection.
+     */
+    private void createCompleteProjection(final boolean ellipse,
+            final double centralMeridian, final double standardParallel) throws FactoryException
+    {
+        createCompleteProjection(new org.apache.sis.internal.referencing.provider.CylindricalEqualArea(),
+                ellipse, centralMeridian, 0, standardParallel, 1, 0, 0);
+    }
+
+    /**
+     * Tests projection of a point the the in ellipsoidal case.
+     *
+     * @throws FactoryException if an error occurred while creating the map projection.
+     * @throws TransformException if an error occurred while projecting a point.
+     */
+    @Test
+    public void testEllipsoidal() throws FactoryException, TransformException {
+        createCompleteProjection(true, 0, 0);
+        tolerance = Formulas.LINEAR_TOLERANCE;
+        toleranceModifier = ToleranceModifier.PROJECTION;
+        final double \u03bb = 2;
+        final double \u03c6 = 1;
+        final double x = 222638.98;             // Test point from Proj.4.
+        final double y = 110568.81;
+        verifyTransform(new double[] {\u03bb, \u03c6,  -\u03bb, \u03c6,  \u03bb, -\u03c6,  -\u03bb, -\u03c6},
+                        new double[] {x, y,  -x, y,  x, -y,  -x, -y});
+    }
+
+    /**
+     * Tests projection of a point the the in spherical case.
+     *
+     * @throws FactoryException if an error occurred while creating the map projection.
+     * @throws TransformException if an error occurred while projecting a point.
+     */
+    @Test
+    public void testSpherical() throws FactoryException, TransformException {
+        createCompleteProjection(false, 0, 0);
+        tolerance = Formulas.LINEAR_TOLERANCE;
+        toleranceModifier = ToleranceModifier.PROJECTION;
+        final double \u03bb = 2;
+        final double \u03c6 = 1;
+        final double x = 222390.10;
+        final double y = 111189.40;
+        verifyTransform(new double[] {\u03bb, \u03c6,  -\u03bb, \u03c6,  \u03bb, -\u03c6,  -\u03bb, -\u03c6},
+                        new double[] {x, y,  -x, y,  x, -y,  -x, -y});
+    }
+}

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

Propchange: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalEqualAreaTest.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=1752855&r1=1752854&r2=1752855&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] Fri Jul 15 17:01:29 2016
@@ -164,6 +164,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.projection.TransverseMercatorTest.class,
     org.apache.sis.referencing.operation.projection.PolarStereographicTest.class,
     org.apache.sis.referencing.operation.projection.ObliqueStereographicTest.class,
+    org.apache.sis.referencing.operation.projection.CylindricalEqualAreaTest.class,
 
     // Coordinate operation and derived Coordinate Reference Systems (cyclic dependency).
     org.apache.sis.referencing.operation.DefaultTransformationTest.class,