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 2018/11/29 18:09:16 UTC

[sis] branch geoapi-4.0 updated (48dbf45 -> 616a963)

This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git.


    from 48dbf45  Add more tests for PassThroughTransform. This forced us to revisit the 'transform' implementation for overlapping arrays. This commit may make https://issues.apache.org/jira/browse/SIS-318 irrelevant.
     new 1c11912  Trivial method renaming for consistency with https://issues.apache.org/jira/browse/SIS-318.
     new 576d9a2  Move the special case as an implementation of tryConcatenate(…).
     new 29e6779  Reduce the amount of transform to inverse when ConcatenatedTransform verify if two transforms are the inverse of each other.
     new f248e6b  Add a test verifying more deeply PassThroughTransform.tryConcatenate(…) work.
     new 616a963  Initial draft of SampleDimension class for coverages.

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/sis/coverage/SampleDimension.java   | 161 ++++++++++++++++++++
 .../sis/coverage/{grid => }/package-info.java      |   8 +-
 .../apache/sis/coverage/grid/GridGeometryTest.java |  47 +++++-
 .../transform/AbstractLinearTransform.java         |  13 +-
 .../operation/transform/AbstractMathTransform.java |  56 ++++++-
 .../operation/transform/ConcatenatedTransform.java | 163 ++++++++++-----------
 .../operation/transform/PassThroughTransform.java  |  25 +++-
 .../operation/transform/TransferFunction.java      |   5 +-
 .../transform/PassThroughTransformTest.java        |  51 +++++++
 9 files changed, 427 insertions(+), 102 deletions(-)
 create mode 100644 core/sis-raster/src/main/java/org/apache/sis/coverage/SampleDimension.java
 copy core/sis-raster/src/main/java/org/apache/sis/coverage/{grid => }/package-info.java (71%)


[sis] 04/05: Add a test verifying more deeply PassThroughTransform.tryConcatenate(…) work.

Posted by de...@apache.org.
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

commit f248e6b621ccf08e32a4db21b06810dd9fe86eba
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu Nov 29 18:08:42 2018 +0100

    Add a test verifying more deeply PassThroughTransform.tryConcatenate(…) work.
---
 .../apache/sis/coverage/grid/GridGeometryTest.java | 47 +++++++++++++++++---
 .../transform/PassThroughTransformTest.java        | 51 ++++++++++++++++++++++
 2 files changed, 93 insertions(+), 5 deletions(-)

diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
index 2da4f77..28ebd88 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
@@ -176,7 +176,7 @@ public final strictfp class GridGeometryTest extends TestCase {
                     DimensionNameType.VERTICAL,
                     DimensionNameType.TIME
                 },
