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 f5fa5b8779ddc94f6747f9356f41133b94d9267e
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Apr 6 17:30:09 2019 +0200

    If the CRS in a netCDF file seems to be a map projection but the Grid class 
does not have enough information for building the right map projection, creates 
an "Not specified" projection.
    This "not specified" projection may be replaced by a projection parsed from 
Well Known Text at a later stage during the netCDF reading process, but 
creating an instance in in Grid class allow us to preserve information like 
mapping from CRS axes to grid axes.
---
 .../referencing/provider/Equirectangular.java      |  11 +-
 .../referencing/operation/DefaultConversion.java   |   2 +-
 .../org/apache/sis/internal/util/Constants.java    |  14 -
 .../java/org/apache/sis/internal/netcdf/Axis.java  |  35 +-
 .../org/apache/sis/internal/netcdf/CRSBuilder.java | 377 ++++++++++++++-------
 .../org/apache/sis/internal/netcdf/Decoder.java    |  15 +-
 .../java/org/apache/sis/internal/netcdf/Grid.java  |  11 +-
 .../apache/sis/internal/netcdf/GridMapping.java    |   5 +-
 .../apache/sis/internal/netcdf/impl/GridInfo.java  |  14 +-
 .../internal/netcdf/ucar/CSBuilderFallback.java    |   5 +-
 .../sis/internal/netcdf/ucar/GridWrapper.java      |   6 +-
 .../org/apache/sis/internal/netcdf/GridTest.java   |   4 +-
 12 files changed, 327 insertions(+), 172 deletions(-)

diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
index ea2d742..033a241 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
@@ -61,7 +61,7 @@ import static java.lang.Math.*;
  *
  * @author  John Grange
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  *
  * @see PseudoPlateCarre
  * @see <a 
