sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Retrofit HyperbolicCassiniSoldner into CassiniSoldner. It reduces code duplication and may be more flexible if we want to implement the Jacobian matrix of hyperbolic case in a future version. It also reduces the visibility of hyperbolic case, which is desired because it is a very specific case. The cost on normal Cassini-Soldner case is hopefully light (a single `if` statement) and may be acceptable since this projection exists mostly for historical purposes.
Date Thu, 30 Apr 2020 19:10:06 GMT
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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new a1f9a65  Retrofit HyperbolicCassiniSoldner into CassiniSoldner. It reduces code duplication
and may be more flexible if we want to implement the Jacobian matrix of hyperbolic case in
a future version. It also reduces the visibility of hyperbolic case, which is desired because
it is a very specific case.  The cost on normal Cassini-Soldner case is hopefully light (a
single `if` statement) and may be acceptable since this projection exists mostly for historical
purposes.
a1f9a65 is described below

commit a1f9a659f64214d1525962b919f579bbc91d252c
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Apr 30 21:05:24 2020 +0200

    Retrofit HyperbolicCassiniSoldner into CassiniSoldner. It reduces code duplication
    and may be more flexible if we want to implement the Jacobian matrix of hyperbolic
    case in a future version. It also reduces the visibility of hyperbolic case, which
    is desired because it is a very specific case.  The cost on normal Cassini-Soldner
    case is hopefully light (a single `if` statement) and may be acceptable since this
    projection exists mostly for historical purposes.
---
 .../referencing/provider/CassiniSoldner.java       |  11 +-
 .../provider/HyperbolicCassiniSoldner.java         |  31 ++--
 .../operation/projection/CassiniSoldner.java       | 129 +++++++++++++-
 .../projection/HyperbolicCassiniSoldner.java       | 196 ---------------------
 .../operation/projection/CassiniSoldnerTest.java   |  66 ++++++-
 .../projection/HyperbolicCassiniSoldnerTest.java   |  93 ----------
 .../sis/test/suite/ReferencingTestSuite.java       |   1 -
 7 files changed, 199 insertions(+), 328 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CassiniSoldner.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CassiniSoldner.java
index ebcca91..262f91b 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CassiniSoldner.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/CassiniSoldner.java
@@ -37,7 +37,7 @@ import org.apache.sis.parameter.Parameters;
  * @module
  */
 @XmlTransient
