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 96a055335c04097aa10a2411687ee0efbb1425fb
Merge: 48705908fc c4eceb3ef1
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Fri Jul 12 15:19:44 2024 +0200

    Merge branch 'geoapi-3.1': initial implementation of `DatumEnsemble`.

 .../apache/sis/coverage/grid/GridCoverage2D.java   |   1 +
 .../sis/feature/builder/FeatureTypeBuilder.java    |   1 +
 .../main/org/apache/sis/image/ComputedImage.java   |   1 +
 .../apache/sis/metadata/ModifiableMetadata.java    | 102 +++--
 .../apache/sis/metadata/iso/DefaultMetadata.java   |   1 -
 .../sis/metadata/iso/acquisition/DefaultEvent.java |   1 -
 .../iso/acquisition/DefaultRequirement.java        |   1 -
 .../DefaultDigitalTransferOptions.java             |   1 -
 .../sis/metadata/iso/extent/DefaultExtent.java     |   2 -
 .../iso/identification/AbstractIdentification.java |  16 +-
 .../identification/DefaultDataIdentification.java  |   5 +-
 .../metadata/iso/identification/DefaultUsage.java  |   2 -
 .../metadata/iso/lineage/DefaultProcessStep.java   |   1 -
 .../maintenance/DefaultMaintenanceInformation.java |   1 -
 .../org/apache/sis/util/iso/DefaultTypeName.java   |  15 +-
 .../main/org/apache/sis/util/iso/Names.java        |  36 +-
 .../main/org/apache/sis/util/iso/TypeNames.java    |   8 -
 .../main/org/apache/sis/io/wkt/Colors.java         |   3 +-
 .../main/org/apache/sis/io/wkt/Convention.java     |  33 +-
 .../main/org/apache/sis/io/wkt/ElementKind.java    |  16 +-
 .../sis/parameter/DefaultParameterDescriptor.java  |   2 +-
 .../pending/geoapi/referencing/MissingMethods.java |  96 +++++
 .../main/org/apache/sis/referencing/CRS.java       |   9 +-
 .../main/org/apache/sis/referencing/CommonCRS.java |  13 +-
 .../apache/sis/referencing/NamedIdentifier.java    |   1 +
 .../sis/referencing/StandardDefinitions.java       |   4 +-
 .../apache/sis/referencing/crs/AbstractCRS.java    |  45 +++
 .../sis/referencing/crs/AbstractDerivedCRS.java    |  18 +-
 .../sis/referencing/crs/DefaultDerivedCRS.java     |  50 +--
 .../sis/referencing/crs/DefaultEngineeringCRS.java |  75 +++-
 .../sis/referencing/crs/DefaultGeocentricCRS.java  |  76 +++-
 .../sis/referencing/crs/DefaultGeodeticCRS.java    |  54 ++-
 .../sis/referencing/crs/DefaultGeographicCRS.java  |  48 ++-
 .../sis/referencing/crs/DefaultParametricCRS.java  |  71 +++-
 .../sis/referencing/crs/DefaultProjectedCRS.java   |  16 +-
 .../sis/referencing/crs/DefaultTemporalCRS.java    |  75 +++-
 .../sis/referencing/crs/DefaultVerticalCRS.java    |  77 +++-
 .../sis/referencing/datum/AbstractDatum.java       |  91 ++++-
 .../referencing/datum/DefaultDatumEnsemble.java    | 213 +++++++++++
 .../referencing/factory/GeodeticObjectFactory.java | 420 +++++++++++++++++----
 .../sis/referencing/factory/sql/EPSGFactory.java   |   4 +-
 .../apache/sis/referencing/internal/Resources.java |  10 +
 .../sis/referencing/internal/Resources.properties  |   2 +
 .../referencing/internal/Resources_fr.properties   |   2 +
 .../transform/DefaultMathTransformFactory.java     |   1 +
 .../operation/transform/InterpolatedTransform.java |   2 +-
 .../sis/referencing/privy/AffineTransform2D.java   |   4 +
 .../referencing/privy/ReferencingUtilities.java    |   4 +-
 .../apache/sis/referencing/privy/WKTKeywords.java  |   1 +
 .../sis/referencing/ImmutableIdentifierTest.java   |   4 +-
 .../referencing/crs/DefaultEngineeringCRSTest.java |   4 +-
 .../referencing/crs/DefaultGeocentricCRSTest.java  |   2 +-
 .../referencing/crs/DefaultTemporalCRSTest.java    |   2 +-
 .../apache/sis/referencing/crs/HardCodedCRS.java   |  40 +-
 .../factory/IdentifiedObjectFinderTest.java        |   5 +-
 .../operation/CoordinateOperationFinderTest.java   |   2 +-
 .../operation/DefaultConversionTest.java           |  12 +-
 .../operation/DefaultTransformationTest.java       |   2 +-
 .../transform/CoordinateSystemTransformTest.java   |   2 +-
 .../referencing/privy/DefinitionVerifierTest.java  |   2 +-
 .../apache/sis/test/integration/MetadataTest.java  |   2 +-
 .../apache/sis/storage/landsat/LandsatStore.java   |   1 +
 .../apache/sis/storage/geotiff/GeoTiffStore.java   |   1 +
 .../org/apache/sis/storage/netcdf/NetcdfStore.java |   1 +
 .../org/apache/sis/io/stream/ChannelDataInput.java |   1 +
 .../apache/sis/storage/CanNotProbeException.java   |   1 +
 .../main/org/apache/sis/storage/DataStore.java     |   3 +
 .../src/org.apache.sis.util/main/module-info.java  |   3 +-
 .../main/org/apache/sis/pending/jdk/JDK19.java     |  21 ++
 .../org/apache/sis/system/OptionalDependency.java  |   1 +
 .../test/org/apache/sis/util/LocalesTest.java      |  17 +-
 .../apache/sis/gui/coverage/BandRangeTable.java    |   1 +
 .../apache/sis/gui/coverage/GridSliceSelector.java |   1 +
 .../main/org/apache/sis/gui/coverage/GridView.java |   1 +
 .../sis/gui/coverage/ImagePropertyExplorer.java    |   3 +-
 .../org/apache/sis/gui/dataset/FeatureTable.java   |   3 +
 .../main/org/apache/sis/gui/dataset/LogViewer.java |   1 +
 .../apache/sis/gui/dataset/ResourceExplorer.java   |   1 +
 .../org/apache/sis/gui/dataset/ResourceTree.java   |   8 +-
 .../org/apache/sis/gui/internal/LogHandler.java    |   2 +-
 .../apache/sis/gui/internal/io/FileAccessView.java |   1 +
 .../org/apache/sis/gui/map/GestureFollower.java    |   1 +
 .../main/org/apache/sis/gui/map/StatusBar.java     |   1 +
 .../apache/sis/gui/metadata/MetadataSummary.java   |   1 +
 .../sis/gui/metadata/StandardMetadataTree.java     |   1 +
 .../gui/referencing/RecentReferenceSystems.java    |   1 +
 86 files changed, 1527 insertions(+), 360 deletions(-)

diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/FeatureTypeBuilder.java
index e095cccb43,575b5d72ed..75fd60b37e
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/FeatureTypeBuilder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/FeatureTypeBuilder.java
@@@ -204,13 -206,12 +204,14 @@@ public class FeatureTypeBuilder extend
       * to values inferred from the given template. The properties list will 
contain properties
       * declared explicitly in the given template, not including properties 
inherited from super types.
       *
 -     * @param template  an existing feature type to use as a template, or 
{@code null} if none.
 +     * <div class="warning"><b>Warning:</b>
 +     * The {@code template} argument type will be changed to {@code 
FeatureType} if and when such interface
 +     * will be defined in GeoAPI.</div>
       *
 -     * @see #setAll(FeatureType)
 +     * @param template  an existing feature type to use as a template, or 
{@code null} if none.
       */
