This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-3.1 in repository https://gitbox.apache.org/repos/asf/sis.git
commit d3af9977592bc92a3bf414f8c62c492d11d347c5 Merge: c4eceb3ef1 f8421abcf5 Author: Martin Desruisseaux <[email protected]> AuthorDate: Wed Jul 17 17:37:11 2024 +0200 Merge branch 'geoapi-4.0' into geoapi-3.1 .../org/apache/sis/feature/AbstractAttribute.java | 2 +- .../org/apache/sis/feature/AbstractFeature.java | 2 +- .../apache/sis/feature/SingletonAttributeTest.java | 2 +- .../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 | 13 +- .../sis/referencing/AbstractIdentifiedObject.java | 7 +- .../main/org/apache/sis/referencing/CRS.java | 34 +- .../main/org/apache/sis/referencing/CommonCRS.java | 76 ++- .../referencing/EllipsoidalHeightSeparator.java | 28 +- .../sis/referencing/MultiRegisterOperations.java | 26 +- .../apache/sis/referencing/crs/AbstractCRS.java | 79 +-- .../sis/referencing/crs/AbstractSingleCRS.java | 314 +++++++++++ .../sis/referencing/crs/DefaultDerivedCRS.java | 22 +- .../sis/referencing/crs/DefaultEngineeringCRS.java | 48 +- .../sis/referencing/crs/DefaultGeocentricCRS.java | 6 +- .../sis/referencing/crs/DefaultGeodeticCRS.java | 44 +- .../sis/referencing/crs/DefaultGeographicCRS.java | 42 +- .../sis/referencing/crs/DefaultImageCRS.java | 30 +- .../sis/referencing/crs/DefaultParametricCRS.java | 48 +- .../sis/referencing/crs/DefaultProjectedCRS.java | 23 +- .../sis/referencing/crs/DefaultTemporalCRS.java | 82 ++- .../sis/referencing/crs/DefaultVerticalCRS.java | 48 +- .../sis/referencing/crs/ExplicitParameters.java | 6 +- .../referencing/datum/DefaultDatumEnsemble.java | 18 +- .../apache/sis/referencing/datum/PseudoDatum.java | 601 +++++++++++++++++++++ .../referencing/factory/GeodeticObjectFactory.java | 12 +- .../apache/sis/referencing/internal/Resources.java | 4 +- .../sis/referencing/internal/Resources.properties | 4 +- .../referencing/internal/Resources_fr.properties | 2 +- .../operation/CoordinateOperationFinder.java | 21 +- .../operation/CoordinateOperationRegistry.java | 12 +- .../referencing/operation/DefaultConversion.java | 15 +- .../DefaultCoordinateOperationFactory.java | 16 +- .../transform/DefaultMathTransformFactory.java | 5 +- .../sis/referencing/privy/DefinitionVerifier.java | 29 +- .../privy/EllipsoidalHeightCombiner.java | 27 +- .../referencing/privy/GeodeticObjectBuilder.java | 47 +- .../referencing/privy/ReferencingUtilities.java | 132 +++-- .../referencing/AbstractIdentifiedObjectTest.java | 2 +- .../sis/storage/geotiff/reader/CRSBuilder.java | 13 +- .../sis/storage/geotiff/writer/GeoEncoder.java | 5 +- .../apache/sis/storage/netcdf/base/CRSBuilder.java | 37 +- .../sis/storage/netcdf/base/GridMapping.java | 9 +- .../main/org/apache/sis/storage/csv/Store.java | 9 +- .../main/org/apache/sis/util/resources/Errors.java | 11 +- .../apache/sis/util/resources/Errors.properties | 7 +- .../apache/sis/util/resources/Errors_fr.properties | 1 + .../org/apache/sis/gui/map/OperationFinder.java | 5 +- .../main/org/apache/sis/gui/referencing/Utils.java | 6 +- 52 files changed, 1535 insertions(+), 524 deletions(-) diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EllipsoidalHeightSeparator.java index 4b70ced344,4dfad99d15..366257c40e --- 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,9 -39,9 +39,12 @@@ import org.apache.sis.util.resources.Er import org.apache.sis.referencing.factory.GeodeticObjectFactory; import static org.apache.sis.referencing.privy.ReferencingUtilities.getPropertiesForModifiedCRS; +// Specific to the main and geoapi-3.1 branches: +import org.opengis.referencing.crs.GeographicCRS; + + // 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. diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java index 9fafafbdc2,486ff17472..794042176d --- 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 @@@ -45,14 -42,13 +42,13 @@@ import org.apache.sis.util.Utilities import org.apache.sis.util.ComparisonMode; import org.apache.sis.util.resources.Errors; +// Specific to the main and geoapi-3.1 branches: +import org.opengis.referencing.crs.GeneralDerivedCRS; +import org.opengis.geometry.MismatchedDimensionException; + // Specific to the geoapi-3.1 and geoapi-4.0 branches: import org.opengis.metadata.Identifier; - import org.opengis.referencing.datum.DatumEnsemble; -// Specific to the geoapi-4.0 branch: -import org.opengis.referencing.crs.DerivedCRS; -import org.opengis.coordinate.MismatchedDimensionException; - /** * Coordinate reference system, defined by a {@linkplain AbstractCS coordinate system} diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultImageCRS.java index 1a466619d6,885a1679bd..3f46606280 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultImageCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultImageCRS.java @@@ -27,12 -26,10 +26,11 @@@ import org.apache.sis.referencing.Abstr 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.metadata.privy.ImplementationHelper; import org.apache.sis.io.wkt.Formatter; -// Specific to the geoapi-4.0 branch: -import org.apache.sis.referencing.datum.DefaultImageDatum; +// Specific to the main and geoapi-3.1 branches: +import org.opengis.referencing.crs.ImageCRS; +import org.opengis.referencing.datum.ImageDatum; /** @@@ -70,7 -67,7 +68,7 @@@ "datum" }) @XmlRootElement(name = "ImageCRS") - public final class DefaultImageCRS extends AbstractCRS implements ImageCRS { -public final class DefaultImageCRS extends AbstractSingleCRS<DefaultImageDatum> { ++public final class DefaultImageCRS extends AbstractSingleCRS<ImageDatum> implements ImageCRS { /** * Serial number for inter-operability with different versions. */ @@@ -125,15 -111,12 +112,14 @@@ * @param properties the properties to be given to the coordinate reference system. * @param datum the datum. * @param cs the coordinate system. + * + * @see org.apache.sis.referencing.factory.GeodeticObjectFactory#createImageCRS(Map, ImageDatum, AffineCS) */ public DefaultImageCRS(final Map<String,?> properties, - final DefaultImageDatum datum, - final AffineCS cs) + final ImageDatum datum, + final AffineCS cs) { - super(properties, cs); - this.datum = Objects.requireNonNull(datum); - super(properties, DefaultImageDatum.class, datum, null, cs); ++ super(properties, ImageDatum.class, datum, null, cs); } /** @@@ -142,56 -125,8 +128,54 @@@ */ private DefaultImageCRS(final DefaultImageCRS original, final AbstractCS cs) { super(original, null, cs); - datum = original.datum; } + /** + * Constructs a new coordinate reference system with the same values as the specified one. + * This copy constructor provides a way to convert an arbitrary implementation into a SIS one + * or a user-defined one (as a subclass), usually in order to leverage some implementation-specific API. + * + * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> + * + * @param crs the coordinate reference system to copy. + * + * @see #castOrCopy(ImageCRS) + */ + protected DefaultImageCRS(final ImageCRS crs) { + super(crs); - datum = crs.getDatum(); + } + + /** + * Returns a SIS coordinate reference system implementation with the same values as the given + * arbitrary implementation. If the given object is {@code null}, then this method returns {@code null}. + * Otherwise if the given object is already a SIS implementation, then the given object is returned unchanged. + * Otherwise a new SIS implementation is created and initialized to the attribute values of the given object. + * + * @param object the object to get as a SIS implementation, or {@code null} if none. + * @return a SIS implementation containing the values of the given object (may be the + * given object itself), or {@code null} if the argument was null. + */ + public static DefaultImageCRS castOrCopy(final ImageCRS object) { + return (object == null) || (object instanceof DefaultImageCRS) + ? (DefaultImageCRS) object : new DefaultImageCRS(object); + } + + /** + * Returns the GeoAPI interface implemented by this class. + * The SIS implementation returns {@code ImageCRS.class}. + * + * <h4>Note for implementers</h4> + * Subclasses usually do not need to override this method since GeoAPI does not define {@code ImageCRS} + * sub-interface. Overriding possibility is left mostly for implementers who wish to extend GeoAPI with + * their own set of interfaces. + * + * @return {@code ImageCRS.class} or a user-defined sub-interface. + */ + @Override + public Class<? extends ImageCRS> getInterface() { + return ImageCRS.class; + } + /** * Returns the datum. * @@@ -199,8 -134,8 +183,8 @@@ */ @Override @XmlElement(name = "imageDatum", required = true) - public DefaultImageDatum getDatum() { + public ImageDatum getDatum() { - return datum; + return super.getDatum(); } /** @@@ -289,12 -224,8 +273,8 @@@ * * @see #getDatum() */ - private void setDatum(final DefaultImageDatum value) { + private void setDatum(final ImageDatum value) { - if (datum == null) { - datum = value; - } else { - ImplementationHelper.propertyAlreadySet(DefaultImageCRS.class, "setDatum", "imageDatum"); - } + setDatum("imageDatum", value); } /** diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java index 663090219d,d5af964f6f..c190cbe749 --- 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 @@@ -40,12 -40,9 +40,13 @@@ import org.apache.sis.util.ArgumentChec import org.apache.sis.util.ComparisonMode; import org.apache.sis.util.Workaround; +// Specific to the main and geoapi-3.1 branches: +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.coordinate.MismatchedDimensionException; + import org.opengis.referencing.datum.DatumEnsemble; /** diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java index 625064eddc,2d157f8e2b..4eceaac3c7 --- 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 @@@ -353,6 -323,22 +326,22 @@@ public class DefaultTemporalCRS extend 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. - return PseudoDatum.of(this).getOrigin(); ++ return TemporalDate.toTemporal(PseudoDatum.of(this).getOrigin()); + } + /** * {@inheritDoc} * diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java index 5984372e29,c476ed9e01..1a3266fe08 --- 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 @@@ -59,10 -58,9 +58,9 @@@ final class ExplicitParameters extends /** * Creates a new temporary {@code Conversion} elements for the parameters of the given CRS. */ - ExplicitParameters(final AbstractDerivedCRS crs, final String keyword) { + ExplicitParameters(final AbstractDerivedCRS<?> crs, final String keyword) { conversion = crs.getConversionFromBase(); - final Datum datum = crs.getDatum(); - ellipsoid = (datum instanceof GeodeticDatum) ? ((GeodeticDatum) datum).getEllipsoid() : null; + ellipsoid = ReferencingUtilities.getEllipsoid(crs); this.keyword = keyword; } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/PseudoDatum.java index 0000000000,6d13d6200f..4ee7b6837c mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/PseudoDatum.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/PseudoDatum.java @@@ -1,0 -1,589 +1,601 @@@ + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.apache.sis.referencing.datum; + + import java.util.Set; + import java.util.Collection; + import java.util.Iterator; + import java.util.Objects; + import java.util.Optional; + import java.util.NoSuchElementException; + import java.util.function.Function; + import java.time.temporal.Temporal; + import java.io.Serializable; + import org.opengis.util.GenericName; + import org.opengis.util.InternationalString; -import org.opengis.metadata.Identifier; + import org.opengis.referencing.IdentifiedObject; + import org.opengis.referencing.ObjectDomain; + import org.opengis.referencing.datum.*; + import org.opengis.referencing.crs.*; + import org.apache.sis.util.Utilities; + import org.apache.sis.util.ComparisonMode; + import org.apache.sis.util.LenientComparable; + import org.apache.sis.util.resources.Errors; + import org.apache.sis.referencing.IdentifiedObjects; + import org.apache.sis.referencing.GeodeticException; + ++// Specific to the main and geoapi-3.1 branches: ++import java.util.Date; ++import org.opengis.referencing.ReferenceIdentifier; ++ + + /** + * A datum ensemble viewed as if it was a single datum for the sake of simplicity. + * This pseudo-datum is a non-standard mechanism used by the Apache <abbr>SIS</abbr> implementation + * for handling datum and datum ensemble in a uniform way. For example, {@code PseudoDatum.of(crs)} + * allows to {@linkplain IdentifiedObjects#isHeuristicMatchForName compare the datum name} without + * the need to check which one of the {@code getDatum()} or {@code getDatumEnsemble()} methods + * returns a non-null value. + * + * <p>{@code PseudoDatum} instances should live only for a short time. + * They should not be stored as {@link SingleCRS} properties. + * If a {@code PseudoDatum} instances is given to the constructor of an Apache <abbr>SIS</abbr> class, + * the constructor will automatically unwraps the {@linkplain #ensemble}.</p> + * + * <h2>Default method implementations</h2> + * Unless otherwise specified in the Javadoc, all methods in this class delegate + * to the same method in the wrapper datum {@linkplain #ensemble}. + * + * <h2>Object comparisons</h2> + * The {@link #equals(Object)} method returns {@code true} only if the two compared objects are instances + * of the same class, which implies that the {@code Object} argument must be a {@code PseudoDatum}. + * The {@link #equals(Object, ComparisonMode)} method with a non-strict comparison mode compares + * the wrapped datum ensemble, which implies that: + * + * <ul> + * <li>A pseudo-datum is never equal to a real datum, regardless the names and identifiers of the compared objects.</li> + * <li>A pseudo-datum can be equal to another pseudo-datum or to a datum ensemble.</li> + * </ul> + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.5 + * + * @param <D> the type of datum contained in this ensemble. + * + * @since 1.5 + */ + public abstract class PseudoDatum<D extends Datum> implements Datum, LenientComparable, Serializable { + /** + * For cross-versions compatibility. + */ + private static final long serialVersionUID = 3889895625961827486L; + + /** + * The datum ensemble wrapped by this pseudo-datum. + */ + @SuppressWarnings("serial") // Most SIS implementations are serializable. + public final DatumEnsemble<D> ensemble; + + /** + * Creates a new pseudo-datum. + * + * @param ensemble the datum ensemble wrapped by this pseudo-datum. + */ + protected PseudoDatum(final DatumEnsemble<D> ensemble) { + this.ensemble = Objects.requireNonNull(ensemble); + } + + /** + * Returns the datum of the given <abbr>CRS</abbr> if presents, or the datum ensemble otherwise. + * This is an alternative to the {@code of(…)} methods when the caller does not need to view the + * object as a datum. + * + * @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, or {@code null}. + */ + public static IdentifiedObject getDatumOrEnsemble(final SingleCRS crs) { + if (crs == null) return null; + final Datum datum = crs.getDatum(); + if (datum != null) { + if (datum instanceof PseudoDatum<?>) { + return ((PseudoDatum) datum).ensemble; + } + return datum; + } + return crs.getDatumEnsemble(); + } + + /** + * Returns the datum or pseudo-datum of the given geodetic <abbr>CRS</abbr>. + * If the given <abbr>CRS</abbr> is associated to a non-null datum, then this method returns that datum. + * Otherwise, this method returns the <abbr>CRS</abbr> datum ensemble wrapped in a pseudo-datum. + * In the latter case, the pseudo-datum implementations of the {@link GeodeticDatum#getEllipsoid()} + * and {@link GeodeticDatum#getPrimeMeridian()} methods expect an ellipsoid or prime meridian which + * is the same for all {@linkplain #ensemble} members. + * If this condition does not hold, a {@link GeodeticException} will be thrown. + * + * @param crs the coordinate reference system for which to get the datum or datum ensemble. + * @return the datum or pseudo-datum of the given <abbr>CRS</abbr>. + * @throws NullPointerException if the given argument is {@code null}, + * or if both the datum and datum ensemble are null. + */ + public static GeodeticDatum of(final GeodeticCRS crs) { + GeodeticDatum datum = crs.getDatum(); + if (datum == null) { + datum = new PseudoDatum.Geodetic(crs.getDatumEnsemble()); + } + return datum; + } + + /** + * Returns the datum or pseudo-datum of the given vertical <abbr>CRS</abbr>. + * If the given <abbr>CRS</abbr> is associated to a non-null datum, then this method returns that datum. + * Otherwise, this method returns the <abbr>CRS</abbr> datum ensemble wrapped in a pseudo-datum. + * + * @param crs the coordinate reference system for which to get the datum or datum ensemble. + * @return the datum or pseudo-datum of the given <abbr>CRS</abbr>. + * @throws NullPointerException if the given argument is {@code null}, + * or if both the datum and datum ensemble are null. + */ + public static VerticalDatum of(final VerticalCRS crs) { + VerticalDatum datum = crs.getDatum(); + if (datum == null) { + datum = new PseudoDatum.Vertical(crs.getDatumEnsemble()); + } + return datum; + } + + /** + * Returns the datum or pseudo-datum of the given temporal <abbr>CRS</abbr>. + * If the given <abbr>CRS</abbr> is associated to a non-null datum, then this method returns that datum. + * Otherwise, this method returns the <abbr>CRS</abbr> datum ensemble wrapped in a pseudo-datum. + * In the latter case, the pseudo-datum implementations of the {@link TemporalDatum#getOrigin()} + * expects a temporal origin which is the same for all {@linkplain #ensemble} members. + * If this condition does not hold, a {@link GeodeticException} will be thrown. + * + * @param crs the coordinate reference system for which to get the datum or datum ensemble. + * @return the datum or pseudo-datum of the given <abbr>CRS</abbr>. + * @throws NullPointerException if the given argument is {@code null}, + * or if both the datum and datum ensemble are null. + */ + public static TemporalDatum of(final TemporalCRS crs) { + TemporalDatum datum = crs.getDatum(); + if (datum == null) { + datum = new PseudoDatum.Time(crs.getDatumEnsemble()); + } + return datum; + } + + /** + * Returns the datum or pseudo-datum of the given parametric <abbr>CRS</abbr>. + * If the given <abbr>CRS</abbr> is associated to a non-null datum, then this method returns that datum. + * Otherwise, this method returns the <abbr>CRS</abbr> datum ensemble wrapped in a pseudo-datum. + * + * @param crs the coordinate reference system for which to get the datum or datum ensemble. + * @return the datum or pseudo-datum of the given <abbr>CRS</abbr>. + * @throws NullPointerException if the given argument is {@code null}, + * or if both the datum and datum ensemble are null. + */ + public static ParametricDatum of(final ParametricCRS crs) { + ParametricDatum datum = crs.getDatum(); + if (datum == null) { + datum = new PseudoDatum.Parametric(crs.getDatumEnsemble()); + } + return datum; + } + + /** + * Returns the datum or pseudo-datum of the given engineering <abbr>CRS</abbr>. + * If the given <abbr>CRS</abbr> is associated to a non-null datum, then this method returns that datum. + * Otherwise, this method returns the <abbr>CRS</abbr> datum ensemble wrapped in a pseudo-datum. + * + * @param crs the coordinate reference system for which to get the datum or datum ensemble. + * @return the datum or pseudo-datum of the given <abbr>CRS</abbr>. + * @throws NullPointerException if the given argument is {@code null}, + * or if both the datum and datum ensemble are null. + */ + public static EngineeringDatum of(final EngineeringCRS crs) { + EngineeringDatum datum = crs.getDatum(); + if (datum == null) { + datum = new PseudoDatum.Engineering(crs.getDatumEnsemble()); + } + return datum; + } + + /** + * Returns the GeoAPI interface of the ensemble members. + * It should also be the interface implemented by this class. + * + * @return the GeoAPI interface of the ensemble members. + */ + public abstract Class<D> getInterface(); + + /** + * Returns the primary name by which the datum ensemble is identified. + * + * @return {@code ensemble.getName()}. + * @hidden + */ + @Override - public Identifier getName() { ++ public ReferenceIdentifier getName() { + return ensemble.getName(); + } + + /** + * Returns alternative names by which the datum ensemble is identified. + * + * @return {@code ensemble.getAlias()}. + * @hidden + */ + @Override + public Collection<GenericName> getAlias() { + return ensemble.getAlias(); + } + + /** + * Returns an identifier which references elsewhere the datum ensemble information. + * + * @return {@code ensemble.getIdentifiers()}. + * @hidden + */ + @Override - public Set<Identifier> getIdentifiers() { ++ public Set<ReferenceIdentifier> getIdentifiers() { + return ensemble.getIdentifiers(); + } + + /** + * Returns the usage of the datum ensemble. + * + * @return {@code ensemble.getDomains()}. + * @hidden + */ + @Override + public Collection<ObjectDomain> getDomains() { + return ensemble.getDomains(); + } + + /** + * Returns an anchor definition which is common to all members of the datum ensemble. + * If the value is not the same for all members (including the case where a member + * has an empty value), then this method returns an empty value. + * + * @return the common anchor definition, or empty if there is no common value. + */ + @Override + public Optional<InternationalString> getAnchorDefinition() { + return getCommonOptionalValue(Datum::getAnchorDefinition); + } + + /** + * Returns an anchor epoch which is common to all members of the datum ensemble. + * If the value is not the same for all members (including the case where a member + * has an empty value), then this method returns an empty value. + * + * @return the common anchor epoch, or empty if there is no common value. + */ + @Override + public Optional<Temporal> getAnchorEpoch() { + return getCommonOptionalValue(Datum::getAnchorEpoch); + } + + /** + * Returns a publication date which is common to all members of the datum ensemble. + * If the value is not the same for all members (including the case where a member + * has an empty value), then this method returns an empty value. + * + * @return the common publication date, or empty if there is no common value. + */ + @Override + public Optional<Temporal> getPublicationDate() { + return getCommonOptionalValue(Datum::getPublicationDate); + } + + /** + * Returns a conventional reference system which is common to all members of the datum ensemble. + * The returned value should never be empty, because it is illegal for a datum ensemble to have + * members with different conventional reference system. If this case nevertheless happens, + * this method returns an empty value. + * + * @return the common conventional reference system, or empty if there is no common value. + */ + @Override + public Optional<IdentifiedObject> getConventionalRS() { + return getCommonOptionalValue(Datum::getConventionalRS); + } + + /** + * Returns an optional value which is common to all ensemble members. + * If all members do not have the same value, returns an empty value. + * + * @param <V> type of value. + * @param getter method to invoke on each member for getting the value. + * @return a value common to all members, or {@code null} if none. + */ + final <V> Optional<V> getCommonOptionalValue(final Function<D, Optional<V>> getter) { + final Iterator<D> it = ensemble.getMembers().iterator(); + check: if (it.hasNext()) { + final Optional<V> value = getter.apply(it.next()); + if (value.isPresent()) { + while (it.hasNext()) { + if (!value.equals(getter.apply(it.next()))) { + break check; + } + } + return value; + } + } + return Optional.empty(); + } + + /** + * Returns a mandatory value which is common to all ensemble members. + * + * @param <V> type of value. + * @param getter method to invoke on each member for getting the value. + * @return a value common to all members. + * @throws NoSuchElementException if the ensemble does not contain at least one member. + * @throws GeodeticException if the value is not the same for all members of the datum ensemble. + */ + final <V> V getCommonMandatoryValue(final Function<D, V> getter) { + final Iterator<D> it = ensemble.getMembers().iterator(); + final V value = getter.apply(it.next()); // Mandatory. + if (it.hasNext()) { + final V other = getter.apply(it.next()); + if (!Objects.equals(value, other)) { + throw new GeodeticException(Errors.format(Errors.Keys.NonUniformValue_2, + (value instanceof IdentifiedObject) ? IdentifiedObjects.getDisplayName((IdentifiedObject) value) : value, + (other instanceof IdentifiedObject) ? IdentifiedObjects.getDisplayName((IdentifiedObject) other) : other)); + } + } + return value; + } + + /** + * Returns comments on or information about the datum ensemble. + * + * @return {@code ensemble.getRemarks()}. + * @hidden + */ + @Override - public Optional<InternationalString> getRemarks() { ++ public InternationalString getRemarks() { + return ensemble.getRemarks(); + } + + /** + * Formats a <i>Well-Known Text</i> (WKT) for the datum ensemble. + * + * @return {@code ensemble.toWKT()}. + * @hidden + */ + @Override + public String toWKT() { + return ensemble.toWKT(); + } + + /** + * Returns a string representation of the datum ensemble. + * + * @return {@code ensemble.toString()}. + * @hidden + */ + @Override + public String toString() { + return ensemble.toString(); + } + + /** + * Returns a hash-code value of this pseudo-datum. + * + * @return a hash-code value of this pseudo-datum. + */ + @Override + public int hashCode() { + return ensemble.hashCode() ^ getClass().hashCode(); + } + + /** + * Compares this pseudo-datum to the given object for equality. + * The two objects are equal if they are of the same classes and + * the wrapped {@link #ensemble} are equal. + * + * @param other the object to compare with this pseudo-datum. + * @return whether the two objects are equal. + */ + @Override + public boolean equals(final Object other) { + return (other != null) && other.getClass() == getClass() && ensemble.equals(((PseudoDatum<?>) other).ensemble); + } + + /** + * Compares this object with the given object for equality. + * If the comparison mode is strict, then this method delegates to {@link #equals(Object)}. + * Otherwise, this method unwrap the ensembles, then compare the ensembles. + * + * @param other the object to compare to {@code this}. + * @param mode the strictness level of the comparison. + * @return {@code true} if both objects are equal according the given comparison mode. + */ + @Override + public boolean equals(Object other, final ComparisonMode mode) { + if (mode == ComparisonMode.STRICT) { + return equals(other); + } + if (other instanceof PseudoDatum<?>) { + other = ((PseudoDatum<?>) other).ensemble; + } + return Utilities.deepEquals(ensemble, other, mode); + } + + /** + * A pseudo-datum for an ensemble of geodetic datum. + */ + private static final class Geodetic extends PseudoDatum<GeodeticDatum> implements GeodeticDatum { + /** For cross-versions compatibility. */ + private static final long serialVersionUID = 7669230365507661290L; + + /** Creates a new pseudo-datum wrapping the given ensemble. */ + Geodetic(final DatumEnsemble<GeodeticDatum> ensemble) { + super(ensemble); + } + + /** + * Returns the GeoAPI interface implemented by this pseudo-datum. + */ + @Override + public Class<GeodeticDatum> getInterface() { + return GeodeticDatum.class; + } + + /** + * Returns the ellipsoid which is indirectly (through a datum) associated to this datum ensemble. + * If all members of the ensemble use the same ellipsoid, then this method returns that ellipsoid. + * + * @return the ellipsoid indirectly associated to this datum ensemble. + * @throws NoSuchElementException if the ensemble does not contain at least one member. + * @throws GeodeticException if the ellipsoid is not the same for all members of the datum ensemble. + */ + @Override + public Ellipsoid getEllipsoid() { + return getCommonMandatoryValue(GeodeticDatum::getEllipsoid); + } + + /** + * Returns the prime meridian which is indirectly (through a datum) associated to this datum ensemble. + * If all members of the ensemble use the same prime meridian, then this method returns that meridian. + * + * @return the prime meridian indirectly associated to this datum ensemble. + * @throws NoSuchElementException if the ensemble does not contain at least one member. + * @throws GeodeticException if the prime meridian is not the same for all members of the datum ensemble. + */ + @Override + public PrimeMeridian getPrimeMeridian() { + return getCommonMandatoryValue(GeodeticDatum::getPrimeMeridian); + } + } + + /** + * A pseudo-datum for an ensemble of vertical datum. + */ + private static final class Vertical extends PseudoDatum<VerticalDatum> implements VerticalDatum { + /** For cross-versions compatibility. */ + private static final long serialVersionUID = 7242417944400289818L; + + /** Creates a new pseudo-datum wrapping the given ensemble. */ + Vertical(final DatumEnsemble<VerticalDatum> ensemble) { + super(ensemble); + } + + /** + * Returns the GeoAPI interface implemented by this pseudo-datum. + */ + @Override + public Class<VerticalDatum> getInterface() { + return VerticalDatum.class; + } + + /** + * Returns a realization method which is common to all members of the datum ensemble. + * If the value is not the same for all members (including the case where a member + * has an empty value), then this method returns an empty value. + * + * @return the common realization method, or empty if there is no common value. + */ + @Override + public Optional<RealizationMethod> getRealizationMethod() { + return getCommonOptionalValue(VerticalDatum::getRealizationMethod); + } ++ ++ /** ++ * @deprecated Replaced by {@link #getRealizationMethod()}. ++ */ ++ @Override ++ @Deprecated ++ public VerticalDatumType getVerticalDatumType() { ++ return getCommonMandatoryValue(VerticalDatum::getVerticalDatumType); ++ } + } + + /** + * A pseudo-datum for an ensemble of temporal datum. + */ + private static final class Time extends PseudoDatum<TemporalDatum> implements TemporalDatum { + /** For cross-versions compatibility. */ + private static final long serialVersionUID = -4208563828181087035L; + + /** Creates a new pseudo-datum wrapping the given ensemble. */ + Time(final DatumEnsemble<TemporalDatum> ensemble) { + super(ensemble); + } + + /** + * Returns the GeoAPI interface implemented by this pseudo-datum. + */ + @Override + public Class<TemporalDatum> getInterface() { + return TemporalDatum.class; + } + + /** + * Returns the temporal origin which is indirectly (through a datum) associated to this datum ensemble. + * If all members of the ensemble use the same temporal origin, then this method returns that origin. + * + * @return the temporal origin indirectly associated to this datum ensemble. + * @throws NoSuchElementException if the ensemble does not contain at least one member. + * @throws GeodeticException if the temporal origin is not the same for all members of the datum ensemble. + */ + @Override - public Temporal getOrigin() { ++ public Date getOrigin() { + return getCommonMandatoryValue(TemporalDatum::getOrigin); + } + } + + /** + * A pseudo-datum for an ensemble of parametric datum. + */ + private static final class Parametric extends PseudoDatum<ParametricDatum> implements ParametricDatum { + /** For cross-versions compatibility. */ + private static final long serialVersionUID = -8277774591738789437L; + + /** Creates a new pseudo-datum wrapping the given ensemble. */ + Parametric(final DatumEnsemble<ParametricDatum> ensemble) { + super(ensemble); + } + + /** Returns the GeoAPI interface implemented by this pseudo-datum. */ + @Override public Class<ParametricDatum> getInterface() { + return ParametricDatum.class; + } + } + + /** + * A pseudo-datum for an ensemble of engineering datum. + */ + private static final class Engineering extends PseudoDatum<EngineeringDatum> implements EngineeringDatum { + /** For cross-versions compatibility. */ + private static final long serialVersionUID = -8978468990963666861L; + + /** Creates a new pseudo-datum wrapping the given ensemble. */ + Engineering(final DatumEnsemble<EngineeringDatum> ensemble) { + super(ensemble); + } + + /** Returns the GeoAPI interface implemented by this pseudo-datum. */ + @Override public Class<EngineeringDatum> getInterface() { + return EngineeringDatum.class; + } + } + } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/DefinitionVerifier.java index 626bfe9463,5e1bd59b0d..d2d82bca6d --- 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 @@@ -307,9 -306,8 +306,9 @@@ public final class DefinitionVerifier * Returns a code indicating in which part the two given CRS differ. The given iterators usually iterate over * exactly one element, but may iterate over more elements if the CRS were instance of {@code CompoundCRS}. * The returned value is one of {@link #METHOD}, {@link #CONVERSION}, {@link #CS}, {@link #DATUM}, - * {@link #PRIME_MERIDIAN} or {@link #OTHER} constants. + * {@link #PRIME_MERIDIAN}, {@link #ELLIPSOID} or {@link #OTHER} constants. */ + @SuppressWarnings("deprecation") private static int diffCode(final Iterator<SingleCRS> authoritative, final Iterator<SingleCRS> given) { while (authoritative.hasNext() && given.hasNext()) { final SingleCRS crsA = authoritative.next(); diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/EllipsoidalHeightCombiner.java index 8dfe6c7152,0a9ac3d3c6..fabc5f747a --- 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 @@@ -143,13 -144,15 +144,15 @@@ public final class EllipsoidalHeightCom 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(); + GeographicCRS base = proj.getBaseCRS(); if (base.getCoordinateSystem().getDimension() == 2) { - base = (GeodeticCRS) createCompoundCRS( + base = (GeographicCRS) createCompoundCRS( IdentifiedObjects.getProperties(base, GeographicCRS.IDENTIFIERS_KEY), base, vertical); } /* diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/GeodeticObjectBuilder.java index 897b375bbd,bd34098df3..df58dc19ca --- 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,8 -63,12 +63,9 @@@ import org.apache.sis.parameter.Paramet // 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: -import org.opengis.referencing.crs.GeodeticCRS; - /** * Helper methods for building Coordinate Reference Systems and related objects.