-                new long[] {0,     0, 2, 6},
+                new long[] {  0,   0, 2, 6},
                 new long[] {100, 200, 3, 9}, false);
         final MathTransform horizontal = MathTransforms.linear(new Matrix3(
                 0.5, 0,    12,
@@ -248,9 +248,46 @@ public final strictfp class GridGeometryTest extends TestCase {
         envelope = new GeneralEnvelope(HardCodedCRS.WGS84);
         envelope.setRange(0, -70.001, +80.002);
         envelope.setRange(1,   4.997,  15.003);
-        final GridExtent extent = grid.getExtent(envelope);
-        assertExtentEquals(
-                new long[] {370,  40,  4},
-                new long[] {389, 339, 10}, extent);
+        assertExtentEquals(new long[] {370,  40,  4},
+                           new long[] {389, 339, 10}, grid.getExtent(envelope));
+    }
+
+    /**
+     * Tests {@link GridGeometry#getExtent(Envelope)} with a non-linear "grid to CRS" transform.
+     *
+     * @throws TransformException if an error occurred while using the "grid to CRS" transform.
+     */
+    @Test
+    @DependsOnMethod("testNonLinear")
+    public void testGetExtentNonLinear() throws TransformException {
+        final GridExtent extent = new GridExtent(
+                new DimensionNameType[] {
+                    DimensionNameType.COLUMN,
+                    DimensionNameType.ROW,
+                    DimensionNameType.VERTICAL
+                },
+                new long[] {  0,  0, 2},
+                new long[] {180, 90, 5}, false);
+        final MathTransform linear = MathTransforms.linear(new Matrix4(
+                2, 0, 0, -180,
+                0, 2, 0,  -90,
+                0, 0, 5,   10,
+                0, 0, 0,    1));
+        final MathTransform latitude  = MathTransforms.interpolate(new double[] {0, 20, 50, 70, 90}, new double[] {-90, -45, 0, 45, 90});
+        final MathTransform gridToCRS = MathTransforms.concatenate(linear, MathTransforms.passThrough(1, latitude, 1));
+        final GridGeometry  grid      = new GridGeometry(extent, PixelInCell.CELL_CENTER, gridToCRS, HardCodedCRS.WGS84_3D);
+        /*
+         * Following tests is similar to the one executed in testGetExtent(). Expected values are only
+         * anti-regression values, except the vertical range which is expected to cover all cells. The
+         * main purpose of this test is to verify that TransformSeparator has been able to extract the
+         * two-dimensional transform despite its non-linear component.
+         */
+        final GeneralEnvelope envelope = new GeneralEnvelope(HardCodedCRS.WGS84);
+        envelope.setRange(0, -70.001, +80.002);
+        envelope.setRange(1,  -4.997,  15.003);
+        final GridExtent actual = grid.getExtent(envelope);
+        assertEquals(extent.getAxisType(0), actual.getAxisType(0));
+        assertExtentEquals(new long[] { 56, 69, 2},
+                           new long[] {130, 73, 4}, actual);
     }
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/PassThroughTransformTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/PassThroughTransformTest.java
index c24d007..c15833f 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/PassThroughTransformTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/PassThroughTransformTest.java
@@ -16,11 +16,15 @@
  */
 package org.apache.sis.referencing.operation.transform;
 
+import java.util.List;
 import java.util.Arrays;
 import java.util.Random;
+import org.opengis.util.FactoryException;
 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.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.referencing.operation.matrix.Matrix3;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.ArraysExt;
@@ -251,4 +255,51 @@ public final strictfp class PassThroughTransformTest extends MathTransformTestCa
                     sourceDim, expectedData, 0, targetAsFloat, 0, numPts, CalculationType.DIRECT_TRANSFORM);
         }
     }
+
+    /**
+     * Tests {@link PassThroughTransform#tryConcatenate(boolean, MathTransform, MathTransformFactory)}.
+     * This tests creates a non-linear transform of 6→7 dimensions, then applies a filter keeping only
+     * target dimensions 1, 4 and 6 (corresponding to source dimensions 1 and 5).
+     *
+     * @throws FactoryException if an error occurred while combining the transforms.
+     */
+    @Test
+    public void testTryConcatenate() throws FactoryException {
+        PassThroughTransform ps = PassThroughTransform.newInstance(2, new PseudoTransform(2, 3), 2);
+        MathTransform c = ps.tryConcatenate(false, MathTransforms.linear(Matrices.create(4, 8, new double[] {
+                0, 1, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 1, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 1, 0,
+                0, 0, 0, 0, 0, 0, 0, 1})), null);
+
+        final List<MathTransform> steps = MathTransforms.getSteps(c);
+        assertEquals("Number of steps", 3, steps.size());
+        /*
+         * We need to remove source dimensions 0, 2, 3 and 4. We can not remove dimensions 2 and 3 before
+         * pass-through because they are used by the sub-transform. It leaves us dimensions 0 and 4 which
+         * can be removed here.
+         */
+        assertMatrixEquals("Expected removal of dimensions 0 and 4 before pass-through", Matrices.create(5, 7, new double[] {
+                0, 1, 0, 0, 0, 0, 0,
+                0, 0, 1, 0, 0, 0, 0,
+                0, 0, 0, 1, 0, 0, 0,
+                0, 0, 0, 0, 0, 1, 0,
+                0, 0, 0, 0, 0, 0, 1}), MathTransforms.getMatrix(steps.get(0)), null);
+        /*
+         * The number of pass-through dimensions have decreased from 2 to 1 on both sides of the sub-transform.
+         */
+        final PassThroughTransform reduced = (PassThroughTransform) steps.get(1);
+        assertEquals("firstAffectedOrdinate", 1, reduced.firstAffectedOrdinate);
+        assertEquals("numTrailingOrdinates",  1, reduced.numTrailingOrdinates);
+        assertSame  ("subTransform", ps.subTransform, reduced.subTransform);
+        /*
+         * We still have to remove source dimensions 2 and 3. Since we removed dimension 0 in previous step,
+         * the indices of dimensions to removed have shifted to 1 and 2.
+         */
+        assertMatrixEquals("Expected removal of dimensions 1 and 2 after pass-through", Matrices.create(4, 6, new double[] {
+                1, 0, 0, 0, 0, 0,
+                0, 0, 0, 1, 0, 0,
+                0, 0, 0, 0, 1, 0,
+                0, 0, 0, 0, 0, 1}), MathTransforms.getMatrix(steps.get(2)), null);
+    }
 }


