This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sis.git

commit f81f4d8c8ceb834d97c41f4722e324867fc36b96
Merge: f7bef1427d 90feafe896
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Jun 23 17:40:41 2026 +0200

    Merge branch 'geoapi-3.1'.
    Contains bug fixes in default implementation of `TileMatrix` interface.

 .../apache/sis/cloud/aws/s3/ClientFileSystem.java  |  13 -
 .../sis/cloud/aws/s3/ClientFileSystemTest.java     |  17 +-
 .../apache/sis/cloud/aws/s3/FileServiceTest.java   |  10 +-
 .../main/org/apache/sis/coverage/Category.java     |  87 +++--
 .../main/org/apache/sis/coverage/CategoryList.java |   2 +-
 .../org/apache/sis/coverage/SampleDimension.java   |  55 ++-
 .../main/org/apache/sis/coverage/package-info.java |   3 +-
 .../main/org/apache/sis/image/ComputedImage.java   |  15 -
 .../main/org/apache/sis/image/PlanarImage.java     |  21 ++
 .../sis/image/internal/shared/ReshapedImage.java   |  14 +-
 .../sis/image/internal/shared/TiledImage.java      |  23 +-
 .../org/apache/sis/coverage/CategoryListTest.java  | 117 +++++++
 .../test/org/apache/sis/coverage/CategoryTest.java | 107 ++++++
 .../apache/sis/coverage/SampleDimensionTest.java   | 132 ++++++++
 .../org/apache/sis/image/ImageProcessorTest.java   |  45 +++
 .../image/internal/shared/ReshapedImageTest.java   |  25 +-
 .../storage/geotiff/writer/ReformattedImage.java   |   4 +-
 .../sis/storage/geotiff/GeoTiffStoreTest.java      | 128 ++++++-
 .../org/apache/sis/storage/geotiff/ReaderTest.java |  49 ++-
 .../storage/geotiff/tiled_without_compression.tiff | Bin 0 -> 2324 bytes
 .../geotiff/untiled_without_compression.tiff       | Bin 0 -> 1240 bytes
 .../apache/sis/storage/tiling/ImageTileMatrix.java | 341 ++++++++++++++-----
 .../apache/sis/storage/tiling/IterationDomain.java |  38 +--
 .../org/apache/sis/storage/tiling/TileMatrix.java  |   2 +
 .../sis/storage/tiling/ImageTileMatrixTest.java    | 371 +++++++++++++++++++++
 .../main/org/apache/sis/math/NumberType.java       |  47 ++-
 .../org/apache/sis/measure/MeasurementRange.java   |  26 +-
 .../main/org/apache/sis/measure/Range.java         | 123 ++++++-
 .../main/org/apache/sis/util/ComparisonMode.java   |  21 +-
 .../main/org/apache/sis/util/Utilities.java        |  15 +-
 .../apache/sis/util/internal/shared/Numerics.java  |  11 +-
 .../apache/sis/measure/MeasurementRangeTest.java   |   1 +
 .../test/org/apache/sis/measure/RangeTest.java     |  24 ++
 gradle/wrapper/gradle-wrapper.properties           |   2 +-
 34 files changed, 1630 insertions(+), 259 deletions(-)

diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/SampleDimension.java
index 8b093dc9a9,88a1575010..5c3cc1533f
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/SampleDimension.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/SampleDimension.java
@@@ -76,10 -82,13 +79,10 @@@ import org.apache.sis.util.iso.Names
   *
   * @author  Martin Desruisseaux (IRD, Geomatys)
   * @author  Alexis Manin (Geomatys)
-  * @version 1.5
+  * @version 1.7
 - *
 - * @see org.opengis.metadata.content.SampleDimension
 - *
   * @since 1.0
   */
