You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/01/21 14:42:16 UTC

[sis-site] branch main updated: Add more "How to" items.

This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sis-site.git


The following commit(s) were added to refs/heads/main by this push:
     new 3a0e8307 Add more "How to" items.
3a0e8307 is described below

commit 3a0e8307869a9fb4c638bb140ec6f27af6cf461a
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Jan 21 14:16:36 2023 +0100

    Add more "How to" items.
---
 content/howto/_index.md                            |  14 +-
 content/howto/envelopes_in_different_crs.md        |  65 ++++
 content/howto/export_metadata_to_xml.md            | 338 +++++++++++++++++++++
 content/howto/geographic_bounding_box.md           |  78 +++++
 .../raster_values_at_geographic_coordinates.md     |   4 +-
 content/howto/transform_envelopes.md               | 105 +++++++
 6 files changed, 599 insertions(+), 5 deletions(-)

diff --git a/content/howto/_index.md b/content/howto/_index.md
index a7801e8e..0f650fbd 100644
--- a/content/howto/_index.md
+++ b/content/howto/_index.md
@@ -12,9 +12,17 @@ The examples are grouped in the following sections:
 
 * [Instantiate a Universal Transverse Mercator (UTM) projection](howto/instantiate_utm_projection.html)
 * [Instantiate a Pseudo Mercator (a.k.a. Google) projection](faq.html#google)
-* [Transform coordinates between two reference systems](howto/transform_coordinates.html)
-* [Get the EPSG code or URN of an existing {{% CRS %}}](howto/lookup_crs_urn.html)
-* [Determine if two {{% CRS %}} are functionally equal](howto/crs_equality.html)
+* [Get the EPSG code or URN of an existing reference system](howto/lookup_crs_urn.html)
+* [Transform points between two reference systems](howto/transform_coordinates.html)
+* [Transform envelopes between two reference systems](howto/transform_envelopes.html)
+* [Union or intersection of envelopes in different reference systems](howto/envelopes_in_different_crs.html)
+* [Determine if two reference systems are functionally equal](howto/crs_equality.html)
+
+
+# Metadata    {#metadata}
+
+* [Get the geographic bounding box of a data file](howto/geographic_bounding_box.html)
+* [Export metadata of a data file to standard XML](howto/export_metadata_to_xml.html)
 
 
 # Grid coverages (rasters)    {#raster}
diff --git a/content/howto/envelopes_in_different_crs.md b/content/howto/envelopes_in_different_crs.md
new file mode 100644
index 00000000..75b15604
--- /dev/null
+++ b/content/howto/envelopes_in_different_crs.md
@@ -0,0 +1,65 @@
+---
+title: Union or intersection of envelopes in different CRS
+---
+
+Before to compute the union or intersection of two or more envelopes (bounding boxes),
+all envelopes must be [transformed](transform_envelopes.html) to the same Coordinate Reference System (CRS).
+But the choice of a common {{% CRS %}} is not easy.
+We must verify that all envelopes are inside the domain of validity of the common {{% CRS %}},
+which may require to choose a common {{% CRS %}} different than the {{% CRS %}} of all envelopes.
+Apache {{% SIS %}} can handle this task automatically.
+
+
+# Direct dependencies
+
+Maven coordinates                           | Module info
+------------------------------------------- | ----------------------------
+`org.apache.sis.storage:sis-referencing`    | `org.apache.sis.referencing`
+
+
+# Code example
+
+Note that all geographic coordinates below express latitude *before* longitude.
+
+{{< highlight java >}}
+import org.opengis.geometry.Envelope;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.geometry.Envelopes;
+import org.apache.sis.geometry.Envelope2D;
+
+public class UnionOfEnvelopes {
+    /**
+     * Demo entry point.
+     *
+     * @param  args  ignored.
+     * @throws FactoryException   if an error occurred while creating a Coordinate Reference System (CRS).
+     * @throws TransformException if an error occurred while transforming coordinates to the target CRS.
+     */
+    public static void main(String[] args) throws FactoryException, TransformException {
+        CoordinateReferenceSystem crs1 = CommonCRS.WGS84.universal(40, 10);     // 40°N 10°E
+        CoordinateReferenceSystem crs2 = CommonCRS.WGS84.universal(40, 20);     // 40°N 20°E
+
+        Envelope2D bbox1 = new Envelope2D(crs1, 500_000, 400_000, 100_000, 100_000);
+        Envelope2D bbox2 = new Envelope2D(crs2, 400_000, 500_000, 100_000, 100_000);
+        Envelope   union = Envelopes.union(bbox1, bbox2);
+
+        System.out.println("First CRS:    " + crs1.getName());
+        System.out.println("Second CRS:   " + crs2.getName());
+        System.out.println("Selected CRS: " + union.getCoordinateReferenceSystem().getName());
+        System.out.println("Union result: " + union);
+    }
+}
+{{< / highlight >}}
+
+
+# Output
+
+```
+First CRS:    EPSG:WGS 84 / UTM zone 32N
+Second CRS:   EPSG:WGS 84 / UTM zone 34N
+Selected CRS: EPSG:WGS 84
+Union result: BOX(3.6184285185271796 9, 5.428225419697392 21)
+```
diff --git a/content/howto/export_metadata_to_xml.md b/content/howto/export_metadata_to_xml.md
new file mode 100644
index 00000000..1e444eb9
--- /dev/null
+++ b/content/howto/export_metadata_to_xml.md
@@ -0,0 +1,338 @@
+---
+title: Geographic bounding box of a data file
+---
+
+This example prints the metadata of a netCDF file in the XML format
+defined by the ISO 19115-3 international standard.
+The coverage values are not read, only the netCDF file header is read.
+
+
+# Direct dependencies
+
+Maven coordinates                   | Module info                     | Remarks
+----------------------------------- | ------------------------------- | --------------------
+`org.apache.sis.storage:sis-netcdf` | `org.apache.sis.storage.netcdf` |
+`edu.ucar:cdm-core`                 |                                 | For netCDF-4 or HDF5
+
+The `cdm-core` dependency can be omitted for netCDF-3 (a.k.a. "classic"),
+GeoTIFF or any other [formats supported by Apache SIS](../formats.html).
+For the dependencies required for reading GeoTIFF instead of netCDF files,
+see the [geographic bounding box](geographic_bounding_box.html) code example.
+
+
+# Code example
+
+The file name in following code need to be updated for yours data.
+
+{{< highlight java >}}
+import java.util.Map;
+import java.io.File;
+import java.io.StringWriter;
+import javax.xml.bind.JAXBException;
+import javax.xml.transform.stream.StreamResult;
+import org.opengis.metadata.Metadata;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.DataStores;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.xml.XML;
+
+public class ExportMetadata {
+    /**
+     * Demo entry point.
+     *
+     * @param  args  ignored.
+     * @throws DataStoreException if an error occurred while reading the data file.
+     * @throws JAXBException if an error occurred while marshalling metadata to XML.
+     */
+    public static void main(String[] args) throws DataStoreException, JAXBException {
+        try (DataStore store = DataStores.open(new File("CMEMS.nc"))) {
+            Metadata metadata = store.getMetadata();
+            System.out.println(XML.marshal(metadata));
+            /*
+             * By default the XML schema is the most recent version of the standard supported
+             * by Apache SIS. But the legacy version published in 2007 is still in wide use.
+             * The legacy version can be requested with the `METADATA_VERSION` property.
+             */
+            Map<String,String> config = Map.of(XML.METADATA_VERSION, "2007");
+            StringWriter result = new StringWriter();
+            XML.marshal(metadata, new StreamResult(result), config);
+            // Result is in `result.toString()`.
+        }
+    }
+}
+{{< / highlight >}}
+
+
+# Output
+
+The output depends on the data and the locale.
+Below is an example:
+
+{{< highlight xml >}}
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<mdb:MD_Metadata xmlns:cit="http://standards.iso.org/iso/19115/-3/cit/1.0"
+                 xmlns:gco="http://standards.iso.org/iso/19115/-3/gco/1.0"
+                 xmlns:mcc="http://standards.iso.org/iso/19115/-3/mcc/1.0"
+                 xmlns:mdb="http://standards.iso.org/iso/19115/-3/mdb/1.0"
+                 xmlns:mrc="http://standards.iso.org/iso/19115/-3/mrc/1.0"
+                 xmlns:mrd="http://standards.iso.org/iso/19115/-3/mrd/1.0"
+                 xmlns:mri="http://standards.iso.org/iso/19115/-3/mri/1.0"
+                 xmlns:mrl="http://standards.iso.org/iso/19115/-3/mrl/1.0"
+                 xmlns:mrs="http://standards.iso.org/iso/19115/-3/mrs/1.0"
+                 xmlns:msr="http://standards.iso.org/iso/19115/-3/msr/1.0">
+  <mdb:metadataStandard>
+    <!-- Omitted for brevity -->
+  </mdb:metadataStandard>
+  <mdb:spatialRepresentationInfo>
+    <msr:MD_GridSpatialRepresentation>
+      <msr:numberOfDimensions>
+        <gco:Integer>3</gco:Integer>
+      </msr:numberOfDimensions>
+      <msr:axisDimensionProperties>
+        <msr:MD_Dimension>
+          <msr:dimensionName>
+            <msr:MD_DimensionNameTypeCode codeList="(…snip…)#MD_DimensionNameTypeCode" codeListValue="column">Column</msr:MD_DimensionNameTypeCode>
+          </msr:dimensionName>
+          <msr:dimensionSize>
+            <gco:Integer>865</gco:Integer>
+          </msr:dimensionSize>
+        </msr:MD_Dimension>
+      </msr:axisDimensionProperties>
+      <msr:axisDimensionProperties>
+        <msr:MD_Dimension>
+          <msr:dimensionName>
+            <msr:MD_DimensionNameTypeCode codeList="(…snip…)#MD_DimensionNameTypeCode" codeListValue="row">Row</msr:MD_DimensionNameTypeCode>
+          </msr:dimensionName>
+          <msr:dimensionSize>
+            <gco:Integer>1081</gco:Integer>
+          </msr:dimensionSize>
+        </msr:MD_Dimension>
+      </msr:axisDimensionProperties>
+      <msr:axisDimensionProperties>
+        <msr:MD_Dimension>
+          <msr:dimensionName>
+            <msr:MD_DimensionNameTypeCode codeList="(…snip…)#MD_DimensionNameTypeCode" codeListValue="time">Time</msr:MD_DimensionNameTypeCode>
+          </msr:dimensionName>
+          <msr:dimensionSize>
+            <gco:Integer>96</gco:Integer>
+          </msr:dimensionSize>
+        </msr:MD_Dimension>
+      </msr:axisDimensionProperties>
+      <msr:cellGeometry>
+        <msr:MD_CellGeometryCode codeList="(…snip…)#MD_CellGeometryCode" codeListValue="area">Area</msr:MD_CellGeometryCode>
+      </msr:cellGeometry>
+      <msr:transformationParameterAvailability>
+        <gco:Boolean>false</gco:Boolean>
+      </msr:transformationParameterAvailability>
+    </msr:MD_GridSpatialRepresentation>
+  </mdb:spatialRepresentationInfo>
+  <mdb:referenceSystemInfo>
+    <mrs:MD_ReferenceSystem>
+      <mrs:referenceSystemIdentifier>
+        <mcc:MD_Identifier>
+          <mcc:code>
+            <gco:CharacterString>time latitude longitude</gco:CharacterString>
+          </mcc:code>
+        </mcc:MD_Identifier>
+      </mrs:referenceSystemIdentifier>
+    </mrs:MD_ReferenceSystem>
+  </mdb:referenceSystemInfo>
+  <mdb:identificationInfo>
+    <mri:MD_DataIdentification>
+      <mri:citation>
+        <cit:CI_Citation>
+          <cit:title>
+            <gco:CharacterString>Ocean surface 15-minutes mean fields for the Iberia-Biscay-Ireland (IBI) region</gco:CharacterString>
+          </cit:title>
+          <cit:identifier>
+            <mcc:MD_Identifier>
+              <mcc:code>
+                <gco:CharacterString>CMEMS_v5r1_IBI_PHY_NRT_PdE_15minav_20220516_20220516_R20220516_FC01</gco:CharacterString>
+              </mcc:code>
+            </mcc:MD_Identifier>
+          </cit:identifier>
+          <cit:citedResponsibleParty>
+            <cit:CI_Responsibility>
+              <cit:role>
+                <cit:CI_RoleCode codeList="(…snip…)#CI_RoleCode" codeListValue="originator">Originator</cit:CI_RoleCode>
+              </cit:role>
+              <cit:party>
+                <cit:CI_Organisation>
+                  <cit:name>
+                    <gco:CharacterString>Puertos del Estado (PdE)</gco:CharacterString>
+                  </cit:name>
+                </cit:CI_Organisation>
+              </cit:party>
+            </cit:CI_Responsibility>
+          </cit:citedResponsibleParty>
+          <cit:otherCitationDetails>
+            <gco:CharacterString>http://marine.copernicus.eu/</gco:CharacterString>
+          </cit:otherCitationDetails>
+        </cit:CI_Citation>
+      </mri:citation>
+      <mri:pointOfContact>
+        <cit:CI_Responsibility>
+          <cit:role>
+            <cit:CI_RoleCode codeList="(…snip…)#CI_RoleCode" codeListValue="pointOfContact">Point of contact</cit:CI_RoleCode>
+          </cit:role>
+          <cit:party>
+            <cit:CI_Organisation>
+              <cit:name>
+                <gco:CharacterString>Puertos del Estado (PdE)</gco:CharacterString>
+              </cit:name>
+            </cit:CI_Organisation>
+          </cit:party>
+        </cit:CI_Responsibility>
+      </mri:pointOfContact>
+      <mri:spatialRepresentationType>
+        <mcc:MD_SpatialRepresentationTypeCode codeList="(…snip…)#MD_SpatialRepresentationTypeCode" codeListValue="grid">Grid</mcc:MD_SpatialRepresentationTypeCode>
+      </mri:spatialRepresentationType>
+      <mri:resourceFormat>
+        <mrd:MD_Format>
+          <mrd:formatSpecificationCitation>
+            <cit:CI_Citation>
+              <cit:title>
+                <gco:CharacterString>Hierarchical Data Format, version 5</gco:CharacterString>
+              </cit:title>
+              <cit:alternateTitle>
+                <gco:CharacterString>NetCDF-4</gco:CharacterString>
+              </cit:alternateTitle>
+            </cit:CI_Citation>
+          </mrd:formatSpecificationCitation>
+        </mrd:MD_Format>
+      </mri:resourceFormat>
+    </mri:MD_DataIdentification>
+  </mdb:identificationInfo>
+  <mdb:contentInfo>
+    <mrc:MD_CoverageDescription>
+      <mrc:attributeGroup>
+        <mrc:MD_AttributeGroup>
+          <mrc:attribute>
+            <mrc:MD_SampleDimension>
+              <mrc:sequenceIdentifier>
+                <gco:MemberName>
+                  <gco:aName>
+                    <gco:CharacterString>zos</gco:CharacterString>
+                  </gco:aName>
+                  <gco:attributeType>
+                    <gco:TypeName>
+                      <gco:aName>
+                        <gco:CharacterString>short[865][1081][96]</gco:CharacterString>
+                      </gco:aName>
+                    </gco:TypeName>
+                  </gco:attributeType>
+                </gco:MemberName>
+              </mrc:sequenceIdentifier>
+              <mrc:description>
+                <gco:CharacterString>Sea surface height</gco:CharacterString>
+              </mrc:description>
+              <mrc:name>
+                <mcc:MD_Identifier>
+                  <mcc:code>
+                    <gco:CharacterString>sea_surface_height_above_geoid</gco:CharacterString>
+                  </mcc:code>
+                </mcc:MD_Identifier>
+              </mrc:name>
+              <mrc:units>m</mrc:units>
+              <mrc:scaleFactor>
+                <gco:Real>0.001</gco:Real>
+              </mrc:scaleFactor>
+              <mrc:offset>
+                <gco:Real>0.0</gco:Real>
+              </mrc:offset>
+            </mrc:MD_SampleDimension>
+          </mrc:attribute>
+          <mrc:attribute>
+            <mrc:MD_SampleDimension>
+              <mrc:sequenceIdentifier>
+                <gco:MemberName>
+                  <gco:aName>
+                    <gco:CharacterString>uo</gco:CharacterString>
+                  </gco:aName>
+                  <gco:attributeType>
+                    <gco:TypeName>
+                      <gco:aName>
+                        <gco:CharacterString>short[865][1081][96]</gco:CharacterString>
+                      </gco:aName>
+                    </gco:TypeName>
+                  </gco:attributeType>
+                </gco:MemberName>
+              </mrc:sequenceIdentifier>
+              <mrc:description>
+                <gco:CharacterString>Eastward velocity</gco:CharacterString>
+              </mrc:description>
+              <mrc:name>
+                <mcc:MD_Identifier>
+                  <mcc:code>
+                    <gco:CharacterString>eastward_sea_water_velocity</gco:CharacterString>
+                  </mcc:code>
+                </mcc:MD_Identifier>
+              </mrc:name>
+              <mrc:units>m∕s</mrc:units>
+              <mrc:scaleFactor>
+                <gco:Real>0.001</gco:Real>
+              </mrc:scaleFactor>
+              <mrc:offset>
+                <gco:Real>0.0</gco:Real>
+              </mrc:offset>
+            </mrc:MD_SampleDimension>
+          </mrc:attribute>
+          <mrc:attribute>
+            <mrc:MD_SampleDimension>
+              <mrc:sequenceIdentifier>
+                <gco:MemberName>
+                  <gco:aName>
+                    <gco:CharacterString>vo</gco:CharacterString>
+                  </gco:aName>
+                  <gco:attributeType>
+                    <gco:TypeName>
+                      <gco:aName>
+                        <gco:CharacterString>short[865][1081][96]</gco:CharacterString>
+                      </gco:aName>
+                    </gco:TypeName>
+                  </gco:attributeType>
+                </gco:MemberName>
+              </mrc:sequenceIdentifier>
+              <mrc:description>
+                <gco:CharacterString>Northward velocity</gco:CharacterString>
+              </mrc:description>
+              <mrc:name>
+                <mcc:MD_Identifier>
+                  <mcc:code>
+                    <gco:CharacterString>northward_sea_water_velocity</gco:CharacterString>
+                  </mcc:code>
+                </mcc:MD_Identifier>
+              </mrc:name>
+              <mrc:units>m∕s</mrc:units>
+              <mrc:scaleFactor>
+                <gco:Real>0.001</gco:Real>
+              </mrc:scaleFactor>
+              <mrc:offset>
+                <gco:Real>0.0</gco:Real>
+              </mrc:offset>
+            </mrc:MD_SampleDimension>
+          </mrc:attribute>
+        </mrc:MD_AttributeGroup>
+      </mrc:attributeGroup>
+    </mrc:MD_CoverageDescription>
+  </mdb:contentInfo>
+  <mdb:resourceLineage>
+    <mrl:LI_Lineage>
+      <mrl:source>
+        <mrl:LI_Source>
+          <mrl:description>
+            <gco:CharacterString>IBI-MFC (PdE Production Center)</gco:CharacterString>
+          </mrl:description>
+        </mrl:LI_Source>
+      </mrl:source>
+    </mrl:LI_Lineage>
+  </mdb:resourceLineage>
+  <mdb:metadataScope>
+    <mdb:MD_MetadataScope>
+      <mdb:resourceScope>
+        <mcc:MD_ScopeCode codeList="(…snip…)#MD_ScopeCode" codeListValue="dataset">Dataset</mcc:MD_ScopeCode>
+      </mdb:resourceScope>
+    </mdb:MD_MetadataScope>
+  </mdb:metadataScope>
+</mdb:MD_Metadata>
+{{< / highlight >}}
diff --git a/content/howto/geographic_bounding_box.md b/content/howto/geographic_bounding_box.md
new file mode 100644
index 00000000..403b1c36
--- /dev/null
+++ b/content/howto/geographic_bounding_box.md
@@ -0,0 +1,78 @@
+---
+title: Geographic bounding box of a data file
+---
+
+This example prints the bounding box of a GeoTIFF image.
+The pixel values are not read, only the GeoTIFF file header is read.
+If the file contains many images, the bounding box of each image is printed.
+
+
+# Direct dependencies
+
+Maven coordinates                           | Module info                           | Remarks
+------------------------------------------- | ------------------------------------- | -----------------------------
+`org.apache.sis.storage:sis-geotiff`        | `org.apache.sis.storage.geotiff`      |
+`org.apache.sis.non-free:sis-embedded-data` | `org.apache.sis.referencing.database` | Optional. Non-Apache license.
+
+The [EPSG dependency](../epsg.html) may or may not be needed,
+depending how the Coordinate Reference System (CRS) is encoded in the GeoTIFF file.
+
+
+# Code example
+
+The file name in following code need to be updated for yours data.
+
+{{< highlight java >}}
+import java.io.File;
+import org.apache.sis.storage.Aggregate;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.DataStores;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.metadata.iso.extent.Extents;
+import org.apache.sis.storage.Resource;
+
+public class GetBBOX {
+    /**
+     * Demo entry point.
+     *
+     * @param  args  ignored.
+     * @throws DataStoreException if an error occurred while reading the data file.
+     */
+    public static void main(String[] args) throws DataStoreException {
+        try (DataStore store = DataStores.open(new File("Airport.tiff"))) {
+            System.out.println("For the whole file");
+            System.out.println(Extents.getGeographicBoundingBox(store.getMetadata()));
+            if (store instanceof Aggregate agg) {
+                for (Resource component : agg.components()) {
+                    System.out.println("For component " + component.getIdentifier());
+                    System.out.println(Extents.getGeographicBoundingBox(component.getMetadata()));
+                }
+            }
+        }
+    }
+}
+{{< / highlight >}}
+
+
+# Output
+
+The output depends on the data and the locale.
+Below is an example:
+
+```
+For the whole file
+Geographic bounding box
+  ├─West bound longitude…… 2°31′33.51153867218″E
+  ├─East bound longitude…… 2°34′15.75923342244″E
+  ├─South bound latitude…… 48°59′20.7793385101″N
+  ├─North bound latitude…… 49°01′07.5236778991″N
+  └─Extent type code……………… True
+
+For component Optional[Airport:1]
+Geographic bounding box
+  ├─West bound longitude…… 2°31′33.51153867218″E
+  ├─East bound longitude…… 2°34′15.75923342244″E
+  ├─South bound latitude…… 48°59′20.7793385101″N
+  ├─North bound latitude…… 49°01′07.5236778991″N
+  └─Extent type code……………… True
+```
diff --git a/content/howto/raster_values_at_geographic_coordinates.md b/content/howto/raster_values_at_geographic_coordinates.md
index 5dd8ecf1..1618e45b 100644
--- a/content/howto/raster_values_at_geographic_coordinates.md
+++ b/content/howto/raster_values_at_geographic_coordinates.md
@@ -122,9 +122,9 @@ public class RasterValuesAtGeographicCoordinates {
      * @throws DataStoreException if an error occurred while reading the raster.
      */
     private static void printComponents(DataStore store) throws DataStoreException {
-        if (store instanceof Aggregate a) {
+        if (store instanceof Aggregate agg) {
             System.out.println("Components found in the data store:");
-            for (Resource component : a.components()) {
+            for (Resource component : agg.components()) {
                 component.getIdentifier().ifPresent((id) -> System.out.println("- " + id));
             }
         } else {
diff --git a/content/howto/transform_envelopes.md b/content/howto/transform_envelopes.md
new file mode 100644
index 00000000..9d8cb450
--- /dev/null
+++ b/content/howto/transform_envelopes.md
@@ -0,0 +1,105 @@
+---
+title: Transform envelopes (bounding boxes)
+---
+
+The transformation of envelopes (or bounding boxes) is a much more difficult task
+than transforming the four corners of a rectangle.
+The rectangle straight lines in source {{% CRS %}} may become curves in the target {{% CRS %}}
+with minimum and maximum values that are not located in any corner.
+The calculation is also more complicated if the envelope contains a pole or crosses the anti-meridian.
+Apache {{% SIS %}} handles those complexities in convenience static methods.
+This is demonstrated by comparing the result of using Apache {{% SIS %}} methods
+with the result of transforming the four corners.
+The latter naive approach has an error of 40 kilometres in this example.
+
+While the example below uses a two-dimensional envelope,
+the same Apache {{% SIS %}} methods can transform efficiently _N_-dimensional envelopes.
+
+
+# Direct dependencies
+
+Maven coordinates                           | Module info
+------------------------------------------- | ----------------------------
+`org.apache.sis.storage:sis-referencing`    | `org.apache.sis.referencing`
+
+
+# Code example
+
+Note that all geographic coordinates below express latitude *before* longitude.
+
+{{< highlight java >}}
+import org.opengis.geometry.Envelope;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.geometry.Envelopes;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.geometry.DirectPosition2D;
+
+public class TransformEnvelopes {
+    /**
+     * Demo entry point.
+     *
+     * @param  args  ignored.
+     * @throws FactoryException   if an error occurred while creating a Coordinate Reference System (CRS).
+     * @throws TransformException if an error occurred while transforming coordinates to the target CRS.
+     */
+    public static void main(String[] args) throws FactoryException, TransformException {
+        CoordinateReferenceSystem sourceCRS = CommonCRS.WGS84.geographic();
+        CoordinateReferenceSystem targetCRS = CommonCRS.WGS84.universal(90, 0);  // Polar stereographic.
+
+        GeneralEnvelope bbox = new GeneralEnvelope(sourceCRS);
+        bbox.setRange(0,  84, 88);             // Latitudes.
+        bbox.setRange(1, -20, 50);             // Longitudes.
+
+        Envelope transformed = Envelopes.transform(bbox, targetCRS);
+        Envelope corners = transformCorners(bbox, CRS.findOperation(sourceCRS, targetCRS, null));
+
+        System.out.println("Source:  " + bbox);
+        System.out.println("Result:  " + transformed);
+        System.out.println("Corners: " + corners);
+        System.out.println("Errors on Y axis: "
+                + (corners.getMinimum(1) - transformed.getMinimum(1)) + " metres.");
+    }
+
+    /**
+     * Transforms the 4 corners of a two-dimensional envelope. This is not recommended.
+     * This method is provided only for demonstrating that this approach is not sufficient.
+     *
+     * @param  bbox       the bounding box to transform.
+     * @param  operation  the coordinate operation to use for transforming corners.
+     * @return the result of transforming the 4 corners of the provided bounding box.
+     * @throws TransformException if a coordinate can not be converted.
+     */
+    private static Envelope transformCorners(Envelope bbox, CoordinateOperation operation) throws TransformException {
+        double[] corners = {
+            bbox.getMinimum(0), bbox.getMinimum(1),
+            bbox.getMaximum(0), bbox.getMinimum(1),
+            bbox.getMaximum(0), bbox.getMaximum(1),
+            bbox.getMinimum(0), bbox.getMaximum(1)
+        };
+        operation.getMathTransform().transform(corners, 0, corners, 0, 4);
+        GeneralEnvelope result = new GeneralEnvelope(operation.getTargetCRS());
+        for (int i=0; i<result.getDimension(); i++) {
+            result.setRange(i, corners[i], corners[i]);
+        }
+        for (int i=0; i<corners.length;) {
+            result.add(new DirectPosition2D(corners[i++], corners[i++]));
+        }
+        return result;
+    }
+}
+{{< / highlight >}}
+
+
+# Output
+
+```
+Source:  BOX(84 -20, 88 50)
+Result:  BOX(1771965.695226812 1333272.44494046, 2510743.0524805877 1857256.625440407)
+Corners: BOX(1771965.695226812 1373480.89677463, 2510743.0524805877 1857256.625440407)
+Errors on Y axis: 40208.451834172476 metres.
+```