# Class Envelopes

Object
Envelopes

public final class Envelopes extends Object
Transforms envelopes to new Coordinate Reference Systems, and miscellaneous utilities.

## Envelope transformations

All `transform(…)` methods in this class take in account the curvature of the transformed shape. For example, the shape of a geographic envelope (figure below on the left side) is not rectangular in a conic projection (figure below on the right side). In order to get the envelope represented by the red rectangle, projecting the four corners of the geographic envelope is not sufficient since we would miss the southerner part.

Example of curvature induced by a map projection
Envelope before map projection | Shape of the projected envelope
--- | ---

Apache SIS tries to detect the curvature by transforming intermediate points in addition to the corners. While optional, it is strongly recommended that all `MathTransform` implementations involved in the operation (directly or indirectly) support derivative, for more accurate calculation of curve extremum. This is the case of most Apache SIS implementations.

The `transform(…)` methods in this class expect an arbitrary `Envelope` with one of the following arguments: `MathTransform`, `CoordinateOperation` or `CoordinateReferenceSystem`. The recommended method is the one expecting a `CoordinateOperation` object, since it contains sufficient information for handling the cases of envelopes that encompass a pole. The method expecting a `CoordinateReferenceSystem` object is merely a convenience method that infers the coordinate operation itself, but at the cost of performance if the same operation needs to be applied on many envelopes.

Since:
  0.3
