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
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 7913e12a60 Notify GDAL in advance of the region which will be read
(all tiles). This commit contains again renaming of some `TiledGridCoverage`
methods in an attempt to make them less ambiguous.
7913e12a60 is described below
commit 7913e12a603d9ff74c668f40efd8725553f898b0
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Sep 18 17:10:40 2024 +0200
Notify GDAL in advance of the region which will be read (all tiles).
This commit contains again renaming of some `TiledGridCoverage` methods in
an attempt to make them less ambiguous.
---
.../org/apache/sis/storage/geotiff/DataSubset.java | 2 +-
.../apache/sis/storage/base/TiledGridCoverage.java | 197 ++++++++++++++-------
.../main/org/apache/sis/storage/gdal/Band.java | 69 ++++++--
.../main/org/apache/sis/storage/gdal/GDAL.java | 22 ++-
.../org/apache/sis/storage/gdal/TiledCoverage.java | 36 +++-
5 files changed, 236 insertions(+), 90 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
index 6140124f95..b64a104605 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
@@ -61,7 +61,7 @@ import static org.apache.sis.pending.jdk.JDK18.ceilDiv;
* <h2>Cell Coordinates</h2>
* When there is no subsampling, {@code DataSubset} uses the same cell
coordinates as {@link DataCube}.
* When there is a subsampling, cell coordinates in this subset are divided by
the subsampling factors.
- * Conversion is done by {@link #pixelToResourceCoordinate(long, int)}.
+ * Conversion is done by {@link #coverageToResourceCoordinate(long, int)}.
*
* <h2>Tile Matrix Coordinates</h2>
* In each {@code DataSubset}, indices of tiles starts at (0, 0, …). This
class does not use
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java
index dab74aaff7..499fa4056e 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java
@@ -64,7 +64,7 @@ import org.opengis.coordinate.MismatchedDimensionException;
* <h2>Cell coordinates</h2>
* When there is no subsampling, this coverage uses the same cell coordinates
as the originating resource.
* When there is a subsampling, cell coordinates in this coverage are divided
by the subsampling factors.
- * Conversions are done by {@link #pixelToResourceCoordinates(Rectangle)}.
+ * Conversions are done by {@link #coverageToResourceCoordinate(long, int)}.
*
* <p><b>DEsign note:</b> {@code TiledGridCoverage} use the same cell
coordinates as the originating
* {@link TiledGridResource} (when no subsampling) because those two classes
use {@code long} integers.
@@ -95,7 +95,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
* This is the intersection between user-specified domain and the source
* {@link TiledGridResource} domain, expanded to an integer number of
tiles.
*/
- private final GridExtent readExtent;
+ protected final GridExtent readExtent;
/**
* Whether to force the {@link #readExtent} tile intersection to the
{@link #virtualTileSize}.
@@ -148,7 +148,7 @@ public abstract class TiledGridCoverage extends
GridCoverage {
/**
* The Tile Matrix Coordinates (<abbr>TMC</abbr>) that tile (0,0) of this
coverage
* would have in the originating {@code TiledGridResource}.
- * This is the value to subtract from tile indices computed from pixel
coordinates.
+ * This is the value to subtract from tile indices computed from cell
coordinates.
*
* <p>The current implementation assumes that the tile (0,0) in the
resource starts
* at cell coordinates (0,0) of the resource.</p>
@@ -159,7 +159,7 @@ public abstract class TiledGridCoverage extends
GridCoverage {
private final long[] tmcOfFirstTile;
/**
- * Conversion from pixel coordinates in this (potentially subsampled)
coverage
+ * Conversion from cell coordinates in this (potentially subsampled)
coverage
* to cell coordinates in the originating resource coverage at full
resolution.
* The conversion from (<var>x</var>, <var>y</var>) to (<var>x′</var>,
<var>y′</var>) is as below,
* where <var>s</var> are subsampling factors and <var>t</var> are
subsampling offsets:
@@ -172,7 +172,7 @@ public abstract class TiledGridCoverage extends
GridCoverage {
* This transform maps {@linkplain
org.apache.sis.coverage.grid.PixelInCell#CELL_CORNER pixel corners}.
*
* @see #getSubsampling(int)
- * @see #pixelToResourceCoordinate(long, int)
+ * @see #coverageToResourceCoordinate(long, int)
*/
private final long[] subsampling, subsamplingOffsets;
@@ -321,50 +321,58 @@ public abstract class TiledGridCoverage extends
GridCoverage {
/**
* Converts a cell coordinate from this coverage to the {@code
TiledGridResource} coordinate space.
* This method removes the subsampling effect, i.e. returns the coordinate
that we would have if this
- * coverage was at full resolution. Such unsampled {@code
TiledGridCoverage} uses the same coordinates
- * as the originating {@link TiledGridResource}.
- *
- * <p>This method uses the "pixel" word for simplicity and because this
method is used mostly
- * for the first two dimensions, but "pixel" should be understood as "grid
coverage cell".</p>
+ * coverage was at full resolution. When there is no subsampling, {@code
TiledGridCoverage} uses the
+ * same coordinates as the originating {@link TiledGridResource}.
*
* @param coordinate coordinate in this {@code TiledGridCoverage} domain.
- * @param dimension dimension of the coordinate.
+ * @param dimension the dimension of the coordinate to convert.
* @return coordinate in this {@code TiledGridResource} with no
subsampling applied.
* @throws ArithmeticException if the coordinate cannot be represented as
a long integer.
- *
- * @see #pixelToResourceCoordinates(Rectangle)
*/
- private long pixelToResourceCoordinate(final long coordinate, final int
dimension) {
+ protected final long coverageToResourceCoordinate(final long coordinate,
final int dimension) {
return addExact(multiplyExact(coordinate, subsampling[dimension]),
subsamplingOffsets[dimension]);
}
/**
* Converts a cell coordinate from {@link TiledGridResource} space to
{@code TiledGridCoverage} coordinate.
- * This is the converse of {@link #pixelToResourceCoordinate(long, int)}.
+ * This method is the converse of {@link
#coverageToResourceCoordinate(long, int)}.
* Note that there is a possible accuracy lost.
*
* @param coordinate coordinate in the {@code TiledGridResource} domain.
- * @param dimension dimension of the coordinate.
+ * @param dimension the dimension of the coordinate to convert.
* @return coordinates in this subsampled {@code TiledGridCoverage} domain.
* @throws ArithmeticException if the coordinate cannot be represented as
a long integer.
*/
- private long resourceToPixelCoordinate(final long coordinate, final int
dimension) {
+ private long resourceToCoverageCoordinate(final long coordinate, final int
dimension) {
return floorDiv(subtractExact(coordinate,
subsamplingOffsets[dimension]), subsampling[dimension]);
}
+ /**
+ * Converts a tile index from the <abbr>TMC</abbr> of this coverage to a
cell coordinate in the originating resource.
+ * Note that the computation (like all methods in this class) uses the
<em>virtual</em> tile size.
+ * This is usually the same as the real tile size, but not always.
+ *
+ * @param tileIndex tile index from the <abbr>TMC</abbr> of this
coverage.
+ * @param dimension the dimension of the coordinate to convert.
+ * @return cell coordinate of the tile lower coordinate in the originating
resource.
+ */
+ final long coverageTileToResourceCell(final long tileIndex, final int
dimension) {
+ return multiplyExact(addExact(tileIndex, tmcOfFirstTile[dimension]),
virtualTileSize[dimension]);
+ }
+
/**
* Converts a cell coordinate from this {@code TiledGridCoverage}
coordinate space to
* the Tile Matrix Coordinate (<abbr>TMC</abbr>) of the tile which
contains that cell.
- * The <abbr>TMC</abbr> is relative to the full {@link TiledGridResource},
+ * The returned <abbr>TMC</abbr> is relative to the full {@link
TiledGridResource},
* i.e. without subtraction of {@link #tmcOfFirstTile}.
*
* @param coordinate coordinates in this {@code TiledGridCoverage}
domain.
- * @param dimension dimension of the coordinate.
+ * @param dimension the dimension of the coordinate to convert.
* @return Tile Matrix Coordinate (TMC) of the tile which contains the
specified cell.
* @throws ArithmeticException if the coordinate cannot be represented as
an integer.
*/
- private long toResourceTileMatrixCoordinate(final long coordinate, final
int dimension) {
- return floorDiv(pixelToResourceCoordinate(coordinate, dimension),
virtualTileSize[dimension]);
+ private long coverageCellToResourceTile(final long coordinate, final int
dimension) {
+ return floorDiv(coverageToResourceCoordinate(coordinate, dimension),
virtualTileSize[dimension]);
}
/**
@@ -411,6 +419,25 @@ public abstract class TiledGridCoverage extends
GridCoverage {
return 1;
}
+ /**
+ * Returns the two-dimensional slice of the given grid extent, converted
to 32-bits integers.
+ * By default, the <var>x</var> axis is the grid dimension at index 0 and
the <var>y</var> axis
+ * is the grid dimension at index 1. Other dimensions are ignored.
+ *
+ * @param extent the grid extent to slice, or {@code null}.
+ * @return two-dimensional slice of the given extent, or {@code null} if
the given extent was null.
+ * @throws ArithmeticException if the extent exceeds the capacity of
32-bits integers.
+ */
+ protected final Rectangle bidimensional(final GridExtent extent) {
+ if (extent == null) {
+ return null;
+ }
+ return new Rectangle(toIntExact(extent.getLow (X_DIMENSION)),
+ toIntExact(extent.getLow (Y_DIMENSION)),
+ toIntExact(extent.getSize(X_DIMENSION)),
+ toIntExact(extent.getSize(Y_DIMENSION)));
+ }
+
/**
* Returns a two-dimensional slice of grid data as a rendered image.
*
@@ -446,8 +473,8 @@ public abstract class TiledGridCoverage extends
GridCoverage {
final long max = available .getHigh(i); // Highest
valid coordinate, inclusive.
final long aoiMin = sliceExtent.getLow (i); // Requested
coordinate in subsampled image.
final long aoiMax = sliceExtent.getHigh(i);
- final long tileUp =
incrementExact(toResourceTileMatrixCoordinate(Math.min(aoiMax, max), i));
- final long tileLo =
toResourceTileMatrixCoordinate(Math.max(aoiMin, min), i);
+ final long tileUp =
incrementExact(coverageCellToResourceTile(Math.min(aoiMax, max), i));
+ final long tileLo =
coverageCellToResourceTile(Math.max(aoiMin, min), i);
if (tileUp <= tileLo) {
final String message = Errors.forLocale(getLocale())
.getString(Errors.Keys.IllegalRange_2, aoiMin,
aoiMax);
@@ -458,8 +485,8 @@ public abstract class TiledGridCoverage extends
GridCoverage {
}
}
// Lower and upper coordinates in subsampled image, rounded to
integer number of tiles and clipped to available data.
- final long lower = /* inclusive
*/Math.max(resourceToPixelCoordinate(/* inclusive */multiplyExact(tileLo,
virtualTileSize[i]), i), min);
- final long upper =
incrementExact(Math.min(resourceToPixelCoordinate(decrementExact(multiplyExact(tileUp,
virtualTileSize[i])), i), max));
+ final long lower = /* inclusive
*/Math.max(resourceToCoverageCoordinate(/* inclusive */multiplyExact(tileLo,
virtualTileSize[i]), i), min);
+ final long upper =
incrementExact(Math.min(resourceToCoverageCoordinate(decrementExact(multiplyExact(tileUp,
virtualTileSize[i])), i), max));
imageSize[i] = toIntExact(subtractExact(upper, lower));
offsetAOI[i] = toIntExact(subtractExact(lower, aoiMin));
tileLower[i] = toIntExact(subtractExact(tileLo,
tmcOfFirstTile[i]));
@@ -812,12 +839,13 @@ public abstract class TiledGridCoverage extends
GridCoverage {
/**
* Pixel coordinates to assign to the upper-left corner of the region
to render, with subsampling applied.
* This is the difference between the region requested by user and the
region which will be rendered.
+ * This is often 0 or negative. May be positive if the image has been
clipped.
*/
private final int[] offsetAOI;
/**
* Cell coordinates of current iterator position relative to the Area
Of Interest specified by user.
- * Those coordinates are in units of the coverage at full resolution.
+ * Those coordinates are in units of the coverage at full resolution
(except for the translation).
* Initial position is {@link #offsetAOI} multiplied by {@link
#subsampling}.
* This array is modified by calls to {@link #next()}.
*/
@@ -903,31 +931,93 @@ public abstract class TiledGridCoverage extends
GridCoverage {
}
/**
- * Returns the extent of the current tile in units of the full
coverage resource (without subsampling).
- * This method is a generalization to <var>n</var> dimensions of the
rectangle computed by the
- * following code:
- *
- * {@snippet lang="java" :
- * WritableRaster tile = createRaster();
- * Rectangle target = tile.getBounds();
- * Rectangle source = pixelToResourceCoordinates(bounds);
- * }
+ * Returns the extent of the full region to read (all tiles) in units
of the originating resource.
+ * The returned extent does not change during the iteration process
(this method is not available
+ * in {@link Snapshot} for that reason). This method is typically
invoked only at the beginning of
+ * the iteration process, when knowing in advance the full read region
allows some optimizations.
*
- * @return extent of this tile in units of the full coverage resource.
+ * <p>The returned region is based on the user's request in her/his
call to {@link #render(GridExtent)},
+ * but is not necessarily identical. The render method may have
expanded or clipped the user's request.</p>
*
- * @see #pixelToResourceCoordinates(Rectangle)
+ * @return extent of all tiles to be traversed by this iterator, in
units of the originating resource.
*/
- public final GridExtent getTileExtentInResource() {
- final int dimension = tileOffsetFull.length;
+ public GridExtent getFullRegionInResourceCoordinates() {
+ final int dimension = tileLower.length;
final var axes = new DimensionNameType[dimension];
final long[] lower = new long[dimension];
final long[] upper = new long[dimension];
for (int i=0; i<dimension; i++) {
- lower[i] = pixelToResourceCoordinate(getTileOrigin(i), i);
- upper[i] = addExact(lower[i], getTileSize(i));
axes [i] = readExtent.getAxisType(i).orElse(null);
+ lower[i] = Math.max(coverageTileToResourceCell(tileLower[i],
i), readExtent.getLow(i));
+ upper[i] = Math.min(coverageTileToResourceCell(tileUpper[i],
i)-1, readExtent.getHigh(i));
}
- return new GridExtent(axes, lower, upper, false);
+ return new GridExtent(axes, lower, upper, true);
+ }
+
+ /**
+ * Converts the given cell coordinate from the originating resource to
the pixel coordinate.
+ * The destination coordinate system is the coordinate system of the
{@link RenderedImage}
+ * to be rendered. Note that the top-left corner of the image is not
necessarily (0,0).
+ *
+ * @param coordinate pixel coordinate in the {@link RenderedImage}
coordinate system.
+ * @param dimension the dimension of the coordinate to convert.
+ * @return cell coordinate in the {@link TiledGridResource} coordinate
system.
+ */
+ public long resourceToImage(long coordinate, final int dimension) {
+ coordinate = Math.subtractExact(coordinate,
coverageTileToResourceCell(tileLower[dimension], dimension));
+ coordinate = Math.floorDiv(coordinate, getSubsampling(dimension));
+ coordinate = Math.addExact(coordinate, offsetAOI[dimension]);
+ return coordinate;
+ }
+
+ /**
+ * Converts the given pixel coordinate to the coordinate in the
originating resource.
+ * The source coordinate system is the coordinate system of the {@link
RenderedImage}
+ * to be rendered. Note that the top-left corner of the image is not
necessarily (0,0).
+ *
+ * @param coordinate pixel coordinate in the {@link RenderedImage}
coordinate system.
+ * @param dimension the dimension of the coordinate to convert.
+ * @return cell coordinate in the {@link TiledGridResource} coordinate
system.
+ */
+ public long imageToResource(long coordinate, final int dimension) {
+ coordinate = subtractExact(coordinate, offsetAOI[dimension]);
// (0,0) at image origin instead of AOI.
+ coordinate = multiplyExact(coordinate, getSubsampling(dimension));
// Full resolution, like in the resource.
+ coordinate = addExact(coordinate,
coverageTileToResourceCell(tileLower[dimension], dimension));
+ return coordinate;
+ }
+
+ /**
+ * Converts cell coordinates from the originating resource to pixel
coordinates.
+ *
+ * @param bounds the coordinates to convert.
+ * @return the converted coordinates.
+ * @throws ArithmeticException if the result cannot be expressed as
32-bits integers.
+ */
+ public Rectangle resourceToImage(final Rectangle bounds) {
+ long x, y; // Convenience for casting `int` to `long`.
+ final var r = new Rectangle();
+ r.x = toIntExact(resourceToImage(x = bounds.x,
X_DIMENSION));
+ r.y = toIntExact(resourceToImage(y = bounds.y,
Y_DIMENSION));
+ r.width = toIntExact(resourceToImage(x + bounds.width,
X_DIMENSION) - r.x);
+ r.height = toIntExact(resourceToImage(y + bounds.height,
Y_DIMENSION) - r.y);
+ return r;
+ }
+
+ /**
+ * Converts cell coordinates from pixel coordinates to the originating
resource.
+ *
+ * @param bounds the coordinates to convert.
+ * @return the converted coordinates.
+ * @throws ArithmeticException if the result cannot be expressed as
32-bits integers.
+ */
+ public Rectangle imageToResource(final Rectangle bounds) {
+ long x, y; // Convenience for casting `int` to `long`.
+ final var r = new Rectangle();
+ r.x = toIntExact(imageToResource(x = bounds.x,
X_DIMENSION));
+ r.y = toIntExact(imageToResource(y = bounds.y,
Y_DIMENSION));
+ r.width = toIntExact(imageToResource(x + bounds.width,
X_DIMENSION) - r.x);
+ r.height = toIntExact(imageToResource(y + bounds.height,
Y_DIMENSION) - r.y);
+ return r;
}
/**
@@ -1120,27 +1210,4 @@ public abstract class TiledGridCoverage extends
GridCoverage {
* (too many exception types to list them all).
*/
protected abstract Raster[] readTiles(TileIterator iterator) throws
IOException, DataStoreException;
-
- /**
- * Converts raster coordinate from this coverage to {@code
TiledGridResource} coordinate space.
- * This method removes the subsampling effect, i.e. returns the
coordinates that we would have if this
- * coverage was at full resolution. Such unsampled {@code
TiledGridCoverage} uses the same coordinates
- * as the originating {@link TiledGridResource}.
- *
- * <p>This method uses the "pixel" word for simplicity and because this
method is used for
- * the two-dimensional case, but "pixel" should be understood as "grid
coverage cell".</p>
- *
- * @param bounds the rectangle to convert.
- * @return the converted rectangle.
- * @throws ArithmeticException if the coordinate cannot be represented as
an integer.
- *
- * @see TileIterator#getTileExtentInResource()
- */
- protected final Rectangle pixelToResourceCoordinates(final Rectangle
bounds) {
- return new Rectangle(
- toIntExact(pixelToResourceCoordinate(bounds.x, X_DIMENSION)),
- toIntExact(pixelToResourceCoordinate(bounds.y, Y_DIMENSION)),
- toIntExact(multiplyExact(subsampling[X_DIMENSION],
bounds.width)),
- toIntExact(multiplyExact(subsampling[Y_DIMENSION],
bounds.height)));
- }
}
diff --git
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Band.java
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Band.java
index c8f06f505c..9e46ccdac1 100644
---
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Band.java
+++
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Band.java
@@ -266,7 +266,7 @@ final class Band {
* @param raster the Java2D raster where to store of fetch the
values to read or write.
* @param rasterBounds region to write or read in raster coordinates.
* @param transferBuffer a temporary buffer used for copying data.
- * @return whether the operation was successful according
<abbr>GDAL</abbr>.
+ * @return whether the operation was successful according
<abbr>GDAL</abbr>.
* @throws ClassCastException if an above-documented prerequisite is not
true.
* @throws DataStoreException if <var>GDAL</var> reported a warning or
fatal error.
*/
@@ -283,10 +283,13 @@ final class Band {
if (readWriteFlags == OpenFlag.READ && !(raster instanceof
WritableRaster)) {
throw new ClassCastException();
}
- final var sampleModel = (ComponentSampleModel)
raster.getSampleModel(); // See prerequisites in Javadoc.
- final var dataBuffer = raster.getDataBuffer();
- final int dataSize =
DataBuffer.getDataTypeSize(dataBuffer.getDataType()) / Byte.SIZE;
- final int[] bankIndices = sampleModel.getBankIndices();
+ final var dataBuffer = raster.getDataBuffer();
+ final var rasterType =
resourceType.forDataBufferType(dataBuffer.getDataType());
+ final int dataSize =
DataBuffer.getDataTypeSize(dataBuffer.getDataType()) / Byte.SIZE;
+ final var sampleModel = (ComponentSampleModel)
raster.getSampleModel(); // See prerequisites in Javadoc.
+ final int pixelStride = Math.multiplyExact(dataSize,
sampleModel.getPixelStride());
+ final int scanlineStride = Math.multiplyExact(dataSize,
sampleModel.getScanlineStride());
+ final int[] bankIndices = sampleModel.getBankIndices();
/*
* The following assertions are critical: if those conditions are not
true, it may crash the JVM.
* For that reason, we test them unconditionally instead of using the
`assert` statement.
@@ -304,14 +307,19 @@ final class Band {
rasterBounds.y -
raster.getSampleModelTranslateY(), i));
final int err;
try {
- err = (int) gdal.rasterIO.invokeExact(selectedBands[i].handle,
readWriteFlags,
- resourceBounds.x, resourceBounds.y,
resourceBounds.width, resourceBounds.height,
- transferBuffer,
- rasterBounds.width,
- rasterBounds.height,
-
resourceType.forDataBufferType(dataBuffer.getDataType()).ordinal(),
- Math.multiplyExact(dataSize,
sampleModel.getPixelStride()),
- Math.multiplyExact(dataSize,
sampleModel.getScanlineStride()));
+ err = (int) gdal.rasterIO.invokeExact(
+ selectedBands[i].handle,
+ readWriteFlags, // Either GF_Read to read a
region of data, or GF_Write to write a region of data.
+ resourceBounds.x, // First column of the region
to be accessed. Zero to start from the left side.
+ resourceBounds.y, // First row of the region to
be accessed. Zero to start from the top.
+ resourceBounds.width, // The width of the region of
the band to be accessed in pixels.
+ resourceBounds.height, // The height of the region of
the band to be accessed in lines.
+ transferBuffer, // The buffer into which the
data is read, or from which it is written.
+ rasterBounds.width, // The width of the region of
the Java2D raster to be accessed.
+ rasterBounds.height, // The height of the region of
the Java2D raster to be accessed.
+ rasterType.ordinal(), // The type of the pixel
values in the destinaton image.
+ pixelStride, // The byte offset from the
start of one pixel to the start of the next pixel.
+ scanlineStride); // The byte offset from the
start of one scanline to the start of the next.
} catch (Throwable e) {
throw GDAL.propagate(e);
}
@@ -322,4 +330,39 @@ final class Band {
}
return true;
}
+
+ /**
+ * Advise driver of upcoming read requests. Contrarily to the above {@code
read(…)} method which receives
+ * a rectangle for one tile at a time, the rectangle received by this
method is for all tiles to be read.
+ *
+ * @param gdal set of handles for invoking <abbr>GDAL</abbr>
functions.
+ * @param resourceBounds region to read in resource coordinates. (0,0)
is the upper-left pixel.
+ * @param imageBounds region to write in image coordinates (may
cover more than one tile).
+ * @param imageType the <abbr>GDAL</abbr> data type of the
destination raster.
+ * @return whether the operation was successful according
<abbr>GDAL</abbr>.
+ * @throws DataStoreException if <var>GDAL</var> reported a warning or
fatal error.
+ */
+ final boolean adviseRead(final GDAL gdal,
+ final Rectangle resourceBounds,
+ final Rectangle imageBounds, // Not the
same as `rasterBounds`.
+ final DataType imageType)
+ throws DataStoreException
+ {
+ final int err;
+ try {
+ err = (int) gdal.adviseRead.invokeExact(
+ handle,
+ resourceBounds.x, // First column of the region to
be accessed. Zero to start from the left side.
+ resourceBounds.y, // First row of the region to be
accessed. Zero to start from the top.
+ resourceBounds.width, // The width of the region of the
band to be accessed in pixels.
+ resourceBounds.height, // The height of the region of the
band to be accessed in lines.
+ imageBounds.width, // The width of the destination
image.
+ imageBounds.height, // The height of the destination
image.
+ imageType.ordinal(), // The type of the pixel values in
the destinaton image.
+ MemorySegment.NULL); // A list of name=value strings
with special control options.
+ } catch (Throwable e) {
+ throw GDAL.propagate(e);
+ }
+ return ErrorHandler.checkCPLErr(err);
+ }
}
diff --git
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
index 1857780d47..246aeb96b9 100644
---
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
+++
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
@@ -21,20 +21,20 @@ import java.util.List;
import java.util.ArrayList;
import java.util.Optional;
import java.util.NoSuchElementException;
-import java.lang.foreign.Arena;
import java.util.logging.Level;
import java.util.logging.LogRecord;
+import java.lang.foreign.Arena;
import java.lang.foreign.Linker;
import java.lang.foreign.ValueLayout;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.FunctionDescriptor;
import java.lang.invoke.MethodHandle;
+import org.apache.sis.util.logging.Logging;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.panama.LibraryLoader;
import org.apache.sis.storage.panama.LibraryStatus;
import org.apache.sis.storage.panama.NativeFunctions;
-import org.apache.sis.util.logging.Logging;
/**
@@ -267,6 +267,12 @@ final class GDAL extends NativeFunctions {
*/
final MethodHandle rasterIO;
+ /**
+ * <abbr>GDAL</abbr> {@code CPLErr GDALRasterAdviseRead(GDALRasterBandH
hRBand, ...)}.
+ * Advise driver of upcoming read requests.
+ */
+ final MethodHandle adviseRead;
+
/**
* Creates the handles for all <abbr>GDAL</abbr> functions which will be
needed.
*
@@ -376,6 +382,18 @@ final class GDAL extends NativeFunctions {
ValueLayout.JAVA_INT, // int nPixelSpace
ValueLayout.JAVA_INT)); // int nLineSpace
+ adviseRead = lookup(linker, "GDALRasterAdviseRead",
FunctionDescriptor.of(
+ ValueLayout.JAVA_INT, // CPLErr error code (return value)
+ ValueLayout.ADDRESS, // GDALRasterBandH hRBand
+ ValueLayout.JAVA_INT, // int nDSXOff
+ ValueLayout.JAVA_INT, // int nDSYOff
+ ValueLayout.JAVA_INT, // int nDSXSize
+ ValueLayout.JAVA_INT, // int nDSYSize
+ ValueLayout.JAVA_INT, // int nBXSize
+ ValueLayout.JAVA_INT, // int nBYSize
+ ValueLayout.JAVA_INT, // GDALDataType eBDataType
+ ValueLayout.ADDRESS)); // CSLConstList papszOptions
+
// Set error handling first in order to redirect initialization
warnings.
setErrorHandler(linker, null);
diff --git
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
index 733e42d4aa..e7b58649e9 100644
---
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
+++
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
@@ -23,6 +23,7 @@ import java.awt.image.WritableRaster;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import org.opengis.util.GenericName;
+import org.apache.sis.util.logging.Logging;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.base.TiledGridCoverage;
import org.apache.sis.storage.base.TiledGridResource;
@@ -78,20 +79,37 @@ final class TiledCoverage extends TiledGridCoverage {
*/
@Override
protected Raster[] readTiles(final TileIterator iterator) throws
IOException, DataStoreException {
+ Rectangle resourceBounds =
bidimensional(iterator.getFullRegionInResourceCoordinates());
+ Rectangle imageBounds;
+ try {
+ imageBounds = iterator.resourceToImage(resourceBounds);
+ } catch (ArithmeticException e) {
+ // Ignore, this is used only as a hint.
+ Logging.ignorableException(GDALStoreProvider.LOGGER,
GDALStore.class, "read", e);
+ imageBounds = null;
+ }
synchronized (owner.getSynchronizationLock()) {
- final Band[] bands = owner.bands(includedBands);
- final GDAL gdal = owner.parent.getProvider().GDAL();
- final var result = new
WritableRaster[iterator.tileCountInQuery];
+ final Band[] bands = owner.bands(includedBands);
+ final GDAL gdal = owner.parent.getProvider().GDAL();
+ final var result = new
WritableRaster[iterator.tileCountInQuery];
+ final DataType rasterType =
owner.dataType.forDataBufferType(model.getDataType());
+ if (imageBounds != null) {
+ // Give a chance to the GDAL driver to prepare itself for the
reading of all tiles in the AOI.
+ for (final Band band : bands) {
+ if (!band.adviseRead(gdal, resourceBounds, imageBounds,
rasterType)) break;
+ }
+ }
try (Arena arena = Arena.ofConfined()) {
final MemorySegment transferBuffer =
arena.allocate(getTileLength());
do {
final WritableRaster tile = iterator.createRaster();
- final Rectangle target =
iterator.getRegionInsideTile(true);
- if (target != null) {
- target.x = Math.addExact(target.x, tile.getMinX());
- target.y = Math.addExact(target.y, tile.getMinY());
- final Rectangle source =
pixelToResourceCoordinates(target);
- if (!Band.transfer(gdal, OpenFlag.READ, bands,
owner.dataType, source, tile, target, transferBuffer)) {
+ final Rectangle rasterBounds =
iterator.getRegionInsideTile(true);
+ if (rasterBounds != null) {
+ rasterBounds.x += tile.getMinX();
+ rasterBounds.y += tile.getMinY();
+ resourceBounds =
iterator.imageToResource(rasterBounds);
+ assert (imageBounds == null) ||
imageBounds.contains(rasterBounds) : rasterBounds + " not in " + imageBounds;
+ if (!Band.transfer(gdal, OpenFlag.READ, bands,
owner.dataType, resourceBounds, tile, rasterBounds, transferBuffer)) {
break; // Exception will be thrown by
`throwOnFailure(…)`
}
}