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 173ab3a Coverage : add evaluate method on GridCoverage class
173ab3a is described below
commit 173ab3a77b565e5dc6296b58054ff63321c4848d
Author: jsorel <[email protected]>
AuthorDate: Fri Nov 15 16:37:30 2019 +0100
Coverage : add evaluate method on GridCoverage class
---
.../org/apache/sis/coverage/grid/GridCoverage.java | 91 +++++++++++++++++++++-
.../java/org/apache/sis/image/PixelIterator.java | 28 ++++++-
.../internal/coverage/ConvertedGridCoverage.java | 7 +-
.../sis/internal/coverage/GridCoverage2D.java | 72 +++--------------
.../coverage/BufferedGridCoverageTest.java | 26 +++++++
.../sis/internal/coverage/GridCoverage2DTest.java | 26 +++++++
6 files changed, 182 insertions(+), 68 deletions(-)
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
index 4e1005e..6da4620 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
@@ -20,11 +20,19 @@ import java.util.List;
import java.util.Collection;
import java.util.Locale;
import java.awt.image.RenderedImage;
+import org.opengis.coverage.PointOutsideCoverageException;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
import org.apache.sis.internal.util.UnmodifiableArrayList;
import org.apache.sis.coverage.SampleDimension;
import org.apache.sis.coverage.SubspaceNotSpecifiedException;
+import org.apache.sis.image.PixelIterator;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.util.collection.DefaultTreeTable;
import org.apache.sis.util.collection.TableColumn;
import org.apache.sis.util.collection.TreeTable;
@@ -45,7 +53,7 @@ import org.opengis.coverage.CannotEvaluateException;
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Johann Sorel (Geomatys)
- * @version 1.0
+ * @version 2.0
* @since 1.0
* @module
*/
@@ -226,6 +234,38 @@ public abstract class GridCoverage {
public abstract RenderedImage render(GridExtent sliceExtent) throws
CannotEvaluateException;
/**
+ * Returns a sequence of double values for a given point in the coverage.
A value for each
+ * {@linkplain SampleDimension sample dimension} is included in the
sequence. The default
+ * interpolation type used when accessing grid values for points which
fall between grid cells
+ * is nearest neighbor.
+ * The CRS of the point may be in any coordinate reference system.
+ * If the CRS of the point is undefined, it is assumed to be the same as
the coverage.
+ *
+ * @param coord The coordinate point where to evaluate.
+ * @param dest An array in which to store values, or {@code null} to
create a new array.
+ * @return The {@code dest} array, or a newly created array if {@code
dest} was null.
+ * @throws PointOutsideCoverageException if the evaluation failed because
the input point
+ * has invalid coordinates.
+ * @throws CannotEvaluateException if the values can't be computed at the
specified coordinate
+ * for an other reason. It may be thrown if the coverage data type
can't be converted
+ * to {@code double} by an identity or widening conversion.
Subclasses may relax this
+ * constraint if appropriate.
+ */
+ public double[] evaluate(DirectPosition coord, double[] dest) throws
CannotEvaluateException {
+ try {
+ coord = toGridCoord(coord);
+ final long[] coordl = toLongExact(coord);
+ final GridExtent subExtent = new GridExtent(null, coordl, coordl,
true);
+ final RenderedImage image = render(subExtent);
+ final PixelIterator ite = PixelIterator.create(image);
+ ite.moveTo(0, 0);
+ return ite.getPixel(dest);
+ } catch (FactoryException | TransformException ex) {
+ throw new CannotEvaluateException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
* Returns a string representation of this grid coverage for debugging
purpose.
* The returned string is implementation dependent and may change in any
future version.
* Current implementation is equivalent to the following, where {@code
<default flags>}
@@ -269,4 +309,53 @@ public abstract class GridCoverage {
branch.newChild().setValue(column, SampleDimension.toString(locale,
sampleDimensions));
return tree;
}
+
+ /**
+ * Converts the specified point to grid coordinate.
+ *
+ * @param point point to transform to grid coordinate
+ * @return point in grid coordinate
+ * @throws org.opengis.util.FactoryException if creating transformation
fails
+ * @throws org.opengis.referencing.operation.TransformException if
transformation fails
+ */
+ protected DirectPosition toGridCoord(final DirectPosition point)
+ throws FactoryException, TransformException
+ {
+ final CoordinateReferenceSystem sourceCRS =
point.getCoordinateReferenceSystem();
+ MathTransform trs =
getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER).inverse();
+ if (sourceCRS != null) {
+ MathTransform toCrs = CRS.findOperation(sourceCRS,
getCoordinateReferenceSystem(), null).getMathTransform();
+ if (!toCrs.isIdentity()) {
+ trs = MathTransforms.concatenate(toCrs, trs);
+ }
+ }
+ return trs.transform(point, null);
+ }
+
+ /**
+ * Converts given grid coordinate to long values and ensure coordinate
+ * is inside grid geometry extent.
+ *
+ * @param position in grid coordinate
+ * @return position as long type in grid coordinate
+ * @throws PointOutsideCoverageException
+ */
+ protected long[] toLongExact(DirectPosition position) throws
PointOutsideCoverageException {
+ final long[] coord = new long[position.getDimension()];
+ final GridExtent extent = getGridGeometry().getExtent();
+ final long[] low = extent.getLow().getCoordinateValues();
+ final long[] high = extent.getHigh().getCoordinateValues();
+
+ for (int i = 0; i < coord.length; i++) {
+ final double dv = position.getOrdinate(i);
+ if (!Double.isFinite(dv)) {
+ throw new PointOutsideCoverageException("Position outside
coverage, axis " + i + " value " + dv);
+ }
+ coord[i] = Math.round(dv);
+ if (coord[i] < low[i] || coord[i] > high[i]) {
+ throw new PointOutsideCoverageException("Position outside
coverage, axis " + i + " value " + coord[i]);
+ }
+ }
+ return coord;
+ }
}
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 4235a5d..a0be7ec 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
@@ -22,6 +22,7 @@ import java.nio.Buffer;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
@@ -163,8 +164,31 @@ public abstract class PixelIterator {
numBands = data.getSampleModel().getNumBands();
tileWidth = data.getTileWidth();
tileHeight = data.getTileHeight();
- tileGridXOffset = data.getTileGridXOffset();
- tileGridYOffset = data.getTileGridYOffset();
+
+ if (data instanceof BufferedImage) {
+ /**
+ * BufferedImage.getSubImage produces an image which raster has a
SampleModelTranslateX
+ * which concatenate the offset requested in getSubImage.
+ * This means each tile raster may have a unique offset and that
+ * we can't compute the real tile pixels bounds until we have read
the tile raster.
+ *
+ * This PixelIterator assume each tile has a deterministic
position in the image
+ * which allows to fetch the exact tiles when required.
+ *
+ * To compensate the raster offset we cheat on the tileGridOffset
in this special case.
+ * We can apply this trick only because we assume BufferedImage is
the only implementation
+ * to behave this way and because a BufferedImage only has one
raster tile.
+ */
+ final Raster rasterTemplate = data.getTile(data.getMinTileX(),
data.getMinTileY());
+ final int innerRasterTranslationX =
rasterTemplate.getSampleModelTranslateX() - data.getMinX();
+ final int innerRasterTranslationY =
rasterTemplate.getSampleModelTranslateY() - data.getMinY();
+ tileGridXOffset = data.getTileGridXOffset() -
innerRasterTranslationX;
+ tileGridYOffset = data.getTileGridYOffset() -
innerRasterTranslationY;
+ } else {
+ tileGridXOffset = data.getTileGridXOffset();
+ tileGridYOffset = data.getTileGridYOffset();
+ }
+
bounds = intersection(data.getMinX(), data.getMinY(),
data.getWidth(), data.getHeight(), subArea, window);
lowerX = bounds.x;
lowerY = bounds.y;
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ConvertedGridCoverage.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ConvertedGridCoverage.java
index 60d6f2b..5fed1e8 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ConvertedGridCoverage.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/ConvertedGridCoverage.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.internal.coverage;
+import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
@@ -125,14 +126,16 @@ final class ConvertedGridCoverage extends GridCoverage {
return render;
}
final Raster raster;
- if (render.getNumXTiles() == 1 && render.getNumYTiles() == 1) {
+ if (render.getNumXTiles() == 1 && render.getNumYTiles() == 1 &&
render.getTileGridXOffset() == 0 && render.getTileGridYOffset() == 0) {
raster = render.getTile(render.getMinTileX(),
render.getMinTileY());
} else {
/*
* This fallback is very inefficient since it copies all data in
one big raster.
* We will replace this class by tiles management in a future
Apache SIS version.
+ *
+ * Note : we need to specify the Rectangle to reset raster
location at 0,0
*/
- raster = render.getData();
+ raster = render.getData(new Rectangle(render.getMinX(),
render.getMinY(), render.getWidth(), render.getHeight()));
}
final SampleModel baseSm = raster.getSampleModel();
final DataBuffer dataBuffer = raster.getDataBuffer();
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java
index b2b9bf8..247baaf 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java
@@ -16,7 +16,6 @@
*/
package org.apache.sis.internal.coverage;
-import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.util.Collection;
@@ -26,14 +25,10 @@ import org.apache.sis.coverage.grid.GridExtent;
import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.internal.image.TranslatedRenderedImage;
import org.apache.sis.referencing.CRS;
-import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.util.ArgumentChecks;
import org.opengis.coverage.CannotEvaluateException;
-import org.opengis.coverage.PointOutsideCoverageException;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.datum.PixelInCell;
-import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.FactoryException;
@@ -60,8 +55,7 @@ public final class GridCoverage2D extends GridCoverage {
/**
* The given RenderedImage may not start at 0,0, so does the gridExtent of
the grid geometry.
- * Image minX/MinY coordinate is expected to be located grid extent lower
corner.
- *
+ * Image 0/0 coordinate is expected to match grid extent lower corner.
*
* @param grid the grid extent, CRS and conversion from cell indices to
CRS.
* @param bands sample dimensions for each image band.
@@ -84,7 +78,6 @@ public final class GridCoverage2D extends GridCoverage {
if (image.getHeight()!= extent.getSize(imageAxes[1])) {
throw new IllegalArgumentException("Image height " +
image.getHeight()+ "does not match grid extent height "+
extent.getSize(imageAxes[1]));
}
-
}
/**
@@ -136,70 +129,23 @@ public final class GridCoverage2D extends GridCoverage {
}
}
+ /**
+ * {@inheritDoc }
+ */
+ @Override
public double[] evaluate(DirectPosition position, double[] buffer) throws
CannotEvaluateException {
-
try {
position = toGridCoord(position);
-
- int x = 0;
- int y = 0;
- for (int i = 0, n = position.getDimension(); i < n; i++) {
- final double dv = position.getOrdinate(i);
- if (Double.isFinite(dv)) {
- throw new PointOutsideCoverageException("Position outside
coverage, axis " + i + " value " + dv);
- }
-
- final int v = Math.toIntExact(Math.round(dv));
- if (i == imageAxes[0]) {
- x = v;
- } else if (i == imageAxes[1]) {
- y = v;
- } else if (v != 0) {
- //coverage is a slice, all other indices must be zero,
otherwise we are outside coverage
- throw new PointOutsideCoverageException("Position outside
coverage, axis " + i + " value " + v);
- }
- }
-
- if (getBounds().contains(x,y)) {
- return image.getTile(XToTileX(x), YToTileY(y)).getPixel(x, y,
buffer);
- }
- throw new PointOutsideCoverageException("");
+ long[] coord = toLongExact(position);
+ int x = Math.toIntExact(Math.round(coord[imageAxes[0]]));
+ int y = Math.toIntExact(Math.round(coord[imageAxes[1]]));
+ return image.getTile(XToTileX(x), YToTileY(y)).getPixel(x, y,
buffer);
} catch (FactoryException | TransformException ex) {
throw new CannotEvaluateException(ex.getMessage(), ex);
}
}
/**
- * Converts the specified point to grid coordinate.
- *
- * @param point point to transform to grid coordinate
- * @return point in grid coordinate
- * @throws org.opengis.util.FactoryException if creating transformation
fails
- * @throws org.opengis.referencing.operation.TransformException if
transformation fails
- */
- protected DirectPosition toGridCoord(final DirectPosition point)
- throws FactoryException, TransformException
- {
- final CoordinateReferenceSystem sourceCRS =
point.getCoordinateReferenceSystem();
- final MathTransform trs;
- if (sourceCRS != null) {
- MathTransform toCrs = CRS.findOperation(sourceCRS,
getCoordinateReferenceSystem(), null).getMathTransform();
- trs = MathTransforms.concatenate(toCrs,
getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER).inverse());
- } else {
- trs = getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER);
- }
- return trs.transform(point, null);
- }
-
- /**
- * Utility method to convert image bounds as {@link java.awt.Rectangle}.
- * @return {@link java.awt.Rectangle} bounds.
- */
- private Rectangle getBounds() {
- return new Rectangle(image.getMinX(), image.getMinY(),
image.getWidth(), image.getHeight());
- }
-
- /**
* Converts a pixel's X coordinate into a horizontal tile index.
* @param x pixel x coordinate
* @return tile x coordinate
diff --git
a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/BufferedGridCoverageTest.java
b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/BufferedGridCoverageTest.java
index 786b803..d151ed0 100644
---
a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/BufferedGridCoverageTest.java
+++
b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/BufferedGridCoverageTest.java
@@ -25,6 +25,7 @@ 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.geometry.DirectPosition2D;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.measure.Units;
import org.apache.sis.referencing.crs.HardCodedCRS;
@@ -34,6 +35,7 @@ import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform1D;
import org.junit.Assert;
import org.junit.Test;
+import org.opengis.coverage.PointOutsideCoverageException;
/**
@@ -100,6 +102,30 @@ public class BufferedGridCoverageTest extends TestCase {
{ -60, -195},
{-216, -380}
});
+
+ /**
+ * Test evaluation
+ */
+ Assert.assertArrayEquals(new double[]{ 70.0}, coverage.evaluate(new
DirectPosition2D(0, 0), null), STRICT);
+ Assert.assertArrayEquals(new double[]{ 2.5}, coverage.evaluate(new
DirectPosition2D(1, 0), null), STRICT);
+ Assert.assertArrayEquals(new double[]{- 8.0}, coverage.evaluate(new
DirectPosition2D(0, 1), null), STRICT);
+ Assert.assertArrayEquals(new double[]{-90.0}, coverage.evaluate(new
DirectPosition2D(1, 1), null), STRICT);
+ //test nearest neighor rounding
+ Assert.assertArrayEquals(new double[]{70.0}, coverage.evaluate(new
DirectPosition2D(-0.499, -0.499), null), STRICT);
+ Assert.assertArrayEquals(new double[]{70.0}, coverage.evaluate(new
DirectPosition2D( 0.499, 0.499), null), STRICT);
+ //test out of coverage
+ try {
+ coverage.evaluate(new DirectPosition2D(-0.51, 0), null);
+ Assert.fail("Point ouside coverage evalue must fail");
+ } catch (PointOutsideCoverageException ex) {
+ //ok
+ }
+ try {
+ coverage.evaluate(new DirectPosition2D(1.51, 0), null);
+ Assert.fail("Point ouside coverage evalue must fail");
+ } catch (PointOutsideCoverageException ex) {
+ //ok
+ }
}
/**
diff --git
a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/GridCoverage2DTest.java
b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/GridCoverage2DTest.java
index d1de1eb..073a100 100644
---
a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/GridCoverage2DTest.java
+++
b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/GridCoverage2DTest.java
@@ -31,6 +31,7 @@ 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.geometry.DirectPosition2D;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.measure.Units;
import org.apache.sis.referencing.crs.HardCodedCRS;
@@ -38,6 +39,7 @@ import
org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.test.TestCase;
import org.junit.Assert;
import org.junit.Test;
+import org.opengis.coverage.PointOutsideCoverageException;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform1D;
import org.opengis.util.FactoryException;
@@ -110,6 +112,30 @@ public class GridCoverage2DTest extends TestCase {
{ -60, -195},
{-216, -380}
});
+
+ /**
+ * Test evaluation
+ */
+ Assert.assertArrayEquals(new double[]{ 70.0}, coverage.evaluate(new
DirectPosition2D(0, 0), null), STRICT);
+ Assert.assertArrayEquals(new double[]{ 2.5}, coverage.evaluate(new
DirectPosition2D(1, 0), null), STRICT);
+ Assert.assertArrayEquals(new double[]{- 8.0}, coverage.evaluate(new
DirectPosition2D(0, 1), null), STRICT);
+ Assert.assertArrayEquals(new double[]{-90.0}, coverage.evaluate(new
DirectPosition2D(1, 1), null), STRICT);
+ //test nearest neighor rounding
+ Assert.assertArrayEquals(new double[]{70.0}, coverage.evaluate(new
DirectPosition2D(-0.499, -0.499), null), STRICT);
+ Assert.assertArrayEquals(new double[]{70.0}, coverage.evaluate(new
DirectPosition2D( 0.499, 0.499), null), STRICT);
+ //test out of coverage
+ try {
+ coverage.evaluate(new DirectPosition2D(-0.51, 0), null);
+ Assert.fail("Point ouside coverage evalue must fail");
+ } catch (PointOutsideCoverageException ex) {
+ //ok
+ }
+ try {
+ coverage.evaluate(new DirectPosition2D(1.51, 0), null);
+ Assert.fail("Point ouside coverage evalue must fail");
+ } catch (PointOutsideCoverageException ex) {
+ //ok
+ }
}
/**