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 58bfc8ba485ec33b6a32407b266c77eb3320d29e
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Mon Sep 30 17:04:00 2024 +0200

    Better control about whether to clip the region to read to the valid area.
    This is required because the GeoTIFF and GDAL data stores have opposite 
needs.
---
 .../org/apache/sis/storage/geotiff/DataCube.java   | 10 +++++
 .../apache/sis/storage/base/TiledGridCoverage.java | 12 +++---
 .../apache/sis/storage/base/TiledGridResource.java | 47 +++++++++++++++++++++-
 .../org/apache/sis/storage/gdal/TiledResource.java | 11 ++++-
 4 files changed, 72 insertions(+), 8 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
index e6f123f9f7..1daafb6383 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
@@ -178,6 +178,16 @@ abstract class DataCube extends TiledGridResource 
implements StoreResource {
      */
     abstract Number getReplaceableFillValue();
 
+    /**
+     * Allows the reading of truncated tiles in the <var>y</var> dimension.
+     * Since there is no data after that dimension, it is safe to stop the
+     * reading process as soon as possible along that axis of each tile.
+     */
+    @Override
+    protected final boolean canReadTruncatedTiles(int dim, boolean suggested) {
+        return suggested | (dim >= 1);      // Y_DIMENSION.
+    }
+
     /**
      * Returns {@code true} if the image can be read with the {@link 
DataSubset} base class,
      * or {@code false} if the more sophisticated {@link CompressedSubset} 
sub-class is needed.
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 499fa4056e..d654e224a6 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
@@ -47,6 +47,7 @@ import org.apache.sis.storage.tiling.TileMatrixSet;
 import org.apache.sis.storage.internal.Resources;
 import org.apache.sis.util.collection.WeakValueHashMap;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.privy.Numerics;
 import static org.apache.sis.pending.jdk.JDK18.ceilDiv;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
@@ -112,10 +113,11 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
      *       (note: this is rare. GeoTIFF for example always stores whole 
tiles).</li>
      * </ul>
      *
-     * <p>In current version this is a flag for the <var>x</var> dimension 
only. In a future version
-     * it could be flags for other dimensions as well (using bitmask) if it 
appears to be useful.</p>
+     * This a list of Boolean flags packed as a bitmask with the flag for the 
first dimension in the lowest bit.
+     * The default implementation always sets the flag of the last dimension 
to {@code false} (0), then sets the
+     * flags of other dimensions to {@code true} (1) if we are not in the case 
of a big untiled image.
      */
-    private final boolean forceTileSize;
+    private final long forceWholeTiles;
 
     /**
      * Size of all tiles in the domain of this {@code TiledGridCoverage}, 
without clipping and subsampling.
@@ -276,7 +278,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
         this.model      = model;
         this.colors     = subset.colorsForBandSubset;
         this.fillValues = subset.fillValues;
-        forceTileSize   = multiplyExact(subsampling[X_DIMENSION], 
subSize[X_DIMENSION]) == virtualTileSize[X_DIMENSION];
+        forceWholeTiles = subset.forceWholeTiles(subSize);
     }
 
     /**
@@ -795,7 +797,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
                 if (offset >= limit) {          // Test for intersection 
before we adjust the limit.
                     return false;
                 }
-                if (dimension == X_DIMENSION && coverage.forceTileSize) {
+                if ((coverage.forceWholeTiles & Numerics.bitmask(dimension)) 
!= 0) {
                     limit = tileSize;
                 }
                 if (subsampled) {
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
index 7f0b36e4e9..1da0df045a 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
@@ -47,6 +47,7 @@ import org.apache.sis.storage.RasterLoadingStrategy;
 import org.apache.sis.storage.event.StoreListeners;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.privy.Numerics;
 import org.apache.sis.util.collection.WeakValueHashMap;
 import static org.apache.sis.storage.base.TiledGridCoverage.X_DIMENSION;
 import static org.apache.sis.storage.base.TiledGridCoverage.Y_DIMENSION;
@@ -222,6 +223,32 @@ public abstract class TiledGridResource extends 
AbstractGridCoverageResource {
         return (dim == 0) ? 
TiledGridCoverage.getPixelsPerElement(getSampleModel(null)) : 1;
     }
 
+    /**
+     * Returns {@code true} if the reader can load truncated tiles. Truncated 
tiles may happen in the
+     * last row and last column of a tile matrix when the image size is not a 
multiple of the tile size.
+     * Some file formats, such as GeoTIFF, unconditionally stores full tiles, 
in which case this method
+     * should return {@code false}. At the opposite, some implementations, 
such as <abbr>GDAL</abbr>,
+     * accept only requests over the valid area, in which case this method 
should return {@code true}.
+     *
+     * <h4>Suggested value</h4>
+     * The {@code suggested} argument is a value computed by the caller based 
on common usages.
+     * The default implementation of {@link TiledGridCoverage} suggests {@code 
true}
+     * if the read operation is inside a single big tile, or {@code false} 
otherwise.
+     * The default implementation of this method returns {@code suggested} 
unchanged.
+     *
+     * <p>Note that even for subclasses that generally do not support the 
reading of truncated tiles,
+     * it is often safe to return {@code true} for the last dimension (for 
example, <var>y</var> in a
+     * two-dimensional image), because there is no data after that dimension.
+     * Therefore, it is often safe to stop the reading process in that 
dimension.
+     *
+     * @param  dim        the dimension: 0 for <var>x</var>, 1 for 
<var>y</var>, <i>etc.</i>
+     * @param  suggested  suggested response to return (see above heuristic 
rules).
+     * @return whether the reader can load truncated tiles along the specified 
dimension.
+     */
+    protected boolean canReadTruncatedTiles(int dim, boolean suggested) {
+        return suggested;
+    }
+
     /**
      * Returns {@code true} if the reader can load only the requested bands 
and skip the other bands,
      * or {@code false} if the reader must load all bands. This value controls 
the amount of data to
@@ -373,8 +400,8 @@ check:  if (dataType.isInteger()) {
 
     /**
      * Parameters that describe the resource subset to be accepted by the 
{@link TiledGridCoverage} constructor.
-     * Instances of this class are temporary and used only for transferring 
information from {@link TiledGridResource}.
-     * This class does not perform I/O operations.
+     * Instances of this class are temporary and used only for transferring 
information from {@link TiledGridResource}
+     * to {@link TiledGridCoverage}. This class does not perform I/O 
operations.
      */
     public final class Subset {
         /**
@@ -582,6 +609,22 @@ check:  if (dataType.isInteger()) {
             cache = sharedCache ? rasters : new 
WeakValueHashMap<>(CacheKey.class);
         }
 
+        /**
+         * Returns flags telling, for each dimension, whether the read region 
should be an integer number of tiles.
+         *
+         * @param  subSize  tile size after subsampling.
+         * @return a bitmask with the flag for the first dimension in the 
lowest bit.
+         */
+        final long forceWholeTiles(final int[] subSize) {
+            long forceWholeTiles = 0;
+            for (int i=0; i<subSize.length; i++) {
+                if (!canReadTruncatedTiles(i, 
Math.multiplyExact(subsampling[i], subSize[i]) != virtualTileSize[i])) {
+                    forceWholeTiles |= Numerics.bitmask(i);
+                }
+            }
+            return forceWholeTiles;
+        }
+
         /**
          * Returns {@code true} if reading data in this subset will read 
contiguous values on the <var>x</var> axis.
          * This method returns {@code true} if all following conditions are 
met:
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 1832044f7f..c72bc57fa3 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
@@ -345,7 +345,7 @@ final class TiledResource extends TiledGridResource {
                  * The axis order used by GDAL is not the axis order in the 
CRS definition.
                  * GDAL provides a separated method for specifying the axis 
swapping.
                  */
-                if (gridToCRS != null) {
+                if (gridToCRS != null && srs != null) {
                     int dimension = (crs != null) ? 
crs.getCoordinateSystem().getDimension() : SpatialRef.BIDIMENSIONAL;
                     final Matrix swap = srs.getDataToCRS(dimension);
                     if (swap != null) {
@@ -559,6 +559,15 @@ final class TiledResource extends TiledGridResource {
         }
     }
 
+    /**
+     * Allows the reading of truncated tiles in all dimensions. In 
<abbr>GDAL</abbr> case, truncating is actually
+     * mandatory because <abbr>GDAL</abbr> requires the read requests to be 
fully contained inside the valid area.
+     */
+    @Override
+    protected boolean canReadTruncatedTiles(int dim, boolean suggested) {
+        return true;
+    }
+
     /**
      * Returns the size of tiles (in pixels) in this resource.
      * The length of the returned array is the number of dimensions.

Reply via email to