-public final class CassiniSoldner extends MapProjection {
+public class CassiniSoldner extends MapProjection {
     /**
      * For cross-version compatibility.
      */
@@ -158,12 +158,19 @@ public final class CassiniSoldner extends MapProjection {
     }
 
     /**
+     * Constructs a provider from a set of parameters.
+     */
+    CassiniSoldner(final ParameterDescriptorGroup parameters) {
+        super(parameters);
+    }
+
+    /**
      * {@inheritDoc}
      *
      * @return the map projection created from the given parameter values.
      */
     @Override
-    protected NormalizedProjection createProjection(final Parameters parameters) {
+    protected final NormalizedProjection createProjection(final Parameters parameters) {
         return new org.apache.sis.referencing.operation.projection.CassiniSoldner(this, parameters);
     }
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/HyperbolicCassiniSoldner.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/HyperbolicCassiniSoldner.java
index 51f569a..72ed7f9 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/HyperbolicCassiniSoldner.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/HyperbolicCassiniSoldner.java
@@ -18,8 +18,6 @@ package org.apache.sis.internal.referencing.provider;
 
 import javax.xml.bind.annotation.XmlTransient;
 import org.opengis.parameter.ParameterDescriptorGroup;
-import org.apache.sis.parameter.Parameters;
-import org.apache.sis.referencing.operation.projection.NormalizedProjection;
 
 
 /**
@@ -32,26 +30,31 @@ import org.apache.sis.referencing.operation.projection.NormalizedProjection;
  * @module
  */
 @XmlTransient
-public final class HyperbolicCassiniSoldner extends MapProjection {
+public final class HyperbolicCassiniSoldner extends CassiniSoldner {
     /**
      * For cross-version compatibility.
      */
     private static final long serialVersionUID = -4948656632564407426L;
 
     /**
+     * The EPSG identifier, to be preferred to the name when available.
+     */
+    public static final String IDENTIFIER = "9833";
+
+    /**
      * The group of all parameters expected by this coordinate operation.
      */
     private static final ParameterDescriptorGroup PARAMETERS;
     static {
         PARAMETERS = builder()
-                .addIdentifier("9833")
+                .addIdentifier(IDENTIFIER)
                 .addName("Hyperbolic Cassini-Soldner")
                 .createGroupForMapProjection(
-                        CassiniSoldner.LATITUDE_OF_ORIGIN,
-                        CassiniSoldner.LONGITUDE_OF_ORIGIN,
-                        CassiniSoldner.SCALE_FACTOR,
-                        CassiniSoldner.FALSE_EASTING,
-                        CassiniSoldner.FALSE_NORTHING);
+                        LATITUDE_OF_ORIGIN,
+                        LONGITUDE_OF_ORIGIN,
+                        SCALE_FACTOR,
+                        FALSE_EASTING,
+                        FALSE_NORTHING);
     }
 
     /**
@@ -60,14 +63,4 @@ public final class HyperbolicCassiniSoldner extends MapProjection {
     public HyperbolicCassiniSoldner() {
         super(PARAMETERS);
     }
-
-    /**
-     * {@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.HyperbolicCassiniSoldner(this,
parameters);
-    }
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CassiniSoldner.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CassiniSoldner.java
index fb17311..6bed850 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CassiniSoldner.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CassiniSoldner.java
@@ -26,7 +26,10 @@ import org.opengis.referencing.operation.OperationMethod;
 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.internal.referencing.provider.HyperbolicCassiniSoldner;
+import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.parameter.Parameters;
+import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.Workaround;
 
 import static java.lang.Math.*;
@@ -34,7 +37,7 @@ import static org.apache.sis.internal.referencing.provider.CassiniSoldner.*;
 
 
 /**
- * <cite>Cassini-Soldner</cite> projection (EPSG code 9806).
+ * <cite>Cassini-Soldner</cite> projection (EPSG codes 9806 and 9833).
  * See the following references for an overview:
  * <ul>
  *   <li><a href="https://en.wikipedia.org/wiki/Cassini_projection">Cassini projection
on Wikipedia</a></li>
@@ -54,7 +57,53 @@ public class CassiniSoldner extends MeridianArcBased {
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = 3007155786839466950L;
+    private static final long serialVersionUID = 8306467537772990906L;
+
+    /**
+     * The hyperbolic variants of this projection. {@link #VANUA} is the special case
+     * of <cite>Vanua Levu Grid</cite>, which is the only hyperbolic variant
for which
+     * inverse projection is supported.
+     *
+     * @see #variant
+     */
+    private static final byte HYPERBOLIC = 1, VANUA = 2;
+
+    /**
+     * The latitude of {@link #VANUA} variant (16°15′S) in radians.
+     */
+    private static final double VANUA_LATITUDE = -(16 + 15./60) * (PI/180);
+
+    /**
+     * The type of Cassini-Soldner projection. Possible values are:
+     *
+     * <ul>
+     *   <li>{@link #STANDARD_VARIANT} if this projection is the standard variant.</li>
+     *   <li>{@link #HYPERBOLIC} if this projection is the "Hyperbolic Cassini-Soldner"
case.</li>
+     *   <li>{@link #VANUA} if this projection is the "Hyperbolic Cassini-Soldner"
case at φ₀=16°15′S.</li>
+     * </ul>
+     *
+     * Other cases may be added in the future.
+     */
+    private final byte variant;
+
+    /**
+     * Meridional distance <var>M</var> from equator to latitude of origin φ₀.
+     * This parameter is explicit only for the hyperbolic variants. The standard variant
does not need it
+     * because {@link #M0} is subtracted in the {@linkplain ContextualParameters.MatrixRole#DENORMALIZATION
+     * denormalization matrix}.
+     */
+    private final double M0;
+
+    /**
+     * Returns the variant of the projection based on the name and identifier of the given
operation method.
+     * See {@link #variant} for the list of possible values.
+     */
+    private static byte getVariant(final OperationMethod method) {
+        if (identMatch(method, "(?i).*\\bHyperbolic\\b.*", HyperbolicCassiniSoldner.IDENTIFIER))
{
+            return HYPERBOLIC;
+        }
+        return STANDARD_VARIANT;
+    }
 
     /**
      * Creates a Cassini-Soldner projection from the given parameters.
@@ -62,13 +111,14 @@ public class CassiniSoldner extends MeridianArcBased {
      *
      * <ul>
      *   <li><cite>"Cassini-Soldner"</cite>.</li>
+     *   <li><cite>"Hyperbolic Cassini-Soldner"</cite>.</li>
      * </ul>
      *
      * @param  method      description of the projection parameters.
      * @param  parameters  the parameter values of the projection to create.
      */
     public CassiniSoldner(final OperationMethod method, final Parameters parameters) {
-        this(initializer(method, parameters, STANDARD_VARIANT));
+        this(initializer(method, parameters));
     }
 
     /**
@@ -76,13 +126,13 @@ public class CassiniSoldner extends MeridianArcBased {
      * ("Relax constraint on placement of this()/super() call in constructors").
      */
     @Workaround(library="JDK", version="1.7")
-    static Initializer initializer(final OperationMethod method, final Parameters parameters,
final byte variant) {
+    private static Initializer initializer(final OperationMethod method, final Parameters
parameters) {
         final EnumMap<ParameterRole, ParameterDescriptor<Double>> roles = new
EnumMap<>(ParameterRole.class);
         roles.put(ParameterRole.CENTRAL_MERIDIAN, LONGITUDE_OF_ORIGIN);
         roles.put(ParameterRole.SCALE_FACTOR,     SCALE_FACTOR);
         roles.put(ParameterRole.FALSE_EASTING,    FALSE_EASTING);
         roles.put(ParameterRole.FALSE_NORTHING,   FALSE_NORTHING);
-        return new Initializer(method, parameters, roles, variant);
+        return new Initializer(method, parameters, roles, getVariant(method));
     }
 
     /**
@@ -90,11 +140,16 @@ public class CassiniSoldner extends MeridianArcBased {
      */
     CassiniSoldner(final Initializer initializer) {
         super(initializer);
+        final double φ0 = toRadians(initializer.getAndStore(LATITUDE_OF_ORIGIN));
+        M0 = distance(φ0, sin(φ0), cos(φ0));
         if (initializer.variant == STANDARD_VARIANT) {
-            final double φ0 = toRadians(initializer.getAndStore(LATITUDE_OF_ORIGIN));
             final MatrixSIS denormalize = getContextualParameters().getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
             denormalize.convertBefore(1, null, -distance(φ0, sin(φ0), cos(φ0)));
+        } else if (abs(φ0 - VANUA_LATITUDE) <= ANGULAR_TOLERANCE) {
+            variant = VANUA;
+            return;
         }
+        variant = initializer.variant;
     }
 
     /**
@@ -102,6 +157,34 @@ public class CassiniSoldner extends MeridianArcBased {
      */
     CassiniSoldner(final CassiniSoldner other) {
         super(other);
+        this.variant = other.variant;
+        this.M0      = other.M0;
+    }
+
+    /**
+     * Returns the names of additional internal parameters which need to be taken in account
when
+     * comparing two {@code CassiniSoldner} projections or formatting them in debug mode.
+     */
+    @Override
+    final String[] getInternalParameterNames() {
+        if (variant != STANDARD_VARIANT) {
+            return new String[] {"M₀"};
+        } else {
+            return super.getInternalParameterNames();
+        }
+    }
+
+    /**
+     * Returns the values of additional internal parameters which need to be taken in account
when
+     * comparing two {@code CassiniSoldner} projections or formatting them in debug mode.
+     */
+    @Override
+    final double[] getInternalParameterValues() {
+        if (variant != STANDARD_VARIANT) {
+            return new double[] {M0};
+        } else {
+            return super.getInternalParameterValues();
+        }
     }
 
     /**
@@ -118,7 +201,7 @@ public class CassiniSoldner extends MeridianArcBased {
     @Override
     public MathTransform createMapProjection(final MathTransformFactory factory) throws FactoryException
{
         CassiniSoldner kernel = this;
-        if (eccentricity == 0) {
+        if (eccentricity == 0 && variant == STANDARD_VARIANT) {
             kernel = new Spherical(this);
         }
         return context.completeTransform(factory, kernel);
@@ -173,10 +256,21 @@ public class CassiniSoldner extends MeridianArcBased {
         if (dstPts != null) {
             dstPts[dstOff  ] = (A - T*A3/6 + Q*T*(A3*A2) / 120) / rν;
             dstPts[dstOff+1] = distance(φ, sinφ, cosφ) + tanφ*A2*(0.5 + S/4) / rν;
+            if (variant != STANDARD_VARIANT) {
+                /*
+                 * Offset: X³ / (6ρν)    where    ρ = (1 – ℯ²)⋅ν³
+                 *       = X³ / (6⋅(1 – ℯ²)⋅ν⁴)
+                 */
+                final double X = dstPts[dstOff+1] - M0;
+                dstPts[dstOff+1] = X - (X*X*X) / (6*(1 - eccentricitySquared) / (rν2*rν2));
+            }
         }
         if (!derivate) {
             return null;
         }
+        if (variant != STANDARD_VARIANT) {
+            throw new ProjectionException(Resources.format(Resources.Keys.CanNotComputeDerivative));
+        }
         /*
          * Following formulas have been derived with WxMaxima, then simplified by hand.
          * See: https://svn.apache.org/repos/asf/sis/analysis/README.html
@@ -210,8 +304,25 @@ public class CassiniSoldner extends MeridianArcBased {
                                     final double[] dstPts, final int dstOff)
             throws ProjectionException
     {
-        final double x     = srcPts[srcOff  ];
-        final double y     = srcPts[srcOff+1];
+        double x = srcPts[srcOff  ];
+        double y = srcPts[srcOff+1];
+        if (variant != STANDARD_VARIANT) {
+            if (variant != VANUA) {
+                throw new ProjectionException(Errors.format(Errors.Keys.UnsupportedArgumentValue_1,
"φ₀≠16°15′S"));
+            }
+            /*
+             * Following symbols are related to EPSG symbols with indices and primes dropped,
and some multiplication
+             * factors inserted or implicit. EPSG said that this formula is specifically
for the Fiji Vanua Levu grid.
+             * I presume that the specificity is in the 315320 constant, which needs to be
divided by semi-major axis
+             * length in our implementation. We take the axis length of Fiji Vanua Levu grid
on the assumption that it
+             * is the axis length used for computing the 315320 constant.
+             */
+            final double sinφ = sin(VANUA_LATITUDE + y * (317063.667 / 315320));    // Constants
are: a / (value from EPSG)
+            final double rν2  = 1 - eccentricitySquared*(sinφ*sinφ);                //
= 1/√ν₁′       (ν₁′ defined by EPSG)
+            final double ρν   = 6*(1 - eccentricitySquared)/(rν2 * rν2);            //
= 6⋅ρ₁′ν₁′/a²  (ρ₁′ defined by EPSG)
+            final double q    = y + y*y*y / ρν;                                     //
= (N + q′)/a   (q′  defined by EPSG)
+            y += q*q*q / ρν + M0;
+        }
         final double φ1    = latitude(y);
         final double sinφ1 = sin(φ1);
         final double cosφ1 = cos(φ1);
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/HyperbolicCassiniSoldner.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/HyperbolicCassiniSoldner.java
deleted file mode 100644
index 01d4437..0000000
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/HyperbolicCassiniSoldner.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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.MathTransform;
-import org.opengis.referencing.operation.MathTransformFactory;
-import org.opengis.referencing.operation.Matrix;
-import org.opengis.referencing.operation.OperationMethod;
-import org.apache.sis.referencing.operation.transform.ContextualParameters;
-import org.apache.sis.parameter.Parameters;
-import org.apache.sis.util.resources.Errors;
-
-import static java.lang.Math.*;
-import static org.apache.sis.internal.referencing.provider.CassiniSoldner.LATITUDE_OF_ORIGIN;
-
-
-/**
- * <cite>Hyperbolic Cassini-Soldner</cite> projection (EPSG code 9833).
- *
- * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
- * @since   1.1
- * @module
- */
-public class HyperbolicCassiniSoldner extends CassiniSoldner {
-    /**
-     * For cross-version compatibility.
-     */
-    private static final long serialVersionUID = -4360637175311262375L;
-
-    /**
-     * The hyperbolic variants of this projection. {@link #VANUA} is the special case
-     * of <cite>Vanua Levu Grid</cite>, which is the only hyperbolic variant
for which
-     * inverse projection is supported.
-     *
-     * @see #variant
-     */
-    private static final byte HYPERBOLIC = 1, VANUA = 2;
-
-    /**
-     * The latitude of {@link #VANUA} variant (16°15′S) in radians.
-     */
-    private static final double VANUA_LATITUDE = -(16 + 15./60) * (PI/180);
-
-    /**
-     * The type of Cassini-Soldner projection. Possible values are:
-     *
-     * <ul>
-     *   <li>{@link #STANDARD_VARIANT} if this projection is the standard variant (handled
by parent class).</li>
-     *   <li>{@link #HYPERBOLIC} if this projection is the "Hyperbolic Cassini-Soldner"
case.</li>
-     *   <li>{@link #VANUA} if this projection is the "Hyperbolic Cassini-Soldner"
case at φ₀=16°15′S.</li>
-     * </ul>
-     *
-     * Other cases may be added in the future.
-     */
-    private final byte variant;
-
-    /**
-     * Meridional distance <var>M</var> from equator to latitude of origin φ₀.
-     * This parameter is explicit only for the hyperbolic variants. The standard variant
does not need it
-     * because {@link #M0} is subtracted in the {@linkplain ContextualParameters.MatrixRole#DENORMALIZATION
-     * denormalization matrix}.
-     */
-    private final double M0;
-
-    /**
-     * Creates a Hyperbolic Cassini-Soldner projection from the given parameters.
-     * The {@code method} argument can be the description of one of the following:
-     *
-     * <ul>
-     *   <li><cite>"Hyperbolic Cassini-Soldner"</cite>.</li>
-     * </ul>
-     *
-     * @param  method      description of the projection parameters.
-     * @param  parameters  the parameter values of the projection to create.
-     */
-    public HyperbolicCassiniSoldner(final OperationMethod method, final Parameters parameters)
{
-        this(initializer(method, parameters, HYPERBOLIC));
-    }
-
-    /**
-     * Creates a new Cassini-Soldner projection from the given initializer.
-     */
-    HyperbolicCassiniSoldner(final Initializer initializer) {
-        super(initializer);
-        final double φ0 = toRadians(initializer.getAndStore(LATITUDE_OF_ORIGIN));
-        variant = (abs(φ0 - VANUA_LATITUDE) <= ANGULAR_TOLERANCE) ? VANUA : initializer.variant;
-        M0 = distance(φ0, sin(φ0), cos(φ0));
-    }
-
-    /**
-     * Returns the names of additional internal parameters which need to be taken in account
when
-     * comparing two {@code CassiniSoldner} projections or formatting them in debug mode.
-     */
-    @Override
-    final String[] getInternalParameterNames() {
-        return new String[] {"M₀"};
-    }
-
-    /**
-     * Returns the values of additional internal parameters which need to be taken in account
when
-     * comparing two {@code CassiniSoldner} projections or formatting them in debug mode.
-     */
-    @Override
-    final double[] getInternalParameterValues() {
-        return new double[] {M0};
-    }
-
-    /**
-     * 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>.
-     *
-     * @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
{
-        return context.completeTransform(factory, this);
-    }
-
-    /**
-     * Converts the specified (λ,φ) coordinate (units in radians) and stores the result
in {@code dstPts}.
-     *
-     * @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
-    {
-        if (dstPts != null) {
-            final double sinφ = sin(srcPts[srcOff+1]);              // Save before to invoke
super.transform(…).
-            super.transform(srcPts, srcOff, dstPts, dstOff, false);
-            final double X    = dstPts[dstOff+1] - M0;
-            final double rν2  = 1 - (sinφ*sinφ)*eccentricitySquared;
-            /*
-             * Offset: X³ / (6ρν)    where    ρ = (1 – ℯ²)⋅ν³
-             *       = X³ / (6⋅(1 – ℯ²)⋅ν⁴)
-             */
-            dstPts[dstOff+1] = X - (X*X*X) / (6*(1 - eccentricitySquared) / (rν2*rν2));
-        }
-        return null;        // // Derivative not supported for this operation method.
-    }
-
-    /**
-     * 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
-    {
-        if (variant != VANUA) {
-            throw new ProjectionException(Errors.format(Errors.Keys.UnsupportedArgumentValue_1,
"φ₀≠16°15′S"));
-        }
-        /*
-         * Following symbols are related to EPSG symbols with indices and primes dropped,
and some multiplication
-         * factors inserted or implicit. EPSG said that this formula is specifically for
the Fiji Vanua Levu grid.
-         * I presume that the specificity is in the 315320 constant, which needs to be divided
by semi-major axis
-         * length in our implementation. We take the axis length of Fiji Vanua Levu grid
on the assumption that it
-         * is the axis length used for computing the 315320 constant.
-         */
-        double x = srcPts[srcOff  ];
-        double y = srcPts[srcOff+1];                                            // = N -
FN    (N & FN defined by EPSG)
-        final double sinφ = sin(VANUA_LATITUDE + y * (317063.667 / 315320));    // Constants
are: a / (value from EPSG)
-        final double rν2  = 1 - eccentricitySquared*(sinφ*sinφ);                // = 1/√ν₁′
      (ν₁′ defined by EPSG)
-        final double ρν   = 6*(1 - eccentricitySquared)/(rν2 * rν2);            // =
6⋅ρ₁′ν₁′/a²  (ρ₁′ defined by EPSG)
-        final double q    = y + y*y*y / ρν;                                     // = (N
+ q′)/a   (q′  defined by EPSG)
-        y += q*q*q / ρν;
-        dstPts[dstOff  ] = x;
-        dstPts[dstOff+1] = y + M0;
-        super.inverseTransform(dstPts, dstOff, dstPts, dstOff);
-    }
-}
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CassiniSoldnerTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CassiniSoldnerTest.java
index a877ce2..4c3e600 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CassiniSoldnerTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CassiniSoldnerTest.java
@@ -41,9 +41,12 @@ import static java.lang.StrictMath.*;
 public final strictfp class CassiniSoldnerTest extends MapProjectionTestCase {
     /**
      * Returns the operation method for the projection tested in this class.
+     *
+     * @param  hyperbolic  {@code false} for standard case, or {@code true} for hyperbolic
case.
      */
-    private static MapProjection method() {
-        return new org.apache.sis.internal.referencing.provider.CassiniSoldner();
+    private static MapProjection method(final boolean hyperbolic) {
+        return hyperbolic ? new org.apache.sis.internal.referencing.provider.HyperbolicCassiniSoldner()
+                          : new org.apache.sis.internal.referencing.provider.CassiniSoldner();
     }
 
     /**
@@ -54,7 +57,7 @@ public final strictfp class CassiniSoldnerTest extends MapProjectionTestCase
{
      * @return newly created projection.
      */
     private static CassiniSoldner create(final boolean ellipse) {
-        final MapProjection provider = method();
+        final MapProjection provider = method(false);
         final Parameters pg = Parameters.castOrWrap(provider.getParameters().createValue());
         if (ellipse) {
             pg.parameter("semi-major").setValue(WGS84_A);
@@ -93,15 +96,15 @@ public final strictfp class CassiniSoldnerTest extends MapProjectionTestCase
{
     }
 
     /**
-     * Tests the point given in EPSG example. This is the same test than {@link #runGeoapiTest()}
-     * but is repeated here for easier debugging.
+     * Tests the point given in EPSG example for the usual "Cassini-Soldner" projection.
+     * This is the same test than {@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 testExampleEPSG() throws FactoryException, TransformException {
-        createCompleteProjection(method(),
+    public void testClassic() throws FactoryException, TransformException {
+        createCompleteProjection(method(false),
                 31706587.88,                            // Semi-major axis (Clarke's links)
                 31706587.88 * (20855233./20926348),     // Semi-minor axis (Clarke's links)
                 -(61 + 20./60),                         // Longitude of natural origin
@@ -125,6 +128,39 @@ public final strictfp class CassiniSoldnerTest extends MapProjectionTestCase
{
     }
 
     /**
+     * Tests the point given in EPSG example for the "Hyperbolic Cassini-Soldner" projection.
+     * This is the same test than {@link #runGeoapiHyperbolicTest()} 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 testHyperbolic() throws FactoryException, TransformException {
+        createCompleteProjection(method(true),
+                317063.667,                             // Semi-major axis (chains)
+                317063.667 * (20854895./20926202),      // Semi-minor axis (chains)
+                179 + 20./60,                           // Longitude of natural origin
+                -(16 + 15./60),                         // Latitude of natural origin
+                NaN,                                    // Standard parallel 1 (none)
+                NaN,                                    // Standard parallel 2 (none)
+                NaN,                                    // Scale factor (none)
+                12513.318,                              // False easting  (chains)
+                16628.885);                             // False northing (chains)
+
+        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(λ, φ);
+        assertSame(p, transform.transform(p, p));
+        assertEquals(16015.2890, p.x, 0.00005);
+        assertEquals(13369.6601, p.y, 0.00005);
+
+        assertSame(p, transform.inverse().transform(p, p));
+        assertEquals(λ, p.x, Formulas.ANGULAR_TOLERANCE);
+        assertEquals(φ, p.y, Formulas.ANGULAR_TOLERANCE);
+    }
+
+    /**
      * Creates a projection and tests the derivatives at a few points.
      *
      * @throws TransformException if an error occurred while projecting a coordinate.
@@ -160,6 +196,20 @@ public final strictfp class CassiniSoldnerTest extends MapProjectionTestCase
{
      */
     @Test
     public void runGeoapiTest() throws FactoryException, TransformException {
-        createGeoApiTest(method()).testCassiniSoldner();
+        createGeoApiTest(method(false)).testCassiniSoldner();
+    }
+
+    /**
+     * Tests the <cite>"Hyperbolic Cassini-Soldner"</cite> (EPSG:9833) projection
method.
+     * 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#testHyperbolicCassiniSoldner()
+     */
+    @Test
+    public void runGeoapiHyperbolicTest() throws FactoryException, TransformException {
+        createGeoApiTestNoDerivatives(method(true)).testHyperbolicCassiniSoldner();
     }
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/HyperbolicCassiniSoldnerTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/HyperbolicCassiniSoldnerTest.java
deleted file mode 100644
index c613a8a..0000000
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/HyperbolicCassiniSoldnerTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.internal.referencing.provider.MapProjection;
-import org.apache.sis.internal.referencing.Formulas;
-import org.apache.sis.geometry.DirectPosition2D;
-import org.apache.sis.test.DependsOn;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-import static java.lang.Double.NaN;
-
-
-/**
- * Tests the {@link HyperbolicCassiniSoldner} class.
- *
- * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
- * @since   1.1
- * @module
- */
-@DependsOn(CassiniSoldnerTest.class)
-public final strictfp class HyperbolicCassiniSoldnerTest extends MapProjectionTestCase {
-    /**
-     * Returns the operation method for the projection tested in this class.
-     */
-    private static MapProjection method() {
-        return new org.apache.sis.internal.referencing.provider.HyperbolicCassiniSoldner();
-    }
-
-    /**
-     * Tests the point given in EPSG example for the standard case.
-     * This is the same test than {@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 testExampleEPSG() throws FactoryException, TransformException {
-        createCompleteProjection(method(),
-                317063.667,                             // Semi-major axis (chains)
-                317063.667 * (20854895./20926202),      // Semi-minor axis (chains)
-                179 + 20./60,                           // Longitude of natural origin
-                -(16 + 15./60),                         // Latitude of natural origin
-                NaN,                                    // Standard parallel 1 (none)
-                NaN,                                    // Standard parallel 2 (none)
-                NaN,                                    // Scale factor (none)
-                12513.318,                              // False easting  (chains)
-                16628.885);                             // False northing (chains)
-
-        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(λ, φ);
-        assertSame(p, transform.transform(p, p));
-        assertEquals(16015.2890, p.x, 0.00005);
-        assertEquals(13369.6601, p.y, 0.00005);
-
-        assertSame(p, transform.inverse().transform(p, p));
-        assertEquals(λ, p.x, Formulas.ANGULAR_TOLERANCE);
-        assertEquals(φ, p.y, Formulas.ANGULAR_TOLERANCE);
-    }
-
-    /**
-     * Tests the <cite>"Hyperbolic Cassini-Soldner"</cite> (EPSG:9833) projection
method.
-     * 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#testHyperbolicCassiniSoldner()
-     */
-    @Test
-    public void runGeoapiTest() throws FactoryException, TransformException {
-        createGeoApiTestNoDerivatives(method()).testHyperbolicCassiniSoldner();
-    }
-}
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
index d832834..eb5fe25 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
@@ -182,7 +182,6 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.projection.TransverseMercatorTest.class,
     org.apache.sis.referencing.operation.projection.ZonedGridSystemTest.class,
     org.apache.sis.referencing.operation.projection.CassiniSoldnerTest.class,
-    org.apache.sis.referencing.operation.projection.HyperbolicCassiniSoldnerTest.class,
     org.apache.sis.referencing.operation.projection.PolarStereographicTest.class,
     org.apache.sis.referencing.operation.projection.ObliqueStereographicTest.class,
     org.apache.sis.referencing.operation.projection.ObliqueMercatorTest.class,


Mime
View raw message