sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jso...@apache.org
Subject [sis] branch geoapi-4.0 updated: Coverage : add method to flip axis in GridCoverageBuilder
Date Fri, 20 Mar 2020 11:18:11 GMT
This is an automated email from the ASF dual-hosted git repository.

jsorel 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 9d80cb1  Coverage : add method to flip axis in GridCoverageBuilder
9d80cb1 is described below

commit 9d80cb10080b8fef14a9974f8585fb7c035b2208
Author: jsorel <johann.sorel@geomatys.com>
AuthorDate: Fri Mar 20 12:17:52 2020 +0100

    Coverage : add method to flip axis in GridCoverageBuilder
---
 .../sis/coverage/grid/GridCoverageBuilder.java     | 119 +++++++++++++++++----
 .../sis/coverage/grid/GridCoverageBuilderTest.java |  35 ++++++
 2 files changed, 131 insertions(+), 23 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
index 9cf1796..1fe7fcd 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
@@ -24,16 +24,24 @@ import java.awt.image.WritableRaster;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.geometry.ImmutableEnvelope;
 import org.apache.sis.internal.coverage.j2d.BufferedGridCoverage;
 import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
+import org.apache.sis.internal.util.DoubleDouble;
+import org.apache.sis.referencing.operation.matrix.Matrices;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ArraysExt;
 import org.opengis.geometry.Envelope;
 import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 
 /**
@@ -53,13 +61,14 @@ public class GridCoverageBuilder {
     private int bufferHeight = -1;
     private int bufferNbSample = -1;
     private GridGeometry grid;
+    private final Set<Integer> flippedAxis = new HashSet<>();
 
     /**
      * Sets coverage data rendered image.
      *
      * @param image The rendered image to be wrapped by {@code GridCoverage2D}, not {@code
null}.
      */
