sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: Avoid resampling the data when the operation can be represented by a change of "gridToCRS" transform.
Date Tue, 31 Mar 2020 17:19:17 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 8d8b09f6bac10e0bed6b5776ad53f512929c9ee4
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Mar 31 19:18:33 2020 +0200

    Avoid resampling the data when the operation can be represented by a change of "gridToCRS"
transform.
---
 .../apache/sis/coverage/grid/GridCoverage2D.java   |  5 +-
 .../sis/coverage/grid/ResampledGridCoverage.java   | 33 +++++++--
 .../coverage/grid/ResampledGridCoverageTest.java   | 84 ++++++++++++++++++----
 3 files changed, 102 insertions(+), 20 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
index 13733ad..154d751 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
@@ -167,9 +167,10 @@ public class GridCoverage2D extends GridCoverage {
      * @param  yDimension   index of extent dimension for image <var>y</var>
coordinates.
      */
     GridCoverage2D(final GridCoverage source, final GridGeometry domain, final GridExtent
extent,
-                   final RenderedImage data, final int xDimension, final int yDimension)
+                   RenderedImage data, final int xDimension, final int yDimension)
     {
         super(source, domain);
+        data = unwrapIfSameSize(data);
         this.data       = data;
         this.xDimension = xDimension;
         this.yDimension = yDimension;
@@ -249,9 +250,7 @@ public class GridCoverage2D extends GridCoverage {
 
     /**
      * Returns the wrapped image if the only difference is a translation, or {@code data}
otherwise.
-     * Workaround for RFE #4093999 ("Relax constraint on placement of this()/super() call
in constructors").
      */
-    @Workaround(library="JDK", version="1.8")
     private static RenderedImage unwrapIfSameSize(RenderedImage data) {
         if (data instanceof ReshapedImage) {
             final RenderedImage image = ((ReshapedImage) data).image;
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
index b8f3762..4e2a977 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
@@ -37,6 +37,7 @@ import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.internal.coverage.j2d.ImageUtilities;
 import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix;
 import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+import org.apache.sis.referencing.operation.transform.LinearTransform;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.matrix.Matrices;
@@ -129,12 +130,34 @@ final class ResampledGridCoverage extends GridCoverage {
     /**
      * If this coverage can be represented as a {@link GridCoverage2D} instance,
      * returns such instance. Otherwise returns {@code this}.
+     *
+     * @param  isGeometryExplicit  whether grid extent or "grid to CRS" transform have been
explicitly
+     *         specified by user. In such case, this method will not be allowed to change
those values.
      */
-    private GridCoverage specialize() {
-        final GridExtent extent = gridGeometry.getExtent();
+    private GridCoverage specialize(final boolean isGeometryExplicit) throws TransformException
{
+        GridExtent extent = gridGeometry.getExtent();
         if (extent.getDimension() < GridCoverage2D.MIN_DIMENSION || extent.getSubDimension()
> BIDIMENSIONAL) {
             return this;
         }
+        /*
+         * If the transform is linear and the user did not specified explicitly a desired
transform or grid extent
+         * (i.e. user specified only a target CRS), keep same image with a different `gridToCRS`
transform instead
+         * than doing a resampling. The intent is to avoid creating a new image if user apparently
doesn't care.
+         */
+        if (!isGeometryExplicit && toSourceCorner instanceof LinearTransform) {
+            MathTransform gridToCRS = gridGeometry.getGridToCRS(PixelInCell.CELL_CORNER);
+            if (gridToCRS instanceof LinearTransform) {
+                final GridGeometry sourceGG = source.getGridGeometry();
+                extent = sourceGG.getExtent();
+                gridToCRS = MathTransforms.concatenate(toSourceCorner.inverse(), gridToCRS);
+                final GridGeometry targetGG = new GridGeometry(extent, PixelInCell.CELL_CORNER,
gridToCRS,
+                                                               getCoordinateReferenceSystem());
+                if (sourceGG.equals(targetGG, ComparisonMode.APPROXIMATE)) {
+                    return source;
+                }
+                return new GridCoverage2D(source, targetGG, extent, source.render(null),
xDimension, yDimension);
+            }
+        }
         return new GridCoverage2D(source, gridGeometry, extent, render(null), xDimension,
yDimension);
     }
 
@@ -191,9 +214,11 @@ final class ResampledGridCoverage extends GridCoverage {
          * in which case we need to compute a default transform trying to preserve resolution
at the
          * point of interest.
          */
-        GridExtent targetExtent = target.isDefined(GridGeometry.EXTENT) ? target.getExtent()
: null;
+        boolean isGeometryExplicit = target.isDefined(GridGeometry.EXTENT);
+        GridExtent targetExtent = isGeometryExplicit ? target.getExtent() : null;
         final MathTransform targetCenterToCRS;
         if (target.isDefined(GridGeometry.GRID_TO_CRS)) {
+            isGeometryExplicit = true;
             targetCenterToCRS = target.getGridToCRS(PixelInCell.CELL_CENTER);
             if (targetExtent == null) {
                 targetExtent = targetExtent(sourceGG.getExtent(), sourceCornerToCRS,
@@ -335,7 +360,7 @@ final class ResampledGridCoverage extends GridCoverage {
         return new ResampledGridCoverage(source, target,
                 MathTransforms.concatenate(targetCornerToCRS, sourceCornerToCRS.inverse()),
                 MathTransforms.concatenate(targetCenterToCRS, sourceCenterToCRS.inverse()),
-                interpolation).specialize();
+                interpolation).specialize(isGeometryExplicit);
     }
 
     /**
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
index a89226d..232608a 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
@@ -18,13 +18,18 @@ package org.apache.sis.coverage.grid;
 
 import java.util.Random;
 import java.awt.image.DataBuffer;
+import java.awt.image.RenderedImage;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.geometry.Envelope2D;
+import org.apache.sis.geometry.ImmutableEnvelope;
 import org.apache.sis.image.Interpolation;
 import org.apache.sis.image.TiledImageMock;
+import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.referencing.crs.HardCodedCRS;
+import org.apache.sis.referencing.operation.HardCodedConversions;
 import org.apache.sis.test.TestUtilities;
+import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
 import static org.opengis.referencing.datum.PixelInCell.CELL_CENTER;
@@ -33,12 +38,16 @@ import static org.opengis.test.Assert.*;
 
 /**
  * Tests the {@link ResampledGridCoverage} implementation.
+ * The tests in this class does not verify interpolation values
+ * (this is {@link org.apache.sis.image.ResampledImageTest} job).
+ * Instead it focus on the grid geometry inferred by the operation.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
  * @since   1.1
  * @module
  */
+@DependsOn(org.apache.sis.image.ResampledImageTest.class)
 public final strictfp class ResampledGridCoverageTest {
     /**
      * Creates a small grid coverage with arbitrary data. The rendered image will
@@ -67,14 +76,14 @@ public final strictfp class ResampledGridCoverageTest {
     }
 
     /**
-     * Tests application of an identity transform.
+     * Tests application of an identity transform from an explicitly specified grid geometry.
      * We expect the source coverage to be returned unchanged.
      *
      * @throws FactoryException if transformation between CRS can not be computed.
      * @throws TransformException if some coordinates can not be transformed to the target
grid geometry.
      */
     @Test
-    public void testIdentity() throws FactoryException, TransformException {
+    public void testExplicitIdentity() throws FactoryException, TransformException {
         final GridCoverage2D source = createGridCoverage();
         GridGeometry gg = source.getGridGeometry();
         gg = new GridGeometry(null, CELL_CENTER, gg.getGridToCRS(CELL_CENTER), gg.getCoordinateReferenceSystem());
@@ -83,28 +92,77 @@ public final strictfp class ResampledGridCoverageTest {
     }
 
     /**
-     * Tests application of a translation.
+     * Tests application of an identity transform without specifying explicitly the desired
grid geometry.
      *
      * @throws FactoryException if transformation between CRS can not be computed.
      * @throws TransformException if some coordinates can not be transformed to the target
grid geometry.
      */
     @Test
-    public void testTranslation() throws FactoryException, TransformException {
+    public void testImplicitIdentity() throws FactoryException, TransformException {
         final GridCoverage2D source = createGridCoverage();
         GridGeometry gg = source.getGridGeometry();
         gg = new GridGeometry(null, CELL_CENTER, null, gg.getCoordinateReferenceSystem());
         final GridCoverage target = ResampledGridCoverage.create(source, gg, Interpolation.NEAREST);
+        assertSame("Identity transform should result in same coverage.", source, target);
+    }
+
+    /**
+     * Tests application of an axis swapping.
+     *
+     * @throws FactoryException if transformation between CRS can not be computed.
+     * @throws TransformException if some coordinates can not be transformed to the target
grid geometry.
+     */
+    @Test
+    public void testAxisSwap() throws FactoryException, TransformException {
+        final GridCoverage2D source = createGridCoverage();
+        GridGeometry gg = new GridGeometry(null, CELL_CENTER, null, HardCodedCRS.WGS84_φλ);
+        final GridCoverage target = ResampledGridCoverage.create(source, gg, Interpolation.NEAREST);
         /*
-         * We let ResampledGridCoverageTest chooses the `GridExtent` itself. Since the default
behavior
-         * is to create extents starting at zero, it should be different than source extent
unless the
-         * random number generator selected a (0,0) location by coincidence.
+         * We expect the same image since `ResampledGridCoverage` should have been
+         * able to apply the operation with only a change of `gridToCRS` transform.
          */
+        assertNotSame(source, target);
+        assertSame(unwrap(source.render(null)),
+                   unwrap(target.render(null)));
+        /*
+         * As an easy way to check that axis swapping has happened, check the envelopes.
+         */
+        final ImmutableEnvelope se = source.getGridGeometry().envelope;
+        final ImmutableEnvelope te = target.getGridGeometry().envelope;
+        assertEquals(se.getLower(0), te.getLower(1), Formulas.ANGULAR_TOLERANCE);
+        assertEquals(se.getLower(1), te.getLower(0), Formulas.ANGULAR_TOLERANCE);
+        assertEquals(se.getUpper(0), te.getUpper(1), Formulas.ANGULAR_TOLERANCE);
+        assertEquals(se.getUpper(1), te.getUpper(0), Formulas.ANGULAR_TOLERANCE);
+    }
+
+    /**
+     * Unwraps the given image if it is an instance of {@link ReshapedImage}.
+     */
+    private static RenderedImage unwrap(final RenderedImage image) {
+        assertEquals("GridCoverage.render(null) should have their origin at (0,0).", 0, image.getMinX());
+        assertEquals("GridCoverage.render(null) should have their origin at (0,0).", 0, image.getMinY());
+        return (image instanceof ReshapedImage) ? ((ReshapedImage) image).image : image;
+    }
+
+    /**
+     * Tests application of a reprojection.
+     *
+     * @throws FactoryException if transformation between CRS can not be computed.
+     * @throws TransformException if some coordinates can not be transformed to the target
grid geometry.
+     */
+    @Test
+    public void testReprojection() throws FactoryException, TransformException {
+        final GridCoverage2D source = createGridCoverage();
+        GridGeometry gg = source.getGridGeometry();
+        gg = new GridGeometry(null, CELL_CENTER, null, HardCodedConversions.mercator());
+        final GridCoverage target = ResampledGridCoverage.create(source, gg, Interpolation.NEAREST);
         assertTrue("GridExtent.startsAtZero", target.getGridGeometry().getExtent().startsAtZero());
-        if (source.getGridGeometry().getExtent().startsAtZero()) {
-            assertSame("Identity transform should result in same coverage.", source, target);
-        } else {
-            assertNotSame(source, target);
-            assertInstanceOf("Expected specialized type for 2D.", GridCoverage2D.class, target);
-        }
+        /*
+         * Mercator projection does not change pixel width, but change pixel height.
+         */
+        final GridExtent sourceExtent = source.getGridGeometry().getExtent();
+        final GridExtent targetExtent = target.getGridGeometry().getExtent();
+        assertEquals(sourceExtent.getSize(0),   targetExtent.getSize(0));
+        assertTrue  (sourceExtent.getSize(1) <= targetExtent.getSize(1));
     }
 }


Mime
View raw message