You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2026/02/20 14:59:03 UTC
(sis) branch geoapi-4.0 updated (f2101a867d -> 4d1a8092ea)
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
from f2101a867d Rework the way that native libraries are loaded for avoiding to destroy the same library many times when the library was loaded from a filename. This is needed for avoiding a JVM crash if the `GDALStoreProvider(Path)` constructor was invoked many times with the same path.
new ebd3ddab59 Renamed `TiledGridResource` as `TiledGridCoverageResource`.
new 4d1a8092ea Initial implementation of the `TileMatrixSet` interface. Tests are still superficial for now.
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../sis/coverage/grid/GridCoverageBuilder.java | 7 +-
.../sis/image/internal/shared/ReshapedImage.java | 25 ++
.../sis/storage/geotiff/CompressedSubset.java | 6 +-
.../org/apache/sis/storage/geotiff/DataCube.java | 4 +-
.../org/apache/sis/storage/geotiff/DataSubset.java | 8 +-
.../apache/sis/storage/geotiff/GeoTiffStore.java | 2 +-
.../sis/storage/geotiff/ImageFileDirectory.java | 5 +-
.../org/apache/sis/storage/geotiff/Reader.java | 4 +-
.../apache/sis/storage/geotiff/package-info.java | 2 +-
.../org/apache/sis/storage/geotiff/ReaderTest.java | 125 ++++++
.../org/apache/sis/storage/geotiff/WriterTest.java | 1 +
.../org/apache/sis/storage/geotiff/untiled.tiff | Bin 0 -> 2284 bytes
.../sis/storage/netcdf/base/RasterResource.java | 2 +
.../apache/sis/storage/GridCoverageResource.java | 10 +-
.../apache/sis/storage/image/SingleImageStore.java | 2 +-
.../org/apache/sis/storage/internal/Resources.java | 5 +
.../sis/storage/internal/Resources.properties | 1 +
.../sis/storage/internal/Resources_fr.properties | 1 +
.../apache/sis/storage/tiling/ImagePyramid.java | 437 ++++++++++++++++++
.../apache/sis/storage/tiling/ImageTileMatrix.java | 492 +++++++++++++++++++++
.../apache/sis/storage/tiling/IterationDomain.java | 220 +++++++++
.../apache/sis/storage/tiling/TileMatrixSet.java | 12 +-
.../sis/storage/tiling/TiledGridCoverage.java | 88 ++--
...esource.java => TiledGridCoverageResource.java} | 201 ++++++++-
.../apache/sis/storage/tiling/TiledResource.java | 4 +-
.../main/org/apache/sis/io/TableAppender.java | 16 +-
.../main/org/apache/sis/io/package-info.java | 2 +-
.../main/org/apache/sis/math/DecimalFunctions.java | 27 +-
.../sis/util/internal/shared/AbstractMap.java | 2 +-
.../org/apache/sis/math/DecimalFunctionsTest.java | 32 +-
.../apache/sis/storage/geoheif/ImageResource.java | 10 +-
.../org/apache/sis/storage/gdal/TiledCoverage.java | 10 +-
.../org/apache/sis/storage/gdal/TiledResource.java | 4 +-
.../org/apache/sis/storage/gdal/GDALStoreTest.java | 3 +-
34 files changed, 1651 insertions(+), 119 deletions(-)
create mode 100644 endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/ReaderTest.java
create mode 100644 endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/untiled.tiff
create mode 100644 endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImagePyramid.java
create mode 100644 endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImageTileMatrix.java
create mode 100644 endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/IterationDomain.java
rename endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/{TiledGridResource.java => TiledGridCoverageResource.java} (82%)
(sis) 02/02: Initial implementation of the `TileMatrixSet` interface. Tests are still superficial for now.
Posted by de...@apache.org.
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 4d1a8092ea9198c46ab80d3c91a9e6dfdb550517
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Feb 14 23:48:14 2026 +0100
Initial implementation of the `TileMatrixSet` interface.
Tests are still superficial for now.
---
.../sis/coverage/grid/GridCoverageBuilder.java | 7 +-
.../sis/image/internal/shared/ReshapedImage.java | 25 ++
.../apache/sis/storage/geotiff/GeoTiffStore.java | 2 +-
.../sis/storage/geotiff/ImageFileDirectory.java | 5 +-
.../org/apache/sis/storage/geotiff/Reader.java | 4 +-
.../apache/sis/storage/geotiff/package-info.java | 2 +-
.../org/apache/sis/storage/geotiff/ReaderTest.java | 125 ++++++
.../org/apache/sis/storage/geotiff/WriterTest.java | 1 +
.../org/apache/sis/storage/geotiff/untiled.tiff | Bin 0 -> 2284 bytes
.../sis/storage/netcdf/base/RasterResource.java | 2 +
.../apache/sis/storage/GridCoverageResource.java | 10 +-
.../apache/sis/storage/image/SingleImageStore.java | 2 +-
.../org/apache/sis/storage/internal/Resources.java | 5 +
.../sis/storage/internal/Resources.properties | 1 +
.../sis/storage/internal/Resources_fr.properties | 1 +
.../apache/sis/storage/tiling/ImagePyramid.java | 437 ++++++++++++++++++
.../apache/sis/storage/tiling/ImageTileMatrix.java | 492 +++++++++++++++++++++
.../apache/sis/storage/tiling/IterationDomain.java | 220 +++++++++
.../apache/sis/storage/tiling/TileMatrixSet.java | 12 +-
.../sis/storage/tiling/TiledGridCoverage.java | 8 +-
.../storage/tiling/TiledGridCoverageResource.java | 177 +++++++-
.../apache/sis/storage/tiling/TiledResource.java | 4 +-
.../main/org/apache/sis/io/TableAppender.java | 16 +-
.../main/org/apache/sis/io/package-info.java | 2 +-
.../main/org/apache/sis/math/DecimalFunctions.java | 27 +-
.../sis/util/internal/shared/AbstractMap.java | 2 +-
.../org/apache/sis/math/DecimalFunctionsTest.java | 32 +-
.../org/apache/sis/storage/gdal/GDALStoreTest.java | 3 +-
28 files changed, 1578 insertions(+), 46 deletions(-)
diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageBuilder.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageBuilder.java
index b99bb4ce9d..be3de3d5bd 100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageBuilder.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverageBuilder.java
@@ -76,9 +76,12 @@ import org.apache.sis.util.resources.Errors;
* }
* }
* var builder = new GridCoverageBuilder();
- * builder.setValues(data).flixAxis(1);
+ * builder.setValues(data).flipGridAxis(1);
*
- * Envelope domain = ...; // Specify here the "real world" coordinates.
+ * // Real world coordinates, around Tokyo in this example.
+ * var domain = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
+ * domain.setRange(1, 32, 40); // Range of latitude values.
+ * domain.setRange(0, 137, 140); // Range of longitude values.
* return builder.setDomain(domain).build();
* }
* }
diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ReshapedImage.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ReshapedImage.java
index ee40136e8f..7db4d35660 100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ReshapedImage.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/internal/shared/ReshapedImage.java
@@ -28,6 +28,7 @@ import static java.lang.Math.min;
import static java.lang.Math.max;
import static java.lang.Math.addExact;
import static java.lang.Math.subtractExact;
+import static java.lang.Math.multiplyFull;
import static java.lang.Math.floorDiv;
import static java.lang.Math.toIntExact;
import org.apache.sis.image.PlanarImage;
@@ -107,6 +108,30 @@ public final class ReshapedImage extends PlanarImage {
this.minTileY = minTileY;
}
+ /**
+ * Returns an image for a single tile.
+ * The indices of the unique tile are set to (0,0).
+ * The coordinate of the image upper-left corner is set to (0,0).
+ *
+ * @param source the image for which to wrap a single tile.
+ * @param tileX column index of the tile.
+ * @param tileY row index of the tile.
+ * @return the single tile. May be the given source or one of its sources.
+ * @throws ArithmeticException if image indices overflow 32-bits integer capacity.
+ */
+ public static RenderedImage singleTile(final RenderedImage source, final int tileX, final int tileY) {
+ final int tileWidth = source.getTileWidth();
+ final int tileHeight = source.getTileHeight();
+ final var image = new ReshapedImage(source,
+ -(multiplyFull(tileX, tileWidth) + source.getTileGridXOffset()), // This negate cannot overflow.
+ -(multiplyFull(tileY, tileHeight) + source.getTileGridYOffset()),
+ 0, 0,
+ tileWidth,
+ tileHeight,
+ 0, 0);
+ return image.isIdentity() ? image.source : image;
+ }
+
/**
* Returns an image with the data of the given image translated by the given amount.
* The number of tiles and their indexes are unchanged.
diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java
index f806703e7d..dffd88becf 100644
--- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java
+++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/GeoTiffStore.java
@@ -82,7 +82,7 @@ import org.apache.sis.util.resources.Errors;
* @author Martin Desruisseaux (Geomatys)
* @author Thi Phuong Hao Nguyen (VNSC)
* @author Alexis Manin (Geomatys)
- * @version 1.5
+ * @version 1.7
* @since 0.8
*/
public class GeoTiffStore extends DataStore implements Aggregate {
diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
index 25570a7b9c..990a2d49be 100644
--- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
+++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
@@ -1540,7 +1540,10 @@ final class ImageFileDirectory extends DataCube {
domain = new GridGeometry(new GridExtent(imageWidth, imageHeight), null, null);
}
final CoverageModifier.Source source = source();
- gridGeometry = (source != null) ? reader.store.customizer.customize(source, domain) : domain;
+ if (source != null) {
+ domain = reader.store.customizer.customize(source, domain);
+ }
+ gridGeometry = domain;
}
return domain;
}
diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Reader.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Reader.java
index 164cfb6c56..0eaeffe620 100644
--- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Reader.java
+++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Reader.java
@@ -419,7 +419,7 @@ final class Reader extends IOBase {
lastIFD = null; // Clear now in case of error.
imageIndex++; // In case next image is full-resolution.
ImageFileDirectory image;
- final List<ImageFileDirectory> overviews = new ArrayList<>();
+ final var overviews = new ArrayList<ImageFileDirectory>();
while ((image = readNextIFD(imageIndex)) != null) {
if (image.isReducedResolution()) {
overviews.add(image);
@@ -430,7 +430,7 @@ final class Reader extends IOBase {
}
/*
* All pyramid levels have been read. If there is only one level,
- * use the image directly. Otherwise create the pyramid.
+ * use the image directly. Otherwise, create the pyramid.
*/
if (overviews.isEmpty()) {
images.add(fullResolution);
diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/package-info.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/package-info.java
index 603b212edc..76accbbda4 100644
--- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/package-info.java
+++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/package-info.java
@@ -33,7 +33,7 @@
* @author Thi Phuong Hao Nguyen (VNSC)
* @author Minh Chinh Vu (VNSC)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.6
+ * @version 1.7
* @since 0.8
*/
package org.apache.sis.storage.geotiff;
diff --git a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/ReaderTest.java b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/ReaderTest.java
new file mode 100644
index 0000000000..360d928fa8
--- /dev/null
+++ b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/ReaderTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage.geotiff;
+
+import java.util.SortedMap;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import org.opengis.util.GenericName;
+import org.opengis.referencing.crs.ProjectedCRS;
+import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.NoSuchDataException;
+import org.apache.sis.storage.tiling.Tile;
+import org.apache.sis.storage.tiling.TileMatrix;
+import org.apache.sis.storage.tiling.TileMatrixSet;
+import org.apache.sis.storage.tiling.TileStatus;
+import org.apache.sis.storage.tiling.TiledResource;
+
+// Test dependencies
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.apache.sis.test.Assertions.assertSingleton;
+import static org.apache.sis.test.Assertions.assertMessageContains;
+import org.apache.sis.test.TestCase;
+
+
+/**
+ * Tests a few read operations.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ *
+ * @todo We should rewrite {@code "untiled.tiff"} as a tiled image.
+ */
+@SuppressWarnings("exports")
+public class ReaderTest extends TestCase {
+ /**
+ * Name of the test file.
+ */
+ private static final String FILENAME = "untiled.tiff";
+
+ /**
+ * Creates a new test case.
+ */
+ public ReaderTest() {
+ }
+
+ /**
+ * Creates a new data store for the test file.
+ *
+ * @return the data store to test.
+ * @throws DataStoreException if an error occurred while creating the data store.
+ */
+ private static GeoTiffStore createStore() throws DataStoreException {
+ return new GeoTiffStore(null, new StorageConnector(ReaderTest.class.getResource(FILENAME)));
+ }
+
+ /**
+ * Returns the single raster of the rendered image of the given tile.
+ */
+ private static Raster raster(final Tile tile) throws DataStoreException {
+ RenderedImage image = assertInstanceOf(GridCoverageResource.class, tile.getResource()).read(null, null).render(null);
+ assertEquals(1, image.getNumXTiles());
+ assertEquals(1, image.getNumYTiles());
+ return image.getTile(image.getMinTileX(), image.getMinTileY());
+ }
+
+ /**
+ * Tests the tile matrix set.
+ *
+ * @todo Need to be updated if we rewrite {@code "untiled.tiff"} as a more interesting image.
+ *
+ * @throws DataStoreException if an error occurred while creating the data store.
+ */
+ @Test
+ public void testTileMatrixSet() throws DataStoreException {
+ try (GeoTiffStore ds = createStore()) {
+ final GridCoverageResource resource = assertInstanceOf(GridCoverageResource.class, assertSingleton(ds.components()));
+ assertInstanceOf(ProjectedCRS.class, resource.getGridGeometry().getCoordinateReferenceSystem());
+
+ final TileMatrixSet pyramid = assertSingleton(assertInstanceOf(TiledResource.class, resource).getTileMatrixSets());
+ assertSame(resource.getGridGeometry().getCoordinateReferenceSystem(), pyramid.getCoordinateReferenceSystem());
+ assertEquals("untiled:1:TMS", pyramid.getIdentifier().toString());
+ assertFalse(pyramid.getEnvelope().isEmpty());
+
+ final SortedMap<GenericName, ? extends TileMatrix> matrices = pyramid.getTileMatrices();
+ assertFalse(matrices.isEmpty());
+ assertEquals(1, matrices.size());
+ assertFalse(matrices.isEmpty()); // Test again because code path changed.
+ assertTrue(matrices.subMap(matrices.firstKey(), matrices.lastKey()).isEmpty());
+
+ final TileMatrix matrix = assertSingleton(matrices.values());
+ assertEquals("untiled:1:TMS:L0", matrix.getIdentifier().toString());
+ assertEquals(assertSingleton(matrices.keySet()), matrix.getIdentifier());
+ assertArrayEquals(resource.getGridGeometry().getResolution(false), matrix.getResolution());
+ assertSame(TileStatus.OUTSIDE_EXTENT, matrix.getTileStatus(1, 0));
+ assertSame(TileStatus.OUTSIDE_EXTENT, matrix.getTileStatus(0, 1));
+ assertSame(TileStatus.UNKNOWN, matrix.getTileStatus(0, 0)); // Because the tile has not yet been loaded.
+
+ assertMessageContains(assertThrows(NoSuchDataException.class, () -> matrix.getTile(1, 0)));
+ final Tile tile = matrix.getTile(0, 0).orElseThrow();
+ assertArrayEquals(new long[] {0, 0}, tile.getIndices());
+ assertEquals(TileStatus.EXISTS, tile.getStatus());
+ assertTrue(tile.getContentPath().isEmpty());
+
+ final Raster raster = raster(tile);
+ assertArrayEquals(tile.getIndices(), assertSingleton(matrix.getTiles(null, false).toList()).getIndices());
+ assertSame(raster, raster(assertSingleton(matrix.getTiles(null, false).toList())));
+ }
+ }
+}
diff --git a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/WriterTest.java b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/WriterTest.java
index edb7c694d0..71f318e57c 100644
--- a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/WriterTest.java
+++ b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/WriterTest.java
@@ -61,6 +61,7 @@ import org.apache.sis.referencing.operation.HardCodedConversions;
* @author Erwan Roussel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class WriterTest extends TestCase {
/**
* Arbitrary size (in pixels) of tiles in the image to test. The TIFF specification restricts those sizes
diff --git a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/untiled.tiff b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/untiled.tiff
new file mode 100644
index 0000000000..96cf0ae509
Binary files /dev/null and b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/untiled.tiff differ
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 3e02b53b18..ee90326291 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
@@ -775,6 +775,8 @@ public final class RasterResource extends AbstractGridCoverageResource implement
/**
* Returns a string representation of this resource for debugging purposes.
+ *
+ * @return a string representation of this resource for debugging purposes.
*/
@Override
public String toString() {
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/GridCoverageResource.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/GridCoverageResource.java
index 6c10e81d73..eec1a421cb 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/GridCoverageResource.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/GridCoverageResource.java
@@ -131,13 +131,13 @@ public interface GridCoverageResource extends DataSet {
/**
* Returns the preferred resolutions (in units of CRS axes) for read operations in this data store.
* If the storage supports pyramid, then the list should contain the resolution at each pyramid level
- * ordered from finest (smallest numbers) to coarsest (largest numbers) resolution.
- * Otherwise the list contains a single element which is the {@linkplain #getGridGeometry() grid geometry}
+ * ordered from finest (smallest numerical values) to coarsest (largest numerical values) resolution.
+ * Otherwise, the list contains a single element which is the {@linkplain #getGridGeometry() grid geometry}
* resolution, or an empty list if no resolution is applicable to the coverage (e.g. because non-constant).
*
- * <p>Each element shall be an array with a length equals to the number of CRS dimensions.
- * In each array, value at index <var>i</var> is the cell size along CRS dimension <var>i</var>
- * in units of the CRS axis <var>i</var>.</p>
+ * <p>Each element shall be an array with a length equals to the number of <abbr>CRS</abbr> dimensions.
+ * In each array, value at index <var>i</var> is the cell size along <abbr>CRS</abbr> dimension <var>i</var>
+ * in units of the <abbr>CRS</abbr> axis <var>i</var>.</p>
*
* <p>Note that arguments given to {@link #subset(CoverageQuery) subset(…)} or {@link #read read(…)} methods
* are <em>not</em> constrained to the resolutions returned by this method. Those resolutions are only hints
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/SingleImageStore.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/SingleImageStore.java
index b27693f864..db400d1b36 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/SingleImageStore.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/SingleImageStore.java
@@ -34,7 +34,7 @@ import org.apache.sis.util.collection.BackingStoreException;
/**
* A world file store which is expected to contain exactly one image.
* This class is used for image formats that are restricted to one image per file.
- * Examples: PNG and BMP image formats.
+ * Examples: <abbr>PNG</abbr> and <abbr>BMP</abbr> image formats.
*
* <p>See {@link WritableSingleImageStore} for the writable variant of this class.</p>
*
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.java
index 15b79f539f..4f451dffab 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.java
@@ -453,6 +453,11 @@ public class Resources extends IndexedResourceBundle {
*/
public static final short SubsetQuery_1 = 77;
+ /**
+ * Tile indexes out of bounds.
+ */
+ public static final short TileIndexesOutOfBounds = 87;
+
/**
* Cannot open {0} data store without “{1}” parameter.
*/
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.properties b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.properties
index 9c688036fb..8d78743ee0 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.properties
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.properties
@@ -98,6 +98,7 @@ StreamIsNotWritable_1 = Stream \u201c{0}\u201d is not writable.
StreamIsReadOnce_1 = The \u201c{0}\u201d data store can be read only once.
StreamIsWriteOnce_1 = Cannot modify previously written data in \u201c{0}\u201d.
SubsetQuery_1 = Query a subset of \u201c{0}\u201d.
+TileIndexesOutOfBounds = Tile indexes out of bounds.
UndefinedParameter_2 = Cannot open {0} data store without \u201c{1}\u201d parameter.
UnexpectedNumberOfCoordinates_4 = The \u201c{0}\u201d feature at {1} has a {3} coordinate values, while we expected a multiple of {2}.
UnexpectedSliceResolution_3 = Expected a resolution of {0} but got {1} for the slice or tile \u201c{2}\u201d.
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources_fr.properties b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources_fr.properties
index 8d63ff1001..187097a201 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources_fr.properties
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources_fr.properties
@@ -103,6 +103,7 @@ StreamIsNotWritable_1 = Le flux de donn\u00e9es \u00ab\u202f{0}\u202
StreamIsReadOnce_1 = Les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb ne peuvent \u00eatre lues qu\u2019une seule fois.
StreamIsWriteOnce_1 = Ne peut pas revenir sur les donn\u00e9es d\u00e9j\u00e0 \u00e9crites dans \u00ab\u202f{0}\u202f\u00bb.
SubsetQuery_1 = Requ\u00eate d\u2019un sous-ensemble de \u00ab\u202f{0}\u202f\u00bb.
+TileIndexesOutOfBounds = Les indexes de la tuile sont en dehors des limites permises.
UndefinedParameter_2 = Ne peut pas ouvrir une source de donn\u00e9es {0} sans le param\u00e8tre \u00ab\u202f{1}\u202f\u00bb.
UnexpectedNumberOfCoordinates_4 = L\u2019entit\u00e9 nomm\u00e9e \u00ab\u202f{0}\u202f\u00bb \u00e0 {1} contient {3} coordonn\u00e9es, alors qu\u2019on attendait un multiple de {2}.
UnexpectedSliceResolution_3 = Une r\u00e9solution de {0} \u00e9tait attendue mais une r\u00e9solution de {1} a \u00e9t\u00e9 trouv\u00e9e dans la tranche ou tuile \u00ab\u202f{2}\u202f\u00bb.
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImagePyramid.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImagePyramid.java
new file mode 100644
index 0000000000..cd1ca756c2
--- /dev/null
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImagePyramid.java
@@ -0,0 +1,437 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage.tiling;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.SortedMap;
+import org.opengis.util.LocalName;
+import org.opengis.util.GenericName;
+import org.opengis.geometry.Envelope;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.coverage.grid.GridCoverageProcessor;
+import org.apache.sis.coverage.grid.IncompleteGridGeometryException;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.geometry.ImmutableEnvelope;
+import org.apache.sis.measure.NumberRange;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.base.StoreUtilities;
+import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.util.iso.Names;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.internal.shared.AbstractMap;
+import org.apache.sis.util.internal.shared.Strings;
+
+
+/**
+ * Default implementation of {@code TileMatrixSet} as a wrapper for {@code GridCoverage} instances.
+ * This is a pyramid of two-dimensional slices represented as {@link java.awt.image.RenderedImage}s.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+final class ImagePyramid extends AbstractMap<GenericName, ImageTileMatrix>
+ implements TileMatrixSet, SortedMap<GenericName, ImageTileMatrix>
+{
+ /**
+ * An alphanumeric identifier unique in the {@code TiledResource} that contains this {@code TileMatrixSet}.
+ * A tiled resource may contain more than one tile matrix set if the resource provides different set of tiles
+ * for different <abbr>CRS</abbr>.
+ *
+ * @see #getIdentifier()
+ */
+ private final GenericName identifier;
+
+ /**
+ * The provider of pyramid levels.
+ */
+ private final TiledGridCoverageResource.Pyramid provider;
+
+ /**
+ * The tile matrices at each level, ordered from coarser resolution to most detailed resolution.
+ * This list is initially empty. Pyramid levels are created when first requested.
+ */
+ private final List<ImageTileMatrix> matrices;
+
+ /**
+ * First valid index value (inclusive) in {@link #matrices}.
+ */
+ private final int lowerMatrixIndex;
+
+ /**
+ * First index value which is known to be invalid.
+ * This is the lowest value for which {@link #getOrCreateLevel(int)} returned {@code null}.
+ * This value may be higher than the true number of levels if we didn't tested all values.
+ */
+ private int upperMatrixIndex;
+
+ /**
+ * The union of the envelopes of all tile matrix sets, or {@code null} if not yet computed.
+ *
+ * @see #getEnvelope()
+ */
+ private Envelope envelope;
+
+ /**
+ * The grid coverage processor to use when tiles use a subset of the bands.
+ *
+ * @see #createResourceView(long[], RenderedImage)
+ */
+ private final GridCoverageProcessor processor;
+
+ /**
+ * Creates a new tile matrix set.
+ *
+ * @param parent identifier of the {@code TiledResource} that contains this {@code TileMatrixSet}.
+ * @param provider information about the tile matrices to create, and provider of pyramid levels.
+ * @param processor the grid coverage processor to use when tiles use a subset of the bands.
+ */
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ ImagePyramid(final GenericName parent,
+ final TiledGridCoverageResource.Pyramid provider,
+ final GridCoverageProcessor processor)
+ {
+ this.provider = provider;
+ this.processor = processor;
+ identifier = Names.createScopedName(parent, null, provider.identifier());
+ matrices = new ArrayList<>();
+ lowerMatrixIndex = 0;
+ upperMatrixIndex = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Creates a new tile matrix set as a subset of the given one.
+ */
+ private ImagePyramid(final ImagePyramid parent, final int lowerMatrixIndex, final int upperMatrixIndex) {
+ this.identifier = parent.identifier;
+ this.provider = parent.provider;
+ this.processor = parent.processor;
+ this.matrices = parent.matrices;
+ this.lowerMatrixIndex = lowerMatrixIndex;
+ this.upperMatrixIndex = upperMatrixIndex;
+ }
+
+ /**
+ * Returns the tile matrix at the given index.
+ * If that tile matrix was not already created, it is created now.
+ * This method shall be invoked with a synchronization lock on {@link #matrices}.
+ *
+ * @param level the level for which to get the tile matrix.
+ * @return tile matrix at the given level, or {@code null} if the given level is too high.
+ * @throws BackingStoreException if an error occurred while fetching information
+ */
+ private ImageTileMatrix getOrCreateLevel(final int level) {
+ if (level < 0 || level >= upperMatrixIndex) {
+ return null;
+ }
+ if (level < matrices.size()) {
+ final ImageTileMatrix tm = matrices.get(level);
+ if (tm != null) {
+ return tm;
+ }
+ }
+ final ImageTileMatrix tm;
+ try {
+ final TiledGridCoverageResource resource = provider.forPyramidLevel(level);
+ if (resource == null) {
+ upperMatrixIndex = level;
+ return null;
+ }
+ GenericName id = Names.createScopedName(identifier, null, provider.identifierOfLevel(level));
+ tm = new ImageTileMatrix(id, resource, processor);
+ } catch (DataStoreException | TransformException e) {
+ throw new BackingStoreException(e);
+ }
+ while (matrices.size() <= level) {
+ matrices.add(null);
+ }
+ matrices.set(level, tm);
+ return tm;
+ }
+
+ /**
+ * Returns the index of the pyramid name for the given identifier, or -1 if none.
+ * This method shall be invoked with a synchronization lock on {@link #matrices}.
+ * The returned value is an index in the {@link #matrices} list.
+ *
+ * @param name identifier of the desired level.
+ * @param required whether to thrown an exception if the identifier is not recognized.
+ * @return index of the desired level, or -1 if none and {@code required} is {@code false}.
+ * @throws IllegalArgumentException if the given name is not recognized and {@code required} is {@code true}.
+ */
+ private int indexOf(final GenericName name, final boolean required) {
+ final LocalName tip = name.tip();
+ if (tip.scope().name().equals(identifier)) {
+ final int level;
+ try {
+ level = provider.levelOfIdentifier(tip.toString());
+ } catch (IllegalArgumentException e) {
+ if (required) throw e;
+ Logging.ignorableException(StoreUtilities.LOGGER, ImagePyramid.class, "indexOf", e);
+ return -1;
+ }
+ if (level >= lowerMatrixIndex && level < upperMatrixIndex) {
+ if (!required || getOrCreateLevel(level) != null) {
+ return level;
+ }
+ }
+ }
+ if (required) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.NoSuchValue_1, name));
+ }
+ return -1;
+ }
+
+ /**
+ * Compares two generic names for order based on the resolution of the associated tile matrix.
+ * If a call to {@code compare(o1, o2)}, the comparator returns a positive number of {@code o1}
+ * is the identifier of a tile matrix having a finer resolution than {@code o2}.
+ */
+ @Override
+ public Comparator<GenericName> comparator() {
+ return (GenericName o1, GenericName o2) -> indexOf(o1, false) - indexOf(o2, false);
+ }
+
+ /**
+ * Returns whether this Tile Matrix Set has no elements.
+ * Empty tile matrix sets should not be returned to users.
+ */
+ @Override
+ public boolean isEmpty() {
+ synchronized (matrices) {
+ if (upperMatrixIndex <= lowerMatrixIndex) {
+ return true;
+ }
+ if (!matrices.isEmpty()) {
+ return false;
+ }
+ return getOrCreateLevel(lowerMatrixIndex) == null;
+ }
+ }
+
+ /**
+ * Returns the number of elements in this tile matrix set.
+ *
+ * <b>Note:</b> if this implementation is modified, revisit {@link #lastKey()}.
+ */
+ @Override
+ public int size() {
+ synchronized (matrices) {
+ int level;
+ while ((level = matrices.size()) < upperMatrixIndex) {
+ getOrCreateLevel(level); // Force fetching.
+ }
+ return upperMatrixIndex - lowerMatrixIndex;
+ }
+ }
+
+ /**
+ * Returns a unique (within {@link TiledResource}) identifier.
+ */
+ @Override
+ public GenericName getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Returns the coordinate reference system of all {@code TileMatrix} instances in this set.
+ *
+ * @throws IncompleteGridGeometryException if the tile matrices have no <abbr>CRS</abbr>.
+ */
+ @Override
+ public CoordinateReferenceSystem getCoordinateReferenceSystem() {
+ final ImageTileMatrix tm;
+ synchronized (matrices) {
+ tm = getOrCreateLevel(0); // Really 0, not `lowerMatrixIndex`.
+ }
+ return tm.getTilingScheme().getCoordinateReferenceSystem();
+ }
+
+ /**
+ * Returns an envelope that encompasses all {@code TileMatrix} instances in this set.
+ *
+ * @throws IncompleteGridGeometryException if a tiling scheme has no envelope. While not strictly mandatory,
+ * for now we consider that missing extent or missing "grid to CRS" transform is probably an error.
+ */
+ @Override
+ public Optional<Envelope> getEnvelope() {
+ synchronized (matrices) {
+ if (envelope == null) {
+ int i = lowerMatrixIndex;
+ Envelope mayReuse = getOrCreateLevel(i).getTilingScheme().getEnvelope();
+ final var union = new GeneralEnvelope(mayReuse);
+ ImageTileMatrix tm;
+ while ((tm = getOrCreateLevel(++i)) != null) {
+ final Envelope e = tm.getTilingScheme().getEnvelope();
+ union.add(e);
+ if (union.equals(e, 0, false)) {
+ mayReuse = e;
+ }
+ }
+ envelope = (union == null || union.equals(mayReuse, 0, false)) ? mayReuse : new ImmutableEnvelope(union);
+ }
+ return Optional.of(envelope);
+ }
+ }
+
+ /**
+ * Returns all {@code TileMatrix} instances in this set, together with their identifiers.
+ * Entries are sorted from coarser resolution to most detailed resolution.
+ */
+ @Override
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public SortedMap<GenericName, ? extends TileMatrix> getTileMatrices() {
+ return this;
+ }
+
+ /**
+ * Returns an iterator over the entries in this map.
+ */
+ @Override
+ protected EntryIterator<GenericName, ImageTileMatrix> entryIterator() {
+ return new EntryIterator<GenericName, ImageTileMatrix>() {
+ /** Index of the next element to return. */
+ private int index;
+
+ /** Next element to return. */
+ private ImageTileMatrix next;
+
+ @Override protected GenericName getKey() {return next.getIdentifier();}
+ @Override protected ImageTileMatrix getValue() {return next;}
+ @Override protected boolean next() {
+ synchronized (matrices) {
+ return (next = getOrCreateLevel(index++)) != null;
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns the tile matrix for the given identifier.
+ *
+ * @param key tile matrix identifier.
+ * @return tile matrix for the given identifier, or {@code null} if none.
+ */
+ @Override
+ public ImageTileMatrix get(final Object key) {
+ if (key instanceof GenericName) {
+ synchronized (matrices) {
+ return getOrCreateLevel(indexOf((GenericName) key, false));
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the first key of the map.
+ */
+ @Override
+ public GenericName firstKey() {
+ final ImageTileMatrix tm;
+ synchronized (matrices) {
+ tm = getOrCreateLevel(lowerMatrixIndex);
+ }
+ if (tm != null) {
+ return tm.getIdentifier();
+ }
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * Returns the last key of the map.
+ */
+ @Override
+ public GenericName lastKey() {
+ final ImageTileMatrix tm;
+ synchronized (matrices) {
+ final int size = size(); // Implementation of `size()` fills the list.
+ tm = (size != 0) ? matrices.get(size - 1) : null;
+ }
+ if (tm != null) {
+ return tm.getIdentifier();
+ }
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * Returns a view of this map whose keys are strictly less than {@code toKey}.
+ *
+ * @param toKey high endpoint (exclusive) of the keys in the returned map.
+ */
+ @Override
+ public SortedMap<GenericName, ImageTileMatrix> headMap(GenericName toKey) {
+ return subMap(lowerMatrixIndex, indexOf(toKey, true));
+ }
+
+ /**
+ * Returns a view of this map whose keys are greater than or equal to {@code fromKey}.
+ *
+ * @param fromKey low endpoint (inclusive) of the keys in the returned map.
+ */
+ @Override
+ public SortedMap<GenericName, ImageTileMatrix> tailMap(GenericName fromKey) {
+ return subMap(indexOf(fromKey, true), upperMatrixIndex);
+ }
+
+ /**
+ * Returns a view this map whose keys range from {@code fromKey}, inclusive, to {@code toKey}, exclusive.
+ *
+ * @param fromKey low endpoint (inclusive) of the keys in the returned map.
+ * @param toKey high endpoint (exclusive) of the keys in the returned map.
+ */
+ @Override
+ public SortedMap<GenericName, ImageTileMatrix> subMap(GenericName fromKey, GenericName toKey) {
+ return subMap(indexOf(fromKey, true), indexOf(toKey, true));
+ }
+
+ /**
+ * Returns a view this map whose keys range from {@code fromKey}, inclusive, to {@code toKey}, exclusive.
+ *
+ * @param fromKey low endpoint (inclusive) of the keys in the returned map.
+ * @param toKey high endpoint (exclusive) of the keys in the returned map.
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ private SortedMap<GenericName, ImageTileMatrix> subMap(final int fromKey, final int toKey) {
+ if (fromKey >= toKey) {
+ return Collections.emptySortedMap();
+ }
+ if (fromKey == lowerMatrixIndex && toKey == upperMatrixIndex) {
+ return this;
+ }
+ return new ImagePyramid(this, fromKey, toKey);
+ }
+
+ /**
+ * Returns a string representation for debugging purposes.
+ *
+ * @return a string representation for debugging purposes.
+ */
+ @Override
+ public String toString() {
+ final Object upper;
+ synchronized (matrices) {
+ final Integer max = (upperMatrixIndex == Integer.MAX_VALUE) ? null : upperMatrixIndex;
+ upper = (matrices.size() >= upperMatrixIndex) ? max : new NumberRange<>(Integer.class, lowerMatrixIndex, true, max, true);
+ }
+ return Strings.toString(getClass(), null, identifier.toString(), "lowerMatrixIndex", lowerMatrixIndex, "upperMatrixIndex", upper);
+ }
+}
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImageTileMatrix.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImageTileMatrix.java
new file mode 100644
index 0000000000..51feda835d
--- /dev/null
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/ImageTileMatrix.java
@@ -0,0 +1,492 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage.tiling;
+
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import java.awt.image.RenderedImage;
+import java.nio.file.Path;
+import org.opengis.util.GenericName;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.referencing.operation.matrix.Matrices;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.storage.MemoryGridCoverageResource;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.NoSuchDataException;
+import org.apache.sis.storage.UnsupportedQueryException;
+import org.apache.sis.storage.InternalDataStoreException;
+import org.apache.sis.storage.Resource;
+import org.apache.sis.storage.base.StoreUtilities;
+import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.coverage.grid.GridCoverage2D;
+import org.apache.sis.coverage.grid.GridCoverageProcessor;
+import org.apache.sis.image.internal.shared.ReshapedImage;
+import org.apache.sis.pending.jdk.JDK18;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.iso.Names;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.math.DecimalFunctions;
+import org.apache.sis.storage.internal.Resources;
+
+
+/**
+ * Default implementation of {@code TileMatrix} as a wrapper for a {@code GridCoverage}.
+ * The tile size must be specified at construction time and must be equal to the size of
+ * the tiles of the rendered image.
+ *
+ * <p>This class is needed only when the application needs details about the tiling scheme,
+ * for example in order to implement a Web Map Tile Service (<abbr>WMTS</abbr>).</p>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+final class ImageTileMatrix implements TileMatrix {
+ /**
+ * An alphanumeric identifier which is unique in the {@code TileMatrixSet} that contains this {@code TileMatrix}.
+ * The identifier contains the zoom level as a number encoded in <abbr>ASCII</abbr>.
+ */
+ private final GenericName identifier;
+
+ /**
+ * The resource for reading the tiles of this tile matrix.
+ */
+ private final TiledGridCoverageResource resource;
+
+ /**
+ * The coverage from which to get the rendered image which contains the tiles.
+ * This coverage uses deferred reading of tiles. Created when first requested.
+ *
+ * @see #coverage()
+ */
+ private TiledGridCoverage coverage;
+
+ /**
+ * Pattern for formatting tile indices using {@link java.util.Formatter}.
+ * The number of digits and the presence of a sign depend on the {@linkplain #tilingScheme tiling scheme}.
+ *
+ * @see #getTileIdentifier(long[])
+ */
+ private final String tileIndicesPattern;
+
+ /**
+ * Extent of valid tile indices and their relationship with "real world" coordinates.
+ * The (0, 0) tile indices should map to the tile in the upper-left corner. The last
+ * row and last column of tiles may contain partial tiles if the coverage size is not
+ * a divisor of the tile size.
+ */
+ private final GridGeometry tilingScheme;
+
+ /**
+ * Size of tiles, in number of {@linkplain #coverage} cells.
+ */
+ private final int[] tileSize;
+
+ /**
+ * Values to add to tile coordinates, after multiplication by {@link #tileSize}, for getting cell coordinates.
+ *
+ * @see #tileToCell(long, int)
+ */
+ private final long[] tileToCell;
+
+ /**
+ * Values to add to tile coordinates in {@link #image} for getting a tile coordinates in {@link #tilingScheme}.
+ * Those values are updated when a new image is rendered.
+ */
+ private long imageToTileX, imageToTileY;
+
+ /**
+ * The image containing the tiles of the tile matrix. Computed when first needed, and may be
+ * recomputed multiple times with different offsets if the tile indices are larger than the
+ * capacity of 32-bits integers.
+ */
+ private RenderedImage image;
+
+ /**
+ * The grid coverage processor to use when tiles use a subset of the bands.
+ *
+ * @see #createResourceView(long[], RenderedImage)
+ */
+ private final GridCoverageProcessor processor;
+
+ /**
+ * Creates a new tile matrix for the given coverage.
+ *
+ * @param identifier identifier unique in the {@code TileMatrixSet} that contains this {@code TileMatrix}.
+ * @param resource the resource for reading the tiles of this tile matrix.
+ * @param processor the grid coverage processor to use when tiles use a subset of the bands.
+ * @throws TransformException if the "tile indices to CRS" transform cannot be computed.
+ */
+ ImageTileMatrix(final GenericName identifier,
+ final TiledGridCoverageResource resource,
+ final GridCoverageProcessor processor)
+ throws DataStoreException, TransformException
+ {
+ this.identifier = identifier;
+ this.processor = processor;
+ this.resource = resource;
+ this.tileSize = resource.getTileSize();
+ final GridGeometry cellGrid = resource.getGridGeometry();
+ final GridExtent extent = cellGrid.getExtent();
+ final int dimension = extent.getDimension();
+ final long[] tileCount = new long[dimension];
+ final MatrixSIS toCells = Matrices.createIdentity(dimension + 1);
+ this.tileToCell = new long[dimension];
+ final var pattern = new StringBuilder(6 * dimension);
+ for (int i=0; i<dimension; i++) {
+ final long offset = extent.getLow(i);
+ final int scale = tileSize[i];
+ toCells.setNumber(i, i, scale);
+ toCells.setNumber(i, dimension, offset);
+ tileToCell[i] = offset;
+ tileCount [i] = JDK18.ceilDiv(extent.getSize(i), scale);
+ /*
+ * Prepare a pattern for formatting the tile indices.
+ * Indices are formatted with fixed number of digits,
+ * using the minimum number needed for the largest index.
+ */
+ if (i != 0) pattern.append(',');
+ pattern.append("%0").append(DecimalFunctions.floorLog10(Math.max(tileCount[i] - 1, 1)) + 1).append('d');
+ }
+ tilingScheme = new GridGeometry(cellGrid, extent.reshape(null, tileCount, false), MathTransforms.linear(toCells));
+ tileIndicesPattern = pattern.toString();
+ }
+
+ /**
+ * Returns an identifier which is unique in the {@code TileMatrixSet} that contains this {@code TileMatrix}.
+ * The identifier contains the zoom level as a number encoded in <abbr>ASCII</abbr>.
+ */
+ @Override
+ public GenericName getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Returns the resolution (in units of CRS axes) at which tiles in this matrix should be used.
+ * The array length is the number of <abbr>CRS</abbr> dimensions, and value at index <var>i</var>
+ * is the resolution along CRS dimension <var>i</var> in units of the CRS axis <var>i</var>.
+ */
+ @Override
+ public double[] getResolution() {
+ try {
+ return resource.getGridGeometry().getResolution(false);
+ } catch (DataStoreException e) {
+ throw new BackingStoreException(e);
+ }
+ }
+
+ /**
+ * Returns a description about how space is partitioned into individual tiled units.
+ * The description contains the extent of valid tile indices, the spatial reference system,
+ * and the conversion from tile indices to the spatial reference system coordinates.
+ *
+ * @return extent of valid tile indices and their relationship with "real world" coordinates.
+ */
+ @Override
+ public GridGeometry getTilingScheme() {
+ return tilingScheme;
+ }
+
+ /**
+ * Returns the coverage, which is read when first needed.
+ * This coverage uses deferred reading of tiles.
+ *
+ * @return the coverage from which to get the rendered image which contains the tiles.
+ * @throws DataStoreException if an error occurred during the construction of the coverage.
+ */
+ private synchronized TiledGridCoverage coverage() throws DataStoreException {
+ if (coverage == null) {
+ coverage = resource.readAtGetTileTime();
+ }
+ return coverage;
+ }
+
+ /**
+ * Fetches information about whether a tile exists, is missing or failed to load.
+ *
+ * @param indices indices of the requested tile (may be outside the tile matrix extent).
+ * @return information about the availability of the specified tile.
+ * @throws DataStoreException if fetching the tile status failed.
+ */
+ @Override
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ public TileStatus getTileStatus(final long... indices) throws DataStoreException {
+ if (tilingScheme.getExtent().contains(indices)) try {
+ final TiledGridCoverage coverage;
+ final RenderedImage image;
+ final long imageToTileX;
+ final long imageToTileY;
+ synchronized (this) {
+ coverage = this.coverage; // Never null if `image` is non-null.
+ image = this.image;
+ imageToTileX = this.imageToTileX;
+ imageToTileY = this.imageToTileY;
+ }
+ if (image != null) {
+ final long tileX = Math.subtractExact(indices[coverage.xDimension], imageToTileX);
+ final long x0 = image.getMinTileX();
+ if (tileX >= x0 && tileX < x0 + image.getNumXTiles()) {
+ final long tileY = Math.subtractExact(indices[coverage.yDimension], imageToTileY);
+ final long y0 = image.getMinTileY();
+ if (tileY >= y0 && tileY < y0 + image.getNumYTiles()) {
+ return getTileStatus(image, Math.toIntExact(tileX), Math.toIntExact(tileY));
+ }
+ }
+ }
+ return TileStatus.UNKNOWN;
+ } catch (ArithmeticException e) {
+ Logging.ignorableException(StoreUtilities.LOGGER, ImageTileMatrix.class, "getTileStatus", e);
+ }
+ return TileStatus.OUTSIDE_EXTENT;
+ }
+
+ /**
+ * Returns the status of the tile at the given index.
+ *
+ * @param image image from which to get a tile status.
+ * @param tileX row index of the tile for which to get the status.
+ * @param tileY column index of the tile for which to get the status.
+ * @return status of the tile at the specified indexes.
+ *
+ * @todo We should check if the image is an instance of {@code ComputedImage},
+ * then check if the tile is in the cache. But it would require that we merge
+ * the feature and storage modules if we want to reuse {@link TileStatus} enumeration.
+ */
+ private static TileStatus getTileStatus(final RenderedImage image, final int tileX, final int tileY) {
+ return TileStatus.EXISTS;
+ }
+
+ /**
+ * Gets a tile at the given indices if not missing.
+ *
+ * @param indices indices of the tile to fetch, as coordinates inside the matrix extent.
+ * @return the tile if it exists, or an empty value if the tile is missing.
+ * @throws NoSuchDataException if the given indices are outside the matrix extent.
+ * @throws DataStoreException if fetching the tile failed for another reason.
+ */
+ @Override
+ public Optional<Tile> getTile(final long... indices) throws DataStoreException {
+ final GridExtent extent = tilingScheme.getExtent();
+ if (extent.contains(indices)) try {
+ final Tile tile = iterator(extent.reshape(indices, indices, true)).createFirstTile();
+ return (tile.getStatus() == TileStatus.MISSING) ? Optional.empty() : Optional.of(tile);
+ } catch (ArithmeticException e) {
+ throw new UnsupportedQueryException(e);
+ }
+ throw new NoSuchDataException(Resources.format(Resources.Keys.TileIndexesOutOfBounds));
+ }
+
+ /**
+ * Retrieves a stream of existing tiles in the specified region.
+ * The stream contains the existing tiles that are inside the given region and excludes missing tiles.
+ * If a tile is {@linkplain TileStatus#IN_ERROR in error}, then the stream nevertheless return a tile
+ * but its {@link Tile#getResource()} method should throw the exception.
+ *
+ * <h4>Limitations</h4>
+ * The current implementation limits the size of the given extent to {@link Integer#MAX_VALUE}
+ * in each dimension. Note that this is a maximum size in tile indices, not in pixel coordinates.
+ *
+ * @param indiceRanges ranges of tile indices in all dimensions, or {@code null} for all tiles.
+ * @param parallel {@code true} for a parallel stream (if supported), or {@code false} for a sequential stream.
+ * @return stream of tiles, excluding missing tiles.
+ * @throws DataStoreException if the tiles can not be fetched in the given ranges of tile indexes.
+ */
+ @Override
+ public Stream<Tile> getTiles(GridExtent indiceRanges, final boolean parallel) throws DataStoreException {
+ ArgumentChecks.ensureDimensionMatches("indiceRanges", tilingScheme.getDimension(), indiceRanges);
+ if (indiceRanges == null) {
+ indiceRanges = tilingScheme.getExtent();
+ }
+ try {
+ return StreamSupport.stream(iterator(indiceRanges).iterator(), parallel);
+ } catch (ArithmeticException e) {
+ throw new UnsupportedQueryException(e);
+ }
+ }
+
+ /**
+ * Creates an object which can be used for retrieving a single tile or a stream tiles.
+ *
+ * @param indiceRanges ranges of tile indices in all dimensions, or {@code null} for all tiles.
+ * @return a request which can be used for getting a tile or a stream of tiles in the given region.
+ * @throws DataStoreException if the tiles can not be fetched in the given ranges of tile indexes.
+ * @throws ArithmeticException if coordinate computation exceeds the capacity of 64-bits integers.
+ */
+ private synchronized IterationDomain<Tile> iterator(final GridExtent indiceRanges) throws DataStoreException {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ final TiledGridCoverage coverage = coverage();
+ boolean retry = false;
+ do { // This loop will be executed only 1 or 2 times.
+ if (image != null) {
+ final long xmin, ymin, xmax, ymax;
+ xmin = Math.subtractExact(indiceRanges.getLow (coverage.xDimension), imageToTileX);
+ xmax = Math.subtractExact(indiceRanges.getHigh(coverage.xDimension), imageToTileX);
+ final long x0 = image.getMinTileX();
+ if (xmin >= x0 && xmax < x0 + image.getNumXTiles()) {
+ ymin = Math.subtractExact(indiceRanges.getLow (coverage.yDimension), imageToTileY);
+ ymax = Math.subtractExact(indiceRanges.getHigh(coverage.yDimension), imageToTileY);
+ final long y0 = image.getMinTileY();
+ if (ymin >= y0 && ymax < y0 + image.getNumYTiles()) {
+ return new Iterator(Math.toIntExact(xmin),
+ Math.toIntExact(ymin),
+ Math.toIntExact(xmax),
+ Math.toIntExact(ymax));
+ }
+ }
+ }
+ /*
+ * Gets the bounds of the image to read. If deferred reading is supported,
+ * we can expand to the bounds of the whole coverage in order to perform a
+ * read operation (deferred) only once.
+ */
+ final GridExtent extent = coverage.getGridGeometry().getExtent();
+ final int dimension = extent.getDimension();
+ final var low = new long[dimension];
+ final var high = new long[dimension];
+ for (int i=0; i<dimension; i++) {
+ final long limit = Math.incrementExact(extent.getHigh(i));
+ high[i] = Math.min(limit, tileToCell(Math.incrementExact(indiceRanges.getHigh(i)), i));
+ low [i] = Math.max(extent.getLow(i), tileToCell(indiceRanges.getLow(i), i));
+ final long span = high[i] - low[i];
+ if (span < 0 || span > Integer.MAX_VALUE) {
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Integer.SIZE));
+ }
+ if (coverage.deferredTileReading) {
+ final long remain = Math.min(extent.getSize(i), Integer.MAX_VALUE) - span;
+ final long after = Math.min(remain >> 1, limit - high[i]);
+ final long before = Math.min(remain - after, low[i] - extent.getLow(i));
+ low [i] -= before;
+ high[i] += after;
+ }
+ }
+ image = coverage.render(extent.reshape(low, high, false));
+ imageToTileX = low[coverage.xDimension];
+ imageToTileY = low[coverage.yDimension];
+ } while ((retry = !retry) == true);
+ throw new InternalDataStoreException(); // Should never happen.
+ }
+
+ /**
+ * Converts the give tile coordinate in the given dimension to cell coordinates.
+ *
+ * @param coordinate the tile coordinate to convert.
+ * @param dimension the dimension of the coordinate to convert.
+ * @return the cell coordinate.
+ * @throws ArithmeticException if the result overflows the capacity of 64-bits integers.
+ */
+ private long tileToCell(final long coordinate, final int dimension) {
+ return Math.addExact(tileToCell[dimension], Math.multiplyExact(coordinate, tileSize[dimension]));
+ }
+
+ /**
+ * Factory for an iterator over tiles in ranges of user-specified tile indices.
+ */
+ private final class Iterator extends IterationDomain<Tile> {
+ /**
+ * Snapshot of {@link ImageTileMatrix#image}.
+ */
+ private final RenderedImage tiles;
+
+ /**
+ * Snapshot of {@link ImageTileMatrix#imageToTileX} and {@link ImageTileMatrix#imageToTileY}.
+ */
+ private final long offsetX, offsetY;
+
+ /**
+ * Creates a new request for tile iterators.
+ *
+ * @param xmin first column index of tiles, inclusive.
+ * @param xmin first row index of tiles, inclusive.
+ * @param xmax last column index of tiles, inclusive.
+ * @param ymax last row index of tiles, inclusive.
+ */
+ Iterator(final int xmin, final int ymin, final int xmax, final int ymax) {
+ super(xmin, ymin, xmax, ymax);
+ tiles = image;
+ offsetX = imageToTileX;
+ offsetY = imageToTileY;
+ }
+
+ /**
+ * Creates the tile at the given indexes.
+ */
+ @Override
+ protected Tile createTile(final int tileX, final int tileY) {
+ return new Tile() {
+ /** This tile viewed as a resource, created when first requested. */
+ private Resource resourceView;
+
+ /** Returns the path to content of the tile if known. */
+ @Override public Optional<Path> getContentPath() throws DataStoreException {
+ return Optional.ofNullable(coverage().getContentPath(getIndices()));
+ }
+
+ /** Returns the indices of this tile in the {@code TileMatrix}. */
+ @Override public long[] getIndices() {
+ return new long[] {offsetX + tileX, offsetY + tileY};
+ }
+
+ /** Returns information about whether the tile failed to load. */
+ @Override public TileStatus getStatus() {
+ return getTileStatus(tiles, tileX, tileY);
+ }
+
+ /** Returns the tile content as a resource. */
+ @Override public synchronized Resource getResource() throws DataStoreException {
+ if (resourceView == null) {
+ resourceView = createResourceView(getIndices(), ReshapedImage.singleTile(tiles, tileX, tileY));
+ }
+ return resourceView;
+ }
+ };
+ }
+ }
+
+ /**
+ * Creates a resource for the tile at the given indices.
+ * The resource wraps a grid coverage, which is itself wrapping the given image.
+ * The given image should contains only the desired tile. The caller currently sets
+ * the tile indexes and image coordinates to (0,0), but this is not mandatory.
+ *
+ * @param indices indices of the tile, as coordinates inside the matrix extent.
+ * @param tile a rendered image which contains only the tile.
+ * @return resource for the specified tile.
+ */
+ private Resource createResourceView(final long[] indices, final RenderedImage tile) throws DataStoreException {
+ final Object[] args = new Object[indices.length];
+ Arrays.setAll(args, (i) -> indices[i]);
+ final GenericName id = Names.createScopedName(identifier, null, String.format(tileIndicesPattern, args));
+ final long[] low = new long[indices.length];
+ final long[] high = new long[indices.length];
+ for (int i=0; i<indices.length; i++) {
+ final long size = tileSize[i];
+ low [i] = Math.addExact(tileToCell[i], Math.multiplyExact(indices[i], size));
+ high[i] = Math.addExact(low[i], size - 1);
+ }
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ final TiledGridCoverage coverage = coverage();
+ GridGeometry cellGrid = coverage.getGridGeometry();
+ final GridExtent extent = cellGrid.getExtent().reshape(low, high, true);
+ cellGrid = cellGrid.derive().subgrid(extent, null).build();
+ final var subset = new GridCoverage2D(cellGrid, coverage.getSampleDimensions(), tile);
+ return new MemoryGridCoverageResource(resource, id, subset, processor);
+ }
+}
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/IterationDomain.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/IterationDomain.java
new file mode 100644
index 0000000000..c4a0aa39e4
--- /dev/null
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/IterationDomain.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage.tiling;
+
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.awt.image.RenderedImage;
+
+
+/**
+ * A request for tiles from a rendered image.
+ * Contrarily to {@link TiledGridCoverage.AOI}, this request works with arbitrary {@link RenderedImage} and does
+ * not manage a tile cache. Tile caching is assumed to be managed by the {@code RenderedImage} implementation.
+ * This class is designed for use with {@link Spliterator} with parallelism.
+ *
+ * <h2>Iteration order</h2>
+ * Iteration order is unspecified. Current implementation iterates from left to right, then top to bottom.
+ * But future implementations may support Hilbert order. If the iteration is split, the iteration order is
+ * interleaved: for example with two threads, the first tile is given to the first thread, the second tile
+ * is given to the second thread, the third tile is given to the first thread, <i>etc.</i> The intend is to
+ * stay relatively close to a reading of tiles in sequential order when all threads are executed in parallel.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ *
+ * @param <T> type of tiles on which the {@link Spliterator} will iterate.
+ */
+abstract class IterationDomain<T> {
+ /**
+ * Minimal column index of the region on which to iterate.
+ */
+ private final int xmin;
+
+ /**
+ * Minimal row index of the region on which to iterate.
+ */
+ private final int ymin;
+
+ /**
+ * Number of columns on which to iterate.
+ * This is {@code (xmax + 1) - xmin} where {@code xmax} is inclusive.
+ */
+ private final long width;
+
+ /**
+ * Maximum value (exclusive) of the indexes of the tiles on which to iterate.
+ * Index is computed using the row-major convention.
+ */
+ private final long limit;
+
+ /**
+ * Creates a new request for tile iterators.
+ *
+ * @param xmin first column index of tiles, inclusive.
+ * @param xmin first row index of tiles, inclusive.
+ * @param xmax last column index of tiles, inclusive.
+ * @param ymax last row index of tiles, inclusive.
+ */
+ protected IterationDomain(final int xmin, final int ymin, final int xmax, final int ymax) {
+ this.xmin = xmin;
+ this.ymin = ymin;
+ this.width = (xmax + 1L) - xmin;
+ this.limit = Math.multiplyExact(width, (ymax + 1L) - ymin);
+ }
+
+ /**
+ * Creates a new item for the tile at the given indexes. If this method returns {@code null},
+ * then the tile is assumed missing and this iterator searches for the next tile.
+ *
+ * @param tileX column index of the tile.
+ * @param tileY row index of the tile.
+ * @return item for the tile at the given indexes, or {@code null} if missing.
+ */
+ protected abstract T createTile(int tileX, int tileY);
+
+ /**
+ * Creates the first tile, or returns {@code null} if the tile is missing.
+ */
+ final T createFirstTile() {
+ return createTile(xmin, ymin);
+ }
+
+ /**
+ * Returns a new iterator over the tiles.
+ *
+ * @return a new iterator.
+ */
+ public final Spliterator<T> iterator() {
+ return new Iterator();
+ }
+
+ /**
+ * Iterator over the tiles.
+ */
+ private final class Iterator implements Spliterator<T> {
+ /**
+ * Row-major index of the next tile to return.
+ */
+ private long index;
+
+ /**
+ * Increment to apply on {@link #index} between each iteration.
+ */
+ private int increment;
+
+ /**
+ * Creates a new iterator which will initially traverse all tiles.
+ */
+ Iterator() {
+ index = Math.multiplyExact(width, ymin);
+ increment = 1;
+ }
+
+ /**
+ * Creates a new iterator which will iterate over half of the tiles covered by the given iterator.
+ * This constructor modifies the supplied iterator for covering the other half.
+ *
+ * @param parent the iterator to split. It will be modified by this method call.
+ */
+ private Iterator(final Iterator parent) {
+ index = parent.index;
+ parent.index += parent.increment;
+ increment = parent.increment *= 2;
+ }
+
+ /**
+ * Returns, if possible, an iterator covering half of the tiles covered by this iterator.
+ * On return, this iterator will cover the other half.
+ *
+ * @return an iterator covering half of the tiles, or {@code null} if this iterator cannot be partitioned.
+ */
+ @Override
+ public Spliterator<T> trySplit() {
+ return (limit - index) > increment ? new Iterator(this) : null;
+ }
+
+ /**
+ * Returns the number of remaining tiles in the iteration.
+ */
+ @Override
+ public long estimateSize() {
+ return (limit - index) / increment;
+ }
+
+ /**
+ * In the context of this iterator, this is synonymous of {@link #estimateSize()}.
+ */
+ @Override
+ public long getExactSizeIfKnown() {
+ return estimateSize();
+ }
+
+ /**
+ * Returns the characteristics of the iteration. The number of tiles is known in advance ({@link #SIZED}),
+ * null values are not allowed ({@link #NONNULL}), there will be no tiles at same indexes ({@link #DISTINCT}),
+ * and the tile matrix will not change ({@link #IMMUTABLE}). This iterator makes no guaranteed about iteration
+ * order (i.e., not {@link #ORDERED}).
+ *
+ * <p>Note that {@link #IMMUTABLE} is not a promise that the pixel values will not change,
+ * as the image may be writable. But this iterator is concerned only about the set of tiles,
+ * not the content of those tiles.</p>
+ */
+ @Override
+ public int characteristics() {
+ return SIZED | SUBSIZED | NONNULL | DISTINCT | IMMUTABLE;
+ }
+
+ /**
+ * If a remaining tile exists, performs the given action on it.
+ *
+ * @param action the action to execute on the next tile.
+ * @return whether the action has been performed.
+ */
+ @Override
+ public boolean tryAdvance(final Consumer<? super T> action) {
+ while (index < limit) {
+ final int tileY = Math.toIntExact(index / width);
+ final int tileX = Math.toIntExact(index % width + xmin);
+ final T tile = createTile(tileX, tileY);
+ index += increment;
+ if (tile != null) {
+ action.accept(tile);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Performs the given action on all remaining tiles.
+ *
+ * @param action the action to execute on all remaining tiles.
+ */
+ @Override
+ public void forEachRemaining(final Consumer<? super T> action) {
+ while (index < limit) {
+ final int tileY = Math.toIntExact(index / width);
+ final int tileX = Math.toIntExact(index % width + xmin);
+ final T tile = createTile(tileX, tileY);
+ index += increment;
+ if (tile != null) {
+ action.accept(tile);
+ }
+ }
+ }
+ }
+}
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSet.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSet.java
index 4799f8c0f0..7699b7b470 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSet.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSet.java
@@ -26,13 +26,13 @@ import org.apache.sis.storage.base.MetadataBuilder;
/**
- * A collection of {@code TileMatrix} in the same CRS but at different scale levels.
+ * A collection of {@code TileMatrix} in the same <abbr>CRS</abbr> but at different scale levels.
* Each {@code TileMatrix} is optimized for a particular scale and is identified by a tile matrix identifier.
* Tile matrices usually have 2 dimensions (width and height), but this API allows any number of dimensions.
* However, the number of dimensions must be the same for all tile matrices.
*
- * <p>The {@code TileMatrixSet} concept is derived from OGC standards. The same concept is called
- * <i>image pyramid</i> or <i>resolution levels</i> in some other standards.
+ * <p>The {@code TileMatrixSet} concept is derived from <abbr>OGC</abbr> standards.
+ * The same concept is called <i>image pyramid</i> or <i>resolution levels</i> in some other standards.
* Some standards require that all scales must be related by a power of 2,
* but {@code TileMatrixSet} does not have this restriction.</p>
*
@@ -110,7 +110,7 @@ public interface TileMatrixSet {
* Returns the coordinate reference system of all {@code TileMatrix} instances in this set.
* This is the value returned by {@code TileMatrix.getTilingScheme().getCoordinateReferenceSystem()}.
*
- * @return the CRS used by all {@code TileMatrix} instances in this set.
+ * @return the <abbr>CRS</abbr> used by all {@code TileMatrix} instances in this set.
*
* @see TileMatrix#getTilingScheme()
*/
@@ -122,12 +122,12 @@ public interface TileMatrixSet {
* of all values returned by {@code TileMatrix.getTilingScheme().getEnvelope()}.
* May be empty if too costly to compute.
*
- * @return the bounding box for all tile matrices in CRS coordinates, if available.
+ * @return the bounding box for all tile matrices in <abbr>CRS</abbr> coordinates, if available.
*/
Optional<Envelope> getEnvelope();
/**
- * Returns all {@link TileMatrix} instances in this set, together with their identifiers.
+ * Returns all {@code TileMatrix} instances in this set, together with their identifiers.
* For each value in the map, the associated key is {@link TileMatrix#getIdentifier()}.
* Entries are sorted from coarser resolution (highest scale denominator) to most detailed
* resolution (lowest scale denominator).
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java
index 68a989962a..151beac5fe 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java
@@ -208,7 +208,8 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* The dimension of the grid which is mapped to the <var>x</var> axis (column indexes) in rendered images.
* This is the value of the {@code xDimension} argument in the last call to the
- * {@link TiledGridCoverageResource#setRasterDimension(int, int)} method before this coverage has been constructed.
+ * {@link TiledGridCoverageResource#setRasterSubspaceDimensions(int, int)} method
+ * before this coverage has been constructed.
* This value is usually 0.
*
* @see #readTiles(TileIterator)
@@ -218,7 +219,8 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* The dimension of the grid which is mapped to the <var>y</var> axis (row indexes) in rendered images.
* This is the value of the {@code yDimension} argument in the last call to the
- * {@link TiledGridCoverageResource#setRasterDimension(int, int)} method before this coverage has been constructed.
+ * {@link TiledGridCoverageResource#setRasterSubspaceDimensions(int, int)} method
+ * before this coverage has been constructed.
* This value is usually 1.
*
* @see #readTiles(TileIterator)
@@ -266,7 +268,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
* This is true if the user explicitly {@linkplain TiledGridCoverageResource#setLoadingStrategy requested
* such deferred loading strategy} and the resource considers that it is worth to do so.
*/
- private final boolean deferredTileReading;
+ final boolean deferredTileReading;
/**
* Creates a new tiled grid coverage. This constructor does not load any tile.
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java
index 3507103dac..9ace524095 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java
@@ -19,6 +19,7 @@ package org.apache.sis.storage.tiling;
import java.util.List;
import java.util.Arrays;
import java.util.Objects;
+import java.util.Collection;
import java.lang.reflect.Array;
import java.awt.image.DataBuffer;
import java.awt.image.ColorModel;
@@ -29,6 +30,8 @@ import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.RasterFormatException;
+import org.opengis.util.GenericName;
+import org.opengis.util.NameFactory;
import org.apache.sis.coverage.SampleDimension;
import org.apache.sis.coverage.grid.GridCoverage;
import org.apache.sis.coverage.grid.GridCoverage2D;
@@ -52,6 +55,7 @@ import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.internal.shared.Numerics;
import org.apache.sis.util.collection.WeakValueHashMap;
+import org.apache.sis.util.iso.DefaultNameFactory;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
import org.opengis.coverage.CannotEvaluateException;
@@ -76,7 +80,7 @@ import org.opengis.coverage.CannotEvaluateException;
* @version 1.7
* @since 1.7
*/
-public abstract class TiledGridCoverageResource extends AbstractGridCoverageResource {
+public abstract class TiledGridCoverageResource extends AbstractGridCoverageResource implements TiledResource {
/**
* Number of dimensions in a rendered image.
* Used for identifying codes where a two-dimensional slice is assumed.
@@ -147,6 +151,13 @@ public abstract class TiledGridCoverageResource extends AbstractGridCoverageReso
*/
private RasterLoadingStrategy loadingStrategy;
+ /**
+ * The tile matrix sets, created when first requested.
+ *
+ * @see #getTileMatrixSets()
+ */
+ private Collection<TileMatrixSet> tileMatrixSets;
+
/**
* The dimension of the grid which is mapped to the <var>x</var> axis (column indexes) in rendered images.
* The default value is 0.
@@ -159,11 +170,6 @@ public abstract class TiledGridCoverageResource extends AbstractGridCoverageReso
*/
private int yDimension;
- /**
- * The grid coverage processor to use when tiles use a subset of the bands.
- */
- final GridCoverageProcessor processor;
-
/**
* Creates a new resource.
*
@@ -172,7 +178,6 @@ public abstract class TiledGridCoverageResource extends AbstractGridCoverageReso
protected TiledGridCoverageResource(final Resource parent) {
super(parent);
yDimension = 1;
- processor = new GridCoverageProcessor();
}
/**
@@ -191,8 +196,9 @@ public abstract class TiledGridCoverageResource extends AbstractGridCoverageReso
*
* @see TiledGridCoverage#xDimension
* @see TiledGridCoverage#yDimension
+ * @see GridExtent#getSubspaceDimensions(int)
*/
- protected void setRasterDimension(final int xDimension, final int yDimension) throws DataStoreException {
+ protected void setRasterSubspaceDimensions(final int xDimension, final int yDimension) throws DataStoreException {
final int max = getGridGeometry().getDimension() - 1;
ArgumentChecks.ensureBetween("xDimension", 0, max, xDimension);
ArgumentChecks.ensureBetween("yDimension", 0, max, yDimension);
@@ -827,6 +833,26 @@ check: if (dataType.isInteger()) {
return loaded;
}
+ /**
+ * Returns a coverage which will read the tiles as late as possible.
+ *
+ * @return the coverage.
+ * @throws DataStoreException if an error occurred while reading the grid coverage data.
+ */
+ final TiledGridCoverage readAtGetTileTime() throws DataStoreException {
+ synchronized (getSynchronizationLock()) {
+ final RasterLoadingStrategy old = loadingStrategy;
+ try {
+ loadingStrategy = RasterLoadingStrategy.AT_GET_TILE_TIME;
+ return read(new Subset(null, null));
+ } catch (RuntimeException e) {
+ throw canNotRead(listeners.getSourceName(), null, e);
+ } finally {
+ loadingStrategy = old;
+ }
+ }
+ }
+
/**
* Whether this resource supports immediate loading of raster data.
* Current implementation does not support immediate loading if the data cube has more than 2 dimensions.
@@ -882,4 +908,139 @@ check: if (dataType.isInteger()) {
loadingStrategy = loadAtReadTime ? RasterLoadingStrategy.AT_READ_TIME
: RasterLoadingStrategy.AT_RENDER_TIME;
}
+
+ /**
+ * Returns the collection of all available tile matrix sets in this resource.
+ * The returned collection typically contains exactly one instance.
+ *
+ * <p>The default implementation uses the information provided by {@link #getPyramids()}
+ * for creating default {@link TileMatrixSet} instances.
+ * It is generally easier for subclasses to override {@link #getPyramids()} instead of this method.</p>
+ *
+ * @return all available {@link TileMatrixSet} instances, or an empty collection if none.
+ * @throws DataStoreException if an error occurred while fetching the tile matrix sets.
+ */
+ @Override
+ @SuppressWarnings("ReturnOfCollectionOrArrayField") // The collection is unmodifiable.
+ public Collection<? extends TileMatrixSet> getTileMatrixSets() throws DataStoreException {
+ synchronized (getSynchronizationLock()) {
+ if (tileMatrixSets == null) {
+ final List<Pyramid> pyramids = getPyramids();
+ final var sets = new TileMatrixSet[pyramids.size()];
+ final GenericName scope = getIdentifier().orElseGet(
+ () -> pyramids.get(0).nameFactory().createLocalName(null, listeners.getSourceName()));
+ final var processor = new GridCoverageProcessor();
+ for (int i=0; i<sets.length; i++) {
+ sets[i] = new ImagePyramid(scope, pyramids.get(i), processor);
+ }
+ tileMatrixSets = List.of(sets);
+ }
+ return tileMatrixSets;
+ }
+ }
+
+ /**
+ * Returns information about the {@code TileMatrixSet} instances to create.
+ * This method is invoked by the default implementation of {@link #getTileMatrixSets()} when first needed.
+ * By default, this method returns a list of only one element, which itself describes a pyramid of only one level.
+ * This single level describes a {@link TileMatrix} at the resolution of this {@code TiledGridCoverageResource}.
+ *
+ * @return information about the tile matrix sets to create.
+ */
+ protected List<Pyramid> getPyramids() {
+ return List.of((level) -> (level == 0) ? this : null);
+ }
+
+ /**
+ * Description of a {@code TileMatrixSet} implemented as an image pyramid.
+ * This interface is used by the default implementation of {@link #getTileMatrixSets()}.
+ * There is usually only one pyramid per {@link TiledGridCoverageResource} instance,
+ * but many pyramids may exist, for example, if data are offered in different
+ * Coordinate Reference System (<abbr>CRS</abbr>).
+ *
+ * <p>Each pyramid can have an arbitrary number of levels.
+ * It is recommended to have one pyramid level for each {@linkplain #getResolutions() preferred resolutions}.
+ * The pyramid levels must be sorted from finest resolution (at level 0) to coarsest resolution.</p>
+ *
+ * <p>The number of levels is unspecified because some data stores cannot provide this information in advance.
+ * Instead, the {@link #forPyramidLevel(int)} method will be invoked with different argument values when each
+ * level is first requested, until that method returns {@code null} for a level too high.</p>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.7
+ * @since 1.7
+ */
+ protected static interface Pyramid {
+ /**
+ * Returns an identifier for this pyramid. The default implementation returns <abbr>TMS</abbr>
+ * as the abbreviation of "Tile Matrix Set". This is often sufficient in the common case where
+ * there is only one Tile Matrix Set per Grid Coverage Resource.
+ *
+ * <p>This value is used for building the value of {@link TileMatrixSet#getIdentifier()}.</p>
+ *
+ * @return an identifier for this pyramid. Default is {@code "TMS"}.
+ *
+ * @see TileMatrixSet#getIdentifier()
+ */
+ default String identifier() {
+ return "TMS";
+ }
+
+ /**
+ * Returns an identifier for the given level of this pyramid. The returned identifier
+ * will be local in the namespace of the pyramid {@linkplain #identifier() identifier}.
+ *
+ * @param level the pyramid level where 0 is the level with the finest resolution.
+ * @return a local identifier for the specified level.
+ */
+ default String identifierOfLevel(int level) {
+ return "L" + level;
+ }
+
+ /**
+ * Returns the level in this pyramid for the given local identifier.
+ * This method is the converse of {@link #identifierOfLevel(int)}.
+ *
+ * @param identifier the identifier for which to get the pyramid level.
+ * @return pyramid level associated to the given identifier.
+ * @throws IllegalArgumentException if the given identifier is not recognized by this pyramid.
+ */
+ default int levelOfIdentifier(final String identifier) {
+ if (identifier.isEmpty() || identifier.charAt(0) != 'L') {
+ throw new IllegalArgumentException(identifier);
+ }
+ // Note: `NumberFormatException` is a subtype of `IllegalArgumentException`.
+ return Integer.parseInt(identifier.substring(1));
+ }
+
+ /**
+ * Returns a resource for the same data as this resource but at a different resolution level.
+ * The resource at index 0 shall be the resource with the finest resolution, and resources at
+ * increasing index values shall be resources with increasingly coarser resolutions.
+ * If the specified level is equal or greater than the number of levels in this pyramid,
+ * then this method shall return {@code null}.
+ *
+ * <p>If this method returns a non-null instance <var>r</var>, then the following condition should hold:
+ * {@code r.getGridGeometry().getResolution(false)} should be equal, ignoring NaN values and rounding
+ * errors, to {@code getResolutions().get(level)}.</p>
+ *
+ * @param level the pyramid level where 0 is the level with the finest resolution.
+ * @return a resource for data at the specified pyramid level, or {@code null} if the given level is too high.
+ * @throws DataStoreException if an error occurred while creating the resource.
+ *
+ * @see #getResolutions()
+ */
+ TiledGridCoverageResource forPyramidLevel(int level) throws DataStoreException;
+
+ /**
+ * Returns the name factory to use for creating identifiers of tiles and tile matrices.
+ * The default implementation returns {@link DefaultNameFactory#provider()}.
+ * Subclasses can override for more control on the identifiers to create.
+ *
+ * @return the name factory to use for creating identifiers of tiles and tile matrices.
+ */
+ default NameFactory nameFactory() {
+ return DefaultNameFactory.provider();
+ }
+ }
}
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledResource.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledResource.java
index bc7c7f72ff..4c9d6e338a 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledResource.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledResource.java
@@ -30,9 +30,7 @@ import org.apache.sis.storage.Resource;
* <p>A {@code TiledResource} may contain multiple {@link TileMatrixSet} instances,
* each one for a different {@link org.opengis.referencing.crs.CoordinateReferenceSystem}.
* Most format specifications only support a single {@link TileMatrixSet},
- * but a few ones like WMTS may have several.</p>
- *
- * <p>All methods in this interface return non-null values.</p>
+ * but a few ones like <abbr>WMTS</abbr> may have several.</p>
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/TableAppender.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/TableAppender.java
index e11a99bde9..fff3fe23a0 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/TableAppender.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/TableAppender.java
@@ -68,7 +68,7 @@ import static org.apache.sis.util.Characters.isLineOrParagraphSeparator;
* ╚═════════╧═════════╧════════╝</pre>
*
* @author Martin Desruisseaux (MPO, IRD, Geomatys)
- * @version 1.0
+ * @version 1.7
*
* @see org.apache.sis.util.collection.TreeTableFormat
*
@@ -227,6 +227,20 @@ public class TableAppender extends Appender implements Flushable {
ownOut = true;
}
+ /**
+ * Creates a new table formatter writing in an internal buffer with the specified column separator and border.
+ *
+ * @param leftBorder string to write on the left side of the table.
+ * @param separator string to write between columns.
+ * @param rightBorder string to write on the right side of the table.
+ *
+ * @since 1.7
+ */
+ public TableAppender(final String leftBorder, final String separator, final String rightBorder) {
+ this(new StringBuilder(256), leftBorder, separator, rightBorder);
+ ownOut = true;
+ }
+
/**
* Creates a new table formatter writing in the given output with a default column separator.
* The default is a vertical double line for the left and right table borders, and a single
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
index 571953421a..7e54a9c545 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
@@ -41,7 +41,7 @@
* Unicode supplementary characters}.
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 1.6
+ * @version 1.7
* @since 0.3
*/
package org.apache.sis.io;
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/DecimalFunctions.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/DecimalFunctions.java
index 70bc3e7271..3a5bfac210 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/DecimalFunctions.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/DecimalFunctions.java
@@ -45,7 +45,7 @@ import static org.apache.sis.pending.jdk.JDK19.DOUBLE_PRECISION;
* since base 10 is not more "real" than base 2 for natural phenomenon.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.7
*
* @see MathFunctions#pow10(int)
* @see Math#log10(double)
@@ -497,6 +497,31 @@ public final class DecimalFunctions {
throw new ArithmeticException(String.valueOf(x));
}
+ /**
+ * Computes {@code (int) floor(log10(x))} from an integer value. For values greater than one,
+ * this is the number of digits - 1 in the decimal representation of the given number.
+ * Values smaller than 1 are not allowed.
+ *
+ * @param x the value for which to compute the logarithm. Must be greater than zero.
+ * @return logarithm of the given value, rounded toward zero.
+ * @throws ArithmeticException if the given value is zero or negative.
+ *
+ * @since 1.7
+ */
+ public static int floorLog10(final long x) {
+ if (x <= 0) {
+ throw new ArithmeticException(String.valueOf(x));
+ }
+ // It would be possible to do something clever, but this method is not invoked very often.
+ int p = 0;
+ long limit = 10;
+ while (x >= limit) {
+ limit *= 10;
+ p++;
+ }
+ return p;
+ }
+
/**
* Returns {@code true} if the given numbers or equal or differ only by {@code accurate}
* having more non-zero trailing decimal fraction digits than {@code approximate}.
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractMap.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractMap.java
index 2da8ee66e8..0104706f87 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractMap.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractMap.java
@@ -708,7 +708,7 @@ public abstract class AbstractMap<K,V> implements Map<K,V> {
*/
@Override
public String toString() {
- final var buffer = new TableAppender(" = ");
+ final var buffer = new TableAppender("", " = ", "");
buffer.setMultiLinesCells(true);
final EntryIterator<K,V> it = entryIterator();
if (it != null) while (it.next()) {
diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/math/DecimalFunctionsTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/math/DecimalFunctionsTest.java
index a5c71ba2ad..6c092803ed 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/math/DecimalFunctionsTest.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/math/DecimalFunctionsTest.java
@@ -33,6 +33,7 @@ import org.apache.sis.test.TestUtilities;
*
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class DecimalFunctionsTest extends TestCase {
/**
* Creates a new test case.
@@ -269,14 +270,14 @@ public final class DecimalFunctionsTest extends TestCase {
*/
@Test
public void testFloorLog10() {
- assertEquals( 0, floorLog10( 1));
- assertEquals( 0, floorLog10( 9));
- assertEquals( 1, floorLog10( 10));
- assertEquals( 1, floorLog10( 11));
- assertEquals( 1, floorLog10( 99));
- assertEquals( 2, floorLog10( 100));
- assertEquals( 2, floorLog10( 999));
- assertEquals( 3, floorLog10(1000));
+ assertEquals( 0, floorLog10( 1d));
+ assertEquals( 0, floorLog10( 9d));
+ assertEquals( 1, floorLog10( 10d));
+ assertEquals( 1, floorLog10( 11d));
+ assertEquals( 1, floorLog10( 99d));
+ assertEquals( 2, floorLog10( 100d));
+ assertEquals( 2, floorLog10( 999d));
+ assertEquals( 3, floorLog10(1000d));
assertEquals( -1, floorLog10(0.100));
assertEquals( -2, floorLog10(0.099));
assertEquals( -2, floorLog10(0.010));
@@ -291,6 +292,21 @@ public final class DecimalFunctionsTest extends TestCase {
try {floorLog10(POSITIVE_INFINITY); fail("Expected ArithmeticException.");} catch (ArithmeticException e) {}
}
+ /**
+ * Tests {@link DecimalFunctions#floorLog10(long)} method.
+ */
+ @Test
+ public void testFloorLog10OnFromInteger() {
+ assertEquals(0, floorLog10( 1L));
+ assertEquals(0, floorLog10( 9L));
+ assertEquals(1, floorLog10( 10L));
+ assertEquals(1, floorLog10( 11L));
+ assertEquals(1, floorLog10( 99L));
+ assertEquals(2, floorLog10( 100L));
+ assertEquals(2, floorLog10( 999L));
+ assertEquals(3, floorLog10(1000L));
+ }
+
/**
* Tests {@link DecimalFunctions#equalsIgnoreMissingFractionDigits(double, double)}.
* This test uses the conversion factor from degrees to radians as a use case.
diff --git a/optional/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java b/optional/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java
index 2bd9814ce1..60eddd1638 100644
--- a/optional/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java
+++ b/optional/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java
@@ -50,8 +50,9 @@ import static org.junit.jupiter.api.Assertions.*;
* Tests {@link GDALStore}.
*
* @author Johann Sorel (Geomatys)
- * @author Quentin BIALOTA (Geomatys)
+ * @author Quentin Bialota (Geomatys)
*/
+@SuppressWarnings("exports")
public final class GDALStoreTest {
/**
* Name of the test file.
(sis) 01/02: Renamed `TiledGridResource` as `TiledGridCoverageResource`.
Posted by de...@apache.org.
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 ebd3ddab597cf911ea6cc61c1b2ad9ef9a1e18b8
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Feb 17 12:15:02 2026 +0100
Renamed `TiledGridResource` as `TiledGridCoverageResource`.
---
.../sis/storage/geotiff/CompressedSubset.java | 6 +-
.../org/apache/sis/storage/geotiff/DataCube.java | 4 +-
.../org/apache/sis/storage/geotiff/DataSubset.java | 8 +--
.../sis/storage/tiling/TiledGridCoverage.java | 84 +++++++++++-----------
...esource.java => TiledGridCoverageResource.java} | 26 +++----
.../apache/sis/storage/geoheif/ImageResource.java | 10 +--
.../org/apache/sis/storage/gdal/TiledCoverage.java | 10 +--
.../org/apache/sis/storage/gdal/TiledResource.java | 4 +-
8 files changed, 76 insertions(+), 76 deletions(-)
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 48750c39b0..1eef4ee1a0 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
@@ -23,7 +23,7 @@ import java.awt.Point;
import java.awt.image.Raster;
import static java.lang.Math.toIntExact;
import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.tiling.TiledGridResource;
+import org.apache.sis.storage.tiling.TiledGridCoverageResource;
import org.apache.sis.storage.geotiff.inflater.Inflater;
import org.apache.sis.image.DataType;
import org.apache.sis.image.internal.shared.RasterFactory;
@@ -99,7 +99,7 @@ final class CompressedSubset extends DataSubset {
* @throws ArithmeticException if the number of tiles overflows 32 bits integer arithmetic.
*/
@SuppressWarnings("LocalVariableHidesMemberVariable")
- CompressedSubset(final DataCube source, final TiledGridResource.Subset subset) throws DataStoreException {
+ CompressedSubset(final DataCube source, final TiledGridCoverageResource.Subset subset) throws DataStoreException {
super(source, subset);
long afterLastBand = sourceScanlineStride - sourcePixelStride;
final int between = Math.multiplyExact(sourcePixelStride, Math.toIntExact(getSubsampling(xDimension) - 1));
@@ -200,7 +200,7 @@ final class CompressedSubset extends DataSubset {
* in the bank. The `pixelsPerElement` factor is usually 1, except when more than one pixel is packed in
* each single primitive type (e.g. 8 bits per byte in bilevel image). The `head` needs to be a multiple
* of `pixelsPerElement`; this restriction is documented in `Inflater.skip(long)` and should have been
- * verified by `TiledGridResource`.
+ * verified by `TiledGridCoverageResource`.
*/
final int pixelsPerElement = getPixelsPerElement(); // Always ≥ 1 and usually = 1.
assert (head % pixelsPerElement) == 0 : head;
diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
index 79596fa269..3c408c4e70 100644
--- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
+++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
@@ -31,7 +31,7 @@ import org.apache.sis.storage.geotiff.base.Resources;
import org.apache.sis.storage.geotiff.base.Predictor;
import org.apache.sis.storage.geotiff.base.Compression;
import org.apache.sis.storage.tiling.TiledGridCoverage;
-import org.apache.sis.storage.tiling.TiledGridResource;
+import org.apache.sis.storage.tiling.TiledGridCoverageResource;
import org.apache.sis.storage.base.StoreResource;
import org.apache.sis.math.Vector;
@@ -47,7 +47,7 @@ import org.apache.sis.math.Vector;
*
* @author Martin Desruisseaux (Geomatys)
*/
-abstract class DataCube extends TiledGridResource implements StoreResource {
+abstract class DataCube extends TiledGridCoverageResource implements StoreResource {
/**
* The GeoTIFF reader which contain this {@code DataCube}.
* Used for fetching information like the input channel and where to report warnings.
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 09b2d4d990..d3ce404665 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
@@ -45,7 +45,7 @@ import org.apache.sis.image.internal.shared.RasterFactory;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.DataStoreContentException;
import org.apache.sis.storage.tiling.TiledGridCoverage;
-import org.apache.sis.storage.tiling.TiledGridResource;
+import org.apache.sis.storage.tiling.TiledGridCoverageResource;
import org.apache.sis.storage.geotiff.base.Resources;
import org.apache.sis.storage.geotiff.reader.ReversedBitsChannel;
import org.apache.sis.io.stream.Region;
@@ -158,7 +158,7 @@ class DataSubset extends TiledGridCoverage implements Localized {
* @param subset description of the {@code owner} subset to cover.
* @throws ArithmeticException if the number of tiles overflows 32 bits integer arithmetic.
*/
- DataSubset(final DataCube source, final TiledGridResource.Subset subset) throws DataStoreException {
+ DataSubset(final DataCube source, final TiledGridCoverageResource.Subset subset) throws DataStoreException {
super(subset);
this.source = source;
this.numTiles = toIntExact(source.getNumTiles());
@@ -496,7 +496,7 @@ class DataSubset extends TiledGridCoverage implements Localized {
* The default implementation in this base class assumes uncompressed data without band subset.
* Subsampling on the <var>X</var> axis is not supported if the image has interleaved pixels.
* Packed pixels (é.g. bilevel images with 8 pixels per byte) are not supported.
- * Those restrictions are verified by {@link DataCube#canReadDirect(TiledGridResource.Subset)}.
+ * Those restrictions are verified by {@link DataCube#canReadDirect(TiledGridCoverageResource.Subset)}.
* Subclasses must override for handling decompression or for resolving above-cited limitations.
*
* @todo It is possible to relax a little bit some restrictions. If the tile width is a divisor
@@ -516,7 +516,7 @@ class DataSubset extends TiledGridCoverage implements Localized {
* @throws RuntimeException if the Java2D image cannot be created for another reason
* (too many exception types to list them all).
*
- * @see DataCube#canReadDirect(TiledGridResource.Subset)
+ * @see DataCube#canReadDirect(TiledGridCoverageResource.Subset)
*/
Raster readSlice(final long[] offsets, final long[] byteCounts, final long[] lower, final long[] upper,
final long[] subsampling, final Point location) throws IOException, DataStoreException
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java
index 40e9648e9d..68a989962a 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java
@@ -70,7 +70,7 @@ import org.opengis.coordinate.MismatchedDimensionException;
* Conversions can be done by {@link #coverageToResourceCoordinate(long, int)}.
*
* <p><b>Design note:</b> {@code TiledGridCoverage} uses the same cell coordinates as the originating
- * {@link TiledGridResource} (when no subsampling) because those two classes use {@code long} integers.
+ * {@link TiledGridCoverageResource} (when no subsampling) because those two classes use {@code long} integers.
* There is no integer overflow to avoid, contrarily to tile indices described below.</p>
*
* <h2>Tile matrix coordinate (<abbr>TMC</abbr>)</h2>
@@ -86,7 +86,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* The area to read in unit of the full coverage (without subsampling).
* This is the intersection between user-specified domain and the source
- * {@link TiledGridResource} domain, expanded to an integer number of chunks.
+ * {@link TiledGridCoverageResource} domain, expanded to an integer number of chunks.
* A chunk size is usually a tile size, but not necessarily as there is other
* criteria to take in account such as "atom" size and subsampling.
*
@@ -104,7 +104,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
* However, there is a few exceptions where the read extent should not be forced to the tile size:
*
* <ul>
- * <li>If the image is untiled, then the {@link org.apache.sis.storage.base.TiledGridResource.Subset}
+ * <li>If the image is untiled, then the {@link org.apache.sis.storage.base.TiledGridCoverageResource.Subset}
* constructor assumes that only the requested region of the tile will be read.</li>
* <li>If the tile is truncated on the storage as well
* (note: this is rare. GeoTIFF for example always stores whole tiles).</li>
@@ -123,17 +123,17 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* Size of all tiles in the domain of this {@code TiledGridCoverage}, without sub-sampling.
- * All coverages created from the same {@link TiledGridResource} shall have the same tile size values.
+ * All coverages created from the same {@link TiledGridCoverageResource} shall have the same tile size values.
* The length of this array is the number of dimensions in the source {@link GridExtent}.
* This is often {@value #BIDIMENSIONAL} but can also be more.
*
* <h4>What is a "virtual" size</h4>
* The tile size stored in this field is usually the size of tiles used by the binary encoding of the file
- * which is read by {@link TiledGridResource}. However, this tile size may differ in two circumstances.
+ * which is read by {@link TiledGridCoverageResource}. However, this tile size may differ in two circumstances.
* In such case, this tile size is said "virtual".
*
* <h5>Tiles coalescence</h5>
- * The first circumstance is when the {@link TiledGridResource} subclass
+ * The first circumstance is when the {@link TiledGridCoverageResource} subclass
* decided to coalesce many tiles from the file in bigger tiles in memory.
* This is sometime useful when a sub-sampling is applied,
* for avoiding that the sub-sampled tiles become too small.
@@ -151,7 +151,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* Values by which to multiply each tile coordinates for obtaining the index in the tile vector.
* The length of this array is the same as {@link #virtualTileSize}. All coverages created from
- * the same {@link TiledGridResource} have the same stride values.
+ * the same {@link TiledGridCoverageResource} have the same stride values.
*/
private final int[] tileStrides;
@@ -163,7 +163,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* The Tile Matrix Coordinates (<abbr>TMC</abbr>) that tile (0,0) of this coverage
- * would have in the originating {@code TiledGridResource}.
+ * would have in the originating {@code TiledGridCoverageResource}.
* This is the value to subtract from tile indices computed from cell coordinates.
*
* <p>The current implementation assumes that the tile (0,0) in the resource starts
@@ -193,8 +193,8 @@ public abstract class TiledGridCoverage extends GridCoverage {
private final long[] subsampling, subsamplingOffsets;
/**
- * Indices of {@link TiledGridResource} bands which have been retained for
- * inclusion in this {@code TiledGridCoverage}, in strictly increasing order.
+ * Indices of {@link TiledGridCoverageResource} bands which have been retained
+ * for inclusion in this {@code TiledGridCoverage}, in strictly increasing order.
* An included band is stored in memory but not necessarily visible to users,
* depending on how the {@link SampleModel} uses each band.
* This array is {@code null} if all bands shall be included.
@@ -208,7 +208,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* The dimension of the grid which is mapped to the <var>x</var> axis (column indexes) in rendered images.
* This is the value of the {@code xDimension} argument in the last call to the
- * {@link TiledGridResource#setRasterDimension(int, int)} method before this coverage has been constructed.
+ * {@link TiledGridCoverageResource#setRasterDimension(int, int)} method before this coverage has been constructed.
* This value is usually 0.
*
* @see #readTiles(TileIterator)
@@ -218,7 +218,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* The dimension of the grid which is mapped to the <var>y</var> axis (row indexes) in rendered images.
* This is the value of the {@code yDimension} argument in the last call to the
- * {@link TiledGridResource#setRasterDimension(int, int)} method before this coverage has been constructed.
+ * {@link TiledGridCoverageResource#setRasterDimension(int, int)} method before this coverage has been constructed.
* This value is usually 1.
*
* @see #readTiles(TileIterator)
@@ -227,14 +227,14 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* Cache of rasters read by this {@code TiledGridCoverage}. This cache may be shared with other coverages
- * created for the same {@link TiledGridResource} resource. For each value, the raster {@code minX} and
+ * created for the same {@link TiledGridCoverageResource} resource. For each value, the raster {@code minX} and
* {@code minY} values can be anything, depending which {@code TiledGridCoverage} was first to load the tile.
*
- * @see TiledGridResource#rasters
+ * @see TiledGridCoverageResource#rasters
* @see AOI#getCachedTile()
* @see #createCacheKey(int)
*/
- private final WeakValueHashMap<TiledGridResource.CacheKey, Raster> rasters;
+ private final WeakValueHashMap<TiledGridCoverageResource.CacheKey, Raster> rasters;
/**
* The sample model for all rasters. The width and height of this sample model are computed
@@ -242,14 +242,14 @@ public abstract class TiledGridCoverage extends GridCoverage {
* The components may be a subset of the bands in the resource if the user requested to read
* only a subset of those bands.
*
- * @see TiledGridResource#getSampleModel(int[])
+ * @see TiledGridCoverageResource#getSampleModel(int[])
*/
protected final SampleModel model;
/**
* The Java2D color model for images rendered from this coverage.
*
- * @see TiledGridResource#getColorModel(int[])
+ * @see TiledGridCoverageResource#getColorModel(int[])
*/
protected final ColorModel colors;
@@ -257,26 +257,26 @@ public abstract class TiledGridCoverage extends GridCoverage {
* The values to use for filling empty spaces in rasters, or {@code null} if zero in all bands.
* If non-null, the array length is equal to the number of bands.
*
- * @see TiledGridResource#getFillValues(int[])
+ * @see TiledGridCoverageResource#getFillValues(int[])
*/
protected final Number[] fillValues;
/**
* Whether the reading of tiles is deferred until {@link RenderedImage#getTile(int, int)} is invoked.
- * This is true if the user explicitly {@linkplain TiledGridResource#setLoadingStrategy requested such
- * deferred loading strategy} and the resource considers that it is worth to do so.
+ * This is true if the user explicitly {@linkplain TiledGridCoverageResource#setLoadingStrategy requested
+ * such deferred loading strategy} and the resource considers that it is worth to do so.
*/
private final boolean deferredTileReading;
/**
* Creates a new tiled grid coverage. This constructor does not load any tile.
*
- * @param subset description of the {@link TiledGridResource} subset to cover.
+ * @param subset description of the {@link TiledGridCoverageResource} subset to cover.
* @throws ArithmeticException if the number of tiles overflows 32 bits integer arithmetic.
*
- * @see TiledGridResource#read(TiledGridResource.Subset)
+ * @see TiledGridCoverageResource#read(TiledGridCoverageResource.Subset)
*/
- protected TiledGridCoverage(final TiledGridResource.Subset subset) {
+ protected TiledGridCoverage(final TiledGridCoverageResource.Subset subset) {
super(subset.domain, subset.ranges);
xDimension = subset.xDimension();
yDimension = subset.yDimension();
@@ -360,10 +360,10 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* Returns the size of all tiles in the domain of this {@code TiledGridCoverage}, without sub-sampling.
- * This is usually the same size as the tiles in the storage which is read by {@link TiledGridResource},
- * but not necessarily. It may be larger if the {@link TiledGridResource} subclass decided to coalesce
- * many real tiles into larger virtual tiles, or it may be smaller when reading a sub-region of an
- * effectively untiled coverage.
+ * This is usually the same size as the tiles in the storage which is read by {@link TiledGridCoverageResource},
+ * but not necessarily. It may be larger if the {@link TiledGridCoverageResource} subclass decided to
+ * coalesce many real tiles into larger virtual tiles, or it may be smaller when reading a sub-region
+ * of an effectively untiled coverage.
*
* @param dimension dimension for which to get tile size.
* @return tile size in the given dimension, without clipping and subsampling.
@@ -408,14 +408,14 @@ public abstract class TiledGridCoverage extends GridCoverage {
}
/**
- * Converts a cell coordinate of this coverage to the {@code TiledGridResource} coordinate space.
+ * Converts a cell coordinate of this coverage to the {@code TiledGridCoverageResource} coordinate space.
* This method removes the subsampling effect,
* i.e. returns the coordinate that we would have if this coverage was at full resolution.
* The result is the given {@code coordinate} value unchanged when there is no subsampling.
*
* @param coordinate coordinate in this {@code TiledGridCoverage} domain.
* @param dimension the dimension of the coordinate to convert.
- * @return coordinate in the {@code TiledGridResource} domain with no subsampling applied.
+ * @return coordinate in the {@code TiledGridCoverageResource} domain with no subsampling applied.
* @throws ArithmeticException if the coordinate cannot be represented as a 64 bits integer.
*/
protected final long coverageToResourceCoordinate(final long coordinate, final int dimension) {
@@ -423,12 +423,12 @@ public abstract class TiledGridCoverage extends GridCoverage {
}
/**
- * Converts a cell coordinate from the {@code TiledGridResource} to this coverage coordinate space.
+ * Converts a cell coordinate from the {@code TiledGridCoverageResource} to this coverage coordinate space.
* This method is the converse of {@link #coverageToResourceCoordinate(long, int)}.
* The result is the given {@code coordinate} value unchanged when there is no subsampling.
* If there is a subsampling, the returned value is rounded toward negative infinity.
*
- * @param coordinate coordinate in the {@code TiledGridResource} domain.
+ * @param coordinate coordinate in the {@code TiledGridCoverageResource} domain.
* @param dimension the dimension of the coordinate to convert.
* @return coordinates in this subsampled {@code TiledGridCoverage} domain.
* @throws ArithmeticException if the coordinate cannot be represented as a 64 bits integer.
@@ -454,7 +454,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* Converts a cell coordinate from this {@code TiledGridCoverage} coordinate space to
* the Tile Matrix Coordinate (<abbr>TMC</abbr>) of the tile which contains that cell.
- * The returned <abbr>TMC</abbr> is relative to the full {@link TiledGridResource},
+ * The returned <abbr>TMC</abbr> is relative to the full {@link TiledGridCoverageResource},
* i.e. without subtraction of {@link #tmcOfFirstTile}.
*
* @param coordinate coordinates in this {@code TiledGridCoverage} domain.
@@ -645,7 +645,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
int indexInResultArray;
/**
- * Current iterator position as an index in the vector of tiles in the {@link TiledGridResource}.
+ * Current iterator position as an index in the vector of tiles in the {@link TiledGridCoverageResource}.
* Tiles are assumed stored in a row-major fashion. This field is incremented by calls to {@link #next()}.
* This index is also used as key in the {@link TiledGridCoverage#rasters} map.
*
@@ -869,7 +869,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
*
* <h4>Subsampling</h4>
* If the {@code subsampled} argument is {@code false}, then this method returns coordinates
- * relative to the tile in the originating {@link TiledGridResource}, i.e. without subsampling.
+ * relative to the tile in the originating {@link TiledGridCoverageResource}, i.e. without subsampling.
* If {@code subsampled} is {@code true}, then this method returns coordinates relative to the
* {@linkplain #createRaster() raster}, i.e. with {@linkplain #getSubsampling(int) subsampling}.
*
@@ -1154,7 +1154,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
* @param coordinate pixel coordinate in the {@link RenderedImage} coordinate system.
* @param dimension the dimension of the coordinate to convert.
* @param ceil {@code true} for rounding up, or {@code false} for rounding down.
- * @return cell coordinate in the {@link TiledGridResource} coordinate system.
+ * @return cell coordinate in the {@link TiledGridCoverageResource} coordinate system.
*/
private long resourceToImage(long coordinate, final int dimension, final boolean ceil) {
coordinate = subtractExact(coordinate, coverageTileToResourceCell(tileLower[dimension], dimension));
@@ -1171,7 +1171,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
*
* @param coordinate pixel coordinate in the {@link RenderedImage} coordinate system.
* @param dimension the dimension of the coordinate to convert.
- * @return cell coordinate in the {@link TiledGridResource} coordinate system.
+ * @return cell coordinate in the {@link TiledGridCoverageResource} coordinate system.
*/
private long imageToResource(long coordinate, final int dimension) {
coordinate = subtractExact(coordinate, offsetAOI[dimension]); // (0,0) at image origin instead of AOI.
@@ -1358,8 +1358,8 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* Creates the key to use for caching the tile at given index.
*/
- private TiledGridResource.CacheKey createCacheKey(final int indexInTileVector) {
- return new TiledGridResource.CacheKey(indexInTileVector, includedBands, subsampling, subsamplingOffsets);
+ private TiledGridCoverageResource.CacheKey createCacheKey(final int indexInTileVector) {
+ return new TiledGridCoverageResource.CacheKey(indexInTileVector, includedBands, subsampling, subsamplingOffsets);
}
/**
@@ -1374,7 +1374,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
* Caches the given raster. See {@link AOI#cache(Raster)} for more information.
*/
private Raster cacheTile(final int indexInTileVector, final Raster tile) {
- final TiledGridResource.CacheKey key = createCacheKey(indexInTileVector);
+ final TiledGridCoverageResource.CacheKey key = createCacheKey(indexInTileVector);
Raster existing = rasters.put(key, tile);
/*
* If a tile already exists, verify if its layout is compatible with the given tile.
@@ -1402,7 +1402,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
/**
* Returns all tiles in the given area of interest. Tile indices are relative to this {@code TiledGridCoverage}:
* (0,0) is the tile in the upper-left corner of this {@code TiledGridCoverage} (not necessarily the upper-left
- * corner of the image in the {@link TiledGridResource}).
+ * corner of the image in the {@link TiledGridCoverageResource}).
*
* <p>The {@link Raster#getMinX()} and {@code getMinY()} coordinates of returned rasters
* shall start at the values given by {@link TileIterator#getTileOrigin(int)}.
@@ -1414,10 +1414,10 @@ public abstract class TiledGridCoverage extends GridCoverage {
* These dimensions are usually 0 and 1 respectively.</p>
*
* <p>This method must be thread-safe. It is implementer responsibility to ensure synchronization,
- * for example using {@link TiledGridResource#getSynchronizationLock()}.</p>
+ * for example using {@link TiledGridCoverageResource#getSynchronizationLock()}.</p>
*
* @param iterator an iterator over the tiles that intersect the Area Of Interest specified by user.
- * @return tiles decoded from the {@link TiledGridResource}.
+ * @return tiles decoded from the {@link TiledGridCoverageResource}.
* @throws Exception if the tile cannot be created. There is too many possible exceptions for listing all types,
* but the main ones are {@link java.io.IOException} for I/O errors and various {@link RuntimeException}
* subtypes for Java2D errors.
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridResource.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java
similarity index 97%
rename from endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridResource.java
rename to endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java
index 72e2ffc996..3507103dac 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridResource.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverageResource.java
@@ -76,7 +76,7 @@ import org.opengis.coverage.CannotEvaluateException;
* @version 1.7
* @since 1.7
*/
-public abstract class TiledGridResource extends AbstractGridCoverageResource {
+public abstract class TiledGridCoverageResource extends AbstractGridCoverageResource {
/**
* Number of dimensions in a rendered image.
* Used for identifying codes where a two-dimensional slice is assumed.
@@ -88,7 +88,7 @@ public abstract class TiledGridResource extends AbstractGridCoverageResource {
/**
* A key in the {@link #rasters} cache of tiles.
- * Each key shall be unique within its enclosing {@link TiledGridResource} instance.
+ * Each key shall be unique within its enclosing {@link TiledGridCoverageResource} instance.
*/
static final class CacheKey {
/** Index in a row-major array of tiles. */ private final int indexInTileVector;
@@ -131,7 +131,7 @@ public abstract class TiledGridResource extends AbstractGridCoverageResource {
* All tiles loaded by any {@link TiledGridCoverage} created from this resource.
* Keys contains tile indices in a row-major array of tiles.
* For each value, the {@link Raster#getMinX()} and {@code minY} values
- * can be anything, depending which {@link TiledGridResource} was first to load the tile.
+ * can be anything, depending which {@link TiledGridCoverageResource} was first to load the tile.
*
* @see TiledGridCoverage#rasters
* @see TiledGridCoverage.AOI#getCachedTile()
@@ -169,7 +169,7 @@ public abstract class TiledGridResource extends AbstractGridCoverageResource {
*
* @param parent the parent resource, or {@code null} if none.
*/
- protected TiledGridResource(final Resource parent) {
+ protected TiledGridCoverageResource(final Resource parent) {
super(parent);
yDimension = 1;
processor = new GridCoverageProcessor();
@@ -433,12 +433,12 @@ check: if (dataType.isInteger()) {
/**
* Parameters that describe the resource subset to be accepted by the {@link TiledGridCoverage} constructor.
- * Instances of this class are temporary and used only for transferring information from {@link TiledGridResource}
+ * Instances of this class are temporary and used only for transferring information from {@link TiledGridCoverageResource}
* to {@link TiledGridCoverage}. This class does not perform I/O operations.
*/
public final class Subset {
/**
- * The full size of the coverage in the enclosing {@link TiledGridResource}.
+ * The full size of the coverage in the enclosing {@link TiledGridCoverageResource}.
* This is taken from {@link #getGridGeometry()} and does not take sub-sampling in account.
*/
final GridExtent sourceExtent;
@@ -446,7 +446,7 @@ check: if (dataType.isInteger()) {
/**
* The area to read in unit of the full coverage (without subsampling).
* This is the intersection between user-specified domain and enclosing
- * {@link TiledGridResource} domain, expanded to an integer number of chunks.
+ * {@link TiledGridCoverageResource} domain, expanded to an integer number of chunks.
* A chunk size is usually a tile size, but not necessarily as there is other
* criteria to take in account such as "atom" size and subsampling.
*/
@@ -469,7 +469,7 @@ check: if (dataType.isInteger()) {
final List<? extends SampleDimension> ranges;
/**
- * Indices of {@link TiledGridResource} bands which have been retained for inclusion
+ * Indices of {@link TiledGridCoverageResource} bands which have been retained for inclusion
* in the {@link TiledGridCoverage} to construct, in strictly increasing order.
* An "included" band is stored in memory but not necessarily visible to the user,
* because the {@link SampleModel} can be configured for ignoring some bands.
@@ -485,13 +485,13 @@ check: if (dataType.isInteger()) {
/**
* Coordinate conversion from subsampled grid to the grid at full resolution.
- * This array contains the factors by which to divide {@link TiledGridResource}
+ * This array contains the factors by which to divide {@link TiledGridCoverageResource}
* cell coordinates in order to obtain {@link TiledGridCoverage} cell coordinates.
*/
final long[] subsampling;
/**
- * Remainder of the divisions of {@link TiledGridResource} cell coordinates by subsampling factors.
+ * Remainder of the divisions of {@link TiledGridCoverageResource} cell coordinates by subsampling factors.
*/
final long[] subsamplingOffsets;
@@ -528,7 +528,7 @@ check: if (dataType.isInteger()) {
/**
* Cache to use for tiles loaded by the {@link TiledGridCoverage}.
- * It is a reference to {@link TiledGridResource#rasters} if shareable.
+ * It is a reference to {@link TiledGridCoverageResource#rasters} if shareable.
*/
final WeakValueHashMap<CacheKey, Raster> cache;
@@ -539,7 +539,7 @@ check: if (dataType.isInteger()) {
* @param range the range argument specified by user in a call to {@code GridCoverageResource.read(…)}.
*
* @throws ArithmeticException if pixel indices exceed 64 bits integer capacity.
- * @throws DataStoreException if a call to {@link TiledGridResource} method failed.
+ * @throws DataStoreException if a call to {@link TiledGridCoverageResource} method failed.
* @throws RasterFormatException if the sample model is not recognized.
* @throws IllegalArgumentException if an error occurred in an operation
* such as creating the {@code SampleModel} subset for selected bands.
@@ -551,7 +551,7 @@ check: if (dataType.isInteger()) {
/*
* 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.
+ * where the `TiledGridCoverageResource` 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.
*/
diff --git a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
index 2f869225b6..d624101ad3 100644
--- a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
+++ b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
@@ -41,7 +41,7 @@ import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.base.StoreResource;
import org.apache.sis.storage.tiling.TiledGridCoverage;
-import org.apache.sis.storage.tiling.TiledGridResource;
+import org.apache.sis.storage.tiling.TiledGridCoverageResource;
import org.apache.sis.storage.isobmff.ByteRanges;
import org.apache.sis.io.stream.ChannelDataInput;
@@ -52,7 +52,7 @@ import org.apache.sis.io.stream.ChannelDataInput;
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
*/
-final class ImageResource extends TiledGridResource implements StoreResource {
+final class ImageResource extends TiledGridCoverageResource implements StoreResource {
/**
* The data store that produced this resource.
*
@@ -274,10 +274,10 @@ final class ImageResource extends TiledGridResource implements StoreResource {
/**
* Creates a new tiled grid coverage.
*
- * @param subset description of the {@link TiledGridResource} subset to cover.
+ * @param subset description of the {@link TiledGridCoverageResource} subset to cover.
* @throws ArithmeticException if the number of tiles overflows 32 bits integer arithmetic.
*/
- Coverage(TiledGridResource.Subset subset) {
+ Coverage(TiledGridCoverageResource.Subset subset) {
super(subset);
}
@@ -292,7 +292,7 @@ final class ImageResource extends TiledGridResource implements StoreResource {
/**
* Returns all tiles in the given area of interest. Tile indices are relative to this {@code Coverage}:
* (0,0) is the tile in the upper-left corner of this {@code Coverage} (not necessarily the upper-left
- * corner of the image in the {@link TiledGridResource}). This method must be thread-safe.
+ * corner of the image in the {@link TiledGridCoverageResource}). This method must be thread-safe.
*
* @param iterator an iterator over the tiles that intersect the Area Of Interest specified by user.
* @return tiles decoded from the enclosing resource.
diff --git a/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java b/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
index 59c990133b..7fbe56ac17 100644
--- a/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
+++ b/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
@@ -26,7 +26,7 @@ import org.opengis.util.GenericName;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.tiling.TiledGridCoverage;
-import org.apache.sis.storage.tiling.TiledGridResource;
+import org.apache.sis.storage.tiling.TiledGridCoverageResource;
// Test dependencies
import org.apache.sis.image.internal.shared.AssertionMessages;
@@ -47,9 +47,9 @@ final class TiledCoverage extends TiledGridCoverage {
* Creates a new tiled grid coverage.
*
* @param owner the resource from which this coverage has been read.
- * @param subset description of the {@link TiledGridResource} subset to cover.
+ * @param subset description of the {@link TiledGridCoverageResource} subset to cover.
*/
- TiledCoverage(final TiledResource owner, final TiledGridResource.Subset subset) {
+ TiledCoverage(final TiledResource owner, final TiledGridCoverageResource.Subset subset) {
super(subset);
this.owner = owner;
}
@@ -72,12 +72,12 @@ final class TiledCoverage extends TiledGridCoverage {
/**
* Returns all tiles in the given area of interest. Tile indices (0,0) locates the tile in the upper-left corner
- * of this {@code TiledGridCoverage} (not necessarily the upper-left corner of the {@link TiledGridResource}).
+ * of this {@code TiledGridCoverage} (not necessarily the upper-left corner of the {@link TiledGridCoverageResource}).
* The {@link Raster#getMinX()} and {@code getMinY()} coordinates of returned rasters
* shall start at the given {@code iterator.offsetAOI} values.
*
* @param iterator an iterator over the tiles that intersect the Area Of Interest specified by user.
- * @return tiles decoded from the {@link TiledGridResource}.
+ * @return tiles decoded from the {@link TiledGridCoverageResource}.
* @throws ArithmeticException if an integer overflow occurred.
*/
@Override
diff --git a/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java b/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
index 2707239a77..4d4b16c684 100644
--- a/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
+++ b/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
@@ -49,7 +49,7 @@ import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.DataStoreReferencingException;
import org.apache.sis.storage.base.MetadataBuilder;
import org.apache.sis.storage.tiling.TiledGridCoverage;
-import org.apache.sis.storage.tiling.TiledGridResource;
+import org.apache.sis.storage.tiling.TiledGridCoverageResource;
import org.apache.sis.system.Configuration;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.resources.Vocabulary;
@@ -64,7 +64,7 @@ import org.apache.sis.util.resources.Vocabulary;
* @author Quentin Bialota (Geomatys)
* @author Martin Desruisseaux (Geomatys)
*/
-final class TiledResource extends TiledGridResource {
+final class TiledResource extends TiledGridCoverageResource {
/**
* Arbitrary number of pixels for considering a tile as too large.
* Tile larger than this size will be divided in smaller tiles.