sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Replace Variable.getShape(), Variable.getGridDimensionNames() and Grid.getShape() methods by a method returning a list of Dimension objects.
Date Sun, 17 Feb 2019 16:52:20 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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new ebb7e86  Replace Variable.getShape(), Variable.getGridDimensionNames() and Grid.getShape() methods by a method returning a list of Dimension objects.
ebb7e86 is described below

commit ebb7e862c8520002dbdd33fb003d16d7caf9f7b6
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sun Feb 17 17:51:47 2019 +0100

    Replace Variable.getShape(), Variable.getGridDimensionNames() and Grid.getShape() methods by a method returning a list of Dimension objects.
---
 .../java/org/apache/sis/internal/netcdf/Axis.java  |   2 +-
 .../sis/internal/netcdf/{impl => }/Dimension.java  |  72 ++++++-------
 .../java/org/apache/sis/internal/netcdf/Grid.java  |  26 +++--
 .../org/apache/sis/internal/netcdf/Variable.java   | 113 +++++++++------------
 .../sis/internal/netcdf/impl/ChannelDecoder.java   |  34 ++++---
 .../impl/{Dimension.java => DimensionInfo.java}    |  20 ++--
 .../sis/internal/netcdf/impl/FeaturesInfo.java     |   4 +-
 .../apache/sis/internal/netcdf/impl/GridInfo.java  |  26 ++---
 .../sis/internal/netcdf/impl/VariableInfo.java     |  59 ++++-------
 .../sis/internal/netcdf/ucar/DimensionWrapper.java |  79 ++++++++++++++
 .../sis/internal/netcdf/ucar/GridWrapper.java      |  20 +---
 .../sis/internal/netcdf/ucar/VariableWrapper.java  |  78 +++++++-------
 .../apache/sis/storage/netcdf/MetadataReader.java  |  12 ++-
 .../apache/sis/internal/netcdf/VariableTest.java   |  39 ++++---
 14 files changed, 319 insertions(+), 265 deletions(-)

diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
index 3398198..3306fcd 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
@@ -122,7 +122,7 @@ public final class Axis extends NamedElement {
      * <p>Note that while we defined those values as unsigned for consistency with {@link Variable} dimensions,
      * not all operations in this {@code Axis} class support values greater than the signed integer range.</p>
      *
-     * @see Variable#getShape()
+     * @see Variable#getGridDimensions()
      */
     private final int[] sourceSizes;
 
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/Dimension.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Dimension.java
similarity index 51%
copy from storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/Dimension.java
copy to storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Dimension.java
index 32ffd2b..0923667 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/Dimension.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Dimension.java
@@ -14,9 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.netcdf.impl;
-
-import org.apache.sis.internal.netcdf.NamedElement;
+package org.apache.sis.internal.netcdf;
 
 
 /**
@@ -27,61 +25,67 @@ import org.apache.sis.internal.netcdf.NamedElement;
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.0
- * @since   0.3
+ * @since   1.0
  * @module
  */
-final class Dimension extends NamedElement {
-    /**
-     * The dimension name.
-     */
-    final String name;
-
+public abstract class Dimension extends NamedElement {
     /**
-     * The number of grid cell value along this dimension, as an unsigned number.
+     * Creates a new dimension.
      */
-    final int length;
+    protected Dimension() {
+    }
 
     /**
-     * Whether this dimension is the "record" (also known as "unlimited") dimension.
-     * There is at most one record dimension in a well-formed netCDF file.
+     * Returns the name of this netCDF dimension.
+     *
+     * @return the name of this netCDF dimension.
      */
-    final boolean isUnlimited;
+    @Override
+    public abstract String getName();
 
     /**
-     * Creates a new dimension of the given name and length.
+     * Returns the number of grid cell values along this dimension, or a negative number if undetermined.
+     * The length may be undetermined if this dimension {@linkplain #isUnlimited() is unlimited}.
      *
-     * @param name         the dimension name.
-     * @param length       the number of grid cell value along this dimension, as an unsigned number.
-     * @param isUnlimited  whether this dimension is the "record" (also known as "unlimited") dimension.
+     * @return number of grid cell values.
      */
-    Dimension(final String name, final int length, final boolean isUnlimited) {
-        this.name        = name;
-        this.length      = length;
-        this.isUnlimited = isUnlimited;
-    }
+    public abstract long length();
 
     /**
-     * Returns the name of this netCDF dimension.
+     * Returns whether this dimension can grow.
+     * In netCDF 3 classic format, only the first dimension can be unlimited.
+     *
+     * @return whether this dimension can grow.
+     *
+     * @see Variable#isUnlimited()
      */
-    @Override
-    public String getName() {
-        return name;
-    }
+    protected abstract boolean isUnlimited();
 
     /**
-     * Returns the number of grid cell value along this dimension.
+     * Writes in the given buffer the length of this dimension between bracket.
+     * The length may be unknown (represented by {@code '?'}).
      */
-    final long length() {
-        return Integer.toUnsignedLong(length);
+    final void writeLength(final StringBuilder buffer) {
+        final long length = length();
+        buffer.append('[');
+        if (length > 0) {
+            buffer.append(length);
+        } else {
+            buffer.append('?');
+        }
+        buffer.append(']');
     }
 
     /**
      * A string representation of this dimension for debugging purpose only.
+     *
+     * @return a string representation of this dimension.
      */
     @Override
     public String toString() {
-        final StringBuilder buffer = new StringBuilder(name).append('[').append(length()).append(']');
-        if (isUnlimited) {
+        final StringBuilder buffer = new StringBuilder(getName());
+        writeLength(buffer);
+        if (isUnlimited()) {
             buffer.append(" (unlimited)");
         }
         return buffer.toString();
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
index 0422ffc..24928f3 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
@@ -123,14 +123,15 @@ public abstract class Grid extends NamedElement {
     public abstract int getTargetDimensions();
 
     /**
-     * Returns the number of cells along each source dimension, in "natural" order.
-     * This method may return {@code null} if the grid shape can not be determined.
+     * Returns the dimensions of this grid, in netCDF (reverse of "natural") order. Each element in the list
+     * contains the number of cells in the dimension, together with implementation-specific information.
+     * The list length should be equal to {@link #getSourceDimensions()}.
      *
-     * @return number of cells along each source dimension, in "natural" (opposite of netCDF) order, or {@code null}.
+     * @return the source dimensions of this grid, in netCDF order.
      *
-     * @see Variable#getShape()
+     * @see Variable#getGridDimensions()
      */
-    protected abstract long[] getShape();
+    protected abstract List<Dimension> getDimensions();
 
     /**
      * Returns the axes of the coordinate reference system. The size of this array is expected equals to the
@@ -232,15 +233,20 @@ public abstract class Grid extends NamedElement {
      * if a dimension has unlimited length. The dimension names are informative only.
      *
      * @param  axes  value of {@link #getAxes(Decoder)}. Element order does not matter for this method.
+     * @return the extent, or {@code null} if not available.
      */
     @SuppressWarnings("fallthrough")
     private GridExtent getExtent(final Axis[] axes) {
-        final long[] high = getShape();
-        if (high == null) {
-            return null;
+        final List<Dimension> dimensions = getDimensions();
+        final int n = dimensions.size();
+        final long[] high = new long[n];
+        for (int i=0; i<n; i++) {
+            final long length = dimensions.get(i).length();
+            if (length <= 0) return null;
+            high[(n-1) - i] = length;
         }
-        final DimensionNameType[] names = new DimensionNameType[high.length];
-        switch (names.length) {
+        final DimensionNameType[] names = new DimensionNameType[n];
+        switch (n) {
             default: names[1] = DimensionNameType.ROW;      // Fall through
             case 1:  names[0] = DimensionNameType.COLUMN;   // Fall through
             case 0:  break;
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 657065c..240c967 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
@@ -19,6 +19,7 @@ package org.apache.sis.internal.netcdf;
 import java.util.Map;
 import java.util.LinkedHashMap;
 import java.util.Collection;
+import java.util.List;
 import java.util.Locale;
 import java.util.regex.Pattern;
 import java.io.IOException;
@@ -157,6 +158,9 @@ public abstract class Variable extends NamedElement {
 
     /**
      * Returns the description of this variable, or {@code null} if none.
+     * This information may be encoded in different attributes like {@code "description"}, {@code "title"},
+     * {@code "long_name"} or {@code "standard_name"}. If the return value is non-null, then it should also
+     * be non-empty.
      *
      * @return the description of this variable, or {@code null}.
      */
@@ -172,15 +176,20 @@ public abstract class Variable extends NamedElement {
      * for parsing also its {@linkplain Axis#direction direction}.</p>
      *
      * @return the unit of measurement, or {@code null}.
+     *
+     * @see #getUnit()
      */
     protected abstract String getUnitsString();
 
     /**
      * Parses the given unit symbol and set the {@link #epoch} if the parsed unit is a temporal unit.
+     * This method is invoked by {@link #getUnit()} when first needed.
      *
      * @param  symbols  the unit symbol to parse.
      * @return the parsed unit.
      * @throws Exception if the unit can not be parsed. This wide exception type is used by the UCAR library.
+     *
+     * @see #getUnit()
      */
     protected abstract Unit<?> parseUnit(String symbols) throws Exception;
 
@@ -192,6 +201,8 @@ public abstract class Variable extends NamedElement {
      * @param  other      the variable from which to copy unit and epoch, or {@code null} if none.
      * @param  overwrite  if non-null, set to the given unit instead than the unit of {@code other}.
      * @return the epoch (may be {@code null}).
+     *
+     * @see #getUnit()
      */
     public final Instant setUnit(final Variable other, Unit<?> overwrite) {
         if (other != null) {
@@ -254,33 +265,19 @@ public abstract class Variable extends NamedElement {
      * @return the variable data type, or {@link DataType#UNKNOWN} if unknown.
      *
      * @see #getAttributeType(String)
+     * @see #writeDataTypeName(StringBuilder)
      */
     public abstract DataType getDataType();
 
     /**
-     * Returns the name of the variable data type as the name of the primitive type
-     * followed by the span of each dimension (in unit of grid cells) between brackets.
-     * Example: {@code "SHORT[180][360]"}.
-     *
-     * @return the name of the variable data type.
-     */
-    public final String getDataTypeName() {
-        final StringBuilder buffer = new StringBuilder(20);
-        buffer.append(getDataType().name().toLowerCase());
-        final int[] shape = getShape();
-        for (int i=shape.length; --i>=0;) {
-            buffer.append('[').append(Integer.toUnsignedLong(shape[i])).append(']');
-        }
-        return buffer.toString();
-    }
-
-    /**
      * Returns whether this variable can grow. A variable is unlimited if at least one of its dimension is unlimited.
      * In netCDF 3 classic format, only the first dimension can be unlimited.
      *
      * @return whether this variable can grow.
+     *
+     * @see Dimension#isUnlimited()
      */
-    public abstract boolean isUnlimited();
+    protected abstract boolean isUnlimited();
 
     /**
      * Returns whether this variable is used as a coordinate system axis, a coverage or something else.
@@ -312,8 +309,8 @@ public abstract class Variable extends NamedElement {
      */
     protected VariableRole getRole() {
         int numVectors = 0;                                     // Number of dimension having more than 1 value.
-        for (final int length : getShape()) {
-            if (Integer.toUnsignedLong(length) >= Grid.MIN_SPAN) {
+        for (final Dimension dimension : getGridDimensions()) {
+            if (dimension.length() >= Grid.MIN_SPAN) {
                 numVectors++;
             }
         }
@@ -339,28 +336,19 @@ public abstract class Variable extends NamedElement {
     public abstract Grid getGrid(Decoder decoder) throws IOException, DataStoreException;
 
     /**
-     * Returns the names of the dimensions of this variable, in the order they are declared in the netCDF file.
+     * Returns the dimensions of this variable in the order they are declared in the netCDF file.
      * The dimensions are those of the grid, not the dimensions of the coordinate system.
-     * This information is used for completing ISO 19115 metadata.
-     *
-     * @return the names of all dimension of the grid, in netCDF order (reverse of "natural" order).
-     */
-    public abstract String[] getGridDimensionNames();
-
-    /**
-     * Returns the length (number of cells) of each grid dimension, in the order they are declared in the netCDF file.
-     * The length of this array shall be equals to the length of the {@link #getGridDimensionNames()} array.
-     * Values shall be handled as unsigned 32 bits integers.
+     * In ISO 19123 terminology, {@link Dimension#length()} on each dimension give the upper corner
+     * of the grid envelope plus one. The lower corner is always (0, 0, …, 0).
      *
-     * <p>In ISO 19123 terminology, this method returns the upper corner of the grid envelope plus one.
-     * The lower corner is always (0, 0, …, 0). This method is used by {@link #getRole()} method,
-     * or for building string representations of this variable.</p>
+     * <p>This information is used for completing ISO 19115 metadata, providing a default implementation of
+     * {@link #getRole()} method, or for building string representation of this variable among others.</p>
      *
-     * @return the number of grid cells for each dimension, as unsigned integer in netCDF order (reverse of "natural" order).
+     * @return all dimension of the grid, in netCDF order (reverse of "natural" order).
      *
-     * @see Grid#getShape()
+     * @see Grid#getDimensions()
      */
-    public abstract int[] getShape();
+    public abstract List<Dimension> getGridDimensions();
 
     /**
      * Returns the names of all attributes associated to this variable.
@@ -400,7 +388,7 @@ public abstract class Variable extends NamedElement {
      * @param  numeric        {@code true} if the value is expected to be numeric, or {@code false} for string.
      * @return the {@link String} or {@link Number} value for the named attribute.
      */
-    public final Object getAttributeValue(final String attributeName, final boolean numeric) {
+    private Object getAttributeValue(final String attributeName, final boolean numeric) {
         Object singleton = null;
         for (final Object value : getAttributeValues(attributeName, numeric)) {
             if (value != null) {
@@ -414,7 +402,7 @@ public abstract class Variable extends NamedElement {
     }
 
     /**
-     * Returns the value of the given attribute as a non-blank string and leading/trailing spaces removed.
+     * Returns the value of the given attribute as a non-blank string with leading/trailing spaces removed.
      * This is a convenience method for {@link #getAttributeValues(String, boolean)} when a singleton value
      * is expected and blank strings ignored.
      *
@@ -532,22 +520,6 @@ public abstract class Variable extends NamedElement {
     }
 
     /**
-     * Compares two numbers which shall be of the same class.
-     */
-    @SuppressWarnings("unchecked")
-    private static int compare(final Number n1, final Number n2) {
-        return ((Comparable) n1).compareTo((Comparable) n2);
-    }
-
-    /**
-     * Whether {@link #read()} invoked {@link Vector#compress(double)} on the returned vector.
-     * This information is used for avoiding to do twice some potentially costly operations.
-     *
-     * @return whether {@link #read()} invokes {@link Vector#compress(double)}.
-     */
-    protected abstract boolean readTriesToCompress();
-
-    /**
      * Reads all the data for this variable and returns them as an array of a Java primitive type.
      * Multi-dimensional variables are flattened as a one-dimensional array (wrapped in a vector).
      * Example:
@@ -581,14 +553,14 @@ public abstract class Variable extends NamedElement {
      * @throws DataStoreException if a logical error occurred.
      * @throws ArithmeticException if the size of the variable exceeds {@link Integer#MAX_VALUE}, or other overflow occurs.
      */
-    public abstract Vector read() throws IOException, DataStoreException;
+    protected abstract Vector read() throws IOException, DataStoreException;
 
     /**
      * Reads a subsampled sub-area of the variable.
      * Constraints on the argument values are:
      *
      * <ul>
-     *   <li>Argument dimensions shall be equal to the length of the {@link #getShape()} array.</li>
+     *   <li>Argument dimensions shall be equal to the size of the {@link #getGridDimensions()} list.</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(getShape()[length - 1 - i])} exclusive.</li>
      *   <li>Values are in "natural" order (inverse of netCDF order).</li>
@@ -741,22 +713,37 @@ public abstract class Variable extends NamedElement {
      * @param  key        one or {@link Errors.Keys} constants.
      * @param  arguments  values to be formatted in the {@link java.text.MessageFormat} pattern.
      */
-    protected final void error(final Class<?> caller, final String method, final Exception exception, final short key, final Object... arguments) {
+    final void error(final Class<?> caller, final String method, final Exception exception, final short key, final Object... arguments) {
         warning(listeners, caller, method, exception, Errors.getResources(listeners.getLocale()), key, arguments);
     }
 
     /**
+     * Appends the name of the variable data type as the name of the primitive type
+     * followed by the span of each dimension (in unit of grid cells) between brackets.
+     * Dimensions are listed in "natural" order (reverse of netCDF order).
+     * Example: {@code "SHORT[360][180]"}.
+     *
+     * @param  buffer  the buffer when to append the name of the variable data type.
+     */
+    public final void writeDataTypeName(final StringBuilder buffer) {
+        buffer.append(getDataType().name().toLowerCase(Locale.US));
+        final List<Dimension> dimensions = getGridDimensions();
+        for (int i=dimensions.size(); --i>=0;) {
+            dimensions.get(i).writeLength(buffer);
+        }
+    }
+
+    /**
      * Returns a string representation of this variable for debugging purpose.
      *
      * @return a string representation of this variable.
+     *
+     * @see #writeDataTypeName(StringBuilder)
      */
     @Override
     public String toString() {
-        final StringBuilder buffer = new StringBuilder(getName()).append(" : ").append(getDataType());
-        final int[] shape = getShape();
-        for (int i=shape.length; --i>=0;) {
-            buffer.append('[').append(Integer.toUnsignedLong(shape[i])).append(']');
-        }
+        final StringBuilder buffer = new StringBuilder(getName()).append(" : ");
+        writeDataTypeName(buffer);
         if (isUnlimited()) {
             buffer.append(" (unlimited)");
         }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
index 16e7d4c..f135d66 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
@@ -194,7 +194,7 @@ public final class ChannelDecoder extends Decoder {
      * @see #readDimensions(int)
      * @see #findDimension(String)
      */
-    private Map<String,Dimension> dimensionMap;
+    private Map<String,DimensionInfo> dimensionMap;
 
     /**
      * The grid geometries, created when first needed.
@@ -213,7 +213,7 @@ public final class ChannelDecoder extends Decoder {
      *   <li>Number of records | {@value #STREAMING}</li>
      *   <li>List of netCDF dimensions  (see {@link #readDimensions(int)})</li>
      *   <li>List of global attributes  (see {@link #readAttributes(int)})</li>
-     *   <li>List of variables          (see {@link #readVariables(int, Dimension[])})</li>
+     *   <li>List of variables          (see {@link #readVariables(int, DimensionInfo[])})</li>
      * </ul>
      *
      * @param  input      the channel and the buffer from where data are read.
@@ -253,7 +253,7 @@ public final class ChannelDecoder extends Decoder {
          * Read the dimension, attribute and variable declarations. We expect exactly 3 lists,
          * where any of them can be flagged as absent by a long (64 bits) 0.
          */
-        Dimension[]        dimensions = null;
+        DimensionInfo[]    dimensions = null;
         VariableInfo[]     variables  = null;
         Map<String,Object> attributes = null;
         for (int i=0; i<3; i++) {
@@ -497,8 +497,8 @@ public final class ChannelDecoder extends Decoder {
      * @param  nelems  the number of dimensions to read.
      * @return the dimensions in the order they are declared in the netCDF file.
      */
-    private Dimension[] readDimensions(final int nelems) throws IOException, DataStoreContentException {
-        final Dimension[] dimensions = new Dimension[nelems];
+    private DimensionInfo[] readDimensions(final int nelems) throws IOException, DataStoreContentException {
+        final DimensionInfo[] dimensions = new DimensionInfo[nelems];
         for (int i=0; i<nelems; i++) {
             final String name = readName();
             int length = input.readInt();
@@ -509,7 +509,7 @@ public final class ChannelDecoder extends Decoder {
                     throw new DataStoreContentException(errors().getString(Errors.Keys.MissingValueForProperty_1, tagPath("numrecs")));
                 }
             }
-            dimensions[i] = new Dimension(name, length, isUnlimited);
+            dimensions[i] = new DimensionInfo(name, length, isUnlimited);
         }
         dimensionMap = toCaseInsensitiveNameMap(dimensions);
         return dimensions;
@@ -570,7 +570,9 @@ public final class ChannelDecoder extends Decoder {
      * @throws DataStoreContentException if a logical error is detected.
      * @throws ArithmeticException if a variable is too large.
      */
-    private VariableInfo[] readVariables(final int nelems, final Dimension[] allDimensions) throws IOException, DataStoreException {
+    private VariableInfo[] readVariables(final int nelems, final DimensionInfo[] allDimensions)
+            throws IOException, DataStoreException
+    {
         if (allDimensions == null) {
             throw malformedHeader();        // May happen if readDimensions(…) has not been invoked.
         }
@@ -578,7 +580,7 @@ public final class ChannelDecoder extends Decoder {
         for (int j=0; j<nelems; j++) {
             final String name = readName();
             final int n = input.readInt();
-            final Dimension[] varDims = new Dimension[n];
+            final DimensionInfo[] varDims = new DimensionInfo[n];
             try {
                 for (int i=0; i<n; i++) {
                     varDims[i] = allDimensions[input.readInt()];
@@ -692,11 +694,11 @@ public final class ChannelDecoder extends Decoder {
      * @param  dimName  the name of the dimension to search.
      * @return dimension of the given name, or {@code null} if none.
      */
-    final Dimension findDimension(final String dimName) {
-        Dimension dim = dimensionMap.get(dimName);         // Give precedence to exact match before to ignore case.
+    final DimensionInfo findDimension(final String dimName) {
+        DimensionInfo dim = dimensionMap.get(dimName);          // Give precedence to exact match before to ignore case.
         if (dim == null) {
             final String lower = dimName.toLowerCase(ChannelDecoder.NAME_LOCALE);
-            if (lower != dimName) {                         // Identity comparison is okay here.
+            if (lower != dimName) {                             // Identity comparison is okay here.
                 dim = dimensionMap.get(lower);
             }
         }
@@ -864,7 +866,7 @@ public final class ChannelDecoder extends Decoder {
      * @param  dimensions  where to report all dimensions used by added axes.
      * @return whether {@code names} was non-null.
      */
-    private boolean listAxes(final CharSequence[] names, final Set<VariableInfo> axes, final Set<Dimension> dimensions) {
+    private boolean listAxes(final CharSequence[] names, final Set<VariableInfo> axes, final Set<DimensionInfo> dimensions) {
         if (names == null) {
             return false;
         }
@@ -897,7 +899,7 @@ public final class ChannelDecoder extends Decoder {
              * from grid coordinates to CRS coordinates). For each key there is usually only one value, but
              * more complicated netCDF files (e.g. using two-dimensional localisation grids) also exist.
              */
-            final Map<Dimension, List<VariableInfo>> dimToAxes = new IdentityHashMap<>();
+            final Map<DimensionInfo, List<VariableInfo>> dimToAxes = new IdentityHashMap<>();
             for (final VariableInfo variable : variables) {
                 switch (convention().roleOf(variable)) {
                     case COVERAGE: {
@@ -908,7 +910,7 @@ public final class ChannelDecoder extends Decoder {
                     }
                     case AXIS: {
                         variable.isCoordinateSystemAxis = true;
-                        for (final Dimension dimension : variable.dimensions) {
+                        for (final DimensionInfo dimension : variable.dimensions) {
                             CollectionsExt.addToMultiValuesMap(dimToAxes, dimension, variable);
                         }
                     }
@@ -919,7 +921,7 @@ public final class ChannelDecoder extends Decoder {
              * so we remember the previously created instances in order to share the grid geometry instances.
              */
             final Set<VariableInfo> axes = new LinkedHashSet<>(8);
-            final Set<Dimension> usedDimensions = new HashSet<>(8);
+            final Set<DimensionInfo> usedDimensions = new HashSet<>(8);
             final Map<GridInfo,GridInfo> shared = new LinkedHashMap<>();
 nextVar:    for (final VariableInfo variable : variables) {
                 if (variable.isCoordinateSystemAxis || variable.dimensions.length == 0) {
@@ -943,7 +945,7 @@ nextVar:    for (final VariableInfo variable : variables) {
                  * incomplete attributes, so we check for other dimensions even if the above loop did some work.
                  */
                 for (int i=variable.dimensions.length; --i >= 0;) {                     // Reverse of netCDF order.
-                    final Dimension dimension = variable.dimensions[i];
+                    final DimensionInfo dimension = variable.dimensions[i];
                     if (usedDimensions.add(dimension)) {
                         final List<VariableInfo> axis = dimToAxes.get(dimension);       // Should have only 1 element.
                         if (axis == null) {
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/Dimension.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/DimensionInfo.java
similarity index 81%
rename from storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/Dimension.java
rename to storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/DimensionInfo.java
index 32ffd2b..64280e5 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/Dimension.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/DimensionInfo.java
@@ -16,7 +16,7 @@
  */
 package org.apache.sis.internal.netcdf.impl;
 
-import org.apache.sis.internal.netcdf.NamedElement;
+import org.apache.sis.internal.netcdf.Dimension;
 
 
 /**
@@ -30,7 +30,7 @@ import org.apache.sis.internal.netcdf.NamedElement;
  * @since   0.3
  * @module
  */
-final class Dimension extends NamedElement {
+final class DimensionInfo extends Dimension {
     /**
      * The dimension name.
      */
@@ -54,7 +54,7 @@ final class Dimension extends NamedElement {
      * @param length       the number of grid cell value along this dimension, as an unsigned number.
      * @param isUnlimited  whether this dimension is the "record" (also known as "unlimited") dimension.
      */
-    Dimension(final String name, final int length, final boolean isUnlimited) {
+    DimensionInfo(final String name, final int length, final boolean isUnlimited) {
         this.name        = name;
         this.length      = length;
         this.isUnlimited = isUnlimited;
@@ -70,20 +70,18 @@ final class Dimension extends NamedElement {
 
     /**
      * Returns the number of grid cell value along this dimension.
+     * In this implementation, the length is never undetermined (negative).
      */
-    final long length() {
+    @Override
+    public long length() {
         return Integer.toUnsignedLong(length);
     }
 
     /**
-     * A string representation of this dimension for debugging purpose only.
+     * Returns whether this dimension can grow.
      */
     @Override
-    public String toString() {
-        final StringBuilder buffer = new StringBuilder(name).append('[').append(length()).append(']');
-        if (isUnlimited) {
-            buffer.append(" (unlimited)");
-        }
-        return buffer.toString();
+    protected boolean isUnlimited() {
+        return isUnlimited;
     }
 }
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 e419064..0bcd1fb 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
@@ -188,8 +188,8 @@ search: for (final VariableInfo counts : decoder.variables) {
             if (counts.dimensions.length == 1 && counts.getDataType().isInteger) {
                 final Object sampleDimName = counts.getAttributeValue(CF.SAMPLE_DIMENSION);
                 if (sampleDimName instanceof String) {
-                    final Dimension featureDimension = counts.dimensions[0];
-                    final Dimension sampleDimension = decoder.findDimension((String) sampleDimName);
+                    final DimensionInfo featureDimension = counts.dimensions[0];
+                    final DimensionInfo sampleDimension = decoder.findDimension((String) sampleDimName);
                     if (sampleDimension == null) {
                         decoder.listeners.warning(decoder.resources().getString(Resources.Keys.DimensionNotFound_3,
                                 decoder.getFilename(), counts.getName(), sampleDimName), null);
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
index 0aea558..265abd9 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
@@ -18,6 +18,7 @@ package org.apache.sis.internal.netcdf.impl;
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.HashMap;
@@ -26,7 +27,9 @@ import java.util.SortedMap;
 import org.apache.sis.internal.netcdf.Axis;
 import org.apache.sis.internal.netcdf.Grid;
 import org.apache.sis.internal.netcdf.Decoder;
+import org.apache.sis.internal.netcdf.Dimension;
 import org.apache.sis.internal.netcdf.Resources;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.measure.Units;
@@ -80,10 +83,10 @@ final class GridInfo extends Grid {
      * They are the dimensions of the grid (<strong>not</strong> the dimensions of the CRS).
      * Dimensions are listed in the order they appear in netCDF file (reverse of "natural" order).
      *
-     * @see #getShape()
+     * @see #getDimensions()
      * @see VariableInfo#dimensions
      */
-    private final Dimension[] domain;
+    private final DimensionInfo[] domain;
 
     /**
      * Describes the output values calculated by the function converting grid indices to geodetic coordinates.
@@ -99,7 +102,7 @@ final class GridInfo extends Grid {
      * @param  domain  describes the input values of the "grid to CRS" conversion, in netCDF order.
      * @param  range   the output values of the "grid to CRS" conversion, in CRS order as much as possible.
      */
-    GridInfo(final Dimension[] domain, final VariableInfo[] range) {
+    GridInfo(final DimensionInfo[] domain, final VariableInfo[] range) {
         this.domain = domain;
         this.range  = range;
     }
@@ -159,18 +162,11 @@ final class GridInfo extends Grid {
     }
 
     /**
-     * Returns the number of cells along each source dimension, in "natural" order.
-     *
-     * @return number of cells along each source dimension, in "natural" (opposite of netCDF) order.
+     * Returns the dimensions of this grid, in netCDF (reverse of "natural") order.
      */
     @Override
-    protected long[] getShape() {
-        final int    dim  = domain.length;
-        final long[] size = new long[dim];
-        for (int i=0; i<dim; i++) {
-            size[(dim-1) - i] = domain[i].length();
-        }
-        return size;
+    protected List<Dimension> getDimensions() {
+        return UnmodifiableArrayList.wrap(domain);
     }
 
     /**
@@ -228,10 +224,10 @@ final class GridInfo extends Grid {
              * straightforward netCDF files. However some more complex files may have 2 dimensions.
              */
             int i = 0;
-            final Dimension[] axisDomain = axis.dimensions;
+            final DimensionInfo[] axisDomain = axis.dimensions;
             final int[] indices = new int[axisDomain.length];
             final int[] sizes   = new int[axisDomain.length];
-            for (final Dimension dimension : axisDomain) {
+            for (final DimensionInfo dimension : axisDomain) {
                 for (int sourceDim = 0; sourceDim < domain.length; sourceDim++) {
                     if (domain[sourceDim] == dimension) {
                         indices[i] = sourceDim;
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 ac2af88..7057ebd 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
@@ -17,6 +17,7 @@
 package org.apache.sis.internal.netcdf.impl;
 
 import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.HashSet;
@@ -32,6 +33,7 @@ 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.Dimension;
 import org.apache.sis.internal.netcdf.Grid;
 import org.apache.sis.internal.netcdf.Variable;
 import org.apache.sis.internal.netcdf.VariableRole;
@@ -40,6 +42,7 @@ import org.apache.sis.internal.storage.io.ChannelDataInput;
 import org.apache.sis.internal.storage.io.HyperRectangleReader;
 import org.apache.sis.internal.storage.io.Region;
 import org.apache.sis.internal.util.StandardDateFormat;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.storage.netcdf.AttributeNames;
@@ -97,10 +100,10 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
      * this variable (a flattened one-dimensional sequence of values), index in the domain of {@code dimensions[length-1]}
      * varies faster, followed by index in the domain of {@code dimensions[length-2]}, <i>etc.</i>
      *
-     * @see #getShape()
+     * @see #getGridDimensions()
      * @see GridInfo#domain
      */
-    final Dimension[] dimensions;
+    final DimensionInfo[] dimensions;
 
     /**
      * The offset (in bytes) to apply for moving to the next record in variable data, or -1 if unknown.
@@ -193,7 +196,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
      */
     VariableInfo(final ChannelDataInput      input,
                  final String                name,
-                 final Dimension[]           dimensions,
+                 final DimensionInfo[]       dimensions,
                  final Map<String,Object>    attributes,
                        DataType              dataType,
                  final int                   size,
@@ -216,7 +219,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
          */
         if (dataType != null && (offsetToNextRecord = dataType.size()) != 0) {
             for (int i=0; i<dimensions.length; i++) {
-                final Dimension dim = dimensions[i];
+                final DimensionInfo dim = dimensions[i];
                 if (!dim.isUnlimited) {
                     offsetToNextRecord = Math.multiplyExact(offsetToNextRecord, dim.length());
                 } else if (i != 0) {
@@ -424,7 +427,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
     /**
      * Returns {@code true} if this variable is an enumeration.
      */
-    public boolean isEnumeration() {
+    final boolean isEnumeration() {
         return meanings != null;
     }
 
@@ -433,7 +436,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
      * In netCDF 3 classic format, only the first dimension can be unlimited.
      */
     @Override
-    public boolean isUnlimited() {
+    protected boolean isUnlimited() {
         // The constructor verified that only the first dimension is unlimited.
         return (dimensions.length != 0) && dimensions[0].isUnlimited;
     }
@@ -480,32 +483,14 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
     }
 
     /**
-     * Returns the names of the dimensions of this variable.
-     * The dimensions are those of the grid, not the dimensions of the coordinate system.
-     * This information is used for completing ISO 19115 metadata.
+     * Returns the dimensions of this variable in the order they are declared in the netCDF file.
+     * The dimensions are those of the grid, not the dimensions (or axes) of the coordinate system.
+     * In ISO 19123 terminology, the {@linkplain Dimension#length() dimension lengths} give the upper
+     * corner of the grid envelope plus one. The lower corner is always (0, 0, …, 0).
      */
     @Override
-    public String[] getGridDimensionNames() {
-        final String[] names = new String[dimensions.length];
-        for (int i=0; i<names.length; i++) {
-            names[i] = dimensions[i].name;
-        }
-        return names;
-    }
-
-    /**
-     * Returns the length (number of cells) of each grid dimension. In ISO 19123 terminology, this method
-     * returns the upper corner of the grid envelope plus one. The lower corner is always (0,0,…,0).
-     *
-     * @return the number of grid cells for each dimension, as unsigned integers.
-     */
-    @Override
-    public int[] getShape() {
-        final int[] shape = new int[dimensions.length];
-        for (int i=0; i<shape.length; i++) {
-            shape[i] = dimensions[i].length;
-        }
-        return shape;
+    public List<Dimension> getGridDimensions() {
+        return UnmodifiableArrayList.wrap(dimensions);
     }
 
     /**
@@ -663,16 +648,6 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
     }
 
     /**
-     * Whether {@link #read()} invokes {@link Vector#compress(double)} on the returned vector.
-     *
-     * @return {@code true}.
-     */
-    @Override
-    protected boolean readTriesToCompress() {
-        return true;
-    }
-
-    /**
      * Sets the values in this variable. The values are normally read from the netCDF file by the {@link #read()} method,
      * but this {@code setValues(Object)} method may also be invoked if we want to overwrite those values.
      *
@@ -713,7 +688,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
      */
     @Override
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
-    public Vector read() throws IOException, DataStoreContentException {
+    protected Vector read() throws IOException, DataStoreContentException {
         if (values == null) {
             if (reader == null) {
                 throw new DataStoreContentException(unknownType());
@@ -840,7 +815,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
      * @param  ordinal  the ordinal of the enumeration for which to get the value.
      * @return the value associated to the given ordinal, or {@code null} if none.
      */
-    public String meaning(final int ordinal) {
+    final String meaning(final int ordinal) {
         return (ordinal >= 0 && ordinal < meanings.length) ? meanings[ordinal] : null;
     }
 
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DimensionWrapper.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DimensionWrapper.java
new file mode 100644
index 0000000..e2949ba
--- /dev/null
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DimensionWrapper.java
@@ -0,0 +1,79 @@
+/*
+ * 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.internal.netcdf.ucar;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import ucar.nc2.Dimension;
+
+
+/**
+ * Wrapper around a UCAR {@link Dimension} for transmitting information to classes outside this package.
+ * Temporary instances of this class are created when required by API for the needs of classes outside this package,
+ * then discarded. We do not save references to {@code DimensionWrapper} since they do not provide new capabilities.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+final class DimensionWrapper extends org.apache.sis.internal.netcdf.Dimension {
+    /**
+     * Wraps all given dimensions.
+     */
+    static List<org.apache.sis.internal.netcdf.Dimension> wrap(final List<Dimension> dimensions) {
+        return dimensions.stream().map(DimensionWrapper::new).collect(Collectors.toList());
+    }
+
+    /**
+     * The netCDF dimension object.
+     */
+    private final Dimension netcdf;
+
+    /**
+     * Wraps the given netCDF dimension object.
+     */
+    private DimensionWrapper(final Dimension netcdf) {
+        this.netcdf = netcdf;
+    }
+
+    /**
+     * Returns the name of this netCDF dimension.
+     */
+    @Override
+    public String getName() {
+        return netcdf.getShortName();
+    }
+
+    /**
+     * Returns the number of grid cell values along this dimension, or a negative number if undetermined.
+     * The length may be undetermined if this dimension {@linkplain #isUnlimited() is unlimited}.
+     */
+    @Override
+    public long length() {
+        return netcdf.getLength();      // May be negative.
+    }
+
+    /**
+     * Returns whether this dimension can grow.
+     */
+    @Override
+    protected boolean isUnlimited() {
+        return netcdf.isUnlimited();
+    }
+}
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
index 585af16..816faaf 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
@@ -61,6 +61,8 @@ final class GridWrapper extends Grid {
      *   <li>{@link ucar.nc2.Variable#getDimensions()}</li>
      *   <li>{@link ucar.nc2.dataset.CoordinateSystem#getDomain()}</li>
      * </ul>
+     *
+     * @see #getDimensions()
      */
     private final List<Dimension> domain;
 
@@ -140,23 +142,11 @@ final class GridWrapper extends Grid {
     }
 
     /**
-     * Returns the number of cells along each source dimension, in "natural" order.
-     * This method may return {@code null} if the grid shape can not be determined.
-     *
-     * @return number of cells along each source dimension, in "natural" (opposite of netCDF) order, or {@code null}.
+     * Returns the dimensions of this grid, in netCDF (reverse of "natural") order.
      */
     @Override
-    protected long[] getShape() {
-        int dim = domain.size();
-        final long[] size = new long[dim--];
-        for (int i=0; i<=dim; i++) {
-            final int length = domain.get(i).getLength();
-            if (length <= 0) {
-                return null;
-            }
-            size[dim - i] = length;
-        }
-        return size;
+    protected List<org.apache.sis.internal.netcdf.Dimension> getDimensions() {
+        return DimensionWrapper.wrap(domain);
     }
 
     /**
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 2940862..50c711f 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
@@ -17,15 +17,14 @@
 package org.apache.sis.internal.netcdf.ucar;
 
 import java.util.List;
+import java.util.Collection;
 import java.io.File;
 import java.io.IOException;
-import java.util.Collection;
 import javax.measure.Unit;
 import ucar.ma2.Array;
 import ucar.ma2.Section;
 import ucar.ma2.InvalidRangeException;
 import ucar.nc2.Attribute;
-import ucar.nc2.Dimension;
 import ucar.nc2.VariableIF;
 import ucar.nc2.dataset.Enhancements;
 import ucar.nc2.dataset.VariableEnhanced;
@@ -83,6 +82,23 @@ final class VariableWrapper extends Variable {
     private transient Vector values;
 
     /**
+     * The grid associated to this variable, or {@code null} if none or not yet computed.
+     * The grid needs to be computed if {@link #gridDetermined} is {@code false}.
+     *
+     * @see #gridDetermined
+     * @see #getGrid(Decoder)
+     */
+    private transient GridWrapper grid;
+
+    /**
+     * Whether {@link #grid} has been computed. Note that the result may still null.
+     *
+     * @see #grid
+     * @see #getGrid(Decoder)
+     */
+    private transient boolean gridDetermined;
+
+    /**
      * Creates a new variable wrapping the given netCDF interface.
      */
     VariableWrapper(final WarningListeners<?> listeners, VariableIF v) {
@@ -208,7 +224,7 @@ final class VariableWrapper extends Variable {
      * Returns whether this variable can grow. A variable is unlimited if at least one of its dimension is unlimited.
      */
     @Override
-    public boolean isUnlimited() {
+    protected boolean isUnlimited() {
         return variable.isUnlimited();
     }
 
@@ -229,42 +245,32 @@ final class VariableWrapper extends Variable {
      */
     @Override
     public Grid getGrid(final Decoder decoder) throws IOException, DataStoreException {
-        if (variable instanceof Enhancements) {
-            final List<CoordinateSystem> cs = ((Enhancements) variable).getCoordinateSystems();
-            if (cs != null && !cs.isEmpty()) {
-                for (final Grid grid : decoder.getGrids()) {
-                    final GridWrapper g = ((GridWrapper) grid).forVariable(variable, cs);
-                    if (g != null) {
-                        return g;
+        if (!gridDetermined) {
+            gridDetermined = true;                      // Set first so we don't try twice in case of failure.
+            if (variable instanceof Enhancements) {
+                final List<CoordinateSystem> systems = ((Enhancements) variable).getCoordinateSystems();
+                if (!systems.isEmpty()) {
+                    for (final Grid candidate : decoder.getGrids()) {
+                        grid = ((GridWrapper) candidate).forVariable(variable, systems);
+                        if (grid != null) {
+                            break;
+                        }
                     }
                 }
             }
         }
-        return null;
+        return grid;
     }
 
     /**
-     * Returns the names of the dimensions of this variable.
-     * The dimensions are those of the grid, not the dimensions of the coordinate system.
-     * This information is used for completing ISO 19115 metadata.
+     * Returns the dimensions of this variable in the order they are declared in the netCDF file.
+     * The dimensions are those of the grid, not the dimensions (or axes) of the coordinate system.
+     * In ISO 19123 terminology, the dimension lengths give the upper corner of the grid envelope plus one.
+     * The lower corner is always (0, 0, …, 0).
      */
     @Override
-    public String[] getGridDimensionNames() {
-        final List<Dimension> dimensions = variable.getDimensions();
-        final String[] names = new String[dimensions.size()];
-        for (int i=0; i<names.length; i++) {
-            names[i] = dimensions.get(i).getShortName();
-        }
-        return names;
-    }
-
-    /**
-     * Returns the length (number of cells) of each grid dimension. In ISO 19123 terminology, this method
-     * returns the upper corner of the grid envelope plus one. The lower corner is always (0,0,…,0).
-     */
-    @Override
-    public int[] getShape() {
-        return variable.getShape();
+    public List<org.apache.sis.internal.netcdf.Dimension> getGridDimensions() {
+        return DimensionWrapper.wrap(variable.getDimensions());
     }
 
     /**
@@ -363,23 +369,13 @@ final class VariableWrapper extends Variable {
     }
 
     /**
-     * Whether {@link #read()} invokes {@link Vector#compress(double)} on the returned vector.
-     *
-     * @return {@code false}.
-     */
-    @Override
-    protected boolean readTriesToCompress() {
-        return false;
-    }
-
-    /**
      * Reads all the data for this variable and returns them as an array of a Java primitive type.
      * Multi-dimensional variables are flattened as a one-dimensional array (wrapped in a vector).
      * This method may replace fill/missing values by NaN values and caches the returned vector.
      */
     @Override
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
-    public Vector read() throws IOException {
+    protected Vector read() throws IOException {
         if (values == null) {
             final Array array = variable.read();                // May be already cached by the UCAR library.
             values = createDecimalVector(get1DJavaArray(array), variable.isUnsigned());
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
index be9f2ac..bf3e41b 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
@@ -903,8 +903,12 @@ split:  while ((start = CharSequences.skipLeadingWhitespaces(value, start, lengt
         final Map<List<String>, List<Variable>> contents = new HashMap<>(4);
         for (final Variable variable : decoder.getVariables()) {
             if (decoder.convention().roleOf(variable) == VariableRole.COVERAGE) {
-                final List<String> dimensions = Arrays.asList(variable.getGridDimensionNames());
-                CollectionsExt.addToMultiValuesMap(contents, dimensions, variable);
+                final List<org.apache.sis.internal.netcdf.Dimension> dimensions = variable.getGridDimensions();
+                final String[] names = new String[dimensions.size()];
+                for (int i=0; i<names.length; i++) {
+                    names[i] = dimensions.get(i).getName();
+                }
+                CollectionsExt.addToMultiValuesMap(contents, Arrays.asList(names), variable);
             }
         }
         final String processingLevel = stringValue(PROCESSING_LEVEL);
@@ -945,7 +949,9 @@ split:  while ((start = CharSequences.skipLeadingWhitespaces(value, start, lengt
         final String name = trim(variable.getName());
         if (name != null) {
             final NameFactory f = decoder.nameFactory;
-            setBandIdentifier(f.createMemberName(null, name, f.createTypeName(null, variable.getDataTypeName())));
+            final StringBuilder buffer = new StringBuilder(20);
+            variable.writeDataTypeName(buffer);
+            setBandIdentifier(f.createMemberName(null, name, f.createTypeName(null, buffer.toString())));
         }
         final String id = variable.getAttributeAsString(CF.STANDARD_NAME);
         if (id != null && !id.equals(name)) {
diff --git a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/VariableTest.java b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/VariableTest.java
index 019750c..e200ff0 100644
--- a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/VariableTest.java
+++ b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/VariableTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.internal.netcdf;
 
+import java.util.List;
 import java.io.IOException;
 import java.time.Instant;
 import org.apache.sis.math.Vector;
@@ -91,7 +92,7 @@ public strictfp class VariableTest extends TestCase {
      *   <li>{@link Variable#getName()}</li>
      *   <li>{@link Variable#getDescription()}</li>
      *   <li>{@link Variable#getDataType()}</li>
-     *   <li>{@link Variable#getShape()} length</li>
+     *   <li>{@link Variable#getGridDimensions()} length</li>
      *   <li>{@link Variable#getRole()}</li>
      * </ul>
      *
@@ -127,7 +128,7 @@ public strictfp class VariableTest extends TestCase {
             assertEquals(name, expected[propertyIndex++], name);
             assertEquals(name, expected[propertyIndex++], variable.getDescription());
             assertEquals(name, expected[propertyIndex++], dataType);
-            assertEquals(name, expected[propertyIndex++], variable.getShape().length);
+            assertEquals(name, expected[propertyIndex++], variable.getGridDimensions().size());
             assertEquals(name, expected[propertyIndex++], variable.getRole());
             assertEquals(0, propertyIndex % NUM_BASIC_PROPERTY_COLUMNS);            // Sanity check for VariableTest itself.
         }
@@ -170,8 +171,21 @@ public strictfp class VariableTest extends TestCase {
     }
 
     /**
-     * Tests {@link Variable#getGridDimensionNames()} and {@link Variable#getShape()}
-     * on a simple two-dimensional dataset.
+     * Returns the dimension names.
+     */
+    private static String[] names(final List<Dimension> dimensions) {
+        return dimensions.stream().map(Dimension::getName).toArray(String[]::new);
+    }
+
+    /**
+     * Returns the dimension lengths.
+     */
+    private static long[] lengths(final List<Dimension> dimensions) {
+        return dimensions.stream().mapToLong(Dimension::length).toArray();
+    }
+
+    /**
+     * Tests {@link Variable#getGridDimensions()} on a simple two-dimensional dataset.
      *
      * @throws IOException if an I/O error occurred while opening the file.
      * @throws DataStoreException if a logical error occurred.
@@ -181,18 +195,18 @@ public strictfp class VariableTest extends TestCase {
         final Variable variable = selectDataset(TestData.NETCDF_2D_GEOGRAPHIC).getVariables()[0];
         assertEquals("SST", variable.getName());
 
+        final List<Dimension> dimensions = variable.getGridDimensions();
         assertArrayEquals("getGridDimensionNames()", new String[] {
             "lat", "lon"
-        }, variable.getGridDimensionNames());
+        }, names(dimensions));
 
-        assertArrayEquals("getGridEnvelope()", new int[] {
+        assertArrayEquals("getGridEnvelope()", new long[] {
             73, 73
-        }, variable.getShape());
+        }, lengths(dimensions));
     }
 
     /**
-     * Tests {@link Variable#getGridDimensionNames()} and {@link Variable#getShape()}
-     * on a compound four-dimensional dataset.
+     * Tests {@link Variable#getGridDimensions()} on a compound four-dimensional dataset.
      *
      * @throws IOException if an I/O error occurred while opening the file.
      * @throws DataStoreException if a logical error occurred.
@@ -202,13 +216,14 @@ public strictfp class VariableTest extends TestCase {
         final Variable variable = getVariablesCIP(selectDataset(TestData.NETCDF_4D_PROJECTED))[6];
         assertEquals("CIP", variable.getName());
 
+        final List<Dimension> dimensions = variable.getGridDimensions();
         assertArrayEquals("getGridDimensionNames()", new String[] {
             "time", "z0", "y0", "x0"
-        }, variable.getGridDimensionNames());
+        }, names(dimensions));
 
-        assertArrayEquals("getGridEnvelope()", new int[] {
+        assertArrayEquals("getGridEnvelope()", new long[] {
             1, 4, 19, 38
-        }, variable.getShape());
+        }, lengths(dimensions));
     }
 
     /**


Mime
View raw message