sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject 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...
Date Fri, 15 Jul 2016 17:01:29 GMT
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>
(φ₁) parameter value.
+     * Valid values range is (-90 … 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>
(λ₀) parameter value.
+     * Valid values range is [-180 … 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φ
= 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(…) 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 -λ₀ 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 λ0 = initializer.getAndStore(LONGITUDE_OF_ORIGIN);
+        if (λ0 != 0) {
+            final DoubleDouble offset = DoubleDouble.createDegreesToRadians();
+            offset.multiply(-λ0);
+            denormalize.convertBefore(0, null, offset);
+        }
+        /*
+         * Compute the scale factor as k₀ = cosφ₁/√(1 - ℯ²⋅sin²φ₁), multiplied
by user-specified scale factor if any.
+         * Explicit scale factor is not formally a Cylindrical Equal Area parameter (it is
rather computed from φ₁),
+         * but we nevertheless support it.
+         */
+        final double φ1 = toRadians(initializer.getAndStore(STANDARD_PARALLEL));
+        final DoubleDouble k0 = verbatim(initializer.scaleAtφ(sin(φ1), cos(φ1)));
+        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₀
+         *      y shall be divided by k₀
+         *
+         * Furthermore we also multiply y by (1-ℯ²)/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> → {@code this} →
<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 (λ,φ) 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 (λ,φ) 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+1];
+        final double sinφ = sin(φ);
+        if (dstPts != null) {
+            dstPts[dstOff  ] = srcPts[srcOff];  // Multiplication by k₀ will be applied
by the denormalization matrix.
+            dstPts[dstOff+1] = qm(sinφ);        // Multiplication by (1-ℯ²)/(2k₀) 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φ(sinφ, cos(φ))) : 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 λ 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 φ = dstPts[dstOff += 2];                   // Same as srcPts[srcOff
+ 1].
+                dstPts[dstOff] = qm(sin(φ));                            // 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] = φ(y / qmPolar);
+        /*
+         * Equation 10-26 of Synder gives β = asin(2y⋅k₀/(a⋅qPolar)).
+         * In our case it simplifies to sinβ = (y/qmPolar) because:
+         *
+         *   - y is already multiplied by 2k₀/a because of the denormalization matrix
+         *   - the missing (1-ℯ²) term in qmPolar (compared to qPolar) is in the denormalization
matrix.
+         *   - taking the arc sine of β is left to φ(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 φ = srcPts[srcOff+1];
+            if (dstPts != null) {
+                dstPts[dstOff  ] = srcPts[srcOff];
+                dstPts[dstOff+1] = sin(φ);
+            }
+            return derivate ? new Matrix2(1, 0, 0, cos(φ)) : 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 φ = dstPts[dstOff += 2];           // Same as srcPts[srcOff
+ 1].
+                    dstPts[dstOff] = sin(φ);
+                }
+            }
+        }
+
+        /**
+         * {@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β) = 2⋅sinβ⋅cosβ</li>
+     *   <li>sin(4β) = (2 - 4⋅sin²β)⋅sin(2β)</li>
+     *   <li>sin(8β) = 4⋅sin(2β)⋅(cos²β - sin²β)⋅(8⋅cos⁴β - 8⋅cos²β
+ 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>φ = ci₂⋅sin(2β) + ci₄⋅sin(4β) + ci₈⋅sin(8β)</blockquote>
+     *
+     * This {@code EqualAreaProjection} class uses those coefficients in {@link #φ(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β) = 2⋅sinβ⋅cosβ, 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 … +1] range</li>
+     *   <li>Output multiplied by {@code (1 - ℯ²)} in the [-2 … +2] range</li>
+     *   <li>Output of the same sign than input</li>
+     *   <li>q(-sinφ) = -q(sinφ)</li>
+     *   <li>q(0) = 0</li>
+     * </ul>
+     *
+     * In the spherical case, <var>q</var> = 2⋅sinφ. 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φ 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φ) {
+        final double ℯsinφ = eccentricity * sinφ;
+        return sinφ / (1 - ℯsinφ*ℯsinφ) + atanh(ℯsinφ) / 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φ  the sine of latitude.
+     * @param  cosφ  the cosines of latitude.
+     * @return the {@code qm} derivative at the specified latitude.
+     */
+    final double dqm_dφ(final double sinφ, final double cosφ) {
+        final double ℯsinφ2 = eccentricitySquared * (sinφ*sinφ);
+        return (cosφ / (1 - ℯsinφ2)) * (1 + ((1 + ℯsinφ2) / (1 - ℯsinφ2)));
+    }
+
+    /**
+     * Computes the latitude using equation 3-18 from Synder.
+     *
+     * @param  sinβ see Synder equation 10-26.
+     * @return the latitude in radians.
+     */
+    final double φ(final double sinβ) {
+        final double β = asin(sinβ);
+        if (!ALLOW_TRIGONOMETRIC_IDENTITIES) {
+            return ci8 * sin(8*β)
+                 + ci4 * sin(4*β)
+                 + ci2 * sin(2*β)
+                 + β;                                                               // 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_β = sinβ*sinβ;                                        //
= sin²β
+            final double cos2_β = 1 - sin2_β;                                       //
= cos²β
+            final double t2β = sinβ * sqrt(cos2_β);                                 //
= sin(2β) /   2
+            final double t4β = 0.5 - sin2_β;                                        //
= sin(4β) / ( 4⋅sin(2β))
+            final double t8β = (cos2_β - sin2_β)*(cos2_β*cos2_β - cos2_β + 1./8); 
 // = sin(8β) / (32⋅sin(2β))
+
+            assert identityEquals(t2β, sin(2*β) / ( 2      ));
+            assert identityEquals(t4β, sin(4*β) / ( 8 * t2β));
+            assert identityEquals(t8β, sin(8*β) / (64 * t2β));
+
+            return (ci8*t8β  +  ci4*t4β  +  ci2) * t2β  +  β;
+        }
+    }
+
+    /**
+     * 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 λ = 2;
+        final double φ = 1;
+        final double x = 222638.98;             // Test point from Proj.4.
+        final double y = 110568.81;
+        verifyTransform(new double[] {λ, φ,  -λ, φ,  λ, -φ,  -λ, -φ},
+                        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 λ = 2;
+        final double φ = 1;
+        final double x = 222390.10;
+        final double y = 111189.40;
+        verifyTransform(new double[] {λ, φ,  -λ, φ,  λ, -φ,  -λ, -φ},
+                        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,




Mime
View raw message