[sis] 01/05: Trivial method renaming for consistency with https://issues.apache.org/jira/browse/SIS-318.

Posted by de...@apache.org.
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

commit 1c11912629ae9041dab7fbbcdb0744dcb2be078d
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu Nov 29 15:03:56 2018 +0100

    Trivial method renaming for consistency with https://issues.apache.org/jira/browse/SIS-318.
---
 .../sis/referencing/operation/transform/PassThroughTransform.java | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
index 148d155..ae1b7f6 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
@@ -294,8 +294,8 @@ public class PassThroughTransform extends AbstractMathTransform implements Seria
      * @param  numPts  number of points to transform.
      */
     @SuppressWarnings("SuspiciousSystemArraycopy")
-    private void apply(final Object srcPts, final int srcOff,
-                       final Object dstPts, int dstOff, int numPts) throws TransformException
+    private void transformOverlapping(final Object srcPts, final int srcOff,
+                                      final Object dstPts, int dstOff, int numPts) throws TransformException
     {
         if (numPts <= 0) return;
         final int subDimSource   = subTransform.getSourceDimensions();
@@ -410,7 +410,7 @@ public class PassThroughTransform extends AbstractMathTransform implements Seria
      */
     @Override
     public void transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) throws TransformException {
-        apply(srcPts, srcOff, dstPts, dstOff, numPts);
+        transformOverlapping(srcPts, srcOff, dstPts, dstOff, numPts);
     }
 
     /**
@@ -420,7 +420,7 @@ public class PassThroughTransform extends AbstractMathTransform implements Seria
      */
     @Override
     public void transform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts) throws TransformException {
-        apply(srcPts, srcOff, dstPts, dstOff, numPts);
+        transformOverlapping(srcPts, srcOff, dstPts, dstOff, numPts);
     }
 
     /**


[sis] 02/05: Move the special case as an implementation of tryConcatenate(…).

Posted by de...@apache.org.
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

commit 576d9a23a66d4ec30ca8513d8e930921a95ec5f4
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu Nov 29 15:04:38 2018 +0100

    Move the special case as an implementation of tryConcatenate(…).
---
 .../operation/transform/ConcatenatedTransform.java | 104 ++++++++++-----------
 1 file changed, 48 insertions(+), 56 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
index 5f4c5f1..0266938 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
@@ -56,7 +56,7 @@ import org.apache.sis.util.resources.Errors;
  * <p>Concatenated transforms are serializable if all their step transforms are serializable.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 1.0
  *
  * @see org.opengis.referencing.operation.MathTransformFactory#createConcatenatedTransform(MathTransform, MathTransform)
  *
@@ -168,59 +168,6 @@ class ConcatenatedTransform extends AbstractMathTransform implements Serializabl
             return mt;
         }
         /*
-         * If at least one math transform is an instance of ConcatenatedTransform and assuming
-         * that MathTransforms are associatives, tries the following arrangements and select
-         * the one with the fewest amount of steps:
-         *
-         *   Assuming :  tr1 = (A * B)
-         *               tr2 = (C * D)
-         *
-         *   Current  :  (A * B) * (C * D)     Will be the selected one if nothing better.
-         *   Try k=0  :  A * (B * (C * D))     Implies A * ((B * C) * D) through recursivity.
-         *   Try k=1  :  ((A * B) * C) * D     Implies (A * (B * C)) * D through recursivity.
-         *   Try k=2  :                        Tried only if try k=1 changed something.
-         *
-         * TODO: The same combination may be computed more than once (e.g. (B * C) above).
-         *       Should not be a big deal if there is not two many steps. In the even where
-         *       it would appears a performance issue, we could maintain a Map of combinations
-         *       already computed. The map would be local to a "create" method execution.
-         */
-        int stepCount = getStepCount(tr1) + getStepCount(tr2);
-        boolean tryAgain = true;                                // Really 'true' because we want at least 2 iterations.
-        for (int k=0; ; k++) {
-            MathTransform c1 = tr1;
-            MathTransform c2 = tr2;
-            final boolean first = (k & 1) == 0;
-            MathTransform candidate = first ? c1 : c2;
-            while (candidate instanceof ConcatenatedTransform) {
-                final ConcatenatedTransform ctr = (ConcatenatedTransform) candidate;
-                if (first) {
-                    c1 = candidate = ctr.transform1;
-                    c2 = create(ctr.transform2, c2, factory);
-                } else {
-                    c1 = create(c1, ctr.transform1, factory);
-                    c2 = candidate = ctr.transform2;
-                }
-                final int c = getStepCount(c1) + getStepCount(c2);
-                if (c < stepCount) {
-                    tr1 = c1;
-                    tr2 = c2;
-                    stepCount = c;
-                    tryAgain = true;
-                }
-            }
-            if (!tryAgain) break;
-            tryAgain = false;
-        }
-        /*
-         * Tries again the check for optimized cases (identity, etc.), because a
-         * transform may have been simplified to identity as a result of the above.
-         */
-        mt = createOptimized(tr1, tr2, factory);
-        if (mt != null) {
-            return mt;
-        }
-        /*
          * Can not avoid the creation of a ConcatenatedTransform object.
          * Check for the type to create (1D, 2D, general case...)
          */
