# Class Extents

Object
Extents

public final class Extents extends Object
Convenience static methods for extracting information from `Extent` or `Metadata` objects. This class provides methods for:

- Fetching geographic, vertical or temporal ranges in a convenient form.
- Computing intersection of bounding boxes.
- Computing area estimations.

Since:
  0.3
See Also:
  - `Envelopes`

- ## Field Summary

  Fields
  Modifier and Type
  Field
  Description
  `static final Extent`
  `WORLD`
  A geographic extent ranging from 180°W to 180°E and 90°S to 90°N.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static double`
  `area(GeographicBoundingBox box)`
  Returns an estimation of the area (in square metres) of the given bounding box.
  `static DirectPosition`
  `centroid(GeographicBoundingBox bbox)`
  Returns the position at the median longitude and latitude values of the given bounding box.
  `static Collection<? extends Extent>`
  `fromIdentificationInfo(Metadata metadata)`
  Returns the extents found in all `Identification` elements of the given metadata.
  `static String`
  `getDescription(Extent extent, Locale locale)`
  Returns the description of the given extent, or `null` if none.
  `static Optional<GeographicBoundingBox>`
  `getGeographicBoundingBox(Stream<? extends Extent> extents)`
  Returns a single geographic bounding box for the given extents.
  `static GeographicBoundingBox`
  `getGeographicBoundingBox(Extent extent)`
  Returns a single geographic bounding box from the specified extent.
  `static GeographicBoundingBox`
  `getGeographicBoundingBox(Metadata metadata)`
  Returns a single geographic bounding box from the specified metadata.
  `static Optional<Instant>`
  `getInstant(Extent extent, ZoneId zone, double location)`
  Returns an instant in the temporal elements of the given extent, or `null` if none.
  `static Range<Date>`
  `getTimeRange(Extent extent)`
  Returns the union of all time ranges found in the given extent, or `null` if none.
  `static MeasurementRange<Double>`
  `getVerticalRange(Extent extent)`
  Returns the union of a subset of vertical ranges found in the given extent, or `null` if none.
  `static Extent`
  `intersection(Extent e1, Extent e2)`
  Returns the intersection of the given extents.
  `static GeographicBoundingBox`
  `intersection(GeographicBoundingBox b1, GeographicBoundingBox b2)`
  Returns the intersection of the given geographic bounding boxes.
  `static TemporalExtent`
  `intersection(TemporalExtent e1, TemporalExtent e2)`
  Returns the intersection of the given temporal extents.
  `static VerticalExtent`
  `intersection(VerticalExtent e1, VerticalExtent e2)`
  Returns the intersection of the given vertical extents.
  `static boolean`
  `isWorld(Extent extent)`
  Returns `true` if the given extent covers the world.
  `static GeographicBoundingBox`
  `union(GeographicBoundingBox b1, GeographicBoundingBox b2)`
  Returns the union of the given geographic bounding boxes.

  ### Methods inherited from class Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Field Details

  - ### WORLD

    public static final Extent WORLD
    A geographic extent ranging from 180°W to 180°E and 90°S to 90°N. This extent has no vertical and no temporal components.
    See Also:
      - `isWorld(Extent)`

- ## Method Details

  - ### fromIdentificationInfo

    public static Collection<? extends Extent> fromIdentificationInfo(Metadata metadata)
    Returns the extents found in all `Identification` elements of the given metadata. If there is only one `Identification` element (which is the usual case), then its collection of extents is returned as-is; the collection is not copied.

    In the less usual case where there is many `Identification` elements providing non-empty collection of extents, then this method returns the union of all collections without duplicated elements (duplication determined by `Object​.equals(Object)`). In the special case where the first non-empty collection of extents contains all other collections, then that collection is returned as-is.

    Rational: above policy makes a best effort for avoiding to create new collections. The reason is that collection implementations may perform lazy calculations of `Extent` elements. This method tries to preserve the lazy behavior (if any).
    Parameters:
      `metadata` - the metadata, or `null` if none.
    Returns:
      extents found in all `Identification` elements, or an empty collection if none.
    Since:
      1.3

  - ### getGeographicBoundingBox

    @OptionalCandidate public static GeographicBoundingBox getGeographicBoundingBox(Metadata metadata)
    Returns a single geographic bounding box from the specified metadata. If the given metadata contains many `Identification` or many `Extent` instances, then this method returns the union of all of them.

    #### Use case

    This convenience method is useful when the metadata is expected to contain only one bounding box, typically because the metadata were obtained from a resource which is known to support only singletons (one raster or one set of features). For more general cases, it is often more appropriate to handle each bounding box separately using `getGeographicBoundingBox(Extent)`.
    Parameters:
      `metadata` - the metadata from which to get a global bounding box, or `null` if none.
    Returns:
      a global bounding box for all extents found in the given metadata, or `null` if none.
    Since:
      1.1

  - ### getGeographicBoundingBox

    public static Optional<GeographicBoundingBox> getGeographicBoundingBox(Stream<? extends Extent> extents)
    Returns a single geographic bounding box for the given extents. For each extent, the bounding box is fetched with `getGeographicBoundingBox(Extent)`. If more than one geographic bound is found, this method computes their union.

    This is a convenience method for fetching the domain of validity of `Datum`, `CoordinateReferenceSystem` or `CoordinateOperation` objects.

    Parameters:
      `extents` - the extents for which to get a single geographic bounding box.
    Returns:
      the union of all geographic bounding boxes found in all extents.
    Throws:
      `InvalidMetadataException` - if an envelope cannot be transformed to a geographic bounding box.
    Since:
      1.4
    See Also:
      - `AbstractIdentifiedObject​.getDomains()`
      - `CRS​.getDomainOfValidity(CoordinateReferenceSystem)`

  - ### getGeographicBoundingBox

    @OptionalCandidate public static GeographicBoundingBox getGeographicBoundingBox(Extent extent)
    Returns a single geographic bounding box from the specified extent. This method tries to find the bounding box in the cheapest way before to fallback on more expensive computations:
    1. First, this method searches geographic elements that are instance of `GeographicBoundingBox`.
       - If exactly one such instance is found, then this method returns that instance directly (no copy).
       - If more than one instance is found, then this method computes and returns the union of all bounding boxes.
    2. If above step found no `GeographicBoundingBox`, then this method inspects geographic elements that are instance of `BoundingPolygon`, taking in account only the envelopes associated to a coordinate reference system of kind `GeographicCRS`. If such envelopes are found, then this method computes and returns their union.
    3. If above step found no polygon's envelope associated to a geographic CRS, then in last resort this method uses all polygon's envelopes regardless their coordinate reference system (provided that the CRS is not null), applying coordinate transformations if needed.
    4. If above step found no polygon's envelope, then this method returns `null`.
    Parameters:
      `extent` - the extent to convert to a geographic bounding box, or `null`.
    Returns:
      a geographic bounding box extracted from the given extent, or `null` if none.
    Throws:
      `InvalidMetadataException` - if an envelope cannot be transformed to a geographic bounding box.
    See Also:
      - `CRS​.getDomainOfValidity(CoordinateReferenceSystem)`

  - ### getVerticalRange

    @OptionalCandidate public static MeasurementRange<Double> getVerticalRange(Extent extent)
    Returns the union of a subset of vertical ranges found in the given extent, or `null` if none. If the given `Extent` object contains more than one vertical extent, then this method performs a choice based on the vertical datum and the unit of measurement:

    - Choice based on realization method
      Only the extents associated (indirectly, through their CRS) to the same non-null `VerticalDatumType` will be taken in account. If all realization methods are absent, then this method conservatively uses only the first vertical extent. Otherwise the realization method used for filtering the vertical extents is:

      - `VerticalDatumType​.GEOIDAL` if at least one extent uses this realization method.
      - Otherwise, `VerticalDatumType​.DEPTH` if at least one extent uses this realization method.
      - Otherwise, the first non-null realization type found in iteration order.
      Rational: like geographic bounding box, the vertical range is an approximated information; the range returned by this method does not carry any information about the vertical CRS and this method does not attempt to perform coordinate transformation. But this method is more useful if the returned ranges are close to a frequently used surface, like the geoid. The same simplification is applied in the `VerticalExtent` element of Well Known Text (WKT) format, which specifies that Vertical extent is an approximate description of location; heights are relative to an unspecified mean sea level.

    - Choice based on units of measurement
      If, after the choice based on the vertical datum described above, there is still more than one vertical extent to consider, then the next criterion checks for the units of measurement.

      - If no range specify a unit of measurement, return the first range and ignore all others.
      - Otherwise take the first range having a unit of measurement. Then:
        - All other ranges having an incompatible unit of measurement will be ignored.
        - All other ranges having a compatible unit of measurement will be converted to the unit of the first retained range, and their union will be computed.
      Example: Heights or depths are often measured using some pressure units, for example hectopascals (hPa). An `Extent` could contain two vertical elements: one with the height measurements in hPa, and the other element with heights transformed to metres using an empirical formula. In such case this method will select the first vertical element on the assumption that it is the "main" one that the metadata producer intended to show. Next, this method will search for other vertical elements using pressure unit. In our example there is none, but if such elements were found, this method would compute their union.
    Parameters:
      `extent` - the extent to convert to a vertical measurement range, or `null`.
    Returns:
      a vertical measurement range created from the given extent, or `null` if none.
    Throws:
      `InvalidMetadataException` - if a vertical range contains a `Double​.NaN` value.
    Since:
      0.4

  - ### getTimeRange

    @OptionalCandidate public static Range<Date> getTimeRange(Extent extent)
    Returns the union of all time ranges found in the given extent, or `null` if none.
    Parameters:
      `extent` - the extent to convert to a time range, or `null`.
    Returns:
      a time range created from the given extent, or `null` if none.
    Since:
      0.4

  - ### getInstant

    public static Optional<Instant> getInstant(Extent extent, ZoneId zone, double location)
    Returns an instant in the temporal elements of the given extent, or `null` if none. First, this method computes the union of all temporal elements. Then this method computes the linear interpolation between the start and end time as in the following pseudo-code:

        return new Date(startTime + (endTime - startTime) * location);

    Special cases:
    - If `location` is 0, then this method returns the beginning.
    - If `location` is 1, then this method returns the ending.
    - If `location` is 0.5, then this method returns the average of start time and end time.
    - If `location` is outside the [0 … 1] range, then the result will be outside the temporal extent.
    Parameters:
      `extent` - the extent from which to get an instant, or `null`.
      `zone` - the timezone to use if a time is local, or `null` if none.
      `location` - 0 for the beginning, 1 for the ending, 0.5 for the average time, or the coefficient (usually in the [0 … 1] range) for interpolating an instant.
    Returns:
      an instant interpolated at the given location.
    Throws:
      `DateTimeException` - if `zone` is null and a temporal value cannot be converted to an instant without that information.
    Since:
      1.5

  - ### getDescription

    @OptionalCandidate public static String getDescription(Extent extent, Locale locale)
    Returns the description of the given extent, or `null` if none.
    Parameters:
      `extent` - the extent from which to get a description, or `null`.
      `locale` - desired locale, or `null` for default.
    Returns:
      description of the given extent, or `null` if none.
    Since:
      1.1

  - ### centroid

    public static DirectPosition centroid(GeographicBoundingBox bbox)
    Returns the position at the median longitude and latitude values of the given bounding box. This method does not check the inclusion status. This method takes in account bounding boxes that cross the anti-meridian.
    Parameters:
      `bbox` - the bounding box for which to get the median longitude and latitude values, or `null`.
    Returns:
      a median position of the given bounding box, or `null` if none.

  - ### area

    public static double area(GeographicBoundingBox box)
    Returns an estimation of the area (in square metres) of the given bounding box. Since `GeographicBoundingBox` provides only approximated information (for example it does not specify the datum), the value returned by this method is also approximated.

    The current implementation performs its computation on the GRS 1980 Authalic Sphere. However, this may change in any future SIS version.

    Parameters:
      `box` - the geographic bounding box for which to compute the area, or `null`.
    Returns:
      an estimation of the area in the given bounding box (m²), or NaN if the given box was null.
    Since:
      0.4

  - ### union

    public static GeographicBoundingBox union(GeographicBoundingBox b1, GeographicBoundingBox b2)
    Returns the union of the given geographic bounding boxes. If any of the arguments is `null`, then this method returns the other argument (which may be null). Otherwise, this method returns a box which is the union of the two given boxes.

    This method never modify the given boxes, but may return directly one of the given arguments if it already represents the union result.

    Parameters:
      `b1` - the first bounding box, or `null`.
      `b2` - the second bounding box, or `null`.
    Returns:
      the union (may be any of the `b1` or `b2` argument if unchanged), or `null` if the two given boxes are null.
    Since:
      1.2
    See Also:
      - `DefaultGeographicBoundingBox​.add(GeographicBoundingBox)`

  - ### intersection

    public static GeographicBoundingBox intersection(GeographicBoundingBox b1, GeographicBoundingBox b2)
    Returns the intersection of the given geographic bounding boxes. If any of the arguments is `null`, then this method returns the other argument (which may be null). Otherwise this method returns a box which is the intersection of the two given boxes. If there is no intersection, the returned bounding box contains `Double​.NaN` bounds.

    This method never modify the given boxes, but may return directly one of the given arguments if it already represents the intersection result.

    Parameters:
      `b1` - the first bounding box, or `null`.
      `b2` - the second bounding box, or `null`.
    Returns:
      the intersection (may be any of the `b1` or `b2` argument if unchanged), or `null` if the two given boxes are null. May contain `Double​.NaN` bounds.
    Throws:
      `IllegalArgumentException` - if the inclusion status is not the same for both boxes.
    Since:
      0.4
    See Also:
      - `DefaultGeographicBoundingBox​.intersect(GeographicBoundingBox)`

  - ### intersection

    public static VerticalExtent intersection(VerticalExtent e1, VerticalExtent e2)
    Returns the intersection of the given vertical extents. If any of the arguments is `null`, then this method returns the other argument (which may be null). Otherwise this method returns a vertical extent which is the intersection of the two given extents.

    This method never modify the given extents, but may return directly one of the given arguments if it already represents the intersection result.

    #### Advantage and inconvenient of this method

    This method cannot intersect extents defined with different datums because height transformations generally require the geodetic positions (latitudes and longitudes) of the heights to transform. For more general transformations, it is better to convert all extent components into a single envelope, then transform the envelope at once. On the other hand, this `intersect(…)` method preserves better the `NilReason` (if any).
    Parameters:
      `e1` - the first extent, or `null`.
      `e2` - the second extent, or `null`.
    Returns:
      the intersection (may be any of the `e1` or `e2` argument if unchanged), or `null` if the two given extents are null.
    Throws:
      `IllegalArgumentException` - if the two extents do not use the same datum, ignoring metadata.
    Since:
      0.8
    See Also:
      - `DefaultVerticalExtent​.intersect(VerticalExtent)`

  - ### intersection

    public static TemporalExtent intersection(TemporalExtent e1, TemporalExtent e2)
    Returns the intersection of the given temporal extents. If any of the arguments is `null`, then this method returns the other argument (which may be null). Otherwise this method returns a temporal extent which is the intersection of the two given extents.

    This method never modify the given extents, but may return directly one of the given arguments if it already represents the intersection result.

    Parameters:
      `e1` - the first extent, or `null`.
      `e2` - the second extent, or `null`.
    Returns:
      the intersection (may be any of the `e1` or `e2` argument if unchanged), or `null` if the two given extents are null.
    Since:
      0.8
    See Also:
      - `DefaultTemporalExtent​.intersect(TemporalExtent)`

  - ### intersection

    public static Extent intersection(Extent e1, Extent e2)
    Returns the intersection of the given extents. If any of the arguments is `null`, then this method returns the other argument (which may be null). Otherwise this method returns an extent which is the intersection of all geographic, vertical and temporal elements in the two given extents.

    This method never modify the given extents, but may return directly one of the given arguments if it already represents the intersection result.

    If there is no intersection, then the returned object implements `Emptiable` and the `Emptiable​.isEmpty()` method returns `true`.

    Parameters:
      `e1` - the first extent, or `null`.
      `e2` - the second extent, or `null`.
    Returns:
      the intersection (may be any of the `e1` or `e2` argument if unchanged), or `null` if the two given extents are null.
    Throws:
      `IllegalArgumentException` - if two elements to intersect are not compatible (e.g. mismatched bounding box inclusion status or mismatched vertical datum).
    Since:
      0.8
    See Also:
      - `DefaultExtent​.intersect(Extent)`

  - ### isWorld

    public static boolean isWorld(Extent extent)
    Returns `true` if the given extent covers the world. The current implementation checks if at least one geographic bounding box has a latitude range of -90.0 to 90.0 and a longitude range of -180.0 to 180.0.
    Parameters:
      `extent` - the extent to check, or `null` if none.
    Returns:
      whether the given extent covers the world.
    Since:
      1.5
    See Also:
      - `WORLD`