| <?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> |