sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1825890 - in /sis/branches/JDK8/core/sis-referencing/src: main/java/org/apache/sis/referencing/operation/transform/ test/java/org/apache/sis/referencing/operation/transform/
Date Mon, 05 Mar 2018 13:11:24 GMT
Author: desruisseaux
Date: Mon Mar  5 13:11:24 2018
New Revision: 1825890

URL: http://svn.apache.org/viewvc?rev=1825890&view=rev
Log:
Implement the inverse of SpecializableTransform (needed for quasi-regular localisation grids).

Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateDomain.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/SpecializableTransformTest.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1825890&r1=1825889&r2=1825890&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
[UTF-8] Mon Mar  5 13:11:24 2018
@@ -1003,11 +1003,14 @@ public abstract class AbstractMathTransf
      */
     protected abstract class Inverse extends AbstractMathTransform implements Serializable
{
         /**
-         * Serial number for inter-operability with different versions. This serial number
is
-         * especially important for inner classes, since the default {@code serialVersionUID}
-         * computation will not produce consistent results across implementations of different
-         * Java compiler. This is because different compilers may generate different names
for
-         * synthetic members used in the implementation of inner classes. See:
+         * Serial number for inter-operability with different versions.
+         *
+         * Note: the default {@code serialVersionUID} computation does not produce consistent
results
+         * across implementation of different Java compilers because different compilers
may generate
+         * different names for synthetic members used in the implementation of inner classes.
 Fixing
+         * the serial number here avoid this issue, but may create new problems since reading
a class
+         * serialized with an Apache SIS library compiled with a different compiler may not
recognize
+         * the fields that are named differently. See:
          *
          * http://developer.java.sun.com/developer/bugParade/bugs/4211550.html
          */

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java?rev=1825890&r1=1825889&r2=1825890&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java
[UTF-8] Mon Mar  5 13:11:24 2018
@@ -19,12 +19,14 @@ package org.apache.sis.referencing.opera
 import java.util.Map;
 import java.util.Arrays;
 import java.util.Objects;
+import java.io.Serializable;
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
 import org.apache.sis.internal.referencing.DirectPositionView;
 import org.apache.sis.internal.referencing.Resources;
@@ -45,7 +47,7 @@ import org.apache.sis.util.ArraysExt;
  * @since   1.0
  * @module
  */
-class SpecializableTransform extends AbstractMathTransform {
+class SpecializableTransform extends AbstractMathTransform implements Serializable {
     /**
      * The generic transform to use if there is no suitable specialization.
      */
@@ -56,13 +58,27 @@ class SpecializableTransform extends Abs
      * Contains also a chain of {@code SubArea}s fully included in this area.
      * Shall be unmodified after {@link SpecializableTransform} construction.
      */
+    @SuppressWarnings("CloneableClassWithoutClone")                             // We will
not use clone().
     private static final class SubArea extends GeneralEnvelope {
         /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = 4197316795428796526L;
+
+        /**
          * The transform to apply in this area.
          */
         final MathTransform transform;
 
         /**
+         * The inverse of the transform, computed when first needed.
+         * Synchronization for multi-threading is done (indirectly) in {@link SpecializableTransform#inverse()}.
+         *
+         * @see #createInverse(SubArea)
+         */
+        MathTransform inverse;
+
+        /**
          * Specialization, or {@code null} if none. If non-null, that sub-area shall be fully
included
          * in this {@code SubArea}. The specialization may itself contain another specialization.
This
          * form a chain from this wider area to smallest area, where each step is a smaller
area.
@@ -121,6 +137,17 @@ class SpecializableTransform extends Abs
         }
 
         /**
+         * Creates the inverse transforms. This method should be invoked only once when first
needed
+         * in a block synchronized (indirectly) by {@link SpecializableTransform#inverse()}.
+         */
+        static void createInverse(SubArea area) throws NoninvertibleTransformException {
+            do {
+                area.inverse = area.transform.inverse();
+                area = area.specialization;
+            } while (area != null);
+        }
+
+        /**
          * Returns the area that contains the given position, or {@code null} if none.
          * This method may be replaced by an R-Tree in a future Apache SIS version.
          */
@@ -147,11 +174,24 @@ class SpecializableTransform extends Abs
             while (area.contains(pos)) {
                 found = area;
                 area = area.specialization;
+                if (area == null) break;
             }
             return found;
         }
 
         /**
+         * Formats the given area and its transform as a pseudo-WKT.
+         * For {@link SpecializableTransform#formatTo(Formatter)} implementation only.
+         */
+        static void format(SubArea area, final Formatter formatter) {
+            while (area != null) {
+                formatter.newLine(); formatter.append(area);
+                formatter.newLine(); formatter.append(area.transform);
+                area = area.specialization;
+            }
+        }
+
+        /**
          * For {@link SpecializableTransform#computeHashCode()} implementation.
          */
         @Override
@@ -194,7 +234,18 @@ class SpecializableTransform extends Abs
     private final SubArea[] domains;
 
     /**
+     * The inverse of this transform, computed when first needed.
+     * Part of serialization for avoiding rounding error issues.
+     *
+     * @see #inverse()
+     */
+    private MathTransform inverse;
+
+    /**
      * Creates a new transform with the given generic transform and some amount of specializations.
+     *
+     * @param  generic  the generic transform to use if there is no suitable specialization.
+     * @param  specializations  more accurate transforms available in sub-areas.
      */
     SpecializableTransform(final MathTransform generic, final Map<Envelope,MathTransform>
specializations)
             throws InvalidGeodeticParameterException
@@ -299,8 +350,9 @@ next:   for (final Map.Entry<Envelope,Ma
         if (tr instanceof AbstractMathTransform) {
             return ((AbstractMathTransform) tr).transform(srcPts, srcOff, dstPts, dstOff,
derivate);
         } else {
+            Matrix derivative = derivate ? tr.derivative(pos) : null;       // Must be before
transform(srcPts, …).
             tr.transform(srcPts, srcOff, dstPts, dstOff, 1);
-            return derivate ? tr.derivative(pos) : null;
+            return derivative;
         }
     }
 
@@ -369,7 +421,7 @@ next:   for (final Map.Entry<Envelope,Ma
      * This method delegates to the most specialized transform.
      */
     @Override
-    public void transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts)
+    public final void transform(double[] srcPts, int srcOff, final double[] dstPts, int dstOff,
final int numPts)
             throws TransformException
     {
         int srcInc = getSourceDimensions();
@@ -402,7 +454,7 @@ next:   for (final Map.Entry<Envelope,Ma
      * This method delegates to the most specialized transform.
      */
     @Override
-    public void transform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts)
+    public final void transform(float[] srcPts, int srcOff, final float[] dstPts, int dstOff,
final int numPts)
             throws TransformException
     {
         int srcInc = getSourceDimensions();
@@ -435,8 +487,9 @@ next:   for (final Map.Entry<Envelope,Ma
      * with single {@code transform(…)} calls for coordinate sequences as long as possible.
      */
     @Override
-    public void transform(final double[] srcPts, int srcOff,
-                          final float [] dstPts, int dstOff, int numPts) throws TransformException
+    public final void transform(final double[] srcPts, final int srcOff,
+                                final float [] dstPts, final int dstOff,
+                                final int numPts) throws TransformException
     {
         final int srcDim = getSourceDimensions();
         final int dstDim = getTargetDimensions();
@@ -450,9 +503,9 @@ next:   for (final Map.Entry<Envelope,Ma
      * with single {@code transform(…)} calls for coordinate sequences as long as possible.
      */
     @Override
-    public void transform(final float [] srcPts, int srcOff,
-                          final double[] dstPts, int dstOff, int numPts)
-            throws TransformException
+    public final void transform(final float [] srcPts, final int srcOff,
+                                final double[] dstPts, final int dstOff,
+                                final int numPts) throws TransformException
     {
         final int srcDim = getSourceDimensions();
         final int dstDim = getTargetDimensions();
@@ -466,7 +519,7 @@ next:   for (final Map.Entry<Envelope,Ma
      * This method is invoked by {@link #hashCode()} when first needed.
      */
     @Override
-    protected int computeHashCode() {
+    protected final int computeHashCode() {
         return super.computeHashCode() + 7*generic.hashCode() ^ Arrays.hashCode(domains);
     }
 
@@ -474,7 +527,7 @@ next:   for (final Map.Entry<Envelope,Ma
      * Compares the specified object with this math transform for equality.
      */
     @Override
-    public boolean equals(final Object object, final ComparisonMode mode) {
+    public final boolean equals(final Object object, final ComparisonMode mode) {
         if (super.equals(object, mode)) {
             final SpecializableTransform other = (SpecializableTransform) object;
             return Utilities.deepEquals(generic, other.generic, mode) &&
@@ -493,13 +546,229 @@ next:   for (final Map.Entry<Envelope,Ma
      * @return the WKT element name, which is {@code "Specializable_MT"}.
      */
     @Override
-    protected String formatTo(final Formatter formatter) {
-        for (final SubArea domain : domains) {
-            formatter.newLine(); formatter.append(generic);
-            formatter.newLine(); formatter.append(domain);
-            formatter.newLine(); formatter.append(domain.transform);
+    protected final String formatTo(final Formatter formatter) {
+        formatter.newLine();
+        formatter.append(generic);
+        for (SubArea domain : domains) {
+            SubArea.format(domain, formatter);
         }
         formatter.setInvalidWKT(SpecializableTransform.class, null);
         return "Specializable_MT";
     }
+
+    /**
+     * Returns the inverse of this transform.
+     */
+    @Override
+    public final synchronized MathTransform inverse() throws NoninvertibleTransformException
{
+        if (inverse == null) {
+            inverse = new Inverse(generic);
+        }
+        return inverse;
+    }
+
+    /**
+     * The inverse of {@link SpecializableTransform}.
+     */
+    private final class Inverse extends AbstractMathTransform.Inverse {
+        /**
+         * The inverse of {@link SpecializableTransform#generic}.
+         */
+        private final MathTransform generic;
+
+        /**
+         * Creates the inverse of a specialized transform having the given properties.
+         */
+        Inverse(final MathTransform generic) throws NoninvertibleTransformException {
+            this.generic = generic.inverse();
+            for (final SubArea domain : domains) {
+                SubArea.createInverse(domain);
+            }
+        }
+
+        /**
+         * Inverse transforms the specified {@code ptSrc} and stores the result in {@code
ptDst}.
+         */
+        @Override
+        public final DirectPosition transform(final DirectPosition ptSrc, DirectPosition
ptDst) throws TransformException {
+            final double[] source = ptSrc.getCoordinate();      // Needs to be first in case
ptDst overwrites ptSrc.
+            ptDst = generic.transform(ptSrc, ptDst);
+            final SubArea domain = SubArea.find(domains, ptDst);
+            if (domain != null) {
+                ptDst = domain.inverse.transform(new DirectPositionView.Double(source, 0,
source.length), ptDst);
+            }
+            return ptDst;
+        }
+
+        /**
+         * Gets the inverse derivative of this transform at a point.
+         * This method is overridden for consistency.
+         */
+        @Override
+        public final Matrix derivative(final DirectPosition point) throws TransformException
{
+            return transform(point.getCoordinate(), 0, null, 0, true);
+        }
+
+        /**
+         * Inverse transforms a single coordinate point in an array, and optionally computes
the transform
+         * derivative at that location.
+         */
+        @Override
+        public final Matrix transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff,
final boolean derivate)
+                throws TransformException
+        {
+            final int srcInc = generic.getSourceDimensions();
+            final int dstInc = generic.getTargetDimensions();
+            if (dstPts == null) {
+                dstPts = new double[dstInc];                    // Needed for checking if
inside a sub-area.
+                dstOff = 0;
+            } else if (srcPts == dstPts && srcOff + srcInc > dstOff &&
srcOff < dstOff + dstInc) {
+                srcPts = Arrays.copyOfRange(srcPts, srcOff, srcInc);
+                srcOff = 0;
+            }
+            /*
+             * Above 'srcPts' dhould keep the source coordinates unchanged even if the source
and destination
+             * given in arguments overlap.  We need this stability because the source coordinates
may be used
+             * twice, if 'secondTry' become true.
+             */
+            MathTransform tr = generic;
+            boolean secondTry = false;
+            Matrix derivative;
+            do {
+                if (tr instanceof AbstractMathTransform) {
+                    derivative = ((AbstractMathTransform) tr).transform(srcPts, srcOff, dstPts,
dstOff, derivate);
+                } else {
+                    tr.transform(srcPts, srcOff, dstPts, dstOff, 1);
+                    derivative = derivate ? tr.derivative(new DirectPositionView.Double(srcPts,
srcOff, srcInc)) : null;
+                }
+                if (secondTry) break;
+                final SubArea domain = SubArea.find(domains, new DirectPositionView.Double(dstPts,
dstOff, dstInc));
+                if (domain != null) {
+                    tr = domain.inverse;
+                    secondTry = true;
+                }
+            } while (secondTry);
+            return derivative;
+        }
+
+        /**
+         * Invoked for transforming, then verifying if more appropriate transform exists
for the result.
+         * This implementation is similar to the algorithm applied by {@link SpecializableTransform}
parent
+         * class, except that {@link SubArea} is verified <em>after</em> transformations
instead than before.
+         */
+        private void transform(final TransformCall transform, final double[] dstPts,
+                int srcOff, int dstOff, int srcInc, int dstInc, int numPts) throws TransformException
+        {
+            final SubArea[] domains = SpecializableTransform.this.domains;
+            transform.apply(generic, srcOff, dstOff, numPts);
+            final DirectPositionView dst = new DirectPositionView.Double(dstPts, dstOff,
dstInc);
+            while (numPts > 0) {
+                SubArea domain = SubArea.find(domains, dst);
+                if (domain == null) {
+                    dst.offset += dstInc;
+                    numPts--;
+                    continue;
+                }
+                do {
+                    final SubArea specialized = domain;             // Contains the specialized
transform to use.
+                    int num = (dst.offset - dstOff) / dstInc;       // Number of points that
are not retransformeD.
+                    srcOff += num * srcInc;                         // Skip the source coordinates
that are not retransformed.
+                    dstOff = dst.offset;                            // Destination index
of the first coordinate to retransform.
+                    do {
+                        dst.offset += dstInc;                       // Destination index
after the last coordinate to transform.
+                        if (--numPts <= 0) {
+                            domain = null;
+                            break;
+                        }
+                        domain = SubArea.find(domain, dst);
+                    } while (domain == specialized);
+                    num = (dst.offset - dstOff) / dstInc;           // Number of points to
retransform.
+                    transform.apply(specialized.inverse, srcOff, dstOff, num);
+                    srcOff += srcInc * num;
+                    dstOff = dst.offset;
+                } while (domain != null);
+            }
+        }
+
+        /**
+         * Inverse transforms a list of coordinate points.
+         * The transformed points are written directly in the destination array.
+         */
+        @Override
+        public void transform(double[] srcPts, int srcOff, final double[] dstPts, final int
dstOff, final int numPts)
+                throws TransformException
+        {
+            if (numPts <= 0) return;
+            final int srcInc = generic.getSourceDimensions();
+            final int dstInc = generic.getTargetDimensions();
+            if (srcPts == dstPts) {
+                final int srcEnd = srcOff + numPts*srcInc;
+                if (srcEnd > dstOff || dstOff + numPts*dstInc > srcOff) {
+                    srcPts = Arrays.copyOfRange(srcPts, srcOff, srcEnd);
+                    srcOff = 0;
+                }
+            }
+            final double[] refPts = srcPts;
+            transform((tr, src, dst, num) -> tr.transform(refPts, src, dstPts, dst, num),
+                      dstPts, srcOff, dstOff, srcInc, dstInc, numPts);
+        }
+
+        /**
+         * Inverse transforms a list of coordinate points. This method uses an temporary
{@code double[]} buffer
+         * for testing {@code SubArea} inclusion with full precision before to cast to {@code
float} values.
+         */
+        @Override
+        public void transform(final float[] srcPts, int srcOff,
+                              final float[] dstPts, int dstOff, int numPts)
+                throws TransformException
+        {
+            if (numPts <= 0) return;
+            final int srcInc = generic.getSourceDimensions();
+            final int dstInc = generic.getTargetDimensions();
+            final double[] buffer = new double[numPts * dstInc];
+            transform((tr, src, dst, num) -> tr.transform(srcPts, src, buffer, dst, num),
+                      buffer, srcOff, 0, srcInc, dstInc, numPts);
+
+            numPts *= dstInc;
+            for (int i=0; i<numPts; i++) {
+                dstPts[dstOff++] = (float) buffer[i];
+            }
+        }
+
+        /**
+         * Inverse transforms a list of coordinate points. This method uses an temporary
{@code double[]} buffer
+         * for testing {@code SubArea} inclusion with full precision before to cast to {@code
float} values.
+         */
+        @Override
+        public void transform(final double[] srcPts, int srcOff,
+                              final float [] dstPts, int dstOff, int numPts) throws TransformException
+        {
+            if (numPts <= 0) return;
+            final int srcInc = generic.getSourceDimensions();
+            final int dstInc = generic.getTargetDimensions();
+            final double[] buffer = new double[numPts * dstInc];
+            transform((tr, src, dst, num) -> tr.transform(srcPts, src, buffer, dst, num),
+                      buffer, srcOff, 0, srcInc, dstInc, numPts);
+
+            numPts *= dstInc;
+            for (int i=0; i<numPts; i++) {
+                dstPts[dstOff++] = (float) buffer[i];
+            }
+        }
+
+        /**
+         * Inverse transforms a list of coordinate points.
+         * The transformed points are written directly in the destination array.
+         */
+        @Override
+        public void transform(final float [] srcPts, int srcOff,
+                              final double[] dstPts, int dstOff, int numPts) throws TransformException
+        {
+            if (numPts <= 0) return;
+            final int srcInc = generic.getSourceDimensions();
+            final int dstInc = generic.getTargetDimensions();
+            transform((tr, src, dst, num) -> tr.transform(srcPts, src, dstPts, dst, num),
+                      dstPts, srcOff, dstOff, srcInc, dstInc, numPts);
+        }
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateDomain.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateDomain.java?rev=1825890&r1=1825889&r2=1825890&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateDomain.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/CoordinateDomain.java
[UTF-8] Mon Mar  5 13:11:24 2018
@@ -32,7 +32,7 @@ import org.apache.sis.referencing.datum.
  * This class can generate random number suitable for their domain.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.6
+ * @version 1.0
  * @since   0.5
  * @module
  */
@@ -215,6 +215,14 @@ public strictfp class CoordinateDomain {
             -10, 10);
 
     /**
+     * Values in the -100 to 100 range in all dimensions.
+     */
+    public static final CoordinateDomain RANGE_100 = new CoordinateDomain(
+            -100, 100,
+            -100, 100,
+            -100, 100);
+
+    /**
      * The domain of the coordinates to test.
      */
     final double xmin, xmax, ymin, ymax, zmin, zmax;

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/SpecializableTransformTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/SpecializableTransformTest.java?rev=1825890&r1=1825889&r2=1825890&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/SpecializableTransformTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/SpecializableTransformTest.java
[UTF-8] Mon Mar  5 13:11:24 2018
@@ -59,20 +59,79 @@ public final strictfp class Specializabl
     }
 
     /**
-     * Tests consistency between different {@code transform(…)} methods.
+     * Invokes {@link #verifyDerivative(double...)} for all points in the given array.
+     */
+    private void verifyDerivatives(final double[] coordinates) throws TransformException
{
+        tolerance = 1E-10;
+        derivativeDeltas = new double[] {0.001};
+        final double[] point = new double[2];
+        for (int i=0; i < coordinates.length; i += 2) {
+            System.arraycopy(coordinates, i, point, 0, 2);
+            verifyDerivative(point);
+        }
+    }
+
+    /**
+     * Verifies the transform with a few hard-coded points. The point are selected in order
to avoid
+     * situations where two transforms could calculate the same point (because of the way
we created
+     * our test transform).
      *
      * @throws InvalidGeodeticParameterException if {@link SpecializableTransform} constructor
reject a parameter.
      * @throws TransformException if a transformation failed.
      */
     @Test
-    public void testConsistency() throws InvalidGeodeticParameterException, TransformException
{
+    public void testTransform() throws InvalidGeodeticParameterException, TransformException
{
+        //                      ┌─── generic ───┐┌───── Special.
1 ──────┐┌──── Special. 2 ─────┐
+        final double[] source = {8,  2,  4,  5,   3,    2,    2,    -2,    -2,    0,   1,
   0,   8,  3};
+        final double[] target = {80, 20, 40, 50,  30.1, 20.1, 20.1, -19.9, -19.8, 0.2, 10.2,
0.2, 80, 30};
+
+        tolerance = 1E-14;
         transform = create();
+        verifyTransform(source, target);
+        verifyDerivatives(source);
+
+        tolerance = 1E-14;
+        transform = transform.inverse();
+        verifyTransform(target, source);
+        verifyDerivatives(target);
+    }
+
+    /**
+     * Tests consistency between different {@code transform(…)} methods in forward transforms.
+     * This test uses a fixed sequence of random numbers. We fix the sequence because the
transform
+     * used in this test is {@linkplain org.apache.sis.math.FunctionProperty#SURJECTIVE surjective}:
+     * some target coordinates can be created from more than one source coordinates. We need
to be
+     * "lucky" enough for not testing a point in this case, otherwise the result is undetermined.
+     *
+     * @throws InvalidGeodeticParameterException if {@link SpecializableTransform} constructor
reject a parameter.
+     * @throws TransformException if a transformation failed.
+     */
+    @Test
+    public void testForwardConsistency() throws InvalidGeodeticParameterException, TransformException
{
+        transform = create();
+        tolerance = 1E-14;
         isDerivativeSupported = false;          // Actually supported, but our test transform
has discontinuities.
-        isInverseTransformSupported = false;
         verifyInDomain(CoordinateDomain.RANGE_10, -672445632505596619L);
     }
 
     /**
+     * Tests consistency between different {@code transform(…)} methods in inverse transforms.
+     * This test uses a fixed sequence of random numbers. We fix the sequence because the
transform
+     * used in this test is {@linkplain org.apache.sis.math.FunctionProperty#SURJECTIVE surjective}.
+     * See {@link #testForwardConsistency()}.
+     *
+     * @throws InvalidGeodeticParameterException if {@link SpecializableTransform} constructor
reject a parameter.
+     * @throws TransformException if a transformation failed.
+     */
+    @Test
+    public void testInverseConsistency() throws InvalidGeodeticParameterException, TransformException
{
+        transform = create().inverse();
+        tolerance = 1E-12;
+        isDerivativeSupported = false;          // Actually supported, but our test transform
has discontinuities.
+        verifyInDomain(CoordinateDomain.RANGE_100, 4308397764777385180L);
+    }
+
+    /**
      * Tests the pseudo Well-Known Text formatting.
      * The format used by this transform is non-standard and may change in any future Apache
SIS version.
      *
@@ -92,6 +151,13 @@ public final strictfp class Specializabl
                 "    PARAMETER[“elt_0_0”, 10.0],\n" +
                 "    PARAMETER[“elt_0_2”, 0.1],\n" +
                 "    PARAMETER[“elt_1_1”, 10.0],\n" +
-                "    PARAMETER[“elt_1_2”, 0.1]]]");
+                "    PARAMETER[“elt_1_2”, 0.1]],\n" +
+                "  DOMAIN[-3 -1,\n" +
+                "          2  1],\n" +
+                "  PARAM_MT[“Affine”,\n" +
+                "    PARAMETER[“elt_0_0”, 10.0],\n" +
+                "    PARAMETER[“elt_0_2”, 0.2],\n" +
+                "    PARAMETER[“elt_1_1”, 10.0],\n" +
+                "    PARAMETER[“elt_1_2”, 0.2]]]");
     }
 }



Mime
View raw message