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,