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 <[email protected]>
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);
+ }
}