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 a1b220381e2fad89b49076a8a7d80b852761118f Author: Martin Desruisseaux <[email protected]> AuthorDate: Fri Dec 6 17:47:40 2024 +0100 Relax the check for the number of dimensions when the grid coverage has less dimensions than expected. Normally, the number of dimensions of the tiles should be equal to the number of dimensions declared in the grid extent. However, it happens sometime that the `TiledGridResource` is a two dimensional image associated to a three-dimensional CRS. This is not recommended, but can happen with GeoTIFF for example. What to do with the extra dimensions is unclear (the GeoTIFF specification itself said nothing). --- .../coverage/grid/CoordinateOperationFinder.java | 2 ++ .../apache/sis/coverage/grid/GridDerivation.java | 6 ++--- .../org/apache/sis/coverage/grid/GridGeometry.java | 8 ++++-- .../apache/sis/storage/base/TiledGridCoverage.java | 6 ++--- .../apache/sis/storage/base/TiledGridResource.java | 30 +++++++++++++++------- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/CoordinateOperationFinder.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/CoordinateOperationFinder.java index 14147de8df..52be84d555 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/CoordinateOperationFinder.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/CoordinateOperationFinder.java @@ -390,6 +390,7 @@ final class CoordinateOperationFinder implements Supplier<double[]> { * if that transform is missing, we cannot continue (we have no way to guess it). */ gridToCRS = source.getGridToCRS(anchor); + @SuppressWarnings("LocalVariableHidesMemberVariable") final CoordinateOperation changeOfCRS = changeOfCRS(); if (changeOfCRS != null) { /* @@ -431,6 +432,7 @@ apply: if (forwardChangeOfCRS == null) { */ final MathTransform inverse() throws FactoryException, TransformException { final MathTransform sourceCrsToGrid = source.getGridToCRS(anchor).inverse(); + @SuppressWarnings("LocalVariableHidesMemberVariable") final CoordinateOperation changeOfCRS = changeOfCRS(); if (changeOfCRS == null) { return sourceCrsToGrid; 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 7695fe9ad6..6d93ce4ea9 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 @@ -526,13 +526,13 @@ public class GridDerivation { } final MathTransform mapCenters; final GridExtent domain = areaOfInterest.extent; - final CoordinateOperationFinder finder = new CoordinateOperationFinder(areaOfInterest, base); + final var finder = new CoordinateOperationFinder(areaOfInterest, base); finder.verifyPresenceOfCRS(false); try { final MathTransform mapCorners = (domain != null) ? finder.gridToGrid() : null; finder.setAnchor(PixelInCell.CELL_CENTER); finder.nowraparound(); - mapCenters = finder.gridToGrid(); // We will use only the scale factors. + mapCenters = finder.gridToGrid(); // We will use only the scale factors. if (domain != null) { final GeneralEnvelope[] envelopes; if (mapCorners != null) { @@ -542,7 +542,7 @@ public class GridDerivation { } setBaseExtentClipped(envelopes); if (baseExtent != base.extent && baseExtent.equals(domain)) { - baseExtent = domain; // Share common instance. + baseExtent = domain; // Share common instance. } } subGridSetter = "subgrid"; 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 54810c7869..0d93ede5cf 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 @@ -609,8 +609,8 @@ public class GridGeometry implements LenientComparable, Serializable { * * <h4>Dimension order</h4> * The given envelope shall always declare dimensions in same order as the given extent. - * This constructor may reorder axes if {@code orientation} is (for example) {@link GridOrientation#DISPLAY}, - * but in such case this constructor will derive itself an envelope and a CRS with reordered axes. + * This constructor may reorder axes if {@code orientation} is {@link GridOrientation#DISPLAY}, + * but in such case this constructor will derive a new envelope and a new CRS from the given envelope. * * @param extent the valid extent of grid coordinates, or {@code null} if unknown. * @param envelope the envelope together with CRS of the "real world" coordinates, or {@code null} if unknown. @@ -618,6 +618,7 @@ public class GridGeometry implements LenientComparable, Serializable { * Ignored (can be null) if {@code envelope} is null. * @throws NullPointerException if {@code extent} and {@code envelope} arguments are both null, * or if {@code envelope} is non-null but {@code orientation} is null. + * @throws MismatchedDimensionException if the envelope does not have the same number of dimensions than the grid extent. * * @see <a href="https://en.wikipedia.org/wiki/Axis-aligned_object">Axis-aligned object on Wikipedia</a> * @@ -634,6 +635,9 @@ public class GridGeometry implements LenientComparable, Serializable { ImmutableEnvelope target = null; // May have different axis order than the specified `envelope` CRS. int[] sourceDimensions = null; // Indices in source envelope of axes colinear with the target envelope. if (envelope != null) { + if (extent != null) { + ArgumentChecks.ensureDimensionMatches("envelope", extent.getDimension(), envelope); + } ArgumentChecks.ensureNonNull("orientation", orientation); if (orientation.crsVariant != null) { final AbstractCRS sourceCRS = AbstractCRS.castOrCopy(envelope.getCoordinateReferenceSystem()); 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 1094a457d5..7735bd2750 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 @@ -241,7 +241,7 @@ public abstract class TiledGridCoverage extends GridCoverage { super(subset.domain, subset.ranges); final GridExtent extent = subset.domain.getExtent(); final int dimension = subset.virtualTileSize.length; - deferredTileReading = subset.deferredTileReading(); // May be shorter than other arrays of grid geometry. + deferredTileReading = subset.deferredTileReading(); // May be shorter than other arrays or the grid geometry. readExtent = subset.readExtent; subsampling = subset.subsampling; subsamplingOffsets = subset.subsamplingOffsets; @@ -449,12 +449,12 @@ public abstract class TiledGridCoverage extends GridCoverage { @Override public RenderedImage render(GridExtent sliceExtent) { final GridExtent available = gridGeometry.getExtent(); - final int dimension = virtualTileSize.length; // May be shorter than grid geometry dimension. + final int dimension = virtualTileSize.length; // May be shorter than the grid geometry dimension. if (sliceExtent == null) { sliceExtent = available; } else { final int sd = sliceExtent.getDimension(); - if (sd != dimension) { + if (sd < dimension || sd > available.getDimension()) { throw new MismatchedDimensionException(Errors.format( Errors.Keys.MismatchedDimension_3, "sliceExtent", dimension, sd)); } 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 1da0df045a..1cf8094061 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 @@ -506,17 +506,29 @@ check: if (dataType.isInteger()) { * such as creating the {@code SampleModel} subset for selected bands. */ public Subset(GridGeometry domain, final int[] range) throws DataStoreException { - List<SampleDimension> bands = getSampleDimensions(); - final RangeArgument rangeIndices = RangeArgument.validate(bands.size(), range, listeners); - final GridGeometry gridGeometry = getGridGeometry(); - sourceExtent = gridGeometry.getExtent(); + // Validate argument first, before more expensive computations. + List<SampleDimension> bands = getSampleDimensions(); + final RangeArgument rangeIndices = RangeArgument.validate(bands.size(), range, listeners); + /* + * Normally, the number of dimensions of `tileSize` should be equal to the number of dimensions + * of the grid geometry (determined by its `GridExtent`). However, we are tolerant to situation + * where the `TiledGridResource` is a two dimensional image associated to a 3-dimensional CRS. + * This is not recommended, but can happen with GeoTIFF for example. What to do with the extra + * dimension is unclear (the GeoTIFF specification itself said nothing), so we just ignore it. + */ final int[] tileSize = getTileSize(); + final int dimension = tileSize.length; // May be shorter than the grid geometry dimension. + GridGeometry gridGeometry = getGridGeometry(); + if ((domain == null || domain.getDimension() == dimension) && gridGeometry.getDimension() > dimension) { + gridGeometry = gridGeometry.selectDimensions(ArraysExt.range(0, dimension)); + } + sourceExtent = gridGeometry.getExtent(); boolean sharedCache = true; if (domain == null) { domain = gridGeometry; readExtent = sourceExtent; - subsamplingOffsets = new long[gridGeometry.getDimension()]; - subsampling = new long[subsamplingOffsets.length]; + subsamplingOffsets = new long[dimension]; + subsampling = new long[dimension]; Arrays.fill(subsampling, 1); } else { /* @@ -526,9 +538,9 @@ 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 var chunkSize = new int [tileSize.length]; - final var maxSubsmp = new long[tileSize.length]; - for (int i=0; i < tileSize.length; i++) { + final var chunkSize = new int [dimension]; + final var maxSubsmp = new long[dimension]; + for (int i=0; i < dimension; i++) { final int atomSize = getAtomSize(i); int span = tileSize[i]; if (span >= sourceExtent.getSize(i)) {
