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 2020/09/12 14:47:27 UTC

[sis] branch geoapi-4.0 updated (894384d -> a56b98f)

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 894384d  Add checks against empty area of interest during pixel iteration.
     new 2b36532  Undeprecate `GridExtent.expand(long...)` given its similarity with method below. Refactor `GridExtent.grow(boolean, boolean long...)` as `expand(long[], long[])`. Add tests.
     new a56b98f  Disable interpolations on image using an IndexColorModel.

The 2 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:
 .../apache/sis/coverage/grid/GridDerivation.java   |   2 +-
 .../org/apache/sis/coverage/grid/GridExtent.java   |  87 ++++++++++-------
 .../java/org/apache/sis/image/PixelIterator.java   |   6 +-
 .../java/org/apache/sis/image/ResampledImage.java  |  15 +++
 .../apache/sis/coverage/grid/GridExtentTest.java   |  19 +++-
 .../sis/internal/storage/MemoryGridResource.java   |   2 +-
 .../internal/storage/MemoryGridResourceTest.java   | 103 +++++++++++++++++++++
 .../apache/sis/test/suite/StorageTestSuite.java    |   1 +
 8 files changed, 196 insertions(+), 39 deletions(-)
 create mode 100644 storage/sis-storage/src/test/java/org/apache/sis/internal/storage/MemoryGridResourceTest.java


[sis] 01/02: Undeprecate `GridExtent.expand(long...)` given its similarity with method below. Refactor `GridExtent.grow(boolean, boolean long...)` as `expand(long[], long[])`. Add tests.

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 2b36532c5989c1fec1fb73d674183bff2f6e69c2
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Sep 12 15:11:38 2020 +0200

    Undeprecate `GridExtent.expand(long...)` given its similarity with method below.
    Refactor `GridExtent.grow(boolean, boolean long...)` as `expand(long[], long[])`.
    Add tests.
---
 .../apache/sis/coverage/grid/GridDerivation.java   |   2 +-
 .../org/apache/sis/coverage/grid/GridExtent.java   |  87 ++++++++++-------
 .../java/org/apache/sis/image/PixelIterator.java   |   6 +-
 .../apache/sis/coverage/grid/GridExtentTest.java   |  19 +++-
 .../sis/internal/storage/MemoryGridResource.java   |   2 +-
 .../internal/storage/MemoryGridResourceTest.java   | 103 +++++++++++++++++++++
 .../apache/sis/test/suite/StorageTestSuite.java    |   1 +
 7 files changed, 181 insertions(+), 39 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 87ebc60..30ccc29 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
