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.
+```