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 8b4bd320fc Provide more metadata and better names for bands. 8b4bd320fc is described below commit 8b4bd320fc7016b122d7a2ff98b0d789a8200b61 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Mon Apr 18 21:12:39 2022 +0200 Provide more metadata and better names for bands. --- .../apache/sis/coverage/grid/GridCoverage2D.java | 7 ++++- .../sis/internal/coverage/j2d/ImageUtilities.java | 17 ++++------- .../internal/coverage/j2d/ImageUtilitiesTest.java | 8 +++--- .../apache/sis/internal/storage/image/Image.java | 13 ++++++++- .../apache/sis/internal/storage/image/Store.java | 7 +++++ .../sis/internal/storage/image/StoreTest.java | 33 +++++++++++++++++++++- 6 files changed, 66 insertions(+), 19 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java index 3eaf6856b5..aa80d6a0e8 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java @@ -395,7 +395,12 @@ public class GridCoverage2D extends GridCoverage { final RenderedImage data, final int numBands) { if (range == null) { - final short[] names = (data != null) ? ImageUtilities.bandNames(data) : ArraysExt.EMPTY_SHORT; + final short[] names; + if (data != null) { + names = ImageUtilities.bandNames(data.getColorModel(), data.getSampleModel()); + } else { + names = ArraysExt.EMPTY_SHORT; + } final SampleDimension[] sd = new SampleDimension[numBands]; final NameFactory factory = DefaultFactories.forBuildin(NameFactory.class); for (int i=0; i<numBands; i++) { diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java index 200d66ae5f..e95631f54c 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java @@ -264,26 +264,19 @@ public final class ImageUtilities extends Static { } /** - * Returns names of bands based on inspection of the color model. + * Returns names of bands based on inspection of the sample model and color model. * The bands are identified by {@link Vocabulary.Keys} values for * red, green, blue, cyan, magenta, yellow, black, gray, <i>etc</i>. * If a band can not be identified, then its corresponding value is 0. * - * @param image the image for which to get band names (can not be null). + * @param cm the color model for which to get band names, or {@code null} if unknown. + * @param sm the image sample model (can not be null). * @return {@link Vocabulary.Keys} identifying the bands. */ @SuppressWarnings("fallthrough") - public static short[] bandNames(final RenderedImage image) { - final SampleModel sm = image.getSampleModel(); - final int n; - if (sm != null) { - n = sm.getNumBands(); - } else { - // Should not happen since SampleModel is essential, but we try to be robust. - n = image.getTile(image.getMinTileX(), image.getMinTileY()).getNumBands(); - } + public static short[] bandNames(final ColorModel cm, final SampleModel sm) { + final int n = sm.getNumBands(); final short[] keys = new short[n]; - final ColorModel cm = image.getColorModel(); if (cm instanceof IndexColorModel) { /* * IndexColorModel normally uses exactly one band. But SIS has a custom subtype which diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/ImageUtilitiesTest.java b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/ImageUtilitiesTest.java index 458903b19f..eaa094f507 100644 --- a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/ImageUtilitiesTest.java +++ b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/ImageUtilitiesTest.java @@ -38,7 +38,7 @@ import static org.apache.sis.internal.util.Numerics.COMPARISON_THRESHOLD; * Tests {@link ImageUtilities}. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 1.1 * @module */ @@ -103,7 +103,7 @@ public final strictfp class ImageUtilitiesTest extends TestCase { } /** - * Verifies that {@link ImageUtilities#bandNames(RenderedImage)} returns expected band names. + * Verifies that {@link ImageUtilities#bandNames(ColorModel, SampleModel)} returns expected band names. * * @param nde expected number of data elements. This number categorizes the tests in this class. * @param type one of the {@link BufferedImage} {@code TYPE_*} constants. @@ -111,8 +111,9 @@ public final strictfp class ImageUtilitiesTest extends TestCase { */ private static void assertBandNamesEqual(final int nde, final int type, final short... names) { final BufferedImage image = new BufferedImage(1, 1, type); + final ColorModel cm = image.getColorModel(); assertEquals("numDataElements", nde, image.getSampleModel().getNumDataElements()); - assertArrayEquals("bandNames", names, ImageUtilities.bandNames(image)); + assertArrayEquals("bandNames", names, ImageUtilities.bandNames(cm, image.getSampleModel())); /* * The following is more for testing our understanding of the way BufferedImage works. * We want to verify that no matter which BufferedImage.TYPE_* constant we used, values @@ -120,7 +121,6 @@ public final strictfp class ImageUtilitiesTest extends TestCase { */ image.getRaster().setPixel(0, 0, new int[] {10, 20, 30, 40}); // Always RGBA order for this test. final Object data = image.getRaster().getDataElements(0, 0, null); - final ColorModel cm = image.getColorModel(); for (final short k : names) { final int expected, actual; switch (k) { diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/Image.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/Image.java index c803dee70b..a212a1fe9e 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/Image.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/Image.java @@ -26,6 +26,7 @@ import javax.imageio.ImageReader; import javax.imageio.ImageReadParam; import javax.imageio.ImageTypeSpecifier; import org.opengis.util.GenericName; +import org.opengis.util.InternationalString; import org.apache.sis.image.ImageProcessor; import org.apache.sis.coverage.SampleDimension; import org.apache.sis.coverage.grid.GridCoverage; @@ -40,7 +41,9 @@ import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.event.StoreListeners; import org.apache.sis.internal.storage.StoreResource; import org.apache.sis.internal.storage.RangeArgument; +import org.apache.sis.internal.coverage.j2d.ImageUtilities; import org.apache.sis.internal.util.UnmodifiableArrayList; +import org.apache.sis.util.resources.Vocabulary; import org.apache.sis.util.ArraysExt; import org.apache.sis.util.iso.Names; @@ -150,13 +153,21 @@ class Image extends AbstractGridCoverageResource implements StoreResource { final ImageTypeSpecifier type = reader.getRawImageType(imageIndex); final SampleDimension[] bands = new SampleDimension[type.getNumBands()]; final SampleDimension.Builder b = new SampleDimension.Builder(); + final short[] names = ImageUtilities.bandNames(type.getColorModel(), type.getSampleModel()); for (int i=0; i<bands.length; i++) { /* * TODO: we could consider a mechanism similar to org.apache.sis.internal.geotiff.SchemaModifier * if there is a need to customize the sample dimensions. `SchemaModifier` could become a shared * public interface. */ - bands[i] = b.setName(i + 1).build(); + final InternationalString name; + final short k; + if (i < names.length && (k = names[i]) != 0) { + name = Vocabulary.formatInternational(k); + } else { + name = Vocabulary.formatInternational(Vocabulary.Keys.Band_1, i+1); + } + bands[i] = b.setName(name).build(); b.clear(); } sampleDimensions = UnmodifiableArrayList.wrap(bands); diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/Store.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/Store.java index a19b90927b..a3ff6092b8 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/Store.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/Store.java @@ -35,6 +35,7 @@ import javax.imageio.stream.ImageInputStream; import org.opengis.metadata.Metadata; import org.opengis.metadata.maintenance.ScopeCode; import org.opengis.referencing.datum.PixelInCell; +import org.opengis.referencing.operation.TransformException; import org.apache.sis.coverage.grid.GridExtent; import org.apache.sis.coverage.grid.GridGeometry; import org.apache.sis.storage.Aggregate; @@ -43,6 +44,7 @@ import org.apache.sis.storage.GridCoverageResource; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.DataStoreClosedException; import org.apache.sis.storage.DataStoreContentException; +import org.apache.sis.storage.DataStoreReferencingException; import org.apache.sis.storage.UnsupportedStorageException; import org.apache.sis.internal.storage.Resources; import org.apache.sis.internal.storage.PRJDataStore; @@ -364,11 +366,16 @@ loop: for (int convention=0;; convention++) { builder.addFormatName(format); // Does nothing if `format` is null. builder.addResourceScope(ScopeCode.COVERAGE, null); builder.addSpatialRepresentation(null, getGridGeometry(MAIN_IMAGE), true); + if (gridGeometry.isDefined(GridGeometry.ENVELOPE)) { + builder.addExtent(gridGeometry.getEnvelope()); + } addTitleOrIdentifier(builder); builder.setISOStandards(false); metadata = builder.buildAndFreeze(); } catch (IOException e) { throw new DataStoreException(e); + } catch (TransformException e) { + throw new DataStoreReferencingException(e); } return metadata; } diff --git a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/StoreTest.java b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/StoreTest.java index c23bfca33b..42feef4317 100644 --- a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/StoreTest.java +++ b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/image/StoreTest.java @@ -16,6 +16,10 @@ */ package org.apache.sis.internal.storage.image; +import java.io.IOException; +import org.opengis.metadata.Metadata; +import org.opengis.metadata.extent.GeographicBoundingBox; +import org.opengis.metadata.identification.Identification; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.ProbeResult; @@ -23,6 +27,7 @@ import org.apache.sis.test.TestCase; import org.junit.Test; import static org.junit.Assert.*; +import static org.apache.sis.test.TestUtilities.getSingleton; /** @@ -54,5 +59,31 @@ public final strictfp class StoreTest extends TestCase { assertEquals("image/png", r.getMimeType()); } - + /** + * Tests the metadata of the {@code "gradient.png"} file. + * + * @throws DataStoreException if an error occurred while reading the file. + * @throws IOException if an error occurred while creating the image reader instance. + */ + @Test + public void testMetadata() throws DataStoreException, IOException { + try (Store store = new Store(null, testData())) { + assertEquals("gradient", store.getIdentifier().get().toString()); + final Metadata metadata = store.getMetadata(); + final Identification id = getSingleton(metadata.getIdentificationInfo()); + final String format = getSingleton(id.getResourceFormats()).getFormatSpecificationCitation().getTitle().toString(); + assertTrue(format, format.contains("PNG")); + assertEquals("WGS 84", getSingleton(metadata.getReferenceSystemInfo()).getName().getCode()); + final GeographicBoundingBox bbox = (GeographicBoundingBox) + getSingleton(getSingleton(id.getExtents()).getGeographicElements()); + assertEquals( -90, bbox.getSouthBoundLatitude(), STRICT); + assertEquals( +90, bbox.getNorthBoundLatitude(), STRICT); + assertEquals(-180, bbox.getWestBoundLongitude(), STRICT); + assertEquals(+180, bbox.getEastBoundLongitude(), STRICT); + /* + * Verify that the metadata is cached. + */ + assertSame(metadata, store.getMetadata()); + } + } }