sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: Compute `PlanarImage.GRID_GEOMETRY_KEY` property for image read from GeoTIFF files.
Date Sat, 10 Jul 2021 19:14:43 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

commit ced335d1cede92c19a81b52d18f8aee6ce5226a5
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat Jul 10 17:01:00 2021 +0200

    Compute `PlanarImage.GRID_GEOMETRY_KEY` property for image read from GeoTIFF files.
---
 .../org/apache/sis/coverage/grid/GridExtent.java   |  8 ++--
 .../org/apache/sis/coverage/grid/GridGeometry.java | 39 +++++++++++++++
 .../internal/coverage/j2d/DeferredProperty.java    | 56 ++++++++++++++++++++++
 .../apache/sis/coverage/grid/GridGeometryTest.java | 23 +++++++++
 .../sis/internal/storage/TiledGridCoverage.java    | 14 ++++--
 5 files changed, 131 insertions(+), 9 deletions(-)

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 fdef600..82bdb09 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
@@ -1436,9 +1436,6 @@ public class GridExtent implements GridEnvelope, LenientComparable,
Serializable
      * Returns an extent translated by the given amount of cells compared to this extent.
      * The returned extent has the same {@linkplain #getSize(int) size} than this extent,
      * i.e. both low and high grid coordinates are displaced by the same amount of cells.
-     * The given translation vector should have a length equal to this extent
-     * {@linkplain #getDimension() dimension}, but shorter arrays are allowed:
-     * missing values are assumed zero (i.e. the extent is not translated in missing dimensions).
      *
      * <div class="note"><b>Example:</b>
      * for an extent (x: [0…10], y: [2…4], z: [0…1]) and a translation {-2, 2},
@@ -1449,12 +1446,13 @@ public class GridExtent implements GridEnvelope, LenientComparable,
Serializable
      * If the array is shorter, missing values default to 0 (i.e. no translation in unspecified
dimensions).
      * If the array is longer, extraneous values are ignored.
      *
-     * @param  translation  translation to apply on each axis in order, or {@code null} if
none.
-     * @return a grid-extent whose coordinates (both low and high ones) have been translated
by given amounts.
+     * @param  translation  translation to apply on each axis in order.
+     * @return a grid extent whose coordinates (both low and high ones) have been translated
by given amounts.
      *         If the given translation is a no-op (no value or only 0 ones), then this extent
is returned as is.
      * @throws ArithmeticException if the translation results in coordinates that overflow
64-bits integer.
      *
      * @see #startsAtZero()
+     * @see GridGeometry#translate(long...)
      *
      * @since 1.1
      */
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 9b50485..b477e72 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
@@ -1285,6 +1285,45 @@ public class GridGeometry implements LenientComparable, Serializable
{
     }
 
     /**
+     * Returns a grid geometry translated by the given amount of cells compared to this grid.
+     * The returned grid has the same {@linkplain GridExtent#getSize(int) size} than this
grid,
+     * i.e. both low and high grid coordinates are displaced by the same amount of cells.
+     * The "grid to CRS" transforms are adjusted accordingly in order to map to the same
+     * "real world" coordinates.
+     *
+     * <h4>Number of arguments</h4>
+     * The {@code translation} array length should be equal to the {@linkplain #getDimension()
number of dimensions}.
+     * If the array is shorter, missing values default to 0 (i.e. no translation in unspecified
dimensions).
+     * If the array is longer, extraneous values are ignored.
+     *
+     * @param  translation  translation to apply on each grid axis in order.
+     * @return a grid geometry whose coordinates (both low and high ones) and
+     *         the "grid to CRS" transforms have been translated by given amounts.
+     *         If the given translation is a no-op (no value or only 0 ones), then this grid
is returned as is.
+     * @throws ArithmeticException if the translation results in coordinates that overflow
64-bits integer.
+     *
+     * @see GridExtent#translate(long...)
+     *
+     * @since 1.1
+     */
+    public GridGeometry translate(final long... translation) {
+        ArgumentChecks.ensureNonNull("translation", translation);
+        final double[] vector = new double[getDimension()];
+        boolean isZero = true;
+        for (int i=Math.min(vector.length, translation.length); --i >= 0;) {
+            isZero &= (translation[i] == 0);
+            vector[i] = Math.negateExact(translation[i]);
+        }
+        if (isZero) {
+            return this;
+        } else try {
+            return new GridGeometry(this, extent.translate(translation), MathTransforms.translation(vector));
+        } catch (TransformException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    /**
      * Returns a grid geometry that encompass only some dimensions of this grid geometry.
      * The specified dimensions will be copied into a new grid geometry if necessary.
      * The selection is applied on {@linkplain #getExtent() grid extent} dimensions;
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/DeferredProperty.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/DeferredProperty.java
index 2473e4e..498b0a9 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/DeferredProperty.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/DeferredProperty.java
@@ -16,8 +16,13 @@
  */
 package org.apache.sis.internal.coverage.j2d;
 
+import java.util.Map;
+import java.util.Collections;
 import java.util.function.Function;
 import java.awt.image.RenderedImage;
+import org.apache.sis.image.PlanarImage;
+import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.coverage.grid.GridGeometry;
 
 
 /**
@@ -70,4 +75,55 @@ public final class DeferredProperty {
         }
         return value;
     }
+
+    /**
+     * Creates a deferred property for computing the value of {@link PlanarImage#GRID_GEOMETRY_KEY}.
+     *
+     * @param  grid        the grid geometry of the grid coverage rendered as an image.
+     * @param  dimensions  the dimensions to keep from the coverage grid geometry.
+     * @return a deferred property for computing the grid geometry of an image.
+     */
+    public static Map<String,Object> forGridGeometry(final GridGeometry grid, final
int[] dimensions) {
+        return Collections.singletonMap(PlanarImage.GRID_GEOMETRY_KEY,
+                new DeferredProperty(new ImageGeometry(grid, dimensions)));
+    }
+
+    /**
+     * A deferred property for computing the value of {@link PlanarImage#GRID_GEOMETRY_KEY}.
+     */
+    private static final class ImageGeometry implements Function<RenderedImage, GridGeometry>
{
+        /** The grid geometry of the grid coverage rendered as an image. */
+        private final GridGeometry grid;
+
+        /** The dimensions to keep from the coverage grid geometry. */
+        private final int dimX, dimY;
+
+        /**
+         * Creates a deferred property for an image grid geometry.
+         *
+         * @param grid        the grid geometry of the grid coverage rendered as an image.
+         * @param dimensions  the dimensions to keep from the coverage grid geometry.
+         */
+        public ImageGeometry(final GridGeometry grid, final int[] dimensions) {
+            this.grid = grid;
+            this.dimX = dimensions[0];
+            this.dimY = dimensions[1];
+        }
+
+        /**
+         * Invoked when the {@link PlanarImage#GRID_GEOMETRY_KEY} value needs to be computed.
+         * The image should have been rendered from a grid coverage having the grid geometry
+         * given at construction time.
+         *
+         * @param  image  the image for which to compute the property.
+         * @return the grid geometry property computed for the given image.
+         */
+        @Override
+        public GridGeometry apply(final RenderedImage image) {
+            final GridExtent extent = grid.getExtent();
+            return grid.reduce(dimX, dimY).translate(
+                    Math.subtractExact(image.getMinX(), extent.getLow(dimX)),
+                    Math.subtractExact(image.getMinY(), extent.getLow(dimY)));
+        }
+    }
 }
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
index b3c4f2b..87d71e2 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.coverage.grid;
 
+import org.opengis.geometry.Envelope;
 import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.referencing.datum.PixelInCell;
 import org.opengis.referencing.operation.Matrix;
@@ -432,6 +433,28 @@ public final strictfp class GridGeometryTest extends TestCase {
     }
 
     /**
+     * Tests {@link GridGeometry#translate(long...)}.
+     */
+    @Test
+    public void testTranslate() {
+        GridGeometry grid = new GridGeometry(
+                new GridExtent(17, 10),
+                PixelInCell.CELL_CENTER,
+                MathTransforms.linear(new Matrix3(
+                    1,   0,  -7,
+                    0,  -1,  50,
+                    0,   0,   1)),
+                HardCodedCRS.WGS84);
+        /*
+         * The "real world" envelope should be unchanged by grid translation.
+         */
+        final Envelope envelope = grid.getEnvelope();
+        grid = grid.translate(12, 15);
+        assertExtentEquals(new long[] {12, 15}, new long[] {12 + 16, 15 + 9}, grid.getExtent());
+        assertEquals(envelope, grid.getEnvelope());
+    }
+
+    /**
      * Tests {@link GridGeometry#reduce(int...)}.
      */
     @Test
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
index af68b33..99e5962 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
@@ -28,6 +28,7 @@ import org.opengis.geometry.MismatchedDimensionException;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.DisjointExtentException;
+import org.apache.sis.internal.coverage.j2d.DeferredProperty;
 import org.apache.sis.internal.coverage.j2d.TiledImage;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.resources.Errors;
@@ -294,8 +295,8 @@ public abstract class TiledGridCoverage extends GridCoverage {
                         Errors.Keys.MismatchedDimension_3, "sliceExtent", dimension, sd));
             }
         }
