You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2025/09/01 10:16:41 UTC
(sis) 01/01: Merge branch 'geoapi-3.1'. This work improves the support of EPSG version 12. The handling of datum and CRS types in `TableInfo` has been improved. The search of EPSG code for an existing object has been improved.
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 a4a443d79e03313f59b1208ea658d18656630b4f
Merge: 0661f9eaf0 4e0bc740bc
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Sep 1 12:13:16 2025 +0200
Merge branch 'geoapi-3.1'.
This work improves the support of EPSG version 12.
The handling of datum and CRS types in `TableInfo` has been improved.
The search of EPSG code for an existing object has been improved.
.../org/apache/sis/console/CRSCommandTest.java | 2 +-
.../sis/coverage/grid/DimensionalityReduction.java | 2 +-
.../org/apache/sis/coverage/grid/GridExtent.java | 2 +-
.../org/apache/sis/coverage/grid/GridGeometry.java | 2 +-
.../sis/coverage/grid/ResampledGridCoverage.java | 7 +-
.../sis/geometry/wrapper/GeometryWrapper.java | 4 +-
.../geometry/wrapper/SpatialOperationContext.java | 9 +-
.../org/apache/sis/geometry/wrapper/jts/JTS.java | 5 +-
.../coverage/grid/DimensionalityReductionTest.java | 2 +-
.../org.apache.sis.metadata/main/module-info.java | 4 +
.../metadata/iso/extent/DefaultVerticalExtent.java | 3 +-
.../sis/metadata/privy/NameToIdentifier.java | 2 +-
.../sis/metadata/sql/privy/SQLUtilities.java | 61 +-
.../org/apache/sis/metadata/sql/privy/Syntax.java | 2 +-
.../sis/pending/geoapi/geometry/Geometry.java} | 28 +-
.../sis/pending/geoapi/geometry/package-info.java} | 34 +-
.../main/org/apache/sis/temporal/TemporalDate.java | 2 +-
.../sis/metadata/sql/privy/SQLUtilitiesTest.java | 4 +-
.../sis/openoffice/ReferencingFunctionsTest.java | 5 +-
.../org/apache/sis/map/coverage/RenderingData.java | 3 +-
.../main/org/apache/sis/portrayal/Canvas.java | 13 +-
.../gazetteer/AbstractLocationType.java | 4 +-
.../gazetteer/GeohashReferenceSystem.java | 5 +-
.../gazetteer/MilitaryGridReferenceSystem.java | 5 +-
.../gazetteer/ReferencingByIdentifiers.java | 4 +-
.../sis/coordinate/DefaultCoordinateMetadata.java | 4 +-
.../sis/geometry/AbstractDirectPosition.java | 6 +-
.../org/apache/sis/geometry/AbstractEnvelope.java | 2 +-
.../org/apache/sis/geometry/EnvelopeReducer.java | 5 +-
.../main/org/apache/sis/geometry/Envelopes.java | 6 +-
.../apache/sis/geometry/WraparoundAdjustment.java | 3 +-
.../main/org/apache/sis/io/wkt/Formatter.java | 2 +-
.../apache/sis/io/wkt/GeodeticObjectParser.java | 125 ++--
.../sis/parameter/DefaultParameterDescriptor.java | 6 +-
.../sis/parameter/DefaultParameterValueGroup.java | 8 +-
.../parameter/UnmodifiableParameterValueGroup.java | 8 +-
.../sis/referencing/AbstractIdentifiedObject.java | 15 +-
.../apache/sis/referencing/AuthorityFactories.java | 54 +-
.../main/org/apache/sis/referencing/Builder.java | 4 +-
.../main/org/apache/sis/referencing/CRS.java | 75 ++-
.../main/org/apache/sis/referencing/CommonCRS.java | 40 +-
.../sis/referencing/DefaultObjectDomain.java | 3 +-
.../sis/referencing/EPSGFactoryFallback.java | 41 +-
.../apache/sis/referencing/IdentifiedObjects.java | 39 +-
.../sis/referencing/StandardDefinitions.java | 70 ++-
.../apache/sis/referencing/crs/AbstractCRS.java | 109 ++--
.../sis/referencing/crs/AbstractDerivedCRS.java | 4 +-
.../sis/referencing/crs/AbstractSingleCRS.java | 56 +-
.../sis/referencing/crs/DefaultCompoundCRS.java | 50 +-
.../sis/referencing/crs/DefaultDerivedCRS.java | 49 +-
.../sis/referencing/crs/DefaultEngineeringCRS.java | 10 +
.../sis/referencing/crs/DefaultGeodeticCRS.java | 16 +-
.../sis/referencing/crs/DefaultParametricCRS.java | 10 +
.../sis/referencing/crs/DefaultProjectedCRS.java | 21 +-
.../sis/referencing/crs/DefaultTemporalCRS.java | 9 +
.../sis/referencing/crs/DefaultVerticalCRS.java | 10 +
.../org/apache/sis/referencing/cs/AbstractCS.java | 93 +--
.../sis/referencing/cs/DefaultCompoundCS.java | 40 +-
.../cs/DefaultCoordinateSystemAxis.java | 4 +-
.../sis/referencing/datum/AbstractDatum.java | 10 +-
.../sis/referencing/datum/DatumOrEnsemble.java | 244 ++++++--
.../referencing/datum/DefaultDatumEnsemble.java | 4 +-
.../sis/referencing/datum/DefaultEllipsoid.java | 11 +-
.../referencing/datum/DefaultGeodeticDatum.java | 6 +-
.../sis/referencing/datum/DefaultImageDatum.java | 4 +-
.../referencing/datum/DefaultPrimeMeridian.java | 14 +-
.../referencing/datum/DefaultTemporalDatum.java | 4 +-
.../referencing/datum/DefaultVerticalDatum.java | 67 +-
.../factory/AuthorityFactoryIdentifier.java | 19 +-
.../referencing/factory/AuthorityFactoryProxy.java | 3 +
.../factory/ConcurrentAuthorityFactory.java | 130 +++-
.../factory/GeodeticAuthorityFactory.java | 2 +-
.../factory/IdentifiedObjectFinder.java | 686 +++++++++++++--------
.../factory/MultiAuthoritiesFactory.java | 113 +++-
.../referencing/factory/sql/AuthorityCodes.java | 177 ++++--
.../factory/sql/CloseableReference.java | 6 +
.../referencing/factory/sql/EPSGCodeFinder.java | 396 ++++++++++--
.../referencing/factory/sql/EPSGDataAccess.java | 531 ++++++++--------
.../sis/referencing/factory/sql/EPSGFactory.java | 32 +-
.../sis/referencing/factory/sql/EPSGInstaller.java | 27 +-
.../factory/sql/InstallationScriptProvider.java | 198 +-----
.../referencing/factory/sql/ObjectPertinence.java | 11 +-
.../sis/referencing/factory/sql/SQLTranslator.java | 34 +-
.../sis/referencing/factory/sql/TableInfo.java | 501 +++++++++------
.../referencing/internal/EPSGParameterDomain.java | 4 +-
.../sis/referencing/internal/MergedProperties.java | 4 +-
.../referencing/internal/PositionTransformer.java | 6 +-
.../apache/sis/referencing/internal/RTreeNode.java | 4 +-
.../referencing/internal/VerticalDatumTypes.java | 21 +-
.../operation/AbstractCoordinateOperation.java | 20 +-
.../operation/AbstractSingleOperation.java | 4 +-
.../apache/sis/referencing/operation/CRSPair.java | 7 +-
.../operation/CoordinateOperationFinder.java | 2 +-
.../operation/CoordinateOperationRegistry.java | 9 +-
.../operation/DefaultConcatenatedOperation.java | 1 +
.../operation/DefaultOperationMethod.java | 10 +-
.../sis/referencing/operation/matrix/Matrices.java | 15 +-
.../operation/projection/NormalizedProjection.java | 6 +-
.../operation/provider/Mercator1SP.java | 7 +-
.../operation/provider/ObliqueMercator.java | 12 +-
.../operation/provider/PolarStereographicA.java | 2 +-
.../operation/provider/PseudoMercator.java | 7 +-
.../operation/provider/TransverseMercator.java | 2 +-
.../operation/transform/AbstractMathTransform.java | 2 +-
.../operation/transform/ConcatenatedTransform.java | 15 +-
.../transform/EllipsoidToRadiusTransform.java | 6 +-
.../operation/transform/PoleRotation.java | 1 +
.../sis/referencing/privy/AxisDirections.java | 4 +-
.../sis/referencing/privy/FilteredIterator.java | 113 ++++
.../org/apache/sis/referencing/privy/LazySet.java | 25 +-
.../apache/sis/referencing/privy/WKTUtilities.java | 32 -
.../org/apache/sis/referencing/Assertions.java | 69 ++-
.../sis/referencing/AuthorityFactoriesTest.java | 2 +-
.../test/org/apache/sis/referencing/CRSTest.java | 4 +-
.../org/apache/sis/referencing/CommonCRSTest.java | 4 +-
.../sis/referencing/EPSGFactoryFallbackTest.java | 2 +-
.../sis/referencing/StandardDefinitionsTest.java | 29 +
.../factory/CommonAuthorityFactoryTest.java | 4 +-
.../factory/ConcurrentAuthorityFactoryTest.java | 2 +-
.../factory/IdentifiedObjectFinderTest.java | 3 +-
.../factory/MultiAuthoritiesFactoryTest.java | 2 +-
.../referencing/factory/sql/EPSGFactoryTest.java | 59 +-
.../referencing/factory/sql/EPSGInstallerTest.java | 20 +-
.../factory/sql/EPSGScriptProvider.java | 113 ++++
.../sis/referencing/factory/sql/TableInfoTest.java | 14 +-
.../transform/DefaultMathTransformFactoryTest.java | 2 +-
.../report/CoordinateOperationMethods.java | 2 +-
.../sis/test/integration/ConsistencyTest.java | 63 +-
.../integration/CoordinateReferenceSystemTest.java | 36 --
.../sis/storage/geotiff/reader/CRSBuilder.java | 6 +-
.../apache/sis/storage/netcdf/base/AxisType.java | 2 +-
.../apache/sis/storage/netcdf/base/CRSMerger.java | 4 +-
.../sis/storage/sql/feature/InfoStatements.java | 14 +-
.../sis/storage/sql/feature/SelectionClause.java | 4 +-
.../sis/storage/aggregate/CoverageAggregator.java | 2 +-
.../apache/sis/storage/aggregate/GroupByCRS.java | 4 +-
.../apache/sis/storage/image/WritableStore.java | 2 +-
.../apache/sis/setup/InstallationResources.java | 2 +-
.../main/org/apache/sis/util/CharSequences.java | 66 +-
.../main/org/apache/sis/util/ComparisonMode.java | 146 +++--
.../org/apache/sis/util/LenientComparable.java | 18 +-
.../main/org/apache/sis/util/Utilities.java | 2 +-
.../org/apache/sis/util/collection/TreeTable.java | 2 +-
.../main/org/apache/sis/util/logging/Logging.java | 2 +-
.../main/org/apache/sis/util/privy/Constants.java | 1 +
.../main/org/apache/sis/util/privy/Numerics.java | 2 +-
.../apache/sis/util/privy/SetOfUnknownSize.java | 6 +-
.../main/org/apache/sis/util/resources/Errors.java | 7 +-
.../apache/sis/util/resources/Errors.properties | 1 -
.../apache/sis/util/resources/Errors_fr.properties | 1 -
.../test/org/apache/sis/test/Assertions.java | 4 +-
.../org/apache/sis/util/CharSequencesTest.java | 5 +-
netbeans-project/nbproject/project.xml | 1 +
optional/build.gradle.kts | 18 +-
.../main/org/apache/sis/gui/map/StatusBar.java | 2 +-
.../gui/referencing/PositionableProjection.java | 3 +-
.../sis/resources/embedded/EmbeddedResources.java | 14 +-
.../sis/resources/embedded/package-info.java | 12 +-
.../resources/embedded/EmbeddedResourcesTest.java | 40 +-
.../apache/sis/resources/embedded/Generator.java | 117 ++--
.../sis/referencing/factory/sql/epsg/.gitignore | 5 +-
.../sis/referencing/factory/sql/epsg/Finish.sql | 0
.../sis/referencing/factory/sql/epsg/Prepare.sql | 4 +-
.../sis/referencing/factory/sql/epsg/README.md | 13 +-
.../factory/sql/epsg/ScriptProvider.java | 32 +-
.../referencing/factory/sql/epsg/DebugTools.sql | 10 +
.../factory/sql/epsg/ScriptProviderTest.java | 4 +-
167 files changed, 3573 insertions(+), 2217 deletions(-)
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/GeometryWrapper.java
index 127f829658,0ef0e3126c..0cb39b8a58
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/GeometryWrapper.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/GeometryWrapper.java
@@@ -24,7 -24,7 +24,6 @@@ import javax.measure.Unit
import javax.measure.Quantity;
import javax.measure.IncommensurableException;
import javax.measure.quantity.Length;
--import org.opengis.geometry.Geometry;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.CoordinateOperation;
@@@ -43,9 -43,14 +42,10 @@@ import org.apache.sis.util.Unconvertibl
import org.apache.sis.util.collection.BackingStoreException;
import org.apache.sis.util.resources.Errors;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import java.util.Set;
-import org.opengis.geometry.Boundary;
-import org.opengis.geometry.TransfiniteSet;
-import org.opengis.geometry.complex.Complex;
-import org.opengis.filter.SpatialOperatorName;
-import org.opengis.filter.DistanceOperatorName;
-import org.opengis.coordinate.MismatchedDimensionException;
+// Specific to the main branch:
++import org.apache.sis.pending.geoapi.geometry.Geometry;
+import org.apache.sis.pending.geoapi.filter.SpatialOperatorName;
+import org.apache.sis.pending.geoapi.filter.DistanceOperatorName;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/module-info.java
index 48064df3d8,d160fb9e63..b03e011efd
--- a/endorsed/src/org.apache.sis.metadata/main/module-info.java
+++ b/endorsed/src/org.apache.sis.metadata/main/module-info.java
@@@ -76,9 -76,6 +76,13 @@@ module org.apache.sis.metadata
/*
* Internal API open only to other Apache SIS modules.
*/
++ exports org.apache.sis.pending.geoapi.geometry to
++ org.apache.sis.referencing,
++ org.apache.sis.feature;
++
+ exports org.apache.sis.pending.geoapi.temporal to
+ org.apache.sis.feature;
+
exports org.apache.sis.temporal to
org.apache.sis.referencing,
org.apache.sis.feature,
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/geometry/Geometry.java
index e3b30f74d9,ffe6f48b2c..3c29399d9e
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/geometry/Geometry.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/geometry/Geometry.java
@@@ -14,33 -14,27 +14,27 @@@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- package org.apache.sis.referencing.factory.sql;
-package org.apache.sis.geometries.operation.spatialedition;
++package org.apache.sis.pending.geoapi.geometry;
- // Test dependencies
- import org.junit.jupiter.api.Test;
- import static org.junit.jupiter.api.Assertions.*;
- import org.apache.sis.test.TestCase;
-import org.apache.sis.geometries.Geometry;
-import org.apache.sis.geometries.operation.Operation;
++import org.opengis.geometry.Envelope;
++import org.opengis.referencing.crs.CoordinateReferenceSystem;
/**
- * Tests {@link TableInfo}.
- * Transform geometry to a Primitive or MultiPrimitive.
++ * Placeholder for a GeoAPI interfaces incomplete in GeoAPI 3.0.
*
- * @author Johann Sorel (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
++ * @since 1.5
++ * @version 1.5
*/
- public final class TableInfoTest extends TestCase {
-public class ToPrimitive extends Operation<ToPrimitive> {
-
- public Geometry result;
-
++public interface Geometry extends org.opengis.geometry.Geometry {
/**
- * Creates a new test case.
- *
- * @param geom geometry to transform
++ * Returns the coordinate reference system.
*/
- public TableInfoTest() {
- public ToPrimitive(Geometry geom) {
- super(geom);
-- }
++ CoordinateReferenceSystem getCoordinateReferenceSystem();
+ /**
- * Tests {@link TableInfo#getObjectClassName(String)}.
++ * Returns the minimum bounding box for this {@code Geometry}.
+ */
- @Test
- public void testgGetObjectClassName() {
- assertEquals("Datum", TableInfo.getObjectClassName("epsg_datum").orElseThrow());
- assertEquals("Ellipsoid", TableInfo.getObjectClassName("epsg_ellipsoid").orElseThrow());
- assertEquals("CoordinateReferenceSystem", TableInfo.getObjectClassName("epsg_coordinatereferencesystem").orElseThrow());
- }
++ Envelope getEnvelope();
}
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/geometry/package-info.java
index e3b30f74d9,4307e9c627..e018f1dd2c
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/geometry/package-info.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/geometry/package-info.java
@@@ -14,33 -14,14 +14,17 @@@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- package org.apache.sis.referencing.factory.sql;
-
- // Test dependencies
- import org.junit.jupiter.api.Test;
- import static org.junit.jupiter.api.Assertions.*;
- import org.apache.sis.test.TestCase;
-
/**
- * Tests {@link TableInfo}.
- * CQL parser.
++ * Placeholder for GeoAPI interfaces not present in GeoAPI 3.0.
+ *
- * @author Johann Sorel (Geomatys)
++ * <STRONG>Do not use!</STRONG>
++ *
++ * This package is for internal use by SIS only. Classes in this package
++ * may change in incompatible ways in any future version without notice.
+ *
+ * @author Martin Desruisseaux (Geomatys)
++ * @since 0.3
++ * @version 1.5
*/
- public final class TableInfoTest extends TestCase {
- /**
- * Creates a new test case.
- */
- public TableInfoTest() {
- }
-
- /**
- * Tests {@link TableInfo#getObjectClassName(String)}.
- */
- @Test
- public void testgGetObjectClassName() {
- assertEquals("Datum", TableInfo.getObjectClassName("epsg_datum").orElseThrow());
- assertEquals("Ellipsoid", TableInfo.getObjectClassName("epsg_ellipsoid").orElseThrow());
- assertEquals("CoordinateReferenceSystem", TableInfo.getObjectClassName("epsg_coordinatereferencesystem").orElseThrow());
- }
-module org.apache.sis.cql {
- requires transitive org.apache.sis.feature;
- requires org.locationtech.jts;
- requires org.antlr.antlr4.runtime;
--}
++package org.apache.sis.pending.geoapi.geometry;
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractDirectPosition.java
index 8d53bf50e5,9624762637..ab836d7d7b
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractDirectPosition.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/AbstractDirectPosition.java
@@@ -204,8 -110,8 +204,8 @@@ public abstract class AbstractDirectPos
* {@code null}, then all coordinate values are set to {@link Double#NaN NaN}.
*
* <p>If this position and the given position have a non-null CRS, then the default implementation
- * requires the CRS to be {@linkplain Utilities#equalsIgnoreMetadata equals (ignoring metadata)},
+ * requires the CRS to be {@linkplain CRS#equivalent equivalent},
- * otherwise a {@code MismatchedCoordinateMetadataException} is thrown. However, subclass may choose
+ * otherwise a {@code MismatchedReferenceSystemException} is thrown. However, subclass may choose
* to assign the CRS of this position to the CRS of the given position.</p>
*
* @param position the new position, or {@code null}.
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
index 4e1266cee8,3ebf31d761..8631f90798
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
@@@ -699,7 -689,9 +700,9 @@@ class GeodeticObjectParser extends Math
* @throws FactoryException if the factory cannot create the coordinate system.
*/
private CoordinateSystem parseCoordinateSystem(final Element parent, String type, int dimension,
- final boolean isWKT1, final Unit<?> defaultUnit, final Datum datum) throws ParseException, FactoryException
+ final boolean isWKT1, final Unit<?> defaultUnit,
- final boolean geodetic, final RealizationMethod vertical)
++ final boolean geodetic, final VerticalDatumType vertical)
+ throws ParseException, FactoryException
{
axisOrder.clear();
final boolean is3D = (dimension >= 3);
@@@ -839,20 -831,17 +842,17 @@@
z = "h";
nz = "Height";
direction = AxisDirection.UP;
- if (datum instanceof VerticalDatum) {
- final VerticalDatumType vt = ((VerticalDatum) datum).getVerticalDatumType();
- if (vt == VerticalDatumType.GEOIDAL) {
- nz = AxisNames.GRAVITY_RELATED_HEIGHT;
- z = "H";
- } else if (vt == VerticalDatumType.DEPTH) {
- direction = AxisDirection.DOWN;
- nz = AxisNames.DEPTH;
- z = "D";
- } else if (VerticalDatumTypes.ellipsoidal(vt)) {
- // Not allowed by ISO 19111 as a standalone axis, but SIS is
- // tolerant to this case since it is sometimes hard to avoid.
- nz = AxisNames.ELLIPSOIDAL_HEIGHT;
- }
- if (vertical == RealizationMethod.GEOID) {
++ if (vertical == VerticalDatumType.GEOIDAL) {
+ nz = AxisNames.GRAVITY_RELATED_HEIGHT;
+ z = "H";
- } else if (vertical == RealizationMethod.TIDAL) {
++ } else if (vertical == VerticalDatumType.DEPTH) {
+ direction = AxisDirection.DOWN;
+ nz = AxisNames.DEPTH;
+ z = "D";
+ } else if (VerticalDatumTypes.ellipsoidal(vertical)) {
+ // Not allowed by ISO 19111 as a standalone axis, but SIS is
+ // tolerant to this case since it is sometimes hard to avoid.
+ nz = AxisNames.ELLIPSOIDAL_HEIGHT;
}
break;
}
@@@ -1939,7 -1928,7 +1940,7 @@@
}
final CoordinateSystem cs;
try {
- cs = parseCoordinateSystem(element, WKTKeywords.vertical, 1, isWKT1, unit, datum);
- cs = parseCoordinateSystem(element, WKTKeywords.vertical, 1, isWKT1, unit, false, datum.getRealizationMethod().orElse(null));
++ cs = parseCoordinateSystem(element, WKTKeywords.vertical, 1, isWKT1, unit, false, datum.getVerticalDatumType());
final Map<String,?> properties = parseMetadataAndClose(element, name, datum);
if (cs instanceof VerticalCS) {
final CRSFactory crsFactory = factories.getCRSFactory();
@@@ -2081,9 -2071,9 +2082,9 @@@
}
final CoordinateSystem cs;
try {
- cs = parseCoordinateSystem(element, WKTKeywords.parametric, 1, false, unit, datum);
+ cs = parseCoordinateSystem(element, WKTKeywords.parametric, 1, false, unit, false, null);
final Map<String,?> properties = parseMetadataAndClose(element, name, datum);
- if (cs instanceof ParametricCS) {
+ if (cs != null) {
final CRSFactory crsFactory = factories.getCRSFactory();
if (baseCRS != null) {
return crsFactory.createDerivedCRS(properties, baseCRS, fromBase, cs);
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java
index f62d17c438,20250ab122..27dc8e2409
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java
@@@ -806,18 -787,11 +808,11 @@@ public class AbstractIdentifiedObject e
return deepEquals(getName(), that.getName(), mode) &&
deepEquals(getAlias(), that.getAlias(), mode) &&
deepEquals(getIdentifiers(), that.getIdentifiers(), mode) &&
- deepEquals(getDomains(), that.getDomains(), mode) &&
+ deepEquals(getDomains(), Legacy.getDomains(that), mode) &&
deepEquals(getRemarks(), that.getRemarks(), mode);
}
- case IGNORE_METADATA:
- case APPROXIMATE:
- case ALLOW_VARIANT:
- case DEBUG: {
- return implementsSameInterface(object);
- }
default: {
- throw new IllegalArgumentException(Errors.format(
- Errors.Keys.UnknownEnumValue_2, ComparisonMode.class, mode));
+ return implementsSameInterface(object);
}
}
}
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java
index c438930a46,21ffa968fd..3ba6f78225
--- 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
@@@ -27,6 -27,6 +27,7 @@@ import java.util.logging.LogRecord
import java.time.temporal.Temporal;
import org.opengis.util.FactoryException;
import org.opengis.geometry.Envelope;
++import org.opengis.geometry.Geometry;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.cs.CartesianCS;
@@@ -51,6 -51,6 +52,8 @@@ import org.opengis.referencing.operatio
import org.opengis.referencing.operation.TransformException;
import org.opengis.metadata.citation.Citation;
import org.opengis.metadata.extent.Extent;
++import org.opengis.metadata.extent.BoundingPolygon;
++import org.opengis.metadata.extent.GeographicExtent;
import org.opengis.metadata.extent.GeographicBoundingBox;
import org.apache.sis.measure.Units;
import org.apache.sis.geometry.Envelopes;
@@@ -96,11 -97,14 +100,13 @@@ 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.coordinate.DefaultCoordinateMetadata;
++import org.apache.sis.referencing.DefaultObjectDomain;
++import org.apache.sis.referencing.crs.AbstractCRS;
+import org.apache.sis.referencing.datum.DefaultGeodeticDatum;
+import org.apache.sis.referencing.datum.DefaultVerticalDatum;
+import static org.apache.sis.pending.geoapi.referencing.MissingMethods.getDatumEnsemble;
/**
@@@ -877,7 -919,39 +911,40 @@@ public final class CRS extends Static
public static Envelope getDomainOfValidity(final CoordinateReferenceSystem crs) {
Envelope envelope = null;
GeneralEnvelope merged = null;
- if (crs != null) {
- for (final ObjectDomain domain : crs.getDomains()) {
++ if (crs instanceof AbstractCRS) {
++ for (final DefaultObjectDomain domain : ((AbstractCRS) crs).getDomains()) {
+ final Extent domainOfValidity = domain.getDomainOfValidity();
+ if (domainOfValidity != null) {
+ for (final GeographicExtent extent : domainOfValidity.getGeographicElements()) {
+ if (extent instanceof BoundingPolygon && !Boolean.FALSE.equals(extent.getInclusion())) {
+ for (final Geometry geometry : ((BoundingPolygon) extent).getPolygons()) {
- final Envelope candidate = geometry.getEnvelope();
++ if (!(geometry instanceof org.apache.sis.pending.geoapi.geometry.Geometry)) continue;
++ final Envelope candidate = ((org.apache.sis.pending.geoapi.geometry.Geometry) geometry).getEnvelope();
+ if (candidate != null) {
+ final CoordinateReferenceSystem sourceCRS = candidate.getCoordinateReferenceSystem();
+ if (sourceCRS == null || equivalent(sourceCRS, crs)) {
+ if (envelope == null) {
+ envelope = candidate;
+ } else {
+ if (merged == null) {
+ envelope = merged = new GeneralEnvelope(envelope);
+ }
+ merged.add(envelope);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ /*
+ * If no envelope was found, uses the geographic bounding box as a fallback. We will
+ * need to transform it from WGS84 to the supplied CRS. This step was not required in
+ * the previous block because the latter selected only envelopes in the right CRS.
+ */
- if (envelope == null) {
+ /* if (envelope == null) */ { // Condition needed on other branches but not on trunk.
final GeographicBoundingBox bounds = getGeographicBoundingBox(crs);
if (bounds != null && !Boolean.FALSE.equals(bounds.getInclusion())) {
/*
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EPSGFactoryFallback.java
index dab6091587,5d6f57441a..2a7f97c27c
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EPSGFactoryFallback.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EPSGFactoryFallback.java
@@@ -133,9 -130,10 +130,9 @@@ final class EPSGFactoryFallback extend
final boolean ellipsoid = type.isAssignableFrom(Ellipsoid .class);
final boolean datum = type.isAssignableFrom(GeodeticDatum.class);
final boolean geographic = type.isAssignableFrom(GeographicCRS.class);
- @SuppressWarnings("deprecation")
final boolean geocentric = type.isAssignableFrom(GeocentricCRS.class);
final boolean projected = type.isAssignableFrom(ProjectedCRS .class);
- final Set<String> codes = new LinkedHashSet<>();
+ final var codes = new LinkedHashSet<String>();
if (pm) codes.add(StandardDefinitions.GREENWICH);
for (final CommonCRS crs : CommonCRS.values()) {
if (ellipsoid) add(codes, crs.ellipsoid);
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/IdentifiedObjects.java
index 2decb802a2,67e0e6a6ae..32b4d34257
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/IdentifiedObjects.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/IdentifiedObjects.java
@@@ -272,7 -271,7 +272,7 @@@ public final class IdentifiedObjects ex
if (authority instanceof IdentifierSpace<?>) {
cs = ((IdentifierSpace<?>) authority).getName();
}
- for (final ReferenceIdentifier identifier : nonNull(object.getIdentifiers())) {
- for (final Identifier identifier : CollectionsExt.nonNull(object.getIdentifiers())) {
++ for (final ReferenceIdentifier identifier : CollectionsExt.nonNull(object.getIdentifiers())) {
if (identifier != null) { // Paranoiac check.
if (cs != null && cs.equalsIgnoreCase(identifier.getCodeSpace())) {
return identifier; // Match based on codespace.
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java
index 3afcfb5a39,457b834f88..028ec16420
--- 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
@@@ -299,25 -294,22 +297,48 @@@ public class AbstractCRS extends Abstra
* User could provide his own CRS implementation outside this SIS package, so we have
* to check for SingleCRS interface. But all SIS classes override this implementation.
*/
- return (this instanceof SingleCRS) ? ((SingleCRS) this).getDatum() : null;
+ if (this instanceof SingleCRS) {
+ final var crs = (SingleCRS) this;
+ final Datum datum = crs.getDatum();
+ if (datum != null) {
+ return datum;
+ }
+ if (legacy) {
- final var ensemble = crs.getDatumEnsemble();
++ final var ensemble = getDatumEnsemble(crs);
+ if (ensemble instanceof Datum) {
+ return (Datum) ensemble;
+ }
+ }
+ }
+ return null;
+ }
+
++ /**
++ * Returns the datum ensemble of the given <abbr>CRS</abbr>.
++ *
++ * @param crs the <abbr>CRS</abbr> from which to get the datum ensemble.
++ * @return the datum ensemble, or {@code null} if none.
++ */
++ static DefaultDatumEnsemble<?> getDatumEnsemble(final CoordinateReferenceSystem crs) {
++ return (crs instanceof AbstractCRS) ? ((AbstractCRS) crs).getDatumEnsemble() : 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;
++ MissingMethods.datumEnsemble = AbstractCRS::getDatumEnsemble;
+ }
+
/**
* Returns the coordinate system.
*
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractSingleCRS.java
index 16172dd7aa,70690e8e25..d1276f17e8
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractSingleCRS.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractSingleCRS.java
@@@ -31,13 -31,12 +31,12 @@@ import org.apache.sis.util.resources.Er
import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.referencing.cs.AbstractCS;
import org.apache.sis.referencing.internal.Resources;
- import org.apache.sis.metadata.privy.Identifiers;
+ import org.apache.sis.referencing.datum.DatumOrEnsemble;
import org.apache.sis.metadata.privy.ImplementationHelper;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.referencing.datum.DatumEnsemble;
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
+import org.opengis.referencing.ReferenceIdentifier;
+import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
- import org.apache.sis.pending.geoapi.referencing.MissingMethods;
/**
@@@ -170,11 -169,11 +169,11 @@@ class AbstractSingleCRS<D extends Datum
AbstractSingleCRS(final SingleCRS crs) {
super(crs);
datum = (D) crs.getDatum();
- if (datum instanceof DatumEnsemble<?>) {
+ if (datum instanceof DefaultDatumEnsemble<?>) {
throw new IllegalArgumentException(
- Errors.format(Errors.Keys.IllegalPropertyValueClass_2, "datum", DatumEnsemble.class));
+ Errors.format(Errors.Keys.IllegalPropertyValueClass_2, "datum", DefaultDatumEnsemble.class));
}
- ensemble = (DefaultDatumEnsemble<D>) MissingMethods.getDatumEnsemble(crs);
- ensemble = (DatumEnsemble<D>) crs.getDatumEnsemble();
++ ensemble = (DefaultDatumEnsemble<D>) getDatumEnsemble(crs);
checkDatum(null);
}
@@@ -238,25 -221,24 +221,24 @@@
@Override
public boolean equals(final Object object, ComparisonMode mode) {
if (super.equals(object, mode)) {
- switch (mode) {
- case STRICT: {
- final var that = (AbstractSingleCRS<?>) object;
- return Objects.equals(datum, that.datum) && Objects.equals(ensemble, that.ensemble);
- }
- default: {
- final var that = (SingleCRS) object;
- final var d1 = this.getDatum();
- final var d2 = that.getDatum();
- if (mode == ComparisonMode.DEBUG) {
- mode = ComparisonMode.ALLOW_VARIANT; // For avoiding too early `AssertionError`.
- }
- if (Utilities.deepEquals(d1, d2, mode)) {
- return mode.allowsVariant() || Utilities.deepEquals(getDatumEnsemble(), MissingMethods.getDatumEnsemble(that), mode);
- } else if (mode.allowsVariant()) {
- return isHeuristicMatchForName(this.getDatumEnsemble(), d2, mode) ||
- isHeuristicMatchForName(MissingMethods.getDatumEnsemble(that), d1, mode);
- }
+ if (mode == ComparisonMode.STRICT) {
+ final var that = (AbstractSingleCRS<?>) object;
+ return Objects.equals(datum, that.datum) && Objects.equals(ensemble, that.ensemble);
+ }
+ final var that = (SingleCRS) object;
+ final var d1 = this.getDatum();
+ final var d2 = that.getDatum();
+ if (mode == ComparisonMode.DEBUG) {
+ mode = ComparisonMode.ALLOW_VARIANT; // For avoiding too early `AssertionError`.
+ }
+ if (Utilities.deepEquals(d1, d2, mode)) {
+ if (d1 != null && d2 != null && mode.isCompatibility()) {
+ return true;
}
- return Utilities.deepEquals(getDatumEnsemble(), that.getDatumEnsemble(), mode);
++ return Utilities.deepEquals(getDatumEnsemble(), getDatumEnsemble(that), mode);
+ } else if (mode.isCompatibility()) {
+ return DatumOrEnsemble.isLegacyDatum(this.getDatumEnsemble(), d2, mode) ||
- DatumOrEnsemble.isLegacyDatum(that.getDatumEnsemble(), d1, mode);
++ DatumOrEnsemble.isLegacyDatum(getDatumEnsemble(that), d1, mode);
}
}
return false;
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
index 657427192c,714f98611d..40f51712e6
--- 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
@@@ -59,12 -60,12 +60,11 @@@ import org.apache.sis.io.wkt.Formatter
import org.apache.sis.util.ComparisonMode;
import org.apache.sis.util.collection.Containers;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.referencing.datum.DatumEnsemble;
-import org.opengis.referencing.datum.ParametricDatum;
-import org.opengis.referencing.crs.ParametricCRS;
-import org.opengis.referencing.cs.ParametricCS;
-import org.opengis.coordinate.MismatchedDimensionException;
+// Specific to the main branch:
+import org.opengis.geometry.MismatchedDimensionException;
+import org.apache.sis.referencing.cs.DefaultParametricCS;
+import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
+import org.apache.sis.referencing.datum.DefaultParametricDatum;
- import org.apache.sis.pending.geoapi.referencing.MissingMethods;
/**
@@@ -429,8 -430,20 +429,20 @@@ public class DefaultDerivedCRS extends
* @since 1.5
*/
@Override
- public DatumEnsemble<?> getDatumEnsemble() {
- return getBaseCRS().getDatumEnsemble();
+ public DefaultDatumEnsemble<?> getDatumEnsemble() {
- return MissingMethods.getDatumEnsemble(getBaseCRS());
++ return getDatumEnsemble(getBaseCRS());
+ }
+
+ /**
+ * Returns the datum or a view of the ensemble as a datum.
+ */
+ @Override
+ Datum getDatumOrEnsemble(final boolean legacy) {
+ final SingleCRS baseCRS = getBaseCRS();
+ if (baseCRS instanceof AbstractCRS) {
+ return ((AbstractCRS) baseCRS).getDatumOrEnsemble(legacy);
+ }
+ return super.getDatumOrEnsemble(legacy);
}
/**
@@@ -681,6 -692,16 +691,11 @@@
return (GeodeticDatum) super.getDatum();
}
- /** Returns the datum ensemble of the base geodetic CRS. */
- @Override public DatumEnsemble<GeodeticDatum> getDatumEnsemble() {
- return ((GeodeticCRS) getBaseCRS()).getDatumEnsemble();
- }
-
+ /** Returns the datum or a view of the ensemble as a datum. */
+ @Override GeodeticDatum getDatumOrEnsemble(final boolean legacy) {
+ return legacy ? DatumOrEnsemble.asDatum((GeodeticCRS) getBaseCRS()) : getDatum();
+ }
+
/** Returns a coordinate reference system of the same type as this CRS but with different axes. */
@Override AbstractCRS createSameType(final AbstractCS derivedCS) {
return new Geodetic(this, derivedCS);
@@@ -732,6 -753,16 +747,11 @@@
return (VerticalDatum) super.getDatum();
}
- /** Returns the datum ensemble of the base vertical CRS. */
- @Override public DatumEnsemble<VerticalDatum> getDatumEnsemble() {
- return ((VerticalCRS) getBaseCRS()).getDatumEnsemble();
- }
-
+ /** Returns the datum or a view of the ensemble as a datum. */
+ @Override VerticalDatum getDatumOrEnsemble(final boolean legacy) {
+ return legacy ? DatumOrEnsemble.asDatum((VerticalCRS) getBaseCRS()) : getDatum();
+ }
+
/** Returns the coordinate system given at construction time. */
@Override public VerticalCS getCoordinateSystem() {
return (VerticalCS) super.getCoordinateSystem();
@@@ -788,6 -819,16 +808,11 @@@
return (TemporalDatum) super.getDatum();
}
- /** Returns the datum ensemble of the base temporal CRS. */
- @Override public DatumEnsemble<TemporalDatum> getDatumEnsemble() {
- return ((TemporalCRS) getBaseCRS()).getDatumEnsemble();
- }
-
+ /** Returns the datum or a view of the ensemble as a datum. */
+ @Override TemporalDatum getDatumOrEnsemble(final boolean legacy) {
+ return legacy ? DatumOrEnsemble.asDatum((TemporalCRS) getBaseCRS()) : getDatum();
+ }
+
/** Returns the coordinate system given at construction time. */
@Override public TimeCS getCoordinateSystem() {
return (TimeCS) super.getCoordinateSystem();
@@@ -840,13 -881,23 +865,18 @@@
}
/** Returns the datum of the base parametric CRS. */
- @Override public ParametricDatum getDatum() {
- return (ParametricDatum) super.getDatum();
- }
-
- /** Returns the datum ensemble of the base parametric CRS. */
- @Override public DatumEnsemble<ParametricDatum> getDatumEnsemble() {
- return ((ParametricCRS) getBaseCRS()).getDatumEnsemble();
+ @Override public DefaultParametricDatum getDatum() {
+ return (DefaultParametricDatum) super.getDatum();
}
+ /** Returns the datum or a view of the ensemble as a datum. */
- @Override ParametricDatum getDatumOrEnsemble(final boolean legacy) {
- return legacy ? DatumOrEnsemble.asDatum((ParametricCRS) getBaseCRS()) : getDatum();
++ @Override DefaultParametricDatum getDatumOrEnsemble(final boolean legacy) {
++ return getDatum();
+ }
+
/** Returns the coordinate system given at construction time. */
- @Override public ParametricCS getCoordinateSystem() {
- return (ParametricCS) super.getCoordinateSystem();
+ @Override public DefaultParametricCS getCoordinateSystem() {
+ return (DefaultParametricCS) super.getCoordinateSystem();
}
/** Returns a coordinate reference system of the same type as this CRS but with different axes. */
@@@ -903,6 -954,16 +933,11 @@@
return (EngineeringDatum) super.getDatum();
}
- /** Returns the datum ensemble of the base engineering CRS. */
- @Override public DatumEnsemble<EngineeringDatum> getDatumEnsemble() {
- return ((EngineeringCRS) getBaseCRS()).getDatumEnsemble();
- }
-
+ /** Returns the datum or a view of the ensemble as a datum. */
+ @Override EngineeringDatum getDatumOrEnsemble(final boolean legacy) {
+ return legacy ? DatumOrEnsemble.asDatum((EngineeringCRS) getBaseCRS()) : getDatum();
+ }
+
/** Returns a coordinate reference system of the same type as this CRS but with different axes. */
@Override AbstractCRS createSameType(final AbstractCS derivedCS) {
return new Engineering(this, derivedCS);
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java
index 06f36fbe55,04ef599736..cf2b16bdf0
--- 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
@@@ -160,14 -160,15 +161,23 @@@ class DefaultGeodeticCRS extends Abstra
return super.getDatum();
}
+ /**
+ * Initializes the handler for getting datum ensemble of an arbitrary CRS.
+ */
+ static {
+ MissingMethods.geodeticDatumEnsemble = (crs) ->
+ (crs instanceof DefaultGeodeticCRS) ? ((DefaultGeodeticCRS) crs).getDatumEnsemble() : null;
+ }
+
+ /**
+ * Returns the datum or a view of the ensemble as a datum.
+ * The {@code legacy} argument tells whether this method is invoked for formatting in a legacy <abbr>WKT</abbr> format.
+ */
+ @Override
+ final GeodeticDatum getDatumOrEnsemble(final boolean legacy) {
+ return legacy ? DatumOrEnsemble.asDatum(this) : getDatum();
+ }
+
/**
* 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/DefaultParametricCRS.java
index f637341b31,5fedc4597a..efb669c751
--- 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
@@@ -23,12 -23,14 +23,13 @@@ import jakarta.xml.bind.annotation.XmlT
import org.apache.sis.referencing.privy.WKTKeywords;
import org.apache.sis.referencing.cs.AxesConvention;
import org.apache.sis.referencing.cs.AbstractCS;
+ import org.apache.sis.referencing.datum.DatumOrEnsemble;
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;
/**
@@@ -193,6 -219,15 +194,15 @@@ public class DefaultParametricCRS exten
return super.getDatumEnsemble();
}
+ /**
+ * Returns the datum or a view of the ensemble as a datum.
+ * The {@code legacy} argument tells whether this method is invoked for formatting in a legacy <abbr>WKT</abbr> format.
+ */
+ @Override
- final ParametricDatum getDatumOrEnsemble(final boolean legacy) {
- return legacy ? DatumOrEnsemble.asDatum(this) : getDatum();
++ final DefaultParametricDatum getDatumOrEnsemble(final boolean legacy) {
++ return getDatum();
+ }
+
/**
* Returns the coordinate system.
*
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java
index 346f14ec00,10036913d9..5aa77cc06b
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java
@@@ -44,10 -45,9 +45,9 @@@ import org.apache.sis.util.Workaround
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.operation.Projection;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.referencing.datum.DatumEnsemble;
-import org.opengis.coordinate.MismatchedDimensionException;
+// Specific to the main branch:
+import org.opengis.geometry.MismatchedDimensionException;
+import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
- import org.apache.sis.pending.geoapi.referencing.MissingMethods;
/**
@@@ -247,8 -247,17 +247,17 @@@ public class DefaultProjectedCRS extend
* @since 1.5
*/
@Override
- public DatumEnsemble<GeodeticDatum> getDatumEnsemble() {
- return getBaseCRS().getDatumEnsemble();
+ public DefaultDatumEnsemble<GeodeticDatum> getDatumEnsemble() {
- return MissingMethods.getDatumEnsemble(getBaseCRS());
++ return (DefaultDatumEnsemble<GeodeticDatum>) getDatumEnsemble(getBaseCRS());
+ }
+
+ /**
+ * Returns the datum or a view of the ensemble as a datum.
+ * The {@code legacy} argument tells whether this method is invoked for formatting in a legacy <abbr>WKT</abbr> format.
+ */
+ @Override
+ final GeodeticDatum getDatumOrEnsemble(final boolean legacy) {
+ return legacy ? DatumOrEnsemble.asDatum(getBaseCRS()) : getDatum();
}
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java
index 4e6d19103c,07991ac8b4..b8bb4f3b62
--- 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
@@@ -507,15 -434,15 +507,15 @@@ public class AbstractDatum extends Abst
* and the {@linkplain #getDomains() domains}.
*
* <h4>Static versus dynamic datum</h4>
- * If this datum implements the {@link DynamicReferenceFrame} interface, then the given object needs
+ * If this datum implements the {@code DynamicReferenceFrame} interface, then the given object needs
- * to also implement that interfaces and provide the same reference epoch for being considered equal.
+ * to also implement that interface and provide the same reference epoch for being considered equal.
- * Conversely, if this datum does not implement {@link DynamicReferenceFrame}, then the given object
+ * Conversely, if this datum does not implement {@code DynamicReferenceFrame}, then the given object
* also need to <em>not</em> implement that interface for being considered equal.
+ * This condition is relaxed with {@link ComparisonMode#COMPATIBILITY} if the two reference frames have a common identifier
+ * or an {@linkplain org.apache.sis.referencing.IdentifiedObjects#isHeuristicMatchForName equivalent name}.
*
* @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.
+ * @param mode the strictness level of the comparison.
* @return {@code true} if both objects are equal.
*/
@Override
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DatumOrEnsemble.java
index 9823622305,d08ce7f653..ce78a4c244
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DatumOrEnsemble.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DatumOrEnsemble.java
@@@ -60,12 -67,34 +66,35 @@@ import org.apache.sis.util.logging.Logg
* @version 1.5
* @since 1.5
*/
+@SuppressWarnings("unchecked") // See `getDatumEnsemble(…)`
public final class DatumOrEnsemble extends Static {
/**
- * Do not allow instantiation of this class.
+ * The {@value} keyword which sometime appear at the end of a datum ensemble name.
*/
- private DatumOrEnsemble() {
+ private static final String ENSEMBLE = "ensemble";
+
+ /**
+ * The datum of a <abbr>CRS</abbr> or specified by the user, or {@code null} if none.
+ */
+ private final Datum datum;
+
+ /**
+ * The ensemble of a <abbr>CRS</abbr> or specified by the user, or {@code null} if none.
+ */
- private final DatumEnsemble<?> ensemble;
++ private final DefaultDatumEnsemble<?> ensemble;
+
+ /**
+ * Criterion for deciding if two properties should be considered equal.
+ */
+ private final ComparisonMode mode;
+
+ /**
+ * For internal usage only. The fact that we may create instances of this class is a hidden implementation details.
+ */
- private DatumOrEnsemble(final Datum datum, final DatumEnsemble<?> ensemble, final ComparisonMode mode) {
++ private DatumOrEnsemble(final Datum datum, final DefaultDatumEnsemble<?> ensemble, final ComparisonMode mode) {
+ this.datum = datum;
+ this.ensemble = ensemble;
+ this.mode = mode;
}
/**
@@@ -279,18 -318,18 +308,18 @@@
*/
@SuppressWarnings("unchecked") // Casts are safe because callers know the method signature of <D>.
private static <C extends SingleCRS, D extends Datum, R extends IdentifiedObject> Optional<R> asTargetDatum(
- final C source, final R sourceDatum,
- final C target, final R targetDatum,
+ final C sourceCRS, final R sourceDatum,
+ final C targetCRS, final R targetDatum,
- final Function<DatumEnsemble<D>, R> constructor)
+ final Function<DefaultDatumEnsemble<D>, R> constructor)
{
if (sourceDatum != null && Utilities.equalsIgnoreMetadata(sourceDatum, targetDatum)) {
return Optional.of(targetDatum);
}
- DatumEnsemble<D> sourceEnsemble;
- DatumEnsemble<D> targetEnsemble;
- DatumEnsemble<D> selected;
- if ((isMember(selected = targetEnsemble = (DatumEnsemble<D>) targetCRS.getDatumEnsemble(), sourceDatum)) ||
- (isMember(selected = sourceEnsemble = (DatumEnsemble<D>) sourceCRS.getDatumEnsemble(), targetDatum)))
+ DefaultDatumEnsemble<D> sourceEnsemble;
+ DefaultDatumEnsemble<D> targetEnsemble;
+ DefaultDatumEnsemble<D> selected;
- if ((isMember(selected = targetEnsemble = (DefaultDatumEnsemble<D>) getDatumEnsemble(target), sourceDatum)) ||
- (isMember(selected = sourceEnsemble = (DefaultDatumEnsemble<D>) getDatumEnsemble(source), targetDatum)))
++ if ((isMember(selected = targetEnsemble = (DefaultDatumEnsemble<D>) getDatumEnsemble(targetCRS), sourceDatum)) ||
++ (isMember(selected = sourceEnsemble = (DefaultDatumEnsemble<D>) getDatumEnsemble(sourceCRS), targetDatum)))
{
return Optional.of(constructor.apply(selected));
}
@@@ -343,15 -382,84 +372,84 @@@
return false;
}
+ /**
+ * Returns whether a legacy definition of a datum may be considered as equivalent to the given datum ensemble.
+ * This is {@code true} if all reference frames (both the specified datum and the ensemble members) have the
+ * same properties (ellipsoid and prime meridians in the geodetic case), and the datum and datum ensemble either
+ * have a common identifier or an {@linkplain IdentifiedObjects#isHeuristicMatchForName(IdentifiedObject, String)
+ * heuristic match of name}.
+ *
+ * <p>This method does not verify if the given datum is a member of the given ensemble.
+ * If the datum was a member, then the two objects would <em>not</em> be conceptually equal.
+ * We would rather have one object clearly identified as more accurate than the other.</p>
+ *
+ * <h4>Use case</h4>
+ * This method is for interoperability between the old and new definitions of <abbr>WGS</abbr> 1984 (<abbr>EPSG</abbr>:4326).
+ * Before <abbr>ISO</abbr> 19111:2019, the <i>datum ensemble</i> concept did not existed in the <abbr>OGC</abbr>/<abbr>ISO</abbr> standards
+ * and <abbr>WGS</abbr> 1984 was defined as a {@link Datum}.
- * In recent standards, <abbr>WGS</abbr> 1984 is defined as a {@link DatumEnsemble}, but the old definition is still encountered.
++ * In recent standards, <abbr>WGS</abbr> 1984 is defined as a {@code DatumEnsemble}, but the old definition is still encountered.
+ * For example, a <abbr>CRS</abbr> may have been parsed from a <abbr title="Geographic Markup Language">GML</abbr> document,
+ * or from a <abbr title="Well-Known Text">WKT</abbr> 1 string, or from a <abbr>ISO</abbr> 19162:2015 string, <i>etc.</i>
+ * This method can be used for detecting such situations.
+ * While <abbr>WGS</abbr> 1984 is the main use case, this method can be used for any datum in the same situation.
+ *
+ * @param ensemble the datum ensemble, or {@code null}.
+ * @param datum the datum, or {@code null}.
+ * @param mode the criterion for comparing ellipsoids and prime meridians.
+ * @return whether the two objects could be considered as equal if the concept of datum ensemble did not existed.
+ */
- public static boolean isLegacyDatum(final DatumEnsemble<?> ensemble, final Datum datum, final ComparisonMode mode) {
++ public static boolean isLegacyDatum(final DefaultDatumEnsemble<?> ensemble, final Datum datum, final ComparisonMode mode) {
+ if (ensemble == null || datum == null) {
+ return false;
+ }
+ // Two null values are not considered equal because they are not of the same type.
+ if (ensemble == datum) {
+ return true;
+ }
+ final var c = new DatumOrEnsemble(datum, ensemble, mode);
+ if (!(c.isPropertyEqual(GeodeticDatum.class, GeodeticDatum::getEllipsoid, Objects::nonNull) &&
+ c.isPropertyEqual(GeodeticDatum.class, GeodeticDatum::getPrimeMeridian, Objects::nonNull) &&
- c.isPropertyEqual(VerticalDatum.class, VerticalDatum::getRealizationMethod, Optional::isPresent)))
++ c.isPropertyEqual(VerticalDatum.class, VerticalDatum::getVerticalDatumType, Objects::nonNull)))
+ {
+ return false;
+ }
+ final Boolean match = Identifiers.hasCommonIdentifier(ensemble.getIdentifiers(), datum.getIdentifiers());
+ if (match != null) {
+ return match;
+ }
+ /*
+ * We could not answer the question using identifiers. Try using the names.
+ * The primary name is likely to not match, because ensemble names in EPSG
+ * dataset often ends with "ensemble" while datum names often do not. But
+ * we are more interrested in the ensemble's aliases in the next line.
+ */
+ if (IdentifiedObjects.isHeuristicMatchForName(ensemble, datum.getName().getCode())) {
+ return true;
+ }
+ /*
+ * Try to remove the "ensemble" prefix in the datum ensemble name and try again.
+ * This time, the comparison will also check `datum` aliases instead of `ensemble`.
+ */
+ String name = ensemble.getName().getCode();
+ if (name.endsWith(ENSEMBLE)) {
+ int i = name.length() - ENSEMBLE.length();
+ if (i > (i = CharSequences.skipTrailingWhitespaces(name, 0, i))) {
+ name = name.substring(0, i); // Remove the "ensemble" suffix.
+ }
+ }
+ return IdentifiedObjects.isHeuristicMatchForName(datum, name);
+ }
+
/**
* Returns the ellipsoid used by the given coordinate reference system.
- * More specifically:
+ * This method searches in the following locations:
*
* <ul>
* <li>If the given <abbr>CRS</abbr> is an instance of {@link SingleCRS} and its datum
* is a {@link GeodeticDatum}, then this method returns the datum ellipsoid.</li>
- * <li>Otherwise, if the given <abbr>CRS</abbr> is associated to a {@code DatumEnsemble} and all members
- * of the ensemble have equal (ignoring metadata) ellipsoid, then returns that ellipsoid.</li>
+ * <li>Otherwise, if the given <abbr>CRS</abbr> is an instance of {@link SingleCRS}, is associated to a
- * {@link DatumEnsemble}, and all members of the ensemble have equal (ignoring metadata) ellipsoid,
++ * {@code DatumEnsemble}, and all members of the ensemble have equal (ignoring metadata) ellipsoid,
+ * then returns that ellipsoid.</li>
* <li>Otherwise, if the given <abbr>CRS</abbr> is an instance of {@link CompoundCRS}, then this method
* searches recursively in each component until a geodetic reference frame is found.</li>
* <li>Otherwise, this method returns an empty value.</li>
@@@ -378,39 -484,98 +474,71 @@@
* @see org.apache.sis.referencing.CRS#getGreenwichLongitude(GeodeticCRS)
*/
public static Optional<PrimeMeridian> getPrimeMeridian(final CoordinateReferenceSystem crs) {
- return getGeodeticProperty(crs, GeodeticDatum::getPrimeMeridian);
+ return Optional.ofNullable(getProperty(crs, GeodeticDatum.class, GeodeticDatum::getPrimeMeridian, Objects::nonNull));
}
- /**
- * Returns the realization method used by the given coordinate reference system.
- * This method searches in the following locations:
- *
- * <ul>
- * <li>If the given <abbr>CRS</abbr> is an instance of {@link SingleCRS} and its datum
- * is a {@link VerticalDatum}, then this method returns the realization method.</li>
- * <li>Otherwise, if the given <abbr>CRS</abbr> is an instance of {@link SingleCRS}, is associated to a
- * {@link DatumEnsemble}, and all members of the ensemble have equal (ignoring metadata) realization
- * methods, then returns that method.</li>
- * <li>Otherwise, if the given <abbr>CRS</abbr> is an instance of {@link CompoundCRS}, then this method
- * searches recursively in each component until a vertical reference frame is found.</li>
- * <li>Otherwise, this method returns an empty value.</li>
- * </ul>
- *
- * This method may return an empty value if a datum ensemble contains different realization methods.
- *
- * @param crs the coordinate reference system for which to get the realization method.
- * @return the realization method, or an empty value if none or not equal for all members.
- *
- * @since 2.0 (temporary version number until this branch is released)
- */
- public static Optional<RealizationMethod> getRealizationMethod(final CoordinateReferenceSystem crs) {
- Optional<RealizationMethod> common = getProperty(crs, VerticalDatum.class, VerticalDatum::getRealizationMethod, Optional::isPresent);
- return (common != null) ? common : Optional.empty();
- }
-
/**
* Implementation of {@code getEllipsoid(CRS)} and {@code getPrimeMeridian(CRS)}.
*
- * @param <P> the type of object to get.
- * @param crs the coordinate reference system for which to get the ellipsoid or prime meridian.
- * @param getter the method to invoke on {@link GeodeticDatum} instances.
- * @return the ellipsoid or prime meridian, or an empty value if none of inconsistent.
+ * @param <P> the type of property to get.
+ * @param <D> the type of datum expected by the given {@code getter}.
+ * @param crs the coordinate reference system for which to get the ellipsoid or prime meridian.
+ * @param getter the method to invoke on {@link Datum} instances for getting the property.
+ * @param nonNull test about whether a property value is non-null or present.
+ * @return the property value, or {@code null} if none or not equal for all members.
*/
- private static <P> Optional<P> getGeodeticProperty(final CoordinateReferenceSystem crs, final Function<GeodeticDatum, P> getter) {
- single: if (crs instanceof SingleCRS) {
+ private static <P, D extends Datum> P getProperty(final CoordinateReferenceSystem crs, final Class<D> datumType,
+ final Function<D, P> getter, final Predicate<P> nonNull)
+ {
+ if (crs instanceof SingleCRS) {
final var scrs = (SingleCRS) crs;
final Datum datum = scrs.getDatum();
- if (datum instanceof GeodeticDatum) {
- P property = getter.apply((GeodeticDatum) datum);
+ if (datumType.isInstance(datum)) {
+ @SuppressWarnings("unchecked")
+ P property = getter.apply((D) datum);
+ if (nonNull.test(property)) {
+ return property;
+ }
+ }
- final var c = new DatumOrEnsemble(datum, scrs.getDatumEnsemble(), ComparisonMode.IGNORE_METADATA);
++ final var c = new DatumOrEnsemble(datum, getDatumEnsemble(scrs), ComparisonMode.IGNORE_METADATA);
+ return c.getEnsembleProperty(null, datumType, getter, nonNull);
+ } else if (crs instanceof CompoundCRS) {
+ for (final CoordinateReferenceSystem c : ((CompoundCRS) crs).getComponents()) {
+ final P property = getProperty(c, datumType, getter, nonNull);
if (property != null) {
- return Optional.of(property);
+ return property;
}
}
- final DefaultDatumEnsemble<?> ensemble = getDatumEnsemble(crs);
- if (ensemble != null) {
- P common = null;
- for (Datum member : ensemble.getMembers()) {
- if (member instanceof GeodeticDatum) {
- final P property = getter.apply((GeodeticDatum) member);
- if (property != null) {
- if (common == null) {
- common = property;
- } else if (!Utilities.equalsIgnoreMetadata(property, common)) {
- break single;
- }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a property of ensemble member if it is the same for all members.
+ * Returns {@code null} if the value is absent or not equal for all members.
+ * If {@code common} is non-null, then this method take in account only the
+ * property values equal to {@code common}
+ * (i.e., it searches if the value is present).
+ *
+ * @param <P> the type of property to get.
+ * @param <D> the type of datum expected by the given {@code getter}.
+ * @param common if non-null, ignore all properties not equal to {@code common}.
+ * @param getter the method to invoke on {@link Datum} instances for getting the property.
+ * @param nonNull test about whether a property value is non-null or present.
+ * @return the property value, or {@code null} if none or not equal for all members.
+ */
+ private <P, D extends Datum> P getEnsembleProperty(P common, final Class<D> datumType, final Function<D,P> getter, final Predicate<P> nonNull) {
+ final boolean searching = (common != null);
+ if (ensemble != null) {
+ for (Datum member : ensemble.getMembers()) {
+ if (datumType.isInstance(member)) {
+ @SuppressWarnings("unchecked")
+ final P property = getter.apply((D) member);
+ if (nonNull.test(property)) {
+ if (common == null) {
+ common = property;
+ } else if (Utilities.deepEquals(property, common, mode) == searching) {
+ return searching ? common : null;
}
}
}
@@@ -434,11 -612,18 +575,18 @@@
* @param object the object from which to get the ensemble accuracy, or {@code null}.
* @return the datum ensemble accuracy if the given object is a datum ensemble.
* @throws NullPointerException if the given object should provide an accuracy but didn't.
+ *
+ * @see org.apache.sis.referencing.CRS#getLinearAccuracy(CoordinateOperation)
*/
public static Optional<PositionalAccuracy> getAccuracy(final IdentifiedObject object) {
- final DatumEnsemble<?> ensemble;
- if (object instanceof DatumEnsemble<?>) {
- ensemble = (DatumEnsemble<?>) object;
+ final DefaultDatumEnsemble<?> ensemble;
+ if (object instanceof DefaultDatumEnsemble<?>) {
+ ensemble = (DefaultDatumEnsemble<?>) object;
+ } else if (object instanceof SingleCRS) {
- ensemble = ((SingleCRS) object).getDatumEnsemble();
++ ensemble = getDatumEnsemble((SingleCRS) object);
+ if (ensemble == null) {
+ return Optional.empty();
+ }
} else {
return Optional.empty();
}
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
index 2cb7303a74,82a0ae2c47..73bd9c4d48
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
@@@ -93,12 -99,18 +96,11 @@@ public class DefaultVerticalDatum exten
private static final long serialVersionUID = 380347456670516572L;
/**
- * The realization method (geoid, tidal, <i>etc.</i>), or {@code null} if unspecified.
- *
- * @see #getRealizationMethod()
- */
- private RealizationMethod method;
-
- /**
-- * The type of this vertical datum.
- * If {@code null}, a value will be inferred from the name by {@link #type()}.
++ * The type of this vertical datum, or {@code null} if unspecified.
++ * In the latter case, a default will be inferred when requested.
*
- * @see #type()
* @see #getVerticalDatumType()
*/
- @SuppressWarnings("deprecation")
private VerticalDatumType type;
/**
@@@ -200,28 -231,18 +202,6 @@@
return VerticalDatum.class;
}
-- /**
- * Returns the type of this datum, or infers the type from the datum name if no type were specified.
- * The latter case occurs after unmarshalling, since GML 3.2 does not contain any attribute for the datum type.
- * It may also happen if the datum were created using reflection.
- *
- * <p>This method uses heuristic rules and may be changed in any future SIS version.</p>
- * Returns the method through which this vertical reference frame is realized.
-- *
- * <p>No synchronization needed; this is not a problem if this value is computed twice.
- * This method returns only existing immutable instances.</p>
- * @return method through which this vertical reference frame is realized.
-- *
- * @see #getVerticalDatumType()
- * @see #getTypeElement()
- * @since 2.0 (temporary version number until this branch is released)
-- */
- private VerticalDatumType type() {
- VerticalDatumType t = type;
- if (t == null) {
- final ReferenceIdentifier name = super.getName();
- type = t = VerticalDatumTypes.fromDatum(name != null ? name.getCode() : null, super.getAlias(), null);
- }
- return t;
- @Override
- public Optional<RealizationMethod> getRealizationMethod() {
- return Optional.ofNullable(method);
-- }
--
/**
* Returns the type of this vertical datum.
*
@@@ -232,10 -253,33 +212,30 @@@
* but in a programmatic way more suitable to coordinate transformation engines.
*
* @return the type of this vertical datum.
- *
- * @deprecated As of ISO 19111:2019, the {@code VerticalDatumType} argument is replaced by {@code RealizationMethod}.
*/
@Override
- @Deprecated(since = "2.0") // Temporary version number until this branch is released.
public VerticalDatumType getVerticalDatumType() {
- return type();
++ if (type == null) {
++ // Do not store the value because it depends on the context (whether we know the axis).
++ return VerticalDatumTypes.fromDatum(super.getName().getCode(), super.getAlias(), null);
++ }
+ return type;
+ }
+
+ /**
+ * Returns the datum type if it was explicitly specified, or otherwise tries to guess it.
+ * This method may return {@code null} if it cannot guess the method. This is used for compatibility
+ * with legacy formats such as WKT 1 or GML 3.1, before realization method became a formal property.
+ */
+ private VerticalDatumType getOrGuessMethod(final FormattableObject parent) {
- @SuppressWarnings("LocalVariableHidesMemberVariable")
- final VerticalDatumType type = getVerticalDatumType();
+ if (type != null && type != VerticalDatumType.OTHER_SURFACE) {
+ return type;
+ }
- return VerticalDatumTypes.fromMethod(getRealizationMethod().orElseGet(() -> {
- CoordinateSystemAxis axis = null;
- if (parent instanceof CoordinateReferenceSystem) {
- axis = ((CoordinateReferenceSystem) parent).getCoordinateSystem().getAxis(0);
- }
- return VerticalDatumTypes.fromDatum(getName().getCode(), getAlias(), axis);
- }));
++ CoordinateSystemAxis axis = null;
++ if (parent instanceof CoordinateReferenceSystem) {
++ axis = ((CoordinateReferenceSystem) parent).getCoordinateSystem().getAxis(0);
++ }
++ return VerticalDatumTypes.fromDatum(getName().getCode(), getAlias(), axis);
}
/**
@@@ -345,7 -402,7 +343,7 @@@
switch (mode) {
case STRICT: {
final var other = (DefaultVerticalDatum) object;
- return type().equals(other.type());
- return Objects.equals(method, other.method) && Objects.equals(type, other.type);
++ return Objects.equals(type, other.type);
}
case BY_CONTRACT: {
final var other = (VerticalDatum) object;
@@@ -373,7 -431,7 +371,7 @@@
*/
@Override
protected long computeHashCode() {
- return super.computeHashCode() + type().hashCode();
- return super.computeHashCode() + 37 * Objects.hashCode(method);
++ return super.computeHashCode() + Objects.hashCode(type);
}
/**
@@@ -429,9 -487,13 +427,12 @@@
*
* @see <a href="http://issues.apache.org/jira/browse/SIS-160">SIS-160: Need XSLT between GML 3.1 and 3.2</a>
*/
- @SuppressWarnings("deprecation")
@XmlElement(name = "verticalDatumType")
private VerticalDatumType getTypeElement() {
- return Context.isGMLVersion(Context.current(), LegacyNamespaces.VERSION_3_2) ? null : getVerticalDatumType();
+ if (Context.isGMLVersion(Context.current(), LegacyNamespaces.VERSION_3_2)) {
+ return null;
+ }
+ return getOrGuessMethod(null);
}
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java
index 20f5dfb954,68cfaace7e..e53f7d944c
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java
@@@ -180,12 -177,9 +180,15 @@@ abstract class AuthorityFactoryProxy<T
*/
static final AuthorityFactoryProxy<InternationalString> description(final Class<? extends IdentifiedObject> classe) {
return new AuthorityFactoryProxy<InternationalString>(InternationalString.class, AuthorityFactoryIdentifier.Type.ANY) {
- @Override InternationalString createFromAPI(AuthorityFactory factory, String code) throws FactoryException {
+ @Override InternationalString create(GeodeticAuthorityFactory factory, String code) throws FactoryException {
return factory.getDescriptionText(classe, code).orElse(null);
}
+ @Override InternationalString createFromAPI(AuthorityFactory factory, String code) throws FactoryException {
++ if (factory instanceof GeodeticAuthorityFactory) {
++ return ((GeodeticAuthorityFactory) factory).getDescriptionText(classe, code).orElse(null);
++ }
+ return factory.getDescriptionText(code);
+ }
@Override AuthorityFactoryProxy<InternationalString> specialize(String typeName) {
return this;
}
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
index dec2641dbf,c684889ab8..fb9e9c5164
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
@@@ -178,24 -173,6 +178,24 @@@ public abstract class GeodeticAuthority
return Optional.ofNullable(IdentifiedObjects.getDisplayName(createObject(type, code)));
}
+ /**
+ * Returns a description of the object corresponding to a code.
+ *
+ * @param code value allocated by authority.
+ * @return a description of the object, or {@code null} if the object
+ * corresponding to the specified {@code code} has no description.
+ * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
+ * @throws FactoryException if the query failed for some other reason.
+ *
+ * @deprecated This method is ambiguous because the EPSG geodetic registry may allocate
+ * the same code to different kinds of object.
+ */
+ @Override
+ @Deprecated(since = "1.5")
+ public InternationalString getDescriptionText(final String code) throws FactoryException {
- return getDescriptionText(IdentifiedObject.class, code).orElse(null);
++ return getDescriptionText(CoordinateReferenceSystem.class, code).orElse(null);
+ }
+
/**
* Returns an object of the specified type from a code. This implementation forwards
* the method call to the most specialized methods determined by the given type.
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
index 2af9d2d64d,34d842d425..70c4797654
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
@@@ -26,16 -27,26 +27,26 @@@ import org.opengis.util.FactoryExceptio
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.AuthorityFactory;
import org.opengis.referencing.NoSuchAuthorityCodeException;
- import org.apache.sis.referencing.AbstractIdentifiedObject;
+ import org.opengis.referencing.datum.Datum;
import org.apache.sis.referencing.IdentifiedObjects;
+ import org.apache.sis.referencing.datum.DatumOrEnsemble;
+ import org.apache.sis.referencing.privy.FilteredIterator;
+ import org.apache.sis.referencing.privy.LazySet;
import org.apache.sis.util.ComparisonMode;
+ import org.apache.sis.util.Disposable;
import org.apache.sis.util.Utilities;
+ import org.apache.sis.util.OptionalCandidate;
import org.apache.sis.util.privy.Constants;
- import org.apache.sis.system.Semaphores;
import org.apache.sis.util.collection.BackingStoreException;
import org.apache.sis.util.logging.Logging;
+ import org.apache.sis.system.Semaphores;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.referencing.datum.DatumEnsemble;
-
+ // Specific to the main and geoapi-3.1 branches:
+ import org.opengis.referencing.ReferenceIdentifier;
+
++// Specific to the main branch:
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
+
/**
* Searches in an authority factory for objects approximately equal to a given object.
@@@ -255,13 -264,45 +264,45 @@@ public class IdentifiedObjectFinder
}
/**
- * Returns {@code true} if a candidate found by {@code IdentifiedObjectFinder} should be considered equals to the
- * requested object. This method invokes the {@code equals(…)} method on the {@code candidate} argument instead
- * than on the user-specified {@code object} on the assumption that implementations coming from the factory are
- * more reliable than user-specified objects.
+ * Returns the comparison mode to use when comparing a candidate against the object to search.
+ */
+ private ComparisonMode getComparisonMode() {
+ return ignoreAxes ? ComparisonMode.ALLOW_VARIANT : ComparisonMode.APPROXIMATE;
+ }
+
+ /**
+ * Returns {@code true} if a candidate created by a factory should be considered equal to the object to search.
+ * The {@code mode} and {@code proxy} arguments may be snapshots of the {@code IdentifiedObjectFinder}'s state
+ * taken at the time when the {@link Instances} iterable has been created.
+ *
+ * <h4>Implementation note</h4>
+ * This method invokes the {@code equals(…)} method on the {@code candidate} argument instead of {@code object}
+ * specified by the user on the assumption that implementations coming from the factory are more reliable than
+ * user-specified objects.
+ *
+ * @param candidate an object created by an authority factory.
+ * @param object the user-specified object to search.
+ * @param mode value of {@link #getComparisonMode()} (may be a snapshot).
+ * @param proxy value of {@link #proxy} (may be a snapshot).
+ * @return whether the given candidate can be considered equal to the object to search.
+ *
+ * @see #createAndFilter(AuthorityFactory, String, IdentifiedObject)
*/
- private boolean match(final IdentifiedObject candidate, final IdentifiedObject object) {
- return Utilities.deepEquals(candidate, object, ignoreAxes ? ComparisonMode.ALLOW_VARIANT : COMPARISON_MODE);
+ private static boolean match(final IdentifiedObject candidate, final IdentifiedObject object,
+ final ComparisonMode mode, final AuthorityFactoryProxy<?> proxy)
+ {
+ if (Utilities.deepEquals(candidate, object, mode)) {
+ return true;
+ }
+ if (Datum.class.isAssignableFrom(proxy.type)) {
- if (candidate instanceof Datum && object instanceof DatumEnsemble<?>) {
- return DatumOrEnsemble.isLegacyDatum((DatumEnsemble<?>) object, (Datum) candidate, mode);
++ if (candidate instanceof Datum && object instanceof DefaultDatumEnsemble<?>) {
++ return DatumOrEnsemble.isLegacyDatum((DefaultDatumEnsemble<?>) object, (Datum) candidate, mode);
+ }
- if (candidate instanceof DatumEnsemble<?> && object instanceof Datum) {
- return DatumOrEnsemble.isLegacyDatum((DatumEnsemble<?>) candidate, (Datum) object, mode);
++ if (candidate instanceof DefaultDatumEnsemble<?> && object instanceof Datum) {
++ return DatumOrEnsemble.isLegacyDatum((DefaultDatumEnsemble<?>) candidate, (Datum) object, mode);
+ }
+ }
+ return false;
}
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGCodeFinder.java
index b94cbce82e,e113272552..acacea9a0a
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGCodeFinder.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGCodeFinder.java
@@@ -53,6 -64,6 +64,11 @@@ import org.apache.sis.referencing.facto
import org.apache.sis.referencing.factory.ConcurrentAuthorityFactory;
import static org.apache.sis.metadata.privy.NameToIdentifier.Simplifier.ESRI_DATUM_PREFIX;
++// Specific to the main branch:
++import org.opengis.referencing.ReferenceIdentifier;
++import org.apache.sis.referencing.crs.DefaultParametricCRS;
++import org.apache.sis.referencing.datum.DefaultParametricDatum;
++
// Specific to the main and geoapi-3.1 branches:
import org.opengis.referencing.crs.GeneralDerivedCRS;
@@@ -322,11 -364,15 +365,15 @@@ crs: if (isInstance(CoordinateRefere
if (object instanceof GeneralDerivedCRS) { // No need to use isInstance(Class, Object) from here.
filter = dependencies("BASE_CRS_CODE", CoordinateReferenceSystem.class, ((GeneralDerivedCRS) object).getBaseCRS(), true);
} else if (object instanceof GeodeticCRS) {
- filter = dependencies("DATUM_CODE", GeodeticDatum.class, ((GeodeticCRS) object).getDatum(), true);
+ filter = dependencies("DATUM_CODE", GeodeticDatum.class, DatumOrEnsemble.asDatum((GeodeticCRS) object), true);
} else if (object instanceof VerticalCRS) {
- filter = dependencies("DATUM_CODE", VerticalDatum.class, ((VerticalCRS) object).getDatum(), true);
+ filter = dependencies("DATUM_CODE", VerticalDatum.class, DatumOrEnsemble.asDatum((VerticalCRS) object), true);
} else if (object instanceof TemporalCRS) {
- filter = dependencies("DATUM_CODE", TemporalDatum.class, ((TemporalCRS) object).getDatum(), true);
+ filter = dependencies("DATUM_CODE", TemporalDatum.class, DatumOrEnsemble.asDatum((TemporalCRS) object), true);
- } else if (object instanceof ParametricCRS) {
- filter = dependencies("DATUM_CODE", ParametricDatum.class, DatumOrEnsemble.asDatum((ParametricCRS) object), true);
++ } else if (object instanceof DefaultParametricCRS) {
++ filter = dependencies("DATUM_CODE", DefaultParametricDatum.class, ((DefaultParametricCRS) object).getDatum(), true);
+ } else if (object instanceof EngineeringCRS) {
+ filter = dependencies("DATUM_CODE", EngineeringDatum.class, DatumOrEnsemble.asDatum((EngineeringCRS) object), true);
} else if (object instanceof SingleCRS) {
filter = dependencies("DATUM_CODE", Datum.class, ((SingleCRS) object).getDatum(), true);
} else {
@@@ -532,4 -583,214 +584,214 @@@
}
buffer.append(')');
}
+
+ /**
+ * Returns a set of authority codes that <strong>may</strong> identify the same object as the specified one.
+ * This implementation tries to get a smaller set than what {@link EPSGDataAccess#getAuthorityCodes(Class)}
+ * would produce. Deprecated objects must be last in iteration order.
+ *
+ * <h4>Exceptions during iteration</h4>
+ * An unchecked {@link BackingStoreException} may be thrown during the iteration
+ * if the action of fetching codes from database was delayed and that action failed.
+ * The exception cause may be {@link FactoryException} or {@link SQLException}.
+ *
+ * @param object the object to search in the database.
+ * @return codes of objects that may be the requested ones.
+ * @throws FactoryException if an error occurred while fetching the set of code candidates.
+ */
+ @Override
+ protected Iterable<String> getCodeCandidates(final IdentifiedObject object) throws FactoryException {
+ for (final TableInfo source : TableInfo.values()) {
+ if (source.isSpecificEnough() && source.type.isInstance(object)) try {
+ return new CodeCandidates(object, source);
+ } catch (SQLException exception) {
+ throw databaseFailure(exception);
+ }
+ }
+ return Set.of();
+ }
+
+ /**
+ * Set of authority codes that <strong>may</strong> identify the same object as the specified one.
+ * This collection returns the codes that can be obtained easily before the more expensive searches.
+ *
+ * @todo We should not keep a reference to the enclosing finder, because the {@link EPSGDataAccess}
+ * may become invalid before the iteration is completed. For now, this is not a problem because this
+ * collection is copied by the {@link EPSGFactory} finder. But this is suboptimal because it defeats
+ * the purpose of object lazy instantiation.
+ */
+ private final class CodeCandidates implements Iterable<String>, Disposable {
+ /** The object to search. */
+ private final IdentifiedObject object;
+
+ /** Workaround for a Derby bug (see {@code filterFalsePositive(…)}). */
+ private String name;
+
+ /** {@code LIKE} Pattern of the name of the object to search. */
+ private String namePattern;
+
+ /** Information about the tables of the object to search. */
+ private final TableInfo source;
+
+ /** Cache of codes found so far. */
+ private final Set<Integer> codes;
+
+ /** Snapshot of the search domain as it was at collection construction time. */
+ private final Domain domain;
+
+ /** Sequential number of the algorithm used for filling the {@link #codes} collection so far. */
+ private byte searchMethod;
+
+ /**
+ * Creates a lazy collection of code candidates.
+ * This constructor loads immediately some codes in order to have an exception early in case of problem.
+ *
+ * @param object the object to search in the database.
+ * @param source information about the table where to search for the object.
+ * @throws SQLException if an error occurred while searching for codes associated to names.
+ * @throws FactoryException if an error occurred while fetching the set of code candidates.
+ */
+ CodeCandidates(final IdentifiedObject object, final TableInfo source) throws SQLException, FactoryException {
+ this.object = object;
+ this.source = source;
+ this.domain = getSearchDomain();
+ this.codes = new LinkedHashSet<>();
+ if (domain != Domain.EXHAUSTIVE_VALID_DATASET) {
- for (final Identifier id : object.getIdentifiers()) {
++ for (final ReferenceIdentifier id : object.getIdentifiers()) {
+ if (Constants.EPSG.equalsIgnoreCase(id.getCodeSpace())) try {
+ codes.add(Integer.valueOf(id.getCode()));
+ } catch (NumberFormatException exception) {
+ Logging.ignorableException(EPSGDataAccess.LOGGER, IdentifiedObjectFinder.class, "find", exception);
+ }
+ }
+ }
+ if (codes.isEmpty()) {
+ fetchMoreCodes(codes);
+ }
+ }
+
+ /**
+ * Invoked when the caller requested to stop the iteration after the current group of elements.
+ * A group of elements is either the codes specified by the identifiers, or the codes found in
+ * the database. We will avoid to stop in the middle of a group.
+ *
+ * <p>This is an undocumented feature of {@link #createFromCodes(IdentifiedObject)}
+ * for stopping an iteration early when at least one match has been found.</p>
+ */
+ @Override
+ public void dispose() {
+ searchMethod = 3; // The value after the last switch in `fetchMoreCodes(Collection)`.
+ }
+
+ /**
+ * Populates the given collection with code candidates.
+ * This method tries less expansive search methods before to tries more expensive search methods.
+ *
+ * @param addTo an initially empty collection where to add the codes.
+ * @return whether at least one code has been added to the given collection.
+ * @throws SQLException if an error occurred while searching for codes associated to names.
+ * @throws FactoryException if an error occurred while fetching the set of code candidates.
+ */
+ private boolean fetchMoreCodes(final Collection<Integer> addTo) throws SQLException, FactoryException {
+ do {
+ switch (searchMethod) {
+ case 0: { // Fetch codes from the name.
+ if (domain != Domain.EXHAUSTIVE_VALID_DATASET) {
+ name = getName(object);
+ if (name != null) { // Should never be null, but we are paranoiac.
+ namePattern = dao.toLikePattern(name);
+ dao.findCodesFromName(source, TableInfo.toCacheKey(object), namePattern, name, addTo);
+ }
+ }
+ break;
+ }
+ case 1: { // Fetch codes from the aliases.
+ if (domain != Domain.EXHAUSTIVE_VALID_DATASET) {
+ if (namePattern != null) {
+ dao.findCodesFromAlias(source, namePattern, name, addTo);
+ }
+ }
+ break;
+ }
+ case 2: { // Search codes based on object properties.
+ if (domain != Domain.DECLARATION) {
+ searchCodesFromProperties(object, domain == Domain.ALL_DATASET, addTo);
+ }
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+ searchMethod++;
+ } while (addTo.isEmpty());
+ return true;
+ }
+
+ /**
+ * Returns additional code candidates which were not yet returned by the iteration.
+ * This method uses the next search method which hasn't be tried.
+ *
+ * @return the additional codes.
+ * @throws BackingStoreException if an error occurred while fetching the set of code candidates.
+ */
+ private Iterator<Integer> fetchMoreCodes() {
+ final var addTo = new ArrayList<Integer>();
+ do {
+ try {
+ if (!fetchMoreCodes(addTo)) break;
+ } catch (SQLException | FactoryException exception) {
+ throw new BackingStoreException(exception);
+ }
+ for (Iterator<Integer> it = addTo.iterator(); it.hasNext();) {
+ if (!codes.add(it.next())) {
+ it.remove(); // Code has already be returned.
+ }
+ }
+ } while (addTo.isEmpty());
+ return addTo.iterator();
+ }
+
+ /**
+ * Returns an iterator over the code candidates. The codes are cached:
+ * the should not be fetched again if a second iteration is executed.
+ *
+ * <h4>Limitation</h4>
+ * The current implementation does not support concurrent iterations, even in the same thread.
+ * This is okay for the usage that Apache <abbr>SIS</abbr> is making of this iterator.
+ */
+ @Override
+ public Iterator<String> iterator() {
+ return new Iterator<String>() {
+ /** Iterator over a subset of the codes. */
+ private Iterator<Integer> sources = codes.iterator();
+
+ /** Tests whether there is more codes to return. */
+ @Override public boolean hasNext() {
+ if (sources.hasNext()) {
+ return true;
+ }
+ sources = fetchMoreCodes();
+ return sources.hasNext();
+ }
+
+ /** Returns the next code. */
+ @Override public String next() {
+ if (!sources.hasNext()) {
+ sources = fetchMoreCodes();
+ }
+ return sources.next().toString();
+ }
+ };
+ }
+
+ /**
+ * Returns a string representation for debugging purposes.
+ * The {@code "size"} property may change during the iteration.
+ */
+ @Override
+ public String toString() {
+ return Strings.toString(getClass(), "object", getName(object), "source", source, "domain", domain, "size", codes.size());
+ }
+ }
}
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
index 8d624ffa7f,edf2c685d9..ec3bc59639
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
@@@ -1998,11 -1981,11 +2006,11 @@@ search: try (ResultSet result = execute
final IdentifiedObject conventionalRS = createConventionalRS(convRSCode);
@SuppressWarnings("LocalVariableHidesMemberVariable")
final Map<String,Object> properties = createProperties(
- "Datum", epsg, name, null, area, scope, remarks, deprecated);
+ TableInfo.DATUM, epsg, name, null, area, scope, remarks, deprecated);
- properties.put(Datum.ANCHOR_DEFINITION_KEY, anchor);
- properties.put(Datum.ANCHOR_EPOCH_KEY, epoch);
- properties.put(Datum.PUBLICATION_DATE_KEY, publish);
- properties.put(Datum.CONVENTIONAL_RS_KEY, conventionalRS);
+ properties.put(AbstractDatum.ANCHOR_DEFINITION_KEY, anchor);
+ properties.put(AbstractDatum.ANCHOR_EPOCH_KEY, epoch);
+ properties.put(AbstractDatum.PUBLICATION_DATE_KEY, publish);
+ properties.put(AbstractDatum.CONVENTIONAL_RS_KEY, conventionalRS);
properties.values().removeIf(Objects::isNull);
final Datum datum = constructor.create(owner.datumFactory, properties);
returnValue = ensureSingleton(datum, returnValue, code);
@@@ -3006,8 -2977,8 +3004,8 @@@ next: while (r.next()
*/
@SuppressWarnings("LocalVariableHidesMemberVariable")
final Map<String,Object> properties = createProperties(
- "Coordinate_Operation Parameter", epsg, name, null, null, null, isReversible, deprecated);
+ TableInfo.PARAMETER, epsg, name, null, null, null, isReversible, deprecated);
- properties.put(Identifier.DESCRIPTION_KEY, description);
+ properties.put(ImmutableIdentifier.DESCRIPTION_KEY, description);
final var descriptor = new DefaultParameterDescriptor<>(properties, 1, 1, type, valueDomain, null, null);
returnValue = ensureSingleton(descriptor, returnValue, code);
if (result.isClosed()) break; // See createProperties(…) for explanation.
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java
index 39925454cf,835c89fb5e..20bc526972
--- 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
@@@ -25,139 -26,193 +26,200 @@@ import org.opengis.referencing.datum.*
import org.opengis.referencing.operation.*;
import org.opengis.parameter.ParameterDescriptor;
import org.apache.sis.referencing.privy.WKTKeywords;
- import org.apache.sis.util.CharSequences;
+// Specific to the main branch:
++import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
++import org.apache.sis.referencing.datum.DefaultGeodeticDatum;
+import org.apache.sis.referencing.crs.DefaultParametricCRS;
+import org.apache.sis.referencing.cs.DefaultParametricCS;
+import org.apache.sis.referencing.datum.DefaultParametricDatum;
+
/**
- * 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.
+ * Information (such as columns of particular interest) about a specific <abbr>EPSG</abbr> table.
+ * This class uses the mixed-case variant of the <abbr>EPSG</abbr> {@linkplain #table table names}.
+ * The {@link #values()} can be tested in preference order for finding the table of an object.
+ * Those tables are used by the {@link EPSGDataAccess#createObject(String)} method in order to
+ * detect which of the following methods should be invoked for a given code:
+ *
+ * {@link EPSGDataAccess#createCoordinateReferenceSystem(String)}
+ * {@link EPSGDataAccess#createCoordinateSystem(String)}
+ * {@link EPSGDataAccess#createDatum(String)}
+ * {@link EPSGDataAccess#createEllipsoid(String)}
+ * {@link EPSGDataAccess#createUnit(String)}
+ *
+ * <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 GeoAPI-specific geocentric <abbr>CRS</abbr> class. This special case is implemented in the
+ * {@link #appendWhere(EPSGDataAccess, Object, StringBuilder)} method.
*
* @author Martin Desruisseaux (IRD, Geomatys)
*/
- final class TableInfo {
+ enum TableInfo {
/**
- * The item used for coordinate reference systems.
+ * Information about the "Coordinate Reference System" table.
*/
- static final TableInfo CRS;
+ CRS(CoordinateReferenceSystem.class,
+ "\"Coordinate Reference System\"",
+ "COORD_REF_SYS_CODE",
+ "COORD_REF_SYS_NAME",
+ "COORD_REF_SYS_KIND",
+ new Class<?>[] { ProjectedCRS.class, GeographicCRS.class, GeocentricCRS.class,
+ VerticalCRS.class, CompoundCRS.class, EngineeringCRS.class,
- DerivedCRS.class, TemporalCRS.class, ParametricCRS.class}, // See comment below
++ DerivedCRS.class, TemporalCRS.class, DefaultParametricCRS.class}, // See comment below
+ new String[] {"projected", "geographic 2D", "geocentric", // 3D case handled below
+ "vertical", "compound", "engineering",
+ "derived", "temporal", "parametric"}, // See comment below
+ "SHOW_CRS", true),
+ /*
+ * Above declaration could omit Derived, Temporal and Parametric cases because they are not defined
+ * by the EPSG repository (at least as of version 8.9). In particular we are not sure if EPSG would
+ * chose to use "time" or "temporal". However, omitting those types slow down a lot the search for
+ * CRS matching an existing one (even if it still work).
+ */
/**
- * The item used for datums.
+ * Information about the "Datum" table.
*/
- static final TableInfo DATUM;
+ DATUM(Datum.class,
+ "\"Datum\"",
+ "DATUM_CODE",
+ "DATUM_NAME",
+ "DATUM_TYPE",
- new Class<?>[] { DatumEnsemble.class, // Need to be first because Apache SIS uses as mixin interface.
++ new Class<?>[] { DefaultDatumEnsemble.class, // Need to be first because Apache SIS uses as mixin interface.
+ GeodeticDatum.class, VerticalDatum.class, EngineeringDatum.class,
- TemporalDatum.class, ParametricDatum.class},
++ TemporalDatum.class, DefaultParametricDatum.class},
+ new String[] {"ensemble",
+ "geodetic", "vertical", "engineering",
+ "temporal", "parametric"}, // Same comment as in the CRS case above.
+ null, true),
/**
- * The item used for ellipsoids.
+ * Information about the "Conventional RS" table.
+ * This enumeration usually needs to be ignored because the current type is too generic.
+ *
+ * @see #isSpecificEnough()
*/
- static final TableInfo ELLIPSOID;
+ CONVENTIONAL_RS(IdentifiedObject.class,
+ "\"Conventional RS\"",
+ "CONVENTIONAL_RS_CODE",
+ "CONVENTIONAL_RS_NAME",
+ null, null, null, null, false),
/**
- * List of tables and columns to test for existence of codes values.
- * Those tables are used by the {@link EPSGDataAccess#createObject(String)} method
- * in order to detect which of the following methods should be invoked for a given code:
- *
- * {@link EPSGDataAccess#createCoordinateReferenceSystem(String)}
- * {@link EPSGDataAccess#createCoordinateSystem(String)}
- * {@link EPSGDataAccess#createDatum(String)}
- * {@link EPSGDataAccess#createEllipsoid(String)}
- * {@link EPSGDataAccess#createUnit(String)}
+ * Information about the "Ellipsoid" table.
+ */
+ ELLIPSOID(Ellipsoid.class,
+ "\"Ellipsoid\"",
+ "ELLIPSOID_CODE",
+ "ELLIPSOID_NAME",
+ null, null, null, null, false),
+
+ /**
+ * Information about the "Prime Meridian" table.
+ */
+ PRIME_MERIDIAN(PrimeMeridian.class,
+ "\"Prime Meridian\"",
+ "PRIME_MERIDIAN_CODE",
+ "PRIME_MERIDIAN_NAME",
+ null, null, null, null, false),
+
+ /**
+ * Information about the "Coordinate_Operation" table.
+ */
+ OPERATION(CoordinateOperation.class,
+ "\"Coordinate_Operation\"",
+ "COORD_OP_CODE",
+ "COORD_OP_NAME",
+ "COORD_OP_TYPE",
+ new Class<?>[] { Conversion.class, Transformation.class},
+ new String[] {"conversion", "transformation"},
+ "SHOW_OPERATION", true),
+
+ /**
+ * Information about the "Coordinate_Operation Method" table.
+ */
+ METHOD(OperationMethod.class,
+ "\"Coordinate_Operation Method\"",
+ "COORD_OP_METHOD_CODE",
+ "COORD_OP_METHOD_NAME",
+ null, null, null, null, false),
+
+ /**
+ * Information about the "Coordinate_Operation Parameter" table.
+ */
+ PARAMETER(ParameterDescriptor.class,
+ "\"Coordinate_Operation Parameter\"",
+ "PARAMETER_CODE",
+ "PARAMETER_NAME",
+ null, null, null, null, false),
+
+ /**
+ * Information about the "Extent" table.
+ */
+ EXTENT(Extent.class,
+ "\"Extent\"",
+ "EXTENT_CODE",
+ "EXTENT_NAME",
+ null, null, null, null, false),
+
+ /**
+ * Information about the "Coordinate System" table.
+ */
+ CS(CoordinateSystem.class,
+ "\"Coordinate System\"",
+ "COORD_SYS_CODE",
+ "COORD_SYS_NAME",
+ "COORD_SYS_TYPE",
+ new Class<?>[] {CartesianCS.class, EllipsoidalCS.class, VerticalCS.class, LinearCS.class,
+ SphericalCS.class, PolarCS.class, CylindricalCS.class,
- TimeCS.class, ParametricCS.class, AffineCS.class},
++ TimeCS.class, DefaultParametricCS.class,AffineCS.class},
+ new String[] {WKTKeywords.Cartesian, WKTKeywords.ellipsoidal, WKTKeywords.vertical, WKTKeywords.linear,
+ WKTKeywords.spherical, WKTKeywords.polar, WKTKeywords.cylindrical,
+ WKTKeywords.temporal, WKTKeywords.parametric, WKTKeywords.affine}, // Same comment as in the CRS case above.
+ null, false),
+
+ /**
+ * Information about the "Coordinate Axis" table.
+ */
+ AXIS(CoordinateSystemAxis.class,
+ "\"Coordinate Axis\" AS CA INNER JOIN \"Coordinate Axis Name\" AS CAN " +
+ "ON CA.COORD_AXIS_NAME_CODE=CAN.COORD_AXIS_NAME_CODE",
+ "COORD_AXIS_CODE",
+ "COORD_AXIS_NAME",
+ null, null, null, null, false),
+
+ /**
+ * Information about the "Unit of Measure" table.
+ */
+ UNIT(Unit.class,
+ "\"Unit of Measure\"",
+ "UOM_CODE",
+ "UNIT_OF_MEAS_NAME",
+ null, null, null, null, false);
+
+ /**
+ * Types to consider as synonymous for searching purposes. This map exists for historical reasons,
+ * because dynamic datum and datum ensemble did not existed in older <abbr>ISO</abbr> 19111 standards.
+ * If an object to search is "geodetic", there is a possibility that it is defined in the old way and
+ * actually appears as a "dynamic geodetic" or "ensemble" in the <abbr>EPSG</abbr> geodetic dataset.
*
- * The order is significant: it is the key for a {@code switch} statement.
+ * <p>The "geographic 3D" case is handled in a special way. It is considered as a synonymous of
+ * "geographic 2D" only when we don't know the number of dimensions.</p>
+ */
+ private static final Map<String, String[]> SYNONYMOUS_TYPES = Map.of(
+ "geodetic", new String[] {"dynamic geodetic", "ensemble"},
+ "geographic 2D", new String[] {"geographic 3D"});
+
+ /**
+ * Types to replace by specialized types when the user-specified instance implements a mixin interface.
+ * For example, {@link DynamicReferenceFrame} means to not search for any geodetic datum, but only for
+ * dynamic geodetic datum.
*/
- static final TableInfo[] EPSG = {
- CRS = new TableInfo(CoordinateReferenceSystem.class,
- "\"Coordinate Reference System\"",
- "COORD_REF_SYS_CODE",
- "COORD_REF_SYS_NAME",
- "COORD_REF_SYS_KIND",
- new Class<?>[] { ProjectedCRS.class, GeographicCRS.class, GeocentricCRS.class,
- VerticalCRS.class, CompoundCRS.class, EngineeringCRS.class,
- DerivedCRS.class, TemporalCRS.class, DefaultParametricCRS.class}, // See comment below
- new String[] {"projected", "geographic", "geocentric",
- "vertical", "compound", "engineering",
- "derived", "temporal", "parametric"}, // See comment below
- "SHOW_CRS", true),
- /*
- * Above declaration could omit Derived, Temporal and Parametric cases because they are not defined
- * by the EPSG repository (at least as of version 8.9). In particular we are not sure if EPSG would
- * chose to use "time" or "temporal". However, omitting those types slow down a lot the search for
- * CRS matching an existing one (even if it still work).
- */
-
- new TableInfo(CoordinateSystem.class,
- "\"Coordinate System\"",
- "COORD_SYS_CODE",
- "COORD_SYS_NAME",
- "COORD_SYS_TYPE",
- new Class<?>[] {CartesianCS.class, EllipsoidalCS.class, VerticalCS.class, LinearCS.class,
- SphericalCS.class, PolarCS.class, CylindricalCS.class,
- TimeCS.class, DefaultParametricCS.class, AffineCS.class},
- new String[] {WKTKeywords.Cartesian, WKTKeywords.ellipsoidal, WKTKeywords.vertical, WKTKeywords.linear,
- WKTKeywords.spherical, WKTKeywords.polar, WKTKeywords.cylindrical,
- WKTKeywords.temporal, WKTKeywords.parametric, WKTKeywords.affine}, // Same comment as in the CRS case above.
- null, false),
-
- new TableInfo(CoordinateSystemAxis.class,
- "\"Coordinate Axis\" AS CA INNER JOIN \"Coordinate Axis Name\" AS CAN " +
- "ON CA.COORD_AXIS_NAME_CODE=CAN.COORD_AXIS_NAME_CODE",
- "COORD_AXIS_CODE",
- "COORD_AXIS_NAME",
- null, null, null, null, false),
-
- DATUM = new TableInfo(Datum.class,
- "\"Datum\"",
- "DATUM_CODE",
- "DATUM_NAME",
- "DATUM_TYPE",
- new Class<?>[] { GeodeticDatum.class, VerticalDatum.class, EngineeringDatum.class,
- TemporalDatum.class, DefaultParametricDatum.class},
- new String[] {"geodetic", "vertical", "engineering",
- "temporal", "parametric"}, // Same comment as in the CRS case above.
- null, true),
-
- ELLIPSOID = new TableInfo(Ellipsoid.class,
- "\"Ellipsoid\"",
- "ELLIPSOID_CODE",
- "ELLIPSOID_NAME",
- null, null, null, null, false),
-
- new TableInfo(PrimeMeridian.class,
- "\"Prime Meridian\"",
- "PRIME_MERIDIAN_CODE",
- "PRIME_MERIDIAN_NAME",
- null, null, null, null, false),
-
- new TableInfo(CoordinateOperation.class,
- "\"Coordinate_Operation\"",
- "COORD_OP_CODE",
- "COORD_OP_NAME",
- "COORD_OP_TYPE",
- new Class<?>[] { Conversion.class, Transformation.class},
- new String[] {"conversion", "transformation"},
- "SHOW_OPERATION", true),
-
- new TableInfo(OperationMethod.class,
- "\"Coordinate_Operation Method\"",
- "COORD_OP_METHOD_CODE",
- "COORD_OP_METHOD_NAME",
- null, null, null, null, false),
-
- new TableInfo(ParameterDescriptor.class,
- "\"Coordinate_Operation Parameter\"",
- "PARAMETER_CODE",
- "PARAMETER_NAME",
- null, null, null, null, false),
-
- new TableInfo(Unit.class,
- "\"Unit of Measure\"",
- "UOM_CODE",
- "UNIT_OF_MEAS_NAME",
- null, null, null, null, false),
- };
+ private static final Map<String, String> DYNAMIC_TYPES = Map.of(
+ "geodetic", "dynamic geodetic");
+ // We would expect a "dynamic vertical" as well, but we don't see it yet in EPSG database.
/**
* The class of object to be created (usually a GeoAPI interface).
@@@ -309,16 -404,46 +411,46 @@@
buffer.append(" WHERE ");
if (typeColumn != null) {
for (int i=0; i<subTypes.length; i++) {
- final Class<?> candidate = subTypes[i];
- if (candidate.isAssignableFrom(userType)) {
- if (factory.translator.useEnumerations()) {
- buffer.append("CAST(").append(typeColumn).append(" AS ")
- .append(EPSGInstaller.ENUM_REPLACEMENT).append(')');
- } else {
- buffer.append(typeColumn);
+ final Class<?> subType = subTypes[i];
+ if (subType.isAssignableFrom(userType)) {
+ /*
+ * Found the type to request in the `COORD_REF_SYS_KIND` or `DATUM_TYPE` columns.
+ * The mixin interfaces need to be handled in a special way.
+ */
+ String typeName = typeNames[i];
- if (DynamicReferenceFrame.class.isAssignableFrom(userType)) {
++ if (DefaultGeodeticDatum.Dynamic.class.isAssignableFrom(userType)) {
+ typeName = DYNAMIC_TYPES.getOrDefault(typeName, typeName);
+ }
+ /*
+ * We may need to look for more than one type if some information are missing
+ * (for example, the dimension when EPSG distinguishes the 2D and 3D cases).
+ */
+ String[] synonymous = SYNONYMOUS_TYPES.get(typeName);
+ if (synonymous != null && dimension > 0 && dimension <= 9) {
+ final String suffix = "2D".replace('2', (char) ('0' + dimension));
+ if (typeName.endsWith(suffix)) {
+ synonymous = null;
+ } else {
+ for (String alternative : synonymous) {
+ if (alternative.endsWith(suffix)) {
+ typeName = alternative;
+ synonymous = null;
+ break;
+ }
+ }
+ }
}
- buffer.append(" LIKE '").append(typeNames[i]).append("%' AND ");
- return candidate;
+ /*
+ * Build the SQL `WHERE` clause.
+ */
+ buffer.append('(').append(typeColumn).append(" = '").append(typeName).append('\'');
+ if (synonymous != null) {
+ for (String alternative : synonymous) {
+ buffer.append(" OR ").append(typeColumn).append(" = '").append(alternative).append('\'');
+ }
+ }
+ buffer.append(") AND ");
+ return subType;
}
}
}
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/PositionTransformer.java
index 914cb6be33,40f545525a..7e6de959a8
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/PositionTransformer.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/PositionTransformer.java
@@@ -28,10 -29,10 +29,9 @@@ import org.apache.sis.referencing.CRS
import org.apache.sis.referencing.MultiRegisterOperations;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.geometry.GeneralDirectPosition;
- import org.apache.sis.util.Utilities;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.referencing.RegisterOperations;
-import org.opengis.coordinate.MismatchedDimensionException;
+// Specific to the main branch:
+import org.opengis.geometry.MismatchedDimensionException;
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
index bb0c5c49d3,e2aacdd5c1..22994f3543
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
@@@ -207,9 -265,10 +210,10 @@@ public final class VerticalDatumTypes i
AxisDirection dir = AxisDirection.UP; // Expected direction for accepting the type.
switch (abbreviation.charAt(0)) {
case 'h': method = ellipsoidal(); break;
- case 'H': method = RealizationMethod.GEOID; break;
+ case 'H': method = VerticalDatumType.GEOIDAL; break;
+ case 'd': // Fall through
- case 'D': method = RealizationMethod.TIDAL; dir = AxisDirection.DOWN; break;
+ case 'D': method = VerticalDatumType.DEPTH; dir = AxisDirection.DOWN; break;
- default: return VerticalDatumType.OTHER_SURFACE;
+ default: return null;
}
if (dir.equals(axis.getDirection())) {
return method;
@@@ -229,24 -288,18 +233,29 @@@
* @param name name of the datum for which to guess a realization method, or {@code null}.
* @return a realization method, or {@code null} if none can be guessed.
*/
- private static RealizationMethod fromDatum(final String name) {
+ private static VerticalDatumType fromDatum(final String name) {
if (name != null) {
if (CharSequences.equalsFiltered("Mean Sea Level", name, Characters.Filter.LETTERS_AND_DIGITS, true)) {
- return RealizationMethod.TIDAL;
+ return VerticalDatumType.GEOIDAL;
}
- if (name.regionMatches(true, 0, "geoid", 0, 5)) {
- return VerticalDatumType.GEOIDAL;
- }
+ int i = 0;
+ do {
+ if (name.regionMatches(true, i, "geoid", 0, 5)) {
- return RealizationMethod.GEOID;
++ return VerticalDatumType.GEOIDAL;
+ }
+ i = name.indexOf(' ', i) + 1;
+ } while (i > 0);
+ if (name.equalsIgnoreCase("Tidal")) {
+ return VerticalDatumType.DEPTH;
+ }
- for (int i=0; i<name.length();) {
++ i = 0;
++ while (i < name.length()) {
+ final int c = name.codePointAt(i);
+ if (Character.isLetter(c)) {
+ return CodeLists.find(VerticalDatumType.class, new VerticalDatumTypes(name));
+ }
+ i += Character.charCount(c);
+ }
}
return null;
}
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
index 12fef44803,7b6a453186..8ad69565f3
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
@@@ -920,10 -856,10 +918,10 @@@ check: for (int isTarget=0; ; isTa
* - Scope, domain and accuracy properties only if NOT in "ignore metadata" mode.
* - Interpolation CRS in all cases (regardless if ignoring metadata or not).
*/
- final CoordinateOperation that = (CoordinateOperation) object;
+ final var that = (CoordinateOperation) object;
if ((mode.isIgnoringMetadata() ||
(deepEquals(getCoordinateOperationAccuracy(), that.getCoordinateOperationAccuracy(), mode))) &&
- deepEquals(getInterpolationCRS(), that.getInterpolationCRS(), mode))
+ deepEquals(getInterpolationCRS().orElse(null), getInterpolationCRS(that), mode))
{
/*
* At this point all metadata match or can be ignored. First, compare the targetCRS.
diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
index d7a85f04cd,eade998322..eeb4852a02
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
@@@ -108,10 -119,10 +122,10 @@@ public final class Assertions extends S
*/
public static void assertEpsgIdentifierEquals(final String expected, final Identifier actual) {
assertNotNull(actual);
- assertEquals(expected, actual.getCode(), "code");
+ assertLegacyEquals(expected, actual.getCode(), "code");
- assertEquals(Constants.EPSG, actual.getCodeSpace(), "codeSpace");
+ assertEquals(Constants.EPSG, (actual instanceof ReferenceIdentifier) ? ((ReferenceIdentifier) actual).getCodeSpace() : null, "codeSpace");
assertEquals(Constants.EPSG, Citations.toCodeSpace(actual.getAuthority()), "authority");
- assertEquals(Constants.EPSG + Constants.DEFAULT_SEPARATOR + expected, IdentifiedObjects.toString(actual), "identifier");
+ assertLegacyEquals(Constants.EPSG + Constants.DEFAULT_SEPARATOR + expected, IdentifiedObjects.toString(actual), "identifier");
}
/**
diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/IdentifiedObjectFinderTest.java
index a9e651991e,671fb48832..28584bcf7a
--- 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
@@@ -76,9 -72,9 +75,9 @@@ public final class IdentifiedObjectFind
* Same test as above, using a CRS without identifier.
* The intent is to force a full scan.
*/
- final CoordinateReferenceSystem search = new DefaultGeographicCRS(
+ final var search = new DefaultGeographicCRS(
Map.of(DefaultGeographicCRS.NAME_KEY, CRS84.getName()),
- 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/factory/MultiAuthoritiesFactoryTest.java
index 99865879a7,99865879a7..87bd14d01f
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/MultiAuthoritiesFactoryTest.java
@@@ -196,7 -196,7 +196,7 @@@ public final class MultiAuthoritiesFact
assertSame(HardCodedDatum.SPHERE, factory.createGeodeticDatum("MOCK: 0:6047"));
assertSame(Extents .WORLD, factory.createExtent ("MOCK: 2.3 : 1262"));
assertSame(Units .METRE, factory.createUnit (" MoCK : : 9001 "));
-- assertEquals("Greenwich", factory.getDescriptionText(IdentifiedObject.class, "MOCK:8901").get().toString());
++ assertEquals("Greenwich", factory.getDescriptionText(PrimeMeridian.class, "MOCK:8901").get().toString());
var e = assertThrows(NoSuchAuthorityFactoryException.class,
() -> factory.createGeodeticDatum("MOCK2:4326"),
diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
index 714b681bb9,33fdd7fa1a..3850bc2ef1
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
@@@ -101,6 -116,26 +116,26 @@@ public final class ConsistencyTest exte
lookup(parseAndFormat(format, code, crs), crs);
}
+ /**
+ * Returns whether testing the given <abbr>CRS</abbr> requires the 2019 version of <abbr>WKT</abbr> format.
+ * We skip the vertical datum having the "local" realization method because this information is lost during
+ * the roundtrip with WKT or WKT 2 version 2015, and the WKT parser tries to guess the method from the axis
+ * abbreviation "d" which result in "tidal".
+ */
+ private static boolean requiresWKT2019(final CoordinateReferenceSystem crs) {
+ final VerticalCRS c = CRS.getVerticalComponent(crs, false);
+ if (c != null) {
+ final var datum = c.getDatum();
+ if (datum != null) {
- final String method = datum.getRealizationMethod().map(CodeList::name).orElse("");
++ final String method = datum.getVerticalDatumType().name();
+ if (method.equalsIgnoreCase("local")) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Verifies the WKT consistency of all CRS instances.
*
diff --cc endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClause.java
index ff31e233f0,79cd823110..cfa89aaad2
--- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClause.java
+++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClause.java
@@@ -34,15 -34,16 +34,15 @@@ import org.apache.sis.geometry.wrapper.
import org.apache.sis.geometry.wrapper.GeometryWrapper;
import org.apache.sis.metadata.sql.privy.SQLBuilder;
import org.apache.sis.filter.privy.WarningEvent;
+ import org.apache.sis.referencing.CRS;
import org.apache.sis.storage.FeatureSet;
import org.apache.sis.system.Modules;
- import org.apache.sis.util.Utilities;
import org.apache.sis.util.Workaround;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.util.CodeList;
-import org.opengis.feature.Feature;
-import org.opengis.filter.Filter;
-import org.opengis.filter.ValueReference;
+// Specific to the main branch:
+import org.apache.sis.filter.Filter;
+import org.apache.sis.feature.AbstractFeature;
+import org.apache.sis.pending.geoapi.filter.ValueReference;
/**
diff --cc optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java
index 60292eef43,4c8393cdcc..f024ec9597
--- a/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java
+++ b/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java
@@@ -133,8 -129,8 +129,8 @@@ public final strictfp class EmbeddedRes
*/
@Test
public void testCrsforCode() throws FactoryException {
- assumeDataPresent();
+ assumeContainsEPSG();
- CoordinateReferenceSystem crs = CRS.forCode("EPSG:6676");
+ var crs = assertInstanceOf(AbstractCRS.class, CRS.forCode("EPSG:6676"));
String area = TestUtilities.getSingleton(crs.getDomains()).getDomainOfValidity().getDescription().toString();
assertTrue(area.contains("Japan"), area);
}