sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: More progress toward decoding the CoordinateSystem from a netCDF file. A side effect of this work is the introduction of ReferencingFactoryContainer, to be needed by various data store (not just netCDF).
Date Mon, 12 Nov 2018 18:46:25 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 77c040a6fb43c6b1bb586305804ae4156444bf2f
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Mon Nov 12 19:44:30 2018 +0100

    More progress toward decoding the CoordinateSystem from a netCDF file.
    A side effect of this work is the introduction of ReferencingFactoryContainer, to be needed by various data store (not just netCDF).
---
 .../sis/internal/metadata/AxisDirections.java      |  35 ++++--
 .../internal/referencing/CoordinateOperations.java |   5 +-
 .../referencing/ReferencingFactoryContainer.java   | 137 +++++++++++++++++++++
 .../sis/internal/metadata/AxisDirectionsTest.java  |  26 +++-
 .../apache/sis/referencing/cs/HardCodedAxes.java   |   2 +-
 .../org/apache/sis/storage/geotiff/CRSBuilder.java |  77 ++----------
 .../java/org/apache/sis/internal/netcdf/Axis.java  | 104 +++++++++++-----
 .../apache/sis/internal/netcdf/GridGeometry.java   |  19 ++-
 .../apache/sis/internal/netcdf/NamedElement.java   |  51 +++-----
 .../org/apache/sis/internal/netcdf/Resources.java  |   6 +
 .../sis/internal/netcdf/Resources.properties       |   1 +
 .../sis/internal/netcdf/Resources_fr.properties    |   1 +
 .../org/apache/sis/internal/netcdf/Variable.java   |  38 +++++-
 .../sis/internal/netcdf/impl/ChannelDecoder.java   |  33 ++++-
 .../sis/internal/netcdf/impl/GridGeometryInfo.java |   8 ++
 .../sis/internal/netcdf/impl/VariableInfo.java     |  16 +--
 .../internal/netcdf/ucar/GridGeometryWrapper.java  |   8 ++
 .../sis/internal/netcdf/ucar/VariableWrapper.java  |  15 +++
 18 files changed, 418 insertions(+), 164 deletions(-)

diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java
index fdff560..9ec0a20 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java
@@ -18,6 +18,7 @@ package org.apache.sis.internal.metadata;
 
 import java.util.Map;
 import java.util.HashMap;
+import java.util.Objects;
 import javax.measure.Unit;
 import javax.measure.quantity.Angle;
 import org.opengis.annotation.UML;
@@ -39,7 +40,7 @@ import static org.apache.sis.util.CharSequences.*;
  * Utilities methods related to {@link AxisDirection}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.4
  * @module
  */
