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
The following commit(s) were added to refs/heads/geoapi-4.0 by this push: new db9cbe5b13 More extensive documentation on the new `getMetadata()` methods. Provide default implementation using the information provided by existing methods. db9cbe5b13 is described below commit db9cbe5b13561c030d68e9983cd9f05839d60604 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Apr 25 19:29:19 2024 +0200 More extensive documentation on the new `getMetadata()` methods. Provide default implementation using the information provided by existing methods. The `throws DataStoreException` is omitted for now in order to be consistent with other methods such as `getEnvelope()`, `getResolution()`, `getCoordinateReferenceSystem()`, `getTilingScheme()`. The exception could be reintroduced, but we would need to revisit the policy of existing methods as well in order to be consistent. --- .../apache/sis/storage/landsat/MetadataReader.java | 2 +- .../geotiff/reader/ImageMetadataBuilder.java | 2 +- .../apache/sis/storage/base/MetadataBuilder.java | 83 ++++++++++++++++++++-- .../main/org/apache/sis/storage/tiling/Tile.java | 23 +++--- .../org/apache/sis/storage/tiling/TileMatrix.java | 73 ++++++++++++++++--- .../apache/sis/storage/tiling/TileMatrixSet.java | 49 +++++++++++-- .../main/org/apache/sis/storage/wkt/Store.java | 4 +- 7 files changed, 202 insertions(+), 34 deletions(-) diff --git a/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/MetadataReader.java b/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/MetadataReader.java index 4f942e6d54..a9f1c9515a 100644 --- a/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/MetadataReader.java +++ b/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/MetadataReader.java @@ -569,7 +569,7 @@ final class MetadataReader extends MetadataBuilder { case "GRID_CELL_SIZE_PANCHROMATIC": case "GRID_CELL_SIZE_REFLECTIVE": case "GRID_CELL_SIZE_THERMAL": { - addResolution(Double.parseDouble(value)); + addLinearResolution(Double.parseDouble(value)); break; } /* diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/ImageMetadataBuilder.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/ImageMetadataBuilder.java index c21eaf27e7..fd198315c2 100644 --- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/ImageMetadataBuilder.java +++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/ImageMetadataBuilder.java @@ -182,7 +182,7 @@ public final class ImageMetadataBuilder extends MetadataBuilder { * Destination: metadata/identificationInfo/spatialResolution/distance */ if (!Double.isNaN(resolution) && resolutionUnit != null) { - addResolution(resolutionUnit.getConverterTo(Units.METRE).convert(resolution)); + addLinearResolution(resolutionUnit.getConverterTo(Units.METRE).convert(resolution)); } /* * Cell size is relevant only if the Threshholding TIFF tag value is 2. By convention in diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java index 4c17c8e31d..652d2bcf25 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java @@ -30,11 +30,13 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Function; +import java.util.function.BiConsumer; import java.util.logging.Level; import java.net.URI; import java.nio.charset.Charset; import javax.measure.Unit; import javax.measure.quantity.Length; +import javax.measure.IncommensurableException; import org.opengis.util.MemberName; import org.opengis.util.GenericName; import org.opengis.util.InternationalString; @@ -55,6 +57,7 @@ import org.opengis.metadata.quality.Element; import org.opengis.metadata.spatial.*; import org.opengis.referencing.ReferenceSystem; import org.opengis.referencing.cs.CoordinateSystem; +import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.crs.VerticalCRS; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.TransformException; @@ -68,7 +71,6 @@ import org.apache.sis.util.privy.CollectionsExt; import org.apache.sis.util.privy.Strings; import org.apache.sis.util.resources.Vocabulary; import org.apache.sis.metadata.ModifiableMetadata; -import org.apache.sis.geometry.AbstractEnvelope; import org.apache.sis.metadata.iso.*; import org.apache.sis.metadata.iso.acquisition.*; import org.apache.sis.metadata.iso.citation.*; @@ -83,6 +85,9 @@ import org.apache.sis.metadata.iso.spatial.*; import org.apache.sis.metadata.sql.MetadataStoreException; import org.apache.sis.metadata.sql.MetadataSource; import org.apache.sis.metadata.privy.Merger; +import org.apache.sis.referencing.NamedIdentifier; +import org.apache.sis.referencing.privy.AxisDirections; +import org.apache.sis.geometry.AbstractEnvelope; import org.apache.sis.storage.Resource; import org.apache.sis.storage.AbstractResource; import org.apache.sis.storage.AbstractFeatureSet; @@ -969,6 +974,20 @@ public class MetadataBuilder { } } + /** + * Adds a data and/or metadata identifier provided as a generic name. + * + * @param id the identifier, or {@code null} if none. + * @param scope whether the date applies to data, to metadata or to both. + * + * @see #addIdentifier(CharSequence, String, Scope) + */ + public final void addIdentifier(final GenericName id, final Scope scope) { + if (id != null) { + addIdentifier((id instanceof Identifier) ? (Identifier) id : new NamedIdentifier(id), scope); + } + } + /** * Adds a resource (data) identifier, a metadata identifier, or both as they are often the same. * The identifier is added only if {@code code} is non-null, regardless other argument values. @@ -1864,6 +1883,8 @@ public class MetadataBuilder { * <li>{@code metadata/spatialRepresentationInfo/axisDimensionProperties/dimensionName}</li> * <li>{@code metadata/spatialRepresentationInfo/axisDimensionProperties/dimensionSize}</li> * <li>{@code metadata/spatialRepresentationInfo/axisDimensionProperties/resolution}</li> + * <li>{@code metadata/identificationInfo/spatialResolution/distance}</li> + * <li>{@code metadata/identificationInfo/temporalResolution}</li> * <li>{@code metadata/identificationInfo/spatialRepresentationType}</li> * <li>{@code metadata/referenceSystemInfo}</li> * </ul> @@ -1916,11 +1937,65 @@ public class MetadataBuilder { for (int i=0; i<resolution.length; i++) { setAxisResolution(i, resolution[i], (cs != null) ? cs.getAxis(i).getUnit() : null); } + addSpatioTemporalResolution(resolution, cs); } } return true; } + /** + * Adds linear and temporal resolutions computed from an array of resolutions for each CRS axis. + * This method tries to separate the horizontal, vertical and temporal components. + * The horizontal components can be linear or angular. + * Storage locations are: + * + * <ul> + * <li>{@code metadata/identificationInfo/spatialResolution/distance}</li> + * <li>{@code metadata/identificationInfo/temporalResolution}</li> + * </ul> + * + * @param resolution the resolution for each coordinate system axis, or {@code null} if unknown. + * @param cs the coordinate system, or {@code null} if unknown. + */ + public final void addSpatioTemporalResolution(final double[] resolution, final CoordinateSystem cs) { + if (resolution != null && cs != null) try { + final int dimension = Math.min(resolution.length, cs.getDimension()); + for (int i=0; i<dimension; i++) { + final CoordinateSystemAxis axis = cs.getAxis(i); + final Unit<?> unit = axis.getUnit(); + final Unit<?> targetUnit; + final BiConsumer<DefaultResolution,Double> setter; + if (Units.isLinear(unit)) { + targetUnit = Units.METRE; + if (AxisDirections.isVertical(axis.getDirection())) { + setter = DefaultResolution::setVertical; + } else { + setter = DefaultResolution::setDistance; + } + } else if (Units.isAngular(unit)) { + targetUnit = Units.DEGREE; + setter = DefaultResolution::setAngularDistance; + } else if (Units.isTemporal(unit) && AxisDirections.isTemporal(axis.getDirection())) { + targetUnit = Units.DAY; + setter = null; + } else { + continue; + } + final double distance = unit.getConverterToAny(targetUnit).convert(resolution[i]); + if (setter == null) { + addTemporalResolution(distance); + } else if (Double.isFinite(distance)) { + var r = new DefaultResolution(); + setter.accept(r, shared(distance)); + addIfNotPresent(identification().getSpatialResolutions(), r); + } + } + } catch (IncommensurableException e) { + // Should never happen because we verified that the unit was linear or temporal. + Logging.unexpectedException(StoreUtilities.LOGGER, MetadataBuilder.class, "addSpatioTemporalResolution", e); + } + } + /** * Adds a linear resolution in metres. * Storage location is: @@ -1931,8 +2006,8 @@ public class MetadataBuilder { * * @param distance the resolution in metres, or {@code NaN} for no-operation. */ - public final void addResolution(final double distance) { - if (!Double.isNaN(distance)) { + public final void addLinearResolution(final double distance) { + if (Double.isFinite(distance)) { final var r = new DefaultResolution(); r.setDistance(shared(distance)); addIfNotPresent(identification().getSpatialResolutions(), r); @@ -1950,7 +2025,7 @@ public class MetadataBuilder { * @param duration the resolution in days, or {@code NaN} for no-operation. */ public final void addTemporalResolution(final double duration) { - if (!Double.isNaN(duration)) { + if (Double.isFinite(duration)) { addIfNotPresent(identification().getTemporalResolutions(), new SimpleDuration(duration)); } } diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/Tile.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/Tile.java index 2961cedf96..b63b4888f0 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/Tile.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/Tile.java @@ -16,12 +16,13 @@ */ package org.apache.sis.storage.tiling; +import java.util.Optional; +import org.opengis.metadata.Metadata; import org.apache.sis.coverage.grid.GridExtent; import org.apache.sis.coverage.grid.GridGeometry; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.GridCoverageResource; import org.apache.sis.storage.Resource; -import org.opengis.metadata.Metadata; /** @@ -33,7 +34,7 @@ import org.opengis.metadata.Metadata; * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) - * @version 1.2 + * @version 1.5 * * @see TileMatrix#getTiles(GridExtent, boolean) * @@ -66,16 +67,20 @@ public interface Tile { /** * Returns information about this tile. + * The returned metadata may differ from the {@linkplain #getResource() tile resource} metadata. + * For example, it may be a subset containing only the information available without reading the resource. + * The tile metadata may be absent if it does not contain any information that are not already provided by + * the {@link TileMatrix} or {@link TileMatrixSet} metadata. + * + * @return information about this tile. * - * Tile metadata may differ from the tile resource metadata. - * It may be a subset of the resource metadata or it may be unrelated. - * The tile metadata may be null if it does not contain any revelant information or - * that are already in the tile matrix or tile matrix set metadata. + * @see Resource#getMetadata() * - * @return information about this tile. Can be {@code null}. - * @throws DataStoreException if an error occurred while reading the metadata. + * @since 1.5 */ - Metadata getMetadata() throws DataStoreException; + default Optional<Metadata> getMetadata() { + return Optional.empty(); + } /** * Returns information about whether the tile failed to load. diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrix.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrix.java index a6538cdc74..db4d4b9aaf 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrix.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrix.java @@ -18,13 +18,15 @@ package org.apache.sis.storage.tiling; import java.util.Optional; import java.util.stream.Stream; -import org.opengis.referencing.datum.PixelInCell; import org.opengis.util.GenericName; +import org.opengis.metadata.Metadata; +import org.opengis.referencing.datum.PixelInCell; +import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.apache.sis.coverage.grid.GridExtent; import org.apache.sis.coverage.grid.GridGeometry; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.NoSuchDataException; -import org.opengis.metadata.Metadata; +import org.apache.sis.storage.base.MetadataBuilder; /** @@ -37,7 +39,7 @@ import org.opengis.metadata.Metadata; * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) - * @version 1.2 + * @version 1.5 * @since 1.2 */ public interface TileMatrix { @@ -52,16 +54,67 @@ public interface TileMatrix { /** * Returns information about this tile matrix. + * The metadata should contain at least the following information: + * + * <ul class="verbose"> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getCitation() citation} / + * {@link org.apache.sis.metadata.iso.citation.DefaultCitation#getTitle() title}:<br> + * a human-readable designation for this tile matrix.</li> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getCitation() citation} / + * {@link org.apache.sis.metadata.iso.citation.DefaultCitation#getIdentifier() identifier}:<br> + * this {@code TileMatrix} {@linkplain #getIdentifier() identifier}.</li> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getExtents() extent}:<br> + * this {@code TileMatrix} envelope.</li> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getSpatialResolutions() spatialResolution}:<br> + * this {@code TileMatrix} resolution along spatial dimensions.</li> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getTemporalResolutions() temporalResolution}:<br> + * this {@code TileMatrix} resolution along the temporal dimension.</li> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getReferenceSystemInfo() referenceSystemInfo}:<br> + * this {@code TileMatrix} coordinate reference system.</li> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getResourceFormats() resourceFormat}:<br> + * a description of the tile format.</li> + * </ul> + * + * <h4>Note for implementers</h4> + * The default implementation creates a modifiable {@link org.apache.sis.metadata.iso.DefaultMetadata} instance + * with values derived from {@link #getIdentifier()}, {@link #getTilingScheme()} and {@link #getResolution()}. + * Subclasses (not users) can cast and complete those metadata. + * In particular, implementations are encouraged to add the {@code title} and {@code resourceFormat} information. * - * Returned metadata should contain a description of the tile format at path : - * {@code Metadata/identificationInfo/resourceFormat}. - * The tile metadata may be null if it does not contain any revelant information or - * that are already in the tile matrix set metadata. + * @return information about this tile matrix. * - * @return information about this tile matrix. Can be {@code null}. - * @throws DataStoreException if an error occurred while reading the metadata. + * @since 1.5 */ - Metadata getMetadata() throws DataStoreException; + default Metadata getMetadata() { + final var mb = new MetadataBuilder(); + mb.addIdentifier(getIdentifier(), MetadataBuilder.Scope.RESOURCE); + final GridGeometry gg = getTilingScheme(); + if (gg != null) { + if (gg.isDefined(GridGeometry.ENVELOPE)) { + mb.addExtent(gg.getEnvelope(), null); + } + if (gg.isDefined(GridGeometry.CRS)) { + CoordinateReferenceSystem crs = gg.getCoordinateReferenceSystem(); + mb.addReferenceSystem(crs); + mb.addSpatioTemporalResolution(getResolution(), crs.getCoordinateSystem()); + // Do not add `gg.getResolution()` because this is not the pixel resolution. + } + } + return mb.build(); + } /* * There is no `getTileSize()` method because tiles are not necessarily for grid coverages. 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 0370882a1d..4799f8c0f0 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 @@ -18,11 +18,11 @@ package org.apache.sis.storage.tiling; import java.util.Optional; import java.util.SortedMap; -import org.apache.sis.storage.DataStoreException; +import org.opengis.util.GenericName; import org.opengis.geometry.Envelope; import org.opengis.metadata.Metadata; import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.opengis.util.GenericName; +import org.apache.sis.storage.base.MetadataBuilder; /** @@ -47,7 +47,7 @@ import org.opengis.util.GenericName; * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) - * @version 1.2 + * @version 1.5 * @since 1.2 */ public interface TileMatrixSet { @@ -62,14 +62,49 @@ public interface TileMatrixSet { /** * Returns information about this tile matrix set. + * The metadata should contain at least the following information: * - * Returned metadata should contain a description of the tile format at path : - * {@code Metadata/identificationInfo/resourceFormat}. + * <ul class="verbose"> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getCitation() citation} / + * {@link org.apache.sis.metadata.iso.citation.DefaultCitation#getTitle() title}:<br> + * a human-readable designation for this tile matrix set.</li> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getCitation() citation} / + * {@link org.apache.sis.metadata.iso.citation.DefaultCitation#getIdentifier() identifier}:<br> + * this {@code TileMatrixSet} {@linkplain #getIdentifier() identifier}.</li> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getExtents() extent}:<br> + * this {@code TileMatrixSet} {@linkplain #getEnvelope() envelope}.</li> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getReferenceSystemInfo() referenceSystemInfo}:<br> + * this {@code TileMatrixSet} {@linkplain #getCoordinateReferenceSystem() coordinate reference system}.</li> + * <li>{@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getResourceFormats() resourceFormat}:<br> + * a description of the tile format.</li> + * </ul> + * + * <h4>Note for implementers</h4> + * The default implementation creates a modifiable {@link org.apache.sis.metadata.iso.DefaultMetadata} instance with + * values derived from {@link #getIdentifier()}, {@link #getEnvelope()} and {@link #getCoordinateReferenceSystem()}. + * Subclasses (not users) can cast and complete those metadata. + * In particular, implementations are encouraged to add the {@code title} and {@code resourceFormat} information. * * @return information about this tile matrix set. Should not be {@code null}. - * @throws DataStoreException if an error occurred while reading the metadata. + * + * @since 1.5 */ - Metadata getMetadata() throws DataStoreException; + default Metadata getMetadata() { + final var mb = new MetadataBuilder(); + mb.addIdentifier(getIdentifier(), MetadataBuilder.Scope.RESOURCE); + getEnvelope().ifPresent((envelope) -> mb.addExtent(envelope, null)); + mb.addReferenceSystem(getCoordinateReferenceSystem()); + return mb.build(); + } /** * Returns the coordinate reference system of all {@code TileMatrix} instances in this set. diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java index afed1b8705..ef03961fef 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java @@ -152,12 +152,12 @@ final class Store extends URIDataStore { public synchronized Metadata getMetadata() throws DataStoreException { if (metadata == null) { parse(); - final MetadataBuilder builder = new MetadataBuilder(); + final var builder = new MetadataBuilder(); InternationalString name = null; int count = 0; for (final Object object : objects) { if (object instanceof ReferenceSystem) { - final ReferenceSystem rs = (ReferenceSystem) object; + final var rs = (ReferenceSystem) object; builder.addReferenceSystem(rs); name = IdentifiedObjects.getDisplayName(rs); count++;