blob: ac358462cf74a4d3ced90b14986371a32440798f [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>CoordinateOperations</title>
<meta charset="UTF-8"/>
<link rel="stylesheet" type="text/css" href="../../../content/book/book.css"/>
</head>
<body>
<!--
Content below this point is copied in "(…)/content/book/en/developer-guide.html" file
by the 'org.apache.sis.internal.book.Assembler' class in 'sis-build-helper' module.
-->
<section>
<header>
<h2 id="CoordinateOperations">Coordinate operations</h2>
</header>
<p>
Given a <em>source</em> coordinate reference system (<abbr>CRS</abbr>) in which existing coordinate values are expressed,
and a <em>target</em> coordinate reference system in which coordinate values are desired,
Apache <abbr>SIS</abbr> can provide a <em>coordinate operation</em> performing the conversion or transformation work.
The search for coordinate operations may use a third argument, optional but recommended,
which is the geographic area of the data to transform.
That later argument is recommended because coordinate operations are often valid only in a some geographic area
(typically a particular country or state), and many transformations may exist
for the same pair of source and target <abbr>CRS</abbr> but different domain of validity.
Different coordinate operations may also be different compromises between accuracy and their domain of validity,
and specifying a smaller area of interest may allow Apache <abbr>SIS</abbr> to select a more accurate operation.
</p>
<div class="example"><p><b>Example:</b>
the <abbr>EPSG</abbr> geodetic dataset (as of version 7.9) defines 77 coordinate operations from the
<cite>North American Datum 1927</cite> (EPSG:4267) coordinate reference system to the
<cite>World Geodetic System 1984</cite> (EPSG:4326) <abbr>CRS</abbr>.
There is one operation valid only for coordinate transformations in Québec,
another operation valid for coordinate transformations in Texas west of 100°W,
another operation for the same state but east of 100°W, <i>etc</i>.
If the user did not specified any geographic area of interest,
then Apache <abbr>SIS</abbr> defaults on the coordinate operation which is valid in the largest area.
In this example, the “largest area” criterion results in the selection of a coordinate operation valid for Canada,
not <abbr>USA</abbr>.</p>
</div>
<h3 id="CRS.findOperation">Getting a coordinate operation</h3>
<p>
The easiest way to obtain a coordinate operation from above-cited information
is to use the <code>org.apache.sis.referencing.CRS</code> convenience class:
</p>
<pre><code>CoordinateOperation cop = <code class="SIS">CRS.findOperation</code>(sourceCRS, targetCRS, areaOfInterest);</code></pre>
<p>
Among the information provided by <code>CoordinateOperation</code> object, the following are of special interest:
</p>
<ul>
<li>The <cite>domain of validity</cite>, either as a textual description (e.g. “Canada – onshore and offshore”)
or with the coordinates of a geographic bounding box.</li>
<li>The <cite>positional accuracy</cite>, which may be anything from 1 centimetre to a few kilometres.</li>
<li>The coordinate operation subtype. Among them, two sub-types provide the same functionalities but with a significant conceptual difference:
<ul class="verbose">
<li>
Coordinate <strong>conversions</strong> are fully determined by mathematical formulas.
Those conversions would have an infinite precision if it was not for the unavoidable rounding errors
inherent to floating-point calculations.
Map projections are in this category.
</li><li>
Coordinate <strong>transformations</strong> are defined empirically.
They often have errors of a few metres which are not caused by limitation in computer accuracy.
Those errors exist because transformations are only approximations of a more complex reality.
Datum shifts from <abbr title="North American Datum 1927">NAD27</abbr> to <abbr title="North American Datum 1983">NAD83</abbr>
are in this category.
</li>
</ul>
</li>
</ul>
<p>
If the coordinate operation is an instance of <code>Transformation</code>,
then the instance selected by <abbr>SIS</abbr> may be one among many possibilities depending on the area of interest.
Furthermore its accuracy is certainly less than the centimetric accuracy that we can expect from a <code>Conversion</code>.
Consequently verifying the domain of validity and the positional accuracy declared in the transformation metadata is of particular importance.
</p>
<h3 id="MathTransform">Executing an operation on coordinate values</h3>
<p>
The <code>CoordinateOperation</code> object introduced in above section provides high-level informations
(source and target <abbr>CRS</abbr>, domain of validity, positional accuracy, operation parameters, <i>etc</i>).
The actual mathematical work is performed by a separated object obtained by a call to <code>CoordinateOperation.getMathTransform()</code>.
At the difference of <code>CoordinateOperation</code> instances, <code>MathTransform</code> instances do not carry any metadata.
They are kind of black box which know nothing about the source and target <abbr>CRS</abbr>
(actually the same <code>MathTransform</code> can be used for different pairs of <abbr>CRS</abbr> if the mathematical work is the same), domain or accuracy.
Furthermore <code>MathTransform</code> may be implemented in a very different way than what <code>CoordinateOperation</code> said.
In particular many conceptually different coordinate operations (e.g. longitude rotations,
change of units of measurement, conversions between two Mercator projections on the same datum, <i>etc.</i>)
are implemented by <code>MathTransform</code> as <a href="#AffineTransform">affine transforms</a> and concatenated for efficiency,
even if <code>CoordinateOperation</code> reports them as a chain of Mercator and other operations.
The “<a href="#CoordinateOperationSteps">conceptual versus real chain of coordinate operations</a>” section explains the differences in more details.
</p>
<p>
The following Java code performs a map projection from geographic coordinates on the <cite>World Geodetic System 1984</cite> (<abbr>WGS84</abbr>) datum
coordinates in the <cite>WGS 84 / UTM zone 33N</cite> coordinate reference system.
In order to make the example a little bit simpler, this code uses predefined constants given by the <code>CommonCRS</code> convenience class.
But more advanced applications will typically use <abbr>EPSG</abbr> codes instead.
Note that all geographic coordinates below express latitude before longitude.
</p>
<pre><code>import org.opengis.geometry.DirectPosition;
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.DirectPosition2D;
public class MyApp {
public static void main(String[] args) throws FactoryException, TransformException {
CoordinateReferenceSystem sourceCRS = CommonCRS.WGS84.geographic();
CoordinateReferenceSystem targetCRS = CommonCRS.WGS84.universal(40, 14); // Get whatever zone is valid for 14°E.
CoordinateOperation operation = <code class="SIS">CRS.findOperation</code>(sourceCRS, targetCRS, null);
// The above lines are costly and should be performed only once before to project many points.
// In this example, the operation that we got is valid for coordinates in geographic area from
// 12°E to 18°E (UTM zone 33) and 0°N to 84°N.
DirectPosition ptSrc = new DirectPosition2D(40, 14); // 40°N 14°E
DirectPosition ptDst = operation.getMathTransform().transform(ptSrc, null);
System.out.println("Source: " + ptSrc);
System.out.println("Target: " + ptDst);
}
}</code></pre>
<h3 id="TransformDerivative">Partial derivatives of coordinate operations</h3>
<p>
Previous section shows how to project a coordinate from one reference system to another one.
There is another, less known, operation which does not compute the projected coordinates of a given point,
but instead the derivative of the projection function at that point.
This operation was defined in an older Open Geospatial specification,
<a href="https://www.ogc.org/standards/ct">OGC 01-009</a>, now considered obsolete but still useful.
Let <var>P</var> be a map projection converting degrees of latitude and longitude (<var>φ</var><var>λ</var>)
into projected coordinates (<var>x</var><var>y</var>) in metres.
The formula below represents the map projection result as a column matrix
(reason will become clearer soon):
</p>
<div class="row-of-boxes">
<div style="min-width:350px; padding-right:60px">
<div class="caption">Equation</div>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="MathML capable browser required">
<mi>P</mi><mo>(</mo><mi>φ</mi><mo>,</mo><mi>λ</mi><mo>)</mo>
<mo>=</mo>
<mfenced open="[" close="]">
<mtable>
<mtr><mtd><mi>x</mi><mo>(</mo><mi>φ</mi><mo>,</mo><mi>λ</mi><mo>)</mo></mtd></mtr>
<mtr><mtd><mi>y</mi><mo>(</mo><mi>φ</mi><mo>,</mo><mi>λ</mi><mo>)</mo></mtd></mtr>
</mtable>
</mfenced>
</math>
</div>
<div style="min-width:500px; padding-left:60px">
<div class="caption">Java code</div>
<pre style="margin:0"><code>DirectPosition geographic = new DirectPosition2D(<var>φ</var>, <var>λ</var>);
DirectPosition projected = <var><b>P</b></var>.transform(geographic, null);
double <var>x</var> = projected.getOrdinate(0);
double <var>y</var> = projected.getOrdinate(1);</code></pre>
</div>
</div>
<p>The map projection partial derivate at this point can be represented by a Jacobian matrix:</p>
<div class="row-of-boxes">
<div style="min-width:350px; padding-right:60px">
<div class="caption">Equation</div>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="MathML capable browser required">
<msup><mi>P</mi><mo></mo></msup><mo>(</mo><mi>φ</mi><mo>,</mo><mi>λ</mi><mo>)</mo>
<mo>=</mo>
<msub><mi>JAC</mi><mrow><mi>P</mi><mo>(</mo><mi>φ</mi><mo>,</mo><mi>λ</mi><mo>)</mo></mrow></msub>
<mo>=</mo>
<mfenced open="[" close="]">
<mtable>
<mtr>
<mtd><mfrac><mrow><mo></mo><mi>x</mi><mo>(</mo><mi>φ</mi><mo>,</mo><mi>λ</mi><mo>)</mo></mrow><mrow><mo></mo><mi>φ</mi></mrow></mfrac></mtd>
<mtd><mfrac><mrow><mo></mo><mi>x</mi><mo>(</mo><mi>φ</mi><mo>,</mo><mi>λ</mi><mo>)</mo></mrow><mrow><mo></mo><mi>λ</mi></mrow></mfrac></mtd>
</mtr>
<mtr>
<mtd><mfrac><mrow><mo></mo><mi>y</mi><mo>(</mo><mi>φ</mi><mo>,</mo><mi>λ</mi><mo>)</mo></mrow><mrow><mo></mo><mi>φ</mi></mrow></mfrac></mtd>
<mtd><mfrac><mrow><mo></mo><mi>y</mi><mo>(</mo><mi>φ</mi><mo>,</mo><mi>λ</mi><mo>)</mo></mrow><mrow><mo></mo><mi>λ</mi></mrow></mfrac></mtd>
</mtr>
</mtable>
</mfenced>
</math>
</div>
<div style="min-width:500px; padding-left:60px">
<div class="caption">Java code</div>
<pre style="margin:0"><code>DirectPosition geographic = new DirectPosition2D(<var>φ</var>, <var>λ</var>);
Matrix jacobian = <var><b>P</b></var>.derivative(geographic);
double dx_dλ = jacobian.getElement(0,1);
double dy_dφ = jacobian.getElement(1,0);</code></pre>
</div>
</div>
<p>
Remaining equations in this section will abridge
<var>x</var>(<var>λ</var><var>φ</var>) as ∂<var>x</var> and
<var>y</var>(<var>λ</var><var>φ</var>) as ∂<var>y</var>,
but reader should keep in mind that each of those derivative values depends on the (<var>λ</var><var>φ</var>) coordinate given at Jacobian matrix calculation time.
The first matrix column tells us that if we apply a small displacement of ∂<var>φ</var> degrees of latitude from the (<var>φ</var><var>λ</var>) position,
— in other words if we move at the (<var>φ</var> + ∂<var>φ</var>, <var>λ</var>) geographic position —
then the projected coordinate will be displaced by (∂<var>x</var>, ∂<var>λ</var>) metres
— in other words it will become (<var>x</var> + ∂<var>x</var>, <var>y</var> + ∂<var>λ</var>).
Similarly the last matrix column gives us the displacement that happen on the projected coordinate
if we apply a small displacement of ∂<var>λ</var> degrees of longitude on the source geographic coordinate.
We can visualize such displacements in a figure like below.
This figure shows the derivative at two points, <var>P</var><sub>1</sub> and <var>P</var><sub>2</sub>,
for emphasing that the result change for every points.
In that figure, vectors <var>U</var> et <var>V</var> stand for the first and second column respectively
in the Jacobian matrices.
</p>
<div class="row-of-boxes">
<div>
<img style="border: solid 1px" src="../../../content/book/images/Derivatives.png" alt="Example of a map projection derivative"/>
</div><div>
<p>where vectors are related to the matrix by:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="MathML capable browser required">
<mtable><mtr>
<mtd>
<mover><mi>U</mi><mo></mo></mover><mo>=</mo>
<mfenced open="[" close="]">
<mtable>
<mtr>
<mtd><mfrac><mrow><mo></mo><mi>x</mi></mrow><mrow><mo></mo><mi>φ</mi></mrow></mfrac></mtd>
</mtr>
<mtr>
<mtd><mfrac><mrow><mo></mo><mi>y</mi></mrow><mrow><mo></mo><mi>φ</mi></mrow></mfrac></mtd>
</mtr>
</mtable>
</mfenced>
</mtd>
<mtd><mtext>et</mtext></mtd>
<mtd>
<mover><mi>V</mi><mo></mo></mover><mo>=</mo>
<mfenced open="[" close="]">
<mtable>
<mtr>
<mtd><mfrac><mrow><mo></mo><mi>x</mi></mrow><mrow><mo></mo><mi>λ</mi></mrow></mfrac></mtd>
</mtr>
<mtr>
<mtd><mfrac><mrow><mo></mo><mi>y</mi></mrow><mrow><mo></mo><mi>λ</mi></mrow></mfrac></mtd>
</mtr>
</mtable>
</mfenced>
</mtd>
</mtr></mtable>
</math>
</div>
</div>
<p>
Above figure shows one usage of map projection derivatives:
they provide the direction of parallels and meridians at a given location in a map projection.
One can use that information for determining if axes have been swapped or their direction reversed.
But the usefulness of map projection derivatives goes further.
</p>
<h4 id="DerivativeAndEnvelope">Transform derivatives applied to envelopes</h4>
<p>
<span style="color: red">TODO</span>
</p>
<div class="row-of-boxes">
<div>
<div class="caption">Envelope before projection</div>
<img style="border: solid 1px; margin-right: 15px" src="../../../content/book/images/GeographicArea.png" alt="Envelope in a geographic CRS"/>
</div><div>
<div class="caption">Geometric shape after projection</div>
<img style="border: solid 1px; margin-left: 15px" src="../../../content/book/images/ConicArea.png" alt="Shape in a projected CRS"/>
</div>
</div>
<p>
<span style="color: red">TODO</span>
</p>
<div class="row-of-boxes">
<div>
<img src="../../../content/book/images/Approximation.png" alt="Cubic approximation of an envelope bounds"/>
</div><div>
Legend:
<ul>
<li><b>Blue:</b> the geometric shape of the envelope after projection.
This is the shape from which to get a new envelope.</li>
<li><b>Red</b> (with hash): The
<var>y</var> = <var>C</var>₀ + <var>C</var>₁λ + <var>C</var>₂λ² + <var>C</var>₃λ³ approximation.</li>
<li><b>Green</b> (dashed line): Position λ<sub>m</sub> of approximation minimum, obtained by resolving
0 = <var>C</var>₁ + 2<var>C</var>₂λ<sub>m</sub> + 3<var>C</var>₃λ<sub>m</sub>².
The same cubic line can have two minimums.</li>
</ul>
</div>
</div>
<p>
<span style="color: red">TODO</span>
</p>
<h4 id="DerivativeAndRaster">Transform derivatives applied to rasters</h4>
<p>
<span style="color: red">TODO</span>
</p>
<div style="display:flex; flex-direction:column; align-items:center">
<div style="display:flex; justify-content:space-between; width:564px">
<div class="caption">Source image</div>
<div class="caption">Destination image</div>
</div>
<img src="../../../content/book/images/RasterProjection.png" alt="Raster reprojection"/>
</div>
<p>
<span style="color: red">TODO</span>
</p>
<div class="row-of-boxes">
<div>
<img src="../../../content/book/images/WarpGrid.png" alt="Warp grid"/>
</div><div>
Legend:
<ul>
<li><b>Blue dots:</b> first iteration (9 points).</li>
<li><b>Green dots:</b> second iteration (25 points, including 16 news).</li>
<li><b>Red dots:</b> third iteration (81 points, including 56 news).</li>
</ul>
Continuing…
<ul>
<li>Forth iteration: 289 points, including 208 news.</li>
<li>Fifth iteration: 1089 points, including 800 news.</li>
<li>Sixth iteration: 4225 points, including 3136 news.</li>
<li></li>
</ul>
</div>
</div>
<p>
<span style="color: red">TODO</span>
</p>
<p style="text-align:center"><img style="border: solid 1px" src="../../../content/book/images/IntersectionOfDerivatives.png" alt="Intersection of derivatives"/></p>
<p>
<span style="color: red">TODO</span>
</p>
<h4 id="GetDerivative">Getting the derivative at a point</h4>
<p>
<span style="color: red">TODO</span>
Example:</p>
<pre><code>AbstractMathTransform projection = ...; // An Apache SIS map projection.
double[] sourcePoint = {longitude, latitude}; // The geographic coordinate to project.
double[] targetPoint = new double[2]; // Where to store the projection result.
Matrix derivative = projection.<code class="SIS">transform</code>(sourcePoint, 0, targetPoint, 0, true);</code></pre>
<p>
<span style="color: red">TODO</span>
</p>
<pre><code>@Override
public Matrix derivative(DirectPosition p) throws TransformException {
Matrix jac = inverse().derivative(transform(p));
return Matrices.inverse(jac);
}</code></pre>
<h3 id="CoordinateOperationSteps">Conceptual versus real chain of coordinate operations</h3>
<p>
Coordinate operations may include many steps, each with their own set of parameters.
For example transformations from one datum (e.g. <abbr>NAD27</abbr>) to another datum (e.g. <abbr>WGS84</abbr>)
can be approximated by an affine transform (translation, rotation and scale) applied on the <em>geocentric</em> coordinates.
This implies that the coordinates must be converted from <em>geographic</em> to geocentric domain before the affine transform,
then back to geographic domain after the affine transform.
The result is a three-steps process illustrated in the “Conceptual chain of operations” column of the example below.
However because that operation chain is very common, the <abbr>EPSG</abbr> geodetic dataset provides a shortcut
named “Geocentric translation <em>in geographic domain</em>”.
Using this operation, the conversion steps between geographic and geocentric <abbr>CRS</abbr> are implicit.
Consequently the datum shifts as specified by <abbr>EPSG</abbr> appears as if it was a single operation,
but this is not the real operation executed by Apache <abbr>SIS</abbr>.
</p>
<div class="example"><p><b>Example:</b>
transformation of geographic coordinates from <abbr>NAD27</abbr> to <abbr>WGS84</abbr> in Canada
can be approximated by the <abbr>EPSG</abbr>:1172 coordinate operation.
This single <abbr>EPSG</abbr> operation is actually a chain of three operations in which two steps are implicit.
The operation as specified by <abbr>EPSG</abbr> is shown in the first column below.
The same operation with the two hidden steps made explicit is shown in the second column.
The last column shows the same operation as implemented by Apache <abbr>SIS</abbr> under the hood,
which contains additional operations discussed below.
For all columns, input coordinates of the first step and output coordinates of the last step
are (<var>latitude</var>, <var>longitude</var>) coordinates in degrees.
</p>
<div style="display:flex; padding-left:24px">
<div style="width:30%; padding-right:15px; border-right:1px solid">
<b>Operation specified by <abbr>EPSG</abbr>:</b>
<ol>
<li><b>Geocentric translation</b> in <em>geographic</em> domain
<ul>
<li>X-axis translation = -10 m</li>
<li>Y-axis translation = 158 m</li>
<li>Z-axis translation = 187 m</li>
</ul>
</li>
</ol>
Conversions between geographic and geocentric domains are implicit.
The semi-major and semi-minor axis lengths required for those conversions
are inferred from the source and target datum.
</div>
<div style="width:30%; padding-left:30px; padding-right:15px; border-right:1px solid">
<b>Conceptual chain of operations:</b>
<ol>
<li><b>Geographic to geocentric</b> conversion
<ul>
<li>Source semi-major = 6378206.4 m</li>
<li>Source semi-minor = 6356583.8 m</li>
</ul>
</li><li><b>Geocentric translation</b>
<ul>
<li>X-axis translation = -10 m</li>
<li>Y-axis translation = 158 m</li>
<li>Z-axis translation = 187 m</li>
</ul>
</li><li><b>Geocentric to geographic</b> conversion
<ul>
<li>Target semi-major = 6378137.0 m</li>
<li>Target semi-minor ≈ 6356752.3 m</li>
</ul>
</li>
</ol>
Axis order and units are implicitly defined by the source and target <abbr>CRS</abbr>.
It is implementation responsibility to perform any needed unit conversions and/or axis swapping.
</div>
<div style="width:30%; padding-left:30px">
<b>Operations actually performed by Apache <abbr>SIS</abbr>:</b>
<ol>
<li><b>Affine parametric</b> conversion
<ul>
<li>Scale factors (λ and φ) = 0</li>
<li>Shear factors (λ and φ) = π/180</li>
</ul>
</li><li>Ellipsoid (radians domain) to centric conversion
<ul>
<li>Eccentricity ≈ 0.08227</li>
</ul>
</li><li><b>Affine parametric transformation</b>
<ul>
<li>Scale factors (X, Y and Z) ≈ 1.00001088</li>
<li>X-axis translation ≈ -1.568 E-6</li>
<li>Y-axis translation ≈ 24.772 E-6</li>
<li>Z-axis translation ≈ 29.319 E-6</li>
</ul>
</li><li>Centric to ellipsoid (radians domain) conversion
<ul>
<li>Eccentricity ≈ 0.08182</li>
</ul>
</li><li><b>Affine parametric</b> conversion
<ul>
<li>Scale factors (λ and φ) = 0</li>
<li>Shear factors (λ and φ) = 180/π</li>
</ul>
</li>
</ol>
</div>
</div>
<p>
The operation chain actually performed by Apache <abbr>SIS</abbr> is very different than the conceptual operation chain
because the coordinate systems are not the same.
Except for the first and last ones, all Apache <abbr>SIS</abbr> steps work on right-handed coordinate systems
(as opposed to the left-handed coordinate system when <var>latitude</var> is before <var>longitude</var>),
with angular units in radians (instead than degrees) and
linear units relative to an ellipsoid of semi-major axis length of 1 (instead than Earth’s size).
Working in those coordinate systems requires additional steps for unit conversions and axes swapping
at the beginning and at the end of the chain.
Apache <abbr>SIS</abbr> uses <cite>affine parametric conversions</cite> for this purpose,
which allow to combine axes swapping and unit conversions in a single step
(see <a href="#AffineTransform">affine transform</a> for more information).
The reason why Apache <abbr>SIS</abbr> splits conceptual operations in such fine-grained operations
is to allow more efficient concatenations of operation steps.
This approach often allows cancellation of two consecutive affine transforms,
for example a conversion from radians to degrees (e.g. after a <cite>geocentric to ellipsoid</cite> conversion)
immediately followed by a conversion from degrees to radians (e.g. before a map projection).
Another example is the <cite>Affine parametric transformation</cite> step above,
which combines both the <cite>geocentric translation</cite> step
and a scale factor implied by the ellipsoid change.
</p>
</div>
<p>
All those operation chains can be viewed in <cite>Well Known Text</cite> (<abbr>WKT</abbr>) or pseudo-<abbr>WKT</abbr> format.
The simplest operation chain, as specified by the authority, is given directly by the
<code>String</code> representation of the <code>CoordinateOperation</code> instance.
This <abbr>WKT</abbr> 2 representation contains not only a description of operations with their parameter values,
but also additional information about the context in which the operation applies (the source and target <abbr>CRS</abbr>)
together with some metadata like the accuracy and domain of validity.
Some operation steps and parameters may be omitted if they can be inferred from the context.
</p>
<div class="example">
<div style="display:flex; padding-left:24px; padding-right:24px">
<div>
<p><b>Example:</b>
the <abbr>WKT</abbr> 2 representation on the right is for the same coordinate operation than the one used in previous example.
This representation can be obtained by a call to <code>System.out.println(cop)</code>
where <code>cop</code> is a <code>CoordinateOperation</code> instance.
Some characteristics of this representation are:
</p>
<ul>
<li><p>The <code>SourceCRS</code> and <code>TargetCRS</code> elements determine axis order and units.
For this reason, axis swapping and unit conversions do not need to be represented in this <abbr>WKT</abbr>.</p></li>
<li><p>The “Geocentric translation in geographic domain” operation implies conversions between geographic and geocentric coordinate reference systems.
Ellipsoid semi-axis lengths are inferred from above <code>SourceCRS</code> and <code>TargetCRS</code> elements,
so they do not need to be specified in this <abbr>WKT</abbr>.</p></li>
<li><p>The operation accuracy (20 metres) is much greater than the numerical floating-point precision.
This kind of metadata could hardly be guessed from the mathematical function alone.</p></li>
</ul>
</div>
<div>
<pre><samp>CoordinateOperation["NAD27 to WGS 84 (3)",
SourceCRS[<span style="font-family:serif"><i>full CRS definition required here but omitted for brevity</i></span>],
TargetCRS[<span style="font-family:serif"><i>full CRS definition required here but omitted for brevity</i></span>],
Method["Geocentric translations (geog2D domain)"],
Parameter["X-axis translation", -10.0, Unit["metre", 1]],
Parameter["Y-axis translation", 158.0, Unit["metre", 1]],
Parameter["Z-axis translation", 187.0, Unit["metre", 1]],
OperationAccuracy[20.0],
Area["Canada - onshore and offshore"],
BBox[40.04, -141.01, 86.46, -47.74],
Id["EPSG", 1172, "8.9"]]</samp></pre>
</div>
</div>
</div>
<p>
An operation chain closer to what Apache <abbr>SIS</abbr> really performs is given by the
<code>String</code> representation of the <code>MathTransform</code> instance.
In this <abbr>WKT</abbr> 1 representation, contextual information and metadata are lost;
a <code>MathTransform</code> is like a mathematical function with no knowledge about the meaning of the coordinates on which it operates.
Since contextual information are lost, implicit operations and parameters become explicit.
This representation is useful for debugging since any axis swapping operation (for example) become visible.
Apache <abbr>SIS</abbr> constructs this representation from the data structure in memory,
but convert them in a more convenient form for human, for example by converting radians to degrees.
</p>
<div class="example">
<div style="display:flex; padding-left:24px; padding-right:24px">
<div>
<p><b>Example:</b>
the <abbr>WKT</abbr> 1 representation on the right is for the same coordinate operation than the one used in previous example.
This representation can be obtained by a call to <code>System.out.println(cop.getMathTransform())</code>
where <code>cop</code> is a <code>CoordinateOperation</code> instance.
Some characteristics of this representation are:
</p>
<ul>
<li><p>Since there is not anymore (on intent) any information about source and target <abbr>CRS</abbr>,
axis swapping (if needed) and unit conversions must be performed explicitly.
This is the task of the first and last affine operations in this <abbr>WKT</abbr>.</p></li>
<li><p>The “Geocentric translation” operation is not anymore applied in the geographic domain, but in the geocentric domain.
Consequently conversions between geographic and geocentric coordinate reference systems must be made explicit.
Those explicit steps are also necessary for specifying the ellipsoid semi-axis lengths,
since they can not anymore by inferred for source and target <abbr>CRS</abbr>.</p></li>
<li><p>Conversions between geographic and geocentric coordinates are three-dimensional.
Consequently operations for increasing and reducing the number of dimensions are inserted.
By default the ellipsoidal height before conversion is set to zero.</p></li>
</ul>
</div>
<div>
<pre><samp>Concat_MT[
Param_MT["Affine parametric transformation",
Parameter[<span style="font-family:serif"><i>parameters performing axis swapping omitted for brevity</i></span>]],
Inverse_MT[Param_MT["Geographic3D to 2D conversion"]],
Param_MT["Geographic/geocentric conversions",
Parameter["semi_major", 6378206.4],
Parameter["semi_minor", 6356583.8]],
Param_MT["Geocentric translations (geocentric domain)",
Parameter["X-axis translation", -10.0],
Parameter["Y-axis translation", 158.0],
Parameter["Z-axis translation", 187.0]],
Param_MT["Geocentric_To_Ellipsoid",
Parameter["semi_major", 6378137.0],
Parameter["semi_minor", 6356752.314245179]],
Param_MT["Geographic3D to 2D conversion"],
Param_MT["Affine parametric transformation",
Parameter[<span style="font-family:serif"><i>parameters performing axis swapping omitted for brevity</i></span>]]]</samp></pre>
</div>
</div>
</div>
<p>
Finally, the raw operation chain can be view by a call to <code>AbstractMathTransform.toString(Convention.INTERNAL)</code>.
This pseudo-<abbr>WKT</abbr> representation shows exactly what Apache <abbr>SIS</abbr> does,
but is rarely used because difficult to read.
It may occasionally be useful for advanced debugging.
</p>
</section>
</body>
</html>