@@ -485,9 +486,7 @@ public final class AxisDirections extends Static {
                 return -1;
             }
             while (--i > 0) {               // Intentionally exclude 0.
-                if (!absolute(subCS.getAxis(i).getDirection()).equals(
-                     absolute(cs.getAxis(i + dim).getDirection())))
-                {
+                if (!isColinear(subCS.getAxis(i).getDirection(), cs.getAxis(i + dim).getDirection())) {
                     return -1;
                 }
             }
@@ -496,6 +495,19 @@ public final class AxisDirections extends Static {
     }
 
     /**
+     * Returns whether the second axis is colinear with the first axis. This method returns {@code true}
+     * if the {@linkplain #absolute absolute} direction of the given directions are equal.
+     * For example "down" is considered colinear with "up".
+     *
+     * @param  d1  the first axis direction to compare.
+     * @param  d2  the second axis direction to compare.
+     * @return {@code true} if both directions are colinear.
+     */
+    public static boolean isColinear(final AxisDirection d1, final AxisDirection d2) {
+        return Objects.equals(absolute(d1), absolute(d2));
+    }
+
+    /**
      * Searches for an axis direction having the given name in the specified list of directions.
      * This method compares the given name with the name of each {@code AxisDirection} in a lenient way:
      *
@@ -653,12 +665,12 @@ public final class AxisDirections extends Static {
              * use "h" as the fallback for unknown vertical axis.
              */
             if (UP.equals(direction)) {
-                if (contains(name, "Gravity",    false)) return "H";
-                if (contains(name, "Elevation",  false)) return "φ";
-                if (contains(name, "Geocentric", false)) return "r";
-                return "h";
+                if (Units.isAngular(unit))               return "α";                // Elevation angle
+                if (contains(name, "Gravity",    false)) return "H";                // Gravity-related height
+                if (contains(name, "Geocentric", false)) return "r";                // Geocentric radius
+                return "h";                                                         // Ellipsoidal height
             } else if (DOWN.equals(direction)) {
-                return "D";                         // "Depth"
+                return "D";                                                         // Depth
             } else if (isGeocentric(direction)) {
                 // For GEOCENTRIC_X, GEOCENTRIC_Y or GEOCENTRIC_Z, just take the last letter.
                 final String dir = direction.name();
@@ -676,7 +688,8 @@ public final class AxisDirections extends Static {
     /**
      * Returns an axis direction for the given abbreviation. This method is (partially) the converse
      * of {@link #suggestAbbreviation(String, AxisDirection, Unit)}. Current implementation does not
-     * recognize all abbreviation generated by above method, but only the main ones.
+     * recognize all abbreviation generated by above method, but only the main ones.  This method is
+     * defined here for making easier to maintain consistency with {@code suggestAbbreviation(…)}.
      *
      * @param  abbreviation  the abbreviation.
      * @return axis direction for the given abbreviation, or {@code null} if unrecognized.
@@ -687,6 +700,7 @@ public final class AxisDirections extends Static {
         }
         final AxisDirection dir;
         switch (abbreviation) {
+            default:                      dir = null;                 break;
             case 'W':                     dir = AxisDirection.WEST;   break;
             case 'S':                     dir = AxisDirection.SOUTH;  break;
             case 'θ': case 'λ': case 'E': dir = AxisDirection.EAST;   break;
@@ -694,7 +708,6 @@ public final class AxisDirections extends Static {
             case 'R': case 'H':           dir = AxisDirection.UP;     break;
             case 'D':                     dir = AxisDirection.DOWN;   break;
             case 'T':                     dir = AxisDirection.FUTURE; break;
-            default:                      dir = null;                 break;
         }
         return dir;
     }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java
index 5bed02e..7d82422 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java
@@ -17,7 +17,6 @@
 package org.apache.sis.internal.referencing;
 
 import java.util.Set;
-import java.util.Objects;
 import java.util.Collections;
 import javax.measure.UnitConverter;
 import javax.measure.IncommensurableException;
@@ -229,9 +228,7 @@ compare:    for (int i=0; i<dim; i++) {
                     do {
                         final long mask = Long.lowestOneBit(candidates);
                         final CoordinateSystemAxis src = source.getAxis(Long.numberOfTrailingZeros(mask));
-                        if (Objects.equals(AxisDirections.absolute(src .getDirection()),
-                                           AxisDirections.absolute(axis.getDirection())))
-                        {
+                        if (AxisDirections.isColinear(src.getDirection(), axis.getDirection())) {
                             try {
                                 final UnitConverter c  = src.getUnit().getConverterToAny(axis.getUnit());
                                 final double minimum   = axis.getMinimumValue();
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingFactoryContainer.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingFactoryContainer.java
new file mode 100644
index 0000000..470af80
--- /dev/null
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingFactoryContainer.java
@@ -0,0 +1,137 @@
+/*
+ * 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.referencing;
+
+import org.opengis.referencing.cs.CSFactory;
+import org.opengis.referencing.crs.CRSFactory;
+import org.opengis.referencing.datum.DatumFactory;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.CoordinateOperationFactory;
+import org.apache.sis.internal.system.DefaultFactories;
+
+
+/**
+ * A container of factories frequently used together.
+ * This class may be temporary until we choose a dependency injection framework
+ * See <a href="https://issues.apache.org/jira/browse/SIS-102">SIS-102</a>.
+ *
+ * <p>This class is not thread safe. Synchronization, if needed, is caller's responsibility.</p>
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+public class ReferencingFactoryContainer {
+    /**
+     * The {@linkplain org.opengis.referencing.datum.Datum datum} factory.
+     * If null, then a default factory will be created only when first needed.
+     */
+    private DatumFactory datumFactory;
+
+    /**
+     * The {@linkplain org.opengis.referencing.cs.CoordinateSystem coordinate system} factory.
+     * If null, then a default factory will be created only when first needed.
+     */
+    private CSFactory csFactory;
+
+    /**
+     * The {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system} factory.
+     * If null, then a default factory will be created only when first needed.
+     */
+    private CRSFactory crsFactory;
+
+    /**
+     * Factory for fetching operation methods and creating defining conversions.
+     * This is needed only for user-defined projected coordinate reference system.
+     */
+    private CoordinateOperationFactory operationFactory;
+
+    /**
+     * The {@linkplain org.opengis.referencing.operation.MathTransform math transform} factory.
+     * If null, then a default factory will be created only when first needed.
+     */
+    private MathTransformFactory mtFactory;
+
+    /**
+     * Creates a new instance for the default factories.
+     */
+    public ReferencingFactoryContainer() {
+    }
+
+    /**
+     * Returns the factory for creating datum, prime meridians and ellipsoids.
+     *
+     * @return the Datum factory (never {@code null}).
+     */
+    public final DatumFactory datumFactory() {
+        if (datumFactory == null) {
+            datumFactory = DefaultFactories.forBuildin(DatumFactory.class);
+        }
+        return datumFactory;
+    }
+
+    /**
+     * Returns the factory for creating coordinate systems and their axes.
+     *
+     * @return the Coordinate System factory (never {@code null}).
+     */
+    public final CSFactory csFactory() {
+        if (csFactory == null) {
+            csFactory = DefaultFactories.forBuildin(CSFactory.class);
+        }
+        return csFactory;
+    }
+
+    /**
+     * Returns the factory for creating coordinate reference systems.
+     *
+     * @return the Coordinate Reference System factory (never {@code null}).
+     */
+    public final CRSFactory crsFactory() {
+        if (crsFactory == null) {
+            crsFactory = DefaultFactories.forBuildin(CRSFactory.class);
+        }
+        return crsFactory;
+    }
+
+    /**
+     * Returns the factory for fetching operation methods and creating defining conversions.
+     * This is needed only for user-defined projected coordinate reference system.
+     * The factory is fetched when first needed.
+     *
+     * @return the Coordinate Operation factory (never {@code null}).
+     */
+    public final CoordinateOperationFactory operationFactory() {
+        if (operationFactory == null) {
+            operationFactory = CoordinateOperations.factory();
+        }
+        return operationFactory;
+    }
+
+    /**
+     * Returns the factory for creating parameterized transforms.
+     *
+     * @return the Math Transform factory (never {@code null}).
+     */
+    public final MathTransformFactory mtFactory() {
+        if (mtFactory == null) {
+            mtFactory = DefaultFactories.forBuildin(MathTransformFactory.class);
+        }
+        return mtFactory;
+    }
+}
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java b/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java
index 3ceaad2..74c44d8 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java
@@ -424,7 +424,7 @@ public final strictfp class AxisDirectionsTest extends TestCase {
         assertEquals("Ω",   AxisDirections.suggestAbbreviation("Spherical latitude",     NORTH,            Units.DEGREE));
         assertEquals("h",   AxisDirections.suggestAbbreviation("Ellipsoidal height",     UP,               Units.METRE));
         assertEquals("H",   AxisDirections.suggestAbbreviation("Gravity-related height", UP,               Units.METRE));
-        assertEquals("φ",   AxisDirections.suggestAbbreviation("Elevation",              UP,               Units.METRE));
+        assertEquals("α",   AxisDirections.suggestAbbreviation("Elevation",              UP,               Units.DEGREE));
         assertEquals("r",   AxisDirections.suggestAbbreviation("Geocentric radius",      UP,               Units.METRE));
         assertEquals("r",   AxisDirections.suggestAbbreviation("Distance",               AWAY_FROM,        Units.METRE));
         assertEquals("θ",   AxisDirections.suggestAbbreviation("Bearing",                CLOCKWISE,        Units.DEGREE));
@@ -451,6 +451,30 @@ public final strictfp class AxisDirectionsTest extends TestCase {
     }
 
     /**
+     * Tests {@link AxisDirections#fromAbbreviation(char)}.
+     * This tests reuse some of the case tested by {@link #testSuggestAbbreviation()}.
+     * The intent is to ensure that those two methods are consistent with each other.
+     *
+     * @since 1.0
+     */
+    @Test
+    public void testFromAbbreviation() {
+        assertEquals(EAST,   AxisDirections.fromAbbreviation('λ'));
+        assertEquals(NORTH,  AxisDirections.fromAbbreviation('φ'));
+        assertEquals(EAST,   AxisDirections.fromAbbreviation('θ'));
+        assertEquals(NORTH,  AxisDirections.fromAbbreviation('Ω'));
+        assertEquals(UP,     AxisDirections.fromAbbreviation('h'));
+        assertEquals(UP,     AxisDirections.fromAbbreviation('H'));
+        assertEquals(UP,     AxisDirections.fromAbbreviation('r'));
+        assertEquals(DOWN,   AxisDirections.fromAbbreviation('D'));
+        assertEquals(FUTURE, AxisDirections.fromAbbreviation('t'));
+        assertEquals(NORTH,  AxisDirections.fromAbbreviation('N'));
+        assertEquals(SOUTH,  AxisDirections.fromAbbreviation('S'));
+        assertEquals(EAST,   AxisDirections.fromAbbreviation('E'));
+        assertEquals(WEST,   AxisDirections.fromAbbreviation('W'));
+    }
+
+    /**
      * Verifies that the abbreviations used in {@link HardCodedAxes} constants are consistent with the abbreviations
      * suggested by {@link AxisDirections#suggestAbbreviation(String, AxisDirection, Unit)}.  Note that a failure in
      * this verification does not necessarily means that {@code suggestAbbreviation(…)} has a bug.  It could also be
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/HardCodedAxes.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/HardCodedAxes.java
index 562d740..a577586 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/HardCodedAxes.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/HardCodedAxes.java
@@ -415,7 +415,7 @@ public final strictfp class HardCodedAxes {
      * This is part of an engineering spherical coordinate system
      * (not to be confused with geodetic spherical coordinate system).
      */
-    public static final DefaultCoordinateSystemAxis ELEVATION = create("Elevation", "φ",
+    public static final DefaultCoordinateSystemAxis ELEVATION = create("Elevation", "α",
             AxisDirection.UP, Units.DEGREE, -90, +90, RangeMeaning.WRAPAROUND);
 
     /**
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
index b92011a..a54a54d 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
@@ -38,7 +38,6 @@ import org.opengis.metadata.spatial.CellGeometry;
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterNotFoundException;
 import org.opengis.referencing.IdentifiedObject;
-import org.opengis.referencing.crs.CRSFactory;
 import org.opengis.referencing.crs.GeocentricCRS;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -53,17 +52,15 @@ import org.opengis.referencing.datum.PrimeMeridian;
 import org.opengis.referencing.datum.VerticalDatum;
 import org.opengis.referencing.operation.Conversion;
 import org.opengis.referencing.operation.CoordinateOperation;
-import org.opengis.referencing.operation.CoordinateOperationFactory;
 import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
 import org.opengis.util.FactoryException;
 
 import org.apache.sis.internal.geotiff.Resources;
 import org.apache.sis.internal.metadata.WKTKeywords;
-import org.apache.sis.internal.referencing.CoordinateOperations;
 import org.apache.sis.internal.referencing.NilReferencingObject;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
-import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.referencing.ReferencingFactoryContainer;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.util.Utilities;
 import org.apache.sis.internal.util.Numerics;
@@ -77,7 +74,6 @@ import org.apache.sis.referencing.cs.AxesConvention;
 import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.referencing.crs.DefaultGeographicCRS;
 import org.apache.sis.referencing.factory.GeodeticAuthorityFactory;
-import org.apache.sis.referencing.factory.GeodeticObjectFactory;
 import org.apache.sis.io.TableAppender;
 import org.apache.sis.util.iso.DefaultNameSpace;
 import org.apache.sis.util.resources.Errors;
@@ -137,7 +133,7 @@ import static org.apache.sis.util.Utilities.equalsIgnoreMetadata;
  * @since 0.8
  * @module
  */
-final class CRSBuilder {
+final class CRSBuilder extends ReferencingFactoryContainer {
     /**
      * Number of {@code short} values in each GeoKey entry.
      */
@@ -217,27 +213,6 @@ final class CRSBuilder {
     private GeodeticAuthorityFactory epsgFactory;
 
     /**
-     * Factory for creating geodetic objects from their components, or {@code null} if not yet fetched.
-     * Constructing a CRS from its components requires parsing many {@link GeoKeys}.
-     *
-     * <div class="note"><b>Note:</b> we do not yet split this field into 3 separated fields for datums,
-     * coordinate systems and coordinate reference systems objects because it is not needed with Apache SIS
-     * implementation of those factories. However we may revisit this choice if we want to let the user specify
-     * his own factories.</div>
-     *
-     * @see #objectFactory()
-     */
-    private GeodeticObjectFactory objectFactory;
-
-    /**
-     * Factory for fetching operation methods and creating defining conversions.
-     * This is needed only for user-defined projected coordinate reference system.
-     *
-     * @see #operationFactory()
-     */
-    private CoordinateOperationFactory operationFactory;
-
-    /**
      * Name of the last object created. This is used by {@link #properties(Object)} for reusing existing instance
      * if possible. This is useful in GeoTIFF files since the same name is used for different geodetic components,
      * for example the datum and the ellipsoid.
@@ -302,37 +277,9 @@ final class CRSBuilder {
     }
 
     /**
-     * Returns the factory for creating geodetic objects from their components.
-     * The factory is fetched when first needed.
-     *
-     * @return the object factory (never {@code null}).
-     * @see <a href="https://issues.apache.org/jira/browse/SIS-102">SIS-102</a>
-     */
-    private GeodeticObjectFactory objectFactory() {
-        if (objectFactory == null) {
-            objectFactory = DefaultFactories.forBuildin(CRSFactory.class, GeodeticObjectFactory.class);
-        }
-        return objectFactory;
-    }
-
-    /**
-     * Returns the factory for fetching operation methods and creating defining conversions.
-     * The factory is fetched when first needed.
-     *
-     * @return the operation factory (never {@code null}).
-     * @see <a href="https://issues.apache.org/jira/browse/SIS-102">SIS-102</a>
-     */
-    private CoordinateOperationFactory operationFactory() {
-        if (operationFactory == null) {
-            operationFactory = CoordinateOperations.factory();
-        }
-        return operationFactory;
-    }
-
-    /**
      * Returns a map with the given name associated to {@value org.opengis.referencing.IdentifiedObject#NAME_KEY}.
      * The given name shall be either an instance of {@link String} or {@link Identifier}.
-     * This is an helper method for creating geodetic objects with {@link #objectFactory}.
+     * This is an helper method for creating geodetic objects with {@link #crsFactory()}.
      */
     private Map<String,?> properties(Object name) {
         if (name == null) {
@@ -753,7 +700,7 @@ final class CRSBuilder {
                 if (crs == null) {
                     missingValue(GeoKeys.GeographicType);
                 } else {
-                    crs = objectFactory().createCompoundCRS(Collections.singletonMap(IdentifiedObject.NAME_KEY, crs.getName()), crs, vertical);
+                    crs = crsFactory().createCompoundCRS(Collections.singletonMap(IdentifiedObject.NAME_KEY, crs.getName()), crs, vertical);
                 }
             }
         }
@@ -920,7 +867,7 @@ final class CRSBuilder {
                      * This is because the citation value is for the CRS (e.g. "WGS84") while the prime
                      * meridian names are very different (e.g. "Paris", "Madrid", etc).
                      */
-                    return objectFactory().createPrimeMeridian(properties(names[PRIMEM]), longitude, unit);
+                    return datumFactory().createPrimeMeridian(properties(names[PRIMEM]), longitude, unit);
                 }
                 break;                      // Default to Greenwich.
             }
@@ -990,14 +937,14 @@ final class CRSBuilder {
                 double inverseFlattening = getAsDouble(GeoKeys.InvFlattening);
                 final Ellipsoid ellipsoid;
                 if (!Double.isNaN(inverseFlattening)) {
-                    ellipsoid = objectFactory().createFlattenedSphere(properties, semiMajor, inverseFlattening, unit);
+                    ellipsoid = datumFactory().createFlattenedSphere(properties, semiMajor, inverseFlattening, unit);
                 } else {
                     /*
                      * If the inverse flattening factory was not defined, fallback on semi-major axis length.
                      * This is a less common way to define ellipsoid (the most common way uses flattening).
                      */
                     final double semiMinor = getMandatoryDouble(GeoKeys.SemiMinorAxis);
-                    ellipsoid = objectFactory().createEllipsoid(properties, semiMajor, semiMinor, unit);
+                    ellipsoid = datumFactory().createEllipsoid(properties, semiMajor, semiMinor, unit);
                 }
                 lastName = ellipsoid.getName();
                 return ellipsoid;
@@ -1076,7 +1023,7 @@ final class CRSBuilder {
                 String              name      = getOrDefault(names, DATUM);
                 final Ellipsoid     ellipsoid = createEllipsoid(names, linearUnit);
                 final PrimeMeridian meridian  = createPrimeMeridian(names, angularUnit);
-                final GeodeticDatum datum     = objectFactory().createGeodeticDatum(properties(name), ellipsoid, meridian);
+                final GeodeticDatum datum     = datumFactory().createGeodeticDatum(properties(name), ellipsoid, meridian);
                 name = Utilities.toUpperCase(name, Characters.Filter.LETTERS_AND_DIGITS);
                 lastName = datum.getName();
                 try {
@@ -1249,7 +1196,7 @@ final class CRSBuilder {
                 if (!Units.DEGREE.equals(angularUnit)) {
                     cs = replaceAngularUnit(cs, angularUnit);
                 }
-                final GeographicCRS crs = objectFactory().createGeographicCRS(properties(getOrDefault(names, GCRS)), datum, cs);
+                final GeographicCRS crs = crsFactory().createGeographicCRS(properties(getOrDefault(names, GCRS)), datum, cs);
                 lastName = crs.getName();
                 return crs;
             }
@@ -1319,7 +1266,7 @@ final class CRSBuilder {
                 if (!Units.METRE.equals(linearUnit)) {
                     cs = replaceLinearUnit(cs, linearUnit);
                 }
-                final GeocentricCRS crs = objectFactory().createGeocentricCRS(properties(getOrDefault(names, GCRS)), datum, cs);
+                final GeocentricCRS crs = crsFactory().createGeocentricCRS(properties(getOrDefault(names, GCRS)), datum, cs);
                 lastName = crs.getName();
                 return crs;
             }
@@ -1458,7 +1405,7 @@ final class CRSBuilder {
                 if (!Units.METRE.equals(linearUnit)) {
                     cs = replaceLinearUnit(cs, linearUnit);
                 }
-                final ProjectedCRS crs = objectFactory().createProjectedCRS(properties(name), baseCRS, projection, cs);
+                final ProjectedCRS crs = crsFactory().createProjectedCRS(properties(name), baseCRS, projection, cs);
                 lastName = crs.getName();
                 return crs;
             }
@@ -1693,7 +1640,7 @@ final class CRSBuilder {
                 if (!Units.METRE.equals(unit)) {
                     cs = (VerticalCS) CoordinateSystems.replaceLinearUnit(cs, unit);
                 }
-                return objectFactory().createVerticalCRS(properties(name), datum, cs);
+                return crsFactory().createVerticalCRS(properties(name), datum, cs);
             }
             default: {
                 return epsgFactory().createVerticalCRS(String.valueOf(epsg));
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 a70e9e6..69ff891 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
@@ -23,10 +23,11 @@ import java.util.HashMap;
 import java.io.IOException;
 import javax.measure.Unit;
 import org.opengis.util.GenericName;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.cs.CSFactory;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.apache.sis.internal.metadata.AxisDirections;
-import org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis;
 import org.apache.sis.referencing.NamedIdentifier;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.storage.netcdf.AttributeNames;
@@ -51,7 +52,7 @@ import ucar.nc2.constants.CF;
  * @since 0.3
  * @module
  */
-public final class Axis {
+public final class Axis extends NamedElement {
     /**
      * The abbreviation, also used as a way to identify the axis type. Possible values are:
      * <ul>
@@ -128,45 +129,47 @@ public final class Axis {
      * @throws ArithmeticException if the size of an axis exceeds {@link Integer#MAX_VALUE}, or other overflow occurs.
      */
     public Axis(final GridGeometry owner, final Variable axis, final AttributeNames.Dimension attributeNames,
-                final char abbreviation, final String direction, final int[] sourceDimensions, final int[] sourceSizes)
+                char abbreviation, final String direction, final int[] sourceDimensions, final int[] sourceSizes)
                 throws IOException, DataStoreException
     {
         /*
-         * Try to get the axis direction from one of the following sources, in preference order:
+         * Try to get the axis direction from one of the following sources,
+         * in preference order (unless an inconsistency is detected):
          *
          *   1) The "positive" attribute value, which can be "up" or "down".
          *   2) The abbreviation, which indirectly tells us the axis type inferred by UCAR library.
          *   3) The direction in unit of measurement formatted as "degrees east" or "degrees north".
          *
-         * Choice #1 is preferred because it tells us the direction of increasing values. By contrast,
-         * choice #2 said nothing about that. However if we find an inconsistency between directions
-         * inferred in those different ways, we give precedence to choices #2 and #3 in that order.
-         * Choice #1 is not considered authoritative because it applies (in principle) to only one of axis.
+         * Choice #1 is preferred because it is the only one telling us the direction of increasing values.
+         * However if we find an inconsistency between directions inferred in those different ways, then we
+         * give precedence to choices #2 and #3 in that order. Choice #1 is not considered authoritative
+         * because it applies (in principle) only to vertical axis.
          */
         AxisDirection dir = Types.forCodeName(AxisDirection.class, direction, false);
         AxisDirection check = AxisDirections.fromAbbreviation(abbreviation);
-        final boolean isSigned = (dir != null);     // Whether is specify the direction of positive values.
+        final boolean isSigned = (dir != null);     // Whether 'dir' takes in account the direction of positive values.
         boolean isConsistent = true;
         if (dir == null) {
             dir = check;
         } else if (check != null) {
-            isConsistent = AxisDirections.absolute(dir).equals(check);
+            isConsistent = AxisDirections.isColinear(dir, check);
         }
         if (isConsistent) {
             check = direction(axis.getUnitsString());
             if (dir == null) {
                 dir = check;
             } else if (check != null) {
-                isConsistent = AxisDirections.absolute(dir).equals(AxisDirections.absolute(check));
+                isConsistent = AxisDirections.isColinear(dir, check);
             }
         }
         if (!isConsistent) {
-            // TODO: report a warning here.
+            axis.warning(owner.getClass(), "getAxes",               // Caller of this constructor.
+                         Resources.Keys.AmbiguousAxisDirection_4, axis.getFilename(), axis.getName(), dir, check);
             if (isSigned) {
-                dir = check;
-                if (AxisDirections.isOpposite(check)) {
-                    dir = AxisDirections.opposite(dir);
+                if (AxisDirections.isOpposite(dir)) {
+                    check = AxisDirections.opposite(check);         // Apply the sign of 'dir' on 'check'.
                 }
+                dir = check;
             }
         }
         this.direction        = dir;
@@ -212,30 +215,69 @@ public final class Axis {
         return null;
     }
 
-    private CoordinateSystemAxis toISO() {
-        final String name = coordinates.getName().trim();
-        final Map<String,Object> properties = new HashMap<>(4);
-        properties.put(CoordinateSystemAxis.NAME_KEY, name);
+    /**
+     * Returns the name of this axis.
+     *
+     * @return the name of this element.
+     */
+    @Override
+    public final String getName() {
+        return coordinates.getName().trim();
+    }
+
+    /**
+     * Creates an ISO 19111 axis from the information stored in this netCDF axis.
+     *
+     * @param  factory  the factory to use for creating the coordinate system axis.
+     */
+    final CoordinateSystemAxis toISO(final CSFactory factory) throws FactoryException {
         /*
-         * Aliases (optional property)
+         * The axis name is stored without namespace, because the variable name in a netCDF file can be anything;
+         * this is not controlled vocabulary. However the standard name, if any, is stored with "NetCDF" namespace
+         * because this is controlled vocabulary.
          */
+        final String name = getName();
+        final Map<String,Object> properties = new HashMap<>(4);
+        properties.put(CoordinateSystemAxis.NAME_KEY, name);                        // Intentionally no namespace.
         final List<GenericName> aliases = new ArrayList<>(2);
         final String standardName = coordinates.getAttributeString(CF.STANDARD_NAME);
         if (standardName != null) {
-            aliases.add(new NamedIdentifier(Citations.NETCDF, standardName));
+            final NamedIdentifier std = new NamedIdentifier(Citations.NETCDF, standardName);
+            if (standardName.equals(name)) {
+                properties.put(CoordinateSystemAxis.NAME_KEY, std);                 // Store as primary name.
+            } else {
+                aliases.add(std);                                                   // Store as alias.
+            }
         }
+        /*
+         * The long name is stored as an optional description of the primary name.
+         * It is also stored as an alias if not redundant with other names.
+         */
         final String alt = coordinates.getAttributeString(CDM.LONG_NAME);
-        if (alt != null && !alt.equals(standardName)) {
-            aliases.add(new NamedIdentifier(Citations.NETCDF, alt));
+        if (alt != null && !similar(alt, name)) {
+            properties.put(org.opengis.metadata.Identifier.DESCRIPTION_KEY, alt);   // Description associated to primary name.
+            if (!similar(alt, standardName)) {
+                aliases.add(new NamedIdentifier(null, alt));                        // Additional alias.
+            }
         }
-        properties.put(CoordinateSystemAxis.ALIAS_KEY, aliases.toArray(new GenericName[aliases.size()]));
-
-        String a = Character.toString(abbreviation).intern();   // TODO: need default value is zero.
-        AxisDirection dir = direction;
-        if (dir == null) {
-            dir = AxisDirection.OTHER;
+        if (!aliases.isEmpty()) {
+            properties.put(CoordinateSystemAxis.ALIAS_KEY, aliases.toArray(new GenericName[aliases.size()]));
+        }
+        /*
+         * Axis abbreviation, direction and unit of measurement are mandatory.
+         * If any of them is null, creation of CoordinateSystemAxis is likely
+         * to fail with an InvalidGeodeticParameterException. But we let the
+         * factory to choose, in case users specify their own factory.
+         */
+        final Unit<?> unit = coordinates.getUnit();
+        final String abbr;
+        if (abbreviation != 0) {
+            abbr = Character.toString(abbreviation).intern();
+        } else if (direction != null && unit != null) {
+            abbr = AxisDirections.suggestAbbreviation(name, direction, unit);
+        } else {
+            abbr = null;
         }
-        Unit<?> unit = coordinates.getUnit();       // TODO: need default value if null.
-        return new DefaultCoordinateSystemAxis(properties, a, dir, unit);
+        return factory.createCoordinateSystemAxis(properties, abbr, direction, unit);
     }
 }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridGeometry.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridGeometry.java
index 76b6518..9a8dc7c 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridGeometry.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridGeometry.java
@@ -16,7 +16,13 @@
  */
 package org.apache.sis.internal.netcdf;
 
+import java.util.Map;
+import java.util.HashMap;
 import java.io.IOException;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.cs.CSFactory;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.apache.sis.storage.DataStoreException;
 
 
@@ -31,7 +37,7 @@ import org.apache.sis.storage.DataStoreException;
  * @since 0.3
  * @module
  */
-public abstract class GridGeometry {
+public abstract class GridGeometry extends NamedElement {
     /**
      * The axes, created when first needed.
      *
@@ -108,4 +114,15 @@ public abstract class GridGeometry {
      * @throws ArithmeticException if the axis size exceeds {@link Integer#MAX_VALUE}, or other overflow occurs.
      */
     protected abstract double coordinateForAxis(Variable axis, int j, int i) throws IOException, DataStoreException;
+
+    final CoordinateSystem createCoordinateSystem(final CSFactory factory) throws IOException, DataStoreException, FactoryException {
+        final Axis[] axes = getAxes();
+        final CoordinateSystemAxis[] csAxes = new CoordinateSystemAxis[axes.length];
+        for (int i=0; i<axes.length; i++) {
+            csAxes[i] = axes[i].toISO(factory);
+        }
+        final Map<String,Object> properties = new HashMap<>(4);
+        properties.put(CoordinateSystem.NAME_KEY, getName());
+        return factory.createEllipsoidalCS(null, csAxes[0], csAxes[1]);
+    }
 }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/NamedElement.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/NamedElement.java
index c11af8d..c835476 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/NamedElement.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/NamedElement.java
@@ -16,13 +16,8 @@
  */
 package org.apache.sis.internal.netcdf;
 
-import java.util.AbstractList;
-import java.util.AbstractMap;
-import java.util.Locale;
-import java.util.Map;
-import org.apache.sis.internal.util.CollectionsExt;
-import org.apache.sis.util.logging.WarningListeners;
-import org.opengis.parameter.InvalidParameterCardinalityException;
+import org.apache.sis.util.Characters;
+import org.apache.sis.util.CharSequences;
 
 
 /**
@@ -49,41 +44,25 @@ public abstract class NamedElement {
     public abstract String getName();
 
     /**
-     * Creates a (<cite>name</cite>, <cite>element</cite>) mapping for the given array of elements.
-     * If the name of an element is not all lower cases, then this method also adds an entry for the
-     * lower cases version of that name in order to allow case-insensitive searches.
+     * Returns {@code true} if the given names are considered equals for the purpose of netCDF decoder.
+     * Two names are considered similar if they are equal ignoring case and characters that are not valid
+     * for an Unicode identifier.
      *
-     * <p>Code searching in the returned map shall ask for the original (non lower-case) name
-     * <strong>before</strong> to ask for the lower-cases version of that name.</p>
-     *
-     * @param  <E>          the type of elements.
-     * @param  elements     the elements to store in the map, or {@code null} if none.
-     * @param  namesLocale  the locale to use for creating the "all lower cases" names.
-     * @return a (<cite>name</cite>, <cite>element</cite>) mapping with lower cases entries where possible.
-     * @throws InvalidParameterCardinalityException if the same name is used for more than one element.
+     * @param  s1  the first characters sequence to compare, or {@code null}.
+     * @param  s2  the second characters sequence to compare, or {@code null}.
+     * @return whether the two characters sequences are considered similar names.
      */
-    public static <E extends NamedElement> Map<String,E> toCaseInsensitiveNameMap(final E[] elements, final Locale namesLocale) {
-        return CollectionsExt.toCaseInsensitiveNameMap(new AbstractList<Map.Entry<String,E>>() {
-            @Override
-            public int size() {
-                return elements.length;
-            }
-
-            @Override
-            public Map.Entry<String,E> get(final int index) {
-                final E e = elements[index];
-                return new AbstractMap.SimpleImmutableEntry<>(e.getName(), e);
-            }
-        }, namesLocale);
+    protected static boolean similar(final CharSequence s1, final CharSequence s2) {
+        return CharSequences.equalsFiltered(s1, s2, Characters.Filter.UNICODE_IDENTIFIER, true);
     }
 
     /**
-     * Returns the resources to use for warnings or error messages.
+     * Returns a string representation of this element. Current implementation returns only the element class and name.
      *
-     * @param  listeners  where the warnings are sent. Used for inferring the locale.
-     * @return the resources for the locales specified by the given argument.
+     * @return string representation of this element for debugging purposes.
      */
-    protected static Resources resources(final WarningListeners<?> listeners) {
-        return Resources.forLocale(listeners != null ? listeners.getLocale() : null);
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[\"" + getName() + "\"]";
     }
 }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
index 7ddb3c8..ba8e2a9 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
@@ -59,6 +59,12 @@ public final class Resources extends IndexedResourceBundle {
         }
 
         /**
+         * NetCDF file “{0}” provides an ambiguous axis direction for variable “{1}”. It could be
+         * {2} or {3}.
+         */
+        public static final short AmbiguousAxisDirection_4 = 9;
+
+        /**
          * Can not compute data location for “{1}” variable in the “{0}” netCDF file.
          */
         public static final short CanNotComputeVariablePosition_2 = 6;
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
index 92f6c3f..e5e0e0c 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
@@ -19,6 +19,7 @@
 # Resources in this file are for "sis-netcdf" usage only and should not be used by any other module.
 # For resources shared by all modules in the Apache SIS project, see "org.apache.sis.util.resources" package.
 #
+AmbiguousAxisDirection_4          = NetCDF file \u201c{0}\u201d provides an ambiguous axis direction for variable \u201c{1}\u201d. It could be {2}\u00a0or {3}.
 CanNotComputeVariablePosition_2   = Can not compute data location for \u201c{1}\u201d variable in the \u201c{0}\u201d netCDF file.
 CanNotUseUCAR                     = Can not use UCAR library for netCDF format. Fallback on Apache SIS implementation.
 DimensionNotFound_3               = Dimension \u201c{2}\u201d declared by attribute \u201c{1}\u201d is not found in the \u201c{0}\u201d file.
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
index 78adf94..9731b7d 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
@@ -24,6 +24,7 @@
 #   U+202F NARROW NO-BREAK SPACE  before  ; ! and ?
 #   U+00A0 NO-BREAK SPACE         before  :
 #
+AmbiguousAxisDirection_4          = Le fichier netCDF \u00ab\u202f{0}\u202f\u00bb fournit une direction d\u2019axe ambigu\u00eb pour la variable \u00ab\u202f{1}\u202f\u00bb. Elle pourrait \u00eatre {2}\u00a0ou {3}.
 CanNotComputeVariablePosition_2   = Ne peut pas calculer la position des donn\u00e9es de la variable \u00ab\u202f{1}\u202f\u00bb dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb.
 CanNotUseUCAR                     = Ne peut pas utiliser la biblioth\u00e8que de l\u2019UCAR pour le format netCDF. L\u2019impl\u00e9mentation de Apache SIS sera utilis\u00e9e \u00e0 la place.
 DimensionNotFound_3               = La dimension \u00ab\u202f{2}\u202f\u00bb d\u00e9clar\u00e9e par l\u2019attribut \u00ab\u202f{1}\u202f\u00bb n\u2019a pas \u00e9t\u00e9 trouv\u00e9e dans le fichier \u00ab\u202f{0}\u202f\u00bb.
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 15840a2..3ba3ce4 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
@@ -17,12 +17,15 @@
 package org.apache.sis.internal.netcdf;
 
 import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import java.io.IOException;
 import java.awt.image.DataBuffer;
 import javax.measure.Unit;
 import javax.measure.format.ParserException;
 import org.apache.sis.math.Vector;
 import org.apache.sis.measure.Units;
+import org.apache.sis.internal.system.Modules;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.logging.WarningListeners;
 import org.apache.sis.util.resources.Errors;
@@ -59,7 +62,7 @@ public abstract class Variable extends NamedElement {
     /**
      * Where to report warnings, if any.
      */
-    protected final WarningListeners<?> listeners;
+    private final WarningListeners<?> listeners;
 
     /**
      * Creates a new variable.
@@ -71,6 +74,14 @@ public abstract class Variable extends NamedElement {
     }
 
     /**
+     * Returns the name of the netCDF file containing this variable, or {@code null} if unknown.
+     * This is used for information purpose only.
+     *
+     * @return name of the netCDF file containing this variable, or {@code null} if unknown.
+     */
+    public abstract String getFilename();
+
+    /**
      * Returns the name of this variable, or {@code null} if none.
      *
      * @return the name of this variable, or {@code null}.
@@ -297,6 +308,31 @@ public abstract class Variable extends NamedElement {
     public abstract Vector read(int[] areaLower, int[] areaUpper, int[] subsampling) throws IOException, DataStoreException;
 
     /**
+     * Returns the resources to use for warnings or error messages.
+     *
+     * @return the resources for the locales specified by the given argument.
+     */
+    protected final Resources resources() {
+        return Resources.forLocale(listeners.getLocale());
+    }
+
+    /**
+     * Reports a warning to the listeners specified at construction time.
+     *
+     * @param  caller     the caller class to report, preferably a public class.
+     * @param  method     the caller method to report, preferable a public method.
+     * @param  key        one or {@link Resources.Keys} constants.
+     * @param  arguments  values to be formatted in the {@link java.text.MessageFormat} pattern.
+     */
+    protected final void warning(final Class<?> caller, final String method, final short key, final Object... arguments) {
+        final LogRecord record = resources().getLogRecord(Level.WARNING, key, arguments);
+        record.setLoggerName(Modules.NETCDF);
+        record.setSourceClassName(caller.getCanonicalName());
+        record.setSourceMethodName(method);
+        listeners.warning(record);
+    }
+
+    /**
      * Returns a string representation of this variable for debugging purpose.
      *
      * @return a string representation of this variable.
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 d2fab63..47c18b1 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
@@ -36,6 +36,7 @@ import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.nio.channels.ReadableByteChannel;
+import java.util.AbstractList;
 import javax.measure.UnitConverter;
 import javax.measure.IncommensurableException;
 import javax.measure.format.ParserException;
@@ -282,7 +283,35 @@ public final class ChannelDecoder extends Decoder {
         }
         this.attributeMap = attributes;
         this.variables    = variables;
-        this.variableMap  = NamedElement.toCaseInsensitiveNameMap(variables, NAME_LOCALE);
+        this.variableMap  = toCaseInsensitiveNameMap(variables);
+    }
+
+    /**
+     * Creates a (<cite>name</cite>, <cite>element</cite>) mapping for the given array of elements.
+     * If the name of an element is not all lower cases, then this method also adds an entry for the
+     * lower cases version of that name in order to allow case-insensitive searches.
+     *
+     * <p>Code searching in the returned map shall ask for the original (non lower-case) name
+     * <strong>before</strong> to ask for the lower-cases version of that name.</p>
+     *
+     * @param  <E>       the type of elements.
+     * @param  elements  the elements to store in the map, or {@code null} if none.
+     * @return a (<cite>name</cite>, <cite>element</cite>) mapping with lower cases entries where possible.
+     * @throws InvalidParameterCardinalityException if the same name is used for more than one element.
+     */
+    private static <E extends NamedElement> Map<String,E> toCaseInsensitiveNameMap(final E[] elements) {
+        return CollectionsExt.toCaseInsensitiveNameMap(new AbstractList<Map.Entry<String,E>>() {
+            @Override
+            public int size() {
+                return elements.length;
+            }
+
+            @Override
+            public Map.Entry<String,E> get(final int index) {
+                final E e = elements[index];
+                return new AbstractMap.SimpleImmutableEntry<>(e.getName(), e);
+            }
+        }, NAME_LOCALE);
     }
 
     /**
@@ -488,7 +517,7 @@ public final class ChannelDecoder extends Decoder {
             }
             dimensions[i] = new Dimension(name, length, isUnlimited);
         }
-        dimensionMap = Dimension.toCaseInsensitiveNameMap(dimensions, NAME_LOCALE);
+        dimensionMap = toCaseInsensitiveNameMap(dimensions);
         return dimensions;
     }
 
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfo.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfo.java
index 51df17c..100f8cd 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfo.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfo.java
@@ -90,6 +90,14 @@ final class GridGeometryInfo extends GridGeometry {
     }
 
     /**
+     * Returns a name for this grid geometry, for information purpose only.
+     */
+    @Override
+    public String getName() {
+        return getFilename();
+    }
+
+    /**
      * Returns the number of dimensions of source coordinates in the <cite>"grid to CRS"</cite> conversion.
      * This is the number of dimensions of the <em>grid</em>.
      */
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 a14bcaf..d11d376 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
@@ -19,8 +19,6 @@ package org.apache.sis.internal.netcdf.impl;
 import java.util.Map;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
 import java.io.IOException;
 import java.lang.reflect.Array;
 import ucar.nc2.constants.CF;
@@ -29,7 +27,6 @@ import ucar.nc2.constants._Coordinate;
 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.system.Modules;
 import org.apache.sis.internal.storage.io.ChannelDataInput;
 import org.apache.sis.internal.storage.io.HyperRectangleReader;
 import org.apache.sis.internal.storage.io.Region;
@@ -224,12 +221,8 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
             final long actual = Integer.toUnsignedLong(size);
             if (actual != expected) {
                 if (expected != 0) {
-                    final LogRecord record = resources(listeners).getLogRecord(Level.WARNING,
+                    warning(ChannelDecoder.class, "readVariables",          // Caller of this constructor.
                             Resources.Keys.MismatchedVariableSize_3, getFilename(), name, actual - expected);
-                    record.setLoggerName(Modules.NETCDF);
-                    record.setSourceClassName(ChannelDecoder.class.getName());      // Caller of this constructor.
-                    record.setSourceMethodName("readVariables");
-                    listeners.warning(record);
                 }
                 if (actual > offsetToNextRecord) {
                     offsetToNextRecord = actual;
@@ -317,7 +310,8 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
     /**
      * Returns the name of the netCDF file containing this variable, or {@code null} if unknown.
      */
-    final String getFilename() {
+    @Override
+    public String getFilename() {
         return (reader != null) ? reader.filename() : null;
     }
 
@@ -574,7 +568,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
         if (isUnlimited()) {
             final int dataSize = reader.dataSize();
             if (offsetToNextRecord < 0 || (offsetToNextRecord % dataSize) != 0) {
-                throw new DataStoreContentException(resources(listeners)
+                throw new DataStoreContentException(resources()
                         .getString(Resources.Keys.CanNotComputeVariablePosition_2, getFilename(), name));
             }
             region.increaseStride(dimensions.length - 1, offsetToNextRecord / dataSize);
@@ -648,7 +642,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
      * Returns the error message for an unknown data type.
      */
     private String unknownType() {
-        return resources(listeners).getString(Resources.Keys.UnsupportedDataType_3, getFilename(), name, dataType);
+        return resources().getString(Resources.Keys.UnsupportedDataType_3, getFilename(), name, dataType);
     }
 
     /**
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridGeometryWrapper.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridGeometryWrapper.java
index 22c4324..9087dd3 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridGeometryWrapper.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridGeometryWrapper.java
@@ -66,6 +66,14 @@ final class GridGeometryWrapper extends GridGeometry {
     }
 
     /**
+     * Returns a name for this grid geometry, for information purpose only.
+     */
+    @Override
+    public String getName() {
+        return netcdfCS.getName();
+    }
+
+    /**
      * Returns the number of dimensions of source coordinates in the <cite>"grid to CRS"</cite> conversion.
      * This is the number of dimensions of the <em>grid</em>.
      */
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 079690e..9d2496e 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,6 +17,7 @@
 package org.apache.sis.internal.netcdf.ucar;
 
 import java.util.List;
+import java.io.File;
 import java.io.IOException;
 import java.util.Collection;
 import ucar.ma2.Array;
@@ -74,6 +75,20 @@ final class VariableWrapper extends Variable {
     }
 
     /**
+     * Returns the name of the netCDF file containing this variable, or {@code null} if unknown.
+     */
+    @Override
+    public String getFilename() {
+        if (variable instanceof ucar.nc2.Variable) {
+            String name = ((ucar.nc2.Variable) variable).getDatasetLocation();
+            if (name != null) {
+                return name.substring(Math.max(name.lastIndexOf('/'), name.lastIndexOf(File.separatorChar)) + 1);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the name of this variable, or {@code null} if none.
      */
     @Override


Mime
View raw message