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: Avoid to provide misleading `OperationMethod` name when there is a change of coordinate system in a chain of operations. https://issues.apache.org/jira/browse/SIS-462
Date Tue, 07 Jul 2020 13:06:35 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 08f34ae  Avoid to provide misleading `OperationMethod` name when there is a change
of coordinate system in a chain of operations. https://issues.apache.org/jira/browse/SIS-462
08f34ae is described below

commit 08f34aeb4340530221c4450058f51e21c4d2aa6b
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Jul 7 15:05:25 2020 +0200

    Avoid to provide misleading `OperationMethod` name when there is a change of coordinate
system in a chain of operations.
    https://issues.apache.org/jira/browse/SIS-462
---
 .../provider/GeocentricToGeographic.java           |  2 +-
 .../operation/CoordinateOperationFinder.java       | 44 ++++++-----
 .../operation/LooselyDefinedMethod.java            | 90 ++++++++++++++++++++++
 .../operation/transform/CartesianToPolar.java      |  4 +-
 .../operation/transform/CartesianToSpherical.java  |  4 +-
 .../transform/CoordinateSystemTransform.java       | 49 ++++++++++--
 .../transform/DefaultMathTransformFactory.java     |  3 +-
 .../operation/transform/PolarToCartesian.java      |  4 +-
 .../operation/transform/SphericalToCartesian.java  |  4 +-
 .../transform/CoordinateSystemTransformTest.java   | 69 +++++++++++++----
 10 files changed, 218 insertions(+), 55 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToGeographic.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToGeographic.java
index dcb90c1..3c67c3c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToGeographic.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricToGeographic.java
@@ -28,7 +28,7 @@ import org.apache.sis.parameter.Parameters;
 
 
 /**
- * The provider for the <strong>inverse</strong> of "<cite>Geographic/geocentric
conversions</cite>" (EPSG:9602).
+ * The provider for the <strong>inverse</strong> of <cite>"Geographic/geocentric
conversions"</cite> (EPSG:9602).
  * This provider creates transforms from geocentric to geographic coordinate reference systems.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
index 0e98480..0ca99e2 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
@@ -267,7 +267,7 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry
{
          * Verify if some extension module handles this pair of CRS in a special way. For
example it may
          * be the "sis-gdal" module checking if the given CRS are wrappers around Proj.4
data structure.
          */
