This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 71b882f252 feat(Geotiff): if added resource is tiled, try to reuse 
it's matrix model.
71b882f252 is described below

commit 71b882f252651c47119bdc100fc060cf998a3a25
Author: jsorel <[email protected]>
AuthorDate: Thu Jun 11 13:27:22 2026 +0200

    feat(Geotiff): if added resource is tiled, try to reuse it's matrix model.
    
    Co-authored-by: Martin Desruisseaux <[email protected]>
---
 .../main/module-info.java                          |  6 +-
 .../apache/sis/storage/geotiff/GeoTiffStore.java   | 41 ++++++++---
 .../apache/sis/storage/geotiff/WritableStore.java  | 36 ++++++++-
 .../org/apache/sis/storage/geotiff/Writer.java     | 24 +++++-
 .../apache/sis/storage/geotiff/package-info.java   |  3 +
 .../apache/sis/storage/base/OverviewIterator.java} | 31 ++++----
 .../storage/base/WritableTiledResourceSupport.java | 86 ++++++++++++++++++++++
 7 files changed, 195 insertions(+), 32 deletions(-)

diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/module-info.java 
b/endorsed/src/org.apache.sis.storage.geotiff/main/module-info.java
index e6e3125b05..7171870355 100644
--- a/endorsed/src/org.apache.sis.storage.geotiff/main/module-info.java
+++ b/endorsed/src/org.apache.sis.storage.geotiff/main/module-info.java
@@ -16,12 +16,16 @@
  */
 
 /**
- * GeoTIFF store.
+ * A data store for reading and writing GeoTIFF files.
+ * This module supports big <abbr>TIFF</abbr> and Cloud Optimized GeoTIFF 
(<abbr>COG</abbr>).
  *
  * @author  Rémi Maréchal (Geomatys)
  * @author  Thi Phuong Hao Nguyen (VNSC)
  * @author  Minh Chinh Vu (VNSC)
  * @author  Martin Desruisseaux (Geomatys)
+ * @author  Alexis Manin (Geomatys)
+ * @author  Estelle Idée (Geomatys)
+ * @author  Johann Sorel (Geomatys)
  * @version 1.7
  * @since   0.8
  */
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 97657c40af..c9ef2dbf25 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
@@ -50,6 +50,7 @@ import org.apache.sis.storage.IncompatibleResourceException;
 import org.apache.sis.storage.IllegalNameException;
 import org.apache.sis.storage.metadata.MetadataBuilder;
 import org.apache.sis.storage.base.StoreUtilities;
+import org.apache.sis.storage.base.OverviewIterator;
 import org.apache.sis.storage.base.URIDataStoreOption;
 import org.apache.sis.storage.base.URIDataStoreProvider;
 import org.apache.sis.storage.base.GridResourceWrapper;
@@ -77,12 +78,22 @@ import org.apache.sis.util.resources.Errors;
 
 /**
  * A data store backed by GeoTIFF files.
+ * Each <abbr>TIFF</abbr> file can contains an arbitrary number of images.
+ * Some of these images may be overviews of a full-resolution image, thus 
forming a
+ * pyramid as defined by the Cloud Optimized GeoTIFF (<abbr>COG</abbr>) 
convention.
+ * This store supports the big <abbr>TIFF</abbr> extension.
+ *
+ * <p>While it is possible to create instances of this class directly,
+ * the recommended way is to use {@link GeoTiffStoreProvider} because
+ * the latter may return instances that implement additional interfaces
+ * such as {@link org.apache.sis.storage.WritableAggregate}.</p>
  *
  * @author  Rémi Maréchal (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Thi Phuong Hao Nguyen (VNSC)
  * @author  Alexis Manin (Geomatys)
  * @author  Estelle Idée (Geomatys)
+ * @author  Johann Sorel (Geomatys)
  * @version 1.7
  * @since   0.8
  */
@@ -688,6 +699,16 @@ public class GeoTiffStore extends DataStore implements 
Aggregate {
         }
     }
 