href="http://geotiff.maptools.org/proj_list/equirectangular.html";>GeoTIFF 
parameters for Equirectangular</a>
@@ -76,6 +76,13 @@ public final class Equirectangular extends AbstractProvider {
      */
     private static final long serialVersionUID = -278288251842178001L;
 
+    /**
+     * Name of this projection in EPSG geodetic dataset.
+     *
+     * @todo Remove with JDK9 after we introduce {@code getInstance()} method.
+     */
+    public static final String NAME = "Equidistant Cylindrical (Spherical)";
+
     /*
      * ACCESS POLICY: Only formal EPSG parameters shall be public.
      * Parameters that we add ourselves should be package-privated.
@@ -182,7 +189,7 @@ public final class Equirectangular extends AbstractProvider 
{
         // Do not declare the ESRI "Equidistant_Cylindrical" projection name 
below,
         // for avoiding confusion with EPSG "Equidistant Cylindrical" 
ellipsoidal projection.
         PARAMETERS = addIdentifierAndLegacy(builder, "1029", "9823")  // 9823 
uses deprecated parameter names
-                .addName(                   "Equidistant Cylindrical 
(Spherical)")
+                .addName(                   NAME)
                 .addName(                   "Plate Carrée")  // Not formally 
defined by EPSG, but cited in documentation.
                 .addName(Citations.OGC,     "Equirectangular")
                 .addName(Citations.ESRI,    "Plate_Carree")
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
index ca98dd6..4e1ebd0 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java
@@ -190,7 +190,7 @@ public class DefaultConversion extends 
AbstractSingleOperation implements Conver
      * The semi-major and semi-minor parameter values will be set 
automatically when the
      * {@link #specialize specialize(…)} method will be invoked.</p>
      *
-     * <p>If both the {@code transform} and {@code parameters} arguments are 
non-null, then the later should describes
+     * <p>If both the {@code transform} and {@code parameters} arguments are 
non-null, then the later should describe
      * the parameters used for creating the transform. Those parameters will 
be stored for information purpose and can
      * be given back by the {@link #getParameterValues()} method.</p>
      *
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java 
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java
index d089889..afd1b0d 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java
@@ -241,20 +241,6 @@ public final class Constants extends Static {
     public static final short EPSG_ANTARCTIC_POLAR_STEREOGRAPHIC = 3031;
 
     /**
-     * EPSG code of "Unknown datum based upon the WGS 84 ellipsoid".
-     * This is a two-dimensional geographic CRS.
-     * Note that the EPSG database defines unknown CRS for many other 
ellipsoids.
-     * For now only the WGS 84 case is used by Apache SIS.
-     */
-    public static final short EPSG_UNKNOWN_CRS = 4030;
-
-    /**
-     * EPSG code of "Not specified (based upon the WGS 84 ellipsoid)".
-     * This is a geodetic datum.
-     */
-    public static final short EPSG_UNKNOWN_DATUM = 6030;
-
-    /**
      * Do not allow instantiation of this class.
      */
     private Constants() {
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 f034fa4..67e602b 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
@@ -384,7 +384,7 @@ public final class Axis extends NamedElement {
 
     /**
      * Returns {@code true} if the given axis specifies the same direction and 
unit of measurement than this axis.
-     * This is used for testing is a predefined axis can be used instead than 
invoking {@link #toISO(CSFactory)}.
+     * This is used for testing if a predefined axis can be used instead than 
invoking {@link #toISO(CSFactory, int)}.
      */
     final boolean isSameUnitAndDirection(final CoordinateSystemAxis axis) {
         if (!axis.getDirection().equals(direction)) {
@@ -467,9 +467,10 @@ public final class Axis extends NamedElement {
      * 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.
+     * @param  order    0 if creating the first axis, 1 if creating the second 
axis, <i>etc</i>.
      * @return the ISO axis.
      */
-    final CoordinateSystemAxis toISO(final CSFactory factory) throws 
FactoryException {
+    final CoordinateSystemAxis toISO(final CSFactory factory, final int order) 
throws FactoryException {
         /*
          * 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
@@ -505,28 +506,40 @@ public final class Axis extends NamedElement {
         /*
          * 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.
-         * We provide default values for the most well-accepted values and 
leave other values to null.
+         * We provide default values for the most well-identified axes and 
leave other values to null.
          * Those null values can be accepted if users specify their own 
factory.
+         *
+         * The default values are SI base units except degrees, which is the 
usually angular units for netCDF files.
+         * Providing default units is a little bit dangerous, but we can not 
create CRS otherwise. Note that wrong
+         * defaults become harmless if the CRS is overwritten by GridMapping 
attributes in Variable.getGridGeometry().
          */
         Unit<?> unit = getUnit();
         if (unit == null) {
             switch (abbreviation) {
-                /*
-                 * TODO: consider moving those default values in a separated 
class,
-                 * for example a netCDF-specific CSFactory, for allowing users 
to override.
-                 */
-                case 'λ': case 'φ': unit = Units.DEGREE; break;
+                case 'λ': case 'φ':                                 // 
Geodetic longitude and latitude.
+                case 'θ': case 'Ω': unit = Units.DEGREE; break;     // 
Spherical longitude and latitude.
+                case 'r': case 'D':                                 // Depth 
and radius.
+                case 'H': case 'h':                                 // 
Gravity-related and ellipsoidal height.
+                case 'E': case 'N': unit = Units.METRE;  break;     // 
Projected easting and northing.
+                case 't':           unit = Units.SECOND; break;     // Time.
+            }
+        }
+        AxisDirection dir = direction;
+        if (dir == null) {
+            switch (order) {
+                case 0: dir = AxisDirection.COLUMN_POSITIVE; break;
+                case 1: dir = AxisDirection.ROW_POSITIVE; break;
             }
         }
         final String abbr;
         if (abbreviation != 0) {
             abbr = Character.toString(abbreviation).intern();
-        } else if (direction != null && unit != null) {
-            abbr = AxisDirections.suggestAbbreviation(name, direction, unit);
+        } else if (dir != null && unit != null) {
+            abbr = AxisDirections.suggestAbbreviation(name, dir, unit);
         } else {
             abbr = null;
         }
-        return factory.createCoordinateSystemAxis(properties, abbr, direction, 
unit);
+        return factory.createCoordinateSystemAxis(properties, abbr, dir, unit);
     }
 
     /**
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
index f097c65..3a62463 100644
--- 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
+++ 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
@@ -33,17 +33,22 @@ import org.opengis.referencing.datum.*;
 import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.crs.CRSFactory;
 import org.opengis.referencing.crs.GeographicCRS;
+import org.opengis.referencing.crs.GeocentricCRS;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
+import org.opengis.referencing.operation.CoordinateOperationFactory;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.Conversion;
 import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.cs.AxesConvention;
 import org.apache.sis.referencing.cs.CoordinateSystems;
-import org.apache.sis.referencing.cs.DefaultSphericalCS;
-import org.apache.sis.referencing.cs.DefaultEllipsoidalCS;
 import org.apache.sis.referencing.crs.AbstractCRS;
-import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.DataStoreContentException;
+import org.apache.sis.referencing.crs.DefaultGeographicCRS;
+import org.apache.sis.referencing.crs.DefaultGeocentricCRS;
+import org.apache.sis.internal.referencing.provider.Equirectangular;
+import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.util.TemporalUtilities;
-import org.apache.sis.internal.util.Constants;
+import org.apache.sis.storage.DataStoreContentException;
+import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.measure.Units;
 import org.apache.sis.math.Vector;
@@ -75,9 +80,13 @@ import org.apache.sis.math.Vector;
 abstract class CRSBuilder<D extends Datum, CS extends CoordinateSystem> {
     /**
      * The coordinate reference system which is presumed the basis of datum on 
netCDF files.
-     * Note: if this default is changed, search also for "WGS 84" strings in 
this class.
+     * Note: if this default is changed, search also for "GRS 1980" strings in 
this class.
+     *
+     * <div class="note"><b>Note:</b> we use GRS 1980 instead than WGS 84 
because the CRS name
+     * clearly said "Unknown datum based upon the GRS 1980 ellipsoid" and for 
consistency with
+     * {@link CommonCRS#SPHERE}, which also use GRS 1980.</div>
      */
-    private static final CommonCRS DEFAULT = CommonCRS.WGS84;
+    private static final CommonCRS DEFAULT = CommonCRS.GRS1980;
 
     /**
      * The type of datum as a GeoAPI sub-interface of {@link Datum}.
@@ -87,7 +96,7 @@ abstract class CRSBuilder<D extends Datum, CS extends 
CoordinateSystem> {
 
     /**
      * Name of the datum on which the CRS is presumed to be based, or {@code 
""}. This is used
-     * for building a datum name like <cite>"Unknown datum presumably based on 
WGS 84"</cite>.
+     * for building a datum name like <cite>"Unknown datum presumably based on 
GRS 1980"</cite>.
      */
     private final String datumBase;
 
@@ -121,17 +130,17 @@ abstract class CRSBuilder<D extends Datum, CS extends 
CoordinateSystem> {
     /**
      * The datum created by {@link #createDatum(DatumFactory, Map)}.
      */
-    D datum;
+    protected D datum;
 
     /**
      * The coordinate system created by {@link #createCS(CSFactory, Map, 
CoordinateSystemAxis[])}.
      */
-    CS coordinateSystem;
+    protected CS coordinateSystem;
 
     /**
-     * The coordinate reference system that may have been create by {@link 
#candidate(Decoder)}.
+     * The coordinate reference system that may have been create by {@link 
#setPredefinedComponents(Decoder)}.
      */
-    SingleCRS referenceSystem;
+    protected SingleCRS referenceSystem;
 
     /**
      * Non-fatal exceptions that may occur while building the coordinate 
reference system.
@@ -278,12 +287,11 @@ previous:   for (int i=components.size(); --i >= 0;) {
          * set the datum, CS and CRS field values to those candidate. Those 
values do not need to be exact; they
          * will be overwritten later if they do not match the netCDF file 
content.
          */
-        datum = datumType.cast(decoder.datumCache[datumIndex]);         // 
Should be initialized before 'candidate' call.
-        candidate(decoder);
+        datum = datumType.cast(decoder.datumCache[datumIndex]);         // 
Should be before 'setPredefinedComponents' call.
+        setPredefinedComponents(decoder);
         /*
-         * If 'candidate(decoder)' offers a datum, we will used it as-is. 
Otherwise create the datum now.
-         * Datum are often not defined in netCDF files, so it will usually be 
EPSG::6030 — "Not specified
-         * (based on WGS 84 ellipsoid)".
+         * If 'setPredefinedComponents(decoder)' offers a datum, we will used 
it as-is. Otherwise create the datum now.
+         * Datum are often not defined in netCDF files, so we use EPSG::6019 — 
"Not specified (based on GRS 1980 ellipsoid)".
          */
         if (datum == null) {
             // Not localized because stored as a String, possibly exported in 
WKT or GML, and 'datumBase' is in English.
@@ -306,8 +314,8 @@ previous:   for (int i=components.size(); --i >= 0;) {
             }
         }
         /*
-         * If 'candidate(decoder)' did not proposed a coordinate system, or if 
it proposed a CS but its
-         * axes do not match the axes in the netCDF file, then create a new 
coordinate system here.
+         * If 'setPredefinedComponents(decoder)' did not proposed a coordinate 
system, or if it proposed a CS
+         * but its axes do not match the axes in the netCDF file, then create 
a new coordinate system here.
          */
         if (referenceSystem == null) {
             final Map<String,?> properties;
@@ -319,7 +327,7 @@ previous:   for (int i=components.size(); --i >= 0;) {
                 for (int i=0; i<iso.length; i++) {
                     final Axis axis = axes[i];
                     joiner.add(axis.getName());
-                    iso[i] = axis.toISO(csFactory);
+                    iso[i] = axis.toISO(csFactory, i);
                 }
                 createCS(csFactory, properties(joiner.toString()), iso);
                 properties = properties(coordinateSystem.getName());
@@ -356,9 +364,9 @@ previous:   for (int i=components.size(); --i >= 0;) {
     }
 
     /**
-     * Reports a non-fatal exception that may occur when processing the value 
returned by {@link #epsgCandidate(Unit)}.
-     * In order to avoid repeating the same warning many times, this method 
collects the warnings together and reports
-     * them in a single log record after we finished creating the CRS.
+     * Reports a non-fatal exception that may occur during {@link 
#setPredefinedComponents(Decoder)}.
+     * In order to avoid repeating the same warning many times, this method 
collects the warnings
+     * together and reports them in a single log record after we finished 
creating the CRS.
      */
     final void recoverableException(final NoSuchAuthorityCodeException e) {
         if (warnings == null) warnings = e;
@@ -378,12 +386,14 @@ previous:   for (int i=components.size(); --i >= 0;) {
      * Returns the EPSG code of a possible coordinate system from EPSG 
database. This method proceed by brief
      * inspection of axis directions and units; there is no guarantees that 
the coordinate system returned by
      * this method match the axes defined in the netCDF file. It is caller's 
responsibility to verify.
-     * This is a helper method for {@link #candidate(Decoder)} implementations.
+     * This is a helper method for {@link #setPredefinedComponents(Decoder)} 
implementations.
      *
      * @param  defaultUnit  the unit to use if unit definition is missing in 
the netCDF file.
      * @return EPSG code of a CS candidate, or {@code null} if none.
+     *
+     * @see Geodetic#isPredefinedCS(Unit)
      */
-    final Integer epsgCandidate(final Unit<?> defaultUnit) {
+    final Integer epsgCandidateCS(final Unit<?> defaultUnit) {
         Unit<?> unit = getFirstAxis().getUnit();
         if (unit == null) unit = defaultUnit;
         final AxisDirection[] directions = new AxisDirection[dimension];
@@ -402,11 +412,11 @@ previous:   for (int i=components.size(); --i >= 0;) {
      * <p>This method may opportunistically set the {@link #datum} and {@link 
#referenceSystem} fields if it
      * can propose a CRS candidate instead than only a CS candidate.</p>
      */
-    abstract void candidate(Decoder decoder) throws FactoryException;
+    abstract void setPredefinedComponents(Decoder decoder) throws 
FactoryException;
 
     /**
      * Creates the datum for the coordinate reference system to build. The 
datum are generally not specified in netCDF files.
-     * To make that clearer, this method builds datum with names like 
<cite>"Unknown datum presumably based on WGS 84"</cite>.
+     * To make that clearer, this method builds datum with names like 
<cite>"Unknown datum presumably based on GRS 1980"</cite>.
      * The newly created datum is assigned to the {@link #datum} field.
      *
      * @param  factory     the factory to use for creating the datum.
@@ -435,20 +445,33 @@ previous:   for (int i=components.size(); --i >= 0;) {
      */
     abstract void createCRS(CRSFactory factory, Map<String,?> properties) 
throws FactoryException;
 
+
+
+
     /**
      * Base classes of {@link Spherical}, {@link Geographic} and {@link 
Projected} builders.
      * They all have in common to be based on a {@link GeodeticDatum}.
      */
     private abstract static class Geodetic<CS extends CoordinateSystem> 
extends CRSBuilder<GeodeticDatum, CS> {
-        /** Whether the coordinate system has longitude before latitude. */
-        boolean isLongitudeFirst;
+        /**
+         * Whether the coordinate system has longitude before latitude.
+         * This flag is set as a side-effect of {@link #isPredefinedCS(Unit)} 
method call.
+         */
+        protected boolean isLongitudeFirst;
 
-        /** For subclasses constructors. */
+        /**
+         * For subclasses constructors.
+         *
+         * @param  minDim  minimum number of dimensions (2 or 3).
+         */
         Geodetic(final byte minDim) {
-            super(GeodeticDatum.class, "WGS 84", (byte) 0, minDim, (byte) 3);
+            super(GeodeticDatum.class, "GRS 1980", (byte) 0, minDim, (byte) 3);
         }
 
-        /** Creates a {@link GeodeticDatum} for <cite>"Unknown datum based on 
WGS 84"</cite>. */
+        /**
+         * Creates a {@link GeodeticDatum} for <cite>"Unknown datum based on 
GRS 1980"</cite>.
+         * This method is invoked only if {@link 
#setPredefinedComponents(Decoder)} failed to create a datum.
+         */
         @Override final void createDatum(DatumFactory factory, Map<String,?> 
properties) throws FactoryException {
             final GeodeticDatum template = DEFAULT.datum();
             datum = factory.createGeodeticDatum(properties, 
template.getEllipsoid(), template.getPrimeMeridian());
@@ -461,8 +484,10 @@ previous:   for (int i=components.size(); --i >= 0;) {
          * If {@code true}, then {@link #isLongitudeFirst} will have been set 
to an indication of axis order.
          *
          * @param  expected  the expected unit of measurement of the first 
axis.
+         *
+         * @see #epsgCandidateCS(Unit)
          */
-        final boolean isPredefined(final Unit<?> expected) {
+        final boolean isPredefinedCS(final Unit<?> expected) {
             final Axis axis = getFirstAxis();
             final Unit<?> unit = axis.getUnit();
             if (unit == null || expected.equals(unit)) {
@@ -473,103 +498,109 @@ previous:   for (int i=components.size(); --i >= 0;) {
             }
             return false;
         }
-
-        /**
-         * If the {@link #datum} field is not already set, initialize it to 
"Not specified (based upon the WGS 84 ellipsoid)".
-         * Subclasses should override this method for setting also the {@link 
#coordinateSystem} and {@link #referenceSystem}
-         * fields if possible.
-         */
-        @Override void candidate(final Decoder decoder) throws 
FactoryException {
-            if (datum == null) try {
-                datum = 
decoder.getDatumAuthorityFactory().createGeodeticDatum(String.valueOf(Constants.EPSG_UNKNOWN_DATUM));
-            } catch (NoSuchAuthorityCodeException e) {
-                recoverableException(e);
-            }
-        }
     }
 
+
+
+
     /**
      * Builder for geocentric CRS with (θ,Ω,r) axes.
      */
     private static final class Spherical extends Geodetic<SphericalCS> {
-        /** Creates a new builder (invoked by lambda function). */
+        /**
+         * Creates a new builder (invoked by lambda function).
+         */
         public Spherical() {
             super((byte) 3);
         }
 
-        /** Possibly sets {@link #coordinateSystem} to a predefined CS 
matching the axes defined in the netCDF file. */
-        @Override void candidate(final Decoder decoder) throws 
FactoryException {
-            super.candidate(decoder);
-            final Integer epsg = epsgCandidate(Units.DEGREE);
-            if (epsg != null) try {
-                coordinateSystem = 
decoder.getCSAuthorityFactory().createSphericalCS(epsg.toString());
-                return;
-            } catch (NoSuchAuthorityCodeException e) {
-                recoverableException(e);
-            }
-            if (isPredefined(Units.DEGREE)) {
-                coordinateSystem = (SphericalCS) 
DEFAULT.spherical().getCoordinateSystem();
+        /**
+         * Possibly sets {@link #datum} and {@link #coordinateSystem} to 
predefined objects
+         * matching the axes defined in the netCDF file.
+         */
+        @Override void setPredefinedComponents(final Decoder decoder) throws 
FactoryException {
+            if (isPredefinedCS(Units.DEGREE)) {
+                GeocentricCRS crs = DEFAULT.spherical();
                 if (isLongitudeFirst) {
-                    coordinateSystem = 
DefaultSphericalCS.castOrCopy(coordinateSystem).forConvention(AxesConvention.RIGHT_HANDED);
+                    crs = 
DefaultGeocentricCRS.castOrCopy(crs).forConvention(AxesConvention.RIGHT_HANDED);
                 }
+                referenceSystem  = crs;
+                coordinateSystem = (SphericalCS) crs.getCoordinateSystem();
+                datum            = crs.getDatum();
+            } else {
+                datum = DEFAULT.datum();
             }
         }
 
-        /** Creates the three-dimensional {@link SphericalCS} from given axes. 
*/
+        /**
+         * Creates the three-dimensional {@link SphericalCS} from given axes. 
This method is invoked only
+         * if {@link #setPredefinedComponents(Decoder)} failed to assign a CS 
or if {@link #build(Decoder)}
+         * found that the {@link #coordinateSystem} does not have compatible 
axes.
+         */
         @Override void createCS(CSFactory factory, Map<String,?> properties, 
CoordinateSystemAxis[] axes) throws FactoryException {
             coordinateSystem = factory.createSphericalCS(properties, axes[0], 
axes[1], axes[2]);
         }
 
-        /** Creates the coordinate reference system from datum and coordinate 
system computed in previous steps. */
+        /**
+         * Creates the coordinate reference system from datum and coordinate 
system computed in previous steps.
+         * This method is invoked under conditions similar to the ones of 
above {@code createCS(…)} method.
+         */
         @Override void createCRS(CRSFactory factory, Map<String,?> properties) 
throws FactoryException {
             referenceSystem = factory.createGeocentricCRS(properties, datum, 
coordinateSystem);
         }
-    };
+    }
+
+
+
 
     /**
      * Geographic CRS with (λ,φ,h) axes.
      * The height, if present, is ellipsoidal height.
      */
     private static final class Geographic extends Geodetic<EllipsoidalCS> {
-        /** Creates a new builder (invoked by lambda function). */
+        /**
+         * Creates a new builder (invoked by lambda function).
+         */
         public Geographic() {
             super((byte) 2);
         }
 
-        /** Tries to creates the coordinate system from EPSG code. */
-        private boolean tryEPSG(final Decoder decoder) throws FactoryException 
{
-            super.candidate(decoder);               // Initialize the datum.
-            final Integer epsg = epsgCandidate(Units.DEGREE);
-            if (epsg != null) try {
-                coordinateSystem = 
decoder.getCSAuthorityFactory().createEllipsoidalCS(epsg.toString());
-                return true;
-            } catch (NoSuchAuthorityCodeException e) {
-                recoverableException(e);
-            }
-            return false;
-        }
-
-        /** Possibly sets {@link #coordinateSystem} to a predefined CS 
matching the axes defined in the netCDF file. */
-        @Override void candidate(final Decoder decoder) throws 
FactoryException {
-            if (isPredefined(Units.DEGREE)) {
-                if (!is3D()) {
-                    GeographicCRS crs = 
decoder.getCRSAuthorityFactory().createGeographicCRS(String.valueOf(Constants.EPSG_UNKNOWN_CRS));
-                    coordinateSystem = crs.getCoordinateSystem();
-                    datum = crs.getDatum();
-                    referenceSystem = crs;
-                } else if (!tryEPSG(decoder)) {
-                    coordinateSystem = 
DEFAULT.geographic3D().getCoordinateSystem();
-                }
-                if (isLongitudeFirst) {
-                    coordinateSystem = 
DefaultEllipsoidalCS.castOrCopy(coordinateSystem).forConvention(AxesConvention.RIGHT_HANDED);
-                    referenceSystem  = null;
+        /**
+         * Possibly sets {@link #datum}, {@link #coordinateSystem} and {@link 
#referenceSystem}
+         * to predefined objects matching the axes defined in the netCDF file.
+         */
+        @Override void setPredefinedComponents(final Decoder decoder) throws 
FactoryException {
+            if (isPredefinedCS(Units.DEGREE)) {
+                GeographicCRS crs;
+                if (is3D()) {
+                    crs = DEFAULT.geographic3D();
+                    if (isLongitudeFirst) {
+                        crs = 
DefaultGeographicCRS.castOrCopy(crs).forConvention(AxesConvention.RIGHT_HANDED);
+                    }
+                } else if (isLongitudeFirst) {
+                    crs = DEFAULT.normalizedGeographic();
+                } else {
+                    crs = DEFAULT.geographic();
                 }
+                referenceSystem  = crs;
+                coordinateSystem = crs.getCoordinateSystem();
+                datum            = crs.getDatum();
             } else {
-                tryEPSG(decoder);
+                datum = DEFAULT.datum();
+                final Integer epsg = epsgCandidateCS(Units.DEGREE);
+                if (epsg != null) try {
+                    coordinateSystem = 
decoder.getCSAuthorityFactory().createEllipsoidalCS(epsg.toString());
+                } catch (NoSuchAuthorityCodeException e) {
+                    recoverableException(e);
+                }
             }
         }
 
-        /** Creates the two- or three-dimensional {@link EllipsoidalCS} from 
given axes. */
+        /**
+         * Creates the two- or three-dimensional {@link EllipsoidalCS} from 
given axes. This method is invoked only if
+         * {@link #setPredefinedComponents(Decoder)} failed to assign a 
coordinate system or if {@link #build(Decoder)}
+         * found that the {@link #coordinateSystem} does not have compatible 
axes.
+         */
         @Override void createCS(CSFactory factory, Map<String,?> properties, 
CoordinateSystemAxis[] axes) throws FactoryException {
             if (axes.length > 2) {
                 coordinateSystem = factory.createEllipsoidalCS(properties, 
axes[0], axes[1], axes[2]);
@@ -578,29 +609,71 @@ previous:   for (int i=components.size(); --i >= 0;) {
             }
         }
 
-        /** Creates the coordinate reference system from datum and coordinate 
system computed in previous steps. */
+        /**
+         * Creates the coordinate reference system from datum and coordinate 
system computed in previous steps.
+         * This method is invoked under conditions similar to the ones of 
above {@code createCS(…)} method.
+         */
         @Override void createCRS(CRSFactory factory, Map<String,?> properties) 
throws FactoryException {
-            referenceSystem =  factory.createGeographicCRS(properties, datum, 
coordinateSystem);
+            referenceSystem = factory.createGeographicCRS(properties, datum, 
coordinateSystem);
         }
-    };
+    }
+
+
+
 
     /**
-     * Projected CRS with (E,N,h) axes.
+     * Projected CRS with (E,N,h) axes. There is not enough information in a 
netCDF files for creating the right
+     * map projection, unless {@code "grid_mapping"} attributes are specified. 
If insufficient information, this
+     * class creates an unknown map projection based on Plate Carrée. Note 
that this map projection may be replaced
+     * by {@link GridMapping#crs} at a later stage.
      */
     private static final class Projected extends Geodetic<CartesianCS> {
-        /** Creates a new builder (invoked by lambda function). */
+        /**
+         * The spherical variant of {@link CRSBuilder#DEFAULT}.
+         * Currently based upon the GRS 1980 Authalic Sphere.
+         */
+        private static final CommonCRS SPHERICAL = CommonCRS.SPHERE;
+
+        /**
+         * Defining conversion for "Not specified (presumed Plate Carrée)". 
This conversion use spherical formulas.
+         * Consequently it should be used with {@link #SPHERICAL} instead of 
{@link CommonCRS#DEFAULT}.
+         */
+        private static final Conversion UNKNOWN_PROJECTION;
+        static {
+            final CoordinateOperationFactory factory = 
DefaultFactories.forBuildin(CoordinateOperationFactory.class);
+            try {
+                final OperationMethod method = 
factory.getOperationMethod(Equirectangular.NAME);
+                UNKNOWN_PROJECTION = factory.createDefiningConversion(
+                        properties("Not specified (presumed Plate Carrée)"),
+                        method, method.getParameters().createValue());
+            } catch (FactoryException e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+        /**
+         * Creates a new builder (invoked by lambda function).
+         */
         public Projected() {
             super((byte) 2);
         }
 
-        /** Possibly sets {@link #coordinateSystem} to a predefined CS 
matching the axes defined in the netCDF file. */
-        @Override void candidate(final Decoder decoder) {
-            if (isPredefined(Units.METRE)) {
-                coordinateSystem = 
DEFAULT.universal(0,0).getCoordinateSystem();
+        /**
+         * Possibly sets {@link #datum}, {@link #coordinateSystem} and {@link 
#referenceSystem}
+         * to predefined objects matching the axes defined in the netCDF file.
+         */
+        @Override void setPredefinedComponents(final Decoder decoder) throws 
FactoryException {
+            datum = SPHERICAL.datum();
+            if (isPredefinedCS(Units.METRE)) {
+                coordinateSystem = 
CommonCRS.WGS84.universal(0,0).getCoordinateSystem();
             }
         }
 
-        /** Creates the two- or three-dimensional {@link CartesianCS} from 
given axes. */
+        /**
+         * Creates the two- or three-dimensional {@link CartesianCS} from 
given axes. This method is invoked only if
+         * {@link #setPredefinedComponents(Decoder)} failed to assign a 
coordinate system or if {@link #build(Decoder)}
+         * found that the {@link #coordinateSystem} does not have compatible 
axes.
+         */
         @Override void createCS(CSFactory factory, Map<String,?> properties, 
CoordinateSystemAxis[] axes) throws FactoryException {
             if (axes.length > 2) {
                 coordinateSystem = factory.createCartesianCS(properties, 
axes[0], axes[1], axes[2]);
@@ -609,24 +682,38 @@ previous:   for (int i=components.size(); --i >= 0;) {
             }
         }
 
-        /** Creates the coordinate reference system from datum and coordinate 
system computed in previous steps. */
+        /**
+         * Creates the coordinate reference system from datum and coordinate 
system computed in previous steps.
+         * The datum for this method is based upon the GRS 1980 Authalic 
Sphere.
+         */
         @Override void createCRS(CRSFactory factory, Map<String,?> properties) 
throws FactoryException {
-            throw new UnsupportedOperationException();  // TODO
+            GeographicCRS baseCRS = (coordinateSystem.getDimension() >= 3) ? 
SPHERICAL.geographic3D() : SPHERICAL.geographic();
+            if (!baseCRS.getDatum().equals(datum)) {
+                baseCRS = factory.createGeographicCRS(properties, datum, 
baseCRS.getCoordinateSystem());
+            }
+            referenceSystem = factory.createProjectedCRS(properties, baseCRS, 
UNKNOWN_PROJECTION, coordinateSystem);
         }
-    };
+    }
+
+
+
 
     /**
      * Vertical CRS with (H) or (D) axis.
      * Used for mean sea level (not for ellipsoidal height).
      */
     private static final class Vertical extends CRSBuilder<VerticalDatum, 
VerticalCS> {
-        /** Creates a new builder (invoked by lambda function). */
+        /**
+         * Creates a new builder (invoked by lambda function).
+         */
         public Vertical() {
             super(VerticalDatum.class, "Mean Sea Level", (byte) 1, (byte) 1, 
(byte) 1);
         }
 
-        /** Possibly sets {@link #coordinateSystem} to a predefined CS 
matching the axes defined in the netCDF file. */
-        @Override void candidate(final Decoder decoder) {
+        /**
+         * Possibly sets {@link #coordinateSystem} to a predefined CS matching 
the axes defined in the netCDF file.
+         */
+        @Override void setPredefinedComponents(final Decoder decoder) {
             final Axis axis = getFirstAxis();
             final Unit<?> unit = axis.getUnit();
             final CommonCRS.Vertical predefined;
@@ -644,34 +731,49 @@ previous:   for (int i=components.size(); --i >= 0;) {
             coordinateSystem = predefined.crs().getCoordinateSystem();
         }
 
-        /** Creates a {@link VerticalDatum} for <cite>"Unknown datum based on 
Mean Sea Level"</cite>. */
+        /**
+         * Creates a {@link VerticalDatum} for <cite>"Unknown datum based on 
Mean Sea Level"</cite>.
+         */
         @Override void createDatum(DatumFactory factory, Map<String,?> 
properties) throws FactoryException {
             datum = factory.createVerticalDatum(properties, 
VerticalDatumType.GEOIDAL);
         }
 
-        /** Creates the one-dimensional {@link VerticalCS} from given axes. */
+        /**
+         * Creates the one-dimensional {@link VerticalCS} from given axes. 
This method is invoked
+         * only if {@link #setPredefinedComponents(Decoder)} failed to assign 
a coordinate system
+         * or if {@link #build(Decoder)} found that the axis or direction are 
not compatible.
+         */
         @Override void createCS(CSFactory factory, Map<String,?> properties, 
CoordinateSystemAxis[] axes) throws FactoryException {
             coordinateSystem = factory.createVerticalCS(properties, axes[0]);
         }
 
-        /** Creates the coordinate reference system from datum and coordinate 
system computed in previous steps. */
+        /**
+         * Creates the coordinate reference system from datum and coordinate 
system computed in previous steps.
+         */
         @Override void createCRS(CRSFactory factory, Map<String,?> properties) 
throws FactoryException {
             referenceSystem =  factory.createVerticalCRS(properties, datum, 
coordinateSystem);
         }
-    };
+    }
+
+
+
 
     /**
      * Temporal CRS with (t) axis. Its datum need to be built
      * in a special way since it contains the time origin.
      */
     private static final class Temporal extends CRSBuilder<TemporalDatum, 
TimeCS> {
-        /** Creates a new builder (invoked by lambda function). */
+        /**
+         * Creates a new builder (invoked by lambda function).
+         */
         public Temporal() {
             super(TemporalDatum.class, "", (byte) 2, (byte) 1, (byte) 1);
         }
 
-        /** Possibly sets {@link #coordinateSystem} to a predefined CS 
matching the axes defined in the netCDF file. */
-        @Override void candidate(final Decoder decoder) {
+        /**
+         * Possibly sets {@link #coordinateSystem} to a predefined CS matching 
the axes defined in the netCDF file.
+         */
+        @Override void setPredefinedComponents(final Decoder decoder) {
             final Axis axis = getFirstAxis();
             final Unit<?> unit = axis.getUnit();
             final CommonCRS.Temporal predefined;
@@ -687,7 +789,9 @@ previous:   for (int i=components.size(); --i >= 0;) {
             coordinateSystem = predefined.crs().getCoordinateSystem();
         }
 
-        /** Creates a {@link VerticalDatum} for <cite>"Unknown datum based on 
…"</cite>. */
+        /**
+         * Creates a {@link TemporalDatum} for <cite>"Unknown datum based on 
…"</cite>.
+         */
         @Override void createDatum(DatumFactory factory, Map<String,?> 
properties) throws FactoryException {
             final Axis axis = getFirstAxis();
             axis.getUnit();                                     // Force epoch 
parsing if not already done.
@@ -701,37 +805,54 @@ previous:   for (int i=components.size(); --i >= 0;) {
             }
         }
 
-        /** Creates the one-dimensional {@link TimeCS} from given axes. */
+        /**
+         * Creates the one-dimensional {@link TimeCS} from given axes. This 
method is invoked only
+         * if {@link #setPredefinedComponents(Decoder)} failed to assign a 
coordinate system or if
+         * {@link #build(Decoder)} found that the axis or direction are not 
compatible.
+         */
         @Override void createCS(CSFactory factory, Map<String,?> properties, 
CoordinateSystemAxis[] axes) throws FactoryException {
             coordinateSystem = factory.createTimeCS(properties, axes[0]);
         }
 
-        /** Creates the coordinate reference system from datum and coordinate 
system computed in previous steps. */
+        /**
+         * Creates the coordinate reference system from datum and coordinate 
system computed in previous steps.
+         */
         @Override void createCRS(CRSFactory factory, Map<String,?> properties) 
throws FactoryException {
             properties = 
properties(getFirstAxis().coordinates.getUnitsString());
             referenceSystem =  factory.createTemporalCRS(properties, datum, 
coordinateSystem);
         }
-    };
+    }
+
+
+
 
     /**
      * Unknown CRS with (x,y,z) axes.
      */
     private static final class Engineering extends 
CRSBuilder<EngineeringDatum, AffineCS> {
-        /** Creates a new builder (invoked by lambda function). */
+        /**
+         * Creates a new builder (invoked by lambda function).
+         */
         public Engineering() {
             super(EngineeringDatum.class, "affine coordinate system", (byte) 
3, (byte) 2, (byte) 3);
         }
 
-        /** No-op since we have no predefined engineering CRS. */
-        @Override void candidate(final Decoder decoder) {
+        /**
+         * No-op since we have no predefined engineering CRS.
+         */
+        @Override void setPredefinedComponents(final Decoder decoder) {
         }
 
-        /** Creates a {@link VerticalDatum} for <cite>"Unknown datum based on 
affine coordinate system"</cite>. */
+        /**
+         * Creates a {@link VerticalDatum} for <cite>"Unknown datum based on 
affine coordinate system"</cite>.
+         */
         @Override void createDatum(DatumFactory factory, Map<String,?> 
properties) throws FactoryException {
             datum = factory.createEngineeringDatum(properties);
         }
 
-        /** Creates two- or three-dimensional {@link AffineCS} from given 
axes. */
+        /**
+         * Creates two- or three-dimensional {@link AffineCS} from given axes.
+         */
         @Override void createCS(CSFactory factory, Map<String,?> properties, 
CoordinateSystemAxis[] axes) throws FactoryException {
             if (axes.length > 2) {
                 coordinateSystem = factory.createAffineCS(properties, axes[0], 
axes[1], axes[2]);
@@ -740,11 +861,13 @@ previous:   for (int i=components.size(); --i >= 0;) {
             }
         }
 
-        /** Creates the coordinate reference system from datum and coordinate 
system computed in previous steps. */
+        /**
+         * Creates the coordinate reference system from datum and coordinate 
system computed in previous steps.
+         */
         @Override void createCRS(CRSFactory factory, Map<String,?> properties) 
throws FactoryException {
             referenceSystem =  factory.createEngineeringCRS(properties, datum, 
coordinateSystem);
         }
-    };
+    }
 
     /**
      * Maximal {@link #datumIndex} value +1. The maximal value can be seen in 
the call to {@code super(…)} constructor
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 3084826..1732715 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
@@ -96,7 +96,7 @@ public abstract class Decoder extends 
ReferencingFactoryContainer implements Clo
 
     /**
      * The geodetic datum, created when first needed. The datum are generally 
not specified in netCDF files.
-     * To make that clearer, we will build datum with names like "Unknown 
datum presumably based on WGS 84".
+     * To make that clearer, we will build datum with names like "Unknown 
datum presumably based on GRS 1980".
      *
      * @see CRSBuilder#build(Decoder)
      */
@@ -332,8 +332,8 @@ public abstract class Decoder extends 
ReferencingFactoryContainer implements Clo
 
     /**
      * Returns for information purpose only the Coordinate Reference Systems 
present in this file.
-     * The CRS returned by this method may not be exactly the CRS to be used 
by variables.
-     * This method is provided for metadata purposes.
+     * The CRS returned by this method may not be exactly the same than the 
ones used by variables.
+     * For example, axis order is not guaranteed. This method is provided for 
metadata purposes.
      *
      * @return coordinate reference systems present in this file.
      * @throws IOException if an I/O operation was necessary but failed.
@@ -347,6 +347,11 @@ public abstract class Decoder extends 
ReferencingFactoryContainer implements Clo
                 addIfNotPresent(list, m.crs);
             }
         }
+        /*
+         * Add the CRS computed by grids only if we did not found any grid 
mapping information.
+         * This is because grid mapping information override the CRS inferred 
by Grid from axes.
+         * Consequently if such information is present, grid CRS may be 
inaccurate.
+         */
         if (list.isEmpty()) {
             for (final Grid grid : getGrids()) {
                 addIfNotPresent(list, grid.getCoordinateReferenceSystem(this));
@@ -357,7 +362,9 @@ public abstract class Decoder extends 
ReferencingFactoryContainer implements Clo
 
     /**
      * Adds the given coordinate reference system to the given list, provided 
that an equivalent CRS
-     * (ignoring axes) is not already present.
+     * (ignoring axes) is not already present. We ignore axes because the same 
CRS may be repeated
+     * with different axis order if values in the localization grid do not 
vary at the same speed in
+     * the same directions.
      */
     private static void addIfNotPresent(final List<CoordinateReferenceSystem> 
list, final CoordinateReferenceSystem crs) {
         if (crs != null) {
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
index 7cc2401..461cdf8 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
@@ -260,8 +260,15 @@ public abstract class Grid extends NamedElement {
     protected abstract boolean containsAllNamedAxes(String[] axisNames);
 
     /**
-     * Returns the coordinate reference system, or {@code null} if none.
-     * This method creates the CRS the first time it is invoked and cache the 
result.
+     * Returns the coordinate reference system inferred from axes, or {@code 
null} if none.
+     * This method creates the CRS the first time it is invoked and caches the 
result,
+     * for allowing {@link Decoder#getReferenceSystemInfo()} to be cheaper.
+     *
+     * <p>This CRS is inferred only from analysis of grid axes. It does not 
take in account {@link GridMapping} information.
+     * This CRS may be overwritten by another CRS parsed from Well Known Text 
or other attributes. This overwriting is done
+     * by {@link Variable#getGridGeometry()}. But even if the CRS is going to 
be overwritten, we still need to create it in
+     * this method because this CRS will be used for adjusting axis order or 
for completion if grid mapping does not include
+     * information for all dimensions.</p>
      *
      * @param   decoder  the decoder for which CRS are constructed.
      * @return  the CRS for this grid geometry, or {@code null}.
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridMapping.java
 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridMapping.java
index 3a0b235..09de0b2 100644
--- 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridMapping.java
+++ 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridMapping.java
@@ -58,7 +58,10 @@ import ucar.nc2.constants.CF;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.0
- * @since   1.0
+ *
+ * @see <a 
href="https://www.unidata.ucar.edu/software/thredds/current/netcdf-java/reference/StandardCoordinateTransforms.html";>UCAR
 projections</a>
+ *
+ * @since 1.0
  * @module
  */
 final class GridMapping {
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
index 46bd7ca..5f02906 100644
--- 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
+++ 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
@@ -54,18 +54,24 @@ final class GridInfo extends Grid {
      * Mapping from values of the {@code "_CoordinateAxisType"} attribute or 
axis name to the abbreviation.
      * Keys are lower cases and values are controlled vocabulary documented in 
{@link Axis#abbreviation}.
      *
+     * <div class="note">"GeoX" and "GeoY" stands for projected coordinates, 
not geocentric coordinates
+     * (<a 
href="https://www.unidata.ucar.edu/software/thredds/current/netcdf-java/reference/CoordinateAttributes.html#AxisTypes";>source</a>).
+     * </div>
+     *
      * @see #getAxisType(String)
      */
     private static final Map<String,Character> AXIS_TYPES = new HashMap<>(26);
     static {
         addAxisTypes('λ', "longitude", "lon", "long");
         addAxisTypes('φ', "latitude",  "lat");
-        addAxisTypes('H', "pressure", "height", "altitude", "elevation", 
"elev");
+        addAxisTypes('H', "pressure", "height", "altitude", "elevation", 
"elev", "geoz");
         addAxisTypes('D', "depth");
+        addAxisTypes('E', "geox");
+        addAxisTypes('N', "geoy");
         addAxisTypes('t', "t", "time", "runtime");
-        addAxisTypes('x', "x", "geox");
-        addAxisTypes('y', "y", "geoy");
-        addAxisTypes('z', "z", "geoz");
+        addAxisTypes('x', "x");
+        addAxisTypes('y', "y");
+        addAxisTypes('z', "z");
     }
 
     /**
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/CSBuilderFallback.java
 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/CSBuilderFallback.java
index 581304e..fb88041 100644
--- 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/CSBuilderFallback.java
+++ 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/CSBuilderFallback.java
@@ -45,7 +45,10 @@ import org.apache.sis.util.CharSequences;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.0
- * @since   1.0
+ *
+ * @see <a 
href="https://www.unidata.ucar.edu/software/thredds/current/netcdf-java/tutorial/CoordSysBuilder.html";>UCAR
 tutorial</a>
+ *
+ * @since 1.0
  * @module
  */
 final class CSBuilderFallback extends CoordSysBuilder {
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
index 767d13e..37d56e4 100644
--- 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
+++ 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
@@ -241,9 +241,9 @@ next:       for (final String name : axisNames) {
             char abbreviation = 0;
             final AxisType type = axis.getAxisType();
             if (type != null) switch (type) {
-                case GeoX:            abbreviation = 'x'; break;
-                case GeoY:            abbreviation = 'y'; break;
-                case GeoZ:            abbreviation = 'z'; break;
+                case GeoX:            abbreviation = netcdfCS.isGeoXY() ? 'E' 
: 'x'; break;
+                case GeoY:            abbreviation = netcdfCS.isGeoXY() ? 'N' 
: 'y'; break;
+                case GeoZ:            abbreviation = netcdfCS.isGeoXY() ? 'H' 
: 'z'; break;
                 case Lon:             abbreviation = 'λ'; break;
                 case Lat:             abbreviation = 'φ'; break;
                 case Pressure:        // Fallthrough: consider as Height
diff --git 
a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/GridTest.java 
b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/GridTest.java
index 27d7e8a..c754a42 100644
--- 
a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/GridTest.java
+++ 
b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/GridTest.java
@@ -110,8 +110,8 @@ public strictfp class GridTest extends TestCase {
         final Axis z = axes[2];
         final Axis t = axes[3];
 
-        assertEquals('x', x.abbreviation);
-        assertEquals('y', y.abbreviation);
+        assertEquals('E', x.abbreviation);
+        assertEquals('N', y.abbreviation);
         assertEquals('H', z.abbreviation);
         assertEquals('t', t.abbreviation);
 

Reply via email to