-        {   // For keeping 'operations' list locale.
+        {   // For keeping `operations` list locale.
             final List<CoordinateOperation> operations = new ArrayList<>();
             for (final SpecializedOperationFactory sp : factorySIS.getSpecializedFactories())
{
                 for (final CoordinateOperation op : sp.findOperations(sourceCRS, targetCRS))
{
@@ -575,6 +575,7 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry
{
         final DefaultMathTransformFactory mtFactory = factorySIS.getDefaultMathTransformFactory();
         MathTransform before = null, after = null;
         ParameterValueGroup parameters;
+        OperationMethod method = null;
         if (identifier == DATUM_SHIFT || identifier == ELLIPSOID_CHANGE) {
             /*
              * If the transform can be represented by a single coordinate operation, returns
that operation.
@@ -598,11 +599,6 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry
{
                 /*
                  * Failed to select a coordinate operation. Maybe because the coordinate
system types are not the same.
                  * Convert unconditionally to XYZ geocentric coordinates and apply the datum
shift in that CS space.
-                 *
-                 * TODO: operation name should not be "Affine" if 'before' or 'after' transforms
are not identity.
-                 *       Reminder: the parameter group name here determines the OperationMethod
later in this method.
-                 *
-                 *       See https://issues.apache.org/jira/browse/SIS-462
                  */
                 if (datumShift != null) {
                     parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE),
datumShift);
@@ -614,6 +610,16 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry
{
                 after  = mtFactory.createCoordinateSystemChange(normalized, targetCS, targetDatum.getEllipsoid());
                 context.setSource(normalized);
                 context.setTarget(normalized);
+                /*
+                 * The name of the `parameters` group determines the `OperationMethod` later
in this method.
+                 * We can not leave that name to "Affine" if `before` or `after` transforms
are not identity.
+                 * Note: we check for identity transforms instead than relaxing to general
`LinearTransform`
+                 * because otherwise, we would have to update values declared in `parameters`.
It is doable
+                 * but not done yet.
+                 */
+                if (!(before.isIdentity() && after.isIdentity())) {
+                    method = LooselyDefinedMethod.AFFINE_GEOCENTRIC;
+                }
             }
         } else if (identifier == GEOCENTRIC_CONVERSION) {
             /*
@@ -643,12 +649,6 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry
{
             } else if (sourceDim == 3 && targetDim == 2 && targetCS instanceof
EllipsoidalCS) {
                 parameters = Geographic3Dto2D.PARAMETERS.createValue();
             } else {
-                /*
-                 * TODO: instead than creating parameters for an identity operation, we should
create the
-                 *       CoordinateOperation directly from the MathTransform created by mtFactory
below.
-                 *       The intent is to get the correct OperationMethod, which should not
be "Affine"
-                 *       if there is a CS type change.
-                 */
                 parameters = Affine.identity(targetDim);
                 /*
                  * createCoordinateSystemChange(…) needs the ellipsoid associated to the
ellipsoidal coordinate system,
@@ -658,6 +658,7 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry
{
                 before = mtFactory.createCoordinateSystemChange(sourceCS, targetCS,
                         (sourceCS instanceof EllipsoidalCS ? sourceDatum : targetDatum).getEllipsoid());
                 context.setSource(targetCS);
+                method = mtFactory.getLastMethodUsed();
             }
         }
         /*
@@ -670,11 +671,14 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry
{
          *     normalized CRS with target datum  →
          *     target CRS
          *
-         * Those steps may be either explicit with the 'before' and 'after' transform, or
implicit with the
-         * Context parameter.
+         * Those steps may be either explicit with the `before` and `after` transforms, or
implicit with the
+         * Context parameter. The operation name is inferred from the parameters, unless
a method has been
+         * specified in advance.
          */
         MathTransform transform = mtFactory.createParameterizedTransform(parameters, context);
-        final OperationMethod method = mtFactory.getLastMethodUsed();
+        if (method == null) {
+            method = mtFactory.getLastMethodUsed();
+        }
         if (before != null) {
             transform = mtFactory.createConcatenatedTransform(before, transform);
             if (after != null) {
@@ -773,7 +777,7 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry
{
          * This part does nothing more than dropping the horizontal components,
          * like the "Geographic3D to 2D conversion" (EPSG:9659).
          * It is not the job of this block to perform unit conversions.
-         * Unit conversions, if needed, are done by 'step3' computed in above block.
+         * Unit conversions, if needed, are done by `step3` computed in above block.
          *
          * The "Geographic3DtoVertical.txt" file in the provider package is a reminder.
          * If this policy is changed, that file should be edited accordingly.
@@ -848,15 +852,15 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry
{
         /*
          * Compute the epoch shift.  The epoch is the time "0" in a particular coordinate
reference system.
          * For example, the epoch for java.util.Date object is january 1, 1970 at 00:00 UTC.
We compute how
-         * much to add to a time in 'sourceCRS' in order to get a time in 'targetCRS'. This
"epoch shift" is
-         * in units of 'targetCRS'.
+         * much to add to a time in `sourceCRS` in order to get a time in `targetCRS`.
+         * This "epoch shift" is in units of `targetCRS`.
          */
         final Unit<Time> targetUnit = targetCS.getAxis(0).getUnit().asType(Time.class);
         double epochShift = sourceDatum.getOrigin().getTime() -
                             targetDatum.getOrigin().getTime();
         epochShift = Units.MILLISECOND.getConverterTo(targetUnit).convert(epochShift);
         /*
-         * Check axis directions. The method 'swapAndScaleAxes' should returns a matrix of
size 2×2.
+         * Check axis directions. The method `swapAndScaleAxes` should returns a matrix of
size 2×2.
          * The element at index (0,0) may be +1 if source and target axes are in the same
direction,
          * or -1 if there are in opposite direction ("PAST" vs "FUTURE"). The value may be
something
          * else than ±1 if a unit conversion is applied too.  For example the value is 60
if time in
@@ -934,7 +938,7 @@ public class CoordinateOperationFinder extends CoordinateOperationRegistry
{
             operation = null;
         } else {
             if (stepComponents.length == 1) {
-                stepSourceCRS = stepComponents[0];    // Slight optimization of the next
block (in the 'else' case).
+                stepSourceCRS = stepComponents[0];    // Slight optimization of the next
block (in the `else` case).
             } else {
                 stepSourceCRS = toAuthorityDefinition(CoordinateReferenceSystem.class,
                         factorySIS.getCRSFactory().createCompoundCRS(derivedFrom(sourceCRS),
stepComponents));
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/LooselyDefinedMethod.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/LooselyDefinedMethod.java
new file mode 100644
index 0000000..ad64c90
--- /dev/null
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/LooselyDefinedMethod.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;
+
+import java.util.HashMap;
+import org.apache.sis.parameter.DefaultParameterDescriptorGroup;
+
+
+/**
+ * Operation methods that do not match exactly any of the predefined "standard" methods and
for which
+ * we do not define unambiguous sets of parameters. Those methods could be replaced by concatenations
+ * of standard methods, but doing so would require more development work.  In the meantime,
we define
+ * those methods for avoiding to mislead users. For example we should not call a coordinate
operation
+ * "Affine" if it also performs conversion from geographic to geocentric coordinates.
+ *
+ * <h2>Restrictions</h2>
+ * We do not provide any mechanism for instantiating a {@code CoordinateOperation} from those
methods.
+ * Consequently a coordinate operation can be formatted in WKT with those operation methods,
but can
+ * not be parsed. Attempt to parse such WKT will result in an error saying that the method
is unknown.
+ * This is better than formatting WKT with a standard but wrong operation name, in which
case parsing
+ * the WKT would produce unexpected results.
+ *
+ * <h2>Future evolution</h2>
+ * A cleaner approach would be to replace those methods by a concatenation of standard methods.
+ * {@link org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory#getLastMethodUsed()}
+ * can be invoked after {@code factory.createCoordinateSystemChange(…)} for getting pieces
of information
+ * needed, but we still have to put all the pieces together.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-462">SIS-462</a>
+ *
+ * @since 0.5
+ * @module
+ */
+final class LooselyDefinedMethod {
+    /**
+     * The <cite>"Affine parametric transformation in geocentric domain"</cite>
method.
+     * It could be done by a concatenation of the following standard methods:
+     *
+     * <ol>
+     *   <li><cite>"Geographic 2D to 3D conversion"</cite></li>
+     *   <li><cite>"Geographic/geocentric conversions"</cite> (EPSG:9602)</li>
+     *   <li><cite>"Affine parametric transformation"</cite> (EPSG:9624)</li>
+     *   <li>Inverse of <cite>"Geographic/geocentric conversions"</cite></li>
+     *   <li><cite>"Geographic 3D to 2D conversion"</cite> (EPSG:9659)</li>
+     * </ol>
+     *
+     * It is not implemented that way for now because we need more work for analyzing the
source and target
+     * coordinate systems (they are not necessarily ellipsoidal, so some "Geographic/geocentric
conversions"
+     * may need to be skipped or replaced by a conversion from/to a spherical CS), check
the number of dimension,
+     * <i>etc.</i>
+     */
+    static final DefaultOperationMethod AFFINE_GEOCENTRIC;
+
+    static {
+        final HashMap<String,Object> properties = new HashMap<>(4);
+        properties.put(DefaultOperationMethod.NAME_KEY,    "Undefined parameters");
+        properties.put(DefaultOperationMethod.REMARKS_KEY, "Placeholder for what should be
a chain of coordinate operations.");
+        final DefaultParameterDescriptorGroup parameters = new DefaultParameterDescriptorGroup(properties,
0, 1);
+
+        properties.put(DefaultOperationMethod.NAME_KEY,    "Affine parametric transformation
in geocentric domain");
+        properties.put(DefaultOperationMethod.REMARKS_KEY, parameters.getRemarks());
+        properties.put(DefaultOperationMethod.FORMULA_KEY, new DefaultFormula(
+                "This operation method is currently an implementation dependent black box.
" +
+                "A future version may redefine this method in terms of more standard methods."));
+        AFFINE_GEOCENTRIC = new DefaultOperationMethod(properties, parameters);
+    }
+
+    /**
+     * Do not allow instantiation of this class.
+     */
+    private LooselyDefinedMethod() {
+    }
+}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CartesianToPolar.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CartesianToPolar.java
index 292b28f..f592a86 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CartesianToPolar.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CartesianToPolar.java
@@ -69,7 +69,7 @@ final class CartesianToPolar extends CoordinateSystemTransform implements
Serial
      * Output coordinates are in radians.
      */
     private CartesianToPolar() {
-        super("Cartesian to polar", 2);
+        super("Cartesian to polar", "Cartesian to cylindrical", 2);
         context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION)
                .convertAfter(1, DoubleDouble.createRadiansToDegrees(), null);
     }
@@ -144,7 +144,7 @@ final class CartesianToPolar extends CoordinateSystemTransform implements
Serial
     }
 
     /*
-     * NOTE: we do not bother to override the methods expecting a 'float' array because those
methods should
+     * NOTE: we do not bother to override the methods expecting a `float` array because those
methods should
      *       be rarely invoked. Since there is usually LinearTransforms before and after
this transform, the
      *       conversion between float and double will be handled by those LinearTransforms.
 If nevertheless
      *       this CartesianToPolar is at the beginning or the end of a transformation chain,
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CartesianToSpherical.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CartesianToSpherical.java
index 1034a8c..5bfd367 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CartesianToSpherical.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CartesianToSpherical.java
@@ -62,7 +62,7 @@ final class CartesianToSpherical extends CoordinateSystemTransform implements
Se
      * Output coordinates are in radians.
      */
     private CartesianToSpherical() {
-        super("Cartesian to spherical", 3);
+        super("Cartesian to spherical", null, 3);
         context.denormalizeGeographicOutputs(0);                // Convert (θ,Ω) from radians
to degrees.
     }
 
@@ -144,7 +144,7 @@ final class CartesianToSpherical extends CoordinateSystemTransform implements
Se
     }
 
     /*
-     * NOTE: we do not bother to override the methods expecting a 'float' array because those
methods should
+     * NOTE: we do not bother to override the methods expecting a `float` array because those
methods should
      *       be rarely invoked. Since there is usually LinearTransforms before and after
this transform, the
      *       conversion between float and double will be handled by those LinearTransforms.
 If nevertheless
      *       this CartesianToSpherical is at the beginning or the end of a transformation
chain,
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
index ece34a9..93a959c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
@@ -29,6 +29,7 @@ import org.opengis.referencing.cs.PolarCS;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.operation.OperationNotFoundException;
+import org.opengis.referencing.operation.OperationMethod;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.referencing.WKTUtilities;
 import org.apache.sis.internal.system.DefaultFactories;
@@ -38,6 +39,7 @@ import org.apache.sis.parameter.DefaultParameterDescriptorGroup;
 import org.apache.sis.referencing.cs.AxesConvention;
 import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.referencing.ImmutableIdentifier;
+import org.apache.sis.referencing.operation.DefaultOperationMethod;
 
 
 /**
@@ -45,7 +47,7 @@ import org.apache.sis.referencing.ImmutableIdentifier;
  * Each subclasses should have a singleton instance.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.7
  * @module
  */
@@ -56,6 +58,25 @@ abstract class CoordinateSystemTransform extends AbstractMathTransform
{
     private final int dimension;
 
     /**
+     * An operation method that describe this coordinate system conversion.
+     * This is used for providing a value in {@link DefaultMathTransformFactory#getLastMethodUsed()}.
+     */
+    private final transient OperationMethod method;
+
+    /**
+     * The {@linkplain #method} augmented with one pass through dimension.
+     * May be the same instance than {@link #method} if that method is already 3D.
+     *
+     * <div class="note"><b>Note:</b> if {@link #method} is "Polar to Cartesian",
+     * then {@code method3D} is "Cylindrical to Cartesian".</div>
+     *
+     * This method is used for {@link org.opengis.referencing.operation.CoordinateOperation}
WKT formatting.
+     * Contrarily to {@link #method}, this {@code method3D} is never used for {@link MathTransform}
WKT.
+     * Instead, the later case is represented by a concatenation of {@link #method} with
a pass-through.
+     */
+    private final transient OperationMethod method3D;
+
+    /**
      * An empty contextual parameter, used only for representing conversion from degrees
to radians.
      */
     final transient ContextualParameters context;
@@ -80,11 +101,21 @@ abstract class CoordinateSystemTransform extends AbstractMathTransform
{
      * Subclasses may need to invoke {@link ContextualParameters#normalizeGeographicInputs(double)}
      * or {@link ContextualParameters#denormalizeGeographicOutputs(double)} after this constructor.
      */
-    CoordinateSystemTransform(final String method, final int dimension) {
+    CoordinateSystemTransform(final String method, final String method3D, final int dimension)
{
         this.dimension = dimension;
+        this.method    = method(method);
+        this.method3D  = (method3D != null) ? method(method3D) : this.method;
+        this.context   = new ContextualParameters(this.method.getParameters(), dimension,
dimension);
+    }
+
+    /**
+     * Creates an operation method of the given name.
+     */
+    private static OperationMethod method(final String name) {
         final Map<String,?> properties = Collections.singletonMap(DefaultParameterDescriptorGroup.NAME_KEY,
-                new ImmutableIdentifier(Citations.SIS, Constants.SIS, method));
-        context = new ContextualParameters(new DefaultParameterDescriptorGroup(properties,
1, 1), dimension, dimension);
+                new ImmutableIdentifier(Citations.SIS, Constants.SIS, name));
+        final DefaultParameterDescriptorGroup descriptor = new DefaultParameterDescriptorGroup(properties,
1, 1);
+        return new DefaultOperationMethod(properties, descriptor);
     }
 
     /**
@@ -160,7 +191,7 @@ abstract class CoordinateSystemTransform extends AbstractMathTransform
{
      * weight in the common case where the conversions handled by this class are not needed.
      */
     static MathTransform create(final MathTransformFactory factory, final CoordinateSystem
source,
-            final CoordinateSystem target) throws FactoryException
+            final CoordinateSystem target, final ThreadLocal<OperationMethod> lastMethod)
throws FactoryException
     {
         int passthrough = 0;
         CoordinateSystemTransform kernel = null;
@@ -196,11 +227,13 @@ abstract class CoordinateSystemTransform extends AbstractMathTransform
{
                 final MathTransform before = factory.createAffineTransform(
                         CoordinateSystems.swapAndScaleAxes(source,
                         CoordinateSystems.replaceAxes(source, AxesConvention.NORMALIZED)));
-                final MathTransform after = factory.createAffineTransform(
+                final MathTransform after  = factory.createAffineTransform(
                         CoordinateSystems.swapAndScaleAxes(
                         CoordinateSystems.replaceAxes(target, AxesConvention.NORMALIZED),
target));
-                return factory.createConcatenatedTransform(before,
-                       factory.createConcatenatedTransform(tr, after));
+                final MathTransform result = factory.createConcatenatedTransform(before,
+                                             factory.createConcatenatedTransform(tr, after));
+                lastMethod.set(passthrough == 0 ? kernel.method : kernel.method3D);
+                return result;
             }
         } catch (IllegalArgumentException | IncommensurableException e) {
             cause = e;
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
index ea43943..a1c77ce 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
@@ -1381,6 +1381,7 @@ public class DefaultMathTransformFactory extends AbstractFactory implements
Math
     {
         ArgumentChecks.ensureNonNull("source", source);
         ArgumentChecks.ensureNonNull("target", target);
+        lastMethod.remove();                                // In case an exception is thrown
before completion.
         if (ellipsoid != null) {
             final boolean isEllipsoidalSource = (source instanceof EllipsoidalCS);
             if (isEllipsoidalSource != (target instanceof EllipsoidalCS)) {
@@ -1408,7 +1409,7 @@ public class DefaultMathTransformFactory extends AbstractFactory implements
Math
                 }
             }
         }
-        return CoordinateSystemTransform.create(this, source, target);
+        return CoordinateSystemTransform.create(this, source, target, lastMethod);
         // No need to use unique(…) here.
     }
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PolarToCartesian.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PolarToCartesian.java
index ff472f6..4bd5c40 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PolarToCartesian.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PolarToCartesian.java
@@ -82,7 +82,7 @@ final class PolarToCartesian extends CoordinateSystemTransform implements
Serial
      * Input coordinates are in radians.
      */
     private PolarToCartesian() {
-        super("Polar to Cartesian", 2);
+        super("Polar to Cartesian", "Cylindrical to Cartesian", 2);
         context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION)
                .convertBefore(1, DoubleDouble.createDegreesToRadians(), null);
     }
@@ -157,7 +157,7 @@ final class PolarToCartesian extends CoordinateSystemTransform implements
Serial
     }
 
     /*
-     * NOTE: we do not bother to override the methods expecting a 'float' array because those
methods should
+     * NOTE: we do not bother to override the methods expecting a `float` array because those
methods should
      *       be rarely invoked. Since there is usually LinearTransforms before and after
this transform, the
      *       conversion between float and double will be handled by those LinearTransforms.
 If nevertheless
      *       this PolarToCartesian is at the beginning or the end of a transformation chain,
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SphericalToCartesian.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SphericalToCartesian.java
index 9c8ecd0..515529b 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SphericalToCartesian.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SphericalToCartesian.java
@@ -81,7 +81,7 @@ final class SphericalToCartesian extends CoordinateSystemTransform implements
Se
      * Input coordinates are in radians.
      */
     private SphericalToCartesian() {
-        super("Spherical to Cartesian", 3);
+        super("Spherical to Cartesian", null, 3);
         context.normalizeGeographicInputs(0);                   // Convert (θ,Ω) from degrees
to radians.
     }
 
@@ -167,7 +167,7 @@ final class SphericalToCartesian extends CoordinateSystemTransform implements
Se
     }
 
     /*
-     * NOTE: we do not bother to override the methods expecting a 'float' array because those
methods should
+     * NOTE: we do not bother to override the methods expecting a `float` array because those
methods should
      *       be rarely invoked. Since there is usually LinearTransforms before and after
this transform, the
      *       conversion between float and double will be handled by those LinearTransforms.
 If nevertheless
      *       this SphericalToCartesian is at the beginning or the end of a transformation
chain,
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
index 8d9f435..dc97b1c 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
@@ -19,6 +19,7 @@ package org.apache.sis.referencing.operation.transform;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.SphericalCS;
+import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.referencing.crs.DefaultGeocentricCRS;
@@ -36,12 +37,14 @@ import org.junit.BeforeClass;
 import org.junit.AfterClass;
 import org.junit.Test;
 
+import static org.junit.Assert.*;
+
 
 /**
  * Tests the {@link CoordinateSystemTransform} static factory method.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.1
  * @since   0.7
  * @module
  */
@@ -61,6 +64,11 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
     private static MathTransformFactory factory;
 
     /**
+     * The operation method used.
+     */
+    private static ThreadLocal<OperationMethod> lastMethod;
+
+    /**
      * Creates the {@link MathTransformFactory} to be used for the tests.
      * We do not use the system-wide factory in order to have better tests isolation.
      */
@@ -69,6 +77,7 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
         factory = new DefaultMathTransformFactory();
         spherical = (SphericalCS) DefaultGeocentricCRS.castOrCopy(CommonCRS.WGS84.spherical())
                             .forConvention(AxesConvention.RIGHT_HANDED).getCoordinateSystem();
+        lastMethod = new ThreadLocal<>();
     }
 
     /**
@@ -76,8 +85,9 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
      */
     @AfterClass
     public static void disposeFactory() {
-        spherical = null;
-        factory = null;
+        spherical  = null;
+        factory    = null;
+        lastMethod = null;
     }
 
     /**
@@ -105,7 +115,25 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
     }
 
     /**
-     * Tests {@link CoordinateSystemTransform#create(MathTransformFactory, CoordinateSystem,
CoordinateSystem)}.
+     * Invokes {@link CoordinateSystemTransform#create CoordinateSystemTransform.create(…)}
+     * and stores the result in {@link #transform}.
+     */
+    private void createTransform(final CoordinateSystem source, final CoordinateSystem target)
throws FactoryException {
+        lastMethod.remove();
+        transform = CoordinateSystemTransform.create(factory, source, target, lastMethod);
+    }
+
+    /**
+     * Verifies that {@link #lastMethod} has the expected value.
+     */
+    private static void assertMethodEquals(final String expected) {
+        final OperationMethod method = lastMethod.get();
+        assertNotNull("lastMethod", method);
+        assertEquals(expected, method.getName().getCode());
+    }
+
+    /**
+     * Tests {@link CoordinateSystemTransform#create CoordinateSystemTransform.create(…)}
      * for a conversion between two spherical coordinate systems.
      *
      * @throws FactoryException if an error occurred while creating the transform.
@@ -113,7 +141,7 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
      */
     @Test
     public void testSphericalToSpherical() throws FactoryException, TransformException {
-        transform = CoordinateSystemTransform.create(factory, HardCodedCS.SPHERICAL, spherical);
+        createTransform(HardCodedCS.SPHERICAL, spherical);
         tolerance = 0;
         final double[][] data = SphericalToCartesianTest.testData();
         final double[] source = data[0];
@@ -123,10 +151,11 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
             ArraysExt.swap(source, i, i+1);
         }
         verifyTransform(source, target);
+        assertNull(lastMethod.get());           // Null for now, but a method may be provided
in a future version.
     }
 
     /**
-     * Tests {@link CoordinateSystemTransform#create(MathTransformFactory, CoordinateSystem,
CoordinateSystem)}.
+     * Tests {@link CoordinateSystemTransform#create CoordinateSystemTransform.create(…)}
      * for a conversion from spherical to Cartesian coordinates.
      *
      * @throws FactoryException if an error occurred while creating the transform.
@@ -135,13 +164,14 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
     @Test
     public void testSphericalToCartesian() throws FactoryException, TransformException {
         tolerance = 1E-9;
-        transform = CoordinateSystemTransform.create(factory, HardCodedCS.SPHERICAL, toCentimetres(HardCodedCS.GEOCENTRIC));
+        createTransform(HardCodedCS.SPHERICAL, toCentimetres(HardCodedCS.GEOCENTRIC));
         final double[][] data = sphericalTestData();
         verifyTransform(data[0], data[1]);
+        assertMethodEquals("Spherical to Cartesian");
     }
 
     /**
-     * Tests {@link CoordinateSystemTransform#create(MathTransformFactory, CoordinateSystem,
CoordinateSystem)}.
+     * Tests {@link CoordinateSystemTransform#create CoordinateSystemTransform.create(…)}
      * for a conversion from Cartesian to spherical coordinates.
      *
      * @throws FactoryException if an error occurred while creating the transform.
@@ -150,9 +180,10 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
     @Test
     public void testCartesianToSpherical() throws FactoryException, TransformException {
         tolerance = 1E-9;
-        transform = CoordinateSystemTransform.create(factory, toCentimetres(HardCodedCS.GEOCENTRIC),
HardCodedCS.SPHERICAL);
+        createTransform(toCentimetres(HardCodedCS.GEOCENTRIC), HardCodedCS.SPHERICAL);
         final double[][] data = sphericalTestData();
         verifyTransform(data[1], data[0]);
+        assertMethodEquals("Cartesian to spherical");
     }
 
     /**
@@ -174,7 +205,7 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
     }
 
     /**
-     * Tests {@link CoordinateSystemTransform#create(MathTransformFactory, CoordinateSystem,
CoordinateSystem)}.
+     * Tests {@link CoordinateSystemTransform#create CoordinateSystemTransform.create(…)}
      * for a conversion from cylindrical to Cartesian coordinates.
      *
      * @throws FactoryException if an error occurred while creating the transform.
@@ -183,13 +214,14 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
     @Test
     public void testCylindricalToCartesian() throws FactoryException, TransformException
{
         tolerance = 1E-9;
-        transform = CoordinateSystemTransform.create(factory, HardCodedCS.CYLINDRICAL, toCentimetres(HardCodedCS.CARTESIAN_3D));
+        createTransform(HardCodedCS.CYLINDRICAL, toCentimetres(HardCodedCS.CARTESIAN_3D));
         final double[][] data = polarTestData(true);
         verifyTransform(data[0], data[1]);
+        assertMethodEquals("Cylindrical to Cartesian");
     }
 
     /**
-     * Tests {@link CoordinateSystemTransform#create(MathTransformFactory, CoordinateSystem,
CoordinateSystem)}.
+     * Tests {@link CoordinateSystemTransform#create CoordinateSystemTransform.create(…)}
      * for a conversion from Cartesian to cylindrical coordinates.
      *
      * @throws FactoryException if an error occurred while creating the transform.
@@ -198,13 +230,14 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
     @Test
     public void testCartesianToCylindrical() throws FactoryException, TransformException
{
         tolerance = 1E-9;
-        transform = CoordinateSystemTransform.create(factory, toCentimetres(HardCodedCS.CARTESIAN_3D),
HardCodedCS.CYLINDRICAL);
+        createTransform(toCentimetres(HardCodedCS.CARTESIAN_3D), HardCodedCS.CYLINDRICAL);
         final double[][] data = polarTestData(true);
         verifyTransform(data[1], data[0]);
+        assertMethodEquals("Cartesian to cylindrical");
     }
 
     /**
-     * Tests {@link CoordinateSystemTransform#create(MathTransformFactory, CoordinateSystem,
CoordinateSystem)}.
+     * Tests {@link CoordinateSystemTransform#create CoordinateSystemTransform.create(…)}
      * for a conversion from polar to Cartesian coordinates.
      *
      * @throws FactoryException if an error occurred while creating the transform.
@@ -213,13 +246,14 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
     @Test
     public void testPolarToCartesian() throws FactoryException, TransformException {
         tolerance = 1E-9;
-        transform = CoordinateSystemTransform.create(factory, HardCodedCS.POLAR, toCentimetres(HardCodedCS.CARTESIAN_2D));
+        createTransform(HardCodedCS.POLAR, toCentimetres(HardCodedCS.CARTESIAN_2D));
         final double[][] data = polarTestData(false);
         verifyTransform(data[0], data[1]);
+        assertMethodEquals("Polar to Cartesian");
     }
 
     /**
-     * Tests {@link CoordinateSystemTransform#create(MathTransformFactory, CoordinateSystem,
CoordinateSystem)}.
+     * Tests {@link CoordinateSystemTransform#create CoordinateSystemTransform.create(…)}
      * for a conversion from Cartesian to polar coordinates.
      *
      * @throws FactoryException if an error occurred while creating the transform.
@@ -228,8 +262,9 @@ public final strictfp class CoordinateSystemTransformTest extends TransformTestC
     @Test
     public void testCartesianToPolar() throws FactoryException, TransformException {
         tolerance = 1E-9;
-        transform = CoordinateSystemTransform.create(factory, toCentimetres(HardCodedCS.CARTESIAN_2D),
HardCodedCS.POLAR);
+        createTransform(toCentimetres(HardCodedCS.CARTESIAN_2D), HardCodedCS.POLAR);
         final double[][] data = polarTestData(false);
         verifyTransform(data[1], data[0]);
+        assertMethodEquals("Cartesian to polar");
     }
 }


Mime
View raw message