+    /**
+     * Returns the source of overviews for the image which is currently in 
process of being written.
+     *
+     * @param  writer  the default source of overview.
+     * @return the source of overview to use.
+     */
+    OverviewIterator overviews(final OverviewIterator writer) {
+        return writer;
+    }
+
     /**
      * Encodes the given image in the GeoTIFF file.
      * The image is appended after any existing images in the GeoTIFF file.
@@ -713,23 +734,19 @@ public class GeoTiffStore extends DataStore implements 
Aggregate {
             final Reader reader = this.reader;
             final Writer writer = writer();
             writer.synchronize(reader, false);
-            long offsetIFD;
+            long offsetIFD = -1;
             try {
                 offsetIFD = writer.append(image, grid, metadata, false);
-                if (writer.isPyramided) {
-                    while (image.getWidth()  > Writer.OVERVIEW_SIZE ||
-                           image.getHeight() > Writer.OVERVIEW_SIZE)
-                    {
-                        image = writer.processor().overview(image);
-                        offsetIFD = writer.append(image, null, null, true);
-                        // Grid and metadata are null as we don't want to 
repeat metadata in overviews.
-                    }
+                final OverviewIterator it = overviews(writer);
+                while ((image = it.nextOverview(image)) != null) {
+                    offsetIFD = writer.append(image, null, null, true);
+                    // Grid and metadata are null as we don't want to repeat 
metadata in overviews.
                 }
             } finally {
                 writer.synchronize(reader, true);
-            }
-            if (reader != null) {
-                reader.offsetOfWrittenIFD(offsetIFD);
+                if (reader != null && offsetIFD >= 0) {
+                    reader.offsetOfWrittenIFD(offsetIFD);       // Last 
successfully written image.
+                }
             }
             index = writer.imageIndex++;
         } catch (RasterFormatException | ArithmeticException | 
IllegalArgumentException e) {
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/WritableStore.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/WritableStore.java
index 04e5a73e56..a744cc71ab 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/WritableStore.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/WritableStore.java
@@ -22,7 +22,12 @@ import org.apache.sis.storage.WritableAggregate;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.base.OverviewIterator;
 import org.apache.sis.storage.base.WritableAggregateSupport;
+import org.apache.sis.storage.base.WritableTiledResourceSupport;
+import org.apache.sis.storage.tiling.TileMatrixSet;
+import org.apache.sis.storage.tiling.TiledResource;
+import org.apache.sis.util.collection.Containers;
 
 
 /**
@@ -30,8 +35,14 @@ import org.apache.sis.storage.base.WritableAggregateSupport;
  *
  * @author  Erwan Roussel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
+ * @author  Johann Sorel (Geomatys)
  */
 final class WritableStore extends GeoTiffStore implements WritableAggregate {
+    /**
+     * Provider of overviews, or {@code null} if none.
+     */
+    private WritableTiledResourceSupport overviews;
+
     /**
      * Creates a new GeoTIFF store from the given file, URL or stream object.
      * This constructor invokes {@link 
StorageConnector#closeAllExcept(Object)},
@@ -54,13 +65,34 @@ final class WritableStore extends GeoTiffStore implements 
WritableAggregate {
      * @throws DataStoreException if the given resource cannot be stored in 
this {@code Aggregate}.
      */
     @Override
-    public Resource add(final Resource resource) throws DataStoreException {
+    public synchronized Resource add(final Resource resource) throws 
DataStoreException {
         final var helper = new WritableAggregateSupport(this);
         if (resource instanceof Aggregate) {
             return helper.writeComponents((Aggregate) resource);
         }
         final GridCoverageResource gr = helper.asGridCoverage(resource);
-        return append(gr.read(null, null), gr.getMetadata());
+        if (gr instanceof TiledResource) {
+            final TileMatrixSet tms = Containers.peekFirst(((TiledResource) 
gr).getTileMatrixSets());
+            if (tms != null) {
+                overviews = new WritableTiledResourceSupport(gr, tms);
+            }
+        }
+        try {
+            return append(gr.read(null, null), gr.getMetadata());
+        } finally {
+            overviews = null;
+        }
+    }
+
+    /**
+     * Returns the source of overviews for the image which is currently in 
process of being written.
+     *
+     * @param  writer  the default source of overview.
+     * @return the source of overview to use.
+     */
+    @Override
+    final OverviewIterator overviews(final OverviewIterator writer) {
+        return (overviews != null) ? overviews : writer;
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Writer.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Writer.java
index 9e7022f300..5f08e4000d 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Writer.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/Writer.java
@@ -49,6 +49,7 @@ import org.apache.sis.storage.DataStoreReferencingException;
 import org.apache.sis.storage.IncompatibleResourceException;
 import org.apache.sis.storage.ReadOnlyStorageException;
 import org.apache.sis.storage.metadata.MetadataFetcher;
+import org.apache.sis.storage.base.OverviewIterator;
 import org.apache.sis.storage.geotiff.writer.TagValue;
 import org.apache.sis.storage.geotiff.writer.TileMatrix;
 import org.apache.sis.storage.geotiff.writer.GeoEncoder;
@@ -82,7 +83,7 @@ import org.opengis.coverage.CannotEvaluateException;
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Estelle Idée (Geomatys)
  */
-final class Writer extends IOBase implements Flushable {
+final class Writer extends IOBase implements OverviewIterator, Flushable {
     /**
      * Maximal size of the highest overview.
      * Used as a criteria for deciding when to stop creating overviews in a 
pyramided file.
@@ -153,7 +154,7 @@ final class Writer extends IOBase implements Flushable {
      *
      * @see #getFormat()
      */
-    final boolean isPyramided;
+    private final boolean isPyramided;
 
     /**
      * Whether to disable the <abbr>TIFF</abbr> requirement that tile sizes 
are multiple of 16 pixels.
@@ -295,13 +296,30 @@ final class Writer extends IOBase implements Flushable {
      * Returns the processor to use for reformatting the image before to write 
it.
      * The processor is created only when this method is first invoked.
      */
-    final ImageProcessor processor() {
+    private ImageProcessor processor() {
         if (processor == null) {
             processor = new ImageProcessor();
         }
         return processor;
     }
 
+    /**
+     * Returns the next overview level, from finest resolution to coarsest 
resolution.
+     *
+     * @param  previous  the image at finer resolution which has been written 
before this method call.
+     * @return the next overview level, or {@code null} if the iteration is 
finished.
+     * @throws DataStoreException if the resource cannot be read.
+     */
+    @Override
+    public final RenderedImage nextOverview(final RenderedImage previous) 
throws DataStoreException {
+        if (isPyramided) {
+            if (previous.getWidth() > OVERVIEW_SIZE || previous.getHeight() > 
OVERVIEW_SIZE) {
+                return processor().overview(previous);
+            }
+        }
+        return null;
+    }
+
     /**
      * Encodes the given image to the output stream given at construction time.
      * The image is appended after any previous images written before the 
given one.
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 76accbbda4..a40b1ead9c 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,6 +33,9 @@
  * @author  Thi Phuong Hao Nguyen (VNSC)
  * @author  Minh Chinh Vu (VNSC)
  * @author  Martin Desruisseaux (Geomatys)
+ * @author  Alexis Manin (Geomatys)
+ * @author  Estelle Idée (Geomatys)
+ * @author  Johann Sorel (Geomatys)
  * @version 1.7
  * @since   0.8
  */
diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/module-info.java 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/OverviewIterator.java
similarity index 51%
copy from endorsed/src/org.apache.sis.storage.geotiff/main/module-info.java
copy to 
endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/OverviewIterator.java
index e6e3125b05..e3d0f5e26f 100644
--- a/endorsed/src/org.apache.sis.storage.geotiff/main/module-info.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/OverviewIterator.java
@@ -14,23 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.sis.storage.base;
+
+import java.awt.image.RenderedImage;
+import org.apache.sis.storage.DataStoreException;
+
 
 /**
- * GeoTIFF store.
+ * Provider of overviews for writing a pyramided image.
+ * Overviews are returned from finest resolution to coarsest resolution.
+ * They may be computed on the fly or read from an existing source.
  *
- * @author  Rémi Maréchal (Geomatys)
- * @author  Thi Phuong Hao Nguyen (VNSC)
- * @author  Minh Chinh Vu (VNSC)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.7
- * @since   0.8
  */
-module org.apache.sis.storage.geotiff {
-    requires jakarta.xml.bind;
-    requires transitive org.apache.sis.storage;
-
-    provides org.apache.sis.storage.DataStoreProvider
-        with org.apache.sis.storage.geotiff.GeoTiffStoreProvider;
-
-    exports org.apache.sis.storage.geotiff;
+public interface OverviewIterator {
+    /**
+     * Returns the next overview level, from finest resolution to coarsest 
resolution.
+     *
+     * @param  previous  the image at finer resolution which can be used as a 
base for the next overview.
+     * @return the next overview level, or {@code null} if the iteration is 
finished.
+     * @throws DataStoreException if the source of overviews cannot be read.
+     */
+    RenderedImage nextOverview(RenderedImage previous) throws 
DataStoreException;
 }
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/WritableTiledResourceSupport.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/WritableTiledResourceSupport.java
new file mode 100644
index 0000000000..908dc95cad
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/WritableTiledResourceSupport.java
@@ -0,0 +1,86 @@
+/*
+ * 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.base;
+
+import java.awt.image.RenderedImage;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.tiling.TileMatrix;
+import org.apache.sis.storage.tiling.TileMatrixSet;
+
+
+/**
+ * Helper classes for writing a tiled resource.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+public final class WritableTiledResourceSupport implements OverviewIterator {
+    /**
+     * The resource from which to read get the data to write.
+     */
+    private final GridCoverageResource resource;
+
+    /**
+     * Grid geometry of the level with finest resolution.
+     */
+    private final GridGeometry baseGrid;
+
+    /**
+     * Tile matrices from coarser resolution (highest scale denominator)
+     * to most detailed resolution (lowest scale denominator).
+     */
+    private final TileMatrix[] matrices;
+
+    /**
+     * Index of the next level to return. We traverse {@link #matrices} in 
reverse order,
+     * from finest resolution to coarsest resolution and ignoring the finest 
resolution.
+     */
+    private int index;
+
+    /**
+     * Creates a new helper class for writing the given tile matrix set.
+     *
+     * @param  resource  the resource from which to read the levels.
+     * @param  tiling    the tile matrix to write.
+     * @throws DataStoreException if the resource cannot be read.
+     */
+    public WritableTiledResourceSupport(final GridCoverageResource resource, 
final TileMatrixSet tiling) throws DataStoreException {
+        this.resource = resource;
+        this.baseGrid = resource.getGridGeometry();
+        this.matrices = 
tiling.getTileMatrices().values().toArray(TileMatrix[]::new);
+        index = matrices.length - 1;
+    }
+
+    /**
+     * Returns the next overview level, from finest resolution to coarsest 
resolution.
+     *
+     * @param  previous  ignored.
+     * @return the next overview level, or {@code null} if the iteration is 
finished.
+     * @throws DataStoreException if the resource cannot be read.
+     */
+    @Override
+    public final RenderedImage nextOverview(final RenderedImage previous) 
throws DataStoreException {
+        if (--index >= 0) {
+            final TileMatrix matrix = matrices[index];
+            final GridGeometry domain = baseGrid.derive().subgrid(null, 
matrix.getResolution()).build();
+            return resource.read(domain, (int[]) null).render(null);
+        }
+        return null;
+    }
+}

Reply via email to