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: Provide a hook for netCDF files to be interpreted according some specialized convention. https://issues.apache.org/jira/browse/SIS-315
Date Wed, 16 Jan 2019 14:02:43 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 3130e0d  Provide a hook for netCDF files to be interpreted according some specialized
convention. https://issues.apache.org/jira/browse/SIS-315
3130e0d is described below

commit 3130e0da912f89c7377de910fdab60137a585f6d
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Jan 16 14:37:11 2019 +0100

    Provide a hook for netCDF files to be interpreted according some specialized convention.
    https://issues.apache.org/jira/browse/SIS-315
---
 .../org/apache/sis/internal/netcdf/Convention.java | 114 +++++++++++++++++++++
 .../org/apache/sis/internal/netcdf/Decoder.java    |  43 ++++++++
 .../org/apache/sis/internal/netcdf/Variable.java   |  34 +++---
 .../apache/sis/internal/netcdf/VariableRole.java   |  43 ++++++++
 .../sis/internal/netcdf/impl/ChannelDecoder.java   |  61 ++++++++---
 .../sis/internal/netcdf/impl/VariableInfo.java     |  14 +--
 .../sis/internal/netcdf/ucar/DecoderWrapper.java   |   2 +
 .../sis/internal/netcdf/ucar/VariableWrapper.java  |   7 +-
 .../apache/sis/storage/netcdf/GridResource.java    |  10 +-
 .../apache/sis/storage/netcdf/MetadataReader.java  |   3 +-
 .../apache/sis/internal/netcdf/VariableTest.java   |  24 ++---
 11 files changed, 297 insertions(+), 58 deletions(-)

diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
new file mode 100644
index 0000000..df8f27c
--- /dev/null
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
@@ -0,0 +1,114 @@
+/*
+ * 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;
+
+import java.util.Iterator;
+import org.apache.sis.internal.referencing.LazySet;
+
+
+/**
+ * Extends the CF-Conventions with some conventions particular to a data producer.
+ * By default, Apache SIS netCDF reader applies the <a href="http://cfconventions.org">CF
conventions</a>.
+ * But some data producers does not provides all necessary information for allowing Apache
SIS to read the
+ * netCDF file. Some information may be missing because considered implicit by the data producer.
+ * This class provides a mechanism for supplying the implicit values.
+ * Conventions can be registered in a file having this exact path:
+ *
+ * <blockquote><pre>META-INF/services/org.apache.sis.internal.netcdf.Convention</pre></blockquote>
+ *
+ * <p><b>This is an experimental class for internal usage only (for now).</b>
+ * The API of this class is likely to change in any future Apache SIS version.
+ * This class may become public (in a modified form) in the future if we gain
+ * enough experience about extending netCDF conventions.</p>
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-315">SIS-315</a>
+ *
+ * @since 1.0
+ * @module
+ */
+public abstract class Convention {
+    /**
+     * All conventions found on the classpath.
+     */
+    private static final LazySet<Convention> AVAILABLES = new LazySet<>(Convention.class);
+
+    /**
+     * For subclass constructors.
+     */
+    protected Convention() {
+    }
+
+    /**
+     * Finds the convention to apply to the file opened by the given decoder, or {@code null}
if none.
+     */
+    static synchronized Convention find(final Decoder decoder) {
+        final Iterator<Convention> it;
+        Convention c;
+        synchronized (AVAILABLES) {
+            it = AVAILABLES.iterator();
+            if (!it.hasNext()) return null;
+            c = it.next();
+        }
+        while (!c.isApplicableTo(decoder)) {
+            synchronized (AVAILABLES) {
+                if (!it.hasNext()) return null;
+                c = it.next();
+            }
+        }
+        return c;
+    }
+
+    /**
+     * Detects if this set of conventions applies to the given netCDF file.
+     *
+     * @param  decoder  the netCDF file to test.
+     * @return {@code true} if this set of conventions can apply.
+     */
+    protected abstract boolean isApplicableTo(Decoder decoder);
+
+    /**
+     * Returns the role of the given variable. In particular, this method shall return
+     * {@link VariableRole#AXIS} if the given variable seems to be a coordinate system axis.
+     *
+     * @param  variable  the variable for which to get the role.
+     * @return role of the given variable.
+     */
+    protected VariableRole roleOf(final Variable variable) {
+        return variable.getRole();
+    }
+
+    /**
+     * Returns the names of the variables containing data for all dimension of a variable.
+     * Each netCDF variable can have an arbitrary number of dimensions identified by their
name.
+     * The data for a dimension are usually stored in a variable of the same name, but not
always.
+     * This method gives an opportunity for subclasses to select the axis variables using
other criterion.
+     * This happen for example if a netCDF file defines two grids for the same dimensions.
+     *
+     * <p>The default implementation returns {@code null}.</p>
+     *
+     * @param  variable  the variable for which the list of axis variables are desired.
+     * @return names of the variables containing axis values, or {@code null} if this
+     *         method performs applies no special convention for the given variable.
+     */
+    protected String[] namesOfAxisVariables(Variable variable) {
+        return null;
+    }
+}
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
index 90756d7..425611b 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
@@ -59,6 +59,11 @@ public abstract class Decoder extends ReferencingFactoryContainer implements
Clo
     public Path location;
 
     /**
+     * Customized conventions to apply in addition of netCDF conventions, or {@code null}
if none.
+     */
+    private Convention convention;
+
+    /**
      * The data store identifier created from the global attributes, or {@code null} if none.
      * Defined as a namespace for use as the scope of children resources (the variables).
      * This is set by netCDF store constructor and shall not be modified afterward.
@@ -109,6 +114,14 @@ public abstract class Decoder extends ReferencingFactoryContainer implements
Clo
     }
 
     /**
+     * Shall be invoked by subclass constructors after the finished their construction, for
completing initialization.
+     * This method checks if an extension to CF-convention applies to the current file.
+     */
+    protected final void initialize() {
+        convention = Convention.find(this);
+    }
+
+    /**
      * Returns a filename for formatting error message and for information purpose.
      * The filename should not contain path, but may contain file extension.
      *
@@ -255,4 +268,34 @@ public abstract class Decoder extends ReferencingFactoryContainer implements
Clo
      * @throws DataStoreException if a logical error occurred.
      */
     public abstract Grid[] getGridGeometries() throws IOException, DataStoreException;
