sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Make GridDerivation more flexible in two ways: 1) If both envelopes contain only an Envelope, compoute the envelopes intersection. 2) If only a margin is specified, apply that margin even if no other change is specified.
Date Mon, 17 Feb 2020 14:37:01 GMT
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 a4bf8d4  Make GridDerivation more flexible in two ways: 1) If both envelopes contain
only an Envelope, compoute the envelopes intersection. 2) If only a margin is specified, apply
that margin even if no other change is specified.
a4bf8d4 is described below

commit a4bf8d4d81924291edb7dccc8705c42c12be3b6b
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Mon Feb 17 15:35:39 2020 +0100

    Make GridDerivation more flexible in two ways:
    1) If both envelopes contain only an Envelope, compoute the envelopes intersection.
    2) If only a margin is specified, apply that margin even if no other change is specified.
---
 .../apache/sis/coverage/grid/GridDerivation.java   | 68 ++++++++++++++++++----
 .../org/apache/sis/coverage/grid/GridExtent.java   |  2 +-
 .../org/apache/sis/coverage/grid/GridGeometry.java | 34 ++++++++---
 .../sis/coverage/grid/GridDerivationTest.java      | 35 ++++++++++-
 .../main/java/org/apache/sis/util/ArraysExt.java   | 20 ++++++-
 5 files changed, 138 insertions(+), 21 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
index 5367538..cb67ca5 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
@@ -79,7 +79,7 @@ import org.opengis.coverage.PointOutsideCoverageException;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Alexis Manin (Geomatys)
- * @version 1.0
+ * @version 1.1
  *
  * @see GridGeometry#derive()
  * @see GridGeometry#reduce(int...)
