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) }; }