diff --git a/geoapi-conformance/src/main/java/org/opengis/test/ToleranceModifier.java b/geoapi-conformance/src/main/java/org/opengis/test/ToleranceModifier.java index 045e8d95f..866a38d58 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/ToleranceModifier.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/ToleranceModifier.java @@ -64,7 +64,7 @@ public interface ToleranceModifier { * geographic coordinates. This modifier is identical to the {@link #GEOGRAPHIC} tolerance * modifier, except that φ and λ axes are interchanged. This is the most common * modifier used when testing {@link GeographicCRS} instances created from the - * EPSG database. + * EPSG geodetic registry. * * @see ToleranceModifiers#geographic(int, int) */ @@ -88,7 +88,7 @@ public interface ToleranceModifier { * the result of an reverse projection. This modifier is identical to the * {@link #PROJECTION} tolerance modifier, except that φ and λ axes are * interchanged. This is the most common modifier used when testing {@link ProjectedCRS} - * instances created from the EPSG database. + * instances created from the EPSG geodetic registry. * * @see ToleranceModifiers#projection(int, int) */ diff --git a/geoapi-conformance/src/main/java/org/opengis/test/Validator.java b/geoapi-conformance/src/main/java/org/opengis/test/Validator.java index a867e38b1..691b8572e 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/Validator.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/Validator.java @@ -20,6 +20,7 @@ import java.util.BitSet; import java.util.Objects; import java.util.Collection; +import java.util.function.BiConsumer; import java.util.logging.Logger; import org.opengis.annotation.Obligation; @@ -212,11 +213,11 @@ protected void conditional(final String message, final Object value, final boole } /** - * Ensures that the elements in the given collection are compliant with the {@link Object} - * {@code equals(Object)} and {@code hashCode()} contract. This method ensures that the - * {@code equals(Object)} methods implement reflexive, symmetric - * and transitive relations. It also ensures that if {@code A.equals(B)}, - * then {@code A.hashCode() == B.hashCode()}. + * Ensures that the elements in the given collection are compliant + * with the {@code equals(Object)} and {@code hashCode()} contract. + * This method ensures that the {@code equals(Object)} methods implement + * reflexive, symmetric and transitive relations. + * It also ensures that if {@code A.equals(B)}, then {@code A.hashCode() == B.hashCode()}. * *

If the given collection is null, then this method does nothing. * If the given collection contains null elements, then those elements are ignored.

@@ -228,6 +229,7 @@ protected void conditional(final String message, final Object value, final boole * * @since 3.1 */ + @SuppressWarnings("ObjectEqualsNull") protected void validate(final Collection collection) { if (collection == null) { return; @@ -279,4 +281,43 @@ protected void validate(final Collection collection) { assertEquals(hashCodes[i], elements[i].hashCode(), "The hash code value has changed."); } } + + /** + * Validates the given collection, then validates each element in that collection. + * This method invokes {@link #validate(Collection)} and adds the restriction that + * the collection and all its element shall be non-null. + * Then the given validate function is invoked for each element. + * Example: + * + * {@snippet lang="java" : + * validate("identifiers", object.getIdentifiers(), ValidatorContainer::validate, false); + * } + * + * @param type of elements to validate. + * @param property name of the property to validate. + * @param values values of the property to validate. + * @param validator the function to invoke for validating each element. + * @param mandatory whether at least one element shall be present. + * + * @since 3.1 + */ + protected void validate(final String property, + final Collection values, + final BiConsumer validator, + final boolean mandatory) + { + assertNotNull(values, () -> property + " shall not be null. " + + (mandatory ? "Expected a collection with at least one element." + : "If absent, it should be an empty collection instead.")); + validate(values); + boolean isPresent = false; + for (final E element : values) { + assertNotNull(element, () -> property + " shall not contain null elements."); + validator.accept(container, element); + isPresent = true; + } + if (mandatory && requireMandatoryAttributes) { + assertTrue(isPresent, () -> " is mandatory. The collection shall not be empty."); + } + } } diff --git a/geoapi-conformance/src/main/java/org/opengis/test/ValidatorContainer.java b/geoapi-conformance/src/main/java/org/opengis/test/ValidatorContainer.java index 9b4a014a8..652cb1b1f 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/ValidatorContainer.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/ValidatorContainer.java @@ -27,6 +27,9 @@ import org.opengis.metadata.*; import org.opengis.metadata.extent.*; import org.opengis.metadata.citation.*; +import org.opengis.metadata.quality.*; +import org.opengis.metadata.maintenance.*; +import org.opengis.metadata.maintenance.Scope; // Resolve ambiguity. import org.opengis.geometry.*; import org.opengis.parameter.*; import org.opengis.referencing.*; @@ -96,6 +99,26 @@ public class ValidatorContainer implements Cloneable { @SuppressWarnings("this-escape") public CitationValidator citation = new CitationValidator(this); + /** + * The validator for {@link DataQuality} and related objects. + * Vendors can change this field to a different validator, or change the setting + * of the referenced validator. This field shall not be set to {@code null} however. + * + * @since 3.1 + */ + @SuppressWarnings("this-escape") + public QualityValidator quality = new QualityValidator(this); + + /** + * The validator for {@link MaintenanceInformation} and related objects. + * Vendors can change this field to a different validator, or change the setting + * of the referenced validator. This field shall not be set to {@code null} however. + * + * @since 3.1 + */ + @SuppressWarnings("this-escape") + public MaintenanceValidator maintenance = new MaintenanceValidator(this); + /** * The validator for {@link Extent} and related objects. * Vendors can change this field to a different validator, or change the setting @@ -180,7 +203,7 @@ public ValidatorContainer() { all = new AbstractList() { /** Returns the number of elements in this list. */ @Override public int size() { - return 11; + return 13; } /** Returns the validator at the given index. */ @@ -189,14 +212,16 @@ public ValidatorContainer() { case 0: return naming; case 1: return metadata; case 2: return citation; - case 3: return extent; - case 4: return datum; - case 5: return cs; - case 6: return crs; - case 7: return parameter; - case 8: return coordinateOperation; - case 9: return geometry; - case 10: return image; + case 3: return quality; + case 4: return maintenance; + case 5: return extent; + case 6: return datum; + case 7: return cs; + case 8: return crs; + case 9: return parameter; + case 10: return coordinateOperation; + case 11: return geometry; + case 12: return image; default: throw new IndexOutOfBoundsException(index); } } @@ -245,15 +270,18 @@ public final void dispatch(final Object object) { if (object instanceof Telephone) validate((Telephone) object); if (object instanceof Address) validate((Address) object); if (object instanceof OnlineResource) validate((OnlineResource) object); + if (object instanceof DataQuality) validate((DataQuality) object); + if (object instanceof Element) validate((Element) object); // Dispatch according sub-type. + if (object instanceof Result) validate((Result) object); // Dispatch according sub-type. if (object instanceof Extent) validate((Extent) object); - if (object instanceof GeographicExtent) validate((GeographicExtent) object); + if (object instanceof GeographicExtent) validate((GeographicExtent) object); // Dispatch according sub-type. if (object instanceof VerticalExtent) validate((VerticalExtent) object); if (object instanceof TemporalExtent) validate((TemporalExtent) object); - if (object instanceof IdentifiedObject) validate((IdentifiedObject) object); + if (object instanceof IdentifiedObject) validate((IdentifiedObject) object); // Dispatch according sub-type. if (object instanceof Identifier) validate((Identifier) object); - if (object instanceof GenericName) validate((GenericName) object); + if (object instanceof GenericName) validate((GenericName) object); // Dispatch according sub-type. if (object instanceof NameSpace) validate((NameSpace) object); - if (object instanceof GeneralParameterValue) validate((GeneralParameterValue) object); + if (object instanceof GeneralParameterValue) validate((GeneralParameterValue) object); // Dispatch according sub-type. if (object instanceof Envelope) validate((Envelope) object); if (object instanceof DirectPosition) validate((DirectPosition) object); if (object instanceof InternationalString) validate((InternationalString) object); @@ -374,6 +402,136 @@ public final void validate(final OnlineResource object) { citation.validate(object); } + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#validate(DataQuality) + * + * @since 3.1 + */ + public final void validate(final DataQuality object) { + quality.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#dispatch(Element) + * + * @since 3.1 + */ + public final void validate(final Element object) { + quality.dispatch(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see MaintenanceValidator#validate(MaintenanceInformation) + * + * @since 3.1 + */ + public final void validate(final PositionalAccuracy object) { + quality.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#dispatch(Result) + * + * @since 3.1 + */ + public final void validate(final Result object) { + quality.dispatch(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#validate(DescriptiveResult) + * + * @since 3.1 + */ + public final void validate(final DescriptiveResult object) { + quality.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#validate(ConformanceResult) + * + * @since 3.1 + */ + public final void validate(final ConformanceResult object) { + quality.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#validate(QuantitativeResult) + * + * @since 3.1 + */ + public final void validate(final QuantitativeResult object) { + quality.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#validate(CoverageResult) + * + * @since 3.1 + */ + public final void validate(final CoverageResult object) { + quality.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see MaintenanceValidator#validate(MaintenanceInformation) + * + * @since 3.1 + */ + public final void validate(final MaintenanceInformation object) { + maintenance.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see MaintenanceValidator#validate(Scope) + * + * @since 3.1 + */ + public final void validate(final Scope object) { + maintenance.validate(object); + } + /** * Tests the conformance of the given object. * @@ -534,7 +692,10 @@ public final void validate(final DerivedCRS object) { * @param object the object to validate, or {@code null}. * * @see CRSValidator#validate(ImageCRS) + * + * @deprecated {@code ImageCRS} is replaced by {@link EngineeringCRS} as of ISO 19111:2019. */ + @Deprecated(since="3.1") public final void validate(final ImageCRS object) { crs.validate(object); } @@ -689,6 +850,7 @@ public final void validate(final TimeCS object) { * * @see CSValidator#validate(UserDefinedCS) */ + @Deprecated(since="3.1") public final void validate(final UserDefinedCS object) { cs.validate(object); } @@ -776,7 +938,11 @@ public final void validate(final TemporalDatum object) { * @param object the object to test, or {@code null}. * * @see DatumValidator#validate(ImageDatum) + * + * @deprecated {@code ImageCRS} is replaced by {@link EngineeringCRS} as of ISO 19111:2019. */ + @Deprecated(since="3.1") + @SuppressWarnings("removal") public final void validate(final ImageDatum object) { datum.validate(object); } @@ -792,6 +958,19 @@ public final void validate(final EngineeringDatum object) { datum.validate(object); } + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see DatumValidator#validate(DatumEnsemble) + * + * @since 3.1 + */ + public final void validate(final DatumEnsemble object) { + datum.validate(object); + } + /** * Tests the conformance of the given object. * @@ -1067,7 +1246,7 @@ public final void validate(final ImageWriterSpi object) { * * @see ImageValidator#validate(IIOMetadataFormat) */ - public void validate(final IIOMetadataFormat object) { + public final void validate(final IIOMetadataFormat object) { image.validate(object); } } diff --git a/geoapi-conformance/src/main/java/org/opengis/test/Validators.java b/geoapi-conformance/src/main/java/org/opengis/test/Validators.java index 0fba5c67e..5c948b8de 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/Validators.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/Validators.java @@ -26,6 +26,9 @@ import org.opengis.metadata.extent.*; import org.opengis.metadata.citation.*; import org.opengis.geometry.*; +import org.opengis.metadata.quality.*; +import org.opengis.metadata.maintenance.*; +import org.opengis.metadata.maintenance.Scope; // Resolve ambiguity. import org.opengis.parameter.*; import org.opengis.referencing.*; import org.opengis.referencing.cs.*; @@ -216,6 +219,136 @@ public static void validate(final OnlineResource object) { DEFAULT.validate(object); } + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see MaintenanceValidator#validate(DataQuality) + * + * @since 3.1 + */ + public static void validate(final DataQuality object) { + DEFAULT.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#dispatch(Element) + * + * @since 3.1 + */ + public static void validate(final Element object) { + DEFAULT.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see MaintenanceValidator#validate(MaintenanceInformation) + * + * @since 3.1 + */ + public static void validate(final PositionalAccuracy object) { + DEFAULT.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#dispatch(Result) + * + * @since 3.1 + */ + public static void validate(final Result object) { + DEFAULT.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#validate(DescriptiveResult) + * + * @since 3.1 + */ + public static void validate(final DescriptiveResult object) { + DEFAULT.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#validate(ConformanceResult) + * + * @since 3.1 + */ + public static void validate(final ConformanceResult object) { + DEFAULT.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#validate(QuantitativeResult) + * + * @since 3.1 + */ + public static void validate(final QuantitativeResult object) { + DEFAULT.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see QualityValidator#validate(CoverageResult) + * + * @since 3.1 + */ + public static void validate(final CoverageResult object) { + DEFAULT.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see MaintenanceValidator#validate(MaintenanceInformation) + * + * @since 3.1 + */ + public static void validate(final MaintenanceInformation object) { + DEFAULT.validate(object); + } + + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see MaintenanceValidator#validate(Scope) + * + * @since 3.1 + */ + public static void validate(final Scope object) { + DEFAULT.validate(object); + } + /** * Tests the conformance of the given object. * @@ -376,7 +509,10 @@ public static void validate(final DerivedCRS object) { * @param object the object to validate, or {@code null}. * * @see CRSValidator#validate(ImageCRS) + * + * @deprecated {@code ImageCRS} is replaced by {@link EngineeringCRS} as of ISO 19111:2019. */ + @Deprecated(since="3.1") public static void validate(final ImageCRS object) { DEFAULT.validate(object); } @@ -531,6 +667,7 @@ public static void validate(final TimeCS object) { * * @see CSValidator#validate(UserDefinedCS) */ + @Deprecated(since="3.1") public static void validate(final UserDefinedCS object) { DEFAULT.validate(object); } @@ -618,7 +755,11 @@ public static void validate(final TemporalDatum object) { * @param object the object to test, or {@code null}. * * @see DatumValidator#validate(ImageDatum) + * + * @deprecated {@code ImageCRS} is replaced by {@link EngineeringCRS} as of ISO 19111:2019. */ + @Deprecated(since="3.1") + @SuppressWarnings("removal") public static void validate(final ImageDatum object) { DEFAULT.validate(object); } @@ -634,6 +775,17 @@ public static void validate(final EngineeringDatum object) { DEFAULT.validate(object); } + /** + * Tests the conformance of the given object. + * + * @param object the object to test, or {@code null}. + * + * @see DatumValidator#validate(DatumEnsemble) + */ + public static void validate(final DatumEnsemble object) { + DEFAULT.validate(object); + } + /** * Tests the conformance of the given object. * diff --git a/geoapi-conformance/src/main/java/org/opengis/test/metadata/CitationValidator.java b/geoapi-conformance/src/main/java/org/opengis/test/metadata/CitationValidator.java index 351034673..3f1d2ffc7 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/metadata/CitationValidator.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/metadata/CitationValidator.java @@ -18,9 +18,7 @@ package org.opengis.test.metadata; import java.util.Date; -import org.opengis.metadata.*; import org.opengis.metadata.citation.*; -import org.opengis.util.InternationalString; import org.opengis.test.ValidatorContainer; import static org.junit.jupiter.api.Assertions.fail; @@ -60,12 +58,8 @@ public void validate(final Citation object) { } validateMandatory(object.getTitle()); validateOptional (object.getEdition()); - for (final InternationalString e : toArray(InternationalString.class, object.getAlternateTitles())) { - container.validate(e); - } - for (final Identifier e : toArray(Identifier.class, object.getIdentifiers())) { - container.validate(e); - } + validate("alternateTitle", object.getAlternateTitles(), ValidatorContainer::validate, false); + validate("identifier", object.getIdentifiers(), ValidatorContainer::validate, false); container.validate(object.getOtherCitationDetails()); validate(toArray(CitationDate.class, object.getDates())); for (final Responsibility e : toArray(Responsibility.class, object.getCitedResponsibleParties())) { diff --git a/geoapi-conformance/src/main/java/org/opengis/test/metadata/ExtentValidator.java b/geoapi-conformance/src/main/java/org/opengis/test/metadata/ExtentValidator.java index 5c0437eb7..591e9a78f 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/metadata/ExtentValidator.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/metadata/ExtentValidator.java @@ -27,8 +27,7 @@ /** - * Validates {@link Extent} and related objects from the - * {@code org.opengis.metadata.extent} package. + * Validates {@link Extent} and related objects from the {@code org.opengis.metadata.extent} package. * *

This class is provided for users wanting to override the validation methods. When the default * behavior is sufficient, the {@link org.opengis.test.Validators} static methods provide a more diff --git a/geoapi-conformance/src/main/java/org/opengis/test/metadata/MaintenanceValidator.java b/geoapi-conformance/src/main/java/org/opengis/test/metadata/MaintenanceValidator.java new file mode 100644 index 000000000..f75477fb1 --- /dev/null +++ b/geoapi-conformance/src/main/java/org/opengis/test/metadata/MaintenanceValidator.java @@ -0,0 +1,78 @@ +/* + * GeoAPI - Java interfaces for OGC/ISO standards + * Copyright © 2008-2024 Open Geospatial Consortium, Inc. + * http://www.geoapi.org + * + * Licensed 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.opengis.test.metadata; + +import org.opengis.metadata.citation.CitationDate; +import org.opengis.metadata.maintenance.MaintenanceInformation; +import org.opengis.metadata.maintenance.Scope; +import org.opengis.test.ValidatorContainer; + + + +/** + * Validates objects from the {@code org.opengis.metadata.maintenance} package. + * + *

This class is provided for users wanting to override the validation methods. + * When the default behavior is sufficient, the {@link org.opengis.test.Validators} + * static methods provide a more convenient way to validate various kinds of objects.

+ * + * @author Martin Desruisseaux (Geomatys) + * @version 3.1 + * @since 3.1 + */ +public class MaintenanceValidator extends MetadataValidator { + /** + * Creates a new validator instance. + * + * @param container the set of validators to use for validating other kinds of objects + * (see {@linkplain #container field javadoc}). + */ + public MaintenanceValidator(final ValidatorContainer container) { + super(container, "org.opengis.metadata.maintenance"); + } + + /** + * Validates the maintenance information. + * + * @param object the object to validate, or {@code null}. + */ + public void validate(final MaintenanceInformation object) { + if (object == null) { + return; + } + container.validate(toArray(CitationDate.class, object.getMaintenanceDates())); + for (Scope scope : toArray(Scope.class, object.getMaintenanceScopes())) { + validate(scope); + } + validate("maintenanceNote", object.getMaintenanceNotes(), ValidatorContainer::validate, false); + validate("contact", object.getContacts(), ValidatorContainer::validate, false); + } + + /** + * Validates the scope. + * + * @param object the object to validate, or {@code null}. + */ + public void validate(final Scope object) { + if (object == null) { + return; + } + mandatory("Scope: must have a level.", object.getLevel()); + validate("extent", object.getExtents(), ValidatorContainer::validate, false); + } +} diff --git a/geoapi-conformance/src/main/java/org/opengis/test/metadata/QualityValidator.java b/geoapi-conformance/src/main/java/org/opengis/test/metadata/QualityValidator.java new file mode 100644 index 000000000..f1618f3cf --- /dev/null +++ b/geoapi-conformance/src/main/java/org/opengis/test/metadata/QualityValidator.java @@ -0,0 +1,209 @@ +/* + * GeoAPI - Java interfaces for OGC/ISO standards + * Copyright © 2008-2024 Open Geospatial Consortium, Inc. + * http://www.geoapi.org + * + * Licensed 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.opengis.test.metadata; + +import org.opengis.util.Record; +import org.opengis.metadata.citation.Citation; +import org.opengis.metadata.content.RangeDimension; +import org.opengis.metadata.quality.*; +import org.opengis.metadata.maintenance.Scope; +import org.opengis.test.ValidatorContainer; + +import static org.junit.jupiter.api.Assertions.*; + + +/** + * Validates {@link DataQuality} and related objects from the {@code org.opengis.metadata.quality} package. + * + *

This class is provided for users wanting to override the validation methods. + * When the default behavior is sufficient, the {@link org.opengis.test.Validators} + * static methods provide a more convenient way to validate various kinds of objects.

+ * + * @author Martin Desruisseaux (Geomatys) + * @version 3.1 + * @since 3.1 + */ +public class QualityValidator extends MetadataValidator { + /** + * Creates a new validator instance. + * + * @param container the set of validators to use for validating other kinds of objects + * (see {@linkplain #container field javadoc}). + */ + public QualityValidator(final ValidatorContainer container) { + super(container, "org.opengis.metadata.quality"); + } + + /** + * For each interface implemented by the given object, invokes the corresponding + * {@code validate(…)} method defined in this class (if any). + * + * @param object the object to dispatch to {@code validate(…)} methods, or {@code null}. + * @return number of {@code validate(…)} methods invoked in this class for the given object. + */ + public int dispatch(final Element object) { + int n = 0; + if (object != null) { + if (object instanceof PositionalAccuracy) {validate((PositionalAccuracy) object); n++;} + if (n == 0) { + validateElement(object); + } + } + return n; + } + + /** + * For each interface implemented by the given object, invokes the corresponding + * {@code validate(…)} method defined in this class (if any). + * + * @param object the object to dispatch to {@code validate(…)} methods, or {@code null}. + * @return number of {@code validate(…)} methods invoked in this class for the given object. + */ + public int dispatch(final Result object) { + int n = 0; + if (object != null) { + if (object instanceof DescriptiveResult) {validate((DescriptiveResult) object); n++;} + if (object instanceof ConformanceResult) {validate((ConformanceResult) object); n++;} + if (object instanceof QuantitativeResult) {validate((QuantitativeResult) object); n++;} + if (object instanceof CoverageResult) {validate((CoverageResult) object); n++;} + if (n == 0) { + validateResult(object); + } + } + return n; + } + + /** + * Validates the data quality. + * + * @param object the object to validate, or {@code null}. + */ + public void validate(final DataQuality object) { + if (object == null) { + return; + } + final Scope scope = object.getScope(); + mandatory("DataQuality: must have a scope.", scope); + container.validate(scope); + final Element[] reports = toArray(Element.class, object.getReports()); + if (requireMandatoryAttributes) { + assertNotEquals(0, reports.length, "DataQuality: must have at least one report."); + } + for (final Element element : reports) { + dispatch(element); + } + } + + /** + * Validates the positional accuracy. + * + * @param object the object to validate, or {@code null}. + */ + public void validate(final PositionalAccuracy object) { + if (object == null) { + return; + } + validateElement(object); + } + + /** + * Validates the properties common to all elements. + * + * @param object the object to validate. + */ + private void validateElement(final Element object) { + container.validate(object.getStandaloneQualityReportDetails()); + for (final Result result : toArray(Result.class, object.getResults())) { + dispatch(result); + } + } + + /** + * Validates the descriptive result. + * + * @param object the object to validate, or {@code null}. + */ + public void validate(final DescriptiveResult object) { + if (object == null) { + return; + } + validateResult(object); + validateMandatory(object.getStatement()); + } + + /** + * Validates the conformance result. + * + * @param object the object to validate, or {@code null}. + */ + public void validate(final ConformanceResult object) { + if (object == null) { + return; + } + validateResult(object); + Citation specification = object.getSpecification(); + mandatory("ConformanceResult: must have a specification.", specification); + container.validate(specification); + container.validate(object.getExplanation()); + mandatory("ConformanceResult: must have a Boolean.", object.pass()); + } + + /** + * Validates the quantitative result. + * + * @param object the object to validate, or {@code null}. + */ + public void validate(final QuantitativeResult object) { + if (object == null) { + return; + } + validateResult(object); + int count = toArray(Record.class, object.getValues()).length; + if (requireMandatoryAttributes) { + assertNotEquals(0, count, "QuantitativeResult: must have at least one value."); + } + } + + /** + * Validates the coverage result. + * + * @param object the object to validate, or {@code null}. + */ + public void validate(final CoverageResult object) { + if (object == null) { + return; + } + validateResult(object); + mandatory("CoverageResult: must have a spatial representation type.", object.getSpatialRepresentationType()); + mandatory("CoverageResult: must have a result spatial representation.", object.getResultSpatialRepresentation()); + final RangeDimension[] ranges = toArray(RangeDimension.class, object.getResultContent()); + if (object.getResultFormat() == null && object.getResultFile() == null && requireMandatoryAttributes) { + assertNotEquals(0, ranges.length, "CoverageResult: must have at least one range dimension" + + " when result format and result file are not provided."); + } + } + + /** + * Validates the properties common to all results. + * + * @param object the object to validate. + */ + private void validateResult(final Result object) { + container.validate(object.getResultScope()); + } +} diff --git a/geoapi-conformance/src/main/java/org/opengis/test/referencing/AuthorityFactoryTest.java b/geoapi-conformance/src/main/java/org/opengis/test/referencing/AuthorityFactoryTest.java index 70e12b96f..d1a4b1bf9 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/referencing/AuthorityFactoryTest.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/referencing/AuthorityFactoryTest.java @@ -313,7 +313,7 @@ public void testWGS84() throws NoSuchAuthorityCodeException, FactoryException { /** * Verifies the horizontal axis direction of the given coordinate system. The standard - * directions are (East,North), but the boolean argument allows to swap and flip those + * directions are (East,North), but the Boolean argument allows to swap and flip those * directions. * * @param message the message to report in case of error. diff --git a/geoapi-conformance/src/main/java/org/opengis/test/referencing/CRSValidator.java b/geoapi-conformance/src/main/java/org/opengis/test/referencing/CRSValidator.java index 43b8e07ff..77113d2a8 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/referencing/CRSValidator.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/referencing/CRSValidator.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.ArrayList; import java.util.LinkedHashSet; +import java.util.function.Supplier; import org.opengis.referencing.cs.*; import org.opengis.referencing.crs.*; @@ -48,13 +49,14 @@ public class CRSValidator extends ReferencingValidator { /** * The axis names mandated by ISO 19111 for some particular kind of CRS. - * See ISO 19111:2007 table 16 at page 25. + * See ISO 19111:2019 table 16 at page 27. */ private static final String[] GEOCENTRIC_AXIS_NAME = {"geocentric X", "geocentric Y", "geocentric Z"}, GEOGRAPHIC_AXIS_NAME = {"geodetic latitude", "geodetic longitude", "ellipsoidal height"}, - PROJECTED_AXIS_NAME = {"northing", "southing", "easting", "westing"}, - SPHERICAL_AXIS_NAME = {"spherical latitude", "spherical longitude", "geocentric radius"}, + PROJECTED_AXIS_NAME = {"northing", "southing", "easting", "westing", "ellipsoidal height"}, + SPHERICAL_AXIS_NAME = {"spherical latitude", "geocentric latitude", "geocentric co-latitude", + "spherical longitude", "geodetic longitude", "geocentric radius"}, VERTICAL_AXIS_NAME = {"depth", "gravity-related height", "gravity-related depth"}; /* * Note: the ISO table does not mention "gravity-related depth" as a standard name. @@ -63,10 +65,10 @@ public class CRSValidator extends ReferencingValidator { */ /** - * {@code true} if validation of the conversion by {@link #validateGeneralDerivedCRS} - * is under way. Used in order to avoid never-ending recursivity. + * {@code true} if validation of the conversion by {@link #validateGeneralDerivedCRS} is under way. + * Used in order to avoid never-ending recursivity. * - * @todo Replace by a more general mechanism straight in {@link ValidatorContainer}. + * @todo Replace by a more general mechanism in {@link ValidatorContainer}. */ private final ThreadLocal VALIDATING = new ThreadLocal<>(); @@ -102,6 +104,7 @@ public CRSValidator(final ValidatorContainer container) { * @param object the object to dispatch to {@code validate(…)} methods, or {@code null}. * @return number of {@code validate(…)} methods invoked in this class for the given object. */ + @SuppressWarnings("deprecation") public int dispatch(final CoordinateReferenceSystem object) { int n = 0; if (object != null) { @@ -117,8 +120,9 @@ public int dispatch(final CoordinateReferenceSystem object) { if (n == 0) { if (object instanceof GeodeticCRS) { validate((GeodeticCRS) object, false, false); + n++; } else { - validateReferenceSystem(object); + validateIdentifiedObject(object); container.validate(object.getCoordinateSystem()); } } @@ -139,7 +143,10 @@ public int dispatch(final CoordinateReferenceSystem object) { * * * @param object the object to validate, or {@code null}. + * + * @deprecated ISO 19111 does not have a specific type for geocentric CRS. Use geodetic CRS instead. */ + @Deprecated(since="3.1") public void validate(final GeocentricCRS object) { validate(object, true, false); } @@ -170,7 +177,7 @@ private void validate(final GeodeticCRS object, final boolean skipGeographic, fi if (object == null) { return; } - validateReferenceSystem(object); + validateIdentifiedObject(object); final CoordinateSystem cs = object.getCoordinateSystem(); mandatory("GeodeticCRS: shall have a CoordinateSystem.", cs); if (!skipGeographic && cs instanceof EllipsoidalCS) { @@ -217,7 +224,7 @@ public void validate(final ProjectedCRS object) { if (object == null) { return; } - validateReferenceSystem(object); + validateIdentifiedObject(object); final GeographicCRS baseCRS = object.getBaseCRS(); mandatory("ProjectedCRS: shall have a base CRS.", baseCRS); @@ -245,7 +252,7 @@ public void validate(final DerivedCRS object) { if (object == null) { return; } - validateReferenceSystem(object); + validateIdentifiedObject(object); final CoordinateReferenceSystem baseCRS = object.getBaseCRS(); mandatory("DerivedCRS: shall have a base CRS.", baseCRS); @@ -269,7 +276,7 @@ public void validate(final DerivedCRS object) { * * @param object the object to validate, or {@code null}. */ - private void validateGeneralDerivedCRS(final GeneralDerivedCRS object) { + private void validateGeneralDerivedCRS(final DerivedCRS object) { if (!Boolean.TRUE.equals(VALIDATING.get())) try { VALIDATING.set(Boolean.TRUE); final Conversion conversion = object.getConversionFromBase(); @@ -280,11 +287,11 @@ private void validateGeneralDerivedCRS(final GeneralDerivedCRS object) { final CoordinateReferenceSystem targetCRS = conversion.getTargetCRS(); if (baseCRS != null && sourceCRS != null) { assertSame(baseCRS, sourceCRS, - "GeneralDerivedCRS: The base CRS should be the source CRS of the conversion."); + "DerivedCRS: The base CRS should be the source CRS of the conversion."); } if (targetCRS != null) { assertSame(object, targetCRS, - "GeneralDerivedCRS: The derived CRS should be the target CRS of the conversion."); + "DerivedCRS: The derived CRS should be the target CRS of the conversion."); } } } finally { @@ -296,12 +303,15 @@ private void validateGeneralDerivedCRS(final GeneralDerivedCRS object) { * Validates the given coordinate reference system. * * @param object the object to validate, or {@code null}. + * + * @deprecated {@code ImageCRS} is replaced by {@link EngineeringCRS} as of ISO 19111:2019. */ + @Deprecated(since="3.1") public void validate(final ImageCRS object) { if (object == null) { return; } - validateReferenceSystem(object); + validateIdentifiedObject(object); final AffineCS cs = object.getCoordinateSystem(); mandatory("ImageCRS: shall have a CoordinateSystem.", cs); container.validate(cs); @@ -316,22 +326,30 @@ public void validate(final ImageCRS object) { * * @param object the object to validate, or {@code null}. */ + @SuppressWarnings("deprecation") public void validate(final EngineeringCRS object) { if (object == null) { return; } - validateReferenceSystem(object); + validateIdentifiedObject(object); final CoordinateSystem cs = object.getCoordinateSystem(); mandatory("EngineeringCRS: shall have a CoordinateSystem.", cs); container.validate(cs); + String message = "EngineeringCRS: illegal coordinate system type. Shall be one of" + + " affine, Cartesian, cylindrical, linear, polar, or spherical."; assertTrue(cs instanceof AffineCS || // Include the CartesianCS case. cs instanceof CylindricalCS || cs instanceof LinearCS || cs instanceof PolarCS || cs instanceof SphericalCS || cs instanceof UserDefinedCS, - "EngineeringCRS: illegal coordinate system type. Shall be one of affine, " - + "Cartesian, cylindrical, linear, polar, spherical or user defined."); + message); + + assertFalse(cs instanceof EllipsoidalCS || + cs instanceof VerticalCS || + cs instanceof ParametricCS || + cs instanceof TimeCS, + message); final Datum datum = object.getDatum(); mandatory("EngineeringCRS: shall have a Datum.", datum); @@ -353,7 +371,7 @@ public void validate(final VerticalCRS object) { if (object == null) { return; } - validateReferenceSystem(object); + validateIdentifiedObject(object); final VerticalCS cs = object.getCoordinateSystem(); mandatory("VerticalCRS: shall have a CoordinateSystem.", cs); container.validate(cs); @@ -374,7 +392,7 @@ public void validate(final TemporalCRS object) { if (object == null) { return; } - validateReferenceSystem(object); + validateIdentifiedObject(object); final TimeCS cs = object.getCoordinateSystem(); mandatory("TemporalCRS: shall have a CoordinateSystem.", cs); container.validate(cs); @@ -396,20 +414,80 @@ public void validate(final CompoundCRS object) { if (object == null) { return; } - validateReferenceSystem(object); + validateIdentifiedObject(object); final CoordinateSystem cs = object.getCoordinateSystem(); mandatory("CompoundCRS: shall have a CoordinateSystem.", cs); container.validate(cs); - + AxisDirection[] directions = null; + if (cs != null) { + directions = new AxisDirection[cs.getDimension()]; + for (int i=0; i components = object.getComponents(); mandatory("CompoundCRS: shall have components.", components); if (components != null) { + int dimension = 0; // If the above 'mandatory(…)' call accepted an empty list, we accept it too. assertNotEquals(1, components.size(), "CompoundCRS: shall have at least 2 components."); for (final CoordinateReferenceSystem component : components) { dispatch(component); + dimension = compareAxes(component.getCoordinateSystem(), directions, dimension); + } + if (directions != null) { + assertEquals(directions.length, dimension, "CompoundCRS: unexpected sum of the dimension of components."); + } + } + /* + * Verify the components again, but without nested compound CRS. + */ + final List singles = object.getSingleComponents(); + mandatory("CompoundCRS: shall have components.", singles); + if (singles != null) { + int dimension = 0; + assertNotEquals(1, singles.size(), "CompoundCRS: shall have at least 2 components."); + for (final SingleCRS component : singles) { + dispatch(component); + dimension = compareAxes(component.getCoordinateSystem(), directions, dimension); + } + if (directions != null) { + assertEquals(directions.length, dimension, "CompoundCRS: unexpected sum of the dimension of components."); + } + } + } + + /** + * Checks if the axis directions of the given coordinate system are the expected one. + * + * @param cs the coordinate system to validate, or {@code null} if none. + * @param directions the expected directions, or {@code null} if unknown. + * @param index index of the first element in the {@code directions} array. + * @return index after the last element in the {@code directions} array. + */ + private static int compareAxes(final CoordinateSystem cs, final AxisDirection[] directions, int index) { + if (directions != null) { + assertNotNull("CompoundCRS: missing coordinate system for component."); + final int dimension = cs.getDimension(); + assertTrue(index + dimension <= directions.length, "CompoundCRS: components have too many dimensions."); + for (int i=0; i message = () -> "CompoundCRS: inconsistent axis at dimension " + d + "."; + final CoordinateSystemAxis axis = cs.getAxis(d); + assertNotNull(axis, message); + assertEquals(expected, axis.getDirection(), message); + } } } + return index; } /** diff --git a/geoapi-conformance/src/main/java/org/opengis/test/referencing/CSValidator.java b/geoapi-conformance/src/main/java/org/opengis/test/referencing/CSValidator.java index 1f4585e93..100b53b4e 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/referencing/CSValidator.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/referencing/CSValidator.java @@ -45,12 +45,20 @@ * For example, {@link EllipsoidalCS} expects two angular values and one linear value (if 3D). */ public class CSValidator extends ReferencingValidator { + /** + * The space in which an axis orientation is defined. + * Orientations in the same space can be compared. + */ + static enum Space { + GEOGRAPHIC, TEMPORAL, ENGINEERING, MATRIX, DISPLAY; + } + /** * Orientation of an {@link AxisDirection}, used to detect if axes are perpendicular. */ static final class Orientation { /** Amount of degrees in one angle unit. */ static final double ANGLE_UNIT = 22.5; - /** Geographic, matrix or display. */ final String category; + /** Geographic, matrix or display. */ final Space category; /** Orientation as a multiple of 22.5°. */ final int orientation; /** @@ -59,47 +67,52 @@ static final class Orientation { * @param category geographic, matrix or display. * @param orientation orientation as a multiple of 22.5°. */ - Orientation(final String category, final int orientation) { + Orientation(final Space category, final int orientation) { this.category = category; this.orientation = orientation; } /** {@return a string representation for debugging purpose only}. */ @Override public String toString() { - return category + ':' + (orientation * ANGLE_UNIT) + '°'; + return category.name() + ':' + (orientation * ANGLE_UNIT) + '°'; } } /** - * The orientation of each {@link AxisDirection} enumeration elements. This is used - * by {@link #validate(CartesianCS)} for asserting that axes are perpendicular. + * The orientation of {@link AxisDirection} enumeration elements for which the direction will be verified. + * This array does not need to contain all axis directions, because it is used only by + * {@link #validate(CartesianCS)} for asserting that axes are perpendicular. */ - static final Orientation[] ORIENTATIONS = new Orientation[32]; + static final Orientation[] ORIENTATIONS = new Orientation[34]; static { - ORIENTATIONS[AxisDirection.NORTH .ordinal()] = new Orientation("geographic", 0); - ORIENTATIONS[AxisDirection.NORTH_NORTH_EAST .ordinal()] = new Orientation("geographic", 1); - ORIENTATIONS[AxisDirection.NORTH_EAST .ordinal()] = new Orientation("geographic", 2); - ORIENTATIONS[AxisDirection.EAST_NORTH_EAST .ordinal()] = new Orientation("geographic", 3); - ORIENTATIONS[AxisDirection.EAST .ordinal()] = new Orientation("geographic", 4); - ORIENTATIONS[AxisDirection.EAST_SOUTH_EAST .ordinal()] = new Orientation("geographic", 5); - ORIENTATIONS[AxisDirection.SOUTH_EAST .ordinal()] = new Orientation("geographic", 6); - ORIENTATIONS[AxisDirection.SOUTH_SOUTH_EAST .ordinal()] = new Orientation("geographic", 7); - ORIENTATIONS[AxisDirection.SOUTH .ordinal()] = new Orientation("geographic", 8); - ORIENTATIONS[AxisDirection.SOUTH_SOUTH_WEST .ordinal()] = new Orientation("geographic", 9); - ORIENTATIONS[AxisDirection.SOUTH_WEST .ordinal()] = new Orientation("geographic", 10); - ORIENTATIONS[AxisDirection.WEST_SOUTH_WEST .ordinal()] = new Orientation("geographic", 11); - ORIENTATIONS[AxisDirection.WEST .ordinal()] = new Orientation("geographic", 12); - ORIENTATIONS[AxisDirection.WEST_NORTH_WEST .ordinal()] = new Orientation("geographic", 13); - ORIENTATIONS[AxisDirection.NORTH_WEST .ordinal()] = new Orientation("geographic", 14); - ORIENTATIONS[AxisDirection.NORTH_NORTH_WEST .ordinal()] = new Orientation("geographic", 15); - ORIENTATIONS[AxisDirection.ROW_NEGATIVE .ordinal()] = new Orientation("matrix", 0); - ORIENTATIONS[AxisDirection.COLUMN_POSITIVE .ordinal()] = new Orientation("matrix", 4); - ORIENTATIONS[AxisDirection.ROW_POSITIVE .ordinal()] = new Orientation("matrix", 8); - ORIENTATIONS[AxisDirection.COLUMN_NEGATIVE .ordinal()] = new Orientation("matrix", 12); - ORIENTATIONS[AxisDirection.DISPLAY_UP .ordinal()] = new Orientation("display", 0); - ORIENTATIONS[AxisDirection.DISPLAY_RIGHT .ordinal()] = new Orientation("display", 4); - ORIENTATIONS[AxisDirection.DISPLAY_DOWN .ordinal()] = new Orientation("display", 8); - ORIENTATIONS[AxisDirection.DISPLAY_LEFT .ordinal()] = new Orientation("display", 12); + ORIENTATIONS[AxisDirection.NORTH .ordinal()] = new Orientation(Space.GEOGRAPHIC, 0); + ORIENTATIONS[AxisDirection.NORTH_NORTH_EAST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 1); + ORIENTATIONS[AxisDirection.NORTH_EAST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 2); + ORIENTATIONS[AxisDirection.EAST_NORTH_EAST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 3); + ORIENTATIONS[AxisDirection.EAST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 4); + ORIENTATIONS[AxisDirection.EAST_SOUTH_EAST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 5); + ORIENTATIONS[AxisDirection.SOUTH_EAST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 6); + ORIENTATIONS[AxisDirection.SOUTH_SOUTH_EAST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 7); + ORIENTATIONS[AxisDirection.SOUTH .ordinal()] = new Orientation(Space.GEOGRAPHIC, 8); + ORIENTATIONS[AxisDirection.SOUTH_SOUTH_WEST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 9); + ORIENTATIONS[AxisDirection.SOUTH_WEST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 10); + ORIENTATIONS[AxisDirection.WEST_SOUTH_WEST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 11); + ORIENTATIONS[AxisDirection.WEST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 12); + ORIENTATIONS[AxisDirection.WEST_NORTH_WEST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 13); + ORIENTATIONS[AxisDirection.NORTH_WEST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 14); + ORIENTATIONS[AxisDirection.NORTH_NORTH_WEST .ordinal()] = new Orientation(Space.GEOGRAPHIC, 15); + ORIENTATIONS[AxisDirection.ROW_NEGATIVE .ordinal()] = new Orientation(Space.MATRIX, 0); + ORIENTATIONS[AxisDirection.COLUMN_POSITIVE .ordinal()] = new Orientation(Space.MATRIX, 4); + ORIENTATIONS[AxisDirection.ROW_POSITIVE .ordinal()] = new Orientation(Space.MATRIX, 8); + ORIENTATIONS[AxisDirection.COLUMN_NEGATIVE .ordinal()] = new Orientation(Space.MATRIX, 12); + ORIENTATIONS[AxisDirection.DISPLAY_UP .ordinal()] = new Orientation(Space.DISPLAY, 0); + ORIENTATIONS[AxisDirection.DISPLAY_RIGHT .ordinal()] = new Orientation(Space.DISPLAY, 4); + ORIENTATIONS[AxisDirection.DISPLAY_DOWN .ordinal()] = new Orientation(Space.DISPLAY, 8); + ORIENTATIONS[AxisDirection.DISPLAY_LEFT .ordinal()] = new Orientation(Space.DISPLAY, 12); + ORIENTATIONS[AxisDirection.FORWARD .ordinal()] = new Orientation(Space.ENGINEERING, 0); + ORIENTATIONS[AxisDirection.STARBOARD .ordinal()] = new Orientation(Space.ENGINEERING, 4); + ORIENTATIONS[AxisDirection.AFT .ordinal()] = new Orientation(Space.ENGINEERING, 8); + ORIENTATIONS[AxisDirection.PORT .ordinal()] = new Orientation(Space.ENGINEERING, 12); } /** @@ -119,6 +132,7 @@ public CSValidator(final ValidatorContainer container) { * @param object the object to dispatch to {@code validate(…)} methods, or {@code null}. * @return number of {@code validate(…)} methods invoked in this class for the given object. */ + @SuppressWarnings("deprecation") public int dispatch(final CoordinateSystem object) { int n = 0; if (object != null) { @@ -285,6 +299,7 @@ public void validate(final TimeCS object) { * * @param object the object to validate, or {@code null}. */ + @Deprecated(since="3.1") public void validate(final UserDefinedCS object) { if (object == null) { return; diff --git a/geoapi-conformance/src/main/java/org/opengis/test/referencing/DatumValidator.java b/geoapi-conformance/src/main/java/org/opengis/test/referencing/DatumValidator.java index fd6730088..604c3f94f 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/referencing/DatumValidator.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/referencing/DatumValidator.java @@ -18,13 +18,18 @@ package org.opengis.test.referencing; import java.util.Date; +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.measure.Unit; import javax.measure.quantity.Angle; import javax.measure.quantity.Length; import org.opengis.referencing.datum.*; +import org.opengis.referencing.IdentifiedObject; import org.opengis.test.ValidatorContainer; +import static java.lang.Double.POSITIVE_INFINITY; import static org.junit.jupiter.api.Assertions.*; import static org.opengis.test.Assertions.assertBetween; @@ -58,6 +63,7 @@ public DatumValidator(ValidatorContainer container) { * @param object the object to dispatch to {@code validate(…)} methods, or {@code null}. * @return number of {@code validate(…)} methods invoked in this class for the given object. */ + @SuppressWarnings("deprecation") public int dispatch(final Datum object) { int n = 0; if (object != null) { @@ -66,6 +72,7 @@ public int dispatch(final Datum object) { if (object instanceof TemporalDatum) {validate((TemporalDatum) object); n++;} if (object instanceof ImageDatum) {validate((ImageDatum) object); n++;} if (object instanceof EngineeringDatum) {validate((EngineeringDatum) object); n++;} + if (object instanceof DatumEnsemble) {validate((DatumEnsemble) object); n++;} if (n == 0) { validateIdentifiedObject(object); } @@ -120,10 +127,15 @@ public void validate(final Ellipsoid object) { final double semiMajor = object.getSemiMajorAxis(); final double semiMinor = object.getSemiMinorAxis(); final double inverseFlattening = object.getInverseFlattening(); - assertTrue(semiMajor > 0, "Ellipsoid: expected semi-major axis length > 0."); - assertTrue(semiMinor > 0, "Ellipsoid: expected semi-minor axis length > 0."); - assertTrue(semiMinor <= semiMajor, "Ellipsoid: expected semi-minor <= semi-major axis length."); + assertTrue(semiMajor > 0, invalidAxisLength("semi-major", '>', 0, semiMajor)); + assertTrue(semiMinor > 0, invalidAxisLength("semi-minor", '>', 0, semiMinor)); + assertTrue(semiMajor < POSITIVE_INFINITY, invalidAxisLength("semi-major", '<', POSITIVE_INFINITY, semiMajor)); + assertTrue(semiMinor <= semiMajor, invalidAxisLength("semi-minor", '≤', semiMajor, semiMinor)); assertTrue(inverseFlattening > 0, "Ellipsoid: expected inverse flattening > 0."); + object.getSemiMedianAxis().ifPresent((semiMedian) -> { + assertTrue(semiMedian > semiMinor, invalidAxisLength("semi-median", '>', semiMinor, semiMedian)); + assertTrue(semiMedian < semiMajor, invalidAxisLength("semi-median", '<', semiMajor, semiMedian)); + }); if (!object.isSphere()) { assertEquals(semiMajor - semiMajor/inverseFlattening, semiMinor, semiMinor*DEFAULT_TOLERANCE, "Ellipsoid: inconsistent semi-major axis length."); @@ -132,6 +144,19 @@ public void validate(final Ellipsoid object) { } } + /** + * Returns a supplier of message to format in case of error while verifying the length of an ellipsoid axis. + * + * @param name name of the ellipsoid axis to verify. + * @param operator the mathematical operator used for comparing the actual value with the limit. + * @param limit minimum or maximum expected value. + * @param actual the actual value found in the ellipsoid. + * @return the message to format if the assertion fails. + */ + private static Supplier invalidAxisLength(String name, char operator, double limit, double actual) { + return () -> "Ellipsoid: expected " + name + " axis length " + operator + ' ' + limit + " but got " + actual + '.'; + } + /** * Validates the given datum. * @@ -161,8 +186,6 @@ public void validate(final VerticalDatum object) { return; } validateIdentifiedObject(object); - final VerticalDatumType type = object.getVerticalDatumType(); - mandatory("VerticalDatum: shall have a datum type.", type); } /** @@ -177,15 +200,16 @@ public void validate(final TemporalDatum object) { validateIdentifiedObject(object); final Date origin = object.getOrigin(); mandatory("TemporalDatum: expected an origin.", origin); - forbidden("TemporalDatum: should not have anchor point.", object.getAnchorPoint()); - forbidden("TemporalDatum: should not have realization epoch.", object.getRealizationEpoch()); } /** * Validates the given datum. * * @param object the object to validate, or {@code null}. + * + * @deprecated Replaced by {@link EngineeringDatum} as of ISO 19111:2019. */ + @Deprecated(since="3.1") public void validate(final ImageDatum object) { if (object == null) { return; @@ -206,4 +230,39 @@ public void validate(final EngineeringDatum object) { } validateIdentifiedObject(object); } + + /** + * Validates the given datum ensemble. + * + * @param object the object to validate, or {@code null}. + * + * @since 3.1 + */ + public void validate(final DatumEnsemble object) { + if (object == null) { + return; + } + validateIdentifiedObject(object); + final Collection members = object.getMembers(); + assertNotNull(members, "DatumEnsemble: shall have at least two members."); + assertTrue(members.size() >= 2, "DatumEnsemble: shall have at least two members."); + final Consumer c = new Consumer<>() { + /** The first conventional RS found in datum. */ + private IdentifiedObject conventionalRS; + + /** Verifies that the given conventional RS is the same as in previous datum. */ + @Override public void accept(final IdentifiedObject rs) { + if (conventionalRS == null) { + conventionalRS = rs; + } else { + assertEquals(conventionalRS, rs, "Members of datum ensemble shall share the same conventional RS."); + } + } + }; + for (final Datum member : members) { + dispatch(member); + member.getConventionalRS().ifPresent(c); + } + container.validate(object.getEnsembleAccuracy()); + } } diff --git a/geoapi-conformance/src/main/java/org/opengis/test/referencing/ObjectFactoryTest.java b/geoapi-conformance/src/main/java/org/opengis/test/referencing/ObjectFactoryTest.java index 911891976..06ac5dade 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/referencing/ObjectFactoryTest.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/referencing/ObjectFactoryTest.java @@ -163,7 +163,7 @@ public void testWGS84_3D() throws FactoryException { final Unit metre = units.metre(); final Unit degree = units.degree(); - // Build a geodetic datum. + // Build a geodetic reference frame. assumeTrue(datumFactory != null, NO_DATUM_FACTORY); validators.validate(datum = datumFactory.createGeodeticDatum(name("World Geodetic System 1984"), datumFactory.createEllipsoid (name("WGS 84"), 6378137.0, 298.257223563, metre), diff --git a/geoapi-conformance/src/main/java/org/opengis/test/referencing/PseudoEpsgFactory.java b/geoapi-conformance/src/main/java/org/opengis/test/referencing/PseudoEpsgFactory.java index b7fb273bc..fa5ec1e7b 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/referencing/PseudoEpsgFactory.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/referencing/PseudoEpsgFactory.java @@ -34,7 +34,6 @@ import org.opengis.referencing.operation.*; import org.opengis.metadata.citation.Citation; import org.opengis.util.FactoryException; -import org.opengis.util.InternationalString; import org.opengis.test.util.PseudoFactory; import org.opengis.test.ValidatorContainer; import org.opengis.test.Units; @@ -211,24 +210,6 @@ public Set getAuthorityCodes(final Class typ return Collections.emptySet(); } - /** - * Returns a description of the object corresponding to a code. - * The default implementation returns {@code null}. - * - * @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 FactoryException if this method cannot provide the requested information. - * - * @deprecated This method is ambiguous. - */ - @Override - @SuppressWarnings("removal") - @Deprecated(since = "3.1", forRemoval = true) - public InternationalString getDescriptionText(final String code) throws FactoryException { - return null; - } - /** * Builds a map of properties for a referencing object to be built. The map shall contain * at least the {@link IdentifiedObject#NAME_KEY} identifier associated to the given value. @@ -323,8 +304,11 @@ public EngineeringDatum createEngineeringDatum(String code) throws FactoryExcept * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. * * @throws FactoryException if this method cannot provide the requested information. + * + * @deprecated {@code ImageDatum} is replaced by {@link EngineeringDatum} as of ISO 19111:2019. */ @Override + @Deprecated(since="3.1") public ImageDatum createImageDatum(String code) throws FactoryException { final int id = parseCode(code); switch (id) { @@ -372,7 +356,7 @@ public ParametricDatum createParametricDatum(String code) throws FactoryExceptio } /** - * Returns a {@linkplain GeodeticDatum geodetic datum} from a code. + * Returns a {@linkplain GeodeticDatum geodetic reference frame} from a code. * * * @@ -1009,8 +993,11 @@ public GeocentricCRS createGeocentricCRS(String code) throws FactoryException { * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. * * @throws FactoryException if this method cannot provide the requested information. + * + * @deprecated {@code ImageCRS} is replaced by {@link EngineeringCRS} as of ISO 19111:2019. */ @Override + @Deprecated(since="3.1") public ImageCRS createImageCRS(String code) throws FactoryException { final int id = parseCode(code); switch (id) { diff --git a/geoapi-conformance/src/main/java/org/opengis/test/referencing/ReferencingValidator.java b/geoapi-conformance/src/main/java/org/opengis/test/referencing/ReferencingValidator.java index 432ee8012..5ab1b42c3 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/referencing/ReferencingValidator.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/referencing/ReferencingValidator.java @@ -17,8 +17,6 @@ */ package org.opengis.test.referencing; -import java.util.Collection; - import org.opengis.referencing.*; import org.opengis.referencing.cs.*; import org.opengis.referencing.crs.*; @@ -27,13 +25,11 @@ import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.metadata.extent.Extent; import org.opengis.metadata.Identifier; -import org.opengis.util.GenericName; import org.opengis.util.InternationalString; import org.opengis.test.Units; import org.opengis.test.Validator; import org.opengis.test.ValidatorContainer; -import static org.junit.jupiter.api.Assertions.*; /** @@ -86,24 +82,11 @@ public final void dispatchObject(final IdentifiedObject object) { if (object instanceof CoordinateOperation) {container.validate((CoordinateOperation) object); n++;} if (object instanceof OperationMethod) {container.validate((OperationMethod) object); n++;} if (n == 0) { - if (object instanceof ReferenceSystem) { - validateReferenceSystem((ReferenceSystem) object); - } else { - validateIdentifiedObject(object); - } + validateIdentifiedObject(object); } } } - /** - * Ensures that the given identifier has a {@linkplain Identifier#getCode() code}. - * - * @param object the object to validate, or {@code null}. - */ - public void validate(final Identifier object) { - container.validate(object); - } - /** * Ensures that the given domain has a scope and a domain of validity. * @@ -123,20 +106,6 @@ public void validate(final ObjectDomain object) { container.validate(domain); } - /** - * Performs the validation that are common to all reference systems. This method is - * invoked by {@code validate} methods after they have determined the type of their - * argument. - * - * @param object the object to validate (cannot be null). - */ - @SuppressWarnings("removal") - final void validateReferenceSystem(final ReferenceSystem object) { - validateIdentifiedObject(object); - container.validate(object.getScope()); - container.validate(object.getDomainOfValidity()); - } - /** * Performs the validation that are common to all identified objects. This method is * invoked by {@code validate} methods after they have determined the type of their @@ -145,31 +114,12 @@ final void validateReferenceSystem(final ReferenceSystem object) { * @param object the object to validate (cannot be null). */ final void validateIdentifiedObject(final IdentifiedObject object) { - validate(object.getName()); - final Collection identifiers = object.getIdentifiers(); - if (identifiers != null) { - validate(identifiers); - for (final Identifier id : identifiers) { - assertNotNull(id, "IdentifiedObject: getIdentifiers() cannot contain null element."); - validate(id); - } - } - final Collection alias = object.getAlias(); - if (alias != null) { - validate(alias); - for (final GenericName name : alias) { - assertNotNull(alias, "IdentifiedObject: getAlias() cannot contain null element."); - container.validate(name); - } - } - final Collection domains = object.getDomains(); - if (domains != null) { - validate(domains); - for (final ObjectDomain domain : domains) { - assertNotNull(domains, "IdentifiedObject: getDomains() cannot contain null element."); - container.validate(domain); - } - } + final Identifier name = object.getName(); + mandatory("IdentifiedObject: shall have a name.", name); + container.validate(name); + validate("identifier", object.getIdentifiers(), ValidatorContainer::validate, false); + validate("alias", object.getAlias(), ValidatorContainer::validate, false); + validate("domain", object.getDomains(), ValidatorContainer::validate, false); container.validate(object.getRemarks()); } } diff --git a/geoapi-conformance/src/main/java/org/opengis/test/report/OperationParametersReport.java b/geoapi-conformance/src/main/java/org/opengis/test/report/OperationParametersReport.java index 758b0c595..e38308efd 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/report/OperationParametersReport.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/report/OperationParametersReport.java @@ -165,11 +165,11 @@ public Row(final IdentifiedObject object, final Set codeSpaces) { int i=0; final String[] array = new String[size]; for (final Map.Entry entry : toCopy.entrySet()) { - String name = escape(entry.getKey()); + String text = escape(entry.getKey()); if (entry.getValue()) { - name = "" + name + ""; + text = "" + text + ""; } - array[i++] = name; + array[i++] = text; } if (names.put(cs, array) != null) { throw new AssertionError(cs); // Should never happen. @@ -289,9 +289,9 @@ final void write(final Appendable out, final String[] codeSpaces, if (codes != null) { // Intentionally no enclosing
    . if (!isGroup) out.append("
  • "); - for (final String name : codes) { + for (final String code : codes) { if (hasMore) out.append("
    "); - out.append(name); + out.append(code); hasMore = true; } if (!isGroup) out.append("
  • "); diff --git a/geoapi-conformance/src/main/java/org/opengis/test/wkt/CRSParserTest.java b/geoapi-conformance/src/main/java/org/opengis/test/wkt/CRSParserTest.java index f1440848d..df081bcb8 100644 --- a/geoapi-conformance/src/main/java/org/opengis/test/wkt/CRSParserTest.java +++ b/geoapi-conformance/src/main/java/org/opengis/test/wkt/CRSParserTest.java @@ -19,6 +19,7 @@ import java.util.Date; import java.util.List; +import java.util.Optional; import javax.measure.Unit; import javax.measure.quantity.Angle; import javax.measure.quantity.Length; @@ -175,7 +176,7 @@ private static void verifyAxisAbbreviations(final CoordinateSystem cs, final Str } /** - * Asserts the given character sequence is either null or equals to the given value. + * Asserts the given character sequence is either null or equal to the given value. * This is used for optional elements like remarks. * * @param property the property being tested, for producing a message in case of assertion failure. @@ -188,6 +189,20 @@ private static void assertNullOrEquals(final String property, final String expec } } + /** + * Asserts the given character sequence is either empty or equal to the given value. + * This is used for optional elements like remarks. + * + * @param property the property being tested, for producing a message in case of assertion failure. + * @param expected the expected value. + * @param actual the actual value. + */ + private static void assertEmptyOrEquals(String property, String expected, Optional actual) { + if (actual.isPresent()) { + assertEquals(expected, actual.get().toString(), property); + } + } + /** * Pre-process the WKT string before parsing. * The default implementation performs the following changes for strict ISO 19162 compliance: @@ -1002,7 +1017,7 @@ public void testEngineeringRotated() throws FactoryException { verifyIdentification (crs, "A construction site CRS", null); verifyDatum (crs.getDatum(), "P1"); - assertNullOrEquals ("datum.anchor", "Peg in south corner", crs.getDatum().getAnchorPoint()); + assertEmptyOrEquals ("datum.anchor", "Peg in south corner", crs.getDatum().getAnchorDefinition()); verifyCoordinateSystem (crs.getCoordinateSystem(), CartesianCS.class, new AxisDirection[] {SOUTH_WEST, SOUTH_EAST}, metre); } @@ -1045,7 +1060,7 @@ public void testEngineeringForShip() throws FactoryException { verifyIdentification (crs, "A ship-centred CRS", null); verifyDatum (crs.getDatum(), "Ship reference point"); - assertNullOrEquals ("datum.anchor", "Centre of buoyancy", crs.getDatum().getAnchorPoint()); + assertEmptyOrEquals ("datum.anchor", "Centre of buoyancy", crs.getDatum().getAnchorDefinition()); verifyAxisAbbreviations(cs = crs.getCoordinateSystem(), "x", "y", "z"); verifyCoordinateSystem (cs, CartesianCS.class, new AxisDirection[] {valueOf("forward"), valueOf("starboard"), DOWN}, metre); } diff --git a/geoapi-conformance/src/test/java/org/opengis/test/referencing/ValidatorTest.java b/geoapi-conformance/src/test/java/org/opengis/test/referencing/ValidatorTest.java index 0e76875db..fc86bfc65 100644 --- a/geoapi-conformance/src/test/java/org/opengis/test/referencing/ValidatorTest.java +++ b/geoapi-conformance/src/test/java/org/opengis/test/referencing/ValidatorTest.java @@ -52,15 +52,16 @@ public void testOrientations() { SOUTH, SOUTH_SOUTH_WEST, SOUTH_WEST, WEST_SOUTH_WEST, WEST, WEST_NORTH_WEST, NORTH_WEST, NORTH_NORTH_WEST, ROW_NEGATIVE, COLUMN_POSITIVE, ROW_POSITIVE, COLUMN_NEGATIVE, - DISPLAY_UP, DISPLAY_RIGHT, DISPLAY_DOWN, DISPLAY_LEFT + DISPLAY_UP, DISPLAY_RIGHT, DISPLAY_DOWN, DISPLAY_LEFT, + FORWARD, STARBOARD, AFT, PORT }; - String type = "geographic"; - int value = 0; - int step = 1; + var type = CSValidator.Space.GEOGRAPHIC; + int value = 0; + int step = 1; for (final AxisDirection direction : directions) { final CSValidator.Orientation orientation = CSValidator.ORIENTATIONS[direction.ordinal()]; assertNotNull(orientation, direction.toString()); - if (!orientation.category.equals(type)) { + if (type != orientation.category) { assertEquals(16, value, "Expected orientations in the [0…360]° range."); type = orientation.category; value = 0; diff --git a/geoapi-examples/src/main/java/module-info.java b/geoapi-examples/src/main/java/module-info.java index aeacf0a30..87f90a8ec 100644 --- a/geoapi-examples/src/main/java/module-info.java +++ b/geoapi-examples/src/main/java/module-info.java @@ -18,7 +18,7 @@ /** - * An example of a very simple GeoAPI implementation. + * Examples of GeoAPI implementations. * This implementation is not complete, but can be used as a starting point for implementers. * Since every files in this module are placed into the Public Domain, * anyone is free to do whatever they wish with those files. @@ -34,7 +34,7 @@ * For example some libraries make no distinction between: * *
      - *
    • Ellipsoid and Geodetic Datum
    • + *
    • Ellipsoid and Geodetic Reference Frame
    • *
    • Coordinate System and Coordinate Reference System
    • *
    • Coordinate Operation and Math Transform
    • *
    diff --git a/geoapi-examples/src/main/java/org/opengis/example/parameter/SimpleParameter.java b/geoapi-examples/src/main/java/org/opengis/example/parameter/SimpleParameter.java index 7e3808d1a..2aeda1df2 100644 --- a/geoapi-examples/src/main/java/org/opengis/example/parameter/SimpleParameter.java +++ b/geoapi-examples/src/main/java/org/opengis/example/parameter/SimpleParameter.java @@ -292,7 +292,7 @@ public int intValue() throws InvalidParameterTypeException { } /** - * Returns the boolean value of an operation parameter. + * Returns the Boolean value of an operation parameter. * If {@linkplain #LENIENT lenient}, this method makes the following choice: * *
      @@ -301,8 +301,8 @@ public int intValue() throws InvalidParameterTypeException { *
    • Throws an exception in all other cases
    • *
    * - * @return the boolean value represented by this parameter. - * @throws InvalidParameterTypeException if the value cannot be converted to a boolean. + * @return the Boolean value represented by this parameter. + * @throws InvalidParameterTypeException if the value cannot be converted to a Boolean. */ @Override public boolean booleanValue() throws InvalidParameterTypeException { @@ -310,7 +310,7 @@ public boolean booleanValue() throws InvalidParameterTypeException { if (value == 0) return false; if (value == 1) return true; } - throw new InvalidParameterTypeException("Value " + value + " is not a boolean.", code); + throw new InvalidParameterTypeException("Value " + value + " is not a Boolean.", code); } /** @@ -490,8 +490,8 @@ public void setValue(final int value) throws InvalidParameterValueException { } /** - * Sets the parameter value as a boolean. If {@linkplain #LENIENT lenient}, the boolean value - * {@code true} is stored as the numeric value 1 and the boolean value {@code false} is stored + * Sets the parameter value as a Boolean. If {@linkplain #LENIENT lenient}, the Boolean value + * {@code true} is stored as the numeric value 1 and the Boolean value {@code false} is stored * as the numeric value 0. * * @throws InvalidParameterValueException if this parameter cannot be set to the given value. @@ -531,16 +531,15 @@ public void setValue(final Object value) throws InvalidParameterValueException { } /** - * Returns a new parameter with the same {@linkplain #authority authority}, {@linkplain #code code} - * and {@linkplain #type} than this parameter. The {@linkplain #value} is left to their default - * value. + * Returns a new parameter with the same authority, code and type than this parameter. + * The {@linkplain #value} is left to their default value. * - *
    Implementation note: - * since this simple class implements both the {@linkplain ParameterValue value} and the + *

    Implementation note

    + * Since this simple class implements both the {@linkplain ParameterValue value} and the * {@linkplain ParameterDescriptor descriptor} interfaces, this method is very similar to * the {@link #clone()} method. However, in more sophisticated libraries, the * {@link ParameterDescriptor#createValue()} and {@link ParameterValue#clone()} - * methods are likely to be defined in different objects.
    + * methods are likely to be defined in different objects. * * @return a new parameter with the same authority, code and type than this parameter. * @@ -552,8 +551,8 @@ public SimpleParameter createValue() { } /** - * Returns a copy of this parameter value. This method is similar to {@link #createValue()} - * except for the following: + * Returns a copy of this parameter value. + * This method is similar to {@link #createValue()} except for the following: * *
      *
    • This method returns an instance of the same class.
    • diff --git a/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleCRS.java b/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleCRS.java index 7383dac10..830af6612 100644 --- a/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleCRS.java +++ b/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleCRS.java @@ -16,6 +16,7 @@ import org.opengis.referencing.cs.EllipsoidalCS; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.cs.CoordinateSystemAxis; +import org.opengis.referencing.cs.CoordinateDataType; import org.opengis.referencing.crs.SingleCRS; import org.opengis.referencing.crs.TemporalCRS; import org.opengis.referencing.crs.VerticalCRS; @@ -286,5 +287,14 @@ public TemporalDatum getDatum() { public Date getOrigin() { return new Date(origin); } + + /** + * Returns the type (measure, integer or data-time) of coordinate values. + * This simple implementation supports only {@link CoordinateDataType#MEASURE}. + */ + @Override + public CoordinateDataType getCoordinateType() { + return CoordinateDataType.MEASURE; + } } } diff --git a/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleDatum.java b/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleDatum.java index 11d2393a0..d5b08dcee 100644 --- a/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleDatum.java +++ b/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleDatum.java @@ -21,8 +21,7 @@ /** * A {@link GeodeticDatum} using the Greenwich prime meridian. - * This class does not distinguish between Geodetic Datum and Ellipsoid, - * therefor it implements the two interfaces. + * This class implements both Geodetic Reference Frame and Ellipsoid in a single class for simplicity. */ public class SimpleDatum extends SimpleIdentifiedObject implements GeodeticDatum, Ellipsoid { /** @@ -89,7 +88,7 @@ public Unit getAngularUnit() { protected final double inverseFlattening; /** - * Creates a new geodetic datum for the given authority, name, and ellipsoid axis length. + * Creates a new geodetic reference frame for the given authority, name, and ellipsoid axis length. * * @param authority organization responsible for definition of the name, or {@code null}. * @param name the name of the new CRS. @@ -105,8 +104,7 @@ public SimpleDatum(final Citation authority, final String name, } /** - * Returns the ellipsoid, which is represented directly by {@code this} implementation - * class since it does not distinguish geodetic datum and ellipsoid. + * Returns the ellipsoid, which is represented directly by {@code this}. * * @return the ellipsoid. */ @@ -161,10 +159,9 @@ public double getInverseFlattening() { } /** - * Indicates if the {@linkplain #getInverseFlattening inverse flattening} is definitive for - * this ellipsoid. The default implementation returns {@code true} for if this ellipsoid is - * not a sphere, since the constructor of this {@code SimpleDatum} class expects a - * {@code inverseFlattening} value. + * Indicates if the {@linkplain #getInverseFlattening inverse flattening} is definitive for this ellipsoid. + * The default implementation returns {@code true} for if this ellipsoid is not a sphere, + * since the constructor of this {@code SimpleDatum} class expects a {@code inverseFlattening} value. * * @return {@code true} if the {@linkplain #getInverseFlattening() inverse flattening} is definitive, * or {@code false} if the {@linkplain #getSemiMinorAxis() polar radius} is definitive. diff --git a/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleTransform.java b/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleTransform.java index 3dde6caa5..3a01d8317 100644 --- a/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleTransform.java +++ b/geoapi-examples/src/main/java/org/opengis/example/referencing/SimpleTransform.java @@ -7,6 +7,7 @@ import java.util.Arrays; import java.util.Objects; +import org.opengis.coordinate.CoordinateSet; import org.opengis.metadata.citation.Citation; import org.opengis.geometry.DirectPosition; @@ -105,6 +106,22 @@ public int getTargetDimensions() { return targetCRS.getCoordinateSystem().getDimension(); } + /** + * Changes coordinates from source CRS to target CRS. + * The default implementation transform the coordinate tuples immediately and stores the result. + * This is okay for small coordinate set, but inefficient for large ones. + * + * @param data the coordinates to change. + * @return the result of changing coordinates. + * @throws TransformException if some coordinates cannot be changed. + * Note that this exception not being thrown is not a guarantee that the computation + * will not fail later, for example during a stream terminal operation. + */ + @Override + public CoordinateSet transform(final CoordinateSet data) throws TransformException { + return new TransformedCoordinateSet(this, data); + } + /** * Gets the math transform, which is represented directly by {@code this} implementation * class since it does not distinguish operation and transform. diff --git a/geoapi-examples/src/main/java/org/opengis/example/referencing/TransformedCoordinateSet.java b/geoapi-examples/src/main/java/org/opengis/example/referencing/TransformedCoordinateSet.java new file mode 100644 index 000000000..e61953d12 --- /dev/null +++ b/geoapi-examples/src/main/java/org/opengis/example/referencing/TransformedCoordinateSet.java @@ -0,0 +1,129 @@ +/* + * GeoAPI - Java interfaces for OGC/ISO standards + * This file is hereby placed into the Public Domain. + * This means anyone is free to do whatever they wish with this file. + */ +package org.opengis.example.referencing; + +import java.util.Arrays; +import java.util.Objects; +import java.util.Iterator; +import java.util.Optional; +import java.util.stream.Stream; +import java.time.temporal.Temporal; +import org.opengis.geometry.DirectPosition; +import org.opengis.coordinate.CoordinateSet; +import org.opengis.coordinate.CoordinateMetadata; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.operation.CoordinateOperation; +import org.opengis.referencing.operation.MathTransform; +import org.opengis.referencing.operation.TransformException; + + +/** + * The result of transforming coordinate tuples using the math transform of a given coordinate operation. + * This class is used for the default implementation of {@link SimpleTransform#transform(CoordinateSet)}. + * This default implementation is okay for small set but should not be used for large ones. + * + * @author Martin Desruisseaux (Geomatys) + * @version 3.1 + * @since 3.1 + */ +final class TransformedCoordinateSet implements CoordinateSet, CoordinateMetadata { + /** + * The CRS of the transformed coordinate tuples. + * + * @see #getCoordinateReferenceSystem() + */ + private final CoordinateReferenceSystem crs; + + /** + * Date at which coordinate tuples are valid, or {@code null} if the CRS is not dynamic. + * + * @see #getCoordinateEpoch() + */ + private final Temporal epoch; + + /** + * The transformed coordinate tuples. + * + * @see #iterator() + * @see #stream() + */ + private final DirectPosition[] positions; + + /** + * Creates a new transformed coordinate set. + * + * @param operation the coordinate operation to apply. + * @param data the coordinate tuples to transform. + * @throws TransformException if the data cannot be changed. + */ + TransformedCoordinateSet(final CoordinateOperation operation, final CoordinateSet data) throws TransformException { + final CoordinateMetadata md = data.getCoordinateMetadata(); + crs = Objects.requireNonNull(operation.getTargetCRS(), "Missing target CRS."); + if (!crs.equals(md.getCoordinateReferenceSystem())) { + throw new TransformException("Unexpected data CRS."); + } + epoch = operation.getTargetEpoch().orElse(null); + if (!Objects.equals(epoch, md.getCoordinateEpoch())) { + throw new TransformException("Missing or unexpected data epoch."); + } + final MathTransform mt = operation.getMathTransform(); + try { + positions = data.stream().sequential().map((p) -> { + try { + return mt.transform(p, null); + } catch (TransformException e) { + throw new RuntimeException(e); // Checked exception not allowed here. + } + }).toArray(DirectPosition[]::new); + } catch (RuntimeException e) { + final Throwable cause = e.getCause(); + if (cause instanceof TransformException) { + throw (TransformException) cause; // The checked exception that we couldn't throw in above lambda. + } + throw e; + } + } + + /** + * Returns the coordinate metadata to which this coordinate set is referenced. + */ + @Override + public CoordinateMetadata getCoordinateMetadata() { + return this; + } + + /** + * Returns the CRS in which the coordinate tuples are given. + */ + @Override + public CoordinateReferenceSystem getCoordinateReferenceSystem() { + return crs; + } + + /** + * Returns the date at which coordinate tuples referenced to a dynamic CRS are valid. + */ + @Override + public Optional getCoordinateEpoch() { + return Optional.ofNullable(epoch); + } + + /** + * Returns the transformed positions described by coordinate tuples. + */ + @Override + public Iterator iterator() { + return Arrays.asList(positions).iterator(); + } + + /** + * Returns a stream of transformed coordinate tuples. + */ + @Override + public Stream stream() { + return Arrays.stream(positions); + } +} diff --git a/geoapi-pending/src/main/java/module-info.java b/geoapi-pending/src/main/java/module-info.java index 003fad4d6..559fb2d15 100644 --- a/geoapi-pending/src/main/java/module-info.java +++ b/geoapi-pending/src/main/java/module-info.java @@ -19,7 +19,7 @@ /** * Java interfaces derived from specifications of International Organization for Standardization (ISO) - * and Open Geospatial Consortium (OGC), pending review. + * and Open Geospatial Consortium (OGC), pending review. * Some of those interfaces may migrate to the normative GeoAPI module when they will be considered ready. * *

      Every interfaces defined in the normative module are reproduced here. @@ -57,6 +57,7 @@ exports org.opengis.referencing.cs; exports org.opengis.referencing.crs; exports org.opengis.referencing.operation; + exports org.opengis.coordinate; exports org.opengis.geometry; exports org.opengis.geometry.coordinate; exports org.opengis.feature; diff --git a/geoapi-pending/src/main/java/org/opengis/geometry/coordinate/BSplineCurve.java b/geoapi-pending/src/main/java/org/opengis/geometry/coordinate/BSplineCurve.java index da076bb38..868ad616f 100644 --- a/geoapi-pending/src/main/java/org/opengis/geometry/coordinate/BSplineCurve.java +++ b/geoapi-pending/src/main/java/org/opengis/geometry/coordinate/BSplineCurve.java @@ -27,7 +27,7 @@ * A piecewise parametric polynomial or rational curve described * in terms of control points and basis functions. If the weights in the knots are equal * then it is a polynomial spline. If not, then it is a rational function spline. If - * the boolean {@link #isPolynomial} is set to {@code true} then the weights shall all be set to 1. + * the Boolean {@link #isPolynomial} is set to {@code true} then the weights shall all be set to 1. * A B-spline curve is a piecewise Bézier curve if it is quasi-uniform except that the * interior knots have multiplicity "degree" rather than having multiplicity one. In * this subtype the knot spacing shall be 1.0, starting at 0.0. A piecewise Bézier curve diff --git a/geoapi-pending/src/main/java/org/opengis/geometry/coordinate/BSplineSurface.java b/geoapi-pending/src/main/java/org/opengis/geometry/coordinate/BSplineSurface.java index a94b950f0..67ec813f6 100644 --- a/geoapi-pending/src/main/java/org/opengis/geometry/coordinate/BSplineSurface.java +++ b/geoapi-pending/src/main/java/org/opengis/geometry/coordinate/BSplineSurface.java @@ -27,7 +27,7 @@ /** * A rational or polynomial parametric surface that is represented by control points, basis * functions and possibly weights. If the weights are all equal then the spline is piecewise - * polynomial. If they are not equal, then the spline is piecewise rational. If the boolean + * polynomial. If they are not equal, then the spline is piecewise rational. If the Boolean * {@link #isPolynomial isPolynomial} is set to {@code true} then the weights shall all be * set to 1. * diff --git a/geoapi-pending/src/main/java/org/opengis/referencing/gazetteer/ReferenceSystemUsingIdentifiers.java b/geoapi-pending/src/main/java/org/opengis/referencing/gazetteer/ReferenceSystemUsingIdentifiers.java index 478992dfa..4f1e07241 100644 --- a/geoapi-pending/src/main/java/org/opengis/referencing/gazetteer/ReferenceSystemUsingIdentifiers.java +++ b/geoapi-pending/src/main/java/org/opengis/referencing/gazetteer/ReferenceSystemUsingIdentifiers.java @@ -109,8 +109,7 @@ public interface ReferenceSystemUsingIdentifiers extends ReferenceSystem { * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019. */ @Override - @SuppressWarnings("removal") - @Deprecated(since="3.1", forRemoval=true) + @Deprecated(since = "3.1") @UML(identifier="domainOfValidity", obligation=MANDATORY, specification=ISO_19112) default Extent getDomainOfValidity() { return ReferenceSystem.super.getDomainOfValidity(); diff --git a/geoapi-pending/src/main/java/org/opengis/temporal/TemporalReferenceSystem.java b/geoapi-pending/src/main/java/org/opengis/temporal/TemporalReferenceSystem.java index a757579b8..572388692 100644 --- a/geoapi-pending/src/main/java/org/opengis/temporal/TemporalReferenceSystem.java +++ b/geoapi-pending/src/main/java/org/opengis/temporal/TemporalReferenceSystem.java @@ -67,8 +67,7 @@ public interface TemporalReferenceSystem extends ReferenceSystem { * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019. */ @Override - @SuppressWarnings("removal") - @Deprecated(since="3.1", forRemoval=true) + @Deprecated(since = "3.1") @UML(identifier="DomainOfValidity", obligation=MANDATORY, specification=ISO_19108) default Extent getDomainOfValidity() { return ReferenceSystem.super.getDomainOfValidity(); diff --git a/geoapi/pom.xml b/geoapi/pom.xml index 00d8ac400..3c430517f 100644 --- a/geoapi/pom.xml +++ b/geoapi/pom.xml @@ -156,7 +156,22 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + --add-reads org.opengis.geoapi=tech.uom.seshat + + + + + tech.uom + seshat + test + + + diff --git a/geoapi/src/main/java/org/opengis/annotation/Obligation.java b/geoapi/src/main/java/org/opengis/annotation/Obligation.java index 706a39ad4..3bd661561 100644 --- a/geoapi/src/main/java/org/opengis/annotation/Obligation.java +++ b/geoapi/src/main/java/org/opengis/annotation/Obligation.java @@ -60,7 +60,7 @@ public enum Obligation implements ControlledVocabulary { /** * The element should always be {@code null}. This obligation code is used only when * a sub-interface overrides an association and force it to a {@code null} value. - * An example is {@link org.opengis.referencing.datum.TemporalDatum#getAnchorPoint()}. + * An example is {@link org.opengis.referencing.datum.TemporalDatum#getAnchorDefinition()}. * * @departure constraint * ISO specifications sometimes override a parent method with a comment saying that the method diff --git a/geoapi/src/main/java/org/opengis/annotation/ResourceBundles.java b/geoapi/src/main/java/org/opengis/annotation/ResourceBundles.java index d7e12615c..ce00724ad 100644 --- a/geoapi/src/main/java/org/opengis/annotation/ResourceBundles.java +++ b/geoapi/src/main/java/org/opengis/annotation/ResourceBundles.java @@ -119,7 +119,7 @@ public static ResourceBundle descriptions(final Locale locale) { * The returned map is the converse of {@link UML#identifier()}. * *

      Examples

      - * {@code classIndex.getString("SC_CRS")} returns + * {@code classIndex.getString("CRS")} returns * {@code "org.opengis.referencing.crs.CoordinateReferenceSystem"}. * *

      Performance note

      @@ -143,5 +143,5 @@ public static Properties classIndex() throws IOException { /** * Number of lines in the {@code "class-index.properties"} file. */ - static final int INDEX_CAPACITY = 247; + static final int INDEX_CAPACITY = 257; } diff --git a/geoapi/src/main/java/org/opengis/annotation/Specification.java b/geoapi/src/main/java/org/opengis/annotation/Specification.java index e60dad1ac..8236022fb 100644 --- a/geoapi/src/main/java/org/opengis/annotation/Specification.java +++ b/geoapi/src/main/java/org/opengis/annotation/Specification.java @@ -20,14 +20,14 @@ /** * ISO/OGC specifications from which an interface, method, enumeration or code list was derived. - * Some specifications are available both at OGC and ISO. + * Some specifications are available both at OGC and ISO. * - *

      Specification versions:

      + *

      Specification versions

      * Each specification has a {@linkplain #defaultVersion() default version} number, which identifies - * more accurately the specification used by the vast majority of non-deprecated GeoAPI elements: + * more accurately the specification used by most non-deprecated GeoAPI elements: *
        - *
      • For OGC specifications, the version number is the OGC revision number.
      • - *
      • For ISO specifications, the version number is the publication year (ignoring corrigendum, + *
      • For OGC specifications, the version number is the OGC revision number.
      • + *
      • For ISO specifications, the version number is the publication year (ignoring corrigendum, * which are implicit). For example if the specification is {@link #ISO_19115}, then version * 2003 stands for ISO 19115:2003/Cor.1:2006.
      • *
      @@ -48,16 +48,7 @@ public enum Specification { /** * ISO 19103, Conceptual schema language. * This is the specification for some interfaces in package {@link org.opengis.util}. - * - *

      ISO abstract:

      - *
      - *

      Provides rules and guidelines for the use of a conceptual schema language - * within the context of geographic information. - * The chosen conceptual schema language is the Unified Modeling Language (UML).

      - * - *

      ISO 19103 provides a profile of UML for use with geographic information. - * The standardization target type of this standard is UML schemas describing geographic information.

      - *
      + * This specification provides basic classes such as names, records and units of measurement. * *

      Version numbers used in GeoAPI:

      *
        @@ -70,19 +61,11 @@ public enum Specification { ISO_19103((short) 2015), /** - * ISO 19107, Feature Geometry - * (OGC Topic 1). + * ISO 19107, Feature Geometry (OGC Topic 1). * This is the specification for package {@link org.opengis.geometry} and sub-packages. - * - *

        ISO abstract:

        - *
        - *

        Specifies conceptual schemas for describing the spatial characteristics of - * geographic features, and a set of spatial operations consistent with these schemas. It treats - * vector geometry and topology up to three dimensions. It defines standard spatial operations - * for use in access, query, management, processing, and data exchange of geographic information - * for spatial (geometric and topological) objects of up to three topological dimensions embedded - * in coordinate spaces of up to three axes.

        - *
        + * It specifies the classes for describing the spatial characteristics of geometries, + * and a set of spatial operations. + * It treats vector geometry and topology up to three dimensions. * *

        Version numbers used in GeoAPI:

        *
          @@ -96,17 +79,7 @@ public enum Specification { /** * ISO 19108, Temporal Schema. * This is the specification for package {@link org.opengis.temporal} and sub-packages. - * - *

          ISO abstract:

          - *
          - *

          Defines concepts for describing temporal characteristics of geographic - * information. It depends upon existing information technology standards for the interchange - * of temporal information. It provides a basis for defining temporal feature attributes, - * feature operations, and feature associations, and for defining the temporal aspects of - * metadata about geographic information. Since this International Standard is concerned with - * the temporal characteristics of geographic information as they are abstracted from the real - * world, it emphasizes valid time rather than transaction time.

          - *
          + * it defines classes for describing temporal characteristics of geographic information. * *

          Version numbers used in GeoAPI:

          *
            @@ -121,20 +94,7 @@ public enum Specification { /** * ISO 19109, Rules for application schema. * This is the specification for package {@link org.opengis.feature}. - * - *

            ISO abstract:

            - *
            - *

            Defines rules for creating and documenting application schemas, including principles for the definition - * of features. Its scope includes the following:

            - *
              - *
            • conceptual modelling of features and their properties from a universe of discourse;
            • - *
            • definition of application schemas;
            • - *
            • use of the conceptual schema language for application schemas;
            • - *
            • transition from the concepts in the conceptual model to the data types in the application schema;
            • - *
            • integration of standardized schemas from other ISO geographic information standards with the application - * schema.
            • - *
            - *
            + * It defines principles for the definition of features. * *

            Version numbers used in GeoAPI:

            *
              @@ -148,44 +108,10 @@ public enum Specification { ISO_19109((short) 2013), /** - * ISO 19111, Referencing by coordinates - * (OGC Topic 2). + * ISO 19111, Referencing by coordinates (OGC Topic 2). * This is the specification for package {@link org.opengis.referencing} and sub-packages. - * - *

              ISO abstract:

              - *
              - *

              Defines the conceptual schema for the description of referencing by coordinates. - * It describes the minimum data required to define coordinate reference systems. - * This document supports the definition of:

              - *
                - *
              • spatial coordinate reference systems where coordinate values do not change with time. The system may: - *
                  - *
                • be geodetic and apply on a national or regional basis, or
                • - *
                • apply locally such as for a building or construction site, or
                • - *
                • apply locally to an image or image sensor;
                • - *
                • be referenced to a moving platform such as a car, a ship, an aircraft or a spacecraft. - * Such a coordinate reference system can be related to a second coordinate reference system - * which is referenced to the Earth through a transformation that includes a time element;
                • - *
                - *
              • - *
              • spatial coordinate reference systems in which coordinate values of points on or near the surface - * of the earth change with time due to tectonic plate motion or other crustal deformation. - * Such dynamic systems include time evolution, however they remain spatial in nature;
              • - *
              • parametric coordinate reference systems which use a non-spatial parameter that varies monotonically - * with height or depth;
              • - *
              • temporal coordinate reference systems which use dateTime, temporal count or temporal measure quantities - * that vary monotonically with time;
              • - *
              • mixed spatial, parametric or temporal coordinate reference systems.
              • - *
              - *

              The definition of a coordinate reference system does not change with time, - * although in some cases some of the defining parameters can include a rate of change of the parameter. - * The coordinate values within a dynamic and in a temporal coordinate reference system can change with time.

              - * - *

              This document also describes the conceptual schema for defining the information required to describe - * operations that change coordinate values. In addition to the minimum data required for the definition of - * the coordinate reference system or coordinate operation, the conceptual schema allows additional descriptive - * information – coordinate reference system metadata – to be provided.

              - *
              + * It defines the minimum data required to define coordinate reference systems, + * and the classes for describe operations that change coordinate values. * *

              Version numbers used in GeoAPI:

              *
                @@ -200,47 +126,10 @@ public enum Specification { */ ISO_19111((short) 2019), - /** - * ISO 19111-2, Part 2: Extension for parametric values - * This is the specification for classes {@link org.opengis.referencing.crs.ParametricCRS} and associated parametric classes. - * - *

                ISO abstract:

                - *
                - *

                Extends the existing spatial referencing standard by defining the schema - * required for describing parameterized systems.

                - *
                - * - *

                Version numbers used in GeoAPI:

                - *
                  - *
                • 2009: ISO 19111-2:2009   ({@linkplain #defaultVersion() default version})
                • - *
                - * - * @see ISO 19111-2:2009 on standards catalogue - * - * @since 3.1 - * - * @todo Remove after we upgraded to ISO 19111:2019. - * This enumeration was not present in GeoAPI 3.0.2, so it is safe to remove. - */ - ISO_19111_2((short) 2009), - /** * ISO 19112, Spatial referencing by geographic identifiers. * This is the specification for package {@link org.opengis.referencing.gazetteer}. - * - *

                ISO abstract:

                - *
                - *

                Defines the conceptual schema for spatial references based on geographic identifiers. - * It establishes a general model for spatial referencing using geographic identifiers, - * defines the components of a spatial reference system and defines the essential components of a gazetteer. - * Spatial referencing by coordinates is not addressed in this document; however, a mechanism for recording - * complementary coordinate references is included.

                - * - *

                ISO 19112 assists users in understanding the spatial references used in datasets. - * It enables gazetteers to be constructed in a consistent manner and supports the development of other standards - * in the field of geographic information. It is applicable to digital geographic data, and its principles may be - * extended to other forms of geographic data such as maps, charts and textual documents.

                - *
                + * It defines the essential components of a gazetteer. * *

                Version numbers used in GeoAPI:

                *
                  @@ -255,36 +144,12 @@ public enum Specification { ISO_19112((short) 2003), /** - * ISO 19115-1, Metadata part 1: fundamentals - * (OGC Topic 11). - * This is the specification for package {@link org.opengis.metadata} and sub-packages. - * - *

                  ISO abstract:

                  - *
                  - *

                  Defines the schema required for describing geographic information and services by means of metadata. + * ISO 19115-1, Metadata part 1: fundamentals (OGC Topic 11). + * This is the specification for package {@link org.opengis.metadata} and most sub-packages. + * It defines the classes for describing geographic information and services by means of metadata. * It provides information about the identification, the extent, the quality, the spatial and temporal aspects, * the content, the spatial reference, the portrayal, distribution, and other properties of digital geographic - * data and services.

                  - * - *

                  ISO 19115-1 is applicable to:

                  - *
                    - *
                  • the cataloguing of all types of resources, clearinghouse activities, and the full description of datasets and services;
                  • - *
                  • geographic services, geographic datasets, dataset series, and individual geographic features and feature properties.
                  • - *
                  - * - *

                  ISO 19115-1 defines:

                  - *
                    - *
                  • mandatory and conditional metadata sections, metadata entities, and metadata elements;
                  • - *
                  • the minimum set of metadata required to serve most metadata applications (data discovery, - * determining data fitness for use, data access, data transfer, and use of digital data and services);
                  • - *
                  • optional metadata elements to allow for a more extensive standard description of resources, if required;
                  • - *
                  • a method for extending metadata to fit specialized needs.
                  • - *
                  - * - *

                  Though ISO 19115-1 is applicable to digital data and services, its principles can be extended to many - * other types of resources such as maps, charts, and textual documents as well as non-geographic data. - * Certain conditional metadata elements might not apply to these other forms of data.

                  - *
                  + * data and services. * *

                  Version numbers used in GeoAPI:

                  *
                    @@ -300,19 +165,11 @@ public enum Specification { /** * ISO 19115-2, Metadata part 2: Extensions for acquisition and processing. - * This is the specification for package {@link org.opengis.metadata} and sub-packages. - * - *

                    ISO abstract:

                    - *
                    - *

                    Extends the existing geographic metadata standard by defining the schema - * required for describing imagery and gridded data. It provides information about the properties + * This is the specification for some sub-packages of {@link org.opengis.metadata}. + * It extends the existing geographic metadata standard by defining the classes for + * describing imagery and gridded data. It provides information about the properties * of the measuring equipment used to acquire the data, the geometry of the measuring process - * employed by the equipment, and the production process used to digitize the raw data. This - * extension deals with metadata needed to describe the derivation of geographic information - * from raw data, including the properties of the measuring system, and the numerical methods - * and computational procedures used in the derivation. The metadata required to address coverage - * data in general is addressed sufficiently in the general part of ISO 19115.

                    - *
                    + * employed by the equipment, and the production process used to digitize the raw data. * *

                    Version numbers used in GeoAPI:

                    *
                      @@ -327,32 +184,8 @@ public enum Specification { /** * ISO 19115-3, Metadata part 3: XML schema implementation for fundamental concepts. * This is the specification for package {@link org.opengis.metadata} and sub-packages. - * - *

                      ISO abstract:

                      - *
                      - *

                      Defines an integrated XML implementation of ISO 19115‑1, ISO 19115‑2, and concepts from ISO/TS 19139 - * by defining the following artefacts:

                      - * - *
                        - *
                      • a set of XML schema required to validate metadata instance documents conforming to - * conceptual model elements defined in ISO 19115‑1, ISO 19115‑2, and ISO/TS 19139;
                      • - *
                      • a set of ISO/IEC 19757‑3 (Schematron) rules that implement validation constraints - * in the ISO 19115‑1 and ISO 19115‑2 UML models that are not validated by the XML schema;
                      • - *
                      • an Extensible Stylesheet Language Transformation (XSLT) for transforming ISO 19115-1 metadata - * encoded using the ISO/TS 19139 XML schema and ISO 19115‑2 metadata encoded using the ISO/TS 19139‑2 - * XML schema into an equivalent document that is valid against the XML schema defined in this document.
                      • - *
                      - * - *

                      ISO/TS 19115-3:2016 describes the procedure used to generate XML schema from ISO geographic information - * conceptual models related to metadata. The procedure includes creation of an UML model for XML implementation - * derived from the conceptual UML model.

                      - * - *

                      This implementation model does not alter the semantics of the target conceptual model, but adds abstract - * classes that remove dependencies between model packages, tagged values and stereotypes required by the UML - * to XML transformation software, and refactors the packaging of a few elements into XML namespaces. - * The XML schema has been generated systematically from the UML model for XML implementation according - * to the rules defined in ISO/TS 19139 or ISO 19118.

                      - *
                      + * It defines an integrated XML implementation of ISO 19115‑1, ISO 19115‑2, + * and concepts from ISO 19139. * *

                      Version numbers used in GeoAPI:

                      *
                        @@ -368,14 +201,8 @@ public enum Specification { /** * ISO 19117, Portrayal. * This is an abstract specification for portraying features. - * - *

                        ISO abstract:

                        - *
                        - *

                        Defines a schema describing the portrayal of geographic information in a - * form understandable by humans. It includes the methodology for describing symbols and mapping - * of the schema to an application schema. It does not include standardization of cartographic - * symbols, and their geometric and functional description.

                        - *
                        + * It defines a schema describing the portrayal of geographic information in a form understandable by humans. + * It does not include standardization of cartographic symbols, and their geometric and functional description. * *

                        Version numbers used in GeoAPI:

                        *
                          @@ -387,26 +214,11 @@ public enum Specification { ISO_19117((short) 2005), /** - * ISO 19123, Schema for coverage geometry and functions - * (OGC Topic 6). + * ISO 19123, Schema for coverage geometry and functions (OGC Topic 6). * This is the specification for package {@link org.opengis.coverage} and sub-packages. - * - *

                          ISO abstract:

                          - *
                          - *

                          Defines a conceptual schema for the spatial characteristics of coverages. - * Coverages support mapping from a spatial, temporal or spatiotemporal domain to feature attribute - * values where feature attribute types are common to all geographic positions within the domain. - * A coverage domain consists of a collection of direct positions in a coordinate space that may - * be defined in terms of up to three spatial dimensions as well as a temporal dimension. - * Examples of coverages include rasters, triangulated irregular networks, point coverages and - * polygon coverages. Coverages are the prevailing data structures in a number of application - * areas, such as remote sensing, meteorology and mapping of bathymetry, elevation, soil and - * vegetation.

                          - * - *

                          ISO 19123 defines the relationship between the domain of a coverage - * and an associated attribute range. The characteristics of the spatial domain are defined - * whereas the characteristics of the attribute range are not part of ISO 19123.

                          - *
                          + * Coverages support mapping from a spatial, temporal or spatiotemporal domain to feature attribute values + * where feature attribute types are common to all geographic positions within the domain. + * ISO 19123 defines the relationship between the domain of a coverage and an associated attribute range. * *

                          Version numbers used in GeoAPI:

                          *
                            @@ -421,16 +233,8 @@ public enum Specification { /** * ISO 19128, Web map server interface. - * - *

                            ISO abstract:

                            - *
                            - *

                            Specifies the behaviour of a service that produces spatially referenced - * maps dynamically from geographic information. It specifies operations to retrieve a description - * of the maps offered by a server, to retrieve a map, and to query a server about features - * displayed on a map. ISO 19128 is applicable to pictorial renderings of maps in a - * graphical format; it is not applicable to retrieval of actual feature data or coverage data - * values.

                            - *
                            + * It specifies the behaviour of a service that produces spatially referenced maps + * dynamically from geographic information. * *

                            Version numbers used in GeoAPI:

                            *
                              @@ -445,12 +249,8 @@ public enum Specification { /** * ISO 19139, Metadata XML schema implementation. * This is the specification for package {@link org.opengis.metadata} and sub-packages. - * - *

                              ISO abstract:

                              - *
                              - *

                              Defines Geographic MetaData XML ({@code gmd}) encoding, an XML Schema - * implementation derived from ISO 19115.

                              - *
                              + * It defines Geographic MetaData XML ({@code gmd}) encoding, + * an XML Schema implementation derived from ISO 19115. * *

                              Version numbers used in GeoAPI:

                              *
                                @@ -467,13 +267,7 @@ public enum Specification { /** * ISO 19143, Filter encoding. * This is the specification for package {@link org.opengis.filter} and sub-packages. - * - *

                                ISO abstract:

                                - *
                                - * A fundamental operation performed on a set of data or resources is that of querying - * in order to obtain a subset of the data which contains certain desired information - * that satisfies some query criteria and which is also, perhaps, sorted in some specified manner. - *
                                + * It defines querying in order to obtain a subset of data which contains certain desired information. * *

                                Version numbers used in GeoAPI:

                                *
                                  @@ -490,23 +284,7 @@ public enum Specification { /** * ISO 19157, Data quality. * This is the specification for package {@link org.opengis.metadata.quality} and sub-packages. - * - *

                                  ISO abstract:

                                  - *
                                  - *

                                  Establishes the principles for describing the quality of geographic data:

                                  - *
                                    - *
                                  • defines components for describing data quality;
                                  • - *
                                  • specifies components and content structure of a register for data quality measures;
                                  • - *
                                  • describes general procedures for evaluating the quality of geographic data;
                                  • - *
                                  • establishes principles for reporting data quality.
                                  • - *
                                  - * - *

                                  ISO 19157:2013 also defines a set of data quality measures for use in evaluating and reporting data quality. - * It is applicable to data producers providing quality information to describe and assess how well a data set - * conforms to its product specification and to data users attempting to determine whether or not specific - * geographic data are of sufficient quality for their particular application. - * ISO 19157:2013 does not attempt to define minimum acceptable levels of quality for geographic data.

                                  - *
                                  + * It establishes the classes for describing the quality of geographic data. * *

                                  Version numbers used in GeoAPI:

                                  *
                                    @@ -524,20 +302,9 @@ public enum Specification { * This is the specification of input and output format of * {@link org.opengis.referencing.crs.CRSFactory#createFromWKT(String)} and * {@link org.opengis.referencing.crs.CoordinateReferenceSystem#toWKT()}. - * - *

                                    ISO abstract:

                                    - *
                                    - *

                                    Defines the structure and content of a text string implementation of the abstract model for coordinate reference systems - * described in ISO 19111:2007 and ISO 19111-2:2009. The string defines frequently needed types of coordinate reference systems - * and coordinate operations in a self-contained form that is easily readable by machines and by humans. - * The essence is its simplicity; as a consequence there are some constraints upon the more open content allowed in ISO 19111:2007. - * To retain simplicity in the well-known text (WKT) description of coordinate reference systems and coordinate operations, - * the scope of this International Standard excludes parameter grouping and pass-through coordinate operations. - * The text string provides a means for humans and machines to correctly and unambiguously interpret and utilise - * a coordinate reference system definition with look-ups or cross references only to define coordinate operation mathematics. - * Because it omits metadata about the source of the data and may omit metadata about the applicability of the information, - * the WKT string is not suitable for the storage of definitions of coordinate reference systems or coordinate operations.

                                    - *
                                    + * It defines the structure and content of a text string implementation of the abstract model described in ISO 19111. + * The string defines frequently needed types of coordinate reference systems and coordinate operations + * in a self-contained form that is readable by machines and by humans. * *

                                    Version numbers used in GeoAPI:

                                    *
                                      @@ -621,7 +388,7 @@ public enum Specification { * * @since 3.1 */ - @Deprecated(since="3.1", forRemoval=true) + @Deprecated(forRemoval=true) OGC_07022((short) 1), /** diff --git a/geoapi/src/main/java/org/opengis/coordinate/CoordinateMetadata.java b/geoapi/src/main/java/org/opengis/coordinate/CoordinateMetadata.java new file mode 100644 index 000000000..7bab6108b --- /dev/null +++ b/geoapi/src/main/java/org/opengis/coordinate/CoordinateMetadata.java @@ -0,0 +1,82 @@ +/* + * GeoAPI - Java interfaces for OGC/ISO standards + * Copyright © 2024 Open Geospatial Consortium, Inc. + * http://www.geoapi.org + * + * Licensed 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.opengis.coordinate; + +import java.util.Optional; +import java.time.temporal.Temporal; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.annotation.UML; + +import static org.opengis.annotation.Obligation.*; +import static org.opengis.annotation.Specification.*; + + +/** + * Metadata required to reference coordinates. + * A {@linkplain CoordinateReferenceSystem coordinate reference system} (CRS) is mandatory. + * If the CRS is dynamic, then a coordinate epoch is also mandatory. + * Whether the CRS is dynamic is determined through the CRS's reference frame definition. + * + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (Geomatys) + * @version 3.1 + * @since 3.1 + */ +@UML(identifier="CoordinateMetadata", specification=ISO_19111) +public interface CoordinateMetadata { + /** + * The coordinate reference system (CRS) in which the coordinate tuples are given. + * + * @departure easeOfUse + * The ISO specification defines two conditional attributes: an identifier ({@code crsID}) or an association + * to a CRS object ({@code crs}), with the requirement that at least one of them shall be supplied. + * GeoAPI retains only the latter for client applications ease of use, therefor making this attribute mandatory. + * Implementers shall resolve identifiers, for example using {@link org.opengis.referencing.crs.CRSAuthorityFactory}. + * + * @return the coordinate reference system (CRS) of coordinate tuples. + */ + @UML(identifier="crs", obligation=MANDATORY, specification=ISO_19111) + CoordinateReferenceSystem getCoordinateReferenceSystem(); + + /** + * Date at which coordinate tuples referenced to a dynamic CRS are valid. + * It should be an object from the {@link java.time} package such as {@link java.time.Instant}. + * This attribute is required if the CRS is dynamic. + * + *

                                      Temporal object type

                                      + * The type of the returned object depends on the epoch accuracy and the calendar in use. + * For coordinates on Earth, the temporal type should be {@link java.time.Year} if the epoch is merely a year, + * {@link java.time.YearMonth} or {@link java.time.LocalDate} if a better precision is available, + * up to {@link java.time.OffsetDateTime} or {@link java.time.Instant} for maximal precision. + * For coordinates on another planet, the time measurement may use a non-Gregorian calendar. + * In the latter case, the type of the returned temporal object is currently implementation dependent. + * + * @departure integration + * The ISO specification uses a decimal year in the Gregorian calendar. + * For example, 2017-03-25 in the Gregorian calendar is epoch 2017.23. + * GeoAPI delegates to the Java time package instead. + * + * @return epoch at which coordinate tuples are valid. + * + * @see org.opengis.referencing.datum.DynamicReferenceFrame#getFrameReferenceEpoch() + */ + @UML(identifier="coordinateEpoch", obligation=CONDITIONAL, specification=ISO_19111) + default Optional getCoordinateEpoch() { + return Optional.empty(); + } +} diff --git a/geoapi/src/main/java/org/opengis/coordinate/CoordinateSet.java b/geoapi/src/main/java/org/opengis/coordinate/CoordinateSet.java new file mode 100644 index 000000000..0c3a26182 --- /dev/null +++ b/geoapi/src/main/java/org/opengis/coordinate/CoordinateSet.java @@ -0,0 +1,92 @@ +/* + * GeoAPI - Java interfaces for OGC/ISO standards + * Copyright © 2024 Open Geospatial Consortium, Inc. + * http://www.geoapi.org + * + * Licensed 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.opengis.coordinate; + +import java.util.Iterator; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.opengis.geometry.DirectPosition; +import org.opengis.annotation.UML; + +import static org.opengis.annotation.Obligation.*; +import static org.opengis.annotation.Specification.*; + + +/** + * A collection of coordinate tuples referenced to the same coordinate reference system (CRS). + * If the CRS is dynamic, the {@code CoordinateSet} metadata contains also a coordinate epoch. + * Operations on the geometry of the tuples within the coordinate set are valid only if all + * tuples are referenced to the same coordinate epoch. + * + *

                                      Coordinate operations

                                      + * Coordinate sets referenced to one CRS may be referenced to another CRS through the + * application of a {@linkplain org.opengis.referencing.operation.CoordinateOperation coordinate operation}. + * If the CRS is dynamic, the {@code CoordinateSet} may be converted to another coordinate epoch + * through a point motion coordinate operation that includes time evolution. + * + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (Geomatys) + * @version 3.1 + * + * @see org.opengis.referencing.operation.CoordinateOperation#transform(CoordinateSet) + * + * @since 3.1 + */ +@UML(identifier="CoordinateSet", specification=ISO_19111) +public interface CoordinateSet extends Iterable { + /** + * Coordinate metadata to which this coordinate set is referenced. Coordinate metadata includes a + * {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system} + * (CRS) and, if the CRS is dynamic, a coordinate epoch. + * + * @return coordinate metadata to which this coordinate set is referenced. + */ + @UML(identifier="coordinateMetadata", obligation=MANDATORY, specification=ISO_19111) + CoordinateMetadata getCoordinateMetadata(); + + /** + * Returns the number of dimensions of coordinate tuples. This is determined by the + * {@linkplain CoordinateMetadata#getCoordinateReferenceSystem() coordinate reference system}. + * + * @return the number of dimensions of coordinate tuples. + */ + default 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) + Iterator 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. + */ + default Stream stream() { + return StreamSupport.stream(spliterator(), false); + } +} diff --git a/geoapi/src/main/java/org/opengis/coordinate/package-info.java b/geoapi/src/main/java/org/opengis/coordinate/package-info.java new file mode 100644 index 000000000..c54fa53f7 --- /dev/null +++ b/geoapi/src/main/java/org/opengis/coordinate/package-info.java @@ -0,0 +1,38 @@ +/* + * GeoAPI - Java interfaces for OGC/ISO standards + * Copyright © 2008-2024 Open Geospatial Consortium, Inc. + * http://www.geoapi.org + * + * Licensed 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. + */ + +/** + * Tuple of coordinate values. + * A coordinate is one of n scalar values that define the position of a single point. + * A coordinate tuple is an ordered list of coordinates that define the position of a single point. + * The {@linkplain org.opengis.geometry.DirectPosition#getCoordinates() coordinates} within a coordinate tuple + * are mutually independent, and the number of coordinates is equal to the tuple + * {@linkplain org.opengis.geometry.DirectPosition#getDimension() dimension}. + * A coordinate set is a collection of coordinate tuples referenced to the same + * {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system}. + * A coordinate set does not necessarily describe a geometry. + * + *

                                      Coordinate tuples can be represented by instances of {@link org.opengis.geometry.DirectPosition} + * or by plain Java arrays of numbers.

                                      + * + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (Geomatys) + * @version 3.1 + * @since 3.1 + */ +package org.opengis.coordinate; diff --git a/geoapi/src/main/java/org/opengis/geoapi/internal/Legacy.java b/geoapi/src/main/java/org/opengis/geoapi/internal/Legacy.java index 6f980781a..c60390f9c 100644 --- a/geoapi/src/main/java/org/opengis/geoapi/internal/Legacy.java +++ b/geoapi/src/main/java/org/opengis/geoapi/internal/Legacy.java @@ -19,6 +19,18 @@ 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.metadata.extent.Extent; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.ObjectDomain; @@ -62,4 +74,43 @@ public static Extent getDomainOfValidity(final Collection domains) if (domains == null) return null; return domains.stream().map(ObjectDomain::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 --git a/geoapi/src/main/java/org/opengis/geometry/DirectPosition.java b/geoapi/src/main/java/org/opengis/geometry/DirectPosition.java index 374304e65..0dda8f4c1 100644 --- a/geoapi/src/main/java/org/opengis/geometry/DirectPosition.java +++ b/geoapi/src/main/java/org/opengis/geometry/DirectPosition.java @@ -1,6 +1,6 @@ /* * GeoAPI - Java interfaces for OGC/ISO standards - * Copyright © 2003-2023 Open Geospatial Consortium, Inc. + * Copyright © 2003-2024 Open Geospatial Consortium, Inc. * http://www.geoapi.org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,9 +33,9 @@ * (such as {@linkplain org.opengis.geometry.Geometry geometries}) that have references to * {@linkplain CoordinateReferenceSystem coordinate reference system} (CRS), * the {@link #getCoordinateReferenceSystem()} method may return {@code null} if this particular - * {@code DirectPosition} is included in a larger object with such a reference to a CRS. + * {@code DirectPosition} is included in a larger object with such a reference to a CRS. * In this case, the coordinate reference system is implicitly assumed to take on the value - * of the containing object's CRS. + * of the containing object's CRS. * *

                                      Optional operation

                                      * A direct position can optionally be modifiable. If this {@code DirectPosition} is unmodifiable, @@ -46,7 +46,7 @@ * GeoAPI moved this interface into the {@code org.opengis.geometry} root package for * convenience, because it is extensively used. * - * @author Martin Desruisseaux (IRD) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 */ @@ -68,11 +68,11 @@ default DirectPosition getDirectPosition() { * May be {@code null} if this particular {@code DirectPosition} is included in a larger object * with such a reference to a {@linkplain CoordinateReferenceSystem coordinate reference system}. * In this case, the coordinate reference system is implicitly assumed to take on the value - * of the containing object's CRS. + * of the containing object's CRS. * *

                                      Default implementation

                                      * The default implementation returns {@code null}. Implementations should override - * this method if the CRS is known or can be taken from the containing object. + * this method if the CRS is known or can be taken from the containing object. * * @return the coordinate reference system (CRS), or {@code null}. */ @@ -93,6 +93,7 @@ default CoordinateReferenceSystem getCoordinateReferenceSystem() { /** * A copy of the coordinates stored as an array of double values. * Changes to the returned array will not affect this {@code DirectPosition}. + * The array length shall be equal to the {@linkplain #getDimension() dimension}. * *

                                      Default implementation

                                      * The default implementation invokes {@link #getCoordinate(int)} for all indices @@ -120,6 +121,7 @@ default double[] getCoordinates() { * in this {@code DirectPosition} object. * * @deprecated Renamed {@link #getCoordinates()}. + * To be removed because of the risk of confusion with {@link #getCoordinate(int)}. */ @Deprecated(since="3.1", forRemoval=true) default double[] getCoordinate() { @@ -148,7 +150,7 @@ default double[] getCoordinate() { * * @deprecated Renamed {@link #getCoordinate(int)}. */ - @Deprecated(since="3.1", forRemoval=true) + @Deprecated(since = "3.1") default double getOrdinate(int dimension) throws IndexOutOfBoundsException { return getCoordinate(dimension); } @@ -184,7 +186,7 @@ default void setCoordinate(int dimension, double value) { * * @deprecated Renamed {@link #setCoordinate(int, double)}. */ - @Deprecated(since="3.1", forRemoval=true) + @Deprecated(since = "3.1") default void setOrdinate(int dimension, double value) throws IndexOutOfBoundsException, UnsupportedOperationException { diff --git a/geoapi/src/main/java/org/opengis/metadata/content/ImageDescription.java b/geoapi/src/main/java/org/opengis/metadata/content/ImageDescription.java index 5db79f248..43113b3f4 100644 --- a/geoapi/src/main/java/org/opengis/metadata/content/ImageDescription.java +++ b/geoapi/src/main/java/org/opengis/metadata/content/ImageDescription.java @@ -36,9 +36,9 @@ @UML(identifier="MD_ImageDescription", specification=ISO_19115) public interface ImageDescription extends CoverageDescription { /** - * Illumination elevation measured in degrees clockwise from the target plane at - * intersection of the optical line of sight with the Earth's surface. For images from a - * scanning device, refer to the centre pixel of the image. + * Illumination elevation measured in degrees clockwise from the target plane + * at intersection of the optical line of sight with the planet's surface. + * For images from a scanning device, refer to the centre pixel of the image. * * @return a value between -90° and +90°, or {@code null} if unspecified. */ diff --git a/geoapi/src/main/java/org/opengis/metadata/content/ImagingCondition.java b/geoapi/src/main/java/org/opengis/metadata/content/ImagingCondition.java index 57a36b5e5..2997939ae 100644 --- a/geoapi/src/main/java/org/opengis/metadata/content/ImagingCondition.java +++ b/geoapi/src/main/java/org/opengis/metadata/content/ImagingCondition.java @@ -53,7 +53,7 @@ public final class ImagingCondition extends CodeList { public static final ImagingCondition CLOUD = new ImagingCondition("CLOUD"); /** - * Acute angle between the plane of the ecliptic (the plane of the Earth's orbit) and + * Acute angle between the plane of the ecliptic (the plane of the planet's orbit) and * the plane of the celestial equator. */ @UML(identifier="degradingObliquity", obligation=CONDITIONAL, specification=ISO_19115) diff --git a/geoapi/src/main/java/org/opengis/metadata/extent/VerticalExtent.java b/geoapi/src/main/java/org/opengis/metadata/extent/VerticalExtent.java index 7e5387d1f..2b35cddc3 100644 --- a/geoapi/src/main/java/org/opengis/metadata/extent/VerticalExtent.java +++ b/geoapi/src/main/java/org/opengis/metadata/extent/VerticalExtent.java @@ -31,7 +31,7 @@ * ISO 19115 provides two ways to define a coordinate reference system, * with the restriction that only one of those two ways can be used: *
                                        - *
                                      1. {@code verticalCRS} of type {@code SC_VerticalCRS} (from ISO 19111),
                                      2. + *
                                      3. {@code verticalCRS} of type {@code VerticalCRS} (from ISO 19111),
                                      4. *
                                      5. {@code verticalCRSId} of type {@code MD_ReferenceSystem} (from ISO 19115).
                                      6. *
                                      * GeoAPI provides only the first way, because the {@code MD_ReferenceSystem} type diff --git a/geoapi/src/main/java/org/opengis/parameter/GeneralParameterDescriptor.java b/geoapi/src/main/java/org/opengis/parameter/GeneralParameterDescriptor.java index 46dc4a872..6902a1bd6 100644 --- a/geoapi/src/main/java/org/opengis/parameter/GeneralParameterDescriptor.java +++ b/geoapi/src/main/java/org/opengis/parameter/GeneralParameterDescriptor.java @@ -137,7 +137,8 @@ * GeoAPI uses a name which contains the "{@code Descriptor}" word for consistency with other * libraries in Java (e.g. {@code ParameterListDescriptor} in Java Advanced Imaging). * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @author Jody Garnett (Refractions Research) * @version 3.1 * @since 2.0 @@ -145,10 +146,10 @@ * @see GeneralParameterValue */ @Classifier(Stereotype.ABSTRACT) -@UML(identifier="CC_GeneralOperationParameter", specification=ISO_19111, version=2007) +@UML(identifier="GeneralOperationParameter", specification=ISO_19111) public interface GeneralParameterDescriptor extends IdentifiedObject { /** - * The name, as used by the service or operation for this parameter. + * Returns the name, as used by the service or operation for this parameter. * *

                                      Unified parameter API

                                      * The metadata standard ({@linkplain Specification#ISO_19115 ISO 19115}) defines the @@ -194,7 +195,7 @@ public interface GeneralParameterDescriptor extends IdentifiedObject { ReferenceIdentifier getName(); /** - * Indication if the parameter is an input to the service, an output or both. + * Returns whether the parameter is an input to the service, an output or both. * This information applies mostly to service metadata. * The default value is {@link ParameterDirection#IN}. * @@ -208,14 +209,14 @@ default ParameterDirection getDirection() { } /** - * A narrative explanation of the role of the parameter. + * Returns a narrative explanation of the role of this parameter. * - * @return a narrative explanation of the role of the parameter, or {@code null} if none. - * - * @since 3.1 + * @return a narrative explanation of the role of this parameter, or {@code null} if none. * * @see #getName() * @see #getRemarks() + * + * @since 3.1 */ @UML(identifier="SV_Parameter.description", obligation=OPTIONAL, specification=ISO_19115) default InternationalString getDescription() { @@ -223,7 +224,7 @@ default InternationalString getDescription() { } /** - * The minimum number of times that values for this parameter group or parameter are required. + * Returns the minimum number of times that values for this parameter group or parameter are required. * The default value is 1. A value of 0 means an optional parameter. * * @return the minimum occurrence. @@ -236,7 +237,7 @@ default int getMinimumOccurs() { } /** - * The maximum number of times that values for this parameter group or parameter can be included. + * Returns the maximum number of times that values for this parameter group or parameter can be included. * The default value is 1. A value greater than 1 means a repeatable parameter. * *

                                      If this parameter is an instance of {@link ParameterDescriptor} used for the description of diff --git a/geoapi/src/main/java/org/opengis/parameter/GeneralParameterValue.java b/geoapi/src/main/java/org/opengis/parameter/GeneralParameterValue.java index cf5990c7f..3a004b37c 100644 --- a/geoapi/src/main/java/org/opengis/parameter/GeneralParameterValue.java +++ b/geoapi/src/main/java/org/opengis/parameter/GeneralParameterValue.java @@ -28,7 +28,8 @@ /** * Abstract parameter value or group of parameter values. * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @author Jody Garnett (Refractions Research) * @version 3.1 * @since 1.0 @@ -36,7 +37,7 @@ * @see GeneralParameterDescriptor */ @Classifier(Stereotype.ABSTRACT) -@UML(identifier="CC_GeneralParameterValue", specification=ISO_19111, version=2007) +@UML(identifier="GeneralParameterValue", specification=ISO_19111) public interface GeneralParameterValue { /** * Returns the abstract definition of this parameter or group of parameters. diff --git a/geoapi/src/main/java/org/opengis/parameter/InvalidParameterTypeException.java b/geoapi/src/main/java/org/opengis/parameter/InvalidParameterTypeException.java index c3152307f..709da9f00 100644 --- a/geoapi/src/main/java/org/opengis/parameter/InvalidParameterTypeException.java +++ b/geoapi/src/main/java/org/opengis/parameter/InvalidParameterTypeException.java @@ -37,8 +37,8 @@ *

                                    • {@link ParameterValue#valueFile()}
                                    • *
                                    * - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 * * @see InvalidParameterValueException @@ -79,6 +79,17 @@ public InvalidParameterTypeException(String message, Throwable cause, String par this.parameterName = parameterName; } + /** + * Creates an exception with a default message, the specified cause and parameter name. + * + * @param cause the cause, saved for later retrieval by the {@link #getCause()} method. + * @param parameter the parameter from which to get the parameter name. + */ + InvalidParameterTypeException(Throwable cause, String parameterName) { + super("Cannot get the “" + parameterName + "” parameter value.", cause); + this.parameterName = parameterName; + } + /** * Returns the parameter name. * diff --git a/geoapi/src/main/java/org/opengis/parameter/ParameterDescriptor.java b/geoapi/src/main/java/org/opengis/parameter/ParameterDescriptor.java index cd9113763..0409a6030 100644 --- a/geoapi/src/main/java/org/opengis/parameter/ParameterDescriptor.java +++ b/geoapi/src/main/java/org/opengis/parameter/ParameterDescriptor.java @@ -48,7 +48,8 @@ * GeoAPI uses a name which contains the "{@code Descriptor}" word for consistency with other * libraries in Java (e.g. {@code ParameterListDescriptor} in Java Advanced Imaging). * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @author Jody Garnett (Refractions Research) * @version 3.1 * @since 2.0 @@ -56,7 +57,7 @@ * @see ParameterValue * @see ParameterDescriptorGroup */ -@UML(identifier="CC_OperationParameter", specification=ISO_19111, version=2007) +@UML(identifier="OperationParameter", specification=ISO_19111) public interface ParameterDescriptor extends GeneralParameterDescriptor { /** * Returns the name that describes the type of parameter values. diff --git a/geoapi/src/main/java/org/opengis/parameter/ParameterDescriptorGroup.java b/geoapi/src/main/java/org/opengis/parameter/ParameterDescriptorGroup.java index abb64e6b1..7a494469a 100644 --- a/geoapi/src/main/java/org/opengis/parameter/ParameterDescriptorGroup.java +++ b/geoapi/src/main/java/org/opengis/parameter/ParameterDescriptorGroup.java @@ -32,7 +32,8 @@ * GeoAPI uses a name which contains the "{@code Descriptor}" word for consistency with other * libraries in Java (e.g. {@code ParameterListDescriptor} in Java Advanced Imaging). * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @author Jody Garnett (Refractions Research) * @version 3.1 * @since 2.0 @@ -40,7 +41,7 @@ * @see ParameterValueGroup * @see ParameterDescriptor */ -@UML(identifier="CC_OperationParameterGroup", specification=ISO_19111, version=2007) +@UML(identifier="OperationParameterGroup", specification=ISO_19111) public interface ParameterDescriptorGroup extends GeneralParameterDescriptor { /** * Returns the parameters in this group. @@ -51,13 +52,14 @@ public interface ParameterDescriptorGroup extends GeneralParameterDescriptor { List descriptors(); /** - * Returns the parameter descriptor in this group for the specified - * {@linkplain Identifier#getCode() identifier code}. + * Returns the parameter descriptor in this group for the specified identifier code. * * @param name the case insensitive identifier code of the parameter to search for. * @return the parameter for the given identifier code. * @throws ParameterNotFoundException if there is no parameter for the given identifier code. * + * @see Identifier#getCode() + * * @departure easeOfUse * This method is not part of the ISO specification. * It has been added in an attempt to make this interface easier to use. @@ -65,8 +67,7 @@ public interface ParameterDescriptorGroup extends GeneralParameterDescriptor { GeneralParameterDescriptor descriptor(String name) throws ParameterNotFoundException; /** - * Creates a new instance of {@linkplain ParameterValueGroup parameter value group} - * initialized with the {@linkplain ParameterDescriptor#getDefaultValue() default values}. + * Creates a new instance of parameter value group initialized with the default values. * While not a requirement, the {@linkplain ParameterValueGroup#getDescriptor() parameter * value descriptor} for the created group will typically be {@code this} descriptor instance. * @@ -84,6 +85,8 @@ public interface ParameterDescriptorGroup extends GeneralParameterDescriptor { * * @return a new parameter instance initialized to the default value. * + * @see ParameterDescriptor#getDefaultValue() + * * @departure extension * This method is not part of the ISO specification. It is provided in GeoAPI as a kind of * factory method. diff --git a/geoapi/src/main/java/org/opengis/parameter/ParameterValue.java b/geoapi/src/main/java/org/opengis/parameter/ParameterValue.java index 87fdfef95..0bbe1bd65 100644 --- a/geoapi/src/main/java/org/opengis/parameter/ParameterValue.java +++ b/geoapi/src/main/java/org/opengis/parameter/ParameterValue.java @@ -19,10 +19,13 @@ import java.net.URI; import javax.measure.Unit; +import javax.measure.UnitConverter; +import javax.measure.IncommensurableException; import org.opengis.annotation.UML; import org.opengis.annotation.Classifier; import org.opengis.annotation.Stereotype; import org.opengis.metadata.citation.Citation; +import org.opengis.metadata.Identifier; import static org.opengis.annotation.Obligation.*; import static org.opengis.annotation.Specification.*; @@ -56,9 +59,16 @@ * The type and constraints on parameter values are given by the {@linkplain #getDescriptor() descriptor}, * Instances of {@code ParameterValue} are created by the {@link ParameterDescriptor#createValue()} method. * + * @departure integration + * This interface merges the {@code OperationParameterValue} interface and {@code ParameterValue} union. + * The {@code valueFileCitation} and {@code geographicObject} properties were omitted because those more + * complex objects can be specified by setting the {@code } parameterized type to + * {@link org.opengis.metadata.Citation} and {@link org.opengis.metadata.Identifier} respectively. + * * @param the type of parameter values. * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @author Jody Garnett (Refractions Research) * @version 3.1 * @since 1.0 @@ -67,7 +77,7 @@ * @see ParameterValueGroup */ @Classifier(Stereotype.UNION) -@UML(identifier="CC_ParameterValue", specification=ISO_19111, version=2007) +@UML(identifier="OperationParameterValue", specification=ISO_19111) public interface ParameterValue extends GeneralParameterValue { /** * Returns the abstract definition of this parameter value. @@ -75,9 +85,40 @@ public interface ParameterValue extends GeneralParameterValue { * @return the abstract definition of this parameter value. */ @Override - @UML(identifier="CC_OperationParameterValue.parameter", obligation=MANDATORY, specification=ISO_19111) + @UML(identifier="parameter", obligation=MANDATORY, specification=ISO_19111) ParameterDescriptor getDescriptor(); + /** + * Returns the name of this parameter, for error messages only. + * All parameter shall have a name since it is a mandatory property, + * but if this parameter is nevertheless unnamed, an arbitrary value is returned. + * + * @return name of this parameter for error message. + */ + private String getName() { + ParameterDescriptor descriptor = getDescriptor(); + if (descriptor != null) { + Identifier name = descriptor.getName(); + if (name != null) { + String code = name.getCode(); + if (code != null) { + return code; + } + } + } + return "unnamed"; + } + + /** + * Returns a message saying that the value of this parameter cannot be converted to the specified unit. + * + * @param unit the illegal unit of measurement. + * @return a message saying that this parameter value cannot be converted. + */ + private String cannotConvert(final Unit unit) { + return "The “" + getName() + "” parameter value cannot be converted to “" + unit + "” units."; + } + /** * Returns the unit of measure of the parameter value. * If the parameter value has no unit (for example because it is a {@link String} type), @@ -95,6 +136,8 @@ public interface ParameterValue extends GeneralParameterValue { /** * Returns the numeric value of this parameter in the specified unit of measure. * This convenience method applies unit conversion on the fly as needed. + * The default implementation invokes {@link #getValue()} and {@link #getUnit()}, + * then tries to cast and convert the value. * * @param unit the unit of measure for the value to be returned. * @return the numeric value represented by this parameter after conversion to type @@ -107,11 +150,26 @@ public interface ParameterValue extends GeneralParameterValue { * @see #setValue(double,Unit) * @see #doubleValueList(Unit) */ - double doubleValue(Unit unit) throws IllegalArgumentException, IllegalStateException; + default double doubleValue(final Unit unit) throws IllegalArgumentException, IllegalStateException { + final T value = getValue(); + final Number number; + try { + number = (Number) value; + } catch (ClassCastException cause) { + throw new InvalidParameterTypeException(cause, getName()); + } + final Unit source = getUnit(); + try { + return source.getConverterToAny(unit).convert(number).doubleValue(); + } catch (IncommensurableException cause) { + throw new IllegalArgumentException(cannotConvert(unit), cause); + } + } /** * Returns the numeric value of this operation parameter. * The unit of measurement is specified by {@link #getUnit()}. + * The default implementation invokes {@link #getValue()}, then tries to cast the value. * * @return the numeric value represented by this parameter after conversion to type {@code double}. * @throws InvalidParameterTypeException if the value is not a numeric type. @@ -127,12 +185,20 @@ public interface ParameterValue extends GeneralParameterValue { * @see #setValue(double) * @see #doubleValueList() */ - @UML(identifier="value", obligation=CONDITIONAL, specification=ISO_19111) - double doubleValue() throws IllegalStateException; + @UML(identifier="ParameterValue.value", obligation=CONDITIONAL, specification=ISO_19111) + default double doubleValue() throws IllegalStateException { + final T value = getValue(); + try { + return ((Number) value).doubleValue(); + } catch (NullPointerException | ClassCastException cause) { + throw new InvalidParameterTypeException(cause, getName()); + } + } /** * Returns the integer value of this parameter, usually used for a count. * An integer value does not have an associated unit of measure. + * The default implementation invokes {@link #getValue()}, then tries to cast the value. * * @return the numeric value represented by this parameter after conversion to type {@code int}. * @throws InvalidParameterTypeException if the value is not an integer type. @@ -145,25 +211,41 @@ public interface ParameterValue extends GeneralParameterValue { * @see #setValue(int) * @see #intValueList() */ - @UML(identifier="integerValue", obligation=CONDITIONAL, specification=ISO_19111) - int intValue() throws IllegalStateException; + @UML(identifier="ParameterValue.integerValue", obligation=CONDITIONAL, specification=ISO_19111) + default int intValue() throws IllegalStateException { + final T value = getValue(); + try { + return Math.toIntExact(((Number) value).longValue()); + } catch (NullPointerException | ClassCastException | ArithmeticException cause) { + throw new InvalidParameterTypeException(cause, getName()); + } + } /** - * Returns the boolean value of this parameter. - * A boolean value does not have an associated unit of measure. + * Returns the Boolean value of this parameter. + * A Boolean value does not have an associated unit of measure. + * The default implementation invokes {@link #getValue()}, then tries to cast the value. * - * @return the boolean value represented by this parameter. - * @throws InvalidParameterTypeException if the value is not a boolean type. + * @return the Boolean value represented by this parameter. + * @throws InvalidParameterTypeException if the value is not a Boolean type. * @throws IllegalStateException if the value cannot be returned for another reason. * * @see #setValue(boolean) */ - @UML(identifier="booleanValue", obligation=CONDITIONAL, specification=ISO_19111) - boolean booleanValue() throws IllegalStateException; + @UML(identifier="ParameterValue.booleanValue", obligation=CONDITIONAL, specification=ISO_19111) + default boolean booleanValue() throws IllegalStateException { + final T value = getValue(); + try { + return (Boolean) value; + } catch (NullPointerException | ClassCastException cause) { + throw new InvalidParameterTypeException(cause, getName()); + } + } /** * Returns the string value of this parameter. * A string value does not have an associated unit of measure. + * The default implementation invokes {@link #getValue()}, then tries to cast the value. * * @return the string value represented by this parameter. * @throws InvalidParameterTypeException if the value is not a string. @@ -172,12 +254,20 @@ public interface ParameterValue extends GeneralParameterValue { * @see #getValue() * @see #setValue(Object) */ - @UML(identifier="stringValue", obligation=CONDITIONAL, specification=ISO_19111) - String stringValue() throws IllegalStateException; + @UML(identifier="ParameterValue.stringValue", obligation=CONDITIONAL, specification=ISO_19111) + default String stringValue() throws IllegalStateException { + final T value = getValue(); + try { + return (String) value; + } catch (ClassCastException cause) { + throw new InvalidParameterTypeException(cause, getName()); + } + } /** * Returns an ordered sequence of numeric values in the specified unit of measure. * This convenience method applies unit conversions on the fly as needed. + * The default implementation invokes {@link #doubleValueList()}, then tries to convert the values. * * @param unit the unit of measure for the value to be returned. * @return the sequence of values represented by this parameter after conversion to type @@ -190,11 +280,27 @@ public interface ParameterValue extends GeneralParameterValue { * @see #setValue(double[],Unit) * @see #doubleValue(Unit) */ - double[] doubleValueList(Unit unit) throws IllegalArgumentException, IllegalStateException; + default double[] doubleValueList(Unit unit) throws IllegalArgumentException, IllegalStateException { + double[] values = doubleValueList(); + final Unit source = getUnit(); + try { + final UnitConverter c = source.getConverterToAny(unit); + if (!c.isIdentity()) { + values = values.clone(); + for (int i=0; i < values.length; i++) { + values[i] = c.convert(values[i]); + } + } + } catch (IncommensurableException cause) { + throw new InvalidParameterTypeException(cause, getName()); + } + return values; + } /** * Returns an ordered sequence of two or more numeric values of this parameter, * where each value has the same associated unit of measure. + * The default implementation invokes {@link #getValue()}, then tries to cast the value. * * @return the sequence of values represented by this parameter. * @throws InvalidParameterTypeException if the value is not an array of {@code double}s. @@ -211,12 +317,20 @@ public interface ParameterValue extends GeneralParameterValue { * @see #setValue(Object) * @see #doubleValue() */ - @UML(identifier="valueList", obligation=CONDITIONAL, specification=ISO_19111) - double[] doubleValueList() throws IllegalStateException; + @UML(identifier="ParameterValue.valueList", obligation=CONDITIONAL, specification=ISO_19111) + default double[] doubleValueList() throws IllegalStateException { + final T value = getValue(); + try { + return (double[]) value; + } catch (ClassCastException cause) { + throw new InvalidParameterTypeException(cause, getName()); + } + } /** * Returns an ordered sequence of two or more integer values of this parameter, usually used for counts. * These integer values do not have an associated unit of measure. + * The default implementation invokes {@link #getValue()}, then tries to cast the value. * * @return the sequence of values represented by this parameter. * @throws InvalidParameterTypeException if the value is not an array of {@code int}s. @@ -229,8 +343,15 @@ public interface ParameterValue extends GeneralParameterValue { * @see #setValue(Object) * @see #intValue() */ - @UML(identifier="integerValueList", obligation=CONDITIONAL, specification=ISO_19111) - int[] intValueList() throws IllegalStateException; + @UML(identifier="ParameterValue.integerValueList", obligation=CONDITIONAL, specification=ISO_19111) + default int[] intValueList() throws IllegalStateException { + final T value = getValue(); + try { + return (int[]) value; + } catch (ClassCastException cause) { + throw new InvalidParameterTypeException(cause, getName()); + } + } /** * Returns a reference to a file or a part of a file containing one or more parameter values. @@ -238,6 +359,8 @@ public interface ParameterValue extends GeneralParameterValue { * as an XML encoded document. Furthermore, the referenced file or part of a file can reference * another part of the same or different files, as allowed in XML documents. * + *

                                    The default implementation invokes {@link #getValue()}, then tries to cast the value.

                                    + * * @return the reference to a file containing parameter values. * @throws InvalidParameterTypeException if the value is not a reference to a file or a URI. * @throws IllegalStateException if the value cannot be returned for another reason. @@ -245,8 +368,15 @@ public interface ParameterValue extends GeneralParameterValue { * @see #getValue() * @see #setValue(Object) */ - @UML(identifier="valueFile", obligation=CONDITIONAL, specification=ISO_19111) - URI valueFile() throws IllegalStateException; + @UML(identifier="ParameterValue.valueFile", obligation=CONDITIONAL, specification=ISO_19111) + default URI valueFile() throws IllegalStateException { + final T value = getValue(); + try { + return (URI) value; + } catch (ClassCastException cause) { + throw new InvalidParameterTypeException(cause, getName()); + } + } /** * Returns the parameter value as an object. The object type is typically (but not restricted to) @@ -288,6 +418,7 @@ public interface ParameterValue extends GeneralParameterValue { /** * Sets the parameter value as a floating point. + * The default implementation delegates to {@link #setValue(Object)}. * * @param value the parameter value. * @throws InvalidParameterValueException if the floating point type is inappropriate for this @@ -297,10 +428,13 @@ public interface ParameterValue extends GeneralParameterValue { * @see #setValue(double,Unit) * @see #doubleValue() */ - void setValue(double value) throws InvalidParameterValueException; + default void setValue(double value) throws InvalidParameterValueException { + setValue((Object) value); + } /** * Sets the parameter value as an integer. + * The default implementation delegates to {@link #setValue(Object)}. * * @param value the parameter value. * @throws InvalidParameterValueException if the integer type is inappropriate for this parameter, @@ -308,17 +442,22 @@ public interface ParameterValue extends GeneralParameterValue { * * @see #intValue() */ - void setValue(int value) throws InvalidParameterValueException; + default void setValue(int value) throws InvalidParameterValueException { + setValue((Object) value); + } /** - * Sets the parameter value as a boolean. + * Sets the parameter value as a Boolean. + * The default implementation delegates to {@link #setValue(Object)}. * * @param value the parameter value. - * @throws InvalidParameterValueException if the boolean type is inappropriate for this parameter. + * @throws InvalidParameterValueException if the Boolean type is inappropriate for this parameter. * * @see #booleanValue() */ - void setValue(boolean value) throws InvalidParameterValueException; + default void setValue(boolean value) throws InvalidParameterValueException { + setValue((Object) value); + } /** * Sets the parameter value as an object. The object type is typically (but not restricted to) diff --git a/geoapi/src/main/java/org/opengis/parameter/ParameterValueGroup.java b/geoapi/src/main/java/org/opengis/parameter/ParameterValueGroup.java index 2924bddce..ce5a72e1c 100644 --- a/geoapi/src/main/java/org/opengis/parameter/ParameterValueGroup.java +++ b/geoapi/src/main/java/org/opengis/parameter/ParameterValueGroup.java @@ -27,11 +27,10 @@ /** * A group of related parameter values. - * - *

                                    The same group can be repeated more than once in an + * The same group can be repeated more than once in an * {@linkplain org.opengis.referencing.operation.CoordinateOperation coordinate operation} * or higher level {@code ParameterValueGroup}, if those instances contain different values - * of one or more {@link ParameterValue}s which suitably distinguish among those groups.

                                    + * of one or more {@link ParameterValue}s which suitably distinguish among those groups. * *

                                    The methods adapted from the ISO 19111 standard are {@link #getDescriptor()} and {@link #values()}. * Other methods (except {@link #clone()}) are convenience methods:

                                    @@ -42,23 +41,26 @@ *
                                  • {@link #addGroup(String)} for creating a new subgroup and adding it to the list of subgroups.
                                  • *
                                  * - *
                                  Design note: - * there is no parameters(String) method returning a list of parameter values + *

                                  Design note:

                                  + * There is no parameters(String) method returning a list of parameter values * because the ISO 19111 standard fixes the {@link ParameterValue} - * {@linkplain ParameterDescriptor#getMaximumOccurs() maximum occurrence} to 1.
                                  + * {@linkplain ParameterDescriptor#getMaximumOccurs() maximum occurrence} to 1. * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @author Jody Garnett (Refractions Research) - * @version 3.0 + * @version 3.1 * @since 1.0 * * @see ParameterDescriptorGroup * @see ParameterValue */ -@UML(identifier="CC_ParameterValueGroup", specification=ISO_19111, version=2007) +@UML(identifier="ParameterValueGroup", specification=ISO_19111) public interface ParameterValueGroup extends GeneralParameterValue { /** - * The abstract definition of this group of parameters. + * Returns the abstract definition of this group of parameters. + * + * @return the abstract definition of this group of parameters. * * @departure rename * The ISO name was "{@code group}". GeoAPI uses "{@code descriptor}" instead in diff --git a/geoapi/src/main/java/org/opengis/referencing/AuthorityFactory.java b/geoapi/src/main/java/org/opengis/referencing/AuthorityFactory.java index 468dc39f6..10a96b2a3 100644 --- a/geoapi/src/main/java/org/opengis/referencing/AuthorityFactory.java +++ b/geoapi/src/main/java/org/opengis/referencing/AuthorityFactory.java @@ -18,25 +18,29 @@ package org.opengis.referencing; import java.util.Set; +import java.util.Optional; import org.opengis.metadata.citation.Citation; import org.opengis.util.Factory; import org.opengis.util.FactoryException; import org.opengis.util.InternationalString; import org.opengis.util.UnimplementedServiceException; import org.opengis.annotation.UML; + +import static org.opengis.annotation.Obligation.*; import static org.opengis.annotation.Specification.*; /** - * Base interface for all authority factories. An authority is an organization - * that maintains definitions of authority codes. An authority code is a compact - * string defined by an authority to reference a particular spatial reference object. + * Base interface for all authority factories. + * An authority is an organization that maintains definitions of authority codes. + * An authority code is a compact string defined by an authority to reference a particular referencing object. * - *

                                  For example, the European Petroleum Survey Group (EPSG) maintains - * a database of coordinate systems, and other spatial referencing objects, where each object has a code - * number ID. For example, the EPSG code for a WGS84 Lat/Lon coordinate system is “4326”.

                                  + *

                                  For example, the EPSG geodetic registry is a database of coordinate + * reference systems, and other spatial referencing objects, where each object has a code number ID. + * For example, the EPSG code for a WGS84 Lat/Lon CRS is 4326.

                                  * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 */ @@ -54,26 +58,23 @@ public interface AuthorityFactory extends Factory { * Returns the set of authority codes for objects of the given type. * The {@code type} argument specifies the base type of identified objects. * - *

                                  Example: if this factory is an instance of {@link org.opengis.referencing.crs.CRSAuthorityFactory}, - * then:

                                  - * - *
                                    - *
                                  • {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem}.class
                                    - * asks for all authority codes accepted by one of - * {@link org.opengis.referencing.crs.CRSAuthorityFactory#createGeographicCRS createGeographicCRS}, - * {@link org.opengis.referencing.crs.CRSAuthorityFactory#createProjectedCRS createProjectedCRS}, - * {@link org.opengis.referencing.crs.CRSAuthorityFactory#createVerticalCRS createVerticalCRS}, - * {@link org.opengis.referencing.crs.CRSAuthorityFactory#createTemporalCRS createTemporalCRS} - * and any other method returning a sub-type of {@code CoordinateReferenceSystem}.

                                  • - *
                                  • {@linkplain org.opengis.referencing.crs.ProjectedCRS}.class
                                    - * asks only for authority codes accepted by - * {@link org.opengis.referencing.crs.CRSAuthorityFactory#createProjectedCRS createProjectedCRS}.

                                  • - *
                                  + *

                                  Example

                                  + * Assuming that this factory is an instance of {@link org.opengis.referencing.crs.CRSAuthorityFactory}, + * if the {@code type} argument value is set to + * {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem}.class, + * then this method should return all authority codes accepted by methods such as + * {@link org.opengis.referencing.crs.CRSAuthorityFactory#createGeographicCRS createGeographicCRS(…)}, + * {@link org.opengis.referencing.crs.CRSAuthorityFactory#createProjectedCRS createProjectedCRS(…)}, + * {@link org.opengis.referencing.crs.CRSAuthorityFactory#createVerticalCRS createVerticalCRS(…)}, + * {@link org.opengis.referencing.crs.CRSAuthorityFactory#createTemporalCRS createTemporalCRS(…)} + * and any other method returning a sub-type of {@code CoordinateReferenceSystem}. + * By contrast, if the {@code type} argument value is set to + * {@linkplain org.opengis.referencing.crs.ProjectedCRS}.class, + * then this method should return only the authority codes accepted by + * {@link org.opengis.referencing.crs.CRSAuthorityFactory#createProjectedCRS createProjectedCRS(…)}. * - * @param type the spatial reference objects type. - * @return the set of authority codes for spatial reference objects of the given type. - * If this factory does not contain any object of the given type, then this method - * returns an {@linkplain java.util.Collections#emptySet() empty set}. + * @param type the type of referencing object for which to get authority codes. + * @return the set of authority codes for referencing objects of the given type. * @throws FactoryException if access to the underlying database failed. * * @departure extension @@ -85,6 +86,25 @@ public interface AuthorityFactory extends Factory { /** * Returns a description of the object corresponding to a code. * The description may be used in graphical user interfaces. + * It may be empty if the object corresponding to the specified code has no description. + * + * @param type the type of objects for which to get a description. + * @param code value allocated by the authority for an object of the given type. + * @return a description of the object. + * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found. + * @throws FactoryException if the query failed for some other reason. + * + * @since 3.1 + */ + @UML(identifier="descriptionText", obligation=OPTIONAL, specification=OGC_01009) + default Optional getDescriptionText(Class type, String code) + throws FactoryException + { + return Optional.empty(); + } + + /** + * 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 @@ -94,13 +114,10 @@ public interface AuthorityFactory extends Factory { * * @deprecated This method is ambiguous because the EPSG geodetic registry may allocate * the same code to different kinds of object. - * - * @todo Provide an alternative, maybe with a {@code Class} argument. */ - @Deprecated(since = "3.1", forRemoval = true) - @UML(identifier="descriptionText", specification=OGC_01009) + @Deprecated(since = "3.1") default InternationalString getDescriptionText(String code) throws FactoryException { - throw new UnimplementedServiceException(this, InternationalString.class, "description"); + return getDescriptionText(IdentifiedObject.class, code).orElse(null); } /** @@ -132,7 +149,7 @@ default InternationalString getDescriptionText(String code) throws FactoryExcept * {@code createCoordinateSystem(…)} or {@code createCoordinateReferenceSystem(…)} should * be invoked instead. */ - @Deprecated(since = "3.1", forRemoval = true) + @Deprecated(since = "3.1") default IdentifiedObject createObject(String code) throws FactoryException { throw new UnimplementedServiceException(this, IdentifiedObject.class); } diff --git a/geoapi/src/main/java/org/opengis/referencing/IdentifiedObject.java b/geoapi/src/main/java/org/opengis/referencing/IdentifiedObject.java index b924e3bb8..cf88d1064 100644 --- a/geoapi/src/main/java/org/opengis/referencing/IdentifiedObject.java +++ b/geoapi/src/main/java/org/opengis/referencing/IdentifiedObject.java @@ -37,16 +37,16 @@ * *
                                    *
                                  • A {@linkplain #getName() name} (e.g. North American Datum of 1983).
                                  • - *
                                  • Alternative names or {@linkplain #getAlias() aliases} (e.g. “NAD83” abbreviation).
                                  • + *
                                  • Alternative names or {@linkplain #getAlias() aliases} (e.g. NAD83 abbreviation).
                                  • *
                                  • {@linkplain #getIdentifiers() Identifiers} allocated by authorities - * (e.g. a register of geodetic codes and parameters might give the NAD83 datum a unique code of “6269”).
                                  • + * (e.g. a register of geodetic codes and parameters might give the NAD83 datum a unique code of 6269). *
                                  • {@linkplain #getRemarks() Remarks} about this object, including data source information.
                                  • *
                                  * * Some typical {@code IdentifiedObject} sub-types are: * *
                                    - *
                                  • {@linkplain org.opengis.referencing.datum.GeodeticDatum Geodetic Datum} (e.g. World Geodetic System 1984),
                                  • + *
                                  • {@linkplain org.opengis.referencing.datum.GeodeticDatum Geodetic Reference Frame} (e.g. World Geodetic System 1984),
                                  • *
                                  • {@linkplain org.opengis.referencing.operation.OperationMethod Operation Method} (e.g. Mercator (variant A)),
                                  • *
                                  • {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem Coordinate Reference System} (e.g. WGS 84 / World Mercator).
                                  • *
                                  @@ -58,20 +58,13 @@ * The other values may or may not be set. * If the authority is EPSG, the implementer may consider using the corresponding metadata values in the EPSG tables. * - * @departure harmonization - * ISO 19111 defines two types, {@code IO_IdentifiedObjectBase} and {@code IO_IdentifiedObject}, as a - * workaround for introducing a base type for the {@code name}, {@code identifier}, {@code alias} - * and {@code remarks} properties without changing the {@code RS_ReferenceSystem} definition inherited - * from ISO 19115. Since GeoAPI replaces ISO 19115 CRS definitions by the ISO 19111 ones for providing a unified - * model, it does not need this workaround. Consequently, GeoAPI merges {@code IO_IdentifiedObjectBase} and - * {@code IO_IdentifiedObject} into this single interface. - * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 2.0 */ @Classifier(Stereotype.ABSTRACT) -@UML(identifier="IO_IdentifiedObject", specification=ISO_19111, version=2007) +@UML(identifier="IdentifiedObject", specification=ISO_19111) public interface IdentifiedObject { /** * Key for the {@value} property to be given to the @@ -126,7 +119,7 @@ public interface IdentifiedObject { String REMARKS_KEY = "remarks"; /** - * The primary name by which this object is identified. + * Returns the primary name by which this object is identified. * *
                                  Upcoming API change — generalization
                                  * As of ISO 19115:2014, {@code ReferenceIdentifier} has been merged with its {@link Identifier} parent interface. @@ -139,7 +132,7 @@ public interface IdentifiedObject { ReferenceIdentifier getName(); /** - * Alternative names by which this object is identified. + * Returns alternative names by which this object is identified. * * @return alternative names and abbreviations, or an empty collection if there is none. */ @@ -149,7 +142,7 @@ default Collection getAlias() { } /** - * An identifier which references elsewhere the object's defining information. + * Returns an identifier which references elsewhere the object's defining information. * Alternatively, an identifier by which this object can be referenced. * *
                                  Upcoming API change — generalization
                                  @@ -165,7 +158,7 @@ default Set getIdentifiers() { } /** - * Usage of this CRS-related object. + * Returns the usage of this CRS-related object. * The domain includes a scope (description of the primary purpose of this object) together * with a domain of validity (spatial and temporal extent in which the object can be used). * Those properties are paired together for facilitating descriptions of usage such as @@ -198,7 +191,7 @@ default Collection getDomains() { } /** - * Comments on or information about this object, including data source information. + * Returns comments on or information about this object, including data source information. * * @return the remarks, or {@code null} if none. */ @@ -208,7 +201,7 @@ default InternationalString getRemarks() { } /** - * Returns a Well-Known Text (WKT) for this object. + * Formats a Well-Known Text (WKT) for this object. * Well-Known Texts (WKT) may come in two formats: * *
                                    diff --git a/geoapi/src/main/java/org/opengis/referencing/NoSuchAuthorityCodeException.java b/geoapi/src/main/java/org/opengis/referencing/NoSuchAuthorityCodeException.java index b46d2b385..8224017d3 100644 --- a/geoapi/src/main/java/org/opengis/referencing/NoSuchAuthorityCodeException.java +++ b/geoapi/src/main/java/org/opengis/referencing/NoSuchAuthorityCodeException.java @@ -25,7 +25,7 @@ * This is a specialization of {@link NoSuchIdentifierException} with the identifier separated in its authority * and code components. * - * @author Martin Desruisseaux (IRD) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 * diff --git a/geoapi/src/main/java/org/opengis/referencing/ObjectDomain.java b/geoapi/src/main/java/org/opengis/referencing/ObjectDomain.java index 5aa69eb50..ae86a4367 100644 --- a/geoapi/src/main/java/org/opengis/referencing/ObjectDomain.java +++ b/geoapi/src/main/java/org/opengis/referencing/ObjectDomain.java @@ -31,6 +31,7 @@ * datum or coordinate system is applied. The domain of validity describes the spatial and * temporal extent in which the object can be used. * + * @author OGC Topic 2 (for abstract model and documentation) * @author Martin Desruisseaux (Geomatys) * @version 3.1 * @since 3.1 diff --git a/geoapi/src/main/java/org/opengis/referencing/ObjectFactory.java b/geoapi/src/main/java/org/opengis/referencing/ObjectFactory.java index df8aa49b3..365d33dce 100644 --- a/geoapi/src/main/java/org/opengis/referencing/ObjectFactory.java +++ b/geoapi/src/main/java/org/opengis/referencing/ObjectFactory.java @@ -17,7 +17,6 @@ */ package org.opengis.referencing; -import java.util.Map; import java.util.Locale; import org.opengis.metadata.Identifier; @@ -28,7 +27,7 @@ /** - * Base interface for all factories of {@linkplain IdentifiedObject identified objects}. + * Base interface for all factories of identified objects. * Factories build up complex objects from simpler objects or values. * This factory allows applications to make * {@linkplain org.opengis.referencing.cs.CoordinateSystem coordinate systems}, @@ -38,7 +37,7 @@ * This factory is very flexible, whereas the authority factory is easier to use. * *

                                    Object properties

                                    - * Most factory methods expect a {@link Map Map<String,?>} argument. + * Most factory methods expect a {@code Map} argument. * The table below lists the keys that {@code ObjectFactory} implementations shall accept, * together with the type of values associated to those keys (the Alternative types column * gives examples of types that factory implementations may accept as well for convenience). @@ -52,32 +51,27 @@ *
* * - * - * + * * * * * - * - * + * * * * * - * - * + * * * * * - * - * + * * * * * - * - * + * * * * diff --git a/geoapi/src/main/java/org/opengis/referencing/ReferenceSystem.java b/geoapi/src/main/java/org/opengis/referencing/ReferenceSystem.java index dae3af77c..229cdd5bf 100644 --- a/geoapi/src/main/java/org/opengis/referencing/ReferenceSystem.java +++ b/geoapi/src/main/java/org/opengis/referencing/ReferenceSystem.java @@ -27,7 +27,7 @@ /** - * Description of a spatial and temporal reference system used by a dataset. + * Base interface of reference systems by coordinates or by identifiers. * A reference system contains the metadata required to interpret spatial location information unambiguously. * Two methods to describe spatial location are distinguished: * @@ -63,7 +63,8 @@ * ISO 19115:2003 {@code RS_ReferenceSystem} than to * ISO 19115:2015 {@code MD_ReferenceSystem}. * - * @author Martin Desruisseaux (IRD) + * @author ISO 19115 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 * @@ -100,8 +101,8 @@ public interface ReferenceSystem extends IdentifiedObject { * * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019. */ - @Deprecated(since="3.1", forRemoval=true) - @UML(identifier="domainOfValidity", obligation=OPTIONAL, specification=ISO_19111, version=2007) + @Deprecated(since="3.1") + @UML(identifier="SC_CRS.domainOfValidity", obligation=OPTIONAL, specification=ISO_19111, version=2007) default Extent getDomainOfValidity() { return Legacy.getDomainOfValidity(getDomains()); } @@ -114,7 +115,7 @@ default Extent getDomainOfValidity() { * * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019. */ - @Deprecated(since="3.1", forRemoval=true) + @Deprecated(since="3.1") @UML(identifier="SC_CRS.scope", obligation=OPTIONAL, specification=ISO_19111, version=2007) default InternationalString getScope() { return Legacy.getScope(getDomains()); diff --git a/geoapi/src/main/java/org/opengis/referencing/ReferenceSystemType.java b/geoapi/src/main/java/org/opengis/referencing/ReferenceSystemType.java index 05a8f56fc..1c3579638 100644 --- a/geoapi/src/main/java/org/opengis/referencing/ReferenceSystemType.java +++ b/geoapi/src/main/java/org/opengis/referencing/ReferenceSystemType.java @@ -28,6 +28,7 @@ /** * Defines type of reference system used. * + * @author ISO 19115 (for abstract model and documentation) * @author Rémi Maréchal (Geomatys) * @version 3.1 * @since 3.1 @@ -300,8 +301,8 @@ public final class ReferenceSystemType extends CodeList { new ReferenceSystemType("TEMPORAL"); /** - * One-dimensional coordinate reference system based on a vertical datum - * (datum describing the relation of gravity-related heights or depths to the earth). + * One-dimensional coordinate reference system based on a vertical datum. + * Vertical datums describe the relation of gravity-related heights or depths to the planet. * *
Example: height or depths.
*/ diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/CRSAuthorityFactory.java b/geoapi/src/main/java/org/opengis/referencing/crs/CRSAuthorityFactory.java index ced21b12b..891366852 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/CRSAuthorityFactory.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/CRSAuthorityFactory.java @@ -28,10 +28,10 @@ /** - * Creates {@linkplain CoordinateReferenceSystem coordinate reference systems} using authority codes. + * Creates coordinate reference systems using authority codes. * External authorities are used to manage definitions of objects used in this interface. * The definitions of these objects are referenced using code strings. - * A commonly used authority is EPSG. + * A commonly used authority is the EPSG geodetic registry. * *

Default methods

* All {@code create(…)} methods in this interface are optional. @@ -43,7 +43,7 @@ * saying that the type or service is not supported. * * - * @author Martin Desruisseaux (IRD) + * @author Martin Desruisseaux (IRD, Geomatys) * @author Johann Sorel (Geomatys) * @version 3.1 * @since 1.0 @@ -219,7 +219,10 @@ default EngineeringCRS createEngineeringCRS(final String code) throws FactoryExc * @return the coordinate reference system 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. + * + * @deprecated {@code ImageCRS} is replaced by {@link EngineeringCRS} as of ISO 19111:2019. */ + @Deprecated(since="3.1") default ImageCRS createImageCRS(final String code) throws FactoryException { final CoordinateReferenceSystem crs = createCoordinateReferenceSystem(code); try { @@ -277,8 +280,7 @@ default ProjectedCRS createProjectedCRS(final String code) throws FactoryExcepti * @deprecated This method is ambiguous. Use {@link #createCoordinateReferenceSystem(String)} instead. */ @Override - @SuppressWarnings("removal") - @Deprecated(since = "3.1", forRemoval = true) + @Deprecated(since = "3.1") default org.opengis.referencing.IdentifiedObject createObject(String code) throws FactoryException { return createCoordinateReferenceSystem(code); } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/CRSFactory.java b/geoapi/src/main/java/org/opengis/referencing/crs/CRSFactory.java index e76cacd1a..6348d6f96 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/CRSFactory.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/CRSFactory.java @@ -34,7 +34,7 @@ /** * Builds up complex Coordinate Reference Systems from simpler objects or values. * {@code CRSFactory} allows applications to make - * {@linkplain CoordinateReferenceSystem Coordinate Reference Systems} + * {@linkplain CoordinateReferenceSystem Coordinate Reference Systems} (CRS) * that cannot be created by a {@link CRSAuthorityFactory}. * This factory is very flexible, whereas the authority factory is easier to use. * So {@link CRSAuthorityFactory} can be used to make "standard" coordinate reference systems, @@ -51,7 +51,7 @@ * the default is to throw an {@link UnimplementedServiceException} * with a message saying that the type or service is not supported. * - * @author Martin Desruisseaux (IRD) + * @author Martin Desruisseaux (IRD, Geomatys) * @author Johann Sorel (Geomatys) * @version 3.1 * @since 1.0 @@ -68,7 +68,7 @@ public interface CRSFactory extends ObjectFactory { * * @param properties name and other properties to give to the new object. * Available properties are {@linkplain ObjectFactory listed there}. - * @param datum geodetic datum to use in created CRS. + * @param datum geodetic reference frame to use in created CRS. * @param cs the ellipsoidal coordinate system for the created CRS. * @return the coordinate reference system for the given properties. * @throws FactoryException if the object creation failed. @@ -86,7 +86,7 @@ default GeographicCRS createGeographicCRS(Map properties, * * @param properties name and other properties to give to the new object. * Available properties are {@linkplain ObjectFactory listed there}. - * @param datum geodetic datum to use in created CRS. + * @param datum geodetic reference frame to use in created CRS. * @param cs the spherical coordinate system for the created CRS. * @return the coordinate reference system for the given properties. * @throws FactoryException if the object creation failed. @@ -103,7 +103,7 @@ default GeocentricCRS createGeocentricCRS(Map properties, * * @param properties name and other properties to give to the new object. * Available properties are {@linkplain ObjectFactory listed there}. - * @param datum geodetic datum to use in created CRS. + * @param datum geodetic reference frame to use in created CRS. * @param cs the Cartesian coordinate system for the created CRS. * @return the coordinate reference system for the given properties. * @throws FactoryException if the object creation failed. @@ -211,7 +211,10 @@ default EngineeringCRS createEngineeringCRS(Map properties, * @param cs the Cartesian or Oblique Cartesian coordinate system for the created CRS. * @return the coordinate reference system for the given properties. * @throws FactoryException if the object creation failed. + * + * @deprecated {@code ImageCRS} is replaced by {@link EngineeringCRS} as of ISO 19111:2019. */ + @Deprecated(since="3.1") default ImageCRS createImageCRS(Map properties, ImageDatum datum, AffineCS cs) throws FactoryException diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/CompoundCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/CompoundCRS.java index c845b5f25..56c39c4b8 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/CompoundCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/CompoundCRS.java @@ -19,6 +19,9 @@ import java.util.Map; import java.util.List; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Collections; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.annotation.UML; @@ -27,70 +30,112 @@ /** - * A coordinate reference system describing the position of points through two or more - * independent coordinate reference systems. Thus it is indirectly associated with two - * or more {@linkplain org.opengis.referencing.cs.CoordinateSystem coordinate systems} - * and {@linkplain org.opengis.referencing.datum.Datum datums} by defining the compound - * CRS as an ordered set of two or more instances of {@link CoordinateReferenceSystem}. + * A CRS describing the position of points through two or more independent CRSs. + * Two CRSs are independent of each other if coordinate values in one cannot be converted or + * transformed into coordinate values in the other. * - *

For spatial coordinates, a number of constraints exist for the construction of Compound CRSs. - * For example, the coordinate reference systems that are combined should not contain any duplicate - * or redundant axes. Valid combinations include:

+ *

For spatial coordinates, a number of constraints exist for the construction of compound CRSs. + * For example, the CRSs that are combined should not contain any duplicate or redundant axes. + * Valid combinations include (non-exhaustive list):

* *
    *
  • Geographic 2D + Vertical
  • *
  • Geographic 2D + Engineering 1D (near vertical)
  • - *
  • Projected + Vertical
  • - *
  • Projected + Engineering 1D (near vertical)
  • - *
  • Engineering (horizontal 2D or 1D linear) + Vertical
  • + *
  • Projected 2D + Vertical
  • + *
  • Projected 2D + Engineering 1D (near vertical)
  • + *
  • Engineering (horizontal 2D) + Vertical
  • *
  • Engineering (1D linear) + Vertical
  • *
* - * Any coordinate reference system, or any of the above listed combinations of coordinate reference systems, - * can have a {@link TemporalCRS} added. More than one temporal CRS may be added if these axes represent - * different time quantities. For example, the oil industry sometimes uses "4D seismic", by which is meant - * seismic data with the vertical axis expressed in milliseconds (signal travel time). A second time axis - * indicates how it changes with time (years), e.g. as a reservoir is gradually exhausted of its recoverable - * oil or gas. + * Any coordinate reference system (CRS), or any of the above listed combinations of CRSs, + * can have a {@link TemporalCRS} added. More than one temporal CRS may be added if these axes represent + * different time quantities. * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 * * @see CRSAuthorityFactory#createCompoundCRS(String) * @see CRSFactory#createCompoundCRS(Map, CoordinateReferenceSystem[]) */ -@UML(identifier="SC_CompoundCRS", specification=ISO_19111, version=2007) +@UML(identifier="CompoundCRS", specification=ISO_19111) public interface CompoundCRS extends CoordinateReferenceSystem { /** - * The ordered list of coordinate reference systems. + * Returns the ordered list of CRS components. + * The returned list may contain nested compound CRS. + * For a list without nesting, as required by ISO 19111, see {@link #getSingleComponents()}. * - * @return the ordered list of coordinate reference systems. + *

Why nested compound CRS

+ * The use of nested compound CRSs can avoid metadata lost when a temporal CRS + * is appended to spatial components defined in a preexisting compound CRS. + * A three-dimensional compound CRS has its own metadata (e.g., an EPSG code) + * that may not be found in the individual horizontal and vertical components. + * A flatten list of horizontal, vertical and temporal components would lost those metadata. + * In particular, the lost of authority code reduces the scope of the + * {@linkplain org.opengis.referencing.operation.CoordinateOperationAuthorityFactory#createFromCoordinateReferenceSystemCodes + * search for coordinate operations} that an application can do. + * + * @return the ordered list of components of this compound CRS. * * @departure generalization - * ISO 19111 said nesting of compound CRSs shall not be permitted; the individual single systems - * shall be aggregated together. However, this approach causes data lost: it is difficult to add - * a temporal CRS to an existing three-dimensional compound CRS without loosing the name and identifiers - * of the 3D CRS, unless nesting is permitted. It is programmatically easier to convert nested CRSs to a - * flat list of single CRSs when needed than to reconstruct the 3D CRS from the single components. - * Consequently, GeoAPI has been keep conformant with the legacy OGC 01-009 specification in this aspect, - * which were allowing nested compound CRS. + * Added as an alternative to the association defined by ISO 19111 for resolving the problem of metadata lost. + * The ISO 19111 requirement is still available as the {@link #getSingleComponents()} method. */ - @UML(identifier="componentReferenceSystem", obligation=MANDATORY, specification=ISO_19111) List getComponents(); + /** + * Returns the ordered list of CRS components, none of which itself compound. + * If this compound CRS contains nested compound CRS components, + * then those components are flattened recursively in a sequence of {@link SingleCRS} objects. + * + * @return the ordered list of components of this compound CRS, none of which itself compound. + * + * @since 3.1 + */ + @UML(identifier="componentReferenceSystem", obligation=MANDATORY, specification=ISO_19111) + default List getSingleComponents() { + var singles = new ArrayList(5); + flatten(singles, new LinkedList<>()); // Linked list is cheap to construct and efficient with 0 or 1 element. + return Collections.unmodifiableList(singles); + } + + /** + * Appends recursively all single components in the given list. + * + * @param singles the list where to add single components. + * @param safety a safety against infinite recursive method calls. + * @throws IllegalStateException if recursive components are detected. + */ + private void flatten(final List singles, final List safety) { + for (CoordinateReferenceSystem crs : getComponents()) { + if (crs instanceof SingleCRS) { + singles.add((SingleCRS) crs); + } else if (crs instanceof CompoundCRS) { + for (Object previous : safety) { + if (previous == this) { + throw new IllegalStateException("Recursive components detected."); + } + } + safety.add(this); + ((CompoundCRS) crs).flatten(singles, safety); + } + } + } + /** * Returns a view over all coordinate systems of this compound CRS. * The returned coordinate system shall have a {@linkplain CoordinateSystem#getDimension() dimension} * equals to the sum of the dimensions of all {@linkplain #getComponents() components}, - * and axes obtained from the coordinate system of each component. + * and axes obtained from the coordinate system of each component in the same order. * * @return view over all coordinate systems of this compound CRS. * * @departure generalization - * ISO 19111 defines this method for {@code SC_SingleCRS} only. GeoAPI declares this method in - * {@code CompoundCRS} as well for user convenience, because CS dimension and axes are commonly - * requested information that are still available (indirectly) for compound CRS. + * ISO 19111 defines this method for {@link SingleCRS} only. + * GeoAPI declares this method in {@code CompoundCRS} as well for user convenience, + * because CS dimension and axes are commonly requested information + * that are still available (indirectly) for compound CRS. */ @Override default CoordinateSystem getCoordinateSystem() { diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/CompoundCS.java b/geoapi/src/main/java/org/opengis/referencing/crs/CompoundCS.java index 138451d62..2c63c978a 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/CompoundCS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/CompoundCS.java @@ -17,6 +17,7 @@ */ package org.opengis.referencing.crs; +import java.io.Serializable; import org.opengis.metadata.Identifier; import org.opengis.referencing.ReferenceIdentifier; import org.opengis.referencing.cs.CoordinateSystem; @@ -33,14 +34,23 @@ * Implementers should override {@link CompoundCRS#getCoordinateSystem()} with their own implementation * for better performances or other characteristics such as WKT support. * + *

Serialization

+ * Instances of this class are serializable if the wrapped CRS implementation is also serializable. + * * @author Martin Desruisseaux (Geomatys) * @version 3.1 * @since 3.1 */ -final class CompoundCS implements CoordinateSystem, ReferenceIdentifier { +final class CompoundCS implements CoordinateSystem, ReferenceIdentifier, Serializable { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = 3782889810638117329L; + /** * The CRS provided by the implementer. */ + @SuppressWarnings("serial") // Whether the CRS is serializable is implementor's decision. private final CompoundCRS crs; /** diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/CoordinateReferenceSystem.java b/geoapi/src/main/java/org/opengis/referencing/crs/CoordinateReferenceSystem.java index 611f92a36..1e4ba9713 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/CoordinateReferenceSystem.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/CoordinateReferenceSystem.java @@ -27,63 +27,57 @@ /** - * Base type of all Coordinate Reference Systems (CRS). + * Base type of all Coordinate Reference Systems (CRS). * This is the base interface for two cases: * *
    *
  • {@link SingleCRS}, defined by a * {@linkplain org.opengis.referencing.cs.CoordinateSystem coordinate system} and a - * {@linkplain org.opengis.referencing.datum.Datum datum};
  • + * {@linkplain org.opengis.referencing.datum.Datum datum} or datum ensemble; *
  • {@link CompoundCRS}, defined as a sequence of {@code SingleCRS}.
  • *
* *

Purpose

- * A coordinate reference system (CRS) captures the choice of values for the parameters that constitute - * the degrees of freedom of the coordinate space. The fact that such a choice has to be made, - * either arbitrarily or by adopting values from survey measurements, leads to the large number - * of coordinate reference systems in use around the world. It is also the cause of the little - * understood fact that the latitude and longitude of a point are not unique. Without the full - * specification of the coordinate reference system, coordinates are ambiguous at best and - * meaningless at worst. + * A coordinate reference system (CRS) captures the choice of values + * for the parameters that constitute the degrees of freedom of the coordinate space. + * The fact that such a choice has to be made, either arbitrarily or by adopting values from survey measurements, + * leads to the large number of coordinate reference systems in use around the world. + * It is also the cause of the little understood fact that the latitude and longitude of a point are not unique. + * Without the full specification of the coordinate reference system, + * coordinates are ambiguous at best and meaningless at worst. * - *

Spatio-temporal CRS

+ *

Spatiotemporal CRS

* The concept of coordinates may be expanded from a strictly spatial context to include time. * Time is then added as another coordinate to the coordinate tuple. It is even possible to add * two time-coordinates, provided the two coordinates describe different independent quantities. * An example of the latter is the time/space position of a subsurface point of which the vertical * coordinate is expressed as the two-way travel time of a sound signal in milliseconds, as is - * common in seismic imaging. A second time-coordinate indicates the time of observation, usually - * expressed in whole years. + * common in seismic imaging. A second time-coordinate indicates the time of observation. * - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 */ @Classifier(Stereotype.ABSTRACT) -@UML(identifier="SC_CRS", specification=ISO_19111, version=2007) +@UML(identifier="CRS", specification=ISO_19111) public interface CoordinateReferenceSystem extends ReferenceSystem { /** - * Returns the coordinate system of a single CRS, or a view over all coordinate systems of a compound CRS. - * More specifically: + * Returns the coordinate axes with specified units of measure. + * The type of the returned coordinate system should be one of the sub-interfaces defined in + * the {@link org.opengis.referencing.cs} package. The subtype implies the mathematical rules that define + * how coordinate values are calculated from distances, angles and other geometric elements and vice versa. * - *
    - *
  • If the CRS instance on which this method is invoked is an instance of the - * {@link SingleCRS} interface, then the CS instance which is returned shall - * be one of the defined sub-interfaces of {@link CoordinateSystem}.
  • + *

    An exception to above recommendation is when this CRS is an instance of {@link CompoundCRS}. + * In that case, the coordinate system type may be hidden and the implied mathematical rules are unspecified. + * However the coordinate system object is still useful as a list of axes.

    * - *
  • If the CRS instance on which this method is invoked is an instance of the - * {@link CompoundCRS} interface, then the CS instance which is returned shall - * have a {@linkplain CoordinateSystem#getDimension() dimension} equals to the - * sum of the dimensions of all {@linkplain CompoundCRS#getComponents() components}, - * and axes obtained from the coordinate system of each component.
  • - *
- * - * @return the coordinate system. + * @return the coordinate axes with specified units of measure. * * @departure generalization - * ISO 19111 defines this method for {@code SC_SingleCRS} only. GeoAPI declares this method in - * this parent interface for user convenience, since CS dimension and axes are commonly requested - * information and will always be available, directly or indirectly, even for {@code SC_CompoundCRS}. + * ISO 19111 defines this method for {@link SingleCRS} only. GeoAPI declares this method in this parent interface + * for user convenience, because CRS dimension and axes are commonly requested information and are + * always available, directly or indirectly, even for {@link CompoundCRS}. */ CoordinateSystem getCoordinateSystem(); } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/DerivedCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/DerivedCRS.java index c1afb1473..98702676d 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/DerivedCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/DerivedCRS.java @@ -18,48 +18,114 @@ package org.opengis.referencing.crs; import java.util.Map; +import org.opengis.referencing.datum.Datum; +import org.opengis.referencing.datum.DatumEnsemble; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.operation.Conversion; import org.opengis.annotation.UML; + +import static org.opengis.annotation.Obligation.MANDATORY; import static org.opengis.annotation.Specification.*; /** - * A coordinate reference system that is defined by its coordinate conversion from another CRS - * but is not a projected CRS. - * This category includes coordinate reference systems derived from a projected CRS. - * - *

A {@code DerivedCRS} instance may also implement one of the interfaces listed below, - * provided that the conditions in the right column are meet:

+ * A CRS that is defined by applying a coordinate conversion to another preexisting CRS. + * The derived CRS inherits its datum (reference frame) or datum ensemble from its base CRS. + * A {@code DerivedCRS} instance may also implement one of the interfaces listed below, + * provided that the conditions in the right column are met: * *
Supported codes
Value typeAlternative typesValue returned by
{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}{@link Identifier}{@link String} (see alternatives below){@link IdentifiedObject#getName()}
{@value org.opengis.referencing.IdentifiedObject#ALIAS_KEY}{@linkplain GenericName}[]{@link GenericName}, {@link String} or {@linkplain String}[]{@link IdentifiedObject#getAlias()}
{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}{@linkplain Identifier}[]{@link Identifier}{@link IdentifiedObject#getIdentifiers()}
{@value org.opengis.referencing.IdentifiedObject#DOMAINS_KEY}{@linkplain ObjectDomain}[]{@link ObjectDomain}{@link IdentifiedObject#getDomains()}
{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}{@link InternationalString}{@link String} (see localization below)
- * + * * + * * * * * *
Derived CRS typesDerived CRS types
Type Conditions
{@link ProjectedCRS} Base CRS is a {@link GeographicCRS} and conversion is a map projection.
{@link GeodeticCRS} Base CRS is also a {@code GeodeticCRS}.
{@link VerticalCRS} Base CRS is also a {@code VerticalCRS}.
{@link TemporalCRS} Base CRS is also a {@code TemporalCRS}.
{@link EngineeringCRS} Base CRS is a {@code GeodeticCRS}, {@code ProjectedCRS} or {@code EngineeringCRS}.
* - * @departure integration - * ISO 19111 defines a {@code SC_DerivedCRSType} code list with the following values: - * {@code geodetic}, {@code vertical}, {@code engineering} and {@code image}. - * But ISO 19162 takes a slightly different approach without such code list. - * Instead, ISO 19162 writes the derived CRS using the WKT keyword of the corresponding CRS type - * ({@code “GeodCRS”}, {@code “VertCRS”}, {@code “TimeCRS”} or {@code “EngCRS”}). - * GeoAPI follows a similar path by not providing a {@code DerivedCRSType} code list. - * Instead, we recommend to implement the corresponding interface as documented in the above table. - * Then, Java expressions like {@code (baseCRS instanceof FooCRS)} provides the same capability - * than the code list with more flexibility. For example, it allows to use a derived CRS of type “vertical” - * with API expecting an instance of {@code VerticalCRS}. + *

Projected CRS

+ * In the special case where the CRS is derived from a base {@link GeographicCRS} by applying + * a coordinate conversion known as a map projection to latitude and longitude ellipsoidal coordinate values, + * the {@link ProjectedCRS} subtype should be used. Projected CRSs are modeled as a special case + * of derived CRS because of their importance in geographic information. + * + *

Derived projected CRS

+ * In the special case where the CRS is derived from a base {@link ProjectedCRS}, + * the coordinate system of the derived CRS is not necessarily Cartesian. + * But the derived CRS still inherit the distortion characteristics of the base projected CRS. * - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 4.0 * @since 1.0 * * @see CRSAuthorityFactory#createDerivedCRS(String) * @see CRSFactory#createDerivedCRS(Map, CoordinateReferenceSystem, Conversion, CoordinateSystem) */ -@UML(identifier="SC_DerivedCRS", specification=ISO_19111, version=2007) +@SuppressWarnings("deprecation") +@UML(identifier="DerivedCRS", specification=ISO_19111) public interface DerivedCRS extends GeneralDerivedCRS { + /** + * Returns the CRS that is the base for this derived CRS. + * This is the {@linkplain Conversion#getSourceCRS() source CRS} + * of the {@linkplain #getConversionFromBase() deriving conversion}. + * + *
Upcoming API change — specialization
+ * According ISO 19111, the return type should be {@link SingleCRS}. + * This change may be applied in GeoAPI 4.0. + *
+ * + * @return the CRS that is the base for this derived CRS. + */ + @Override + @UML(identifier="baseCRS", obligation=MANDATORY, specification=ISO_19111) + CoordinateReferenceSystem getBaseCRS(); + + /** + * Returns the conversion from the base CRS to this derived CRS. + * The source CRS of the conversion, if non null, shall be the {@linkplain #getBaseCRS() base CRS}. + * The target CRS of the conversion, if non-null, shall be this CRS. + * + * @return the conversion from the base CRS to this derived CRS. + * + * @departure rename + * Was {@code toBase} in OGC 01-009, {@code conversion} in ISO 19111:2007 + * and {@code derivingConversion} in ISO 19111:2019. By analogy with 01-009, + * GeoAPI defines a method name which contains the "{@code FromBase}" words + * for making clear which CRS is the source or which one is the target. + */ + @Override + @UML(identifier="derivingConversion", obligation=MANDATORY, specification=ISO_19111) + Conversion getConversionFromBase(); + + /** + * Returns the same datum as the base CRS. + * This property may be null if the base CRS is related to an object + * identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @return the datum of the base CRS, or {@code null} if the base is related to + * an object identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + */ + @Override + default Datum getDatum() { + final CoordinateReferenceSystem crs = getBaseCRS(); + return (crs instanceof SingleCRS) ? ((SingleCRS) getBaseCRS()).getDatum() : null; + } + + /** + * Returns the same datum ensemble as the base CRS. + * This property may be null if the base CRS is related to an object + * identified only by a single {@linkplain #getDatum() datum}. + * + * @return the datum ensemble of the base CRS, or {@code null} if the base is + * related to an object identified only by a single {@linkplain #getDatum() datum}. + * + * @since 3.1 + */ + @Override + default DatumEnsemble getDatumEnsemble() { + final CoordinateReferenceSystem crs = getBaseCRS(); + return (crs instanceof SingleCRS) ? ((SingleCRS) getBaseCRS()).getDatumEnsemble() : null; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/EngineeringCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/EngineeringCRS.java index 47fd74765..4aeaebd91 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/EngineeringCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/EngineeringCRS.java @@ -24,58 +24,78 @@ import static org.opengis.annotation.Obligation.*; import static org.opengis.annotation.Specification.*; +import org.opengis.referencing.datum.DatumEnsemble; /** - * A 1-, 2- or 3-dimensional contextually local coordinate reference system. - * It can be divided into two broad categories: + * A 1-, 2- or 3-dimensional CRS used locally. + * It can be divided into three broad categories: * *
    - *
  • earth-fixed systems applied to engineering activities on or near the surface of the earth;
  • - *
  • CRSs on moving platforms such as road vehicles, vessels, aircraft, or spacecraft.
  • + *
  • planet-fixed systems applied to engineering activities on or near the surface of the planet;
  • + *
  • CRSs on moving platforms such as road vehicles, vessels, aircraft, or spacecraft.
  • + *
  • CRSs used to describe spatial location internally on an image.
  • *
* - * Earth-fixed Engineering CRSs are commonly based on a simple flat-earth approximation of the - * earth's surface, and the effect of earth curvature on feature geometry is ignored: calculations - * on coordinates use simple plane arithmetic without any corrections for earth curvature. The - * application of such Engineering CRSs to relatively small areas and "contextually local" is in - * this case equivalent to "spatially local". + *

Planet-fixed engineering CRSs are commonly based on a simple flat-earth approximation + * of the planet's surface, and the effect of planet curvature on feature geometry is ignored: + * calculations on coordinates use simple plane arithmetic without any corrections for planet curvature.

* - *

Engineering CRSs used on moving platforms are usually intermediate coordinate reference - * systems that are computationally required to calculate coordinates referenced to - * {@linkplain GeocentricCRS geocentric}, {@linkplain GeographicCRS geographic} or - * {@linkplain ProjectedCRS projected} CRSs. These engineering coordinate reference - * systems are subject to all the motions of the platform with which they are associated. - * In this case "contextually local" means that the associated coordinates are meaningful - * only relative to the moving platform. Earth curvature is usually irrelevant and is therefore - * ignored. In the spatial sense their applicability may extend from the immediate vicinity of - * the platform (e.g. a moving seismic ship) to the entire earth (e.g. in space applications). - * The determining factor is the mathematical model deployed in the positioning calculations. - * Transformation of coordinates from these moving Engineering CRSs to earth-referenced coordinate - * reference systems involves time-dependent coordinate operation parameters.

+ *

Engineering CRSs used on moving platforms + * are subject to all the motions of the platform with which they are associated. + * In this case the associated coordinates are meaningful only relative to the moving platform. + * Transformation of coordinates from these moving engineering CRSs to planet-referenced + * CRSs involves time-dependent coordinate operation parameters.

* - *

This type of CRS can be used with coordinate systems of type + *

Permitted coordinate systems

+ * This type of CRS can be used with coordinate systems of type * {@link org.opengis.referencing.cs.AffineCS}, * {@link org.opengis.referencing.cs.CartesianCS}, * {@link org.opengis.referencing.cs.CylindricalCS}, * {@link org.opengis.referencing.cs.LinearCS}, * {@link org.opengis.referencing.cs.PolarCS}, - * {@link org.opengis.referencing.cs.SphericalCS}, - * {@link org.opengis.referencing.cs.UserDefinedCS}.

+ * {@link org.opengis.referencing.cs.SphericalCS}. * - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 * * @see CRSAuthorityFactory#createEngineeringCRS(String) * @see CRSFactory#createEngineeringCRS(Map, EngineeringDatum, CoordinateSystem) */ -@UML(identifier="SC_EngineeringCRS", specification=ISO_19111, version=2007) +@UML(identifier="EngineeringCRS", specification=ISO_19111) public interface EngineeringCRS extends SingleCRS { /** * Returns the datum, which shall be an engineering one. + * This property may be null if this CRS is related to an object + * identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @return the engineering datum, or {@code null} if this CRS is related to + * an object identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @condition Mandatory if the {@linkplain #getDatumEnsemble() datum ensemble} is not documented. */ @Override - @UML(identifier="datum", obligation=MANDATORY, specification=ISO_19111) + @UML(identifier="datum", obligation=CONDITIONAL, specification=ISO_19111) EngineeringDatum getDatum(); + + /** + * Returns the datum ensemble, which shall have engineering datum members. + * This property may be null if this CRS is related to an object + * identified only by a single {@linkplain #getDatum() datum}. + * + *

The default implementation returns {@code null}.

+ * + * @return the datum ensemble, or {@code null} if this CRS is related + * to an object identified only by a single {@linkplain #getDatum() datum}. + * + * @condition Mandatory if the {@linkplain #getDatum() datum} is not documented. + * @since 3.1 + */ + @Override + @UML(identifier="datum", obligation=CONDITIONAL, specification=ISO_19111) + default DatumEnsemble getDatumEnsemble() { + return null; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/GeneralDerivedCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/GeneralDerivedCRS.java index 874d53ebe..ff8f83cbb 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/GeneralDerivedCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/GeneralDerivedCRS.java @@ -27,19 +27,16 @@ /** - * A coordinate reference system that is defined by its coordinate conversion from another coordinate reference system. - * Derived CRS are not directly associated to a {@linkplain org.opengis.referencing.datum.Datum datum}. + * A CRS that is defined by applying a coordinate conversion to another preexisting CRS. + * The derived CRS inherits its datum (reference frame) or datum ensemble from its base CRS. * - *

In principle, all sub-types of {@link CoordinateReferenceSystem} may take on the role of either source or - * derived CRS with the exception of a {@link GeocentricCRS} and a {@link ProjectedCRS}. The latter is modelled - * as an object class under its own name, rather than as a general derived CRS of type "projected". - * This has been done to honour common practice, which acknowledges projected CRSs as one of the best known - * types of coordinate reference systems.

- * - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 + * + * @deprecated As of ISO 19111:2019, this interface is renamed as {@link DerivedCRS}. */ +@Deprecated(since="3.1") @Classifier(Stereotype.ABSTRACT) @UML(identifier="SC_GeneralDerivedCRS", specification=ISO_19111, version=2007) public interface GeneralDerivedCRS extends SingleCRS { @@ -53,20 +50,14 @@ public interface GeneralDerivedCRS extends SingleCRS { * * @return the base coordinate reference system. */ - @UML(identifier="baseCRS", obligation=MANDATORY, specification=ISO_19111) + @UML(identifier="baseCRS", obligation=MANDATORY, specification=ISO_19111, version=2007) CoordinateReferenceSystem getBaseCRS(); /** * Returns the conversion from the {@linkplain #getBaseCRS() base CRS} to this CRS. * * @return the conversion from the base CRS. - * - * @departure rename - * "{@code conversion}" may be confusing as a method name - * since it does not indicate which CRS is the source or which is the target. - * The OGC 01-009 specification used the {@code toBase()} method name. - * By analogy with 01-009, GeoAPI defines a method name which contains the "{@code FromBase}" expression. */ - @UML(identifier="conversion", obligation=MANDATORY, specification=ISO_19111) + @UML(identifier="conversion", obligation=MANDATORY, specification=ISO_19111, version=2007) Conversion getConversionFromBase(); } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/GeocentricCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/GeocentricCRS.java index 5433926cb..2a7bbac5c 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/GeocentricCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/GeocentricCRS.java @@ -29,28 +29,29 @@ /** - * A 3-dimensional coordinate reference system with the origin at the approximate centre of mass of the earth. - * A geocentric CRS deals with the earth's curvature by taking a 3-dimensional spatial view, which obviates - * the need to model the earth's curvature. + * A 3-dimensional CRS with the origin at the approximate centre of mass of the planet. + * A geocentric CRS deals with the planet's curvature by taking a 3-dimensional spatial view, which + * obviates the need to model the planet's curvature. * - *

This type of CRS can be used with coordinate systems of type + *

Permitted coordinate systems

+ * This type of CRS can be used with coordinate systems of type * {@link org.opengis.referencing.cs.CartesianCS Cartesian} or - * {@link org.opengis.referencing.cs.SphericalCS Spherical}.

+ * {@link org.opengis.referencing.cs.SphericalCS Spherical}. * - * @departure historic - * This interface is kept conformant with the specification published in 2003. The 2007 revision - * of ISO 19111 removed the {@code SC_GeographicCRS} and {@code SC_GeocentricCRS} types, - * handling both using the {@code SC_GeodeticCRS} parent type. - * GeoAPI keeps them since the distinction between those two types is in wide use. - * - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 * * @see CRSAuthorityFactory#createGeocentricCRS(String) * @see CRSFactory#createGeocentricCRS(Map, GeodeticDatum, CartesianCS) * @see CRSFactory#createGeographicCRS(Map, GeodeticDatum, EllipsoidalCS) + * + * @deprecated This type was defined in 2003 but removed in 2007. + * The ISO 19111 revision published in 2019 still excludes this type. + * The {@link GeodeticCRS} base type should be used instead. */ +@Deprecated(since="3.1") @UML(identifier="SC_GeocentricCRS", specification=ISO_19111, version=2003) public interface GeocentricCRS extends GeodeticCRS { /** diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/GeodeticCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/GeodeticCRS.java index 02082198c..bfd972b2f 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/GeodeticCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/GeodeticCRS.java @@ -17,6 +17,7 @@ */ package org.opengis.referencing.crs; +import org.opengis.referencing.datum.DatumEnsemble; import org.opengis.referencing.datum.GeodeticDatum; import org.opengis.annotation.UML; @@ -25,25 +26,54 @@ /** - * A 2- or 3-dimensional coordinate reference system associated with a geodetic datum. - * Geodetic CRSs provide an accurate representation of the geometry of geographic features - * for a large portion of the Earth's surface. + * A 2- or 3-dimensional CRS used over the whole planet or substantial parts of it. + * This is used to describe large portions of the planet's surface up to the entire planet's surface. + * If the geodetic reference frame is dynamic then the geodetic CRS is dynamic, else it is static. * - *

This type of CRS can be used with coordinate systems of type + *

Permitted coordinate systems

+ * This type of CRS can be used with coordinate systems of type * {@link org.opengis.referencing.cs.CartesianCS}, * {@link org.opengis.referencing.cs.SphericalCS} or - * {@link org.opengis.referencing.cs.EllipsoidalCS}.

+ * {@link org.opengis.referencing.cs.EllipsoidalCS}. + * In the latter case, the {@link GeographicCRS} specialization should be used. * - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 2.1 */ -@UML(identifier="SC_GeodeticCRS", specification=ISO_19111, version=2007) +@UML(identifier="GeodeticCRS", specification=ISO_19111) public interface GeodeticCRS extends SingleCRS { /** - * Returns the datum, which shall be geodetic. + * Returns the reference frame, which shall be geodetic. + * This property may be null if this CRS is related to an object + * identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @return the reference frame, or {@code null} if this CRS is related to + * an object identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @condition Mandatory if the {@linkplain #getDatumEnsemble() datum ensemble} is not documented. */ @Override - @UML(identifier="datum", obligation=MANDATORY, specification=ISO_19111) + @UML(identifier="datum", obligation=CONDITIONAL, specification=ISO_19111) GeodeticDatum getDatum(); + + /** + * Returns the datum ensemble, whose members shall be geodetic reference frames. + * This property may be null if this CRS is related to an object + * identified only by a single {@linkplain #getDatum() datum}. + * + *

The default implementation returns {@code null}.

+ * + * @return the datum ensemble, or {@code null} if this CRS is related + * to an object identified only by a single {@linkplain #getDatum() datum}. + * + * @condition Mandatory if the {@linkplain #getDatum() datum} is not documented. + * @since 3.1 + */ + @Override + @UML(identifier="datumEnsemble", obligation=CONDITIONAL, specification=ISO_19111) + default DatumEnsemble getDatumEnsemble() { + return null; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/GeographicCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/GeographicCRS.java index afed66c99..648d605b1 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/GeographicCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/GeographicCRS.java @@ -19,6 +19,7 @@ import java.util.Map; import org.opengis.referencing.cs.EllipsoidalCS; +import org.opengis.referencing.datum.Ellipsoid; import org.opengis.referencing.datum.GeodeticDatum; import org.opengis.annotation.UML; @@ -27,38 +28,31 @@ /** - * A 2- or 3-dimensional coordinate reference system based on an ellipsoidal approximation of the geoid. - * This provides an accurate representation of the geometry of geographic features for a large - * portion of the earth's surface. + * A 2- or 3-dimensional CRS based on an ellipsoidal approximation of the geoid. + * This provides an accurate representation of the geometry of geographic features for a large portion of the planet's surface. + * A 2D geographic CRS is used when positions of features are described on the surface of the reference ellipsoid. + * A 3D geographic CRS is used when positions are described on, above or below the reference ellipsoid. * - *

A Geographic CRS is not suitable for mapmaking on a planar surface, because it describes geometry - * on a curved surface. It is impossible to represent such geometry in a Euclidean plane without - * introducing distortions. The need to control these distortions has given rise to the development - * of the science of {@linkplain org.opengis.referencing.operation.Projection map projections}.

+ *

A geographic CRS is not suitable for mapmaking on a planar surface, + * because it describes geometry on a curved surface. + * It is impossible to represent such geometry in a Euclidean plane without introducing distortions. + * The need to control these distortions has given rise to the development of {@link ProjectedCRS}s.

* - *

This type of CRS can be used with coordinate systems of type - * {@link org.opengis.referencing.cs.EllipsoidalCS}.

+ *

The {@link GeodeticDatum} associated to this CRS must have an {@link Ellipsoid}. + * I.e., the ellipsoid is generally optional but become mandatory in the context of {@code GeographicCRS}.

* - * @departure constraint - * This interface is kept conformant with the specification published in 2003. The 2007 revision - * of ISO 19111 removed the {@code SC_GeographicCRS} and {@code SC_GeocentricCRS} types, - * handling both using the {@code SC_GeodeticCRS} parent type. GeoAPI keeps them for two reasons: - *
    - *
  • The distinction between those two types is in wide use.
  • - *
  • A distinct geographic type allows GeoAPI to restrict the coordinate system type to {@code EllipsoidalCS}. - * ISO 19111 uses a {@code union} for expressing this restriction at the {@code SC_GeodeticCRS} level, but - * the Java language does not provide such construct. A distinct geographic type is one way to achieve the - * same goal.
  • - *
+ *

Permitted coordinate systems

+ * This type of CRS can be used with coordinate systems of type {@link EllipsoidalCS} only. * - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 * * @see CRSAuthorityFactory#createGeographicCRS(String) * @see CRSFactory#createGeographicCRS(Map, GeodeticDatum, EllipsoidalCS) */ -@UML(identifier="SC_GeographicCRS", specification=ISO_19111, version=2003) +@UML(identifier="GeographicCRS", specification=ISO_19111) public interface GeographicCRS extends GeodeticCRS { /** * Returns the coordinate system, which shall be ellipsoidal. diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/ImageCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/ImageCRS.java index 0db329de5..771542b2c 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/ImageCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/ImageCRS.java @@ -28,7 +28,7 @@ /** - * A 2-dimensional engineering coordinate reference system applied to locations in images. + * A 2-dimensional engineering CRS applied to locations in images. * *
Note: * image coordinate reference systems are treated as a separate sub-type because a separate @@ -39,13 +39,16 @@ * {@link org.opengis.referencing.cs.CartesianCS} or * {@link org.opengis.referencing.cs.AffineCS}.

* - * @author Martin Desruisseaux (IRD) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.0 * @since 1.0 * * @see CRSAuthorityFactory#createImageCRS(String) * @see CRSFactory#createImageCRS(Map, ImageDatum, AffineCS) + * + * @deprecated Replaced by {@link EngineeringCRS} as of ISO 19111:2019. */ +@Deprecated(since="3.1") @UML(identifier="SC_ImageCRS", specification=ISO_19111, version=2007) public interface ImageCRS extends SingleCRS { /** diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/ParametricCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/ParametricCRS.java index 17abcd739..545c438b5 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/ParametricCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/ParametricCRS.java @@ -19,16 +19,17 @@ import java.util.Map; import org.opengis.referencing.cs.ParametricCS; +import org.opengis.referencing.datum.DatumEnsemble; import org.opengis.referencing.datum.ParametricDatum; import org.opengis.annotation.UML; -import static org.opengis.annotation.Obligation.MANDATORY; -import static org.opengis.annotation.Specification.ISO_19111_2; +import static org.opengis.annotation.Obligation.*; +import static org.opengis.annotation.Specification.ISO_19111; /** - * A 1-dimensional coordinate reference system which uses parameter values or functions. - * A coordinate reference system shall be of the parametric type if a physical or material + * A 1-dimensional CRS which uses parameter values or functions. + * A CRS shall be of the parametric type if a physical or material * property or function is used as the dimension. * The values or functions can vary monotonically with height. * @@ -37,17 +38,19 @@ * density (isopycnals) in oceanographic applications. *
* - *

This type of CRS can be used with coordinate systems of type - * {@link org.opengis.referencing.cs.ParametricCS}.

+ *

Permitted coordinate systems

+ * This type of CRS can be used with coordinate systems of type {@link ParametricCS} only. * + * @author OGC Topic 2 (for abstract model and documentation) * @author Johann Sorel (Geomatys) + * @author Martin Desruisseaux (Geomatys) * @version 3.1 * @since 3.1 * * @see CRSAuthorityFactory#createParametricCRS(String) * @see CRSFactory#createParametricCRS(Map, ParametricDatum, ParametricCS) */ -@UML(identifier="SC_ParametricCRS", specification=ISO_19111_2) +@UML(identifier="ParametricCRS", specification=ISO_19111) public interface ParametricCRS extends SingleCRS { /** * Returns the coordinate system, which shall be parametric. @@ -55,13 +58,38 @@ public interface ParametricCRS extends SingleCRS { * @return the parametric coordinate system. */ @Override - @UML(identifier="coordinateSystem", obligation=MANDATORY, specification=ISO_19111_2) + @UML(identifier="coordinateSystem", obligation=MANDATORY, specification=ISO_19111) ParametricCS getCoordinateSystem(); /** * Returns the datum, which shall be parametric. + * This property may be null if this CRS is related to an object + * identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @return the parametric datum, or {@code null} if this CRS is related to + * an object identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @condition Mandatory if the {@linkplain #getDatumEnsemble() datum ensemble} is not documented. */ @Override - @UML(identifier="datum", obligation=MANDATORY, specification=ISO_19111_2) + @UML(identifier="datum", obligation=CONDITIONAL, specification=ISO_19111) ParametricDatum getDatum(); + + /** + * Returns the datum ensemble, which shall have parametric datum members. + * This property may be null if this CRS is related to an object + * identified only by a single {@linkplain #getDatum() datum}. + * + *

The default implementation returns {@code null}.

+ * + * @return the datum ensemble, or {@code null} if this CRS is related + * to an object identified only by a single {@linkplain #getDatum() datum}. + * + * @condition Mandatory if the {@linkplain #getDatum() datum} is not documented. + */ + @Override + @UML(identifier="datum", obligation=CONDITIONAL, specification=ISO_19111) + default DatumEnsemble getDatumEnsemble() { + return null; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/ProjectedCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/ProjectedCRS.java index 02d32ee38..550dd5552 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/ProjectedCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/ProjectedCRS.java @@ -19,6 +19,7 @@ import java.util.Map; import org.opengis.referencing.cs.CartesianCS; +import org.opengis.referencing.datum.DatumEnsemble; import org.opengis.referencing.datum.GeodeticDatum; import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.Projection; @@ -29,56 +30,81 @@ /** - * A 2-dimensional coordinate reference system used to approximate the shape of the earth on a planar surface. - * It is done in such a way that the distortion that is inherent to the approximation is carefully - * controlled and known. Distortion correction is commonly applied to calculated bearings and - * distances to produce values that are a close match to actual field values. + * A 2- or 3-dimensional CRS based on an approximation of the shape of the planet's surface by a plane. + * It is done in such a way that the distortion that is inherent to the approximation is carefully controlled and known. + * Distortion correction is commonly applied to calculated bearings and distances + * to produce values that are a close match to actual field values. * - *

This type of CRS can be used with coordinate systems of type - * {@link org.opengis.referencing.cs.CartesianCS}.

+ *

Permitted coordinate systems

+ * This type of CRS can be used with coordinate systems of type {@link CartesianCS} only. * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 * * @see CRSAuthorityFactory#createProjectedCRS(String) * @see CRSFactory#createProjectedCRS(Map, GeographicCRS, Conversion, CartesianCS) */ -@UML(identifier="SC_ProjectedCRS", specification=ISO_19111, version=2007) -public interface ProjectedCRS extends GeneralDerivedCRS { +@UML(identifier="ProjectedCRS", specification=ISO_19111) +public interface ProjectedCRS extends DerivedCRS { /** - * Returns the base coordinate reference system, which must be geographic. + * Returns the CRS that is the base for this projected CRS. + * This is the {@linkplain Conversion#getSourceCRS() source CRS} + * of the {@linkplain #getConversionFromBase() deriving conversion}. * - * @return the base geographic CRS. + * @return the CRS that is the base for this projected CRS. */ @Override GeographicCRS getBaseCRS(); /** - * Returns the map projection from the base CRS to this CRS. + * Returns the map projection from the base CRS to this projected CRS. + * The source CRS of the conversion, if non null, shall be the {@linkplain #getBaseCRS() base CRS}. + * The target CRS of the conversion, if non-null, shall be this CRS. * - * @return the conversion from the {@linkplain #getBaseCRS() base CRS} to this projected CRS. + * @return the map projection from the base CRS to this projected CRS. */ @Override Projection getConversionFromBase(); /** * Returns the coordinate system, which shall be Cartesian. + * In the 3D case the ellipsoidal height from the base CRS + * is retained to form a three-dimensional Cartesian coordinate system. * - * @return the Cartesian coordinate system. + * @return the Cartesian coordinate system associated to this projected CRS. */ @Override @UML(identifier="coordinateSystem", obligation=MANDATORY, specification=ISO_19111) CartesianCS getCoordinateSystem(); /** - * Returns the same datum as the base CRS datum. + * Returns the same datum as the base geodetic CRS. + * This property may be null if the base CRS is related to an object + * identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. * - * @return the datum of this projected CRS, which is the {@linkplain #getBaseCRS() base CRS} datum. + * @return the datum of the base geodetic CRS, or {@code null} if the base is related + * to an object identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. */ @Override @UML(identifier="datum", obligation=MANDATORY, specification=ISO_19111) default GeodeticDatum getDatum() { return getBaseCRS().getDatum(); } + + /** + * Returns the same datum ensemble as the base geodetic CRS. + * This property may be null if the base CRS is related to an object + * identified only by a single {@linkplain #getDatum() datum}. + * + * @return the datum ensemble of the base geodetic CRS, or {@code null} if the base + * is related to an object identified only by a single {@linkplain #getDatum() datum}. + * + * @since 3.1 + */ + @Override + default DatumEnsemble getDatumEnsemble() { + return getBaseCRS().getDatumEnsemble(); + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/SingleCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/SingleCRS.java index c3442f725..696aea4c8 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/SingleCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/SingleCRS.java @@ -18,6 +18,7 @@ package org.opengis.referencing.crs; import org.opengis.referencing.datum.Datum; +import org.opengis.referencing.datum.DatumEnsemble; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.annotation.UML; import org.opengis.annotation.Classifier; @@ -28,42 +29,86 @@ /** - * Base type of {@linkplain CoordinateSystem coordinate systems} related to an object by a {@linkplain Datum datum}. - * For {@linkplain org.opengis.referencing.datum.GeodeticDatum geodetic} - * and {@linkplain org.opengis.referencing.datum.VerticalDatum vertical} datums, the object will be the Earth. + * Base type of CRS related to an object by a datum or datum ensemble. + * The object will generally, but not necessarily, be the Earth. It can be identified either + * by a {@linkplain #getDatum() datum} or a {@linkplain #getDatumEnsemble() datum ensemble}. + * At least one of those two properties shall be present. * - *

The valid coordinate system type and the datum type are constrained by the CRS type. - * For example, {@code GeographicCRS} can be associated only to {@code EllipsoidalCS} and - * {@code GeodeticDatum}.

+ *

The valid coordinate system type and the datum type are constrained by the CRS type. + * For example, {@link GeographicCRS} can be associated only to {@code GeodeticReferenceFrame} + * (a specialization of {@code Datum}) and {@code EllipsoidalCS}. + * The constraints are documented in the Javadoc of sub-interfaces.

* - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 2.0 * - * @see org.opengis.referencing.cs.CoordinateSystem - * @see org.opengis.referencing.datum.Datum + * @see CoordinateSystem + * @see Datum */ @Classifier(Stereotype.ABSTRACT) -@UML(identifier="SC_SingleCRS", specification=ISO_19111, version=2007) +@UML(identifier="SingleCRS", specification=ISO_19111) public interface SingleCRS extends CoordinateReferenceSystem { /** - * Returns the coordinate system associated to this CRS. + * Returns the coordinate system associated to this CRS. + * The coordinate system (CS) is composed of a set of coordinate axes with specified units of measure. + * The CS subtype implies the mathematical rules that define how coordinate values are calculated + * from distances, angles and other geometric elements and vice versa. + * + * @return the coordinate system associated to this CRS. */ @Override @UML(identifier="coordinateSystem", obligation=MANDATORY, specification=ISO_19111) CoordinateSystem getCoordinateSystem(); /** - * Returns the datum associated directly or indirectly to this CRS. - * In the case of {@link GeneralDerivedCRS}, this method returns the - * datum of the {@linkplain GeneralDerivedCRS#getBaseCRS() base CRS}. + * Returns the datum associated directly or indirectly to this CRS. + * In the case of {@link DerivedCRS}, this method returns the + * datum of the {@linkplain DerivedCRS#getBaseCRS() base CRS}. + * This property may be null if this CRS is related to an object + * identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + *

A datum specifies the relationship of a coordinate system to the object, thus ensuring that the abstract + * mathematical concept “coordinate system” can be applied to the practical problem of describing positions of + * features on or near the planet's surface by means of coordinates. + * The object will generally, but not necessarily, be the Earth. + * For certain CRSs, the object may be a moving platform.

* - * @return the datum. + * @return the datum, or {@code null} if this CRS is related to an object + * identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. * * @departure easeOfUse * The ISO specification declares the datum as absent when the association is indirect. * GeoAPI recommends to follow the link to the base CRS for users convenience. + * + * @condition Mandatory if the {@linkplain #getDatumEnsemble() datum ensemble} is not documented. */ @UML(identifier="datum", obligation=CONDITIONAL, specification=ISO_19111) Datum getDatum(); + + /** + * Returns the datum ensemble associated directly or indirectly to this CRS. + * In the case of {@link DerivedCRS}, this method returns the datum ensemble + * of the {@linkplain DerivedCRS#getBaseCRS() base CRS}. + * This property may be null if this CRS is related to an object + * identified only by a single {@linkplain #getDatum() datum}. + * + *

The default implementation returns {@code null}.

+ * + * @return the datum ensemble, or {@code null} if this CRS is related + * to an object identified only by a single {@linkplain #getDatum() datum}. + * + * @condition Mandatory if the {@linkplain #getDatum() datum} is not documented. + * @since 3.1 + */ + @UML(identifier="datumEnsemble", obligation=CONDITIONAL, specification=ISO_19111) + default DatumEnsemble getDatumEnsemble() { + /* + * API design note: we cannot use `Optional.empty()` because the use of `Optional>` + * as the return type prevents sub-interfaces to specialize the `` part with a more specific type. + * Anyway, this attribute is not exactly optional but conditional. + */ + return null; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/TemporalCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/TemporalCRS.java index efe166ff1..441edd527 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/TemporalCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/TemporalCRS.java @@ -19,6 +19,7 @@ import java.util.Map; import org.opengis.referencing.cs.TimeCS; +import org.opengis.referencing.datum.DatumEnsemble; import org.opengis.referencing.datum.TemporalDatum; import org.opengis.annotation.UML; @@ -27,19 +28,22 @@ /** - * A 1-dimensional coordinate reference system used for the recording of time. + * A 1-dimensional CRS used for the recording of time. + * Any CRS can be associate with a temporal CRS to form a spatio-temporal {@link CompoundCRS}. + * More than one temporal CRS may be included if these axes represent different time quantities. * - *

This type of CRS can be used with coordinate systems of type - * {@link org.opengis.referencing.cs.TimeCS}.

+ *

Permitted coordinate systems

+ * This type of CRS can be used with coordinate systems of type {@link TimeCS} only. * - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 * * @see CRSAuthorityFactory#createTemporalCRS(String) * @see CRSFactory#createTemporalCRS(Map, TemporalDatum, TimeCS) */ -@UML(identifier="SC_TemporalCRS", specification=ISO_19111) +@UML(identifier="TemporalCRS", specification=ISO_19111) public interface TemporalCRS extends SingleCRS { /** * Returns the coordinate system, which shall be temporal. @@ -52,8 +56,34 @@ public interface TemporalCRS extends SingleCRS { /** * Returns the datum, which shall be temporal. + * This property may be null if this CRS is related to an object + * identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @return the temporal datum, or {@code null} if this CRS is related to + * an object identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @condition Mandatory if the {@linkplain #getDatumEnsemble() datum ensemble} is not documented. */ @Override - @UML(identifier="datum", obligation=MANDATORY, specification=ISO_19111) + @UML(identifier="datum", obligation=CONDITIONAL, specification=ISO_19111) TemporalDatum getDatum(); + + /** + * Returns the datum ensemble, which shall have temporal datum members. + * This property may be null if this CRS is related to an object + * identified only by a single {@linkplain #getDatum() datum}. + * + *

The default implementation returns {@code null}.

+ * + * @return the datum ensemble, or {@code null} if this CRS is related + * to an object identified only by a single {@linkplain #getDatum() datum}. + * + * @condition Mandatory if the {@linkplain #getDatum() datum} is not documented. + * @since 3.1 + */ + @Override + @UML(identifier="datum", obligation=CONDITIONAL, specification=ISO_19111) + default DatumEnsemble getDatumEnsemble() { + return null; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/VerticalCRS.java b/geoapi/src/main/java/org/opengis/referencing/crs/VerticalCRS.java index 6d43dab0e..a63f6a6fc 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/VerticalCRS.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/VerticalCRS.java @@ -20,6 +20,7 @@ import java.util.Map; import org.opengis.referencing.cs.VerticalCS; import org.opengis.referencing.datum.VerticalDatum; +import org.opengis.referencing.datum.DatumEnsemble; import org.opengis.annotation.UML; import static org.opengis.annotation.Obligation.*; @@ -27,31 +28,26 @@ /** - * A 1-dimensional coordinate reference system used for recording heights or depths. Vertical CRSs make use - * of the direction of gravity to define the concept of height or depth, but the relationship with - * gravity may not be straightforward. + * A 1-dimensional CRS used for recording heights or depths. + * Vertical CRSs make use of the direction of gravity to define the concept of height or depth, + * but the relationship with gravity may not be straightforward. * - *

By implication, ellipsoidal heights (h) cannot be captured in a vertical coordinate - * reference system. Ellipsoidal heights cannot exist independently, but only as inseparable part - * of a 3D coordinate tuple defined in a geographic 3D coordinate reference system.

+ *

By implication, ellipsoidal heights (h) cannot be captured in a vertical CRS. + * Ellipsoidal heights cannot exist independently, but only as inseparable part of a 3D coordinate tuple + * defined in a geographic or projected 3D CRS.

* - *
Note: - * some applications may relax the above rule and accept ellipsoidal heights in some contexts. - * For example, as a transient state while parsing the legacy Well-Known Text version 1, - * or any other format based on legacy specifications where ellipsoidal heights were allowed as an - * independent axis. However, implementers are encouraged to assemble the full 3D CRS as soon as they can.
+ *

Permitted coordinate systems

+ * This type of CRS can be used with coordinate systems of type {@link VerticalCS} only. * - *

This type of CRS can be used with coordinate systems of type - * {@link org.opengis.referencing.cs.VerticalCS}.

- * - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 * * @see CRSAuthorityFactory#createVerticalCRS(String) * @see CRSFactory#createVerticalCRS(Map, VerticalDatum, VerticalCS) */ -@UML(identifier="SC_VerticalCRS", specification=ISO_19111, version=2007) +@UML(identifier="VerticalCRS", specification=ISO_19111) public interface VerticalCRS extends SingleCRS { /** * Returns the coordinate system, which shall be vertical. @@ -63,9 +59,35 @@ public interface VerticalCRS extends SingleCRS { VerticalCS getCoordinateSystem(); /** - * Returns the datum, which must be vertical. + * Returns the reference frame, which shall be vertical. + * This property may be null if this CRS is related to an object + * identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @return the reference frame, or {@code null} if this CRS is related to + * an object identified only by a {@linkplain #getDatumEnsemble() datum ensemble}. + * + * @condition Mandatory if the {@linkplain #getDatumEnsemble() datum ensemble} is not documented. */ @Override - @UML(identifier="datum", obligation=MANDATORY, specification=ISO_19111) + @UML(identifier="datum", obligation=CONDITIONAL, specification=ISO_19111) VerticalDatum getDatum(); + + /** + * Returns the datum ensemble, which shall have vertical datum members. + * This property may be null if this CRS is related to an object + * identified only by a single {@linkplain #getDatum() datum}. + * + *

The default implementation returns {@code null}.

+ * + * @return the datum ensemble, or {@code null} if this CRS is related + * to an object identified only by a single {@linkplain #getDatum() datum}. + * + * @condition Mandatory if the {@linkplain #getDatum() datum} is not documented. + * @since 3.1 + */ + @Override + @UML(identifier="datum", obligation=CONDITIONAL, specification=ISO_19111) + default DatumEnsemble getDatumEnsemble() { + return null; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/crs/package-info.java b/geoapi/src/main/java/org/opengis/referencing/crs/package-info.java index b2df3adfa..ff739462c 100644 --- a/geoapi/src/main/java/org/opengis/referencing/crs/package-info.java +++ b/geoapi/src/main/java/org/opengis/referencing/crs/package-info.java @@ -17,107 +17,39 @@ */ /** - * {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem Coordinate reference systems} - * ({@linkplain org.opengis.referencing.cs.CoordinateSystem coordinate systems} with a - * {@linkplain org.opengis.referencing.datum.Datum datum}). The following is adapted from - * {@linkplain org.opengis.annotation.Specification#ISO_19111 OpenGIS® Spatial Referencing by - * Coordinates (Topic 2)} specification. - * - *

A coordinate reference system (CRS) consists of one coordinate system (CS) that is related to the earth - * or platform through one datum. The coordinate system is composed of a set of coordinate axes with specified - * units of measure. This concept implies the mathematical rules that define how coordinate values are calculated - * from distances, angles and other geometric elements and vice versa.

- * - *

A datum specifies the relationship of a coordinate system to the object, thus ensuring that the abstract - * mathematical concept “coordinate system” can be applied to the practical problem of describing positions of - * features on or near the earth's surface by means of coordinates. The object will generally, but not necessarily, - * be the earth; for certain coordinate reference systems, the object may be a moving platform.

+ * Reference systems by coordinates. + * A {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem Coordinate Reference System} + * (CRS) generally consists of one {@linkplain org.opengis.referencing.cs Coordinate System} + * (a set of {@linkplain org.opengis.referencing.cs.CoordinateSystemAxis axes} with implied mathematical rules + * for calculating distances and angles from coordinates) that is related to the Earth, another celestial body + * or a platform through one {@linkplain org.opengis.referencing.datum datum} or datum ensemble. * *

{@code CoordinateReferenceSystem} instances and their components shall be immutable. - * For CRS defined on moving platforms such as cars, ships, aircraft and spacecraft, - * transformation to an earth-fixed coordinate reference system may include a time element. - * Time-variability of coordinate reference systems may be covered by creating different - * {@code CoordinateReferenceSystem} instances, each with a different datum, for consecutive epochs. - * The date of realization of the datum shall then be included in its definition. - * Furthermore, it is recommended that the date of realization be included in the names of those datums - * and coordinate reference systems.

- * - *

Sub-types of coordinate reference system

- *

Geodetic survey practice usually divides coordinate reference systems into a number of sub-types. - * The common classification criterion for sub-typing of coordinate reference systems can be described - * as the way in which they deal with earth curvature. This has a direct effect on the portion of the - * earth's surface that can be covered by that type of CRS with an acceptable degree of error. - * Thus the following principal sub-types of coordinate reference system are distinguished:

- * - *
- *

Geocentric: - * Type of coordinate reference system that deals with the earth's curvature by taking the 3D spatial view, - * which obviates the need to model the earth's curvature. - * The origin of a geocentric CRS is at the approximate centre of mass of the earth.

- * - *

Geographic: - * Type of coordinate reference system based on an ellipsoidal approximation of the geoid. - * This provides an accurate representation of the geometry of geographic features for a large portion - * of the earth's surface. Geographic coordinate reference systems can be 2D or 3D. - * A 2D Geographic CRS is used when positions of features are described on the surface of the reference ellipsoid; - * a 3D Geographic CRS is used when positions are described on, above or below the reference ellipsoid.

- * - *

Projected: - * Type of coordinate reference system that is based on an approximation of the shape of the earth's surface by a plane. - * The distortion that is inherent to the approximation is carefully controlled and known. - * Distortion correction is commonly applied to calculated bearings and distances - * to produce values that are a close match to actual field values.

- * - *

Engineering: - * Type of coordinate reference system that is that is used only in a contextually local sense. - * This sub-type is used to model two broad categories of local coordinate reference systems:

- *
    - *
  • earth-fixed systems, applied to engineering activities on or near the surface of the earth;
  • - *
  • coordinates on moving platforms such as road vehicles, vessels, aircraft or spacecraft.
  • - *
- * - *

Image: - * An Image CRS is an Engineering CRS applied to images. Image CRSs are treated as - * a separate sub-type because a separate user community exists for images with its - * own vocabulary. The definition of the associated Image Datum contains two data - * attributes not relevant for other datums and coordinate reference systems.

- * - *

Vertical: - * Type of coordinate reference system used for the recording of heights or depths. - * Vertical CRSs make use of the direction of gravity to define the concept of height or depth, - * but its relationship with gravity may not be straightforward. - * By implication ellipsoidal heights (h) cannot be captured in a vertical CRS. - * Ellipsoidal heights cannot exist independently, but only as inseparable part of a 3D coordinate tuple - * defined in a geographic 3D coordinate reference system.

- * - *

Temporal: - * Used for the recording of time in association with any of the listed spatial coordinate reference systems. - * Any CRS can be associate with a temporal CRS to form a spatio-temporal compound CRS. - * More than one temporal CRS may be included if these axes represent different time quantities.

- *
- * - *

In addition to the above principal sub-types, so called because they represent concepts generally known - * in geodetic practice, two more sub-types have been defined to permit modelling of certain relationships - * and constraints that exist between the principal sub-types.

- * - *
- *

Compound: - * The traditional separation of horizontal and vertical position has resulted in coordinate reference systems - * that are horizontal (2D) in nature and vertical (1D). It is established practice to combine the horizontal - * coordinates of a point with a height or depth from a different CRS. The coordinate reference system to which - * these 3D coordinates are referenced combines the separate horizontal and vertical coordinate reference systems - * of the horizontal and vertical coordinates. Such a CRS is called a compound CRS. - * It consists of an ordered sequence of the two or more single coordinate reference systems.

- * - *

Derived: - * Some coordinate reference systems are defined by applying a coordinate conversion to another CRS. - * Such a CRS is called a derived CRS and the coordinate reference system it was derived from - * by applying the conversion is called the source or base CRS. + * For CRS defined on moving platforms such as cars, ships, aircraft and spacecraft, + * transformation to a planet-fixed coordinate reference system may include a time element. + * For a dynamic CRS, locations on or near the surface of the planet will move + * within the CRS due to crustal motion or deformation, therefor data needs a + * {@linkplain org.opengis.coordinate.CoordinateMetadata#getCoordinateEpoch() coordinate epoch} + * in addition of the CRS. In both cases, time-variability is handled by + * coordinate operations rather than changes in the CRS definition.

+ * + *

Compound CRS

+ * The traditional separation of horizontal, vertical and temporal position has resulted in different interfaces + * for the horizontal (2D), vertical (1D) and temporal (1D) components. It is established practice to combine the + * horizontal coordinates of a point with a height or depth from a different CRS, and sometime a time. + * The CRS to which these 3D or 4D coordinates are referenced is called a compound + * coordinate reference system. + * + *

Derived CRS

+ * Some coordinate reference systems are defined by applying a coordinate conversion to another CRS. + * Such a CRS is called a derived coordinate reference system and the CRS + * it was derived from by applying the conversion is called the base CRS. * A coordinate conversion is an arithmetic operation with zero or more parameters that have defined values. - * The base CRS and derived CRS have the same datum.

- *
+ * The base CRS and derived CRS have the same datum or datum ensemble. + * Projected CRSs are special cases of derived CRSs. * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 */ diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/AffineCS.java b/geoapi/src/main/java/org/opengis/referencing/cs/AffineCS.java index 1e8f03ff4..861625083 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/AffineCS.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/AffineCS.java @@ -25,17 +25,17 @@ /** * A 2- or 3-dimensional coordinate system in Euclidean space with straight axes that are not necessarily orthogonal. * - *

This type of CS can be used by coordinate reference systems of type - * {@link org.opengis.referencing.crs.EngineeringCRS} or - * {@link org.opengis.referencing.crs.ImageCRS}.

+ *

This type of CS can be used by coordinate reference systems of type + * {@link org.opengis.referencing.crs.EngineeringCRS}.

* - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 2.0 * * @see CSFactory#createAffineCS(Map, CoordinateSystemAxis, CoordinateSystemAxis) * @see CSFactory#createAffineCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis) */ -@UML(identifier="CS_AffineCS", specification=ISO_19111, version=2007) +@UML(identifier="AffineCS", specification=ISO_19111) public interface AffineCS extends CoordinateSystem { } diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/AxisDirection.java b/geoapi/src/main/java/org/opengis/referencing/cs/AxisDirection.java index fea864b60..ff6925326 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/AxisDirection.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/AxisDirection.java @@ -29,18 +29,19 @@ * The direction of positive increase in the coordinate value for a coordinate system axis. * This direction is exact in some cases, and is approximate in other cases. * - *

Some coordinate systems use non-standard orientations. For example, the first axis in - * South African grids usually points West, instead of East. This information is obviously - * relevant for algorithms converting South African grid coordinates into Lat/Long.

+ *

Some coordinate systems use non-standard orientations. + * For example, the first axis in South African grids usually points West, instead of East. + * This information is relevant for algorithms converting South African grid coordinates into Lat/Long.

* - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 * * @see CoordinateSystemAxis#getDirection() */ -@Vocabulary(capacity=32) -@UML(identifier="CS_AxisDirection", specification=ISO_19111, version=2007) +@Vocabulary(capacity=41) +@UML(identifier="AxisDirection", specification=ISO_19111) public final class AxisDirection extends CodeList { /** * Serial number for compatibility with different versions. @@ -51,15 +52,18 @@ public final class AxisDirection extends CodeList { * Unknown or unspecified axis orientation. * * @category Other + * + * @deprecated Replaced by {@link #UNSPECIFIED} in ISO 19111:2019. */ - @UML(identifier="CS_AxisOrientationEnum.CS_AO_Other", specification=OGC_01009) + @Deprecated(since = "3.1") + @UML(identifier="CS_AxisOrientationEnum.CS_AO_Other", obligation=CONDITIONAL, specification=OGC_01009) public static final AxisDirection OTHER = new AxisDirection("OTHER"); /** - * Axis positive direction is north. In a geographic or projected CRS, - * north is defined through the geodetic datum. In an engineering CRS, - * north may be defined with respect to an engineering object rather - * than a geographical direction. + * Axis positive direction is north. + * In a geographic or projected CRS, north is defined through the geodetic reference frame. + * In an engineering CRS, north may be defined with respect to an engineering object + * rather than a geographical direction. * * @category Rose */ @@ -92,7 +96,7 @@ public final class AxisDirection extends CodeList { /** * Axis positive direction is π/2 radians clockwise from north. - * This is usually used for Grid X coordinates and Longitude. + * This is usually used for grid X coordinates and for longitude. * * @category Rose */ @@ -190,7 +194,7 @@ public final class AxisDirection extends CodeList { /** * Axis positive direction is up relative to gravity. - * This is used for {@linkplain VerticalCS vertical} coordinate systems. + * This is used for {@linkplain VerticalCS vertical coordinate systems}. * * @category Vertical */ @@ -199,7 +203,7 @@ public final class AxisDirection extends CodeList { /** * Axis positive direction is down relative to gravity. - * This is used for {@linkplain VerticalCS vertical} coordinate systems. + * This is used for {@linkplain VerticalCS vertical coordinate systems}. * * @category Vertical */ @@ -207,8 +211,9 @@ public final class AxisDirection extends CodeList { public static final AxisDirection DOWN = new AxisDirection("DOWN"); /** - * Axis positive direction is in the equatorial plane from the centre of the - * modelled earth towards the intersection of the equator with the prime meridian. + * Axis positive direction is toward geocentric X. + * This is the direction in the equatorial plane from the center of the modeled planet + * towards the intersection of the equator with the prime meridian. * * @category Geocentric */ @@ -216,9 +221,9 @@ public final class AxisDirection extends CodeList { public static final AxisDirection GEOCENTRIC_X = new AxisDirection("GEOCENTRIC_X"); /** - * Axis positive direction is in the equatorial plane from the centre of the - * modelled earth towards the intersection of the equator and the meridian π/2 - * radians eastwards from the prime meridian. + * Axis positive direction is toward geocentric Y. + * This is the direction in the equatorial plane from the center of the modeled planet + * towards the intersection of the equator and the meridian π/2 radians eastwards from the prime meridian. * * @category Geocentric */ @@ -226,32 +231,15 @@ public final class AxisDirection extends CodeList { public static final AxisDirection GEOCENTRIC_Y = new AxisDirection("GEOCENTRIC_Y"); /** - * Axis positive direction is from the centre of the modelled earth parallel to - * its rotation axis and towards its north pole. + * Axis positive direction is toward geocentric Z. + * This is the direction from the center of the modeled planet + * parallel to its rotation axis and towards its north pole. * * @category Geocentric */ @UML(identifier="geocentricZ", obligation=CONDITIONAL, specification=ISO_19111) public static final AxisDirection GEOCENTRIC_Z = new AxisDirection("GEOCENTRIC_Z"); - /** - * Axis positive direction is towards the future. - * This is used for {@linkplain TimeCS time} coordinate systems. - * - * @category Temporal - */ - @UML(identifier="future", obligation=CONDITIONAL, specification=ISO_19111) - public static final AxisDirection FUTURE = new AxisDirection("FUTURE"); - - /** - * Axis positive direction is towards the past. - * This is used for {@linkplain TimeCS time} coordinate systems. - * - * @category Temporal - */ - @UML(identifier="past", obligation=CONDITIONAL, specification=ISO_19111) - public static final AxisDirection PAST = new AxisDirection("PAST"); - /** * Axis positive direction is towards higher pixel column. * @@ -316,6 +304,109 @@ public final class AxisDirection extends CodeList { @UML(identifier="displayDown", obligation=CONDITIONAL, specification=ISO_19111) public static final AxisDirection DISPLAY_DOWN = new AxisDirection("DISPLAY_DOWN"); + /** + * Axis positive direction is forward. + * For an observer at the center of the object this will be towards its front, bow or nose. + * + * @category Engineering + * @since 3.1 + */ + @UML(identifier="forward", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection FORWARD = new AxisDirection("FORWARD"); + + /** + * Axis positive direction is aft. + * For an observer at the center of the object this will be towards its back, stern or tail. + * + * @category Engineering + * @since 3.1 + */ + @UML(identifier="aft", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection AFT = new AxisDirection("AFT"); + + /** + * Axis positive direction is port. + * For an observer looking forward from the center of the object this will be towards its left. + * + * @category Engineering + * @since 3.1 + */ + @UML(identifier="port", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection PORT = new AxisDirection("PORT"); + + /** + * Axis positive direction is starboard. + * For an observer looking forward from the center of the object this will be towards its right. + * + * @category Engineering + * @since 3.1 + */ + @UML(identifier="starboard", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection STARBOARD = new AxisDirection("STARBOARD"); + + /** + * Axis positive direction is clockwise from a specified direction. + * + * @category Engineering + * @since 3.1 + */ + @UML(identifier="clockwise", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection CLOCKWISE = new AxisDirection("CLOCKWISE"); + + /** + * Axis positive direction is counter clockwise from a specified direction. + * + * @category Engineering + * @since 3.1 + */ + @UML(identifier="counterClockwise", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection COUNTER_CLOCKWISE = new AxisDirection("COUNTER_CLOCKWISE"); + + /** + * Axis positive direction is towards the object. + * + * @category Engineering + * @since 3.1 + */ + @UML(identifier="towards", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection TOWARDS = new AxisDirection("TOWARDS"); + + /** + * Axis positive direction is away from the object. + * + * @category Engineering + * @since 3.1 + */ + @UML(identifier="awayFrom", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection AWAY_FROM = new AxisDirection("AWAY_FROM"); + + /** + * Axis positive direction is towards the future. + * This is used for {@linkplain TimeCS temporal coordinate systems}. + * + * @category Temporal + */ + @UML(identifier="future", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection FUTURE = new AxisDirection("FUTURE"); + + /** + * Axis positive direction is towards the past. + * This is used for {@linkplain TimeCS temporal coordinate systems}. + * + * @category Temporal + */ + @UML(identifier="past", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection PAST = new AxisDirection("PAST"); + + /** + * Axis positive direction is unspecified. + * + * @category Other + * @since 3.1 + */ + @UML(identifier="unspecified", obligation=CONDITIONAL, specification=ISO_19111) + public static final AxisDirection UNSPECIFIED = new AxisDirection("UNSPECIFIED"); + /** * Constructs an element of the given name. * diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/CSAuthorityFactory.java b/geoapi/src/main/java/org/opengis/referencing/cs/CSAuthorityFactory.java index 124ab316d..38a28c6ff 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/CSAuthorityFactory.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/CSAuthorityFactory.java @@ -32,7 +32,7 @@ * Creates {@linkplain CoordinateSystem coordinate systems} using authority codes. * External authorities are used to manage definitions of objects used in this interface. * The definitions of these objects are referenced using code strings. - * A commonly used authority is EPSG. + * A commonly used authority is the EPSG geodetic registry. * *

Default methods

* All {@code create(…)} methods in this interface are optional. @@ -248,8 +248,7 @@ default ParametricCS createParametricCS(final String code) throws FactoryExcepti * @deprecated This method is ambiguous. Use {@link #createCoordinateSystem(String)} instead. */ @Override - @SuppressWarnings("removal") - @Deprecated(since = "3.1", forRemoval = true) + @Deprecated(since = "3.1") default org.opengis.referencing.IdentifiedObject createObject(String code) throws FactoryException { return createCoordinateSystem(code); } diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/CSFactory.java b/geoapi/src/main/java/org/opengis/referencing/cs/CSFactory.java index b45fa23bb..caea7f96b 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/CSFactory.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/CSFactory.java @@ -324,7 +324,11 @@ default LinearCS createLinearCS(Map properties, * @param axis1 the second axis. * @return the coordinate system for the given properties and axes. * @throws FactoryException if the object creation failed. + * + * @deprecated User-defined CS needs their own constructor + * for instantiating their specialized type. */ + @Deprecated(since="3.1") default UserDefinedCS createUserDefinedCS(Map properties, CoordinateSystemAxis axis0, CoordinateSystemAxis axis1) throws FactoryException @@ -342,7 +346,11 @@ default UserDefinedCS createUserDefinedCS(Map properties, * @param axis2 the third axis. * @return the coordinate system for the given properties and axes. * @throws FactoryException if the object creation failed. + * + * @deprecated User-defined CS needs their own constructor + * for instantiating their specialized type. */ + @Deprecated(since="3.1") default UserDefinedCS createUserDefinedCS(Map properties, CoordinateSystemAxis axis0, CoordinateSystemAxis axis1, diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/CartesianCS.java b/geoapi/src/main/java/org/opengis/referencing/cs/CartesianCS.java index d4bec7965..c4067f5f1 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/CartesianCS.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/CartesianCS.java @@ -26,11 +26,10 @@ * A 2- or 3-dimensional coordinate system in Euclidean space with orthogonal straight axes. * All axes shall have the same length unit of measure. * - *

This type of CS can be used by coordinate reference systems of type + *

This type of CS can be used by coordinate reference systems of type * {@link org.opengis.referencing.crs.GeocentricCRS}, - * {@link org.opengis.referencing.crs.ProjectedCRS}, - * {@link org.opengis.referencing.crs.EngineeringCRS} or - * {@link org.opengis.referencing.crs.ImageCRS}. + * {@link org.opengis.referencing.crs.ProjectedCRS} or + * {@link org.opengis.referencing.crs.EngineeringCRS}. * The following examples describe some possible set of axes for Cartesian CS used with the above-cited CRS:

* * @@ -58,12 +57,13 @@ *
* * - * - * - * + * + * + * *
Example 4: used with an Engineering CRS for a moving platform
Axis name Abbr. Direction Unit
Aheadx {@code AxisDirection.valueOf("FORWARD")} metre
Righty {@code AxisDirection.valueOf("STARBOARD")} metre
Down z {@link AxisDirection#DOWN} metre
Aheadx {@link AxisDirection#FORWARD} metre
Righty {@link AxisDirection#STARBOARD} metre
Down z {@link AxisDirection#DOWN} metre
* - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 * @@ -71,6 +71,6 @@ * @see CSFactory#createCartesianCS(Map, CoordinateSystemAxis, CoordinateSystemAxis) * @see CSFactory#createCartesianCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis) */ -@UML(identifier="CS_CartesianCS", specification=ISO_19111, version=2007) +@UML(identifier="CartesianCS", specification=ISO_19111) public interface CartesianCS extends AffineCS { } diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateDataType.java b/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateDataType.java new file mode 100644 index 000000000..e2575a29d --- /dev/null +++ b/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateDataType.java @@ -0,0 +1,126 @@ +/* + * GeoAPI - Java interfaces for OGC/ISO standards + * Copyright © 2003-2024 Open Geospatial Consortium, Inc. + * http://www.geoapi.org + * + * Licensed 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.opengis.referencing.cs; + +import java.time.temporal.ChronoUnit; +import org.opengis.util.CodeList; +import org.opengis.annotation.UML; +import org.opengis.geoapi.internal.Vocabulary; + +import static org.opengis.annotation.Obligation.CONDITIONAL; +import static org.opengis.annotation.Specification.ISO_19111; + + +/** + * Type (measure, integer or date) of coordinate values. + * Most axes implicitly use the {@link #MEASURE} type. + * + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (Geomatys) + * @version 3.1 + * + * @see TimeCS#getCoordinateType() + * + * @since 3.1 + */ +@Vocabulary(capacity=3) +@UML(identifier="CoordinateDataType", specification=ISO_19111) +public final class CoordinateDataType extends CodeList { + /** + * Serial number for compatibility with different versions. + */ + private static final long serialVersionUID = -7295884834031271882L; + + /** + * Quantity expressed as a count used for a temporal or ordinal coordinate system axis. + * The fractional values between integer values may have no meaning. + */ + @UML(identifier="integer", obligation=CONDITIONAL, specification=ISO_19111) + public static final CoordinateDataType INTEGER = new CoordinateDataType("INTEGER"); + + /** + * Quantity expressed as a measure used for a temporal coordinate system axis. + * This is the usual type for coordinate system axes. + */ + @UML(identifier="measure", obligation=CONDITIONAL, specification=ISO_19111) + public static final CoordinateDataType MEASURE = new CoordinateDataType("MEASURE"); + + /** + * Numbers related to dates or times expressed in the proleptic Gregorian calendar. + * This is used for a temporal coordinate system axis. + * The time elapsed between two consecutive coordinate values may vary. + * + *

Examples

+ *

Coordinate values may be years expressed as a decimal year in the Gregorian calendar. + * For example, 2017-03-25 in the Gregorian calendar is epoch 2017.23. + * The number of days between two integer values may vary because of leap years. + * For a coordinate system using this convention, {@link TimeCS#getTemporalUnit()} + * should return {@link ChronoUnit#YEARS}.

+ * + *

Coordinate values may be a count of months since a given year. For example, 1 = January 2000, + * 2 = February 2000, 3 = March 2000, …, 13 = January 2001, 14 = February 2001, etc. + * The number of days between two integer values may very because of different month lengths. + * For a coordinate system using this convention, {@link TimeCS#getTemporalUnit()} + * should return {@link ChronoUnit#MONTHS}.

+ */ + @UML(identifier="dateTime", obligation=CONDITIONAL, specification=ISO_19111) + public static final CoordinateDataType DATE_TIME = new CoordinateDataType("DATE_TIME"); + + /** + * Constructs an element of the given name. + * + * @param name the name of the new element. This name shall not be in use by another element of this type. + */ + private CoordinateDataType(final String name) { + super(name); + } + + /** + * Returns the list of {@code CoordinateDataType}s. + * + * @return the list of codes declared in the current JVM. + */ + public static CoordinateDataType[] values() { + return values(CoordinateDataType.class); + } + + /** + * Returns the list of codes of the same kind as this code list element. + * Invoking this method is equivalent to invoking {@link #values()}, except that + * this method can be invoked on an instance of the parent {@code CodeList} class. + * + * @return all code {@linkplain #values() values} for this code list. + */ + @Override + public CoordinateDataType[] family() { + return values(); + } + + /** + * Returns the coordinate data type that matches the given string, or returns a new one if none match it. + * This methods returns the first instance (in declaration order) for which the {@linkplain #name() name} + * is {@linkplain String#equalsIgnoreCase(String) equals, ignoring case}, to the given name. + * If no existing instance is found, then a new one is created for the given name. + * + * @param code the name of the code to fetch or to create. + * @return a code matching the given name. + */ + public static CoordinateDataType valueOf(String code) { + return valueOf(CoordinateDataType.class, code, CoordinateDataType::new).get(); + } +} diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateSystem.java b/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateSystem.java index 93dcabd89..6564028f6 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateSystem.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateSystem.java @@ -27,15 +27,15 @@ /** - * The set of coordinate system axes that spans a given coordinate space. + * Sequence of non-repeating coordinate system axes that spans a given coordinate space. * A coordinate system (CS) is derived from a set of (mathematical) rules * for specifying how coordinates in a given space are to be assigned to points. * The coordinate values in a coordinate tuple shall be recorded - * in the order in which the coordinate system axes associations are recorded, - * whenever those coordinates use a coordinate reference system that uses this coordinate system. + * in the order in which the coordinate system axes associations are recorded. * - * @author Martin Desruisseaux (IRD) - * @version 3.0.1 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 * * @see org.opengis.referencing.cs.CoordinateSystemAxis @@ -43,7 +43,7 @@ * @see org.opengis.referencing.crs.CoordinateReferenceSystem */ @Classifier(Stereotype.ABSTRACT) -@UML(identifier="CS_CoordinateSystem", specification=ISO_19111, version=2007) +@UML(identifier="CoordinateSystem", specification=ISO_19111) public interface CoordinateSystem extends IdentifiedObject { /** * Returns the dimension of the coordinate system. diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateSystemAxis.java b/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateSystemAxis.java index 95d6402be..cad7d3031 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateSystemAxis.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/CoordinateSystemAxis.java @@ -28,47 +28,59 @@ /** * Definition of a coordinate system axis. + * Each axis is characterized by a unique combination of name, abbreviation, direction and unit. * *

Axis name

* Usage of coordinate system axis names is constrained by geodetic custom in a number of cases, - * depending mainly on the coordinate reference system type. These constraints are shown in the - * table below. This constraint works in two directions; for example the names geodetic - * latitude and geodetic longitude shall be used to designate the coordinate - * axis names associated with a geographic coordinate reference system. Conversely, these names - * shall not be used in any other context. + * depending mainly on the coordinate reference system type. These constraints are shown in the table below. + * This constraint works in two directions; for example the names geodetic latitude and geodetic longitude + * shall be used to designate the coordinate axis names associated with a geographic coordinate reference system. + * Conversely, these names shall not be used in any other context. * * * - * - * - * - * - * + * + * + * + * + * + * + * * - * + * + * + * + * + * + * + * * - * - * - * + * *
Context of coordinate system axis names usage
CSCRSPermitted coordinate system axis names
CartesianGeocentricGeocentric X, - * Geocentric Y, - * Geocentric Z
SphericalGeocentricSpherical Latitude, - * Spherical Longitude, - * Geocentric Radius
CSCRSPermitted coordinate system axis names
CartesianGeodetic“geocentric X”, + * “geocentric Y”, + * “geocentric Z
CartesianProjected“easting” or “westing”, + * “northing” or “southing”, + * “ellipsoidal height” (if 3D)
EllipsoidalGeographicGeodetic Latitude, - * Geodetic Longitude, - * Ellipsoidal height (if 3D)
“geodetic latitude”, + * “geodetic longitude”, + * “ellipsoidal height” (if 3D)
SphericalGeodetic“spherical latitude”, + * “spherical longitude”, + * “geocentric radius” (if 3D)
“geocentric latitude”, + * “geodetic longitude”, + * “geocentric radius” (if 3D)
“geocentric co-latitude”, + * “geodetic longitude”, + * “geocentric radius” (if 3D)
VerticalVerticalGravity-related height or Depth
CartesianProjectedEasting or Westing, - * Northing or Southing
“depth” or “gravity-related height”
* - * Image and engineering coordinate reference systems may make use of names specific to the - * local context or custom and are therefore not included as constraints in the above list. + * Parametric, temporal and engineering coordinate reference systems may make use of names specific + * to the local context or custom and are therefore not included as constraints in the above list. * *

Axis direction

- * The {@linkplain #getDirection() direction} of the coordinate axes is often only approximate; - * two geographic coordinate reference systems will make use of the same ellipsoidal coordinate - * system. These coordinate systems are associated with the earth through two different geodetic - * datums, which may lead to the two systems being slightly rotated with respect to each other. + * The {@linkplain #getDirection() direction} of the coordinate axes is often only approximate. + * Two geographic coordinate reference systems will make use of the same ellipsoidal coordinate system. + * These coordinate systems are associated with the planet through two different geodetic reference frames, + * which may lead to the two systems being slightly rotated with respect to each other. * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 * @@ -76,7 +88,7 @@ * @see CSAuthorityFactory#createCoordinateSystemAxis(String) * @see CSFactory#createCoordinateSystemAxis(Map, String, AxisDirection, Unit) */ -@UML(identifier="CS_CoordinateSystemAxis", specification=ISO_19111, version=2007) +@UML(identifier="CoordinateSystemAxis", specification=ISO_19111) public interface CoordinateSystemAxis extends IdentifiedObject { /** * Returns the abbreviation used for this coordinate system axes. @@ -97,9 +109,9 @@ public interface CoordinateSystemAxis extends IdentifiedObject { * {@linkplain AxisDirection#UP up} or {@linkplain AxisDirection#DOWN down}. * *

Within any set of coordinate system axes, only one of each pair of terms can be used. - * For earth-fixed coordinate reference systems, this direction is often approximate + * For planet-fixed coordinate reference systems, this direction is often approximate * and intended to provide a human interpretable meaning to the axis. - * When a geodetic datum is used, the precise directions of the axes may therefore + * When a geodetic reference frame is used, the precise directions of the axes may therefore * vary slightly from this approximate direction.

* *

Note that an {@link org.opengis.referencing.crs.EngineeringCRS} often requires @@ -116,14 +128,20 @@ public interface CoordinateSystemAxis extends IdentifiedObject { * whenever those coordinates use a coordinate reference system that uses a coordinate system * that uses this axis. * + *

Obligation

+ * This element may be {@code null} if this axis is part of an ordinal CS + * (a coordinate system in which axes use {@linkplain CoordinateDataType#INTEGER integer values}) + * and in the case of a {@link TimeCS} using {@linkplain CoordinateDataType#DATE_TIME date-time}. + * This property is mandatory for all other cases. + * * @return the coordinate system axis unit. */ - @UML(identifier="axisUnitID", obligation=MANDATORY, specification=ISO_19111) + @UML(identifier="axisUnitID", obligation=CONDITIONAL, specification=ISO_19111) Unit getUnit(); /** - * Returns the minimum value normally allowed for this axis, - * in the {@linkplain #getUnit() unit of measure for the axis}. + * Returns the minimum value normally allowed for this axis. + * The value shall be in the {@linkplain #getUnit() unit of measure for the axis}. * If there is no minimum value, then this method returns * {@linkplain Double#NEGATIVE_INFINITY negative infinity}. * @@ -135,8 +153,8 @@ default double getMinimumValue() { } /** - * Returns the maximum value normally allowed for this axis, - * in the {@linkplain #getUnit() unit of measure for the axis}. + * Returns the maximum value normally allowed for this axis. + * The value shall be in the {@linkplain #getUnit() unit of measure for the axis}. * If there is no maximum value, then this method returns * {@linkplain Double#POSITIVE_INFINITY positive infinity}. * @@ -148,11 +166,10 @@ default double getMaximumValue() { } /** - * Returns the meaning of axis value range specified by the {@linkplain #getMinimumValue() - * minimum} and {@linkplain #getMaximumValue() maximum} values. This element shall be omitted - * when both minimum and maximum values are omitted. It may be included when minimum and/or - * maximum values are included. If this element is omitted when minimum or maximum values are - * included, the meaning is unspecified. + * Returns the meaning of axis value range specified by the minimum and maximum values. + * This element shall be omitted when both minimum and maximum values are omitted. + * It may be included when minimum and/or maximum values are included. + * If this element is omitted when minimum or maximum values are included, the meaning is unspecified. * * @return the range meaning, or {@code null} in none. * diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/CylindricalCS.java b/geoapi/src/main/java/org/opengis/referencing/cs/CylindricalCS.java index e06cced3e..2e118e880 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/CylindricalCS.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/CylindricalCS.java @@ -23,20 +23,30 @@ /** - * A 3-dimensional coordinate system consisting of a {@link PolarCS} extended by a straight axis perpendicular - * to the plane spanned by the polar CS. + * A 3-dimensional cylindrical coordinate system. + * It consists of a {@link PolarCS} extended by a straight axis perpendicular to the plane spanned by the polar CS. * - *

This type of CS can be used by coordinate reference systems of type + *

This type of CS can be used by coordinate reference systems of type * {@link org.opengis.referencing.crs.EngineeringCRS}.

* - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 * * @see PolarCS * @see CSAuthorityFactory#createCylindricalCS(String) * @see CSFactory#createCylindricalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis) */ -@UML(identifier="CS_CylindricalCS", specification=ISO_19111, version=2007) +@UML(identifier="CylindricalCS", specification=ISO_19111) public interface CylindricalCS extends CoordinateSystem { + /** + * Returns the number of dimensions, which is 3 for this type of coordinate system. + * + * @return always 3. + */ + @Override + default int getDimension() { + return 3; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/EllipsoidalCS.java b/geoapi/src/main/java/org/opengis/referencing/cs/EllipsoidalCS.java index b356cf531..e0f77bdf2 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/EllipsoidalCS.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/EllipsoidalCS.java @@ -23,10 +23,10 @@ /** - * A 2- or 3-dimensional coordinate system in which position is specified by - * geodetic latitude, geodetic longitude, and (in the 3D case) ellipsoidal height. + * A 2- or 3-dimensional coordinate system for use with geodetic CRS. + * Position is specified by geodetic latitude, geodetic longitude, and (in the 3D case) ellipsoidal height. * - *

This type of CS can be used by coordinate reference systems of type + *

This type of CS can be used by coordinate reference systems of type * {@link org.opengis.referencing.crs.GeographicCRS}. * The following examples describe some possible set of axes for ellipsoidal CS used with the above-cited CRS:

* @@ -45,7 +45,8 @@ * Ellipsoidal heighth {@link AxisDirection#UP} metre * * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 * @@ -53,6 +54,6 @@ * @see CSFactory#createEllipsoidalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis) * @see CSFactory#createEllipsoidalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis) */ -@UML(identifier="CS_EllipsoidalCS", specification=ISO_19111, version=2007) +@UML(identifier="EllipsoidalCS", specification=ISO_19111) public interface EllipsoidalCS extends CoordinateSystem { } diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/LinearCS.java b/geoapi/src/main/java/org/opengis/referencing/cs/LinearCS.java index 17ecb0958..cd4497ae7 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/LinearCS.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/LinearCS.java @@ -31,15 +31,25 @@ * usage of the line feature representing a road to describe points on or along that road. * * - *

This type of CS can be used by coordinate reference systems of type + *

This type of CS can be used by coordinate reference systems of type * {@link org.opengis.referencing.crs.EngineeringCRS}.

* - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 1.0 * * @see CSFactory#createLinearCS(Map, CoordinateSystemAxis) */ -@UML(identifier="CS_LinearCS", specification=ISO_19111, version=2007) +@UML(identifier="LinearCS", specification=ISO_19111) public interface LinearCS extends CoordinateSystem { + /** + * Returns the number of dimensions, which is 1 for this type of coordinate system. + * + * @return always 1. + */ + @Override + default int getDimension() { + return 1; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/ParametricCS.java b/geoapi/src/main/java/org/opengis/referencing/cs/ParametricCS.java index e55c43822..74c045940 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/ParametricCS.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/ParametricCS.java @@ -20,23 +20,35 @@ import java.util.Map; import org.opengis.annotation.UML; -import static org.opengis.annotation.Specification.ISO_19111_2; +import static org.opengis.annotation.Specification.ISO_19111; /** - * A 1-dimensional coordinate system containing a single axis. + * A 1-dimensional coordinate system in which a physical property or function is used as the dimension. * This coordinate system uses parameter values or functions to describe the position of a point. + * That parameter is not inherently spatial. It may be for example the atmospheric pressure. * - *

This type of CS can be used by coordinate reference systems of type + *

This type of CS can be used by coordinate reference systems of type * {@link org.opengis.referencing.crs.ParametricCRS}.

* + * @author OGC Topic 2 (for abstract model and documentation) * @author Johann Sorel (Geomatys) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 3.1 * * @see CSAuthorityFactory#createParametricCS(String) * @see CSFactory#createParametricCS(Map, CoordinateSystemAxis) */ -@UML(identifier="CS_ParametricCS", specification=ISO_19111_2) +@UML(identifier="ParametricCS", specification=ISO_19111) public interface ParametricCS extends CoordinateSystem { + /** + * Returns the number of dimensions, which is 1 for this type of coordinate system. + * + * @return always 1. + */ + @Override + default int getDimension() { + return 1; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/PolarCS.java b/geoapi/src/main/java/org/opengis/referencing/cs/PolarCS.java index 1c96d9e5e..7065428dc 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/PolarCS.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/PolarCS.java @@ -23,25 +23,23 @@ /** - * A 2-dimensional coordinate system in which position is specified by the distance from the - * origin and the angle between the line from the origin to a point and a reference direction. + * A 2-dimensional polar coordinate system. + * Position is specified by the distance from the origin and the angle + * between the line from the origin to a point and a reference direction. * - *

This type of CS can be used by coordinate reference systems of type + *

This type of CS can be used by coordinate reference systems of type * {@link org.opengis.referencing.crs.EngineeringCRS}. * The following examples describe some possible set of axes for polar CS used with the above-cited CRS:

* * * * - * - * + * + * *
Example: used with an Engineering CRS
Axis name Abbr. Direction Unit
Distance r {@code AxisDirection.valueOf("AWAY_FROM")} metre
Bearing Θ {@code AxisDirection.valueOf("CLOCKWISE")} degree
Distance r {@link AxisDirection#AWAY_FROM} metre
Bearing Θ {@link AxisDirection#CLOCKWISE} degree
* - *
Note: - * the above example uses two axis directions that are not defined in ISO 19111, - * but found in ISO 19162 as "{@code awayFrom}" and "{@code clockwise}".
- * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 * @@ -49,6 +47,15 @@ * @see CSAuthorityFactory#createPolarCS(String) * @see CSFactory#createPolarCS(Map, CoordinateSystemAxis, CoordinateSystemAxis) */ -@UML(identifier="CS_PolarCS", specification=ISO_19111, version=2007) +@UML(identifier="PolarCS", specification=ISO_19111) public interface PolarCS extends CoordinateSystem { + /** + * Returns the number of dimensions, which is 2 for this type of coordinate system. + * + * @return always 2. + */ + @Override + default int getDimension() { + return 2; + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/RangeMeaning.java b/geoapi/src/main/java/org/opengis/referencing/cs/RangeMeaning.java index e5e9db7dc..eff7a924a 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/RangeMeaning.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/RangeMeaning.java @@ -28,14 +28,17 @@ /** * Meaning of the axis value range specified through minimum value and maximum value. * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (Geomatys) * @version 3.1 * @since 2.1 * + * @see CoordinateSystemAxis#getMinimumValue() + * @see CoordinateSystemAxis#getMaximumValue() * @see CoordinateSystemAxis#getRangeMeaning() */ @Vocabulary(capacity=2) -@UML(identifier="CS_RangeMeaning", specification=ISO_19111, version=2007) +@UML(identifier="RangeMeaning", specification=ISO_19111) public final class RangeMeaning extends CodeList { /** * Serial number for compatibility with different versions. @@ -43,25 +46,23 @@ public final class RangeMeaning extends CodeList { private static final long serialVersionUID = -3525560558294789416L; /** - * Any value between and including {@linkplain CoordinateSystemAxis#getMinimumValue() minimum value} - * and {@linkplain CoordinateSystemAxis#getMaximumValue() maximum value} is valid. + * Any value between and including minimum and maximum value is valid. */ @UML(identifier="exact", obligation=CONDITIONAL, specification=ISO_19111) public static final RangeMeaning EXACT = new RangeMeaning("EXACT"); /** - * The axis is continuous with values wrapping around at the - * {@linkplain CoordinateSystemAxis#getMinimumValue() minimum value} and - * {@linkplain CoordinateSystemAxis#getMaximumValue() maximum value}. + * The axis is continuous with values wrapping around at the minimum and maximum value. * Values with the same meaning repeat modulo the difference between maximum value and * minimum value. * - *
Example: in a geographic CRS, longitude values are - * often defined with a finite extent (e.g., from -180 degrees to +180 degrees). The minimum - * and maximum longitude limits define a single line (on the ellipsoid, sphere, or cylinder), - * known as the anti-meridian, across which longitude values are discontinuous: as this line - * is crossed, longitude changes abruptly (e.g., going West from a little more than -180° to - * a little less than +180°).
+ *

Example

+ * In a geographic CRS, longitude values are often defined with a finite extent + * (e.g., from -180 degrees to +180 degrees). The minimum and maximum longitude limits define + * a single line (on the ellipsoid, sphere, or cylinder), known as the anti-meridian, + * across which longitude values are discontinuous: + * as this line is crossed, longitude changes abruptly + * (e.g., going West from a little more than -180° to a little less than +180°). */ @UML(identifier="wraparound", obligation=CONDITIONAL, specification=ISO_19111) public static final RangeMeaning WRAPAROUND = new RangeMeaning("WRAPAROUND"); diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/SphericalCS.java b/geoapi/src/main/java/org/opengis/referencing/cs/SphericalCS.java index 1b56eeca4..4652b5c97 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/SphericalCS.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/SphericalCS.java @@ -27,7 +27,7 @@ * In the two-dimensional case, the radius is omitted and may be implicitly an ellipsoid surface. * Not to be confused with an {@link EllipsoidalCS} based on an ellipsoid "degenerated" into a sphere. * - *

This type of CS can be used by coordinate reference systems of type + *

This type of CS can be used by coordinate reference systems of type * {@link org.opengis.referencing.crs.GeocentricCRS} or * {@link org.opengis.referencing.crs.EngineeringCRS}. * The following examples describe some possible set of axes for spherical CS used with the above-cited CRS:

@@ -43,22 +43,19 @@ * * * - * - * - * + * + * + * *
Example 2: used with an Engineering CRS
Axis name Abbr. Direction Unit
Distance r {@code AxisDirection.valueOf("AWAY_FROM")} kilometre
Longitude φ {@code AxisDirection.valueOf("COUNTER_CLOCKWISE")} degree
Elevation Θ {@link AxisDirection#UP} degree
Distance r {@link AxisDirection#AWAY_FROM} kilometre
Longitude φ {@link AxisDirection#COUNTER_CLOCKWISE} degree
Elevation Θ {@link AxisDirection#UP} degree
* - *
Note: - * the above example uses two axis directions that are not defined in ISO 19111, - * but found in ISO 19162 as "{@code awayFrom}" and "{@code counterClockwise}".
- * - * @author Martin Desruisseaux (IRD) + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.1 * @since 1.0 * * @see CSAuthorityFactory#createSphericalCS(String) * @see CSFactory#createSphericalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis) */ -@UML(identifier="CS_SphericalCS", specification=ISO_19111, version=2007) +@UML(identifier="SphericalCS", specification=ISO_19111) public interface SphericalCS extends CoordinateSystem { } diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/TimeCS.java b/geoapi/src/main/java/org/opengis/referencing/cs/TimeCS.java index 857639b84..a9fbde3c2 100644 --- a/geoapi/src/main/java/org/opengis/referencing/cs/TimeCS.java +++ b/geoapi/src/main/java/org/opengis/referencing/cs/TimeCS.java @@ -1,6 +1,6 @@ /* * GeoAPI - Java interfaces for OGC/ISO standards - * Copyright © 2004-2023 Open Geospatial Consortium, Inc. + * Copyright © 2004-2024 Open Geospatial Consortium, Inc. * http://www.geoapi.org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,25 +18,101 @@ package org.opengis.referencing.cs; import java.util.Map; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; +import javax.measure.quantity.Time; import org.opengis.annotation.UML; + +import static org.opengis.annotation.Obligation.*; import static org.opengis.annotation.Specification.*; /** * A 1-dimensional coordinate system containing a single time axis. * This coordinate system is used to describe the temporal position of a point - * in the specified time units from a specified time origin. + * in the specified time units from a time origin implied by the temporal datum. + * ISO 19111 defines three forms of temporal coordinate system: + * + *
    + *
  • Measure of a temporal quantity as real numbers.
  • + *
  • Count of a temporal quantity as integer numbers.
  • + *
  • Numbers related to dates or times expressed in the proleptic Gregorian calendar.
  • + *
+ * + * Those forms are specified by {@link #getCoordinateType()}. + * GeoAPI does not define specialized sub-interfaces for each form. * *

This type of CS can be used by coordinate reference systems of type * {@link org.opengis.referencing.crs.TemporalCRS}.

* - * @author Martin Desruisseaux (IRD) - * @version 3.0 + * @author OGC Topic 2 (for abstract model and documentation) + * @author Martin Desruisseaux (IRD, Geomatys) + * @version 3.1 * @since 2.0 * * @see CSAuthorityFactory#createTimeCS(String) * @see CSFactory#createTimeCS(Map, CoordinateSystemAxis) + * + * @departure integration + * ISO 19111:2019 renamed this interface from {@code TimeCS} to {@code TemporalCS}. + * GeoAPI has kept the ISO 19111:2007 interface name for historical reasons, + * but also to emphasize the relationship with the {@link java.time} package. */ -@UML(identifier="CS_TimeCS", specification=ISO_19111, version=2007) +@UML(identifier="TemporalCS", specification=ISO_19111) public interface TimeCS extends CoordinateSystem { + /** + * Returns the number of dimensions, which is 1 for this type of coordinate system. + * + * @return always 1. + */ + @Override + default int getDimension() { + return 1; + } + + /** + * Returns the type (measure, integer or data-time) of coordinate values. + * If the time interval between two integer values is constant and interpolations are allowed, + * then the returned value will typically be {@link CoordinateDataType#MEASURE}. + * If the time interval is not constant but can be related to some field of the Gregorian calendar + * (e.g., months or years), then the returned value can be {@link CoordinateDataType#DATE_TIME}. + * + * @return the type of coordinate values. + * + * @since 3.1 + */ + @UML(identifier="coordinateType", obligation=MANDATORY, specification=ISO_19111) + CoordinateDataType getCoordinateType(); + + /** + * Returns the unit of measurement of coordinate values. The returned unit is equivalent to the unit + * returned by {@link CoordinateSystemAxis#getUnit()}, but expressed as a {@link java.time} object, + * preferably an instance from the {@link ChronoUnit} enumeration. + * + *

Note that contrarily to the usual units of measurement, the duration of {@link java.time} units can vary. + * For example {@link ChronoUnit#DAYS} is estimated to be about 24 hours long, because the actual + * duration can vary due to daylight saving time changes. Implementations can specify whether the temporal + * unit is estimated or exact with {@link TemporalUnit#isDurationEstimated()}.

+ * + *

Default implementation

+ * The default implementation returns a {@link ChronoUnit} or a multiple of a {@code ChronoUnit} with a + * {@linkplain ChronoUnit#getDuration() duration} equals to 1 {@linkplain CoordinateSystemAxis#getUnit() axis unit}. + * If and only if the {@linkplain #getCoordinateType() coordinate type} is {@link CoordinateDataType#DATE_TIME}, + * the search for {@link ChronoUnit} is relaxed with an arbitrary tolerance of one hour per month for units + * equal or longer than {@link ChronoUnit#MONTHS}. + * The intend is to accept different definitions of years, for example, 365.2425 days according Java + * versus 365.24219265 days according the IUGS definition of tropical year. + * + * @return unit measurement of coordinate values, preferably as a {@link ChronoUnit} enumeration value. + * + * @see ChronoUnit#SECONDS + * @see ChronoUnit#DAYS + * @see ChronoUnit#YEARS + * + * @since 3.1 + */ + default TemporalUnit getTemporalUnit() { + return TimeUnit.valueOf(getAxis(0).getUnit().asType(Time.class), + CoordinateDataType.DATE_TIME.equals(getCoordinateType())); + } } diff --git a/geoapi/src/main/java/org/opengis/referencing/cs/TimeUnit.java b/geoapi/src/main/java/org/opengis/referencing/cs/TimeUnit.java new file mode 100644 index 000000000..59366c333 --- /dev/null +++ b/geoapi/src/main/java/org/opengis/referencing/cs/TimeUnit.java @@ -0,0 +1,287 @@ +/* + * GeoAPI - Java interfaces for OGC/ISO standards + * Copyright © 2024 Open Geospatial Consortium, Inc. + * http://www.geoapi.org + * + * Licensed 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.opengis.referencing.cs; + +import java.util.Map; +import java.util.TreeMap; +import java.util.Arrays; +import java.io.Serializable; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalUnit; +import javax.measure.Unit; +import javax.measure.quantity.Time; +import javax.measure.UnconvertibleException; + + +/** + * Mapping from {@link javax.measure} API to {@link java.time} API. + * This implementation delegates practically all work to {@link ChronoUnit}. + * The code in this class is mostly about choosing which {@link ChronoUnit} + * seems the best match for a given unit of measurement. + * + * @author Martin Desruisseaux (Geomatys) + * @version 3.1 + * @since 3.1 + */ +final class TimeUnit implements TemporalUnit, Serializable { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = -6300465049394758570L; + + /** + * The duration of all supported temporal units, in seconds. + * Elements in this array must be sorted in increasing order. + */ + private static final double[] DURATIONS; + + /** + * Tolerance in seconds when comparing durations. + * The {@link ChronoUnit} API considers all durations equal or greater than one day as estimated rather than exact. + * Therefor, it is not a violation of {@link java.time} API if the tolerance threshold is relaxed for those units. + * This class tolerates a difference of one hour per month. The intend is to accept different definitions of years, + * e.g., 365.2425 days according Java versus 365.24219265 days according the IUGS definition of tropical year. + */ + private static final double[] TOLERANCES; + + /** + * The temporal units corresponding to each element in the {@link #DURATIONS} array. + * This array has the same length as {@link #DURATIONS} and {@link #TOLERANCES}. + */ + private static final ChronoUnit[] UNITS; + + /** + * Initializes the static fields. + */ + static { + final var m = new TreeMap(); + for (final ChronoUnit unit : ChronoUnit.values()) { + if (unit.isDateBased() || unit.isTimeBased()) { + final Duration duration = unit.getDuration(); + m.put(duration.getSeconds() + duration.getNano() / 1E+9, unit); + } + } + int i = m.size(); + DURATIONS = new double[i]; + TOLERANCES = new double[i]; + UNITS = new ChronoUnit[i]; + i = 0; + double tolerance = 0; + for (final Map.Entry e : m.entrySet()) { + switch (UNITS[i] = e.getValue()) { + case MILLENNIA: + case CENTURIES: + case DECADES: tolerance *= 10; break; + case YEARS: tolerance *= 12; break; + case MONTHS: tolerance = 60*60; break; + } + DURATIONS [i] = e.getKey(); + TOLERANCES[i] = (tolerance != 0 && UNITS[i].isDurationEstimated()) ? tolerance : 2*Math.ulp(DURATIONS[i]); + i++; + } + } + + /** + * The duration of this {@code TimeUnit}, as an amount of a {@code ChronoUnit}. + * Shall be a number greater than 1. + */ + private final long duration; + + /** + * The unit of the duration. + */ + private final ChronoUnit unit; + + /** + * Creates a new temporal unit as an integer amount of a standard {@code ChronoUnit}. + * + * @param duration duration in amount of {@code unit}. + * @param unit unit of the {@code duration} argument. + */ + private TimeUnit(final long duration, final ChronoUnit unit) { + this.duration = duration; + this.unit = unit; + } + + /** + * Searches for a temporal unit equivalent to the given unit of measurement. + * If the duration of the given unit is equal to the duration of one of the {@link ChronoUnit}s, + * then that unit is returned. Otherwise, if the duration can be expressed as a multiple of one + * of the {@link ChronoUnit}s, then the multiple and the unit are wrapped in a {@code TimeUnit}. + * + * @param unit the unit of measurement to map. + * @param lenient whether to use an arbitrary tolerance threshold. + * @return the temporal unit equivalent to the given unit of measurement. + * @throws ArithmeticException if the given unit overflows or underflows the capability of the {@link java.time} API. + */ + static TemporalUnit valueOf(final Unit