See Also:
  - `Extents`
  - `CRS`

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static Envelope`
  `compound(Envelope... components)`
  Puts together a list of envelopes, each of them using an independent coordinate reference system.
  `static CoordinateOperation`
  `findOperation(Envelope source, Envelope target)`
  Finds a mathematical operation from the CRS of the given source envelope to the CRS of the given target envelope.
  `static Envelope`
  `fromWKT(CharSequence wkt)`
  Returns the bounding box of a geometry defined in Well Known Text (WKT) format.
  `static GeneralEnvelope`
  `intersect(Envelope... envelopes)`
  Computes the intersection of all given envelopes, transforming them to a common CRS if necessary.
  `static String`
  `toPolygonWKT(Envelope envelope)`
  Formats the given envelope as a `POLYGON` element in the Well Known Text (WKT) format.
  `static String`
  `toString(Envelope envelope)`
  Formats the given envelope as a `BOX` element.
  `static Optional<Range<Instant>>`
  `toTimeRange(Envelope envelope)`
  Returns the time range of the first dimension associated to a temporal CRS.
  `static Envelope`
  `transform(Envelope envelope, CoordinateReferenceSystem targetCRS)`
  Transforms the given envelope to the specified CRS.
  `static GeneralEnvelope`
  `transform(CoordinateOperation operation, Envelope envelope)`
  Transforms an envelope using the given coordinate operation.
  `static GeneralEnvelope`
  `transform(MathTransform transform, Envelope envelope)`
  Transforms an envelope using the given math transform.
  `static Map<Parameters, GeneralEnvelope>`
  `transformWithWraparound(MathTransform transform, Envelope envelope)`
  Transforms potentially many times an envelope using the given math transform.
  `static GeneralEnvelope`
  `union(Envelope... envelopes)`
  Computes the union of all given envelopes, transforming them to a common CRS if necessary.

  ### Methods inherited from class Object

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

- ## Method Details

  - ### compound

    public static Envelope compound(Envelope... components) throws FactoryException
    Puts together a list of envelopes, each of them using an independent coordinate reference system. The dimension of the returned envelope is the sum of the dimension of all components. If all components have a coordinate reference system, then the returned envelope will have a compound coordinate reference system.
    Parameters:
      `components` - the envelopes to aggregate in a single envelope, in the given order.
    Returns:
      the aggregation of all given envelopes.
    Throws:
      `FactoryException` - if the geodetic factory failed to create the compound CRS.
    Since:
      1.0
    See Also:
      - `CRS​.compound(CoordinateReferenceSystem...)`
      - `MathTransforms​.compound(MathTransform...)`

  - ### union

    public static GeneralEnvelope union(Envelope... envelopes) throws TransformException
    Computes the union of all given envelopes, transforming them to a common CRS if necessary. If all envelopes use equivalent CRS, or if the CRS of all envelopes is `null`, then the union is computed without transforming any envelope. Otherwise, all envelopes are transformed to a common CRS before union is computed. The CRS of the returned envelope may different than the CRS of all given envelopes.
    Parameters:
      `envelopes` - the envelopes for which to compute union. Null elements are ignored.
    Returns:
      union of given envelopes, or `null` if the given array does not contain non-null elements.
    Throws:
      `TransformException` - if this method cannot determine a common CRS, or if a transformation failed.
    Since:
      1.0
    See Also:
      - `GeneralEnvelope​.add(Envelope)`

  - ### intersect

    public static GeneralEnvelope intersect(Envelope... envelopes) throws TransformException
    Computes the intersection of all given envelopes, transforming them to a common CRS if necessary. If all envelopes use equivalent CRS, or if the CRS of all envelopes is `null`, then the intersection is computed without transforming any envelope. Otherwise, all envelopes are transformed to a common CRS before intersection is computed. The CRS of the returned envelope may different than the CRS of all given envelopes.
    Parameters:
      `envelopes` - the envelopes for which to compute intersection. Null elements are ignored.
    Returns:
      intersection of given envelopes, or `null` if the given array does not contain non-null elements.
    Throws:
      `TransformException` - if this method cannot determine a common CRS, or if a transformation failed.
    Since:
      1.0
    See Also:
      - `GeneralEnvelope​.intersect(Envelope)`

  - ### findOperation

    public static CoordinateOperation findOperation(Envelope source, Envelope target) throws FactoryException
    Finds a mathematical operation from the CRS of the given source envelope to the CRS of the given target envelope. For non-null georeferenced envelopes, this method is equivalent to the following code with `areaOfInterest` computed as the union of the two envelopes:

    > `return CRS​.findOperation(CoordinateReferenceSystem, CoordinateReferenceSystem, GeographicBoundingBox) CRS​.findOperation(source​.getCoordinateReferenceSystem(), target​.getCoordinateReferenceSystem(), areaOfInterest)`

    If at least one envelope is null or has no CRS, then this method returns `null`.
    Parameters:
      `source` - the source envelope, or `null`.
      `target` - the target envelope, or `null`.
    Returns:
      the mathematical operation from the CRS of the `source` envelope to the CRS of the `target` envelope, or `null` if at least one argument is null or has no CRS.
    Throws:
      `OperationNotFoundException` - if no operation was found between the CRSs of the given envelopes.
      `FactoryException` - if the operation cannot be created for another reason.
    Since:
      1.0
    See Also:
      - `CRS​.findOperation(CoordinateReferenceSystem, CoordinateReferenceSystem, GeographicBoundingBox)`

  - ### transform

    public static Envelope transform(Envelope envelope, CoordinateReferenceSystem targetCRS) throws TransformException
    Transforms the given envelope to the specified CRS. If any argument is null, or if the envelope CRS is null or the same instance as the given target CRS, then the given envelope is returned unchanged. Otherwise a new transformed envelope is returned.

    #### Performance tip

    If there is many envelopes to transform with the same source and target CRS, then it is more efficient to get the `CoordinateOperation` or `MathTransform` instance once and invoke one of the others `transform(…)` methods.
    Parameters:
      `envelope` - the envelope to transform (may be `null`).
      `targetCRS` - the target CRS (may be `null`).
    Returns:
      a new transformed envelope, or directly `envelope` if no change was required.
    Throws:
      `TransformException` - if a transformation was required and failed.
    Since:
      0.5

  - ### transform

    public static GeneralEnvelope transform(CoordinateOperation operation, Envelope envelope) throws TransformException
    Transforms an envelope using the given coordinate operation. The transformation is only approximated: the returned envelope may be bigger than the smallest possible bounding box, but should not be smaller in most cases.

    This method can handle the case where the envelope contains the North or South pole, or when it cross the ±180° longitude.

    #### Usage note

    If the envelope CRS is non-null, then the caller should ensure that the operation source CRS is the same as the envelope CRS. In case of mismatch, this method transforms the envelope to the operation source CRS before to apply the operation. This extra step may cause a lost of accuracy. In order to prevent this method from performing such pre-transformation (if not desired), callers can ensure that the envelope CRS is `null` before to call this method.
    Parameters:
      `operation` - the operation to use.
      `envelope` - envelope to transform, or `null`. This envelope will not be modified.
    Returns:
      the transformed envelope, or `null` if `envelope` was null.
    Throws:
      `TransformException` - if a transform failed.
    Since:
      0.5
    See Also:
      - `transform(MathTransform, Envelope)`

  - ### transform

    public static GeneralEnvelope transform(MathTransform transform, Envelope envelope) throws TransformException
    Transforms an envelope using the given math transform. The transformation is only approximated: the returned envelope may be bigger than necessary, or smaller than required if the bounding box contains a pole. The coordinate reference system of the returned envelope will be null.

    #### Limitation

    This method cannot handle the case where the envelope contains the North or South pole. Furthermore, envelopes crossing the ±180° longitude are handled only if the given transform contains `WraparoundTransform` steps, as this method does not add such steps itself. For a more robust envelope transformation, use `transform(CoordinateOperation, Envelope)` instead.
    Parameters:
      `transform` - the transform to use.
      `envelope` - envelope to transform, or `null`. This envelope will not be modified.
    Returns:
      the transformed envelope, or `null` if `envelope` was null.
    Throws:
      `TransformException` - if a transform failed.
    Since:
      0.5
    See Also:
      - `transform(CoordinateOperation, Envelope)`

  - ### transformWithWraparound

    public static Map<Parameters, GeneralEnvelope> transformWithWraparound(MathTransform transform, Envelope envelope) throws TransformException
    Transforms potentially many times an envelope using the given math transform. If the given envelope is `null`, then this method returns an empty map. Otherwise, if the given transform does not contain any `WraparoundTransform` step, then this method is equivalent to `transform(MathTransform, Envelope)` returned in a singleton map. Otherwise, this method returns many transformed envelopes where each envelope describes approximately the same region. For example:
    - If the envelope CRS is geographic, the map values are the same envelope shifted by 360° of longitude.
    - If the envelope CRS is projected, then the 360° shifts are applied before the map projection. It may result in very different coordinates.
    The keys identify which translations were applied on wraparound axes for computing the associated envelope. For example, a key may identify an envelope computed with a shift of 360° of longitude on some coordinates, and another key may identify the same envelope but computed with a shift of −360° of longitude. The content of those keys is implementation dependent and users should not rely on the exact parameters. The main contract is that, for envelopes computed with the same transform, the values that are associated with equal keys were computed with wraparounds applied in the same way (e.g. +360° versus −360°).

    Example: the union of two envelopes taking in account wraparounds can be computed as below:

            MathTransform transform = ...;
            Map<Parameters, GeneralEnvelope> result = Envelopes.transformWithWraparound(transform, envelope1);
            Envelopes.transformWithWraparound(transform, envelope2).forEach((key, value) -> {
                GeneralEnvelope previous = result.putIfAbsent(key, value);
                if (previous != null) previous.add(value);   // Envelope union.
            });

    Note that the key may be `null` if the given transform does not contain any `WraparoundTransform` step.
    Parameters:
      `transform` - the transform to use.
      `envelope` - envelope to transform, or `null`. This envelope will not be modified.
    Returns:
      the transformed envelopes, or an empty map if `envelope` was null.
    Throws:
      `TransformException` - if a transform failed.
    Since:
      1.5
    See Also:
      - `transform(MathTransform, Envelope)`
      - `WraparoundTransform`

  - ### fromWKT

    public static Envelope fromWKT(CharSequence wkt) throws FactoryException
    Returns the bounding box of a geometry defined in Well Known Text (WKT) format. This method does not check the consistency of the provided WKT. For example, it does not check that every points in a `LINESTRING` have the same dimension. However, this method ensures that the parenthesis are balanced, in order to catch some malformed WKT. See `GeneralEnvelope(CharSequence)` for more information about the parsing rules.

    #### Examples

    - `BOX(-180 -90, 180 90)` (not really a geometry, but understood by many software products)
    - `POINT(6 10)`
    - `MULTIPOLYGON(((1 1, 5 1, 1 5, 1 1),(2 2, 3 2, 3 3, 2 2)))`
    - `GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(3 8,7 10))`
    Parameters:
      `wkt` - the `BOX`, `POLYGON` or other kind of element to parse.
    Returns:
      the envelope of the given geometry.
    Throws:
      `FactoryException` - if the given WKT cannot be parsed.
    See Also:
      - `toString(Envelope)`
      - `CRS​.fromWKT(String)`
      - `org​.apache​.sis​.io​.wkt`

  - ### toString

    public static String toString(Envelope envelope)
    Formats the given envelope as a `BOX` element. The output is like below, where n is the number of dimensions (omitted if equals to 2):

    > `BOX` n `D(`lower corner`,` upper corner`)`

    The string returned by this method can be parsed by the `GeneralEnvelope` constructor.

    #### Note on standards

    The `BOX` element is not part of the standard Well Known Text (WKT) format. However, it is understood by many software libraries, for example GDAL and PostGIS.
    Parameters:
      `envelope` - the envelope to format.
    Returns:
      this envelope as a `BOX` or `BOX3D` (most typical dimensions) element.
    See Also:
      - `fromWKT(CharSequence)`
      - `org​.apache​.sis​.io​.wkt`

  - ### toPolygonWKT

    public static String toPolygonWKT(Envelope envelope) throws IllegalArgumentException
    Formats the given envelope as a `POLYGON` element in the Well Known Text (WKT) format. `POLYGON` can be used as an alternative to `BOX` when the element needs to be considered as a standard WKT geometry.

    The string returned by this method can be parsed by the `GeneralEnvelope` constructor.

    Parameters:
      `envelope` - the envelope to format.
    Returns:
      the envelope as a `POLYGON` in WKT format.
    Throws:
      `IllegalArgumentException` - if the given envelope cannot be formatted.
    See Also:
      - `org​.apache​.sis​.io​.wkt`

  - ### toTimeRange

    public static Optional<Range<Instant>> toTimeRange(Envelope envelope)
    Returns the time range of the first dimension associated to a temporal CRS. This convenience method converts floating point values to instants using `DefaultTemporalCRS​.toInstant(double)`.
    Parameters:
      `envelope` - envelope from which to extract time range, or `null` if none.
    Returns:
      time range in the given envelope.
    Since:
      1.1
    See Also:
      - `AbstractEnvelope​.getTimeRange()`
      - `GeneralEnvelope​.setTimeRange(Instant, Instant)`