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

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

commit 88c4efc2c7686f7aab2592570a08d70fd8378e96
Merge: cb32b1b2f0 788778542c
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon Jul 21 10:24:41 2025 +0200

    Merge branch 'geoapi-4.0' into geoapi-3.1.
    Contains work in preparation to upgrade of the EPSG database
    and improvement in the handling of CRS of geometries of features.

 .../org/apache/sis/coverage/SampleDimension.java   |  10 +-
 .../org/apache/sis/coverage/grid/GridCoverage.java |   2 +-
 .../org/apache/sis/feature/AbstractFeature.java    |  55 +-
 .../main/org/apache/sis/feature/DenseFeature.java  |  35 +-
 .../org/apache/sis/feature/EnvelopeOperation.java  |   2 +-
 .../main/org/apache/sis/feature/Features.java      |  14 +-
 .../main/org/apache/sis/feature/SparseFeature.java |  35 +-
 .../sis/feature/privy/AttributeConvention.java     |  57 +-
 .../apache/sis/filter/BinaryGeometryFilter.java    |  34 +-
 .../org/apache/sis/filter/BinarySpatialFilter.java |   5 +-
 .../main/org/apache/sis/filter/DistanceFilter.java |   5 +-
 .../apache/sis/filter/InvalidXPathException.java   |   2 +-
 .../sis/filter/internal/GeometryConverter.java     |  50 +-
 .../sis/filter/internal/GeometryFromFeature.java   | 108 +++
 .../main/org/apache/sis/filter/internal/Node.java  |  38 +-
 .../apache/sis/filter/sqlmm/FunctionWithSRID.java  |   4 +-
 .../apache/sis/filter/sqlmm/GeometryParser.java    |   3 +-
 .../org/apache/sis/filter/sqlmm/ST_Transform.java  |   6 +-
 .../apache/sis/geometry/wrapper/Geometries.java    |  74 +-
 .../sis/geometry/wrapper/GeometryWithCRS.java      |  92 ---
 .../sis/geometry/wrapper/GeometryWrapper.java      | 316 ++++----
 .../apache/sis/geometry/wrapper/esri/Wrapper.java  |   3 +-
 .../apache/sis/geometry/wrapper/j2d/Factory.java   |   2 +-
 .../sis/geometry/wrapper/j2d/PointWrapper.java     |  13 +-
 .../apache/sis/geometry/wrapper/j2d/Wrapper.java   |   8 +-
 .../apache/sis/geometry/wrapper/jts/Factory.java   |  28 +-
 .../org/apache/sis/geometry/wrapper/jts/JTS.java   |  45 +-
 .../apache/sis/geometry/wrapper/jts/Wrapper.java   |  79 +-
 .../main/org/apache/sis/image/PixelIterator.java   |   6 +-
 .../apache/sis/image/privy/ColorModelFactory.java  |   3 +
 .../apache/sis/image/privy/ColorScaleBuilder.java  |  17 +-
 .../feature/builder/AttributeTypeBuilderTest.java  |   2 +-
 .../sis/feature/privy/AttributeConventionTest.java |   9 +-
 .../apache/sis/geometry/wrapper/jts/JTSTest.java   |   6 +-
 .../org/apache/sis/metadata/MetadataStandard.java  |  67 +-
 .../org/apache/sis/metadata/PropertyAccessor.java  |   4 +-
 .../sis/metadata/StandardImplementation.java       |   3 +-
 .../main/org/apache/sis/metadata/TreeNode.java     |   2 +-
 .../org/apache/sis/metadata/TreeNodeChildren.java  |   2 +-
 .../apache/sis/metadata/internal/ExcludedSet.java  |   6 +-
 .../apache/sis/metadata/privy/SecondaryTrait.java  |  45 ++
 .../apache/sis/metadata/sql/MetadataSource.java    |   6 +-
 .../org/apache/sis/metadata/sql/privy/Dialect.java |  20 +-
 .../apache/sis/metadata/sql/privy/Reflection.java  |   2 -
 .../apache/sis/metadata/sql/privy/SQLBuilder.java  |  59 +-
 .../sis/metadata/sql/privy/SQLUtilities.java       |   9 +-
 .../sis/metadata/sql/privy/ScriptRunner.java       |  15 +-
 .../apache/sis/metadata/sql/privy/Supports.java    |  12 +-
 .../org/apache/sis/metadata/sql/privy/Syntax.java  |  76 +-
 .../org/apache/sis/portrayal/CanvasContext.java    |   8 +-
 .../org/apache/sis/geometry/CoordinateFormat.java  |   4 +-
 .../main/org/apache/sis/io/wkt/VerticalInfo.java   |   4 +-
 .../main/org/apache/sis/referencing/CRS.java       |   7 +-
 .../main/org/apache/sis/referencing/CommonCRS.java |   6 +-
 .../apache/sis/referencing/GeodeticCalculator.java |   9 +-
 .../sis/referencing/MultiRegisterOperations.java   |   4 +-
 .../sis/referencing/crs/AbstractSingleCRS.java     |  37 +-
 .../sis/referencing/crs/DefaultGeographicCRS.java  |   6 +-
 .../sis/referencing/crs/DefaultTemporalCRS.java    |   4 +-
 .../sis/referencing/crs/ExplicitParameters.java    |   4 +-
 .../sis/referencing/datum/DatumOrEnsemble.java     | 456 +++++++++++
 .../referencing/datum/DefaultDatumEnsemble.java    | 674 +++++++++++++++-
 .../referencing/datum/DefaultPrimeMeridian.java    |   2 +-
 .../apache/sis/referencing/datum/PseudoDatum.java  | 885 ---------------------
 .../referencing/factory/GeodeticObjectFactory.java |   2 +-
 .../referencing/factory/sql/EPSGCodeFinder.java    |  10 +-
 .../referencing/factory/sql/EPSGDataAccess.java    | 176 ++--
 .../sis/referencing/factory/sql/EPSGFactory.java   |  24 +-
 .../sis/referencing/factory/sql/EPSGInstaller.java |  34 +-
 .../sis/referencing/factory/sql/EPSG_Prepare.sql   |  18 +-
 .../sis/referencing/factory/sql/EPSG_README.md     |  15 +-
 .../factory/sql/InstallationScriptProvider.java    | 102 ++-
 .../sis/referencing/factory/sql/SQLTranslator.java | 385 +++++----
 .../sis/referencing/factory/sql/TableInfo.java     |  69 +-
 .../sis/referencing/factory/sql/package-info.java  |   4 +-
 .../internal/ParameterizedTransformBuilder.java    |   6 +-
 .../apache/sis/referencing/internal/Resources.java |   2 +-
 .../sis/referencing/internal/Resources.properties  |   2 +-
 .../referencing/internal/Resources_fr.properties   |   2 +-
 .../referencing/internal/SignReversalComment.java  |   4 +-
 .../operation/CoordinateOperationFinder.java       |  31 +-
 .../operation/CoordinateOperationRegistry.java     |   6 +-
 .../operation/DefaultConcatenatedOperation.java    |   4 +-
 .../referencing/operation/DefaultConversion.java   |   3 +-
 .../DefaultCoordinateOperationFactory.java         |   4 +-
 .../referencing/operation/SubOperationInfo.java    |   1 +
 .../referencing/operation/gridded/GridFile.java    |   8 +-
 .../sis/referencing/operation/matrix/Matrix1.java  |  13 +
 .../sis/referencing/operation/matrix/Matrix2.java  |  37 +
 .../sis/referencing/operation/matrix/Matrix3.java  |  18 +
 .../sis/referencing/operation/matrix/Matrix4.java  |  20 +
 .../referencing/operation/matrix/MatrixSIS.java    |   8 +-
 .../operation/provider/AbstractProvider.java       |   8 +-
 .../provider/FranceGeocentricInterpolation.java    |   2 +
 .../sis/referencing/operation/provider/NADCON.java |   2 +
 .../sis/referencing/operation/provider/NTv2.java   |   2 +
 .../operation/transform/AbstractMathTransform.java |   2 +-
 .../transform/DefaultMathTransformFactory.java     |   6 +-
 .../operation/transform/TransformSeparator.java    |  10 +-
 .../sis/referencing/privy/AffineTransform2D.java   |  10 +-
 .../sis/referencing/privy/DefinitionVerifier.java  |  38 +-
 .../privy/EllipsoidalHeightCombiner.java           |   4 +-
 .../referencing/privy/ReferencingUtilities.java    |  90 ---
 .../apache/sis/referencing/privy/WKTUtilities.java |   6 +-
 .../sis/io/wkt/GeodeticObjectParserTest.java       |   2 +-
 .../sis/referencing/GeodesicsOnEllipsoidTest.java  |   4 +-
 .../referencing/factory/sql/EPSGInstallerTest.java |  12 +-
 .../sis/referencing/factory/sql/TableInfoTest.java |   7 +-
 .../transform/InterpolatedTransformTest.java       |   2 +-
 .../main/org/apache/sis/storage/landsat/Band.java  |  13 +-
 .../sis/storage/geotiff/CompressedSubset.java      |  23 +-
 .../org/apache/sis/storage/geotiff/DataCube.java   |  19 +-
 .../org/apache/sis/storage/geotiff/DataSubset.java |  26 +-
 .../sis/storage/geotiff/ImageFileDirectory.java    | 110 ++-
 .../apache/sis/storage/geotiff/inflater/LZW.java   |   3 +-
 .../sis/storage/geotiff/reader/CRSBuilder.java     |   6 +-
 .../sis/storage/geotiff/writer/GeoEncoder.java     |   6 +-
 .../sis/storage/netcdf/base/FeatureSetTest.java    |   6 +-
 .../apache/sis/storage/sql/feature/Analyzer.java   |   5 +-
 .../apache/sis/storage/sql/feature/Database.java   |   6 +-
 .../sis/storage/sql/feature/FeatureAdapter.java    |   4 +-
 .../sis/storage/sql/feature/FeatureIterator.java   |   4 +-
 .../sis/storage/sql/feature/FeatureStream.java     |  34 +-
 .../sis/storage/sql/feature/InfoStatements.java    |  17 +-
 .../sis/storage/sql/postgis/ExtentEstimator.java   |   6 +-
 .../apache/sis/storage/sql/postgis/Postgres.java   |   2 +-
 .../storage/sql/feature/GeometryGetterTest.java    |   8 +-
 .../apache/sis/storage/base/TiledGridCoverage.java |  55 +-
 .../apache/sis/storage/base/TiledGridResource.java |  25 +-
 .../main/org/apache/sis/storage/csv/Store.java     |   4 +-
 .../sis/storage/modifier/CoverageModifier.java     | 103 ++-
 .../main/org/apache/sis/system/DataDirectory.java  |   2 +-
 .../sis/util/privy/UnmodifiableArrayList.java      |   2 +-
 .../org/apache/sis/util/CharSequencesTest.java     |  63 +-
 .../org.apache.sis.geometries.processor.Processor  |  28 +
 .../org/apache/sis/geometries/AttributesType.java  |   6 +-
 .../main/org/apache/sis/geometries/BBox.java       |  19 +-
 ...DefaultMultiPolygon.java => CompoundCurve.java} |  14 +-
 .../main/org/apache/sis/geometries/Geometries.java |  21 +-
 .../org/apache/sis/geometries/GeometryFactory.java | 105 +++
 .../main/org/apache/sis/geometries/LineString.java |  10 +-
 .../main/org/apache/sis/geometries/LinearRing.java |   1 +
 .../main/org/apache/sis/geometries/MultiCurve.java |   1 +
 .../org/apache/sis/geometries/MultiLineString.java |   1 +
 .../main/org/apache/sis/geometries/MultiPoint.java |   1 +
 .../org/apache/sis/geometries/MultiPolygon.java    |   2 +
 ...DefaultLineString.java => MultiPolyhedron.java} |  21 +-
 .../org/apache/sis/geometries/MultiSurface.java    |   1 +
 .../main/org/apache/sis/geometries/OBBox.java      |   3 +-
 .../main/org/apache/sis/geometries/Point.java      |   3 +-
 .../org/apache/sis/geometries/PointSequence.java   |  32 +-
 .../main/org/apache/sis/geometries/Polygon.java    |   2 +
 .../main/org/apache/sis/geometries/Polyhedron.java |  63 ++
 .../org/apache/sis/geometries/PreparedTIN.java     |  11 +-
 .../sis/geometries/{LinearRing.java => Prism.java} |  38 +-
 .../main/org/apache/sis/geometries/Sphere.java     |   3 +-
 .../main/org/apache/sis/geometries/TIN.java        |   1 +
 .../main/org/apache/sis/geometries/Triangle.java   |  22 +-
 .../CircularString.java}                           |  17 +-
 .../sis/geometries/math/AbstractTupleArray.java    |  37 -
 .../org/apache/sis/geometries/math/Matrix.java     |   5 +-
 .../org/apache/sis/geometries/math/Matrix2D.java   |  50 +-
 .../org/apache/sis/geometries/math/Matrix3D.java   |  42 +-
 .../org/apache/sis/geometries/math/Matrix4D.java   |  40 +-
 .../org/apache/sis/geometries/math/MatrixND.java   |  44 -
 .../main/org/apache/sis/geometries/math/Tuple.java |  46 +-
 .../org/apache/sis/geometries/math/TupleArray.java |   2 +-
 .../apache/sis/geometries/math/TupleArrays.java    |  95 ++-
 .../sis/geometries/math/TupleUnmodifiable.java     |  15 -
 .../org/apache/sis/geometries/math/Vectors.java    | 111 ++-
 .../sis/geometries/{ => mesh}/MeshPrimitive.java   |  41 +-
 .../{ => mesh}/MeshPrimitiveComparator.java        |   9 +-
 .../{ => mesh}/MeshPrimitiveIndexes.java           |   2 +-
 .../{ => mesh}/MeshPrimitiveVisitor.java           |  12 +-
 .../geometries/{ => mesh}/MultiMeshPrimitive.java  |   5 +-
 .../geometries/operation/GeometryOperations.java   |   4 +-
 .../operation/spatialanalysis2d/ISOBand.java       |  16 +-
 .../operation/spatialanalysis2d/ISOLine.java       |  10 +-
 .../operation/spatialanalysis2d/IsoInclusion.java  |   2 +-
 .../org/apache/sis/geometries/package-info.java    |   2 +
 .../geometries/{ => privy}/AbstractGeometry.java   |  10 +-
 .../sis/geometries/{ => privy}/ArraySequence.java  |  13 +-
 .../{ => privy}/DefaultGeometryCollection.java     |   4 +-
 .../geometries/{ => privy}/DefaultLineString.java  |   5 +-
 .../geometries/{ => privy}/DefaultLinearRing.java  |   5 +-
 .../DefaultMultiCurve.java}                        |   8 +-
 .../{ => privy}/DefaultMultiLineString.java        |   7 +-
 .../geometries/{ => privy}/DefaultMultiPoint.java  |   6 +-
 .../{ => privy}/DefaultMultiPolygon.java           |   5 +-
 .../{ => privy}/DefaultMultiSurface.java           |   4 +-
 .../sis/geometries/{ => privy}/DefaultPoint.java   |   5 +-
 .../{ => privy}/DefaultPointSequence.java          |   5 +-
 .../sis/geometries/{ => privy}/DefaultPolygon.java |   6 +-
 .../geometries/{ => privy}/DefaultTriangle.java    |   5 +-
 .../processor/spatialanalysis2d/Intersection.java  |  15 +-
 .../processor/spatialedition/ComputeAttribute.java |  28 +-
 .../geometries/processor/spatialedition/To3D.java  |  35 +-
 .../processor/spatialedition/ToPrimitive.java      |  50 +-
 .../processor/spatialedition/Transform.java        | 112 ++-
 .../simplify/greedyinsert/TINBuilder.java          |  10 +-
 .../simplify/greedyinsert/WTriangle.java           |   6 +-
 .../sis/geometries/triangulate/EarClipping.java    |   2 +-
 .../geometries/triangulate/delaunay/Delaunay.java  |   4 +-
 .../triangulate/delaunay/OrientedEdge.java         |   6 +-
 .../triangulate/delaunay/OrientedTriangle.java     |  10 +-
 .../org/apache/sis/geometries/GeometriesTest.java  | 170 ----
 .../org/apache/sis/geometries/PreparedTINTest.java |   1 +
 .../org/apache/sis/geometries/PrimitiveTest.java   |  64 --
 .../org/apache/sis/geometries/TriangleTest.java    |   6 +-
 .../sis/geometries/math/AbstractTupleTest.java     |  38 -
 .../apache/sis/geometries/math/VectorsTest.java    |   8 +-
 .../{ => mesh}/PrimitiveIndexesTest.java           |   4 +-
 .../PrimitiveTest.java}                            |  67 +-
 .../{ => mesh}/PrimitiveVisitorTest.java           |   9 +-
 .../operation/spatialanalysis2d/IsoBandTest.java   |   2 +-
 .../geometries/{ => privy}/ArraySequenceTest.java  |   4 +-
 .../processor/spatialanalysis2d/DistanceTest.java  |  14 +-
 .../spatialanalysis2d/IntersectionTest.java        |   2 +-
 .../processor/spatialedition/To3DTest.java         |   2 +-
 .../processor/spatialedition/ToPrimitiveTest.java  |  48 +-
 .../processor/spatialedition/TransformTest.java    |   4 +-
 .../triangulate/delaunay/DelaunayTest.java         |   4 +-
 .../triangulate/delaunay/OrientedEdgeTest.java     |   4 +-
 .../sis/storage/geopackage/GpkgStoreTest.java      |   2 +-
 .../sis/storage/shapefile/ShapefileStore.java      |  21 +-
 .../sis/storage/shapefile/ShapefileStoreTest.java  |   4 +
 netbeans-project/nbproject/project.xml             |   1 +
 .../main/org/apache/sis/gui/map/StatusBar.java     |  55 +-
 .../org/apache/sis/gui/map/ValuesFormatter.java    |  25 +-
 .../org/apache/sis/gui/map/ValuesFromCoverage.java |   9 +-
 .../sis/referencing/factory/sql/epsg/README.md     |  25 +-
 .../factory/sql/epsg/DataScriptFormatter.java      |  30 +-
 .../sis/referencing/factory/sql/epsg/README.md     |  87 +-
 .../factory/sql/epsg/ScriptProviderTest.java       |   2 +-
 234 files changed, 4169 insertions(+), 3298 deletions(-)

diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/Geometries.java
index facdd123be,00c44ba879..d65ca53799
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/Geometries.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/Geometries.java
@@@ -38,9 -39,13 +39,13 @@@ import org.apache.sis.system.Loggers
  import org.apache.sis.math.Vector;
  import org.apache.sis.setup.GeometryLibrary;
  import org.apache.sis.util.resources.Errors;
+ import org.apache.sis.util.collection.BackingStoreException;
+ 
+ // Specific to the geoapi-3.1 and 4.0 branches:
+ import org.opengis.feature.Feature;
  
 -// Specific to the geoapi-4.0 branch:
 -import org.opengis.coordinate.MismatchedDimensionException;
 +// Specific to the main and geoapi-3.1 branches:
 +import org.opengis.geometry.MismatchedDimensionException;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
index 4eceaac3c7,9941694085..f81040f0f3
--- 
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
@@@ -339,7 -336,7 +339,7 @@@ public class DefaultTemporalCRS extend
       * @since 1.5
       */
      public final Temporal getOrigin() {     // Must be final because invoked 
at construction time.
-         return TemporalDate.toTemporal(PseudoDatum.of(this).getOrigin());
 -        return DatumOrEnsemble.asDatum(this).getOrigin();
++        return 
TemporalDate.toTemporal(DatumOrEnsemble.asDatum(this).getOrigin());
      }
  
      /**
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java
index 1a3266fe08,f49c4ab2eb..b052185510
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java
@@@ -58,9 -58,9 +58,9 @@@ final class ExplicitParameters extends 
      /**
       * Creates a new temporary {@code Conversion} elements for the parameters 
of the given CRS.
       */
 -    ExplicitParameters(final AbstractDerivedCRS crs, final String keyword) {
 +    ExplicitParameters(final AbstractDerivedCRS<?> crs, final String keyword) 
{
          conversion = crs.getConversionFromBase();
-         ellipsoid = ReferencingUtilities.getEllipsoid(crs);
+         ellipsoid = DatumOrEnsemble.getEllipsoid(crs).orElse(null);
          this.keyword = keyword;
      }
  
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultDatumEnsemble.java
index ebffe6fdfe,1e831e468d..1dfbebbb57
--- 
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
@@@ -32,9 -51,11 +51,14 @@@ import org.apache.sis.util.ComparisonMo
  import org.apache.sis.util.Utilities;
  import org.apache.sis.util.resources.Errors;
  
++// Specific to the main and geoapi-3.1 branches:
++import java.util.Date;
++
  // Specific to the geoapi-3.1 and geoapi-4.0 branches:
- import java.util.Objects;
+ import org.opengis.referencing.crs.ParametricCRS;
  import org.opengis.referencing.datum.DatumEnsemble;
+ import org.opengis.referencing.datum.ParametricDatum;
+ import org.opengis.referencing.datum.RealizationMethod;
  
  
  /**
@@@ -283,4 -473,397 +476,397 @@@ check:  if (it.hasNext()) 
          }
          return WKTKeywords.Ensemble;
      }
+ 
+     /**
+      * An ensemble viewed as a low-accuracy geodetic datum.
+      *
+      * @see #create(Map, Collection, PositionalAccuracy)
+      * @see #castOrCopy(DatumEnsemble)
+      * @see DatumOrEnsemble#of(GeodeticCRS)
+      * @see DatumOrEnsemble#asTargetDatum(GeodeticCRS, GeodeticCRS)
+      */
+     static final class Geodetic extends DefaultDatumEnsemble<GeodeticDatum> 
implements GeodeticDatum {
+         /** For cross-versions compatibility. */
+         private static final long serialVersionUID = 7669230365507661290L;
+ 
+         /** Returns the given ensemble as a pseudo-datum. */
+         static GeodeticDatum datum(DatumEnsemble<GeodeticDatum> ensemble) {
+             return (ensemble == null || ensemble instanceof GeodeticDatum)
+                     ? (GeodeticDatum) ensemble : new Geodetic(ensemble);
+         }
+ 
+         /** Creates a new ensemble as a copy of the given ensemble. */
+         Geodetic(DatumEnsemble<? extends GeodeticDatum> ensemble) {
+             super(ensemble, GeodeticDatum.class);
+         }
+ 
+         /** Creates a new ensemble from the given properties. */
+         Geodetic(Map<String,?> properties, List<? extends GeodeticDatum> 
members, PositionalAccuracy accuracy) {
+             super(properties, members, accuracy, GeodeticDatum.class);
+         }
+ 
+         /**
+          * Returns the ellipsoid which is indirectly (through a datum) 
associated to this datum ensemble.
+          * If all members of the ensemble use the same ellipsoid, then this 
method returns that ellipsoid.
+          *
+          * @return the ellipsoid indirectly associated to this datum ensemble.
+          * @throws NoSuchElementException if the ensemble does not contain at 
least one member.
+          * @throws GeodeticException if the ellipsoid is not the same for all 
members of the datum ensemble.
+          */
+         @Override
+         public Ellipsoid getEllipsoid() {
+             return getCommonMandatoryValue(GeodeticDatum::getEllipsoid);
+         }
+ 
+         /**
+          * Returns the prime meridian which is indirectly (through a datum) 
associated to this datum ensemble.
+          * If all members of the ensemble use the same prime meridian, then 
this method returns that meridian.
+          *
+          * @return the prime meridian indirectly associated to this datum 
ensemble.
+          * @throws NoSuchElementException if the ensemble does not contain at 
least one member.
+          * @throws GeodeticException if the prime meridian is not the same 
for all members of the datum ensemble.
+          */
+         @Override
+         public PrimeMeridian getPrimeMeridian() {
+             return getCommonMandatoryValue(GeodeticDatum::getPrimeMeridian);
+         }
+     }
+ 
+     /**
+      * An ensemble viewed as a low-accuracy vertical datum.
+      *
+      * @see #create(Map, Collection, PositionalAccuracy)
+      * @see #castOrCopy(DatumEnsemble)
+      * @see DatumOrEnsemble#of(VerticalCRS)
+      * @see DatumOrEnsemble#asTargetDatum(VerticalCRS, VerticalCRS)
+      */
+     static final class Vertical extends DefaultDatumEnsemble<VerticalDatum> 
implements VerticalDatum {
+         /** For cross-versions compatibility. */
+         private static final long serialVersionUID = 7242417944400289818L;
+ 
+         /** Returns the given ensemble as a pseudo-datum. */
+         static VerticalDatum datum(DatumEnsemble<VerticalDatum> ensemble) {
+             return (ensemble == null || ensemble instanceof VerticalDatum)
+                     ? (VerticalDatum) ensemble : new Vertical(ensemble);
+         }
+ 
+         /** Creates a new ensemble as a copy of the given ensemble. */
+         Vertical(DatumEnsemble<? extends VerticalDatum> ensemble) {
+             super(ensemble, VerticalDatum.class);
+         }
+ 
+         /** Creates a new ensemble from the given properties. */
+         Vertical(Map<String,?> properties, List<? extends VerticalDatum> 
members, PositionalAccuracy accuracy) {
+             super(properties, members, accuracy, VerticalDatum.class);
+         }
+ 
+         /**
+          * Returns a realization method which is common to all members of the 
datum ensemble.
+          * If the value is not the same for all members, or if at least one 
member has an empty value,
+          * then this method returns an empty value.
+          *
+          * @return the common realization method, or empty if there is no 
common value.
+          */
+         @Override
+         public Optional<RealizationMethod> getRealizationMethod() {
+             return 
getCommonOptionalValue(VerticalDatum::getRealizationMethod);
+         }
+     }
+ 
+     /**
+      * An ensemble viewed as a low-accuracy temporal datum.
+      *
+      * @see #create(Map, Collection, PositionalAccuracy)
+      * @see #castOrCopy(DatumEnsemble)
+      * @see DatumOrEnsemble#of(TemporalCRS)
+      * @see DatumOrEnsemble#asTargetDatum(TemporalCRS, TemporalCRS)
+      */
+     static final class Time extends DefaultDatumEnsemble<TemporalDatum> 
implements TemporalDatum {
+         /** For cross-versions compatibility. */
+         private static final long serialVersionUID = -4208563828181087035L;
+ 
+         /** Returns the given ensemble as a pseudo-datum. */
+         static TemporalDatum datum(DatumEnsemble<TemporalDatum> ensemble) {
+             return (ensemble == null || ensemble instanceof TemporalDatum)
+                     ? (TemporalDatum) ensemble : new Time(ensemble);
+         }
+ 
+         /** Creates a new ensemble as a copy of the given ensemble. */
+         Time(DatumEnsemble<? extends TemporalDatum> ensemble) {
+             super(ensemble, TemporalDatum.class);
+         }
+ 
+         /** Creates a new ensemble from the given properties. */
+         Time(Map<String,?> properties, List<? extends TemporalDatum> members, 
PositionalAccuracy accuracy) {
+             super(properties, members, accuracy, TemporalDatum.class);
+         }
+ 
+         /**
+          * Returns the temporal origin which is indirectly (through a datum) 
associated to this datum ensemble.
+          * If all members of the ensemble use the same temporal origin, then 
this method returns that origin.
+          *
+          * @return the temporal origin indirectly associated to this datum 
ensemble.
+          * @throws NoSuchElementException if the ensemble does not contain at 
least one member.
+          * @throws GeodeticException if the temporal origin is not the same 
for all members of the datum ensemble.
+          */
+         @Override
 -        public Temporal getOrigin() {
++        public Date getOrigin() {
+             return getCommonMandatoryValue(TemporalDatum::getOrigin);
+         }
+     }
+ 
+     /**
+      * An ensemble viewed as a low-accuracy parametric datum.
+      *
+      * @see #create(Map, Collection, PositionalAccuracy)
+      * @see #castOrCopy(DatumEnsemble)
+      * @see DatumOrEnsemble#of(ParametricCRS)
+      * @see DatumOrEnsemble#asTargetDatum(ParametricCRS, ParametricCRS)
+      */
+     static final class Parametric extends 
DefaultDatumEnsemble<ParametricDatum> implements ParametricDatum {
+         /** For cross-versions compatibility. */
+         private static final long serialVersionUID = -8277774591738789437L;
+ 
+         /** Returns the given ensemble as a pseudo-datum. */
+         static ParametricDatum datum(DatumEnsemble<ParametricDatum> ensemble) 
{
+             return (ensemble == null || ensemble instanceof ParametricDatum)
+                     ? (ParametricDatum) ensemble : new Parametric(ensemble);
+         }
+ 
+         /** Creates a new ensemble as a copy of the given ensemble. */
+         Parametric(DatumEnsemble<? extends ParametricDatum> ensemble) {
+             super(ensemble, ParametricDatum.class);
+         }
+ 
+         /** Creates a new ensemble from the given properties. */
+         Parametric(Map<String,?> properties, List<? extends ParametricDatum> 
members, PositionalAccuracy accuracy) {
+             super(properties, members, accuracy, ParametricDatum.class);
+         }
+     }
+ 
+     /**
+      * An ensemble viewed as a low-accuracy engineering datum.
+      *
+      * @see #create(Map, Collection, PositionalAccuracy)
+      * @see #castOrCopy(DatumEnsemble)
+      * @see DatumOrEnsemble#of(EngineeringCRS)
+      * @see DatumOrEnsemble#asTargetDatum(EngineeringCRS, EngineeringCRS)
+      */
+     static final class Engineering extends 
DefaultDatumEnsemble<EngineeringDatum> implements EngineeringDatum {
+         /** For cross-versions compatibility. */
+         private static final long serialVersionUID = -8978468990963666861L;
+ 
+         /** Returns the given ensemble as a pseudo-datum. */
+         static EngineeringDatum datum(DatumEnsemble<EngineeringDatum> 
ensemble) {
+             return (ensemble == null || ensemble instanceof EngineeringDatum)
+                     ? (EngineeringDatum) ensemble : new Engineering(ensemble);
+         }
+ 
+         /** Creates a new ensemble as a copy of the given ensemble. */
+         Engineering(DatumEnsemble<? extends EngineeringDatum> ensemble) {
+             super(ensemble, EngineeringDatum.class);
+         }
+ 
+         /** Creates a new ensemble from the given properties. */
+         Engineering(Map<String,?> properties, List<? extends 
EngineeringDatum> members, PositionalAccuracy accuracy) {
+             super(properties, members, accuracy, EngineeringDatum.class);
+         }
+     }
+ 
+     /**
+      * Factory methods for finding the base type of all members of a list and 
instantiate
+      * the {@link DefaultDatumEnsemble} subclass corresponding to the type of 
all members.
+      * Each instance of {@code Factory} is immutable and thread-safe.
+      * There is one instance for each supported type.
+      *
+      * @param  <D>  base type of all members in the ensembles constructed by 
this factory instance.
+      *
+      * @see #create(Map, Collection, PositionalAccuracy)
+      * @see #castOrCopy(DatumEnsemble)
+      */
+     private static abstract class Factory<D extends Datum> {
+         /**
+          * Base type of all members in the ensembles constructed by this 
factory instance.
+          */
+         private final Class<D> type;
+ 
+         /**
+          * Creates a new factory for ensembles in which all members are 
instances of the given type.
+          *
+          * @param  type  base type of all members in the ensembles 
constructed by this factory instance.
+          */
+         private Factory(final Class<D> type) {
+             this.type = type;
+         }
+ 
+         /**
+          * Finds a common type for all members in the given list, then 
creates a datum ensemble of that type.
+          * In principle, the {@code <D>} type should be restricted to one of 
the types hard-coded in this class.
+          * However, it is okay if {@code <D>} is a custom subclass because it 
appears only in the following places:
+          *
+          * <ul>
+          *   <li>{@link #getMembers()}, which is a read-only collection (it 
is safe to cast {@code List<? extends D>}
+          *       as {@code List<D>} when no write operation is allowed).</li>
+          * </ul>
+          *
+          * Exactly one of {@code object} and {@code properties} should be 
non-null.
+          *
+          * @param  <D>          base type of all members in the ensembles to 
create.
+          * @param  object       the source ensemble to copy, or {@code null} 
if none.
+          * @param  properties   the properties of the ensemble to create, or 
{@code null}.
+          * @param  members      members of the ensemble to copy or create.
+          * @param  accuracy     inaccuracy of the ensemble. Mandatory if 
{@code properties} is non-null.
+          * @return the copied or created ensemble.
+          * @throws ClassCastException if at least one member is not a {@link 
Datum}.
+          *         Should never happen if the parameterized type of {@code 
members} is respected.
+          */
+         static <D extends Datum> DefaultDatumEnsemble<D> forMemberType(
+                 final DatumEnsemble<? extends D> object,
+                 final Map<String,?> properties,
+                 final List<? extends D> members,
+                 final PositionalAccuracy accuracy)
+         {
+ nextType:   for (final Factory<?> factory : FACTORIES) {
+                 for (final Object member : members) {
+                     if (!factory.type.isInstance(member)) {
+                         continue nextType;
+                     }
+                 }
+                 /*
+                  * A more correct type would be `Factory<? super D>` because 
of the use of `isInstance(member)`
+                  * instead of strict class equality. However, even `? super 
D` is not guaranteed to be correct,
+                  * because nothing prevent a member from implementing two 
interfaces. In such case, the type of
+                  * the first matching factory could be unrelated to `D` 
(e.g., `D` is `ParametricDatum` but all
+                  * members also implement `VerticalDatum`). However, despite 
this uncertainty, the cast is okay
+                  * because `D` appears only in `getMembers()` which returns a 
read-only collection. There is no
+                  * method returning `Class<D>` and no guarantees that the 
returned object will implement `D`.
+                  */
+                 @SuppressWarnings("unchecked")
+                 final var selected = (Factory<D>) factory;      // See above 
comment.
+                 if (object != null) {
+                     return selected.copy(object);
+                 } else {
+                     return selected.create(properties, members, accuracy);
+                 }
+             }
+             throw new ClassCastException();
+         }
+ 
+         /**
+          * Creates a new ensemble of the type associated with this factory 
instance.
+          *
+          * @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).
+          * @return the datum ensemble.
+          * @throws IllegalArgumentException if a member is an instance of 
{@link DatumEnsemble}, of if at least two
+          *         different {@linkplain AbstractDatum#getConventionalRS() 
conventional reference systems} are found.
+          *
+          * @see #create(Map, Collection, PositionalAccuracy)
+          */
+         abstract DefaultDatumEnsemble<D> create(Map<String,?> properties, 
List<? extends D> members, PositionalAccuracy accuracy);
+ 
+         /**
+          * Creates a new ensemble with the same values as the specified one.
+          *
+          * @param  ensemble  the ensemble to copy.
+          * @throws IllegalArgumentException if a member is an instance of 
{@link DatumEnsemble}, of if at least two
+          *         different {@linkplain AbstractDatum#getConventionalRS() 
conventional reference systems} are found.
+          *
+          * @see #castOrCopy(DatumEnsemble)
+          */
+         abstract DefaultDatumEnsemble<D> copy(DatumEnsemble<? extends D> 
object);
+ 
+         /**
+          * Factories for all datum types supported by this class. The types 
are (in order) {@link GeodeticDatum},
+          * {@link VerticalDatum}, {@link TemporalDatum}, {@link 
ParametricDatum} and {@link EngineeringDatum}.
+          * The types are tested in iteration order. For example, if a member 
implements both {@code VerticalDatum}
+          * {@code ParametricDatum} interface, then {@code VerticalDatum} has 
precedence.
+          */
+         private static final Factory<?>[] FACTORIES = {
+             new Factory<GeodeticDatum>(GeodeticDatum.class) {
+                 @Override
+                 DefaultDatumEnsemble<GeodeticDatum> copy(DatumEnsemble<? 
extends GeodeticDatum> object) {
+                     return new Geodetic(object);
+                 }
+ 
+                 @Override
+                 DefaultDatumEnsemble<GeodeticDatum> create(Map<String,?> 
properties,
+                         List<? extends GeodeticDatum> members, 
PositionalAccuracy accuracy)
+                 {
+                     return new Geodetic(properties, members, accuracy);
+                 }
+             },
+ 
+             new Factory<VerticalDatum>(VerticalDatum.class) {
+                 @Override
+                 DefaultDatumEnsemble<VerticalDatum> copy(DatumEnsemble<? 
extends VerticalDatum> object) {
+                     return new Vertical(object);
+                 }
+ 
+                 @Override
+                 DefaultDatumEnsemble<VerticalDatum> create(Map<String,?> 
properties,
+                         List<? extends VerticalDatum> members, 
PositionalAccuracy accuracy)
+                 {
+                     return new Vertical(properties, members, accuracy);
+                 }
+             },
+ 
+             new Factory<TemporalDatum>(TemporalDatum.class) {
+                 @Override
+                 DefaultDatumEnsemble<TemporalDatum> copy(DatumEnsemble<? 
extends TemporalDatum> object) {
+                     return new Time(object);
+                 }
+ 
+                 @Override
+                 DefaultDatumEnsemble<TemporalDatum> create(Map<String,?> 
properties,
+                         List<? extends TemporalDatum> members, 
PositionalAccuracy accuracy)
+                 {
+                     return new Time(properties, members, accuracy);
+                 }
+             },
+ 
+             new Factory<ParametricDatum>(ParametricDatum.class) {
+                 @Override
+                 DefaultDatumEnsemble<ParametricDatum> copy(DatumEnsemble<? 
extends ParametricDatum> object) {
+                     return new Parametric(object);
+                 }
+ 
+                 @Override
+                 DefaultDatumEnsemble<ParametricDatum> create(Map<String,?> 
properties,
+                         List<? extends ParametricDatum> members, 
PositionalAccuracy accuracy)
+                 {
+                     return new Parametric(properties, members, accuracy);
+                 }
+             },
+ 
+             new Factory<EngineeringDatum>(EngineeringDatum.class) {
+                 @Override
+                 DefaultDatumEnsemble<EngineeringDatum> copy(DatumEnsemble<? 
extends EngineeringDatum> object) {
+                     return new Engineering(object);
+                 }
+ 
+                 @Override
+                 DefaultDatumEnsemble<EngineeringDatum> create(Map<String,?> 
properties,
+                         List<? extends EngineeringDatum> members, 
PositionalAccuracy accuracy)
+                 {
+                     return new Engineering(properties, members, accuracy);
+                 }
+             },
+ 
+             new Factory<Datum>(Datum.class) {
+                 @Override
+                 DefaultDatumEnsemble<Datum> copy(DatumEnsemble<? extends 
Datum> object) {
+                     return new DefaultDatumEnsemble<>(object, Datum.class);
+                 }
+ 
+                 @Override
+                 DefaultDatumEnsemble<Datum> create(Map<String,?> properties,
+                         List<? extends Datum> members, PositionalAccuracy 
accuracy)
+                 {
+                     return new DefaultDatumEnsemble<>(properties, members, 
accuracy, Datum.class);
+                 }
+             }
+         };
+     }
  }
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java
index 2e5cbb01d9,f632440ebb..e032777c6a
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java
@@@ -26,10 -26,13 +26,10 @@@ import org.opengis.parameter.ParameterD
  import org.apache.sis.referencing.privy.WKTKeywords;
  import org.apache.sis.util.CharSequences;
  
 -// Specific to the geoapi-4.0 branch:
 -import org.apache.sis.referencing.crs.DefaultGeocentricCRS;
 -
  
  /**
-  * Information about a specific table. The MS-Access dialect of SQL is 
assumed;
-  * it will be translated into ANSI SQL later by {@link 
SQLTranslator#apply(String)} if needed.
+  * Information about a specific table. This class uses the mixed-case variant 
of the <abbr>EPSG</abbr> table names.
+  * If needed, those names will be converted by {@link 
SQLTranslator#apply(String)} to the actual table names.
   *
   * @author  Martin Desruisseaux (IRD, Geomatys)
   */
@@@ -61,11 -64,16 +61,11 @@@ final class TableInfo 
       * {@link EPSGDataAccess#createUnit(String)}
       *
       * The order is significant: it is the key for a {@code switch} statement.
 -     *
 -     * <h4>Ambiguity</h4>
 -     * As of ISO 19111:2019, we have no standard way to identify the 
geocentric case from a {@link Class} argument
 -     * because the standard does not provide the {@code GeocentricCRS} 
interface. This implementation fallbacks on
 -     * the SIS-specific geocentric CRS class, with a {@link 
#where(IdentifiedObject, StringBuilder)} method which
 -     * will substitute implementation-neutral objects by the Apache SIS class.
       */
 +    @SuppressWarnings("deprecation")
      static final TableInfo[] EPSG = {
          CRS = new TableInfo(CoordinateReferenceSystem.class,
-                 "[Coordinate Reference System]",
+                 "\"Coordinate Reference System\"",
                  "COORD_REF_SYS_CODE",
                  "COORD_REF_SYS_NAME",
                  "COORD_REF_SYS_KIND",
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/DefinitionVerifier.java
index 5d531c0e2f,bb84005056..ae75b10e61
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/DefinitionVerifier.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/DefinitionVerifier.java
@@@ -305,10 -306,9 +306,10 @@@ public final class DefinitionVerifier 
      /**
       * Returns a code indicating in which part the two given CRS differ. The 
given iterators usually iterate over
       * exactly one element, but may iterate over more elements if the CRS 
were instance of {@code CompoundCRS}.
-      * The returned value is one of {@link #METHOD}, {@link #CONVERSION}, 
{@link #CS}, {@link #DATUM},
-      * {@link #PRIME_MERIDIAN}, {@link #ELLIPSOID} or {@link #OTHER} 
constants.
+      * The returned value is one of {@link #METHOD}, {@link #CONVERSION}, 
{@link #CS}, {@link #PRIME_MERIDIAN},
+      * {@link #ELLIPSOID}, {@link #DATUM} or {@link #OTHER} constants.
       */
 +    @SuppressWarnings("deprecation")
      private static int diffCode(final Iterator<SingleCRS> authoritative, 
final Iterator<SingleCRS> given) {
          while (authoritative.hasNext() && given.hasNext()) {
              final SingleCRS crsA = authoritative.next();
diff --cc 
optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/README.md
index ea9c066c1b,9e3fe552c7..8ccd767e75
--- 
a/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/README.md
+++ 
b/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/README.md
@@@ -85,25 -83,30 +83,30 @@@ Then open the `FKeys.sql` file for edit
  * At the end of all `ALTER TABLE` statement, append `ON UPDATE RESTRICT ON 
DELETE RESTRICT`.
  * Suppress trailing spaces and save.
  
- In most cases this results in unmodified `FKeys.sql` file compared to the 
previous version.
- 
+ Usually, the above editions result in no change compared to the previous 
scripts (ignoring white spaces),
+ in which case the maintainer can just revert the changes in order to preserve 
the formatting.
+ However, if some changes are found in the schema, then hard-coded values in 
the `DataScriptFormatter` class may need
+ to be modified, in particular the `booleanColumnIndicesForTables` and 
`doubleColumnIndicesForTables` collections.
  
- ### Main
  Execute the `main` method of the 
`org.apache.sis.referencing.factory.sql.epsg.DataScriptFormatter` class
- located in the test directory of `sis-referencing` module
- (adjust version numbers as needed; we may provide an easier way after 
migration to Jigsaw modules):
+ located in the test directory of the `org.apache.sis.non-free:sis-epsg` Maven 
sub-project.
+ Adjust version numbers as needed in the following commands:
  
- ```
+ ```bash
  cd _<path to SIS project directory>_
- mvn clean install
+ gradle clean test jar
  export 
CLASSPATH=~/.m2/repository/org/apache/derby/derby/10.14.2.0/derby-10.14.2.0.jar
- export CLASSPATH=$PWD/core/sis-metadata/target/test-classes:$CLASSPATH
- export 
CLASSPATH=$PWD/target/binaries/sis-referencing-1.x-SNAPSHOT.jar:$CLASSPATH
- export CLASSPATH=$PWD/core/sis-metadata/target/test-classes:$CLASSPATH
- export CLASSPATH=$PWD/core/sis-referencing/target/test-classes:$CLASSPATH
- cd <path to local copy of http://svn.apache.org/repos/asf/sis/data/non-free/>
- java org.apache.sis.referencing.factory.sql.epsg.DataScriptFormatter 
$EPSG_SCRIPTS/PostgreSQL_Data_Script.sql \
-      
sis-epsg/src/main/resources/org/apache/sis/referencing/factory/sql/epsg/Data.sql
+ export 
CLASSPATH=~/.m2/repository/javax/measure/unit-api/2.1.3/unit-api-2.1.3.jar:$CLASSPATH
 -export 
CLASSPATH=$PWD/geoapi/snapshot/geoapi/target/geoapi-4.0-SNAPSHOT.jar:$CLASSPATH
++export 
CLASSPATH=$PWD/geoapi/snapshot/geoapi/target/geoapi-3.1-SNAPSHOT.jar:$CLASSPATH
+ export 
CLASSPATH=$PWD/endorsed/build/libs/org.apache.sis.referencing.jar:$CLASSPATH
+ export 
CLASSPATH=$PWD/endorsed/build/libs/org.apache.sis.metadata.jar:$CLASSPATH
+ export CLASSPATH=$PWD/endorsed/build/libs/org.apache.sis.util.jar:$CLASSPATH
+ export 
CLASSPATH=$PWD/endorsed/build/classes/java/test/org.apache.sis.referencing:$CLASSPATH
+ export 
CLASSPATH=$PWD/endorsed/build/classes/java/test/org.apache.sis.metadata:$CLASSPATH
+ export 
CLASSPATH=$PWD/optional/build/classes/java/test/org.apache.sis.referencing.epsg:$CLASSPATH
+ 
+ # From any directory
+ java org.apache.sis.referencing.factory.sql.epsg.DataScriptFormatter 
$EPSG_SCRIPTS/PostgreSQL_Data_Script.sql $NON_FREE_DIR/Data.sql
  ```
  
  Run the tests. It it convenient to run 
`org.apache.sis.referencing.factory.sql.EPSGInstallerTest`


Reply via email to