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 b35ebd489f42d2e5a37b1a51f3ea7d7cab173df1 Merge: eaa46cbfba b86dbe8b92 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Apr 5 16:05:56 2024 +0200 Merge branch 'geoapi-3.1'. This is the beginning of an upgrade to ISO 19111:2019. .../org/apache/sis/metadata/PropertyAccessor.java | 7 +- .../apache/sis/metadata/PropertyComparator.java | 2 +- .../main/org/apache/sis/util/iso/Types.java | 2 +- .../apache/sis/metadata/PropertyAccessorTest.java | 22 +-- .../apache/sis/test/mock/GeographicCRSMock.java | 45 ++++++ .../org/apache/sis/test/mock/VerticalCRSMock.java | 34 ++--- .../test/org/apache/sis/util/iso/TypesTest.java | 1 + .../sis/openoffice/ReferencingFunctions.java | 2 +- .../main/module-info.java | 1 + .../sis/coordinate/AbstractCoordinateSet.java | 122 +++++++++++++++ .../sis/coordinate/DefaultCoordinateMetadata.java | 169 +++++++++++++++++++++ .../org/apache/sis/coordinate/package-info.java | 28 ++++ .../apache/sis/geometry/WraparoundAdjustment.java | 2 +- .../main/org/apache/sis/io/wkt/Convention.java | 3 +- .../main/org/apache/sis/io/wkt/Formatter.java | 24 ++- .../main/org/apache/sis/io/wkt/VerticalInfo.java | 4 +- .../main/org/apache/sis/io/wkt/WKTDictionary.java | 20 ++- .../geoapi/referencing/DynamicReferenceFrame.java | 36 +++++ .../pending/geoapi/referencing/package-info.java | 21 +++ .../main/org/apache/sis/referencing/CRS.java | 67 +++++++- .../org/apache/sis/referencing/DisplayName.java | 84 ++++++++++ .../apache/sis/referencing/IdentifiedObjects.java | 24 ++- .../sis/referencing/StandardDefinitions.java | 4 +- .../sis/referencing/crs/DefaultCompoundCRS.java | 27 ++-- .../sis/referencing/crs/DefaultDerivedCRS.java | 10 +- .../sis/referencing/crs/DefaultEngineeringCRS.java | 2 + .../sis/referencing/crs/DefaultImageCRS.java | 4 + .../org/apache/sis/referencing/crs/SubTypes.java | 7 +- .../sis/referencing/cs/DefaultUserDefinedCS.java | 3 + .../org/apache/sis/referencing/cs/SubTypes.java | 1 + .../sis/referencing/datum/AbstractDatum.java | 103 +++++++++---- .../referencing/datum/DefaultEngineeringDatum.java | 8 +- .../referencing/datum/DefaultGeodeticDatum.java | 8 +- .../sis/referencing/datum/DefaultImageDatum.java | 14 +- .../referencing/datum/DefaultParametricDatum.java | 9 +- .../referencing/datum/DefaultTemporalDatum.java | 8 +- .../referencing/datum/DefaultVerticalDatum.java | 17 ++- .../org/apache/sis/referencing/datum/SubTypes.java | 1 + .../apache/sis/referencing/datum/package-info.java | 2 +- .../referencing/factory/AuthorityFactoryProxy.java | 25 +-- .../factory/CommonAuthorityFactory.java | 30 ++-- .../factory/ConcurrentAuthorityFactory.java | 19 ++- .../factory/GeodeticAuthorityFactory.java | 61 +++++++- .../referencing/factory/GeodeticObjectFactory.java | 22 ++- .../factory/IdentifiedObjectFinder.java | 4 +- .../factory/MultiAuthoritiesFactory.java | 22 ++- .../referencing/factory/sql/AuthorityCodes.java | 8 +- .../referencing/factory/sql/EPSGDataAccess.java | 37 +++-- .../sis/referencing/factory/sql/package-info.java | 2 +- .../referencing/internal/EPSGFactoryProxyCRS.java | 1 + .../internal/EPSGFactoryProxyDatum.java | 1 + .../org/apache/sis/referencing/internal/Epoch.java | 93 ++++++++++++ .../apache/sis/referencing/internal/Legacy.java | 51 +++++++ .../apache/sis/referencing/internal/Resources.java | 5 + .../sis/referencing/internal/Resources.properties | 1 + .../referencing/internal/Resources_fr.properties | 1 + .../operation/AbstractCoordinateOperation.java | 91 +++++++++-- .../operation/DefaultConcatenatedOperation.java | 4 +- .../referencing/operation/DefaultConversion.java | 5 +- .../DefaultCoordinateOperationFactory.java | 2 +- .../operation/TransformedCoordinateSet.java | 148 ++++++++++++++++++ .../sis/referencing/privy/AxisDirections.java | 8 +- .../apache/sis/referencing/privy/WKTKeywords.java | 7 +- .../sis/xml/bind/referencing/CD_ImageDatum.java | 1 + .../sis/xml/bind/referencing/CS_UserDefinedCS.java | 1 + .../sis/io/wkt/GeodeticObjectParserTest.java | 2 +- .../org/apache/sis/io/wkt/WKTDictionaryTest.java | 20 ++- .../sis/parameter/DefaultParameterValueTest.java | 6 +- .../sis/referencing/AuthorityFactoriesTest.java | 17 ++- .../sis/referencing/EPSGFactoryFallbackTest.java | 1 + .../sis/referencing/crs/DefaultImageCRSTest.java | 1 + .../apache/sis/referencing/crs/HardCodedCRS.java | 1 + .../referencing/cs/DefaultCylindricalCSTest.java | 2 +- .../sis/referencing/cs/DefaultPolarCSTest.java | 2 +- .../sis/referencing/cs/DefaultSphericalCSTest.java | 2 +- .../apache/sis/referencing/cs/HardCodedAxes.java | 4 +- .../datum/DefaultGeodeticDatumTest.java | 35 +++-- .../datum/DefaultVerticalDatumTest.java | 2 +- .../sis/referencing/datum/HardCodedDatum.java | 1 + .../factory/CommonAuthorityFactoryTest.java | 18 ++- .../factory/MultiAuthoritiesFactoryTest.java | 2 +- .../referencing/factory/sql/EPSGFactoryTest.java | 12 +- .../apache/sis/referencing/internal/EpochTest.java | 92 +++++++++++ .../operation/SingleOperationMarshallingTest.java | 8 +- .../operation/provider/ProvidersTest.java | 1 - .../sis/referencing/privy/AxisDirectionsTest.java | 2 + .../apache/sis/test/integration/MetadataTest.java | 81 +++++----- .../sis/test/integration/MetadataVerticalTest.java | 6 +- .../org/apache/sis/storage/netcdf/base/Axis.java | 2 +- .../apache/sis/storage/netcdf/base/Linearizer.java | 5 +- .../main/org/apache/sis/storage/wkt/Store.java | 5 +- .../main/org/apache/sis/util/Utilities.java | 12 +- .../sis/util/logging/MonolineFormatterTest.java | 4 + .../apache/sis/gui/referencing/AuthorityCodes.java | 3 +- 94 files changed, 1610 insertions(+), 337 deletions(-) diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/Types.java index c9de32dd5a,af97040987..f92b64afc2 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/Types.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/Types.java @@@ -497,8 -506,8 +497,8 @@@ public final class Types extends Stati * The package prefix (e.g. {@code "CI_"} in {@code "CI_Citation"}) can be omitted. * The flexibility is provided for allowing transition to newer ISO standards, * which are dropping the package prefixes. - * For example, {@code "AxisDirection"} in ISO 19111:2007 - * has been renamed {@code "AxisDirection"} in ISO 19111:2018. + * For example, {@code "CS_AxisDirection"} in ISO 19111:2007 - * has been renamed {@code "AxisDirection"} in ISO 19111:2018. ++ * has been renamed {@code "AxisDirection"} in ISO 19111:2019. * * <p>Only identifiers for the stable part of GeoAPI or for some Apache SIS classes are recognized. * This method does not handle the identifiers for interfaces in the {@code geoapi-pending} module.</p> diff --cc endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyAccessorTest.java index b76c0b5be4,b3389026ae..e5a8c9a11d --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyAccessorTest.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyAccessorTest.java @@@ -238,17 -240,16 +239,16 @@@ public final class PropertyAccessorTes */ @Test public void testConstructorWithCovariantReturnType() { - final Class<?> type = GeographicCRS.class; - assertMappingEquals(new PropertyAccessor(type, type, type), - //……Declaring type……………………………Method……………………………………………JavaBeans……………………………UML identifier………………Sentence…………………………………Type………………………………………………………… - GeographicCRS.class, "getCoordinateSystem", "coordinateSystem", "coordinateSystem", "Coordinate system", EllipsoidalCS.class, // Covariant return type - GeodeticCRS.class, "getDatum", "datum", "datum", "Datum", GeodeticDatum.class, // Covariant return type - IdentifiedObject.class, "getName", "name", "name", "Name", ReferenceIdentifier.class, - IdentifiedObject.class, "getAlias", "alias", "alias", "Alias", GenericName[].class, - ReferenceSystem.class, "getDomainOfValidity", "domainOfValidity", "domainOfValidity", "Domain of validity", Extent.class, - IdentifiedObject.class, "getIdentifiers", "identifiers", "identifier", "Identifiers", ReferenceIdentifier[].class, - IdentifiedObject.class, "getRemarks", "remarks", "remarks", "Remarks", InternationalString.class, - ReferenceSystem.class, "getScope", "scope", "SC_CRS.scope", "Scope", InternationalString.class); + assertMappingEquals(new PropertyAccessor(GeographicCRS.class, GeographicCRSMock.class, GeographicCRSMock.class), + //……Declaring type……………………………Method……………………………………………JavaBeans……………………………UML identifier……………………Sentence………………………………Type………………………………………………………… + GeographicCRS.class, "getCoordinateSystem", "coordinateSystem", "coordinateSystem", "Coordinate system", EllipsoidalCS.class, // Covariant return type + GeodeticCRS.class, "getDatum", "datum", "datum", "Datum", GeodeticDatum.class, // Covariant return type - GeodeticCRS.class, "getDatumEnsemble", "datumEnsemble", "datumEnsemble", "Datum ensemble", DatumEnsemble.class, // Covariant return type + IdentifiedObject.class, "getName", "name", "name", "Name", ReferenceIdentifier.class, + IdentifiedObject.class, "getAlias", "alias", "alias", "Alias", GenericName[].class, + IdentifiedObject.class, "getIdentifiers", "identifiers", "identifier", "Identifiers", ReferenceIdentifier[].class, - IdentifiedObject.class, "getDomains", "domains", "ObjectUsage.domain", "Domains", ObjectDomain[].class, - IdentifiedObject.class, "getRemarks", "remarks", "remarks", "Remarks", InternationalString.class); ++ ReferenceSystem.class, "getScope", "scope", "SC_CRS.scope", "Scope", InternationalString.class, ++ ReferenceSystem.class, "getDomainOfValidity", "domainOfValidity", "domainOfValidity", "Domain of validity", Extent.class, ++ IdentifiedObject.class, "getRemarks", "remarks", "remarks", "Remarks", InternationalString.class); } /** diff --cc endorsed/src/org.apache.sis.metadata/test/org/apache/sis/test/mock/GeographicCRSMock.java index 0000000000,7a3e43ef1f..c2a0594866 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/test/mock/GeographicCRSMock.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/test/mock/GeographicCRSMock.java @@@ -1,0 -1,45 +1,45 @@@ + /* + * 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.test.mock; + + import jakarta.xml.bind.annotation.XmlType; + import org.opengis.referencing.crs.GeographicCRS; + + + /** + * A dummy class for {@link GeographicCRS}. Used for defining property order in + * {@link org.apache.sis.metadata.PropertyAccessorTest#testConstructorWithCovariantReturnType()}. + * + * @author Martin Desruisseaux (Geomatys) + */ + @XmlType(name = "GeodeticCRSType", propOrder = { + "coordinateSystem", + "datum", - "datumEnsemble", + "name", + "alias", + "identifiers", - "domains", ++ "scope", ++ "domainOfValidity", + "remarks" + }) + public abstract class GeographicCRSMock implements GeographicCRS { + /** + * Do not allow (for now) instantiation of this class. + */ + private GeographicCRSMock() { + } + } diff --cc endorsed/src/org.apache.sis.metadata/test/org/apache/sis/test/mock/VerticalCRSMock.java index e1a52bbc0c,aecb15b43e..82e971b46f --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/test/mock/VerticalCRSMock.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/test/mock/VerticalCRSMock.java @@@ -95,12 -106,13 +95,12 @@@ public final class VerticalCRSMock exte * Creates a new vertical CRS for the given name. * * @param name the CRS, CS, datum and axis name. - * @param up {@code true} if the axis direction is up, or {@code false} if down. - * @param unit the unit of measurement. - * @param method the realization method (geoid, tidal, <i>etc.</i>). * @param minimumValue the minium value. * @param maximumValue the maximum value. + * @param unit the unit of measurement. + * @param up {@code true} if the axis direction is up, or {@code false} if down. */ - private VerticalCRSMock(final String name, final RealizationMethod method, VerticalDatumType type, + private VerticalCRSMock(final String name, VerticalDatumType type, final double minimumValue, final double maximumValue, final Unit<?> unit, final boolean up) { super(name); @@@ -119,19 -132,18 +119,19 @@@ return new Object[] {getCode(), alias, minimumValue, maximumValue, unit, up}; } - @Override public String getAbbreviation() {return up ? "h" : "d";} - @Override public InternationalString getScope() {return null;} - @Override public InternationalString getAnchorPoint() {return null;} - @Override public Date getRealizationEpoch() {return null;} - @Override public Extent getDomainOfValidity() {return null;} - @Override public VerticalDatumType getVerticalDatumType() {return type;} - @Override public VerticalDatum getDatum() {return this;} - @Override public VerticalCS getCoordinateSystem() {return this;} - @Override public int getDimension() {return 1;} - @Override public CoordinateSystemAxis getAxis(int dimension) {return this;} - @Override public AxisDirection getDirection() {return up ? AxisDirection.UP : AxisDirection.DOWN;} - @Override public double getMinimumValue() {return minimumValue;} - @Override public double getMaximumValue() {return maximumValue;} - @Override public RangeMeaning getRangeMeaning() {return RangeMeaning.EXACT;} - @Override public Unit<?> getUnit() {return unit;} + @Override public String getAbbreviation() {return up ? "h" : "d";} + @Override public InternationalString getScope() {return null;} ++ @Override public InternationalString getAnchorPoint() {return null;} ++ @Override public Date getRealizationEpoch() {return null;} + @Override public Extent getDomainOfValidity() {return null;} - @Override public Optional<RealizationMethod> getRealizationMethod() {return Optional.ofNullable(method);} + @Override public VerticalDatumType getVerticalDatumType() {return type;} + @Override public VerticalDatum getDatum() {return this;} + @Override public VerticalCS getCoordinateSystem() {return this;} + @Override public int getDimension() {return 1;} + @Override public CoordinateSystemAxis getAxis(int dimension) {return this;} + @Override public AxisDirection getDirection() {return up ? AxisDirection.UP : AxisDirection.DOWN;} + @Override public double getMinimumValue() {return minimumValue;} + @Override public double getMaximumValue() {return maximumValue;} + @Override public RangeMeaning getRangeMeaning() {return RangeMeaning.EXACT;} + @Override public Unit<?> getUnit() {return unit;} } diff --cc endorsed/src/org.apache.sis.openoffice/main/org/apache/sis/openoffice/ReferencingFunctions.java index 9109fa275f,2701a8bf28..c6a2624b8e --- a/endorsed/src/org.apache.sis.openoffice/main/org/apache/sis/openoffice/ReferencingFunctions.java +++ b/endorsed/src/org.apache.sis.openoffice/main/org/apache/sis/openoffice/ReferencingFunctions.java @@@ -174,8 -173,8 +174,8 @@@ public class ReferencingFunctions exten if (object != null) { return object.getName().getCode(); } - // In Apache SIS implementation, 'getDescriptionText' returns the name. + // In Apache SIS implementation, `getDescriptionText(…)` returns the identified object name. - name = CRS.getAuthorityFactory(null).getDescriptionText(IdentifiedObject.class, codeOrPath).orElse(null); + name = CRS.getAuthorityFactory(null).getDescriptionText(codeOrPath); } catch (Exception exception) { return getLocalizedMessage(exception); } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/coordinate/AbstractCoordinateSet.java index 0000000000,212b77f0f6..bea3303341 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/coordinate/AbstractCoordinateSet.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/coordinate/AbstractCoordinateSet.java @@@ -1,0 -1,80 +1,122 @@@ + /* + * 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.coordinate; + + import java.util.Objects; + import java.io.Serializable; + import org.apache.sis.referencing.IdentifiedObjects; + -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.coordinate.CoordinateSet; -import org.opengis.coordinate.CoordinateMetadata; ++// Specific to the main branch: ++import java.util.Iterator; ++import java.util.stream.Stream; ++import java.util.stream.StreamSupport; ++import static org.opengis.annotation.Obligation.MANDATORY; ++import static org.opengis.annotation.Specification.ISO_19111; ++import org.opengis.annotation.UML; ++import org.opengis.geometry.DirectPosition; + + + /** + * Skeletal implementation of a collection of coordinate tuples referenced to the same <abbr>CRS</abbr> and epoch. + * This implementation is serializable if the coordinate metadata given at construction time is also serializable. + * ++ * <h2>Future evolution</h2> ++ * This class is expected to implement a {@code CoordinateSet} interface after the next GeoAPI release. ++ * + * @author Martin Desruisseaux (Geomatys) + * @version 1.5 + * @since 1.5 + */ -public abstract class AbstractCoordinateSet implements CoordinateSet, Serializable { ++public abstract class AbstractCoordinateSet implements Iterable<DirectPosition>, Serializable { + /** + * Serial number for inter-operability with different versions. + */ - private static final long serialVersionUID = 3656426153519035462L; ++ private static final long serialVersionUID = 3656426153519035461L; + + /** + * Coordinate reference system and epoch (if dynamic) of this coordinate set. + */ - @SuppressWarnings("serial") // Apache SIS implementations of this interface are serializable. - private final CoordinateMetadata metadata; ++ private final DefaultCoordinateMetadata metadata; + + /** + * Creates a new set of coordinate tuples. + * + * @param metadata coordinate reference system and epoch (if dynamic) of this coordinate set. + */ - protected AbstractCoordinateSet(final CoordinateMetadata metadata) { ++ protected AbstractCoordinateSet(final DefaultCoordinateMetadata metadata) { + this.metadata = Objects.requireNonNull(metadata); + } + + /** + * Returns the coordinate metadata to which this coordinate set is referenced. + * ++ * <div class="warning"><b>Upcoming API change</b><br> ++ * {@code DefaultCoordinateMetadata} class may be replaced by {@code CoordinateMetadata} interface ++ * after upgrade to GeoAPI 3.1. ++ * </div> ++ * + * @return coordinate metadata to which this coordinate set is referenced. + */ - @Override - public CoordinateMetadata getCoordinateMetadata() { ++ public DefaultCoordinateMetadata getCoordinateMetadata() { + return metadata; + } + ++ /** ++ * Returns the number of dimensions of coordinate tuples. This is determined by the ++ * {@linkplain DefaultCoordinateMetadata#getCoordinateReferenceSystem() coordinate reference system}. ++ * ++ * @return the number of dimensions of coordinate tuples. ++ */ ++ public int getDimension() { ++ // All methods invoked below are for attributes declared as mandatory. Values shall not be null. ++ return getCoordinateMetadata().getCoordinateReferenceSystem().getCoordinateSystem().getDimension(); ++ } ++ ++ /** ++ * Returns the positions described by coordinate tuples. ++ * ++ * @return position described by coordinate tuples. ++ */ ++ @Override ++ @UML(identifier="coordinateTuple", obligation=MANDATORY, specification=ISO_19111) ++ public abstract Iterator<DirectPosition> iterator(); ++ ++ /** ++ * Returns a stream of coordinate tuples. ++ * Whether the stream is sequential or parallel is implementation dependent. ++ * The default implementation creates a sequential stream. ++ * ++ * @return a sequential or parallel stream of coordinate tuples. ++ */ ++ public Stream<DirectPosition> stream() { ++ return StreamSupport.stream(spliterator(), false); ++ } ++ + /** + * Returns a string representation of this coordinate set for debugging purposes. + * This string representation may change in any future version. + * + * @return a string representation for debugging purposes. + */ + @Override + public String toString() { + final var sb = new StringBuilder("CoordinateSet"); + sb.append('[').append(IdentifiedObjects.getDisplayName(metadata.getCoordinateReferenceSystem(), null)); + metadata.getCoordinateEpoch().ifPresent((epoch) -> sb.append(" @ ").append(epoch)); + return sb.toString(); + } + } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/coordinate/DefaultCoordinateMetadata.java index 0000000000,facb63e4c2..7067078383 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/coordinate/DefaultCoordinateMetadata.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/coordinate/DefaultCoordinateMetadata.java @@@ -1,0 -1,208 +1,169 @@@ + /* + * 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.coordinate; + + import java.util.Objects; + import java.util.Optional; + import java.io.Serializable; + import java.time.temporal.Temporal; + import org.opengis.referencing.crs.CoordinateReferenceSystem; + import org.apache.sis.referencing.IdentifiedObjects; + import org.apache.sis.referencing.privy.WKTUtilities; + import org.apache.sis.referencing.privy.WKTKeywords; + import org.apache.sis.referencing.internal.Resources; + import org.apache.sis.referencing.internal.Epoch; + import org.apache.sis.referencing.CRS; + import org.apache.sis.io.wkt.FormattableObject; + import org.apache.sis.io.wkt.Formatter; + import org.apache.sis.util.ComparisonMode; + import org.apache.sis.util.LenientComparable; + import org.apache.sis.util.Utilities; + -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.coordinate.CoordinateMetadata; - + + /** + * Default implementation of metadata required to reference coordinates. + * Metadata include a coordinate reference system and the epoch at which the coordinates are valid. + * This default implementation provides <i>Well-Known Text</i> support. + * It is immutable and serializable if the CRS and epoch are also serializable. + * ++ * <h2>Future evolution</h2> ++ * This class is expected to implement a {@code CoordinateMetadata} interface after the next GeoAPI release. ++ * + * @author Martin Desruisseaux (Geomatys) + * @version 1.5 + * @since 1.5 + */ + public class DefaultCoordinateMetadata extends FormattableObject - implements CoordinateMetadata, LenientComparable, Serializable ++ implements LenientComparable, Serializable + { + /** + * Serial number for inter-operability with different versions. + */ + private static final long serialVersionUID = -754447822292824735L; + + /** + * The coordinate reference system (<abbr>CRS</abbr>) in which the coordinate tuples are given. + * + * @see #getCoordinateReferenceSystem() + */ + @SuppressWarnings("serial") // Apache SIS implementations of this interface are serializable. + private final CoordinateReferenceSystem crs; + + /** + * Date at which coordinate tuples are valid, or {@code null} if the CRS is not dynamic. + * + * @see #getCoordinateEpoch() + */ + @SuppressWarnings("serial") // Java implementations of this interface are serializable. + private final Temporal epoch; + + /** + * Creates a new coordinate metadata. + * + * @param crs the coordinate reference system (<abbr>CRS</abbr>) in which the coordinate tuples are given. + * @param epoch date at which coordinate tuples are valid, or {@code null} if the CRS is not dynamic. + * @throws IllegalArgumentException if {@code epoch} is null while the CRS is dynamic or has a dynamic component. + */ + public DefaultCoordinateMetadata(final CoordinateReferenceSystem crs, final Temporal epoch) { + this.crs = Objects.requireNonNull(crs); + this.epoch = epoch; + if (epoch == null && CRS.getFrameReferenceEpoch(crs).isPresent()) { + throw new IllegalArgumentException(Resources.format(Resources.Keys.MissingReferenceFrameEpoch_1, + IdentifiedObjects.getDisplayName(crs, null))); + } + } + - /** - * Creates a new coordinate metadata 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 metadata the coordinate metadata to copy. - * - * @see #castOrCopy(CoordinateMetadata) - */ - protected DefaultCoordinateMetadata(final CoordinateMetadata metadata) { - this(metadata.getCoordinateReferenceSystem(), metadata.getCoordinateEpoch().orElse(null)); - } - - /** - * Returns a SIS datum 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 DefaultCoordinateMetadata castOrCopy(final CoordinateMetadata object) { - if (object == null || object instanceof DefaultCoordinateMetadata) { - return (DefaultCoordinateMetadata) object; - } else { - return new DefaultCoordinateMetadata(object); - } - } - + /** + * Returns the <abbr>CRS</abbr> in which the coordinate tuples are given. + * Should never be null in principle, however this implementation does not enforce this restriction. + * + * @return the coordinate reference system (CRS) of coordinate tuples. + */ - @Override + public CoordinateReferenceSystem getCoordinateReferenceSystem() { + return crs; + } + + /** + * Returns the date at which coordinate tuples referenced to a dynamic <abbr>CRS</abbr> are valid. + * + * @return epoch at which coordinate tuples are valid. + */ - @Override + public Optional<Temporal> getCoordinateEpoch() { + return Optional.ofNullable(epoch); + } + + /** + * Returns a hash code value for this coordinate metadata. + * + * @return a hash code value. + */ + @Override + public int hashCode() { + return crs.hashCode() * 11 + Objects.hashCode(epoch); + } + + /** + * Compares this metadata with the given object for equality. + * + * @param obj the object to compare with this metadata. + * @return whether the two objects are equal. + */ + @Override + public final boolean equals(final Object obj) { + return equals(obj, ComparisonMode.STRICT); + } + + /** + * Compares this metadata with the given object for equality. + * + * @param obj the object to compare to {@code this}. + * @param mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or + * {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only + * properties relevant to coordinate transformations. + * @return {@code true} if both objects are equal. + */ + @Override + public boolean equals(final Object obj, final ComparisonMode mode) { + if (this == obj) { + return true; + } + if (obj != null) { + if (mode == ComparisonMode.STRICT) { + if (obj.getClass() == getClass()) { + final var other = (DefaultCoordinateMetadata) obj; + return crs.equals(other.crs) && Objects.equals(epoch, other.epoch); + } - } else if (obj instanceof CoordinateMetadata) { - final var other = (CoordinateMetadata) obj; - return Utilities.deepEquals(getCoordinateReferenceSystem(), other.getCoordinateReferenceSystem(), mode) - && Objects.equals(getCoordinateEpoch(), other.getCoordinateEpoch()); + } + } + return false; + } + + /** + * Formats this metadata as a <i>Well Known Text</i> {@code CoordinateMetadata[…]} element. + * + * @param formatter the formatter where to format the inner content of this WKT element. + * @return {@code "CoordinateMetadata"}. + */ + @Override + protected String formatTo(final Formatter formatter) { + formatter.append(WKTUtilities.toFormattable(crs)); + if (epoch != null) { + formatter.append(new Epoch(epoch)); + } + return WKTKeywords.CoordinateMetadata; + } + } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/VerticalInfo.java index 91564453bf,37275e7cc3..017d807552 --- 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 @@@ -32,6 -31,9 +31,9 @@@ import org.apache.sis.metadata.privy.Ax import org.apache.sis.metadata.iso.extent.DefaultExtent; import org.apache.sis.metadata.iso.extent.DefaultVerticalExtent; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.referencing.datum.RealizationMethod; ++// Specific to the main branch: ++import org.opengis.referencing.datum.VerticalDatumType; + /** * Stores temporary information needed for completing the construction of an {@link DefaultVerticalExtent} instance. diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/pending/geoapi/referencing/DynamicReferenceFrame.java index 0000000000,f35ee94813..d2cf1b37cb mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/pending/geoapi/referencing/DynamicReferenceFrame.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/pending/geoapi/referencing/DynamicReferenceFrame.java @@@ -1,0 -1,38 +1,36 @@@ + /* + * 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.map.service; ++package org.apache.sis.pending.geoapi.referencing; + ++import java.time.temporal.Temporal; ++import org.opengis.referencing.datum.Datum; ++import org.opengis.annotation.UML; + -/** - * Exception that may be thrown by a portraying operation. - * - * @author Johann Sorel (Geomatys) - */ -public class RenderingException extends Exception { ++import static org.opengis.annotation.Obligation.*; ++import static org.opengis.annotation.Specification.*; + - public RenderingException(String message) { - super(message); - } + - public RenderingException(Throwable cause) { - super(cause); - } - - public RenderingException(String message, Throwable cause) { - super(message, cause); - } ++/** ++ * Placeholder for an interface that may be added in GeoAPI 3.1. ++ */ ++public interface DynamicReferenceFrame extends Datum { ++ /** ++ * {@return the epoch to which the coordinates of stations defining the dynamic datum are referenced}. ++ */ ++ @UML(identifier="frameReferenceEpoch", obligation=MANDATORY, specification=ISO_19111) ++ Temporal getFrameReferenceEpoch(); + } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/pending/geoapi/referencing/package-info.java index 0000000000,4307e9c627..3172e99803 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/pending/geoapi/referencing/package-info.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/pending/geoapi/referencing/package-info.java @@@ -1,0 -1,27 +1,21 @@@ + /* + * 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. + */ + + /** - * CQL parser. - * - * @author Johann Sorel (Geomatys) ++ * Place-holder for some interfaces not present in GeoAPI 3.0 but proposed for addition in GeoAPI 3.1. + */ -module org.apache.sis.cql { - requires transitive org.apache.sis.feature; - requires org.locationtech.jts; - requires org.antlr.antlr4.runtime; -} ++package org.apache.sis.pending.geoapi.referencing; diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java index e27e943ae6,3faccae36c..f2df4e7307 --- 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 @@@ -90,8 -91,13 +91,10 @@@ import org.apache.sis.util.privy.Numeri import org.apache.sis.util.resources.Errors; import org.apache.sis.util.logging.Logging; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.geometry.Geometry; -import org.opengis.referencing.ObjectDomain; -import org.opengis.referencing.datum.DynamicReferenceFrame; -import org.opengis.coordinate.CoordinateMetadata; -import org.opengis.metadata.extent.BoundingPolygon; -import org.opengis.metadata.extent.GeographicExtent; +// Specific to the main branch: ++import org.apache.sis.pending.geoapi.referencing.DynamicReferenceFrame; ++import org.apache.sis.coordinate.DefaultCoordinateMetadata; +import org.apache.sis.referencing.internal.Legacy; /** @@@ -618,6 -624,32 +621,32 @@@ public final class CRS extends Static return bestCRS; } + /** + * Finds a mathematical operation that transforms or converts coordinates between the given <abbr>CRS</abbr>s and epochs. + * This method performs the same work as the {@linkplain #findOperation(CoordinateReferenceSystem, CoordinateReferenceSystem, + * GeographicBoundingBox) variant working on CRS objects}, except that the coordinate epochs may be taken in account. + * + * @param source the CRS and epoch of source coordinates. + * @param target the CRS and epoch of target coordinates. + * @param areaOfInterest the area of interest, or {@code null} if none. + * @return the mathematical operation from {@code source} to {@code target}. + * @throws OperationNotFoundException if no operation was found between the given pair of <abbr>CRS</abbr>s and epochs. + * @throws FactoryException if the operation cannot be created for another reason. + * + * @since 1.5 + */ - public static CoordinateOperation findOperation(final CoordinateMetadata source, - final CoordinateMetadata target, ++ public static CoordinateOperation findOperation(final DefaultCoordinateMetadata source, ++ final DefaultCoordinateMetadata target, + final GeographicBoundingBox areaOfInterest) + throws FactoryException + { + // TODO: take epoch in account. + if (source.getCoordinateEpoch().isPresent() || target.getCoordinateEpoch().isPresent()) { + throw new FactoryException("This version of Apache SIS does not yet support coordinate epoch."); + } + return findOperation(source.getCoordinateReferenceSystem(), target.getCoordinateReferenceSystem(), areaOfInterest); + } + /** * Finds a mathematical operation that transforms or converts coordinates from the given source to the * given target coordinate reference system. If an estimation of the geographic area containing the points @@@ -882,6 -956,34 +911,34 @@@ return envelope; } + /** + * Returns the epoch to which the coordinates of stations defining the dynamic CRS are referenced. - * If the CRS is associated to a {@linkplain DynamicReferenceFrame dynamic datum}, then reference ++ * If the CRS is associated to a dynamic reference frame, then the reference + * epoch of that datum is returned. Otherwise if the CRS is {@linkplain CompoundCRS compound}, + * then the first reference epoch found in a component is returned. + * + * @param crs the coordinate reference frame from which to get the epoch, or {@code null}. + * @return epoch to which the coordinates of stations defining the dynamic CRS frame are referenced. + * + * @since 1.5 + */ + public static Optional<Temporal> getFrameReferenceEpoch(final CoordinateReferenceSystem crs) { + if (crs instanceof SingleCRS) { + final Datum datum = ((SingleCRS) crs).getDatum(); + if (datum instanceof DynamicReferenceFrame) { + return Optional.of(((DynamicReferenceFrame) datum).getFrameReferenceEpoch()); + } + } else if (crs instanceof CompoundCRS) { - for (SingleCRS component : ((CompoundCRS) crs).getSingleComponents()) { ++ for (SingleCRS component : getSingleComponents(crs)) { + final Datum datum = component.getDatum(); + if (datum instanceof DynamicReferenceFrame) { + return Optional.of(((DynamicReferenceFrame) datum).getFrameReferenceEpoch()); + } + } + } + return Optional.empty(); + } + /** * Creates a compound coordinate reference system from an ordered list of CRS components. * A CRS is inferred from the given components and the domain of validity is set to the diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/StandardDefinitions.java index a2718d1401,8a66d58abf..015db59586 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/StandardDefinitions.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/StandardDefinitions.java @@@ -75,6 -74,9 +74,9 @@@ import org.apache.sis.measure.Latitude import org.apache.sis.measure.Units; import static org.apache.sis.metadata.privy.ReferencingServices.AUTHALIC_RADIUS; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.referencing.datum.RealizationMethod; ++// Specific to the main branch: ++import org.opengis.referencing.datum.VerticalDatumType; + /** * Definitions of referencing objects identified by the {@link CommonCRS} enumeration values. diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultCompoundCRS.java index 217df1d310,43869b2b63..9fe96587ce --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultCompoundCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultCompoundCRS.java @@@ -51,8 -53,10 +51,8 @@@ import org.apache.sis.util.Utilities import org.apache.sis.util.collection.CheckedContainer; import org.apache.sis.util.collection.Containers; import org.apache.sis.util.resources.Errors; + import org.apache.sis.util.privy.UnmodifiableArrayList; -import org.apache.sis.metadata.privy.Identifiers; + import org.apache.sis.xml.bind.referencing.SC_CRS; -import org.apache.sis.xml.NilObject; import org.apache.sis.io.wkt.Formatter; import org.apache.sis.io.wkt.Convention; diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java index 53d1d17533,e379725baf..f26cb27efb --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java @@@ -127,13 -129,13 +131,13 @@@ public class AbstractDatum extends Abst * <th>Value type</th> * <th>Returned by</th> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_DEFINITION_KEY}</td> + * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_POINT_KEY}</td> * <td>{@link InternationalString} or {@link String}</td> - * <td>{@link #getAnchorPoint()}</td> + * <td>{@link #getAnchorDefinition()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td> - * <td>{@link Date}</td> - * <td>{@link #getRealizationEpoch()}</td> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_EPOCH_KEY}</td> ++ * <td>{@code "anchorEpoch"}</td> + * <td>{@link Temporal}</td> + * <td>{@link #getAnchorEpoch()}</td> * </tr><tr> * <th colspan="3" class="hsep">Defined in parent class (reminder)</th> * </tr><tr> @@@ -161,10 -163,20 +165,19 @@@ * * @param properties the properties to be given to the identified object. */ - @SuppressWarnings("deprecation") public AbstractDatum(final Map<String,?> properties) { super(properties); - realizationEpoch = ImplementationHelper.toMilliseconds(property(properties, REALIZATION_EPOCH_KEY, Date.class)); - anchorDefinition = Types.toInternationalString(properties, ANCHOR_POINT_KEY); - anchorDefinition = Types.toInternationalString(properties, ANCHOR_DEFINITION_KEY); ++ anchorDefinition = Types.toInternationalString(properties, "anchorDefinition"); + if (anchorDefinition == null) { + anchorDefinition = Types.toInternationalString(properties, ANCHOR_POINT_KEY); + } - anchorEpoch = property(properties, ANCHOR_EPOCH_KEY, Temporal.class); ++ anchorEpoch = property(properties, "anchorEpoch", Temporal.class); + if (anchorEpoch == null) { + Date date = property(properties, REALIZATION_EPOCH_KEY, Date.class); + if (date != null) { + anchorEpoch = date.toInstant(); + } + } } /** @@@ -178,8 -190,8 +191,11 @@@ */ protected AbstractDatum(final Datum datum) { super(datum); - realizationEpoch = ImplementationHelper.toMilliseconds(datum.getRealizationEpoch()); - anchorEpoch = datum.getAnchorEpoch().orElse(null); - anchorDefinition = datum.getAnchorDefinition().orElse(null); ++ Date date = datum.getRealizationEpoch(); ++ if (date != null) { ++ anchorEpoch = date.toInstant(); ++ } + anchorDefinition = datum.getAnchorPoint(); } /** @@@ -244,53 -253,58 +257,82 @@@ * </ul> * * @return description, possibly including coordinates, of the point or points used to anchor the datum to the Earth. + * + * @since 1.5 + */ - @Override + public Optional<InternationalString> getAnchorDefinition() { + return Optional.ofNullable(anchorDefinition); + } + + /** + * Returns a description of the point(s) used to anchor the datum to the Earth. + * + * @deprecated Renamed {@link #getAnchorDefinition()} as of ISO 19111:2019. + * + * @return a description of the point(s) used to anchor the datum to the Earth. */ @Override + @Deprecated(since = "1.5") @XmlElement(name = "anchorDefinition") public InternationalString getAnchorPoint() { return anchorDefinition; } /** - * The time after which this datum definition is valid. - * This time may be precise or merely a year. + * Returns the epoch at which a static datum matches a dynamic datum from which it has been derived. + * This time may be precise or merely a year (e.g. 1983 for NAD83). + * + * @return epoch at which a static datum matches a dynamic datum from which it has been derived. * - * <p>If an old datum is superseded by a new datum, then the realization epoch for the new datum - * defines the upper limit for the validity of the old datum.</p> + * @see java.time.Year + * @see java.time.YearMonth + * @see java.time.LocalDate + * + * @since 1.5 + */ - @Override + public Optional<Temporal> getAnchorEpoch() { + return Optional.ofNullable(anchorEpoch); + } + + /** + * The time after which this datum definition is valid. * * @return the time after which this datum definition is valid, or {@code null} if none. + * + * @deprecated Since ISO 19111:2019, replaced by {@link #getAnchorEpoch()}. */ @Override + @Deprecated(since = "1.5") @XmlSchemaType(name = "date") @XmlElement(name = "realizationEpoch") public Date getRealizationEpoch() { - return ImplementationHelper.toDate(realizationEpoch); - return Datum.super.getRealizationEpoch(); ++ return getAnchorEpoch().map(Legacy::toDate).orElse(null); + } + + /** + * Returns the region or timeframe in which this datum is valid, or {@code null} if unspecified. + * + * @return area or region or timeframe in which this datum is valid, or {@code null}. + * + * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019. + */ + @Override + @Deprecated(since = "1.4") + public Extent getDomainOfValidity() { + return Legacy.getDomainOfValidity(getDomains()); + } + + /** + * Returns the domain or limitations of usage, or {@code null} if unspecified. + * + * @return description of domain of usage, or limitations of usage, for which this datum object is valid. + * + * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019. + */ + @Override + @Deprecated(since = "1.4") + public InternationalString getScope() { + return Legacy.getScope(getDomains()); } /** diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java index 10ea0de84d,5e161c9876..6cb79554d2 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java @@@ -88,13 -88,13 +88,13 @@@ public class DefaultEngineeringDatum ex * <td>{@link InternationalString} or {@link String}</td> * <td>{@link #getRemarks()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_DEFINITION_KEY}</td> + * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_POINT_KEY}</td> * <td>{@link InternationalString} or {@link String}</td> - * <td>{@link #getAnchorPoint()}</td> + * <td>{@link #getAnchorDefinition()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td> - * <td>{@link java.util.Date}</td> - * <td>{@link #getRealizationEpoch()}</td> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_EPOCH_KEY}</td> ++ * <td>{@code "anchorEpoch"}</td> + * <td>{@link java.time.temporal.Temporal}</td> + * <td>{@link #getAnchorEpoch()}</td> * </tr> * </table> * diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java index 14d96f2598,b7273fee42..2d9a67779e --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java @@@ -232,13 -232,13 +232,13 @@@ public class DefaultGeodeticDatum exten * <td>{@link InternationalString} or {@link String}</td> * <td>{@link #getRemarks()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_DEFINITION_KEY}</td> + * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_POINT_KEY}</td> * <td>{@link InternationalString} or {@link String}</td> - * <td>{@link #getAnchorPoint()}</td> + * <td>{@link #getAnchorDefinition()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td> - * <td>{@link Date}</td> - * <td>{@link #getRealizationEpoch()}</td> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_EPOCH_KEY}</td> ++ * <td>{@code "anchorEpoch"}</td> + * <td>{@link java.time.temporal.Temporal}</td> + * <td>{@link #getAnchorEpoch()}</td> * </tr> * </table> * diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultImageDatum.java index c3b7ec856a,9100a447af..204fe5b5ee --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultImageDatum.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultImageDatum.java @@@ -104,13 -108,13 +108,13 @@@ public class DefaultImageDatum extends * <td>{@link InternationalString} or {@link String}</td> * <td>{@link #getRemarks()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_DEFINITION_KEY}</td> + * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_POINT_KEY}</td> * <td>{@link InternationalString} or {@link String}</td> - * <td>{@link #getAnchorPoint()}</td> + * <td>{@link #getAnchorDefinition()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td> - * <td>{@link java.util.Date}</td> - * <td>{@link #getRealizationEpoch()}</td> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_EPOCH_KEY}</td> ++ * <td>{@code "anchorEpoch"}</td> + * <td>{@link java.time.temporal.Temporal}</td> + * <td>{@link #getAnchorEpoch()}</td> * </tr> * </table> * diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultParametricDatum.java index 312bd0afef,1037b5b9d0..cc63fdbf93 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultParametricDatum.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultParametricDatum.java @@@ -94,13 -96,13 +93,13 @@@ public class DefaultParametricDatum ext * <td>{@link org.opengis.util.InternationalString} or {@link String}</td> * <td>{@link #getRemarks()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_DEFINITION_KEY}</td> + * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_POINT_KEY}</td> * <td>{@link org.opengis.util.InternationalString} or {@link String}</td> - * <td>{@link #getAnchorPoint()}</td> + * <td>{@link #getAnchorDefinition()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td> - * <td>{@link Date}</td> - * <td>{@link #getRealizationEpoch()}</td> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_EPOCH_KEY}</td> ++ * <td>{@code "anchorEpoch"}</td> + * <td>{@link java.time.temporal.Temporal}</td> + * <td>{@link #getAnchorEpoch()}</td> * </tr> * </table> * diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultTemporalDatum.java index 189c199dc1,cb7a8d5e53..f7aade22ba --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultTemporalDatum.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultTemporalDatum.java @@@ -129,13 -129,13 +129,13 @@@ public class DefaultTemporalDatum exten * <td>{@link InternationalString} or {@link String}</td> * <td>{@link #getRemarks()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_DEFINITION_KEY}</td> + * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_POINT_KEY}</td> * <td>{@link InternationalString} or {@link String}</td> - * <td>{@link #getAnchorPoint()}</td> + * <td>{@link #getAnchorDefinition()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td> - * <td>{@link Date}</td> - * <td>{@link #getRealizationEpoch()}</td> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_EPOCH_KEY}</td> ++ * <td>{@code "anchorEpoch"}</td> + * <td>{@link java.time.temporal.Temporal}</td> + * <td>{@link #getAnchorEpoch()}</td> * </tr> * </table> * diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java index 44ef22bf56,5405bca909..96e209538a --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java @@@ -130,13 -138,13 +131,13 @@@ public class DefaultVerticalDatum exten * <td>{@link InternationalString} or {@link String}</td> * <td>{@link #getRemarks()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_DEFINITION_KEY}</td> + * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_POINT_KEY}</td> * <td>{@link InternationalString} or {@link String}</td> - * <td>{@link #getAnchorPoint()}</td> + * <td>{@link #getAnchorDefinition()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td> - * <td>{@link java.util.Date}</td> - * <td>{@link #getRealizationEpoch()}</td> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_EPOCH_KEY}</td> ++ * <td>{@code "anchorEpoch"}</td> + * <td>{@link java.time.temporal.Temporal}</td> + * <td>{@link #getAnchorEpoch()}</td> * </tr> * </table> * @@@ -255,10 -314,13 +256,12 @@@ } switch (mode) { case STRICT: { - return type().equals(((DefaultVerticalDatum) object).type()); + final var other = (DefaultVerticalDatum) object; - return Objects.equals(method, other.method) && type().equals(other.type()); ++ return type().equals(other.type()); } case BY_CONTRACT: { - return Objects.equals(getVerticalDatumType(), ((VerticalDatum) object).getVerticalDatumType()); + final var other = (VerticalDatum) object; - return Objects.equals(getRealizationMethod(), other.getRealizationMethod()) && - Objects.equals(getVerticalDatumType(), other.getVerticalDatumType()); ++ return Objects.equals(getVerticalDatumType(), other.getVerticalDatumType()); } default: { /* diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java index 193f65e9ce,4f7a0b5c3d..d75513bbe5 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java @@@ -172,13 -172,15 +172,18 @@@ abstract class AuthorityFactoryProxy<T abstract T createFromAPI(AuthorityFactory factory, String code) throws FactoryException; /** - * The proxy for the {@link GeodeticAuthorityFactory#getDescriptionText(String)} method. + * The proxy for the {@link GeodeticAuthorityFactory#getDescriptionText(Class, String)} method. + * + * @param classe the type of object for which to get a description. */ - static final AuthorityFactoryProxy<InternationalString> DESCRIPTION = - new AuthorityFactoryProxy<InternationalString>(InternationalString.class, AuthorityFactoryIdentifier.ANY) { + static final AuthorityFactoryProxy<InternationalString> description(final Class<? extends IdentifiedObject> classe) { + return new AuthorityFactoryProxy<InternationalString>(InternationalString.class, AuthorityFactoryIdentifier.ANY) { - @Override InternationalString createFromAPI(AuthorityFactory factory, String code) throws FactoryException { ++ @Override InternationalString create(GeodeticAuthorityFactory factory, String code) throws FactoryException { + return factory.getDescriptionText(classe, code).orElse(null); + } + @Override InternationalString createFromAPI(AuthorityFactory factory, String code) throws FactoryException { + return factory.getDescriptionText(code); + } @Override AuthorityFactoryProxy<InternationalString> specialize(String typeName) { return this; } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java index e934aeecfc,981f93e539..a5d86d0ee3 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java @@@ -156,20 -152,43 +157,60 @@@ public abstract class GeodeticAuthority * The description can be used for example in a combo box in a graphical user interface. * * <h4>Default implementation</h4> - * The default implementation invokes {@link #createObject(String)} for the given code - * and returns the {@linkplain AbstractIdentifiedObject#getName() object name}. - * This may be costly since it involves a full object creation. + * The default implementation invokes {@link #createObject(Class, String)} for the given class and code, + * then returns the {@linkplain IdentifiedObjects#getDisplayName(IdentifiedObject) display name} of the object. + * This implementation may be costly because it involves a full object creation. * Subclasses are encouraged to provide a more efficient implementation if they can. * + * @param type the type of object for which to get a description. + * @param code value allocated by authority. + * @return a description of the object, or empty if the object exists but has no description. + * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found. + * @throws FactoryException if an error occurred while fetching the description. + * + * @since 1.5 + */ - @Override + public Optional<InternationalString> getDescriptionText(Class<? extends IdentifiedObject> type, String code) + throws FactoryException + { + return Optional.ofNullable(IdentifiedObjects.getDisplayName(createObject(type, code))); + } + ++ /** ++ * Returns a description of the object corresponding to a code. ++ * + * @param code value allocated by authority. + * @return a description of the object, or {@code null} if the object + * corresponding to the specified {@code code} has no description. + * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found. - * @throws FactoryException if an error occurred while fetching the description. ++ * @throws FactoryException if the query failed for some other reason. ++ * ++ * @deprecated This method is ambiguous because the EPSG geodetic registry may allocate ++ * the same code to different kinds of object. + */ + @Override ++ @Deprecated(since = "1.5") + public InternationalString getDescriptionText(final String code) throws FactoryException { - return new SimpleInternationalString(createObject(code).getName().getCode()); ++ return getDescriptionText(IdentifiedObject.class, code).orElse(null); ++ } ++ + /** + * Returns an object of the specified type from a code. This implementation forwards + * the method call to the most specialized methods determined by the given type. + * + * @param <T> the compile-time value of the {@code type} argument. + * @param type the type of object to create. + * @param code value allocated by authority. + * @return the object for the given code. + * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found. + * @throws FactoryException if the object creation failed for some other reason. + * + * @since 1.5 + */ + public final <T extends IdentifiedObject> T createObject(final Class<T> type, final String code) + throws NoSuchAuthorityCodeException, FactoryException + { + return cast(type, (IdentifiedObject) AuthorityFactoryProxy.getInstance(type).create(this, code), code); } /** diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java index bedf50ca0f,2778ac1f24..4e0d34c89f --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java @@@ -140,15 -140,15 +140,15 @@@ import org.apache.sis.xml.XML * </tr><tr> * <td>{@value org.opengis.referencing.ReferenceSystem#SCOPE_KEY}</td> * <td>{@link String} or {@link InternationalString}</td> - * <td>{@link DefaultObjectDomain#getScope()}</td> + * <td>{@link org.apache.sis.referencing.DefaultObjectDomain#getScope()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_DEFINITION_KEY}</td> + * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_POINT_KEY}</td> * <td>{@link InternationalString} or {@link String}</td> - * <td>{@link AbstractDatum#getAnchorPoint()}</td> + * <td>{@link AbstractDatum#getAnchorDefinition()}</td> * </tr><tr> - * <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td> - * <td>{@link Date}</td> - * <td>{@link AbstractDatum#getRealizationEpoch()}</td> - * <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_EPOCH_KEY}</td> - * <td>{@link java.time.temporal.Temporal}</td> - * <td>{@link AbstractDatum#getAnchorEpoch()}</td> ++ * <td>{@value "anchorEpoch"}</td> ++ * <td>{@link java.time.temporal.Temporal}</td> ++ * <td>{@link AbstractDatum#getAnchorEpoch()}</td> * </tr><tr> * <td>{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td> * <td>{@link InternationalString} or {@link String}</td> diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Legacy.java index 73d7c3de26,a5abfd686b..041fa54fbf --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Legacy.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Legacy.java @@@ -29,19 -29,6 +29,31 @@@ import org.apache.sis.referencing.cs.De import org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis; import org.apache.sis.referencing.privy.ReferencingUtilities; +// Specific to the main branch: +import java.util.List; +import java.util.Objects; +import java.util.Collection; ++import java.util.Date; ++import java.time.Instant; ++import java.time.Year; ++import java.time.YearMonth; ++import java.time.LocalDate; ++import java.time.LocalDateTime; ++import java.time.OffsetDateTime; ++import java.time.ZonedDateTime; ++import java.time.ZoneOffset; ++import java.time.LocalTime; ++import java.time.OffsetTime; ++import java.time.temporal.Temporal; +import org.opengis.referencing.ReferenceSystem; +import org.opengis.referencing.IdentifiedObject; +import org.opengis.util.InternationalString; +import org.opengis.metadata.extent.Extent; +import org.opengis.referencing.datum.Datum; +import org.opengis.referencing.operation.CoordinateOperation; +import org.apache.sis.referencing.DefaultObjectDomain; +import org.apache.sis.referencing.AbstractIdentifiedObject; + /** * Utilities related to version 1 of Well Known Text format, or to ISO 19111:2007. @@@ -122,57 -109,4 +134,96 @@@ public final class Legacy } return cs; } + + /** + * Temporary getter method used as a workaround for the lack of this method in GeoAPI 3.0 interface. + * + * @param object the object from which to get the domain. + * @return domain of the given object. + */ + public static Collection<DefaultObjectDomain> getDomains(final IdentifiedObject object) { + if (object instanceof AbstractIdentifiedObject) { + return ((AbstractIdentifiedObject) object).getDomains(); + } + final Extent domainOfValidity; + final InternationalString scope; + if (object instanceof ReferenceSystem) { + final var c = (ReferenceSystem) object; + scope = c.getScope(); + domainOfValidity = c.getDomainOfValidity(); + } else if (object instanceof Datum) { + final var c = (Datum) object; + scope = c.getScope(); + domainOfValidity = c.getDomainOfValidity(); + } else if (object instanceof CoordinateOperation) { + final var c = (CoordinateOperation) object; + scope = c.getScope(); + domainOfValidity = c.getDomainOfValidity(); + } else { + return null; + } + if (scope == null && domainOfValidity == null) { + return null; + } + return List.of(new DefaultObjectDomain(scope, domainOfValidity)); + } + + /** + * Returns the first non-null scope found in the given collection. + * + * @param domains the value of {@link AbstractIdentifiedObject#getDomains()}. + * @return a description of domain of usage, or {@code null} if none. + */ + public static InternationalString getScope(final Collection<DefaultObjectDomain> domains) { + return domains.stream().map(DefaultObjectDomain::getScope).filter(Objects::nonNull).findFirst().orElse(null); + } + + /** + * Returns the first non-null domain of validity found in the given collection. + * + * @param domains the value of {@link AbstractIdentifiedObject#getDomains()}. + * @return the valid domain, or {@code null} if not available. + */ + public static Extent getDomainOfValidity(final Collection<DefaultObjectDomain> domains) { + return domains.stream().map(DefaultObjectDomain::getDomainOfValidity).filter(Objects::nonNull).findFirst().orElse(null); + } ++ ++ /** ++ * Converts a {@link java.time} object to a legacy {@link Date} object. ++ * If the time zone is not specified, UTC is assumed. ++ * ++ * @param t the date to convert. ++ * @return the given temporal object as a date, or {@code null} if the method doesn't know how to convert. ++ * @throws ArithmeticException if numeric overflow occurs. ++ */ ++ public static Date toDate(final Temporal t) { ++ final Instant instant; ++ if (t instanceof Instant) { ++ instant = (Instant) t; ++ } else { ++ final OffsetDateTime odt; ++ if (t instanceof OffsetDateTime) { ++ odt = (OffsetDateTime) t; ++ } else if (t instanceof ZonedDateTime) { ++ odt = ((ZonedDateTime) t).toOffsetDateTime(); ++ } else if (t instanceof LocalDateTime) { ++ odt = ((LocalDateTime) t).atOffset(ZoneOffset.UTC); ++ } else { ++ final LocalDate date; ++ if (t instanceof LocalDate) { ++ date = (LocalDate) t; ++ } else if (t instanceof YearMonth) { ++ date = ((YearMonth) t).atDay(1); ++ } else if (t instanceof Year) { ++ date = ((Year) t).atDay(1); ++ } else { ++ return null; ++ } ++ odt = date.atTime(OffsetTime.of(LocalTime.MIDNIGHT, ZoneOffset.UTC)); ++ } ++ instant = odt.toInstant(); ++ } ++ // Do not use `Date.from(Instant)` because we want the `ArithmeticException` in case of overflow. ++ return new Date(instant.toEpochMilli()); ++ } } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java index a67090b38c,3c6683a62b..f7f4ac8ea2 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java @@@ -41,6 -42,6 +42,7 @@@ import org.opengis.referencing.operatio import org.opengis.referencing.operation.OperationMethod; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.PassThroughOperation; ++import org.opengis.referencing.operation.TransformException; import org.opengis.parameter.GeneralParameterValue; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterValueGroup; @@@ -73,9 -74,9 +75,14 @@@ import org.apache.sis.system.Semaphores import org.apache.sis.system.Loggers; import static org.apache.sis.util.Utilities.deepEquals; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.coordinate.CoordinateSet; -import org.opengis.referencing.operation.TransformException; +// Specific to the main branch: ++import java.time.temporal.Temporal; ++import org.opengis.annotation.UML; ++import static org.opengis.annotation.Obligation.CONDITIONAL; ++import static org.opengis.annotation.Specification.ISO_19111; +import org.opengis.metadata.extent.Extent; +import org.apache.sis.referencing.internal.Legacy; ++import org.apache.sis.coordinate.AbstractCoordinateSet; /** @@@ -538,20 -543,13 +548,46 @@@ check: for (int isTarget=0; ; isTa * in order to interpolate in a grid. This method returns the CRS of the grid where such interpolations * are performed. * - * @return the CRS (neither source or target CRS) required for interpolating the values, or {@code null} if none. + * @return the <abbr>CRS</abbr> required for interpolating the values. */ - public CoordinateReferenceSystem getInterpolationCRS() { - return interpolationCRS; - @Override + public Optional<CoordinateReferenceSystem> getInterpolationCRS() { + return Optional.ofNullable(interpolationCRS); } + /** + * Returns the interpolation CRS of the given coordinate operation, or {@code null} if none. + */ + static CoordinateReferenceSystem getInterpolationCRS(final CoordinateOperation operation) { + return (operation instanceof AbstractCoordinateOperation) - ? ((AbstractCoordinateOperation) operation).getInterpolationCRS() : null; ++ ? ((AbstractCoordinateOperation) operation).getInterpolationCRS().orElse(null) : null; ++ } ++ ++ /** ++ * Returns the date at which source coordinate tuples are valid. ++ * This is mandatory if the <abbr>CRS</abbr> is dynamic. ++ * ++ * @return epoch at which source coordinate tuples are valid. ++ * ++ * @since 1.5 ++ */ ++ @UML(identifier="sourceCoordinateEpoch", obligation=CONDITIONAL, specification=ISO_19111) ++ public Optional<Temporal> getSourceEpoch() { ++ return Optional.empty(); ++ } ++ ++ /** ++ * Returns the date at which target coordinate tuples are valid. ++ * This is mandatory if the <abbr>CRS</abbr> is dynamic. ++ * ++ * @return epoch at which target coordinate tuples are valid. ++ * ++ * @since 1.5 ++ */ ++ @UML(identifier="targetCoordinateEpoch", obligation=CONDITIONAL, specification=ISO_19111) ++ public Optional<Temporal> getTargetEpoch() { ++ return Optional.empty(); + } + /** * Returns the version of the coordinate operation. Different versions of a coordinate * {@linkplain DefaultTransformation transformation} may exist because of the stochastic @@@ -660,6 -632,38 +696,37 @@@ return transform; } + /** + * Changes coordinates from being referenced to the source <abbr>CRS</abbr> + * and/or epoch to being referenced to the target <abbr>CRS</abbr> and/or epoch. + * This method operates on coordinate tuples and does not deal with interpolation of geometry types. + * + * <h4>Implementation specific behavior</h4> + * The default implementation has the following characteristics. Those characteristics are not + * guaranteed to be met by all implementations of the {@link CoordinateOperation} interface: + * + * <ul> + * <li>If the <abbr>CRS</abbr> and/or epoch of the given data are not equivalent to the source <abbr>CRS</abbr> and/or + * epoch of this coordinate operation, this method will automatically prepend an additional operation step.</li> + * <li>The coordinate tuples are not transformed immediately, but instead will be computed on-the-fly - * in the stream returned by {@link CoordinateSet#stream()}.</li> ++ * in the stream returned by {@link AbstractCoordinateSet#stream()}.</li> + * <li>If a {@link TransformException} occurs during on-the-fly coordinate operation, it will be wrapped + * in an unchecked {@link org.apache.sis.util.collection.BackingStoreException}.</li> + * <li>The returned coordinate set is serializable if the given data and the math transform are also serializable.</li> + * </ul> + * + * @param data the coordinates to change. + * @return the result of changing coordinates. + * @throws TransformException if some coordinates cannot be changed. Note that an absence of exception during + * this method call is not a guarantee that the coordinate changes succeeded, because other errors can + * occur during the stream execution. + * + * @since 1.5 + */ - @Override - public CoordinateSet transform(final CoordinateSet data) throws TransformException { ++ public AbstractCoordinateSet transform(final AbstractCoordinateSet data) throws TransformException { + return new TransformedCoordinateSet(this, data); + } + /** * Returns the operation method. This apply only to {@link AbstractSingleOperation} subclasses, * which will make this method public. @@@ -858,7 -862,7 +925,7 @@@ final CoordinateOperation that = (CoordinateOperation) object; if ((mode.isIgnoringMetadata() || (deepEquals(getCoordinateOperationAccuracy(), that.getCoordinateOperationAccuracy(), mode))) && - deepEquals(getInterpolationCRS(), getInterpolationCRS(that), mode)) - deepEquals(getInterpolationCRS(), that.getInterpolationCRS(), mode)) ++ deepEquals(getInterpolationCRS().orElse(null), getInterpolationCRS(that), mode)) { /* * At this point all metadata match or can be ignored. First, compare the targetCRS. diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/TransformedCoordinateSet.java index 0000000000,e998216d16..6cc3fe3575 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/TransformedCoordinateSet.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/TransformedCoordinateSet.java @@@ -1,0 -1,151 +1,148 @@@ + /* + * 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.operation; + + import java.util.Iterator; + import java.util.Spliterator; + import java.util.stream.Stream; + import java.util.function.Consumer; + import java.util.function.UnaryOperator; + import org.opengis.util.FactoryException; + import org.opengis.geometry.DirectPosition; + import org.opengis.geometry.MismatchedDimensionException; + import org.opengis.metadata.extent.GeographicBoundingBox; + import org.opengis.referencing.operation.MathTransform; + import org.opengis.referencing.operation.TransformException; + import org.apache.sis.referencing.CRS; + import org.apache.sis.referencing.internal.Resources; + import org.apache.sis.referencing.operation.transform.MathTransforms; + import org.apache.sis.util.collection.BackingStoreException; + import org.apache.sis.coordinate.DefaultCoordinateMetadata; + import org.apache.sis.coordinate.AbstractCoordinateSet; + -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.coordinate.CoordinateSet; -import org.opengis.coordinate.CoordinateMetadata; - + + /** + * The result of transforming coordinate tuples using the math transform of a given coordinate operation. + * + * @author Martin Desruisseaux (Geomatys) + */ + final class TransformedCoordinateSet extends AbstractCoordinateSet implements UnaryOperator<DirectPosition> { + /** + * Serial number for inter-operability with different versions. + */ - private static final long serialVersionUID = -6533100977777070895L; ++ private static final long serialVersionUID = -6533100977777070894L; + + /** + * The data to transform. + */ - @SuppressWarnings("serial") // Apache SIS implementations of this interface are serializable. - private final CoordinateSet data; ++ private final AbstractCoordinateSet data; + + /** + * The transform to apply on coordinate tuples. + * + * @see #iterator() + * @see #stream() + */ + @SuppressWarnings("serial") // Apache SIS implementations of this interface are serializable. + private final MathTransform transform; + + /** + * Creates a new transformed coordinate set. + * + * @param op the coordinate operation to apply. + * @param data the coordinate tuples to transform. + * @throws TransformException if the transform cannot be prepared. + */ - TransformedCoordinateSet(final AbstractCoordinateOperation op, final CoordinateSet data) throws TransformException { ++ TransformedCoordinateSet(final AbstractCoordinateOperation op, final AbstractCoordinateSet data) ++ throws TransformException ++ { + super(new DefaultCoordinateMetadata(op.getTargetCRS(), op.getTargetEpoch().orElse(null))); + @SuppressWarnings("LocalVariableHidesMemberVariable") + MathTransform transform = op.getMathTransform(); + if (transform == null) { + throw new TransformException(Resources.format(Resources.Keys.OperationHasNoTransform_2, op.getClass(), op.getName())); + } - final CoordinateMetadata metadata = data.getCoordinateMetadata(); ++ final DefaultCoordinateMetadata metadata = data.getCoordinateMetadata(); + if (metadata != null) try { + GeographicBoundingBox aoi = CRS.getGeographicBoundingBox(op); - CoordinateMetadata step = new DefaultCoordinateMetadata(op.getSourceCRS(), op.getSourceEpoch().orElse(null)); ++ final var step = new DefaultCoordinateMetadata(op.getSourceCRS(), op.getSourceEpoch().orElse(null)); + transform = MathTransforms.concatenate(CRS.findOperation(metadata, step, aoi).getMathTransform(), transform); + } catch (FactoryException | MismatchedDimensionException e) { + throw new TransformException(e.getMessage(), e); + } + this.transform = transform; + this.data = data; + } + + /** + * Returns the number of dimension of output coordinate tuples. + * This method is overridden in case that {@link #crs} is null. + */ + @Override + public int getDimension() { + return transform.getTargetDimensions(); + } + + /** + * Returns the transformed positions described by coordinate tuples. + */ + @Override + public Iterator<DirectPosition> iterator() { + return stream().iterator(); + } + + /** + * Returns the transformed positions described by coordinate tuples. + */ + @Override + public Spliterator<DirectPosition> spliterator() { + return stream().spliterator(); + } + + /** + * Returns a stream of transformed coordinate tuples. + */ + @Override + public Stream<DirectPosition> stream() { + return data.stream().map(this); + } + + /** + * Performs an action for each coordinate tuple of this stream. + * + * @param action the action to perform. + */ + @Override + public void forEach(Consumer<? super DirectPosition> action) { + stream().forEach(action); + } + + /** + * Transforms the given coordinate tuples. + * + * @param source coordinates in source CRS. + * @return coordinates in target CRS. + */ + @Override + public DirectPosition apply(final DirectPosition source) { + try { + return transform.transform(source, null); + } catch (TransformException e) { + throw new BackingStoreException(e); + } + } + } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/WKTKeywords.java index 8ea10291ce,f2d6f8561a..1c5611ce3e --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/WKTKeywords.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/WKTKeywords.java @@@ -272,6 -274,7 +274,7 @@@ public final class WKTKeywords extends addType(org.opengis.referencing.cs.CoordinateSystemAxis.class, Axis); addType(org.apache.sis.referencing.datum.BursaWolfParameters.class, ToWGS84); addType(org.opengis.referencing.operation.MathTransform.class, Param_MT, Concat_MT, Inverse_MT, PassThrough_MT); - addType(org.opengis.coordinate.CoordinateMetadata.class, CoordinateMetadata); ++ addType(org.apache.sis.coordinate.DefaultCoordinateMetadata.class, CoordinateMetadata); addType(org.opengis.geometry.DirectPosition.class, Point); } diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java index 07e14245c1,362722ed93..c5670c8171 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java @@@ -223,7 -223,7 +223,7 @@@ public final class GeodeticObjectParser " ANCHOR[“Tananarive observatory”]]"); assertNameAndIdentifierEqual("Tananarive 1925", 0, datum); - assertEquals("Tananarive observatory", String.valueOf(datum.getAnchorPoint())); - assertEquals("Tananarive observatory", datum.getAnchorDefinition().get().toString()); ++ assertEquals("Tananarive observatory", datum.getAnchorPoint().toString()); final Ellipsoid ellipsoid = datum.getEllipsoid(); assertNameAndIdentifierEqual("International 1924", 0, ellipsoid); diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultCylindricalCSTest.java index 8145de416a,0b44ba6140..ec240fd61c --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultCylindricalCSTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultCylindricalCSTest.java @@@ -27,8 -26,8 +26,9 @@@ import org.junit.jupiter.api.Test import static org.junit.jupiter.api.Assertions.*; import org.apache.sis.test.TestCase; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import static org.opengis.test.Assertions.assertAxisDirectionsEqual; +// Specific to the main branch: ++import org.apache.sis.referencing.privy.AxisDirections; +import static org.apache.sis.test.GeoapiAssert.assertAxisDirectionsEqual; /** diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultPolarCSTest.java index 1660f1da19,bb21729e08..17c96d7cb5 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultPolarCSTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultPolarCSTest.java @@@ -27,8 -26,8 +26,9 @@@ import org.junit.jupiter.api.Test import static org.junit.jupiter.api.Assertions.*; import org.apache.sis.test.TestCase; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import static org.opengis.test.Assertions.assertAxisDirectionsEqual; +// Specific to the main branch: ++import org.apache.sis.referencing.privy.AxisDirections; +import static org.apache.sis.test.GeoapiAssert.assertAxisDirectionsEqual; /** diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultSphericalCSTest.java index e335ca979b,4aadf4bb69..0022abe341 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultSphericalCSTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultSphericalCSTest.java @@@ -25,8 -24,8 +24,9 @@@ import org.junit.jupiter.api.Test import static org.junit.jupiter.api.Assertions.*; import org.apache.sis.test.TestCase; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import static org.opengis.test.Assertions.assertAxisDirectionsEqual; +// Specific to the main branch: ++import org.apache.sis.referencing.privy.AxisDirections; +import static org.apache.sis.test.GeoapiAssert.assertAxisDirectionsEqual; /** diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/HardCodedAxes.java index d226b26205,fe5edb9942..48ac69bb32 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/HardCodedAxes.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/HardCodedAxes.java @@@ -21,9 -21,8 +21,11 @@@ import javax.measure.Unit import org.opengis.referencing.cs.AxisDirection; import org.opengis.referencing.cs.RangeMeaning; import org.apache.sis.metadata.privy.AxisNames; - import org.apache.sis.referencing.privy.AxisDirections; import org.apache.sis.measure.Units; ++// Specific to the main branch: ++import org.apache.sis.referencing.privy.AxisDirections; ++ /** * Collection of axes for testing purpose. diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java index 5bd1bd577b,581667a272..dc96f66d81 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java @@@ -45,7 -42,7 +45,6 @@@ public final class ProvidersTest extend /** * Returns all providers to test. */ - @SuppressWarnings("removal") - @SuppressWarnings("deprecation") private static Class<?>[] methods() { return new Class<?>[] { Affine.class, diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/privy/AxisDirectionsTest.java index d2afcbfc18,4a347861f2..8dd70537dc --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/privy/AxisDirectionsTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/privy/AxisDirectionsTest.java @@@ -24,10 -24,7 +24,12 @@@ import org.opengis.referencing.cs.Coord import org.opengis.referencing.cs.CoordinateSystemAxis; import static org.opengis.referencing.cs.AxisDirection.*; import org.apache.sis.measure.Units; + ++// Specific to the main branch: +import static org.apache.sis.referencing.privy.AxisDirections.AWAY_FROM; +import static org.apache.sis.referencing.privy.AxisDirections.CLOCKWISE; +import static org.apache.sis.referencing.privy.AxisDirections.COUNTER_CLOCKWISE; + // Test dependencies import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java index cf83fc00a0,34b91b8cb5..b66a6e2c76 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/MetadataTest.java @@@ -71,9 -73,6 +70,10 @@@ import org.apache.sis.test.TestUtilitie import org.apache.sis.xml.test.DocumentComparator; import org.apache.sis.xml.test.TestCase; +// Specific to the main branch: ++import org.opengis.referencing.datum.VerticalDatumType; +import org.apache.sis.pending.geoapi.evolution.UnsupportedCodeList; + /** * Tests XML (un)marshalling of a metadata object containing various elements @@@ -125,19 -112,19 +125,18 @@@ public final class MetadataTest extend * with only the role changed. Note that we need to create an instance of the deprecated class, * because this is what will be unmarshalled from the XML document. */ -- @SuppressWarnings("deprecation") - final DefaultResponsibleParty author = new DefaultResponsibleParty(Role.AUTHOR); - final Anchor country = new Anchor(URI.create("SDN:C320:2:FR"), "France"); // Non-public SIS class. - final var author = new DefaultResponsibleParty(Role.AUTHOR); ++ final var author = new DefaultResponsibleParty(Role.AUTHOR); + final var country = new Anchor(URI.create("SDN:C320:2:FR"), "France"); // Non-public SIS class. { - final DefaultOnlineResource online = new DefaultOnlineResource(URI.create("http://www.ifremer.fr/sismer/")); + final var online = new DefaultOnlineResource(URI.create("http://www.ifremer.fr/sismer/")); online.setProtocol(Constants.HTTP); - final DefaultContact contact = new DefaultContact(online); + final var contact = new DefaultContact(online); contact.getIdentifierMap().putSpecialized(IdentifierSpace.ID, "IFREMER"); contact.setPhones(List.of( - new DefaultTelephone("+33 (0)2 xx.xx.xx.x6", TelephoneType.VOICE), - new DefaultTelephone("+33 (0)2 xx.xx.xx.x4", TelephoneType.FACSIMILE) + telephone("+33 (0)2 xx.xx.xx.x6", "VOICE"), + telephone("+33 (0)2 xx.xx.xx.x4", "FACSIMILE") )); - final DefaultAddress address = new DefaultAddress(); + final var address = new DefaultAddress(); address.setDeliveryPoints(Set.of("Brest institute")); address.setCity(new SimpleInternationalString("Plouzane")); address.setPostalCode("29280"); @@@ -157,16 -144,16 +156,15 @@@ new DefaultCitationDate(TestUtilities.date("1990-06-04 22:00:00"), DateType.REVISION), new DefaultCitationDate(TestUtilities.date("1979-08-02 22:00:00"), DateType.CREATION))); { -- @SuppressWarnings("deprecation") - final DefaultResponsibleParty originator = new DefaultResponsibleParty(Role.ORIGINATOR); - final DefaultOnlineResource online = new DefaultOnlineResource(URI.create("http://www.com.univ-mrs.fr/LOB/")); + final var originator = new DefaultResponsibleParty(Role.ORIGINATOR); + final var online = new DefaultOnlineResource(URI.create("http://www.com.univ-mrs.fr/LOB/")); online.setProtocol(Constants.HTTP); - final DefaultContact contact = new DefaultContact(online); + final var contact = new DefaultContact(online); contact.setPhones(List.of( - new DefaultTelephone("+33 (0)4 xx.xx.xx.x5", TelephoneType.VOICE), - new DefaultTelephone("+33 (0)4 xx.xx.xx.x8", TelephoneType.FACSIMILE) + telephone("+33 (0)4 xx.xx.xx.x5", "VOICE"), + telephone("+33 (0)4 xx.xx.xx.x8", "FACSIMILE") )); - final DefaultAddress address = new DefaultAddress(); + final var address = new DefaultAddress(); address.setDeliveryPoints(Set.of("Oceanology institute")); address.setCity(new SimpleInternationalString("Marseille")); address.setPostalCode("13288"); @@@ -181,8 -168,8 +179,7 @@@ Locale.ENGLISH, // Language, TopicCategory.OCEANS); // Topic category { -- @SuppressWarnings("deprecation") - final DefaultResponsibleParty custodian = new DefaultResponsibleParty((DefaultResponsibility) author); + final var custodian = new DefaultResponsibleParty((DefaultResponsibility) author); custodian.setRole(Role.CUSTODIAN); identification.setPointOfContacts(Set.of(custodian)); } @@@ -213,17 -200,17 +210,16 @@@ * Data indentification / Resource constraint. */ { - final DefaultLegalConstraints constraint = new DefaultLegalConstraints(); + final var constraint = new DefaultLegalConstraints(); - constraint.setAccessConstraints(Set.of(Restriction.LICENCE)); + constraint.setAccessConstraints(Set.of(Restriction.LICENSE)); identification.setResourceConstraints(Set.of(constraint)); } /* * Data indentification / Aggregate information. */ { -- @SuppressWarnings("deprecation") - final DefaultAggregateInformation aggregateInfo = new DefaultAggregateInformation(); - final DefaultCitation name = new DefaultCitation("Some oceanographic campaign"); + final var aggregateInfo = new DefaultAggregateInformation(); + final var name = new DefaultCitation("Some oceanographic campaign"); name.setAlternateTitles(Set.of(new SimpleInternationalString("Pseudo group of data"))); name.setDates(Set.of(new DefaultCitationDate(TestUtilities.date("1990-06-04 22:00:00"), DateType.REVISION))); aggregateInfo.setName(name); @@@ -235,19 -222,19 +231,19 @@@ * Data indentification / Extent. */ { - final DefaultCoordinateSystemAxis axis = new DefaultCoordinateSystemAxis( + final var axis = new DefaultCoordinateSystemAxis( nameAndIdentifier("depth", "Depth", null), "D", AxisDirection.DOWN, Units.METRE); - final DefaultVerticalCS cs = new DefaultVerticalCS( + final var cs = new DefaultVerticalCS( nameAndIdentifier("depth", "Depth", null), axis); - final DefaultVerticalDatum datum = new DefaultVerticalDatum( + final var datum = new DefaultVerticalDatum( - nameAndIdentifier("D28", "Depth below D28", "For testing purpose"), (RealizationMethod) null); + nameAndIdentifier("D28", "Depth below D28", "For testing purpose"), VerticalDatumType.OTHER_SURFACE); - final DefaultVerticalCRS vcrs = new DefaultVerticalCRS( + final var vcrs = new DefaultVerticalCRS( nameAndIdentifier("D28", "Depth below D28", "CRS for testing purpose"), datum, cs); - final DefaultTemporalExtent temporal = new DefaultTemporalExtent(); + final var temporal = new DefaultTemporalExtent(); temporal.setBounds(TestUtilities.date("1990-06-05 00:00:00"), TestUtilities.date("1990-07-02 00:00:00")); identification.setExtents(Set.of(new DefaultExtent( null, @@@ -311,9 -298,9 +307,8 @@@ * Distribution information. */ { -- @SuppressWarnings("deprecation") - final DefaultResponsibleParty distributor = new DefaultResponsibleParty((DefaultResponsibility) author); - final DefaultDistribution distributionInfo = new DefaultDistribution(); + final var distributor = new DefaultResponsibleParty((DefaultResponsibility) author); + final var distributionInfo = new DefaultDistribution(); distributor.setRole(Role.DISTRIBUTOR); distributionInfo.setDistributors(Set.of(new DefaultDistributor(distributor))); diff --cc optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/AuthorityCodes.java index bdbd05dce4,92e0c60078..b9e523c9fd --- a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/AuthorityCodes.java +++ b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/AuthorityCodes.java @@@ -392,7 -392,8 +392,8 @@@ final class AuthorityCodes extends Obse for (final Code code : snapshot) { String text; try { - text = Strings.trimOrNull(Types.toString(factory.getDescriptionText(code.code), locale)); - var i18n = factory.getDescriptionText(CoordinateReferenceSystem.class, code.code).orElse(null); ++ var i18n = factory.getDescriptionText(code.code); + text = Strings.trimOrNull(Types.toString(i18n, locale)); if (text == null) { text = Vocabulary.forLocale(locale).getString(Vocabulary.Keys.Unnamed); }