- public class SampleDimension implements Serializable {
 -public class SampleDimension implements IdentifiedType, LenientComparable, 
Serializable {
++public class SampleDimension implements LenientComparable, Serializable {
      /**
       * Serial number for inter-operability with different versions.
       */
diff --cc 
endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImageTileMatrix.java
index d73349db09,3587faa0a7..3ce1b3edc0
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImageTileMatrix.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImageTileMatrix.java
@@@ -43,7 -52,7 +52,6 @@@ import org.apache.sis.coverage.grid.Inc
  import org.apache.sis.image.ComputedImage;
  import org.apache.sis.image.internal.shared.ReshapedImage;
  import org.apache.sis.pending.jdk.JDK18;
--import org.apache.sis.util.ArgumentChecks;
  import org.apache.sis.util.iso.Names;
  import org.apache.sis.util.logging.Logging;
  import org.apache.sis.util.resources.Errors;
@@@ -353,17 -397,101 +396,96 @@@ final class ImageTileMatrix implements 
       * @param  parallel  {@code true} for a parallel stream (if supported), 
or {@code false} for a sequential stream.
       * @return stream of tiles, excluding missing tiles.
       * @throws DataStoreException if the tiles cannot be fetched in the given 
ranges of tile indexes.
+      * @throws SubspaceNotSpecifiedException if tile size in dimensions other 
than X and Y is greater than 1.
+      * @throws ArithmeticException if some coordinates are too large.
+      *         This exception may also happen latter, during the execution of 
the returned stream.
       */
      @Override
-     public Stream<Tile> getTiles(GridExtent indiceRanges, final boolean 
parallel) throws DataStoreException {
-         if (indiceRanges == null) {
-             indiceRanges = tilingScheme.getExtent();
+     public Stream<Tile> getTiles(GridExtent tileRanges, final boolean 
parallel) throws DataStoreException {
 -        /*
 -         * Argument check: "indiceRanges" is the name used in public API, but 
we use `tileRanges` in this
 -         * implementation for avoiding confusion with `cellRanges`. No need 
to expose this change to user.
 -         */
 -        ArgumentChecks.ensureDimensionMatches("indiceRanges", 
tilingScheme.getDimension(), tileRanges);
+         if (tileRanges == null) {
+             tileRanges = tilingScheme.getExtent();
          }
-         try {
-             return StreamSupport.stream(iterator(indiceRanges).iterator(), 
parallel);
-         } catch (ArithmeticException e) {
-             throw new UnsupportedQueryException(e);
+         /*
+          * Gets the bounds of the images to read. If deferred reading is 
supported,
+          * we can expand to the bounds of the whole coverage in order to 
perform a
+          * read operation (deferred) only once. The bounds are intersected 
with the
+          * coverage extent.
+          */
+         @SuppressWarnings("LocalVariableHidesMemberVariable")
+         final var coverage = coverage();
+         final int xDimension = coverage.xDimension;
+         final int yDimension = coverage.yDimension;
+         final GridExtent cellRanges = coverage.getGridGeometry().getExtent();
+         final int dimension = cellRanges.getDimension();
+         final var cellLow  = new long[dimension];
+         final var tileLow  = new long[dimension];
+         final var cellHigh = new long[dimension];
+         final var tileHigh = new long[dimension];
+         boolean is2D = true;
+         for (int i=0; i<dimension; i++) {
+             // Intersection between request and available data, in cell 
coordinates.
+             cellLow [i] = max(cellRanges.getLow (i), 
tileToCell(tileRanges.getLow(i), i));
+             cellHigh[i] = min(cellRanges.getHigh(i), 
decrementExact(tileToCell(incrementExact(tileRanges.getHigh(i)), i)));
+             final long span = cellHigh[i] - cellLow[i] + 1;
+             if (span < 0 || span > Integer.MAX_VALUE) {
+                 throw new 
ArithmeticException(resource.errors().getString(Errors.Keys.IntegerOverflow_1, 
Integer.SIZE));
+             }
+             // Convert back to tile indices after intersection.
+             tileLow [i] = cellToTile(cellLow [i], i);
+             tileHigh[i] = cellToTile(cellHigh[i], i);
+             /*
+              * Special cases for (X,Y) dimensions: if deferred loading is 
enabled, expand the request
+              * to the maximum size that we can handle with 32-bits integers. 
This is okay because the
+              * actual loading will not happen at `read(…)` invocation time. 
Note that we do not expand
+              * `tileLow` and `tileHigh` because they should stay bounded to 
user's request.
+              */
+             if (i != xDimension && i != yDimension) {
+                 is2D &= (cellLow[i] == cellHigh[i]);
+             } else if (coverage.deferredTileReading) {
+                 final long remain = min(cellRanges.getSize(i), 
Integer.MAX_VALUE) - span;
+                 final long after  = min(remain / 2, cellRanges.getHigh(i) - 
cellHigh[i]);
+                 final long before = min(remain - after, cellLow[i] - 
cellRanges.getLow(i));
+                 cellLow [i] -= before;
+                 cellHigh[i] += after;
+             }
+         }
+         if (is2D) {
+             // This code should only be reached if the requested extent is 2D.
+             assert tileRanges.getDegreesOfFreedom() <= 
TiledGridCoverageResource.BIDIMENSIONAL;
+             return StreamSupport.stream(builder(tileLow, tileHigh, cellLow, 
cellHigh).iterator(), parallel);
          }
+         /*
+          * Splits the given extent in a lazy sequence of 2D extents.
+          * For each 2D slice, we query all tiles contained in the user 
requested tile ranges.
+          *
+          * Note: the iteration over slices is executed in sequential order 
even if `parallel` is true.
+          * Allowing parallel iteration over slices could hurt performance, 
because it would cause the
+          * loading of rendered images from different slices in parallel. As 
this class `image` caching
+          * strategy is based on 2D extents, we instead push parallelism down 
on tile loading level directly
+          * (see `flatMap` block).
+          */
+         final long[] tileHighInExtraDimensions = tileHigh.clone();
+         tileHighInExtraDimensions[xDimension] = tileLow[xDimension];
+         tileHighInExtraDimensions[yDimension] = tileLow[yDimension];
+         return tileRanges.reshape(tileLow, tileHighInExtraDimensions, 
true).latticePointStream(false)
+                 .flatMap((final long[] sliceLow) -> {
+                     final long[] sliceHigh = sliceLow.clone();
+                     sliceHigh[xDimension] = tileHigh[xDimension];
+                     sliceHigh[yDimension] = tileHigh[yDimension];
+                     for (int i=0; i<sliceLow.length; i++) {
+                         if (i != xDimension && i != yDimension) {
+                             cellLow [i] = tileToCell(sliceLow[i], i);
+                             cellHigh[i] = addExact(cellLow[i], tileSize[i] - 
1);    // Inclusive.
+                         }
+                     }
+                     final IteratorBuilder builder;
+                     try {
+                         builder = builder(sliceLow, sliceHigh, cellLow, 
cellHigh);
+                     } catch (DataStoreException e) {
+                         throw new BackingStoreException("Cannot load tiles 
for 2D extent", e);
+                     }
+                     return StreamSupport.stream(builder.iterator(), parallel);
+                 });
      }
  
      /**

Reply via email to