+
+    /**
+     * Returns the role of the given variable. In particular, this method shall return
+     * {@link VariableRole#AXIS} if the given variable seems to be a coordinate system axis.
+     *
+     * @param  variable  the variable for which to get the role, or {@code null}.
+     * @return role of the given variable, or {@code null} if the given variable was null.
+     */
+    public final VariableRole roleOf(final Variable variable) {
+        if (variable == null) {
+            return null;
+        }
+        if (convention != null) {
+            return convention.roleOf(variable);
+        }
+        return variable.getRole();
+    }
+
+    /**
+     * If there is some specialized convention for current file that mandate a different
set of
+     * axes for the given variable, returns the name of the variables for those axes. Otherwise
+     * (i.e. if the file can be parsed as a standard CF-compliant file), returns {@code null}.
+     *
+     * @param  variable  the variable for which the list of axis variables are desired.
+     * @return names of the variables containing axis values, or {@code null} if this
+     *         method performs applies no special convention for the given variable.
+     */
+    protected final String[] namesOfAxisVariables(final Variable variable) {
+        return (convention != null) ? convention.namesOfAxisVariables(variable) : null;
+    }
 }
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 62e857a..a363279 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
@@ -295,8 +295,13 @@ public abstract class Variable extends NamedElement {
     public abstract boolean isUnlimited();
 
     /**
-     * Returns {@code true} if the given variable can be used for generating an image.
-     * This method checks for the following conditions:
+     * Returns whether this variable is used as a coordinate system axis, a coverage or something
else.
+     * In particular this method shall return {@link VariableRole#AXIS} if this variable
seems to be a
+     * coordinate system axis instead than the actual data. By netCDF convention, coordinate
system axes
+     * have the name of one of the dimensions defined in the netCDF header.
+     *
+     * <p>The default implementation returns {@link VariableRole#COVERAGE} if the given
variable can be used
+     * for generating an image, by checking the following conditions:</p>
      *
      * <ul>
      *   <li>Images require at least {@value Grid#MIN_DIMENSION} dimensions of size
equals or greater than {@value Grid#MIN_SPAN}.
@@ -308,9 +313,16 @@ public abstract class Variable extends NamedElement {
      *       to confuse them with images.</li>
      * </ul>
      *
-     * @return {@code true} if the variable can be considered a coverage.
+     * Subclasses shall override this method for checking the {@link VariableRole#AXIS} case
before to delegate
+     * to this method.
+     *
+     * <p>This method has protected access because it should not be invoked directly
except in overridden methods.
+     * Code using variable role should invoke {@link Decoder#roleOf(Variable)} instead, for
allowing specialization
+     * by {@link Convention}.</p>
+     *
+     * @return the role of this variable.
      */
-    public final boolean isCoverage() {
+    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) {
@@ -320,21 +332,13 @@ public abstract class Variable extends NamedElement {
         if (numVectors >= Grid.MIN_DIMENSION) {
             final DataType dataType = getDataType();
             if (dataType.rasterDataType != DataBuffer.TYPE_UNDEFINED) {
-                return !isCoordinateSystemAxis();
+                return VariableRole.COVERAGE;
             }
         }
-        return false;
+        return VariableRole.OTHER;
     }
 
     /**
-     * Returns {@code true} if this variable seems to be a coordinate system axis instead
than the actual data.
-     * By netCDF convention, coordinate system axes have the name of one of the dimensions
defined in the netCDF header.
-     *
-     * @return {@code true} if this variable seems to be a coordinate system axis.
-     */
-    public abstract boolean isCoordinateSystemAxis();
-
-    /**
      * Returns the grid geometry for this variable, or {@code null} if this variable is not
a data cube.
      * Not all variables have a grid geometry. For example collections of features do not
have such grid.
      * The same grid geometry may be shared by many variables.
@@ -361,7 +365,7 @@ public abstract class Variable extends NamedElement {
      * Values shall be handled as unsigned 32 bits integers.
      *
      * <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 #isCoverage()}
method,
+     * 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>
      *
      * @return the number of grid cells for each dimension, as unsigned integer in netCDF
order (reverse of "natural" order).
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/VariableRole.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/VariableRole.java
new file mode 100644
index 0000000..380d30a
--- /dev/null
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/VariableRole.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+
+/**
+ * Specifies whether a variable is used as a coordinate system axis, a coverage or other
purpose.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+public enum VariableRole {
+    /**
+     * The variable is a coordinate system axis.
+     */
+    AXIS,
+
+    /**
+     * The variable is a grid coverage.
+     */
+    COVERAGE,
+
+    /**
+     * Unidentified kind of variable.
+     */
+    OTHER
+}
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 f468756..720b172 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
@@ -277,6 +277,7 @@ public final class ChannelDecoder extends Decoder {
         this.attributeMap = attributes;
         this.variables    = variables;
         this.variableMap  = toCaseInsensitiveNameMap(variables);
+        initialize();
     }
 
     /**
@@ -843,6 +844,33 @@ public final class ChannelDecoder extends Decoder {
     }
 
     /**
+     * Adds to the given set all variables of the given names. This operation is performed
when the set of axes is
+     * specified by a {@code "coordinates"} attribute associated to a data variable, or by
customized conventions
+     * specified by {@link org.apache.sis.internal.netcdf.Convention#namesOfAxisVariables(Variable)}.
+     *
+     * @param  names       names of variables containing axis data, or {@code null} if none.
+     * @param  axes        where to add named variables.
+     * @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) {
+        if (names == null) {
+            return false;
+        }
+        for (int i=names.length; --i >= 0;) {
+            final VariableInfo axis = findVariable(names[i].toString());
+            if (axis == null) {
+                dimensions.clear();
+                axes.clear();
+                break;
+            }
+            axes.add(axis);
+            dimensions.addAll(Arrays.asList(axis.dimensions));
+        }
+        return true;
+    }
+
+    /**
      * Returns all grid geometries found in the netCDF file.
      * This method returns a direct reference to an internal array - do not modify.
      *
@@ -860,9 +888,18 @@ public final class ChannelDecoder extends Decoder {
              */
             final Map<Dimension, List<VariableInfo>> dimToAxes = new IdentityHashMap<>();
             for (final VariableInfo variable : variables) {
-                if (variable.isCoordinateSystemAxis()) {
-                    for (final Dimension dimension : variable.dimensions) {
-                        CollectionsExt.addToMultiValuesMap(dimToAxes, dimension, variable);
+                switch (roleOf(variable)) {
+                    case COVERAGE: {
+                        // If Convention.roleOf(…) overwrote the value computed by VariableInfo,
+                        // remember the new value for avoiding to ask again in next loops.
+                        variable.isCoordinateSystemAxis = false;
+                        break;
+                    }
+                    case AXIS: {
+                        variable.isCoordinateSystemAxis = true;
+                        for (final Dimension dimension : variable.dimensions) {
+                            CollectionsExt.addToMultiValuesMap(dimToAxes, dimension, variable);
+                        }
                     }
                 }
             }
@@ -874,7 +911,7 @@ public final class ChannelDecoder extends Decoder {
             final Set<Dimension> usedDimensions = new HashSet<>(8);
             final Map<GridInfo,GridInfo> shared = new LinkedHashMap<>();
 nextVar:    for (final VariableInfo variable : variables) {
-                if (variable.isCoordinateSystemAxis() || variable.dimensions.length == 0)
{
+                if (variable.isCoordinateSystemAxis || variable.dimensions.length == 0) {
                     continue;
                 }
                 /*
@@ -886,21 +923,11 @@ nextVar:    for (final VariableInfo variable : variables) {
                  */
                 axes.clear();
                 usedDimensions.clear();
-                final CharSequence[] coordinates = variable.getCoordinateVariables();
-                if (coordinates.length != 0) {
-                    for (int i=coordinates.length; --i >= 0;) {
-                        final VariableInfo axis = findVariable(coordinates[i].toString());
-                        if (axis == null) {
-                            usedDimensions.clear();
-                            axes.clear();
-                            break;
-                        }
-                        axes.add(axis);
-                        usedDimensions.addAll(Arrays.asList(axis.dimensions));
-                    }
+                if (!listAxes(variable.getCoordinateVariables(), axes, usedDimensions)) {
+                    listAxes(namesOfAxisVariables(variable), axes, usedDimensions);
                 }
                 /*
-                 * In theory the "coordinates" attribute would enumerate all axis needed
for covering all dimensions,
+                 * In theory the "coordinates" attribute would enumerate all axes needed
for covering all dimensions,
                  * and we would not need to check for variables having dimension names. However
in practice there is
                  * incomplete attributes, so we check for other dimensions even if the above
loop did some work.
                  */
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 9250446..fbffe03 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
@@ -34,6 +34,7 @@ import org.apache.sis.internal.netcdf.Decoder;
 import org.apache.sis.internal.netcdf.DataType;
 import org.apache.sis.internal.netcdf.Grid;
 import org.apache.sis.internal.netcdf.Variable;
+import org.apache.sis.internal.netcdf.VariableRole;
 import org.apache.sis.internal.netcdf.Resources;
 import org.apache.sis.internal.storage.io.ChannelDataInput;
 import org.apache.sis.internal.storage.io.HyperRectangleReader;
@@ -154,10 +155,8 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
      * {@code true} if this variable seems to be a coordinate system axis, as determined
by comparing its name
      * with the name of all dimensions in the netCDF file. This information is computed at
construction time
      * because requested more than once.
-     *
-     * @see #isCoordinateSystemAxis()
      */
-    private boolean isCoordinateSystemAxis;
+    boolean isCoordinateSystemAxis;
 
     /**
      * The values of the whole variable, or {@code null} if not yet read. This vector should
be assigned only
@@ -437,13 +436,13 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
     }
 
     /**
-     * Returns {@code true} if this variable seems to be a coordinate system axis,
+     * Returns {@code AXIS} if this variable seems to be a coordinate system axis,
      * determined by comparing its name with the name of all dimensions in the netCDF file.
      * Also determined by inspection of {@code "coordinates"} attribute on other variables.
      */
     @Override
-    public boolean isCoordinateSystemAxis() {
-        return isCoordinateSystemAxis;
+    protected VariableRole getRole() {
+        return isCoordinateSystemAxis ? VariableRole.AXIS : super.getRole();
     }
 
     /**
@@ -557,6 +556,9 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
      * This method does not search the lower-case variant of the given name because the argument
given to this method
      * is usually a hard-coded value from {@link CF} or {@link CDM} conventions, which are
already in lower-cases.
      *
+     * <p>All {@code getAttributeValue(…)} methods in this class ultimately invokes
this method.
+     * This provide a single point to override if the functionality needs to be extended.</p>
+     *
      * @param  attributeName  name of attribute to search, in the expected case.
      * @return variable attribute value of the given name, or {@code null} if none.
      */
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
index edda4ff..acc53b7 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
@@ -108,6 +108,7 @@ public final class DecoderWrapper extends Decoder implements CancelTask
{
     public DecoderWrapper(final NetcdfFile file, final GeometryLibrary geomlib, final WarningListeners<DataStore>
listeners) {
         super(geomlib, listeners);
         this.file = file;
+        initialize();
     }
 
     /**
@@ -124,6 +125,7 @@ public final class DecoderWrapper extends Decoder implements CancelTask
{
     {
         super(geomlib, listeners);
         file = NetcdfDataset.openDataset(filename, false, this);
+        initialize();
     }
 
     /**
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 abefde8..94da9c7 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
@@ -41,6 +41,7 @@ import org.apache.sis.internal.netcdf.DataType;
 import org.apache.sis.internal.netcdf.Decoder;
 import org.apache.sis.internal.netcdf.Grid;
 import org.apache.sis.internal.netcdf.Variable;
+import org.apache.sis.internal.netcdf.VariableRole;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.util.logging.WarningListeners;
 import org.apache.sis.storage.DataStoreException;
@@ -206,11 +207,11 @@ final class VariableWrapper extends Variable {
     }
 
     /**
-     * Returns {@code true} if this variable seems to be a coordinate system axis.
+     * Returns {@code AXIS} if this variable seems to be a coordinate system axis.
      */
     @Override
-    public boolean isCoordinateSystemAxis() {
-        return variable.isCoordinateVariable();
+    protected VariableRole getRole() {
+        return variable.isCoordinateVariable() ? VariableRole.AXIS : super.getRole();
     }
 
     /**
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 8ea8595..a7c7ccd 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
@@ -34,6 +34,7 @@ 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.netcdf.VariableRole;
 import org.apache.sis.internal.storage.AbstractGridResource;
 import org.apache.sis.internal.storage.ResourceOnFileSystem;
 import org.apache.sis.coverage.SampleDimension;
@@ -155,8 +156,11 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS
         final List<Resource> resources = new ArrayList<>();
         for (int i=0; i<variables.length; i++) {
             final Variable variable = variables[i];
-            final Grid grid;
-            if (variable == null || !variable.isCoverage() || (grid = variable.getGridGeometry(decoder))
== null) {
+            if (decoder.roleOf(variable) != VariableRole.COVERAGE) {
+                continue;                                                   // Skip variables
that are not grid coverages.
+            }
+            final Grid grid = variable.getGridGeometry(decoder);
+            if (grid == null) {
                 continue;                                                   // Skip variables
that are not grid coverages.
             }
             siblings.add(variable);
@@ -180,7 +184,7 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS
                     int suffixLength = name.length() - suffixStart;
                     for (int j=i; ++j < variables.length;) {
                         final Variable candidate = variables[j];
-                        if (candidate == null || !candidate.isCoverage()) {
+                        if (decoder.roleOf(candidate) != VariableRole.COVERAGE) {
                             variables[j] = null;                                // For avoiding
to revisit that variable again.
                             continue;
                         }
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 d8735a8..db25359 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
@@ -60,6 +60,7 @@ import org.apache.sis.metadata.sql.MetadataStoreException;
 import org.apache.sis.internal.netcdf.Axis;
 import org.apache.sis.internal.netcdf.Decoder;
 import org.apache.sis.internal.netcdf.Variable;
+import org.apache.sis.internal.netcdf.VariableRole;
 import org.apache.sis.internal.netcdf.Grid;
 import org.apache.sis.internal.storage.io.IOUtilities;
 import org.apache.sis.internal.storage.MetadataBuilder;
@@ -893,7 +894,7 @@ split:  while ((start = CharSequences.skipLeadingWhitespaces(value, start,
lengt
     private void addContentInfo() {
         final Map<List<String>, List<Variable>> contents = new HashMap<>(4);
         for (final Variable variable : decoder.getVariables()) {
-            if (variable.isCoverage()) {
+            if (decoder.roleOf(variable) == VariableRole.COVERAGE) {
                 final List<String> dimensions = Arrays.asList(variable.getGridDimensionNames());
                 CollectionsExt.addToMultiValuesMap(contents, dimensions, variable);
             }
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 a8bfeb7..019750c 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
@@ -47,7 +47,7 @@ public strictfp class VariableTest extends TestCase {
      * Expected number of columns per variables for the {@code expected} argument
      * given to the {@link #assertBasicPropertiesEqual(Object[], Variable[])} method.
      */
-    private static final int NUM_BASIC_PROPERTY_COLUMNS = 6;
+    private static final int NUM_BASIC_PROPERTY_COLUMNS = 5;
 
     /**
      * Whether the {@code "runtime"} variable in {@link TestData#NETCDF_4D_PROJECTED} is
considered an axis or not.
@@ -92,8 +92,7 @@ public strictfp class VariableTest extends TestCase {
      *   <li>{@link Variable#getDescription()}</li>
      *   <li>{@link Variable#getDataType()}</li>
      *   <li>{@link Variable#getShape()} length</li>
-     *   <li>{@link Variable#isCoordinateSystemAxis()}</li>
-     *   <li>{@link Variable#isCoverage()}</li>
+     *   <li>{@link Variable#getRole()}</li>
      * </ul>
      *
      * @throws IOException if an I/O error occurred while opening the file.
@@ -102,14 +101,14 @@ public strictfp class VariableTest extends TestCase {
     @Test
     public void testBasicProperties() throws IOException, DataStoreException {
         assertBasicPropertiesEqual(new Object[] {
-        // __name______________description_____________________datatype_______dim__axis?__raster?
-            "grid_mapping_0", null,                            DataType.INT,    0, false,
false,
-            "x0",             "projection_x_coordinate",       DataType.FLOAT,  1, true,
 false,
-            "y0",             "projection_y_coordinate",       DataType.FLOAT,  1, true,
 false,
-            "z0",             "Flight levels in 100s of feet", DataType.FLOAT,  1, true,
 false,
-            "time",           "Data time",                     DataType.DOUBLE, 1, true,
 false,
-            "runtime",        "Data generation time",          DataType.DOUBLE, 1, isRuntimeAnAxis,
false,
-            "CIP",            "Current Icing Product",         DataType.FLOAT,  4, false,
true
+        // __name______________description_____________________datatype_______dim__role
+            "grid_mapping_0", null,                            DataType.INT,    0, VariableRole.OTHER,
+            "x0",             "projection_x_coordinate",       DataType.FLOAT,  1, VariableRole.AXIS,
+            "y0",             "projection_y_coordinate",       DataType.FLOAT,  1, VariableRole.AXIS,
+            "z0",             "Flight levels in 100s of feet", DataType.FLOAT,  1, VariableRole.AXIS,
+            "time",           "Data time",                     DataType.DOUBLE, 1, VariableRole.AXIS,
+            "runtime",        "Data generation time",          DataType.DOUBLE, 1, isRuntimeAnAxis
? VariableRole.AXIS : VariableRole.OTHER,
+            "CIP",            "Current Icing Product",         DataType.FLOAT,  4, VariableRole.COVERAGE
         }, getVariablesCIP(selectDataset(TestData.NETCDF_4D_PROJECTED)));
     }
 
@@ -129,8 +128,7 @@ public strictfp class VariableTest extends TestCase {
             assertEquals(name, expected[propertyIndex++], variable.getDescription());
             assertEquals(name, expected[propertyIndex++], dataType);
             assertEquals(name, expected[propertyIndex++], variable.getShape().length);
-            assertEquals(name, expected[propertyIndex++], variable.isCoordinateSystemAxis());
-            assertEquals(name, expected[propertyIndex++], variable.isCoverage());
+            assertEquals(name, expected[propertyIndex++], variable.getRole());
             assertEquals(0, propertyIndex % NUM_BASIC_PROPERTY_COLUMNS);            // Sanity
check for VariableTest itself.
         }
         assertEquals("Expected more variables.",


Mime
View raw message