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 1476b6c6ec43fda73a1948aeb7276ff6c2e1bd17
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Mon Apr 7 17:58:37 2025 +0200

    Allow to customize the the sample dimensions and grid geometry created by 
GeoHEIF store.
---
 .../sis/storage/geoheif/CoverageBuilder.java       | 38 ++++++++++++++--------
 .../apache/sis/storage/geoheif/GeoHeifStore.java   |  9 ++++-
 .../main/org/apache/sis/storage/geoheif/Image.java |  8 +++++
 .../apache/sis/storage/geoheif/ImageResource.java  |  5 ++-
 .../sis/storage/geoheif/ResourceBuilder.java       |  8 ++---
 .../sis/storage/geoheif/UncompressedImage.java     |  2 +-
 6 files changed, 48 insertions(+), 22 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java
index 4d317121cb..93af7d6001 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java
@@ -26,7 +26,6 @@ import java.util.Collection;
 import java.util.StringJoiner;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
-import java.util.function.Supplier;
 import java.io.IOException;
 import java.nio.ByteOrder;
 import java.awt.Dimension;
@@ -46,8 +45,8 @@ import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.PixelInCell;
 import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.storage.base.MetadataBuilder;
+import org.apache.sis.storage.modifier.CoverageModifier;
 import org.apache.sis.storage.isobmff.Box;
 import org.apache.sis.storage.isobmff.base.ItemInfoEntry;
 import org.apache.sis.storage.isobmff.base.ItemProperties;
