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,