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 6ae933802f1a25722381207e617c2bd8551e2c89
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Tue Sep 17 18:34:53 2024 +0200

    Handle subsampling as long integers instead of 32 bits integers.
    This is needed because small overviews of large images require a
    subsampling which is about as large as the image size.
    
    https://issues.apache.org/jira/browse/SIS-605
---
 .../apache/sis/coverage/grid/GridDerivation.java   | 177 +++++++++++++--------
 .../org/apache/sis/coverage/grid/GridExtent.java   |  61 +++++--
 .../org/apache/sis/coverage/grid/GridGeometry.java |  27 +++-
 .../apache/sis/coverage/privy/RangeArgument.java   |  10 +-
 .../sis/coverage/grid/GridDerivationTest.java      |  16 +-
 .../apache/sis/coverage/grid/GridExtentTest.java   |   4 +-
 .../apache/sis/coverage/grid/GridGeometryTest.java |   2 +-
 .../sis/coverage/privy/RangeArgumentTest.java      |   2 +-
 .../sis/storage/geotiff/CompressedSubset.java      |  10 +-
 .../org/apache/sis/storage/geotiff/DataSubset.java |   4 +-
 .../sis/storage/netcdf/base/RasterResource.java    |   2 +-
 .../apache/sis/storage/netcdf/base/Variable.java   |   4 +-
 .../sis/storage/netcdf/classic/VariableInfo.java   |  10 +-
 .../sis/storage/netcdf/ucar/VariableWrapper.java   |  13 +-
 .../apache/sis/io/stream/HyperRectangleWriter.java |   2 +-
 .../main/org/apache/sis/io/stream/Region.java      |   4 +-
 .../apache/sis/storage/base/TiledGridCoverage.java |  45 +++---
 .../apache/sis/storage/base/TiledGridResource.java |  61 ++++---
 .../apache/sis/storage/esri/RawRasterReader.java   |   4 +-
 .../sis/storage/image/WorldFileResource.java       |  18 ++-
 .../sis/io/stream/HyperRectangleReaderTest.java    |  10 +-
 .../sis/io/stream/HyperRectangleWriterTest.java    |   2 +-
 .../io/stream/SubsampledRectangleWriterTest.java   |   2 +-
 .../sis/storage/test/CoverageReadConsistency.java  |  12 +-
 .../org/apache/sis/storage/gdal/TiledResource.java |   6 +-
 25 files changed, 304 insertions(+), 204 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