@@ -102,6 +102,9 @@ public class GridDerivation {
 
     /**
      * If non-null, the extent will be expanded by that amount of cells on each grid dimension.
+     * This array is non-null only if at least one non-zero margin has been specified. Trailing
+     * zero values are omitted (consequently this array may be shorter than {@link GridExtent}
+     * number of dimensions).
      *
      * @see #margin(int...)
      */
@@ -160,6 +163,15 @@ public class GridDerivation {
     private String subGridSetter;
 
     /**
+     * Intersection between the grid envelope and the area of interest, computed when only
envelopes are available.
+     * Normally we do not compute this envelope directly; instead we compute the grid extent
and the "grid to CRS"
+     * transform. This envelope is computed only if it can not be computed from other grid
geometry properties.
+     *
+     * @see #subgrid(Envelope, double...)
+     */
+    private GeneralEnvelope intersection;
+
+    /**
      * Creates a new builder for deriving a grid geometry from the specified base.
      *
      * @param  base  the base to use as a template for deriving a new grid geometry.
@@ -243,13 +255,15 @@ public class GridDerivation {
         int[] margin = null;
         for (int i=cellCounts.length; --i >= 0;) {
             final int n = cellCounts[i];
-            ArgumentChecks.ensurePositive("cellCounts", n);
-            if (margin == null) {
-                margin = new int[i+1];
+            if (n != 0) {
+                ArgumentChecks.ensurePositive("cellCounts", n);
+                if (margin == null) {
+                    margin = new int[i+1];
+                }
+                margin[i] = n;
             }
-            margin[i] = n;
         }
-        this.margin = margin;           // Set only on success.
+        this.margin = margin;           // Set only on success. We want null if all margin
values are 0.
         return this;
     }
 
@@ -398,6 +412,9 @@ public class GridDerivation {
      */
     public GridDerivation subgrid(final GridGeometry gridOfInterest) {
         ArgumentChecks.ensureNonNull("gridOfInterest", gridOfInterest);
+        if (gridOfInterest.isEnvelopeOnly()) {
+            return subgrid(gridOfInterest.envelope, (double[]) null);
+        }
         ensureSubgridNotSet();
         subGridSetter = "subgrid";
         if (!base.equals(gridOfInterest)) {
@@ -497,12 +514,14 @@ public class GridDerivation {
      */
     public GridDerivation subgrid(final Envelope areaOfInterest, double... resolution) {
         ensureSubgridNotSet();
-        MathTransform cornerToCRS = base.requireGridToCRS(false);
+        final boolean isEnvelopeOnly = base.isEnvelopeOnly() && (resolution == null
|| resolution.length == 0);
+        MathTransform cornerToCRS = isEnvelopeOnly ? MathTransforms.identity(base.envelope.getDimension())
+                                                   : base.requireGridToCRS(false);      
  // Normal case.
         subGridSetter = "subgrid";
         try {
             /*
              * If the envelope CRS is different than the expected CRS, concatenate the envelope
transformation
-             * to the 'gridToCRS' transform.  We should not transform the envelope here -
only concatenate the
+             * to the `gridToCRS` transform.  We should not transform the envelope here -
only concatenate the
              * transforms - because transforming envelopes twice would add errors.
              */
             MathTransform baseToAOI = null;
@@ -523,6 +542,17 @@ public class GridDerivation {
                 }
             }
             /*
+             * If the grid geometry contains only an envelope, and if user asked nothing
more than intersecting
+             * envelopes, then we will return a new GridGeometry with that intersection and
nothing else.
+             */
+            if (isEnvelopeOnly) {
+                if (areaOfInterest != null) {
+                    intersection = new GeneralEnvelope(base.envelope);
+                    intersection.intersect(areaOfInterest);
+                }
+                return this;
+            }
+            /*
              * If the envelope dimensions do not encompass all grid dimensions, the transform
is probably non-invertible.
              * We need to reduce the number of grid dimensions in the transform for having
a one-to-one relationship.
              */
@@ -897,8 +927,26 @@ public class GridDerivation {
          * need for envelope clipping performed by GridGeometry constructor.
          */
         final GridExtent extent = (scaledExtent != null) ? scaledExtent : baseExtent;
-        if (toBase != null || extent != base.extent) try {
-            return new GridGeometry(base, extent, toBase);
+        try {
+            if (toBase != null || extent != base.extent) {
+                return new GridGeometry(base, extent, toBase);
+            }
+            /*
+             * Intersection should be non-null only if we have not been able to compute more
reliable properties
+             * (grid extent and "grid to CRS" transform). It should happen only if `gridToCRS`
is null, but we
+             * nevertheless pass it to the constructor as a matter of principle.
+             */
+            if (intersection != null) {
+                return new GridGeometry(PixelInCell.CELL_CENTER, base.gridToCRS, intersection,
rounding);
+            }
+            /*
+             * Case when the only requested change was a margin. It is okay to test after
`intersection`
+             * because a non-null envelope intersection means that this `GridDerivation`
does not have
+             * required information for applying a margin anyway (no `GridExtent`, no `gridToCRS`).
+             */
+            if (margin != null && baseExtent != null) {
+                return new GridGeometry(base, baseExtent.expand(ArraysExt.copyAsLongs(margin)),
null);
+            }
         } catch (TransformException e) {
             throw new IllegalGridGeometryException(e, "envelope");
         }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index 261e4d6..787e67c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -442,7 +442,7 @@ public class GridExtent implements GridEnvelope, Serializable {
              * dimension indices.  Note that the resulting extent will be intersected with
enclosing extent
              * at the next step, which may cancel the margin effect.
              *
-             * Note about overflow checks: if m>0, then x < x+m unless the result overflows
the 'long' capacity.
+             * Note about overflow checks: if m>0, then x < x+m unless the result overflows
the `long` capacity.
              */
             if (margin != null && i < margin.length) {
                 final int m = margin[i];
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
index 976e9ff..3c90fc5 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
@@ -295,16 +295,20 @@ public class GridGeometry implements Serializable {
      * This constructor is used for creating a grid geometry over a subregion (for example
with the grid extent
      * computed by {@link GridDerivation#subgrid(Envelope, double...)}) or grid geometry
for a subsampled raster.
      *
-     * <p>If {@code toOther} is non-null, it should be a transform from the given {@code
extent} coordinates to the
-     * {@code other} grid coordinates. That transform should be merely a {@linkplain MathTransforms#scale(double...)
-     * scale} and {@linkplain MathTransforms#translation(double...) translation} even if
more complex transforms are
-     * accepted. The {@link #cornerToCRS} transform of the new grid geometry will be set
to the following concatenation:</p>
+     * <h4>Conversion between old and new grid geometry</h4>
+     * If {@code toOther} is non-null, it defines the conversion from grid coordinates of
given {@code extent} to grid
+     * coordinates of {@code other}. That transform should be a {@linkplain MathTransforms#scale(double...)
scale} and
+     * a {@linkplain MathTransforms#translation(double...) translation} only, but more complex
transforms are accepted.
+     * The {@link #cornerToCRS} transform of the new grid geometry will be set to the following
concatenation:
      *
      * <blockquote>{@code this.cornerToCRS} = {@code toOther} → {@code other.cornerToCRS}</blockquote>
      *
-     * The new {@linkplain #getEnvelope() grid geometry envelope} will be {@linkplain GeneralEnvelope#intersect(Envelope)
-     * clipped} to the envelope of the other grid geometry. This is for preventing the envelope
to become larger under the
-     * effect of subsampling (because {@link GridExtent#subsample(int[]) each cell become
larger}).
+     * The new {@linkplain #getEnvelope() grid geometry envelope} will be computed from the
new extent and transform,
+     * then {@linkplain GeneralEnvelope#intersect(Envelope) clipped} to the envelope of the
other grid geometry.
+     * This clip is for preventing the envelope to become larger under the effect of subsampling
because
+     * {@linkplain GridExtent#subsample(int[]) each cell become larger}. The clip is not
applied when {@code toOther}
+     * is {@code null} because in such case, we presume that the grid extent has been changed
for another reason than
+     * subsampling (e.g. application of a margin, in which case we want the envelope to be
expanded).
      *
      * @param  other    the other grid geometry to copy.
      * @param  extent   the new extent for the grid geometry to construct, or {@code null}
if none.
@@ -338,8 +342,13 @@ public class GridGeometry implements Serializable {
             resolution  = resolution(gridToCRS, extent);
             nonLinears  = findNonLinearTargets(gridToCRS);
         }
+        /*
+         * Recompute the envelope and clip only if a sub-sampling may have been applied (toOther
!= null).
+         * The reason for clipping is because subsampling may cause cells to appear larger.
+         */
         ImmutableEnvelope envelope = other.envelope;            // We will share the same
instance if possible.
-        ImmutableEnvelope computed = computeEnvelope(gridToCRS, getCoordinateReferenceSystem(envelope),
envelope);
+        ImmutableEnvelope computed = computeEnvelope(gridToCRS, getCoordinateReferenceSystem(envelope),
+                                                     toOther == null ? null : envelope);
      // Clip.
         if (computed == null || !computed.equals(envelope)) {
             envelope = computed;
         }
@@ -1166,6 +1175,15 @@ public class GridGeometry implements Serializable {
     }
 
     /**
+     * Returns {@code true} if this grid geometry contains only an envelope and no other
information.
+     * Note: if {@link #gridToCRS} is {@code null}, then {@link #cornerToCRS} and {@link
#resolution}
+     * should be null as well.
+     */
+    final boolean isEnvelopeOnly() {
+        return gridToCRS == null && extent == null && envelope != null;
+    }
+
+    /**
      * Returns an object that can be used for creating a new grid geometry derived from this
grid geometry.
      * {@code GridDerivation} does not change the state of this {@code GridGeometry} but
instead creates
      * new instances as needed. Examples of modifications include clipping to a sub-area
or applying a sub-sampling.
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
index fc0e164..0d207b7 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridDerivationTest.java
@@ -50,7 +50,7 @@ import static org.apache.sis.coverage.grid.GridGeometryTest.assertExtentEquals;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Alexis Manin (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
@@ -460,4 +460,37 @@ public final strictfp class GridDerivationTest extends TestCase {
             assertNotNull(e.getMessage());
         }
     }
+
+    /**
+     * Tests {@link GridDerivation#subgrid(GridGeometry)} when the two envelopes contain
only an envelope.
+     */
+    @Test
+    public void testWithEnvelopeOnly() {
+        final GridGeometry g1 = new GridGeometry(null, new Envelope2D(null, 10, 20, 110,
70));
+        final GridGeometry g2 = new GridGeometry(null, new Envelope2D(null, -5, 25, 100,
90));
+        final GridGeometry r  = g1.derive().subgrid(g2).build();
+        assertTrue(r.isEnvelopeOnly());
+        assertEnvelopeEquals(new Envelope2D(null, 10, 25, 85, 65), r.getEnvelope(), STRICT);
+    }
+
+    /**
+     * Tests {@link GridDerivation#margin(int...)} when no other operation is requested.
+     */
+    @Test
+    public void testWithMarginOnly() {
+        final GridGeometry grid = new GridGeometry(
+                new GridExtent(10, 20), PixelInCell.CELL_CENTER,
+                MathTransforms.linear(new Matrix3(
+                        2, 0, 0,
+                        0, 3, 0,
+                        0, 0, 1)), HardCodedCRS.WGS84);
+
+        final GridGeometry expanded = grid.derive().margin(4,5).build();
+        assertSame(grid.gridToCRS, expanded.gridToCRS);
+        assertExtentEquals(new long[] {-4, -5},
+                           new long[] {13, 24}, expanded.getExtent());
+
+        assertEnvelopeEquals(new Envelope2D(null, -1,  -1.5, 20, 60),     grid.getEnvelope(),
STRICT);
+        assertEnvelopeEquals(new Envelope2D(null, -9, -16.5, 36, 90), expanded.getEnvelope(),
STRICT);
+    }
 }
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java b/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java
index bb8d743..5d924d0 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java
@@ -67,7 +67,7 @@ import java.lang.reflect.Array;
  * objects.
  *
  * @author Martin Desruisseaux (IRD, Geomatys)
- * @version 1.0
+ * @version 1.1
  *
  * @see Arrays
  *
@@ -1845,6 +1845,24 @@ public final class ArraysExt extends Static {
     }
 
     /**
+     * Returns a copy of the given array where each value has been casted to the {@code long}
type.
+     *
+     * @param  data  the array to copy, or {@code null}.
+     * @return a copy of the given array with values casted to the {@code long} type,
+     *         or {@code null} if the given array was null.
+     *
+     * @since 1.1
+     */
+    public static long[] copyAsLongs(final int[] data) {
+        if (data == null) return null;
+        final long[] result = new long[data.length];
+        for (int i=0; i<data.length; i++) {
+            result[i] = data[i];
+        }
+        return result;
+    }
+
+    /**
      * Returns a copy of the given array where each value has been casted to the {@code float}
type.
      * This method does not verify if the casts would cause data loss.
      *


Mime
View raw message