-        final int[] sd = sliceExtent.getSubspaceDimensions(BIDIMENSIONAL);
-        if (sd[1] != 1) {
+        final int[] selectedDimensions = sliceExtent.getSubspaceDimensions(BIDIMENSIONAL);
+        if (selectedDimensions[1] != 1) {
             // TODO
             throw new UnsupportedOperationException("Non-horizontal slices not yet implemented.");
         }
@@ -321,7 +322,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
                         throw new DisjointExtentException(message);
                     }
                 }
-                final long lower =                Math.max(toSubsampledPixel(           
   multiplyExact(tileLo, tileSize[i]),  i), min);
+                final long lower = /* inclusive */Math.max(toSubsampledPixel(/* inclusive
*/multiplyExact(tileLo, tileSize[i]),  i), min);
                 final long upper = incrementExact(Math.min(toSubsampledPixel(decrementExact(multiplyExact(tileUp,
tileSize[i])), i), max));
                 imageSize[i] = toIntExact(subtractExact(upper, lower));
                 offsetAOI[i] = toIntExact(subtractExact(lower, aoiMin));
@@ -332,7 +333,12 @@ public abstract class TiledGridCoverage extends GridCoverage {
              * Get all tiles in the specified region. I/O operations, if needed, happen here.
              */
             final WritableRaster[] result = readTiles(new AOI(tileLower, tileUpper, offsetAOI,
dimension));
-            image = new TiledImage(null, colors, imageSize[0], imageSize[1], tileLower[0],
tileLower[1], result);
+            /*
+             * Wraps in an image all the tiles that we just read, together with the following
properties:
+             *    - Two-dimensional conversion from pixel coordinates to "real world" coordinates.
+             */
+            final Map<String,Object> properties = DeferredProperty.forGridGeometry(getGridGeometry(),
selectedDimensions);
+            image = new TiledImage(properties, colors, imageSize[0], imageSize[1], tileLower[0],
tileLower[1], result);
         } catch (Exception e) {     // Too many exception types for listing them all.
             throw new CannotEvaluateException(Resources.forLocale(getLocale()).getString(
                     Resources.Keys.CanNotRenderImage_1, getDisplayName()), e);

Mime
View raw message