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 87cac26081c3e15873c6fdb9f2a2c28eb33f9720
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Aug 8 19:12:55 2025 +0200

    `MemoryGridResource.read(…)` should support more than 2 dimensions.
    Adjust the check for geoid-based realization method.
---
 .../org/apache/sis/coverage/grid/GridExtent.java   |   6 +-
 .../sis/coverage/grid/ResampledGridCoverage.java   |   4 +-
 .../referencing/internal/VerticalDatumTypes.java   |   2 +-
 .../sis/storage/base/MemoryGridResource.java       | 143 ++++++++++++---------
 4 files changed, 92 insertions(+), 63 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
index 5afcbfa8b5..b5e33cb0e9 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
@@ -724,8 +724,10 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
      * @return the number of dimensions where this grid extent has a size 
greater than 1.
      *
      * @see #getSubspaceDimensions(int)
+     *
+     * @since 1.5
      */
-    final int getSubDimension() {
+    public int getDegreesOfFreedom() {
         int n = 0;
         final int dimension = getDimension();
         for (int i=0; i<dimension; i++) {
@@ -1035,6 +1037,8 @@ public class GridExtent implements GridEnvelope, 
LenientComparable, Serializable
      * @return indices of sub-space dimensions, in increasing order in an 
array of length {@code numDim}.
      * @throws SubspaceNotSpecifiedException if there is more than {@code 
numDim} dimensions having a size greater than 1.
      * @throws CannotEvaluateException if this grid extent does not have at 
least {@code numDim} dimensions.
+     *
+     * @see #getDegreesOfFreedom()
      */
     public int[] getSubspaceDimensions(final int numDim) {
         final int m = ensureValidDimension(numDim);
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ResampledGridCoverage.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ResampledGridCoverage.java
index 4e543b4821..f13e8aab52 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ResampledGridCoverage.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/ResampledGridCoverage.java
@@ -198,8 +198,8 @@ final class ResampledGridCoverage extends 
DerivedGridCoverage {
             }
         }
         GridExtent extent = gridGeometry.getExtent();
-        if (extent.getDimension()    < GridCoverage2D.BIDIMENSIONAL ||
-            extent.getSubDimension() > GridCoverage2D.BIDIMENSIONAL)
+        if (extent.getDimension()        < GridCoverage2D.BIDIMENSIONAL ||
+            extent.getDegreesOfFreedom() > GridCoverage2D.BIDIMENSIONAL)
         {
             return this;
         }
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
index ecea0315c6..6f7c9b7c7b 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
@@ -272,7 +272,7 @@ public final class VerticalDatumTypes {
             if (CharSequences.equalsFiltered("Mean Sea Level", name, 
Characters.Filter.LETTERS_AND_DIGITS, true)) {
                 return RealizationMethod.TIDAL;
             }
-            if (name.contains("geoid")) {
+            if (name.regionMatches(true, 0, "geoid", 0, 5)) {
                 return RealizationMethod.GEOID;
             }
         }
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MemoryGridResource.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MemoryGridResource.java
index 9f768646d5..dc4e70c0f2 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MemoryGridResource.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MemoryGridResource.java
@@ -31,7 +31,9 @@ import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridRoundingMode;
 import org.apache.sis.coverage.grid.PixelInCell;
 import org.apache.sis.storage.AbstractGridCoverageResource;
+import org.apache.sis.storage.RasterLoadingStrategy;
 import org.apache.sis.storage.event.StoreListeners;
+import static org.apache.sis.storage.base.TiledGridCoverage.BIDIMENSIONAL;
 
 
 /**
@@ -42,7 +44,7 @@ import org.apache.sis.storage.event.StoreListeners;
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  */
-public final class MemoryGridResource extends AbstractGridCoverageResource {
+public class MemoryGridResource extends AbstractGridCoverageResource {
     /**
      * The grid coverage specified at construction time.
      */
@@ -55,17 +57,24 @@ public final class MemoryGridResource extends 
AbstractGridCoverageResource {
     private final GridCoverageProcessor processor;
 
     /**
-     * The resource identifier, may be null.
+     * The resource identifier, or {@code null} if none.
      */
     private final GenericName identifer;
 
+    /**
+     * Whether to defer the calls to {@link GridCoverage#render(GridExtent)}.
+     * If {@code false}, the calls will be done sooner (if possible)
+     * as if data were read from a file immediately.
+     */
+    private boolean deferredRendering;
+
     /**
      * Creates a new coverage stored in memory.
      *
-     * @param  parent     listeners of the parent resource, or {@code null} if 
none.
-     * @param  identifier resource identifier or {@code null} if none.
-     * @param  coverage   stored coverage retained as-is (not copied). Cannot 
be null.
-     * @param  processor  the grid coverage processor for selecting bands, or 
{@code null} for default.
+     * @param  parent      listeners of the parent resource, or {@code null} 
if none.
+     * @param  identifier  resource identifier, or {@code null} if none.
+     * @param  coverage    stored coverage retained as-is (not copied). Cannot 
be null.
+     * @param  processor   the grid coverage processor for selecting bands, or 
{@code null} for default.
      */
     public MemoryGridResource(final StoreListeners parent, final GenericName 
identifier,
                               final GridCoverage coverage, final 
GridCoverageProcessor processor)
@@ -104,11 +113,29 @@ public final class MemoryGridResource extends 
AbstractGridCoverageResource {
         return coverage.getSampleDimensions();
     }
 
+    /**
+     * Returns an indication about whether {@code read(…)} tries to force the 
loading of data.
+     */
+    @Override
+    public RasterLoadingStrategy getLoadingStrategy() {
+        return deferredRendering ? RasterLoadingStrategy.AT_RENDER_TIME
+                                 : RasterLoadingStrategy.AT_READ_TIME;
+    }
+
+    /**
+     * Sets the preference about whether {@code read(…)} should try to force 
the loading of data.
+     */
+    @Override
+    public boolean setLoadingStrategy(final RasterLoadingStrategy strategy) {
+        deferredRendering = 
!strategy.equals(RasterLoadingStrategy.AT_READ_TIME);
+        return strategy == getLoadingStrategy();
+    }
+
     /**
      * Returns a subset of the wrapped grid coverage. If a non-null grid 
geometry is specified, then
      * this method tries to return a grid coverage matching the given grid 
geometry on a best-effort basis.
-     * In current implementation this is either a {@link 
org.apache.sis.coverage.grid.GridCoverage2D} or
-     * the original grid coverage.
+     * In the current implementation, this is either a {@link 
org.apache.sis.coverage.grid.GridCoverage2D}
+     * or the original grid coverage.
      *
      * @param  domain  desired grid extent and resolution, or {@code null} for 
the whole domain.
      * @param  ranges  0-based indices of sample dimensions to read, or {@code 
null} or an empty sequence for reading them all.
@@ -126,6 +153,9 @@ public final class MemoryGridResource extends 
AbstractGridCoverageResource {
         if (ranges != null && ranges.length != 0) {
             subset = processor.selectSampleDimensions(subset, ranges);
         }
+        if (domain == null) {
+            return subset;
+        }
         /*
          * The given `domain` may use arbitrary `gridToCRS` and `CRS` 
properties.
          * For this simple implementation we need the same `gridToCRS` and 
`CRS`
@@ -136,66 +166,60 @@ public final class MemoryGridResource extends 
AbstractGridCoverageResource {
          * TODO: a future implementation may apply subsampling efficiently,
          *       by adjusting the pixel stride in SampleModel.
          */
-        GridExtent intersection = null;
         final GridGeometry source = subset.getGridGeometry();
-        if (domain == null) {
-            domain = source;
-        } else {
-            intersection = source.derive()
-                    .rounding(GridRoundingMode.ENCLOSING)
-                    .subgrid(domain).getIntersection();             // Take in 
account the change of CRS if needed.
-            if (intersection.contains(source.getExtent())) {
-                intersection = null;                                // Will 
request the whole image.
-                domain = source;
-            }
+        GridExtent intersection = source.derive()
+                .rounding(GridRoundingMode.ENCLOSING)
+                .subgrid(domain).getIntersection();             // Take in 
account the change of CRS if needed.
+        if (deferredRendering || intersection.getDegreesOfFreedom() > 
BIDIMENSIONAL) {
+            return processor.clip(subset, intersection);
         }
         /*
-         * Quick check before to invoke the potentially costly 
`coverage.render(…)` method.
+         * Invoke `GridCoverage.render(GridExtent)- immediately. It simulates 
a "load data at `read(…)` invocation time" strategy.
+         * Note: we would consider to remove all the remaining code in this 
method and unconditionally rely on above clipping.
          */
-        if (intersection == null) {
+        if (intersection.contains(source.getExtent())) {
             return subset;
         }
+        final int dimX, dimY;
+        {   // For local scope of `gridDimensions`.
+            int[] gridDimensions = 
intersection.getSubspaceDimensions(BIDIMENSIONAL);
+            dimX = gridDimensions[0];
+            dimY = gridDimensions[1];
+        }
         /*
          * After `render(…)` execution, the (minX, minY) image coordinates are 
the differences between
-         * the extent that we requested and the one that we got. If that 
differences is not zero, then
+         * the extent that we requested and the one that we got. If these 
differences are not zero,
          * we need to translate the `GridExtent` in order to make it matches 
what we got. But before to
          * apply that translation, we adjust the grid size (because it may add 
another translation).
          */
-        RenderedImage data = subset.render(intersection);
-        if (intersection != null) {
-            final int[]  sd      = intersection.getSubspaceDimensions(2);
-            final int    dimX    = sd[0];
-            final int    dimY    = sd[1];
-            final long   ox      = intersection.getLow(dimX);
-            final long   oy      = intersection.getLow(dimY);
-            final long[] changes = new long[Math.max(dimX, dimY) + 1];
-            for (int i = changes.length; --i >= 0;) {
-                changes[i] = intersection.getSize(i);       // We need only 
the dimensions that may change.
-            }
-            changes[dimX] = data.getWidth();
-            changes[dimY] = data.getHeight();
-            intersection  = intersection.resize(changes);
-            /*
-             * Apply the translation after we resized the grid extent, because 
the resize operation
-             * may have caused an additional translation. We cancel that 
translation with terms that
-             * restore the (ox,oy) lower coordinates before to add the data 
minimum X,Y.
-             */
-            Arrays.fill(changes, 0);
-            changes[dimX] = Math.addExact(ox - intersection.getLow(dimX), 
data.getMinX());
-            changes[dimY] = Math.addExact(oy - intersection.getLow(dimY), 
data.getMinX());
-            intersection  = intersection.translate(changes);
-            /*
-             * If the result is the same intersection as the source coverage,
-             * we can return that coverage directly.
-             */
-            if (intersection.equals(source.getExtent())) {
-                return subset;
-            } else {
-                var crs = source.isDefined(GridGeometry.CRS) ? 
source.getCoordinateReferenceSystem() : null;
-                domain = new GridGeometry(intersection, 
PixelInCell.CELL_CORNER,
-                        source.getGridToCRS(PixelInCell.CELL_CORNER), crs);
-            }
+        final RenderedImage data = subset.render(intersection);
+        final long ox = intersection.getLow(dimX);
+        final long oy = intersection.getLow(dimY);
+        final var changes = new long[Math.max(dimX, dimY) + 1];
+        for (int i = changes.length; --i >= 0;) {
+            changes[i] = intersection.getSize(i);       // We need only the 
dimensions that may change.
+        }
+        changes[dimX] = data.getWidth();
+        changes[dimY] = data.getHeight();
+        intersection  = intersection.resize(changes);
+        /*
+         * Apply the translation after we resized the grid extent, because the 
resize operation
+         * may have caused an additional translation. We cancel that 
translation with terms that
+         * restore the (ox,oy) lower coordinates before to add the data 
minimum X,Y.
+         */
+        Arrays.fill(changes, 0);
+        changes[dimX] = Math.addExact(ox - intersection.getLow(dimX), 
data.getMinX());
+        changes[dimY] = Math.addExact(oy - intersection.getLow(dimY), 
data.getMinX());
+        intersection  = intersection.translate(changes);
+        /*
+         * If the result is the same intersection as the source coverage,
+         * we can return that coverage directly.
+         */
+        if (intersection.equals(source.getExtent())) {
+            return subset;
         }
+        var crs = source.isDefined(GridGeometry.CRS) ? 
source.getCoordinateReferenceSystem() : null;
+        domain = new GridGeometry(intersection, PixelInCell.CELL_CORNER, 
source.getGridToCRS(PixelInCell.CELL_CORNER), crs);
         return new GridCoverageBuilder()
                 .setValues(data)
                 .setDomain(domain)
@@ -212,7 +236,7 @@ public final class MemoryGridResource extends 
AbstractGridCoverageResource {
      */
     @Override
     public boolean equals(final Object obj) {
-        if (obj instanceof MemoryGridResource) {
+        if (obj != null && obj.getClass() == getClass()) {
             final var other = (MemoryGridResource) obj;
             return Objects.equals(identifer, other.identifer) &&
                    coverage.equals(other.coverage)   &&
@@ -236,9 +260,10 @@ public final class MemoryGridResource extends 
AbstractGridCoverageResource {
     }
 
     /**
-     * Returns the string representation of the wrapped coverage.
+     * Returns a string representation of this resource.
+     * The default implementation returns the string representation of the 
wrapped coverage.
      *
-     * @return the string representation of the wrapped coverage.
+     * @return the string representation of this resource.
      */
     @Override
     public String toString() {

Reply via email to