+     @SuppressWarnings("this-escape")    // The invoked method does not store 
`this` and is not overrideable.
 -    public FeatureTypeBuilder(final FeatureType template) {
 +    public FeatureTypeBuilder(final DefaultFeatureType template) {
          this(null, null, null);
          if (template != null) {
              initialize(template);
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/Names.java
index 79eda67aec,50ee62a5b4..e6015be4fc
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/Names.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/Names.java
@@@ -330,9 -331,9 +331,9 @@@ public final class Names extends Stati
      public static MemberName createMemberName(final CharSequence namespace, 
final String separator,
              final CharSequence localPart, final TypeName attributeType)
      {
-         ensureNonNull("localPart", localPart);
-         ensureNonNull("attributeType", attributeType);
+         ArgumentChecks.ensureNonNull("localPart", localPart);
+         ArgumentChecks.ensureNonNull("attributeType", attributeType);
 -        final NameFactory factory = DefaultNameFactory.provider();
 +        final DefaultNameFactory factory = DefaultNameFactory.provider();
          return factory.createMemberName(createNameSpace(factory, namespace, 
separator), localPart, attributeType);
      }
  
@@@ -421,22 -422,21 +422,23 @@@
          if (type == null) {
              return null;
          }
 -        final Type t = type.toJavaType().orElse(null);
 -        if (t instanceof Class<?>) {
 -            return (Class<?>) t;
 +        if (type instanceof DefaultTypeName) {
 +            final Type t = ((DefaultTypeName) type).toJavaType().orElse(null);
 +            if (t instanceof Class<?>) {
 +                return (Class<?>) t;
 +            }
          }
-         final Class<?> c;
+         ClassNotFoundException cause;
          try {
-             c = TypeNames.toClass(TypeNames.namespace(type.scope()), 
type.toString());
+             final Class<?> c = 
TypeNames.toClass(TypeNames.namespace(type.scope()), type.toString());
+             if (c != Void.TYPE) {
+                 return c;
+             }
+             cause = null;
          } catch (ClassNotFoundException e) {
-             throw new UnknownNameException(TypeNames.unknown(type), e);
-         }
-         if (c == Void.TYPE) {
-             throw new UnknownNameException(TypeNames.unknown(type));
+             cause = e;
          }
-         return c;
+         throw new 
UnknownNameException(Errors.format(Errors.Keys.UnknownType_1, 
type.toFullyQualifiedName()), cause);
      }
  
      /**
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/ElementKind.java
index 1e00e8b74a,6979e1d209..6c5649aaf1
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/ElementKind.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/ElementKind.java
@@@ -27,6 -28,6 +27,9 @@@ import org.opengis.referencing.operatio
  import org.opengis.parameter.GeneralParameterValue;
  import org.apache.sis.util.Numbers;
  
++// Specific to the main branch:
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
++
  
  /**
   * Kind of an element in a <i>Well Known Text</i>.
@@@ -145,6 -155,7 +157,7 @@@ public enum ElementKind 
       *   <caption>Mapping from Java type to WKT element</caption>
       *   <tr><th>Base type</th>                     <th>Kind</th></tr>
       *   <tr><td>{@link Datum}</td>                 <td>{@link 
#DATUM}</td></tr>
 -     *   <tr><td>{@link DatumEnsemble}</td>         <td>{@link 
#ENSEMBLE}</td></tr>
++     *   <tr><td>{@link DefaultDatumEnsemble}</td>  <td>{@link 
#ENSEMBLE}</td></tr>
       *   <tr><td>{@link OperationMethod}</td>       <td>{@link 
#METHOD}</td></tr>
       *   <tr><td>{@link GeneralParameterValue}</td> <td>{@link 
#PARAMETER}</td></tr>
       *   <tr><td>{@link CoordinateSystemAxis}</td>  <td>{@link 
#AXIS}</td></tr>
@@@ -165,6 -176,7 +178,7 @@@
      public static ElementKind forType(final Class<?> type) {
          if (type != null) {
              if (Datum                .class.isAssignableFrom(type)) return 
DATUM;
 -            if (DatumEnsemble        .class.isAssignableFrom(type)) return 
ENSEMBLE;
++            if (DefaultDatumEnsemble .class.isAssignableFrom(type)) return 
ENSEMBLE;
              if (OperationMethod      .class.isAssignableFrom(type)) return 
METHOD;
              if (GeneralParameterValue.class.isAssignableFrom(type)) return 
PARAMETER;
              if (CoordinateSystemAxis .class.isAssignableFrom(type)) return 
AXIS;
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/pending/geoapi/referencing/MissingMethods.java
index 0000000000,0000000000..a84e462276
new file mode 100644
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/pending/geoapi/referencing/MissingMethods.java
@@@ -1,0 -1,0 +1,96 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.sis.pending.geoapi.referencing;
++
++import java.util.function.Function;
++import org.opengis.referencing.crs.*;
++import org.opengis.referencing.datum.*;
++import org.apache.sis.referencing.crs.DefaultVerticalCRS;
++import org.apache.sis.referencing.crs.DefaultTemporalCRS;
++import org.apache.sis.referencing.crs.DefaultEngineeringCRS;
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
++
++
++/**
++ * Placeholder for methods missing in the GeoAPI 3.0 interface.
++ */
++public final class MissingMethods {
++    /**
++     * To be set by static {@code AbstractCRS} initializer.
++     */
++    public static volatile Function<CoordinateReferenceSystem, 
DefaultDatumEnsemble<?>> datumEnsemble;
++
++    /**
++     * To be set by static {@code DefaultGeodeticCRS} initializer.
++     */
++    public static volatile Function<GeodeticCRS, 
DefaultDatumEnsemble<GeodeticDatum>> geodeticDatumEnsemble;
++
++    private MissingMethods() {
++    }
++
++    /**
++     * Returns the datum ensemble of an arbitrary CRS.
++     *
++     * @param  datum  the CRS from which to get a datum ensemble, or {@code 
null} if none.
++     * @return the datum ensemble, or {@code null} if none.
++     */
++    public static DefaultDatumEnsemble<?> getDatumEnsemble(final 
CoordinateReferenceSystem crs) {
++        final var m = datumEnsemble;
++        return (m != null) ? m.apply(crs) : null;
++    }
++
++    /**
++     * Returns the datum ensemble of an arbitrary geodetic CRS.
++     *
++     * @param  datum  the CRS from which to get a datum ensemble, or {@code 
null} if none.
++     * @return the datum ensemble, or {@code null} if none.
++     */
++    public static DefaultDatumEnsemble<GeodeticDatum> getDatumEnsemble(final 
GeodeticCRS crs) {
++        final var m = geodeticDatumEnsemble;
++        return (m != null) ? m.apply(crs) : null;
++    }
++
++    /**
++     * Returns the datum ensemble of an arbitrary vertical CRS.
++     *
++     * @param  datum  the CRS from which to get a datum ensemble, or {@code 
null} if none.
++     * @return the datum ensemble, or {@code null} if none.
++     */
++    public static DefaultDatumEnsemble<VerticalDatum> getDatumEnsemble(final 
VerticalCRS crs) {
++        return (crs instanceof DefaultVerticalCRS) ? ((DefaultVerticalCRS) 
crs).getDatumEnsemble() : null;
++    }
++
++    /**
++     * Returns the datum ensemble of an arbitrary temporal CRS.
++     *
++     * @param  datum  the CRS from which to get a datum ensemble, or {@code 
null} if none.
++     * @return the datum ensemble, or {@code null} if none.
++     */
++    public static DefaultDatumEnsemble<TemporalDatum> getDatumEnsemble(final 
TemporalCRS crs) {
++        return (crs instanceof DefaultTemporalCRS) ? ((DefaultTemporalCRS) 
crs).getDatumEnsemble() : null;
++    }
++
++    /**
++     * Returns the datum ensemble of an arbitrary engineering CRS.
++     *
++     * @param  datum  the CRS from which to get a datum ensemble, or {@code 
null} if none.
++     * @return the datum ensemble, or {@code null} if none.
++     */
++    public static DefaultDatumEnsemble<EngineeringDatum> 
getDatumEnsemble(final EngineeringCRS crs) {
++        return (crs instanceof DefaultEngineeringCRS) ? 
((DefaultEngineeringCRS) crs).getDatumEnsemble() : null;
++    }
++}
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java
index 3022a83648,2cdf0dc587..b38190d740
--- 
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
@@@ -93,9 -93,14 +93,10 @@@ import org.apache.sis.util.logging.Logg
  // Specific to the main and geoapi-3.1 branches:
  import org.opengis.referencing.crs.GeneralDerivedCRS;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.geometry.Geometry;
 -import org.opengis.referencing.ObjectDomain;
 -import org.opengis.referencing.crs.DerivedCRS;
 -import org.opengis.referencing.datum.DynamicReferenceFrame;
 -import org.opengis.metadata.extent.BoundingPolygon;
 -import org.opengis.metadata.extent.GeographicExtent;
 -import org.opengis.coordinate.CoordinateMetadata;
 +// Specific to the main branch:
 +import org.apache.sis.pending.geoapi.referencing.DynamicReferenceFrame;
 +import org.apache.sis.coordinate.DefaultCoordinateMetadata;
++import static 
org.apache.sis.pending.geoapi.referencing.MissingMethods.getDatumEnsemble;
  
  
  /**
@@@ -1246,7 -1293,8 +1247,8 @@@ public final class CRS extends Static 
                   */
                  final Map<String, ?> properties = 
ReferencingUtilities.getPropertiesForModifiedCRS(crs);
                  if (crs instanceof GeodeticCRS) {
-                     return new DefaultGeographicCRS(properties, 
((GeodeticCRS) crs).getDatum(), (EllipsoidalCS) cs);
+                     final var source = (GeodeticCRS) crs;
 -                    return new DefaultGeographicCRS(properties, 
source.getDatum(), source.getDatumEnsemble(), (EllipsoidalCS) cs);
++                    return new DefaultGeographicCRS(properties, 
source.getDatum(), getDatumEnsemble(source), (EllipsoidalCS) cs);
                  }
                  /*
                   * In Apache SIS implementation, the Conversion contains the 
source and target CRS together with
@@@ -1264,7 -1312,8 +1266,8 @@@
                  /*
                   * If the CRS is neither geographic or projected, then it is 
engineering.
                   */
-                 return new DefaultEngineeringCRS(properties, 
((EngineeringCRS) crs).getDatum(), cs);
+                 final var source = (EngineeringCRS) crs;
 -                return new DefaultEngineeringCRS(properties, 
source.getDatum(), source.getDatumEnsemble(), cs);
++                return new DefaultEngineeringCRS(properties, 
source.getDatum(), getDatumEnsemble(source), cs);
              }
          }
          if (crs instanceof CompoundCRS) {
@@@ -1335,7 -1384,7 +1338,7 @@@
                  VerticalCRS c = CommonCRS.Vertical.ELLIPSOIDAL.crs();
                  if (!c.getCoordinateSystem().getAxis(0).equals(axis)) {
                      final Map<String,?> properties = 
IdentifiedObjects.getProperties(c);
-                     c = new DefaultVerticalCRS(properties, c.getDatum(), new 
DefaultVerticalCS(properties, axis));
 -                    c = new DefaultVerticalCRS(properties, c.getDatum(), 
c.getDatumEnsemble(), new DefaultVerticalCS(properties, axis));
++                    c = new DefaultVerticalCRS(properties, c.getDatum(), 
getDatumEnsemble(c), new DefaultVerticalCS(properties, axis));
                  }
                  return c;
              }
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CommonCRS.java
index 62c37c6d07,5f54b5e9d5..f3d219197f
--- 
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
@@@ -86,9 -86,8 +86,10 @@@ import org.apache.sis.measure.Latitude
  import org.apache.sis.measure.Units;
  import static org.apache.sis.util.privy.Constants.SECONDS_PER_DAY;
  
 -// 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.crs.GeocentricCRS;
 +import org.opengis.referencing.datum.VerticalDatumType;
++import static 
org.apache.sis.pending.geoapi.referencing.MissingMethods.getDatumEnsemble;
  
  
  /**
@@@ -717,7 -716,7 +718,7 @@@ public enum CommonCRS 
                          cs = (EllipsoidalCS) 
StandardDefinitions.createCoordinateSystem(StandardDefinitions.ELLIPSOIDAL_3D, 
true);
                      }
                      // Use same name and datum than the geographic CRS.
-                     object = new DefaultGeographicCRS(properties(base, 
geo3D), base.getDatum(), cs);
 -                    object = new DefaultGeographicCRS(properties(base, 
geo3D), base.getDatum(), base.getDatumEnsemble(), cs);
++                    object = new DefaultGeographicCRS(properties(base, 
geo3D), base.getDatum(), getDatumEnsemble(base), cs);
                      cachedGeo3D = object;
                  }
              }
@@@ -782,7 -777,7 +783,7 @@@
                      if (cs == null) {
                          cs = (CartesianCS) 
StandardDefinitions.createCoordinateSystem(StandardDefinitions.EARTH_CENTRED, 
true);
                      }
-                     object = new DefaultGeocentricCRS(properties(base, 
geocentric), base.getDatum(), cs);
 -                    object = new DefaultGeocentricCRS(properties(base, 
geocentric), base.getDatum(), base.getDatumEnsemble(), cs);
++                    object = new DefaultGeocentricCRS(properties(base, 
geocentric), base.getDatum(), getDatumEnsemble(base), cs);
                      cachedGeocentric = object;
                  }
              }
@@@ -837,7 -828,7 +838,7 @@@
                      if (cs == null) {
                          cs = (SphericalCS) 
StandardDefinitions.createCoordinateSystem(StandardDefinitions.SPHERICAL, true);
                      }
-                     object = new 
DefaultGeocentricCRS(IdentifiedObjects.getProperties(base, exclude()), 
base.getDatum(), cs);
 -                    object = new 
DefaultGeocentricCRS(IdentifiedObjects.getProperties(base, exclude()), 
base.getDatum(), base.getDatumEnsemble(), cs);
++                    object = new 
DefaultGeocentricCRS(IdentifiedObjects.getProperties(base, exclude()), 
base.getDatum(), getDatumEnsemble(base), cs);
                      cachedSpherical = object;
                  }
              }
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java
index 013953fcb6,9fafafbdc2..c47378a318
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java
@@@ -38,6 -40,7 +40,8 @@@ import org.apache.sis.referencing.privy
  import org.apache.sis.metadata.privy.ImplementationHelper;
  import org.apache.sis.io.wkt.Convention;
  import org.apache.sis.io.wkt.Formatter;
++import org.apache.sis.pending.geoapi.referencing.MissingMethods;
+ import org.apache.sis.util.ArgumentChecks;
  import org.apache.sis.util.Utilities;
  import org.apache.sis.util.ComparisonMode;
  import org.apache.sis.util.resources.Errors;
@@@ -46,8 -49,9 +50,9 @@@
  import org.opengis.referencing.crs.GeneralDerivedCRS;
  import org.opengis.geometry.MismatchedDimensionException;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.metadata.Identifier;
 -import org.opengis.referencing.datum.DatumEnsemble;
 +// Specific to the main branch:
 +import org.opengis.referencing.ReferenceIdentifier;
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
  
  
  /**
@@@ -207,6 -211,30 +212,30 @@@ public class AbstractCRS extends Abstra
                  Errors.Keys.MismatchedDimension_3, "cs", expected, actual));
      }
  
+     /**
+      * Verifies the consistency between the datum and the ensemble.
+      * At least one of the {@code datum} and {@code ensemble} arguments shall 
be non-null.
+      *
+      * @param  datum       the datum, or {@code null} if the CRS is 
associated only to a datum ensemble.
+      * @param  ensemble    collection of reference frames which for low 
accuracy requirements may be considered to be
+      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
+      * @throws NullPointerException if both arguments are null.
+      * @throws IllegalArgumentException
+      */
 -    static <D extends Datum> void checkDatum(final D datum, final 
DatumEnsemble<D> ensemble) {
++    static <D extends Datum> void checkDatum(final D datum, final 
DefaultDatumEnsemble<D> ensemble) {
+         if (ensemble == null) {
+             ArgumentChecks.ensureNonNull("datum", datum);
+         } else if (datum != null) {
+             for (final D member : ensemble.getMembers()) {
+                 if (Utilities.equalsIgnoreMetadata(datum, member)) {
+                     return;
+                 }
+             }
+             throw new 
IllegalArgumentException(Resources.format(Resources.Keys.NotAMemberOfDatumEnsemble_2,
+                     IdentifiedObjects.getDisplayName(ensemble), 
IdentifiedObjects.getDisplayName(datum)));
+         }
+     }
+ 
      /**
       * Creates a new CRS derived from the specified one, but with different 
axis order or unit.
       *
@@@ -305,6 -332,6 +334,22 @@@
          return (this instanceof SingleCRS) ? ((SingleCRS) this).getDatum() : 
null;
      }
  
++    /**
++     * Returns the datum ensemble.
++     *
++     * @return the datum ensemble, or {@code null} if none.
++     */
++    DefaultDatumEnsemble<?> getDatumEnsemble() {
++        return null;
++    }
++
++    /**
++     * Initializes the handler for getting datum ensemble of an arbitrary CRS.
++     */
++    static {
++        MissingMethods.datumEnsemble = (crs) -> (crs instanceof AbstractCRS) 
? ((AbstractCRS) crs).getDatumEnsemble() : null;
++    }
++
      /**
       * Returns the coordinate system.
       *
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
index 7551b8a41f,87123736ca..3c475877ed
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
@@@ -282,10 -284,10 +282,10 @@@ public class DefaultDerivedCRS extends 
          if (baseCRS != null && derivedCS != null) {
              final String type = getTypeKeyword(properties, baseCRS, 
derivedCS);
              if (type != null) switch (type) {
-                 case WKTKeywords.GeodeticCRS:   return new Geodetic  
(properties, (GeodeticCRS)   baseCRS, conversion,                derivedCS);
-                 case WKTKeywords.VerticalCRS:   return new Vertical  
(properties, (VerticalCRS)   baseCRS, conversion,   (VerticalCS) derivedCS);
-                 case WKTKeywords.TimeCRS:       return new Temporal  
(properties, (TemporalCRS)   baseCRS, conversion,       (TimeCS) derivedCS);
-                 case WKTKeywords.ParametricCRS: return new 
Parametric(properties, (ParametricCRS) baseCRS, conversion, 
(DefaultParametricCS) derivedCS);
+                 case WKTKeywords.GeodeticCRS:   return new Geodetic  
(properties, (GeodeticCRS)   baseCRS, baseToDerived,                derivedCS);
+                 case WKTKeywords.VerticalCRS:   return new Vertical  
(properties, (VerticalCRS)   baseCRS, baseToDerived,   (VerticalCS) derivedCS);
+                 case WKTKeywords.TimeCRS:       return new Temporal  
(properties, (TemporalCRS)   baseCRS, baseToDerived,       (TimeCS) derivedCS);
 -                case WKTKeywords.ParametricCRS: return new 
Parametric(properties, (ParametricCRS) baseCRS, baseToDerived, (ParametricCS) 
derivedCS);
++                case WKTKeywords.ParametricCRS: return new 
Parametric(properties, (ParametricCRS) baseCRS, baseToDerived, 
(DefaultParametricCS) derivedCS);
                  case WKTKeywords.EngineeringCRS: {
                      /*
                       * This case may happen for baseCRS of kind GeodeticCRS, 
ProjectedCRS or EngineeringCRS.
@@@ -810,8 -827,8 +810,8 @@@
          }
  
          /** Creates a new parametric CRS from the given properties. */
-         Parametric(Map<String,?> properties, ParametricCRS baseCRS, 
Conversion conversion, DefaultParametricCS derivedCS) {
-             super(properties, baseCRS, conversion, derivedCS);
 -        Parametric(Map<String,?> properties, ParametricCRS baseCRS, 
Conversion baseToDerived, ParametricCS derivedCS) {
++        Parametric(Map<String,?> properties, ParametricCRS baseCRS, 
Conversion baseToDerived, DefaultParametricCS derivedCS) {
+             super(properties, baseCRS, baseToDerived, derivedCS);
          }
  
          /** Creates a new parametric CRS from the given properties. */
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultEngineeringCRS.java
index bb2cc2743a,0366a81529..ee0410f016
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultEngineeringCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultEngineeringCRS.java
@@@ -32,6 -31,9 +31,9 @@@ import org.apache.sis.referencing.privy
  import org.apache.sis.xml.bind.referencing.CS_CoordinateSystem;
  import org.apache.sis.io.wkt.Formatter;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.referencing.datum.DatumEnsemble;
++// Specific to the main branch:
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
+ 
  
  /**
   * A 1-, 2- or 3-dimensional contextually local coordinate reference system.
@@@ -96,8 -98,18 +98,18 @@@ public class DefaultEngineeringCRS exte
      @SuppressWarnings("serial")         // Most SIS implementations are 
serializable.
      private EngineeringDatum datum;
  
+     /**
+      * Collection of reference frames which for low accuracy requirements may 
be considered to be
+      * insignificantly different from each other. May be {@code null} if 
there is no such ensemble.
+      *
+      * @see #getDatumEnsemble()
+      */
+     @SuppressWarnings("serial")     // Most SIS implementations are 
serializable.
 -    private final DatumEnsemble<EngineeringDatum> ensemble;
++    private final DefaultDatumEnsemble<EngineeringDatum> ensemble;
+ 
      /**
       * Creates a coordinate reference system from the given properties, datum 
and coordinate system.
+      * At least one of the {@code datum} and {@code ensemble} arguments shall 
be non-null.
       * The properties given in argument follow the same rules as for the
       * {@linkplain AbstractReferenceSystem#AbstractReferenceSystem(Map) 
super-class constructor}.
       * The following table is a reminder of main (not all) properties:
@@@ -139,10 -155,24 +155,24 @@@
       */
      public DefaultEngineeringCRS(final Map<String,?> properties,
                                   final EngineeringDatum datum,
 -                                 final DatumEnsemble<EngineeringDatum> 
ensemble,
++                                 final DefaultDatumEnsemble<EngineeringDatum> 
ensemble,
                                   final CoordinateSystem cs)
      {
          super(properties, cs);
-         this.datum = Objects.requireNonNull(datum);
+         this.datum    = datum;
+         this.ensemble = ensemble;
+         checkDatum(datum, ensemble);
+     }
+ 
+     /**
 -     * @deprecated A {@code DatumEnsemble} argument has been added.
++     * @deprecated A {@code DefaultDatumEnsemble} argument has been added.
+      */
+     @Deprecated(since="1.5", forRemoval=true)
+     public DefaultEngineeringCRS(final Map<String,?> properties,
+                                  final EngineeringDatum datum,
+                                  final CoordinateSystem cs)
+     {
+         this(properties, datum, null, cs);
      }
  
      /**
@@@ -167,7 -198,9 +198,9 @@@
       */
      protected DefaultEngineeringCRS(final EngineeringCRS crs) {
          super(crs);
-         datum = crs.getDatum();
+         datum    = crs.getDatum();
 -        ensemble = crs.getDatumEnsemble();
++        ensemble = (crs instanceof DefaultEngineeringCRS) ? 
((DefaultEngineeringCRS) crs).getDatumEnsemble() : null;
+         checkDatum(datum, ensemble);
      }
  
      /**
@@@ -212,6 -248,22 +248,26 @@@
          return datum;
      }
  
+     /**
+      * Returns a collection of datums which, for low accuracy requirements,
+      * may be considered to be insignificantly different from each other.
+      * This property may be null if this <abbr>CRS</abbr> is related to an 
object
+      * identified only by a single {@linkplain #getDatum() datum}.
+      *
++     * <div class="warning"><b>Warning:</b> in a future SIS version, the 
return type may
++     * be changed to the {@code org.opengis.referencing.datum.DatumEnsemble} 
interface.
++     * This change is pending GeoAPI revision.</div>
++     *
+      * @return the datum ensemble, or {@code null} if this <abbr>CRS</abbr> 
is related
+      *         to an object identified only by a single {@linkplain 
#getDatum() datum}.
+      *
+      * @since 1.5
+      */
+     @Override
 -    public DatumEnsemble<EngineeringDatum> getDatumEnsemble() {
++    public DefaultDatumEnsemble<EngineeringDatum> getDatumEnsemble() {
+         return ensemble;
+     }
+ 
      /**
       * {@inheritDoc}
       *
@@@ -354,7 -408,7 +411,7 @@@
  
      /**
       * Returns the coordinate system if it is not an instance of any of the 
types handled by specialized methods.
--     * It is the case of {@link EllipsoidalCS}, {@link VerticalCS}, {@link 
TimeCS} and {@link ParametricCS}.
++     * It is the case of {@link EllipsoidalCS}, {@link VerticalCS}, {@link 
TimeCS} and {@link DefaultParametricCS}.
       */
      @XmlElement(name = "coordinateSystem", required = true)
      @XmlJavaTypeAdapter(CS_CoordinateSystem.class)
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java
index 96abe6b511,376505ba97..bed6a0eb05
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java
@@@ -29,6 -29,6 +29,9 @@@ import org.apache.sis.referencing.cs.Ab
  import org.apache.sis.util.ArgumentChecks;
  import org.apache.sis.io.wkt.Formatter;
  
++// Specific to the main branch:
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
++
  // Specific to the main and geoapi-3.1 branches:
  import org.opengis.referencing.crs.GeocentricCRS;
  
@@@ -123,16 -128,21 +127,21 @@@ public class DefaultGeocentricCRS exten
       * </table>
       *
       * @param  properties  the properties to be given to the coordinate 
reference system.
-      * @param  datum       the datum.
+      * @param  datum       the datum, or {@code null} if the CRS is 
associated only to a datum ensemble.
+      * @param  ensemble    collection of reference frames which for low 
accuracy requirements may be considered to be
+      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
       * @param  cs          the coordinate system, which must be 
three-dimensional.
       *
 -     * @see 
org.apache.sis.referencing.factory.GeodeticObjectFactory#createGeodeticCRS(Map, 
GeodeticDatum, CartesianCS)
 +     * @see 
org.apache.sis.referencing.factory.GeodeticObjectFactory#createGeocentricCRS(Map,
 GeodeticDatum, CartesianCS)
+      *
+      * @since 1.5
       */
      public DefaultGeocentricCRS(final Map<String,?> properties,
                                  final GeodeticDatum datum,
-                                 final CartesianCS   cs)
 -                                final DatumEnsemble<GeodeticDatum> ensemble,
++                                final DefaultDatumEnsemble<GeodeticDatum> 
ensemble,
+                                 final CartesianCS cs)
      {
-         super(properties, datum, cs);
+         super(properties, datum, ensemble, cs);
          checkDimension(3, 3, cs);
      }
  
@@@ -142,19 -153,46 +152,46 @@@
       * {@linkplain #DefaultGeocentricCRS(Map, GeodeticDatum, CartesianCS) 
above constructor}.
       *
       * @param  properties  the properties to be given to the coordinate 
reference system.
-      * @param  datum       the datum.
+      * @param  datum       the datum, or {@code null} if the CRS is 
associated only to a datum ensemble.
+      * @param  ensemble    collection of reference frames which for low 
accuracy requirements may be considered to be
+      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
       * @param  cs          the coordinate system.
       *
 -     * @see 
org.apache.sis.referencing.factory.GeodeticObjectFactory#createGeodeticCRS(Map, 
GeodeticDatum, SphericalCS)
 +     * @see 
org.apache.sis.referencing.factory.GeodeticObjectFactory#createGeocentricCRS(Map,
 GeodeticDatum, SphericalCS)
