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 058a84cae16e41f7c10922a9703957d19ba05e25
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Thu Jul 24 12:09:15 2025 +0200

    Support the `REALIZATION_METHOD_CODE` column of the "Datum" table of EPSG 
version 10+.
---
 .../apache/sis/io/wkt/GeodeticObjectParser.java    |  6 +-
 .../referencing/datum/DefaultVerticalDatum.java    |  8 +-
 .../referencing/factory/sql/EPSGDataAccess.java    | 87 ++++++++++++++++------
 .../sis/referencing/factory/sql/SQLTranslator.java | 24 ++++--
 .../org/apache/sis/referencing/internal/Epoch.java |  2 -
 .../referencing/internal/VerticalDatumTypes.java   | 57 ++++++++++----
 .../internal/VerticalDatumTypesTest.java           | 33 +++++---
 7 files changed, 155 insertions(+), 62 deletions(-)

diff --git 
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
index 2b969c5339..fcc952c75c 100644
--- 
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
@@ -1447,10 +1447,10 @@ class GeodeticObjectParser extends MathTransformParser 
implements Comparator<Coo
         @SuppressWarnings("deprecation")
         RealizationMethod method = null;
         if (isWKT1) {
-            method = 
VerticalDatumTypes.fromLegacy(element.pullInteger("datum"));
+            method = 
VerticalDatumTypes.fromLegacyCode(element.pullInteger("datum"));
         }
         if (method == null) {
-            method = VerticalDatumTypes.guess(name, null, null);
+            method = VerticalDatumTypes.fromDatum(name, null, null);
         }
         final DatumFactory datumFactory = factories.getDatumFactory();
         try {
@@ -1936,7 +1936,7 @@ class GeodeticObjectParser extends MathTransformParser 
implements Comparator<Coo
                  * more information. Verify if we can have a better type now, 
and if so rebuild the datum.
                  */
                 if (datum.getRealizationMethod().isEmpty()) {
-                    var type = 
VerticalDatumTypes.guess(datum.getName().getCode(), datum.getAlias(), 
cs.getAxis(0));
+                    var type = 
VerticalDatumTypes.fromDatum(datum.getName().getCode(), datum.getAlias(), 
cs.getAxis(0));
                     if (type != null) {
                         final DatumFactory datumFactory = 
factories.getDatumFactory();
                         datum = 
datumFactory.createVerticalDatum(IdentifiedObjects.getProperties(datum), type);
diff --git 
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
index c7ac53c321..4a097c0eed 100644
--- 
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
@@ -98,6 +98,8 @@ public class DefaultVerticalDatum extends AbstractDatum 
implements VerticalDatum
 
     /**
      * The realization method (geoid, tidal, <i>etc.</i>), or {@code null} if 
unspecified.
+     *
+     * @see #getRealizationMethod()
      */
     private RealizationMethod method;
 
@@ -366,7 +368,7 @@ public class DefaultVerticalDatum extends AbstractDatum 
implements VerticalDatum
     protected String formatTo(final Formatter formatter) {
         super.formatTo(formatter);
         if (formatter.getConvention().majorVersion() == 1) {
-            formatter.append(VerticalDatumTypes.toLegacy(method));
+            formatter.append(VerticalDatumTypes.toLegacyCode(method));
             return WKTKeywords.Vert_Datum;
         }
         return formatter.shortOrLong(WKTKeywords.VDatum, 
WKTKeywords.VerticalDatum);
@@ -408,7 +410,7 @@ public class DefaultVerticalDatum extends AbstractDatum 
implements VerticalDatum
         if (Context.isGMLVersion(Context.current(), 
LegacyNamespaces.VERSION_3_2)) {
             return null;
         }
-        return VerticalDatumTypes.toName(method);
+        return VerticalDatumTypes.toLegacyName(method);
     }
 
     /**
@@ -416,7 +418,7 @@ public class DefaultVerticalDatum extends AbstractDatum 
implements VerticalDatum
      */
     private void setTypeElement(final String type) {
         if (method == null) {
-            method = VerticalDatumTypes.fromName(type);
+            method = VerticalDatumTypes.fromLegacyName(type);
         } else {
             
ImplementationHelper.propertyAlreadySet(DefaultVerticalDatum.class, "setType", 
"verticalDatumType");
         }
diff --git 
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
index 0b3b995688..e2cef4ba6a 100644
--- 
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
@@ -88,6 +88,7 @@ import 
org.apache.sis.referencing.internal.EPSGParameterDomain;
 import org.apache.sis.referencing.internal.ParameterizedTransformBuilder;
 import org.apache.sis.referencing.internal.PositionalAccuracyConstant;
 import org.apache.sis.referencing.internal.SignReversalComment;
+import org.apache.sis.referencing.internal.VerticalDatumTypes;
 import org.apache.sis.referencing.internal.Resources;
 import org.apache.sis.referencing.internal.ServicesForMetadata;
 import org.apache.sis.parameter.DefaultParameterDescriptor;
@@ -239,7 +240,7 @@ public class EPSGDataAccess extends 
GeodeticAuthorityFactory implements CRSAutho
      * A pool of prepared statements. Keys are {@link String} objects related 
to their originating method
      * (for example "Ellipsoid" for {@link #createEllipsoid(String)}).
      */
-    private final Map<String,PreparedStatement> statements = new HashMap<>();
+    private final Map<String, PreparedStatement> statements = new HashMap<>();
 
     /**
      * The set of authority codes for different types. This map is used by the 
{@link #getAuthorityCodes(Class)}
@@ -265,7 +266,15 @@ public class EPSGDataAccess extends 
GeodeticAuthorityFactory implements CRSAutho
      *
      * @see #getAxisName(Integer)
      */
-    private final Map<Integer,AxisName> axisNames = new HashMap<>();
+    private final Map<Integer, AxisName> axisNames = new HashMap<>();
+
+    /**
+     * Cache for realization methods. This service is not provided by {@code 
ConcurrentAuthorityFactory}
+     * because the realization method table is specific to the 
<abbr>EPSG</abbr> authority factory.
+     *
+     * @see #getRealizationMethod(Integer)
+     */
+    private final Map<Integer, RealizationMethod> realizationMethods = new 
HashMap<>();
 
     /**
      * Cache of naming systems other than EPSG. There is usually few of them 
(at most 15).
@@ -273,13 +282,13 @@ public class EPSGDataAccess extends 
GeodeticAuthorityFactory implements CRSAutho
      *
      * @see #createProperties(String, Integer, String, CharSequence, String, 
String, CharSequence, boolean)
      */
-    private final Map<String,NameSpace> namingSystems = new HashMap<>();
+    private final Map<String, NameSpace> namingSystems = new HashMap<>();
 
     /**
      * The properties to be given the objects to construct.
      * Reused every time {@code createProperties(…)} is invoked.
      */
-    private final Map<String,Object> properties = new HashMap<>();
+    private final Map<String, Object> properties = new HashMap<>();
 
     /**
      * A safety guard for preventing never-ending loops in recursive calls to 
some {@code createFoo(String)} methods.
@@ -293,7 +302,7 @@ public class EPSGDataAccess extends 
GeodeticAuthorityFactory implements CRSAutho
      *
      * Keys are EPSG codes and values are the type of object being constructed 
(but those values are not yet used).
      */
-    private final Map<Integer,Class<?>> safetyGuard = new HashMap<>();
+    private final Map<Integer, Class<?>> safetyGuard = new HashMap<>();
 
     /**
      * {@code true} for disabling the logging of warnings when this factory 
creates deprecated objects.
@@ -1396,7 +1405,7 @@ codes:  for (int i=0; i<codes.length; i++) {
                                 endOfRecursion(GeographicCRS.class, epsg);
                             }
                         }
-                        DatumEnsemble<GeodeticDatum> ensemble = 
tryAsEnsemble(datum, GeodeticDatum.class);
+                        DatumEnsemble<GeodeticDatum> ensemble = 
wasDatumEnsemble(datum, GeodeticDatum.class);
                         if (ensemble != null) datum = null;
                         crs = 
crsFactory.createGeographicCRS(createProperties("Coordinate Reference System",
                                 epsg, name, null, area, scope, remarks, 
deprecated), datum, ensemble, cs);
@@ -1493,7 +1502,7 @@ codes:  for (int i=0; i<codes.length; i++) {
                     case "vertical": {
                         VerticalCS    cs    = owner.createVerticalCS   
(getString(code, result, 8));
                         VerticalDatum datum = 
owner.createVerticalDatum(getString(code, result, 9));
-                        DatumEnsemble<VerticalDatum> ensemble = 
tryAsEnsemble(datum, VerticalDatum.class);
+                        DatumEnsemble<VerticalDatum> ensemble = 
wasDatumEnsemble(datum, VerticalDatum.class);
                         if (ensemble != null) datum = null;
                         crs = 
crsFactory.createVerticalCRS(createProperties("Coordinate Reference System",
                                 epsg, name, null, area, scope, remarks, 
deprecated), datum, ensemble, cs);
@@ -1509,7 +1518,7 @@ codes:  for (int i=0; i<codes.length; i++) {
                     case "temporal": {
                         TimeCS        cs    = owner.createTimeCS       
(getString(code, result, 8));
                         TemporalDatum datum = 
owner.createTemporalDatum(getString(code, result, 9));
-                        DatumEnsemble<TemporalDatum> ensemble = 
tryAsEnsemble(datum, TemporalDatum.class);
+                        DatumEnsemble<TemporalDatum> ensemble = 
wasDatumEnsemble(datum, TemporalDatum.class);
                         if (ensemble != null) datum = null;
                         crs = 
crsFactory.createTemporalCRS(createProperties("Coordinate Reference System",
                                 epsg, name, null, area, scope, remarks, 
deprecated), datum, ensemble, cs);
@@ -1544,7 +1553,7 @@ codes:  for (int i=0; i<codes.length; i++) {
                     case "geocentric": {
                         CoordinateSystem cs = 
owner.createCoordinateSystem(getString(code, result, 8));
                         GeodeticDatum datum = owner.createGeodeticDatum   
(getString(code, result, 9));
-                        DatumEnsemble<GeodeticDatum> ensemble = 
tryAsEnsemble(datum, GeodeticDatum.class);
+                        DatumEnsemble<GeodeticDatum> ensemble = 
wasDatumEnsemble(datum, GeodeticDatum.class);
                         if (ensemble != null) datum = null;
                         @SuppressWarnings("LocalVariableHidesMemberVariable")
                         final Map<String,Object> properties = 
createProperties("Coordinate Reference System",
@@ -1565,7 +1574,7 @@ codes:  for (int i=0; i<codes.length; i++) {
                     case "engineering": {
                         CoordinateSystem cs    = 
owner.createCoordinateSystem(getString(code, result, 8));
                         EngineeringDatum datum = 
owner.createEngineeringDatum(getString(code, result, 9));
-                        DatumEnsemble<EngineeringDatum> ensemble = 
tryAsEnsemble(datum, EngineeringDatum.class);
+                        DatumEnsemble<EngineeringDatum> ensemble = 
wasDatumEnsemble(datum, EngineeringDatum.class);
                         if (ensemble != null) datum = null;
                         crs = 
crsFactory.createEngineeringCRS(createProperties("Coordinate Reference System",
                                 epsg, name, null, area, scope, remarks, 
deprecated), datum, ensemble, cs);
@@ -1577,7 +1586,7 @@ codes:  for (int i=0; i<codes.length; i++) {
                     case "parametric": {
                         ParametricCS    cs    = owner.createParametricCS   
(getString(code, result, 8));
                         ParametricDatum datum = 
owner.createParametricDatum(getString(code, result, 9));
-                        DatumEnsemble<ParametricDatum> ensemble = 
tryAsEnsemble(datum, ParametricDatum.class);
+                        DatumEnsemble<ParametricDatum> ensemble = 
wasDatumEnsemble(datum, ParametricDatum.class);
                         if (ensemble != null) datum = null;
                         crs = 
crsFactory.createParametricCRS(createProperties("Coordinate Reference System",
                                 epsg, name, null, area, scope, remarks, 
deprecated), datum, ensemble, cs);
@@ -1622,7 +1631,7 @@ codes:  for (int i=0; i<codes.length; i++) {
      * @return the given datum as an ensemble if it should be considered as 
such, or {@code null} otherwise.
      * @throws ClassCastException if at least one member is not an instance of 
the specified type.
      */
-    private static <D extends Datum> DatumEnsemble<D> tryAsEnsemble(final D 
datum, final Class<D> memberType) {
+    private static <D extends Datum> DatumEnsemble<D> wasDatumEnsemble(final D 
datum, final Class<D> memberType) {
         if (datum instanceof DatumEnsemble<?>) {
             return DefaultDatumEnsemble.castOrCopy((DatumEnsemble<?>) 
datum).cast(memberType);
         }
@@ -1656,17 +1665,18 @@ codes:  for (int i=0; i<codes.length; i++) {
         ArgumentChecks.ensureNonNull("code", code);
         Datum returnValue = null;
         try (ResultSet result = executeQuery("Datum", "DATUM_CODE", 
"DATUM_NAME",
-                "SELECT DATUM_CODE," +
-                      " DATUM_NAME," +
-                      " DATUM_TYPE," +
-                      " ORIGIN_DESCRIPTION," +
-                      " REALIZATION_EPOCH," +
-                      " AREA_OF_USE_CODE," +        // Deprecated since EPSG 
version 10 (always null)
-                      " DATUM_SCOPE," +
-                      " REMARKS," +
-                      " DEPRECATED," +
-                      " ELLIPSOID_CODE," +          // Only for geodetic type
-                      " PRIME_MERIDIAN_CODE" +      // Only for geodetic type
+                "SELECT DATUM_CODE,"             +  // [ 1]
+                      " DATUM_NAME,"             +  // [ 2]
+                      " DATUM_TYPE,"             +  // [ 3]
+                      " ORIGIN_DESCRIPTION,"     +  // [ 4]
+                      " REALIZATION_EPOCH,"      +  // [ 5]
+                      " AREA_OF_USE_CODE,"       +  // [ 6] — Deprecated since 
EPSG version 10 (always null)
+                      " DATUM_SCOPE,"            +  // [ 7]
+                      " REMARKS,"                +  // [ 8]
+                      " DEPRECATED,"             +  // [ 9]
+                      " ELLIPSOID_CODE,"         +  // [10] — Only for 
geodetic type
+                      " PRIME_MERIDIAN_CODE,"    +  // [11] — Only for 
geodetic type
+                      " REALIZATION_METHOD_CODE" +  // [12] — Only for 
vertical type
                 " FROM \"Datum\"" +
                 " WHERE DATUM_CODE = ?", code))
         {
@@ -1736,7 +1746,8 @@ codes:  for (int i=0; i<codes.length; i++) {
                         break;
                     }
                     case "vertical": {
-                        datum = datumFactory.createVerticalDatum(properties, 
null);
+                        final RealizationMethod method = 
getRealizationMethod(getOptionalInteger(result, 12));
+                        datum = datumFactory.createVerticalDatum(properties, 
method);
                         break;
                     }
                     /*
@@ -2459,6 +2470,34 @@ codes:  for (int i=0; i<codes.length; i++) {
         return returnValue;
     }
 
+    /**
+     * Returns the realization method for the specified code.
+     *
+     * @param  code  code of the realization method, or {@code null} if none.
+     * @return realization method, or {@code null} if the given code was null.
+     */
+    private RealizationMethod getRealizationMethod(final Integer code) throws 
FactoryException, SQLException {
+        assert Thread.holdsLock(this);
+        RealizationMethod returnValue = realizationMethods.get(code);
+        if (returnValue == null && code != null) {
+            try (ResultSet result = executeQuery("DatumRealizationMethod",
+                    "SELECT REALIZATION_METHOD_NAME" +
+                    " FROM \"DatumRealizationMethod\"" +
+                    " WHERE REALIZATION_METHOD_CODE = ?", code))
+            {
+                while (result.next()) {
+                    final String name = getString(code, result, 1);
+                    returnValue = 
ensureSingleton(VerticalDatumTypes.fromMethod(name), returnValue, code);
+                }
+            }
+            if (returnValue == null) {
+                throw noSuchAuthorityCode(RealizationMethod.class, 
String.valueOf(code));
+            }
+            realizationMethods.put(code, returnValue);
+        }
+        return returnValue;
+    }
+
     /**
      * Creates an unit of measurement from a code.
      * Current implementation first checks if {@link Units#valueOfEPSG(int)} 
can provide a hard-coded unit
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/SQLTranslator.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/SQLTranslator.java
index d3c836baf9..83a6998c9d 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/SQLTranslator.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/SQLTranslator.java
@@ -366,9 +366,11 @@ public class SQLTranslator implements 
Function<String,String> {
         /*
          * A column named "BASE_CRS_CODE" in EPSG version 9 has been renamed 
"SOURCE_GEOGCRS_CODE" in version 10.
          * Detects which name is used, with precedence to latest database 
version if the two columns are found.
+         * Opportunistically use this detection for generating empty columns 
for fields that did not existed in
+         * the "Datum" table of EPSG version 9.
          */
+        boolean isOldSchema = false;
 skip:   try (ResultSet result = md.getColumns(catalog, schemaPattern, 
toActualTableName("Coordinate Reference System"), baseCRS)) {
-            boolean isOldSchema = false;
             while (result.next()) {
                 final String column = result.getString(Reflection.COLUMN_NAME);
                 if ("BASE_CRS_CODE".equalsIgnoreCase(column)) {
@@ -378,11 +380,12 @@ skip:   try (ResultSet result = md.getColumns(catalog, 
schemaPattern, toActualTa
                     isOldSchema = 
"SOURCE_GEOGCRS_CODE".equalsIgnoreCase(column);
                 }
             }
-            if (isOldSchema) {
-                columnRenaming = new HashMap<>(columnRenaming);
-                columnRenaming.put("BASE_CRS_CODE", "SOURCE_GEOGCRS_CODE");
-                columnRenaming = Map.copyOf(columnRenaming);
-            }
+        }
+        if (isOldSchema) {
+            columnRenaming = new HashMap<>(columnRenaming);
+            columnRenaming.put("BASE_CRS_CODE", "SOURCE_GEOGCRS_CODE");     // 
In table "Coordinate Reference System".
+            addMissingColumn("REALIZATION_METHOD_CODE", "INTEGER");         // 
In table "Datum".
+            columnRenaming = Map.copyOf(columnRenaming);
         }
         /*
          * Detect if the database uses boolean types where applicable.
@@ -420,6 +423,15 @@ skip:   try (ResultSet result = md.getColumns(catalog, 
schemaPattern, toActualTa
                 && !useBoolean;
     }
 
+    /**
+     * Declares that the column of the given name shall be replaced by a 
placeholder.
+     * This method is invoked for columns added in <abbr>EPSG</abbr> version 
10 or later
+     * when we detected that the schema in use is <abbr>EPSG</abbr> version 8 
or before;
+     */
+    private void addMissingColumn(final String column, final String type) {
+        columnRenaming.put(column, "CAST(NULL AS " + type + ") AS " + column);
+    }
+
     /**
      * Returns the catalog that contains the EPSG schema. This is the catalog 
specified at construction time
      * if it was non-null, or the catalog discovered by the constructor 
otherwise.
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Epoch.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Epoch.java
index 5be71863b9..de6020319d 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Epoch.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Epoch.java
@@ -35,8 +35,6 @@ import org.apache.sis.util.privy.Constants;
  * This is a temporary object used for Well-Known Text formatting.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.5
- * @since   1.5
  */
 public final class Epoch extends FormattableObject {
     /**
diff --git 
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
index 58a22e5fb3..ecea0315c6 100644
--- 
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
@@ -24,6 +24,7 @@ import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.cs.AxisDirection;
 import org.apache.sis.util.Characters;
 import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.privy.CodeLists;
 import org.apache.sis.measure.Units;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
@@ -31,11 +32,15 @@ import org.opengis.referencing.datum.RealizationMethod;
 
 
 /**
- * Extensions to the standard set of {@link RealizationEpoch}.
+ * Extensions to the standard set of {@link RealizationMethod}.
  * Some of those constants are derived from a legacy {@code VerticalDatumType} 
code list.
  * 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.
  *
+ * <h2>Note on class naming</h2>
+ * {@code RealizationMethods} could have been a more appropriate name.
+ * For now we keep {@code VerticalDatumTypes} as a way to remind that this is 
currently only about vertical datum.
+ *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  */
 public final class VerticalDatumTypes {
@@ -73,6 +78,11 @@ public final class VerticalDatumTypes {
      */
     static final String BAROMETRIC = "BAROMETRIC";
 
+    /**
+     * A value used in the <abbr>EPSG</abbr> database.
+     */
+    static final String LOCAL = "LOCAL";
+
     /**
      * Do not allow instantiation of this class.
      */
@@ -113,7 +123,7 @@ public final class VerticalDatumTypes {
      * @param  code  the legacy vertical datum code.
      * @return the vertical datum type, or {@code null} if none.
      */
-    public static RealizationMethod fromLegacy(final int code) {
+    public static RealizationMethod fromLegacyCode(final int code) {
         switch (code) {
         //  case 2000: return null;                                     // 
CS_VD_Other
             case 2001: return RealizationMethod.valueOf(ORTHOMETRIC);   // 
CS_VD_Orthometric
@@ -132,7 +142,7 @@ public final class VerticalDatumTypes {
      * @param  method  the realization method, or {@code null} if unknown.
      * @return the legacy code for the given datum type, or 0 if unknown.
      */
-    public static int toLegacy(final RealizationMethod method) {
+    public static int toLegacyCode(final RealizationMethod method) {
         if (method != null) {
             switch (method.name().toUpperCase(Locale.US)) {
                 case ORTHOMETRIC: return 2001;      // CS_VD_Orthometric
@@ -154,7 +164,7 @@ public final class VerticalDatumTypes {
      * @param  method  the realization method, or {@code null}.
      * @return the vertical datum type name (never null).
      */
-    public static String toName(final RealizationMethod method) {
+    public static String toLegacyName(final RealizationMethod method) {
         if (method == RealizationMethod.GEOID) return "geoidal";
         if (method == RealizationMethod.TIDAL) return "depth";
         if (method != null) {
@@ -170,15 +180,36 @@ public final class VerticalDatumTypes {
      * @param  type  the vertical datum type, or {@code null}.
      * @return the realization method, or {@code null} if none.
      */
-    public static RealizationMethod fromName(final String type) {
-        if ("GEOIDAL"  .equalsIgnoreCase(type)) return RealizationMethod.GEOID;
-        if ("DEPTH"    .equalsIgnoreCase(type)) return RealizationMethod.TIDAL;
+    public static RealizationMethod fromLegacyName(final String type) {
+        if ("geoidal"  .equalsIgnoreCase(type)) return RealizationMethod.GEOID;
+        if ("depth"    .equalsIgnoreCase(type)) return RealizationMethod.TIDAL;
+        if (LOCAL      .equalsIgnoreCase(type)) return 
RealizationMethod.valueOf(LOCAL);
         if (BAROMETRIC .equalsIgnoreCase(type)) return 
RealizationMethod.valueOf(BAROMETRIC);
         if (ORTHOMETRIC.equalsIgnoreCase(type)) return 
RealizationMethod.valueOf(ORTHOMETRIC);
         if (ELLIPSOIDAL.equalsIgnoreCase(type)) return ellipsoidal();
         return null;
     }
 
+    /**
+     * Returns the realization method from heuristic rules applied on the name.
+     *
+     * @param  name  the realization method name, or {@code null}.
+     * @return the realization method, or {@code null} if the given name was 
null.
+     */
+    public static RealizationMethod fromMethod(final String name) {
+        RealizationMethod method = fromLegacyName(name);
+        if (method == null && name != null && !name.isBlank()) {
+            final int s = name.lastIndexOf('-');
+            if (s >= 0 && 
name.substring(s+1).strip().equalsIgnoreCase("based")) {
+                method = CodeLists.forCodeName(RealizationMethod.class, 
name.substring(0, s));
+            }
+            if (method == null) {
+                method = RealizationMethod.valueOf(name);
+            }
+        }
+        return method;
+    }
+
     /**
      * 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
@@ -191,16 +222,16 @@ public final class VerticalDatumTypes {
      * @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.
      */
-    public static RealizationMethod guess(final String name, final 
Collection<? extends GenericName> aliases,
+    public static RealizationMethod fromDatum(final String name, final 
Collection<? extends GenericName> aliases,
             final CoordinateSystemAxis axis)
     {
-        RealizationMethod method = guess(name);
+        RealizationMethod method = fromDatum(name);
         if (method != null) {
             return method;
         }
         if (aliases != null) {
             for (final GenericName alias : aliases) {
-                method = guess(alias.tip().toString());
+                method = fromDatum(alias.tip().toString());
                 if (method != null) {
                     return method;
                 }
@@ -230,13 +261,13 @@ public final class VerticalDatumTypes {
     }
 
     /**
-     * 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}.
+     * Guesses the realization method of a datum of the given name.
+     * If the realization method cannot be determined, returns {@code null}.
      *
      * @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 RealizationMethod fromDatum(final String name) {
         if (name != null) {
             if (CharSequences.equalsFiltered("Mean Sea Level", name, 
Characters.Filter.LETTERS_AND_DIGITS, true)) {
                 return RealizationMethod.TIDAL;
diff --git 
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
index 28f4e0ca9f..b5fe441aec 100644
--- 
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
@@ -33,7 +33,6 @@ import org.opengis.referencing.datum.RealizationMethod;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-@SuppressWarnings("deprecation")
 public final class VerticalDatumTypesTest extends TestCase {
     /**
      * Creates a new test case.
@@ -53,23 +52,35 @@ public final class VerticalDatumTypesTest extends TestCase {
     }
 
     /**
-     * Tests the {@link VerticalDatumTypes#fromLegacy(int)} method.
+     * Tests the {@link VerticalDatumTypes#fromLegacyCode(int)} method.
      */
     @Test
-    public void testFromLegacy() {
-        assertEquals(VerticalDatumTypes.ellipsoidal(), 
VerticalDatumTypes.fromLegacy(2002));
-        assertEquals(RealizationMethod .GEOID,         
VerticalDatumTypes.fromLegacy(2005));
-        assertEquals(RealizationMethod .TIDAL,         
VerticalDatumTypes.fromLegacy(2006));
+    public void testFromLegacyCode() {
+        assertEquals(VerticalDatumTypes.ellipsoidal(), 
VerticalDatumTypes.fromLegacyCode(2002));
+        assertEquals(RealizationMethod .GEOID,         
VerticalDatumTypes.fromLegacyCode(2005));
+        assertEquals(RealizationMethod .TIDAL,         
VerticalDatumTypes.fromLegacyCode(2006));
     }
 
     /**
-     * Tests the {@link VerticalDatumTypes#toLegacy(RealizationMethod)} method.
+     * Tests the {@link VerticalDatumTypes#toLegacyCode(RealizationMethod)} 
method.
      */
     @Test
-    public void testToLegacy() {
-        assertEquals(2002, 
VerticalDatumTypes.toLegacy(VerticalDatumTypes.ellipsoidal()));
-        assertEquals(2005, VerticalDatumTypes.toLegacy(RealizationMethod 
.GEOID));
-        assertEquals(2006, VerticalDatumTypes.toLegacy(RealizationMethod 
.TIDAL));
+    public void testToLegacyCode() {
+        assertEquals(2002, 
VerticalDatumTypes.toLegacyCode(VerticalDatumTypes.ellipsoidal()));
+        assertEquals(2005, VerticalDatumTypes.toLegacyCode(RealizationMethod 
.GEOID));
+        assertEquals(2006, VerticalDatumTypes.toLegacyCode(RealizationMethod 
.TIDAL));
+    }
+
+    /**
+     * Tests the {@link VerticalDatumTypes#fromMethod(String)} method
+     * with names from the <abbr>EPSG</abbr> database.
+     */
+    @Test
+    public void testFromMethod() {
+        assertEquals(RealizationMethod .LEVELLING, 
VerticalDatumTypes.fromMethod("Levelling-based"));
+        assertEquals(RealizationMethod .GEOID,     
VerticalDatumTypes.fromMethod("Geoid-based"));
+        assertEquals(RealizationMethod .TIDAL,     
VerticalDatumTypes.fromMethod("Tidal"));
+        assertEquals(VerticalDatumTypes.LOCAL,     
VerticalDatumTypes.fromMethod("Local").name());
     }
 
     /**

Reply via email to