@@ -954,7 +954,7 @@ public class GridDerivation {
              * required information for applying a margin anyway (no `GridExtent`, no `gridToCRS`).
              */
             if (margin != null && baseExtent != null) {
-                return new GridGeometry(base, baseExtent.grow(true, true, ArraysExt.copyAsLongs(margin)), 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 bb4c958..04ac1ea 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
@@ -1060,38 +1060,53 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable
     }
 
     /**
-     * Expands or shrinks this grid extent by the given amount of cells along each dimension.
+     * Returns a grid extent expanded by the given amount of cells on both sides along each dimension.
      * This method adds the given margins to the {@linkplain #getHigh(int) high coordinates}
-     * and subtracts the same margins to the {@linkplain #getLow(int) low coordinates}.
+     * and subtracts the same margins from the {@linkplain #getLow(int) low coordinates}.
+     * If a negative margin is supplied, the extent size decreases accordingly.
+     *
+     * <p>Invoking this method is equivalent to invoking
+     * <code>{@linkplain #expand(long[], long[]) expand}(margins, margins)</code>.
+     *
+     * <h4>Number of arguments</h4>
+     * The {@code margins} array length should be equal to the {@linkplain #getDimension() number of dimensions}.
+     * If the array is shorter, missing values default to 0 (i.e. sizes in unspecified dimensions are unchanged).
+     * If the array is longer, extraneous values are ignored.
      *
-     * @param  margins amount of cells to add or subtract.
+     * @param  margins  amount of cells to add or subtract on both sides for each dimension.
      * @return a grid extent expanded by the given amount, or {@code this} if there is no change.
      * @throws ArithmeticException if expanding this extent by the given margins overflows {@code long} capacity.
-     *
-     * @deprecated Replaced by {@link #grow(boolean, boolean, long...)}.
      */
-    @Deprecated
     public GridExtent expand(final long... margins) {
-        return grow(true, true, margins);
+        ArgumentChecks.ensureNonNull("margins", margins);
+        final int m = getDimension();
+        final int length = Math.min(m, margins.length);
+        if (isZero(margins, length)) {
+            return this;
+        }
+        final GridExtent resized = new GridExtent(this);
+        final long[] c = resized.coordinates;
+        for (int i=0; i<length; i++) {
+            final long p = margins[i];
+            c[i] = Math.subtractExact(c[i], p);
+            c[i+m] = Math.addExact(c[i+m], p);
+        }
+        return resized;
     }
 
     /**
-     * Returns a grid extent expanded by the given amount of cells on the specified sides along each dimension.
-     * This method can modify the low coordinates, the high coordinates or both.
-     * <ul>
-     *   <li>If {@code low} is true, subtracts the given margin from {@linkplain #getLow(int) low coordinates}.</li>
-     *   <li>If {@code high} is true, adds the same margin to {@linkplain #getHigh(int) high coordinates}.</li>
-     * </ul>
-     * If a negative margin is supplied, the extent size decreases accordingly.
+     * Returns a grid extent expanded by the given amount of cells along each dimension.
+     * This method adds {@code addToHigh} to the {@linkplain #getHigh(int) high coordinates}
+     * and subtracts {@code subtractFromLow} from the {@linkplain #getLow(int) low coordinates}.
+     * If a negative value is supplied, the extent size decreases accordingly.
      *
      * <h4>Number of arguments</h4>
-     * The {@code margins} array length should be equal to the {@linkplain #getDimension() number of dimensions}.
-     * If the array is shorter, missing values default to 0 (i.e. sizes in unspecified dimensions are unchanged).
-     * If the array is longer, extraneous values are ignored.
+     * The array lengths should be equal to the {@linkplain #getDimension() number of dimensions}.
+     * If an array is shorter, missing values default to 0 (i.e. sizes in unspecified dimensions are unchanged).
+     * If an array is longer, extraneous values are ignored.
      *
-     * @param  low      whether to subtract the specified margin from low coordinates.
-     * @param  high     whether to add the specified margin to high coordinates.
-     * @param  margins  amount of cells to add or subtract on specified sides of each dimension.
+     * @param  subtractFromLow  amount of cells to subtract from low coordinates in each dimension, or {@code null} if none.
+     * @param  addToHigh        amount of cells to add to high coordinates for each dimension, or {@code null} if none.
      * @return a grid extent expanded by the given amount, or {@code this} if there is no change.
      * @throws ArithmeticException if expanding this extent by the given margin overflows {@code long} capacity.
      *
@@ -1099,21 +1114,27 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable
      *
      * @since 1.1
      */
-    public GridExtent grow(final boolean low, final boolean high, final long... margins) {
-        ArgumentChecks.ensureNonNull("margins", margins);
+    public GridExtent expand(final long[] subtractFromLow, final long[] addToHigh) {
         final int m = getDimension();
-        final int length = Math.min(m, margins.length);
-        if ((low | high) && !isZero(margins, length)) {
-            final GridExtent resized = new GridExtent(this);
-            final long[] c = resized.coordinates;
-            for (int i=0; i<length; i++) {
-                final long p = margins[i];
-                if (low)  c[i] = Math.subtractExact(c[i], p);
-                if (high) c[i+m] = Math.addExact(c[i+m], p);
-            }
-            return resized;
+        int nlo = 0;
+        if (subtractFromLow != null) {
+            nlo = Math.min(m, subtractFromLow.length);
+            if (isZero(subtractFromLow, nlo)) nlo = 0;
         }
-        return this;
+        int nhi = 0;
+        if (addToHigh != null) {
+            nhi = Math.min(m, addToHigh.length);
+            if (isZero(addToHigh, nhi)) nhi = 0;
+        }
+        final int length = Math.max(nlo, nhi);
+        if (length == 0) {
+            return this;
+        }
+        final GridExtent resized = new GridExtent(this);
+        final long[] c = resized.coordinates;
+        for (int i=0; i<nlo; i++) c[i] = Math.subtractExact(c[i], subtractFromLow[i]);
+        for (int i=0; i<nhi; i++) c[i+m] = Math.addExact(c[i+m], addToHigh[i]);
+        return resized;
     }
 
     /**
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java b/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
index c9577d1..1ad8820 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
@@ -241,9 +241,11 @@ public abstract class PixelIterator {
 
         /**
          * Sets the region (in pixel coordinates) where to perform the iteration.
-         * By default, iterators will traverse all pixels in the given image or raster.
+         * If this method is not invoked, then  by default iterators will traverse all pixels in the image or raster.
+         * If a sub-area is specified, then the traversed area is the {@linkplain Rectangle#intersection(Rectangle)
+         * intersection} of {@code subArea} with the image or {@linkplain Raster#getBounds() raster bounds}.
          *
-         * @param  subArea  region where to iterator, or {@code null} for iterating over all image domain.
+         * @param  subArea  region to intersect, or {@code null} for iterating over all image domain.
          * @return {@code this} for method call chaining.
          * @throws IllegalArgumentException if the given rectangle is empty.
          */
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
index c21a5d3..21ecebd 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
@@ -166,18 +166,33 @@ public final strictfp class GridExtentTest extends TestCase {
     }
 
     /**
-     * Tests {@link GridExtent#grow(boolean, boolean, long...)}.
+     * Tests {@link GridExtent#expand(long...)}.
      */
     @Test
     public void testExpand() {
         GridExtent extent = create3D();
-        extent = extent.grow(true, true, 20, -10);
+        assertSame(extent, extent.expand(new long[3]));
+        extent = extent.expand(20, -10);            // One less dimension than `exent` dimension.
         assertExtentEquals(extent, 0,  80, 519);
         assertExtentEquals(extent, 1, 210, 789);
         assertExtentEquals(extent, 2,  40,  49);
     }
 
     /**
+     * Tests {@link GridExtent#expand(long[], long[])}.
+     */
+    @Test
+    public void testExpand2() {
+        GridExtent extent = create3D();
+        assertSame(extent, extent.expand(new long[2], null));
+        assertSame(extent, extent.expand(null, new long[] {0,0,0,5}));      // Extraneous dimension should be ignored.
+        extent = extent.expand(new long[] {30}, new long[] {20, -10});
+        assertExtentEquals(extent, 0,  70, 519);
+        assertExtentEquals(extent, 1, 200, 789);
+        assertExtentEquals(extent, 2,  40,  49);
+    }
+
+    /**
      * Tests {@link GridExtent#resize(long...)}.
      */
     @Test
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryGridResource.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryGridResource.java
index 5efd4ab..9f81dbe 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryGridResource.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryGridResource.java
@@ -140,7 +140,7 @@ public class MemoryGridResource extends AbstractGridResource {
             intersection  = intersection.translate(changes);
             changes[dimX] = Math.subtractExact(data.getWidth(),  intersection.getSize(dimX));
             changes[dimY] = Math.subtractExact(data.getHeight(), intersection.getSize(dimY));
-            intersection  = intersection.grow(false, true, changes);
+            intersection  = intersection.expand(null, changes);
             if (intersection.equals(source.getExtent())) {
                 if (sameBands) {
                     return coverage;
diff --git a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/MemoryGridResourceTest.java b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/MemoryGridResourceTest.java
new file mode 100644
index 0000000..71688c7
--- /dev/null
+++ b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/MemoryGridResourceTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.internal.storage;
+
+import java.awt.image.BufferedImage;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.coverage.grid.GridCoverage2D;
+import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
+import org.apache.sis.referencing.crs.HardCodedCRS;
+import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests {@link MemoryGridResource}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+@DependsOn(AbstractGridResourceTest.class)
+public final strictfp class MemoryGridResourceTest extends TestCase {
+    /**
+     * Arbitrary size for the grid to test.
+     */
+    private static final int WIDTH = 31, HEIGHT = 23;
+
+    /**
+     * The coordinate reference system used by the tests.
+     */
+    private final CoordinateReferenceSystem crs;
+
+    /**
+     * The conversion from pixel coordinates to geographic coordinates for the coverage in this test.
+     */
+    private final AffineTransform2D gridToCRS;
+
+    /**
+     * The resource to be tested by each test method.
+     */
+    private final MemoryGridResource resource;
+
+    /**
+     * Creates a new test case.
+     */
+    public MemoryGridResourceTest() {
+        crs = HardCodedCRS.WGS84;
+        gridToCRS = new AffineTransform2D(2, 0, 0, 3, 0, 0);
+        final GridGeometry grid = new GridGeometry(new GridExtent(WIDTH, HEIGHT), PixelInCell.CELL_CENTER, gridToCRS, crs);
+        final BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_BYTE_BINARY);
+        resource = new MemoryGridResource(null, new GridCoverage2D(grid, null, image));
+    }
+
+    /**
+     * Creates an arbitrary grid geometry included inside the {@linkplain #resource} extent.
+     */
+    private GridGeometry createSubGrid() {
+        final GridExtent extent = new GridExtent(null,
+                new long[] {7, 4},
+                new long[] {WIDTH - 9, HEIGHT - 11}, true);
+
+        return new GridGeometry(extent, PixelInCell.CELL_CENTER, gridToCRS, crs);
+    }
+
+    /**
+     * Tests {@link MemoryGridResource#read(GridGeometry, int...)}.
+     */
+    @Test
+    public void testRead() {
+        final GridGeometry request  = createSubGrid();
+        final GridCoverage coverage = resource.read(request);
+        /*
+         * PENDING_JDK_FIX: replace following lines by new tests
+         * after https://bugs.openjdk.java.net/browse/JDK-8166038 is fixed.
+         */
+        if (coverage.render(null) instanceof BufferedImage) {
+            final GridGeometry expected = resource.getGridGeometry();
+            assertEquals(expected, coverage.getGridGeometry());
+        }
+    }
+}
diff --git a/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java b/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
index 600a9ad..b89e232 100644
--- a/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
+++ b/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
@@ -40,6 +40,7 @@ import org.junit.BeforeClass;
     org.apache.sis.internal.storage.io.RewindableLineReaderTest.class,
     org.apache.sis.internal.storage.MetadataBuilderTest.class,
     org.apache.sis.internal.storage.AbstractGridResourceTest.class,
+    org.apache.sis.internal.storage.MemoryGridResourceTest.class,
     org.apache.sis.storage.FeatureNamingTest.class,
     org.apache.sis.storage.ProbeResultTest.class,
     org.apache.sis.storage.StorageConnectorTest.class,


[sis] 02/02: Disable interpolations on image using an IndexColorModel.

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 a56b98f503a6a448392d4f041569de9fd745849f
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Sep 12 15:45:46 2020 +0200

    Disable interpolations on image using an IndexColorModel.
---
 .../main/java/org/apache/sis/image/ResampledImage.java    | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
index 2e54c40..84ae73e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
@@ -25,6 +25,7 @@ import java.awt.Dimension;
 import java.awt.Rectangle;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
 import java.awt.image.Raster;
 import java.awt.image.RenderedImage;
 import java.awt.image.WritableRaster;
@@ -241,6 +242,20 @@ public class ResampledImage extends ComputedImage {
                     Errors.Keys.MismatchedDimension_3, "toSource", BIDIMENSIONAL, numDim));
         }
         /*
+         * If the image uses an index color model, interpolating the indexed values does not produce
+         * the expected colors. Safest approach is to disable completely interpolations in that case.
+         *
+         * Note: we could interpolate if we knew that all index values, without exception (i.e. no index for
+         * missing values), are related to measurements by a linear function. It practice it rarely happens,
+         * because there is usually at least one index value reserved for missing values. Scientific data in
+         * SIS are usually stored as floating point type (with missing values mapped to NaN), which can not
+         * be associated to `IndexColorModel`. For now we don't try to perform a more sophisticated detection
+         * of which interpolations are allowed, but a future SIS version may revisit this policy if needed.
+         */
+        if (source.getColorModel() instanceof IndexColorModel) {
+            interpolation = Interpolation.NEAREST;
+        }
+        /*
          * If the interpolation requires more than 2×2 pixels, we will need to shift the transform
          * to source image. For example if the interpolation requires 4×4 pixels, the interpolation
          * point will be between the second and third pixel, so there is one more pixel on the left