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.geometry;
019
020import org.opengis.geometry.coordinate.Position;
021import org.opengis.referencing.crs.CoordinateReferenceSystem;
022import org.opengis.annotation.UML;
023import org.opengis.annotation.Classifier;
024import org.opengis.annotation.Stereotype;
025
026import static org.opengis.annotation.Obligation.*;
027import static org.opengis.annotation.Specification.*;
028
029
030/**
031 * Holds the coordinates for a position within some coordinate reference system.
032 * Since {@code DirectPosition}s, as data types, will often be included in larger objects
033 * (such as {@linkplain org.opengis.geometry.Geometry geometries}) that have references to
034 * {@linkplain CoordinateReferenceSystem coordinate reference system} (CRS),
035 * the {@link #getCoordinateReferenceSystem()} method may return {@code null} if this particular
036 * {@code DirectPosition} is included in a larger object with such a reference to a <abbr>CRS</abbr>.
037 * In this case, the coordinate reference system is implicitly assumed to take on the value
038 * of the containing object's <abbr>CRS</abbr>.
039 *
040 * <h2>Optional operation</h2>
041 * A direct position can optionally be modifiable. If this {@code DirectPosition} is unmodifiable,
042 * then all setter methods will throw {@link UnsupportedOperationException}.
043 *
044 * @departure easeOfUse
045 *   The ISO specification defines this interface in the {@code coordinate} sub-package.
046 *   GeoAPI moved this interface into the {@code org.opengis.geometry} root package for
047 *   convenience, because it is extensively used.
048 *
049 * @author  Martin Desruisseaux (IRD, Geomatys)
050 * @version 3.1
051 * @since   1.0
052 */
053@Classifier(Stereotype.DATATYPE)
054@UML(identifier="DirectPosition", specification=ISO_19107)
055public interface DirectPosition extends Position {
056    /**
057     * Returns this direct position.
058     *
059     * @return {@code this} (usually).
060     *
061     * @deprecated This method is inherited from {@code GM_Position}, which has been removed in ISO 19107:2019.
062     */
063    @Override
064    @Deprecated(since = "3.1")
065    default DirectPosition getDirectPosition() {
066        return this;
067    }
068
069    /**
070     * The coordinate reference system (CRS) in which the coordinate tuple is given.
071     * May be {@code null} if this particular {@code DirectPosition} is included in a larger object
072     * with such a reference to a {@linkplain CoordinateReferenceSystem coordinate reference system}.
073     * In this case, the coordinate reference system is implicitly assumed to take on the value
074     * of the containing object's <abbr>CRS</abbr>.
075     *
076     * <h4>Default implementation</h4>
077     * The default implementation returns {@code null}. Implementations should override
078     * this method if the <abbr>CRS</abbr> is known or can be taken from the containing object.
079     *
080     * @return the coordinate reference system (CRS), or {@code null}.
081     */
082    @UML(identifier="coordinateReferenceSystem", obligation=MANDATORY, specification=ISO_19107)
083    default CoordinateReferenceSystem getCoordinateReferenceSystem() {
084        return null;
085    }
086
087    /**
088     * The length of coordinate sequence (the number of entries). This is determined by the
089     * {@linkplain #getCoordinateReferenceSystem() coordinate reference system}.
090     *
091     * @return the dimensionality of this position.
092     */
093    @UML(identifier="dimension", obligation=MANDATORY, specification=ISO_19107)
094    int getDimension();
095
096    /**
097     * A <b>copy</b> of the coordinates stored as an array of double values.
098     * Changes to the returned array will not affect this {@code DirectPosition}.
099     * The array length shall be equal to the {@linkplain #getDimension() dimension}.
100     *
101     * <h4>Default implementation</h4>
102     * The default implementation invokes {@link #getCoordinate(int)} for all indices
103     * from 0 inclusive to {@link #getDimension()} exclusive, and stores the values
104     * in a newly created array.
105     *
106     * @return a copy of the coordinates. Changes in the returned array will not be reflected back
107     *         in this {@code DirectPosition} object.
108     *
109     * @since 3.1
110     */
111    @UML(identifier="coordinate", obligation=MANDATORY, specification=ISO_19107)
112    default double[] getCoordinates() {
113        final double[] coordinates = new double[getDimension()];
114        for (int i=0; i<coordinates.length; i++) {
115            coordinates[i] = getCoordinate(i);
116        }
117        return coordinates;
118    }
119
120    /**
121     * A <b>copy</b> of the coordinates presented as an array of double values.
122     *
123     * @return a copy of the coordinates. Changes in the returned array will not be reflected back
124     *         in this {@code DirectPosition} object.
125     *
126     * @deprecated Renamed {@link #getCoordinates()}.
127     * To be removed because of the risk of confusion with {@link #getCoordinate(int)}.
128     */
129    @Deprecated(since="3.1", forRemoval=true)
130    default double[] getCoordinate() {
131        return getCoordinates();
132    }
133
134    /**
135     * Returns the coordinate at the specified dimension.
136     *
137     * @param  dimension  the dimension in the range 0 to {@linkplain #getDimension dimension}−1.
138     * @return the coordinate at the specified dimension.
139     * @throws IndexOutOfBoundsException if the given index is negative or is equal or greater
140     *         than the {@linkplain #getDimension() number of dimensions}.
141     *
142     * @since 3.1
143     */
144    double getCoordinate(int dimension);
145
146    /**
147     * Returns the coordinate at the specified dimension.
148     *
149     * @param  dimension  the dimension in the range 0 to {@linkplain #getDimension dimension}−1.
150     * @return the coordinate at the specified dimension.
151     * @throws IndexOutOfBoundsException if the given index is negative or is equal or greater
152     *         than the {@linkplain #getDimension() number of dimensions}.
153     *
154     * @deprecated Renamed {@link #getCoordinate(int)}.
155     */
156    @Deprecated(since = "3.1")
157    default double getOrdinate(int dimension) throws IndexOutOfBoundsException {
158        return getCoordinate(dimension);
159    }
160
161    /**
162     * Sets the coordinate value along the specified dimension.
163     * This is an optional operation.
164     *
165     * <h4>Default implementation</h4>
166     * The default implementation throws {@code UnsupportedOperationException}.
167     * Implementations need to override this method if this direct position is mutable.
168     *
169     * @param  dimension  the dimension for the coordinate of interest.
170     * @param  value      the coordinate value of interest.
171     * @throws IndexOutOfBoundsException if the given index is negative or is equal or greater
172     *         than the {@linkplain #getDimension() position dimension}.
173     * @throws UnsupportedOperationException if this direct position is immutable.
174     *
175     * @since 3.1
176     */
177    default void setCoordinate(int dimension, double value) {
178        throw new UnsupportedOperationException();
179    }
180
181    /**
182     * Sets the coordinate value along the specified dimension.
183     *
184     * @param  dimension  the dimension for the coordinate of interest.
185     * @param  value      the coordinate value of interest.
186     * @throws IndexOutOfBoundsException if the given index is negative or is equal or greater
187     *         than the {@linkplain #getDimension() position dimension}.
188     * @throws UnsupportedOperationException if this direct position is immutable.
189     *
190     * @deprecated Renamed {@link #setCoordinate(int, double)}.
191     */
192    @Deprecated(since = "3.1")
193    default void setOrdinate(int dimension, double value)
194            throws IndexOutOfBoundsException, UnsupportedOperationException
195    {
196        setCoordinate(dimension, value);
197    }
198
199    /**
200     * Compares this direct position with the specified object for equality.
201     * Two direct positions are considered equal if the following conditions are met:
202     *
203     * <ul>
204     *   <li>{@code object} is non-null and is an instance of {@code DirectPosition}.</li>
205     *   <li>Both direct positions have the same {@linkplain #getDimension() number of dimensions}.</li>
206     *   <li>Both direct positions have the same or equal {@linkplain #getCoordinateReferenceSystem()
207     *       coordinate reference system}.</li>
208     *   <li>For all dimension <var>i</var>, the {@linkplain #getCoordinates coordinate value} of both
209     *       direct positions at that dimension are equals in the sense of {@link Double#equals(Object)}.</li>
210     * </ul>
211     *
212     * In other words, this method shall be equivalent to the following code:
213     *
214     * {@snippet lang="java" :
215     * public boolean equals(Object obj) {
216     *     return (obj instanceof DirectPosition other)
217     *             && Arrays.equals(getCoordinates(), other.getCoordinates())
218     *             && Objects.equals(getCoordinateReferenceSystem(), other.getCoordinateReferenceSystem());
219     * }
220     * }
221     *
222     * @param  object  the object to compare with this direct position for equality.
223     * @return {@code true} if the given object is equal to this direct position.
224     */
225    @Override
226    boolean equals(Object object);
227
228    /**
229     * Returns a hash code value for this direct position.
230     * This method shall be equivalent to the following code:
231     *
232     * {@snippet lang="java" :
233     * public int hashCode() {
234     *     return Arrays.hashCode(getCoordinates()) + Objects.hashCode(getCoordinateReferenceSystem());
235     * }
236     * }
237     *
238     * @return a hash code value for this direct position.
239     */
240    @Override
241    int hashCode();
242}