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()`.