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 java.awt.geom.Rectangle2D;                                   // Used in @see javadoc tags
021
022import org.opengis.referencing.cs.RangeMeaning;                     // For javadoc
023import org.opengis.referencing.crs.CoordinateReferenceSystem;
024import org.opengis.annotation.UML;
025import org.opengis.annotation.Classifier;
026import org.opengis.annotation.Stereotype;
027
028import static org.opengis.annotation.Obligation.*;
029import static org.opengis.annotation.Specification.*;
030
031
032/**
033 * A minimum bounding box or rectangle. Regardless of dimension, an {@code Envelope} can
034 * be represented without ambiguity as two direct positions (coordinate points). To encode an
035 * {@code Envelope}, it is sufficient to encode these two points. This is consistent with
036 * all of the data types in this specification, their state is represented by their publicly
037 * accessible attributes.
038 *
039 * <h2>Crossing the anti-meridian of a geographic CRS</h2>
040 * The <cite>Web Coverage Service</cite> (WCS) 1.1 specification uses an extended interpretation
041 * of the bounding box definition. In a WCS 1.1 data structure, the {@linkplain #getUpperCorner()
042 * upper corner} defines the edges region in the directions of <em>increasing</em> coordinate
043 * values in the envelope CRS, while the {@linkplain #getLowerCorner() lower corner} defines the
044 * edges region in the direction of <em>decreasing</em> coordinate values. They are usually the
045 * algebraic maximum and minimum coordinates respectively, but not always. For example, an envelope
046 * crossing the anti-meridian could have an upper corner longitude less than the lower corner longitude.
047 * Whether an envelope supports the extended bounding box interpretation or not is
048 * implementation-dependent. If supported, the extended interpretation is applicable only to
049 * axes having a {@link RangeMeaning#WRAPAROUND WRAPAROUND} range meaning, which is usually
050 * the longitude axis.
051 *
052 * @departure easeOfUse
053 *   The ISO specification defines this interface in the {@code coordinate} sub-package.
054 *   GeoAPI moved this interface into the {@code org.opengis.geometry} root package for
055 *   convenience, because it is extensively used.
056 *
057 * @author  Martin Desruisseaux (IRD)
058 * @version 3.1
059 * @since   1.0
060 */
061@Classifier(Stereotype.DATATYPE)
062@UML(identifier="GM_Envelope", specification=ISO_19107)
063public interface Envelope {
064    /**
065     * Returns the envelope coordinate reference system, or {@code null} if unknown.
066     * If non-null, it shall be the same as {@linkplain #getLowerCorner() lower corner}
067     * and {@linkplain #getUpperCorner() upper corner} CRS.
068     *
069     * @return the envelope CRS, or {@code null} if unknown.
070     *
071     * @departure easeOfUse
072     *   ISO does not define this method - the CRS or the dimension can be obtained only through
073     *   one of the corner {@code DirectPosition} objects. GeoAPI adds this method for convenience
074     *   as a more direct way of obtaining the information and to free the user from the need to
075     *   choose an arbitrary corner (very defensive code might feel the need to get the value from
076     *   both corners to check they were the same).
077     */
078    CoordinateReferenceSystem getCoordinateReferenceSystem();
079
080    /**
081     * The length of coordinate sequence (the number of entries) in this envelope. Mandatory
082     * even when the {@linkplain #getCoordinateReferenceSystem() coordinate reference system}
083     * is unknown.
084     *
085     * @return the dimensionality of this envelope.
086     *
087     * @departure easeOfUse
088     *   ISO does not define this method - the CRS or the dimension can be obtained only through
089     *   one of the corner {@code DirectPosition} objects. GeoAPI adds this method for convenience
090     *   as a more direct way of obtaining the information and to free the user from the need to
091     *   choose an arbitrary corner (very defensive code might feel the need to get the value from
092     *   both corners to check they were the same).
093     */
094    int getDimension();
095
096    /**
097     * The limits in the direction of decreasing coordinate values for each dimension.
098     * This is typically a coordinate position consisting of all the minimal coordinates
099     * for each dimension for all points within the {@code Envelope}.
100     *
101     * <h4>Crossing the anti-meridian of a geographic CRS</h4>
102     * The <cite>Web Coverage Service</cite> (WCS) 1.1 specification uses an extended interpretation
103     * of the bounding box definition. In a WCS 1.1 data structure, the lower corner defines the
104     * edges region in the directions of <em>decreasing</em> coordinate values in the envelope CRS.
105     * This is usually the algebraic {@linkplain #getMinimum(int) minimum} coordinates, but not
106     * always. For example, an envelope crossing the anti-meridian could have a lower corner
107     * longitude greater than the {@linkplain #getUpperCorner() upper corner} longitude.
108     * Whether this envelope supports the extended bounding box interpretation or not is
109     * implementation-dependent. If supported, the extended interpretation is applicable only to
110     * axes having a {@link RangeMeaning#WRAPAROUND WRAPAROUND} range meaning - usually the
111     * longitude axis. On typical map representations, the {@code getLowerCorner()} method
112     * name still "visually" appropriate since the <i>lower</i> corner still toward the
113     * bottom of the map even if the left corner became the right corner.
114     *
115     * @return the lower corner, typically (but not necessarily) containing minimal coordinate values.
116     */
117    @UML(identifier="lowerCorner", obligation=MANDATORY, specification=ISO_19107)
118    DirectPosition getLowerCorner();
119
120    /**
121     * The limits in the direction of increasing coordinate values for each dimension.
122     * This is typically a coordinate position consisting of all the maximal coordinates
123     * for each dimension for all points within the {@code Envelope}.
124     *
125     * <h4>Crossing the anti-meridian of a geographic CRS</h4>
126     * The <cite>Web Coverage Service</cite> (WCS) 1.1 specification uses an extended interpretation
127     * of the bounding box definition. In a WCS 1.1 data structure, the upper corner defines the
128     * edges region in the directions of <em>increasing</em> coordinate values in the envelope CRS.
129     * This is usually the algebraic {@linkplain #getMaximum(int) maximum} coordinates, but not
130     * always. For example, an envelope crossing the anti-meridian could have an upper corner
131     * longitude less than the {@linkplain #getLowerCorner() lower corner} longitude.
132     * Whether this envelope supports the extended bounding box interpretation or not is
133     * implementation-dependent. If supported, the extended interpretation is applicable only to
134     * axes having a {@link RangeMeaning#WRAPAROUND WRAPAROUND} range meaning - usually the
135     * longitude axis. On typical map representations, the {@code getUpperCorner()} method
136     * name still "visually" appropriate since the <i>upper</i> corner still toward the
137     * top of the map even if the right corner became the left corner.
138     *
139     * @return the upper corner, typically (but not necessarily) containing maximal coordinate values.
140     */
141    @UML(identifier="upperCorner", obligation=MANDATORY, specification=ISO_19107)
142    DirectPosition getUpperCorner();
143
144    /**
145     * Returns the minimal coordinate value for the specified dimension. In the typical case of
146     * non-empty envelopes <em>not</em> crossing the anti-meridian, this method is a shortcut
147     * for the following code without the cost of creating a temporary {@link DirectPosition}
148     * object:
149     *
150     * <blockquote><code>
151     * minimum = {@linkplain #getLowerCorner()}.{@linkplain DirectPosition#getCoordinate(int) getCoordinate}(dimension);
152     * </code></blockquote>
153     *
154     * <h4>Crossing the anti-meridian of a geographic CRS</h4>
155     * If the axis range meaning is {@link RangeMeaning#WRAPAROUND WRAPAROUND} and this envelope
156     * supports the {@linkplain #getLowerCorner() lower} and {@linkplain #getUpperCorner() upper}
157     * corners extended interpretation, then <var>lower</var> may possibly be greater than
158     * <var>upper</var>. In such case, implementations shall select some value such that
159     * <var>minimum</var> &lt; <var>maximum</var> (ignoring NaN). It may be the
160     * {@linkplain org.opengis.referencing.cs.CoordinateSystemAxis#getMinimumValue() axis minimum value},
161     * {@linkplain Double#NEGATIVE_INFINITY negative infinity}, {@linkplain Double#NaN NaN} or other
162     * value, at implementer choice.
163     *
164     * @param  dimension  the dimension for which to obtain the coordinate value.
165     * @return the minimal coordinate at the given dimension.
166     * @throws IndexOutOfBoundsException if the given index is negative or is equal or greater
167     *         than the {@linkplain #getDimension() envelope dimension}.
168     *
169     * @departure easeOfUse
170     *   This method is not part of ISO specification. GeoAPI adds this method for convenience and efficiency,
171     *   since some implementations might store the minimum and maximum coordinate values directly in the
172     *   {@code Envelope} itself rather than in a contained {@code DirectPosition} corner.
173     *
174     * @see Rectangle2D#getMinX()
175     * @see Rectangle2D#getMinY()
176     */
177    double getMinimum(int dimension) throws IndexOutOfBoundsException;
178
179    /**
180     * Returns the maximal coordinate value for the specified dimension. In the typical case of
181     * non-empty envelopes <em>not</em> crossing the anti-meridian, this method is a shortcut
182     * for the following code without the cost of creating a temporary {@link DirectPosition}
183     * object:
184     *
185     * <blockquote><code>
186     * maximum = {@linkplain #getUpperCorner()}.{@linkplain DirectPosition#getCoordinate(int) getCoordinate}(dimension);
187     * </code></blockquote>
188     *
189     * <h4>Crossing the anti-meridian of a geographic CRS</h4>
190     * If the axis range meaning is {@link RangeMeaning#WRAPAROUND WRAPAROUND} and this envelope
191     * supports the {@linkplain #getLowerCorner() lower} and {@linkplain #getUpperCorner() upper}
192     * corners extended interpretation, then <var>upper</var> may possibly be less than
193     * <var>lower</var>. In such case, implementations shall select some value such that
194     * <var>maximum</var> &gt; <var>minimum</var> (ignoring NaN). It may be the
195     * {@linkplain org.opengis.referencing.cs.CoordinateSystemAxis#getMaximumValue() axis maximum value},
196     * {@linkplain Double#POSITIVE_INFINITY positive infinity}, {@linkplain Double#NaN NaN} or other
197     * value, at implementer choice.
198     *
199     * @param  dimension  the dimension for which to obtain the coordinate value.
200     * @return the maximal coordinate at the given dimension.
201     * @throws IndexOutOfBoundsException if the given index is negative or is equal or greater
202     *         than the {@linkplain #getDimension() envelope dimension}.
203     *
204     * @departure easeOfUse
205     *   This method is not part of ISO specification. GeoAPI adds this method for convenience and efficiency,
206     *   since some implementations might store the minimum and maximum coordinate values directly in the
207     *   {@code Envelope} itself rather than in a contained {@code DirectPosition} corner.
208     *
209     * @see Rectangle2D#getMaxX()
210     * @see Rectangle2D#getMaxY()
211     */
212    double getMaximum(int dimension) throws IndexOutOfBoundsException;
213
214    /**
215     * Returns the median coordinate along the specified dimension.
216     * In most cases, the result is equal (minus rounding error) to:
217     *
218     * <blockquote><code>
219     * median = ({@linkplain #getMinimum(int) getMinimum}(dimension) + {@linkplain #getMaximum(int) getMaximum}(dimension)) / 2;
220     * </code></blockquote>
221     *
222     * <h4>Crossing the anti-meridian of a geographic CRS</h4>
223     * If this envelope supports the {@linkplain #getLowerCorner() lower} and
224     * {@linkplain #getUpperCorner() upper} corners extended interpretation, and if the axis
225     * range meaning is {@link RangeMeaning#WRAPAROUND WRAPAROUND}, then a special cases occurs
226     * when <var>upper</var> &lt; <var>lower</var>. In such cases, the coordinate values from the
227     * lower and upper corner may be used instead of the minimum and maximum values, with the
228     * periodicity (360° for longitudes) added to the upper value before to perform the median calculation.
229     * Implementations are free to use variants of the above algorithm. For example some
230     * libraries may add different multiples of the periodicity in order to ensure that the
231     * median value is inside the axis range.
232     *
233     * @param  dimension  the dimension for which to obtain the coordinate value.
234     * @return the median coordinate at the given dimension.
235     * @throws IndexOutOfBoundsException if the given index is negative or is equal or greater
236     *         than the {@linkplain #getDimension() envelope dimension}.
237     *
238     * @departure easeOfUse
239     *   This method is not part of ISO specification. GeoAPI adds this method for convenience and efficiency,
240     *   since some implementations might store the minimum and maximum coordinate values directly in the
241     *   {@code Envelope} itself rather than in a contained {@code DirectPosition} corner.
242     *
243     * @see Rectangle2D#getCenterX()
244     * @see Rectangle2D#getCenterY()
245     */
246    double getMedian(int dimension) throws IndexOutOfBoundsException;
247
248    /**
249     * Returns the envelope span (typically width or height) along the specified dimension.
250     * In most cases, the result is equal (minus rounding error) to:
251     *
252     * <blockquote><code>
253     * span = {@linkplain #getMaximum(int) getMaximum}(dimension) - {@linkplain #getMinimum(int) getMinimum}(dimension);
254     * </code></blockquote>
255     *
256     * <h4>Crossing the anti-meridian of a geographic CRS</h4>
257     * If this envelope supports the {@linkplain #getLowerCorner() lower} and
258     * {@linkplain #getUpperCorner() upper} corners extended interpretation, and if the axis
259     * range meaning is {@link RangeMeaning#WRAPAROUND WRAPAROUND}, then a special cases occurs
260     * when <var>upper</var> &lt; <var>lower</var>. In such cases, the coordinate values from the
261     * lower and upper corner may be used instead of the minimum and maximum values, with the
262     * periodicity (360° for longitudes) added to the upper value before to perform the span calculation.
263     * Implementations are free to use variants of the above algorithm. For example some
264     * libraries may add different multiples of the periodicity.
265     *
266     * @param  dimension  the dimension for which to obtain the span.
267     * @return the span (typically width or height) at the given dimension.
268     * @throws IndexOutOfBoundsException if the given index is negative or is equal or greater
269     *         than the {@linkplain #getDimension() envelope dimension}.
270     *
271     * @departure easeOfUse
272     *   This method is not part of ISO specification. GeoAPI adds this method for convenience and efficiency,
273     *   since some implementations might store the span values directly in the {@code Envelope} itself rather
274     *   than calculating it from the corners.
275     *
276     * @see Rectangle2D#getWidth()
277     * @see Rectangle2D#getHeight()
278     */
279    double getSpan(int dimension) throws IndexOutOfBoundsException;
280}