@@ -85,6 +84,12 @@ final class CoverageBuilder implements Emptiable {
      */
     final GeoHeifStore store;
 
+    /**
+     * An index of the image to build, for information purpose only.
+     * This is given to {@link CoverageModifier.Source} constructor.
+     */
+    private final int imageIndex;
+
     /**
      * Name or identifier of the resource. This is taken from {@link 
ItemInfoEntry#itemName}
      * if available, or {@link ItemInfoEntry#itemID} otherwise. The item name 
is documented
@@ -198,11 +203,13 @@ final class CoverageBuilder implements Emptiable {
      * Creates a new builder with the given properties.
      *
      * @param  store            the data store which is creating a grid 
coverage.
+     * @param  imageIndex       an index of the image, for information purpose 
only.
      * @param  properties       source of coverage properties for this 
coverage item.
      * @param  duplicatedBoxes  names of boxes that were duplicated. Used for 
logging a warning only once per type of box.
      */
-    CoverageBuilder(final GeoHeifStore store, final ItemProperties.ForID 
properties, final Set<String> duplicatedBoxes) {
+    CoverageBuilder(final GeoHeifStore store, final int imageIndex, final 
ItemProperties.ForID properties, final Set<String> duplicatedBoxes) {
         this.store = store;
+        this.imageIndex = imageIndex;
         unknownBoxes = new LinkedHashMap<>();
         if (properties == null) {
             return;
@@ -355,7 +362,7 @@ final class CoverageBuilder implements Emptiable {
      * @throws RasterFormatException if the sample dimensions or sample model 
cannot be created.
      * @throws DataStoreException if the "grid to <abbr>CRS</abbr>" transform 
cannot be created.
      */
-    final ImageResource build(final String name, final Supplier<Image> image) 
throws DataStoreException {
+    final ImageResource build(final String name, final Image.Supplier image) 
throws DataStoreException {
         this.name = name;
         return new ImageResource(this, null, image);
     }
@@ -477,8 +484,9 @@ final class CoverageBuilder implements Emptiable {
      *
      * @return the sample model (never {@code null}).
      * @throws RasterFormatException if the sample dimensions or sample model 
cannot be created.
+     * @throws DataStoreException if an error occurred in {@link 
CoverageModifier}.
      */
-    public final SampleModel sampleModel() {
+    public final SampleModel sampleModel() throws DataStoreException {
         if (sampleModel == null) {
             sampleDimensions(false);
             if (sampleModel == null) {
@@ -496,8 +504,9 @@ final class CoverageBuilder implements Emptiable {
      *
      * @param  full  {@code true} for creating all objects, or {@code false} 
for creating only the sample model.
      * @throws RasterFormatException if the sample dimensions or sample model 
cannot be created.
+     * @throws DataStoreException if an error occurred in {@link 
CoverageModifier}.
      */
-    private void sampleDimensions(final boolean full) {
+    private void sampleDimensions(final boolean full) throws 
DataStoreException {
         final int nc = (model          == null) ? 0 : model.components.length;
         final int nt = (componentTypes == null) ? 0 : componentTypes.length;
         final int ns = (bitsPerChannel == null) ? 0 : bitsPerChannel.length;
@@ -559,7 +568,8 @@ final class CoverageBuilder implements Emptiable {
                 } else {
                     sb.setName(band);
                 }
-                metadata().addNewBand(sd[band] = sb.build());
+                var source = new CoverageModifier.BandSource(store, 
imageIndex, band, numBands, dataType);
+                metadata().addNewBand(sd[band] = 
store.customizer.customize(source, sb));
                 sb.clear();
             }
             if (bitDepth == 0) {
@@ -648,8 +658,9 @@ final class CoverageBuilder implements Emptiable {
      *
      * @return the sample dimensions for all bands.
      * @throws RasterFormatException if the sample dimensions or sample model 
cannot be created.
+     * @throws DataStoreException if an error occurred in {@link 
CoverageModifier}.
      */
-    public final List<SampleDimension> sampleDimensions() {
+    public final List<SampleDimension> sampleDimensions() throws 
DataStoreException {
         if (sampleDimensions == null) {
             sampleDimensions(true);
         }
@@ -658,16 +669,16 @@ final class CoverageBuilder implements Emptiable {
 
     /**
      * Creates the grid geometry and opportunistically prepares metadata 
related to it.
-     * This method should be invoked exactly once. It may be invoked not at all
-     * when this object is used for building a tile instead of an image.
+     * This method should be invoked exactly once, preferably after {@link 
#sampleDimensions()}.
+     * It may be invoked not at all when this object is used for building a 
tile instead of an image.
      *
      * @todo Need to add information from the {@code ExtraDimensionProperty} 
box.
      *       These information include name, minimum, maximum and resolution.
      *
      * @return the grid geometry.
-     * @throws DataStoreContentException if the "grid to <abbr>CRS</abbr>" 
transform cannot be created.
+     * @throws DataStoreException if the "grid to <abbr>CRS</abbr>" transform 
cannot be created.
      */
-    public final GridGeometry gridGeometry() throws DataStoreContentException {
+    public final GridGeometry gridGeometry() throws DataStoreException {
         final var extent = new GridExtent(width, height);
         MathTransform gridToCRS = null;
         if (affine != null) {
@@ -682,6 +693,7 @@ final class CoverageBuilder implements Emptiable {
         if (gridGeometry.isDefined(GridGeometry.ENVELOPE)) {
             metadata.addExtent(gridGeometry.getEnvelope(), store.listeners());
         }
-        return gridGeometry;
+        var source = new CoverageModifier.Source(store, imageIndex, dataType);
+        return store.customizer.customize(source, gridGeometry);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/GeoHeifStore.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/GeoHeifStore.java
index 4a917c00f1..0bbcd6bbd7 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/GeoHeifStore.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/GeoHeifStore.java
@@ -43,6 +43,7 @@ import org.apache.sis.storage.Resource;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.base.MetadataBuilder;
 import org.apache.sis.storage.base.URIDataStore;
+import org.apache.sis.storage.modifier.CoverageModifier;
 import org.apache.sis.storage.event.StoreListeners;
 import org.apache.sis.storage.isobmff.Root;
 import org.apache.sis.storage.isobmff.Reader;
@@ -124,6 +125,11 @@ public class GeoHeifStore extends DataStore implements 
Aggregate {
      */
     private Resource[] content;
 
+    /**
+     * The user-specified method for customizing the grid geometry and band 
definitions. Never {@code null}.
+     */
+    final CoverageModifier customizer;
+
     /**
      * Creates a new GeoHEIF store from the given file, URL or stream object.
      * This constructor invokes {@link 
StorageConnector#closeAllExcept(Object)},
@@ -138,6 +144,7 @@ public class GeoHeifStore extends DataStore implements 
Aggregate {
         location    = connector.getStorageAs(URI.class);
         path        = connector.getStorageAs(Path.class);
         input       = connector.commit(ChannelImageInputStream.class, 
GeoHeifStoreProvider.NAME);
+        customizer  = CoverageModifier.getOrDefault(connector);
         nameFactory = DefaultNameFactory.provider();
         if (location != null) {
             String filename = 
IOUtilities.filenameWithoutExtension(input.filename);
@@ -249,7 +256,7 @@ public class GeoHeifStore extends DataStore implements 
Aggregate {
             builder.addFormatReaderSIS(GeoHeifStoreProvider.NAME);
             builder.addResourceScope(ScopeCode.COVERAGE, null);
             getIdentifier().ifPresent((id) -> builder.addIdentifier(id, 
MetadataBuilder.Scope.ALL));
-            metadata = builder.buildAndFreeze();
+            metadata = customizer.customize(new CoverageModifier.Source(this), 
builder.build());
         }
         return metadata;
     }
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Image.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Image.java
index fbf5b8152d..75742b29f3 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Image.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Image.java
@@ -75,6 +75,14 @@ abstract class Image {
         // Do NOT invoke `builder.sampleModel()`, because that information is 
not available for all types.
     }
 
+    /**
+     * A supplier of image, used for deferred computation.
+     */
+    @FunctionalInterface
+    interface Supplier {
+        Image get() throws DataStoreException;
+    }
+
     /**
      * Returns the sample model and color model of this image.
      * For uncompressed image, this information is not available and this 
method always throws an exception.
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 417f8f3902..d5f35f50a0 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
@@ -20,7 +20,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.IdentityHashMap;
-import java.util.function.Supplier;
 import java.io.IOException;
 import static java.lang.Math.addExact;
 import static java.lang.Math.multiplyExact;
@@ -125,12 +124,12 @@ final class ImageResource extends TiledGridResource 
implements StoreResource {
      * @throws RasterFormatException if the sample dimensions or sample model 
cannot be created.
      * @throws DataStoreException if the "grid to <abbr>CRS</abbr>" transform 
cannot be created.
      */
-    ImageResource(final CoverageBuilder builder, Image[] tiles, final 
Supplier<Image> image) throws DataStoreException {
+    ImageResource(final CoverageBuilder builder, Image[] tiles, final 
Image.Supplier image) throws DataStoreException {
         super(builder.store);
         this.store       = builder.store;
         identifier       = builder.name();
-        gridGeometry     = builder.gridGeometry();
         sampleDimensions = builder.sampleDimensions();
+        gridGeometry     = builder.gridGeometry();      // Should be after 
`sampleDimensions()`.
         if (tiles == null) {
             // Shall be after the call to `sampleDimensions()`.
             tiles = new Image[] {
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ResourceBuilder.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ResourceBuilder.java
index 70ec018b47..e3bc44b1d1 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ResourceBuilder.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ResourceBuilder.java
@@ -23,7 +23,6 @@ import java.util.Set;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
-import java.util.function.Supplier;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import java.awt.image.RasterFormatException;
@@ -88,7 +87,7 @@ final class ResourceBuilder {
     private final Map<Integer, ItemProperties.ForID> properties;
 
     /**
-     * Information about all resources.
+     * Information about all resources, in the order the were found in the 
file.
      * Keys are resource identifiers, or 0 for the {@linkplain #primaryItem}.
      * Keys are {@link ItemInfoEntry#itemID} values of the associated map 
value.
      * Each list should contain only one item, but this class nevertheless 
accept many.
@@ -321,9 +320,10 @@ final class ResourceBuilder {
                 warning("The \"{0}\" resource is protected.", name);
                 continue;
             }
+            final int imageIndex = (addTo != null ? addTo : resources).size();
             final ItemProperties.ForID itemProperties = 
properties.remove(itemID);
             final CoverageBuilder coverage = 
builders.computeIfAbsent(itemProperties,
-                    (p) -> new CoverageBuilder(store, p, duplicatedBoxes));
+                    (p) -> new CoverageBuilder(store, imageIndex, p, 
duplicatedBoxes));
             if (coverage.reportUnknownBoxes(name)) {
                 // Warning already logged by `reportUnknownBoxes(…)`.
                 continue;
@@ -336,7 +336,7 @@ final class ResourceBuilder {
                 firstBuilder = coverage;
             }
             final ByteReader locator;
-            final Supplier<Image> image;
+            final Image.Supplier image;
             switch (entry.itemType) {
                 default: {
                     warning("Unsupported type " + 
Box.formatFourCC(entry.itemType) + " for the \"{0}\" resource.", name);
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
index 238d6adca4..945520ff7e 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
@@ -66,7 +66,7 @@ final class UncompressedImage extends Image {
      * @param  name     a name that identifies this image, for debugging 
purpose.
      * @throws RasterFormatException if the sample model cannot be created.
      */
-    UncompressedImage(final CoverageBuilder builder, final ByteReader locator, 
final String name) {
+    UncompressedImage(final CoverageBuilder builder, final ByteReader locator, 
final String name) throws DataStoreException {
         super(builder, locator, name);
         sampleModel = builder.sampleModel();
         dataType    = builder.dataType();     // Shall be after 
`sampleModel()`.

Reply via email to