001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    Copyright © 2003-2024 Open Geospatial Consortium, Inc.
004 *    http://www.geoapi.org
005 *
006 *    Licensed under the Apache License, Version 2.0 (the "License");
007 *    you may not use this file except in compliance with the License.
008 *    You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *    Unless required by applicable law or agreed to in writing, software
013 *    distributed under the License is distributed on an "AS IS" BASIS,
014 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 *    See the License for the specific language governing permissions and
016 *    limitations under the License.
017 */
018package org.opengis.referencing.operation;
019
020import java.util.Optional;
021import java.util.Collection;
022import java.util.Collections;
023import java.time.temporal.Temporal;
024import org.opengis.coordinate.CoordinateSet;
025import org.opengis.referencing.IdentifiedObject;
026import org.opengis.referencing.datum.Datum;
027import org.opengis.referencing.datum.DynamicReferenceFrame;
028import org.opengis.referencing.crs.CoordinateReferenceSystem;
029import org.opengis.referencing.crs.SingleCRS;
030import org.opengis.metadata.quality.PositionalAccuracy;
031import org.opengis.metadata.extent.Extent;
032import org.opengis.util.InternationalString;
033import org.opengis.annotation.UML;
034import org.opengis.annotation.Classifier;
035import org.opengis.annotation.Stereotype;
036import org.opengis.geoapi.internal.Legacy;
037
038import static org.opengis.annotation.Obligation.*;
039import static org.opengis.annotation.Specification.*;
040
041
042/**
043 * A mathematical operation on coordinates that transforms or converts coordinates to another <abbr>CRS</abbr> or epoch.
044 * The {@linkplain #getSourceCRS() source <abbr>CRS</abbr>} and {@linkplain #getTargetCRS() target <abbr>CRS</abbr>}
045 * properties indicate the <abbr>CRS</abbr> from which coordinates are changed
046 * and the <abbr>CRS</abbr> to which coordinates are changed respectively.
047 * They are mandatory for all subtypes of coordinate operation except {@link Conversion},
048 * but GeoAPI recommends to provide a value even in the latter case.
049 *
050 * <h2>Inverse operation</h2>
051 * Many but not all coordinate operations (from
052 * {@linkplain CoordinateReferenceSystem coordinate reference system} <var>A</var> to
053 * {@linkplain CoordinateReferenceSystem coordinate reference system} <var>B</var>)
054 * also uniquely define the inverse operation (from
055 * {@linkplain CoordinateReferenceSystem coordinate reference system} <var>B</var> to
056 * {@linkplain CoordinateReferenceSystem coordinate reference system} <var>A</var>).
057 * In some cases, the operation method algorithm for the inverse operation is the same as for the forward algorithm,
058 * but the signs of some operation parameter values must be reversed.
059 * In other cases, different algorithms are required for the forward and inverse operations,
060 * but the same operation parameter values are used.
061 * If (some) entirely different parameter values are needed, a different coordinate operation shall be defined.
062 *
063 * @author  OGC Topic 2 (for abstract model and documentation)
064 * @author  Martin Desruisseaux (Geomatys)
065 * @version 3.1
066 * @since   1.0
067 *
068 * @see CoordinateOperationAuthorityFactory#createCoordinateOperation(String)
069 * @see CoordinateOperationAuthorityFactory#createFromCoordinateReferenceSystemCodes(String, String)
070 * @see CoordinateOperationFactory#createOperation(CoordinateReferenceSystem, CoordinateReferenceSystem)
071 */
072@Classifier(Stereotype.ABSTRACT)
073@UML(identifier="CoordinateOperation", specification=ISO_19111)
074public interface CoordinateOperation extends IdentifiedObject {
075    /**
076     * Key for the <code>{@value}</code> property.
077     * This is used for setting the value to be returned by {@link #getOperationVersion()}.
078     *
079     * @see #getOperationVersion()
080     */
081    String OPERATION_VERSION_KEY = "operationVersion";
082
083    /**
084     * Key for the <code>{@value}</code> property.
085     * This is used for setting the value to be returned by {@link #getCoordinateOperationAccuracy()}.
086     *
087     * @see #getCoordinateOperationAccuracy()
088     */
089    String COORDINATE_OPERATION_ACCURACY_KEY = "coordinateOperationAccuracy";
090
091    /**
092     * Key for the <code>{@value}</code> property.
093     *
094     * @see org.opengis.referencing.ObjectDomain#getDomainOfValidity()
095     *
096     * @deprecated Moved to {@link org.opengis.referencing.ObjectDomain} as of ISO 19111:2019.
097     */
098    @Deprecated(since="3.1")
099    String DOMAIN_OF_VALIDITY_KEY = "domainOfValidity";
100
101    /**
102     * Key for the <code>{@value}</code> property.
103     *
104     * @see org.opengis.referencing.ObjectDomain#getScope()
105     *
106     * @deprecated Moved to {@link org.opengis.referencing.ObjectDomain} as of ISO 19111:2019.
107     */
108    @Deprecated(since="3.1")
109    String SCOPE_KEY = "scope";
110
111    /**
112     * Returns the <abbr>CRS</abbr> from which coordinates are changed. This property may be {@code null}
113     * for {@link CoordinateOperationFactory#createDefiningConversion defining conversions}, but otherwise
114     * <em>should</em> be specified for other conversions and <em>shall</em> be specified for transformations
115     * and point motion operations.
116     *
117     * <h4>Obligation</h4>
118     * ISO 19111 defines this property as optional for {@link Conversion} because the source <abbr>CRS</abbr>
119     * can be specified by the {@link org.opengis.referencing.crs.DerivedCRS#getBaseCRS()} property instead.
120     * However, GeoAPI recommends to provide a value even in the latter case.
121     *
122     * @return the <abbr>CRS</abbr> from which coordinates are changed, or {@code null} for a defining conversion.
123     *
124     * @see #getSourceEpoch()
125     */
126    @UML(identifier="sourceCRS", obligation=CONDITIONAL, specification=ISO_19111)
127    CoordinateReferenceSystem getSourceCRS();
128
129    /**
130     * Returns the <abbr>CRS</abbr> to which coordinates are changed. This property may be {@code null}
131     * for {@link CoordinateOperationFactory#createDefiningConversion defining conversions}, but otherwise
132     * <em>should</em> be specified for other conversions and <em>shall</em> be specified for transformations
133     * and point motion operations.
134     *
135     * <h4>Obligation</h4>
136     * ISO 19111 defines this property as optional for {@link Conversion} because the target <abbr>CRS</abbr>
137     * can be specified by the {@link org.opengis.referencing.crs.DerivedCRS} instance instead.
138     * However, GeoAPI recommends to provide a value even in the latter case.
139     *
140     * @return the <abbr>CRS</abbr> to which coordinates are changed, or {@code null} for a defining conversion.
141     *
142     * @see #getTargetEpoch()
143     */
144    @UML(identifier="targetCRS", obligation=CONDITIONAL, specification=ISO_19111)
145    CoordinateReferenceSystem getTargetCRS();
146
147    /**
148     * Returns the <abbr>CRS</abbr> to be used for interpolations in a grid.
149     * Some single coordinate operations employ methods which include interpolation within a grid to derive
150     * the values of operation parameters. The <abbr>CRS</abbr> to be used for the interpolation
151     * may be different from either the source <abbr>CRS</abbr> or the target <abbr>CRS</abbr>.
152     *
153     * <h4>Example</h4>
154     * Vertical offsets between two vertical <abbr>CRS</abbr>s interpolated from a grid.
155     * The source and target <abbr>CRS</abbr>s will both be vertical <abbr>CRS</abbr>s,
156     * the interpolation <abbr>CRS</abbr> is a geographic <abbr>CRS</abbr> to which the grid is referenced.
157     *
158     * @return the <abbr>CRS</abbr> to be used for interpolations in a grid.
159     *
160     * @since 3.1
161     */
162    @UML(identifier="interpolationCRS", obligation=OPTIONAL, specification=ISO_19111)
163    default Optional<CoordinateReferenceSystem> getInterpolationCRS() {
164        return Optional.empty();
165    }
166
167    /**
168     * If the given <abbr>CRS</abbr> is dynamic, returns its epoch.
169     *
170     * @param  crs  the <abbr>CRS</abbr> from which to get the epoch, or {@code null}.
171     * @return epoch of the dynamic reference frame of the given <abbr>CRS</abbr>, if any.
172     */
173    private static Optional<Temporal> getEpoch(final CoordinateReferenceSystem crs) {
174        if (crs instanceof SingleCRS) {
175            final Datum datum = ((SingleCRS) crs).getDatum();
176            if (datum instanceof DynamicReferenceFrame) {
177                return Optional.of(((DynamicReferenceFrame) datum).getFrameReferenceEpoch());
178            }
179        }
180        return Optional.empty();
181    }
182
183    /**
184     * Returns the date at which source coordinate tuples are valid.
185     * This is mandatory if the <abbr>CRS</abbr> is {@linkplain DynamicReferenceFrame dynamic}.
186     *
187     * <h4>Default implementation</h4>
188     * The default implementation checks if the datum of the {@linkplain #getSourceCRS() source CRS}
189     * is a {@link DynamicReferenceFrame dynamic reference frame}. If {@code true}, then the frame
190     * reference epoch is returned. If {@code false}, then an empty value is returned.
191     *
192     * @return epoch at which source coordinate tuples are valid.
193     *
194     * @see #getSourceCRS()
195     * @see DynamicReferenceFrame#getFrameReferenceEpoch()
196     *
197     * @since 3.1
198     */
199    @UML(identifier="sourceCoordinateEpoch", obligation=CONDITIONAL, specification=ISO_19111)
200    default Optional<Temporal> getSourceEpoch() {
201        return getEpoch(getSourceCRS());
202    }
203
204    /**
205     * Returns the date at which target coordinate tuples are valid.
206     * This is mandatory if the <abbr>CRS</abbr> is {@linkplain DynamicReferenceFrame dynamic}.
207     *
208     * <h4>Default implementation</h4>
209     * The default implementation checks if the datum of the {@linkplain #getTargetCRS() target CRS}
210     * is a {@link DynamicReferenceFrame dynamic reference frame}. If {@code true}, then the frame
211     * reference epoch is returned. If {@code false}, then an empty value is returned.
212     *
213     * @return epoch at which target coordinate tuples are valid.
214     *
215     * @see #getTargetCRS()
216     * @see DynamicReferenceFrame#getFrameReferenceEpoch()
217     *
218     * @since 3.1
219     */
220    @UML(identifier="targetCoordinateEpoch", obligation=CONDITIONAL, specification=ISO_19111)
221    default Optional<Temporal> getTargetEpoch() {
222        return getEpoch(getTargetCRS());
223    }
224
225    /**
226     * Returns the version of this coordinate transformation or point motion operation.
227     * The version is an identification of the instantiation due to the stochastic nature of the parameters.
228     * It is mandatory when describing a transformation or point motion operation,
229     * and should not be supplied for a conversion.
230     *
231     * @return version of the coordinate transformation or point motion, or {@code null} if none.
232     */
233    @UML(identifier="operationVersion", obligation=CONDITIONAL, specification=ISO_19111)
234    default String getOperationVersion() {
235        return null;
236    }
237
238    /**
239     * Returns estimate(s) of the impact of this operation on point accuracy.
240     * It gives position error estimates for target coordinates of this coordinate operation,
241     * assuming no errors in source coordinates.
242     *
243     * @return the position error estimates, or an empty collection if not available.
244     */
245    @UML(identifier="coordinateOperationAccuracy", obligation=OPTIONAL, specification=ISO_19111)
246    default Collection<PositionalAccuracy> getCoordinateOperationAccuracy() {
247        return Collections.emptyList();
248    }
249
250    /**
251     * Returns the area or region or timeframe in which this coordinate operation is valid.
252     *
253     * @return the coordinate operation valid domain, or {@code null} if not available.
254     *
255     * @see CoordinateReferenceSystem#getDomainOfValidity()
256     *
257     * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019.
258     */
259    @Deprecated(since = "3.1")
260    @UML(identifier="domainOfValidity", obligation=OPTIONAL, specification=ISO_19111, version=2007)
261    default Extent getDomainOfValidity() {
262        return Legacy.getDomainOfValidity(getDomains());
263    }
264
265    /**
266     * Returns a description of domain of usage, or limitations of usage, for which this operation is valid.
267     *
268     * @return a description of domain of usage, or {@code null} if none.
269     *
270     * @departure historic
271     *   This method has been kept conformant with the specification published in 2003.
272     *   The revision published in 2007 replaced the singleton by a collection and changed the
273     *   obligation from "optional" to "mandatory", requiring a return value of
274     *   <q>not known</q> if the scope is unknown. This change is still under review.
275     *
276     * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019.
277     */
278    @Deprecated(since = "3.1")
279    @UML(identifier="scope", obligation=OPTIONAL, specification=ISO_19111, version=2007)
280    default InternationalString getScope() {
281        return Legacy.getScope(getDomains());
282    }
283
284    /**
285     * Returns the mathematical operation which performs the actual work of changing coordinate values.
286     * The math transform will transform positions in the
287     * {@linkplain #getSourceCRS() source <abbr>CRS</abbr>} into positions in the
288     * {@linkplain #getTargetCRS() target <abbr>CRS</abbr>}.
289     * It may be {@code null} in the case of
290     * {@linkplain CoordinateOperationFactory#createDefiningConversion defining conversions}.
291     *
292     * @return the transform from source to target <abbr>CRS</abbr>, or {@code null} if not applicable.
293     */
294    @UML(identifier="CT_CoordinateTransformation.getMathTransform", specification=OGC_01009)
295    MathTransform getMathTransform();
296
297    /**
298     * Changes coordinates from being referenced to the source <abbr>CRS</abbr>
299     * and/or epoch to being referenced to the target <abbr>CRS</abbr> and/or epoch.
300     * The <abbr>CRS</abbr> and epoch (if any) of the given data <em>should</em> (see below) be equivalent to the
301     * {@linkplain #getSourceCRS() source CRS} and {@linkplain #getSourceEpoch() source epoch} of this operation.
302     * The <abbr>CRS</abbr> and epoch (if any) of the returned data <em>shall</em> be equivalent to the
303     * {@linkplain #getTargetCRS() target CRS} and {@linkplain #getTargetEpoch() target epoch} of this operation.
304     * The order of coordinate tuples <em>shall</em> be preserved.
305     *
306     * <p>This method operates on coordinate tuples and does not deal with interpolation of geometry types.
307     * When a coordinate set is subjected to a coordinate operation, its geometry might or might not be preserved.</p>
308     *
309     * <h4>Implementation flexibility</h4>
310     * If the <abbr>CRS</abbr> and/or epoch of the given data are not equivalent to the source <abbr>CRS</abbr>
311     * and/or epoch of this coordinate operation, implementations can either throw an exception or automatically
312     * prepend an additional operation step.
313     *
314     * <p>Implementations are free to compute the coordinate changes immediately or to delay the computation.
315     * For example, the coordinate changes may be computed on-the-fly during {@link CoordinateSet#stream()} consumption.
316     * In the latter case, invalid coordinates may not cause a {@link TransformException} to be thrown when this method
317     * is invoked, but instead cause an unchecked exception to be thrown later,
318     * typically during the stream terminal operation.</p>
319     *
320     * @param  data  the coordinates to change.
321     * @return the result of changing coordinates.
322     * @throws TransformException if some coordinates cannot be changed. Note that an absence of exception during
323     *         this method call is not a guarantee that the coordinate changes succeeded, because implementations
324     *         may have deferred the actual computation.
325     *
326     * @since 3.1
327     */
328    @UML(identifier="transform", specification=ISO_19111)
329    CoordinateSet transform(CoordinateSet data) throws TransformException;
330}