+      *
+      * @since 1.5
       */
      public DefaultGeocentricCRS(final Map<String,?> properties,
                                  final GeodeticDatum datum,
-                                 final SphericalCS   cs)
 -                                final DatumEnsemble<GeodeticDatum> ensemble,
++                                final DefaultDatumEnsemble<GeodeticDatum> 
ensemble,
+                                 final SphericalCS cs)
      {
-         super(properties, datum, cs);
+         super(properties, datum, ensemble, cs);
          checkDimension(2, 3, cs);
      }
  
+     /**
 -     * @deprecated A {@code DatumEnsemble} argument has been added.
++     * @deprecated A {@code DefaultDatumEnsemble} argument has been added.
+      */
+     @Deprecated(since="1.5", forRemoval=true)
+     public DefaultGeocentricCRS(final Map<String,?> properties,
+                                 final GeodeticDatum datum,
+                                 final CartesianCS cs)
+     {
+         this(properties, datum, null, cs);
+     }
+ 
+     /**
 -     * @deprecated A {@code DatumEnsemble} argument has been added.
++     * @deprecated A {@code DefaultDatumEnsemble} argument has been added.
+      */
+     @Deprecated(since="1.5", forRemoval=true)
+     public DefaultGeocentricCRS(final Map<String,?> properties,
+                                 final GeodeticDatum datum,
+                                 final SphericalCS cs)
+     {
+         this(properties, datum, null, cs);
+     }
+ 
      /**
       * Creates a new CRS derived from the specified one, but with different 
axis order or unit.
       * This is for implementing the {@link #createSameType(AbstractCS)} 
method only.
@@@ -230,6 -266,22 +270,26 @@@
          return super.getDatum();
      }
  
+     /**
+      * Returns a collection of datums which, for low accuracy requirements,
+      * may be considered to be insignificantly different from each other.
+      * This property may be null if this <abbr>CRS</abbr> is related to an 
object
+      * identified only by a single {@linkplain #getDatum() datum}.
+      *
++     * <div class="warning"><b>Warning:</b> in a future SIS version, the 
return type may
++     * be changed to the {@code org.opengis.referencing.datum.DatumEnsemble} 
interface.
++     * This change is pending GeoAPI revision.</div>
++     *
+      * @return the datum ensemble, or {@code null} if this <abbr>CRS</abbr> 
is related
+      *         to an object identified only by a single {@linkplain 
#getDatum() datum}.
+      *
+      * @since 1.5
+      */
+     @Override
 -    public DatumEnsemble<GeodeticDatum> getDatumEnsemble() {
++    public DefaultDatumEnsemble<GeodeticDatum> getDatumEnsemble() {
+         return ensemble;
+     }
+ 
      /**
       * {@inheritDoc}
       *
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java
index ae28b165c8,7f706ab223..b1f79eb8e8
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java
@@@ -31,7 -30,7 +30,6 @@@ import org.opengis.referencing.crs.Geod
  import org.opengis.referencing.crs.SingleCRS;
  import org.opengis.referencing.datum.GeodeticDatum;
  import org.opengis.referencing.datum.PrimeMeridian;
--import org.opengis.metadata.Identifier;
  import org.apache.sis.referencing.AbstractReferenceSystem;
  import org.apache.sis.referencing.CRS;
  import org.apache.sis.referencing.cs.AbstractCS;
@@@ -46,8 -45,8 +44,10 @@@ import org.apache.sis.io.wkt.Convention
  import org.apache.sis.io.wkt.Formatter;
  import org.apache.sis.measure.Units;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.referencing.datum.DatumEnsemble;
 +// Specific to the main branch:
 +import org.opengis.referencing.ReferenceIdentifier;
++import org.apache.sis.pending.geoapi.referencing.MissingMethods;
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
  
  
  /**
@@@ -89,6 -88,15 +89,15 @@@ class DefaultGeodeticCRS extends Abstra
      @SuppressWarnings("serial")     // Most SIS implementations are 
serializable.
      private GeodeticDatum datum;
  
+     /**
+      * Collection of reference frames which for low accuracy requirements may 
be considered to be
+      * insignificantly different from each other. May be {@code null} if 
there is no such ensemble.
+      *
+      * @see #getDatumEnsemble()
+      */
+     @SuppressWarnings("serial")     // Most SIS implementations are 
serializable.
 -    final DatumEnsemble<GeodeticDatum> ensemble;
++    final DefaultDatumEnsemble<GeodeticDatum> ensemble;
+ 
      /**
       * Creates a coordinate reference system from the given properties, datum 
and coordinate system.
       * The properties given in argument follow the same rules as for the
@@@ -102,6 -112,7 +113,7 @@@
       */
      DefaultGeodeticCRS(final Map<String,?> properties,
                         final GeodeticDatum datum,
 -                       final DatumEnsemble<GeodeticDatum> ensemble,
++                       final DefaultDatumEnsemble<GeodeticDatum> ensemble,
                         final CoordinateSystem cs)
      {
          super(properties, cs);
@@@ -112,9 -125,10 +126,10 @@@
       * Creates a new CRS derived from the specified one, but with different 
axis order or unit.
       * This is for implementing the {@link #createSameType(AbstractCS)} 
method only.
       */
 -    DefaultGeodeticCRS(final DefaultGeodeticCRS original, final Identifier 
id, final AbstractCS cs) {
 +    DefaultGeodeticCRS(final DefaultGeodeticCRS original, final 
ReferenceIdentifier id, final AbstractCS cs) {
          super(original, id, cs);
-         datum = original.datum;
+         datum    = original.datum;
+         ensemble = original.ensemble;
      }
  
      /**
@@@ -128,7 -142,9 +143,9 @@@
       */
      protected DefaultGeodeticCRS(final GeodeticCRS crs) {
          super(crs);
-         datum = crs.getDatum();
+         datum    = crs.getDatum();
 -        ensemble = crs.getDatumEnsemble();
++        ensemble = (crs instanceof DefaultGeodeticCRS) ? 
((DefaultGeodeticCRS) crs).getDatumEnsemble() : null;
+         checkDatum(datum, ensemble);
      }
  
      /**
@@@ -171,6 -187,6 +188,24 @@@
          return datum;
      }
  
++    /**
++     * Returns the datum ensemble.
++     *
++     * @return the datum ensemble, or {@code null} if none.
++     */
++    @Override
++    DefaultDatumEnsemble<GeodeticDatum> getDatumEnsemble() {
++        return ensemble;
++    }
++
++    /**
++     * Initializes the handler for getting datum ensemble of an arbitrary CRS.
++     */
++    static {
++        MissingMethods.geodeticDatumEnsemble = (crs) ->
++                (crs instanceof DefaultGeodeticCRS) ? ((DefaultGeodeticCRS) 
crs).getDatumEnsemble() : null;
++    }
++
      /**
       * Returns a coordinate reference system of the same type as this CRS but 
with different axes.
       * This method shall be overridden by all {@code DefaultGeodeticCRS} 
subclasses in this package.
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java
index 98c88f661c,e8b4027634..9dfa6615ef
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java
@@@ -38,8 -38,9 +38,9 @@@ import static org.apache.sis.util.privy
  import static org.apache.sis.util.privy.Constants.CRS83;
  import static org.apache.sis.util.privy.Constants.CRS84;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.metadata.Identifier;
 -import org.opengis.referencing.datum.DatumEnsemble;
 +// Specific to the main branch:
 +import org.opengis.referencing.ReferenceIdentifier;
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
  
  
  /**
@@@ -151,12 -157,24 +157,24 @@@ public class DefaultGeographicCRS exten
       */
      public DefaultGeographicCRS(final Map<String,?> properties,
                                  final GeodeticDatum datum,
 -                                final DatumEnsemble<GeodeticDatum> ensemble,
++                                final DefaultDatumEnsemble<GeodeticDatum> 
ensemble,
                                  final EllipsoidalCS cs)
      {
-         super(properties, datum, cs);
+         super(properties, datum, ensemble, cs);
          checkDimension(2, 3, cs);
      }
  
+     /**
 -     * @deprecated A {@code DatumEnsemble} argument has been added.
++     * @deprecated A {@code DefaultDatumEnsemble} argument has been added.
+      */
+     @Deprecated(since="1.5", forRemoval=true)
+     public DefaultGeographicCRS(final Map<String,?> properties,
+                                 final GeodeticDatum datum,
+                                 final EllipsoidalCS cs)
+     {
+         this(properties, datum, null, cs);
+     }
+ 
      /**
       * Creates a new CRS derived from the specified one, but with different 
axis order or unit.
       * This is for implementing the {@link #createSameType(AbstractCS)} 
method only.
@@@ -237,6 -257,22 +257,26 @@@
          return super.getDatum();
      }
  
+     /**
+      * Returns a collection of datums which, for low accuracy requirements,
+      * may be considered to be insignificantly different from each other.
+      * This property may be null if this <abbr>CRS</abbr> is related to an 
object
+      * identified only by a single {@linkplain #getDatum() datum}.
+      *
++     * <div class="warning"><b>Warning:</b> in a future SIS version, the 
return type may
++     * be changed to the {@code org.opengis.referencing.datum.DatumEnsemble} 
interface.
++     * This change is pending GeoAPI revision.</div>
++     *
+      * @return the datum ensemble, or {@code null} if this <abbr>CRS</abbr> 
is related
+      *         to an object identified only by a single {@linkplain 
#getDatum() datum}.
+      *
+      * @since 1.5
+      */
+     @Override
 -    public DatumEnsemble<GeodeticDatum> getDatumEnsemble() {
++    public DefaultDatumEnsemble<GeodeticDatum> getDatumEnsemble() {
+         return ensemble;
+     }
+ 
      /**
       * Returns the coordinate system.
       *
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultParametricCRS.java
index b0da7ed38a,e55064a583..7d63756418
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultParametricCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultParametricCRS.java
@@@ -27,9 -26,11 +26,10 @@@ import org.apache.sis.referencing.cs.Ax
  import org.apache.sis.referencing.cs.AbstractCS;
  import org.apache.sis.io.wkt.Formatter;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.referencing.cs.ParametricCS;
 -import org.opengis.referencing.crs.ParametricCRS;
 -import org.opengis.referencing.datum.ParametricDatum;
 -import org.opengis.referencing.datum.DatumEnsemble;
 +// Specific to the main branch:
 +import org.apache.sis.referencing.cs.DefaultParametricCS;
 +import org.apache.sis.referencing.datum.DefaultParametricDatum;
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
  
  
  /**
@@@ -69,17 -70,28 +69,27 @@@ public class DefaultParametricCRS exten
      private static final long serialVersionUID = 4013698133331342649L;
  
      /**
-      * The datum.
+      * The datum, or {@code null} if the CRS is associated only to a datum 
ensemble.
       *
       * <p><b>Consider this field as final!</b>
 -     * This field is modified only at unmarshalling time by {@link 
#setDatum(ParametricDatum)}</p>
 +     * This field is modified only at unmarshalling time by {@code 
setDatum(ParametricDatum)}</p>
       *
       * @see #getDatum()
       */
 -    @SuppressWarnings("serial")         // Most SIS implementations are 
serializable.
 -    private ParametricDatum datum;
 +    private DefaultParametricDatum datum;
  
+     /**
+      * Collection of reference frames which for low accuracy requirements may 
be considered to be
+      * insignificantly different from each other. May be {@code null} if 
there is no such ensemble.
+      *
+      * @see #getDatumEnsemble()
+      */
+     @SuppressWarnings("serial")     // Most SIS implementations are 
serializable.
 -    private final DatumEnsemble<ParametricDatum> ensemble;
++    private final DefaultDatumEnsemble<DefaultParametricDatum> ensemble;
+ 
      /**
       * Creates a coordinate reference system from the given properties, datum 
and coordinate system.
+      * At least one of the {@code datum} and {@code ensemble} arguments shall 
be non-null.
       * The properties given in argument follow the same rules as for the
       * {@linkplain 
org.apache.sis.referencing.AbstractReferenceSystem#AbstractReferenceSystem(Map)
       * super-class constructor}. The following table is a reminder of main 
(not all) properties:
@@@ -113,23 -125,39 +123,41 @@@
       *   </tr>
       * </table>
       *
 +     * <div class="warning"><b>Warning:</b> in a future SIS version, the 
parameter types may be changed to
 +     * {@code org.opengis.referencing.datum.ParametricDatum} and {@code 
org.opengis.referencing.cs.ParametricCS}
 +     * Those change are pending GeoAPI revision.</div>
 +     *
       * @param  properties  the properties to be given to the coordinate 
reference system.
-      * @param  datum       the datum.
+      * @param  datum       the datum, or {@code null} if the CRS is 
associated only to a datum ensemble.
+      * @param  ensemble    collection of reference frames which for low 
accuracy requirements may be considered to be
+      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
       * @param  cs          the coordinate system.
+      *
 -     * @see 
org.apache.sis.referencing.factory.GeodeticObjectFactory#createParametricCRS(Map,
 ParametricDatum, ParametricCS)
 -     *
+      * @since 1.5
       */
      public DefaultParametricCRS(final Map<String,?> properties,
 -                                final ParametricDatum datum,
 -                                final DatumEnsemble<ParametricDatum> ensemble,
 -                                final ParametricCS cs)
 +                                final DefaultParametricDatum datum,
++                                final 
DefaultDatumEnsemble<DefaultParametricDatum> ensemble,
 +                                final DefaultParametricCS cs)
      {
          super(properties, cs);
-         this.datum = Objects.requireNonNull(datum);
+         this.datum    = datum;
+         this.ensemble = ensemble;
+         checkDatum(datum, ensemble);
          checkDimension(1, 1, cs);
      }
  
+     /**
 -     * @deprecated A {@code DatumEnsemble} argument has been added.
++     * @deprecated A {@code DefaultDatumEnsemble} argument has been added.
+      */
+     @Deprecated(since="1.5", forRemoval=true)
+     public DefaultParametricCRS(final Map<String,?> properties,
 -                                final ParametricDatum datum,
 -                                final ParametricCS cs)
++                                final DefaultParametricDatum datum,
++                                final DefaultParametricCS cs)
+     {
+         this(properties, datum, null, cs);
+     }
+ 
      /**
       * Creates a new CRS derived from the specified one, but with different 
axis order or unit.
       * This is for implementing the {@link #createSameType(AbstractCS)} 
method only.
@@@ -146,20 -175,55 +175,25 @@@
       *
       * <p>This constructor performs a shallow copy, i.e. the properties are 
not cloned.</p>
       *
 -     * @param  crs  the coordinate reference system to copy.
 +     * <div class="warning"><b>Warning:</b> in a future SIS version, the 
parameter type may be changed
 +     * to {@code org.opengis.referencing.crs.ParametricCRS}. This change is 
pending GeoAPI revision.</div>
       *
 -     * @see #castOrCopy(ParametricCRS)
 +     * @param  crs  the coordinate reference system to copy.
       */
 -    protected DefaultParametricCRS(final ParametricCRS crs) {
 +    protected DefaultParametricCRS(final DefaultParametricCRS crs) {
          super(crs);
-         datum = crs.getDatum();
+         datum    = crs.getDatum();
+         ensemble = crs.getDatumEnsemble();
+         checkDatum(datum, ensemble);
      }
  
 -    /**
 -     * Returns a SIS coordinate reference system implementation with the same 
values as the given
 -     * arbitrary implementation. If the given object is {@code null}, then 
this method returns {@code null}.
 -     * Otherwise if the given object is already a SIS implementation, then 
the given object is returned unchanged.
 -     * Otherwise a new SIS implementation is created and initialized to the 
attribute values of the given object.
 -     *
 -     * @param  object  the object to get as a SIS implementation, or {@code 
null} if none.
 -     * @return a SIS implementation containing the values of the given object 
(may be the
 -     *         given object itself), or {@code null} if the argument was null.
 -     */
 -    public static DefaultParametricCRS castOrCopy(final ParametricCRS object) 
{
 -        return (object == null || object instanceof DefaultParametricCRS)
 -                ? (DefaultParametricCRS) object : new 
DefaultParametricCRS(object);
 -    }
 -
 -    /**
 -     * Returns the GeoAPI interface implemented by this class.
 -     * The SIS implementation returns {@code ParametricCRS.class}.
 -     *
 -     * <h4>Note for implementers</h4>
 -     * Subclasses usually do not need to override this method since GeoAPI 
does not define {@code ParametricCRS}
 -     * sub-interface. Overriding possibility is left mostly for implementers 
who wish to extend GeoAPI with their
 -     * own set of interfaces.
 -     *
 -     * @return {@code ParametricCRS.class} or a user-defined sub-interface.
 -     */
 -    @Override
 -    public Class<? extends ParametricCRS> getInterface() {
 -        return ParametricCRS.class;
 -    }
 -
      /**
-      * Returns the datum.
+      * Returns the reference surface used as the origin of this 
<abbr>CRS</abbr>.
+      * This property may be null if this <abbr>CRS</abbr> is related to an 
object
+      * identified only by a {@linkplain #getDatumEnsemble() datum ensemble}.
       *
-      * @return the datum.
+      * @return the parametric datum, or {@code null} if this <abbr>CRS</abbr> 
is related to
+      *         an object identified only by a {@linkplain #getDatumEnsemble() 
datum ensemble}.
       */
      @Override
      @XmlElement(name = "parametricDatum", required = true)
@@@ -167,6 -231,22 +201,26 @@@
          return datum;
      }
  
+     /**
+      * Returns a collection of datums which, for low accuracy requirements,
+      * may be considered to be insignificantly different from each other.
+      * This property may be null if this <abbr>CRS</abbr> is related to an 
object
+      * identified only by a single {@linkplain #getDatum() datum}.
+      *
++     * <div class="warning"><b>Warning:</b> in a future SIS version, the 
return type may
++     * be changed to the {@code org.opengis.referencing.datum.DatumEnsemble} 
interface.
++     * This change is pending GeoAPI revision.</div>
++     *
+      * @return the datum ensemble, or {@code null} if this <abbr>CRS</abbr> 
is related
+      *         to an object identified only by a single {@linkplain 
#getDatum() datum}.
+      *
+      * @since 1.5
+      */
+     @Override
 -    public DatumEnsemble<ParametricDatum> getDatumEnsemble() {
++    public DefaultDatumEnsemble<DefaultParametricDatum> getDatumEnsemble() {
+         return ensemble;
+     }
+ 
      /**
       * Returns the coordinate system.
       *
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
index 59c3fef550,625064eddc..957e0076a9
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
@@@ -45,6 -44,6 +44,9 @@@ import org.apache.sis.math.Fraction
  import static org.apache.sis.util.privy.Constants.NANOS_PER_SECOND;
  import static org.apache.sis.util.privy.Constants.MILLIS_PER_SECOND;
  
++// Specific to the main branch:
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
++
  // Specific to the main and geoapi-3.1 branches:
  import org.apache.sis.temporal.TemporalDate;
  
@@@ -100,6 -102,15 +102,15 @@@ public class DefaultTemporalCRS extend
      @SuppressWarnings("serial")     // Most SIS implementations are 
serializable.
      private TemporalDatum datum;
  
+     /**
+      * Collection of reference frames which for low accuracy requirements may 
be considered to be
+      * insignificantly different from each other. May be {@code null} if 
there is no such ensemble.
+      *
+      * @see #getDatumEnsemble()
+      */
+     @SuppressWarnings("serial")     // Most SIS implementations are 
serializable.
 -    private final DatumEnsemble<TemporalDatum> ensemble;
++    private final DefaultDatumEnsemble<TemporalDatum> ensemble;
+ 
      /**
       * Conversion factor from values in this CRS to values in seconds. We use 
{@link UnitConverter}
       * instead of {@code double} because the SIS implementation of {@code 
UnitConverter} performs
@@@ -160,14 -176,28 +176,28 @@@
      @SuppressWarnings("this-escape")
      public DefaultTemporalCRS(final Map<String,?> properties,
                                final TemporalDatum datum,
-                               final TimeCS        cs)
 -                              final DatumEnsemble<TemporalDatum> ensemble,
++                              final DefaultDatumEnsemble<TemporalDatum> 
ensemble,
+                               final TimeCS cs)
      {
          super(properties, cs);
-         this.datum = Objects.requireNonNull(datum);
+         this.datum    = datum;
+         this.ensemble = ensemble;
+         checkDatum(datum, ensemble);
          checkDimension(1, 1, cs);
          initializeConverter();
      }
  
+     /**
 -     * @deprecated A {@code DatumEnsemble} argument has been added.
++     * @deprecated A {@code DefaultDatumEnsemble} argument has been added.
+      */
+     @Deprecated(since="1.5", forRemoval=true)
+     public DefaultTemporalCRS(final Map<String,?> properties,
+                               final TemporalDatum datum,
+                               final TimeCS cs)
+     {
+         this(properties, datum, null, cs);
+     }
+ 
      /**
       * Creates a new CRS derived from the specified one, but with different 
axis order or unit.
       * This is for implementing the {@link #createSameType(AbstractCS)} 
method only.
@@@ -192,7 -223,9 +223,9 @@@
      @SuppressWarnings("this-escape")
      protected DefaultTemporalCRS(final TemporalCRS crs) {
          super(crs);
-         datum = crs.getDatum();
+         datum    = crs.getDatum();
 -        ensemble = crs.getDatumEnsemble();
++        ensemble = (crs instanceof DefaultTemporalCRS) ? 
((DefaultTemporalCRS) crs).getDatumEnsemble() : null;
+         checkDatum(datum, ensemble);
          initializeConverter();
      }
  
@@@ -269,6 -305,22 +305,26 @@@
          return datum;
      }
  
+     /**
+      * Returns a collection of datums which, for low accuracy requirements,
+      * may be considered to be insignificantly different from each other.
+      * This property may be null if this <abbr>CRS</abbr> is related to an 
object
+      * identified only by a single {@linkplain #getDatum() datum}.
+      *
++     * <div class="warning"><b>Warning:</b> in a future SIS version, the 
return type may
++     * be changed to the {@code org.opengis.referencing.datum.DatumEnsemble} 
interface.
++     * This change is pending GeoAPI revision.</div>
++     *
+      * @return the datum ensemble, or {@code null} if this <abbr>CRS</abbr> 
is related
+      *         to an object identified only by a single {@linkplain 
#getDatum() datum}.
+      *
+      * @since 1.5
+      */
+     @Override
 -    public DatumEnsemble<TemporalDatum> getDatumEnsemble() {
++    public DefaultDatumEnsemble<TemporalDatum> getDatumEnsemble() {
+         return ensemble;
+     }
+ 
      /**
       * Returns the coordinate system.
       *
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultVerticalCRS.java
index 3442f719b6,e5be3b1c7d..2fc9133ede
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultVerticalCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultVerticalCRS.java
@@@ -31,6 -30,9 +30,9 @@@ import org.apache.sis.referencing.privy
  import org.apache.sis.metadata.privy.ImplementationHelper;
  import org.apache.sis.io.wkt.Formatter;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.referencing.datum.DatumEnsemble;
++// Specific to the main branch:
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
+ 
  
  /**
   * A 1-dimensional coordinate reference system used for recording heights or 
depths.
@@@ -79,8 -81,18 +81,18 @@@ public class DefaultVerticalCRS extend
      @SuppressWarnings("serial")     // Most SIS implementations are 
serializable.
      private VerticalDatum datum;
  
+     /**
+      * Collection of reference frames which for low accuracy requirements may 
be considered to be
+      * insignificantly different from each other. May be {@code null} if 
there is no such ensemble.
+      *
+      * @see #getDatumEnsemble()
+      */
+     @SuppressWarnings("serial")     // Most SIS implementations are 
serializable.
 -    private final DatumEnsemble<VerticalDatum> ensemble;
++    private final DefaultDatumEnsemble<VerticalDatum> ensemble;
+ 
      /**
       * Creates a coordinate reference system from the given properties, datum 
and coordinate system.
+      * At least one of the {@code datum} and {@code ensemble} arguments shall 
be non-null.
       * The properties given in argument follow the same rules as for the
       * {@linkplain AbstractReferenceSystem#AbstractReferenceSystem(Map) 
super-class constructor}.
       * The following table is a reminder of main (not all) properties:
@@@ -122,13 -138,27 +138,27 @@@
       */
      public DefaultVerticalCRS(final Map<String,?> properties,
                                final VerticalDatum datum,
-                               final VerticalCS    cs)
 -                              final DatumEnsemble<VerticalDatum> ensemble,
++                              final DefaultDatumEnsemble<VerticalDatum> 
ensemble,
+                               final VerticalCS cs)
      {
          super(properties, cs);
-         this.datum = Objects.requireNonNull(datum);
+         this.datum    = datum;
+         this.ensemble = ensemble;
+         checkDatum(datum, ensemble);
          checkDimension(1, 1, cs);
      }
  
+     /**
 -     * @deprecated A {@code DatumEnsemble} argument has been added.
++     * @deprecated A {@code DefaultDatumEnsemble} argument has been added.
+      */
+     @Deprecated(since="1.5", forRemoval=true)
+     public DefaultVerticalCRS(final Map<String,?> properties,
+                               final VerticalDatum datum,
+                               final VerticalCS cs)
+     {
+         this(properties, datum, null, cs);
+     }
+ 
      /**
       * Creates a new CRS derived from the specified one, but with different 
axis order or unit.
       * This is for implementing the {@link #createSameType(AbstractCS)} 
method only.
@@@ -151,7 -182,9 +182,9 @@@
       */
      protected DefaultVerticalCRS(final VerticalCRS crs) {
          super(crs);
-         datum = crs.getDatum();
+         datum    = crs.getDatum();
 -        ensemble = crs.getDatumEnsemble();
++        ensemble = (crs instanceof DefaultVerticalCRS) ? 
((DefaultVerticalCRS) crs).getDatumEnsemble() : null;
+         checkDatum(datum, ensemble);
      }
  
      /**
@@@ -196,6 -232,22 +232,26 @@@
          return datum;
      }
  
+     /**
+      * Returns a collection of datums which, for low accuracy requirements,
+      * may be considered to be insignificantly different from each other.
+      * This property may be null if this <abbr>CRS</abbr> is related to an 
object
+      * identified only by a single {@linkplain #getDatum() datum}.
+      *
++     * <div class="warning"><b>Warning:</b> in a future SIS version, the 
return type may
++     * be changed to the {@code org.opengis.referencing.datum.DatumEnsemble} 
interface.
++     * This change is pending GeoAPI revision.</div>
++     *
+      * @return the datum ensemble, or {@code null} if this <abbr>CRS</abbr> 
is related
+      *         to an object identified only by a single {@linkplain 
#getDatum() datum}.
+      *
+      * @since 1.5
+      */
+     @Override
 -    public DatumEnsemble<VerticalDatum> getDatumEnsemble() {
++    public DefaultDatumEnsemble<VerticalDatum> getDatumEnsemble() {
+         return ensemble;
+     }
+ 
      /**
       * Returns the coordinate system.
       *
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java
index 5538212f17,e0f39c219d..3cd29c850f
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java
@@@ -95,6 -94,6 +96,24 @@@ public class AbstractDatum extends Abst
       */
      private static final long serialVersionUID = 5380816794438838309L;
  
++    /**
++     * Key for the <code>{@value}</code> property to be given to the
++     * {@code DatumFactory.createFoo(Map, ...)} methods.
++     * This is used for setting the value to be returned by {@link 
#getPublicationDate()}.
++     *
++     * @since 1.5
++     */
++    public static final String PUBLICATION_DATE_KEY = "publicationDate";
++
++    /**
++     * Key for the <code>{@value}</code> property to be given to the
++     * {@code DatumFactory.createFoo(Map, ...)} methods.
++     * This is used for setting the value to be returned by {@link 
#getConventionalRS()}.
++     *
++     * @since 1.5
++     */
++    public static final String CONVENTIONAL_RS_KEY = "conventionalRS";
++
      /**
       * Description, possibly including coordinates, of the point or points 
used to anchor the datum to the Earth.
       * Also known as the "origin", especially for Engineering and Image 
Datums.
@@@ -140,6 -155,14 +175,14 @@@
       *     <td>{@link Temporal}</td>
       *     <td>{@link #getAnchorEpoch()}</td>
       *   </tr><tr>
 -     *     <td>{@value 
org.opengis.referencing.datum.Datum#PUBLICATION_DATE_KEY}</td>
++     *     <td>{@value #PUBLICATION_DATE_KEY}</td>
+      *     <td>{@link Temporal}</td>
+      *     <td>{@link #getPublicationDate()}</td>
+      *   </tr><tr>
 -     *     <td>{@value 
org.opengis.referencing.datum.Datum#CONVENTIONAL_RS_KEY}</td>
++     *     <td>{@value #CONVENTIONAL_RS_KEY}</td>
+      *     <td>{@link IdentifiedObject}</td>
+      *     <td>{@link #getConventionalRS()}</td>
+      *   </tr><tr>
       *     <th colspan="3" class="hsep">Defined in parent class 
(reminder)</th>
       *   </tr><tr>
       *     <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
@@@ -192,11 -218,10 +237,19 @@@
       */
      protected AbstractDatum(final Datum datum) {
          super(datum);
 -        anchorEpoch      = datum.getAnchorEpoch().orElse(null);
 -        anchorDefinition = datum.getAnchorDefinition().orElse(null);
 -        publicationDate  = datum.getPublicationDate().orElse(null);
 -        conventionalRS   = datum.getConventionalRS().orElse(null);
 +        Date date = datum.getRealizationEpoch();
 +        if (date != null) {
 +            anchorEpoch = date.toInstant();
 +        }
 +        anchorDefinition = datum.getAnchorPoint();
++        if (datum instanceof AbstractDatum) {
++            final var cd = (AbstractDatum) datum;
++            publicationDate  = cd.getPublicationDate().orElse(null);
++            conventionalRS   = cd.getConventionalRS().orElse(null);
++        } else {
++            publicationDate = null;
++            conventionalRS  = null;
++        }
      }
  
      /**
@@@ -307,35 -334,35 +360,59 @@@
      @XmlSchemaType(name = "date")
      @XmlElement(name = "realizationEpoch")
      public Date getRealizationEpoch() {
 -        return Datum.super.getRealizationEpoch();
 +        return getAnchorEpoch().map(Legacy::toDate).orElse(null);
 +    }
 +
 +    /**
 +     * Returns the region or timeframe in which this datum is valid, or 
{@code null} if unspecified.
 +     *
 +     * @return area or region or timeframe in which this datum is valid, or 
{@code null}.
 +     *
 +     * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019.
 +     */
 +    @Override
 +    @Deprecated(since = "1.4")
 +    public Extent getDomainOfValidity() {
 +        return Legacy.getDomainOfValidity(getDomains());
 +    }
 +
 +    /**
 +     * Returns the domain or limitations of usage, or {@code null} if 
unspecified.
 +     *
 +     * @return description of domain of usage, or limitations of usage, for 
which this datum object is valid.
 +     *
 +     * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019.
 +     */
 +    @Override
 +    @Deprecated(since = "1.4")
 +    public InternationalString getScope() {
 +        return Legacy.getScope(getDomains());
      }
  
+     /**
+      * Returns the date on which the datum definition was published.
+      *
+      * @return date on which the datum definition was published.
+      *
+      * @since 1.5
+      */
 -    @Override
+     public Optional<Temporal> getPublicationDate() {
+         return Optional.ofNullable(publicationDate);
+     }
+ 
+     /**
+      * Returns the name, identifier, alias and remarks for the reference 
system realized by this reference frame.
+      * All datums that are members of a {@linkplain DefaultDatumEnsemble 
datum ensemble} shall have the same
+      * conventional reference system.
+      *
+      * @return reference system realized by this reference frame.
+      *
+      * @since 1.5
+      */
 -    @Override
+     public Optional<IdentifiedObject> getConventionalRS() {
+         return Optional.ofNullable(conventionalRS);
+     }
+ 
      /**
       * Returns {@code true} if either the {@linkplain #getName() primary 
name} or at least
       * one {@linkplain #getAlias() alias} matches the given string according 
heuristic rules.
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultDatumEnsemble.java
index 0000000000,9210e22176..fbade17b6d
mode 000000,100644..100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultDatumEnsemble.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultDatumEnsemble.java
@@@ -1,0 -1,273 +1,213 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.referencing.datum;
+ 
+ import java.util.Map;
+ import java.util.List;
+ import java.util.Collection;
 -import java.util.Objects;
+ import org.apache.sis.io.wkt.Formatter;
+ import org.apache.sis.io.wkt.Convention;
+ import org.opengis.referencing.IdentifiedObject;
+ import org.opengis.referencing.datum.Datum;
 -import org.opengis.referencing.datum.DatumEnsemble;
+ import org.opengis.metadata.quality.PositionalAccuracy;
+ import org.apache.sis.referencing.AbstractIdentifiedObject;
+ import org.apache.sis.referencing.internal.Resources;
+ import org.apache.sis.referencing.privy.WKTKeywords;
+ import org.apache.sis.util.ArgumentChecks;
+ import org.apache.sis.util.ComparisonMode;
+ import org.apache.sis.util.Utilities;
+ 
+ 
+ /**
+  * Collection of datums which for low accuracy requirements may be considered
+  * to be insignificantly different from each other.
+  *
+  * @author  OGC Topic 2 (for abstract model and documentation)
+  * @author  Martin Desruisseaux (IRD, Geomatys)
+  * @version 1.5
+  *
+  * @param <D> the type of datum contained in this ensemble.
+  *
+  * @since 1.5
+  */
 -public class DefaultDatumEnsemble<D extends Datum> extends 
AbstractIdentifiedObject implements DatumEnsemble<D> {
++public class DefaultDatumEnsemble<D extends Datum> extends 
AbstractIdentifiedObject {
+     /**
+      * Serial number for inter-operability with different versions.
+      */
+     private static final long serialVersionUID = -2757133322734036975L;
+ 
+     /**
+      * Datum or reference frames which are members of this ensemble.
+      */
+     @SuppressWarnings("serial")                     // Standard Java 
implementations are serializable.
+     private final List<D> members;
+ 
+     /**
+      * Inaccuracy introduced through use of this ensemble.
+      * This property is mandatory.
+      */
+     @SuppressWarnings("serial")                     // Most SIS 
implementations are serializable.
+     private final PositionalAccuracy ensembleAccuracy;
+ 
+     /**
+      * Creates a datum ensemble from the given properties.
+      * The properties given in argument follow the same rules as for the
+      * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) 
super-class constructor}.
+      *
+      * <table class="sis">
+      *   <caption>Recognized properties (non exhaustive list)</caption>
+      *   <tr>
+      *     <th>Property name</th>
+      *     <th>Value type</th>
+      *     <th>Returned by</th>
+      *   </tr><tr>
+      *     <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
+      *     <td>{@link Identifier} or {@link String}</td>
+      *     <td>{@link #getName()}</td>
+      *   </tr><tr>
+      *     <td>{@value 
org.opengis.referencing.IdentifiedObject#ALIAS_KEY}</td>
+      *     <td>{@link GenericName} or {@link CharSequence} (optionally as 
array)</td>
+      *     <td>{@link #getAlias()}</td>
+      *   </tr><tr>
+      *     <td>{@value 
org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td>
+      *     <td>{@link Identifier} (optionally as array)</td>
+      *     <td>{@link #getIdentifiers()}</td>
+      *   </tr><tr>
+      *     <td>{@value 
org.opengis.referencing.IdentifiedObject#DOMAINS_KEY}</td>
+      *     <td>{@link org.opengis.referencing.ObjectDomain} (optionally as 
array)</td>
+      *     <td>{@link #getDomains()}</td>
+      *   </tr><tr>
+      *     <td>{@value 
org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td>
+      *     <td>{@link InternationalString} or {@link String}</td>
+      *     <td>{@link #getRemarks()}</td>
+      *   </tr>
+      * </table>
+      *
+      * @param  properties  the properties to be given to the identified 
object.
+      * @param  members     datum or reference frames which are members of 
this ensemble.
+      * @param  accuracy    inaccuracy introduced through use of this ensemble 
(mandatory).
+      * @throws IllegalArgumentException if at least two different
+      *         {@linkplain AbstractDatum#getConventionalRS() conventional 
reference systems} are found.
+      */
+     public DefaultDatumEnsemble(Map<String,?> properties, Collection<? 
extends D> members, PositionalAccuracy accuracy) {
+         super(properties);
+         ArgumentChecks.ensureNonNull("accuracy", accuracy);
+         ensembleAccuracy = accuracy;
+         this.members = List.copyOf(members);
+         validate();
+     }
+ 
 -    /**
 -     * Creates a new ensemble with the same values as the specified one.
 -     * This copy constructor provides a way to convert an arbitrary 
implementation into a SIS one
 -     * or a user-defined one (as a subclass), usually in order to leverage 
some implementation-specific API.
 -     *
 -     * <p>This constructor performs a shallow copy, i.e. the properties are 
not cloned.</p>
 -     *
 -     * @param  ensemble  the ensemble to copy.
 -     */
 -    protected DefaultDatumEnsemble(final DatumEnsemble<? extends D> ensemble) 
{
 -        super(ensemble);
 -        members = List.copyOf(ensemble.getMembers());
 -        ensembleAccuracy = 
Objects.requireNonNull(ensemble.getEnsembleAccuracy());
 -        validate();
 -    }
 -
 -    /**
 -     * Returns a SIS ensemble implementation with the values of the given 
arbitrary implementation.
 -     * This method performs the first applicable action in the following 
choices:
 -     *
 -     * <ul>
 -     *   <li>If the given object is {@code null}, then this method returns 
{@code null}.</li>
 -     *   <li>Otherwise if the given object is already an instance of
 -     *       {@code DefaultDatumEnsemble}, then it is returned unchanged.</li>
 -     *   <li>Otherwise a new {@code DefaultDatumEnsemble} instance is created 
using the
 -     *       {@linkplain #DefaultDatumEnsemble(DatumEnsemble) copy 
constructor} and returned.
 -     *       Note that this is a <em>shallow</em> copy operation,
 -     *       because the other properties contained in the given object are 
not recursively copied.</li>
 -     * </ul>
 -     *
 -     * @param  <D>     the type of datum contained in the ensemble.
 -     * @param  object  the object to get as a SIS implementation, or {@code 
null} if none.
 -     * @return a SIS implementation containing the values of the given object 
(may be the
 -     *         given object itself), or {@code null} if the argument was null.
 -     */
 -    public static <D extends Datum> DefaultDatumEnsemble<D> castOrCopy(final 
DatumEnsemble<D> object) {
 -        if (object == null || object instanceof DefaultDatumEnsemble<?>) {
 -            return (DefaultDatumEnsemble<D>) object;
 -        } else {
 -            return new DefaultDatumEnsemble<>(object);
 -        }
 -    }
 -
+     /**
+      * Verifies this ensemble. All members shall have the same conventional 
reference system.
+      */
+     private void validate() {
+         IdentifiedObject rs = null;
+         for (final D datum : members) {
 -            final IdentifiedObject dr = 
datum.getConventionalRS().orElse(null);
++            if (!(datum instanceof AbstractDatum)) continue;
++            final IdentifiedObject dr = ((AbstractDatum) 
datum).getConventionalRS().orElse(null);
+             if (dr != null) {
+                 if (rs == null) {
+                     rs = dr;
+                 } else if (!rs.equals(dr)) {
+                     throw new 
IllegalArgumentException(Resources.format(Resources.Keys.ShallHaveSameConventionalRS));
+                 }
+             }
+         }
+     }
+ 
 -    /**
 -     * Returns the GeoAPI interface implemented by this class.
 -     * The SIS implementation returns {@code DatumEnsemble.class}.
 -     *
 -     * <h4>Note for implementers</h4>
 -     * Subclasses usually do not need to override this method since GeoAPI 
does not define {@code DatumEnsemble}
 -     * sub-interface. Overriding possibility is left mostly for implementers 
who wish to extend GeoAPI with their
 -     * own set of interfaces.
 -     *
 -     * @return the datum interface implemented by this class.
 -     */
 -    @Override
 -    @SuppressWarnings("unchecked")
 -    public Class<? extends DatumEnsemble<D>> getInterface() {
 -        return (Class) DatumEnsemble.class;
 -    }
 -
+     /**
+      * Returns the datum or reference frames which are members of this 
ensemble.
+      *
+      * @return datum or reference frames which are members of this ensemble.
+      */
 -    @Override
+     @SuppressWarnings("ReturnOfCollectionOrArrayField")     // Collection is 
unmodifiable.
+     public Collection<D> getMembers() {
+         return members;
+     }
+ 
+     /**
+      * Returns the inaccuracy introduced through use of this ensemble.
+      *
+      * @return inaccuracy introduced through use of this ensemble.
+      */
 -    @Override
+     public PositionalAccuracy getEnsembleAccuracy() {
+         return ensembleAccuracy;
+     }
+ 
+     /**
+      * Compares the specified object with this ensemble for equality.
+      * If the {@code mode} argument value is {@link ComparisonMode#STRICT 
STRICT} or
+      * {@link ComparisonMode#BY_CONTRACT BY_CONTRACT}, then all available 
properties are compared including the
+      * {@linkplain #getDomains() domains}.
+      *
+      * @param  object  the object to compare to {@code this}.
+      * @param  mode    {@link ComparisonMode#STRICT STRICT} for performing a 
strict comparison, or
+      *                 {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} 
for comparing only
+      *                 properties relevant to coordinate transformations.
+      * @return {@code true} if both objects are equal.
+      */
+     @Override
+     public boolean equals(final Object object, final ComparisonMode mode) {
+         if (!super.equals(object, mode)) {
+             return false;
+         }
+         switch (mode) {
+             case STRICT: {
+                 final var that = (DefaultDatumEnsemble) object;
+                 return members.equals(that.members) && 
ensembleAccuracy.equals(that.ensembleAccuracy);
+             }
+             default: {
 -                final var that = (DatumEnsemble) object;
++                if (!(object instanceof DefaultDatumEnsemble<?>)) {
++                    return false;
++                }
++                final var that = (DefaultDatumEnsemble<?>) object;
+                 return Utilities.deepEquals(getMembers(), that.getMembers(), 
mode) &&
+                        Utilities.deepEquals(getEnsembleAccuracy(), 
that.getEnsembleAccuracy(), mode);
+             }
+         }
+     }
+ 
+     /**
+      * Invoked by {@code hashCode()} for computing the hash code when first 
needed.
+      * See {@link 
org.apache.sis.referencing.AbstractIdentifiedObject#computeHashCode()}
+      * for more information.
+      *
+      * @return the hash code value. This value may change in any future 
Apache SIS version.
+      * @hidden because not useful.
+      */
+     @Override
+     protected long computeHashCode() {
+         return super.computeHashCode() + 7*members.hashCode() + 
37*ensembleAccuracy.hashCode();
+     }
+ 
+     /**
+      * Formats the inner part of the <i>Well Known Text</i> (WKT) 
representation for this ensemble.
+      * See {@link AbstractIdentifiedObject#formatTo(Formatter)} for more 
information.
+      *
+      * @param  formatter  the formatter where to format the inner content of 
this WKT element.
+      * @return the {@linkplain org.apache.sis.io.wkt.KeywordCase#CAMEL_CASE 
CamelCase} keyword
+      *         for the WKT element, or {@code null} if unknown.
+      */
+     @Override
+     protected String formatTo(final Formatter formatter) {
+         super.formatTo(formatter);
+         if (Convention.WKT2_2015.compareTo(formatter.getConvention()) >= 0) {
+             formatter.setInvalidWKT(this, null);
+         }
+         return WKTKeywords.Ensemble;
+     }
+ }
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
index 10d288dde1,6401dbf97e..9e88d79f05
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
@@@ -57,10 -59,13 +59,13 @@@ import org.apache.sis.util.collection.W
  import org.apache.sis.util.iso.AbstractFactory;
  import org.apache.sis.util.resources.Messages;
  import org.apache.sis.util.resources.Errors;
- import org.apache.sis.io.wkt.Parser;
  import org.apache.sis.util.logging.Logging;
+ import org.apache.sis.io.wkt.Parser;
  import org.apache.sis.xml.XML;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import java.time.temporal.Temporal;
++// Specific to the main branch:
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
+ 
  
  /**
   * Creates {@linkplain org.apache.sis.referencing.crs.AbstractCRS Coordinate 
Reference System} (CRS) implementations,
@@@ -315,15 -320,11 +320,15 @@@ public class GeodeticObjectFactory exte
      }
  
      /**
-      * Creates a geocentric coordinate reference system from a {@linkplain 
CartesianCS Cartesian coordinate system}.
+      * Creates a geocentric coordinate reference system from a Cartesian 
coordinate system.
       * Geocentric CRS have their origin at the approximate centre of mass of 
the earth.
 -     * An {@linkplain #createGeodeticCRS(Map, GeodeticDatum, SphericalCS) 
alternate method} allows creation of the
 +     * An {@linkplain #createGeocentricCRS(Map, GeodeticDatum, SphericalCS) 
alternate method} allows creation of the
       * same kind of CRS with spherical coordinate system instead of a 
Cartesian one.
       *
 +     * <div class="warning"><b>Warning:</b> In a future SIS version, the 
return type may be changed to the
 +     * {@link GeodeticCRS} parent interface. This is because ISO 19111 does 
not defines specific interface
 +     * for the geocentric case. Users should assign the return value to a 
{@code GeodeticCRS} type.</div>
 +     *
       * <h4>Dependencies</h4>
       * The components needed by this method can be created by the following 
methods:
       * <ol>
@@@ -340,7 -343,38 +347,37 @@@
       * The default implementation creates a {@link DefaultGeocentricCRS} 
instance.
       *
       * @param  properties  name and other properties to give to the new 
object.
-      * @param  datum       the geodetic reference frame to use in created CRS.
+      * @param  datum       geodetic reference frame, or {@code null} if the 
CRS is associated only to a datum ensemble.
+      * @param  ensemble    collection of reference frames which for low 
accuracy requirements may be considered to be
+      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
+      * @param  cs          the three-dimensional Cartesian coordinate system 
for the created CRS.
+      * @throws FactoryException if the object creation failed.
+      *
+      * @see GeodeticAuthorityFactory#createGeodeticCRS(String)
+      * @see DefaultGeocentricCRS#DefaultGeocentricCRS(Map, GeodeticDatum, 
CartesianCS)
+      *
+      * @since 1.5
+      */
 -    @Override
+     public GeodeticCRS createGeodeticCRS(final Map<String,?> properties,
+                                          final GeodeticDatum datum,
 -                                         final DatumEnsemble<GeodeticDatum> 
ensemble,
++                                         final 
DefaultDatumEnsemble<GeodeticDatum> ensemble,
+                                          final CartesianCS cs)
+             throws FactoryException
+     {
+         final DefaultGeocentricCRS crs;
+         try {
+             crs = new DefaultGeocentricCRS(complete(properties), datum, 
ensemble, cs);
+         } catch (IllegalArgumentException exception) {
+             throw new InvalidGeodeticParameterException(exception);
+         }
+         return unique("createGeodeticCRS", crs);
+     }
+ 
+     /**
+      * Creates a geocentric coordinate reference system from a Cartesian 
coordinate system.
+      *
+      * @param  properties  name and other properties to give to the new 
object.
+      * @param  datum       the geodetic datum to use in created CRS.
       * @param  cs          the three-dimensional Cartesian coordinate system 
for the created CRS.
       * @throws FactoryException if the object creation failed.
       *
@@@ -397,15 -433,11 +435,15 @@@
      }
  
      /**
-      * Creates a geocentric coordinate reference system from a {@linkplain 
SphericalCS spherical coordinate system}.
+      * Creates a geocentric coordinate reference system from a spherical 
coordinate system.
       * Geocentric CRS have their origin at the approximate centre of mass of 
the earth.
 -     * An {@linkplain #createGeodeticCRS(Map, GeodeticDatum, CartesianCS) 
alternate method} allows creation of the
 +     * An {@linkplain #createGeocentricCRS(Map, GeodeticDatum, CartesianCS) 
alternate method} allows creation of the
       * same kind of CRS with Cartesian coordinate system instead of a 
spherical one.
       *
 +     * <div class="warning"><b>Warning:</b> In a future SIS version, the 
return type may be changed to the
 +     * {@link GeodeticCRS} parent interface. This is because ISO 19111 does 
not defines specific interface
 +     * for the geocentric case. Users should assign the return value to a 
{@code GeodeticCRS} type.</div>
 +     *
       * <h4>Dependencies</h4>
       * The components needed by this method can be created by the following 
methods:
       * <ol>
@@@ -427,9 -463,39 +469,38 @@@
       * @throws FactoryException if the object creation failed.
       *
       * @see DefaultGeocentricCRS#DefaultGeocentricCRS(Map, GeodeticDatum, 
SphericalCS)
 -     * @see GeodeticAuthorityFactory#createGeodeticCRS(String)
 +     * @see GeodeticAuthorityFactory#createGeocentricCRS(String)
+      *
+      * @since 1.5
+      */
 -    @Override
+     public GeodeticCRS createGeodeticCRS(final Map<String,?> properties,
+                                          final GeodeticDatum datum,
 -                                         final DatumEnsemble<GeodeticDatum> 
ensemble,
++                                         final 
DefaultDatumEnsemble<GeodeticDatum> ensemble,
+                                          final SphericalCS cs)
+             throws FactoryException
+     {
+         final DefaultGeocentricCRS crs;
+         try {
+             crs = new DefaultGeocentricCRS(complete(properties), datum, 
ensemble, cs);
+         } catch (IllegalArgumentException exception) {
+             throw new InvalidGeodeticParameterException(exception);
+         }
+         return unique("createGeodeticCRS", crs);
+     }
+ 
+     /**
+      * Creates a geocentric coordinate reference system from a spherical 
coordinate system.
+      *
+      * @param  properties  name and other properties to give to the new 
object.
+      * @param  datum       the geodetic datum to use in created CRS.
+      * @param  cs          the three-dimensional Cartesian coordinate system 
for the created CRS.
+      * @throws FactoryException if the object creation failed.
+      *
+      * @deprecated ISO 19111:2019 does not define an explicit class for 
geocentric CRS.
+      * Use {@link #createGeodeticCRS(Map, GeodeticDatum, SphericalCS)} 
instead.
       */
      @Override
 -    @Deprecated(since = "2.0")  // Temporary version number until this branch 
is released.
++    @Deprecated
      public GeocentricCRS createGeocentricCRS(final Map<String,?> properties,
              final GeodeticDatum datum, final SphericalCS cs) throws 
FactoryException
      {
@@@ -499,9 -566,11 +571,10 @@@
       *
       * @since 1.4
       */
 -    @Override
      public SphericalCS createSphericalCS(final Map<String,?> properties,
-             final CoordinateSystemAxis axis0,
-             final CoordinateSystemAxis axis1) throws FactoryException
+                                          final CoordinateSystemAxis axis0,
+                                          final CoordinateSystemAxis axis1)
+             throws FactoryException
      {
          final DefaultSphericalCS cs;
          try {
@@@ -541,10 -614,15 +618,14 @@@
       *
       * @see DefaultGeographicCRS#DefaultGeographicCRS(Map, GeodeticDatum, 
EllipsoidalCS)
       * @see GeodeticAuthorityFactory#createGeographicCRS(String)
+      *
+      * @since 1.5
       */
--    @Override
      public GeographicCRS createGeographicCRS(final Map<String,?> properties,
-             final GeodeticDatum datum, final EllipsoidalCS cs) throws 
FactoryException
+                                              final GeodeticDatum datum,
 -                                             final 
DatumEnsemble<GeodeticDatum> ensemble,
++                                             final 
DefaultDatumEnsemble<GeodeticDatum> ensemble,
+                                              final EllipsoidalCS cs)
+             throws FactoryException
      {
          final DefaultGeographicCRS crs;
          try {
@@@ -555,6 -633,33 +636,51 @@@
          return unique("createGeographicCRS", crs);
      }
  
++    /**
++     * Creates a geographic <abbr>CRS</abbr> from a reference frame.
++     * This is a shortcut for the {@linkplain #createGeographicCRS(Map, 
GeodeticDatum, DefaultDatumEnsemble, EllipsoidalCS)
++     * more generic method} without datum ensemble.
++     *
++     * @param  properties  name and other properties to give to the new 
object.
++     *                     Available properties are {@linkplain ObjectFactory 
listed there}.
++     * @param  datum       geodetic reference frame to use in created CRS.
++     * @param  cs          the ellipsoidal coordinate system for the created 
CRS.
++     * @return the coordinate reference system for the given properties.
++     * @throws FactoryException if the object creation failed.
++     */
++    @Override
++    public GeographicCRS createGeographicCRS(Map<String, ?> properties, 
GeodeticDatum datum, EllipsoidalCS cs)
++            throws FactoryException
++    {
++        return createGeographicCRS(properties, datum, null, cs);
++    }
++
+     /**
+      * Creates a datum ensemble from a collection of members and an ensemble 
accuracy.
+      *
+      * @param  <D>         the type of datum contained in the ensemble.
+      * @param  properties  name and other properties to give to the new 
object.
+      * @param  members     datum or reference frames which are members of the 
datum ensemble.
+      * @param  accuracy    inaccuracy introduced through use of the given 
collection of datums.
+      * @return the datum ensemble for the given properties.
+      * @throws FactoryException if the object creation failed.
+      *
+      * @since 1.5
+      */
 -    @Override
 -    public <D extends Datum> DatumEnsemble<D> createDatumEnsemble(final 
Map<String,?> properties,
++    public <D extends Datum> DefaultDatumEnsemble<D> 
createDatumEnsemble(final Map<String,?> properties,
+                                                                   final 
Collection<? extends D> members,
+                                                                   final 
PositionalAccuracy accuracy)
+             throws FactoryException
+     {
+         final DefaultDatumEnsemble<D> ensemble;
+         try {
+             ensemble = new DefaultDatumEnsemble<>(complete(properties), 
members, accuracy);
+         } catch (IllegalArgumentException exception) {
+             throw new InvalidGeodeticParameterException(exception);
+         }
+         return unique("createDatumEnsemble", ensemble);
+     }
+ 
      /**
       * Creates a geodetic reference frame from ellipsoid and (optionally) 
Bursa-Wolf parameters.
       * Geodetic reference frame defines the location and orientation of an 
ellipsoid that approximates the shape of the earth.
@@@ -894,9 -1012,11 +1033,11 @@@
       * <ol>
       *   <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, 
Unit)}</li>
       *   <li>{@link #createVerticalCS(Map, CoordinateSystemAxis)}</li>
 -     *   <li>{@link #createVerticalDatum(Map, RealizationMethod)}</li>
 +     *   <li>{@link #createVerticalDatum(Map, VerticalDatumType)}</li>
+      *   <li>{@link #createDatumEnsemble(Map, Collection, 
PositionalAccuracy)} (optional)</li>
       * </ol>
       *
+      * At least one of the {@code datum} and {@code ensemble} arguments shall 
be non-null.
       * The default implementation creates a {@link DefaultVerticalCRS} 
instance.
       *
       * @param  properties  name and other properties to give to the new 
object.
@@@ -906,10 -1028,15 +1049,14 @@@
       *
       * @see DefaultVerticalCRS#DefaultVerticalCRS(Map, VerticalDatum, 
VerticalCS)
       * @see GeodeticAuthorityFactory#createVerticalCRS(String)
+      *
+      * @since 1.5
       */
--    @Override
      public VerticalCRS createVerticalCRS(final Map<String,?> properties,
-             final VerticalDatum datum, final VerticalCS cs) throws 
FactoryException
+                                          final VerticalDatum datum,
 -                                         final DatumEnsemble<VerticalDatum> 
ensemble,
++                                         final 
DefaultDatumEnsemble<VerticalDatum> ensemble,
+                                          final VerticalCS cs)
+             throws FactoryException
      {
          final DefaultVerticalCRS crs;
          try {
@@@ -920,6 -1047,33 +1067,25 @@@
          return unique("createVerticalCRS", crs);
      }
  
+     /**
 -     * Creates a vertical datum from a realization method.
 -     * The default implementation creates a {@link DefaultVerticalDatum} 
instance.
++     * Creates a vertical <abbr>CRS</abbr> from a reference frame.
++     * This is a shortcut for the {@linkplain #createVerticalCRS(Map, 
VerticalDatum, DefaultDatumEnsemble, VerticalCS)
++     * more generic method} without datum ensemble.
+      *
+      * @param  properties  name and other properties to give to the new 
object.
 -     * @param  method      the realization method of the vertical datum, or 
{@code null} if none.
++     *                     Available properties are {@linkplain ObjectFactory 
listed there}.
++     * @param  datum       vertical datum to use in created CRS.
++     * @param  cs          the vertical coordinate system for the created CRS.
++     * @return the coordinate reference system for the given properties.
+      * @throws FactoryException if the object creation failed.
 -     *
 -     * @see DefaultVerticalDatum#DefaultVerticalDatum(Map, RealizationMethod)
 -     * @see GeodeticAuthorityFactory#createVerticalDatum(String)
 -     *
 -     * @since 2.0 (temporary version number until this branch is released)
+      */
+     @Override
 -    public VerticalDatum createVerticalDatum(final Map<String,?> properties,
 -                                             final RealizationMethod method)
++    public VerticalCRS createVerticalCRS(Map<String, ?> properties, 
VerticalDatum datum, VerticalCS cs)
+             throws FactoryException
+     {
 -        final DefaultVerticalDatum datum;
 -        try {
 -            datum = new DefaultVerticalDatum(complete(properties), method);
 -        } catch (IllegalArgumentException exception) {
 -            throw new InvalidGeodeticParameterException(exception);
 -        }
 -        return unique("createVerticalDatum", datum);
++        return createVerticalCRS(properties, datum, null, cs);
+     }
+ 
      /**
       * Creates a vertical datum from an enumerated type value.
       * The default implementation creates a {@link DefaultVerticalDatum} 
instance.
@@@ -996,10 -1158,14 +1167,13 @@@
       *
       * @see DefaultTemporalCRS#DefaultTemporalCRS(Map, TemporalDatum, TimeCS)
       * @see GeodeticAuthorityFactory#createTemporalCRS(String)
+      *
+      * @since 1.5
       */
--    @Override
      public TemporalCRS createTemporalCRS(final Map<String,?> properties,
-             final TemporalDatum datum, final TimeCS cs) throws 
FactoryException
+                                          final TemporalDatum datum,
 -                                         final DatumEnsemble<TemporalDatum> 
ensemble,
++                                         final 
DefaultDatumEnsemble<TemporalDatum> ensemble,
+                                          final TimeCS cs) throws 
FactoryException
      {
          final DefaultTemporalCRS crs;
          try {
@@@ -1010,6 -1176,6 +1184,25 @@@
          return unique("createTemporalCRS", crs);
      }
  
++    /**
++     * Creates a temporal <abbr>CRS</abbr> from a datum.
++     * This is a shortcut for the {@linkplain #createTemporalCRS(Map, 
TemporalDatum, DefaultDatumEnsemble, TimeCS)
++     * more generic method} without datum ensemble.
++     *
++     * @param  properties  name and other properties to give to the new 
object.
++     *         Available properties are {@linkplain ObjectFactory listed 
there}.
++     * @param  datum  temporal datum to use in created CRS.
++     * @param  cs  the temporal coordinate system for the created CRS.
++     * @return the coordinate reference system for the given properties.
++     * @throws FactoryException if the object creation failed.
++     */
++    @Override
++    public TemporalCRS createTemporalCRS(Map<String, ?> properties, 
TemporalDatum datum, TimeCS cs)
++            throws FactoryException
++    {
++        return createTemporalCRS(properties, datum, null, cs);
++    }
++
      /**
       * Creates a temporal datum from an enumerated type value.
       * The default implementation creates a {@link DefaultTemporalDatum} 
instance.
@@@ -1023,7 -1189,8 +1216,8 @@@
       */
      @Override
      public TemporalDatum createTemporalDatum(final Map<String,?> properties,
-             final Date origin) throws FactoryException
 -                                             final Temporal origin)
++                                             final Date origin)
+             throws FactoryException
      {
          final DefaultTemporalDatum datum;
          try {
@@@ -1077,25 -1245,30 +1272,34 @@@
       *   <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, 
Unit)}</li>
       *   <li>{@link #createParametricCS(Map, CoordinateSystemAxis)}</li>
       *   <li>{@link #createParametricDatum(Map)}</li>
+      *   <li>{@link #createDatumEnsemble(Map, Collection, 
PositionalAccuracy)} (optional)</li>
       * </ol>
       *
+      * At least one of the {@code datum} and {@code ensemble} arguments shall 
be non-null.
       * The default implementation creates a {@link DefaultParametricCRS} 
instance.
       *
 +     * <div class="warning"><b>Warning:</b> in a future SIS version, the 
parameter types may be changed to
 +     * {@code org.opengis.referencing.datum.ParametricDatum} and {@code 
org.opengis.referencing.cs.ParametricCS},
 +     * and the return type may be changed to {@code 
org.opengis.referencing.crs.ParametricCRS}.
 +     * Those change are pending GeoAPI revision.</div>
 +     *
       * @param  properties  name and other properties to give to the new 
object.
-      * @param  datum       the parametric datum to use in created CRS.
+      * @param  datum       parametric datum, or {@code null} if the CRS is 
associated only to a datum ensemble.
+      * @param  ensemble    collection of datum which for low accuracy 
requirements may be considered to be
+      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
       * @param  cs          the parametric coordinate system for the created 
CRS.
       * @throws FactoryException if the object creation failed.
       *
 -     * @see DefaultParametricCRS#DefaultParametricCRS(Map, ParametricDatum, 
ParametricCS)
 +     * @see DefaultParametricCRS#DefaultParametricCRS(Map, 
DefaultParametricDatum, DefaultParametricCS)
       * @see GeodeticAuthorityFactory#createParametricCRS(String)
+      *
+      * @since 1.5
       */
 -    @Override
 -    public ParametricCRS createParametricCRS(final Map<String,?> properties,
 -                                             final ParametricDatum datum,
 -                                             final 
DatumEnsemble<ParametricDatum> ensemble,
 -                                             final ParametricCS cs)
 +    public DefaultParametricCRS createParametricCRS(final Map<String,?> 
properties,
-             final DefaultParametricDatum datum, final DefaultParametricCS cs) 
throws FactoryException
++                                             final DefaultParametricDatum 
datum,
++                                             final 
DefaultDatumEnsemble<DefaultParametricDatum> ensemble,
++                                             final DefaultParametricCS cs)
+             throws FactoryException
      {
          final DefaultParametricCRS crs;
          try {
@@@ -1106,6 -1279,6 +1310,26 @@@
          return unique("createParametricCRS", crs);
      }
  
++    /**
++     * Creates a parametric <abbr>CRS</abbr> from a datum.
++     * This is a shortcut for the {@linkplain #createParametricCRS(Map, 
ParametricDatum, DatumEnsemble, ParametricCS)
++     * more generic method} without datum ensemble.
++     *
++     * @param  properties  name and other properties to give to the new 
object.
++     *         Available properties are {@linkplain ObjectFactory listed 
there}.
++     * @param  datum  temporal datum to use in created CRS.
++     * @param  cs  the temporal coordinate system for the created CRS.
++     * @return the coordinate reference system for the given properties.
++     * @throws FactoryException if the object creation failed.
++     */
++    public DefaultParametricCRS createParametricCRS(final Map<String,?> 
properties,
++                                             final DefaultParametricDatum 
datum,
++                                             final DefaultParametricCS cs)
++            throws FactoryException
++    {
++        return createParametricCRS(properties, datum, null, cs);
++    }
++
      /**
       * Creates a parametric datum.
       * The default implementation creates a {@link DefaultParametricDatum} 
instance.
@@@ -1153,7 -1321,11 +1377,10 @@@
       * @see DefaultParametricCS#DefaultParametricCS(Map, CoordinateSystemAxis)
       * @see GeodeticAuthorityFactory#createParametricCS(String)
       */
-     public DefaultParametricCS createParametricCS(Map<String, ?> properties, 
CoordinateSystemAxis axis) throws FactoryException {
 -    @Override
 -    public ParametricCS createParametricCS(final Map<String, ?> properties,
++    public DefaultParametricCS createParametricCS(final Map<String, ?> 
properties,
+                                            final CoordinateSystemAxis axis)
+             throws FactoryException
+     {
          final DefaultParametricCS cs;
          try {
              cs = new DefaultParametricCS(complete(properties), axis);
@@@ -1313,10 -1491,15 +1546,14 @@@
       *
       * @see DefaultEngineeringCRS#DefaultEngineeringCRS(Map, 
EngineeringDatum, CoordinateSystem)
       * @see GeodeticAuthorityFactory#createEngineeringCRS(String)
+      *
+      * @since 1.5
       */
--    @Override
      public EngineeringCRS createEngineeringCRS(final Map<String,?> properties,
-             final EngineeringDatum datum, final CoordinateSystem cs) throws 
FactoryException
+                                                final EngineeringDatum datum,
 -                                               final 
DatumEnsemble<EngineeringDatum> ensemble,
++                                               final 
DefaultDatumEnsemble<EngineeringDatum> ensemble,
+                                                final CoordinateSystem cs)
+             throws FactoryException
      {
          final DefaultEngineeringCRS crs;
          try {
@@@ -1327,6 -1510,6 +1564,25 @@@
          return unique("createEngineeringCRS", crs);
      }
  
++    /**
++     * Creates a engineering <abbr>CRS</abbr> from a datum.
++     * This is a shortcut for the {@linkplain #createEngineeringCRS(Map, 
EngineeringDatum, DefaultDatumEnsemble, CoordinateSystem)
++     * more generic method} without datum ensemble.
++     *
++     * @param  properties  name and other properties to give to the new 
object.
++     *                     Available properties are {@linkplain ObjectFactory 
listed there}.
++     * @param  datum       engineering datum to use in created CRS.
++     * @param  cs          the coordinate system for the created CRS.
++     * @return the coordinate reference system for the given properties.
++     * @throws FactoryException if the object creation failed.
++     */
++    @Override
++    public EngineeringCRS createEngineeringCRS(Map<String, ?> properties, 
EngineeringDatum datum, CoordinateSystem cs)
++            throws FactoryException
++    {
++        return createEngineeringCRS(properties, datum, null, cs);
++    }
++
      /**
       * Creates an engineering datum.
       * The default implementation creates a {@link DefaultEngineeringDatum} 
instance.
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
index 886673f8e5,1b312740a4..54e8ba460e
--- 
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
@@@ -52,13 -52,6 +52,14 @@@ import org.apache.sis.referencing.cs.De
  import org.apache.sis.referencing.internal.VerticalDatumTypes;
  import 
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
  
 +// Specific to the main branch:
 +import java.util.Collection;
 +import java.util.NoSuchElementException;
 +import org.opengis.referencing.ReferenceIdentifier;
++import org.apache.sis.pending.geoapi.referencing.MissingMethods;
 +import org.apache.sis.metadata.privy.Identifiers;
 +import org.apache.sis.xml.NilObject;
 +
  
  /**
   * A set of static methods working on GeoAPI referencing objects.
@@@ -395,9 -344,10 +396,10 @@@ public final class ReferencingUtilitie
              if (crs instanceof GeographicCRS && 
Utilities.equalsIgnoreMetadata(normalizedCS, cs)) {
                  return (GeographicCRS) crs;
              }
+             final var source = (GeodeticCRS) crs;
              return new DefaultGeographicCRS(
                      Map.of(DefaultGeographicCRS.NAME_KEY, 
NilReferencingObject.UNNAMED),
-                     ((GeodeticCRS) crs).getDatum(), normalizedCS);
 -                    source.getDatum(), source.getDatumEnsemble(), 
normalizedCS);
++                    source.getDatum(), 
MissingMethods.getDatumEnsemble(source), normalizedCS);
          }
          if (crs instanceof CompoundCRS) {
              for (final CoordinateReferenceSystem e : ((CompoundCRS) 
crs).getComponents()) {
diff --cc 
endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/IdentifiedObjectFinderTest.java
index 4aba0eb1e7,eb7371b579..a9e651991e
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/IdentifiedObjectFinderTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/IdentifiedObjectFinderTest.java
@@@ -30,6 -30,6 +30,9 @@@ import static org.junit.jupiter.api.Ass
  import org.apache.sis.test.TestCase;
  import static org.apache.sis.test.Assertions.assertEqualsIgnoreMetadata;
  
++// Specific to the main branch:
++import static 
org.apache.sis.pending.geoapi.referencing.MissingMethods.getDatumEnsemble;
++
  
  /**
   * Tests {@link IdentifiedObjectFinder}.
@@@ -75,7 -75,7 +78,7 @@@ public final class IdentifiedObjectFind
           */
          final CoordinateReferenceSystem search = new DefaultGeographicCRS(
                  Map.of(DefaultGeographicCRS.NAME_KEY, CRS84.getName()),
-                 CRS84.getDatum(), CRS84.getCoordinateSystem());
 -                CRS84.getDatum(), CRS84.getDatumEnsemble(), 
CRS84.getCoordinateSystem());
++                CRS84.getDatum(), getDatumEnsemble(CRS84), 
CRS84.getCoordinateSystem());
          assertEqualsIgnoreMetadata(CRS84, search);              // Required 
condition for next test.
  
          finder.setSearchDomain(IdentifiedObjectFinder.Domain.DECLARATION);
diff --cc 
endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultConversionTest.java
index 5f535526c4,8f4124dbd8..2ea4268ce1
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultConversionTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultConversionTest.java
@@@ -50,8 -50,8 +50,9 @@@ import org.apache.sis.parameter.Default
  import static org.apache.sis.test.Assertions.assertMessageContains;
  import static org.apache.sis.test.Assertions.assertSerializedEquals;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import static org.opengis.test.Assertions.assertMatrixEquals;
 +// Specific to the main branch:
 +import static org.apache.sis.test.GeoapiAssert.assertMatrixEquals;
++import static 
org.apache.sis.pending.geoapi.referencing.MissingMethods.getDatumEnsemble;
  
  
  /**
@@@ -97,8 -98,10 +99,10 @@@ public final class DefaultConversionTes
       * Changes only the coordinate system of the given CRS, which is supposed 
geographic.
       */
      private static GeographicCRS changeCS(final CoordinateReferenceSystem 
crs, final EllipsoidalCS cs) {
-         return new DefaultGeographicCRS(Map.of(DefaultGeographicCRS.NAME_KEY, 
crs.getName()),
-                                         ((GeodeticCRS) crs).getDatum(), cs);
+         final var source = (GeodeticCRS) crs;
+         return new DefaultGeographicCRS(
+                 Map.of(DefaultGeographicCRS.NAME_KEY, crs.getName()),
 -                source.getDatum(), source.getDatumEnsemble(), cs);
++                source.getDatum(), getDatumEnsemble(source), cs);
      }
  
      /**
diff --cc 
endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java
index 7505b27228,36338f1c75..998643870f
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java
@@@ -238,10 -230,10 +238,10 @@@ public final class MetadataTest extend
                          nameAndIdentifier("depth", "Depth", null), axis);
  
                  final var datum = new DefaultVerticalDatum(
 -                        nameAndIdentifier("D28", "Depth below D28", "For 
testing purpose"), (RealizationMethod) null);
 +                        nameAndIdentifier("D28", "Depth below D28", "For 
testing purpose"), VerticalDatumType.OTHER_SURFACE);
  
                  final var vcrs = new DefaultVerticalCRS(
-                         nameAndIdentifier("D28", "Depth below D28", "CRS for 
testing purpose"), datum, cs);
+                         nameAndIdentifier("D28", "Depth below D28", "CRS for 
testing purpose"), datum, null, cs);
  
                  final var temporal = new DefaultTemporalExtent(
                          OffsetDateTime.parse("1990-06-05T00:00Z"),

Reply via email to