sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: Addition of GridChang as a helper class for implementation of GridResource.read(GridGeometry, int...).
Date Sat, 15 Dec 2018 17:19:21 GMT
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 afae70fc2fb03ce5717ec6ff234730673a3d5382
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat Dec 15 18:18:47 2018 +0100

    Addition of GridChang as a helper class for implementation of GridResource.read(GridGeometry,
int...).
---
 .../org/apache/sis/coverage/grid/GridChange.java   | 314 +++++++++++++++++++++
 .../org/apache/sis/coverage/grid/GridExtent.java   |   2 +-
 .../org/apache/sis/coverage/grid/GridGeometry.java |   4 +-
 .../org/apache/sis/geometry/EnvelopesTest.java     |  22 ++
 .../org/apache/sis/internal/netcdf/Variable.java   |  11 +-
 .../sis/internal/netcdf/impl/FeaturesInfo.java     |  15 +-
 .../sis/internal/netcdf/impl/VariableInfo.java     |  18 +-
 .../sis/internal/netcdf/ucar/VariableWrapper.java  |  23 +-
 .../apache/sis/storage/netcdf/GridResource.java    |  33 +--
 .../sis/internal/storage/AbstractGridResource.java |  29 --
 10 files changed, 384 insertions(+), 87 deletions(-)

diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java
new file mode 100644
index 0000000..cc38a0f
--- /dev/null
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.coverage.grid;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.io.Serializable;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.operation.NoninvertibleTransformException;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.geometry.Envelopes;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.resources.Vocabulary;
+
+
+/**
+ * Information about the conversion from one grid geometry to another grid geometry.
+ * This class holds the {@link MathTransform}s converting cell coordinates from the
+ * source grid to cell coordinates in the target grid, together with the grid extent
+ * that results from this conversion.
+ *
+ * <div class="note"><b>Usage:</b>
+ * This class can be helpful for implementation of
+ * {@link org.apache.sis.storage.GridCoverageResource#read(GridGeometry, int...)}.
+ * Example:
+ *
+ * {@preformat java
+ *     public GridCoverage read(GridGeometry domain, int... range) throws DataStoreException
{
+ *         GridChange change = new GridChange(domain, getGridGeometry());
+ *         GridExtent toRead = change.getTargetRange();
+ *         int[] subsampling = change.getTargetStrides());
+ *     }
+ * }
+ * </div>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+public class GridChange implements Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -7819047186271172885L;
+
+    /**
+     * The conversions from source grid to target grid, mapping pixel corners or pixel centers.
+     */
+    private final MathTransform mapCorners, mapCenters;
+
+    /**
+     * Intersection of the two grid geometry extents, in units of the target grid geometry.
+     * This is the expected ranges after conversions from source grid coordinates to target
+     * grid coordinates, clipped to target extent and ignoring {@linkplain #scales}.
+     *
+     * @see #getTargetRange()
+     */
+    private final GridExtent targetRange;
+
+    /**
+     * An estimation of the multiplication factors when converting cell coordinates from
source
+     * grid to target grid. Those factors appear in the order of <em>target</em>
grid axes.
+     * May be {@code null} if the conversion is identity.
+     *
+     * @see #getTargetStrides()
+     */
+    private final double[] scales;
+
+    /**
+     * Creates a description of the conversion from given source grid geometry to given target
grid geometry.
+     * The following information are mandatory:
+     * <ul>
+     *   <li>Source {@linkplain GridGeometry#getExtent() extent}.</li>
+     *   <li>Source "{@linkplain GridGeometry#getGridToCRS(PixelInCell) grid to CRS}"
conversion.</li>
+     *   <li>Target "grid to CRS" conversion.</li>
+     * </ul>
+     *
+     * The following information are optional but recommended:
+     * <ul>
+     *   <li>Source coordinate reference system.</li>
+     *   <li>Target {@linkplain GridGeometry#getCoordinateReferenceSystem() coordinate
reference system}.</li>
+     *   <li>Target {@linkplain GridGeometry#getExtent() extent}.</li>
+     * </ul>
+     *
+     * @param  source  the source grid geometry.
+     * @param  target  the target grid geometry.
+     * @throws IncompleteGridGeometryException if a "grid to CRS" conversion is not defined,
+     *         or if the {@code source} does not specify an {@linkplain GridGeometry#getExtent()
extent}.
+     * @throws TransformException if an error occurred during conversion from source to target.
+     */
+    public GridChange(final GridGeometry source, final GridGeometry target) throws TransformException
{
+        ArgumentChecks.ensureNonNull("source", source);
+        ArgumentChecks.ensureNonNull("target", target);
+        if (source.equals(target)) {
+            // Optimization for a common case.
+            mapCorners = mapCenters = MathTransforms.identity(source.getDimension());
+            targetRange = source.getExtent();
+        } else {
+            final CoordinateOperation crsChange;
+            try {
+                crsChange = Envelopes.findOperation(source.envelope, target.envelope);
+            } catch (FactoryException e) {
+                throw new TransformException(Errors.format(Errors.Keys.CanNotTransformEnvelope),
e);
+            }
+            final GridExtent domain = source.getExtent();
+            mapCorners  = path(source, crsChange, target, PixelInCell.CELL_CORNER);
+            mapCenters  = path(source, crsChange, target, PixelInCell.CELL_CENTER);
+            targetRange = new GridExtent(domain.toCRS(mapCorners, mapCenters), target.extent,
null);
+            /*
+             * Get an estimation of the scale factors when converting from source to target.
+             * If all scale factors are 1, we will not store the array for consistency with
+             * above block for identity case.
+             */
+            final Matrix matrix = MathTransforms.getMatrix(mapCenters);
+            final double[] resolution;
+            if (matrix != null) {
+                resolution = GridGeometry.resolution(matrix, 1);
+            } else {
+                resolution = GridGeometry.resolution(mapCenters.derivative(domain.getCentroid()),
0);
+            }
+            for (int i=resolution.length; --i >= 0;) {
+                if (resolution[i] != 1) {
+                    scales = resolution;
+                    return;
+                }
+            }
+        }
+        scales = null;
+    }
+
+    /**
+     * Returns the concatenation of all transformation steps from the given source to the
given target.
+     *
+     * @param  source     the source grid geometry.
+     * @param  crsChange  the change of coordinate reference system, or {@code null} if none.
+     * @param  target     the target grid geometry.
+     * @param  anchor     whether we want the transform for pixel corner or pixel center.
+     */
+    private static MathTransform path(final GridGeometry source, final CoordinateOperation
crsChange,
+            final GridGeometry target, final PixelInCell anchor) throws NoninvertibleTransformException
+    {
+        MathTransform tr = source.getGridToCRS(anchor);
+        if (crsChange != null) {
+            tr = MathTransforms.concatenate(tr, crsChange.getMathTransform());
+        }
+        return MathTransforms.concatenate(tr, target.getGridToCRS(anchor).inverse());
+    }
+
+    /**
+     * Returns an <em>estimation</em> of the scale factor when converting source
grid coordinates
+     * to target grid coordinates. This is for information purpose only since this method
combines
+     * potentially different scale factors for all dimensions.
+     *
+     * @return an <em>estimation</em> of the scale factor for all dimensions.
+     */
+    public double getGlobalScale() {
+        if (scales != null) {
+            double sum = 0;
+            int count = 0;
+            for (final double value : scales) {
+                if (Double.isFinite(value)) {
+                    sum += value;
+                    count++;
+                }
+            }
+            if (count != 0) {
+                return sum / count;
+            }
+        }
+        return 1;
+    }
+
+    /**
+     * Returns the conversion from source grid coordinates to target grid coordinates.
+     * The {@code anchor} argument specifies whether the conversion maps cell centers
+     * or cell corners of both grids.
+     *
+     * @param  anchor  the cell part to map (center or corner).
+     * @return conversion from source grid coordinates to target grid coordinates.
+     *
+     * @see GridGeometry#getGridToCRS(PixelInCell)
+     */
+    public MathTransform getConversion(final PixelInCell anchor) {
+        if (PixelInCell.CELL_CENTER.equals(anchor)) {
+            return mapCenters;
+        } else if (PixelInCell.CELL_CORNER.equals(anchor)) {
+            return mapCorners;
+        }  else {
+            return PixelTranslation.translate(mapCenters, PixelInCell.CELL_CENTER, anchor);
+        }
+    }
+
+    /**
+     * Returns the intersection of the two grid geometry extents, in units of the target
grid cells.
+     * This is the expected ranges of grid coordinates after conversions from source to target
grid,
+     * clipped to target grid extent and ignoring {@linkplain #getTargetStrides() strides}.
+     *
+     * @return intersection of grid geometry extents in units of target cells.
+     */
+    public GridExtent getTargetRange() {
+        return targetRange;
+    }
+
+    /**
+     * Returns an <em>estimation</em> of the steps for accessing cells along
each axis of target range.
+     * Given a {@linkplain #getConversion(PixelInCell) conversion} from source grid coordinates
+     * (<var>x</var>, <var>y</var>, <var>z</var>) to
target grid coordinates
+     * (<var>x′</var>, <var>y′</var>, <var>z′</var>)
defined as below (generalize to as many dimensions as needed):
+     *
+     * <ul>
+     *   <li><var>x′</var> = s₀⋅<var>x</var></li>
+     *   <li><var>y′</var> = s₁⋅<var>y</var></li>
+     *   <li><var>z′</var> = s₂⋅<var>z</var></li>
+     * </ul>
+     *
+     * Then this method returns {|s₀|, |s₁|, |s₂|} rounded toward zero and clamped
to 1
+     * (i.e. all values in the returned array are strictly positive, no zero values).
+     * It means that an iteration over source grid coordinates with a step Δ<var>x</var>=1
+     * corresponds approximately to an iteration in target grid coordinates with a step of
Δ<var>x′</var>=s₀,
+     * a step Δ<var>y</var>=1 corresponds approximately to a step Δ<var>y′</var>=s₁,
<i>etc.</i>
+     * If the conversion changes grid axis order, then the order of elements in the returned
array
+     * is the order of axes in the {@linkplain #getTargetRange() target range}.
+     *
+     * <p>In a <em>inverse</em> conversion from target to source grid,
the value returned by this
+     * method would be the sub-sampling to apply while reading the target grid.</p>
+     *
+     * @return an <em>estimation</em> of the steps for accessing cells along
each axis of target range.
+     */
+    public int[] getTargetStrides() {
+        final int[] subsamplings;
+        if (scales == null) {
+            subsamplings = new int[targetRange.getDimension()];
+            Arrays.fill(subsamplings, 1);
+        } else {
+            subsamplings = new int[scales.length];
+            for (int i=0; i<subsamplings.length; i++) {
+                subsamplings[i] = Math.max(1, (int) Math.nextUp(scales[i]));    // Really
want rounding toward 0.
+            }
+        }
+        return subsamplings;
+    }
+
+    /**
+     * Returns a hash code value for this grid change.
+     *
+     * @return hash code value.
+     */
+    @Override
+    public int hashCode() {
+        /*
+         * The mapCorners is closely related to mapCenters, so we omit it.
+         * The scales array is derived from mapCenters, so we omit it too.
+         */
+        return mapCorners.hashCode() + 59 * targetRange.hashCode();
+    }
+
+    /**
+     * Compares this grid change with the given object for equality.
+     *
+     * @param  other  the other object to compare with this grid change.
+     * @return {@code true} if both objects are equal.
+     */
+    @Override
+    public boolean equals(final Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (other != null && other.getClass() == getClass()) {
+            final GridChange that = (GridChange) other;
+            return mapCorners.equals(that.mapCorners) &&
+                   mapCenters.equals(that.mapCenters) &&
+                   targetRange.equals(that.targetRange) &&
+                   Arrays.equals(scales, that.scales);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a string representation of this grid change for debugging purpose.
+     *
+     * @return a string representation for debugging purpose.
+     */
+    @Override
+    public String toString() {
+        final String lineSeparator = System.lineSeparator();
+        final StringBuilder buffer = new StringBuilder(256)
+                .append("Grid change").append(lineSeparator)
+                .append("└ Scale factor ≈ ").append((float) getGlobalScale()).append(lineSeparator)
+                .append("Target range").append(lineSeparator);
+        targetRange.appendTo(buffer, Vocabulary.getResources((Locale) null), true);
+        return buffer.toString();
+    }
+}
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index ab553c8..0f8ea50 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -735,7 +735,7 @@ public class GridExtent implements Serializable {
      * Writes a string representation of this grid envelope in the given buffer.
      *
      * @param out         where to write the string representation.
-     * @param vocabulary  resources for some words, or {@code null} if not yet fetched.
+     * @param vocabulary  resources for some words.
      * @param tree        whether to format lines of a tree in the margin on the left.
      */
     final void appendTo(final StringBuilder out, final Vocabulary vocabulary, final boolean
tree) {
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
index 97c8eeb..76b89f1 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
@@ -637,7 +637,7 @@ public class GridGeometry implements Serializable {
      * (with pixels that may be tens of kilometres large) is a recurrent problem. We want
to encourage developers
      * to always think about wether the desired <cite>grid to CRS</cite> transform
shall map pixel corner or center.</div>
      *
-     * @param  anchor  the pixel part to map.
+     * @param  anchor  the cell part to map (center or corner).
      * @return the conversion from grid coordinates to "real world" coordinates (never {@code
null}).
      * @throws IllegalArgumentException if the given {@code anchor} is not a known code list
value.
      * @throws IncompleteGridGeometryException if this grid geometry has no transform —
@@ -703,7 +703,7 @@ public class GridGeometry implements Serializable {
      *         This is 0 if the matrix is a derivative (i.e. we ignore nothing), or 1 if
the matrix
      *         is an affine transform (i.e. we ignore the translation column and the [0 0
… 1] row).
      */
-    private static double[] resolution(final Matrix gridToCRS, final int numToIgnore) {
+    static double[] resolution(final Matrix gridToCRS, final int numToIgnore) {
         final double[] resolution = new double[gridToCRS.getNumRow() - numToIgnore];
         final double[] buffer     = new double[gridToCRS.getNumCol() - numToIgnore];
         for (int j=0; j<resolution.length; j++) {
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
index b7ee17e..a58cd29 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
@@ -24,6 +24,7 @@ import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.Conversion;
 import org.opengis.referencing.operation.MathTransform2D;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.referencing.operation.transform.MathTransformWrapper;
@@ -231,6 +232,27 @@ public final strictfp class EnvelopesTest extends TransformTestCase<GeneralEnvel
     }
 
     /**
+     * Tests {@link Envelopes#findOperation(Envelope, Envelope)}.
+     *
+     * @throws FactoryException if an error occurred while searching the operation.
+     *
+     * @since 1.0
+     */
+    @Test
+    public void testFindOperation() throws FactoryException {
+        final GeneralEnvelope source = new GeneralEnvelope(HardCodedCRS.WGS84);
+        final GeneralEnvelope target = new GeneralEnvelope(HardCodedCRS.GEOCENTRIC);
+        source.setRange(0, 20, 30);
+        source.setRange(1, 40, 45);
+        target.setRange(0, 6000, 8000);
+        target.setRange(1, 7000, 9000);
+        target.setRange(2, 4000, 5000);
+        CoordinateOperation op = Envelopes.findOperation(source, target);
+        assertInstanceOf("findOperation", Conversion.class, op);
+        assertEquals("Geographic/geocentric conversions", ((Conversion) op).getMethod().getName().getCode());
+    }
+
+    /**
      * Test {@link Envelopes#compound(Envelope...)} method.
      *
      * @throws FactoryException if an error occurred while creating the compound CRS.
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
index 55fb0be..985455b 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
@@ -27,6 +27,7 @@ import java.time.Instant;
 import javax.measure.Unit;
 import org.opengis.referencing.operation.Matrix;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.math.Vector;
 import org.apache.sis.math.DecimalFunctions;
 import org.apache.sis.measure.NumberRange;
@@ -550,23 +551,23 @@ public abstract class Variable extends NamedElement {
      * Constraints on the argument values are:
      *
      * <ul>
-     *   <li>All arrays length shall be equal to the length of the {@link #getShape()}
array.</li>
+     *   <li>Argument dimensions shall be equal to the length of the {@link #getShape()}
array.</li>
      *   <li>For each index <var>i</var>, value of {@code area[i]} shall
be in the range from 0 inclusive
-     *       to {@code Integer.toUnsignedLong(getGridEnvelope()[i])} exclusive.</li>
+     *       to {@code Integer.toUnsignedLong(getShape()[length - 1 - i])} exclusive.</li>
+     *   <li>Values are in "natural" order (inverse of netCDF order).</li>
      * </ul>
      *
      * If the variable has more than one dimension, then the data are packed in a one-dimensional
vector
      * in the same way than {@link #read()}.
      *
-     * @param  areaLower    index of the first value to read along each dimension.
-     * @param  areaUpper    index after the last value to read along each dimension.
+     * @param  area         indices of cell values to read along each dimension, in "natural"
order.
      * @param  subsampling  sub-sampling along each dimension. 1 means no sub-sampling.
      * @return the data as an array of a Java primitive type.
      * @throws IOException if an error occurred while reading the data.
      * @throws DataStoreException if a logical error occurred.
      * @throws ArithmeticException if the size of the region to read exceeds {@link Integer#MAX_VALUE},
or other overflow occurs.
      */
-    public abstract Vector read(int[] areaLower, int[] areaUpper, int[] subsampling) throws
IOException, DataStoreException;
+    public abstract Vector read(GridExtent area, int[] subsampling) throws IOException, DataStoreException;
 
     /**
      * Wraps the given data in a {@link Vector} with the assumption that accuracy in base
10 matters.
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java
index 149443c..75402e8 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java
@@ -29,6 +29,7 @@ import java.util.stream.StreamSupport;
 import java.util.function.Consumer;
 import java.io.IOException;
 import org.apache.sis.math.Vector;
+import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.internal.netcdf.DataType;
 import org.apache.sis.internal.netcdf.DiscreteSampling;
 import org.apache.sis.internal.netcdf.Resources;
@@ -343,22 +344,22 @@ search: for (final VariableInfo counts : decoder.variables) {
          */
         @Override
         public boolean tryAdvance(final Consumer<? super Feature> action) {
-            final int   length = counts.intValue(index);
-            final int[] lower  = {position};
-            final int[] upper  = {position + length};
-            final int[] step   = {1};
+            final int length = counts.intValue(index);
+            final GridExtent extent = new GridExtent(null, new long[] {position},
+                            new long[] {Math.addExact(position, length)}, false);
+            final int[] step = {1};
             final Vector   id, t;
             final Vector[] coords = new Vector[coordinates.length];
             final Object[] props  = new Object[properties.length];
             try {
                 id = identifiers.read();                    // Efficiency should be okay
because of cached value.
-                t = time.read(lower, upper, step);
+                t = time.read(extent, step);
                 for (int i=0; i<coordinates.length; i++) {
-                    coords[i] = coordinates[i].read(lower, upper, step);
+                    coords[i] = coordinates[i].read(extent, step);
                 }
                 for (int i=0; i<properties.length; i++) {
                     final VariableInfo p = properties[i];
-                    final Vector data = p.read(lower, upper, step);
+                    final Vector data = p.read(extent, step);
                     if (p.isEnumeration()) {
                         final String[] meanings = new String[data.size()];
                         for (int j=0; j<meanings.length; j++) {
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
index 95394ae..3fbf5ac 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
@@ -29,6 +29,7 @@ import javax.measure.Unit;
 import ucar.nc2.constants.CF;
 import ucar.nc2.constants.CDM;
 import ucar.nc2.constants._Coordinate;
+import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.internal.netcdf.Decoder;
 import org.apache.sis.internal.netcdf.DataType;
 import org.apache.sis.internal.netcdf.Grid;
@@ -721,15 +722,15 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
     /**
      * Reads a sub-sampled sub-area of the variable.
      * Multi-dimensional variables are flattened as a one-dimensional array (wrapped in a
vector).
+     * Array elements are in "natural" order (inverse of netCDF order).
      *
-     * @param  areaLower    index of the first value to read along each dimension, as unsigned
integers.
-     * @param  areaUpper    index after the last value to read along each dimension, as unsigned
integers.
+     * @param  area         indices of cell values to read along each dimension, in "natural"
order.
      * @param  subsampling  sub-sampling along each dimension. 1 means no sub-sampling.
      * @return the data as an array of a Java primitive type.
      * @throws ArithmeticException if the size of the region to read exceeds {@link Integer#MAX_VALUE},
or other overflow occurs.
      */
     @Override
-    public Vector read(int[] areaLower, int[] areaUpper, int[] subsampling) throws IOException,
DataStoreException {
+    public Vector read(final GridExtent area, final int[] subsampling) throws IOException,
DataStoreException {
         if (reader == null) {
             throw new DataStoreContentException(unknownType());
         }
@@ -759,15 +760,12 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
         final long[] size  = new long[dimension];
         final long[] lower = new long[dimension];
         final long[] upper = new long[dimension];
-        final int [] sub   = new int [dimension];
         for (int i=0; i<dimension; i++) {
-            final int j = (dimension - 1) - i;
-            lower[i] = Integer.toUnsignedLong(areaLower[j]);
-            upper[i] = Integer.toUnsignedLong(areaUpper[j]);
-            sub  [i] = subsampling[j];
-            size [i] = dimensions[j].length();
+            lower[i] = area.getLow(i);
+            upper[i] = Math.incrementExact(area.getHigh(i));
+            size [i] = dimensions[(dimension - 1) - i].length();
         }
-        final Region region = new Region(size, lower, upper, sub);
+        final Region region = new Region(size, lower, upper, subsampling);
         applyUnlimitedDimensionStride(region);
         return Vector.create(reader.read(region), dataType.isUnsigned);
     }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
index 799aea7..2c081c7 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
@@ -35,6 +35,7 @@ import ucar.nc2.dataset.EnhanceScaleMissing;
 import ucar.nc2.units.SimpleUnit;
 import ucar.nc2.units.DateUnit;
 import org.opengis.referencing.operation.Matrix;
+import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.math.Vector;
 import org.apache.sis.internal.netcdf.DataType;
 import org.apache.sis.internal.netcdf.Decoder;
@@ -368,23 +369,27 @@ final class VariableWrapper extends Variable {
 
     /**
      * Reads a sub-sampled sub-area of the variable.
+     * Array elements are in inverse of netCDF order.
      *
-     * @param  areaLower    index of the first value to read along each dimension.
-     * @param  areaUpper    index after the last value to read along each dimension.
+     * @param  area         indices of cell values to read along each dimension, in "natural"
order.
      * @param  subsampling  sub-sampling along each dimension. 1 means no sub-sampling.
      * @return the data as an array of a Java primitive type.
      */
     @Override
-    public Vector read(final int[] areaLower, final int[] areaUpper, final int[] subsampling)
-            throws IOException, DataStoreException
-    {
-        final int[] size = new int[areaUpper.length];
-        for (int i=0; i<size.length; i++) {
-            size[i] = areaUpper[i] - areaLower[i];
+    public Vector read(final GridExtent area, final int[] subsampling) throws IOException,
DataStoreException {
+        int n = area.getDimension();
+        final int[] lower = new int[n];
+        final int[] size  = new int[n];
+        final int[] sub   = new int[n--];
+        for (int i=0; i<=n; i++) {
+            final int j = (n - i);
+            lower[j] = Math.toIntExact(area.getLow(i));
+            size [j] = Math.toIntExact(area.getSize(i));
+            sub  [j] = subsampling[i];
         }
         final Array array;
         try {
-            array = variable.read(new Section(areaLower, size, subsampling));
+            array = variable.read(new Section(lower, size, sub));
         } catch (InvalidRangeException e) {
             throw new DataStoreException(e);
         }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
index 35d9345..0acde9c 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
@@ -24,6 +24,7 @@ import java.nio.file.Path;
 import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
 import org.opengis.referencing.operation.MathTransform1D;
+import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.referencing.operation.transform.TransferFunction;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.internal.netcdf.Decoder;
@@ -32,14 +33,14 @@ import org.apache.sis.internal.netcdf.Variable;
 import org.apache.sis.internal.storage.AbstractGridResource;
 import org.apache.sis.internal.storage.ResourceOnFileSystem;
 import org.apache.sis.coverage.SampleDimension;
-import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.coverage.grid.GridChange;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.resources.Vocabulary;
-import org.apache.sis.util.resources.Errors;
 import org.apache.sis.math.Vector;
+import org.apache.sis.storage.DataStoreReferencingException;
 import ucar.nc2.constants.CDM;                      // We use only String constants.
 
 
@@ -207,21 +208,15 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS
      */
     @Override
     public GridCoverage read(GridGeometry domain, final int... range) throws DataStoreException
{
-        domain = validateReadArgument(domain);
-        final GridExtent extent = domain.getExtent();
-        final int   dimension   = domain.getDimension();
-        final int[] areaLower   = new int[dimension];
-        final int[] areaUpper   = new int[dimension];
-        final int[] subsampling = new int[dimension];
-        for (int i=0; i<dimension; i++) {
-            final int j = (dimension - 1) - i;
-            areaLower[j] = unsigned(extent.getLow (i));             // Inclusive.
-            areaUpper[j] = unsigned(extent.getHigh(i) + 1);         // Exclusive.
-            subsampling[j] = 1;
+        if (domain == null) {
+            domain = gridGeometry;
         }
         final Vector samples;
         try {
-            samples = data.read(areaLower, areaUpper, subsampling);
+            final GridChange change = new GridChange(domain, gridGeometry);
+            samples = data.read(change.getTargetRange(), change.getTargetStrides());
+        } catch (TransformException e) {
+            throw new DataStoreReferencingException(e);
         } catch (IOException e) {
             throw new DataStoreException(e);
         }
@@ -230,16 +225,6 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS
     }
 
     /**
-     * Returns the given value as an unsigned integer.
-     */
-    private static int unsigned(final long value) throws DataStoreException {
-        if (value < 0L || value > 0xFFFFFFFFL) {
-            throw new DataStoreException(Errors.format(Errors.Keys.IndexOutOfBounds_1, value));
-        }
-        return (int) value;
-    }
-
-    /**
      * Gets the paths to files used by this resource, or an empty array if unknown.
      */
     @Override
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
index d5f1e78..bd06abb 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java
@@ -17,15 +17,12 @@
 package org.apache.sis.internal.storage;
 
 import org.opengis.geometry.Envelope;
-import org.opengis.geometry.MismatchedDimensionException;
-import org.opengis.referencing.datum.PixelInCell;
 import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.storage.Resource;
-import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.logging.WarningListeners;
 
 
@@ -89,30 +86,4 @@ public abstract class AbstractGridResource extends AbstractResource implements
G
             metadata.addNewBand(band);
         }
     }
-
-    /**
-     * Verifies the validity of the grid geometry specified by the user
-     * for a {@link #read(GridGeometry, int...)} operation.
-     *
-     * @param  domain  the user-specified grid geometry, or {@code null}.
-     * @return the grid geometry to use for reading.
-     * @throws DataStoreException if an error occurred while validating the grid geometry.
-     */
-    protected final GridGeometry validateReadArgument(GridGeometry domain) throws DataStoreException
{
-        final GridGeometry stored = getGridGeometry();
-        if (domain == null) {
-            return stored;
-        }
-        final int dimension = stored.getDimension();
-        final int ad = domain.getDimension();
-        if (ad != stored.getDimension()) {
-            throw new MismatchedDimensionException(Errors.format(Errors.Keys.MismatchedDimension_3,
"domain", dimension, ad));
-        }
-        if (domain.isDefined(GridGeometry.GRID_TO_CRS) &&
-                !stored.getGridToCRS(PixelInCell.CELL_CENTER).equals(domain.getGridToCRS(PixelInCell.CELL_CENTER)))
-        {
-            throw new IllegalArgumentException("Mismatched grid to CRS transform.");
-        }
-        return domain;
-    }
 }


Mime
View raw message