This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 94d30d99d2 The referencing module needs to check if the datum is null,
and fallback on the datum ensemble if needed. This commit does not resolve all
cases. In particular, the cases that depend on the EPSG database update are not
yet done: EPSGDataAccess, CoordinateOperationRegistry and
CoordinateOperationFinder.
94d30d99d2 is described below
commit 94d30d99d2bd07a08ee6cd4af5150135ca48537a
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Jul 13 16:38:36 2024 +0200
The referencing module needs to check if the datum is null, and fallback on
the datum ensemble if needed.
This commit does not resolve all cases. In particular, the cases that
depend on the EPSG database update
are not yet done: EPSGDataAccess, CoordinateOperationRegistry and
CoordinateOperationFinder.
---
.../org/apache/sis/portrayal/CanvasContext.java | 12 +-
.../gazetteer/GeohashReferenceSystem.java | 6 +-
.../gazetteer/MilitaryGridReferenceSystem.java | 2 +-
.../org/apache/sis/geometry/CoordinateFormat.java | 7 +-
.../main/org/apache/sis/io/wkt/VerticalInfo.java | 14 ++-
.../main/org/apache/sis/referencing/CRS.java | 34 +++---
.../main/org/apache/sis/referencing/CommonCRS.java | 41 ++++++-
.../referencing/EllipsoidalHeightSeparator.java | 24 +++-
.../sis/referencing/MultiRegisterOperations.java | 23 +++-
.../apache/sis/referencing/crs/AbstractCRS.java | 4 +-
.../sis/referencing/crs/DefaultGeographicCRS.java | 70 +++++++++++
.../sis/referencing/crs/DefaultTemporalCRS.java | 39 +++++-
.../sis/referencing/crs/ExplicitParameters.java | 6 +-
.../apache/sis/referencing/internal/Resources.java | 2 +-
.../sis/referencing/internal/Resources.properties | 2 +-
.../referencing/internal/Resources_fr.properties | 2 +-
.../referencing/operation/DefaultConversion.java | 15 ++-
.../DefaultCoordinateOperationFactory.java | 16 +--
.../transform/DefaultMathTransformFactory.java | 5 +-
.../sis/referencing/privy/DefinitionVerifier.java | 26 ++--
.../privy/EllipsoidalHeightCombiner.java | 24 ++--
.../referencing/privy/GeodeticObjectBuilder.java | 36 ++++--
.../referencing/privy/ReferencingUtilities.java | 136 ++++++++++++++-------
.../sis/storage/geotiff/reader/CRSBuilder.java | 8 +-
.../sis/storage/geotiff/writer/GeoEncoder.java | 62 +++++-----
.../apache/sis/storage/netcdf/base/CRSBuilder.java | 37 ++++--
.../sis/storage/netcdf/base/GridMapping.java | 9 +-
.../main/org/apache/sis/util/resources/Errors.java | 5 +
.../apache/sis/util/resources/Errors.properties | 1 +
.../apache/sis/util/resources/Errors_fr.properties | 1 +
30 files changed, 473 insertions(+), 196 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/portrayal/CanvasContext.java
b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/portrayal/CanvasContext.java
index aae76518d5..b6c9144308 100644
---
a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/portrayal/CanvasContext.java
+++
b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/portrayal/CanvasContext.java
@@ -19,7 +19,6 @@ package org.apache.sis.portrayal;
import java.util.Optional;
import java.util.OptionalDouble;
import org.opengis.metadata.extent.GeographicBoundingBox;
-import org.opengis.referencing.crs.GeodeticCRS;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.Ellipsoid;
import org.opengis.referencing.operation.Matrix;
@@ -35,6 +34,7 @@ import org.apache.sis.measure.Units;
import org.apache.sis.geometry.Envelopes;
import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.referencing.privy.Formulas;
+import org.apache.sis.referencing.privy.ReferencingUtilities;
import org.apache.sis.system.Configuration;
@@ -201,10 +201,12 @@ final class CanvasContext extends
CoordinateOperationContext {
}
combined[j] = m;
}
- final Ellipsoid ellipsoid = ((GeodeticCRS)
objectiveToGeographic.getTargetCRS()).getDatum().getEllipsoid();
- double radius = Formulas.radiusOfConformalSphere(ellipsoid,
combined[1]);
- radius =
ellipsoid.getAxisUnit().getConverterTo(Units.METRE).convert(radius);
- resolution = MathFunctions.magnitude(combined) * radius;
+ final Ellipsoid ellipsoid =
ReferencingUtilities.getEllipsoid(objectiveToGeographic.getTargetCRS());
+ if (ellipsoid != null) {
+ double radius =
Formulas.radiusOfConformalSphere(ellipsoid, combined[1]);
+ radius =
ellipsoid.getAxisUnit().getConverterTo(Units.METRE).convert(radius);
+ resolution = MathFunctions.magnitude(combined) * radius;
+ }
}
}
}
diff --git
a/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/GeohashReferenceSystem.java
b/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/GeohashReferenceSystem.java
index 1666a27797..512e608b95 100644
---
a/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/GeohashReferenceSystem.java
+++
b/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/GeohashReferenceSystem.java
@@ -60,7 +60,7 @@ import org.opengis.referencing.gazetteer.LocationType;
*
* @author Chris Mattmann (JPL)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.4
+ * @version 1.5
*
* @see <a href="https://en.wikipedia.org/wiki/Geohash">Geohash on
Wikipedia</a>
*
@@ -315,7 +315,7 @@ public class GeohashReferenceSystem extends
ReferencingByIdentifiers {
*/
@Override
public Quantity<Length> getPrecision(DirectPosition position) {
- final Ellipsoid ellipsoid =
normalizedCRS.getDatum().getEllipsoid();
+ final Ellipsoid ellipsoid = normalizedCRS.getEllipsoid();
final Unit<Length> unit = ellipsoid.getAxisUnit();
final int latNumBits = (5*length) >>> 1; // Number of
bits for latitude value.
final int lonNumBits = latNumBits + (length & 1); // Longitude
has 1 more bit when length is odd.
@@ -353,7 +353,7 @@ public class GeohashReferenceSystem extends
ReferencingByIdentifiers {
numLat = Latitude .MAX_VALUE / p;
numLon = Longitude.MAX_VALUE / p;
} else {
- final Ellipsoid ellipsoid =
normalizedCRS.getDatum().getEllipsoid();
+ final Ellipsoid ellipsoid = normalizedCRS.getEllipsoid();
p = unit.getConverterToAny(ellipsoid.getAxisUnit()).convert(p);
if (position != null) try {
position = toGeographic(position);
diff --git
a/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
b/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
index 85906610bc..24bacf8623 100644
---
a/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
+++
b/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
@@ -576,7 +576,7 @@ public class MilitaryGridReferenceSystem extends
ReferencingByIdentifiers {
* Returns the ellipsoid of the geodetic reference frame of MGRS
identifiers.
*/
final Ellipsoid getEllipsoid() {
- return datum.geographic().getDatum().getEllipsoid();
+ return datum.ellipsoid();
}
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/CoordinateFormat.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/CoordinateFormat.java
index b5752a0760..b2a15f9d9d 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/CoordinateFormat.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/CoordinateFormat.java
@@ -51,6 +51,7 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.TemporalCRS;
import org.opengis.referencing.datum.Ellipsoid;
import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.crs.DefaultTemporalCRS;
import org.apache.sis.referencing.privy.Formulas;
import org.apache.sis.referencing.privy.AxisDirections;
import org.apache.sis.referencing.privy.ReferencingUtilities;
@@ -58,13 +59,13 @@ import org.apache.sis.system.Loggers;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.Characters;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.privy.LocalizedParseException;
import org.apache.sis.util.privy.Constants;
import org.apache.sis.util.privy.Numerics;
import org.apache.sis.math.DecimalFunctions;
import org.apache.sis.math.MathFunctions;
-import org.apache.sis.util.logging.Logging;
-import org.apache.sis.util.resources.Errors;
import org.apache.sis.temporal.TemporalDate;
import org.apache.sis.measure.Angle;
import org.apache.sis.measure.AngleFormat;
@@ -580,7 +581,7 @@ public class CoordinateFormat extends
CompoundFormat<DirectPosition> {
}
types [i] = DATE;
formats[i] = getFormat(Date.class);
- epochs [i] = TemporalDate.toInstant(((TemporalCRS)
t).getDatum().getOrigin(), null);
+ epochs [i] =
TemporalDate.toInstant(DefaultTemporalCRS.castOrCopy((TemporalCRS)
t).getOrigin(), null);
setConverter(dimension, i,
unit.asType(Time.class).getConverterTo(Units.SECOND));
if (direction == AxisDirection.PAST) {
negate(i);
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/VerticalInfo.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/VerticalInfo.java
index 37275e7cc3..a11f49dbec 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/VerticalInfo.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/VerticalInfo.java
@@ -27,6 +27,7 @@ import org.opengis.referencing.cs.CSFactory;
import org.opengis.referencing.cs.VerticalCS;
import org.opengis.referencing.crs.CRSFactory;
import org.opengis.referencing.crs.VerticalCRS;
+import org.opengis.referencing.datum.VerticalDatum;
import org.apache.sis.metadata.privy.AxisNames;
import org.apache.sis.metadata.iso.extent.DefaultExtent;
import org.apache.sis.metadata.iso.extent.DefaultVerticalExtent;
@@ -107,8 +108,11 @@ final class VerticalInfo {
* became empty as a result of this operation.
*/
final VerticalInfo resolve(final VerticalCRS crs) {
- if (crs != null && crs.getDatum().getRealizationMethod().orElse(null)
== RealizationMethod.GEOID) {
- return resolve(crs, crs.getCoordinateSystem().getAxis(0));
+ if (crs != null) {
+ final VerticalDatum datum = crs.getDatum();
+ if (datum != null && datum.getRealizationMethod().orElse(null) ==
RealizationMethod.GEOID) {
+ return resolve(crs, crs.getCoordinateSystem().getAxis(0));
+ }
}
return this;
}
@@ -179,8 +183,10 @@ final class VerticalInfo {
* cases the previous name may contain terms like "depth", which
are not appropriate for our new CRS.
*/
final VerticalCS cs = csFactory.createVerticalCS
(properties(axis.getName()), axis);
- extent.setVerticalCRS(crsFactory.createVerticalCRS(
- properties((isUP ? compatibleCRS : axis).getName()),
compatibleCRS.getDatum(), cs));
+ extent.setVerticalCRS(crsFactory.createVerticalCRS(properties((isUP ?
compatibleCRS : axis).getName()),
+
compatibleCRS.getDatum(),
+
compatibleCRS.getDatumEnsemble(),
+ cs));
return next;
}
diff --git
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
index 3f71eab3bf..6174b51d20 100644
---
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
@@ -45,7 +45,6 @@ import org.opengis.referencing.crs.TemporalCRS;
import org.opengis.referencing.crs.VerticalCRS;
import org.opengis.referencing.crs.EngineeringCRS;
import org.opengis.referencing.datum.Datum;
-import org.opengis.referencing.datum.GeodeticDatum;
import org.opengis.referencing.operation.Conversion;
import org.opengis.referencing.operation.OperationNotFoundException;
import org.opengis.referencing.operation.CoordinateOperation;
@@ -1124,7 +1123,7 @@ public final class CRS extends Static {
{
final long current = (Numerics.bitmask(dimension) - 1) << previous;
final long intersect = selected & current;
- if (intersect != 0) {
+choice: if (intersect != 0) {
if (intersect == current) {
addTo.add(crs);
selected &= ~current;
@@ -1135,18 +1134,23 @@ public final class CRS extends Static {
if ((selected & current) == 0) break; // Stop if
it would be useless to continue.
previous += dimension;
}
- } else if (dimension == 3 && crs instanceof SingleCRS) {
- final Datum datum = ((SingleCRS) crs).getDatum();
- if (datum instanceof GeodeticDatum) {
- final boolean isVertical = Long.bitCount(intersect) == 1;
// Presumed for now, verified later.
- final int verticalDimension =
Long.numberOfTrailingZeros((isVertical ? intersect : ~intersect) >>> previous);
- final CoordinateSystemAxis verticalAxis =
crs.getCoordinateSystem().getAxis(verticalDimension);
- if
(AxisDirections.isVertical(verticalAxis.getDirection())) try {
- addTo.add(new
EllipsoidalHeightSeparator((GeodeticDatum) datum,
isVertical).separate((SingleCRS) crs));
- selected &= ~current;
- } catch (IllegalArgumentException | ClassCastException e) {
- throw new
FactoryException(Resources.format(Resources.Keys.CanNotSeparateCRS_1,
crs.getName()));
- }
+ } else if (dimension == 3) {
+ final GeodeticCRS baseCRS;
+ if (crs instanceof GeodeticCRS) {
+ baseCRS = (GeodeticCRS) crs;
+ } else if (crs instanceof ProjectedCRS) {
+ baseCRS = ((ProjectedCRS) crs).getBaseCRS();
+ } else {
+ break choice;
+ }
+ final boolean isVertical = Long.bitCount(intersect) == 1;
// Presumed for now, verified later.
+ final int verticalDimension =
Long.numberOfTrailingZeros((isVertical ? intersect : ~intersect) >>> previous);
+ final CoordinateSystemAxis verticalAxis =
crs.getCoordinateSystem().getAxis(verticalDimension);
+ if (AxisDirections.isVertical(verticalAxis.getDirection()))
try {
+ addTo.add(new EllipsoidalHeightSeparator(baseCRS,
isVertical).separate((SingleCRS) crs));
+ selected &= ~current;
+ } catch (IllegalArgumentException | ClassCastException e) {
+ throw new
FactoryException(Resources.format(Resources.Keys.CanNotSeparateCRS_1,
crs.getName()));
}
}
}
@@ -1552,7 +1556,7 @@ check: while (lower != 0 || upper != dimension) {
* @since 0.5
*/
public static double getGreenwichLongitude(final GeodeticCRS crs) {
- return
ReferencingUtilities.getGreenwichLongitude(crs.getDatum().getPrimeMeridian(),
Units.DEGREE);
+ return
ReferencingUtilities.getGreenwichLongitude(ReferencingUtilities.getPrimeMeridian(crs),
Units.DEGREE);
}
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CommonCRS.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CommonCRS.java
index 5f54b5e9d5..b8331ff590 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CommonCRS.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CommonCRS.java
@@ -69,6 +69,7 @@ import
org.apache.sis.referencing.factory.GeodeticAuthorityFactory;
import org.apache.sis.referencing.factory.UnavailableFactoryException;
import org.apache.sis.metadata.iso.citation.Citations;
import org.apache.sis.referencing.operation.provider.TransverseMercator;
+import org.apache.sis.referencing.privy.ReferencingUtilities;
import org.apache.sis.referencing.privy.Formulas;
import org.apache.sis.referencing.internal.Resources;
import org.apache.sis.system.SystemListener;
@@ -87,6 +88,7 @@ import org.apache.sis.measure.Units;
import static org.apache.sis.util.privy.Constants.SECONDS_PER_DAY;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
+import org.opengis.referencing.datum.DatumEnsemble;
import org.opengis.referencing.datum.RealizationMethod;
@@ -508,6 +510,9 @@ public enum CommonCRS {
/**
* Returns the {@code CommonCRS} enumeration value for the given datum, or
{@code null} if none.
+ *
+ * @param datum the datum to represent as an enumeration value, or
{@code null}.
+ * @return enumeration value for the given datum, or {@code null} if none.
*/
static CommonCRS forDatum(final GeodeticDatum datum) {
/*
@@ -828,7 +833,10 @@ public enum CommonCRS {
if (cs == null) {
cs = (SphericalCS)
StandardDefinitions.createCoordinateSystem(StandardDefinitions.SPHERICAL, true);
}
- object = new
DefaultGeocentricCRS(IdentifiedObjects.getProperties(base, exclude()),
base.getDatum(), base.getDatumEnsemble(), cs);
+ object = new
DefaultGeocentricCRS(IdentifiedObjects.getProperties(base, exclude()),
+ base.getDatum(),
+ base.getDatumEnsemble(),
+ cs);
cachedSpherical = object;
}
}
@@ -854,7 +862,7 @@ public enum CommonCRS {
* <tr><td>World Geodetic System 1984</td>
<td>{@link #WGS84}</td> <td>6326</td></tr>
* </table></blockquote>
*
- * @return the geodetic reference frame associated to this enum.
+ * @return the geodetic reference frame associated to this enum, or {@code
null} for a datum ensemble.
*
* @see #forDatum(CoordinateReferenceSystem)
* @see org.apache.sis.referencing.datum.DefaultGeodeticDatum
@@ -883,6 +891,17 @@ public enum CommonCRS {
return object;
}
+ /**
+ * Returns the datum ensemble associated to this geodetic object.
+ *
+ * @return the datum ensemble associated to this enum, or {@code null} if
none.
+ *
+ * @since 1.5
+ */
+ public DatumEnsemble<GeodeticDatum> datumEnsemble() {
+ return geographic().getDatumEnsemble();
+ }
+
/**
* Returns the ellipsoid associated to this geodetic object.
* The following table summarizes the ellipsoids known to this class,
@@ -997,8 +1016,13 @@ public enum CommonCRS {
if (object instanceof Ellipsoid) {
return (Ellipsoid) object;
}
- final GeodeticDatum datum = datum(object);
- return (datum != null) ? datum.getEllipsoid() : null;
+ if (object instanceof GeodeticDatum) {
+ return ((GeodeticDatum) object).getEllipsoid();
+ }
+ if (object instanceof CoordinateReferenceSystem) {
+ return
ReferencingUtilities.getEllipsoid((CoordinateReferenceSystem) object);
+ }
+ return null;
}
/**
@@ -1008,8 +1032,13 @@ public enum CommonCRS {
if (object instanceof PrimeMeridian) {
return (PrimeMeridian) object;
}
- final GeodeticDatum datum = datum(object);
- return (datum != null) ? datum.getPrimeMeridian() : null;
+ if (object instanceof GeodeticDatum) {
+ return ((GeodeticDatum) object).getPrimeMeridian();
+ }
+ if (object instanceof CoordinateReferenceSystem) {
+ return
ReferencingUtilities.getPrimeMeridian((CoordinateReferenceSystem) object);
+ }
+ return null;
}
/*
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
index d03a744c4f..e4ca8f5200 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
@@ -39,6 +39,9 @@ import org.apache.sis.util.resources.Errors;
import org.apache.sis.referencing.factory.GeodeticObjectFactory;
import static
org.apache.sis.referencing.privy.ReferencingUtilities.getPropertiesForModifiedCRS;
+// Specific to the geoapi-3.1 and geoapi-4.0 branches:
+import org.opengis.referencing.datum.DatumEnsemble;
+
/**
* Helper class for separating the ellipsoidal height from the horizontal part
of a CRS.
@@ -54,19 +57,25 @@ final class EllipsoidalHeightSeparator implements
AxisFilter {
*/
private final GeodeticDatum datum;
+ /**
+ * The datum ensemble of the <abbr>CRS</abbr> to separate, or {@code null}
if none.
+ */
+ private final DatumEnsemble<GeodeticDatum> ensemble;
+
/**
* Whether to extract the vertical component ({@code true}) or the
horizontal component ({@code false}).
*/
private final boolean vertical;
/**
- * Creates a new separator for a CRS having the given datum.
+ * Creates a new separator for a CRS having the given base.
*
- * @param datum the datum of the CRS to separate.
+ * @param baseCRS the CRS to separate, or the base CRS of the projected
CRS to separate.
* @param vertical whether to extract the vertical component ({@code
true}) or the horizontal component ({@code false}).
*/
- EllipsoidalHeightSeparator(final GeodeticDatum datum, final boolean
vertical) {
- this.datum = datum;
+ EllipsoidalHeightSeparator(final GeodeticCRS baseCRS, final boolean
vertical) {
+ this.datum = baseCRS.getDatum();
+ this.ensemble = baseCRS.getDatumEnsemble();
this.vertical = vertical;
}
@@ -100,7 +109,10 @@ final class EllipsoidalHeightSeparator implements
AxisFilter {
if (vertical) {
VerticalCRS component = CommonCRS.Vertical.ELLIPSOIDAL.crs();
if
(!Utilities.equalsIgnoreMetadata(component.getCoordinateSystem(), cs)) {
- component =
factory().createVerticalCRS(getPropertiesForModifiedCRS(component),
component.getDatum(), (VerticalCS) cs);
+ component =
factory().createVerticalCRS(getPropertiesForModifiedCRS(component),
+ component.getDatum(),
+
component.getDatumEnsemble(),
+ (VerticalCS) cs);
}
return component;
}
@@ -121,7 +133,7 @@ final class EllipsoidalHeightSeparator implements
AxisFilter {
final CommonCRS c = CommonCRS.forDatum(datum);
if (c != null) return c.normalizedGeographic();
}
- return
factory().createGeographicCRS(getPropertiesForModifiedCRS(crs), datum,
(EllipsoidalCS) cs);
+ return
factory().createGeographicCRS(getPropertiesForModifiedCRS(crs), datum,
ensemble, (EllipsoidalCS) cs);
}
/*
* In the projected CRS case, in addition of reducing the number of
dimensions in the CartesianCS,
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/MultiRegisterOperations.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/MultiRegisterOperations.java
index 2ddcd417b8..25b4aaede9 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/MultiRegisterOperations.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/MultiRegisterOperations.java
@@ -50,7 +50,8 @@ import org.apache.sis.util.iso.AbstractFactory;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
import org.opengis.referencing.RegisterOperations;
import org.opengis.referencing.crs.SingleCRS;
-import org.apache.sis.util.Utilities;
+import org.opengis.referencing.crs.CompoundCRS;
+import org.apache.sis.referencing.privy.ReferencingUtilities;
/**
@@ -341,10 +342,22 @@ public class MultiRegisterOperations extends
AbstractFactory implements Register
public boolean areMembersOfSameEnsemble(CoordinateReferenceSystem source,
CoordinateReferenceSystem target)
throws FactoryException
{
- return (source instanceof SingleCRS) && (target instanceof SingleCRS)
- && Utilities.equalsIgnoreMetadata(
- ((SingleCRS) source).getDatumEnsemble(),
- ((SingleCRS) target).getDatumEnsemble());
+ if (source instanceof SingleCRS && target instanceof SingleCRS) {
+ return ReferencingUtilities.areMembersOfSameEnsemble((SingleCRS)
source, (SingleCRS) target);
+ }
+ if (source instanceof CompoundCRS && target instanceof CompoundCRS) {
+ final List<SingleCRS> sources = ((CompoundCRS)
source).getSingleComponents();
+ final List<SingleCRS> targets = ((CompoundCRS)
target).getSingleComponents();
+ final int n = targets.size();
+ if (sources.size() == n) {
+ for (int i=0; i<n; i++) {
+ if
(!ReferencingUtilities.areMembersOfSameEnsemble(sources.get(i),
targets.get(i))) {
+ return false;
+ }
+ }
+ }
+ }
+ return false;
}
/**
diff --git
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
index 8aec007e7b..30f4513010 100644
---
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
@@ -219,7 +219,7 @@ public class AbstractCRS extends AbstractReferenceSystem
implements CoordinateRe
* @param ensemble collection of reference frames which for low
accuracy requirements may be considered to be
* insignificantly different from each other, or
{@code null} if there is no such ensemble.
* @throws NullPointerException if both arguments are null.
- * @throws IllegalArgumentException
+ * @throws IllegalArgumentException if the datum is not a member of the
ensemble.
*/
static <D extends Datum> void checkDatum(final D datum, final
DatumEnsemble<D> ensemble) {
if (ensemble == null) {
@@ -232,6 +232,8 @@ public class AbstractCRS extends AbstractReferenceSystem
implements CoordinateRe
}
throw new
IllegalArgumentException(Resources.format(Resources.Keys.NotAMemberOfDatumEnsemble_2,
IdentifiedObjects.getDisplayName(ensemble),
IdentifiedObjects.getDisplayName(datum)));
+ } else {
+ ArgumentChecks.ensureNonEmpty("ensemble", ensemble.getMembers());
}
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java
index a68014ca05..40ff61cca7 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java
@@ -18,7 +18,11 @@ package org.apache.sis.referencing.crs;
import java.util.Map;
import java.util.Arrays;
+import java.util.Iterator;
import jakarta.xml.bind.annotation.XmlTransient;
+import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.datum.Ellipsoid;
+import org.opengis.referencing.datum.PrimeMeridian;
import org.opengis.referencing.datum.GeodeticDatum;
import org.opengis.referencing.crs.GeodeticCRS;
import org.opengis.referencing.crs.GeographicCRS;
@@ -26,10 +30,13 @@ import org.opengis.referencing.cs.EllipsoidalCS;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.referencing.GeodeticException;
+import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.referencing.ImmutableIdentifier;
import org.apache.sis.referencing.AbstractReferenceSystem;
import org.apache.sis.referencing.cs.AxesConvention;
import org.apache.sis.referencing.cs.AbstractCS;
+import org.apache.sis.util.resources.Errors;
import org.apache.sis.io.wkt.Formatter;
import org.apache.sis.measure.Longitude;
import static org.apache.sis.util.privy.Constants.CRS;
@@ -196,6 +203,7 @@ public class DefaultGeographicCRS extends
DefaultGeodeticCRS implements Geograph
if (!(cs instanceof EllipsoidalCS)) {
throw illegalCoordinateSystemType(cs);
}
+ checkDimension(2, 3, cs);
}
/**
@@ -283,6 +291,68 @@ public class DefaultGeographicCRS extends
DefaultGeodeticCRS implements Geograph
return (EllipsoidalCS) super.getCoordinateSystem();
}
+ /**
+ * Returns the ellipsoid which is indirectly (through a datum) associated
to this <abbr>CRS</abbr>.
+ * If the {@linkplain #getDatum() datum} is non-null, then this method
returns the datum ellipsoid.
+ * Otherwise, if all members of the {@linkplain #getDatumEnsemble() datum
ensemble} use the same ellipsoid,
+ * then this method returns that ellipsoid.
+ *
+ * @return the ellipsoid indirectly associated to this <abbr>CRS</abbr>.
+ * @throws NullPointerException if an ellipsoid, which are mandatory in
the context of geographic <abbr>CRS</abbr>, is null.
+ * @throws GeodeticException if the ellipsoid is not the same for all
members of the datum ensemble.
+ *
+ * @since 1.5
+ */
+ public Ellipsoid getEllipsoid() {
+ final GeodeticDatum datum = super.getDatum();
+ if (datum != null) {
+ return datum.getEllipsoid(); // Has precedence regardless
the value.
+ }
+ // If the datum is null, then the datum ensemble must be non-null.
+ final Iterator<GeodeticDatum> it = ensemble.getMembers().iterator();
+ final Ellipsoid ellipsoid = it.next().getEllipsoid(); // Mandatory
+ while (it.hasNext()) {
+ checkDatumConsistency(ellipsoid, it.next().getEllipsoid());
+ }
+ return ellipsoid;
+ }
+
+ /**
+ * Returns the prime meridian which is indirectly (through a datum)
associated to this <abbr>CRS</abbr>.
+ * If the {@linkplain #getDatum() datum} is non-null, then this method
returns the datum prime meridian.
+ * Otherwise, if all members of the {@linkplain #getDatumEnsemble() datum
ensemble} use the same prime meridian,
+ * then this method returns that meridian.
+ *
+ * @return the prime meridian indirectly associated to this
<abbr>CRS</abbr>.
+ * @throws NullPointerException if a prime meridian, which are mandatory,
is null.
+ * @throws GeodeticException if the prime meridian is not the same for all
members of the datum ensemble.
+ *
+ * @since 1.5
+ */
+ public PrimeMeridian getPrimeMeridian() {
+ final GeodeticDatum datum = super.getDatum();
+ if (datum != null) {
+ return datum.getPrimeMeridian(); // Has precedence regardless
the value.
+ }
+ // If the datum is null, then the datum ensemble must be non-null.
+ final Iterator<GeodeticDatum> it = ensemble.getMembers().iterator();
+ final PrimeMeridian pm = it.next().getPrimeMeridian(); // Mandatory
+ while (it.hasNext()) {
+ checkDatumConsistency(pm, it.next().getPrimeMeridian());
+ }
+ return pm;
+ }
+
+ /**
+ * Ensures that the ellipsoid or prime meridian has the same value in all
members of a datum ensemble.
+ */
+ private static void checkDatumConsistency(final IdentifiedObject expected,
final IdentifiedObject actual) {
+ if (!expected.equals(actual)) {
+ throw new
GeodeticException(Errors.format(Errors.Keys.NonUniformValue_2,
+ IdentifiedObjects.getDisplayName(expected),
IdentifiedObjects.getDisplayName(actual)));
+ }
+ }
+
/**
* {@inheritDoc}
*
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
index 8080e6010c..b3fbfe6256 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.referencing.crs;
+import java.util.Iterator;
import java.util.Map;
import java.util.Date;
import java.time.Instant;
@@ -33,6 +34,7 @@ import javax.measure.quantity.Time;
import org.opengis.referencing.cs.TimeCS;
import org.opengis.referencing.crs.TemporalCRS;
import org.opengis.referencing.datum.TemporalDatum;
+import org.apache.sis.referencing.GeodeticException;
import org.apache.sis.referencing.AbstractReferenceSystem;
import org.apache.sis.referencing.cs.AxesConvention;
import org.apache.sis.referencing.cs.AbstractCS;
@@ -41,6 +43,7 @@ import org.apache.sis.metadata.privy.ImplementationHelper;
import org.apache.sis.io.wkt.Formatter;
import org.apache.sis.measure.Units;
import org.apache.sis.math.Fraction;
+import org.apache.sis.util.resources.Errors;
import static org.apache.sis.util.privy.Constants.NANOS_PER_SECOND;
import static org.apache.sis.util.privy.Constants.MILLIS_PER_SECOND;
@@ -118,9 +121,9 @@ public class DefaultTemporalCRS extends AbstractCRS
implements TemporalCRS {
private transient UnitConverter toSeconds;
/**
- * The {@linkplain TemporalDatum#getOrigin origin} in seconds since
January 1st, 1970.
- * This field could be implicit in the {@link #toSeconds} converter, but
we still handle
- * it explicitly in order to use integer arithmetic.
+ * The {@linkplain #getOrigin origin} in seconds since January 1st, 1970.
+ * This field could be implicit in the {@link #toSeconds} converter,
+ * but we still handle it explicitly in order to use integer arithmetic.
*/
private transient long origin;
@@ -258,7 +261,7 @@ public class DefaultTemporalCRS extends AbstractCRS
implements TemporalCRS {
*/
private void initializeConverter() {
toSeconds = getUnit().getConverterTo(Units.SECOND);
- final Temporal t = datum.getOrigin();
+ final Temporal t = getOrigin();
origin = t.getLong(ChronoField.INSTANT_SECONDS);
int r = t.get(ChronoField.NANO_OF_SECOND);
if (r != 0) {
@@ -350,6 +353,34 @@ public class DefaultTemporalCRS extends AbstractCRS
implements TemporalCRS {
return
super.getCoordinateSystem().getAxis(0).getUnit().asType(Time.class);
}
+ /**
+ * Returns the temporal origin which is indirectly (through a datum)
associated to this <abbr>CRS</abbr>.
+ * If the {@linkplain #getDatum() datum} is non-null, then this method
returns the datum origin.
+ * Otherwise, if all members of the {@linkplain #getDatumEnsemble() datum
ensemble} use the same origin,
+ * then this method returns that origin.
+ *
+ * @return the origin indirectly associated to this <abbr>CRS</abbr>.
+ * @throws NullPointerException if an origin, which are mandatory, is null.
+ * @throws GeodeticException if the origin is not the same for all members
of the datum ensemble.
+ *
+ * @since 1.5
+ */
+ public final Temporal getOrigin() { // Must be final because invoked
at construction time.
+ if (datum != null) {
+ return datum.getOrigin(); // Has precedence regardless the
value.
+ }
+ // If the datum is null, then the datum ensemble must be non-null.
+ final Iterator<TemporalDatum> it = ensemble.getMembers().iterator();
+ final Temporal origin = it.next().getOrigin();
+ while (it.hasNext()) {
+ final Temporal actual = it.next().getOrigin();
+ if (!origin.equals(actual)) {
+ throw new
GeodeticException(Errors.format(Errors.Keys.NonUniformValue_2, origin, actual));
+ }
+ }
+ return origin;
+ }
+
/**
* {@inheritDoc}
*
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java
index 65a7624e86..c476ed9e01 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java
@@ -19,12 +19,11 @@ package org.apache.sis.referencing.crs;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.GeneralParameterDescriptor;
-import org.opengis.referencing.datum.Datum;
-import org.opengis.referencing.datum.GeodeticDatum;
import org.opengis.referencing.datum.Ellipsoid;
import org.opengis.referencing.operation.Conversion;
import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.referencing.operation.DefaultOperationMethod;
+import org.apache.sis.referencing.privy.ReferencingUtilities;
import org.apache.sis.referencing.privy.WKTKeywords;
import org.apache.sis.referencing.privy.WKTUtilities;
import org.apache.sis.util.privy.Constants;
@@ -61,8 +60,7 @@ final class ExplicitParameters extends FormattableObject {
*/
ExplicitParameters(final AbstractDerivedCRS crs, final String keyword) {
conversion = crs.getConversionFromBase();
- final Datum datum = crs.getDatum();
- ellipsoid = (datum instanceof GeodeticDatum) ? ((GeodeticDatum)
datum).getEllipsoid() : null;
+ ellipsoid = ReferencingUtilities.getEllipsoid(crs);
this.keyword = keyword;
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
index 6ed76bff3f..1709cc3110 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
@@ -438,7 +438,7 @@ public class Resources extends IndexedResourceBundle {
/**
* The given “{0}” description does not conform to the “{1}”
authoritative description.
* Differences are found in {2,choice,0#conversion method|1#conversion
description|2#coordinate
- * system|3#datum|4#prime meridian|5#CRS}.
+ * system|3#datum|4#prime meridian|5#ellipsoid|6#CRS}.
*/
public static final short NonConformCRS_3 = 73;
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.properties
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.properties
index 41f456f7cf..0f3b751097 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.properties
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.properties
@@ -42,7 +42,7 @@ MisnamedParameter_1 = Despite its name, this
parameter is effectiv
NotFormalProjectionParameter_1 = This parameter borrowed from the
\u201c{0}\u201d projection is not formally a parameter of this projection.
NonConformAxes_2 = The coordinate system axes in the given
\u201c{0}\u201d description do not conform to the expected axes according
\u201c{1}\u201d authoritative description.
NonConformCRS_3 = The given \u201c{0}\u201d description does
not conform to the \u201c{1}\u201d authoritative description. \
- Differences are found in
{2,choice,0#conversion method|1#conversion description|2#coordinate
system|3#datum|4#prime meridian|5#CRS}.
+ Differences are found in
{2,choice,0#conversion method|1#conversion description|2#coordinate
system|3#datum|4#prime meridian|5#ellipsoid|6#CRS}.
RestrictedToPoleLatitudes = The only valid entries are \u00b190\u00b0
or equivalent in alternative angle units.
#
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources_fr.properties
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources_fr.properties
index 163eac2614..245a08e502 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources_fr.properties
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources_fr.properties
@@ -47,7 +47,7 @@ MisnamedParameter_1 = Malgr\u00e9 son nom, ce
param\u00e8tre produ
NotFormalProjectionParameter_1 = Ce param\u00e8tre emprunt\u00e9 \u00e0 la
projection \u00ab\u202f{0}\u202f\u00bb n\u2019est pas formellement un
param\u00e8tre de cette projection.
NonConformAxes_2 = Les axes du syst\u00e8me de
coordonn\u00e9es d\u00e9finis dans \u00ab\u202f{0}\u202f\u00bb ne sont pas
conformes aux axes attendus d\u2019apr\u00e8s la description officielle de
\u00ab\u202f{1}\u202f\u00bb.
NonConformCRS_3 = La description donn\u00e9e pour
\u00ab\u202f{0}\u202f\u00bb n\u2019est pas conforme \u00e0 la description
officielle de \u00ab\u202f{1}\u202f\u00bb. \
- Des diff\u00e9rences ont \u00e9t\u00e9
trouv\u00e9es dans {2,choice,0#la m\u00e9thode de conversion|1#la description
de la conversion|2#le syst\u00e8me de coordonn\u00e9es|3#le
r\u00e9f\u00e9rentiel|4#le m\u00e9ridien d\u2019origine|5#le CRS}.
+ Des diff\u00e9rences ont \u00e9t\u00e9
trouv\u00e9es dans {2,choice,0#la m\u00e9thode de conversion|1#la description
de la conversion|2#le syst\u00e8me de coordonn\u00e9es|3#le
r\u00e9f\u00e9rentiel|4#le m\u00e9ridien
d\u2019origine|5#l\u2019ellipso\u00efde|6#le CRS}.
RestrictedToPoleLatitudes = Les seules valeurs valides sont
\u00b190\u00b0 ou \u00e9quivalent dans d\u2019autres unit\u00e9s.
#
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
index af3c6d7ae3..5b6030e34e 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
@@ -23,6 +23,7 @@ import jakarta.xml.bind.annotation.XmlRootElement;
import javax.measure.IncommensurableException;
import org.opengis.util.FactoryException;
import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.operation.Conversion;
import org.opengis.referencing.operation.OperationMethod;
import org.opengis.referencing.operation.MathTransform;
@@ -30,7 +31,6 @@ import org.opengis.referencing.operation.MathTransformFactory;
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.crs.SingleCRS;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.datum.Datum;
import org.apache.sis.referencing.cs.CoordinateSystems;
import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
import org.apache.sis.referencing.internal.ParameterizedTransformBuilder;
@@ -409,8 +409,17 @@ public class DefaultConversion extends
AbstractSingleOperation implements Conver
final CoordinateReferenceSystem actual)
{
if ((expected instanceof SingleCRS) && (actual instanceof SingleCRS)) {
- final Datum datum = ((SingleCRS) expected).getDatum();
- if (datum != null && !Utilities.equalsIgnoreMetadata(datum,
((SingleCRS) actual).getDatum())) {
+ final var crs1 = (SingleCRS) expected;
+ final var crs2 = (SingleCRS) actual;
+ IdentifiedObject datum = crs1.getDatum();
+ IdentifiedObject other;
+ if (datum != null) {
+ other = crs2.getDatum();
+ } else {
+ datum = crs1.getDatumEnsemble();
+ other = crs2.getDatumEnsemble();
+ }
+ if (datum != null && other != null &&
!Utilities.equalsIgnoreMetadata(datum, other)) {
throw new MismatchedDatumException(Resources.format(
Resources.Keys.IncompatibleDatum_2, datum.getName(),
param));
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
index 3fcaec2cd3..1573b2ef58 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
@@ -30,24 +30,24 @@ import
org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.SingleCRS;
import org.opengis.referencing.crs.CRSFactory;
import org.opengis.referencing.cs.CSFactory;
-import org.opengis.referencing.datum.Datum;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.referencing.AbstractIdentifiedObject;
+import org.apache.sis.referencing.factory.GeodeticObjectFactory;
+import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
+import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
+import
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
import org.apache.sis.referencing.internal.Resources;
import org.apache.sis.referencing.internal.MergedProperties;
import org.apache.sis.referencing.internal.ParameterizedTransformBuilder;
import org.apache.sis.referencing.privy.CoordinateOperations;
import org.apache.sis.referencing.privy.ReferencingFactoryContainer;
+import org.apache.sis.referencing.privy.ReferencingUtilities;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.Classes;
import org.apache.sis.util.Utilities;
import org.apache.sis.util.Debug;
import org.apache.sis.util.privy.Constants;
-import org.apache.sis.referencing.factory.GeodeticObjectFactory;
-import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
-import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
-import
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
import org.apache.sis.util.collection.WeakHashSet;
import org.apache.sis.util.collection.Containers;
import org.apache.sis.util.collection.Cache;
@@ -409,13 +409,13 @@ public class DefaultCoordinateOperationFactory extends
AbstractFactory implement
{
List<SingleCRS> components = CRS.getSingleComponents(sourceCRS);
int n = components.size(); // Number of remaining
datum from sourceCRS to verify.
- final Datum[] datum = new Datum[n];
+ final IdentifiedObject[] datum = new IdentifiedObject[n];
for (int i=0; i<n; i++) {
- datum[i] = components.get(i).getDatum();
+ datum[i] =
ReferencingUtilities.getDatumOrEnsemble(components.get(i));
}
components = CRS.getSingleComponents(targetCRS);
next: for (int i=components.size(); --i >= 0;) {
- final Datum d = components.get(i).getDatum();
+ final IdentifiedObject d =
ReferencingUtilities.getDatumOrEnsemble(components.get(i));
for (int j=n; --j >= 0;) {
if (Utilities.equalsIgnoreMetadata(d, datum[j])) {
System.arraycopy(datum, j+1, datum, j, --n - j); //
Remove the datum from the list.
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
index e64ab32b0d..84062a6b64 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
@@ -45,6 +45,7 @@ import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.privy.Constants;
import org.apache.sis.util.iso.AbstractFactory;
import org.apache.sis.util.collection.WeakHashSet;
+import org.apache.sis.referencing.privy.ReferencingUtilities;
import org.apache.sis.referencing.privy.CoordinateOperations;
import org.apache.sis.referencing.operation.DefaultOperationMethod;
import org.apache.sis.referencing.internal.ParameterizedTransformBuilder;
@@ -552,7 +553,7 @@ public class DefaultMathTransformFactory extends
AbstractFactory implements Math
public void setSource(final GeodeticCRS crs) {
if (crs != null) {
sourceCS = crs.getCoordinateSystem();
- sourceEllipsoid = crs.getDatum().getEllipsoid();
+ sourceEllipsoid = ReferencingUtilities.getEllipsoid(crs);
} else {
sourceCS = null;
sourceEllipsoid = null;
@@ -592,7 +593,7 @@ public class DefaultMathTransformFactory extends
AbstractFactory implements Math
public void setTarget(final GeodeticCRS crs) {
if (crs != null) {
targetCS = crs.getCoordinateSystem();
- targetEllipsoid = crs.getDatum().getEllipsoid();
+ targetEllipsoid = ReferencingUtilities.getEllipsoid(crs);
} else {
targetCS = null;
targetEllipsoid = null;
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/DefinitionVerifier.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/DefinitionVerifier.java
index 866957855b..2bcd77dd12 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/DefinitionVerifier.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/DefinitionVerifier.java
@@ -24,8 +24,6 @@ import org.opengis.util.FactoryException;
import org.opengis.metadata.citation.Citation;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.NoSuchAuthorityCodeException;
-import org.opengis.referencing.datum.Datum;
-import org.opengis.referencing.datum.GeodeticDatum;
import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.SingleCRS;
@@ -301,13 +299,13 @@ public final class DefinitionVerifier {
* Indicates in which part of CRS description a difference has been found.
Numerical values must match the number
* in the {@code {choice}} instruction in the message associated to {@link
Resources.Keys#NonConformCRS_3}.
*/
- private static final int METHOD=0, CONVERSION=1, CS=2, DATUM=3,
PRIME_MERIDIAN=4, OTHER=5;
+ private static final int METHOD=0, CONVERSION=1, CS=2, DATUM=3,
PRIME_MERIDIAN=4, ELLIPSOID=5, OTHER=6;
/**
* Returns a code indicating in which part the two given CRS differ. The
given iterators usually iterate over
* exactly one element, but may iterate over more elements if the CRS were
instance of {@code CompoundCRS}.
* The returned value is one of {@link #METHOD}, {@link #CONVERSION},
{@link #CS}, {@link #DATUM},
- * {@link #PRIME_MERIDIAN} or {@link #OTHER} constants.
+ * {@link #PRIME_MERIDIAN}, {@link #ELLIPSOID} or {@link #OTHER} constants.
*/
private static int diffCode(final Iterator<SingleCRS> authoritative, final
Iterator<SingleCRS> given) {
while (authoritative.hasNext() && given.hasNext()) {
@@ -324,15 +322,17 @@ public final class DefinitionVerifier {
if (!Utilities.equalsApproximately(crsA.getCoordinateSystem(),
crsG.getCoordinateSystem())) {
return CS;
}
- final Datum datumA = crsA.getDatum();
- final Datum datumG = crsG.getDatum();
- if (!Utilities.equalsApproximately(datumA, datumG)) {
- if ((datumA instanceof GeodeticDatum) && (datumG
instanceof GeodeticDatum) &&
- !Utilities.equalsApproximately(((GeodeticDatum)
datumA).getPrimeMeridian(),
- ((GeodeticDatum)
datumG).getPrimeMeridian()))
- {
- return PRIME_MERIDIAN;
- }
+ if
(!Utilities.equalsApproximately(ReferencingUtilities.getEllipsoid(crsA),
+
ReferencingUtilities.getEllipsoid(crsG)))
+ {
+ return ELLIPSOID;
+ }
+ if
(!Utilities.equalsApproximately(ReferencingUtilities.getPrimeMeridian(crsA),
+
ReferencingUtilities.getPrimeMeridian(crsG)))
+ {
+ return PRIME_MERIDIAN;
+ }
+ if (!Utilities.equalsApproximately(crsA.getDatum(),
crsG.getDatum())) {
return DATUM;
}
break;
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/EllipsoidalHeightCombiner.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/EllipsoidalHeightCombiner.java
index c8791dbd93..27deaab23a 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/EllipsoidalHeightCombiner.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/EllipsoidalHeightCombiner.java
@@ -116,14 +116,14 @@ public final class EllipsoidalHeightCombiner {
final VerticalDatum datum = ((VerticalCRS)
vertical).getDatum();
if (ReferencingUtilities.isEllipsoidalHeight(datum)) {
int axisPosition = 0;
- CoordinateSystem cs = null;
+ CoordinateSystem cs2D;
CoordinateReferenceSystem crs = null;
- if (i == 0 || (cs = getCsIfHorizontal2D(crs = components[i
- 1])) == null) {
+ if (i == 0 || (cs2D = getCsIfHorizontal2D(crs =
components[i - 1])) == null) {
/*
* GeographicCRS are normally before VerticalCRS. But
Apache SIS is tolerant to the
* opposite order (note however that such ordering is
illegal according ISO 19162).
*/
- if (i+1 >= components.length || (cs =
getCsIfHorizontal2D(crs = components[i + 1])) == null) {
+ if (i+1 >= components.length || (cs2D =
getCsIfHorizontal2D(crs = components[i + 1])) == null) {
continue;
}
axisPosition = 1;
@@ -136,15 +136,17 @@ public final class EllipsoidalHeightCombiner {
* implementation recycles the properties of the existing
two-dimensional CRS.
*/
final CoordinateSystemAxis[] axes = new
CoordinateSystemAxis[3];
- axes[axisPosition++ ] = cs.getAxis(0);
- axes[axisPosition++ ] = cs.getAxis(1);
+ axes[axisPosition++ ] = cs2D.getAxis(0);
+ axes[axisPosition++ ] = cs2D.getAxis(1);
axes[axisPosition %= 3] =
vertical.getCoordinateSystem().getAxis(0);
- final Map<String,?> csProps =
IdentifiedObjects.getProperties(cs, CoordinateSystem.IDENTIFIERS_KEY);
+ final Map<String,?> csProps =
IdentifiedObjects.getProperties(cs2D, CoordinateSystem.IDENTIFIERS_KEY);
final Map<String,?> crsProps = (components.length == 2) ?
properties
:
IdentifiedObjects.getProperties(crs, CoordinateReferenceSystem.IDENTIFIERS_KEY);
if (crs instanceof GeodeticCRS) {
- cs = factories.getCSFactory()
.createEllipsoidalCS(csProps, axes[0], axes[1], axes[2]);
- crs =
factories.getCRSFactory().createGeographicCRS(crsProps, ((GeodeticCRS)
crs).getDatum(), (EllipsoidalCS) cs);
+ final var geod = (GeodeticCRS) crs;
+ final EllipsoidalCS cs3D;
+ cs3D = factories.getCSFactory()
.createEllipsoidalCS(csProps, axes[0], axes[1], axes[2]);
+ crs =
factories.getCRSFactory().createGeographicCRS(crsProps, geod.getDatum(),
geod.getDatumEnsemble(), cs3D);
} else {
final ProjectedCRS proj = (ProjectedCRS) crs;
GeodeticCRS base = proj.getBaseCRS();
@@ -161,8 +163,10 @@ public final class EllipsoidalHeightCombiner {
fromBase =
factories.getCoordinateOperationFactory().createDefiningConversion(
IdentifiedObjects.getProperties(fromBase),
fromBase.getMethod(),
fromBase.getParameterValues());
- cs = factories.getCSFactory()
.createCartesianCS(csProps, axes[0], axes[1], axes[2]);
- crs =
factories.getCRSFactory().createProjectedCRS(crsProps, base, fromBase,
(CartesianCS) cs);
+
+ final CartesianCS cs3D;
+ cs3D = factories.getCSFactory()
.createCartesianCS(csProps, axes[0], axes[1], axes[2]);
+ crs =
factories.getCRSFactory().createProjectedCRS(crsProps, base, fromBase, cs3D);
}
/*
* Remove the VerticalCRS and store the three-dimensional
GeographicCRS in place of the previous
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/GeodeticObjectBuilder.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/GeodeticObjectBuilder.java
index 0c84f2a1eb..1c30eea584 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/GeodeticObjectBuilder.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/GeodeticObjectBuilder.java
@@ -63,6 +63,7 @@ import org.apache.sis.parameter.Parameters;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
import org.opengis.referencing.ObjectDomain;
+import org.opengis.referencing.datum.DatumEnsemble;
import org.opengis.referencing.operation.MathTransform;
// Specific to the geoapi-4.0 branch:
@@ -86,6 +87,11 @@ public class GeodeticObjectBuilder extends
Builder<GeodeticObjectBuilder> {
*/
private GeodeticDatum datum;
+ /**
+ * The datum ensemble, or {@code null} if none.
+ */
+ private DatumEnsemble<GeodeticDatum> ensemble;
+
/**
* The name of the conversion to use for creating a {@code ProjectedCRS}
or {@code DerivedCRS}.
* This name is for information purpose; its value does not impact the
numerical results of coordinate operations.
@@ -361,6 +367,7 @@ public class GeodeticObjectBuilder extends
Builder<GeodeticObjectBuilder> {
method = c.getMethod();
parameters = c.getParameterValues();
datum = crs.getDatum();
+ ensemble = crs.getDatumEnsemble();
properties.putAll(IdentifiedObjects.getProperties(crs,
ProjectedCRS.IDENTIFIERS_KEY));
return this;
}
@@ -509,8 +516,9 @@ public class GeodeticObjectBuilder extends
Builder<GeodeticObjectBuilder> {
*/
public ProjectedCRS createProjectedCRS() throws FactoryException {
GeographicCRS crs = getBaseCRS();
- if (datum != null) {
- crs = factories.getCRSFactory().createGeographicCRS(name(datum),
datum, crs.getCoordinateSystem());
+ if (datum != null || ensemble != null) {
+ crs = factories.getCRSFactory().createGeographicCRS(
+ name(datum != null ? datum : ensemble), datum, ensemble,
crs.getCoordinateSystem());
}
return createProjectedCRS(crs, factories.getStandardProjectedCS());
}
@@ -532,8 +540,12 @@ public class GeodeticObjectBuilder extends
Builder<GeodeticObjectBuilder> {
*/
public GeographicCRS createGeographicCRS() throws FactoryException {
final GeographicCRS crs = getBaseCRS();
- if (datum != null) properties.putIfAbsent(GeographicCRS.NAME_KEY,
datum.getName());
- return factories.getCRSFactory().createGeographicCRS(properties,
datum, crs.getCoordinateSystem());
+ IdentifiedObject id = datum;
+ if (id == null) id = ensemble;
+ if (id != null) {
+ properties.putIfAbsent(GeographicCRS.NAME_KEY, id.getName());
+ }
+ return factories.getCRSFactory().createGeographicCRS(properties,
datum, ensemble, crs.getCoordinateSystem());
}
/**
@@ -551,19 +563,19 @@ public class GeodeticObjectBuilder extends
Builder<GeodeticObjectBuilder> {
* This not only saves a little bit of memory, but also provides
better names.
*/
TimeCS cs = null;
- TemporalDatum datum = null;
+ TemporalDatum td = null;
for (final CommonCRS.Temporal c : CommonCRS.Temporal.values()) {
- if (datum == null) {
+ if (td == null) {
final TemporalDatum candidate = c.datum();
if (origin.equals(candidate.getOrigin())) {
- datum = candidate;
+ td = candidate;
}
}
if (cs == null) {
final TemporalCRS crs = c.crs();
final TimeCS candidate = crs.getCoordinateSystem();
if (unit.equals(candidate.getAxis(0).getUnit())) {
- if (datum == candidate && properties.isEmpty()) {
+ if (td == candidate && properties.isEmpty()) {
return crs;
}
cs = candidate;
@@ -586,15 +598,15 @@ public class GeodeticObjectBuilder extends
Builder<GeodeticObjectBuilder> {
if (properties.get(TemporalCRS.NAME_KEY) == null) {
properties.putAll(name(cs));
}
- if (datum == null) {
+ if (td == null) {
final Object remarks =
properties.remove(TemporalCRS.REMARKS_KEY);
final Object identifier =
properties.remove(TemporalCRS.IDENTIFIERS_KEY);
- datum =
factories.getDatumFactory().createTemporalDatum(properties, origin);
+ td =
factories.getDatumFactory().createTemporalDatum(properties, origin);
properties.put(TemporalCRS.IDENTIFIERS_KEY, identifier);
properties.put(TemporalCRS.REMARKS_KEY, remarks);
- properties.put(TemporalCRS.NAME_KEY, datum.getName()); //
Share the Identifier instance.
+ properties.put(TemporalCRS.NAME_KEY, td.getName()); //
Share the Identifier instance.
}
- return factories.getCRSFactory().createTemporalCRS(properties,
datum, cs);
+ return factories.getCRSFactory().createTemporalCRS(properties, td,
cs);
} finally {
onCreate(true);
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
index 48fae67a4a..07aa3e2bbd 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
@@ -18,6 +18,7 @@ package org.apache.sis.referencing.privy;
import java.util.Map;
import java.util.HashMap;
+import java.util.function.Function;
import javax.measure.Unit;
import javax.measure.quantity.Angle;
import org.opengis.annotation.UML;
@@ -52,6 +53,9 @@ import org.apache.sis.referencing.cs.DefaultEllipsoidalCS;
import org.apache.sis.referencing.internal.VerticalDatumTypes;
import
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
+// Specific to the geoapi-3.1 and geoapi-4.0 branches:
+import org.opengis.referencing.datum.DatumEnsemble;
+
/**
* A set of static methods working on GeoAPI referencing objects.
@@ -202,6 +206,38 @@ public final class ReferencingUtilities extends Static {
return (types.length != 0) ? types[0] : type;
}
+ /**
+ * Returns whether the given <abbr>CRS</abbr> use the same datum or the
same datum ensemble.
+ *
+ * @param crs1 the first <abbr>CRS</abbr>.
+ * @param crs2 the second <abbr>CRS</abbr>.
+ * @return whether the two reference systems use the same datum or the
same datum ensemble.
+ */
+ public static boolean areMembersOfSameEnsemble(final SingleCRS crs1, final
SingleCRS crs2) {
+ IdentifiedObject d1 = crs1.getDatum();
+ IdentifiedObject d2 = crs2.getDatum();
+ if (d1 == null && d2 == null) {
+ d1 = crs1.getDatumEnsemble();
+ d2 = crs2.getDatumEnsemble();
+ if (d1 == null && d2 == null) {
+ return false;
+ }
+ }
+ return Utilities.equalsIgnoreMetadata(d1, d2);
+ }
+
+ /**
+ * Returns the datum of the given <abbr>CRS</abbr> if presents, or the
datum ensemble otherwise.
+ *
+ * @param crs the <abbr>CRS</abbr> from which to get the datum or
ensemble, or {@code null}.
+ * @return the datum if present, or the datum ensemble otherwise.
+ */
+ public static IdentifiedObject getDatumOrEnsemble(final SingleCRS crs) {
+ if (crs == null) return null;
+ final Datum datum = crs.getDatum();
+ return (datum != null) ? datum : crs.getDatumEnsemble();
+ }
+
/**
* Returns {@code true} if the type of the given datum is ellipsoidal. A
vertical datum is not allowed
* to be ellipsoidal according ISO 19111, but Apache SIS relaxes this
restriction in some limited cases,
@@ -228,66 +264,80 @@ public final class ReferencingUtilities extends Static {
* <ul>
* <li>If the given CRS 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 CRS is an instance of {@link CompoundCRS},
then this method
+ * <li>Otherwise, if the given CRS is associated to a {@link
DatumEnsemble} and all members of the
+ * ensemble have equal (ignoring metadata) ellipsoid, then returns
that ellipsoid.</li>
+ * <li>Otherwise, if the given CRS is an instance of {@link
CompoundCRS}, then this method
* invokes itself recursively for each component until a geodetic
reference frame is found.</li>
- * <li>Otherwise this method returns {@code null}.</li>
+ * <li>Otherwise, this method returns {@code null}.</li>
* </ul>
*
- * Note that this method does not check if there is more than one
ellipsoid (it should never be the case).
+ * Note that this method does not check if a compound <abbr>CRS</abbr>
contains more than one ellipsoid
+ * (it should never be the case). Note also that this method may return
{@code null} even if the CRS is
+ * geodetic.
*
* @param crs the coordinate reference system for which to get the
ellipsoid.
* @return the ellipsoid, or {@code null} if none.
*/
public static Ellipsoid getEllipsoid(final CoordinateReferenceSystem crs) {
- if (crs != null) {
- if (crs instanceof SingleCRS) {
- final Datum datum = ((SingleCRS) crs).getDatum();
- if (datum instanceof GeodeticDatum) {
- final Ellipsoid e = ((GeodeticDatum) datum).getEllipsoid();
- if (e != null) return e;
- }
- }
- if (crs instanceof CompoundCRS) {
- for (final CoordinateReferenceSystem c : ((CompoundCRS)
crs).getComponents()) {
- final Ellipsoid e = getEllipsoid(c);
- if (e != null) return e;
- }
- }
- }
- return null;
+ return getGeodeticProperty(crs, GeodeticDatum::getEllipsoid);
}
/**
- * Returns the ellipsoid used by the specified coordinate reference
system, provided that the two first dimensions
- * use an instance of {@link GeographicCRS}. Otherwise (i.e. if the two
first dimensions are not geographic),
- * returns {@code null}.
+ * Returns the prime meridian used by the given coordinate reference
system, or {@code null} if none.
+ * This method applies the same rules as {@link
#getEllipsoid(CoordinateReferenceSystem)}.
*
- * <p>This method excludes geocentric CRS on intent. Some callers needs
this exclusion as a way to identify
- * which CRS in a Geographic/Geocentric conversion is the geographic one.
Another point of view is to said
- * that if this method returns a non-null value, then the coordinates are
expected to be either two-dimensional
- * or three-dimensional with an ellipsoidal height.</p>
+ * @param crs the coordinate reference system for which to get the prime
meridian.
+ * @return the prime meridian, or {@code null} if none.
+ */
+ public static PrimeMeridian getPrimeMeridian(final
CoordinateReferenceSystem crs) {
+ return getGeodeticProperty(crs, GeodeticDatum::getPrimeMeridian);
+ }
+
+ /**
+ * Implementation of {@code getEllipsoid(CRS)} and {@code
getPrimeMeridian(CRS)}.
*
- * @param crs the coordinate reference system for which to get the
ellipsoid.
- * @return the ellipsoid in the given CRS, or {@code null} if none.
+ * @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 {@code null} if none.
*/
- public static Ellipsoid
getEllipsoidOfGeographicCRS(CoordinateReferenceSystem crs) {
- while (!(crs instanceof GeodeticCRS)) {
- if (crs instanceof CompoundCRS) {
- crs = ((CompoundCRS) crs).getComponents().get(0);
- } else {
- return null;
+ private static <P> P getGeodeticProperty(final CoordinateReferenceSystem
crs, final Function<GeodeticDatum, P> getter) {
+single: if (crs instanceof SingleCRS) {
+ final SingleCRS scrs = (SingleCRS) crs;
+ final Datum datum = scrs.getDatum();
+ if (datum instanceof GeodeticDatum) {
+ P property = getter.apply((GeodeticDatum) datum);
+ if (property != null) {
+ return property;
+ }
+ }
+ final DatumEnsemble<?> ensemble = scrs.getDatumEnsemble();
+ 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 common;
}
}
- /*
- * In order to determine if the CRS is geographic, checking the
CoordinateSystem type is more reliable
- * then checking if the CRS implements the GeographicCRS interface.
This is because the GeographicCRS
- * interface is GeoAPI-specific, so a CRS may be OGC-compliant without
implementing that interface.
- */
- if (crs.getCoordinateSystem() instanceof EllipsoidalCS) {
- return ((GeodeticCRS) crs).getDatum().getEllipsoid();
- } else {
- return null; // Geocentric CRS.
+ if (crs instanceof CompoundCRS) {
+ for (final CoordinateReferenceSystem c : ((CompoundCRS)
crs).getComponents()) {
+ final P property = getGeodeticProperty(c, getter);
+ if (property != null) {
+ return property;
+ }
+ }
}
+ return null;
}
/**
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/CRSBuilder.java
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/CRSBuilder.java
index d0fa83779a..381d6e5a9a 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/CRSBuilder.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/CRSBuilder.java
@@ -903,11 +903,11 @@ public final class CRSBuilder extends
ReferencingFactoryContainer {
name = Strings.toUpperCase(name,
Characters.Filter.LETTERS_AND_DIGITS, true);
lastName = datum.getName();
try {
- final GeodeticDatum predefined =
CommonCRS.valueOf(name).datum();
- if (equalsIgnoreMetadata(predefined.getEllipsoid(),
ellipsoid) &&
- equalsIgnoreMetadata(predefined.getPrimeMeridian(),
meridian))
+ final CommonCRS predefined = CommonCRS.valueOf(name);
+ if (equalsIgnoreMetadata(predefined.ellipsoid(),
ellipsoid) &&
+ equalsIgnoreMetadata(predefined.primeMeridian(),
meridian))
{
- return predefined;
+ return predefined.datum();
}
} catch (IllegalArgumentException e) {
// Not a name that can be mapped to CommonCRS. Ignore.
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/GeoEncoder.java
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/GeoEncoder.java
index 9b5b9df2cc..0519241239 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/GeoEncoder.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/GeoEncoder.java
@@ -325,38 +325,42 @@ public final class GeoEncoder {
writeModelType(isBaseCRS ? GeoCodes.ModelTypeProjected : type);
if (writeEPSG(GeoKeys.GeodeticCRS, crs)) {
writeName(GeoKeys.GeodeticCitation, "GCS Name", crs);
- final GeodeticDatum datum = crs.getDatum();
- if (writeEPSG(GeoKeys.GeodeticDatum, datum)) {
- appendName(WKTKeywords.Datum, datum);
- final PrimeMeridian primem = datum.getPrimeMeridian();
- final double longitude;
- if (writeEPSG(GeoKeys.PrimeMeridian, primem)) {
- appendName(WKTKeywords.PrimeM, datum);
- longitude = primem.getGreenwichLongitude();
+ final GeodeticDatum datum = crs.getDatum();
+ final Ellipsoid ellipsoid =
ReferencingUtilities.getEllipsoid(crs);
+ if (datum == null && ellipsoid != null) {
+ // Case of a datum ensemble instead of a single datum.
+ writeShort(GeoKeys.GeodeticDatum, GeoCodes.userDefined);
+ } else if (!writeEPSG(GeoKeys.GeodeticDatum, datum)) {
+ return true;
+ }
+ appendName(WKTKeywords.Datum, datum);
+ final PrimeMeridian primem =
ReferencingUtilities.getPrimeMeridian(crs);
+ final double longitude;
+ if (writeEPSG(GeoKeys.PrimeMeridian, primem)) {
+ appendName(WKTKeywords.PrimeM, primem);
+ longitude = primem.getGreenwichLongitude();
+ } else {
+ longitude = 0; // Means
"do not write prime meridian".
+ }
+ final Unit<Length> axisUnit = ellipsoid.getAxisUnit();
+ final Unit<?> linearUnit = units.putIfAbsent(UnitKey.LINEAR,
axisUnit);
+ final UnitConverter toLinear =
axisUnit.getConverterToAny(linearUnit != null ? linearUnit : axisUnit);
+ writeUnit(UnitKey.LINEAR); // Must be after the `units` map
have been updated.
+ writeUnit(UnitKey.ANGULAR);
+ if (writeEPSG(GeoKeys.Ellipsoid, ellipsoid)) {
+ appendName(WKTKeywords.Ellipsoid, ellipsoid);
+ writeDouble(GeoKeys.SemiMajorAxis,
toLinear.convert(ellipsoid.getSemiMajorAxis()));
+ if (ellipsoid.isSphere() || !ellipsoid.isIvfDefinitive()) {
+ writeDouble(GeoKeys.SemiMinorAxis,
toLinear.convert(ellipsoid.getSemiMinorAxis()));
} else {
- longitude = 0; //
Means "do not write prime meridian".
- }
- final Ellipsoid ellipsoid = datum.getEllipsoid();
- final Unit<Length> axisUnit = ellipsoid.getAxisUnit();
- final Unit<?> linearUnit =
units.putIfAbsent(UnitKey.LINEAR, axisUnit);
- final UnitConverter toLinear =
axisUnit.getConverterToAny(linearUnit != null ? linearUnit : axisUnit);
- writeUnit(UnitKey.LINEAR); // Must be after the `units`
map have been updated.
- writeUnit(UnitKey.ANGULAR);
- if (writeEPSG(GeoKeys.Ellipsoid, ellipsoid)) {
- appendName(WKTKeywords.Ellipsoid, ellipsoid);
- writeDouble(GeoKeys.SemiMajorAxis,
toLinear.convert(ellipsoid.getSemiMajorAxis()));
- if (ellipsoid.isSphere() || !ellipsoid.isIvfDefinitive()) {
- writeDouble(GeoKeys.SemiMinorAxis,
toLinear.convert(ellipsoid.getSemiMinorAxis()));
- } else {
- writeDouble(GeoKeys.InvFlattening,
ellipsoid.getInverseFlattening());
- }
- }
- if (longitude != 0) {
- Unit<Angle> unit = primem.getAngularUnit();
- UnitConverter c =
unit.getConverterToAny(units.getOrDefault(UnitKey.ANGULAR, Units.DEGREE));
- writeDouble(GeoKeys.PrimeMeridianLongitude,
c.convert(longitude));
+ writeDouble(GeoKeys.InvFlattening,
ellipsoid.getInverseFlattening());
}
}
+ if (longitude != 0) {
+ Unit<Angle> unit = primem.getAngularUnit();
+ UnitConverter c =
unit.getConverterToAny(units.getOrDefault(UnitKey.ANGULAR, Units.DEGREE));
+ writeDouble(GeoKeys.PrimeMeridianLongitude,
c.convert(longitude));
+ }
} else if (isBaseCRS) {
writeUnit(UnitKey.ANGULAR); // Map projection parameters
may need this unit.
}
diff --git
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/CRSBuilder.java
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/CRSBuilder.java
index 87352f7e20..b3d4d844fd 100644
---
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/CRSBuilder.java
+++
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/CRSBuilder.java
@@ -56,6 +56,7 @@ import org.apache.sis.storage.DataStoreContentException;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.netcdf.internal.Resources;
import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.Utilities;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.measure.Units;
@@ -621,8 +622,18 @@ previous: for (int i=components.size(); --i >= 0;) {
* This method is invoked only if {@link
#setPredefinedComponents(Decoder)} failed to create a datum.
*/
@Override final void createDatum(DatumFactory factory, Map<String,?>
properties) throws FactoryException {
- final GeodeticDatum template = defaultCRS.datum();
- datum = factory.createGeodeticDatum(properties,
template.getEllipsoid(), template.getPrimeMeridian());
+ datum = factory.createGeodeticDatum(properties,
defaultCRS.ellipsoid(), defaultCRS.primeMeridian());
+ }
+
+ /**
+ * Sets the datum from the enumeration value of a predefined CRS.
+ * The predefined CRS is {@link #defaultCRS} or a spherical CRS.
+ */
+ protected final void setDatum(final CommonCRS crs) {
+ datum = crs.datum();
+ if (datum == null) {
+ datumEnsemble = crs.datumEnsemble();
+ }
}
/**
@@ -676,8 +687,9 @@ previous: for (int i=components.size(); --i >= 0;) {
referenceSystem = crs;
coordinateSystem = (SphericalCS) crs.getCoordinateSystem();
datum = crs.getDatum();
+ datumEnsemble = crs.getDatumEnsemble();
} else {
- datum = defaultCRS.datum();
+ setDatum(defaultCRS);
}
}
@@ -739,8 +751,9 @@ previous: for (int i=components.size(); --i >= 0;) {
referenceSystem = crs;
coordinateSystem = crs.getCoordinateSystem();
datum = crs.getDatum();
+ datumEnsemble = crs.getDatumEnsemble();
} else {
- datum = defaultCRS.datum();
+ setDatum(defaultCRS);
final Integer epsg = epsgCandidateCS(Units.DEGREE);
if (epsg != null) try {
coordinateSystem =
decoder.getCSAuthorityFactory().createEllipsoidalCS(epsg.toString());
@@ -768,7 +781,7 @@ previous: for (int i=components.size(); --i >= 0;) {
* This method is invoked under conditions similar to the ones of
above {@code createCS(…)} method.
*/
@Override void createCRS(CRSFactory factory, Map<String,?> properties)
throws FactoryException {
- referenceSystem = factory.createGeographicCRS(properties, datum,
coordinateSystem);
+ referenceSystem = factory.createGeographicCRS(properties, datum,
datumEnsemble, coordinateSystem);
}
}
@@ -817,7 +830,7 @@ previous: for (int i=components.size(); --i >= 0;) {
@Override void setPredefinedComponents(final Decoder decoder) throws
FactoryException {
super.setPredefinedComponents(decoder);
sphericalDatum = decoder.convention().defaultHorizontalCRS(true);
- datum = sphericalDatum.datum();
+ setDatum(sphericalDatum);
if (isPredefinedCS(Units.METRE)) {
coordinateSystem = decoder.getStandardProjectedCS();
}
@@ -843,8 +856,10 @@ previous: for (int i=components.size(); --i >= 0;) {
@Override void createCRS(CRSFactory factory, Map<String,?> properties)
throws FactoryException {
final boolean is3D = (coordinateSystem.getDimension() >= 3);
GeographicCRS baseCRS = is3D ? sphericalDatum.geographic3D() :
sphericalDatum.geographic();
- if (!baseCRS.getDatum().equals(datum)) {
- baseCRS = factory.createGeographicCRS(properties, datum,
baseCRS.getCoordinateSystem());
+ if (!Utilities.equalsIgnoreMetadata(baseCRS.getDatum(), datum) &&
+ !Utilities.equalsIgnoreMetadata(baseCRS.getDatumEnsemble(),
datumEnsemble))
+ {
+ baseCRS = factory.createGeographicCRS(properties, datum,
datumEnsemble, baseCRS.getCoordinateSystem());
}
referenceSystem = factory.createProjectedCRS(properties, baseCRS,
UNKNOWN_PROJECTION, coordinateSystem);
}
@@ -912,7 +927,7 @@ previous: for (int i=components.size(); --i >= 0;) {
* Creates the coordinate reference system from datum and coordinate
system computed in previous steps.
*/
@Override void createCRS(CRSFactory factory, Map<String,?> properties)
throws FactoryException {
- referenceSystem = factory.createVerticalCRS(properties, datum,
coordinateSystem);
+ referenceSystem = factory.createVerticalCRS(properties, datum,
datumEnsemble, coordinateSystem);
}
}
@@ -992,7 +1007,7 @@ previous: for (int i=components.size(); --i >= 0;) {
@Override void createCRS(CRSFactory factory, Map<String,?> properties)
throws FactoryException {
properties =
properties(getFirstAxis().coordinates.getUnitsString());
if (datum != null) {
- referenceSystem = factory.createTemporalCRS(properties,
datum, coordinateSystem);
+ referenceSystem = factory.createTemporalCRS(properties,
datum, datumEnsemble, coordinateSystem);
} else {
referenceSystem = factory.createEngineeringCRS(properties,
CommonCRS.Engineering.TIME.datum(), coordinateSystem);
@@ -1058,7 +1073,7 @@ previous: for (int i=components.size(); --i >= 0;) {
* Creates the coordinate reference system from datum and coordinate
system computed in previous steps.
*/
@Override void createCRS(CRSFactory factory, Map<String,?> properties)
throws FactoryException {
- referenceSystem = factory.createEngineeringCRS(properties, datum,
coordinateSystem);
+ referenceSystem = factory.createEngineeringCRS(properties, datum,
datumEnsemble, coordinateSystem);
}
}
diff --git
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/GridMapping.java
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/GridMapping.java
index e470fbd1d5..609cc18f5f 100644
---
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/GridMapping.java
+++
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/GridMapping.java
@@ -78,6 +78,9 @@ import org.apache.sis.io.wkt.WKTFormat;
import org.apache.sis.io.wkt.Warnings;
import org.apache.sis.measure.Units;
+// Specific to the geoapi-3.1 and geoapi-4.0 branches:
+import org.opengis.referencing.datum.DatumEnsemble;
+
/**
* Temporary objects for creating a {@link GridGeometry} instance defined by
attributes on a variable.
@@ -367,6 +370,7 @@ final class GridMapping {
*/
final Object bursaWolf = definition.remove(Convention.TOWGS84);
final GeodeticDatum datum;
+ DatumEnsemble<GeodeticDatum> ensemble = null;
if (isSpecified | bursaWolf != null) {
Map<String,Object> properties = properties(definition,
Convention.GEODETIC_DATUM_NAME, ellipsoid);
if (bursaWolf instanceof BursaWolfParameters) {
@@ -377,13 +381,16 @@ final class GridMapping {
datum = datumFactory.createGeodeticDatum(properties, ellipsoid,
meridian);
} else {
datum = defaultDefinitions.datum();
+ if (datum == null) {
+ ensemble = defaultDefinitions.datumEnsemble();
+ }
}
/*
* Geographic CRS from all above properties.
*/
if (isSpecified) {
final Map<String,?> properties = properties(definition,
Convention.GEOGRAPHIC_CRS_NAME, datum);
- return decoder.getCRSFactory().createGeographicCRS(properties,
datum,
+ return decoder.getCRSFactory().createGeographicCRS(properties,
datum, ensemble,
defaultDefinitions.geographic().getCoordinateSystem());
} else {
return defaultDefinitions.geographic();
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
index 78d747d87c..027b7f1b48 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
@@ -739,6 +739,11 @@ public class Errors extends IndexedResourceBundle {
*/
public static final short NonTemporalUnit_1 = 133;
+ /**
+ * Expected the “{0}” value for all members, but found a member with
the “{1}” value.
+ */
+ public static final short NonUniformValue_2 = 207;
+
/**
* No element for the “{0}” identifier, or the identifier is a forward
reference.
*/
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
index 103ad55d34..9fdefff89d 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
@@ -158,6 +158,7 @@ NonLinearUnit_1 = \u201c{0}\u201d is not
a linear unit.
NonScaleUnit_1 = \u201c{0}\u201d is not a scale unit.
NonTemporalUnit_1 = \u201c{0}\u201d is not a time unit.
NonRatioUnit_1 = The scale of measurement for
\u201c{0}\u201d unit is not a ratio scale.
+NonUniformValue_2 = Expected the \u201c{0}\u201d value for all
members, but found a member with the \u201c{1}\u201d value.
NotABackwardReference_1 = No element for the \u201c{0}\u201d
identifier, or the identifier is a forward reference.
NotADivisorOrMultiple_4 = Value of \u2018{0}\u2019 shall be a
{1,choice,0#divisor|1#multiple} of {2} but the given value is {3}.
NotAnInteger_1 = {0} is not an integer value.
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
index ef040968eb..ef89339a87 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
@@ -155,6 +155,7 @@ NonLinearUnit_1 =
\u00ab\u202f{0}\u202f\u00bb n\u2019est pas u
NonScaleUnit_1 = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas
une unit\u00e9 d\u2019\u00e9chelles.
NonTemporalUnit_1 = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas
une unit\u00e9 de temps.
NonRatioUnit_1 = L\u2019\u00e9chelle de mesure de
l\u2019unit\u00e9 \u00ab\u202f{0}\u202f\u00bb n\u2019est pas une \u00e9chelle
de rapports.
+NonUniformValue_2 = La valeur \u00ab\u202f{0}\u202f\u00bb
\u00e9tait attendue pour tous les membres, mais un membre a la valeur
\u00ab\u202f{1}\u202f\u00bb.
NotABackwardReference_1 = Il n\u2019y a pas d\u2019\u00e9l\u00e9ment
pour l\u2019identifiant \u201c{0}\u201d, ou l\u2019identifiant est une
r\u00e9f\u00e9rence vers l\u2019avant.
NotADivisorOrMultiple_4 = La valeur de \u2018{0}\u2019 doit
\u00eatre un {1,choice,0#diviseur|1#multiple} de {2}, mais la valeur
donn\u00e9e est {3}.
NotAnInteger_1 = {0} n\u2019est pas un nombre entier.