sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/03: First implementation of GridCoverage.render(...) taking in account the slice point.
Date Sat, 05 Jan 2019 17:11:41 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 ff45593df165f4b1d30028535a6ab90f2728a907
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat Jan 5 17:56:26 2019 +0100

    First implementation of GridCoverage.render(...) taking in account the slice point.
---
 .../coverage/SubspaceNotSpecifiedException.java    |  67 ++++++++++
 .../org/apache/sis/coverage/grid/GridChange.java   |  42 +++---
 .../org/apache/sis/coverage/grid/GridCoverage.java |   4 +-
 .../org/apache/sis/coverage/grid/GridExtent.java   |  99 ++++++++++++--
 .../org/apache/sis/coverage/grid/GridGeometry.java |   3 +-
 .../apache/sis/internal/raster/RasterFactory.java  | 142 ++++++++++++++++++---
 .../org/apache/sis/internal/raster/Resources.java  |  19 ++-
 .../sis/internal/raster/Resources.properties       |   8 +-
 .../sis/internal/raster/Resources_fr.properties    |   8 +-
 .../apache/sis/coverage/grid/GridChangeTest.java   |   8 +-
 .../apache/sis/coverage/grid/GridExtentTest.java   |  18 +++
 .../org/apache/sis/internal/netcdf/Resources.java  |   5 +
 .../sis/internal/netcdf/Resources.properties       |   1 +
 .../sis/internal/netcdf/Resources_fr.properties    |   1 +
 .../apache/sis/storage/netcdf/GridResource.java    |  31 ++++-
 .../java/org/apache/sis/storage/netcdf/Image.java  |  20 +--
 16 files changed, 397 insertions(+), 79 deletions(-)

diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/SubspaceNotSpecifiedException.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/SubspaceNotSpecifiedException.java
new file mode 100644
index 0000000..df8037e
--- /dev/null
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/SubspaceNotSpecifiedException.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import org.opengis.coverage.CannotEvaluateException;
+
+
+/**
+ * Thrown when an operation can only be applied on a subspace of a multi-dimensional coverage,
+ * but not such subspace has been specified.
+ * For example if a {@link org.apache.sis.coverage.grid.GridCoverage} has three or more dimensions,
+ * then a two-dimensional slice must be specified in order to produce a {@link java.awt.image.RenderedImage}
+ * from that grid coverage.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Linear_subspace">Linear subspace on Wikipedia</a>
+ *
+ * @since 1.0
+ * @module
+ */
+public class SubspaceNotSpecifiedException extends CannotEvaluateException {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = -8993517725073815199L;
+
+    /**
+     * Constructs an exception with no detail message.
+     */
+    public SubspaceNotSpecifiedException() {
+    }
+
+    /**
+     * Constructs an exception with the specified detail message.
+     *
+     * @param  message  the detail message.
+     */
+    public SubspaceNotSpecifiedException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs an exception with the specified detail message and cause.
+     *
+     * @param  message  the detail message.
+     * @param  cause    the cause for this exception.
+     */
+    public SubspaceNotSpecifiedException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
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
index 70407d9..ab2bd73 100644
--- 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
@@ -57,7 +57,7 @@ import org.apache.sis.util.Debug;
  *         public GridCoverage read(GridGeometry domain, int... range) throws DataStoreException {
  *             GridChange change = new GridChange(domain, getGridGeometry());
  *             GridExtent toRead = change.getTargetExtent();
- *             int[] subsampling = change.getTargetStrides());
+ *             int[] subsampling = change.getTargetSubsamplings());
  *             // Do reading here.
  *         }
  *     }