@@ -299,19 +246,30 @@ class ConcatenatedTransform extends AbstractMathTransform implements Serializabl
         /*
          * Give a chance to AbstractMathTransform to returns an optimized object.
          * Examples: Logarithmic versus Exponential transforms, PassThrouthTransform.
+         * We try both ways (concatenation and pre-concatenation) and see which way
+         * produce the shortest concatenation chain.
          */
+        int stepCount = 0;
+        MathTransform shortest = null;
         if (tr1 instanceof AbstractMathTransform) {
             final MathTransform optimized = ((AbstractMathTransform) tr1).tryConcatenate(false, tr2, factory);
             if (optimized != null) {
-                return optimized;
+                stepCount = getStepCount(optimized);
+                shortest  = optimized;
             }
         }
         if (tr2 instanceof AbstractMathTransform) {
             final MathTransform optimized = ((AbstractMathTransform) tr2).tryConcatenate(true, tr1, factory);
             if (optimized != null) {
-                return optimized;
+                if (shortest == null || getStepCount(optimized) < stepCount) {
+                    return optimized;
+                }
+                shortest = optimized;
             }
         }
+        if (shortest != null) {
+            return shortest;
+        }
         /*
          * If one transform is the inverse of the other, return the identity transform.
          */
@@ -878,6 +836,40 @@ class ConcatenatedTransform extends AbstractMathTransform implements Serializabl
     }
 
     /**
+     * Concatenates or pre-concatenates in an optimized way this transform with the given transform, if possible.
+     * This method try to delegate the concatenation to {@link #transform1} or {@link #transform2}. Assuming that
+     * transforms are associative, this is equivalent to trying the following arrangements:
+     *
+     * {@preformat text
+     *   Instead of : other → tr1 → tr2
+     *   Try:         (other → tr1) → tr2          where (…) denote an optimized concatenation.
+     *
+     *   Instead of : tr1 → tr2 → other
+     *   Try:         tr1 → (tr2 → other)          where (…) denote an optimized concatenation.
+     * }
+     *
+     * @return the simplified transform, or {@code null} if no such optimization is available.
+     * @throws FactoryException if an error occurred while combining the transforms.
+     */
+    @Override
+    protected MathTransform tryConcatenate(final boolean applyOtherFirst, final MathTransform other, final MathTransformFactory factory)
+            throws FactoryException
+    {
+        if (applyOtherFirst) {
+            final MathTransform candidate = createOptimized(other, transform1, factory);
+            if (candidate != null) {
+                return create(candidate, transform2, factory);
+            }
+        } else {
+            final MathTransform candidate = createOptimized(transform2, other, factory);
+            if (candidate != null) {
+                return create(transform1, candidate, factory);
+            }
+        }
+        return super.tryConcatenate(applyOtherFirst, other, factory);
+    }
+
+    /**
      * Tests whether this transform does not move any points.
      * Implementation checks if the two transforms are identity.
      *


[sis] 03/05: Reduce the amount of transform to inverse when ConcatenatedTransform verify if two transforms are the inverse of each other.

Posted by de...@apache.org.
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

commit 29e677956ae083a6b4ff19b309dd1200a2b57441
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu Nov 29 16:17:03 2018 +0100

    Reduce the amount of transform to inverse when ConcatenatedTransform verify if two transforms are the inverse of each other.
---
 .../transform/AbstractLinearTransform.java         | 13 ++++-
 .../operation/transform/AbstractMathTransform.java | 56 +++++++++++++++++++-
 .../operation/transform/ConcatenatedTransform.java | 61 ++++++++++------------
 .../operation/transform/PassThroughTransform.java  | 17 ++++++
 4 files changed, 113 insertions(+), 34 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java
index 62b2166..2bc05f3 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java
@@ -21,6 +21,7 @@ import java.io.Serializable;
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.internal.referencing.provider.Affine;
@@ -41,7 +42,7 @@ import org.apache.sis.util.resources.Errors;
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 1.0
  * @since   0.6
  * @module
  */