index c46ec97aa7..7695fe9ad6 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
@@ -65,7 +65,7 @@ import org.opengis.coverage.PointOutsideCoverageException;
  *
  * <ol>
  *   <li>{@link #rounding(GridRoundingMode)}, {@link #margin(int...)} and/or 
{@link #chunkSize(int...)} in any order</li>
- *   <li>{@link #subgrid(GridGeometry)}, {@link #subgrid(Envelope, double...)} 
or {@link #subgrid(GridExtent, int...)}</li>
+ *   <li>{@link #subgrid(GridGeometry)}, {@link #subgrid(Envelope, double...)} 
or {@link #subgrid(GridExtent, long...)}</li>
  *   <li>{@link #slice(DirectPosition)} and/or {@link #sliceByRatio(double, 
int...)}</li>
  * </ol>
  *
@@ -128,9 +128,9 @@ public class GridDerivation {
     /**
      * The maximum subsampling values (inclusive), or {@code null} if none.
      *
-     * @see #maximumSubsampling(int...)
+     * @see #maximumSubsampling(long...)
      */
-    private int[] maximumSubsampling;
+    private long[] maximumSubsampling;
 
     // ──────── FIELDS COMPUTED BY METHODS IN THIS CLASS 
──────────────────────────────────────────────────────────────
 
@@ -283,7 +283,7 @@ public class GridDerivation {
      * <ul>
      *   <li>{@link #subgrid(GridGeometry)}</li>
      *   <li>{@link #subgrid(Envelope, double...)}</li>
-     *   <li>{@link #subgrid(GridExtent, int...)}</li>
+     *   <li>{@link #subgrid(GridExtent, long...)}</li>
      * </ul>
      *
      * For each dimension <var>i</var> of the grid computed by above methods, 
the {@linkplain GridExtent#getLow(int) low}
@@ -313,7 +313,7 @@ public class GridDerivation {
      */
     public GridDerivation margin(final int... cellCounts) {
         ensureSubgridNotSet();
-        margin = validateCellCounts("cellCounts", cellCounts, 0);
+        margin = validateCellCounts(cellCounts, 0);
         return this;
     }
 
@@ -344,10 +344,40 @@ public class GridDerivation {
      */
     public GridDerivation chunkSize(final int... cellCounts) {
         ensureSubgridNotSet();
-        chunkSize = validateCellCounts("cellCounts", cellCounts, 1);
+        chunkSize = validateCellCounts(cellCounts, 1);
         return this;
     }
 
+    /**
+     * Returns a copy of the {@code values} array with trailing {@code 
defaultValue} trimmed.
+     * Returns {@code null} if all values are trimmed. This method verifies 
that values are valid.
+     *
+     * @param  property    argument name to use in error message in case of 
errors.
+     * @param  cellCounts  user supplied values as an {@code int[]} or {@code 
long[]} array.
+     * @return values to save in {@link GridDerivation}.
+     */
+    private static int[] validateCellCounts(final int[] cellCounts, final int 
defaultValue) {
+        int[] copy = null;
+        ArgumentChecks.ensureNonNull("cellCounts", cellCounts);
+        for (int i = cellCounts.length; --i >= 0;) {
+            final int value = cellCounts[i];
+            if (value != defaultValue) {
+                if (defaultValue == 0) {
+                    ArgumentChecks.ensurePositive("cellCounts", value);
+                } else {
+                    ArgumentChecks.ensureStrictlyPositive("cellCounts", value);
+                }
+                if (copy == null) {
+                    copy = new int[i+1];
+                }
+            } else if (copy == null) {
+                continue;
+            }
+            copy[i] = value;
+        }
+        return copy;
+    }
+
     /**
      * Specifies the maximum subsampling values (inclusive) for each dimension.
      * If a subsampling value is greater than a specified value in the 
corresponding dimension,
@@ -365,41 +395,40 @@ public class GridDerivation {
      * @throws IllegalStateException if {@link #subgrid(Envelope, double...)} 
or {@link #slice(DirectPosition)}
      *         has already been invoked.
      *
-     * @since 1.1
+     * @since 1.5
      */
-    public GridDerivation maximumSubsampling(final int... subsampling) {
+    public GridDerivation maximumSubsampling(final long... subsampling) {
         ensureSubgridNotSet();
-        maximumSubsampling = validateCellCounts("subsampling", subsampling, 
Integer.MAX_VALUE);
+        long[] copy = null;
+        ArgumentChecks.ensureNonNull("subsampling", subsampling);
+        for (int i = subsampling.length; --i >= 0;) {
+            final long value = subsampling[i];
+            if (value != Long.MAX_VALUE) {
+                ArgumentChecks.ensureStrictlyPositive("subsampling", value);
+                if (copy == null) copy = new long[i+1];
+            } else if (copy == null) {
+                continue;
+            }
+            copy[i] = value;
+        }
+        maximumSubsampling = copy;
         return this;
     }
 
     /**
-     * Returns a copy of the {@code values} array with trailing {@code 
defaultValue} trimmed.
-     * Returns {@code null} if all values are trimmed. This method verifies 
that values are valid.
+     * Specifies the maximum subsampling values (32-bits version).
+     * See {@link #maximumSubsampling(long...)} for details.
      *
-     * @param  property  argument name to use in error message in case of 
errors.
-     * @param  values    user supplied values.
-     * @return values to save in {@link GridDerivation}.
+     * @param  subsampling  maximal subsampling values (inclusive).
+     * @return {@code this} for method call chaining.
+     * @since 1.1
+     *
+     * @deprecated Use the version with {@code long} integers instead of 
{@code int}.
+     * Small overviews of large images require large subsampling factors.
      */
-    private static int[] validateCellCounts(final String property, final int[] 
values, final int defaultValue) {
-        ArgumentChecks.ensureNonNull(property, values);
-        int[] copy = null;
-        for (int i=values.length; --i >= 0;) {
-            final int n = values[i];
-            if (n != defaultValue) {
-                if (defaultValue == 0) {
-                    ArgumentChecks.ensurePositive(property, n);
-                } else {
-                    ArgumentChecks.ensureStrictlyPositive(property, n);
-                }
-                if (copy == null) {
-                    copy = new int[i+1];
-                    Arrays.fill(copy, defaultValue);
-                }
-                copy[i] = n;
-            }
-        }
-        return copy;
+    @Deprecated(since="1.5")
+    public GridDerivation maximumSubsampling(final int[] subsampling) {
+        return maximumSubsampling(ArraysExt.copyAsLongs(subsampling));
     }
 
     /**
@@ -409,7 +438,7 @@ public class GridDerivation {
      * The new grid geometry resolution will be integer multiples of the 
{@link #base} grid geometry resolution.
      *
      * <p>If {@code gridExtent} contains only an envelope, then this method 
delegates to {@link #subgrid(Envelope, double...)}.
-     * Otherwise if {@code gridExtent} contains only an extent, then this 
method delegates to {@link #subgrid(GridExtent, int...)}.
+     * Otherwise if {@code gridExtent} contains only an extent, then this 
method delegates to {@link #subgrid(GridExtent, long...)}.
      * Otherwise the following information are mandatory:</p>
      * <ul>
      *   <li>{@linkplain GridGeometry#getExtent() Extent} in {@code 
areaOfInterest}.</li>
@@ -440,7 +469,7 @@ public class GridDerivation {
      *         public GridCoverage read(GridGeometry domain, int... range) 
throws DataStoreException {
      *             GridDerivation change = 
getGridGeometry().derive().subgrid(domain);
      *             GridExtent toRead = change.buildExtent();
-     *             int[] subsampling = change.getSubsampling());
+     *             long[] subsampling = change.getSubsampling());
      *             // Do reading here.
      *         }
      *     }
@@ -533,7 +562,7 @@ public class GridDerivation {
         if (scales == null) {
             return this;
         }
-        final int[] subsampling = new int[scales.length];
+        final var subsampling = new long[scales.length];
         for (int i=0; i<subsampling.length; i++) {
             subsampling[i] = roundSubsampling(scales[i], i);
         }
@@ -861,9 +890,9 @@ public class GridDerivation {
      * @see #getIntersection()
      * @see #getSubsampling()
      *
-     * @since 1.1
+     * @since 1.5
      */
-    public GridDerivation subgrid(final GridExtent areaOfInterest, int... 
subsampling) {
+    public GridDerivation subgrid(final GridExtent areaOfInterest, long... 
subsampling) {
         ensureSubgridNotSet();
         final int n = base.getDimension();
         if (areaOfInterest != null) {
@@ -889,6 +918,24 @@ public class GridDerivation {
         return subsample(subsampling);
     }
 
+    /**
+     * Requests a grid geometry over a sub-region (32-bits version).
+     * See {@link #subgrid(GridExtent, long...)} for details.
+     *
+     * @param  areaOfInterest  the desired grid extent in unit of base grid 
cell, or {@code null}.
+     * @param  subsampling     the subsampling to apply on each grid 
dimension, or {@code null} if none.
+     * @return {@code this} for method call chaining.
+     *
+     * @since 1.1
+     *
+     * @deprecated Use the version with {@code long} integers instead of 
{@code int}.
+     * Small overviews of large images require large subsampling factors.
+     */
+    @Deprecated(since="1.5")
+    public GridDerivation subgrid(GridExtent areaOfInterest, int[] 
subsampling) {
+        return subgrid(areaOfInterest, ArraysExt.copyAsLongs(subsampling));
+    }
+
     /**
      * Applies a subsampling on the grid geometry to build.
      * This method can be invoked as an alternative to {@code subgrid(…)} 
methods if only the resolution needs to be changed.
@@ -905,7 +952,7 @@ public class GridDerivation {
      *
      * <h4>Preconditions</h4>
      * This method assumes that subsampling are divisors of {@linkplain 
#chunkSize(int...) chunk sizes}
-     * and are not greater than the {@linkplain #maximumSubsampling(int...) 
maximum subsampling}.
+     * and are not greater than the {@linkplain #maximumSubsampling(long...) 
maximum subsampling}.
      * It is caller responsibility to ensure that those preconditions are met.
      *
      * @param  subsampling  the subsampling to apply on each grid dimension. 
All values shall be greater than zero.
@@ -914,11 +961,11 @@ public class GridDerivation {
      * @throws IllegalStateException if a subsampling has already been set,
      *         for example by a call to {@link #subgrid(Envelope, double...) 
subgrid(…)}.
      *
-     * @see #subgrid(GridExtent, int...)
+     * @see #subgrid(GridExtent, long...)
      * @see #getSubsampling()
-     * @see GridExtent#subsample(int...)
+     * @see GridExtent#subsample(long...)
      */
-    private GridDerivation subsample(final int... subsampling) {
+    private GridDerivation subsample(final long... subsampling) {
         if (toBase != null) {
             throw new 
IllegalStateException(Errors.format(Errors.Keys.ValueAlreadyDefined_1, 
"subsampling"));
         }
@@ -930,7 +977,7 @@ public class GridDerivation {
         Matrix affine = null;
         final int dimension = extent.getDimension();
         for (int i = Math.min(dimension, subsampling.length); --i >= 0;) {
-            final int s = subsampling[i];
+            final long s = subsampling[i];
             if (s != 1) {
                 if (affine == null) {
                     affine = Matrices.createIdentity(dimension + 1);
@@ -1215,7 +1262,7 @@ public class GridDerivation {
      * returns the {t₀, t₁, t₂} values. All subsampling values are strictly 
positive integers.
      *
      * <p>This method can be invoked after {@link #build()} for getting 
additional information.
-     * If {@link #subgrid(GridExtent, int...)} has been invoked, then this 
method returns the
+     * If {@link #subgrid(GridExtent, long...)} has been invoked, then this 
method returns the
      * values that were given in the {@code subsampling} argument.</p>
      *
      * <h4>Application to iterations</h4>
@@ -1229,21 +1276,21 @@ public class GridDerivation {
      *
      * @see #getSubsamplingOffsets()
      * @see #subgrid(GridGeometry)
-     * @see #subgrid(GridExtent, int...)
+     * @see #subgrid(GridExtent, long...)
      *
      * @since 1.1
      */
-    public int[] getSubsampling() {
-        final int[] subsampling;
+    public long[] getSubsampling() {
+        final long[] subsampling;
         if (toBase == null) {
-            subsampling = new int[base.getDimension()];
+            subsampling = new long[base.getDimension()];
             Arrays.fill(subsampling, 1);
         } else {
-            subsampling = new int[toBase.getTargetDimensions()];
+            subsampling = new long[toBase.getTargetDimensions()];
             final Matrix affine = toBase.getMatrix();
             for (int j=0; j < subsampling.length; j++) {
                 final double e = affine.getElement(j,j);
-                if ((subsampling[j] = (int) e) != e) {
+                if ((subsampling[j] = (long) e) != e) {
                     throw new 
IllegalStateException(Errors.format(Errors.Keys.NotAnInteger_1, e));
                 }
             }
@@ -1279,15 +1326,15 @@ public class GridDerivation {
      * @param  scale      the scale factor to round.
      * @param  dimension  the dimension of the scale factor to round.
      */
-    private int roundSubsampling(final double scale, final int dimension) {
-        int subsampling;
+    private long roundSubsampling(final double scale, final int dimension) {
+        long subsampling;
         switch (rounding) {
             default:        throw new AssertionError(rounding);
-            case NEAREST:   subsampling = (int) Math.min(Math.round(scale), 
Integer.MAX_VALUE); break;
-            case CONTAINED: subsampling = (int) Math.ceil(scale - 
tolerance(dimension)); break;
-            case ENCLOSING: subsampling = (int) (scale + 
tolerance(dimension)); break;
+            case NEAREST:   subsampling = Math.round(scale); break;
+            case CONTAINED: subsampling = (long) Math.ceil(scale - 
tolerance(dimension)); break;
+            case ENCLOSING: subsampling = (long)          (scale + 
tolerance(dimension)); break;
         }
-        int max = Integer.MAX_VALUE;
+        long max = Long.MAX_VALUE;
         if (maximumSubsampling != null && dimension < 
maximumSubsampling.length) {
             max = maximumSubsampling[dimension];
             if (subsampling > max) subsampling = max;
@@ -1302,7 +1349,7 @@ public class GridDerivation {
              * we can remove an integer number of tiles because tiles can be 
fully skipped at read time.
              */
             final int size = chunkSize[dimension];
-            final int r = subsampling % size;       // Reduced subsampling 
(with integer amont of tiles removed).
+            final int r = (int) (subsampling % size);   // Reduced subsampling 
(with integer amont of tiles removed).
             if (r > 1 && (size % r) != 0) {
                 final int[] divisors = MathFunctions.divisors(size);
                 final int i = ~Arrays.binarySearch(divisors, r);
@@ -1314,10 +1361,10 @@ public class GridDerivation {
                  * It is better to know now if there is any problem here.
                  */
                 int s = divisors[i-1];
-                final int offset = subsampling - r;
+                final long offset = subsampling - r;
                 if (rounding != GridRoundingMode.ENCLOSING && i < 
divisors.length) {
                     final int above = divisors[i];
-                    if (max == Integer.MAX_VALUE || above <= max - offset) {
+                    if (max == Long.MAX_VALUE || above <= max - offset) {
                         if (rounding == GridRoundingMode.CONTAINED || above - 
r < r - s) {
                             s = above;
                         }
@@ -1352,21 +1399,21 @@ public class GridDerivation {
      *
      * @see #getSubsampling()
      * @see #subgrid(GridGeometry)
-     * @see #subgrid(GridExtent, int...)
+     * @see #subgrid(GridExtent, long...)
      *
      * @since 1.1
      */
-    public int[] getSubsamplingOffsets() {
-        final int[] offsets;
+    public long[] getSubsamplingOffsets() {
+        final long[] offsets;
         if (toBase == null) {
-            offsets = new int[base.getDimension()];
+            offsets = new long[base.getDimension()];
         } else {
             final int srcDim = toBase.getSourceDimensions();
-            offsets = new int[toBase.getTargetDimensions()];
+            offsets = new long[toBase.getTargetDimensions()];
             final Matrix affine = toBase.getMatrix();
             for (int j=0; j < offsets.length; j++) {
                 final double e = affine.getElement(j, srcDim);
-                if ((offsets[j] = (int) e) != e) {
+                if ((offsets[j] = (long) e) != e) {
                     throw new 
IllegalStateException(Errors.format(Errors.Keys.NotAnInteger_1, e));
                 }
             }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
index a63cb2f97c..a29bfa0ed6 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
@@ -1414,7 +1414,9 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
      *
      * @since 1.1
      */
-    public GridExtent insertDimension(final int index, final DimensionNameType 
axisType, final long low, long high, final boolean isHighIncluded) {
+    public GridExtent insertDimension(final int index, final DimensionNameType 
axisType,
+                                      final long low, long high, final boolean 
isHighIncluded)
+    {
         final int dimension = getDimension();
         Objects.checkIndex(index, dimension+1);
         if (!isHighIncluded) {
@@ -1599,7 +1601,7 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
      * @throws ArithmeticException if resizing this extent to the given size 
overflows {@code long} capacity.
      *
      * @see #getSize(int)
-     * @see GridDerivation#subgrid(GridExtent, int...)
+     * @see GridDerivation#subgrid(GridExtent, long...)
      */
     public GridExtent resize(final long... sizes) {
         final int m = getDimension();
@@ -1652,18 +1654,19 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
      * If the array is shorter, missing values default to 1 (i.e. samplings in 
unspecified dimensions are unchanged).
      * If the array is longer, extraneous values are ignored.
      *
-     * @param  periods  the subsampling. Length shall be equal to the number 
of dimension and all values shall be greater than zero.
+     * @param  periods  the subsampling factors for each dimension of this 
grid extent.
      * @return the subsampled extent, or {@code this} if subsampling results 
in the same extent.
      * @throws IllegalArgumentException if a period is not greater than zero.
      *
-     * @see GridDerivation#subgrid(GridExtent, int...)
+     * @see GridDerivation#subgrid(GridExtent, long...)
+     * @since 1.5
      */
-    public GridExtent subsample(final int... periods) {
+    public GridExtent subsample(final long... periods) {
         final int m = getDimension();
         final int length = Math.min(m, periods.length);
         final GridExtent sub = new GridExtent(this);
         for (int i=0; i<length; i++) {
-            final int s = periods[i];
+            final long s = periods[i];
             if (s > 1) {
                 final int j = i + m;
                 long low  = coordinates[i];
@@ -1672,7 +1675,7 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
                     throw new 
ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE));
                 }
                 long r = Long.divideUnsigned(size, s);
-                if (r*s == size) r--;                   // Make inclusive if 
the division did not already rounded toward 0.
+                if (r*s == size) r--;   // Make inclusive if the division did 
not already rounded toward 0.
                 sub.coordinates[i] = low /= s;
                 sub.coordinates[j] = low + r;
             } else if (s <= 0) {
@@ -1683,6 +1686,22 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
         return Arrays.equals(coordinates, sub.coordinates) ? this : sub;
     }
 
+    /**
+     * Creates a new subsampled grid extent (32-bits version).
+     * See {@link #subsample(long...)} for details.
+     *
+     * @param  periods  the subsampling factors for each dimension of this 
grid extent.
+     * @return the subsampled extent, or {@code this} if subsampling results 
in the same extent.
+     * @throws IllegalArgumentException if a period is not greater than zero.
+     *
+     * @deprecated Use the version with {@code long} integers instead of 
{@code int}.
+     * Small overviews of large images require large subsampling factors.
+     */
+    @Deprecated(since="1.5")
+    public GridExtent subsample(final int[] periods) {
+        return subsample(ArraysExt.copyAsLongs(periods));
+    }
+
     /**
      * Creates a new grid extent upsampled by the given number of cells along 
each grid dimensions.
      * This method multiplies {@linkplain #getLow(int) low} and {@linkplain 
#getHigh(int) high} coordinates
@@ -1695,20 +1714,20 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
      * If the array is shorter, missing values default to 1 (i.e. samplings in 
unspecified dimensions are unchanged).
      * If the array is longer, extraneous values are ignored.
      *
-     * @param  periods  the upsampling. Length shall be equal to the number of 
dimension and all values shall be greater than zero.
+     * @param  periods  the upsampling factors for each dimension of this grid 
extent.
      * @return the upsampled extent, or {@code this} if upsampling results in 
the same extent.
      * @throws IllegalArgumentException if a period is not greater than zero.
      * @throws ArithmeticException if the upsampled extent overflows the 
{@code long} capacity.
      *
-     * @see GridGeometry#upsample(int...)
-     * @since 1.3
+     * @see GridGeometry#upsample(long...)
+     * @since 1.5
      */
-    public GridExtent upsample(final int... periods) {
+    public GridExtent upsample(final long... periods) {
         final int m = getDimension();
         final int length = Math.min(m, periods.length);
         final GridExtent sub = new GridExtent(this);
         for (int i=0; i<length; i++) {
-            final int s = periods[i];
+            final long s = periods[i];
             if (s > 1) {
                 final int j = i + m;
                 sub.coordinates[i] = Math.multiplyExact(coordinates[i], s);
@@ -1721,6 +1740,24 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
         return Arrays.equals(coordinates, sub.coordinates) ? this : sub;
     }
 
+    /**
+     * Creates a new upsampled grid extent (32-bits version).
+     * See {@link #upsample(long...)} for details.
+     *
+     * @param  periods  the upsampling factors for each dimension of this grid 
extent.
+     * @return the upsampled extent, or {@code this} if upsampling results in 
the same extent.
+     * @throws IllegalArgumentException if a period is not greater than zero.
+     * @throws ArithmeticException if the upsampled extent overflows the 
{@code long} capacity.
+     * @since 1.3
+     *
+     * @deprecated Use the version with {@code long} integers instead of 
{@code int}.
+     * Small overviews of large images require large subsampling factors.
+     */
+    @Deprecated(since="1.5")
+    public GridExtent upsample(final int[] periods) {
+        return upsample(ArraysExt.copyAsLongs(periods));
+    }
+
     /**
      * Returns a slice of this grid extent computed by a ratio between 0 and 1 
inclusive.
      * This is a helper method for {@link GridDerivation#sliceByRatio(double, 
int...)} implementation.
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
index 89880ba706..7007c688a7 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
@@ -335,7 +335,7 @@ public class GridGeometry implements LenientComparable, 
Serializable {
      * The new {@linkplain #getEnvelope() grid geometry envelope} will be 
computed from the new extent and transform,
      * then {@linkplain GeneralEnvelope#intersect(Envelope) clipped} to the 
envelope of the other grid geometry.
      * This clip is for preventing the envelope to become larger under the 
effect of subsampling because
-     * {@linkplain GridExtent#subsample(int[]) each cell become larger}. The 
clip is not applied when {@code toOther}
+     * {@linkplain GridExtent#subsample(long[]) each cell become larger}. The 
clip is not applied when {@code toOther}
      * is {@code null} because in such case, we presume that the grid extent 
has been changed for another reason than
      * subsampling (e.g. application of a margin, in which case we want the 
envelope to be expanded).
      *
@@ -1473,14 +1473,14 @@ public class GridGeometry implements LenientComparable, 
Serializable {
      * If the array is shorter, missing values default to 1 (i.e. samplings in 
unspecified dimensions are unchanged).
      * If the array is longer, extraneous values are ignored.
      *
-     * @param  periods  the upsampling. Length shall be equal to the number of 
dimension and all values shall be greater than zero.
+     * @param  periods  the upsampling factors for each dimension of this grid 
geometry.
      * @return the upsampled grid geometry, or {@code this} is upsampling 
results in the same extent.
      * @throws IllegalArgumentException if a period is not greater than zero.
      *
-     * @see GridExtent#upsample(int...)
-     * @since 1.3
+     * @see GridExtent#upsample(long...)
+     * @since 1.5
      */
-    public GridGeometry upsample(final int... periods) {
+    public GridGeometry upsample(final long... periods) {
         GridExtent newExtent = extent;
         if (newExtent != null) {
             newExtent = newExtent.upsample(periods);
@@ -1524,6 +1524,23 @@ public class GridGeometry implements LenientComparable, 
Serializable {
         return new GridGeometry(newExtent, cornerToCenter(newGridToCRS), 
newGridToCRS, envelope, newResolution, nonLinears);
     }
 
+    /**
+     * Creates a new upsampled grid geometry (32-bits version).
+     * See {@link #upsample(long...)} for details.
+     *
+     * @param  periods  the upsampling factors for each dimension of this grid 
geometry.
+     * @return the upsampled grid geometry, or {@code this} is upsampling 
results in the same extent.
+     * @throws IllegalArgumentException if a period is not greater than zero.
+     * @since 1.3
+     *
+     * @deprecated Use the version with {@code long} integers instead of 
{@code int}.
+     * Small overviews of large images require large subsampling factors.
+     */
+    @Deprecated(since="1.5")
+    public GridGeometry upsample(final int[] periods) {
+        return upsample(ArraysExt.copyAsLongs(periods));
+    }
+
     /**
      * Translates grid coordinates by the given number of cells without 
changing "real world" coordinates.
      * The returned grid has the same {@linkplain GridExtent#getSize(int) 
size} than this grid,
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/RangeArgument.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/RangeArgument.java
index 4c152d901c..a43d48c286 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/RangeArgument.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/RangeArgument.java
@@ -57,7 +57,7 @@ public final class RangeArgument {
      * the {@code insertFoo(…)} methods are invoked.
      *
      * @see #insertBandDimension(GridExtent, int)
-     * @see #insertSubsampling(int[], int)
+     * @see #insertSubsampling(long[], int)
      */
     private int first, last, interval;
 
@@ -140,7 +140,7 @@ public final class RangeArgument {
 
     /**
      * Returns {@code true} if user specified all bands in increasing order.
-     * This method always return {@code false} if {@link 
#insertSubsampling(int[], int)} has been invoked.
+     * This method always return {@code false} if {@link 
#insertSubsampling(long[], int)} has been invoked.
      *
      * @return whether user specified all bands in increasing order without 
subsampling inserted.
      */
@@ -221,7 +221,7 @@ public final class RangeArgument {
     /**
      * Returns the i<sup>th</sup> index of the band to read from the resource, 
after subsampling has been applied.
      * The subsampling results from calls to {@link 
#insertBandDimension(GridExtent, int)} and
-     * {@link #insertSubsampling(int[], int)} methods.
+     * {@link #insertSubsampling(long[], int)} methods.
      *
      * {@snippet lang="java" :
      *     areaOfInterest = rangeIndices.insertBandDimension(areaOfInterest, 
bandDimension);
@@ -256,7 +256,7 @@ public final class RangeArgument {
     /**
      * Returns the given extent with a new dimension added for the bands. The 
extent in the new dimension
      * will range from the minimum {@code range} value to the maximum {@code 
range} value inclusive.
-     * This method should be used together with {@link 
#insertSubsampling(int[], int)}.
+     * This method should be used together with {@link 
#insertSubsampling(long[], int)}.
      *
      * <h4>Use case</h4>
      * This method is useful for reading a <var>n</var>-dimensional data cube 
with values stored in a
@@ -294,7 +294,7 @@ public final class RangeArgument {
      * @param  bandDimension  index of the band dimension.
      * @return a new subsampling array with the same values as the given array 
plus one dimension for bands.
      */
-    public int[] insertSubsampling(int[] subsampling, final int bandDimension) 
{
+    public long[] insertSubsampling(long[] subsampling, final int 
bandDimension) {
         final int[] delta = new int[packed.length - 1];
         for (int i=0; i<delta.length; i++) {
             delta[i] = getSourceIndex(i+1) - getSourceIndex(i);
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
index 968e266042..81c8d84229 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridDerivationTest.java
@@ -122,8 +122,8 @@ public final class GridDerivationTest extends TestCase {
         GridExtent     extent = change.getIntersection();
         GridExtentTest.assertExtentEquals(extent, 0,  2000, 5549);            
// Subrange of base extent.
         GridExtentTest.assertExtentEquals(extent, 1, -1000, 8000);
-        assertArrayEquals(new int[] {50, 300}, change.getSubsampling());    // 
s = scaleQuery / scaleBase
-        assertArrayEquals(new int[] {0, -100}, change.getSubsamplingOffsets());
+        assertArrayEquals(new long[] {50, 300}, change.getSubsampling());    
// s = scaleQuery / scaleBase
+        assertArrayEquals(new long[] {0, -100}, 
change.getSubsamplingOffsets());
         /*
          * Above (50, 300) subsampling shall be applied and the `gridToCRS` 
transform adjusted consequently.
          */
@@ -361,7 +361,7 @@ public final class GridDerivationTest extends TestCase {
     }
 
     /**
-     * Tests {@link GridDerivation#subgrid(GridExtent, int...)}
+     * Tests {@link GridDerivation#subgrid(GridExtent, long...)}
      * with an integer number of tiles, operating only on extents.
      */
     @Test
@@ -420,8 +420,8 @@ public final class GridDerivationTest extends TestCase {
         GridExtent     extent = change.getIntersection();
         GridExtentTest.assertExtentEquals(extent, 0,  3900, 5849);
         GridExtentTest.assertExtentEquals(extent, 1,  -910, 8000);
-        assertArrayEquals(new int[] {39, 294}, change.getSubsampling());
-        assertArrayEquals(new int[] { 0, -28}, change.getSubsamplingOffsets());
+        assertArrayEquals(new long[] {39, 294}, change.getSubsampling());
+        assertArrayEquals(new long[] { 0, -28}, 
change.getSubsamplingOffsets());
 
         final GridGeometry tg = change.build();
         extent = tg.getExtent();
@@ -453,8 +453,8 @@ public final class GridDerivationTest extends TestCase {
          * Subsampling values checked below shall be equal or smaller
          * than the values given to `maximumSubsampling(…)`.
          */
-        assertArrayEquals(new int[] {15,  84}, change.getSubsampling());
-        assertArrayEquals(new int[] { 0, -70}, change.getSubsamplingOffsets());
+        assertArrayEquals(new long[] {15,  84}, change.getSubsampling());
+        assertArrayEquals(new long[] { 0, -70}, 
change.getSubsamplingOffsets());
         assertMatrixEquals(new Matrix3(30,   0, 200,
                                         0, -84, 570,
                                         0,   0,   1),
@@ -479,7 +479,7 @@ public final class GridDerivationTest extends TestCase {
     }
 
     /**
-     * Tests {@link GridDerivation#subgrid(GridExtent, int...)} with a null 
"grid to CRS" transform.
+     * Tests {@link GridDerivation#subgrid(GridExtent, long...)} with a null 
"grid to CRS" transform.
      */
     @Test
     public void testSubgridWithoutTransform() {
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
index 31e4661fad..f24c819464 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridExtentTest.java
@@ -85,7 +85,7 @@ public final class GridExtentTest extends TestCase {
     }
 
     /**
-     * Tests {@link GridExtent#subsample(int...)}.
+     * Tests {@link GridExtent#subsample(long...)}.
      */
     @Test
     public void testSubsample() {
@@ -97,7 +97,7 @@ public final class GridExtentTest extends TestCase {
     }
 
     /**
-     * Tests {@link GridExtent#upsample(int...)}.
+     * Tests {@link GridExtent#upsample(long...)}.
      */
     @Test
     public void testUpsample() {
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
index d59e9a0924..3b1d182cbc 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
@@ -514,7 +514,7 @@ public final class GridGeometryTest extends TestCase {
     }
 
     /**
-     * Tests {@link GridGeometry#upsample(int...)}.
+     * Tests {@link GridGeometry#upsample(long...)}.
      */
     @Test
     public void testUpsample() {
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/privy/RangeArgumentTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/privy/RangeArgumentTest.java
index 8b95ca6c27..34e6fcaeff 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/privy/RangeArgumentTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/privy/RangeArgumentTest.java
@@ -77,7 +77,7 @@ public final class RangeArgumentTest extends TestCase 
implements Localized {
     public void testRangeArgumentForInterleavedModel() {
         final RangeArgument r = RangeArgument.validate(7, new int[] {4, 6, 2}, 
this);
         assertEquals(3, r.insertBandDimension(new GridExtent(360, 180), 
2).getDimension());
-        assertArrayEquals(new int[] {3, 1, 2}, r.insertSubsampling(new int[] 
{3, 1}, 2));
+        assertArrayEquals(new long[] {3, 1, 2}, r.insertSubsampling(new long[] 
{3, 1}, 2));
         assertEquals(3, r.getNumBands());
         assertEquals(4, r.getFirstSpecified());
         assertEquals(2, r.getSourceIndex(0));           // Expect sorted 
source indices: {2, 4, 6}.
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/CompressedSubset.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/CompressedSubset.java
index 5d418d27be..02988462ce 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/CompressedSubset.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/CompressedSubset.java
@@ -107,8 +107,8 @@ final class CompressedSubset extends DataSubset {
     @SuppressWarnings("LocalVariableHidesMemberVariable")
     CompressedSubset(final DataCube source, final TiledGridResource.Subset 
subset) throws DataStoreException {
         super(source, subset);
-        scanlineStride     = sourcePixelStride * getTileSize(X_DIMENSION);
-        final int between  = sourcePixelStride * (getSubsampling(X_DIMENSION) 
- 1);
+        scanlineStride     = Math.multiplyExact(sourcePixelStride, 
getTileSize(X_DIMENSION));
+        final int between  = Math.multiplyExact(sourcePixelStride, 
Math.toIntExact(getSubsampling(X_DIMENSION) - 1));
         long afterLastBand = scanlineStride - sourcePixelStride;
         if (includedBands != null && sourcePixelStride > 1) {
             final int[] skips = new int[includedBands.length];
@@ -169,7 +169,7 @@ final class CompressedSubset extends DataSubset {
      * Computes the number of pixels to read in dimension <var>i</var>.
      * The arguments given to this method are the ones given to the {@code 
readSlice(…)} method.
      */
-    private static int pixelCount(final long[] lower, final long[] upper, 
final int[] subsampling, final int i) {
+    private static int pixelCount(final long[] lower, final long[] upper, 
final long[] subsampling, final int i) {
         final int n = toIntExact((upper[i] - lower[i] - 1) / subsampling[i] + 
1);
         assert (n > 0) : n;
         return n;
@@ -188,13 +188,13 @@ final class CompressedSubset extends DataSubset {
      */
     @Override
     Raster readSlice(final long[] offsets, final long[] byteCounts, final 
long[] lower, final long[] upper,
-                     final int[] subsampling, final Point location) throws 
IOException, DataStoreException
+                     final long[] subsampling, final Point location) throws 
IOException, DataStoreException
     {
         final DataType dataType = getDataType();
         final int  width        = pixelCount(lower, upper, subsampling, 
X_DIMENSION);
         final int  height       = pixelCount(lower, upper, subsampling, 
Y_DIMENSION);
         final int  chunksPerRow = width * (targetPixelStride / 
samplesPerChunk);
-        final int  betweenRows  = subsampling[1] - 1;
+        final int  betweenRows  = Math.toIntExact(subsampling[1] - 1);
         final long head         = beforeFirstBand + sourcePixelStride * 
(lower[X_DIMENSION]);
         final long tail         = afterLastBand   - sourcePixelStride * 
(lower[X_DIMENSION] + (width-1)*subsampling[X_DIMENSION]);
         /*
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 0ed305eedb..6140124f95 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
@@ -370,7 +370,7 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
                  */
                 final long[] lower       = new long[BIDIMENSIONAL];   // 
Coordinates of the first pixel to read relative to the tile.
                 final long[] upper       = new long[BIDIMENSIONAL];   // 
Coordinates after the last pixel to read relative to the tile.
-                final int[]  subsampling = new int [BIDIMENSIONAL];
+                final long[] subsampling = new long[BIDIMENSIONAL];
                 final Point  origin      = new Point();
                 final long[] offsets     = new long[numBanks];
                 final long[] byteCounts  = new long[numBanks];
@@ -483,7 +483,7 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
      * @see DataCube#canReadDirect(TiledGridResource.Subset)
      */
     Raster readSlice(final long[] offsets, final long[] byteCounts, final 
long[] lower, final long[] upper,
-                     final int[] subsampling, final Point location) throws 
IOException, DataStoreException
+                     final long[] subsampling, final Point location) throws 
IOException, DataStoreException
     {
         final DataType type = getDataType();
         final int sampleSize = type.size();     // Assumed same as 
`SampleModel.getSampleSize(…)` by preconditions.
diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/RasterResource.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/RasterResource.java
index 8101ff2362..ddfb51efb7 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/RasterResource.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/RasterResource.java
@@ -650,7 +650,7 @@ public final class RasterResource extends 
AbstractGridCoverageResource implement
                     .rounding(GridRoundingMode.ENCLOSING)
                     .subgrid((domain != null) ? domain : gridGeometry);
             GridExtent areaOfInterest = targetGeometry.getIntersection();      
     // Pixel indices of data to read.
-            int[]      subsampling    = targetGeometry.getSubsampling();       
     // Slice to read or subsampling to apply.
+            long[]     subsampling    = targetGeometry.getSubsampling();       
     // Slice to read or subsampling to apply.
             int        numBuffers     = bands.length;                          
     // By default, one variable per band.
             targetDomain = targetGeometry.build();                             
     // Adjust user-specified domain to data geometry.
             if (bandDimension >= 0) {
diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Variable.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Variable.java
index 06739b6094..26c696a390 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Variable.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Variable.java
@@ -1050,7 +1050,7 @@ public abstract class Variable extends Node {
      * @throws DataStoreException if a logical error occurred.
      * @throws ArithmeticException if the size of the region to read exceeds 
{@link Integer#MAX_VALUE}, or other overflow occurs.
      */
-    public abstract Vector read(GridExtent area, int[] subsampling) throws 
IOException, DataStoreException;
+    public abstract Vector read(GridExtent area, long[] subsampling) throws 
IOException, DataStoreException;
 
     /**
      * Reads a subsampled sub-area of the variable and returns them as a list 
of any object.
@@ -1063,7 +1063,7 @@ public abstract class Variable extends Node {
      * @throws DataStoreException if a logical error occurred.
      * @throws ArithmeticException if the size of the region to read exceeds 
{@link Integer#MAX_VALUE}, or other overflow occurs.
      */
-    public abstract List<?> readAnyType(GridExtent area, int[] subsampling) 
throws IOException, DataStoreException;
+    public abstract List<?> readAnyType(GridExtent area, long[] subsampling) 
throws IOException, DataStoreException;
 
     /**
      * Reads all the data for this variable and returns them as an array of a 
Java primitive type.
diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/classic/VariableInfo.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/classic/VariableInfo.java
index fffa87d900..a051dbdbc0 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/classic/VariableInfo.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/classic/VariableInfo.java
@@ -663,7 +663,7 @@ final class VariableInfo extends Variable implements 
Comparable<VariableInfo> {
      * @throws ArithmeticException if the size of the region to read exceeds 
{@link Integer#MAX_VALUE}, or other overflow occurs.
      */
     @Override
-    public Vector read(final GridExtent area, final int[] subsampling) throws 
IOException, DataStoreException {
+    public Vector read(final GridExtent area, final long[] subsampling) throws 
IOException, DataStoreException {
         return Vector.create(readArray(area, subsampling), 
dataType.isUnsigned);
     }
 
@@ -676,7 +676,7 @@ final class VariableInfo extends Variable implements 
Comparable<VariableInfo> {
      * @return the data as a list of {@link Number} or {@link String} 
instances.
      */
     @Override
-    public List<?> readAnyType(final GridExtent area, final int[] subsampling) 
throws IOException, DataStoreException {
+    public List<?> readAnyType(final GridExtent area, final long[] 
subsampling) throws IOException, DataStoreException {
         final Object array = readArray(area, subsampling);
         if (dataType == DataType.CHAR && dimensions.length >= 
STRING_DIMENSION) {
             return createStringList(array, area);
@@ -696,9 +696,9 @@ final class VariableInfo extends Variable implements 
Comparable<VariableInfo> {
      * @throws ArithmeticException if the size of the variable exceeds {@link 
Integer#MAX_VALUE}, or other overflow occurs.
      *
      * @see #read()
-     * @see #read(GridExtent, int[])
+     * @see #read(GridExtent, long[])
      */
-    private Object readArray(final GridExtent area, int[] subsampling) throws 
IOException, DataStoreException {
+    private Object readArray(final GridExtent area, long[] subsampling) throws 
IOException, DataStoreException {
         if (reader == null) {
             throw new DataStoreContentException(unknownType());
         }
@@ -733,7 +733,7 @@ final class VariableInfo extends Variable implements 
Comparable<VariableInfo> {
             }
         }
         if (subsampling == null) {
-            subsampling = new int[dimension];
+            subsampling = new long[dimension];
             Arrays.fill(subsampling, 1);
         }
         final Region region = new Region(size, lower, upper, subsampling);
diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/ucar/VariableWrapper.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/ucar/VariableWrapper.java
index 86fd376688..fba9001d16 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/ucar/VariableWrapper.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/ucar/VariableWrapper.java
@@ -478,9 +478,10 @@ final class VariableWrapper extends 
org.apache.sis.storage.netcdf.base.Variable
      * @param  area         indices of cell values to read along each 
dimension, in "natural" order.
      * @param  subsampling  subsampling along each dimension, or {@code null} 
if none.
      * @return the data as a vector wrapping a Java array.
+     * @throws ArithmeticException if an argument exceeds the capacity of 32 
bits integer.
      */
     @Override
-    public Vector read(final GridExtent area, final int[] subsampling) throws 
IOException, DataStoreException {
+    public Vector read(final GridExtent area, final long[] subsampling) throws 
IOException, DataStoreException {
         final Object array = readArray(area, subsampling);
         return Vector.create(array, variable.getDataType().isUnsigned());
     }
@@ -492,9 +493,10 @@ final class VariableWrapper extends 
org.apache.sis.storage.netcdf.base.Variable
      * @param  area         indices of cell values to read along each 
dimension, in "natural" order.
      * @param  subsampling  subsampling along each dimension, or {@code null} 
if none.
      * @return the data as a list of {@link Number} or {@link String} 
instances.
+     * @throws ArithmeticException if an argument exceeds the capacity of 32 
bits integer.
      */
     @Override
-    public List<?> readAnyType(final GridExtent area, final int[] subsampling) 
throws IOException, DataStoreException {
+    public List<?> readAnyType(final GridExtent area, final long[] 
subsampling) throws IOException, DataStoreException {
         final Object array = readArray(area, subsampling);
         final ucar.ma2.DataType type = variable.getDataType();
         if (type == ucar.ma2.DataType.CHAR && variable.getRank() >= 
STRING_DIMENSION) {
@@ -511,11 +513,12 @@ final class VariableWrapper extends 
org.apache.sis.storage.netcdf.base.Variable
      * @param  area         indices of cell values to read along each 
dimension, in "natural" order.
      * @param  subsampling  subsampling along each dimension, or {@code null} 
if none.
      * @return the data as an array of a Java primitive type.
+     * @throws ArithmeticException if an argument exceeds the capacity of 32 
bits integer.
      *
      * @see #read()
-     * @see #read(GridExtent, int[])
+     * @see #read(GridExtent, long[])
      */
-    private Object readArray(final GridExtent area, final int[] subsampling) 
throws IOException, DataStoreException {
+    private Object readArray(final GridExtent area, final long[] subsampling) 
throws IOException, DataStoreException {
         int n = area.getDimension();
         final int[] lower = new int[n];
         final int[] size  = new int[n];
@@ -526,7 +529,7 @@ final class VariableWrapper extends 
org.apache.sis.storage.netcdf.base.Variable
             lower[j] = Math.toIntExact(area.getLow(i));
             size [j] = Math.toIntExact(area.getSize(i));
             if (sub != null) {
-                sub[j] = subsampling[i];
+                sub[j] = Math.toIntExact(subsampling[i]);
             }
         }
         final Array array;
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleWriter.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleWriter.java
index f4ec6a0c1d..ae69c83767 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleWriter.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleWriter.java
@@ -199,7 +199,7 @@ public class HyperRectangleWriter {
             };
             regionLower[0] = Math.multiplyExact(regionLower[0], pixelStride);
             regionUpper[0] = Math.multiplyExact(regionUpper[0], pixelStride);
-            var subset = new Region(sourceSize, regionLower, regionUpper, new 
int[] {1,1});
+            var subset = new Region(sourceSize, regionLower, regionUpper, new 
long[] {1,1});
             length = subset.length;
             if (bandOffsets == null || (bandOffsets.length == pixelStride && 
ArraysExt.isRange(0, bandOffsets))) {
                 return new HyperRectangleWriter(subset);
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java
index 281b01c469..b3109ae75b 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java
@@ -104,7 +104,7 @@ public final class Region {
      * @throws ArithmeticException if the size of the region to read exceeds 
{@link Integer#MAX_VALUE},
      *                             or the total hyper-cube size exceeds {@link 
Long#MAX_VALUE}.
      */
-    public Region(final long[] sourceSize, final long[] regionLower, final 
long[] regionUpper, final int[] subsampling) {
+    public Region(final long[] sourceSize, final long[] regionLower, final 
long[] regionUpper, final long[] subsampling) {
         final int dimension = sourceSize.length;
         targetSize = new int[dimension];
         skips = new long[dimension + 1];
@@ -112,7 +112,7 @@ public final class Region {
         long stride   = 1;
         long skip     = 0;
         for (int i=0; i<dimension;) {
-            final int  step  = subsampling[i];
+            final long step  = subsampling[i];
             final long lower = regionLower[i];
             final long count = ceilDiv(subtractExact(regionUpper[i], lower), 
step);
             final long upper = addExact(lower, 
incrementExact(multiplyExact(count-1, step)));
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 684d9a5226..dab74aaff7 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
@@ -31,7 +31,6 @@ import java.awt.image.WritableRaster;
 import static java.lang.Math.addExact;
 import static java.lang.Math.subtractExact;
 import static java.lang.Math.multiplyExact;
-import static java.lang.Math.multiplyFull;
 import static java.lang.Math.incrementExact;
 import static java.lang.Math.decrementExact;
 import static java.lang.Math.toIntExact;
@@ -175,7 +174,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
      * @see #getSubsampling(int)
      * @see #pixelToResourceCoordinate(long, int)
      */
-    private final int[] subsampling, subsamplingOffsets;
+    private final long[] subsampling, subsamplingOffsets;
 
     /**
      * Indices of {@link TiledGridResource} bands which have been retained for
@@ -277,7 +276,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
         this.model      = model;
         this.colors     = subset.colorsForBandSubset;
         this.fillValues = subset.fillValues;
-        forceTileSize   = multiplyFull(subSize[X_DIMENSION], 
subsampling[X_DIMENSION]) == virtualTileSize[X_DIMENSION];
+        forceTileSize   = multiplyExact(subsampling[X_DIMENSION], 
subSize[X_DIMENSION]) == virtualTileSize[X_DIMENSION];
     }
 
     /**
@@ -315,7 +314,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
      * @param  dimension  dimension for which to get subsampling.
      * @return subsampling as a value ≥ 1.
      */
-    protected final int getSubsampling(final int dimension) {
+    protected final long getSubsampling(final int dimension) {
         return subsampling[dimension];
     }
 
@@ -726,7 +725,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
          * @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, final boolean subsampled)
+                final long[] subsampling, final boolean subsampled)
         {
             int dimension = Math.min(lower.length, upper.length);
             final TiledGridCoverage coverage = getCoverage();
@@ -760,7 +759,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
                      * number of white squares in tiles (4,3,3,4 in the above 
example),
                      * except when there is only 1 tile to read in which case 
offset is tolerated.
                      */
-                    final int s = coverage.getSubsampling(dimension);
+                    final long s = coverage.getSubsampling(dimension);
                     offset %= s;
                     if (offset != 0) {
                         offset += s;
@@ -773,7 +772,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
                     limit = tileSize;
                 }
                 if (subsampled) {
-                    final int s = coverage.getSubsampling(dimension);
+                    final long s = coverage.getSubsampling(dimension);
                     offset /= s;        // Round toward 0 because we should 
not have negative coordinates.
                     limit = (limit - 1) / s + 1;
                 }
@@ -850,7 +849,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
                 final int count   = subtractExact(tileUpper[i], lower);
                 indexInTileVector = addExact(indexInTileVector, 
multiplyExact(tileStrides[i], lower));
                 tileCountInQuery  = multiplyExact(tileCountInQuery, count);
-                tileOffsetFull[i] = multiplyFull(offsetAOI[i], 
getSubsampling(i));
+                tileOffsetFull[i] = multiplyExact(getSubsampling(i), 
offsetAOI[i]);
                 /*
                  * Following is the pixel coordinate after the last pixel in 
current dimension.
                  * This is not stored; the intent is to get a potential 
`ArithmeticException`
@@ -871,26 +870,26 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
          * extra indices are ignored and missing indices are inherited from 
this AOI.
          * This method is independent to the iterator position of this {@code 
AOI}.
          *
-         * @param  tileLower  indices (relative to enclosing {@code 
TiledGridCoverage}) of the upper-left tile to read.
-         * @param  tileUpper  indices (relative to enclosing {@code 
TiledGridCoverage}) after the bottom-right tile to read.
+         * @param  firstTile  indices (relative to enclosing {@code 
TiledGridCoverage}) of the upper-left tile to read.
+         * @param  endTile    indices (relative to enclosing {@code 
TiledGridCoverage}) after the bottom-right tile to read.
          * @return a new {@code TileIterator} instance for the specified 
sub-region.
          */
-        public TileIterator subset(final int[] tileLower, final int[] 
tileUpper) {
-            final int[] offset = this.offsetAOI.clone();
-            final int[] lower  = this.tileLower.clone();
-            for (int i = Math.min(tileLower.length, lower.length); --i >= 0;) {
+        public TileIterator subset(final int[] firstTile, final int[] endTile) 
{
+            final int[] offset = offsetAOI.clone();
+            final int[] lower  = tileLower.clone();
+            for (int i = Math.min(firstTile.length, lower.length); --i >= 0;) {
                 final int base = lower[i];
-                final int s = tileLower[i];
+                final int s = firstTile[i];
                 if (s > base) {
                     lower[i] = s;
                     // Use of `ceilDiv(…)` is for consistency with 
`getTileOrigin(int)`.
-                    long origin = ceilDiv(multiplyExact(s - base, 
getTileSize(i)), getSubsampling(i));
-                    offset[i] = toIntExact(addExact(offset[i], origin));
+                    long origin = ceilDiv(multiplyExact(getTileSize(i), s - 
base), getSubsampling(i));
+                    offset[i] = toIntExact(addExact(origin, offset[i]));
                 }
             }
-            final int[] upper = this.tileUpper.clone();
-            for (int i = Math.min(tileUpper.length, upper.length); --i >= 0;) {
-                upper[i] = Math.max(lower[i], Math.min(upper[i], 
tileUpper[i]));
+            final int[] upper = tileUpper.clone();
+            for (int i = Math.min(endTile.length, upper.length); --i >= 0;) {
+                upper[i] = Math.max(lower[i], Math.min(upper[i], endTile[i]));
             }
             return new TileIterator(lower, upper, offset, offset.length);
         }
@@ -994,7 +993,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
                 // Rewind to index for tileLower[i].
                 indexInTileVector -= (tmcInSubset[i] - tileLower[i]) * 
tileStrides[i];
                 tmcInSubset   [i]  = tileLower[i];
-                tileOffsetFull[i]  = multiplyFull(offsetAOI[i], 
getSubsampling(i));
+                tileOffsetFull[i]  = multiplyExact(getSubsampling(i), 
offsetAOI[i]);
             }
             return true;
         }
@@ -1141,7 +1140,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
         return new Rectangle(
                 toIntExact(pixelToResourceCoordinate(bounds.x, X_DIMENSION)),
                 toIntExact(pixelToResourceCoordinate(bounds.y, Y_DIMENSION)),
-                multiplyExact(bounds.width,  subsampling[X_DIMENSION]),
-                multiplyExact(bounds.height, subsampling[Y_DIMENSION]));
+                toIntExact(multiplyExact(subsampling[X_DIMENSION], 
bounds.width)),
+                toIntExact(multiplyExact(subsampling[Y_DIMENSION], 
bounds.height)));
     }
 }
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 b4bff677b3..7f0b36e4e9 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
@@ -91,14 +91,14 @@ public abstract class TiledGridResource extends 
AbstractGridCoverageResource {
      * Each key shall be unique within its enclosing {@link TiledGridResource} 
instance.
      */
     static final class CacheKey {
-        /** Index in a row-major array of tiles. */ private final int   
indexInTileVector;
-        /** Bands in strictly increasing order.  */ private final int[] 
includedBands;
-        /** Subsampling factors at read time.    */ private final int[] 
subsampling;
-        /** Remainder of subsampling divisions.  */ private final int[] 
subsamplingOffsets;
+        /** Index in a row-major array of tiles. */ private final int    
indexInTileVector;
+        /** Bands in strictly increasing order.  */ private final int[]  
includedBands;
+        /** Subsampling factors at read time.    */ private final long[] 
subsampling;
+        /** Remainder of subsampling divisions.  */ private final long[] 
subsamplingOffsets;
 
         /** Creates a key with given arrays hold be reference (no copy). */
         CacheKey(final int indexInTileVector, final int[] includedBands,
-                 final int[] subsampling, final int[] subsamplingOffsets)
+                 final long[] subsampling, final long[] subsamplingOffsets)
         {
             this.indexInTileVector  = indexInTileVector;
             this.includedBands      = includedBands;
@@ -197,7 +197,7 @@ public abstract class TiledGridResource extends 
AbstractGridCoverageResource {
      * @return the size of tiles (in pixels) in this resource.
      * @throws DataStoreException if an error occurred while fetching the tile 
size.
      */
-    protected long[] getVirtualTileSize(int[] subsampling) throws 
DataStoreException {
+    protected long[] getVirtualTileSize(long[] subsampling) throws 
DataStoreException {
         return ArraysExt.copyAsLongs(getTileSize());
     }
 
@@ -425,19 +425,19 @@ check:  if (dataType.isInteger()) {
          * This array contains the factors by which to divide {@link 
TiledGridResource}
          * cell coordinates in order to obtain {@link TiledGridCoverage} cell 
coordinates.
          */
-        final int[] subsampling;
+        final long[] subsampling;
 
         /**
          * Remainder of the divisions of {@link TiledGridResource} cell 
coordinates by subsampling factors.
          */
-        final int[] subsamplingOffsets;
+        final long[] subsamplingOffsets;
 
         /**
          * Size of tiles (or chunks) in the resource, without clipping and 
subsampling.
          * May be a virtual tile size (i.e., tiles larger than the real tiles) 
if the
          * resource can easily coalesce many tiles in a single read operation.
          *
-         * @see #getVirtualTileSize(int[])
+         * @see #getVirtualTileSize(long[])
          */
         final long[] virtualTileSize;
 
@@ -488,8 +488,8 @@ check:  if (dataType.isInteger()) {
             if (domain == null) {
                 domain             = gridGeometry;
                 readExtent         = sourceExtent;
-                subsamplingOffsets = new int[gridGeometry.getDimension()];
-                subsampling        = new int[subsamplingOffsets.length];
+                subsamplingOffsets = new long[gridGeometry.getDimension()];
+                subsampling        = new long[subsamplingOffsets.length];
                 Arrays.fill(subsampling, 1);
             } else {
                 /*
@@ -499,33 +499,28 @@ check:  if (dataType.isInteger()) {
                  * Note that it is possible to disable this restriction in a 
single dimension, typically the X one
                  * when reading a TIFF image using strips instead of tiles.
                  */
-                final int atomSizeX = getAtomSize(0);
-                final int atomSizeY = getAtomSize(1);
-                int tileWidth   = tileSize[X_DIMENSION];
-                int tileHeight  = tileSize[Y_DIMENSION];
-                if (tileWidth  >= sourceExtent.getSize(X_DIMENSION)) 
{tileWidth  = atomSizeX; sharedCache = false;}
-                if (tileHeight >= sourceExtent.getSize(Y_DIMENSION)) 
{tileHeight = atomSizeY; sharedCache = false;}
-                /*
-                 * Note: if we allow X_DIMENSION and Y_DIMENSION to be 
anything in the future, then
-                 * BIDIMENSIONAL must become `max(xDim, yDim) + 1` and array 
must be initialized to 1.
-                 */
-                final int[] chunkSize  = new 
int[TiledGridCoverage.BIDIMENSIONAL];
-                chunkSize[X_DIMENSION] = tileWidth;
-                chunkSize[Y_DIMENSION] = tileHeight;
-                /*
-                 * Maximal subsampling supported. We put no restriction if 
subsamplig can occur anywhere
-                 * ("atome size" of 1) and disable subsampling otherwise for 
avoiding code complexity.
-                 */
-                final int[] maximumSubsampling = new int[chunkSize.length];
-                Arrays.fill(maximumSubsampling, Integer.MAX_VALUE);
-                if (atomSizeX != 1) maximumSubsampling[X_DIMENSION] = 1;
-                if (atomSizeY != 1) maximumSubsampling[Y_DIMENSION] = 1;
+                final var chunkSize = new int [tileSize.length];
+                final var maxSubsmp = new long[tileSize.length];
+                for (int i=0; i < tileSize.length; i++) {
+                    final int atomSize = getAtomSize(i);
+                    int span = tileSize[i];
+                    if (span >= sourceExtent.getSize(i)) {
+                        span = atomSize;
+                        sharedCache = false;
+                    }
+                    /*
+                     * We put no restriction on the maximum subsampling if 
subsamplig can occur anywhere
+                     * ("atome size" of 1) and disable subsampling otherwise 
for avoiding code complexity.
+                     */
+                    maxSubsmp[i] = (atomSize == 1) ? Long.MAX_VALUE : 1;
+                    chunkSize[i] = (i == X_DIMENSION || i == Y_DIMENSION) ? 
span : 1;
+                }
                 /*
                  * Build the domain in units of subsampled pixels, and get the 
same extent (`readExtent`)
                  * without subsampling, i.e. in units of cells of the original 
grid resource.
                  */
                 final GridDerivation target = 
gridGeometry.derive().chunkSize(chunkSize)
-                            .maximumSubsampling(maximumSubsampling)
+                            .maximumSubsampling(maxSubsmp)
                             .rounding(GridRoundingMode.ENCLOSING)
                             .subgrid(domain);
 
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterReader.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterReader.java
index 74951f412e..2caf4b8ad3 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterReader.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterReader.java
@@ -133,7 +133,7 @@ final class RawRasterReader extends HyperRectangleReader {
         final long[] fullSize;              // The number of sample values 
along each dimension.
         final long[] regionLower;           // Indices of the first value to 
read along each dimension.
         final long[] regionUpper;           // Indices after the last value to 
read along each dimension.
-        final int[]  subsampling;           // Subsampling along each 
dimension. Shall be greater than zero.
+        final long[] subsampling;           // Subsampling along each 
dimension. Shall be greater than zero.
         if (layout instanceof ComponentSampleModel) {
             final ComponentSampleModel cm = (ComponentSampleModel) layout;
             scanlineStride       = cm.getScanlineStride();
@@ -151,7 +151,7 @@ final class RawRasterReader extends HyperRectangleReader {
         regionUpper = fullSize.clone();
         if (domain == null) {
             domain = gridGeometry;
-            subsampling = new int[] {1, 1};
+            subsampling = new long[] {1, 1};
         } else {
             /*
              * Take in account the requested domain with the following 
restrictions:
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileResource.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileResource.java
index cd2131ea87..244875d69d 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileResource.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileResource.java
@@ -267,13 +267,13 @@ class WorldFileResource extends 
AbstractGridCoverageResource implements StoreRes
                 if (domain == null) {
                     domain = gridGeometry;
                 } else {
-                    final GridDerivation gd = 
gridGeometry.derive().rounding(GridRoundingMode.ENCLOSING).subgrid(domain);
-                    final GridExtent extent = gd.getIntersection();
-                    final int[] subsampling = gd.getSubsampling();
-                    final int[] offsets     = gd.getSubsamplingOffsets();
-                    final int   subX        = subsampling[X_DIMENSION];
-                    final int   subY        = subsampling[Y_DIMENSION];
-                    final Rectangle region  = new Rectangle(
+                    final GridDerivation  gd = 
gridGeometry.derive().rounding(GridRoundingMode.ENCLOSING).subgrid(domain);
+                    final GridExtent  extent = gd.getIntersection();
+                    final long[] subsampling = gd.getSubsampling();
+                    final long[] offsets     = gd.getSubsamplingOffsets();
+                    final long   subX        = subsampling[X_DIMENSION];
+                    final long   subY        = subsampling[Y_DIMENSION];
+                    final Rectangle region   = new Rectangle(
                             toIntExact(extent.getLow (X_DIMENSION)),
                             toIntExact(extent.getLow (Y_DIMENSION)),
                             toIntExact(extent.getSize(X_DIMENSION)),
@@ -294,7 +294,9 @@ class WorldFileResource extends 
AbstractGridCoverageResource implements StoreRes
                     domain = gd.build();
                     GridExtent subExtent = domain.getExtent();
                     param.setSourceRegion(region);
-                    param.setSourceSubsampling(subX, subY,
+                    param.setSourceSubsampling(
+                            toIntExact(subX),
+                            toIntExact(subY),
                             toIntExact(subExtent.getLow(X_DIMENSION) * subX + 
offsets[X_DIMENSION] - region.x),
                             toIntExact(subExtent.getLow(Y_DIMENSION) * subY + 
offsets[Y_DIMENSION] - region.y));
                 }
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleReaderTest.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleReaderTest.java
index fbfb3694d7..60c0c8d441 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleReaderTest.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleReaderTest.java
@@ -57,7 +57,7 @@ public final class HyperRectangleReaderTest extends TestCase {
     /**
      * Subsampling values to use for the test.
      */
-    private final int[] subsampling = new int[size.length];
+    private final long[] subsampling = new long[size.length];
 
     /**
      * The reader to test for an hyper-cube of {@code short} values, created 
by {@link #initialize(Random, boolean)}.
@@ -151,10 +151,10 @@ public final class HyperRectangleReaderTest extends 
TestCase {
     private void verifyRegionRead() throws IOException {
         final short[] data = (short[]) reader.read(new Region(size, lower, 
upper, subsampling));
         int p = 0;
-        final int s3 = subsampling[3];
-        final int s2 = subsampling[2];
-        final int s1 = subsampling[1];
-        final int s0 = subsampling[0];
+        final long s3 = subsampling[3];
+        final long s2 = subsampling[2];
+        final long s1 = subsampling[1];
+        final long s0 = subsampling[0];
         for (long i3=lower[3]; i3<upper[3]; i3 += s3) {
             for (long i2=lower[2]; i2<upper[2]; i2 += s2) {
                 for (long i1=lower[1]; i1<upper[1]; i1 += s1) {
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleWriterTest.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleWriterTest.java
index 5809c65995..9104b0cbff 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleWriterTest.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/HyperRectangleWriterTest.java
@@ -115,7 +115,7 @@ public final class HyperRectangleWriterTest extends 
TestCase {
                 new long[] {sourceSizeX,  sourceSizeY,  sourceSizeZ},
                 new long[] {lowerX,       lowerY,       lowerZ},
                 new long[] {upperX,       upperY,       upperZ},
-                new  int[] {subsamplingX, subsamplingY, subsamplingZ});
+                new long[] {subsamplingX, subsamplingY, subsamplingZ});
 
         final byte[] target = new byte[dataSize * region.targetLength(3)];
         final var buffer = ByteBuffer.allocate(random.nextInt(10) + 
Double.BYTES);
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/SubsampledRectangleWriterTest.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/SubsampledRectangleWriterTest.java
index bce77b933c..49edefd180 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/SubsampledRectangleWriterTest.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/SubsampledRectangleWriterTest.java
@@ -85,7 +85,7 @@ public final class SubsampledRectangleWriterTest extends 
TestCase {
         final int    length = width * height;
         final long[] lower  = new long[2];
         final long[] upper  = new long[] {width, height};
-        final int[]  subsm  = new int[]  {1,1};
+        final long[] subsm  = new long[] {1,1};
         final A source = creator.apply(length);
         for (int i=0; i<length; i++) {
             Array.setByte(source, i, (byte) (BASE + i));
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
index 1f92a0b74f..d46151044a 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
@@ -304,7 +304,7 @@ public abstract class CoverageReadConsistency<S extends 
DataStore> extends TestC
      * @param  high         pre-allocated array where to write the upper grid 
coordinates, inclusive.
      * @param  subsampling  pre-allocated array where to write the subsampling.
      */
-    private GridGeometry randomDomain(final GridGeometry gg, final long[] low, 
final long[] high, final int[] subsampling) {
+    private GridGeometry randomDomain(final GridGeometry gg, final long[] low, 
final long[] high, final long[] subsampling) {
         final GridExtent fullExtent = gg.getExtent();
         final int dimension = fullExtent.getDimension();
         for (int d=0; d<dimension; d++) {
@@ -357,8 +357,8 @@ public abstract class CoverageReadConsistency<S extends 
DataStore> extends TestC
         final int    dimension   = gg.getDimension();
         final long[] low         = new long[dimension];
         final long[] high        = new long[dimension];
-        final int [] subsampling = new int [dimension];
-        final int [] subOffsets  = new int [dimension];
+        final long[] subsampling = new long[dimension];
+        final long[] subOffsets  = new long[dimension];
         final int    numBands    = resource.getSampleDimensions().size();
         /*
          * We will collect statistics on execution time only if the
@@ -504,7 +504,7 @@ nextSlice:  for (;;) {
      * @return pixel iterator over requested area, or {@code null} if 
unavailable.
      */
     private static PixelIterator iterator(final GridCoverage coverage, long[] 
sliceMin, long[] sliceMax,
-            final int[] subsampling, final int[] subOffsets, final boolean 
allowSubsampling)
+            final long[] subsampling, final long[] subOffsets, final boolean 
allowSubsampling)
     {
         if (coverage == null) {
             return null;
@@ -538,8 +538,8 @@ nextSlice:  for (;;) {
          * and set the offset to the complement.
          */
         if (allowSubsampling) {
-            final int subX = subsampling[0];
-            final int subY = subsampling[1];
+            final int subX = StrictMath.toIntExact(subsampling[0]);
+            final int subY = StrictMath.toIntExact(subsampling[1]);
             if (subX > image.getTileWidth() || subY > image.getTileHeight()) {
                 return null;        // `SubsampledImage` does not support this 
case.
             }
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 1a71900078..dbd7ea7034 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
@@ -559,10 +559,10 @@ final class TiledResource extends TiledGridResource {
      * This is useful in particular with pyramided images read with 
potentially high subsampling values.
      */
     @Override
-    protected long[] getVirtualTileSize(final int[] subsampling) {
+    protected long[] getVirtualTileSize(final long[] subsampling) {
         return new long[] {
-            Math.multiplyFull(subsampling[0], tileWidth),
-            Math.multiplyFull(subsampling[1], tileHeight)
+            Math.multiplyExact(subsampling[0], tileWidth),
+            Math.multiplyExact(subsampling[1], tileHeight)
         };
     }
 

Reply via email to