@@ -102,7 +102,7 @@ public class GridChange implements Serializable {
      * 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()
+     * @see #getTargetSubsamplings()
      */
     private final double[] scales;
 
@@ -272,7 +272,7 @@ public class GridChange implements Serializable {
     /**
      * 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}.
+     * clipped to target grid extent and ignoring {@linkplain #getTargetSubsamplings() sub-samplings}.
      *
      * @return intersection of grid geometry extents in units of target cells.
      */
@@ -305,7 +305,7 @@ public class GridChange implements Serializable {
      *
      * @return an <em>estimation</em> of the steps for accessing cells along each axis of target range.
      */
-    public int[] getTargetStrides() {
+    public int[] getTargetSubsamplings() {
         final int[] subsamplings;
         if (scales == null) {
             subsamplings = new int[targetExtent.getDimension()];
@@ -320,41 +320,41 @@ public class GridChange implements Serializable {
     }
 
     /**
-     * Returns the grid geometry resulting from sub-sampling the target grid with the given strides.
-     * The {@code strides} argument is usually the array returned by {@link #getTargetStrides()}, but not necessarily.
-     * The {@linkplain GridGeometry#getExtent() extent} of the returned grid geometry will be derived from
-     * {@link #getTargetExtent()} as below for each dimension <var>i</var>:
+     * Returns the grid geometry resulting from sub-sampling the target grid with the given periods.
+     * The {@code periods} argument is usually the array returned by {@link #getTargetSubsamplings()}, but
+     * not necessarily. The {@linkplain GridGeometry#getExtent() extent} of the returned grid geometry will
+     * be derived from {@link #getTargetExtent()} as below for each dimension <var>i</var>:
      *
      * <ul>
-     *   <li>The {@linkplain GridExtent#getLow(int)  low}  is divided by {@code strides[i]}, rounded toward zero.</li>
-     *   <li>The {@linkplain GridExtent#getSize(int) size} is divided by {@code strides[i]}, rounded toward zero.</li>
+     *   <li>The {@linkplain GridExtent#getLow(int)  low}  is divided by {@code periods[i]}, rounded toward zero.</li>
+     *   <li>The {@linkplain GridExtent#getSize(int) size} is divided by {@code periods[i]}, rounded toward zero.</li>
      *   <li>The {@linkplain GridExtent#getHigh(int) high} is recomputed from above low and size.</li>
      * </ul>
      *
      * The {@linkplain GridGeometry#getGridToCRS(PixelInCell) grid to CRS} transform is scaled accordingly
      * in order to map approximately to the same {@linkplain GridGeometry#getEnvelope() envelope}.
      *
-     * @param  strides  the sub-sampling to apply on each grid dimension. All values shall be greater than zero.
+     * @param  periods  the sub-sampling to apply on each grid dimension. All values shall be greater than zero.
      *         If the array length is shorter than the number of dimensions, missing values are assumed to be 1.
      * @return a grid geometry derived from the target geometry with the given sub-sampling.
      * @throws TransformException if an error occurred during computation of target grid geometry.
      */
-    public GridGeometry getTargetGeometry(final int... strides) throws TransformException {
+    public GridGeometry getTargetGeometry(final int... periods) throws TransformException {
         GridExtent extent = getTargetExtent();
         double[] factors = null;
         Matrix toGiven = null;
-        if (strides != null) {
-            // Validity of the strides values will be verified by GridExtent.subsampling(…) invoked below.
+        if (periods != null) {
+            // Validity of the periods values will be verified by GridExtent.subsampling(…) invoked below.
             final GridExtent unscaled = extent;
             final int dimension = extent.getDimension();
-            for (int i = Math.min(dimension, strides.length); --i >= 0;) {
-                final int s = strides[i];
+            for (int i = Math.min(dimension, periods.length); --i >= 0;) {
+                final int s = periods[i];
                 if (s != 1) {
                     if (factors == null) {
-                        extent = extent.subsample(strides);
+                        extent = extent.subsample(periods);
                         factors = new double[dimension];
                         Arrays.fill(factors, 1);
-                        if (!extent.startsWithZero()) {
+                        if (!extent.startsAtZero()) {
                             toGiven = Matrices.createIdentity(dimension + 1);
                         }
                     }
@@ -468,18 +468,18 @@ public class GridChange implements Serializable {
         }
         /*
          * GridChange (example)
-         *   └─Target strides
+         *   └─Target subsamplings
          *       ├─{50, 300}
          *       └─Global ≈ 175.0
          */
         buffer.setLength(0);
         buffer.append('{');
-        for (int s : getTargetStrides()) {
+        for (int s : getTargetSubsamplings()) {
             if (buffer.length() > 1) buffer.append(", ");
             buffer.append(s);
         }
         section = root.newChild();
-        section.setValue(column, "Target strides");
+        section.setValue(column, "Target subsamplings");
         section.newChild().setValue(column, buffer.append('}').toString()); buffer.setLength(0);
         section.newChild().setValue(column, buffer.append("Global ≈ ").append((float) getGlobalScale()).toString());
         return tree;
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
index 78ab4b5..dea57aa 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
@@ -24,6 +24,7 @@ import org.opengis.geometry.DirectPosition;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.coverage.SubspaceNotSpecifiedException;
 import org.apache.sis.util.collection.DefaultTreeTable;
 import org.apache.sis.util.collection.TableColumn;
 import org.apache.sis.util.collection.TreeTable;
@@ -139,7 +140,8 @@ public abstract class GridCoverage {
      *         May be {@code null} if this coverage can render only one image, for example because its CRS is two-dimensional.
      * @return the grid slice as a rendered image.
      * @throws PointOutsideCoverageException if the given slice point is illegal.
-     * @throws CannotEvaluateException if this method can not produce the render image for another reason.
+     * @throws SubspaceNotSpecifiedException if the given argument is not sufficient for reducing the grid to a two-dimensional slice.
+     * @throws CannotEvaluateException if this method can not produce the rendered image for another reason.
      */
     public abstract RenderedImage render(DirectPosition slicePoint) throws CannotEvaluateException;
 
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 c0b130d..3b8f41e 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
@@ -44,6 +44,7 @@ import org.apache.sis.internal.util.Strings;
 import org.apache.sis.geometry.AbstractEnvelope;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.geometry.Envelopes;
+import org.apache.sis.coverage.SubspaceNotSpecifiedException;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.transform.TransformSeparator;
 import org.apache.sis.io.TableAppender;
@@ -52,6 +53,7 @@ import org.apache.sis.util.iso.Types;
 
 // Branch-dependent imports
 import org.opengis.coverage.grid.GridEnvelope;
+import org.opengis.coverage.CannotEvaluateException;
 import org.opengis.coverage.PointOutsideCoverageException;
 
 
@@ -490,7 +492,7 @@ public class GridExtent implements Serializable {
      *
      * @return whether all low coordinates are zero.
      */
-    public boolean startsWithZero() {
+    public boolean startsAtZero() {
         for (int i = getDimension(); --i >= 0;) {
             if (coordinates[i] != 0) {
                 return false;
@@ -595,6 +597,83 @@ public class GridExtent implements Serializable {
     }
 
     /**
+     * Returns indices of all dimensions where this grid extent has a size greater than 1.
+     * This method can be used for getting the grid extent of a <var>s</var>-dimensional slice
+     * in a <var>n</var>-dimensional cube where <var>s</var> ≦ <var>n</var>.
+     *
+     * <div class="note"><b>Example:</b>
+     * suppose that we want to get a two-dimensional slice <var>(y,z)</var> in a four-dimensional data cube <var>(x,y,z,t)</var>.
+     * The first step is to specify the <var>x</var> and <var>t</var> coordinates of the slice.
+     * In this example we set <var>x</var> to 5 and <var>t</var> to 8.
+     *
+     * {@preformat java
+     *     GridGeometry grid = ...;             // Geometry of the (x,y,z,t) grid.
+     *     GridGeometry slice4D = grid.slice(new GeneralDirectPosition(5, NaN, NaN, 8));
+     * }
+     *
+     * Above code created a slice at the requested position, but that slice still have 4 dimensions.
+     * It is a "slice" because the <var>x</var> and <var>t</var> dimensions of {@code slice4D} have only one cell.
+     * If a two-dimensional slice is desired, then above operations can be completed as below.
+     * In this example, the result of {@code getSubspaceDimensions(2)} call will be {1,2}.
+     *
+     * {@preformat java
+     *     int[]  subDimensions = slice4D.getExtent().getSubspaceDimensions(2);
+     *     GridGeometry slice2D = slice4D.reduce(subDimensions);
+     * }
+     *
+     * Note that in this particular example, it would have been more efficient to execute {@code grid.reduce(1,2)} directly.
+     * This {@code getSubspaceDimensions(int)} method is more useful for inferring a {@code slice2D} from a {@code slice4D}
+     * which has been created elsewhere, or when we do not really want the {@code slice2D} but only its dimension indices.
+     * </div>
+     *
+     * This method returns exactly <var>s</var> indices. If there is more than <var>s</var> dimensions having a
+     * {@linkplain #getSize(int) size} greater than 1, then a {@link SubspaceNotSpecifiedException} is thrown.
+     * If there is less than <var>s</var> dimensions having a size greater than 1, then the returned list of
+     * dimensions is completed with some dimensions of size 1, starting with the first dimensions in this grid
+     * extent, until there is exactly <var>s</var> dimensions. This this grid extent does not have <var>s</var>
+     * dimensions, then a {@link CannotEvaluateException} is thrown.
+     *
+     * @param  s  number of dimensions of the sub-space.
+     * @return indices of sub-space dimensions, in increasing order in an array of length <var>s</var>.
+     * @throws SubspaceNotSpecifiedException if there is more than <var>s</var> dimensions having a size greater than 1.
+     * @throws CannotEvaluateException if this grid extent does not have at least <var>s</var> dimensions.
+     */
+    public int[] getSubspaceDimensions(final int s) {
+        ArgumentChecks.ensurePositive("s", s);
+        final int m = getDimension();
+        if (s > m) {
+            throw new CannotEvaluateException(Resources.format(Resources.Keys.GridEnvelopeMustBeNDimensional_1, s));
+        }
+        final int[] selected = new int[s];
+        int count = 0;
+        for (int i=0; i<m; i++) {
+            final long low  = coordinates[i];
+            final long high = coordinates[i+m];
+            if (low != high) {
+                if (count < s) {
+                    selected[count++] = i;
+                } else {
+                    throw new SubspaceNotSpecifiedException(Resources.format(Resources.Keys.NoNDimensionalSlice_3,
+                                    s, getAxisIdentification(i,i), Numerics.toUnsignedDouble(high - low)));
+                }
+            }
+        }
+        final int missing = s - count;
+        if (missing != 0) {
+            System.arraycopy(selected, 0, selected, missing, count);
+            count = 0;
+            for (int i=0; ; i++) {                          // An IndexOutOfBoundsException would be a bug in our algorithm.
+                if (coordinates[i] == coordinates[i+m]) {
+                    selected[count++] = i;
+                    if (count == missing) break;
+                }
+            }
+            Arrays.sort(selected);
+        }
+        return selected;
+    }
+
+    /**
      * Returns the type (vertical, temporal, …) of grid axis at given dimension.
      * This information is provided because the grid axis type can not always be inferred from the context.
      * Some examples are:
@@ -820,8 +899,8 @@ public class GridExtent implements Serializable {
     /**
      * Creates a new grid extent sub-sampled by the given amount of cells along each grid dimensions.
      * This method divides {@linkplain #getLow(int) low coordinates} and {@linkplain #getSize(int) grid sizes}
-     * by the given strides, rounding toward zero. The {@linkplain #getHigh(int) high coordinates} are adjusted
-     * accordingly (this is often equivalent to dividing high coordinates by the strides too, but a difference
+     * by the given periods, rounding toward zero. The {@linkplain #getHigh(int) high coordinates} are adjusted
+     * accordingly (this is often equivalent to dividing high coordinates by the periods too, but a difference
      * of one cell may exist).
      *
      * <div class="note"><b>Note:</b>
@@ -833,19 +912,19 @@ public class GridExtent implements Serializable {
      * This method does not reduce the number of dimensions of the grid extent.
      * For dimensionality reduction, see {@link #reduce(int...)}.
      *
-     * @param  strides  the strides. Length shall be equal to the number of dimension and all values shall be greater than zero.
+     * @param  periods  the sub-samplings. Length shall be equal to the number of dimension and all values shall be greater than zero.
      * @return the sub-sampled extent, or {@code this} is sub-sampling results in the same extent.
-     * @throws IllegalArgumentException if a stride is not greater than zero.
+     * @throws IllegalArgumentException if a period is not greater than zero.
      *
      * @see GridGeometry#subgrid(Envelope, double...)
      */
-    public GridExtent subsample(final int... strides) {
-        ArgumentChecks.ensureNonNull("strides", strides);
+    public GridExtent subsample(final int... periods) {
+        ArgumentChecks.ensureNonNull("periods", periods);
         final int m = getDimension();
-        ArgumentChecks.ensureDimensionMatches("strides", m, strides);
+        ArgumentChecks.ensureDimensionMatches("periods", m, periods);
         final GridExtent sub = new GridExtent(this);
         for (int i=0; i<m; i++) {
-            final int s = strides[i];
+            final int s = periods[i];
             if (s > 1) {
                 final int j = i + m;
                 long low  = coordinates[i];
@@ -856,7 +935,7 @@ public class GridExtent implements Serializable {
                 sub.coordinates[i] = low /= s;
                 sub.coordinates[j] = low + r;
             } else if (s <= 0) {
-                throw new IllegalArgumentException(Errors.format(Errors.Keys.ValueNotGreaterThanZero_2, Strings.toIndexed("strides", i), s));
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.ValueNotGreaterThanZero_2, Strings.toIndexed("periods", i), s));
             }
         }
         return Arrays.equals(coordinates, sub.coordinates) ? this : sub;
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 c93cb85..cddfa57 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
@@ -1126,10 +1126,11 @@ public class GridGeometry implements Serializable {
      * <p>This method performs a <cite>dimensionality reduction</cite>.
      * This method can not be used for changing dimension order.</p>
      *
-     * @param  dimensions  the dimensions to select, in strictly increasing order.
+     * @param  dimensions  the grid (not CRS) dimensions to select, in strictly increasing order.
      * @return the sub grid geometry, or {@code this} if the given array contains all dimensions of this grid grid geometry.
      * @throws IndexOutOfBoundsException if an index is out of bounds.
      *
+     * @see GridExtent#getSubspaceDimensions(int)
      * @see GridExtent#reduce(int...)
      * @see org.apache.sis.referencing.CRS#reduce(CoordinateReferenceSystem, int...)
      */
diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/RasterFactory.java b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/RasterFactory.java
index 002ec85..9619ca6 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/RasterFactory.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/RasterFactory.java
@@ -28,9 +28,16 @@ import java.awt.image.DataBufferFloat;
 import java.awt.image.DataBufferDouble;
 import java.awt.image.SampleModel;
 import java.awt.image.BandedSampleModel;
+import java.awt.image.ComponentSampleModel;
+import java.awt.image.PixelInterleavedSampleModel;
 import java.awt.image.WritableRaster;
+import org.opengis.geometry.MismatchedDimensionException;
+import org.apache.sis.coverage.SubspaceNotSpecifiedException;
+import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Static;
+import org.apache.sis.util.Workaround;
+import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 
 
@@ -50,42 +57,143 @@ public final class RasterFactory extends Static {
     }
 
     /**
+     * Wraps a sub-region of the given data buffer in a raster.
+     * The raster width, raster height, pixel stride and scanline stride are inferred from the grid extents.
+     * The sample model type is selected according the number of bands and the pixel stride.
+     *
+     * @param  buffer          buffer that contains the sample values.
+     * @param  bankIndices     bank indices for each band, or {@code null} for 0, 1, 2, 3….
+     * @param  bandOffsets     number of data elements from the first element of the bank to the first sample of the band, or {@code null} for all 0.
+     * @param  source          extent of the data wrapped by the given buffer. May have any number of dimensions.
+     * @param  target          extent of the subspace to wrap in a raster.
+     * @param  startAtZero     whether to force the raster to start at (0,0) instead than the target extent low coordinates.
+     * @return a raster built from given properties.
+     * @throws ArithmeticException if a stride calculation overflows the 32 bits integer capacity.
+     * @throws SubspaceNotSpecifiedException if this method can not infer a two-dimensional slice from {@code target}.
+     */
+    public static WritableRaster createRaster(final DataBuffer buffer, final int[] bankIndices, final int[] bandOffsets,
+            final GridExtent source, final GridExtent target, final boolean startAtZero)
+    {
+        int dimension = target.getDimension();
+        if (source.getDimension() != dimension) {
+            throw new MismatchedDimensionException(Errors.format(
+                    Errors.Keys.MismatchedDimension_3, "target", source.getDimension(), dimension));
+        }
+        final int[] dimensions = target.getSubspaceDimensions(2);
+        int xd = dimensions[0];
+        int yd = dimensions[1];
+        final Point location;
+        if (startAtZero) {
+            location = null;
+        } else {
+            location = new Point(Math.toIntExact(target.getLow(xd)),
+                                 Math.toIntExact(target.getLow(yd)));
+        }
+        final int width  = Math.toIntExact(target.getSize(xd));
+        final int height = Math.toIntExact(target.getSize(yd));
+        /*
+         * After this point, xd and yd should be indices relative to source extent.
+         * For now we keep them unchanged on the assumption that the two grid extents have the same dimensions.
+         */
+        long pixelStride = 1;
+        for (int i=0; i<xd; i++) {
+            pixelStride = Math.multiplyExact(pixelStride, target.getSize(i));
+        }
+        long scanlineStride = pixelStride;
+        for (int i=xd; i<yd; i++) {
+            scanlineStride = Math.multiplyExact(scanlineStride, target.getSize(i));
+        }
+        return createRaster(buffer, width, height,
+                Math.toIntExact(pixelStride), Math.toIntExact(scanlineStride), bankIndices, bandOffsets, location);
+    }
+
+    /**
      * Wraps the given data buffer in a raster.
+     * The sample model type is selected according the number of bands and the pixel stride.
      *
      * @param  buffer          buffer that contains the sample values.
      * @param  width           raster width in pixels.
      * @param  height          raster height in pixels.
-     * @param  scanlineStride  line stride of raster data.
+     * @param  pixelStride     number of data elements between two samples for the same band on the same line.
+     * @param  scanlineStride  number of data elements between a given sample and the corresponding sample in the same column of the next line.
      * @param  bankIndices     bank indices for each band, or {@code null} for 0, 1, 2, 3….
-     * @param  bandOffsets     offsets of all bands, or {@code null} for all 0.
+     * @param  bandOffsets     number of data elements from the first element of the bank to the first sample of the band, or {@code null} for all 0.
      * @param  location        the upper-left corner of the raster, or {@code null} for (0,0).
      * @return a raster built from given properties.
      *
+     * @see WritableRaster#createInterleavedRaster(DataBuffer, int, int, int, int, int[], Point)
      * @see WritableRaster#createBandedRaster(DataBuffer, int, int, int, int[], int[], Point)
      */
-    public static WritableRaster createBandedRaster​(final DataBuffer buffer,
-            final int width, final int height, final int scanlineStride,
+    @SuppressWarnings("fallthrough")
+    public static WritableRaster createRaster(final DataBuffer buffer,
+            final int width, final int height, final int pixelStride, final int scanlineStride,
             int[] bankIndices, int[] bandOffsets, final Point location)
     {
-        if (bankIndices == null) {
-            bankIndices = ArraysExt.sequence(0, buffer.getNumBanks());
-        }
+        ArgumentChecks.ensureStrictlyPositive("width",          width);
+        ArgumentChecks.ensureStrictlyPositive("height",         height);
+        ArgumentChecks.ensureStrictlyPositive("pixelStride",    pixelStride);
+        ArgumentChecks.ensureStrictlyPositive("scanlineStride", scanlineStride);
         if (bandOffsets == null) {
-            bandOffsets = new int[bankIndices.length];
+            bandOffsets = new int[buffer.getNumBanks()];
         }
         final int dataType = buffer.getDataType();
-        switch (dataType) {
-            case DataBuffer.TYPE_BYTE:
-            case DataBuffer.TYPE_USHORT:
-            case DataBuffer.TYPE_INT: {
-                // This constructor supports only above-cited types.
-                return WritableRaster.createBandedRaster(buffer, width, width, scanlineStride, bankIndices, bandOffsets, location);
+        /*
+         * This SampleModel variable is a workaround for WritableRaster static methods not supporting all data types.
+         * If 'dataType' is unsupported, then we create a SampleModel ourselves in the 'switch' statements below and
+         * use it for creating a WritableRaster at the end of this method. This variable, together with the 'switch'
+         * statements, may be removed in a future SIS version if all types become supported by the JDK.
+         */
+        @Workaround(library = "JDK", version = "10")
+        final SampleModel model;
+        if (buffer.getNumBanks() == 1 && (bankIndices == null || bankIndices[0] == 0)) {
+            /*
+             * Sample data are stored for all bands in a single bank of the DataBuffer.
+             * Each sample of a pixel occupies one data element of the DataBuffer.
+             * The number of bands is inferred from bandOffsets.length.
+             */
+            switch (dataType) {
+                case DataBuffer.TYPE_BYTE:
+                case DataBuffer.TYPE_USHORT: {
+                    // 'scanlineStride' and 'pixelStride' really interchanged in that method signature.
+                    return WritableRaster.createInterleavedRaster(buffer, width, height, scanlineStride, pixelStride, bandOffsets, location);
+                }
+                case DataBuffer.TYPE_INT: {
+                    if (bandOffsets.length == 1) {
+                        // From JDK javadoc: "To create a 1-band Raster of type TYPE_INT, use createPackedRaster()".
+                        return WritableRaster.createPackedRaster(buffer, width, height, Integer.SIZE, location);
+                    }
+                    // else fallthrough.
+                }
+                default: {
+                    model = new PixelInterleavedSampleModel(dataType, width, height, pixelStride, scanlineStride, bandOffsets);
+                    break;
+                }
+            }
+        } else {
+            if (bankIndices == null) {
+                bankIndices = ArraysExt.sequence(0, bandOffsets.length);
             }
-            default: {
-                SampleModel model = new BandedSampleModel(dataType, width, height, scanlineStride, bankIndices, bandOffsets);
-                return WritableRaster.createWritableRaster(model, buffer, location);
+            if (pixelStride == 1) {
+                /*
+                 * All pixels are consecutive (pixelStride = 1) but may be on many bands.
+                 */
+                switch (dataType) {
+                    case DataBuffer.TYPE_BYTE:
+                    case DataBuffer.TYPE_USHORT:
+                    case DataBuffer.TYPE_INT: {
+                        // This constructor supports only above-cited types.
+                        return WritableRaster.createBandedRaster(buffer, width, width, scanlineStride, bankIndices, bandOffsets, location);
+                    }
+                    default: {
+                        model = new BandedSampleModel(dataType, width, height, scanlineStride, bankIndices, bandOffsets);
+                        break;
+                    }
+                }
+            } else {
+                model = new ComponentSampleModel(dataType, width, height, pixelStride, scanlineStride, bankIndices, bandOffsets);
             }
         }
+        return WritableRaster.createWritableRaster(model, buffer, location);
     }
 
     /**
diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java
index 865cd1d..2728aab 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.java
@@ -79,18 +79,23 @@ public final class Resources extends IndexedResourceBundle {
         public static final short CategoryRangeOverlap_4 = 13;
 
         /**
-         * Indices ({3}) are outside grid coverage. The value at dimension {0} shall be between {1} and
-         * {2} inclusive.
+         * Indices ({3}) are outside grid coverage. The value at dimension {0} shall be between
+         * {1,number} and {2,number} inclusive.
          */
         public static final short GridCoordinateOutsideCoverage_4 = 21;
 
         /**
+         * The grid envelope must have at least {0} dimensions.
+         */
+        public static final short GridEnvelopeMustBeNDimensional_1 = 25;
+
+        /**
          * Sample value range {1} for “{0}” category is illegal.
          */
         public static final short IllegalCategoryRange_2 = 15;
 
         /**
-         * Illegal grid envelope [{1} … {2}] for dimension {0}.
+         * Illegal grid envelope [{1,number} … {2,number}] for dimension {0}.
          */
         public static final short IllegalGridEnvelope_3 = 8;
 
@@ -140,6 +145,12 @@ public final class Resources extends IndexedResourceBundle {
         public static final short NoCategoryForValue_1 = 14;
 
         /**
+         * Can not infer a {0}-dimensional slice from the grid envelope. Dimension {1} has {2,number}
+         * cells.
+         */
+        public static final short NoNDimensionalSlice_3 = 26;
+
+        /**
          * non-linear in {0} dimension{0,choice,1#|2#s}:
          */
         public static final short NonLinearInDimensions_1 = 20;
@@ -150,7 +161,7 @@ public final class Resources extends IndexedResourceBundle {
         public static final short NotStrictlyOrderedDimensions = 24;
 
         /**
-         * The ({0}, {1}) pixel coordinate is outside iterator domain.
+         * The ({0,number}, {1,number}) pixel coordinate is outside iterator domain.
          */
         public static final short OutOfIteratorDomain_2 = 1;
 
diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties
index fd2bd6f..90419df 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties
+++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources.properties
@@ -23,9 +23,10 @@ CanNotEnumerateValuesInRange_1    = Can not enumerate values in the {0} range.
 CanNotMapToGridDimensions         = Some envelope dimensions can not be mapped to grid dimensions.
 CanNotSimplifyTransferFunction_1  = Can not simplify transfer function of sample dimension \u201c{0}\u201d.
 CategoryRangeOverlap_4            = The two categories \u201c{0}\u201d and \u201c{2}\u201d have overlapping ranges: {1} and {3} respectively.
-GridCoordinateOutsideCoverage_4   = Indices ({3}) are outside grid coverage. The value at dimension {0} shall be between {1} and {2} inclusive.
+GridCoordinateOutsideCoverage_4   = Indices ({3}) are outside grid coverage. The value at dimension {0} shall be between {1,number} and {2,number} inclusive.
+GridEnvelopeMustBeNDimensional_1  = The grid envelope must have at least {0} dimensions.
 IllegalCategoryRange_2            = Sample value range {1} for \u201c{0}\u201d category is illegal.
-IllegalGridEnvelope_3             = Illegal grid envelope [{1} \u2026 {2}] for dimension {0}.
+IllegalGridEnvelope_3             = Illegal grid envelope [{1,number} \u2026 {2,number}] for dimension {0}.
 IllegalGridGeometryComponent_1    = Can not create a grid geometry with the given \u201c{0}\u201d component.
 IllegalTransferFunction_1         = Illegal transfer function for \u201c{0}\u201d category.
 IncompatibleTile_2                = The ({0}, {1}) tile has an unexpected size, number of bands or sample layout.
@@ -35,9 +36,10 @@ MismatchedImageLocation           = The two images have different size or pixel
 MismatchedSampleModel             = The two images use different sample models.
 MismatchedTileGrid                = The two images have different tile grid.
 NoCategoryForValue_1              = No category for value {0}.
+NoNDimensionalSlice_3             = Can not infer a {0}-dimensional slice from the grid envelope. Dimension {1} has {2,number} cells.
 NonLinearInDimensions_1           = non-linear in {0} dimension{0,choice,1#|2#s}:
 NotStrictlyOrderedDimensions      = The specified dimensions are not in strictly ascending order.
-OutOfIteratorDomain_2             = The ({0}, {1}) pixel coordinate is outside iterator domain.
+OutOfIteratorDomain_2             = The ({0,number}, {1,number}) pixel coordinate is outside iterator domain.
 PointOutsideCoverageDomain_1      = Point ({0}) is outside the coverage domain.
 TooManyQualitatives               = Too many qualitative categories.
 UnspecifiedCRS                    = Coordinate reference system is unspecified.
diff --git a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties
index 4dcd1d7..f2a6c2d 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties
+++ b/core/sis-raster/src/main/java/org/apache/sis/internal/raster/Resources_fr.properties
@@ -28,9 +28,10 @@ CanNotEnumerateValuesInRange_1    = Ne peut pas \u00e9num\u00e9rer les valeurs d
 CanNotMapToGridDimensions         = Certaines dimensions de l\u2019enveloppe ne correspondent pas \u00e0 des dimensions de la grille.
 CanNotSimplifyTransferFunction_1  = Ne peut pas simplifier la fonction de transfert de la dimension d\u2019\u00e9chantillonnage \u00ab\u202f{0}\u202f\u00bb.
 CategoryRangeOverlap_4            = Les deux cat\u00e9gories \u00ab\u202f{0}\u202f\u00bb et \u00ab\u202f{2}\u202f\u00bb ont des plages de valeurs qui se chevauchent\u2008: {1} et {3} respectivement.
-GridCoordinateOutsideCoverage_4   = Les indices ({3}) sont en dehors du domaine de la grille. La valeur \u00e0 la dimension {0} doit \u00eatre entre {1} et {2} inclusivement.
+GridCoordinateOutsideCoverage_4   = Les indices ({3}) sont en dehors du domaine de la grille. La valeur \u00e0 la dimension {0} doit \u00eatre entre {1,number} et {2,number} inclusivement.
+GridEnvelopeMustBeNDimensional_1  = L\u2019enveloppe de la grille doit avoir au moins {0} dimensions.
 IllegalCategoryRange_2            = La plage de valeurs {1} pour la cat\u00e9gorie \u00ab\u202f{0}\u202f\u00bb est ill\u00e9gale.
-IllegalGridEnvelope_3             = La plage d\u2019index [{1} \u2026 {2}] de la dimension {0} n\u2019est pas valide.
+IllegalGridEnvelope_3             = La plage d\u2019index [{1,number} \u2026 {2,number}] de la dimension {0} n\u2019est pas valide.
 IllegalGridGeometryComponent_1    = Ne peut pas construire une g\u00e9om\u00e9trie de grille avec la composante \u00ab\u202f{0}\u202f\u00bb donn\u00e9e.
 IllegalTransferFunction_1         = Fonction de transfert ill\u00e9gale pour la cat\u00e9gorie \u00ab\u202f{0}\u202f\u00bb.
 IncompatibleTile_2                = La tuile ({0}, {1}) a une taille, un nombre de bandes ou une disposition des valeurs inattendu.
@@ -40,9 +41,10 @@ MismatchedImageLocation           = Les deux images ont une taille ou des coordo
 MismatchedSampleModel             = Les deux images disposent les pixels diff\u00e9remment.
 MismatchedTileGrid                = Les deux images utilisent des grilles de tuiles diff\u00e9rentes.
 NoCategoryForValue_1              = Aucune cat\u00e9gorie n\u2019est d\u00e9finie pour la valeur {0}.
+NoNDimensionalSlice_3             = Ne peut pas inf\u00e9rer une tranche \u00e0 {0} dimensions \u00e0 partir de l\u2019enveloppe de la grille. La dimension {1} a {2,number} cellules.
 NonLinearInDimensions_1           = non-lin\u00e9aire dans {0} dimension{0,choice,1#|2#s}\u2008:
 NotStrictlyOrderedDimensions      = Les dimensions sp\u00e9cifi\u00e9es ne sont pas en ordre strictement croissant.
-OutOfIteratorDomain_2             = La coordonn\u00e9e pixel ({0}, {1}) est en dehors du domaine de l\u2019it\u00e9rateur.
+OutOfIteratorDomain_2             = La coordonn\u00e9e pixel ({0,number}, {1,number}) est en dehors du domaine de l\u2019it\u00e9rateur.
 PointOutsideCoverageDomain_1      = Le point ({0}) est en dehors du domaine de la couverture de donn\u00e9es.
 TooManyQualitatives               = Trop de cat\u00e9gories qualitatives.
 UnspecifiedCRS                    = Le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es n\u2019a pas \u00e9t\u00e9 sp\u00e9cifi\u00e9.
diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridChangeTest.java b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridChangeTest.java
index 00cc475..555016a 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridChangeTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridChangeTest.java
@@ -66,9 +66,9 @@ public final strictfp class GridChangeTest extends TestCase {
         GridExtent   extent = change.getTargetExtent();
         assertExtentEquals(extent, 0,  2000, 5549);                         // Subrange of target extent.
         assertExtentEquals(extent, 1, -1000, 8000);
-        assertArrayEquals("strides", new int[] {50, 300}, change.getTargetStrides());       // s = scaleSource / scaleTarget
+        assertArrayEquals("subsamplings", new int[] {50, 300}, change.getTargetSubsamplings());  // s = scaleSource / scaleTarget
         /*
-         * Scale factors in following matrix shall be the same than above strides.
+         * Scale factors in following matrix shall be the same than above sub-samplings.
          * Translation appears only with PixelInCell different than the one used at construction.
          */
         Matrix3 c = new Matrix3();
@@ -79,7 +79,7 @@ public final strictfp class GridChangeTest extends TestCase {
         c.m12 = 149.5;
         assertMatrixEquals("CELL_CENTER", c, MathTransforms.getMatrix(change.getConversion(PixelInCell.CELL_CENTER)), STRICT);
         /*
-         * If we do not ask for strides, the 'gridToCRS' transforms shall be the same than the 'target' geometry.
+         * If we do not ask for sub-samplings, the 'gridToCRS' transforms shall be the same than the 'target' geometry.
          * The envelope is the intersection of the envelopes of 'source' and 'target' geometries, documented above.
          */
         GridGeometry tg = change.getTargetGeometry();
@@ -91,7 +91,7 @@ public final strictfp class GridChangeTest extends TestCase {
         expected.setRange(1, -7501,  1500);
         assertEnvelopeEquals(expected, tg.getEnvelope(), STRICT);
         /*
-         * If we ask for strides, then the envelope should be approximately the same or smaller. Note that without
+         * If we ask for sub-samplings, then the envelope should be approximately the same or smaller. Note that without
          * the clipping documented in GridExtent(GridExtent, int...) constructor, the envelope could be larger.
          */
         tg = change.getTargetGeometry(50, 300);
diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
index c6ca296..d70678d 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
@@ -23,6 +23,7 @@ import org.opengis.coverage.PointOutsideCoverageException;
 import org.apache.sis.geometry.AbstractEnvelope;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.geometry.GeneralDirectPosition;
+import org.apache.sis.coverage.SubspaceNotSpecifiedException;
 import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.test.TestCase;
@@ -169,6 +170,23 @@ public final strictfp class GridExtentTest extends TestCase {
     }
 
     /**
+     * Tests {@link GridExtent#getSubspaceDimensions(int)}.
+     */
+    @Test
+    public void testGetSubspaceDimensions() {
+        final GridExtent extent = new GridExtent(null, new long[] {100, 5, 200, 40}, new long[] {500, 5, 800, 40}, true);
+        assertArrayEquals(new int[] {0,  2  }, extent.getSubspaceDimensions(2));
+        assertArrayEquals(new int[] {0,1,2  }, extent.getSubspaceDimensions(3));
+        assertArrayEquals(new int[] {0,1,2,3}, extent.getSubspaceDimensions(4));
+        try {
+            extent.getSubspaceDimensions(1);
+            fail("Should not reduce to 1 dimension.");
+        } catch (SubspaceNotSpecifiedException e) {
+            assertNotNull(e.getMessage());
+        }
+    }
+
+    /**
      * Tests {@link GridExtent#toString()}.
      * Note that the string representation may change in any future SIS version.
      */
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
index 15d2346..5ce117b 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
@@ -101,6 +101,11 @@ public final class Resources extends IndexedResourceBundle {
         public static final short MismatchedVariableSize_3 = 8;
 
         /**
+         * Variables “{1}” and “{2}” in netCDF file “{0}” does not have the same type.
+         */
+        public static final short MismatchedVariableType_3 = 13;
+
+        /**
          * Reference system of type ‘{1}’ can not have {2} axes. The axes found in the “{0}” netCDF
          * file are: {3}.
          */
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
index bec3630..c2f0084 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
@@ -27,6 +27,7 @@ CanNotUseUCAR                     = Can not use UCAR library for netCDF format.
 DimensionNotFound_3               = Dimension \u201c{2}\u201d declared by attribute \u201c{1}\u201d is not found in the \u201c{0}\u201d file.
 DuplicatedReference_2             = Duplicated reference to \u201c{1}\u201d in netCDF file \u201c{0}\u201d.
 MismatchedVariableSize_3          = The declared size of variable \u201c{1}\u201d in netCDF file \u201c{0}\u201d is {2} bytes greater than expected.
+MismatchedVariableType_3          = Variables \u201c{1}\u201d and \u201c{2}\u201d in netCDF file \u201c{0}\u201d does not have the same type.
 UnexpectedAxisCount_4             = Reference system of type \u2018{1}\u2019 can not have {2}\u00a0axes. The axes found in the \u201c{0}\u201d netCDF file are: {3}.
 UnexpectedDimensionForVariable_4  = Variable \u201c{1}\u201d in file \u201c{0}\u201d has a dimension \u201c{3}\u201d while we expected \u201c{2}\u201d.
 UnsupportedDataType_3             = NetCDF file \u201c{0}\u201d uses unsupported data type {2} for variable \u201c{1}\u201d.
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
index 6bf855a..8a8a578 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
@@ -32,6 +32,7 @@ CanNotUseUCAR                     = Ne peut pas utiliser la biblioth\u00e8que de
 DimensionNotFound_3               = La dimension \u00ab\u202f{2}\u202f\u00bb d\u00e9clar\u00e9e par l\u2019attribut \u00ab\u202f{1}\u202f\u00bb n\u2019a pas \u00e9t\u00e9 trouv\u00e9e dans le fichier \u00ab\u202f{0}\u202f\u00bb.
 DuplicatedReference_2             = R\u00e9f\u00e9rence vers \u00ab\u202f{1}\u202f\u00bb dupliqu\u00e9e dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb.
 MismatchedVariableSize_3          = La longueur d\u00e9clar\u00e9e de la variable \u00ab\u202f{1}\u202f\u00bb dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb d\u00e9passe de {2} octets la valeur attendue.
+MismatchedVariableType_3          = Les variables \u00ab\u202f{1}\u202f\u00bb et \u00ab\u202f{2}\u202f\u00bb dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb ne sont pas du m\u00eame type.
 UnexpectedAxisCount_4             = Les syst\u00e8mes de r\u00e9f\u00e9rence de type \u2018{1}\u2019 ne peuvent pas avoir {2}\u00a0axes. Les axes trouv\u00e9s dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb sont\u2008: {3}.
 UnexpectedDimensionForVariable_4  = La variable \u00ab\u202f{1}\u202f\u00bb dans le fichier \u00ab\u202f{0}\u202f\u00bb a une dimension \u00ab\u202f{3}\u202f\u00bb alors qu\u2019on attendait \u00ab\u202f{2}\u202f\u00bb.
 UnsupportedDataType_3             = Le fichier netCDF \u00ab\u202f{0}\u202f\u00bb utilise un type de donn\u00e9es non-support\u00e9 {2} pour la variable \u00ab\u202f{1}\u202f\u00bb.
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 6e94e98..1d40374 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
@@ -33,6 +33,7 @@ import org.apache.sis.internal.netcdf.Decoder;
 import org.apache.sis.internal.netcdf.Grid;
 import org.apache.sis.internal.netcdf.DataType;
 import org.apache.sis.internal.netcdf.Variable;
+import org.apache.sis.internal.netcdf.Resources;
 import org.apache.sis.internal.storage.AbstractGridResource;
 import org.apache.sis.internal.storage.ResourceOnFileSystem;
 import org.apache.sis.coverage.SampleDimension;
@@ -353,13 +354,21 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS
         if (domain == null) {
             domain = gridGeometry;
         }
-        final DataType          dataType;
-        final DataBuffer        imageBuffer;
+        final Variable first = data[range[0]];
+        final DataType dataType = first.getDataType();
+        for (int i=1; i<data.length; i++) {
+            final Variable variable = data[range[i]];
+            if (!dataType.equals(variable.getDataType())) {
+                throw new DataStoreContentException(Resources.forLocale(getLocale()).getString(
+                        Resources.Keys.MismatchedVariableType_3, getFilename(), first.getName(), variable.getName()));
+            }
+        }
+        final DataBuffer imageBuffer;
         final SampleDimension[] selected = new SampleDimension[range.length];
         try {
             final Buffer[]   samples = new Buffer[range.length];
             final GridChange change  = new GridChange(domain, gridGeometry);
-            final int[]      strides = change.getTargetStrides();
+            final int[] subsamplings = change.getTargetSubsamplings();
             SampleDimension.Builder builder = null;
             /*
              * Iterate over netCDF variables in the order they appear in the file, not in the order requested
@@ -383,16 +392,15 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS
                         }
                         if (values == null) {
                             // Optional.orElseThrow() below should never fail since Variable.read(…) wraps primitive array.
-                            values = variable.read(change.getTargetExtent(), strides).buffer().get();
+                            values = variable.read(change.getTargetExtent(), subsamplings).buffer().get();
                         }
                         selected[j] = def;
                         samples[j] = values;
                     }
                 }
             }
-            dataType = data[range[0]].getDataType();
+            domain = change.getTargetGeometry(subsamplings);
             imageBuffer = RasterFactory.wrap(dataType.rasterDataType, samples);
-            domain = change.getTargetGeometry(strides);
         } catch (TransformException e) {
             throw new DataStoreReferencingException(e);
         } catch (IOException e) {
@@ -407,6 +415,17 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS
     }
 
     /**
+     * Returns the name of the netCDF file. This is used for error messages.
+     */
+    private String getFilename() {
+        if (location != null) {
+            return location.getFileName().toString();
+        } else {
+            return Vocabulary.getResources(getLocale()).getString(Vocabulary.Keys.Unnamed);
+        }
+    }
+
+    /**
      * Gets the paths to files used by this resource, or an empty array if unknown.
      */
     @Override
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Image.java b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Image.java
index ff75602..ba322ce 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Image.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Image.java
@@ -23,10 +23,10 @@ import java.awt.image.BufferedImage;
 import java.awt.image.RenderedImage;
 import java.awt.image.WritableRaster;
 import org.opengis.geometry.DirectPosition;
+import org.opengis.coverage.CannotEvaluateException;
 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.GridExtent;
 import org.apache.sis.internal.raster.RasterFactory;
 import org.apache.sis.internal.raster.ColorModelFactory;
 
@@ -64,13 +64,15 @@ final class Image extends GridCoverage {
      */
     @Override
     public RenderedImage render(final DirectPosition slicePoint) {
-        // TODO: use slicePoint.
-        final GridExtent extent = getGridGeometry().getExtent();
-        final int width  = Math.toIntExact(extent.getSize(0));
-        final int height = Math.toIntExact(extent.getSize(1));
-        final WritableRaster raster = RasterFactory.createBandedRaster(data, width, height, width, null, null, null);
-        final ColorModel colors = ColorModelFactory.createColorModel(getSampleDimensions(), VISIBLE_BAND, data.getDataType(),
-                ColorModelFactory.GRAYSCALE);
-        return new BufferedImage(colors, raster, false, null);
+        GridGeometry source = getGridGeometry();
+        GridGeometry target = source;
+        if (slicePoint != null) target = target.slice(slicePoint);
+        try {
+            WritableRaster raster = RasterFactory.createRaster(data, null, null, source.getExtent(), target.getExtent(), true);
+            ColorModel colors = ColorModelFactory.createColorModel(getSampleDimensions(), VISIBLE_BAND, data.getDataType(), ColorModelFactory.GRAYSCALE);
+            return new BufferedImage(colors, raster, false, null);
+        } catch (ArithmeticException | IllegalArgumentException e) {
+            throw new CannotEvaluateException(null, e);
+        }
     }
 }


Mime
View raw message