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 4e676ada33 Bug fix: need to take subsampling in account when computing the destination region. 4e676ada33 is described below commit 4e676ada331f7d6e006cba18f719325dbe3f0e9d Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sun Sep 15 19:19:38 2024 +0200 Bug fix: need to take subsampling in account when computing the destination region. --- .../org/apache/sis/storage/geotiff/DataSubset.java | 2 +- .../apache/sis/storage/base/TiledGridCoverage.java | 41 ++++++++++++++++------ .../org/apache/sis/storage/gdal/TiledCoverage.java | 14 ++++---- .../org/apache/sis/storage/gdal/TiledResource.java | 3 +- 4 files changed, 42 insertions(+), 18 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 b1e5ca3be0..668cb11692 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 @@ -378,7 +378,7 @@ class DataSubset extends TiledGridCoverage implements Localized { try (Closeable finisher = createInflater()) { for (int i=0; i<numMissings; i++) { final Tile tile = missings[i]; - if (tile.getRegionInsideTile(lower, upper, subsampling, BIDIMENSIONAL)) { + if (tile.getRegionInsideTile(lower, upper, subsampling, false)) { origin.x = tile.originX; origin.y = tile.originY; tile.copyTileInfo(tileOffsets, offsets, includedBanks, numTiles); 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 c54f888654..eccfd9f5c5 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 @@ -663,19 +663,27 @@ public abstract class TiledGridCoverage extends GridCoverage { } /** - * Returns the coordinates of the pixels to read <em>inside</em> the tile, ignoring subsampling. + * Returns the coordinates of the pixels to read <em>inside</em> the tile. * The tile upper-left corner is assumed (0,0). Therefore, the lower coordinates computed by this * method are usually (0,0) and the rectangle size is usually the tile size, but those values may * be different if the enclosing {@link TiledGridCoverage} contains only one (potentially big) tile. * The rectangle may also be smaller when reading tiles on the last row or column of the tile matrix. * + * <p>If the {@code subsampled} argument is {@code false}, then this method returns coordinates + * relative to the tile in the originating {@link TiledGridResource}, i.e. without subsampling. + * If {@code subsampled} is {@code true}, then this method returns coordinates relative to the + * {@linkplain #createRaster() raster}, i.e. with {@linkplain #getSubsampling(int) subsampling}. + * Note that the {@linkplain Raster#getMinX() raster origin} still needs to be added to the + * {@linkplain Rectangle#getLocation() rectangle location} for obtaining coordinates in the raster space.</p> + * + * @param subsampled whether to return coordinates with subsampling applied. * @return pixel to read inside the tile, or {@code null} if the region is empty. * @throws ArithmeticException if the tile coordinates overflow 32 bits integer capacity. */ - public Rectangle getRegionInsideTile() { + public Rectangle getRegionInsideTile(final boolean subsampled) { final long[] lower = new long[BIDIMENSIONAL]; final long[] upper = new long[BIDIMENSIONAL]; - if (getRegionInsideTile(lower, upper, null, BIDIMENSIONAL)) { + if (getRegionInsideTile(lower, upper, null, subsampled)) { return new Rectangle( toIntExact(lower[X_DIMENSION]), toIntExact(lower[Y_DIMENSION]), @@ -686,7 +694,7 @@ public abstract class TiledGridCoverage extends GridCoverage { } /** - * Returns the coordinates of the pixels to read <em>inside</em> the tile, ignoring subsampling. + * Returns the coordinates of the pixels to read <em>inside</em> the tile. * The tile upper-left corner is assumed (0,0). Therefore, the lower coordinates computed by this * method are usually (0,0) and the upper coordinates are usually the tile size, but those values * may be different if the enclosing {@link TiledGridCoverage} contains only one (potentially big) tile. @@ -697,15 +705,23 @@ public abstract class TiledGridCoverage extends GridCoverage { * but is constant for all tiles regardless the subregion to read. * The same values can be obtained by {@link #getSubsampling(int)}.</p> * - * <p>This method is a generalization of {@link #getRegionInsideTile()} to any number of dimensions.</p> + * <p>If the {@code subsampled} argument is {@code false}, then this method returns coordinates + * relative to the tile in the originating {@link TiledGridResource}, i.e. without subsampling. + * If {@code subsampled} is {@code true}, then this method returns coordinates relative to the + * tile resulting from the read operation, i.e. with {@linkplain #getSubsampling(int) subsampling}.</p> + * + * <p>This method is a generalization of {@link #getRegionInsideTile(boolean)} to any number of dimensions.</p> * * @param lower a pre-allocated array where to store relative coordinates of the first pixel. * @param upper a pre-allocated array where to store relative coordinates after the last pixel. * @param subsampling a pre-allocated array where to store subsampling, or {@code null} if not needed. - * @param dimension number of elements to write in the {@code lower} and {@code upper} arrays. + * @param subsampled whether to return coordinates with subsampling applied. * @return {@code true} on success, or {@code false} if the tile is empty. */ - public boolean getRegionInsideTile(final long[] lower, final long[] upper, final int[] subsampling, int dimension) { + public boolean getRegionInsideTile(final long[] lower, final long[] upper, + final int[] subsampling, final boolean subsampled) + { + int dimension = Math.min(lower.length, upper.length); final TiledGridCoverage coverage = getCoverage(); if (subsampling != null) { System.arraycopy(coverage.subsampling, 0, subsampling, 0, dimension); @@ -749,6 +765,11 @@ public abstract class TiledGridCoverage extends GridCoverage { if (dimension == X_DIMENSION && coverage.forceTileSize) { limit = tileSize; } + if (subsampled) { + final int s = coverage.getSubsampling(dimension); + offset /= s; // Round toward 0 because we should not have negative coordinates. + limit = (limit - 1) / s + 1; + } lower[dimension] = offset; upper[dimension] = limit; } @@ -789,8 +810,8 @@ public abstract class TiledGridCoverage extends GridCoverage { private final int[] offsetAOI; /** - * Pixel coordinates of current iterator position relative to the Area Of Interest specified by user. - * Those coordinates are in units of the full resolution image. + * 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. * Initial position is {@link #offsetAOI} multiplied by {@link #subsampling}. * This array is modified by calls to {@link #next()}. */ @@ -925,7 +946,7 @@ public abstract class TiledGridCoverage extends GridCoverage { * to the next tile. The tile seems "too far", but it will either be discarded at a later step * (because of empty intersection with AOI) or compensated by the offset caused by subsampling. * At first the index values seem inconsistent, but after we discard the tiles where - * `getRegionInsideTile(…)` returns `false` they become consistent. + * `getRegionInsideTile(…)` returns `false`, they become consistent. */ return toIntExact(ceilDiv(tileOffsetFull[dimension], getSubsampling(dimension))); } 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 e4eccc4ec2..733e42d4aa 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 @@ -86,12 +86,14 @@ final class TiledCoverage extends TiledGridCoverage { final MemorySegment transferBuffer = arena.allocate(getTileLength()); do { final WritableRaster tile = iterator.createRaster(); - final Rectangle target = iterator.getRegionInsideTile(); - 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)) { - break; // Exception will be thrown by `throwOnFailure(…)` + 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)) { + break; // Exception will be thrown by `throwOnFailure(…)` + } } result[iterator.getTileIndexInResultArray()] = tile; } while (iterator.next()); diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java index 0c9d7fa60f..dc9316db6f 100644 --- a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java +++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java @@ -231,6 +231,7 @@ final class TiledResource extends TiledGridResource { var name = Vocabulary.formatInternational(Vocabulary.Keys.Image_1, count + 1); rasters[count++] = new TiledResource(parent, entry.getKey(), entry.getValue(), name); } + // Search for the main image and, if found, move it first. for (int i=0; i<count; i++) { final TiledResource main = rasters[i]; if (main.width == mainWidth && main.height == mainHeight) { @@ -432,7 +433,7 @@ final class TiledResource extends TiledGridResource { final double max = band.getValue(gdal.getRasterMaximum, MemorySegment.NULL); colorModel = ColorModelFactory.createGrayScale(dataType.imageType, selectedBands.length, gray, min, max); } - sampleModel = new BandedSampleModel(dataType.imageType, width, height, selectedBands.length); + sampleModel = new BandedSampleModel(dataType.imageType, tileWidth, tileHeight, selectedBands.length); selectedBandIndices = bandIndices; }