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 586f44a6d9 Add "Geographic/topocentric conversions" (EPSG:9837).
586f44a6d9 is described below

commit 586f44a6d9960efb722eb4cd253c3299be4a7dea
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon Aug 8 22:00:09 2022 +0200

    Add "Geographic/topocentric conversions" (EPSG:9837).
    
    https://issues.apache.org/jira/browse/SIS-259
---
 .../provider/GeocentricToTopocentric.java          |  97 ++++++++++----
 .../provider/GeographicToTopocentric.java          | 142 +++++++++++++++++++++
 ...g.opengis.referencing.operation.OperationMethod |   1 +
 .../referencing/provider/ProvidersTest.java        |   1 +
 .../provider/TopocentricConversionMock.java        |  63 ---------
 .../sis/test/integration/ConsistencyTest.java      |   1 +
 ...g.opengis.referencing.operation.OperationMethod |   1 -
 7 files changed, 214 insertions(+), 92 deletions(-)

diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToTopocentric.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToTopocentric.java
index 87e6da2f1f..e6ede8a086 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToTopocentric.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToTopocentric.java
@@ -36,14 +36,15 @@ import org.apache.sis.internal.util.Constants;
 
 import static java.lang.Math.cos;
 import static java.lang.Math.sin;
+import static java.lang.Math.toRadians;
 
 
 /**
- * The provider for the <cite>"Geocentric/topocentric conversions"</cite> 
(EPSG:9836)).
+ * The provider for the <cite>"Geocentric/topocentric conversions"</cite> 
(EPSG:9836).
  * This operation is implemented using existing {@link MathTransform} 
implementations;
  * there is no need for a class specifically for this transform.
  *
- * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
  * @version 1.3
  * @since   1.3
  * @module
@@ -121,7 +122,7 @@ public final class GeocentricToTopocentric extends 
AbstractProvider {
     }
 
     /**
-     * Notifies {@code DefaultMathTransformFactory} that 
Geographic/topocentric conversions
+     * Notifies {@code DefaultMathTransformFactory} that 
Geocentric/topocentric conversions
      * require values for the {@code "semi_major"} and {@code "semi_minor"} 
parameters.
      *
      * @return 1, meaning that the operation requires a source ellipsoid.
@@ -144,44 +145,84 @@ public final class GeocentricToTopocentric extends 
AbstractProvider {
     public MathTransform createMathTransform(final MathTransformFactory 
factory, final ParameterValueGroup values)
             throws FactoryException
     {
-        return create(factory, Parameters.castOrWrap(values));
+        try {
+            return create(factory, Parameters.castOrWrap(values), false);
+        } catch (TransformException e) {
+            throw new FactoryException(e);
+        }
     }
 
     /**
      * Implementation of {@link #createMathTransform(MathTransformFactory, 
ParameterValueGroup)}
      * shared with {@link GeographicToTopocentric}.
+     *
+     * @param  factory     the factory to use for creating the transform.
+     * @param  values      the parameter values that define the transform to 
create.
+     * @param  geographic  {@code true} if the source coordinates are 
geographic, or
+     *                     {@code false} if the source coordinates are 
geocentric.
      */
