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 bb9c139b68ecaa1cabac920166fb039b494e11ae Author: Martin Desruisseaux <[email protected]> AuthorDate: Fri Aug 8 14:49:27 2025 +0200 Add a `PlanarImage.XY_DIMENSIONS_KEY` property. It contains the dimensions selected by `GridCoverage.render(GridExtent)`. --- .../apache/sis/coverage/grid/GridCoverage2D.java | 2 +- .../apache/sis/coverage/grid/ImageRenderer.java | 50 ++++++++++++++++------ .../main/org/apache/sis/image/BandSelectImage.java | 22 +++++----- .../main/org/apache/sis/image/ImageOverlay.java | 2 + .../main/org/apache/sis/image/PlanarImage.java | 25 ++++++++++- .../sis/image/PositionalConsistencyImage.java | 6 ++- .../org/apache/sis/image/SourceAlignedImage.java | 7 ++- .../coverage/grid/ConvertedGridCoverageTest.java | 9 ++-- .../sis/coverage/grid/GridCoverage2DTest.java | 4 +- .../org/apache/sis/map/coverage/RenderingData.java | 22 ++++++---- .../main/org/apache/sis/portrayal/Canvas.java | 2 +- 11 files changed, 105 insertions(+), 46 deletions(-) diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java index e3bd525acd..86dd521f02 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage2D.java @@ -191,7 +191,7 @@ public class GridCoverage2D extends GridCoverage { final GridExtent extent = gridGeometry.getExtent(); final int[] imageAxes; if (source instanceof GridCoverage2D) { - final GridCoverage2D gs = (GridCoverage2D) source; + final var gs = (GridCoverage2D) source; xDimension = gs.xDimension; yDimension = gs.yDimension; gridToImageX = gs.gridToImageX; diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ImageRenderer.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ImageRenderer.java index 6a9d10d465..21b99a6306 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ImageRenderer.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ImageRenderer.java @@ -57,6 +57,7 @@ import org.apache.sis.util.ComparisonMode; import org.apache.sis.util.ArraysExt; import org.apache.sis.util.resources.Errors; import org.apache.sis.math.Vector; +import static org.apache.sis.image.PlanarImage.XY_DIMENSIONS_KEY; import static org.apache.sis.image.PlanarImage.GRID_GEOMETRY_KEY; import static org.apache.sis.image.PlanarImage.SAMPLE_DIMENSIONS_KEY; @@ -416,6 +417,8 @@ public class ImageRenderer { * * @return indices of <var>x</var> and <var>y</var> coordinate values in a grid coordinate tuple. * + * @see org.apache.sis.image.PlanarImage#XY_DIMENSIONS_KEY + * * @since 1.3 */ public final int[] getXYDimensions() { @@ -469,6 +472,7 @@ public class ImageRenderer { * The properties recognized by current implementation are: * * <ul> + * <li>{@value org.apache.sis.image.PlanarImage#XY_DIMENSIONS_KEY}.</li> * <li>{@value org.apache.sis.image.PlanarImage#GRID_GEOMETRY_KEY}.</li> * <li>{@value org.apache.sis.image.PlanarImage#SAMPLE_DIMENSIONS_KEY}.</li> * <li>Any property added by calls to {@link #addProperty(String, Object)}.</li> @@ -481,6 +485,7 @@ public class ImageRenderer { */ public Object getProperty(final String key) { switch (key) { + case XY_DIMENSIONS_KEY: return getXYDimensions(); case GRID_GEOMETRY_KEY: return getImageGeometry(GridCoverage2D.BIDIMENSIONAL); case SAMPLE_DIMENSIONS_KEY: return bands.clone(); } @@ -501,12 +506,17 @@ public class ImageRenderer { public void addProperty(final String key, final Object value) { ArgumentChecks.ensureNonNull("key", key); ArgumentChecks.ensureNonNull("value", value); - if (!(GRID_GEOMETRY_KEY.equals(key) || SAMPLE_DIMENSIONS_KEY.equals(key))) { - if (properties == null) { - properties = new Hashtable<>(); - } - if (properties.putIfAbsent(key, value) == null) { - return; + switch (key) { + case XY_DIMENSIONS_KEY: + case GRID_GEOMETRY_KEY: + case SAMPLE_DIMENSIONS_KEY: break; + default: { + if (properties == null) { + properties = new Hashtable<>(); + } + if (properties.putIfAbsent(key, value) == null) { + return; + } } } throw new IllegalArgumentException(Errors.format(Errors.Keys.ElementAlreadyPresent_1, key)); @@ -596,7 +606,7 @@ public class ImageRenderer { */ public void setData(final Vector... data) { ensureExpectedBandCount(data.length, true); - final Buffer[] buffers = new Buffer[data.length]; + final var buffers = new Buffer[data.length]; DataType dataType = null; for (int i=0; i<data.length; i++) { final Vector v = data[i]; @@ -733,6 +743,8 @@ public class ImageRenderer { * The image upper-left corner is located at the position given by {@link #getBounds()}. * The two-dimensional {@linkplain #getImageGeometry(int) image geometry} is stored as * a property associated to the {@value org.apache.sis.image.PlanarImage#GRID_GEOMETRY_KEY} key. + * The dimensions of the source grid that are represented in the image are associated to the + * {@value org.apache.sis.image.PlanarImage#XY_DIMENSIONS_KEY} key. * The sample dimensions are stored as a property associated to the * {@value org.apache.sis.image.PlanarImage#SAMPLE_DIMENSIONS_KEY} key. * @@ -769,11 +781,12 @@ public class ImageRenderer { } final WritableRaster wr = (raster instanceof WritableRaster) ? (WritableRaster) raster : null; if (wr != null && cm != null && (imageX | imageY) == 0) { - return new Untiled(cm, wr, properties, imageGeometry, supplier, bands); + return new Untiled(cm, wr, properties, gridDimensions, imageGeometry, supplier, bands); } if (properties == null) { properties = new Hashtable<>(); } + properties.putIfAbsent(XY_DIMENSIONS_KEY, gridDimensions); properties.putIfAbsent(GRID_GEOMETRY_KEY, (supplier != null) ? new DeferredProperty(supplier) : imageGeometry); properties.putIfAbsent(SAMPLE_DIMENSIONS_KEY, bands); if (wr != null) { @@ -791,6 +804,11 @@ public class ImageRenderer { * in the form {@code if (image instanceof BufferedImage)}. */ private static final class Untiled extends ObservableImage { + /** + * The value associated to the {@value org.apache.sis.image.PlanarImage#XY_DIMENSIONS_KEY} key. + */ + private final int[] gridDimensions; + /** * The value associated to the {@value org.apache.sis.image.PlanarImage#GRID_GEOMETRY_KEY} key, * or {@code null} if not yet computed. @@ -813,12 +831,13 @@ public class ImageRenderer { */ @SuppressWarnings("UseOfObsoleteCollectionType") Untiled(final ColorModel colors, final WritableRaster raster, final Hashtable<?,?> properties, - final GridGeometry geometry, final SliceGeometry supplier, final SampleDimension[] bands) + final int[] gridDimensions, final GridGeometry geometry, final SliceGeometry supplier, final SampleDimension[] bands) { super(colors, raster, false, properties); - this.geometry = geometry; - this.supplier = supplier; - this.bands = bands; + this.gridDimensions = gridDimensions; + this.geometry = geometry; + this.supplier = supplier; + this.bands = bands; } /** @@ -826,8 +845,10 @@ public class ImageRenderer { */ @Override public String[] getPropertyNames() { - return ArraysExt.concatenate(super.getPropertyNames(), - new String[] {GRID_GEOMETRY_KEY, SAMPLE_DIMENSIONS_KEY}); + return ArraysExt.concatenate(super.getPropertyNames(), new String[] { + XY_DIMENSIONS_KEY, + GRID_GEOMETRY_KEY, + SAMPLE_DIMENSIONS_KEY}); } /** @@ -842,6 +863,7 @@ public class ImageRenderer { switch (key) { default: return super.getProperty(key); case SAMPLE_DIMENSIONS_KEY: return bands.clone(); + case XY_DIMENSIONS_KEY: return gridDimensions.clone(); case GRID_GEOMETRY_KEY: { synchronized (this) { if (geometry == null) { diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandSelectImage.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandSelectImage.java index e1bcd92e8f..a3357beb0e 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandSelectImage.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/BandSelectImage.java @@ -46,22 +46,24 @@ import org.apache.sis.image.privy.ObservableImage; * @author Martin Desruisseaux (Geomatys) */ class BandSelectImage extends SourceAlignedImage { - /** - * Properties to inherit from the source images, after bands reduction if applicable. - * - * @see #getProperty(String) - */ - private static final Set<String> INHERITED_PROPERTIES = Set.of( - GRID_GEOMETRY_KEY, POSITIONAL_ACCURACY_KEY, // Properties to forward as-is. - SAMPLE_DIMENSIONS_KEY, SAMPLE_RESOLUTIONS_KEY, STATISTICS_KEY); // Properties to forward after band reduction. - /** * Inherited properties that require band reduction. * Shall be a subset of {@link #INHERITED_PROPERTIES}. * All values must be arrays. */ static final Set<String> REDUCED_PROPERTIES = Set.of( - SAMPLE_DIMENSIONS_KEY, SAMPLE_RESOLUTIONS_KEY, STATISTICS_KEY); + SAMPLE_DIMENSIONS_KEY, + SAMPLE_RESOLUTIONS_KEY, + STATISTICS_KEY); + + /** + * Properties to inherit from the source images, after bands reduction if applicable. + * + * @see #getProperty(String) + */ + private static final Set<String> INHERITED_PROPERTIES = Set.of( + ArraysExt.concatenate(POSITIONAL_PROPERTIES.toArray(String[]::new), // Properties to forward as-is. + REDUCED_PROPERTIES.toArray(String[]::new))); // Properties to forward after band reduction. /** * The selected bands. diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageOverlay.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageOverlay.java index a81e34a308..a52e06d866 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageOverlay.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/ImageOverlay.java @@ -237,6 +237,7 @@ final class ImageOverlay extends MultiSourceImage { * we count their occurrences. */ switch (name) { + case XY_DIMENSIONS_KEY: case GRID_GEOMETRY_KEY: case SAMPLE_DIMENSIONS_KEY: case POSITIONAL_ACCURACY_KEY: count.put(name, n); break; @@ -267,6 +268,7 @@ final class ImageOverlay extends MultiSourceImage { @Override public Object getProperty(final String key) { switch (key) { + case XY_DIMENSIONS_KEY: case GRID_GEOMETRY_KEY: // Fall through case SAMPLE_DIMENSIONS_KEY: return getConstantProperty(key); case POSITIONAL_ACCURACY_KEY: return getCombinedProperty(key, Quantity[].class, (q) -> q.clone(), ImageOverlay::combine, false); diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PlanarImage.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PlanarImage.java index 19c776b1cd..9715ac8945 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PlanarImage.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PlanarImage.java @@ -111,6 +111,23 @@ import org.apache.sis.pending.jdk.JDK18; * @since 1.1 */ public abstract class PlanarImage implements RenderedImage { + /** + * Key for a property identifying the grid dimensions that are represented as a two-dimensional image. + * For an image which is the result of {@linkplain org.apache.sis.coverage.grid.GridCoverage#render + * rendering a two-dimensional slice} of a multi-dimensional grid coverage, this property maps the + * <var>x</var> and <var>y</var> axes of this image to the dimensions of the grid of the source coverage. + * + * <p>The property value is an {@code int[]} array of length 2. + * The value at array index 0 identifies the source grid dimension of the <var>x</var> image axis, which is usually 0. + * The value at array index 1 identifies the source grid dimension of the <var>y</var> image axis, which is usually 1.</p> + * + * @see org.apache.sis.coverage.grid.ImageRenderer#getXYDimensions() + * @see org.apache.sis.coverage.grid.GridExtent#getSubspaceDimensions(int) + * + * @since 1.5 + */ + public static final String XY_DIMENSIONS_KEY = "org.apache.sis.XYDimensions"; + /** * Key for a property defining a conversion from pixel coordinates to "real world" coordinates. * Other information include an envelope in "real world" coordinates and an estimation of pixel resolution. @@ -129,8 +146,9 @@ public abstract class PlanarImage implements RenderedImage { public static final String GRID_GEOMETRY_KEY = "org.apache.sis.GridGeometry"; /** - * Estimation of positional accuracy, typically in metres or pixel units. Pixel positions may have limited accuracy - * in they are computed by {@linkplain org.opengis.referencing.operation.Transformation coordinate transformations}. + * Key for a property giving an estimation of positional accuracy, typically in metres or pixel units. + * Pixel positions may have limited accuracy when they are computed by + * {@linkplain org.opengis.referencing.operation.Transformation coordinate transformations}. * The position may also be inaccurate because of approximation applied for faster rendering. * * <p>Values should be instances of <code>{@link javax.measure.Quantity[]}</code>. The array length @@ -249,6 +267,9 @@ public abstract class PlanarImage implements RenderedImage { * <th>Keys</th> * <th>Values</th> * </tr><tr> + * <td>{@value #XY_DIMENSIONS_KEY}</td> + * <td>Indexes of the dimensions of the source grid which are represented as a rendered image.</td> + * </tr><tr> * <td>{@value #GRID_GEOMETRY_KEY}</td> * <td>Conversion from pixel coordinates to "real world" coordinates.</td> * </tr><tr> diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PositionalConsistencyImage.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PositionalConsistencyImage.java index 34fef74203..dae65538bc 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PositionalConsistencyImage.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PositionalConsistencyImage.java @@ -39,7 +39,10 @@ final class PositionalConsistencyImage extends SourceAlignedImage { * @see #getPropertyNames() */ private static final Set<String> INHERITED_PROPERTIES = Set.of( - GRID_GEOMETRY_KEY, POSITIONAL_ACCURACY_KEY, MASK_KEY); + XY_DIMENSIONS_KEY, + GRID_GEOMETRY_KEY, + POSITIONAL_ACCURACY_KEY, + MASK_KEY); /** * Properties added by this image, no matter if present in source image or not. Must be consistent with @@ -83,6 +86,7 @@ final class PositionalConsistencyImage extends SourceAlignedImage { } case POSITIONAL_ACCURACY_KEY: case GRID_GEOMETRY_KEY: + case XY_DIMENSIONS_KEY: case MASK_KEY: { return getSource().getProperty(key); } diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/SourceAlignedImage.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/SourceAlignedImage.java index 302a8edfb4..4c8388951d 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/SourceAlignedImage.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/SourceAlignedImage.java @@ -55,8 +55,11 @@ abstract class SourceAlignedImage extends ComputedImage { * May be used as the {@code inherit} argument in {@link #filterPropertyNames(String[], Set, String[])}. * Inheriting those properties make sense for operations that do not change pixel coordinates. */ - static final Set<String> POSITIONAL_PROPERTIES = Set.of(GRID_GEOMETRY_KEY, - POSITIONAL_ACCURACY_KEY, ResampledImage.POSITIONAL_CONSISTENCY_KEY); + static final Set<String> POSITIONAL_PROPERTIES = Set.of( + XY_DIMENSIONS_KEY, + GRID_GEOMETRY_KEY, + POSITIONAL_ACCURACY_KEY, + ResampledImage.POSITIONAL_CONSISTENCY_KEY); /** * The color model for this image. May be {@code null}. diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ConvertedGridCoverageTest.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ConvertedGridCoverageTest.java index 6c696e8f90..d743e98f4d 100644 --- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ConvertedGridCoverageTest.java +++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/ConvertedGridCoverageTest.java @@ -23,10 +23,10 @@ import org.opengis.referencing.operation.MathTransform1D; import org.apache.sis.referencing.operation.transform.MathTransforms; import org.apache.sis.referencing.privy.AffineTransform2D; import org.apache.sis.coverage.SampleDimension; +import org.apache.sis.image.PlanarImage; import org.apache.sis.math.MathFunctions; import org.apache.sis.measure.NumberRange; import org.apache.sis.measure.Units; -import static org.apache.sis.image.PlanarImage.SAMPLE_DIMENSIONS_KEY; // Test dependencies import org.junit.jupiter.api.Test; @@ -82,9 +82,10 @@ public final class ConvertedGridCoverageTest extends TestCase { */ private static RenderedImage render(final GridCoverage coverage) { final RenderedImage image = coverage.render(null); - final Object bands = image.getProperty(SAMPLE_DIMENSIONS_KEY); - final var sd = assertInstanceOf(SampleDimension[].class, bands); - assertArrayEquals(coverage.getSampleDimensions().toArray(SampleDimension[]::new), sd); + assertArrayEquals(coverage.getSampleDimensions().toArray(SampleDimension[]::new), + assertInstanceOf(SampleDimension[].class, image.getProperty(PlanarImage.SAMPLE_DIMENSIONS_KEY))); + assertArrayEquals(new int[] {0, 1}, + assertInstanceOf(int[].class, image.getProperty(PlanarImage.XY_DIMENSIONS_KEY))); return image; } diff --git a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridCoverage2DTest.java b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridCoverage2DTest.java index 4721081340..e975d5361a 100644 --- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridCoverage2DTest.java +++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridCoverage2DTest.java @@ -262,8 +262,8 @@ public class GridCoverage2DTest extends TestCase { */ final var singleRow = new GridExtent(GRID_SIZE, 1).translate(0, 1); result = coverage.render(singleRow); - assertInstanceOf(BufferedImage.class, result); - assertPixelsEqual(coverage.render(null), new Rectangle(0, 1, GRID_SIZE, 1), result, null); + assertInstanceOf(BufferedImage.class, result); + assertPixelsEqual(coverage.render(null), new Rectangle(0, 1, GRID_SIZE, 1), result, null); /* * Column extraction: * - Expected size (1,2) is verified by `assertPixelsEqual(…)`. diff --git a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/RenderingData.java b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/RenderingData.java index d2663e45f2..30363b34d3 100644 --- a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/RenderingData.java +++ b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/coverage/RenderingData.java @@ -398,17 +398,21 @@ public class RenderingData implements CloneAccess { final GridGeometry old = dataGeometry; final List<SampleDimension> ranges = coverage.getSampleDimensions(); final RenderedImage image = coverage.render(sliceExtent); - final Object value = image.getProperty(PlanarImage.GRID_GEOMETRY_KEY); final GridGeometry domain; final int[] xyDims; - if (value instanceof GridGeometry) { - domain = (GridGeometry) value; - xyDims = (sliceExtent == null) ? ArraysExt.range(0, BIDIMENSIONAL) - : sliceExtent.getSubspaceDimensions(BIDIMENSIONAL); - } else { - var r = new ImageRenderer(coverage, sliceExtent); - domain = r.getImageGeometry(BIDIMENSIONAL); - xyDims = r.getXYDimensions(); + { // For local scope of image properties. + Object value = image.getProperty(PlanarImage.GRID_GEOMETRY_KEY); + if (value instanceof GridGeometry) { + domain = (GridGeometry) value; + value = image.getProperty(PlanarImage.XY_DIMENSIONS_KEY); + xyDims = (value instanceof int[]) ? (int[]) value + : (sliceExtent != null) ? sliceExtent.getSubspaceDimensions(BIDIMENSIONAL) + : ArraysExt.range(0, BIDIMENSIONAL); + } else { + var r = new ImageRenderer(coverage, sliceExtent); + domain = r.getImageGeometry(BIDIMENSIONAL); + xyDims = r.getXYDimensions(); + } } setImageSpace(domain, ranges, xyDims); // Implies `dataGeometry = domain`. currentSlice = sliceExtent; diff --git a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/portrayal/Canvas.java b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/portrayal/Canvas.java index 363dc5f7ca..9ae081d419 100644 --- a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/portrayal/Canvas.java +++ b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/portrayal/Canvas.java @@ -1055,7 +1055,7 @@ public class Canvas extends Observable implements Localized { */ final GridExtent extent = newValue.getExtent(); final int[] displayDimensions = extent.getSubspaceDimensions(getDisplayDimensions()); - final GeneralEnvelope newBounds = new GeneralEnvelope(getDisplayCRS()); + final var newBounds = new GeneralEnvelope(getDisplayCRS()); for (int i=0; i<displayDimensions.length; i++) { final int s = displayDimensions[i]; newBounds.setRange(i, extent.getLow(s), Math.incrementExact(extent.getHigh(s)));
