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 <martin.desruisse...@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); - } }