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/06/10 16:01:03 UTC

[sis] branch geoapi-4.0 updated (2c3e3be -> 682573b)

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 2c3e3be  Remove GridCoverage.evaluate(…) method, replaced by an `Evaluator` class. This commit is only a refactoring without changes in functionalities. Future commits will add more capabilities or optimizations.
     new 8910f34  Add a flag for reducing the amount of exceptions created when a point is outside coverage bounds.
     new 734ea4a  Move `BufferedGridCoverage` in public API.
     new 682573b  More efficient implementation of `BufferedGridCoverage.evaluator()`.

The 3 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:
 .../org/apache/sis/gui/map/ValuesUnderCursor.java  |   1 +
 .../grid}/BufferedGridCoverage.java                | 168 +++++++++++++++++++--
 .../sis/coverage/grid/ConvertedGridCoverage.java   |  14 +-
 .../org/apache/sis/coverage/grid/Evaluator.java    |  43 +++++-
 .../apache/sis/coverage/grid/GridCoverage2D.java   |   3 +
 .../sis/coverage/grid/GridCoverageBuilder.java     |   1 -
 .../org/apache/sis/coverage/grid/package-info.java |  20 ++-
 .../grid}/BufferedGridCoverageTest.java            |   7 +-
 .../sis/coverage/grid/GridCoverage2DTest.java      |   2 +-
 .../apache/sis/test/suite/FeatureTestSuite.java    |   2 +-
 .../org/apache/sis/internal/netcdf/Raster.java     |   2 +-
 11 files changed, 232 insertions(+), 31 deletions(-)
 rename core/sis-feature/src/main/java/org/apache/sis/{internal/coverage/j2d => coverage/grid}/BufferedGridCoverage.java (50%)
 rename core/sis-feature/src/test/java/org/apache/sis/{internal/coverage/j2d => coverage/grid}/BufferedGridCoverageTest.java (88%)


[sis] 03/03: More efficient implementation of `BufferedGridCoverage.evaluator()`.

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 682573bba033b3903ee2e42570633d0b2f0e94ce
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Jun 10 17:14:18 2020 +0200

    More efficient implementation of `BufferedGridCoverage.evaluator()`.
---
 .../sis/coverage/grid/BufferedGridCoverage.java    | 159 +++++++++++++++++++--
 .../sis/coverage/grid/ConvertedGridCoverage.java   |   9 ++
 .../org/apache/sis/coverage/grid/Evaluator.java    |   6 +-
 3 files changed, 164 insertions(+), 10 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/BufferedGridCoverage.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/BufferedGridCoverage.java
index 85112f9..15392d3 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/BufferedGridCoverage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/BufferedGridCoverage.java
@@ -26,6 +26,8 @@ import java.awt.image.DataBufferShort;
 import java.awt.image.DataBufferUShort;
 import java.awt.image.RasterFormatException;
 import java.awt.image.RenderedImage;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.internal.feature.Resources;
 import org.apache.sis.util.ArgumentChecks;
@@ -34,15 +36,41 @@ import org.apache.sis.util.resources.Errors;
 // Branch-specific imports
 import org.apache.sis.internal.jdk9.JDK9;
 import org.opengis.coverage.CannotEvaluateException;
