You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2024/04/22 13:58:06 UTC

(sis) branch main updated (1483fcf71b -> 428919e44a)

This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/sis.git


    from 1483fcf71b Merge branch 'geoapi-3.1'
     add 918e07bcbc Avoid usage of the deprecated `AxisDirection.OTHER` code list value.
     add adbe2180c3 ISO 19111 upgrade: VerticalDatumType removed, replaced by RealizationMethod.
     add 43241772b6 Merge branch 'geoapi-4.0' into geoapi-3.1: reduce usage of deprecated `AxisDirection.OTHER` and `VerticalDatumType`.
     add 3e79841b4c Post-merge adjustments in `VerticalDatumTypes`.
     add ff6542a3f7 Merge 'geoapi-4.0' into geoapi-3.1.
     new 428919e44a Merge branch 'geoapi-3.1': reduce usage of `AxisDirection.OTHER` and `VerticalDatumType`.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/sis/coverage/grid/GridExtentCRS.java    |   6 +-
 .../apache/sis/metadata/iso/extent/Extents.java    |  62 ++++-----
 .../sis/metadata/iso/extent/ExtentsTest.java       |   9 +-
 .../apache/sis/io/wkt/GeodeticObjectParser.java    |  22 +--
 .../main/org/apache/sis/referencing/CRS.java       |   4 +-
 .../main/org/apache/sis/referencing/CommonCRS.java |  24 ++--
 .../org/apache/sis/referencing/cs/AbstractCS.java  |   4 +-
 .../main/org/apache/sis/referencing/cs/Codes.java  |  24 ++--
 .../org/apache/sis/referencing/cs/Normalizer.java  |   4 +-
 .../referencing/datum/DefaultVerticalDatum.java    |  14 +-
 .../referencing/factory/sql/EPSGCodeFinder.java    |   7 -
 .../referencing/factory/sql/EPSGDataAccess.java    |  14 +-
 .../sis/referencing/factory/sql/TableInfo.java     |   1 -
 .../apache/sis/referencing/internal/Legacy.java    |   8 +-
 .../referencing/internal/VerticalDatumTypes.java   | 147 ++++++++++++---------
 .../operation/CoordinateOperationRegistry.java     |   2 +-
 .../sis/referencing/operation/matrix/Matrices.java |   2 +-
 .../sis/referencing/privy/AxisDirections.java      |  27 ++--
 .../privy/EllipsoidalHeightCombiner.java           |   3 +-
 .../referencing/privy/ReferencingUtilities.java    |   7 +-
 .../org/apache/sis/io/wkt/TransliteratorTest.java  |  13 +-
 .../org/apache/sis/referencing/CommonCRSTest.java  |  20 +--
 .../apache/sis/referencing/cs/HardCodedAxes.java   |   4 +-
 .../datum/DefaultVerticalDatumTest.java            |   4 +-
 .../sis/referencing/datum/HardCodedDatum.java      |   2 +-
 .../referencing/datum/VerticalDatum (GML 3.1).xml  |   2 +-
 .../internal/VerticalDatumTypesTest.java           |  32 +++--
 .../sis/referencing/privy/AxisDirectionsTest.java  |  21 +--
 .../org/apache/sis/storage/netcdf/base/Axis.java   |   2 +-
 29 files changed, 251 insertions(+), 240 deletions(-)


(sis) 01/01: Merge branch 'geoapi-3.1': reduce usage of `AxisDirection.OTHER` and `VerticalDatumType`.

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 428919e44a91cd9c40080b2d1630132d0ad322d4
Merge: 1483fcf71b ff6542a3f7
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Apr 22 15:55:59 2024 +0200

    Merge branch 'geoapi-3.1': reduce usage of `AxisDirection.OTHER` and `VerticalDatumType`.

 .../apache/sis/coverage/grid/GridExtentCRS.java    |   6 +-
 .../apache/sis/metadata/iso/extent/Extents.java    |  62 ++++-----
 .../sis/metadata/iso/extent/ExtentsTest.java       |   9 +-
 .../apache/sis/io/wkt/GeodeticObjectParser.java    |  22 +--
 .../main/org/apache/sis/referencing/CRS.java       |   4 +-
 .../main/org/apache/sis/referencing/CommonCRS.java |  24 ++--
 .../org/apache/sis/referencing/cs/AbstractCS.java  |   4 +-
 .../main/org/apache/sis/referencing/cs/Codes.java  |  24 ++--
 .../org/apache/sis/referencing/cs/Normalizer.java  |   4 +-
 .../referencing/datum/DefaultVerticalDatum.java    |  14 +-
 .../referencing/factory/sql/EPSGCodeFinder.java    |   7 -
 .../referencing/factory/sql/EPSGDataAccess.java    |  14 +-
 .../sis/referencing/factory/sql/TableInfo.java     |   1 -
 .../apache/sis/referencing/internal/Legacy.java    |   8 +-
 .../referencing/internal/VerticalDatumTypes.java   | 147 ++++++++++++---------
 .../operation/CoordinateOperationRegistry.java     |   2 +-
 .../sis/referencing/operation/matrix/Matrices.java |   2 +-
 .../sis/referencing/privy/AxisDirections.java      |  27 ++--
 .../privy/EllipsoidalHeightCombiner.java           |   3 +-
 .../referencing/privy/ReferencingUtilities.java    |   7 +-
 .../org/apache/sis/io/wkt/TransliteratorTest.java  |  13 +-
 .../org/apache/sis/referencing/CommonCRSTest.java  |  20 +--
 .../apache/sis/referencing/cs/HardCodedAxes.java   |   4 +-
 .../datum/DefaultVerticalDatumTest.java            |   4 +-
 .../sis/referencing/datum/HardCodedDatum.java      |   2 +-
 .../referencing/datum/VerticalDatum (GML 3.1).xml  |   2 +-
 .../internal/VerticalDatumTypesTest.java           |  32 +++--
 .../sis/referencing/privy/AxisDirectionsTest.java  |  21 +--
 .../org/apache/sis/storage/netcdf/base/Axis.java   |   2 +-
 29 files changed, 251 insertions(+), 240 deletions(-)

diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtentCRS.java
index ae28430fdb,95020e45e4..8d078fddfd
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtentCRS.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtentCRS.java
@@@ -296,7 -299,7 +296,7 @@@ final class GridExtentCRS 
                      abbreviation = "t"; direction = AxisDirection.FUTURE; hasTime = true;
                  } else {
                      abbreviation = abbreviation(target);
-                     direction = AxisDirection.OTHER;
 -                    direction = AxisDirection.UNSPECIFIED;
++                    direction = AxisDirections.UNSPECIFIED;
                      hasOther = true;
                  }
                  /*
@@@ -308,7 -311,7 +308,7 @@@
                      final CoordinateSystemAxis previous = axes[k];
                      if (previous != null) {
                          if (direction.equals(AxisDirections.absolute(previous.getDirection()))) {
-                             direction = AxisDirection.OTHER;
 -                            direction = AxisDirection.UNSPECIFIED;
++                            direction = AxisDirections.UNSPECIFIED;
                              hasOther = true;
                          }
                          if (abbreviation.equals(previous.getAbbreviation())) {
@@@ -331,7 -334,7 +331,7 @@@
              if (axes[j] == null) {
                  final String name = Vocabulary.forLocale(locale).getString(Vocabulary.Keys.Dimension_1, j);
                  final String abbreviation = abbreviation(j);
-                 axes[j] = axis(csFactory, name, abbreviation, AxisDirection.OTHER);
 -                axes[j] = axis(csFactory, name, abbreviation, AxisDirection.UNSPECIFIED);
++                axes[j] = axis(csFactory, name, abbreviation, AxisDirections.UNSPECIFIED);
              }
          }
          /*
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/extent/Extents.java
index 360b50cc81,2fda0bf211..b4ec7fc882
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/extent/Extents.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/extent/Extents.java
@@@ -371,16 -362,15 +365,15 @@@ public final class Extents extends Stat
       * performs a choice based on the vertical datum and the unit of measurement:
       *
       * <ul class="verbose">
-      *   <li><p><b>Choice based on vertical datum</b><br>
+      *   <li><p><b>Choice based on realization method</b><br>
 -     *   Only the extents associated (indirectly, through their CRS) to the same non-null {@link RealizationMethod}
 +     *   Only the extents associated (indirectly, through their CRS) to the same non-null {@link VerticalDatumType}
-      *   will be taken in account. If all datum types are null, then this method conservatively uses only the first
-      *   vertical extent. Otherwise the datum type used for filtering the vertical extents is:</p>
+      *   will be taken in account. If all realization methods are absent, then this method conservatively uses only
+      *   the first vertical extent. Otherwise the realization method used for filtering the vertical extents is:</p>
       *
       *   <ul>
-      *     <li>{@link VerticalDatumType#GEOIDAL} or {@link VerticalDatumType#DEPTH DEPTH} if at least one extent
-      *         uses those datum types. For this method, {@code DEPTH} is considered as equivalent to {@code GEOIDAL}
-      *         except for the axis direction.</li>
-      *     <li>Otherwise, the first non-null datum type found in iteration order.</li>
 -     *     <li>{@link RealizationMethod#GEOID} if at least one extent uses this realization method.</li>
 -     *     <li>Otherwise, {@link RealizationMethod#TIDAL} if at least one extent uses this realization method.</li>
++     *     <li>{@link VerticalDatumType#GEOIDAL} if at least one extent uses this realization method.</li>
++     *     <li>Otherwise, {@link VerticalDatumType#DEPTH} if at least one extent uses this realization method.</li>
+      *     <li>Otherwise, the first non-null realization type found in iteration order.</li>
       *   </ul>
       *
       *   <div class="note"><b>Rational:</b> like {@linkplain #getGeographicBoundingBox(Extent) geographic bounding box},
@@@ -420,23 -409,21 +412,20 @@@
       * @since 0.4
       */
      @OptionalCandidate
 -    @SuppressWarnings("deprecation")
      public static MeasurementRange<Double> getVerticalRange(final Extent extent) {
          MeasurementRange<Double> range = null;
-         VerticalDatumType selectedType = null;
 -        RealizationMethod selectedMethod = null;
++        VerticalDatumType selectedMethod = null;
          if (extent != null) {
              for (final VerticalExtent element : nonNull(extent.getVerticalElements())) {
                  double min = element.getMinimumValue();
                  double max = element.getMaximumValue();
                  final VerticalCRS crs = element.getVerticalCRS();
-                 VerticalDatumType type = null;
 -                RealizationMethod method = null;
++                VerticalDatumType method = null;
                  Unit<?> unit = null;
                  if (crs != null) {
                      final VerticalDatum datum = crs.getDatum();
                      if (datum != null) {
-                         type = datum.getVerticalDatumType();
-                         if (type == VerticalDatumType.DEPTH) {
-                             type = VerticalDatumType.GEOIDAL;
-                         }
 -                        method = datum.getRealizationMethod().orElse(method);
++                        method = datum.getVerticalDatumType();
                      }
                      final CoordinateSystemAxis axis = crs.getCoordinateSystem().getAxis(0);
                      unit = axis.getUnit();
@@@ -456,14 -443,17 +445,17 @@@
                      }
                      /*
                       * If the new range is not measured relative to the same kind of surface than the previous range,
-                      * then we do not know how to combine those ranges. Do nothing, unless the new range is a Mean Sea
-                      * Level Height in which case we forget all previous ranges and use the new one instead.
+                      * then we do not know how to combine those ranges. Do nothing, unless the new range is a geoidal
+                      * height in which case we forget all previous ranges and use the new one instead.
                       */
-                     if (!type.equals(selectedType)) {
-                         if (!type.equals(VerticalDatumType.GEOIDAL)) {
+                     if (method != selectedMethod) {
 -                        if (selectedMethod == RealizationMethod.GEOID ||
 -                                   (method != RealizationMethod.GEOID &&
 -                                    method != RealizationMethod.TIDAL))
++                        if (selectedMethod == VerticalDatumType.GEOIDAL ||
++                                   (method != VerticalDatumType.GEOIDAL &&
++                                    method != VerticalDatumType.DEPTH))
+                         {
                              continue;
                          }
-                     } else if (selectedType != null) {
+                     } else if (selectedMethod != null) {
                          /*
                           * If previous range did not specify any unit, then unconditionally replace it by
                           * the new range since it provides more information. If both ranges specify units,
diff --cc endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/extent/ExtentsTest.java
index 71f0b919df,848979dc2f..f562dd5a60
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/extent/ExtentsTest.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/extent/ExtentsTest.java
@@@ -31,6 -31,9 +31,9 @@@ import org.apache.sis.measure.Units
  import org.apache.sis.measure.MeasurementRange;
  import static org.apache.sis.metadata.privy.ReferencingServices.NAUTICAL_MILE;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.referencing.datum.RealizationMethod;
++// Specific to the main branch:
++import org.opengis.referencing.datum.VerticalDatumType;
+ 
  // Test dependencies
  import org.junit.jupiter.api.Test;
  import static org.junit.jupiter.api.Assertions.*;
@@@ -82,7 -85,7 +85,7 @@@ public final class ExtentsTest extends 
          Unit<?> unit = null;
          for (final DefaultVerticalExtent e : extents) {
              unit = e.getVerticalCRS().getCoordinateSystem().getAxis(0).getUnit();
-             if (Units.isLinear(unit)) break;
 -            if (e.getVerticalCRS().getDatum().getRealizationMethod().orElse(null) == RealizationMethod.GEOID) break;
++            if (e.getVerticalCRS().getDatum().getVerticalDatumType() == VerticalDatumType.GEOIDAL) break;
          }
          final UnitConverter c = unit.getConverterToAny(Units.METRE);
          /*
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
index 09d1ba92d7,643d8ceb56..af7f934d23
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
@@@ -875,7 -865,7 +875,7 @@@ class GeodeticObjectParser extends Math
                      if (defaultUnit == null) {
                          throw parent.missingComponent(WKTKeywords.ParametricUnit);
                      }
-                     direction = AxisDirection.OTHER;
 -                    direction = AxisDirection.UNSPECIFIED;
++                    direction = AxisDirections.UNSPECIFIED;
                      nz = "Parametric";
                      z = "p";
                      break;
@@@ -1461,12 -1446,13 +1461,12 @@@
              return null;
          }
          final String name = element.pullString("name");
-         VerticalDatumType type = null;
 -        @SuppressWarnings("deprecation")
 -        RealizationMethod method = null;
++        VerticalDatumType method = null;
          if (isWKT1) {
-             type = VerticalDatumTypes.fromLegacy(element.pullInteger("datum"));
+             method = VerticalDatumTypes.fromLegacy(element.pullInteger("datum"));
          }
-         if (type == null) {
-             type = VerticalDatumTypes.guess(name, null, null);
+         if (method == null) {
+             method = VerticalDatumTypes.guess(name, null, null);
          }
          final DatumFactory datumFactory = factories.getDatumFactory();
          try {
@@@ -2258,7 -2247,7 +2258,7 @@@
                  buffer.append(number);
                  axes[i] = csFactory.createCoordinateSystemAxis(
                          singletonMap(CoordinateSystemAxis.NAME_KEY, buffer.toString()),
-                         number, AxisDirection.OTHER, Units.UNITY);
 -                        number, AxisDirection.UNSPECIFIED, Units.UNITY);
++                        number, AxisDirections.UNSPECIFIED, Units.UNITY);
              }
              final Map<String,Object> properties = parseMetadataAndClose(element, name, baseCRS);
              final Map<String,Object> axisName = singletonMap(CoordinateSystem.NAME_KEY, AxisDirections.appendTo(new StringBuilder("CS"), axes));
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java
index 8873622ad1,7c012a1e8e..fc312dda24
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java
@@@ -945,8 -992,8 +945,8 @@@ public final class CRS extends Static 
       * of the domain of validity of all components.
       *
       * <h4>Ellipsoidal height</h4>
-      * If a two-dimensional geographic or projected CRS if followed or preceded by a vertical CRS with ellipsoidal
-      * {@linkplain org.apache.sis.referencing.datum.DefaultVerticalDatum#getVerticalDatumType() datum type}, then
+      * If a two-dimensional geographic or projected CRS is followed or preceded by a vertical CRS with ellipsoidal
 -     * {@linkplain org.apache.sis.referencing.datum.DefaultVerticalDatum#getRealizationMethod() realization method},
++     * {@linkplain org.apache.sis.referencing.datum.DefaultVerticalDatum#getVerticalDatumType() datum type},
       * this method combines them in a single three-dimensional geographic or projected CRS.  Note that standalone
       * ellipsoidal heights are not allowed according ISO 19111. But if such situation is nevertheless found, then
       * the action described here fixes the issue. This is the reverse of <code>{@linkplain #getVerticalComponent
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CommonCRS.java
index 622d9c2836,8abad5348b..3ff72a19f8
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CommonCRS.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CommonCRS.java
@@@ -48,9 -47,9 +48,9 @@@ import org.opengis.referencing.datum.Da
  import org.opengis.referencing.datum.Ellipsoid;
  import org.opengis.referencing.datum.GeodeticDatum;
  import org.opengis.referencing.datum.PrimeMeridian;
+ import org.opengis.referencing.datum.TemporalDatum;
  import org.opengis.referencing.datum.VerticalDatum;
 -import org.opengis.referencing.datum.RealizationMethod;
 +import org.opengis.referencing.datum.VerticalDatumType;
- import org.opengis.referencing.datum.TemporalDatum;
  import org.opengis.referencing.datum.EngineeringDatum;
  import org.opengis.metadata.extent.GeographicBoundingBox;
  import static org.opengis.referencing.IdentifiedObject.NAME_KEY;
@@@ -1259,8 -1247,8 +1256,6 @@@ public enum CommonCRS 
           *   <tr><th>Direction:</th>            <td>{@link AxisDirection#UP}</td></tr>
           *   <tr><th>Unit:</th>                 <td>{@link Units#METRE}</td></tr>
           * </table></blockquote>
--         *
-          * @see VerticalDatumType#GEOIDAL
 -         * @see RealizationMethod#TIDAL
           */
          MEAN_SEA_LEVEL(true, (short) 5714, (short) 5100),
  
@@@ -1274,8 -1262,8 +1269,6 @@@
           *   <tr><th>Direction:</th>            <td>{@link AxisDirection#DOWN}</td></tr>
           *   <tr><th>Unit:</th>                 <td>{@link Units#METRE}</td></tr>
           * </table></blockquote>
--         *
-          * @see VerticalDatumType#GEOIDAL
 -         * @see RealizationMethod#TIDAL
           */
          DEPTH(true, (short) 5715, (short) 5100),
  
@@@ -1343,9 -1332,8 +1337,8 @@@
           * Creates a new enumeration value of the given name.
           *
           * <h4>API design note</h4>
-          * This constructor does not expect {@link VerticalDatumType} constant in order to avoid too
-          * early class initialization. In particular, we do not want early dependency to the SIS-specific
-          * {@code VerticalDatumTypes.ELLIPSOIDAL} constant.
 -         * This constructor does not expect {@link RealizationMethod} constant in order to avoid
++         * This constructor does not expect {@link VerticalDatumType} constant in order to avoid
+          * the creation of non-standard code list value before they are actually needed.
           */
          private Vertical(final boolean isEPSG, final short crs, final short datum) {
              this.isEPSG = isEPSG;
@@@ -1481,7 -1467,16 +1472,12 @@@
                          if (isEPSG) {
                              object = StandardDefinitions.createVerticalDatum(datum);
                          } else {
+                             /*
+                              * All cases where the first constructor argument is `false`, currently BAROMETRIC and
+                              * ELLIPSOIDAL. The way to construct the ellipsoidal pseudo-method shall be equivalent
+                              * to a call to `VerticalDatumTypes.ellipsoidal()`.
+                              */
 -                            RealizationMethod method = null;
 -                            if (this != OTHER_SURFACE) {
 -                                method = RealizationMethod.valueOf(name());
 -                            }
 -                            object = new DefaultVerticalDatum(properties(datum), method);
 +                            object = new DefaultVerticalDatum(properties(datum), VerticalDatumType.valueOf(name()));
                          }
                          cached = object;
                      }
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AbstractCS.java
index 4a03d9f291,1625e20723..a43be5bf76
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AbstractCS.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AbstractCS.java
@@@ -228,7 -229,7 +228,7 @@@ public class AbstractCS extends Abstrac
               * more than one time axis. Such case happen in meteorological models.
               */
              final AxisDirection dir = AxisDirections.absolute(direction);
-             if (!dir.equals(AxisDirection.OTHER)) {
 -            if (dir != AxisDirection.UNSPECIFIED && dir != AxisDirection.OTHER) {
++            if (dir != AxisDirections.UNSPECIFIED && dir != AxisDirection.OTHER) {
                  for (int j=i; --j>=0;) {
                      final AxisDirection other = axes[j].getDirection();
                      final AxisDirection abs = AxisDirections.absolute(other);
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
index 4f911e51e5,776b9d95dd..6b29262c9d
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
@@@ -198,26 -223,15 +198,25 @@@ public class DefaultVerticalDatum exten
      }
  
      /**
 -     * Returns the method through which this vertical reference frame is realized.
 +     * Returns the type of this datum, or infers the type from the datum name if no type were specified.
 +     * The latter case occurs after unmarshalling, since GML 3.2 does not contain any attribute for the datum type.
 +     * It may also happen if the datum were created using reflection.
 +     *
-      * <p>This method uses heuristic rules and may be changed in any future SIS version. If the type cannot be
-      * determined, default on the ellipsoidal type since it will usually implies no additional calculation.</p>
++     * <p>This method uses heuristic rules and may be changed in any future SIS version.</p>
       *
 -     * @return method through which this vertical reference frame is realized.
 +     * <p>No synchronization needed; this is not a problem if this value is computed twice.
 +     * This method returns only existing immutable instances.</p>
       *
 -     * @since 2.0
 +     * @see #getVerticalDatumType()
 +     * @see #getTypeElement()
       */
 -    @Override
 -    public Optional<RealizationMethod> getRealizationMethod() {
 -        return Optional.ofNullable(method);
 +    private VerticalDatumType type() {
 +        VerticalDatumType t = type;
 +        if (t == null) {
 +            final ReferenceIdentifier name = super.getName();
 +            type = t = VerticalDatumTypes.guess(name != null ? name.getCode() : null, super.getAlias(), null);
 +        }
 +        return t;
      }
  
      /**
@@@ -264,10 -283,9 +263,9 @@@
              }
              default: {
                  /*
 -                 * RealizationMethod is considered as metadata because it is related to the anchor definition,
 +                 * VerticalDatumType is considered as metadata because it is related to the anchor definition,
                   * which is itself considered as metadata. Furthermore, GeodeticObjectParser and EPSGDataAccess
-                  * do not always set this property to the same value: the former uses the information provided
-                  * by the coordinate system axis while the other does not.
+                  * do not always set this property to the same value, because of historical changes in the WKT.
                   */
                  return true;
              }
@@@ -290,10 -308,10 +288,10 @@@
       * Formats this datum as a <i>Well Known Text</i> {@code VerticalDatum[…]} element.
       *
       * <h4>Compatibility note</h4>
-      * OGC 01-009 defined numerical codes for various vertical datum types, for example 2005 for geoidal height
-      * and 2002 for ellipsoidal height. Such codes were formatted for all {@code Datum} subtypes in WKT 1.
-      * Datum types became provided only for vertical datum in the ISO 19111:2003 specification, then removed
-      * completely in ISO 19111:2007.
+      * OGC 01-009 defined numerical codes for various vertical datum types, for example 2005 for geoidal height.
+      * Such codes were formatted for all {@code Datum} subtypes in WKT 1. Datum types became specified only for
+      * vertical datum in the ISO 19111:2003 standard, then removed completely in the ISO 19111:2007 standard.
 -     * They were reintroduced in a different form ({@link RealizationMethod}) in the ISO 19111:2019 standard.
++     * They were reintroduced in a different form ({@code RealizationMethod}) in the ISO 19111:2019 standard.
       *
       * @return {@code "VerticalDatum"} (WKT 2) or {@code "Vert_Datum"} (WKT 1).
       *
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
index 3b89bbf16c,a05d123ee6..e8f36e5fb3
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
@@@ -1719,14 -1712,8 +1713,8 @@@ codes:  for (int i=0; i<codes.length; i
                          datum = datumFactory.createGeodeticDatum(properties, ellipsoid, meridian);
                          break;
                      }
-                     /*
-                      * Vertical datum type is hard-coded to geoidal. It would be possible to infer other
-                      * types by looking at the coordinate system, but it could result in different datum
-                      * associated to the same EPSG code.  Since vertical datum type is no longer part of
-                      * ISO 19111:2007, it is probably not worth to handle such cases.
-                      */
                      case "vertical": {
-                         datum = datumFactory.createVerticalDatum(properties, VERTICAL_DATUM_TYPE);
 -                        datum = datumFactory.createVerticalDatum(properties, (RealizationMethod) null);
++                        datum = datumFactory.createVerticalDatum(properties, VerticalDatumType.GEOIDAL);
                          break;
                      }
                      /*
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Legacy.java
index eba35c1897,ca37336531..ff134ec0fa
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Legacy.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Legacy.java
@@@ -90,7 -63,8 +90,7 @@@ public final class Legacy 
       * the ISO 19111's ones (ISO names are "Geocentric X", "Geocentric Y" and "Geocentric Z"). This constant uses
       * the invalid names and directions for WKT 1 parsing/formatting purposes.
       */
-     private static final CartesianCS LEGACY = new DefaultCartesianCS(Map.of(NAME_KEY, "Legacy geocentric"),
 -    @SuppressWarnings("deprecation")
+     private static final CartesianCS GEOCENTRIC = new DefaultCartesianCS(Map.of(NAME_KEY, "Legacy geocentric"),
              new DefaultCoordinateSystemAxis(Map.of(NAME_KEY, "X"), "X", AxisDirection.OTHER, Units.METRE),
              new DefaultCoordinateSystemAxis(Map.of(NAME_KEY, "Y"), "Y", AxisDirection.EAST,  Units.METRE),
              new DefaultCoordinateSystemAxis(Map.of(NAME_KEY, "Z"), "Z", AxisDirection.NORTH, Units.METRE));
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
index b6de9fa530,11f58d1e6d..6ccaa8d0c4
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
@@@ -16,11 -16,11 +16,12 @@@
   */
  package org.apache.sis.referencing.internal;
  
+ import java.util.Locale;
  import java.util.Collection;
 +import java.util.function.Predicate;
  import javax.measure.Unit;
 +import org.opengis.util.CodeList;
  import org.opengis.util.GenericName;
 -import org.opengis.referencing.datum.RealizationMethod;
  import org.opengis.referencing.datum.VerticalDatumType;
  import org.opengis.referencing.cs.CoordinateSystemAxis;
  import org.opengis.referencing.cs.AxisDirection;
@@@ -36,71 -35,93 +37,90 @@@ import org.apache.sis.measure.Units
   * Those constants are not in public API because they were intentionally omitted from ISO 19111,
   * and the ISO experts said that they should really not be public.
   *
 + * <p>This class implements {@link Predicate} for opportunist reasons.
 + * This implementation convenience may change in any future SIS version.</p>
 + *
   * @author  Martin Desruisseaux (IRD, Geomatys)
   */
 -public final class VerticalDatumTypes {
 +public final class VerticalDatumTypes implements Predicate<CodeList<?>> {
      /**
-      * A vertical datum for ellipsoidal heights that are measured along the
-      * normal to the ellipsoid used in the definition of horizontal datum.
+      * A pseudo-realization method for ellipsoidal heights that are measured along
+      * the normal to the ellipsoid used in the definition of horizontal datum.
+      * <strong>The use of this method is deprecated</strong> as ellipsoidal height
+      * should never be separated from the horizontal components according ISO 19111.
       *
-      * <p>Identifier: {@code CS_DatumType.CS_VD_Ellipsoidal}</p>
+      * <h4>Legacy</h4>
+      * This type was associated to code 2000 in the {@code Vert_Datum} element of the legacy WKT 1 format.
+      * The UML identifier was {@code CS_DatumType.CS_VD_Ellipsoidal}.
+      *
+      * @see org.apache.sis.referencing.CommonCRS.Vertical#ELLIPSOIDAL
       */
-     public static final VerticalDatumType ELLIPSOIDAL = VerticalDatumType.valueOf("ELLIPSOIDAL");
+     static final String ELLIPSOIDAL = "ELLIPSOIDAL";
  
      /**
-      * A vertical datum for orthometric heights that are measured along the plumb line.
+      * A vertical datum type for orthometric heights that are measured along the plumb line.
       *
-      * <p>Identifier: {@code CS_DatumType.CS_VD_Orthometric}</p>
+      * <h4>Legacy</h4>
+      * This type was associated to code 2001 in the {@code Vert_Datum} element of the legacy WKT 1 format.
+      * The UML identifier was {@code CS_DatumType.CS_VD_Orthometric}.
       */
-     public static final VerticalDatumType ORTHOMETRIC = VerticalDatumType.valueOf("ORTHOMETRIC");
+     private static final String ORTHOMETRIC = "ORTHOMETRIC";
  
      /**
-      * Mapping from the numeric values used in legacy specification (OGC 01-009) to {@link VerticalDatumType}.
-      * Indices in this array are the legacy codes minus 2000.
+      * A vertical datum type for origin of the vertical axis based on atmospheric pressure.
+      *
+      * <h4>Legacy</h4>
+      * This type was associated to code 2003 in the {@code Vert_Datum} element of the legacy WKT 1 format.
+      * The UML identifier was {@code CS_DatumType.CS_VD_AltitudeBarometric}.
       *
-      * <strong>This array shall not be fill before the above static constants.</strong>
+      * @see org.apache.sis.referencing.CommonCRS.Vertical#BAROMETRIC
       */
-     private static final VerticalDatumType[] TYPES;
+     static final String BAROMETRIC = "BAROMETRIC";
  
 -    /**
 -     * Do not allow instantiation of this class.
 -     */
 -    private VerticalDatumTypes() {
 -    }
 -
      /**
-      * Mapping from {@link VerticalDatumType} to the numeric values used in legacy specification (OGC 01-009).
+      * Returns a pseudo-realization method for ellipsoidal heights.
+      * <strong>The use of this method is deprecated</strong> as ellipsoidal height
+      * should never be separated from the horizontal components according ISO 19111.
+      *
+      * <h4>Maintenance note</h4>
 -     * If the implementation of this method is modified, search for {@code RealizationMethod.valueOf}
++     * If the implementation of this method is modified, search for {@code VerticalDatumType.valueOf}
+      * at least in {@link org.apache.sis.referencing.CommonCRS.Vertical#datum()} and make sure that
+      * the code is equivalent.
+      *
+      * @return the ellipsoidal pseudo-realization method.
       */
-     private static final short[] LEGACY_CODES;
-     static {
-         TYPES = new VerticalDatumType[7];
-         LEGACY_CODES = new short[Math.max(ELLIPSOIDAL.ordinal(), ORTHOMETRIC.ordinal()) + 1];
-         for (short code = 2000; code <= 2006; code++) {
-             final VerticalDatumType type;
-             switch (code) {
-                 case 2000: type = VerticalDatumType .OTHER_SURFACE; break;  // CS_VD_Other
-                 case 2001: type = VerticalDatumTypes.ORTHOMETRIC;   break;  // CS_VD_Orthometric
-                 case 2002: type = VerticalDatumTypes.ELLIPSOIDAL;   break;  // CS_VD_Ellipsoidal
-                 case 2003: type = VerticalDatumType .BAROMETRIC;    break;  // CS_VD_AltitudeBarometric
-                 case 2005: type = VerticalDatumType .GEOIDAL;       break;  // CS_VD_GeoidModelDerived
-                 case 2006: type = VerticalDatumType .DEPTH;         break;  // CS_VD_Depth
-                 default:   continue;
-             }
-             TYPES[code - 2000] = type;
-             LEGACY_CODES[type.ordinal()] = code;
-         }
 -    public static RealizationMethod ellipsoidal() {
 -        return RealizationMethod.valueOf(ELLIPSOIDAL);
++    public static VerticalDatumType ellipsoidal() {
++        return VerticalDatumType.valueOf(ELLIPSOIDAL);
+     }
+ 
+     /**
+      * Returns {@code true} if the given value is the ellipsoidal pseudo-realization method.
+      *
+      * @param  method  the method to test, or {@code null}.
+      * @return whether the given method is the ellipsoidal pseudo-realization method.
+      */
 -    public static boolean ellipsoidal(final RealizationMethod method) {
++    public static boolean ellipsoidal(final VerticalDatumType method) {
+         return (method != null) && ELLIPSOIDAL.equalsIgnoreCase(method.name());
      }
  
      /**
       * Returns the vertical datum type from a legacy code. The legacy codes were defined in
-      * <a href="https://www.ogc.org/standards/ct">OGC 01-009</a>
-      * (<cite>Coordinate Transformation Services)</cite>, which also defined the version 1 of
-      * <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
-      * Known Text</cite></a> format (WKT 1). This method is used for WKT 1 parsing.
+      * OGC 01-009 (<cite>Coordinate Transformation Services)</cite>, which also defined the version 1
+      * of <cite>Well Known Text</cite></a> format (WKT 1). This method is used for WKT 1 parsing.
       *
       * @param  code  the legacy vertical datum code.
-      * @return the vertical datum type, or {@code null} if the code is unrecognized.
+      * @return the vertical datum type, or {@code null} if none.
       */
-     public static VerticalDatumType fromLegacy(int code) {
-         code -= 2000;
-         return (code >= 0 && code < TYPES.length) ? TYPES[code] : null;
 -    public static RealizationMethod fromLegacy(final int code) {
++    public static VerticalDatumType fromLegacy(final int code) {
+         switch (code) {
+         //  case 2000: return null;                                     // CS_VD_Other
 -            case 2001: return RealizationMethod.valueOf(ORTHOMETRIC);   // CS_VD_Orthometric
++            case 2001: return VerticalDatumType.valueOf(ORTHOMETRIC);   // CS_VD_Orthometric
+             case 2002: return ellipsoidal();                            // CS_VD_Ellipsoidal
 -            case 2003: return RealizationMethod.valueOf(BAROMETRIC);    // CS_VD_AltitudeBarometric
 -            case 2005: return RealizationMethod.GEOID;                  // CS_VD_GeoidModelDerived
 -            case 2006: return RealizationMethod.TIDAL;                  // CS_VD_Depth
++            case 2003: return VerticalDatumType.BAROMETRIC;             // CS_VD_AltitudeBarometric
++            case 2005: return VerticalDatumType.GEOIDAL;                // CS_VD_GeoidModelDerived
++            case 2006: return VerticalDatumType.DEPTH;                  // CS_VD_Depth
+             default:   return null;
+         }
      }
  
      /**
@@@ -110,35 -131,76 +130,38 @@@
       * @param  type  the vertical datum type, or {@code null} if unknown.
       * @return the legacy code for the given datum type, or 0 if unknown.
       */
+     @SuppressWarnings("deprecation")
      public static int toLegacy(final VerticalDatumType type) {
          if (type != null) {
-             final int ordinal = type.ordinal();
-             if (ordinal >= 0 && ordinal < LEGACY_CODES.length) {
-                 return LEGACY_CODES[ordinal];
+             switch (type.name().toUpperCase(Locale.US)) {
+                 case ORTHOMETRIC: return 2001;      // CS_VD_Orthometric
+                 case ELLIPSOIDAL: return 2002;      // CS_VD_Ellipsoidal
+                 case BAROMETRIC:  return 2003;      // CS_VD_AltitudeBarometric
+                 case "GEOIDAL":   return 2005;      // CS_VD_GeoidModelDerived
+                 case "DEPTH":     return 2006;      // CS_VD_Depth
              }
          }
-         return 0;
+         return 2000;
      }
  
 -    /**
 -     * Returns the vertical datum type from a realization method.
 -     * If the given method cannot be mapped to a legacy type, then this method returns "other surface".
 -     * This is because the vertical datum type was a mandatory property in legacy OGC/ISO standards.
 -     * This method is used for writing GML documents older than GML 3.2.
 -     *
 -     * @param  method  the realization method, or {@code null}.
 -     * @return the vertical datum type (never null).
 -     */
 -    @SuppressWarnings("deprecation")
 -    public static VerticalDatumType fromMethod(final RealizationMethod method) {
 -        if (method == RealizationMethod.GEOID) return VerticalDatumType.GEOIDAL;
 -        if (method == RealizationMethod.TIDAL) return VerticalDatumType.DEPTH;
 -        if (method != null) {
 -            return VerticalDatumType.valueOf(method.name().toUpperCase(Locale.US));
 -        }
 -        return VerticalDatumType.OTHER_SURFACE;
 -    }
 -
 -    /**
 -     * Returns the realization method from a vertical datum type.
 -     * This method is used for reading GML documents older than GML 3.2.
 -     *
 -     * @param  type  the vertical datum type, or {@code null}.
 -     * @return the realization method, or {@code null} if none.
 -     */
 -    @SuppressWarnings("deprecation")
 -    public static RealizationMethod toMethod(final VerticalDatumType type) {
 -        if (type != null) {
 -            if (type == VerticalDatumType.GEOIDAL)         return RealizationMethod.GEOID;
 -            if (type == VerticalDatumType.DEPTH)           return RealizationMethod.TIDAL;
 -            if (type == VerticalDatumType.BAROMETRIC)      return RealizationMethod.valueOf(BAROMETRIC);
 -            if (ORTHOMETRIC.equalsIgnoreCase(type.name())) return RealizationMethod.valueOf(ORTHOMETRIC);
 -            if (ELLIPSOIDAL.equalsIgnoreCase(type.name())) return ellipsoidal();
 -        }
 -        return null;
 -    }
 -
      /**
-      * Guesses the type of a datum from its name, aliases or a given vertical axis. This is sometimes needed
-      * after XML unmarshalling or WKT parsing, since GML 3.2 and ISO 19162 do not contain any attribute for
-      * the datum type.
+      * Guesses the realization method of a datum from its name, aliases or a given vertical axis.
+      * This is sometimes needed after XML unmarshalling or WKT parsing, because GML 3.2 and ISO 19162
+      * do not contain any attribute for the datum type.
       *
-      * <p>This method uses heuristic rules and may be changed in any future SIS version.
-      * If the type cannot be determined, defaults to {@link VerticalDatumType#OTHER_SURFACE}.</p>
+      * <p>This method uses heuristic rules and may be changed in any future SIS version.</p>
       *
       * @param  name     the name of the datum for which to guess a type, or {@code null} if unknown.
       * @param  aliases  the aliases of the datum for which to guess a type, or {@code null} if unknown.
       * @param  axis     the vertical axis for which to guess a type, or {@code null} if unknown.
 -     * @return a datum type, or {@code null} if none can be guessed.
 +     * @return a datum type, or {@link VerticalDatumType#OTHER_SURFACE} if none can be guessed.
       */
 -    public static RealizationMethod guess(final String name, final Collection<? extends GenericName> aliases,
 +    public static VerticalDatumType guess(final String name, final Collection<? extends GenericName> aliases,
              final CoordinateSystemAxis axis)
      {
-         VerticalDatumType type = guess(name);
-         if (type != null) {
-             return type;
 -        RealizationMethod method = guess(name);
++        VerticalDatumType method = guess(name);
+         if (method != null) {
+             return method;
          }
          if (aliases != null) {
              for (final GenericName alias : aliases) {
@@@ -155,13 -217,13 +178,13 @@@
                  if (abbreviation.length() == 1) {
                      AxisDirection dir = AxisDirection.UP;               // Expected direction for accepting the type.
                      switch (abbreviation.charAt(0)) {
-                         case 'h': type = ELLIPSOIDAL; break;
-                         case 'H': type = VerticalDatumType.GEOIDAL; break;
-                         case 'D': type = VerticalDatumType.DEPTH; dir = AxisDirection.DOWN; break;
+                         case 'h': method = ellipsoidal(); break;
 -                        case 'H': method = RealizationMethod.GEOID; break;
 -                        case 'D': method = RealizationMethod.TIDAL; dir = AxisDirection.DOWN; break;
 -                        default:  return null;
++                        case 'H': method = VerticalDatumType.GEOIDAL; break;
++                        case 'D': method = VerticalDatumType.DEPTH; dir = AxisDirection.DOWN; break;
 +                        default:  return VerticalDatumType.OTHER_SURFACE;
                      }
                      if (dir.equals(axis.getDirection())) {
-                         return type;
+                         return method;
                      }
                  }
              } else if (Units.isPressure(unit)) {
@@@ -172,23 -234,19 +195,23 @@@
      }
  
      /**
-      * Guesses the type of a datum of the given name. This method attempts to guess only if the given name
-      * contains at least one letter. If the type cannot be determined, returns {@code null}.
+      * Guesses the realization method of a datum of the given name. This method attempts to guess only if
+      * the given name contains at least one letter. If the type cannot be determined, returns {@code null}.
       *
-      * @param  name  name of the datum for which to guess a type, or {@code null}.
-      * @return a datum type, or {@code null} if none can be guessed.
+      * @param  name  name of the datum for which to guess a realization method, or {@code null}.
+      * @return a realization method, or {@code null} if none can be guessed.
       */
 -    private static RealizationMethod guess(final String name) {
 +    private static VerticalDatumType guess(final String name) {
          if (name != null) {
              if (CharSequences.equalsFiltered("Mean Sea Level", name, Characters.Filter.LETTERS_AND_DIGITS, true)) {
 -                return RealizationMethod.TIDAL;
 +                return VerticalDatumType.GEOIDAL;
              }
 -            if (name.contains("geoid")) {
 -                return RealizationMethod.GEOID;
 +            for (int i=0; i<name.length();) {
 +                final int c = name.codePointAt(i);
 +                if (Character.isLetter(c)) {
 +                    return CodeLists.find(VerticalDatumType.class, new VerticalDatumTypes(name));
 +                }
 +                i += Character.charCount(c);
              }
          }
          return null;
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
index 96a6d5e2ad,4671df63a3..3826d39454
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
@@@ -35,11 -35,6 +35,11 @@@ import org.apache.sis.util.iso.Types
  import org.apache.sis.measure.Units;
  import static org.apache.sis.util.CharSequences.*;
  
 +// Specific to the main branch:
 +import org.opengis.annotation.UML;
 +import static org.opengis.annotation.Obligation.CONDITIONAL;
- import static org.opengis.annotation.Specification.UNSPECIFIED;
++import static org.opengis.annotation.Specification.ISO_19111;
 +
  
  /**
   * Utilities methods related to {@link AxisDirection}.
@@@ -71,53 -66,8 +71,59 @@@ public final class AxisDirections exten
       *
       * @see #isUserDefined(AxisDirection)
       */
 -    private static final int LAST_ORDINAL = UNSPECIFIED.ordinal();
 +    private static final int LAST_ORDINAL = DISPLAY_DOWN.ordinal();
 +
 +    /**
 +     * Forward direction.
 +     * For an observer at the centre of the object this is will be towards its front, bow or nose.
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
-     @UML(identifier="forward", obligation=CONDITIONAL, specification=UNSPECIFIED)
++    @UML(identifier="forward", obligation=CONDITIONAL, specification=ISO_19111)
 +    public static final AxisDirection FORWARD = AxisDirection.valueOf("FORWARD");
 +
 +    /**
 +     * Starboard direction.
 +     * For an observer at the centre of the object this will be towards its right.
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
-     @UML(identifier="starboard", obligation=CONDITIONAL, specification=UNSPECIFIED)
++    @UML(identifier="starboard", obligation=CONDITIONAL, specification=ISO_19111)
 +    public static final AxisDirection STARBOARD = AxisDirection.valueOf("STARBOARD");
 +
 +    /**
 +     * Port direction.
 +     * For an observer at the centre of the object this will be towards its left.
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
-     @UML(identifier="port", obligation=CONDITIONAL, specification=UNSPECIFIED)
++    @UML(identifier="port", obligation=CONDITIONAL, specification=ISO_19111)
 +    public static final AxisDirection PORT = AxisDirection.valueOf("PORT");
 +
 +    /**
 +     * Direction of geographic angles (bearing).
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
-     @UML(identifier="clockwise", obligation=CONDITIONAL, specification=UNSPECIFIED)
++    @UML(identifier="clockwise", obligation=CONDITIONAL, specification=ISO_19111)
 +    public static final AxisDirection CLOCKWISE = AxisDirection.valueOf("CLOCKWISE");
 +
 +    /**
 +     * Direction of arithmetic angles. Used in polar coordinate systems.
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
-     @UML(identifier="counterClockwise", obligation=CONDITIONAL, specification=UNSPECIFIED)
++    @UML(identifier="counterClockwise", obligation=CONDITIONAL, specification=ISO_19111)
 +    public static final AxisDirection COUNTER_CLOCKWISE = AxisDirection.valueOf("COUNTER_CLOCKWISE");
 +
 +    /**
 +     * Distance from the origin in a polar coordinate system.
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
-     @UML(identifier="awayFrom", obligation=CONDITIONAL, specification=UNSPECIFIED)
++    @UML(identifier="awayFrom", obligation=CONDITIONAL, specification=ISO_19111)
 +    public static final AxisDirection AWAY_FROM = AxisDirection.valueOf("AWAY_FROM");
 +
++    /**
++     * Axis positive direction is unspecified.
++     */
++    @UML(identifier="unspecified", obligation=CONDITIONAL, specification=ISO_19111)
++    public static final AxisDirection UNSPECIFIED = AxisDirection.valueOf("UNSPECIFIED");
+ 
      /**
       * For each direction, the opposite direction.
       * This map shall be immutable after construction.
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
index a1a8eb1a5c,65b6d458f0..9fc219d785
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
@@@ -272,10 -220,7 +272,7 @@@ public final class ReferencingUtilitie
       */
      public static boolean isEllipsoidalHeight(final VerticalDatum datum) {
          if (datum != null) {
-             final VerticalDatumType type = datum.getVerticalDatumType();
-             if (type != null) {
-                 return "ELLIPSOIDAL".equalsIgnoreCase(type.name());
-             }
 -            return datum.getRealizationMethod().map(VerticalDatumTypes::ellipsoidal).orElse(false);
++            return VerticalDatumTypes.ellipsoidal(datum.getVerticalDatumType());
          }
          return false;
      }
diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/TransliteratorTest.java
index 31c0786c52,3ba62d844f..0e4ad77061
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/TransliteratorTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/TransliteratorTest.java
@@@ -21,6 -21,6 +21,7 @@@ import org.opengis.referencing.cs.Spher
  import org.opengis.referencing.cs.EllipsoidalCS;
  import org.opengis.referencing.cs.CoordinateSystem;
  import org.apache.sis.metadata.privy.AxisNames;
++import org.apache.sis.referencing.privy.AxisDirections;
  
  // Test dependencies
  import org.junit.jupiter.api.Test;
@@@ -98,12 -98,12 +99,12 @@@ public final class TransliteratorTest e
      @Test
      public void testToUnicodeAbbreviation() {
          final Transliterator t = Transliterator.DEFAULT;
-         assertEquals("φ",  t.toUnicodeAbbreviation("ellipsoidal", AxisDirection.NORTH, "P"), "P");
-         assertEquals("φ",  t.toUnicodeAbbreviation("ellipsoidal", AxisDirection.NORTH, "B"), "B");
-         assertEquals("λ",  t.toUnicodeAbbreviation("ellipsoidal", AxisDirection.EAST,  "L"), "L");
-         assertEquals("θ",  t.toUnicodeAbbreviation("polar",       AxisDirection.OTHER, "U"), "U");
-         assertEquals("Ω",  t.toUnicodeAbbreviation("spherical",   AxisDirection.NORTH, "U"), "U");
-         assertEquals("θ",  t.toUnicodeAbbreviation("spherical",   AxisDirection.EAST,  "V"), "V");
+         assertEquals("φ",  t.toUnicodeAbbreviation("ellipsoidal", AxisDirection.NORTH,     "P"), "P");
+         assertEquals("φ",  t.toUnicodeAbbreviation("ellipsoidal", AxisDirection.NORTH,     "B"), "B");
+         assertEquals("λ",  t.toUnicodeAbbreviation("ellipsoidal", AxisDirection.EAST,      "L"), "L");
 -        assertEquals("θ",  t.toUnicodeAbbreviation("polar",       AxisDirection.CLOCKWISE, "U"), "U");
++        assertEquals("θ",  t.toUnicodeAbbreviation("polar",       AxisDirections.CLOCKWISE,"U"), "U");
+         assertEquals("Ω",  t.toUnicodeAbbreviation("spherical",   AxisDirection.NORTH,     "U"), "U");
+         assertEquals("θ",  t.toUnicodeAbbreviation("spherical",   AxisDirection.EAST,      "V"), "V");
      }
  
      /**
diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CommonCRSTest.java
index b2560e43fb,c109be58ca..80f6689c5d
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CommonCRSTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CommonCRSTest.java
@@@ -214,34 -214,37 +214,34 @@@ public final class CommonCRSTest extend
       * Verifies the vertical datum enumeration.
       */
      @Test
 -    @SuppressWarnings("deprecation")
      public void testVertical() {
          for (final CommonCRS.Vertical e : CommonCRS.Vertical.values()) {
-             final VerticalDatumType datumType;
 -            final RealizationMethod method;
++            final VerticalDatumType method;
              final String axisName, datumName;
              switch (e) {
-                 case NAVD88:         axisName = AxisNames.GRAVITY_RELATED_HEIGHT; datumName = "North American Vertical Datum 1988"; datumType = VerticalDatumType. GEOIDAL;       break;
-                 case BAROMETRIC:     axisName = "Barometric altitude";            datumName = "Constant pressure surface";          datumType = VerticalDatumType. BAROMETRIC;    break;
-                 case MEAN_SEA_LEVEL: axisName = AxisNames.GRAVITY_RELATED_HEIGHT; datumName = "Mean Sea Level";                     datumType = VerticalDatumType. GEOIDAL;       break;
-                 case DEPTH:          axisName = AxisNames.DEPTH;                  datumName = "Mean Sea Level";                     datumType = VerticalDatumType. GEOIDAL;       break;
-                 case ELLIPSOIDAL:    axisName = AxisNames.ELLIPSOIDAL_HEIGHT;     datumName = "Ellipsoid";                          datumType = VerticalDatumTypes.ELLIPSOIDAL;   break;
-                 case OTHER_SURFACE:  axisName = "Height";                         datumName = "Other surface";                      datumType = VerticalDatumType. OTHER_SURFACE; break;
 -                case NAVD88:         axisName = AxisNames.GRAVITY_RELATED_HEIGHT; datumName = "North American Vertical Datum 1988"; method = RealizationMethod. GEOID; break;
 -                case BAROMETRIC:     axisName = "Barometric altitude";            datumName = "Constant pressure surface";          method = RealizationMethod.valueOf("BAROMETRIC"); break;
 -                case MEAN_SEA_LEVEL: axisName = AxisNames.GRAVITY_RELATED_HEIGHT; datumName = "Mean Sea Level";                     method = RealizationMethod. TIDAL; break;
 -                case DEPTH:          axisName = AxisNames.DEPTH;                  datumName = "Mean Sea Level";                     method = RealizationMethod. TIDAL; break;
++                case NAVD88:         axisName = AxisNames.GRAVITY_RELATED_HEIGHT; datumName = "North American Vertical Datum 1988"; method = VerticalDatumType. GEOIDAL;       break;
++                case BAROMETRIC:     axisName = "Barometric altitude";            datumName = "Constant pressure surface";          method = VerticalDatumType. BAROMETRIC;    break;
++                case MEAN_SEA_LEVEL: axisName = AxisNames.GRAVITY_RELATED_HEIGHT; datumName = "Mean Sea Level";                     method = VerticalDatumType. GEOIDAL;       break;
++                case DEPTH:          axisName = AxisNames.DEPTH;                  datumName = "Mean Sea Level";                     method = VerticalDatumType. GEOIDAL;       break;
+                 case ELLIPSOIDAL:    axisName = AxisNames.ELLIPSOIDAL_HEIGHT;     datumName = "Ellipsoid";                          method = VerticalDatumTypes.ellipsoidal(); break;
 -                case OTHER_SURFACE:  axisName = "Height";                         datumName = "Other surface";                      method = null; break;
++                case OTHER_SURFACE:  axisName = "Height";                         datumName = "Other surface";                      method = VerticalDatumType. OTHER_SURFACE; break;
                  default: throw new AssertionError(e);
              }
              final String        name  = e.name();
              final VerticalDatum datum = e.datum();
              final VerticalCRS   crs   = e.crs();
 -            if (e.isEPSG) {
 +            if (e.isEPSG && !name.startsWith("NAV")) {
                  /*
-                  * BAROMETRIC, ELLIPSOIDAL and OTHER_SURFACE uses an axis named "Height", which is not
-                  * a valid axis name according ISO 19111. We skip the validation test for those enums.
+                  * BAROMETRIC and ELLIPSOIDAL uses an axis named "Height", which is not a valid
+                  * axis name according ISO 19111. We skip the validation test for those enums.
                   */
                  Validators.validate(crs);
              }
              assertSame(datum, e.datum(), name);                         // Datum before CRS creation.
              assertSame(crs.getDatum(), e.datum(), name);                // Datum after CRS creation.
              assertEquals(datumName, datum.getName().getCode(), name);
-             assertEquals(datumType, datum.getVerticalDatumType(), name);
++            assertEquals(method, datum.getVerticalDatumType(), name);
              assertEquals(axisName,  crs.getCoordinateSystem().getAxis(0).getName().getCode(), name);
 -            if (!e.isEPSG) {  // Because the information is not in EPSG database 9.x.
 -                assertEquals(method, datum.getRealizationMethod().orElse(null), name);
 -            }
          }
      }
  
diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/HardCodedAxes.java
index 48ac69bb32,b7b43a955b..bffb391065
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/HardCodedAxes.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/HardCodedAxes.java
@@@ -471,8 -468,8 +471,8 @@@ public final class HardCodedAxes 
       * and the unit is dimensionless. This constant is sometimes used as a placeholder
       * for axes that were not properly defined.
       */
-     public static final DefaultCoordinateSystemAxis UNDEFINED = create("Undefined", "z",
-             AxisDirection.OTHER, Units.UNITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, null);
+     public static final DefaultCoordinateSystemAxis UNDEFINED = create("Undefined", "m",
 -            AxisDirection.UNSPECIFIED, Units.UNITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, null);
++            AxisDirections.UNSPECIFIED, Units.UNITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, null);
  
      /**
       * Creates a new axis of the given name, abbreviation, direction and unit.
diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java
index 81fb6a38bd,9de1a8749b..fe792940f1
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java
@@@ -65,34 -65,6 +65,34 @@@ public final class DefaultVerticalDatum
                         : "VerticalDatum.xml");
      }
  
 +    /**
 +     * Tests the {@link DefaultVerticalDatum#getVerticalDatumType()} method in a state
 +     * simulating unmarshalling of GML 3.2 document.
 +     *
 +     * @throws NoSuchFieldException   Should never happen.
 +     * @throws IllegalAccessException Should never happen.
 +     */
 +    @Test
 +    public void testAfterUnmarshal() throws NoSuchFieldException, IllegalAccessException {
 +        final Field typeField = DefaultVerticalDatum.class.getDeclaredField("type");
 +        typeField.setAccessible(true);
 +        assertEquals(VerticalDatumType .GEOIDAL,       typeForName(typeField, "Geoidal height"));
 +        assertEquals(VerticalDatumType .DEPTH,         typeForName(typeField, "Some depth measurement"));
-         assertEquals(VerticalDatumTypes.ELLIPSOIDAL,   typeForName(typeField, "Ellipsoidal height"));
++        assertEquals(VerticalDatumTypes.ellipsoidal(), typeForName(typeField, "Ellipsoidal height"));
 +        assertEquals(VerticalDatumType .OTHER_SURFACE, typeForName(typeField, "NotADepth"));
 +    }
 +
 +    /**
 +     * Returns the vertical datum type inferred by {@link DefaultVerticalDatum} for the given name.
 +     */
 +    private static VerticalDatumType typeForName(final Field typeField, final String name) throws IllegalAccessException {
 +        final var datum = new DefaultVerticalDatum(
 +                Map.of(DefaultVerticalDatum.NAME_KEY, name),
 +                VerticalDatumType.OTHER_SURFACE);
 +        typeField.set(datum, null);
 +        return datum.getVerticalDatumType();
 +    }
 +
      /**
       * Tests {@link DefaultVerticalDatum#toWKT()}.
       */
diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/internal/VerticalDatumTypesTest.java
index 950f9f7f82,2b846e8736..7a19958814
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/internal/VerticalDatumTypesTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/internal/VerticalDatumTypesTest.java
@@@ -16,7 -16,9 +16,8 @@@
   */
  package org.apache.sis.referencing.internal;
  
 -import org.opengis.referencing.datum.RealizationMethod;
  import org.opengis.referencing.datum.VerticalDatumType;
+ import org.apache.sis.referencing.CommonCRS;
  import org.apache.sis.util.ArraysExt;
  
  // Test dependencies
@@@ -37,14 -40,25 +38,25 @@@ public final class VerticalDatumTypesTe
      public VerticalDatumTypesTest() {
      }
  
+     /**
+      * Verifies name constraint with values defined in {@link org.apache.sis.referencing.CommonCRS.Vertical}.
+      * Some enumeration values must have the same names as the constants defined in {@link VerticalDatumTypes},
 -     * because the realization method is obtained by a call to {@link RealizationMethod#valueOf(String)}.
++     * because the realization method is obtained by a call to {@link VerticalDatumType#valueOf(String)}.
+      */
+     @Test
+     public void verifyNameConstraint() {
+         assertEquals(VerticalDatumTypes.ELLIPSOIDAL, CommonCRS.Vertical.ELLIPSOIDAL.name());
+         assertEquals(VerticalDatumTypes.BAROMETRIC,  CommonCRS.Vertical.BAROMETRIC.name());
+     }
+ 
      /**
       * Tests the {@link VerticalDatumTypes#fromLegacy(int)} method.
       */
      @Test
      public void testFromLegacy() {
-         assertEquals(VerticalDatumTypes.ELLIPSOIDAL, VerticalDatumTypes.fromLegacy(2002));
-         assertEquals(VerticalDatumType .GEOIDAL,     VerticalDatumTypes.fromLegacy(2005));
-         assertEquals(VerticalDatumType .DEPTH,       VerticalDatumTypes.fromLegacy(2006));
+         assertEquals(VerticalDatumTypes.ellipsoidal(), VerticalDatumTypes.fromLegacy(2002));
 -        assertEquals(RealizationMethod .GEOID,         VerticalDatumTypes.fromLegacy(2005));
 -        assertEquals(RealizationMethod .TIDAL,         VerticalDatumTypes.fromLegacy(2006));
++        assertEquals(VerticalDatumType .GEOIDAL,       VerticalDatumTypes.fromLegacy(2005));
++        assertEquals(VerticalDatumType .DEPTH,         VerticalDatumTypes.fromLegacy(2006));
      }
  
      /**
@@@ -58,13 -72,13 +70,13 @@@
      }
  
      /**
-      * Tests the list of vertical datum types. Note that {@link #testFromLegacy()} must be executed
-      * first for ensuring {@link VerticalDatumTypes} class initialization prior this test.
+      * Verifies the list of realization methods.
       */
      @Test
-     public void testVerticalDatumTypes() {
+     public void verifyCodeList() {
 -        final RealizationMethod expected = VerticalDatumTypes.ellipsoidal();    // Must be first.
 -        final RealizationMethod[] types = RealizationMethod.values();
 -        assertEquals(RealizationMethod.LEVELLING, types[0]);
++        final VerticalDatumType expected = VerticalDatumTypes.ellipsoidal();    // Must be first.
 +        final VerticalDatumType[] types = VerticalDatumType.values();
 +        assertEquals(VerticalDatumType.OTHER_SURFACE, types[0]);
-         assertTrue(ArraysExt.contains(types, VerticalDatumTypes.ELLIPSOIDAL));
+         assertTrue(ArraysExt.contains(types, expected));
      }
  }
diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/privy/AxisDirectionsTest.java
index 528027e9ca,b926d08d7c..f50eb7f5f9
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/privy/AxisDirectionsTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/privy/AxisDirectionsTest.java
@@@ -32,11 -32,6 +32,12 @@@ import org.apache.sis.referencing.cs.Ha
  import org.apache.sis.referencing.cs.HardCodedCS;
  import org.apache.sis.test.TestCase;
  
 +// Specific to the main branch:
 +import static org.apache.sis.referencing.privy.AxisDirections.AWAY_FROM;
 +import static org.apache.sis.referencing.privy.AxisDirections.CLOCKWISE;
 +import static org.apache.sis.referencing.privy.AxisDirections.COUNTER_CLOCKWISE;
++import static org.apache.sis.referencing.privy.AxisDirections.UNSPECIFIED;
 +
  
  /**
   * Tests the {@link AxisDirections} class.
diff --cc endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Axis.java
index 5784bbf8e5,8bff2968de..9ca1fbd600
--- a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Axis.java
+++ b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/Axis.java
@@@ -630,7 -630,7 +630,7 @@@ public final class Axis extends NamedEl
              } else switch (order) {
                  case 0:  dir = AxisDirection.COLUMN_POSITIVE; break;
                  case 1:  dir = AxisDirection.ROW_POSITIVE;    break;
-                 default: dir = AxisDirection.OTHER;           break;
 -                default: dir = AxisDirection.UNSPECIFIED;     break;
++                default: dir = AxisDirections.UNSPECIFIED;    break;
              }
          }
          final String abbr;