This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/sis.git
commit d1adc5678e03d1d04abdd1e832ce08f7aa774599 Merge: f0a1dfadf1 51c67748ee Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Jul 19 18:13:34 2024 +0200 Merge branch 'geoapi-3.1' .../apache/sis/buildtools/gradle/Assembler.java | 2 +- .../org/apache/sis/buildtools/gradle/UnoPkg.java | 2 +- endorsed/build.gradle.kts | 3 + .../apache/sis/coverage/grid/DimensionReducer.java | 2 +- .../main/org/apache/sis/filter/TemporalFilter.java | 2 +- .../coverage/grid/ResampledGridCoverageTest.java | 2 +- .../iso/quality/DefaultEvaluationMethod.java | 16 +- .../iso/quality/DefaultMeasureReference.java | 14 +- .../sis/metadata/privy/ImplementationHelper.java | 2 +- .../{internal => privy}/RecordSchemaSIS.java | 5 +- .../sis/metadata/privy/TransformationAccuracy.java | 90 ------- .../apache/sis/metadata/sql/MetadataSource.java | 2 +- .../org/apache/sis/util/iso/DefaultRecord.java | 2 +- .../org/apache/sis/util/iso/DefaultRecordType.java | 2 +- .../main/org/apache/sis/xml/MarshallerPool.java | 2 +- .../org/apache/sis/xml/privy/XmlUtilities.java | 2 +- .../iso/quality/DefaultQuantitativeResultTest.java | 2 +- .../sis/openoffice/ReferencingFunctionsTest.java | 2 +- .../apache/sis/io/wkt/GeodeticObjectParser.java | 4 +- .../main/org/apache/sis/referencing/CRS.java | 2 +- .../main/org/apache/sis/referencing/CommonCRS.java | 10 +- .../sis/referencing/crs/DefaultGeodeticCRS.java | 2 +- .../apache/sis/referencing/datum/PseudoDatum.java | 267 ++++++++++++++++++--- .../referencing/factory/sql/EPSGDataAccess.java | 42 ++-- .../sis/referencing/internal/AnnotatedMatrix.java | 1 - .../PositionalAccuracyConstant.java | 144 ++++++++--- .../apache/sis/referencing/internal/Resources.java | 10 + .../sis/referencing/internal/Resources.properties | 4 +- .../referencing/internal/Resources_fr.properties | 4 +- .../operation/AbstractCoordinateOperation.java | 2 +- .../operation/CoordinateOperationContext.java | 2 +- .../operation/CoordinateOperationFinder.java | 71 ++++-- .../operation/CoordinateOperationRegistry.java | 15 +- .../operation/DefaultConcatenatedOperation.java | 2 +- .../DefaultCoordinateOperationFactory.java | 23 +- .../referencing/privy/ReferencingUtilities.java | 24 -- .../datum/DefaultGeodeticDatumTest.java | 2 +- .../factory/ConcurrentAuthorityFactoryTest.java | 49 ++-- .../internal/PositionalAccuracyConstantTest.java | 111 +++++++++ .../operation/CoordinateOperationFinderTest.java | 4 +- .../DefaultCoordinateOperationFactoryTest.java | 2 +- .../privy/PositionalAccuracyConstantTest.java | 68 ------ .../sis/storage/geotiff/reader/CRSBuilder.java | 2 +- .../storage/geotiff/writer/CompressionChannel.java | 2 +- .../apache/sis/storage/base/MetadataBuilder.java | 2 +- .../sis/util/collection/WeakValueHashMap.java | 24 ++ netbeans-project/nbproject/project.properties | 1 + .../main/org/apache/sis/gui/internal/FontGIS.java | 2 +- 48 files changed, 664 insertions(+), 388 deletions(-) diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultMeasureReference.java index 924fa1f2a9,cfd4a3e88c..a92bd391b0 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultMeasureReference.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultMeasureReference.java @@@ -24,12 -24,10 +24,13 @@@ import org.opengis.util.InternationalSt import org.opengis.metadata.Identifier; import org.opengis.metadata.quality.Element; import org.apache.sis.util.privy.CollectionsExt; + import org.apache.sis.util.iso.Types; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.metadata.quality.MeasureReference; +// Specific to the main branch: +import org.opengis.annotation.UML; +import static org.opengis.annotation.Obligation.OPTIONAL; +import static org.opengis.annotation.Obligation.CONDITIONAL; +import static org.opengis.annotation.Specification.UNSPECIFIED; /** diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/DefaultRecordType.java index 00789215e4,55bb94d6ef..9e7965f145 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/DefaultRecordType.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/DefaultRecordType.java @@@ -42,8 -42,11 +42,8 @@@ import org.apache.sis.util.CharSequence import org.apache.sis.util.ObjectConverters; import org.apache.sis.util.resources.Errors; import org.apache.sis.converter.SurjectiveConverter; - import org.apache.sis.metadata.internal.RecordSchemaSIS; + import org.apache.sis.metadata.privy.RecordSchemaSIS; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.util.NameFactory; - /** * An immutable definition of the type of a {@linkplain DefaultRecord record}. diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CommonCRS.java index c4ce6961ad,cb5394aeac..e894c0d817 --- 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 @@@ -2086,9 -2084,17 +2086,17 @@@ public enum CommonCRS */ public boolean datumUsedBy(final CoordinateReferenceSystem crs) { for (final SingleCRS component : CRS.getSingleComponents(crs)) { - if (ReferencingUtilities.uses(component, datum)) { + if (Utilities.equalsIgnoreMetadata(datum, component.getDatum())) { return true; } - final var ensemble = component.getDatumEnsemble(); ++ final var ensemble = getDatumEnsemble(component); + if (ensemble != null) { + for (final Datum member : ensemble.getMembers()) { + if (Utilities.equalsIgnoreMetadata(datum, member)) { + return true; + } + } + } } return false; } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/PseudoDatum.java index d5ef3f68e1,ee840991a4..83fd8e8334 --- 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 @@@ -26,7 -27,10 +27,8 @@@ import java.util.function.Function import java.io.Serializable; import org.opengis.util.GenericName; import org.opengis.util.InternationalString; + import org.opengis.metadata.quality.PositionalAccuracy; 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; @@@ -200,6 -201,224 +182,210 @@@ public abstract class PseudoDatum<D ext return datum; } + /** + * Returns the datum or pseudo-datum of the result of an operation between the given geodetic <abbr>CRS</abbr>s. + * If the two given coordinate reference systems are associated to the same datum, then this method returns + * the <var>target</var> datum. Otherwise, this method returns a pseudo-datum for the largest ensemble which + * fully contains the datum or datum ensemble of the other <abbr>CRS</abbr>. If none of the <var>source</var> + * or <var>target</var> datum ensembles met that criterion, then this method returns an empty value. + * A non-empty value means that it is okay, for low accuracy requirements, to ignore the datum shift. + * + * @param source the source <abbr>CRS</abbr> of a coordinate operation. + * @param target the target <abbr>CRS</abbr> of a coordinate operation. + * @return datum or pseudo-datum of the coordinate operation result if it is okay to ignore datum shift. + */ + public static Optional<GeodeticDatum> ofOperation(final GeodeticCRS source, final GeodeticCRS target) { + return ofOperation(source, source.getDatum(), + target, target.getDatum(), + Geodetic::new); + } + + /** + * Returns the datum or pseudo-datum of the result of an operation between the given vertical <abbr>CRS</abbr>s. + * See {@link #ofOperation(GeodeticCRS, GeodeticCRS)} for more information. + * + * @param source the source <abbr>CRS</abbr> of a coordinate operation. + * @param target the target <abbr>CRS</abbr> of a coordinate operation. + * @return datum or pseudo-datum of the coordinate operation result if it is okay to ignore datum shift. + */ + public static Optional<VerticalDatum> ofOperation(final VerticalCRS source, final VerticalCRS target) { + return ofOperation(source, source.getDatum(), + target, target.getDatum(), + Vertical::new); + } + + /** + * Returns the datum or pseudo-datum of the result of an operation between the given temporal <abbr>CRS</abbr>s. + * See {@link #ofOperation(GeodeticCRS, GeodeticCRS)} for more information. + * + * @param source the source <abbr>CRS</abbr> of a coordinate operation. + * @param target the target <abbr>CRS</abbr> of a coordinate operation. + * @return datum or pseudo-datum of the coordinate operation result if it is okay to ignore datum shift. + */ + public static Optional<TemporalDatum> ofOperation(final TemporalCRS source, final TemporalCRS target) { + return ofOperation(source, source.getDatum(), + target, target.getDatum(), + Time::new); + } + - /** - * Returns the datum or pseudo-datum of the result of an operation between the given parametric <abbr>CRS</abbr>s. - * See {@link #ofOperation(GeodeticCRS, GeodeticCRS)} for more information. - * - * @param source the source <abbr>CRS</abbr> of a coordinate operation. - * @param target the target <abbr>CRS</abbr> of a coordinate operation. - * @return datum or pseudo-datum of the coordinate operation result if it is okay to ignore datum shift. - */ - public static Optional<ParametricDatum> ofOperation(final ParametricCRS source, final ParametricCRS target) { - return ofOperation(source, source.getDatum(), - target, target.getDatum(), - Parametric::new); - } - + /** + * Returns the datum or pseudo-datum of the result of an operation between the given engineering <abbr>CRS</abbr>s. + * See {@link #ofOperation(GeodeticCRS, GeodeticCRS)} for more information. + * + * @param source the source <abbr>CRS</abbr> of a coordinate operation. + * @param target the target <abbr>CRS</abbr> of a coordinate operation. + * @return datum or pseudo-datum of the coordinate operation result if it is okay to ignore datum shift. + */ + public static Optional<EngineeringDatum> ofOperation(final EngineeringCRS source, final EngineeringCRS target) { + return ofOperation(source, source.getDatum(), + target, target.getDatum(), + Engineering::new); + } + + /** + * Returns the datum or pseudo-datum of a coordinate operation from <var>source</var> to <var>target</var>. + * If the two given coordinate reference systems are associated to the same datum, then this method returns + * the <var>target</var> datum. Otherwise, this method returns a pseudo-datum for the largest ensemble which + * fully contains the datum or datum ensemble of the other <abbr>CRS</abbr>. If none of the <var>source</var> + * or <var>target</var> datum ensembles met that criterion, then this method returns an empty value. + * A non-empty value means that it is okay, for low accuracy requirements, to ignore the datum shift. + * + * @param source the source <abbr>CRS</abbr> of a coordinate operation. + * @param sourceDatum the datum of the source <abbr>CRS</abbr>. + * @param target the target <abbr>CRS</abbr> of a coordinate operation. + * @param targetDatum the datum of the target <abbr>CRS</abbr>. + * @param constructor function to invoke for wrapping a datum ensemble in a pseudo-datum. + * @return datum or pseudo-datum of the coordinate operation result if it is okay to ignore datum shift. + */ + @SuppressWarnings("unchecked") // Casts are safe because callers know the method signature of <D>. + private static <C extends SingleCRS, D extends Datum, R extends IdentifiedObject> Optional<R> ofOperation( + final C source, final R sourceDatum, + final C target, final R targetDatum, - final Function<DatumEnsemble<D>, R> constructor) ++ final Function<DefaultDatumEnsemble<D>, R> constructor) + { + if (sourceDatum != null && Utilities.equalsIgnoreMetadata(sourceDatum, targetDatum)) { + return Optional.of(targetDatum); + } - DatumEnsemble<D> sourceEnsemble; - DatumEnsemble<D> targetEnsemble; - DatumEnsemble<D> selected; - if ((isMember(selected = targetEnsemble = (DatumEnsemble<D>) target.getDatumEnsemble(), sourceDatum)) || - (isMember(selected = sourceEnsemble = (DatumEnsemble<D>) source.getDatumEnsemble(), targetDatum))) ++ DefaultDatumEnsemble<D> sourceEnsemble; ++ DefaultDatumEnsemble<D> targetEnsemble; ++ DefaultDatumEnsemble<D> selected; ++ if ((isMember(selected = targetEnsemble = (DefaultDatumEnsemble<D>) getDatumEnsemble(target), sourceDatum)) || ++ (isMember(selected = sourceEnsemble = (DefaultDatumEnsemble<D>) getDatumEnsemble(source), targetDatum))) + { + return Optional.of(constructor.apply(selected)); + } + if (sourceEnsemble != null && targetEnsemble != null) { + selected = targetEnsemble; + Collection<D> large = targetEnsemble.getMembers(); + Collection<D> small = sourceEnsemble.getMembers(); + if (small.size() > large.size()) { + selected = sourceEnsemble; + var t = large; + large = small; + small = t; + } + small = new ArrayDeque<>(small); + for (final Datum member : large) { + final Iterator<D> it = small.iterator(); + while (it.hasNext()) { + if (Utilities.equalsIgnoreMetadata(member, it.next())) { + it.remove(); + if (small.isEmpty()) { + /* + * Found all members of the smaller ensemble. Take the larger ensemble, + * as it contains both ensembles and should have conservative accuracy. + */ + return Optional.of(constructor.apply(selected)); + } + break; // For removing only the first match. + } + } + } + } + return Optional.empty(); + } + + /** + * Returns whether the given datum is a member of the given ensemble. + * + * @param datum the datum to test, or {@code null}. + * @param ensemble the ensemble to test, or {@code null}. + * @return whether the ensemble contains the given datum. + */ - private static boolean isMember(final DatumEnsemble<?> ensemble, final IdentifiedObject datum) { ++ private static boolean isMember(final DefaultDatumEnsemble<?> ensemble, final IdentifiedObject datum) { + if (ensemble != null) { + for (final Datum member : ensemble.getMembers()) { + if (Utilities.equalsIgnoreMetadata(datum, member)) { + return true; + } + } + } + return false; + } + + /** + * Returns the datum or ensemble of a coordinate operation from <var>source</var> to <var>target</var>. + * If the two given coordinate reference systems are associated to the same datum, then this method returns + * the <var>target</var> datum. Otherwise, this method returns the largest ensemble which fully contains the + * datum or datum ensemble of the other <abbr>CRS</abbr>. If none of the <var>source</var> or <var>target</var> + * datum ensembles met that criterion, then this method returns an empty value. + * A non-empty value means that it is okay, for low accuracy requirements, to ignore the datum shift. + * + * <p>This is an alternative to the {@code ofOperation(…)} methods when the caller does not need to view + * the returned object as a datum.</p> + * + * @param source the source <abbr>CRS</abbr> of a coordinate operation, or {@code null}. + * @param target the target <abbr>CRS</abbr> of a coordinate operation, or {@code null}. + * @return datum or datum ensemble of the coordinate operation result if it is okay to ignore datum shift. + */ + public static Optional<IdentifiedObject> getDatumOrEnsemble(final SingleCRS source, final SingleCRS target) { + if (source == null) return Optional.ofNullable(getDatumOrEnsemble(target)); + if (target == null) return Optional.ofNullable(getDatumOrEnsemble(source)); + return ofOperation(source, source.getDatum(), + target, target.getDatum(), + (ensemble) -> 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 + * returned 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(); ++ return getDatumEnsemble(crs); + } + + /** + * If the given object is a datum ensemble or a wrapper for a datum ensemble, returns its accuracy. - * This method recognizes the {@link DatumEnsemble} and {@link PseudoDatum} types. ++ * This method recognizes the {@link DefaultDatumEnsemble} and {@link PseudoDatum} types. + * + * @param object the object from which to get the ensemble accuracy, or {@code null}. + * @return the datum ensemble accuracy if the given object is a datum ensemble or a wrapper. + * @throws NullPointerException if the given object should provide an accuracy but didn't. + */ + public static Optional<PositionalAccuracy> getEnsembleAccuracy(final IdentifiedObject object) { - final DatumEnsemble<?> ensemble; - if (object instanceof DatumEnsemble<?>) { - ensemble = (DatumEnsemble<?>) object; ++ final DefaultDatumEnsemble<?> ensemble; ++ if (object instanceof DefaultDatumEnsemble<?>) { ++ ensemble = (DefaultDatumEnsemble<?>) object; + } else if (object instanceof PseudoDatum<?>) { + ensemble = ((PseudoDatum<?>) object).ensemble; + } else { + return Optional.empty(); + } + return Optional.of(ensemble.getEnsembleAccuracy()); // Intentional NullPointerException if this property is null. + } + /** * Returns the GeoAPI interface of the ensemble members. * It should also be the interface implemented by this class. @@@ -241,14 -460,27 +427,16 @@@ return ensemble.getIdentifiers(); } - /** - * Returns the usage of the datum ensemble. - * - * @return {@code ensemble.getDomains()}. - * @hidden - */ - @Override - public Collection<ObjectDomain> getDomains() { - return ensemble.getDomains(); - } - /** - * Returns the anchor point common to all datum members, if any. + * Returns the domain of validity common to all datum members, if any. * * @return value common to all ensemble members, or {@code null} if none. + * @hidden */ @Override - public InternationalString getAnchorPoint() { - return getCommonNullableValue(Datum::getAnchorPoint); + @Deprecated + public Extent getDomainOfValidity() { + return getCommonNullableValue(Datum::getDomainOfValidity); } /** @@@ -262,25 -496,78 +452,29 @@@ } /** - * Returns the domain of validity common to all datum members, if any. + * Returns the anchor point common to all datum members, if any. * * @return value common to all ensemble members, or {@code null} if none. + * @hidden */ @Override - public Extent getDomainOfValidity() { - return getCommonNullableValue(Datum::getDomainOfValidity); + @Deprecated + public InternationalString getAnchorPoint() { + return getCommonNullableValue(Datum::getAnchorPoint); } - /** - * 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 the scope common to all datum members, if any. + * Returns the realization epoch common to all datum members, if any. * * @return value common to all ensemble members, or {@code null} if none. + * @hidden */ @Override - public InternationalString getScope() { - return getCommonNullableValue(Datum::getScope); + @Deprecated + public Date getRealizationEpoch() { + return getCommonNullableValue(Datum::getRealizationEpoch); } - /** - * 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 {@code null}. @@@ -495,14 -782,25 +689,13 @@@ check: if (it.hasNext()) 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); - } - /** - * Returns a datum type which is common to all members of the datum ensemble. - * - * @return the common datum type. + * @deprecated Replaced by {@link #getRealizationMethod()}. */ @Override + @Deprecated public VerticalDatumType getVerticalDatumType() { - return getCommonMandatoryValue(VerticalDatum::getVerticalDatumType); + return getCommonNullableValue(VerticalDatum::getVerticalDatumType); } } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java index 12423894d1,039ae92ea8..ae85930faa --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java @@@ -117,16 -120,10 +120,13 @@@ import org.apache.sis.measure.Measureme import org.apache.sis.measure.NumberRange; import org.apache.sis.measure.Units; import org.apache.sis.pending.jdk.JDK16; - import static org.apache.sis.util.privy.Constants.UTC; - import static org.apache.sis.util.Utilities.equalsIgnoreMetadata; - import static org.apache.sis.referencing.internal.ServicesForMetadata.CONNECTION; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.metadata.Identifier; -import org.opengis.referencing.ObjectDomain; +// Specific to the main branch: +import org.apache.sis.referencing.internal.ServicesForMetadata; +import org.apache.sis.temporal.TemporalDate; +import org.apache.sis.referencing.cs.DefaultParametricCS; +import org.apache.sis.referencing.datum.DefaultParametricDatum; +import org.apache.sis.referencing.factory.GeodeticObjectFactory; /** diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java index ad2cc220c3,fc23726ebe..c2b71822c6 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationFinder.java @@@ -743,9 -753,9 +749,9 @@@ public class CoordinateOperationFinder CoordinateSystem interpolationCS = interpolationCRS.getCoordinateSystem(); if (!(interpolationCS instanceof EllipsoidalCS)) { final EllipsoidalCS cs = CommonCRS.WGS84.geographic3D().getCoordinateSystem(); - if (!equalsIgnoreMetadata(interpolationCS, cs)) { + if (!Utilities.equalsIgnoreMetadata(interpolationCS, cs)) { final GeographicCRS stepCRS = factorySIS.crsFactory - .createGeographicCRS(derivedFrom(sourceCRS), sourceCRS.getDatum(), sourceCRS.getDatumEnsemble(), cs); + .createGeographicCRS(derivedFrom(sourceCRS), PseudoDatum.of(sourceCRS), cs); step1 = createOperation(sourceCRS, toAuthorityDefinition(GeographicCRS.class, stepCRS)); interpolationCRS = step1.getTargetCRS(); interpolationCS = interpolationCRS.getCoordinateSystem(); diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java index 036fb14c51,8c97dcf7c4..384290d9da --- 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 @@@ -212,75 -206,6 +212,51 @@@ public final class ReferencingUtilitie return (types.length != 0) ? types[0] : type; } + /** + * Copies all {@link SingleCRS} components from the given source to the given collection. + * For each {@link CompoundCRS} element found in the iteration, this method replaces the + * {@code CompoundCRS} by its {@linkplain CompoundCRS#getComponents() components}, which + * may themselves have other {@code CompoundCRS}. Those replacements are performed recursively + * until we obtain a flat view of CRS components. + * + * @param source the collection of single or compound CRS. + * @param addTo where to add the single CRS in order to obtain a flat view of {@code source}. + * @return {@code true} if this method found only single CRS in {@code source}, in which case {@code addTo} + * got the same content (assuming that {@code addTo} was empty prior this method call). + * @throws NoSuchElementException if a CRS component is missing. + * @throws ClassCastException if a CRS is neither a {@link SingleCRS} or a {@link CompoundCRS}. + * + * @see org.apache.sis.referencing.CRS#getSingleComponents(CoordinateReferenceSystem) + */ + public static boolean getSingleComponents(final Iterable<? extends CoordinateReferenceSystem> source, + final Collection<? super SingleCRS> addTo) throws ClassCastException + { + boolean sameContent = true; + for (final CoordinateReferenceSystem candidate : source) { + if (candidate instanceof CompoundCRS) { + getSingleComponents(((CompoundCRS) candidate).getComponents(), addTo); + sameContent = false; + } else if (candidate instanceof SingleCRS) { + addTo.add((SingleCRS) candidate); + } else { + /* + * Illegal class. Try to provide a better error message, in particular when the CRS component + * is nil because it is an unresolved xlink in a GML document. Nil objects are proxies, which + * have hard to understand class names. + */ + final String message; + if (candidate instanceof NilObject) { + message = Errors.format(Errors.Keys.NilObject_1, Identifiers.getNilReason((NilObject) candidate)); + throw new NoSuchElementException(message); + } else { + message = Errors.format(Errors.Keys.NestedElementNotAllowed_1, getInterface(candidate)); + throw new ClassCastException(message); + } + } + } + return sameContent; + } + - /** - * Returns whether the given <abbr>CRS</abbr> uses the given datum. - * - * @param crs the <abbr>CRS</abbr>, or {@code null}. - * @param datum the datum to compare with the <abbr>CRS</abbr> datum or datum ensemble. - * @return whether the given CRS <abbr>CRS</abbr> uses the specified datum. - */ - public static boolean uses(final SingleCRS crs, final Datum datum) { - if (crs != null && datum != null) { - if (Utilities.equalsIgnoreMetadata(crs.getDatum(), datum)) { - return true; - } - final var ensemble = MissingMethods.getDatumEnsemble(crs); - if (ensemble != null) { - for (final Datum member : ensemble.getMembers()) { - if (Utilities.equalsIgnoreMetadata(member, datum)) { - return true; - } - } - } - } - return false; - } - /** * 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, diff --cc netbeans-project/nbproject/project.properties index 028958f6db,46801fed4b..da94918e12 --- a/netbeans-project/nbproject/project.properties +++ b/netbeans-project/nbproject/project.properties @@@ -110,8 -110,10 +110,9 @@@ test.options = --add-modules jama,Geogr --add-exports org.apache.sis.metadata/org.apache.sis.xml.privy=org.apache.sis.storage.geotiff \ --add-exports org.apache.sis.metadata/org.apache.sis.xml.bind.gcx=org.apache.sis.referencing \ --add-exports org.apache.sis.metadata/org.apache.sis.metadata.privy=org.apache.sis.referencing.gazetteer \ + --add-exports org.apache.sis.referencing/org.apache.sis.referencing.internal=org.apache.sis.openoffice \ --add-exports org.apache.sis.feature/org.apache.sis.feature.privy=org.apache.sis.storage.sql \ - --add-exports org.apache.sis.feature/org.apache.sis.geometry.wrapper.jts=org.apache.sis.storage.sql,org.apache.sis.portrayal.map \ - --add-exports org.apache.sis.storage/org.apache.sis.storage.base=org.apache.sis.portrayal.map \ + --add-exports org.apache.sis.feature/org.apache.sis.geometry.wrapper.jts=org.apache.sis.storage.sql \ --add-exports org.apache.sis.storage/org.apache.sis.storage.test=${modules.list} #