-    static MathTransform create(final MathTransformFactory factory, final 
Parameters values)
-            throws FactoryException
+    static MathTransform create(final MathTransformFactory factory, final 
Parameters values, final boolean geographic)
+            throws FactoryException, TransformException
     {
         final ParameterValue<?> ap = values.parameter(Constants.SEMI_MAJOR);
         final Unit<Length> unit = ap.getUnit().asType(Length.class);
         final double a = ap.doubleValue();
         final double b = 
values.parameter(Constants.SEMI_MINOR).doubleValue(unit);
-        final double x = values.doubleValue(ORIGIN_X, unit);
-        final double y = values.doubleValue(ORIGIN_Y, unit);
-        final double z = values.doubleValue(ORIGIN_Z, unit);
-        final double[] coordinates = new double[] {x/a, y/a, z/a};
-        final MathTransform t = new EllipsoidToCentricTransform(a, b, unit, 
false, EllipsoidToCentricTransform.TargetType.CARTESIAN);
-        try {
-            t.inverse().transform(coordinates, 0, coordinates, 0, 1);
-            final double λ    = coordinates[0];         // In radians.
-            final double φ    = coordinates[1];
-            final double sinλ = sin(λ);
-            final double cosλ = cos(λ);
-            final double sinφ = sin(φ);
-            final double cosφ = cos(φ);
+        final double x, y, z, λ, φ;
+        final MathTransform toGeocentric;
+        if (geographic) {
             /*
-             * Following transform uses the inverse of the matrix R given in 
EPSG guidance note
-             * because it allows us to put the (x,y,z) translation terms 
directly in the matrix.
+             * Full conversion from (longitude, latitude, height) in degrees
+             * to geocentric coordinates in linear units (usually metres).
              */
-            return factory.createAffineTransform(new Matrix4(
-                    -sinλ,  -sinφ*cosλ,  cosφ*cosλ,  x,
-                     cosλ,  -sinφ*sinλ,  cosφ*sinλ,  y,
-                        0,   cosφ,       sinφ,       z,
-                        0,   0,          0,          1)).inverse();
-        } catch (TransformException e) {
-            throw new FactoryException(e);
+            toGeocentric = 
EllipsoidToCentricTransform.createGeodeticConversion(factory,
+                    a, b, unit, true, 
EllipsoidToCentricTransform.TargetType.CARTESIAN);
+
+            final double[] origin = new double[] {
+                values.doubleValue(GeographicToTopocentric.ORIGIN_X),
+                values.doubleValue(GeographicToTopocentric.ORIGIN_Y),
+                values.doubleValue(GeographicToTopocentric.ORIGIN_Z)};
+
+            λ = toRadians(origin[0]);
+            φ = toRadians(origin[1]);
+            toGeocentric.transform(origin, 0, origin, 0, 1);
+            x = origin[0];
+            y = origin[1];
+            z = origin[2];
+        } else {
+            /*
+             * Shorter conversion from (longitude, latitude) in radians to
+             * geocentric coordinates as fractions of semi-major axis length.
+             * This conversion is used only in this block and is not kept.
+             */
+            toGeocentric = new EllipsoidToCentricTransform(
+                    a, b, unit, false, 
EllipsoidToCentricTransform.TargetType.CARTESIAN);
+
+            final double[] origin = new double[] {
+                (x = values.doubleValue(ORIGIN_X, unit)) / a,
+                (y = values.doubleValue(ORIGIN_Y, unit)) / a,
+                (z = values.doubleValue(ORIGIN_Z, unit)) / a};
+
+            toGeocentric.inverse().transform(origin, 0, origin, 0, 1);
+            λ = origin[0];         // Already in radians.
+            φ = origin[1];
+        }
+        final double sinλ = sin(λ);
+        final double cosλ = cos(λ);
+        final double sinφ = sin(φ);
+        final double cosφ = cos(φ);
+        /*
+         * Following transform uses the inverse of the matrix R given in EPSG 
guidance note
+         * because it allows us to put the (x,y,z) translation terms directly 
in the matrix.
+         */
+        MathTransform mt = factory.createAffineTransform(new Matrix4(
+                -sinλ,  -sinφ*cosλ,  cosφ*cosλ,  x,
+                 cosλ,  -sinφ*sinλ,  cosφ*sinλ,  y,
+                    0,   cosφ,       sinφ,       z,
+                    0,   0,          0,          1)).inverse();
+        if (geographic) {
+            mt = factory.createConcatenatedTransform(toGeocentric, mt);
         }
+        return mt;
     }
 }
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToTopocentric.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToTopocentric.java
new file mode 100644
index 0000000000..f247f195c8
--- /dev/null
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToTopocentric.java
@@ -0,0 +1,142 @@
+/*
+ * 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 org.opengis.util.FactoryException;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.referencing.operation.Conversion;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.measure.Units;
+import org.apache.sis.parameter.Parameters;
+
+
+/**
+ * The provider for the <cite>"Geographic/topocentric conversions"</cite> 
(EPSG:9837).
+ * This operation is implemented using existing {@link MathTransform} 
implementations;
+ * there is no need for a class specifically for this transform.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.3
+ * @since   1.3
+ * @module
+ */
+public class GeographicToTopocentric extends AbstractProvider {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -3829993731324133815L;
+
+    /**
+     * The operation parameter descriptor for the <cite>Longitude of 
topocentric origin</cite> parameter value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     */
+    static final ParameterDescriptor<Double> ORIGIN_X;
+
+    /**
+     * The operation parameter descriptor for the <cite>Latitude of 
topocentric origin</cite> parameter value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     */
+    static final ParameterDescriptor<Double> ORIGIN_Y;
+
+    /**
+     * The operation parameter descriptor for the <cite>Ellipsoidal height of 
topocentric origin</cite> parameter value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     */
+    static final ParameterDescriptor<Double> ORIGIN_Z;
+
+    /**
+     * The group of all parameters expected by this coordinate operation.
+     */
+    private static final ParameterDescriptorGroup PARAMETERS;
+    static {
+        final ParameterBuilder builder = builder();
+        ORIGIN_X = createLongitude(builder
+                .addIdentifier("8835")
+                .addName("Longitude of topocentric origin"));
+
+        ORIGIN_Y = createLatitude(builder
+                .addIdentifier("8834")
+                .addName("Latitude of topocentric origin"), true);
+
+        ORIGIN_Z = builder
+                .addIdentifier("8836")
+                .addName("Ellipsoidal height of topocentric origin")
+                .create(0, Units.METRE);
+
+        PARAMETERS = builder
+                .addIdentifier("9837")
+                .addName("Geographic/topocentric conversions")
+                .createGroupForMapProjection(ORIGIN_Y, ORIGIN_X, ORIGIN_Z);
+                // Not really a map projection, but we leverage the same axis 
parameters.
+    }
+
+    /**
+     * Constructs a provider for the 3-dimensional case.
+     */
+    public GeographicToTopocentric() {
+        super(3, 3, PARAMETERS);
+    }
+
+    /**
+     * Returns the operation type.
+     *
+     * @return {@code Conversion.class}.
+     */
+    @Override
+    public Class<Conversion> getOperationType() {
+        return Conversion.class;
+    }
+
+    /**
+     * Notifies {@code DefaultMathTransformFactory} that 
Geographic/topocentric conversions
+     * require values for the {@code "semi_major"} and {@code "semi_minor"} 
parameters.
+     *
+     * @return 1, meaning that the operation requires a source ellipsoid.
+     */
+    @Override
+    public int getEllipsoidsMask() {
+        return 1;
+    }
+
+    /**
+     * Creates a transform from the specified group of parameter values.
+     * The unit of measurement of input coordinates will be the units of the 
ellipsoid axes.
+     *
+     * @param  factory  the factory to use for creating the transform.
+     * @param  values   the parameter values that define the transform to 
create.
+     * @return the conversion from geographic to topocentric coordinates.
+     * @throws FactoryException if an error occurred while creating a 
transform.
+     */
+    @Override
+    public MathTransform createMathTransform(final MathTransformFactory 
factory, final ParameterValueGroup values)
+            throws FactoryException
+    {
+        try {
+            return GeocentricToTopocentric.create(factory, 
Parameters.castOrWrap(values), true);
+        } catch (TransformException e) {
+            throw new FactoryException(e);
+        }
+    }
+}
diff --git 
a/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
 
