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 bbdefb11e245d9db4d42d76cb14e014d044a88ef Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Mon Aug 8 16:17:37 2022 +0200 Add "Geocentric/topocentric conversions" (EPSG:9836). https://issues.apache.org/jira/browse/SIS-258 --- .../provider/GeocentricToTopocentric.java | 187 +++++++++++++++++++++ ...g.opengis.referencing.operation.OperationMethod | 1 + .../referencing/provider/ProvidersTest.java | 1 + 3 files changed, 189 insertions(+) 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 new file mode 100644 index 0000000000..87e6da2f1f --- /dev/null +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToTopocentric.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.internal.referencing.provider; + +import javax.measure.Unit; +import javax.measure.quantity.Length; +import org.opengis.util.FactoryException; +import org.opengis.parameter.ParameterValue; +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.referencing.operation.transform.EllipsoidToCentricTransform; +import org.apache.sis.referencing.operation.matrix.Matrix4; +import org.apache.sis.parameter.ParameterBuilder; +import org.apache.sis.parameter.Parameters; +import org.apache.sis.measure.Units; +import org.apache.sis.internal.util.Constants; + +import static java.lang.Math.cos; +import static java.lang.Math.sin; + + +/** + * 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) + * @version 1.3 + * @since 1.3 + * @module + */ +public final class GeocentricToTopocentric extends AbstractProvider { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = 6064563343153407987L; + + /** + * The operation parameter descriptor for the <cite>Geocentric X of topocentric origin</cite> (X) parameter value. + * + * <!-- Generated by ParameterNameTableGenerator --> + */ + private static final ParameterDescriptor<Double> ORIGIN_X; + + /** + * The operation parameter descriptor for the <cite>Geocentric Y of topocentric origin</cite> (Y) parameter value. + * + * <!-- Generated by ParameterNameTableGenerator --> + */ + private static final ParameterDescriptor<Double> ORIGIN_Y; + + /** + * The operation parameter descriptor for the <cite>Geocentric Z of topocentric origin</cite> (Z) parameter value. + * + * <!-- Generated by ParameterNameTableGenerator --> + */ + private 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 = builder + .addIdentifier("8837") + .addName("Geocentric X of topocentric origin") + .create(Double.NaN, Units.METRE); + + ORIGIN_Y = builder + .addIdentifier("8838") + .addName("Geocentric Y of topocentric origin") + .create(Double.NaN, Units.METRE); + + ORIGIN_Z = builder + .addIdentifier("8839") + .addName("Geocentric Z of topocentric origin") + .create(Double.NaN, Units.METRE); + + PARAMETERS = builder + .addIdentifier("9836") + .addName("Geocentric/topocentric conversions") + .createGroupForMapProjection(ORIGIN_X, ORIGIN_Y, ORIGIN_Z); + // Not really a map projection, but we leverage the same axis parameters. + } + + /** + * Constructs a provider for the 3-dimensional case. + */ + public GeocentricToTopocentric() { + 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 geocentric 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 + { + return create(factory, Parameters.castOrWrap(values)); + } + + /** + * Implementation of {@link #createMathTransform(MathTransformFactory, ParameterValueGroup)} + * shared with {@link GeographicToTopocentric}. + */ + static MathTransform create(final MathTransformFactory factory, final Parameters values) + throws FactoryException + { + 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(φ); + /* + * 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. + */ + 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); + } + } +} 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 a1ac1bfbf8..7c942a8f7c 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 @@ -76,3 +76,4 @@ org.apache.sis.internal.referencing.provider.FranceGeocentricInterpolation 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 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 f13cda4040..b9d6055e36 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 @@ -71,6 +71,7 @@ public final strictfp class ProvidersTest extends TestCase { GeocentricTranslation3D.class, GeographicToGeocentric.class, GeocentricToGeographic.class, + GeocentricToTopocentric.class, Geographic3Dto2D.class, Geographic2Dto3D.class, Molodensky.class,