+import org.opengis.coverage.PointOutsideCoverageException;
 
 
 /**
- * A {@link GridCoverage} with data stored in an in-memory Java2D buffer.
- * Those data can be shown as {@link RenderedImage}.
+ * Basic access to grid data values backed by a <var>n</var>-dimensional {@link DataBuffer}.
+ * Those data can be shown as an untiled {@link RenderedImage}.
  * Images are created when {@link #render(GridExtent)} is invoked instead than at construction time.
  * This delayed construction makes this class better suited to <var>n</var>-dimensional grids since
  * those grids can not be wrapped into a single {@link RenderedImage}.
  *
+ * <div class="note"><b>Comparison with alternatives:</b>
+ * this class expects all data to reside in-memory and does not support tiling.
+ * Pixels are stored in a row-major fashion with all bands in a single array <em>or</em> one array per band.
+ * By contrast, {@link GridCoverage2D} allows more flexibility in data layout and supports tiling with data
+ * loaded or computed on-the-fly, but is restricted to two-dimensional images (which may be slices in a
+ * <var>n</var>-dimensional grid).</div>
+ *
+ * The number of bands is determined by the number of {@link SampleDimension}s specified at construction time.
+ * The {@linkplain DataBuffer#getNumBanks() number of banks} is either 1 or the number of bands.
+ *
+ * <ul class="verbose">
+ *   <li>If the number of banks is 1, all data are packed in a single array with band indices varying fastest,
+ *       then column indices (<var>x</var>), then row indices (<var>y</var>), then other dimensions.</li>
+ *   <li>If the number of banks is greater than 1, then each band is stored in a separated array.
+ *       In each array, sample values are stored with column indices (<var>x</var>) varying fastest,
+ *       then row indices (<var>y</var>), then other dimensions.
+ *       In the two-dimensional case, this layout is also known as <cite>row-major</cite>.</li>
+ * </ul>
+ *
+ * The number of cells in each dimension is specified by the {@link GridExtent} of the geometry given at
+ * construction time. By default the {@linkplain GridExtent#getSize(int) extent size} in the two first dimensions
+ * will define the {@linkplain RenderedImage#getWidth() image width} and {@linkplain RenderedImage#getHeight() height},
+ * but different dimensions may be used depending on which dimensions are identified as the
+ * {@linkplain GridExtent#getSubspaceDimensions(int) subspace dimensions}.
+ *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
@@ -51,9 +79,10 @@ import org.opengis.coverage.CannotEvaluateException;
  */
 public class BufferedGridCoverage extends GridCoverage {
     /**
-     * The sample values, potentially multi-banded. The bands may be stored either in a single bank (pixel interleaved image)
-     * or in different banks (banded image). This class detects automatically which of those two sample models is used when
-     * {@link #render(GridExtent)} is invoked.
+     * The sample values, potentially multi-banded. The bands may be stored either in a single bank
+     * ({@linkplain java.awt.image.PixelInterleavedSampleModel pixel interleaved} image) or in different banks
+     * ({@linkplain java.awt.image.BandedSampleModel banded} image). This class detects automatically
+     * which of those two sample models is used when {@link #render(GridExtent)} is invoked.
      */
     protected final DataBuffer data;
 
@@ -61,7 +90,8 @@ public class BufferedGridCoverage extends GridCoverage {
      * Constructs a grid coverage using the specified grid geometry, sample dimensions and data buffer.
      * This method stores the given buffer by reference (no copy). The bands in the given buffer can be
      * stored either in a single bank (pixel interleaved image) or in different banks (banded image).
-     * This class detects automatically which of those two sample models is used.
+     * This class detects automatically which of those two sample models is used
+     * (see class javadoc for more information).
      *
      * <p>Note that {@link DataBuffer} does not contain any information about image size.
      * Consequently {@link #render(GridExtent)} depends on the domain {@link GridExtent},
@@ -74,7 +104,7 @@ public class BufferedGridCoverage extends GridCoverage {
      * @throws NullPointerException if an argument is {@code null}.
      * @throws IllegalArgumentException if the data buffer has an incompatible number of banks.
      * @throws IllegalGridGeometryException if the grid extent is larger than the data buffer capacity.
-     * @throws ArithmeticException if the grid extent is larger than 64 bits integer capacity.
+     * @throws ArithmeticException if the number of cells is larger than 64 bits integer capacity.
      */
     public BufferedGridCoverage(final GridGeometry domain, final Collection<? extends SampleDimension> range, final DataBuffer data) {
         super(domain, range);
@@ -109,8 +139,8 @@ public class BufferedGridCoverage extends GridCoverage {
 
     /**
      * Constructs a grid coverage using the specified grid geometry, sample dimensions and data type.
-     * This constructor creates a single-bank {@link DataBuffer} (pixel interleaved sample model) with
-     * all sample values initialized to zero.
+     * This constructor creates a single-bank {@link DataBuffer} (pixel interleaved sample model)
+     * with all sample values initialized to zero.
      *
      * @param  grid      the grid extent, CRS and conversion from cell indices to CRS.
      * @param  bands     sample dimensions for each image band.
@@ -147,6 +177,18 @@ public class BufferedGridCoverage extends GridCoverage {
     }
 
     /**
+     * Creates a new function for computing or interpolating sample values at given locations.
+     *
+     * <h4>Multi-threading</h4>
+     * {@code Evaluator}s are not thread-safe. For computing sample values concurrently,
+     * a new {@link Evaluator} instance should be created for each thread.
+     */
+    @Override
+    public Evaluator evaluator() {
+        return new CellAccessor(this);
+    }
+
+    /**
      * Returns a two-dimensional slice of grid data as a rendered image.
      * This method returns a view; sample values are not copied.
      *
@@ -162,4 +204,103 @@ public class BufferedGridCoverage extends GridCoverage {
             throw new CannotEvaluateException(e.getMessage(), e);
         }
     }
+
+    /**
+     * Implementation of evaluator returned by {@link #evaluator()}.
+     */
+    private static class CellAccessor extends Evaluator {
+        /**
+         * A copy of {@link BufferedGridCoverage#data} reference.
+         */
+        private final DataBuffer data;
+
+        /**
+         * The grid lower values. Those values need to be subtracted to each
+         * grid coordinate before to compute index in {@link #data} buffer.
+         */
+        private final long[] lower;
+
+        /**
+         * Grid size with shifted index. The size of dimension 0 is stored at index 1, the size of dimension 1
+         * is stored in index 2, <i>etc.</i>. Element 0 contains the number of banks. This layout is convenient
+         * for computing index in {@link DataBuffer}.
+         */
+        private final long[] sizes;
+
+        /**
+         * {@code true} for banded sample model, or {@code false} for pixel interleaved sample model.
+         */
+        private final boolean banded;
+
+        /**
+         * Creates a new evaluator for the specified coverage.
+         */
+        CellAccessor(final BufferedGridCoverage coverage) {
+            super(coverage);
+            data = coverage.data;
+            final GridExtent extent = coverage.getGridGeometry().getExtent();
+            lower = new long[extent.getDimension()];
+            sizes = new long[lower.length + 1];
+            sizes[0] = data.getNumBanks();
+            for (int i=0; i<lower.length; i++) {
+                lower[i]   = extent.getLow(i);
+                sizes[i+1] = extent.getSize(i);
+            }
+            banded = sizes[0] > 1;
+            values = new double[coverage.getSampleDimensions().size()];
+        }
+
+        /**
+         * Returns a sequence of double values for a given point in the coverage.
+         * The CRS of the given point may be any coordinate reference system,
+         * or {@code null} for the same CRS than the coverage.
+         */
+        @Override
+        public double[] apply(final DirectPosition point) throws CannotEvaluateException {
+            final int pos;
+            try {
+                final FractionalGridCoordinates gc = toGridPosition(point);
+                int  i = lower.length;
+                long s = sizes[i];
+                long index = 0;
+                while (--i >= 0) {
+                    final long low = lower[i];
+                    long p = gc.getCoordinateValue(i);
+                    // (p - low) may overflow, so we must test (p < low) before.
+                    if (p < low || (p -= low) >= s) {
+                        if (isNullIfOutside()) {
+                            return null;
+                        }
+                        final PointOutsideCoverageException ex = new PointOutsideCoverageException(
+                                gc.pointOutsideCoverage(getCoverage().gridGeometry.extent), point);
+                        ex.setOffendingLocation(point);
+                        throw ex;
+                    }
+                    /*
+                     * Following should never overflow, otherwise BufferedGridCoverage
+                     * constructor would have failed. Should never be negative neither.
+                     */
+                    index = (index + p) * (s = sizes[i]);
+                }
+                /*
+                 * Failure on next line would not be caused by a point outside coverage bounds.
+                 * So it should not be rethrown as PointOutsideCoverageException.
+                 */
+                pos = Math.toIntExact(index);
+            } catch (ArithmeticException | TransformException ex) {
+                throw new CannotEvaluateException(ex.getMessage(), ex);
+            }
+            final double[] values = this.values;
+            if (banded) {
+                for (int i=0; i<values.length; i++) {
+                    values[i] = data.getElemDouble(i, pos);
+                }
+            } else {
+                for (int i=0; i<values.length; i++) {
+                    values[i] = data.getElemDouble(i + pos);
+                }
+            }
+            return values;
+        }
+    }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java
index 3d117c0..5027b58 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java
@@ -207,6 +207,15 @@ final class ConvertedGridCoverage extends GridCoverage {
         }
 
         /**
+         * Forwards configuration to the wrapped evaluator.
+         */
+        @Override
+        public void setNullIfOutside(final boolean flag) {
+            evaluator.setNullIfOutside(flag);
+            super.setNullIfOutside(flag);
+        }
+
+        /**
          * Returns a sequence of double values for a given point in the coverage.
          * This method delegates to the source coverage, then converts the values.
          *
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/Evaluator.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/Evaluator.java
index c416f7e..852203e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/Evaluator.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/Evaluator.java
@@ -41,6 +41,10 @@ import org.apache.sis.util.ArgumentChecks;
  * Evaluators are not thread-safe. An instance of {@code Evaluator} should be created
  * for each thread that need to compute sample values.
  *
+ * <h2>Limitations</h2>
+ * Current implementation performs nearest-neighbor sampling only.
+ * A future version will provide interpolations.
+ *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
@@ -80,7 +84,7 @@ public class Evaluator implements Function<DirectPosition, double[]> {
      * Array where to store sample values computed by {@link #apply(DirectPosition)}.
      * For performance reasons, the same array may be recycled on every method call.
      */
-    private double[] values;
+    double[] values;
 
     /**
      * Whether to return {@code null} instead than throwing an exception if given point


[sis] 02/03: Move `BufferedGridCoverage` in public API.

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 734ea4a7866d051becf93cddf47a7b14376d59b5
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Jun 10 13:59:32 2020 +0200

    Move `BufferedGridCoverage` in public API.
---
 .../coverage/j2d => coverage/grid}/BufferedGridCoverage.java     | 9 ++-------
 .../java/org/apache/sis/coverage/grid/GridCoverageBuilder.java   | 1 -
 .../coverage/j2d => coverage/grid}/BufferedGridCoverageTest.java | 7 ++-----
 .../java/org/apache/sis/coverage/grid/GridCoverage2DTest.java    | 2 +-
 .../test/java/org/apache/sis/test/suite/FeatureTestSuite.java    | 2 +-
 .../src/main/java/org/apache/sis/internal/netcdf/Raster.java     | 2 +-
 6 files changed, 7 insertions(+), 16 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BufferedGridCoverage.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/BufferedGridCoverage.java
similarity index 96%
rename from core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BufferedGridCoverage.java
rename to core/sis-feature/src/main/java/org/apache/sis/coverage/grid/BufferedGridCoverage.java
index 7586e44..85112f9 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BufferedGridCoverage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/BufferedGridCoverage.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.coverage.j2d;
+package org.apache.sis.coverage.grid;
 
 import java.util.Collection;
 import java.awt.image.DataBuffer;
@@ -27,11 +27,6 @@ import java.awt.image.DataBufferUShort;
 import java.awt.image.RasterFormatException;
 import java.awt.image.RenderedImage;
 import org.apache.sis.coverage.SampleDimension;
-import org.apache.sis.coverage.grid.GridCoverage;
-import org.apache.sis.coverage.grid.GridExtent;
-import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.coverage.grid.IllegalGridGeometryException;
-import org.apache.sis.coverage.grid.ImageRenderer;
 import org.apache.sis.internal.feature.Resources;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
@@ -51,7 +46,7 @@ import org.opengis.coverage.CannotEvaluateException;
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
- * @since   1.0
+ * @since   1.1
  * @module
  */
 public class BufferedGridCoverage extends GridCoverage {
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 0a2ae54..81aea94 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
@@ -33,7 +33,6 @@ import java.awt.image.WritableRaster;
 import org.opengis.geometry.Envelope;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.coverage.SampleDimension;
-import org.apache.sis.internal.coverage.j2d.BufferedGridCoverage;
 import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
 import org.apache.sis.internal.coverage.j2d.TiledImage;
 import org.apache.sis.internal.feature.Resources;
diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/BufferedGridCoverageTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/BufferedGridCoverageTest.java
similarity index 88%
rename from core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/BufferedGridCoverageTest.java
rename to core/sis-feature/src/test/java/org/apache/sis/coverage/grid/BufferedGridCoverageTest.java
index fe957aa..7ed1c2d 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/BufferedGridCoverageTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/BufferedGridCoverageTest.java
@@ -14,16 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.coverage.j2d;
+package org.apache.sis.coverage.grid;
 
 import java.util.List;
 import java.awt.image.BufferedImage;
 import java.awt.image.DataBuffer;
 import java.awt.image.WritableRaster;
 import org.apache.sis.coverage.SampleDimension;
-import org.apache.sis.coverage.grid.GridCoverage;
-import org.apache.sis.coverage.grid.GridCoverage2DTest;
-import org.apache.sis.coverage.grid.GridGeometry;
 
 
 /**
@@ -52,7 +49,7 @@ public final strictfp class BufferedGridCoverageTest extends GridCoverage2DTest
      * @return the coverage instance to test, with above-cited values.
      */
     @Override
-    protected GridCoverage createTestCoverage(final GridGeometry grid, final List<SampleDimension> sd) {
+    GridCoverage createTestCoverage(final GridGeometry grid, final List<SampleDimension> sd) {
         /*
          * Create the grid coverage, gets its image and set values directly as short integers.
          */
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
index 525df47..e94fe90 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
@@ -94,7 +94,7 @@ public strictfp class GridCoverage2DTest extends TestCase {
      * @param  sd    the sample dimensions of the coverage to create.
      * @return the coverage instance to test, with above-cited values.
      */
-    protected GridCoverage createTestCoverage(final GridGeometry grid, final List<SampleDimension> sd) {
+    GridCoverage createTestCoverage(final GridGeometry grid, final List<SampleDimension> sd) {
         /*
          * Create an image and set values directly as integers. We do not use one of the
          * BufferedImage.TYPE_* constant because this test uses some negative values.
diff --git a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
index 6f9ab2c..21a1ac4 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
@@ -97,10 +97,10 @@ import org.junit.runners.Suite;
     org.apache.sis.coverage.grid.FractionalGridCoordinatesTest.class,
     org.apache.sis.coverage.grid.ReshapedImageTest.class,
     org.apache.sis.coverage.grid.GridCoverage2DTest.class,
+    org.apache.sis.coverage.grid.BufferedGridCoverageTest.class,
     org.apache.sis.coverage.grid.GridCoverageBuilderTest.class,
     org.apache.sis.coverage.grid.ResampledGridCoverageTest.class,
     org.apache.sis.internal.coverage.j2d.BandedSampleConverterTest.class,
-    org.apache.sis.internal.coverage.j2d.BufferedGridCoverageTest.class,
 
     // Index
     org.apache.sis.index.tree.PointTreeNodeTest.class,
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java
index 0200eb4..39a6b65 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java
@@ -25,7 +25,7 @@ import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.ImageRenderer;
-import org.apache.sis.internal.coverage.j2d.BufferedGridCoverage;
+import org.apache.sis.coverage.grid.BufferedGridCoverage;
 
 
 /**


[sis] 01/03: Add a flag for reducing the amount of exceptions created when a point is outside coverage bounds.

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 8910f3400127d1ab823cc3e4530341fbb31b25a2
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Jun 10 12:41:49 2020 +0200

    Add a flag for reducing the amount of exceptions created when a point is outside coverage bounds.
---
 .../org/apache/sis/gui/map/ValuesUnderCursor.java  |  1 +
 .../sis/coverage/grid/ConvertedGridCoverage.java   |  5 ++-
 .../org/apache/sis/coverage/grid/Evaluator.java    | 37 +++++++++++++++++++++-
 .../apache/sis/coverage/grid/GridCoverage2D.java   |  3 ++
 .../org/apache/sis/coverage/grid/package-info.java | 20 +++++++++++-
 5 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java
index a191e6c..4baf8a9 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java
@@ -305,6 +305,7 @@ public abstract class ValuesUnderCursor {
                 return;
             }
             evaluator = coverage.forConvertedValues(true).evaluator();
+            evaluator.setNullIfOutside(true);
             if (previous != null && bands.equals(previous.getSampleDimensions())) {
                 // Same configuration than previous coverage.
                 return;
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java
index 787e6e0..3d117c0 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ConvertedGridCoverage.java
@@ -215,9 +215,8 @@ final class ConvertedGridCoverage extends GridCoverage {
          */
         @Override
         public double[] apply(final DirectPosition point) throws CannotEvaluateException {
-            final double[] values;
-            try {
-                values = evaluator.apply(point);
+            final double[] values = evaluator.apply(point);
+            if (values != null) try {
                 for (int i=0; i<converters.length; i++) {
                     values[i] = converters[i].transform(values[i]);
                 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/Evaluator.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/Evaluator.java
index b7c16e2..c416f7e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/Evaluator.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/Evaluator.java
@@ -54,7 +54,7 @@ public class Evaluator implements Function<DirectPosition, double[]> {
     /**
      * The coverage in which to evaluate sample values.
      */
-    protected final GridCoverage coverage;
+    private final GridCoverage coverage;
 
     /**
      * The source coordinate reference system of the converter,
@@ -83,6 +83,14 @@ public class Evaluator implements Function<DirectPosition, double[]> {
     private double[] values;
 
     /**
+     * Whether to return {@code null} instead than throwing an exception if given point
+     * is outside coverage bounds.
+     *
+     * @see #isNullIfOutside()
+     */
+    private boolean nullIfOutside;
+
+    /**
      * Creates a new evaluator for the given coverage. This constructor is protected for allowing
      * {@link GridCoverage} subclasses to provide their own {@code Evaluator} implementations.
      * For using an evaluator, invoke {@link GridCoverage#evaluator()} instead.
@@ -106,6 +114,30 @@ public class Evaluator implements Function<DirectPosition, double[]> {
     }
 
     /**
+     * Returns whether to return {@code null} instead than throwing an exception if a point is outside coverage bounds.
+     * The default value is {@code false}, which means that the default {@link #apply(DirectPosition)} behavior is to
+     * throw {@link PointOutsideCoverageException} for points outside bounds.
+     *
+     * @return whether {@link #apply(DirectPosition)} return {@code null} for points outside coverage bounds.
+     */
+    public boolean isNullIfOutside() {
+        return nullIfOutside;
+    }
+
+    /**
+     * Sets whether to return {@code null} instead than throwing an exception if a point is outside coverage bounds.
+     * The default value is {@code false}. Setting this flag to {@code true} may improve performances if the caller
+     * expects that many points will be outside coverage bounds, since it reduces the amount of exceptions to be
+     * created.
+     *
+     * @param  flag  whether {@link #apply(DirectPosition)} should use {@code null} return value instead than
+     *               {@link PointOutsideCoverageException} for signaling that a point is outside coverage bounds.
+     */
+    public void setNullIfOutside(final boolean flag) {
+        nullIfOutside = flag;
+    }
+
+    /**
      * Returns a sequence of double values for a given point in the coverage.
      * The CRS of the given point may be any coordinate reference system;
      * coordinate transformations will be applied as needed.
@@ -145,6 +177,9 @@ public class Evaluator implements Function<DirectPosition, double[]> {
                 final GridExtent subExtent = gc.toExtent(gridGeometry.extent, size);
                 return evaluate(coverage.render(subExtent), 0, 0);
             } catch (ArithmeticException | IndexOutOfBoundsException | DisjointExtentException ex) {
+                if (nullIfOutside) {
+                    return null;
+                }
                 throw (PointOutsideCoverageException) new PointOutsideCoverageException(
                         gc.pointOutsideCoverage(gridGeometry.extent), point).initCause(ex);
             }
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 6f46031..27785cd 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
@@ -534,6 +534,9 @@ public class GridCoverage2D extends GridCoverage {
                     final int y = toIntExact(addExact(gc.getCoordinateValue(yDimension), gridToImageY));
                     return evaluate(data, x, y);
                 } catch (ArithmeticException | IndexOutOfBoundsException | DisjointExtentException ex) {
+                    if (isNullIfOutside()) {
+                        return null;
+                    }
                     throw (PointOutsideCoverageException) new PointOutsideCoverageException(
                             gc.pointOutsideCoverage(gridGeometry.extent), point).initCause(ex);
                 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/package-info.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/package-info.java
index 7e382c0..4ba9038 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/package-info.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/package-info.java
@@ -17,9 +17,27 @@
 
 
 /**
- * A coverage backed by a regular grid. In the two-dimensional case, the grid coverage is an image and the cells are pixels.
+ * A coverage backed by a regular grid.
+ * In the two-dimensional case, the grid coverage is an image and the cells are pixels.
  * In the three-dimensional case, the cells are voxels.
  *
+ * <p>{@link org.apache.sis.coverage.grid.GridCoverage2D}
+ * is a two-dimensional slice in a <var>n</var>-dimensional cube of data.
+ * Despite its name, {@code GridCoverage2D} instances can be associated to <var>n</var>-dimensional
+ * {@linkplain org.opengis.geometry.Envelope envelopes} providing that only two dimensions have a
+ * {@link org.apache.sis.coverage.grid.GridExtent#getSize(int) grid span} greater than 1.</p>
+ *
+ * <p>{@link org.apache.sis.coverage.grid.GridCoverageBuilder} is a convenience class
+ * making easier to create a grid coverage for some common cases.</p>
+ *
+ * <h2>Accurate definition of georeferencing information</h2>
+ * While it is possible to create a grid coverage from a geodetic
+ * {@linkplain org.opengis.geometry.Envelope envelope}, this approach should be used <em>in last resort</em> only.
+ * Instead, always specify the <cite>grid to CRS</cite> affine transform.
+ * This is preferable because envelopes have ambiguities
+ * (do we need to swap the longitude and latitude axes? Do we need to flip the <var>y</var> axis?).
+ * On the other hand, the <cite>grid to CRS</cite> affine transform is fully determinist.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
  * @version 1.1