b/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
index 7c942a8f7c..4c04e55057 100644
--- 
a/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
+++ 
b/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
@@ -77,3 +77,4 @@ org.apache.sis.internal.referencing.provider.Interpolation1D
 org.apache.sis.internal.referencing.provider.SatelliteTracking
 org.apache.sis.internal.referencing.provider.Wraparound
 org.apache.sis.internal.referencing.provider.GeocentricToTopocentric
+org.apache.sis.internal.referencing.provider.GeographicToTopocentric
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
index b9d6055e36..12e85a3d1f 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
@@ -72,6 +72,7 @@ public final strictfp class ProvidersTest extends TestCase {
             GeographicToGeocentric.class,
             GeocentricToGeographic.class,
             GeocentricToTopocentric.class,
+            GeographicToTopocentric.class,
             Geographic3Dto2D.class,
             Geographic2Dto3D.class,
             Molodensky.class,
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TopocentricConversionMock.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TopocentricConversionMock.java
deleted file mode 100644
index c46a4481dd..0000000000
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/TopocentricConversionMock.java
+++ /dev/null
@@ -1,63 +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.internal.referencing.provider;
-
-import org.opengis.parameter.ParameterDescriptor;
-import org.opengis.parameter.ParameterDescriptorGroup;
-import org.apache.sis.parameter.ParameterBuilder;
-import org.apache.sis.measure.Units;
-
-
-/**
- * The provider for <cite>"Geographic/topocentric conversions"</cite> 
conversion (EPSG:9837).
- *
- * This conversion is not yet implemented in Apache SIS, but we need to at 
least accept the parameters
- * for a Well Known Text (WKT) parsing test in the {@link 
org.apache.sis.io.wkt.WKTParserTest} class.
- *
- * <p>This class may be promoted to a real operation if we implement the 
formulas in a future Apache SIS version.</p>
- *
- * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
- * @since   0.6
- * @module
- */
-@SuppressWarnings("serial")
-public final strictfp class TopocentricConversionMock extends ProviderMock {
-    /**
-     * The group of all parameters expected by this coordinate operation.
-     */
-    private static final ParameterDescriptorGroup PARAMETERS;
-    static {
-        final ParameterBuilder builder = builder();
-        final ParameterDescriptor<?>[] parameters = {
-            createLatitude (builder.addIdentifier("8834").addName("Latitude of 
topocentric origin"), true),
-            createLongitude(builder.addIdentifier("8835").addName("Longitude 
of topocentric origin")),
-            builder.addIdentifier("8836").addName("Ellipsoidal height of 
topocentric origin").create(0, Units.METRE)
-        };
-        PARAMETERS = builder
-                .addIdentifier("9837")
-                .addName("Geographic/topocentric conversions")
-                .createGroup(parameters);
-    }
-
-    /**
-     * Creates a new <cite>"Geographic/topocentric conversions"</cite> 
operation method.
-     */
-    public TopocentricConversionMock() {
-        super(3, 3, PARAMETERS);
-    }
-}
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/ConsistencyTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/ConsistencyTest.java
index fab5bee949..10470181af 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/ConsistencyTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/ConsistencyTest.java
@@ -75,6 +75,7 @@ public final strictfp class ConsistencyTest extends TestCase {
     private static final Set<String> EXCLUDES = new HashSet<>(Arrays.asList(
         "CRS:1",            // Computer display: WKT parser alters the (i,j) 
axis names.
         "EPSG:5819",        // EPSG topocentric example A: error while parsing 
WKT.
+        "EPSG:5820",        // EPSG topocentric example B: error while parsing 
WKT.
         "AUTO2:42001",      // This projection requires parameters, but we 
provide none.
         "AUTO2:42002",      // This projection requires parameters, but we 
provide none.
         "AUTO2:42003",      // This projection requires parameters, but we 
provide none.
diff --git 
a/core/sis-referencing/src/test/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
 
b/core/sis-referencing/src/test/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
index b6d93bb6ff..2fcd0f566f 100644
--- 
a/core/sis-referencing/src/test/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
+++ 
b/core/sis-referencing/src/test/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
@@ -1,4 +1,3 @@
 # Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements;
 # and to You under the Apache License, Version 2.0.
-org.apache.sis.internal.referencing.provider.TopocentricConversionMock
 org.apache.sis.internal.referencing.provider.SeismicBinGridMock

Reply via email to