@@ -109,6 +110,16 @@ abstract class AbstractLinearTransform extends AbstractMathTransform implements
     }
 
     /**
+     * Returns {@code true} if this transform is the inverse of the given transform.
+     * If this method is unsure, it conservatively returns {@code false}.
+     */
+    @Override
+    final boolean isInverseOf(final MathTransform other) {
+        // Skip the check if the other transform is not linear.
+        return (other instanceof LinearTransform) && super.isInverseOf(other);
+    }
+
+    /**
      * Creates the inverse transform of this object.
      */
     @Override
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
index e4e292d..73e5642 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
@@ -78,7 +78,7 @@ import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
  * running the same SIS version.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
  *
  * @see DefaultMathTransformFactory
  * @see org.apache.sis.referencing.operation.AbstractCoordinateOperation
@@ -791,6 +791,60 @@ public abstract class AbstractMathTransform extends FormattableObject
     }
 
     /**
+     * Returns {@code true} if this transform is the inverse of the given transform.
+     * If this method is unsure, it conservatively returns {@code false}.
+     *
+     * <p>The default implementation invokes {@link #inverse()} and compares the transforms.
+     * Subclasses should override with a more efficient implementation if they can avoid the
+     * call to {@link #inverse()} (unless that call is cheap).</p>
+     *
+     * @param  other  the transform that may be the inverse of this transform.
+     * @return whether this transform is the inverse of the given transform. If unsure, {@code false}.
+     */
+    boolean isInverseOf(final MathTransform other) {
+        return inverseEquals(this, other);
+    }
+
+    /**
+     * Returns {@code true} if {@code tr1} is the inverse of {@code tr2}.
+     * If this method is unsure, it conservatively returns {@code false}.
+     * This implementation delegates to {@link #isInverseOf(MathTransform)}
+     * if possible, or to the default implementation otherwise. The transform
+     * that may be inverted is {@code tr1}.
+     */
+    static boolean areInverse(final MathTransform tr1, final MathTransform tr2) {
+        if (tr1 instanceof AbstractMathTransform) {
+            return ((AbstractMathTransform) tr1).isInverseOf(tr2);
+        } else {
+            return inverseEquals(tr1, tr2);
+        }
+    }
+
+    /**
+     * Implementation of {@link #isInverseOf(MathTransform)} for arbitrary math transform.
+     * This method invokes {@link #inverse()} on {@code tr1}.
+     */
+    private static boolean inverseEquals(MathTransform tr1, final MathTransform tr2) {
+        if (tr1.getSourceDimensions() != tr2.getTargetDimensions() ||
+            tr1.getTargetDimensions() != tr2.getSourceDimensions())
+        {
+            return false;
+        }
+        try {
+            tr1 = tr1.inverse();
+        } catch (NoninvertibleTransformException e) {
+            return false;
+        }
+        if (tr1 instanceof LenientComparable) {
+            return ((LenientComparable) tr1).equals(tr2, ComparisonMode.APPROXIMATIVE);
+        }
+        if (tr2 instanceof LenientComparable) {
+            return ((LenientComparable) tr2).equals(tr1, ComparisonMode.APPROXIMATIVE);
+        }
+        return tr1.equals(tr2);
+    }
+
+    /**
      * Concatenates or pre-concatenates in an optimized way this math transform with the given one, if possible.
      * A new math transform is created to perform the combined transformation.
      * The {@code applyOtherFirst} value determines the transformation order as bellow:
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
index 0266938..4135bb8 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
@@ -38,7 +38,6 @@ import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.system.Semaphores;
 import org.apache.sis.util.Classes;
-import org.apache.sis.util.LenientComparable;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.io.wkt.Convention;
@@ -114,28 +113,6 @@ class ConcatenatedTransform extends AbstractMathTransform implements Serializabl
     }
 
     /**
-     * Tests if one math transform is the inverse of the other, or approximately the inverse.
-     * Used for {@link #createOptimized(MathTransform, MathTransform, MathTransformFactory)} implementation.
-     */
-    private static boolean areInverse(final MathTransform tr1, MathTransform tr2) {
-        try {
-            tr2 = tr2.inverse();
-        } catch (NoninvertibleTransformException e) {
-            return false;
-        }
-        if (tr1 == tr2) {
-            return true;
-        }
-        if (tr1 instanceof LenientComparable) {
-            return ((LenientComparable) tr1).equals(tr2, ComparisonMode.APPROXIMATIVE);
-        }
-        if (tr2 instanceof LenientComparable) {
-            return ((LenientComparable) tr2).equals(tr1, ComparisonMode.APPROXIMATIVE);
-        }
-        return tr1.equals(tr2);
-    }
-
-    /**
      * Concatenates the two given transforms.
      * If the concatenation result works with two-dimensional input and output points,
      * then the returned transform will implement {@link MathTransform2D}.
@@ -805,6 +782,20 @@ class ConcatenatedTransform extends AbstractMathTransform implements Serializabl
     }
 
     /**
+     * Gets the derivative of this transform at a point.
+     *
+     * @param  point  the coordinate point where to evaluate the derivative.
+     * @return the derivative at the specified point (never {@code null}).
+     * @throws TransformException if the derivative can't be evaluated at the specified point.
+     */
+    @Override
+    public Matrix derivative(final DirectPosition point) throws TransformException {
+        final Matrix matrix1 = transform1.derivative(point);
+        final Matrix matrix2 = transform2.derivative(transform1.transform(point, null));
+        return Matrices.multiply(matrix2, matrix1);
+    }
+
+    /**
      * Creates the inverse transform of this object.
      */
     @Override
