You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2019/10/27 16:25:31 UTC

[sis] 01/03: CoverageSubset clip the area to read to the area of interest specified in the query.

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 bec5a17479b82e8ad3b6164d965b3896d42dde27
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Oct 27 16:52:54 2019 +0100

    CoverageSubset clip the area to read to the area of interest specified in the query.
---
 .../sis/internal/storage/query/CoverageQuery.java  | 62 +++++++------------
 .../sis/internal/storage/query/CoverageSubset.java | 71 +++++++++++++++++-----
 2 files changed, 77 insertions(+), 56 deletions(-)

diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageQuery.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageQuery.java
index b88a673..08724c0 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageQuery.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageQuery.java
@@ -27,6 +27,7 @@ import org.apache.sis.measure.AngleFormat;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.Query;
+import org.apache.sis.storage.UnsupportedQueryException;
 import org.apache.sis.util.ArgumentChecks;
 
 
@@ -86,16 +87,26 @@ public final class CoverageQuery extends Query implements Cloneable {
 
     /**
      * Sets the indices of samples dimensions to read.
+     * A {@code null} value means to read all sample dimensions (no filtering on range).
+     * If non-null, then the {@code range} array shall contain at least one element,
+     * all elements must be positive and no value can be duplicated.
      *
-     * @param  range   0-based indices of sample dimensions to read,
-     *                 or {@code null} or an empty sequence for reading them all.
+     * @param  range   0-based indices of sample dimensions to read, or {@code null} for reading them all.
+     * @throws IllegalArgumentException if the given array is empty or contains negative or duplicated values.
      */
-    public void setRange(final int... range) {
-        this.range = (range != null && range.length != 0) ? range.clone() : null;
+    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
+    public void setRange(int... range) {
+        if (range != null) {
+            range = range.clone();
+            ArgumentChecks.ensureNonEmpty("range", range, 0, Integer.MAX_VALUE, true);
+            // Assign only after we verified that the argument is valid.
+        }
+        this.range = range;
     }
 
     /**
-     * Returns the indices of samples dimensions to read.
+     * Returns the indices of samples dimensions to read, or {@code null} if there is no filtering on range.
+     * If non-null, the returned array shall never be empty.
      *
      * @return 0-based indices of sample dimensions to read, or {@code null} for reading them all.
      */
@@ -104,41 +115,6 @@ public final class CoverageQuery extends Query implements Cloneable {
     }
 
     /**
-     * Converts the given range indices from the indices relative to the sub-coverage
-     * to indices relative to the source coverage.
-     *
-     * @param  caller   the caller, used for error messages only.
-     * @param  request  the range indices to convert, or {@code null} or empty if none.
-     * @return the range of indices relative to the source coverage.
-     * @throws IllegalArgumentException if an index is invalid.
-     */
-    final int[] subrange(final CoverageSubset caller, final int[] request) {
-        if (range == null) {
-            return request;
-        }
-        if (request == null || request.length == 0) {
-            return range.clone();
-        }
-        final int[] sub = new int[request.length];
-        for (int i=0; i<request.length; i++) {
-            final int j = request[i];
-            if (j >= 0 && j < range.length) {
-                sub[i] = range[j];
-            } else {
-                throw new IllegalArgumentException(caller.invalidRange(j));
-            }
-        }
-        return sub;
-    }
-
-    /**
-     * Returns the number of range indices, or 0 if no range sub-setting has been specified.
-     */
-    final int getRangeCount() {
-        return (range != null) ? range.length : 0;
-    }
-
-    /**
      * Set a number of additional cells to read on each border of the source grid coverage.
      * If non-zero, this property expands the {@linkplain #getDomain() domain} to be read
      * by an amount specified in unit of cells of the image to be read. Those cells do not
@@ -171,9 +147,13 @@ public final class CoverageQuery extends Query implements Cloneable {
      *
      * @param  source  the coverage resource to filter.
      * @return a view over the given coverage resource containing only the given domain and range.
+     * @throws UnsupportedQueryException if this query contains filtering options not yet supported.
      */
-    public GridCoverageResource execute(final GridCoverageResource source) {
+    public GridCoverageResource execute(final GridCoverageResource source) throws UnsupportedQueryException {
         ArgumentChecks.ensureNonNull("source", source);
+        if (getSourceDomainExpansion() != 0) {
+            throw new UnsupportedQueryException("Source domain expansion not yet supported.");
+        }
         return new CoverageSubset(source, clone());
     }
 
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageSubset.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageSubset.java
index 2765820..278ec3f 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageSubset.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageSubset.java
@@ -20,6 +20,7 @@ import java.util.List;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.coverage.grid.GridRoundingMode;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.event.StoreListeners;
 import org.apache.sis.storage.DataStoreException;
@@ -31,6 +32,11 @@ import org.apache.sis.internal.util.UnmodifiableArrayList;
 
 /**
  * The result of {@link CoverageQuery#execute(GridCoverageResource)}.
+ * This implementation merges the domain and range specified by the query with
+ * arguments of {@link GridCoverageResource#read(GridGeometry, int...)} method.
+ *
+ * <p>Current version does not yet support non-zero value for
+ * {@link CoverageQuery#getSourceDomainExpansion()}.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
@@ -39,17 +45,20 @@ import org.apache.sis.internal.util.UnmodifiableArrayList;
  */
 final class CoverageSubset extends AbstractGridResource {
     /**
-     * The coverage resource instances to filter.
+     * The coverage resource instances which provides the data.
      */
     private final GridCoverageResource source;
 
     /**
-     * The query for filtering the source coverage.
+     * The domain and range to read from the {@linkplain #source} coverage.
      */
     private final CoverageQuery query;
 
     /**
      * Creates a new coverage resource by filtering the given coverage using the given query.
+     *
+     * @param source  the coverage resource instances which provides the data.
+     * @param query   the domain and range to read from the {@code source} coverage.
      */
     CoverageSubset(final GridCoverageResource source, final CoverageQuery query) {
         super(source instanceof StoreListeners ? (StoreListeners) source : null);
@@ -59,6 +68,9 @@ final class CoverageSubset extends AbstractGridResource {
 
     /**
      * Returns the valid extent of grid coordinates clipped to the area specified in the query.
+     * It should be the geometry of the coverage that we get when invoking {@link #read read(…)}
+     * with {@code null} arguments, but this is not guaranteed.
+     * The returned grid geometry may be approximate.
      *
      * @return extent of grid coordinates clipped to the query.
      * @throws DataStoreException if an error occurred while reading definitions from the underlying data store,
@@ -66,10 +78,25 @@ final class CoverageSubset extends AbstractGridResource {
      */
     @Override
     public GridGeometry getGridGeometry() throws DataStoreException {
-        GridGeometry domain = source.getGridGeometry();
-        final GridGeometry sub = query.getDomain();
-        if (sub != null) try {
-            domain = domain.derive().subgrid(sub).build();
+        return clip(source.getGridGeometry(), query.getDomain(), GridRoundingMode.NEAREST);
+    }
+
+    /**
+     * Clips the given domain to an area of interest. If any argument is null, the other one is returned.
+     *
+     * @param  domain          the domain to clip, or {@code null}.
+     * @param  areaOfInterest  the area of interest, or {@code null}.
+     * @param  rounding        whether to clip to nearest box or an enclosing box.
+     * @return intersection of the given grid geometry.
+     * @throws DataStoreException if the intersection can not be computed.
+     */
+    private GridGeometry clip(final GridGeometry domain, final GridGeometry areaOfInterest,
+            final GridRoundingMode rounding) throws DataStoreException
+    {
+        if (domain == null) return areaOfInterest;
+        if (areaOfInterest == null) return domain;
+        try {
+            return domain.derive().rounding(rounding).subgrid(areaOfInterest).build();
         } catch (IllegalArgumentException | IllegalStateException e) {
             final String msg = Resources.forLocale(getLocale()).getString(Resources.Keys.CanNotIntersectDataWithQuery);
             final Exception cause = getReferencingCause(e);
@@ -79,7 +106,6 @@ final class CoverageSubset extends AbstractGridResource {
                 throw new DataStoreException(msg, e);
             }
         }
-        return domain;
     }
 
     /**
@@ -92,17 +118,17 @@ final class CoverageSubset extends AbstractGridResource {
     @Override
     public List<SampleDimension> getSampleDimensions() throws DataStoreException {
         final List<SampleDimension> dimensions = source.getSampleDimensions();
-        if (query.getRangeCount() == 0) {
+        final int[] range = query.getRange();
+        if (range == null) {
             return dimensions;
         }
-        final int[] range = query.getRange();
         final SampleDimension[] subset = new SampleDimension[range.length];
         for (int i=0; i<range.length; i++) {
             final int j = range[i];
             try {
                 subset[i] = dimensions.get(j);
             } catch (IndexOutOfBoundsException e) {
-                throw new DataStoreException(invalidRange(j), e);
+                throw new DataStoreException(invalidRange(dimensions.size(), j), e);
             }
         }
         return UnmodifiableArrayList.wrap(subset);
@@ -118,18 +144,33 @@ final class CoverageSubset extends AbstractGridResource {
      */
     @Override
     public GridCoverage read(GridGeometry domain, int... range) throws DataStoreException {
-        if (domain == null) {
-            domain = query.getDomain();
+        domain = clip(domain, query.getDomain(), GridRoundingMode.ENCLOSING);
+        final int[] qr = query.getRange();
+        if (range == null) {
+            range = qr;
+        } else if (qr != null) {
+            final int[] sub = new int[range.length];
+            for (int i=0; i<range.length; i++) {
+                final int j = range[i];
+                if (j >= 0 && j < qr.length) {
+                    sub[i] = qr[j];
+                } else {
+                    throw new IllegalArgumentException(invalidRange(qr.length, j));
+                }
+            }
+            range = sub;
         }
-        range = query.subrange(this, range);
         return source.read(domain, range);
     }
 
     /**
      * Creates an exception message for an invalid range index.
+     *
+     * @param size   number of sample dimensions in source coverage.
+     * @param index  the index which is out of bounds.
      */
-    final String invalidRange(final int index) {
+    private String invalidRange(final int size, final int index) {
         return Resources.forLocale(getLocale()).getString(
-                Resources.Keys.InvalidSampleDimensionIndex_2, query.getRangeCount() - 1, index);
+                Resources.Keys.InvalidSampleDimensionIndex_2, size - 1, index);
     }
 }