This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit fc7eca86b224889013a1f2d368e6ba87300c376a
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Fri Apr 26 11:38:49 2024 +0200

    Initial version of Equidistant Cylindrical (EPSG:1028) map projection
    with formulas as published in EPSG guidance notes.
    
    https://issues.apache.org/jira/browse/SIS-233
---
 .../main/module-info.java                          |   1 +
 .../operation/projection/CylindricalEqualArea.java |   4 +-
 .../projection/EquidistantCylindrical.java         | 194 +++++++++++++++++++++
 .../operation/projection/NormalizedProjection.java |   5 +-
 .../operation/provider/EquidistantCylindrical.java |  81 +++++++++
 .../operation/provider/Equirectangular.java        |  24 ++-
 .../operation/transform/MathTransformProvider.java |   2 +-
 .../apache/sis/referencing/ClenshawSummation.java  |  11 +-
 .../operation/projection/CassiniSoldnerTest.java   |   6 +-
 .../projection/EquidistantCylindricalTest.java     |  90 ++++++++++
 .../operation/provider/ProvidersTest.java          |   1 +
 geoapi/snapshot                                    |   2 +-
 12 files changed, 407 insertions(+), 14 deletions(-)

diff --git a/endorsed/src/org.apache.sis.referencing/main/module-info.java 
b/endorsed/src/org.apache.sis.referencing/main/module-info.java
index 70b0b34ee3..d79c190aac 100644
--- a/endorsed/src/org.apache.sis.referencing/main/module-info.java
+++ b/endorsed/src/org.apache.sis.referencing/main/module-info.java
@@ -127,6 +127,7 @@ module org.apache.sis.referencing {
              org.apache.sis.referencing.operation.provider.Orthographic,
              
org.apache.sis.referencing.operation.provider.ModifiedAzimuthalEquidistant,
              
org.apache.sis.referencing.operation.provider.AzimuthalEquidistantSpherical,
+             
org.apache.sis.referencing.operation.provider.EquidistantCylindrical,
              
org.apache.sis.referencing.operation.provider.ZonedTransverseMercator,
              org.apache.sis.referencing.operation.provider.Sinusoidal,
              org.apache.sis.referencing.operation.provider.PseudoSinusoidal,
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java
index e27c23a72f..24cbefaf78 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java
@@ -122,8 +122,8 @@ public class CylindricalEqualArea extends 
AuthalicConversion {
     /**
      * 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.
+     * @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));
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/EquidistantCylindrical.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/EquidistantCylindrical.java
new file mode 100644
index 0000000000..de2edd02df
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/EquidistantCylindrical.java
@@ -0,0 +1,194 @@
+/*
+ * 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 static java.lang.Math.sin;
+import static java.lang.Math.cos;
+import static java.lang.Math.toRadians;
+import org.opengis.util.FactoryException;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.OperationMethod;
+import org.apache.sis.util.Workaround;
+import org.apache.sis.util.privy.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.provider.Equirectangular;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
+import org.apache.sis.referencing.operation.transform.MathTransformProvider;
+
+
+/**
+ * <cite>Equidistant Cylindrical</cite> projection (EPSG code 1028).
+ * This class implements only the ellipsoidal case. There is no specialization 
for the spherical case,
+ * because the latter can be implemented by an affine transform instead of 
{@code NormalizedProjection}.
+ *
+ * <h4>Limitations</h4>
+ * The trigonometric series used in this implementation is adequate for a 
flattening of 1/290 or less.
+ * The series has not yet been optimized with Clenshaw summation.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+public class EquidistantCylindrical extends NormalizedProjection {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 6598912212024442670L;
+
+    /**
+     * Coefficients for the forward (f) and inverse (i) projection.
+     * This is used in a trigonometric series with multiple angles.
+     *
+     * <h4>Missed optimization</h4>
+     * We should replace the multiple angles by power polynomials using the 
Clenshaw summation algorithm.
+     * However the {@code org.apache.sis.referencing.ClenshawSummation} class 
(in test packages) that we
+     * used for this purpose in other map projections is limited to 6 terms, 
and we have 7 terms here.
+     */
+    private final double cf0, cf2, cf4, cf6, cf8, cf10, cf12, cf14,
+                              ci2, ci4, ci6, ci8, ci10, ci12, ci14;
+
+    /**
+     * Creates an Equidistant Cylindrical projection from the given parameters.
+     *
+     * @param method     description of the projection parameters.
+     * @param parameters the parameter values of the projection to create.
+     */
+    public EquidistantCylindrical(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);
+        roles.put(ParameterRole.FALSE_EASTING,    
Equirectangular.FALSE_EASTING);
+        roles.put(ParameterRole.FALSE_NORTHING,   
Equirectangular.FALSE_NORTHING);
+        roles.put(ParameterRole.CENTRAL_MERIDIAN, 
Equirectangular.LONGITUDE_OF_ORIGIN);
+        return new Initializer(method, parameters, roles, null);
+    }
+
+    /**
+     * 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 EquidistantCylindrical(final Initializer initializer) {
+        super(initializer, null);
+        final double φ1 = 
toRadians(initializer.getAndStore(Equirectangular.STANDARD_PARALLEL));
+        final DoubleDouble sx = 
initializer.rν2(sin(φ1)).sqrt().multiply(cos(φ1), false);
+        final MatrixSIS denormalize = 
context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
+        denormalize.convertBefore(0, sx, null);
+
+        // Coefficients for the forward projection.
+        final double e2 = eccentricitySquared;
+        final double e4 = e2*e2;
+        final double e6 = e2*e4;
+        final double e8 = e4*e4;
+        cf0  = 1 - e2*(1./4 + e2*( 3./64  + e2*( 5./256  + e2*(175./16384  + 
e2*( 441./65536   + e2*(  4851./1048576 + e2*( 14157./4194304 )))))));
+        cf2  =   - e2*(3./8 + e2*( 3./32  + e2*(45./1024 + e2*(105./4096   + 
e2*(2205./131072  + e2*(  6237./524288  + e2*(297297./33554432)))))));
+        cf4  =                e4*(15./256 + e2*(45./1024 + e2*(525./16384  + 
e2*(1575./65536   + e2*(155925./8388608 + e2*(495495./33554432))))));
+        cf6  =                            - e6*(35./3072 + e2*(175./12288  + 
e2*(3675./262144  + e2*( 13475./1048576 + e2*(385385./33554432)))));
+        cf8  =                                           + e8*(315./131072 + 
e2*(2205./524288  + e2*( 43659./8388608 + e2*(189189./33554432))));
+        cf10 =                                                        - 
(e4*e6)*( 693./1310720 + e2*(  6237./5242880 + e2*(297297./167772160)));
+        cf12 =                                                                 
             (e6*e6)*(  1001./8388608 + e2*( 11011./33554432));
+        cf14 =                                                                 
                                 - (e6*e8)*(  6435./234881024);
+
+        // Coefficients for the inverse projection.
+        final double n = 
initializer.axisLengthRatio().ratio_1m_1p().doubleValue();
+        final double n2 = n*n;
+        final double n3 = n*n2;
+        final double n4 = n2*n2;
+        ci2  = n*(3./2 + n2*(-27./32 + n2*(269./512   + n2*(  -6607./24576))));
+        ci4  =           n2*( 21./16 + n2*( -55./32   + n2*(   6759./4096)));
+        ci6  =           n3*(151./96 + n2*(-417./128  + n2*(  87963./20480)));
+        ci8  =                         n4*(1097./512  + n2*( -15543./2560));
+        ci10 =                    (n3*n2)*(8011./2560 + n2*( -69119./6144));
+        ci12 =                                     (n3*n3)*( 293393./61440);
+        ci14 =                                     (n3*n4)*(6845701./860160);
+    }
+
+    /**
+     * Returns the sequence of <i>normalization</i> → {@code this} → 
<i>denormalization</i> 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>.
+     * If the ellipsoid is spherical, this map projection is replaced by an 
affine transform.
+     *
+     * @param  parameters  parameters and 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 
MathTransformProvider.Context parameters) throws FactoryException {
+        if (eccentricity == 0) {
+            return Equirectangular.provider().createMathTransform(parameters);
+        }
+        return completeWithWraparound(parameters);
+    }
+
+    /**
+     * Projects the specified (λ,φ) coordinates (units in radians) and stores 
the result in {@code dstPts}.
+     * In addition, opportunistically computes the projection derivative if 
{@code derivate} is {@code true}.
+     * The results must be multiplied by the denormalization matrix before to 
get linear distances.
+     *
+     * @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 coordinates cannot 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];
+        if (dstPts != null) {
+            dstPts[dstOff] = srcPts[srcOff];
+            dstPts[dstOff+1] = cf14*sin(14*φ) + cf12*sin(12*φ) + 
cf10*sin(10*φ) + cf8*sin(8*φ)
+                             +  cf6*sin( 6*φ)  + cf4*sin( 4*φ) +  cf2*sin( 
2*φ) + cf0*φ;
+        }
+        if (!derivate) {
+            return null;
+        }
+        final var derivative = new Matrix2();
+        derivative.m11 = cf14*cos(14*φ)*14 + cf12*cos(12*φ)*12 + 
cf10*cos(10*φ)*10 + cf8*cos(8*φ)*8
+                       +  cf6*cos( 6*φ)*6  +  cf4*cos( 4*φ)*4  +  cf2*cos( 
2*φ)*2  + cf0;
+        return derivative;
+    }
+
+    /**
+     * 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 cannot be converted.
+     */
+    @Override
+    protected void inverseTransform(final double[] srcPts, final int srcOff,
+                                    final double[] dstPts, final int dstOff)
+            throws ProjectionException
+    {
+        final double μ = srcPts[srcOff+1] / cf0;
+        dstPts[dstOff] = srcPts[srcOff];
+        dstPts[dstOff+1] = ci14*sin(14*μ) + ci12*sin(12*μ) + ci10*sin(10*μ) + 
ci8*sin(8*μ)
+                         +  ci6*sin( 6*μ) +  ci4*sin( 4*μ) +  ci2*sin( 2*μ) + 
μ;
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
index 8b22d9f146..1226a486e1 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
@@ -718,11 +718,12 @@ public abstract class NormalizedProjection extends 
AbstractMathTransform2D imple
      * If this assumption is not applicable to a particular subclass, then it 
is implementer responsibility to check
      * the range.
      *
-     * @param  srcPts    the array containing the source point coordinates, as 
(<var>longitude</var>, <var>latitude</var>)
-     *                   angles in <strong>radians</strong>.
+     * @param  srcPts    the array containing the source point coordinates,
+     *                   as (<var>longitude</var>, <var>latitude</var>) angles 
in <strong>radians</strong>.
      * @param  srcOff    the offset of the single coordinate tuple to be 
converted in the source array.
      * @param  dstPts    the array into which the converted coordinates is 
returned (may be the same as {@code srcPts}).
      *                   Coordinates will be expressed in a dimensionless 
unit, as a linear distance on a unit sphere or ellipse.
+     *                   This array may be {@code null} if the caller is 
interested only in the derivative.
      * @param  dstOff    the offset of the location of the converted 
coordinates that is stored in the destination array.
      * @param  derivate  {@code true} for computing the derivative, or {@code 
false} if not needed.
      * @return the matrix of the projection derivative at the given source 
position,
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/EquidistantCylindrical.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/EquidistantCylindrical.java
new file mode 100644
index 0000000000..c03e4859c3
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/EquidistantCylindrical.java
@@ -0,0 +1,81 @@
+/*
+ * 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.provider;
+
+import jakarta.xml.bind.annotation.XmlTransient;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.referencing.operation.projection.NormalizedProjection;
+
+
+/**
+ * The provider for <q>Equidistant Cylindrical</q> projection
+ * (EPSG:1028, <span class="deprecated">EPSG:9842</span>).
+ *
+ * <h2>Note</h2>
+ * EPSG:1028 is the current codes, while EPSG:9842 is a deprecated code.
+ * The new and deprecated definitions differ only by their parameters. In the 
Apache SIS implementation,
+ * both current and legacy definitions are known, but the legacy names are 
marked as deprecated.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+@XmlTransient
+public final class EquidistantCylindrical extends MapProjection {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -1180656445349342258L;
+
+    /**
+     * The group of all parameters expected by this coordinate operation.
+     */
+    private static final ParameterDescriptorGroup PARAMETERS;
+    static {
+        final ParameterBuilder builder = builder();
+        PARAMETERS = addIdentifierAndLegacy(builder, "1028", "9842")    // 
9842 uses deprecated parameter names.
+                .addName(                   "Equidistant Cylindrical")
+                .addName(Citations.ESRI,    "Equidistant_Cylindrical")
+                .addName(Citations.GEOTIFF, "CT_Equirectangular")
+                .addName(Citations.PROJ4,   "eqc")
+                .addIdentifier(Citations.GEOTIFF, "17")
+                .createGroupForMapProjection(
+                        Equirectangular.STANDARD_PARALLEL,
+                        Equirectangular.LATITUDE_OF_ORIGIN,     // Not 
formally an Equidistant Cylindrical parameter.
+                        Equirectangular.LONGITUDE_OF_ORIGIN,
+                        Equirectangular.FALSE_EASTING,
+                        Equirectangular.FALSE_NORTHING);
+    }
+
+    /**
+     * Constructs a new provider.
+     */
+    public EquidistantCylindrical() {
+        super(PARAMETERS);
+    }
+
+    /**
+     * Creates a map projection on an ellipsoid having a semi-major axis 
length of 1.
+     *
+     * @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.EquidistantCylindrical(this, 
parameters);
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Equirectangular.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Equirectangular.java
index e0b023c68f..42d25915da 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Equirectangular.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Equirectangular.java
@@ -248,14 +248,11 @@ public final class Equirectangular extends 
AbstractProvider {
 
         // Do not declare the ESRI "Equidistant_Cylindrical" projection name 
below,
         // for avoiding confusion with EPSG "Equidistant Cylindrical" 
ellipsoidal projection.
-        PARAMETERS = addIdentifierAndLegacy(builder, "1029", "9823")  // 9823 
uses deprecated parameter names
+        PARAMETERS = addIdentifierAndLegacy(builder, "1029", "9823")    // 
9823 uses deprecated parameter names.
                 .addName(                   NAME)
                 .addName(                   "Plate Carrée")  // Not formally 
defined by EPSG, but cited in documentation.
                 .addName(Citations.OGC,     "Equirectangular")
                 .addName(Citations.ESRI,    "Plate_Carree")
-                .addName(Citations.GEOTIFF, "CT_Equirectangular")
-                .addName(Citations.PROJ4,   "eqc")
-                .addIdentifier(Citations.GEOTIFF, "17")
                 .createGroupForMapProjection(
                         STANDARD_PARALLEL,
                         LATITUDE_OF_ORIGIN,     // Not formally an 
Equirectangular parameter.
@@ -264,10 +261,29 @@ public final class Equirectangular extends 
AbstractProvider {
                         FALSE_NORTHING);
     }
 
+    /**
+     * The canonical instance of this map projection.
+     *
+     * @see #provider()
+     */
+    private static final Equirectangular INSTANCE = new Equirectangular();
+
+    /**
+     * Returns the canonical instance of this map projection.
+     * This method is invoked by {@link java.util.ServiceLoader} using 
reflection.
+     *
+     * @return the canonical instance of this map projection.
+     */
+    public static Equirectangular provider() {
+        return INSTANCE;
+    }
+
     /**
      * Constructs a new provider.
      *
      * @see MapProjection#MapProjection(Class, ParameterDescriptorGroup)
+     *
+     * @todo Make this constructor private after we stop class-path support.
      */
     public Equirectangular() {
         super(Conversion.class, PARAMETERS,
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformProvider.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformProvider.java
index a215ab8718..2b4a1d69a9 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformProvider.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformProvider.java
@@ -122,7 +122,7 @@ public interface MathTransformProvider {
     {
         return createMathTransform(new MathTransformProvider.Context() {
             @Override public MathTransformFactory getFactory() {
-                return (factory != null) ? factory : 
DefaultMathTransformFactory.provider();
+                return (factory != null) ? factory : 
Context.super.getFactory();
             }
             @Override public ParameterValueGroup getCompletedParameters() {
                 return parameters;
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/ClenshawSummation.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/ClenshawSummation.java
index 6a40477726..c50951578b 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/ClenshawSummation.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/ClenshawSummation.java
@@ -55,7 +55,15 @@ import org.apache.sis.pending.jdk.JDK21;
  * <a 
href="https://svn.apache.org/repos/asf/sis/analysis/Map%20projection%20formulas.ods";>Subversion</a>.
  * This class is used for more complex formulas where the use of spreadsheet 
become too difficult.
  *
+ * <h2>Limitations</h2>
+ * Current implementation can handle a maximum of 6 terms in the trigonometric 
series (see {@link #compute()}).
+ * This limit is too short for {@link 
org.apache.sis.referencing.operation.projection.EquidistantCylindrical}.
+ * It would be possible to generalize using an iterative algorithm. Given that 
{@code EquidistantCylindrical}
+ * is used less often than Mercator or Lambert projections, we have not done 
this optimization yet.
+ *
  * @author  Martin Desruisseaux (Geomatys)
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-465";>SIS-465</a>
  */
 public final class ClenshawSummation {
     /**
@@ -339,7 +347,8 @@ public final class ClenshawSummation {
 
     /**
      * Performs the Clenshaw summation. Current implementation uses hard-coded 
coefficients for 6 terms.
-     * See Karney (2010) equation 59 if generalization to an arbitrary number 
of coefficients is desired.
+     * See Charles F. F. Karney, Geodesics on an ellipsoid of revolution 
(2011) equation 59
+     * if generalization to an arbitrary number of coefficients is desired.
      *
      * @see <a href="https://issues.apache.org/jira/browse/SIS-465";>SIS-465</a>
      */
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/CassiniSoldnerTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/CassiniSoldnerTest.java
index c9af252c22..9a89d6076c 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/CassiniSoldnerTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/CassiniSoldnerTest.java
@@ -86,7 +86,7 @@ public final class CassiniSoldnerTest extends 
MapProjectionTestCase {
          * Fix φ=45°, which implies tan(φ)=1.
          * Test using the CassiniSoldner spherical equation.
          */
-        final DirectPosition2D point = new DirectPosition2D();
+        final var point = new DirectPosition2D();
         final double domain = toRadians(5);
         final double step   = domain / 20;
         for (double λ = -domain; λ <= domain; λ += step) {
@@ -122,7 +122,7 @@ public final class CassiniSoldnerTest extends 
MapProjectionTestCase {
 
         final double λ = -62;
         final double φ =  10;
-        final DirectPosition2D p = new DirectPosition2D(λ, φ);
+        final var p = new DirectPosition2D(λ, φ);
         assertSame(p, transform.transform(p, p));
         assertEquals(66644.94, p.x, 0.005);
         assertEquals(82536.22, p.y, 0.005);
@@ -155,7 +155,7 @@ public final class CassiniSoldnerTest extends 
MapProjectionTestCase {
 
         final double λ =  179 + (59 + 39.6115/60)/60;   // 179°59′39.6115″E
         final double φ = -(16 + (50 + 29.2435/60)/60);  //  16°50′29.2435″S
-        final DirectPosition2D p = new DirectPosition2D(λ, φ);
+        final var p = new DirectPosition2D(λ, φ);
         assertSame(p, transform.transform(p, p));
         assertEquals(16015.2890, p.x, 0.00005);
         assertEquals(13369.6601, p.y, 0.00005);
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/EquidistantCylindricalTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/EquidistantCylindricalTest.java
new file mode 100644
index 0000000000..cf14b0a4d0
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/EquidistantCylindricalTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.apache.sis.parameter.Parameters;
+import org.apache.sis.geometry.DirectPosition2D;
+import org.apache.sis.referencing.privy.Formulas;
+import org.apache.sis.referencing.operation.provider.MapProjection;
+
+// Test dependencies
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+
+/**
+ * Tests the {@link EquidistantCylindrical} projection.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+public final class EquidistantCylindricalTest extends MapProjectionTestCase {
+    /**
+     * Creates a new test case.
+     */
+    public EquidistantCylindricalTest() {
+    }
+
+    /**
+     * Returns the provider for the "Equidistant Cylindrical" projection.
+     */
+    private static MapProjection provider() {
+        return new 
org.apache.sis.referencing.operation.provider.EquidistantCylindrical();
+    }
+
+    /**
+     * Tests the point given in EPSG example.
+     * This is the same test as {@link #runGeoapiTest()} but is repeated here 
for easier debugging.
+     *
+     * @throws FactoryException if an error occurred while creating the map 
projection.
+     * @throws TransformException if an error occurred while projecting a 
coordinate.
+     */
+    @Test
+    public void testSinglePoint() throws FactoryException, TransformException {
+        final MapProjection provider = provider();
+        final Parameters pg = 
Parameters.castOrWrap(provider.getParameters().createValue());
+        pg.parameter("semi-major").setValue(WGS84_A);
+        pg.parameter("semi-minor").setValue(WGS84_B);
+        transform = provider.createMathTransform(null, pg);
+
+        final double λ = 10;
+        final double φ = 55;
+        final var p = new DirectPosition2D(λ, φ);
+        assertSame(p, transform.transform(p, p));
+        assertEquals(1113194.91, p.x, 0.005);
+        assertEquals(6097230.31, p.y, 0.005);
+
+        assertSame(p, transform.inverse().transform(p, p));
+        assertEquals(λ, p.x, Formulas.ANGULAR_TOLERANCE);
+        assertEquals(φ, p.y, Formulas.ANGULAR_TOLERANCE);
+    }
+
+    /**
+     * Tests the <q>Equidistant Cylindrical</q> (EPSG:1028) projection.
+     * This test is defined in GeoAPI conformance test suite.
+     *
+     * @throws FactoryException if an error occurred while creating the map 
projection.
+     * @throws TransformException if an error occurred while projecting a 
coordinate.
+     *
+     * @see 
org.opengis.test.referencing.ParameterizedTransformTest#testEquidistantCylindrical()
+     */
+    @Test
+    public void runGeoapiTest() throws FactoryException, TransformException {
+        createGeoApiTest(provider()).testEquidistantCylindrical();
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
index aedf86cd9a..72e3e25d60 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
@@ -106,6 +106,7 @@ public final class ProvidersTest extends TestCase {
             Orthographic.class,
             ModifiedAzimuthalEquidistant.class,
             AzimuthalEquidistantSpherical.class,
+            EquidistantCylindrical.class,
             ZonedTransverseMercator.class,
             SatelliteTracking.class,
             Sinusoidal.class,
diff --git a/geoapi/snapshot b/geoapi/snapshot
index 42382222dc..41a6799cb2 160000
--- a/geoapi/snapshot
+++ b/geoapi/snapshot
@@ -1 +1 @@
-Subproject commit 42382222dc30ef4158fb58cc96e1a517d2c44a6b
+Subproject commit 41a6799cb2940149f5f034db64365241198da439

Reply via email to