@@ -822,17 +813,23 @@ class ConcatenatedTransform extends AbstractMathTransform implements Serializabl
     }
 
     /**
-     * Gets the derivative of this transform at a point.
-     *
-     * @param  point  the coordinate point where to evaluate the derivative.
-     * @return the derivative at the specified point (never {@code null}).
-     * @throws TransformException if the derivative can't be evaluated at the specified point.
+     * Returns {@code true} if this transform is the inverse of the given transform.
+     * If this method is unsure, it conservatively returns {@code false}.
      */
     @Override
-    public Matrix derivative(final DirectPosition point) throws TransformException {
-        final Matrix matrix1 = transform1.derivative(point);
-        final Matrix matrix2 = transform2.derivative(transform1.transform(point, null));
-        return Matrices.multiply(matrix2, matrix1);
+    final boolean isInverseOf(final MathTransform other) {
+        final List<MathTransform> s1 = getSteps();
+        final List<MathTransform> s2 = MathTransforms.getSteps(other);
+        final int size = s1.size();
+        if (s2.size() != size) {
+            return false;
+        }
+        for (int i=0; i<size; i++) {
+            if (!areInverse(s1.get(size-1 - i), s2.get(i))) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
index ae1b7f6..bae1e8a 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
@@ -641,6 +641,23 @@ public class PassThroughTransform extends AbstractMathTransform implements Seria
     }
 
     /**
+     * Returns {@code true} if this transform is the inverse of the given transform.
+     * If this method is unsure, it conservatively returns {@code false}.
+     */
+    @Override
+    final boolean isInverseOf(final MathTransform other) {
+        if (other instanceof PassThroughTransform) {
+            final PassThroughTransform ps = (PassThroughTransform) other;
+            if (firstAffectedOrdinate == ps.firstAffectedOrdinate &&
+                numTrailingOrdinates  == ps.numTrailingOrdinates)
+            {
+                return areInverse(subTransform, ps.subTransform);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Concatenates or pre-concatenates in an optimized way this transform with the given transform, if possible.
      * This method applies the following special cases:
      *


[sis] 05/05: Initial draft of SampleDimension class for coverages.

Posted by de...@apache.org.
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

commit 616a963d335599026643aff6d67468c2309fe27a
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu Nov 29 19:09:00 2018 +0100

    Initial draft of SampleDimension class for coverages.
---
 .../org/apache/sis/coverage/SampleDimension.java   | 161 +++++++++++++++++++++
 .../java/org/apache/sis/coverage/package-info.java |  30 ++++
 .../operation/transform/TransferFunction.java      |   5 +-
 3 files changed, 195 insertions(+), 1 deletion(-)

diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/SampleDimension.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/SampleDimension.java
new file mode 100644
index 0000000..62bbc58
--- /dev/null
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/SampleDimension.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.coverage;
+
+import java.util.Optional;
+import javax.measure.Unit;
+import org.opengis.util.InternationalString;
+import org.opengis.referencing.operation.MathTransform1D;
+import org.apache.sis.measure.NumberRange;
+import org.apache.sis.util.Classes;
+
+
+/**
+ * Describes the data values in a coverage (the range) when those values are numbers.
+ * For a grid coverage or a raster, a sample dimension may be a band.
+ *
+ * <div class="section">Relationship with metadata</div>
+ * This class provides the same information than ISO 19115 {@link org.opengis.metadata.content.SampleDimension},
+ * but organized in a different way. The use of the same name may seem a risk, but those two types are typically
+ * not used in same time.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+public class SampleDimension {
+    /**
+     * Description for this sample dimension. Typically used as a way to perform a band select by
+     * using human comprehensible descriptions instead of just numbers. Web Coverage Service (WCS)
+     * can use this name in order to perform band sub-setting as directed from a user request.
+     */
+    private final InternationalString name;
+
+    /**
+     * The range of sample values.
+     * May be {@code null} if this sample dimension has no non-{@code NaN} value.
+     */
+    private final NumberRange<?> range;
+
+    /**
+     * The values to indicate "no data" for this sample dimension.
+     */
+    private final Number[] noDataValues;
+
+    /**
+     * The transform from sample to geophysics value. May be {@code null} if this sample dimension
+     * do not defines any transform (which is not the same that defining an identity transform).
+     */
+    private final MathTransform1D transferFunction;
+
+    /**
+     * The units of measurement for this sample dimension, or {@code null} if not applicable.
+     */
+    private final Unit<?> units;
+
+    /**
+     * Creates a sample dimension with the specified properties.
+     *
+     * @param name     the sample dimension title or description.
+     * @param nodata   the values to indicate "no data".
+     * @param range    the range of sample values.
+     * @param toUnit   the transfer function for converting sample values to geophysics values in {@code units}.
+     * @param units    the units of measurement for this sample dimension, or {@code null} if not applicable.
+     */
+    <T extends Number & Comparable<? super T>> SampleDimension(
+            final InternationalString name,
+            final NumberRange<T>      range,
+            final T[]                 nodata,
+            final MathTransform1D     toUnit,
+            final Unit<?>             units)
+    {
+        this.name             = name;
+        this.range            = range;
+        this.noDataValues     = nodata;
+        this.transferFunction = toUnit;
+        this.units            = units;
+    }
+
+    /**
+     * Returns a name or description for this sample dimension. This is typically used as a way to perform a band select
+     * by using human comprehensible descriptions instead of just numbers. Web Coverage Service (WCS) can use this name
+     * in order to perform band sub-setting as directed from a user request.
+     *
+     * @return The title or description of this sample dimension.
+     */
+    public InternationalString getName() {
+        return name;
+    }
+
+    /**
+     * Returns the values to indicate "no data" for this sample dimension.
+     *
+     * @return the values to indicate no data values for this sample dimension, or an empty array if none.
+     */
+    public Number[] getNoDataValues() {
+        return noDataValues.clone();
+    }
+
+    /**
+     * Returns the range of values in this sample dimension.
+     * May be absent if this sample dimension has no non-{@code NaN} value.
+     *
+     * @return the range of values.
+     */
+    public Optional<NumberRange<?>> getRange() {
+        return Optional.ofNullable(range);
+    }
+
+    /**
+     * Returns the <cite>transfer function</cite> from sample values to geophysics values.
+     * This method returns a transform expecting sample values as input and computing geophysics values as output.
+     * This transform will take care of converting all "{@linkplain #getNoDataValues() no data values}" into {@code NaN} values.
+     * The <code>transferFunction.{@linkplain MathTransform1D#inverse() inverse()}</code> transform is capable to differentiate
+     * {@code NaN} values to get back the original sample value.
+     *
+     * @return The <cite>transfer function</cite> from sample to geophysics values. May be absent if this sample dimension
+     *         do not defines any transform (which is not the same that defining an identity transform).
+     *
+     * @see org.apache.sis.referencing.operation.transform.TransferFunction
+     */
+    public Optional<MathTransform1D> getTransferFunction() {
+        return Optional.ofNullable(transferFunction);
+    }
+
+    /**
+     * Returns the units of measurement for this sample dimension.
+     * This unit applies to values obtained after the {@linkplain #getTransferFunction() transfer function}.
+     * May be absent if not applicable.
+     *
+     * @return the units of measurement.
+     */
+    public Optional<Unit<?>> getUnits() {
+        return Optional.ofNullable(units);
+    }
+
+    /**
+     * Returns a string representation of this sample dimension.
+     * This string is for debugging purpose only and may change in future version.
+     *
+     * @return a string representation of this sample dimension for debugging purpose.
+     */
+    @Override
+    public String toString() {
+        return Classes.getShortClassName(this) + "[“" + name + "”]";
+    }
+}
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/package-info.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/package-info.java
new file mode 100644
index 0000000..6bdf0cc
--- /dev/null
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/package-info.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Functions that associates positions within a bounded space (its domain) to values (its range).
+ * This package makes no assumption on the domain geometry; it is not necessarily a grid.
+ * For the most common case — a coverage backed by a regular grid (also called "raster") — see
+ * {@link org.apache.sis.coverage.grid}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+package org.apache.sis.coverage;
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransferFunction.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransferFunction.java
index 7a138eb..77fd203 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransferFunction.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransferFunction.java
@@ -59,7 +59,10 @@ import org.apache.sis.referencing.operation.matrix.Matrix2;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.0
- * @since   0.5
+ *
+ * @see org.apache.sis.coverage.SampleDimension#getTransferFunction()
+ *
+ * @since 0.5
  * @module
  */
 public class TransferFunction implements Cloneable, Serializable {