You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/04/24 09:56:33 UTC

[sis] branch geoapi-4.0 updated: `MathTransforms.linear(MathTransform, DirectPosition)` and `tangent(…)` where duplicating functionality. Deprecate the former in favor of the latter.

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 f6509802be `MathTransforms.linear(MathTransform, DirectPosition)` and `tangent(…)` where duplicating functionality. Deprecate the former in favor of the latter.
f6509802be is described below

commit f6509802be691dadc2110be818d56dd2016596b3
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Apr 24 11:55:46 2023 +0200

    `MathTransforms.linear(MathTransform, DirectPosition)` and `tangent(…)` where duplicating functionality.
    Deprecate the former in favor of the latter.
---
 .../org/apache/sis/portrayal/CanvasFollower.java   |   2 +-
 .../sis/referencing/operation/matrix/Matrices.java |   8 +-
 .../operation/transform/MathTransforms.java        | 193 +++++++++------------
 .../operation/transform/MathTransformsTest.java    |  71 ++++----
 4 files changed, 124 insertions(+), 150 deletions(-)

diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasFollower.java b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasFollower.java
index c568e78384..fab7d849c6 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasFollower.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/CanvasFollower.java
@@ -365,7 +365,7 @@ public class CanvasFollower implements PropertyChangeListener, Disposable {
                              */
                             if (objectiveTransform != null) {
                                 DirectPosition poi = getSourceObjectivePOI();
-                                AffineTransform t = AffineTransforms2D.castOrCopy(MathTransforms.linear(objectiveTransform, poi));
+                                AffineTransform t = AffineTransforms2D.castOrCopy(MathTransforms.tangent(objectiveTransform, poi));
                                 AffineTransform c = t.createInverse();
                                 c.preConcatenate(before);
                                 c.preConcatenate(t);
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
index 2fed5db647..69e26da43d 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
@@ -714,10 +714,10 @@ public final class Matrices extends Static {
      * is non-null, all its coordinate values are copied in the last column of the returned matrix.
      *
      * <h4>Relationship with {@code MathTransform}</h4>
-     * When used together with {@link MathTransforms#derivativeAndTransform MathTransforms.derivativeAndTransform(…)},
-     * the {@code derivative} argument is the derivative computed by {@code derivativeAndTransform(…)} and the
-     * {@code translation} vector is the position computed by that method. The result is an approximation of the
-     * transform in the vicinity of the position given to {@code derivativeAndTransform(…)}.
+     * When used together with {@link MathTransforms#derivativeAndTransform MathTransforms.derivativeAndTransform(…)}
+     * with source coordinates all set to zero, the {@code derivative} and {@code translation} arguments can be
+     * respectively the return value and destination coordinates computed by {@code derivativeAndTransform(…)}.
+     * The {@code createAffine(…)} result is then an approximation of the transform in the vicinity of the origin.
      *
      * @param  derivative   the scale, shear and rotation of the affine transform.
      * @param  translation  the translation vector (the last column) of the affine transform.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
index 9b7cbae31f..a153122df3 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
@@ -220,11 +220,34 @@ public final class MathTransforms extends Static {
         return new ProjectiveTransform(matrix).optimize();
     }
 
+    /**
+     * Returns a linear (usually affine) transform which approximates the given transform in the vicinity of the given position.
+     *
+     * @param  transform  the transform to approximate by an affine transform.
+     * @param  position   position in source CRS around which to get the an affine transform approximation.
+     * @return a transform approximating the given transform around the given position.
+     * @throws TransformException if an error occurred while transforming the given position
+     *         or computing the derivative at that position.
+     *
+     * @since 1.0
+     *
+     * @deprecated This method duplicates {@link #tangent(MathTransform, DirectPosition)}.
+     */
+    @Deprecated(since="1.4", forRemoval=true)
+    public static LinearTransform linear(MathTransform transform, DirectPosition position) throws TransformException {
+        return tangent(transform, position);
+    }
+
     /**
      * Returns a linear (usually affine) transform which approximates the given transform in the vicinity of the given position.
      * If the given transform is already an instance of {@link LinearTransform}, then it is returned as-is.
      * Otherwise an approximation for the given position is created using the
-     * {@linkplain MathTransform#derivative(DirectPosition) transform derivative} at that position.
+     * {@linkplain MathTransform#derivative(DirectPosition) transform derivative} at that given position.
+     * The returned transform has the same number of source and target dimensions than the given transform.
+     *
+     * <p>If the given transform is a one-dimensional curve, then this method computes the tangent line at the given position.
+     * The same computation is generalized to any number of dimensions (tangent plane if the given transform is two-dimensional,
+     * <i>etc.</i>).</p>
      *
      * <h4>Invariant</h4>
      * Transforming the given {@code position} using the given {@code transform} produces the same result
@@ -232,23 +255,23 @@ public final class MathTransforms extends Static {
      * This invariant holds only for that particular position; the transformation of any other positions
      * may produce different results.
      *
-     * @param  transform  the transform to approximate by an affine transform.
-     * @param  position   position in source CRS around which to get the an affine transform approximation.
+     * @param  toApproximate  the potentially non-linear transform to approximate by a linear transform.
+     * @param  tangentPoint   position in source CRS around which to get the an line approximation.
      * @return a transform approximating the given transform around the given position.
      * @throws TransformException if an error occurred while transforming the given position
      *         or computing the derivative at that position.
      *
-     * @since 1.0
+     * @since 1.1
      *
      * @see #getMatrix(MathTransform, DirectPosition)
      */
-    public static LinearTransform linear(final MathTransform transform, final DirectPosition position) throws TransformException {
-        if (transform instanceof LinearTransform) {
+    public static LinearTransform tangent(final MathTransform toApproximate, final DirectPosition tangentPoint) throws TransformException {
+        if (toApproximate instanceof LinearTransform) {
             // We accept null position here for consistency with MathTransform.derivative(DirectPosition).
-            ArgumentChecks.ensureDimensionMatches("position", transform.getSourceDimensions(), position);
-            return (LinearTransform) transform;
+            ArgumentChecks.ensureDimensionMatches("tangentPoint", toApproximate.getSourceDimensions(), tangentPoint);
+            return (LinearTransform) toApproximate;
         } else {
-            return linear(getMatrix(transform, position));
+            return linear(getMatrix(toApproximate, tangentPoint));
         }
     }
 
@@ -679,51 +702,71 @@ public final class MathTransforms extends Static {
      * Otherwise the returned matrix can be used for {@linkplain #linear(Matrix) building a linear transform} which can be
      * used as an approximation of the given transform for short distances around the given position.
      *
-     * @param  transform  the transform to approximate by an affine transform.
-     * @param  position   position in source CRS around which to get the coefficients of an affine transform approximation.
-     * @return the matrix of the given transform around the given position.
-     * @throws TransformException if an error occurred while transforming the given position or computing the derivative at
-     *         that position.
+     * @param  toApproximate  the potentially non-linear transform to approximate by an affine transform.
+     * @param  tangentPoint   position in source CRS around which to get the coefficients of an affine transform approximation.
+     * @return the matrix representation of the affine approximation of the given transform around the given position.
+     * @throws TransformException if an error occurred while transforming the given position
+     *         or computing the derivative at that position.
      *
      * @since 1.0
      *
-     * @see #linear(MathTransform, DirectPosition)
+     * @see #tangent(MathTransform, DirectPosition)
      */
-    public static Matrix getMatrix(final MathTransform transform, final DirectPosition position) throws TransformException {
-        ArgumentChecks.ensureNonNull("transform", transform);
-        final int srcDim = transform.getSourceDimensions();
-        ArgumentChecks.ensureDimensionMatches("position", srcDim, position);            // Null position is okay for now.
-        final Matrix affine = getMatrix(transform);
+    public static Matrix getMatrix(final MathTransform toApproximate, final DirectPosition tangentPoint) throws TransformException {
+        ArgumentChecks.ensureNonNull("toApproximate", toApproximate);
+        final int srcDim = toApproximate.getSourceDimensions();
+        ArgumentChecks.ensureDimensionMatches("tangentPoint", srcDim, tangentPoint);    // Null position is okay for now.
+        final Matrix affine = getMatrix(toApproximate);
         if (affine != null) {
             return affine;
             // We accept null position here for consistency with MathTransform.derivative(DirectPosition).
         }
-        ArgumentChecks.ensureNonNull("position", position);
-        final int tgtDim = transform.getTargetDimensions();
-        double[] pts = new double[Math.max(srcDim + 1, tgtDim)];
+        ArgumentChecks.ensureNonNull("tangentPoint", tangentPoint);
+        final int tgtDim = toApproximate.getTargetDimensions();
+        double[] coordinates = new double[Math.max(tgtDim, srcDim + 1)];
         for (int i=0; i<srcDim; i++) {
-            pts[i] = position.getOrdinate(i);
-        }
-        final Matrix d = derivativeAndTransform(transform, pts, 0, pts, 0);
-        final MatrixSIS a = Matrices.createZero(tgtDim + 1, srcDim + 1);
-        for (int j=0; j<tgtDim; j++) {
-            for (int i=0; i<srcDim; i++) {
-                a.setElement(j, i, d.getElement(j, i));
-            }
-            a.setElement(j, srcDim, pts[j]);
-            pts[j] = -position.getOrdinate(j);                  // To be used by a.translate(pts) later.
+            coordinates[i] = tangentPoint.getOrdinate(i);
         }
-        a.setElement(tgtDim, srcDim, 1);
+        final Matrix derivative = derivativeAndTransform(toApproximate, coordinates, 0, coordinates, 0);
+        final MatrixSIS m = Matrices.createAffine(derivative, new DirectPositionView.Double(coordinates, 0, tgtDim));
         /*
          * At this point, the translation column in the matrix is set as if the coordinate system origin
          * was at the given position. We want to keep the original coordinate system origin. We do that
-         * be applying a translation in the opposite direction before the affine transform. Translation
-         * terms were opportunistically set in the previous loop.
+         * be applying a translation in the opposite direction before the affine transform.
          */
-        pts = ArraysExt.resize(pts, srcDim + 1);
-        pts[srcDim] = 1;
-        a.translate(pts);
-        return a;
+        coordinates = ArraysExt.resize(coordinates, srcDim + 1);
+        for (int i=0; i<srcDim; i++) {
+            coordinates[i] = -tangentPoint.getOrdinate(i);
+        }
+        coordinates[srcDim] = 1;
+        m.translate(coordinates);
+        return m;
+    }
+
+    /**
+     * Returns source coordinate values where the transform is mathematically and numerically applicable.
+     * This is <em>not</em> the domain of validity for which a coordinate reference system has been defined,
+     * because this method ignores "real world" considerations such as datum and country boundaries.
+     * This method is for allowing callers to crop their data for removing areas that may cause numerical problems,
+     * for example latitudes too close to a pole before Mercator projection.
+     *
+     * <p>See {@link AbstractMathTransform#getDomain(DomainDefinition)} for more information.
+     * This static method delegates to above-cited method if possible, or returns an empty value otherwise.</p>
+     *
+     * @param  evaluated  transform for which to evaluate a domain, or {@code null}.
+     * @return estimation of a domain where this transform is considered numerically applicable.
+     * @throws TransformException if the domain cannot be estimated.
+     *
+     * @see AbstractMathTransform#getDomain(DomainDefinition)
+     * @see org.opengis.referencing.operation.CoordinateOperation#getDomainOfValidity()
+     *
+     * @since 1.3
+     */
+    public static Optional<Envelope> getDomain(final MathTransform evaluated) throws TransformException {
+        if (evaluated instanceof AbstractMathTransform) {
+            return ((AbstractMathTransform) evaluated).getDomain(new DomainDefinition());
+        }
+        return Optional.empty();
     }
 
     /**
@@ -768,74 +811,4 @@ public final class MathTransforms extends Static {
         }
         return derivative;
     }
-
-    /**
-     * Computes a linear approximation of the given math transform at the given position.
-     * If the given transform is already an instance of {@link LinearTransform}, then it is returned as-is.
-     * Otherwise an affine transform is created from the {@linkplain MathTransform#derivative(DirectPosition)
-     * transform derivative} and the tangent point coordinates. The returned transform has the same number of
-     * source and target dimensions than the given transform.
-     *
-     * <p>If the given transform is a one dimensional curve, then this method computes the tangent at the given
-     * position. The same computation is generalized to any number of dimensions (computes a tangent plane if the
-     * given transform is two-dimensional, <i>etc.</i>).</p>
-     *
-     * @param  toApproximate  the non-linear transform to approximate by a linear transform.
-     * @param  tangentPoint   the point where to compute a linear approximation.
-     * @return linear approximation of the given math transform at the given position.
-     * @throws TransformException if the point cannot be transformed
-     *         or if a problem occurred while calculating the derivative.
-     *
-     * @since 1.1
-     */
-    public static LinearTransform tangent(final MathTransform toApproximate, final DirectPosition tangentPoint)
-            throws TransformException
-    {
-        ArgumentChecks.ensureNonNull("toApproximate", toApproximate);
-        if (toApproximate instanceof LinearTransform) {
-            return (LinearTransform) toApproximate;
-        }
-        final int srcDim = toApproximate.getSourceDimensions();
-        ArgumentChecks.ensureDimensionMatches("tangentPoint", srcDim, tangentPoint);
-        final int tgtDim = toApproximate.getTargetDimensions();
-        final double[] coordinates = new double[Math.max(srcDim, tgtDim)];
-        for (int i=0; i<srcDim; i++) {
-            coordinates[i] = tangentPoint.getOrdinate(i);
-        }
-        final Matrix derivative = derivativeAndTransform(toApproximate, coordinates, 0, coordinates, 0);
-        final MatrixSIS m = Matrices.createAffine(derivative, new DirectPositionView.Double(coordinates, 0, tgtDim));
-        for (int i=0; i<srcDim; i++) {
-            m.convertBefore(i, null, -tangentPoint.getOrdinate(i));
-        }
-        final LinearTransform tangent = linear(m);
-        assert tangent.getSourceDimensions() == srcDim;
-        assert tangent.getTargetDimensions() == tgtDim;
-        return tangent;
-    }
-
-    /**
-     * Returns source coordinate values where the transform is mathematically and numerically applicable.
-     * This is <em>not</em> the domain of validity for which a coordinate reference system has been defined,
-     * because this method ignores "real world" considerations such as datum and country boundaries.
-     * This method is for allowing callers to crop their data for removing areas that may cause numerical problems,
-     * for example latitudes too close to a pole before Mercator projection.
-     *
-     * <p>See {@link AbstractMathTransform#getDomain(DomainDefinition)} for more information.
-     * This static method delegates to above-cited method if possible, or returns an empty value otherwise.</p>
-     *
-     * @param  evaluated  transform for which to evaluate a domain, or {@code null}.
-     * @return estimation of a domain where this transform is considered numerically applicable.
-     * @throws TransformException if the domain cannot be estimated.
-     *
-     * @see AbstractMathTransform#getDomain(DomainDefinition)
-     * @see org.opengis.referencing.operation.CoordinateOperation#getDomainOfValidity()
-     *
-     * @since 1.3
-     */
-    public static Optional<Envelope> getDomain(final MathTransform evaluated) throws TransformException {
-        if (evaluated instanceof AbstractMathTransform) {
-            return ((AbstractMathTransform) evaluated).getDomain(new DomainDefinition());
-        }
-        return Optional.empty();
-    }
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformsTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformsTest.java
index d8c0274821..974071affb 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformsTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformsTest.java
@@ -171,13 +171,44 @@ public final class MathTransformsTest extends TestCase {
     }
 
     /**
-     * Tests {@link MathTransforms#linear(MathTransform, DirectPosition)}.
+     * Tests {@link MathTransforms#tangent(MathTransform, DirectPosition)} of a linear transform.
      *
-     * @throws TransformException if an error occurred while computing the derivative.
+     * @throws TransformException should never happen since this test uses a linear transform.
      */
     @Test
     @DependsOnMethod("testGetMatrix")
-    public void testLinearUsingPosition() throws TransformException {
+    public void testTangentOfLinear() throws TransformException {
+        /*
+         * The random values in Matrix and DirectPosition below does not matter; we will just verify
+         * that we get the same values in final result. In particular the `tangentPoint` coordinates
+         * are ignored since we use a linear transform for this test.
+         */
+        final Matrix expected = Matrices.create(3, 4, new double[] {
+            -4, 5, 7, 2,
+             3, 4, 2, 9,
+             0, 0, 0, 1,
+        });
+        final DirectPosition tangentPoint = new GeneralDirectPosition(3, 8, 7);
+        MathTransform transform = MathTransforms.linear(expected);
+        assertSame(transform, MathTransforms.tangent(transform, tangentPoint));
+        /*
+         * Above test returned the transform directly because it found that it was already an instance
+         * of `LinearTransform`. For a real test, we need to hide that fact to the `tangent` method.
+         */
+        transform = new MathTransformWrapper(transform);
+        final LinearTransform result = MathTransforms.tangent(transform, tangentPoint);
+        assertNotSame(transform, result);
+        assertMatrixEquals("tangent", expected, result.getMatrix(), STRICT);
+    }
+
+    /**
+     * Tests {@link MathTransforms#tangent(MathTransform, DirectPosition)} of a non-linear transform.
+     *
+     * @throws TransformException if an error occurred while computing the derivative.
+     */
+    @Test
+    @DependsOnMethod("testTangentOfLinear")
+    public void testTangent() throws TransformException {
         final DirectPosition pos = new GeneralDirectPosition(3, 1.5, 6);
         MathTransform tr = MathTransforms.linear(new Matrix4(
             0,  5,  0,  9,
@@ -185,11 +216,11 @@ public final class MathTransformsTest extends TestCase {
             0,  0, 12, -3,
             0,  0,  0,  1));
 
-        LinearTransform linear = MathTransforms.linear(tr, pos);
+        LinearTransform linear = MathTransforms.tangent(tr, pos);
         assertSame("Linear transform shall be returned unchanged.", tr, linear);
 
         tr = MathTransforms.concatenate(nonLinear3D(), tr);
-        linear = MathTransforms.linear(tr, pos);
+        linear = MathTransforms.tangent(tr, pos);
         assertNotSame(tr, linear);
         /*
          * Transformation using above approximation shall produce the same result than the original
@@ -213,34 +244,4 @@ public final class MathTransformsTest extends TestCase {
         assertInstanceOf("2D", MathTransform2D.class, tr);
         assertFalse("isIdentity", tr.isIdentity());
     }
-
-    /**
-     * Tests {@link MathTransforms#tangent(MathTransform, DirectPosition)}.
-     *
-     * @throws TransformException should never happen since this test uses a linear transform.
-     */
-    @Test
-    public void testTangent() throws TransformException {
-        /*
-         * The random values in Matrix and DirectPosition below does not matter; we will just verify
-         * that we get the same values in final result. In particular the `tangentPoint` coordinates
-         * are ignored since we use a linear transform for this test.
-         */
-        final Matrix expected = Matrices.create(3, 4, new double[] {
-            -4, 5, 7, 2,
-             3, 4, 2, 9,
-             0, 0, 0, 1,
-        });
-        final DirectPosition tangentPoint = new GeneralDirectPosition(3, 8, 7);
-        MathTransform transform = MathTransforms.linear(expected);
-        assertSame(transform, MathTransforms.tangent(transform, tangentPoint));
-        /*
-         * Above test returned the transform directly because it found that it was already an instance
-         * of `LinearTransform`. For a real test, we need to hide that fact to the `tangent` method.
-         */
-        transform = new MathTransformWrapper(transform);
-        final LinearTransform result = MathTransforms.tangent(transform, tangentPoint);
-        assertNotSame(transform, result);
-        assertMatrixEquals("tangent", expected, result.getMatrix(), STRICT);
-    }
 }