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 faa3997687 Verify in a test that extended precision is propagated 
through MathTransform inversion and concatenation.
faa3997687 is described below

commit faa399768721c37113e9b66e41172a37b9f050db
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Wed Jan 11 12:04:38 2023 +0100

    Verify in a test that extended precision is propagated through 
MathTransform inversion and concatenation.
    
    https://issues.apache.org/jira/browse/SIS-568
---
 .../transform/ProjectiveTransformTest.java         | 83 ++++++++++++++++------
 .../transform/TransformResultComparator.java       | 46 +++++++++++-
 2 files changed, 106 insertions(+), 23 deletions(-)

diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java
index 45e55585a9..f7c03e26d8 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java
@@ -26,8 +26,12 @@ import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.matrix.Matrix2;
+import org.apache.sis.referencing.operation.matrix.Matrix4;
+import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix;
 import org.apache.sis.internal.referencing.provider.Affine;
+import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.parameter.Parameterized;
+import org.apache.sis.math.Fraction;
 
 // Test imports
 import org.opengis.test.Validators;
@@ -75,35 +79,35 @@ public class ProjectiveTransformTest extends 
AffineTransformTest {
         super(new MathTransformFactoryBase() {
             @Override
             public MathTransform createAffineTransform(final Matrix matrix) {
-                final ProjectiveTransform pt;
+                final ProjectiveTransform tested;
                 if (matrix.getNumRow() == 3 && matrix.getNumCol() == 3) {
-                    pt = new ProjectiveTransform2D(matrix);
+                    tested = new ProjectiveTransform2D(matrix);
                 } else {
-                    pt = new ProjectiveTransform(matrix);
+                    tested = new ProjectiveTransform(matrix);
                 }
-                MathTransform tr = pt.optimize();
-                if (tr != pt) {
+                final MathTransform reference = tested.optimize();
+                if (tested != reference) {
                     /*
                      * Opportunistically tests `ScaleTransform` together with 
`ProjectiveTransform`.
                      * We take `ScaleTransform` as a reference implementation 
because it is simpler.
                      */
-                    tr = new TransformResultComparator(tr, pt, 1E-12);
+                    return new TransformResultComparator(reference, tested, 
1E-12);
                 }
-                return tr;
+                return tested;
             }
         });
     }
 
     /**
-     * Returns the transform without {@link TransformResultComparator} wrapper.
-     * The transform is the one computed by {@link 
ProjectiveTransform#optimize()}.
+     * Whether the post-test validation should skip its check for a transform 
of the given dimension.
+     * {@code ProjectiveTransformTest} needs to skip the case for dimension 1 
because there is no
+     * {@code ProjectiveTransform1D} class. However, {@link 
LinearTransformTest} can do the check
+     * for all dimensions.
+     *
+     * @see #ensureImplementRightInterface()
      */
-    private MathTransform getOptimizedTransform() {
-        MathTransform tr = transform;
-        if (tr instanceof TransformResultComparator) {
-            tr = ((TransformResultComparator) tr).reference;
-        }
-        return tr;
+    boolean skipInterfaceCheckForDimension(final int dimension) {
+        return dimension == 1;
     }
 
     /*
@@ -170,13 +174,48 @@ public class ProjectiveTransformTest extends 
AffineTransformTest {
     }
 
     /**
-     * {@code true} if {@link #ensureImplementRightInterface()} should skip 
its check for a transform
-     * of the given dimension. {@code ProjectiveTransformTest} needs to skip 
the case for dimension 1
-     * because there is no {@code ProjectiveTransform1D} class. However, 
{@link LinearTransformTest}
-     * can check for all dimensions.
+     * Tests the concatenation of transforms that would result in rounding 
errors
+     * in extended-precision matrix operations were not used.
+     *
+     * @throws FactoryException if the transform cannot be created.
+     * @throws TransformException if a coordinate conversion failed.
+     *
+     * @since 1.4
      */
-    boolean skipInterfaceCheckForDimension(final int dimension) {
-        return dimension == 1;
+    @Test
+    public void testRoundingErrors() throws FactoryException, 
TransformException {
+        final Matrix4 num = new Matrix4(); num.m00 =  2; num.m11 = 3.25; 
num.m22 = -17;
+        final Matrix4 den = new Matrix4(); den.m00 = 37; den.m11 = 1000; 
den.m22 = 127;
+        transform = TransformResultComparator.concatenate(
+                mtFactory.createAffineTransform(num),
+                mtFactory.createAffineTransform(den).inverse(),
+                mtFactory);
+        matrix = ((LinearTransform) getOptimizedTransform()).getMatrix();
+        /*
+         * Verify matrix elements after inversion and concatenation.
+         * The extended precision types should be used.
+         */
+        final ExtendedPrecisionMatrix m = (ExtendedPrecisionMatrix) matrix;
+        assertEquals(new Fraction(2, 37),                 
m.getElementOrNull(0,0));
+        assertEquals(DoubleDouble.of(325).divide(100000), 
m.getElementOrNull(1,1));
+        assertEquals(new Fraction(-17, 127),              
m.getElementOrNull(2,2));
+        /*
+         * Test a transformation, expecting exact result.
+         */
+        verifyTransform(new double[] {2645.5,  19500, 2222.5},
+                        new double[] {   143, 63.375, -297.5});
+    }
+
+    /**
+     * Returns the transform without {@link TransformResultComparator} wrapper.
+     * The transform is the one computed by {@link 
ProjectiveTransform#optimize()}.
+     */
+    private MathTransform getOptimizedTransform() {
+        MathTransform tr = transform;
+        while (tr instanceof TransformResultComparator) {
+            tr = ((TransformResultComparator) tr).reference;
+        }
+        return tr;
     }
 
     /**
@@ -193,7 +232,7 @@ public class ProjectiveTransformTest extends 
AffineTransformTest {
          * possible types, so the test would fail if checking its result. More 
complete optimizations
          * are tested in the `LinearTransformTest` subclass.
          */
-        if (transform instanceof TransformResultComparator) {
+        while (transform instanceof TransformResultComparator) {
             transform = ((TransformResultComparator) transform).tested;
         }
         /*
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformResultComparator.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformResultComparator.java
index 03824c865a..f142b8f6ad 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformResultComparator.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformResultComparator.java
@@ -21,17 +21,19 @@ import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.referencing.operation.NoninvertibleTransformException;
 
 import static org.opengis.test.Assert.*;
+import org.opengis.util.FactoryException;
 
 
 /**
  * Compares the results of two {@link MathTransform} implementations.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
  * @since   0.7
  */
 final class TransformResultComparator implements MathTransform {
@@ -182,4 +184,46 @@ final class TransformResultComparator implements 
MathTransform {
     public String toWKT() {
         return tested.toWKT();
     }
+
+    /**
+     * Concatenates two transforms that may be comparators.
+     * Instances of {@code TransformResultComparator} are unwrapped before 
concatenation,
+     * then the concatenation result is re-wrapped in {@code 
TransformResultComparator}.
+     *
+     * @param  tr1      the first math transform.
+     * @param  tr2      the second math transform.
+     * @param  factory  the factory which is (indirectly) invoking this 
method, or {@code null} if none.
+     * @return the concatenated transform.
+     */
+    static MathTransform concatenate(final MathTransform tr1, final 
MathTransform tr2,
+            final MathTransformFactory factory) throws FactoryException
+    {
+        double tolerance;
+        final MathTransform t1, r1, t2, r2;
+        if (tr1 instanceof TransformResultComparator) {
+            final TransformResultComparator c = (TransformResultComparator) 
tr1;
+            t1 = c.tested;
+            r1 = c.reference;
+            tolerance = c.tolerance;
+        } else {
+            t1 = r1 = tr1;
+            tolerance = 0;
+        }
+        if (tr2 instanceof TransformResultComparator) {
+            final TransformResultComparator c = (TransformResultComparator) 
tr2;
+            t2 = c.tested;
+            r2 = c.reference;
+            if (c.tolerance > tolerance) {
+                tolerance = c.tolerance;
+            }
+        } else {
+            t2 = r2 = tr2;
+        }
+        final MathTransform tested = ConcatenatedTransform.create(t1, t2, 
factory);
+        if (r1 == t1 && r2 == t2) {
+            return tested;
+        }
+        final MathTransform reference = ConcatenatedTransform.create(r1, r2, 
factory);
+        return new TransformResultComparator(reference, tested, tolerance);
+    }
 }

Reply via email to