-    public void setValues(RenderedImage image) {
+    public GridCoverageBuilder setValues(RenderedImage image) {
         ArgumentChecks.ensureNonNull("image", image);
         this.image = image;
         this.raster = null;
@@ -67,6 +76,7 @@ public class GridCoverageBuilder {
         this.bufferWidth = -1;
         this.bufferHeight = -1;
         this.bufferNbSample = -1;
+        return this;
     }
 
     /**
@@ -74,7 +84,7 @@ public class GridCoverageBuilder {
      *
      * @param raster The raster to be wrapped by {@code GridCoverage2D}, not {@code null}.
      */
-    public void setValues(WritableRaster raster) {
+    public GridCoverageBuilder setValues(WritableRaster raster) {
         ArgumentChecks.ensureNonNull("raster", raster);
         this.image = null;
         this.raster = raster;
@@ -82,6 +92,7 @@ public class GridCoverageBuilder {
         this.bufferWidth = -1;
         this.bufferHeight = -1;
         this.bufferNbSample = -1;
+        return this;
     }
 
 //    /**
@@ -156,7 +167,7 @@ public class GridCoverageBuilder {
      *
      * @param data the coverage datas, not {@code null}.
      */
-    public void setValues(DataBuffer data) {
+    public GridCoverageBuilder setValues(DataBuffer data) {
         ArgumentChecks.ensureNonNull("data", data);
         this.image = null;
         this.raster = null;
@@ -164,6 +175,7 @@ public class GridCoverageBuilder {
         this.bufferWidth = -1;
         this.bufferHeight = -1;
         this.bufferNbSample = -1;
+        return this;
     }
 
     private void setValues(DataBuffer data, int width, int height) {
@@ -182,8 +194,8 @@ public class GridCoverageBuilder {
      *
      * @param envelope The new grid geometry envelope, or {@code null}.
      */
-    public void setDomain(Envelope envelope) {
-        setDomain(envelope == null ? null : new GridGeometry(null, envelope));
+    public GridCoverageBuilder setDomain(Envelope envelope) {
+        return setDomain(envelope == null ? null : new GridGeometry(null, envelope));
     }
 
     /**
@@ -191,8 +203,9 @@ public class GridCoverageBuilder {
      *
      * @param grid The new grid geometry, or {@code null}.
      */
-    public void setDomain(GridGeometry grid) {
+    public GridCoverageBuilder setDomain(GridGeometry grid) {
         this.grid = grid;
+        return this;
     }
 
     /**
@@ -200,8 +213,9 @@ public class GridCoverageBuilder {
      *
      * @param range The new sample dimensions, or {@code null}.
      */
-    public void setRanges(final SampleDimension... range) {
+    public GridCoverageBuilder setRanges(final SampleDimension... range) {
         this.ranges = (range == null) ? null : new ArrayList<>(Arrays.asList(range));
+        return this;
     }
 
     /**
@@ -209,8 +223,23 @@ public class GridCoverageBuilder {
      *
      * @param range The new sample dimensions, or {@code null}.
      */
-    public void setRanges(Collection<? extends SampleDimension> range) {
+    public GridCoverageBuilder setRanges(Collection<? extends SampleDimension> range)
{
         this.ranges = (range == null) ? null : new ArrayList<>(range);
+        return this;
+    }
+
+    /**
+     * When building coverage with a grid geometry without a grid to crs transform
+     * the grid to crs is computed automaticaly.
+     * The default behavior creates a grid geometry with increasing values on all
+     * axis. This method allows to reverse direction on an axis.
+     *
+     * @param dimension
+     */
+    public GridCoverageBuilder flipAxis(int dimension) {
+        ArgumentChecks.ensurePositive("idx", dimension);
+        flippedAxis.add(dimension);
+        return this;
     }
 
     /**
@@ -224,14 +253,15 @@ public class GridCoverageBuilder {
      */
     public GridCoverage build() {
 
-        if (image != null) {
-            return new GridCoverage2D(grid, ranges, image);
-        } else if (raster != null) {
+        GridGeometry grid = this.grid;
+        List<SampleDimension> ranges = this.ranges;
+        RenderedImage image = this.image;
 
+        //create an image from raster
+        if (raster != null) {
             final int dataType = raster.getSampleModel().getDataType();
             final int numBands = raster.getSampleModel().getNumBands();
 
-            List<SampleDimension> ranges = this.ranges;
             if (ranges == null) {
                 ranges = new ArrayList<>(numBands);
                 for (int i = 0; i < numBands; i++) {
@@ -240,14 +270,15 @@ public class GridCoverageBuilder {
             }
 
             final ColorModel colors = ColorModelFactory.createColorModel(ranges.toArray(new
SampleDimension[0]), 0, dataType, ColorModelFactory.GRAYSCALE);
-            final BufferedImage image = new BufferedImage(colors, raster, false, null);
-            return new GridCoverage2D(grid, this.ranges, image);
+            image = new BufferedImage(colors, raster, false, null);
+        }
 
+        if (image != null) {
+            grid = addExtentIfAbsent(grid, image.getWidth(), image.getHeight(), flippedAxis);
+            //use provided ranges, even if null, GridCoverage2D makes a better work at building
them
+            return new GridCoverage2D(grid, this.ranges, image);
         } else if (buffer != null) {
 
-            GridGeometry grid = this.grid;
-            List<SampleDimension> ranges = this.ranges;
-
             //verify and enrich grid geometry
             if (bufferWidth != -1) {
                 if (grid.isDefined(GridGeometry.EXTENT)) {
@@ -260,7 +291,7 @@ public class GridCoverageBuilder {
                         throw new IllegalGridGeometryException("Grid height differ from buffer
height, expected " + bufferHeight + " found " + extent.getSize(1));
                     }
                 } else {
-                    grid = addExtentIfAbsent(grid, bufferWidth, bufferHeight);
+                    grid = addExtentIfAbsent(grid, bufferWidth, bufferHeight, flippedAxis);
                 }
             }
             //verify sample dimensions
@@ -287,7 +318,7 @@ public class GridCoverageBuilder {
      * If the given domain does not have a {@link GridExtent}, creates a new grid geometry
      * with an extent of given size.
      */
-    private static GridGeometry addExtentIfAbsent(GridGeometry domain, int width, int height)
{
+    private static GridGeometry addExtentIfAbsent(GridGeometry domain, int width, int height,
Set<Integer> flippedAxis) {
         if (domain == null) {
             GridExtent extent = new GridExtent(width, height);
             domain = new GridGeometry(extent, PixelInCell.CELL_CENTER, null, null);
@@ -309,13 +340,55 @@ public class GridCoverageBuilder {
                 if (!ArraysExt.contains(axisTypes, DimensionNameType.COLUMN)) axisTypes[0]
= DimensionNameType.COLUMN;
                 if (!ArraysExt.contains(axisTypes, DimensionNameType.ROW))    axisTypes[1]
= DimensionNameType.ROW;
                 final GridExtent extent = new GridExtent(axisTypes, low, high, true);
-                try {
-                    domain = new GridGeometry(domain, extent, null);
-                } catch (TransformException e) {
-                    throw new IllegalGridGeometryException(e);                  // Should
never happen.
+                if (domain.isDefined(GridGeometry.GRID_TO_CRS)) {
+                    try {
+                        domain = new GridGeometry(domain, extent, null);
+                    } catch (TransformException e) {
+                        throw new IllegalGridGeometryException(e);                  // Should
never happen.
+                    }
+                } else if (flippedAxis.isEmpty()) {
+                    domain = new GridGeometry(extent, domain.envelope);
+                } else {
+                    // create transform with flipped axis
+                    boolean nilEnvelope = true;
+                    final ImmutableEnvelope env = ImmutableEnvelope.castOrCopy(domain.envelope);
+                    if (env == null || ((nilEnvelope = env.isAllNaN()) && env.getCoordinateReferenceSystem()
== null)) {
+                        //do nothing
+                    } else if (!nilEnvelope) {
+                        /*
+                         * If we have both the extent and an envelope with at least one non-NaN
coordinates,
+                         * create the `cornerToCRS` transform. The `gridToCRS` calculation
uses the knowledge
+                         * that all scale factors are on diagonal with no sign reversal,
which allows simpler
+                         * calculation than full matrix multiplication. Use double-double
arithmetic everywhere.
+                         */
+                        final MatrixSIS affine = extent.cornerToCRS(env);
+                        final MathTransform cornerToCRS = MathTransforms.linear(affine);
+                        final int srcDim = cornerToCRS.getSourceDimensions();       // Translation
column in matrix.
+                        final int tgtDim = cornerToCRS.getTargetDimensions();       // Number
of matrix rows before last row.
+                        for (int j=0; j<tgtDim; j++) {
+                            final DoubleDouble scale  = (DoubleDouble) affine.getNumber(j,
j);
+                            final DoubleDouble offset = (DoubleDouble) affine.getNumber(j,
srcDim);
+                            scale.multiply(0.5);
+                            offset.add(scale);
+                            affine.setNumber(j, srcDim, offset);
+                        }
+                        MathTransform gridToCRS = MathTransforms.linear(affine);
+
+                        //apply flipped axis
+                        final MatrixSIS flip = Matrices.createDiagonal(affine.getNumRow(),
affine.getNumCol());
+                        for (Integer i : flippedAxis) {
+                            flip.setElement(i, i, -1);
+                            flip.setElement(i, srcDim, extent.getSize(i, true));
+                        }
+
+                        gridToCRS = MathTransforms.concatenate(MathTransforms.linear(flip),
gridToCRS);
+
+                        domain = new GridGeometry(extent, PixelInCell.CELL_CENTER, gridToCRS,
env.getCoordinateReferenceSystem());
+                    }
                 }
             }
         }
         return domain;
     }
 }
+
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
index 18eda9f..dae3155 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverageBuilderTest.java
@@ -23,11 +23,14 @@ import java.awt.image.RenderedImage;
 import java.awt.image.WritableRaster;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
 import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.util.NullArgumentException;
 import org.junit.Assert;
 import org.junit.Test;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.MathTransform;
 
 /**
  *
@@ -209,4 +212,36 @@ public class GridCoverageBuilderTest extends TestCase {
 
     }
 
+    /**
+     * Tests {@link GridCoverageBuilder#flipAxis(int)}.
+     */
+    @Test
+    public void flippedAxisTest() {
+
+        final RenderedImage image = new BufferedImage(360, 180, BufferedImage.TYPE_INT_ARGB);
+
+        final GeneralEnvelope domain = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
+        domain.setRange(0, -180, +180);
+        domain.setRange(1, -90, +90);
+
+        final GridCoverageBuilder builder = new GridCoverageBuilder();
+        builder.setValues(image);
+        builder.setDomain(domain);
+
+        { //check positive increasing grid to crs
+            GridCoverage coverage = builder.build();
+            Assert.assertTrue(domain.equals(coverage.getGridGeometry().getEnvelope(), 0.0,
false));
+            MathTransform gridToCRS = coverage.getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER);
+            Assert.assertEquals(new AffineTransform2D(1, 0, 0, 1, -179.5, -89.5), gridToCRS);
+        }
+
+        { //check negative increasing grid to crs
+            builder.flipAxis(1);
+            GridCoverage coverage = builder.build();
+            Assert.assertTrue(domain.equals(coverage.getGridGeometry().getEnvelope(), 0.0,
false));
+            MathTransform gridToCRS = coverage.getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER);
+            Assert.assertEquals(new AffineTransform2D(1, 0, 0, -1, -179.5, 89.5), gridToCRS);
+        }
+
+    }
 }


Mime
View raw message