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 640019466211ac9af699bff4486d95b6dd10b013
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Tue Sep 20 13:05:59 2022 +0200

    If a slice does not intersect the requested extent, try other slice 
candidates.
    It may happen if the "best" slice was selected using only temporal creteria
    but the geographic area of that particular slice does not intersect.
---
 .../sis/coverage/grid/DisjointExtentException.java |  6 +-
 .../aggregate/ConcatenatedGridCoverage.java        | 80 ++++++++++++++++------
 .../sis/storage/aggregate/GridSliceLocator.java    |  2 +-
 .../sis/storage/aggregate/MergeStrategy.java       | 14 ++--
 4 files changed, 73 insertions(+), 29 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DisjointExtentException.java
 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DisjointExtentException.java
index 1aa54b6a0b..aebaa23bd9 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DisjointExtentException.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DisjointExtentException.java
@@ -25,7 +25,7 @@ import org.apache.sis.internal.feature.Resources;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.3
  * @since   1.0
  * @module
  */
@@ -81,8 +81,10 @@ public class DisjointExtentException extends 
IllegalGridGeometryException {
      * @param source   extent of the source.
      * @param request  extent of a slice requested by user.
      * @param dim      index of the dimension having an invalid value.
+     *
+     * @since 1.3
      */
-    DisjointExtentException(final GridExtent source, final GridExtent request, 
final int dim) {
+    public DisjointExtentException(final GridExtent source, final GridExtent 
request, final int dim) {
         this(source.getAxisIdentification(dim, dim),
                 source .getLow(dim), source .getHigh(dim),
                 request.getLow(dim), request.getHigh(dim));
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/ConcatenatedGridCoverage.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/ConcatenatedGridCoverage.java
index db43078431..b3672cffa7 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/ConcatenatedGridCoverage.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/ConcatenatedGridCoverage.java
@@ -18,6 +18,7 @@ package org.apache.sis.storage.aggregate;
 
 import java.util.List;
 import java.util.ArrayList;
+import java.util.logging.Logger;
 import java.awt.image.RenderedImage;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridExtent;
@@ -28,8 +29,10 @@ import org.apache.sis.coverage.grid.DisjointExtentException;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.internal.storage.Resources;
+import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.collection.Cache;
+import org.apache.sis.util.logging.Logging;
 
 // Branch-dependent imports
 import org.opengis.coverage.CannotEvaluateException;
@@ -242,6 +245,8 @@ final class ConcatenatedGridCoverage extends GridCoverage {
 
     /**
      * Returns a two-dimensional slice of grid data as a rendered image.
+     * Invoking this method may cause the loading of data from {@link 
ConcatenatedGridResource}.
+     * Most recently used slices are cached for future invocations of this 
method.
      *
      * @param  extent  a subspace of this grid coverage extent where all 
dimensions except two have a size of 1 cell.
      * @return the grid slice as a rendered image. Image location is relative 
to {@code sliceExtent}.
@@ -255,11 +260,10 @@ final class ConcatenatedGridCoverage extends GridCoverage 
{
         } else {
             extent = gridGeometry.getExtent();
         }
+        final GridGeometry   request;           // The geographic area and 
temporal extent requested by user.
+        final GridGeometry[] candidates;        // Grid geometry of all slices 
that intersect the request.
         final int count = upper - lower;
-        if (count != 1) {
-            if (count == 0) {
-                throw new DisjointExtentException();
-            }
+        if (count > 1) {
             if (strategy == null) {
                 /*
                  * Can not infer a slice. If the user specified a single slice 
but that slice
@@ -278,35 +282,71 @@ final class ConcatenatedGridCoverage extends GridCoverage 
{
                 throw new 
SubspaceNotSpecifiedException(Resources.format(message, arguments));
             }
             /*
-             * Select a slice using the user-specified merge strategy.
-             * Current implementation does only a selection; a future version 
may allow real merges.
+             * Prepare a list of slice candidates. Later in this method, a 
single slice will be selected
+             * among those candidates using the user-specified merge strategy. 
Elements in `candidates`
+             * array will become null if that candidate did not worked and we 
want to look again among
+             * remaining candidates.
              */
-            final GridGeometry[] geometries = new GridGeometry[count];
             try {
+                request    = new GridGeometry(getGridGeometry(), extent, null);
+                candidates = new GridGeometry[count];
                 for (int i=0; i<count; i++) {
                     final int j = lower + i;
                     final Object slice = slices[j];
-                    geometries[i] = isDeferred(j) ? ((GridCoverageResource) 
slice).getGridGeometry()
+                    candidates[i] = isDeferred(j) ? ((GridCoverageResource) 
slice).getGridGeometry()
                                                   : ((GridCoverage)         
slice).getGridGeometry();
                 }
-                lower += strategy.apply(new GridGeometry(getGridGeometry(), 
extent, null), geometries);
             } catch (DataStoreException | TransformException e) {
                 throw new 
CannotEvaluateException(Resources.format(Resources.Keys.CanNotSelectSlice), e);
             }
+        } else {
+            request    = null;
+            candidates = null;
         }
         /*
-         * Argument have been validated and slice has been located.
-         * If the coverage has not already been loaded, load it now.
+         * The following loop should be executed exactly once. However it may 
happen that the "best" slice
+         * actually does not intersect the requested extent, for example 
because the merge strategy looked
+         * only for temporal intersection and did not saw that the geographic 
extents do not intersect.
          */
-        final GridCoverage coverage;
-        final Object slice = slices[lower];
-        if (!isDeferred(lower)) {
-            coverage = (GridCoverage) slice;    // Should never fail.
-        } else try {
-            coverage = loader.getOrLoad(lower, (GridCoverageResource) 
slice).forConvertedValues(isConverted);
-        } catch (DataStoreException e) {
-            throw new 
CannotEvaluateException(Resources.format(Resources.Keys.CanNotReadSlice_1, 
lower + startAt), e);
+        DisjointExtentException failure = null;
+        if (count > 0) do {
+            int index = lower;
+            if (candidates != null) {
+                final Integer n = strategy.apply(request, candidates);
+                if (n == null) break;
+                candidates[n] = null;
+                index += n;
+            }
+            final Object slice = slices[index];
+            final GridCoverage coverage;
+            if (!isDeferred(index)) {
+                coverage = (GridCoverage) slice;        // This cast should 
never fail.
+            } else try {
+                coverage = loader.getOrLoad(index, (GridCoverageResource) 
slice).forConvertedValues(isConverted);
+            } catch (DataStoreException e) {
+                throw new 
CannotEvaluateException(Resources.format(Resources.Keys.CanNotReadSlice_1, 
index + startAt), e);
+            }
+            /*
+             * At this point, coverage of the "best" slice has been fetched 
from the cache or read from resource.
+             * Delegate the rendering to that coverage, after converting the 
extent from this grid coverage space
+             * to the slice coordinate space. If the coverage said that the 
converted extent does not intersect,
+             * try the "next best" slice until we succeed or until we 
exhausted the candidate list.
+             */
+            try {
+                final RenderedImage image = 
coverage.render(locator.toSliceExtent(extent, index));
+                if (failure != null) {
+                    
Logging.ignorableException(Logger.getLogger(Modules.STORAGE),
+                            ConcatenatedGridCoverage.class, "render", failure);
+                }
+                return image;
+            } catch (DisjointExtentException e) {
+                if (failure == null) failure = e;
+                else failure.addSuppressed(e);
+            }
+        } while (candidates != null);
+        if (failure == null) {
+            failure = new DisjointExtentException(gridGeometry.getExtent(), 
extent, locator.searchDimension);
         }
-        return coverage.render(locator.toSliceExtent(extent, lower));
+        throw failure;
     }
 }
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/GridSliceLocator.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/GridSliceLocator.java
index 786c940fd6..1aaa45d040 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/GridSliceLocator.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/GridSliceLocator.java
@@ -49,7 +49,7 @@ final class GridSliceLocator {
      *
      * @see GroupByTransform#findConcatenatedDimensions()
      */
-    private final int searchDimension;
+    final int searchDimension;
 
     /**
      * Lows grid coordinates of each slice (inclusive) in the search dimension.
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/MergeStrategy.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/MergeStrategy.java
index 6c80c7d835..00fcc2b4fa 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/MergeStrategy.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/MergeStrategy.java
@@ -131,7 +131,7 @@ public final class MergeStrategy {
      * A future version may allow real merge operations.
      *
      * @param  request     the geographic area and temporal extent requested 
by user.
-     * @param  candidates  grid geometry of all slices that intersect the 
request.
+     * @param  candidates  grid geometry of all slices that intersect the 
request. Null elements are ignored.
      * @return index of best slice according the heuristic rules of this 
{@code MergeStrategy}.
      */
     final Integer apply(final GridGeometry request, final GridGeometry[] 
candidates) {
@@ -145,11 +145,13 @@ public final class MergeStrategy {
         }
         for (int i=0; i < candidates.length; i++) {
             final GridGeometry candidate = candidates[i];
-            final Instant[] t = candidate.getTemporalExtent();
-            final int n = t.length;
-            selector.evaluate(candidate.getGeographicExtent().orElse(null),
-                              (n == 0) ? null : t[0],
-                              (n == 0) ? null : t[n-1], i);
+            if (candidate != null) {
+                final Instant[] t = candidate.getTemporalExtent();
+                final int n = t.length;
+                selector.evaluate(candidate.getGeographicExtent().orElse(null),
+                                  (n == 0) ? null : t[0],
+                                  (n == 0) ? null : t[n-1], i);
+            }
         }
         return selector.best();
     }

Reply via email to