diff options
| author | PROJ deploybot <proj.bot@proj.bot> | 2022-03-22 20:00:06 +0000 |
|---|---|---|
| committer | PROJ deploybot <proj.bot@proj.bot> | 2022-03-22 20:00:06 +0000 |
| commit | a3f43744feec86272fe532124679d3a013ef9a8c (patch) | |
| tree | 27e4198db6011e3097eb7bcfe7197684aba7583a /operations/operations_computation.html | |
| download | PROJ-gh-pages.tar.gz PROJ-gh-pages.zip | |
update with results of commit https://github.com/OSGeo/PROJ/commit/53c07a8bd211b7aee4bc07a9c6726005504b7181gh-pages
Diffstat (limited to 'operations/operations_computation.html')
| -rw-r--r-- | operations/operations_computation.html | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/operations/operations_computation.html b/operations/operations_computation.html new file mode 100644 index 00000000..3ab5848c --- /dev/null +++ b/operations/operations_computation.html @@ -0,0 +1,875 @@ +<!DOCTYPE html> +<html class="writer-html5" lang="en" > +<head> + <meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> + + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Computation of coordinate operations between two CRS — PROJ 9.0.0 documentation</title> + <link rel="stylesheet" href="../_static/pygments.css" type="text/css" /> + <link rel="stylesheet" href="../_static/css/theme.css" type="text/css" /> + <link rel="shortcut icon" href="../_static/favicon.png"/> + <link rel="canonical" href="https://proj.orgoperations/operations_computation.html"/> + <!--[if lt IE 9]> + <script src="../_static/js/html5shiv.min.js"></script> + <![endif]--> + + <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script> + <script src="../_static/jquery.js"></script> + <script src="../_static/underscore.js"></script> + <script src="../_static/doctools.js"></script> + <script src="../_static/js/theme.js"></script> + <link rel="author" title="About these documents" href="../about.html" /> + <link rel="index" title="Index" href="../genindex.html" /> + <link rel="search" title="Search" href="../search.html" /> + <link rel="next" title="Resource files" href="../resource_files.html" /> + <link rel="prev" title="The pipeline operator" href="pipeline.html" /> +</head> + +<body class="wy-body-for-nav"> + <div class="wy-grid-for-nav"> + <nav data-toggle="wy-nav-shift" class="wy-nav-side"> + <div class="wy-side-scroll"> + <div class="wy-side-nav-search" style="background: #353130" > + <a href="../index.html"> + <img src="../_static/logo.png" class="logo" alt="Logo"/> + </a> + <div class="version"> + 9.0.0 + </div> +<div role="search"> + <form id="rtd-search-form" class="wy-form" action="../search.html" method="get"> + <input type="text" name="q" placeholder="Search docs" /> + <input type="hidden" name="check_keywords" value="yes" /> + <input type="hidden" name="area" value="default" /> + </form> +</div> + </div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu"> + <ul class="current"> +<li class="toctree-l1"><a class="reference internal" href="../about.html">About</a></li> +<li class="toctree-l1"><a class="reference internal" href="../news.html">News</a></li> +<li class="toctree-l1"><a class="reference internal" href="../download.html">Download</a></li> +<li class="toctree-l1"><a class="reference internal" href="../install.html">Installation</a></li> +<li class="toctree-l1"><a class="reference internal" href="../usage/index.html">Using PROJ</a></li> +<li class="toctree-l1"><a class="reference internal" href="../apps/index.html">Applications</a></li> +<li class="toctree-l1 current"><a class="reference internal" href="index.html">Coordinate operations</a><ul class="current"> +<li class="toctree-l2"><a class="reference internal" href="projections/index.html">Projections</a></li> +<li class="toctree-l2"><a class="reference internal" href="conversions/index.html">Conversions</a></li> +<li class="toctree-l2"><a class="reference internal" href="transformations/index.html">Transformations</a></li> +<li class="toctree-l2"><a class="reference internal" href="pipeline.html">The pipeline operator</a></li> +<li class="toctree-l2 current"><a class="current reference internal" href="#">Computation of coordinate operations between two CRS</a><ul> +<li class="toctree-l3"><a class="reference internal" href="#introduction">Introduction</a></li> +<li class="toctree-l3"><a class="reference internal" href="#geographic-crs-to-geographic-crs-with-known-identifiers">Geographic CRS to Geographic CRS, with known identifiers</a></li> +<li class="toctree-l3"><a class="reference internal" href="#filtering-and-sorting-of-coordinate-operations">Filtering and sorting of coordinate operations</a></li> +<li class="toctree-l3"><a class="reference internal" href="#geodetic-geographic-crs-to-geodetic-geographic-crs-without-known-identifiers">Geodetic/geographic CRS to Geodetic/geographic CRS, without known identifiers</a></li> +<li class="toctree-l3"><a class="reference internal" href="#geodetic-geographic-crs-to-geodetic-geographic-crs-without-direct-transformation">Geodetic/geographic CRS to Geodetic/geographic CRS, without direct transformation</a></li> +<li class="toctree-l3"><a class="reference internal" href="#projected-crs-to-any-target-crs">Projected CRS to any target CRS</a></li> +<li class="toctree-l3"><a class="reference internal" href="#vertical-crs-to-a-geographic-crs">Vertical CRS to a Geographic CRS</a></li> +<li class="toctree-l3"><a class="reference internal" href="#vertical-crs-to-a-vertical-crs">Vertical CRS to a Vertical CRS</a></li> +<li class="toctree-l3"><a class="reference internal" href="#compound-crs-to-a-geographic-crs">Compound CRS to a Geographic CRS</a></li> +<li class="toctree-l3"><a class="reference internal" href="#compoundcrs-to-compoundcrs">CompoundCRS to CompoundCRS</a></li> +<li class="toctree-l3"><a class="reference internal" href="#when-the-source-or-target-crs-is-a-boundcrs">When the source or target CRS is a BoundCRS</a></li> +</ul> +</li> +</ul> +</li> +<li class="toctree-l1"><a class="reference internal" href="../resource_files.html">Resource files</a></li> +<li class="toctree-l1"><a class="reference internal" href="../geodesic.html">Geodesic calculations</a></li> +<li class="toctree-l1"><a class="reference internal" href="../development/index.html">Development</a></li> +<li class="toctree-l1"><a class="reference internal" href="../specifications/index.html">Specifications</a></li> +<li class="toctree-l1"><a class="reference internal" href="../community/index.html">Community</a></li> +<li class="toctree-l1"><a class="reference internal" href="../faq.html">FAQ</a></li> +<li class="toctree-l1"><a class="reference internal" href="../glossary.html">Glossary</a></li> +<li class="toctree-l1"><a class="reference internal" href="../zreferences.html">References</a></li> +</ul> + + </div> + </div> + </nav> + + <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" style="background: #353130" > + <i data-toggle="wy-nav-top" class="fa fa-bars"></i> + <a href="../index.html">PROJ</a> + </nav> + + <div class="wy-nav-content"> + <div class="rst-content"> + <div role="navigation" aria-label="Page navigation"> + <ul class="wy-breadcrumbs"> + <li><a href="../index.html" class="icon icon-home"></a> »</li> + <li><a href="index.html">Coordinate operations</a> »</li> + <li>Computation of coordinate operations between two CRS</li> + <li class="wy-breadcrumbs-aside"> + <a href="https://github.com/OSGeo/PROJ/edit/8.2/docs/source/operations/operations_computation.rst" class="fa fa-github"> Edit on GitHub</a> + </li> + </ul><div class="rst-breadcrumbs-buttons" role="navigation" aria-label="Sequential page navigation"> + <a href="pipeline.html" class="btn btn-neutral float-left" title="The pipeline operator" accesskey="p"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a> + <a href="../resource_files.html" class="btn btn-neutral float-right" title="Resource files" accesskey="n">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a> + </div> + <hr/> +</div> + <div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article"> + <div itemprop="articleBody"> + + <section id="computation-of-coordinate-operations-between-two-crs"> +<span id="operations-computation"></span><h1>Computation of coordinate operations between two CRS<a class="headerlink" href="#computation-of-coordinate-operations-between-two-crs" title="Permalink to this headline">¶</a></h1> +<dl class="field-list simple"> +<dt class="field-odd">Author</dt> +<dd class="field-odd"><p>Even Rouault</p> +</dd> +<dt class="field-even">Last Updated</dt> +<dd class="field-even"><p>2021-02-10</p> +</dd> +</dl> +<section id="introduction"> +<h2>Introduction<a class="headerlink" href="#introduction" title="Permalink to this headline">¶</a></h2> +<p>When using <strong class="command">projinfo -s {crs_def} -t {crs_def}</strong>, +<strong class="command">cs2cs {crs_def} {crs_def}</strong> or the underlying +<a class="reference internal" href="../development/reference/functions.html#c.proj_create_crs_to_crs" title="proj_create_crs_to_crs"><code class="xref c c-func docutils literal notranslate"><span class="pre">proj_create_crs_to_crs()</span></code></a> or <code class="xref cpp cpp-func docutils literal notranslate"><span class="pre">proj_create_operations()</span></code> functions, +PROJ applies an algorithm to compute one or several candidate coordinate operations, +that can be expressed as a PROJ <a class="reference internal" href="pipeline.html#pipeline"><span class="std std-ref">pipeline</span></a> to transform between the source +and the target CRS. This document is about the description of this algorithm that +finds the actual operations to apply to be able later to perform transform coordinates. +So this is mostly about metadata management around coordinate operation methods, +and not about the actual mathematics used to implement those methods. +As a matter of fact with PROJ 6, there are about 60 000 +lines of code dealing with “metadata” management (including conversions between PROJ +strings, all CRS WKT variants), to be compared to 30 000 for the purely computation part.</p> +<p>This document is meant as a plain text explanation of the code for developers, +but also as a in-depth examination of what happens under the hood for curious PROJ +users. It is important to keep in mind that it is not meant to be the ultimate +source of truth of how coordinate operations should be computed. There are clearly +implementation choices and compromises that can be questioned.</p> +<p>Let us start with an example to research operations between the NAD27 and NAD83 +geographic CRS:</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s NAD27 -t NAD83 --summary --spatial-test intersects --grid-check none + +Candidate operations found: <span class="m">10</span> +DERIVED_FROM<span class="o">(</span>EPSG<span class="o">)</span>:1312, NAD27 to NAD83 <span class="o">(</span><span class="m">3</span><span class="o">)</span>, <span class="m">1</span>.0 m, Canada +DERIVED_FROM<span class="o">(</span>EPSG<span class="o">)</span>:1313, NAD27 to NAD83 <span class="o">(</span><span class="m">4</span><span class="o">)</span>, <span class="m">1</span>.5 m, Canada - NAD27, at least one grid missing +DERIVED_FROM<span class="o">(</span>EPSG<span class="o">)</span>:1241, NAD27 to NAD83 <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">0</span>.15 m, USA - CONUS including EEZ +DERIVED_FROM<span class="o">(</span>EPSG<span class="o">)</span>:1243, NAD27 to NAD83 <span class="o">(</span><span class="m">2</span><span class="o">)</span>, <span class="m">0</span>.5 m, USA - Alaska including EEZ +DERIVED_FROM<span class="o">(</span>EPSG<span class="o">)</span>:1573, NAD27 to NAD83 <span class="o">(</span><span class="m">6</span><span class="o">)</span>, <span class="m">1</span>.5 m, Canada - Quebec, at least one grid missing +EPSG:1462, NAD27 to NAD83 <span class="o">(</span><span class="m">5</span><span class="o">)</span>, <span class="m">1</span>.0 m, Canada - Quebec, at least one grid missing +EPSG:9111, NAD27 to NAD83 <span class="o">(</span><span class="m">9</span><span class="o">)</span>, <span class="m">1</span>.5 m, Canada - Saskatchewan, at least one grid missing +unknown id, Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, World, has ballpark transformation +EPSG:8555, NAD27 to NAD83 <span class="o">(</span><span class="m">7</span><span class="o">)</span>, <span class="m">0</span>.15 m, USA - CONUS and GoM, at least one grid missing +EPSG:8549, NAD27 to NAD83 <span class="o">(</span><span class="m">8</span><span class="o">)</span>, <span class="m">0</span>.5 m, USA - Alaska, at least one grid missing +</pre></div> +</div> +<p>The algorithm involves many cases, so we will progress in the explanation from +the most simple case to more complex ones. We document here the working of this +algorithm as implemented in PROJ 8.0.0. +The results of some examples might also be quite sensitive to the content of the +PROJ database and the PROJ version used.</p> +<p>From a code point of view, the entry point of the algorithm is the C++ +<code class="xref cpp cpp-func docutils literal notranslate"><span class="pre">osgeo::proj::operation::CoordinateOperation::createOperations()</span></code> method.</p> +<p>It combines several strategies:</p> +<blockquote> +<div><ul class="simple"> +<li><p>look up in the PROJ database for available operations</p></li> +<li><p>consider the pair (source CRS, target CRS) to synthetize operations depending +on the nature of the source and target CRS.</p></li> +</ul> +</div></blockquote> +</section> +<section id="geographic-crs-to-geographic-crs-with-known-identifiers"> +<h2>Geographic CRS to Geographic CRS, with known identifiers<a class="headerlink" href="#geographic-crs-to-geographic-crs-with-known-identifiers" title="Permalink to this headline">¶</a></h2> +<p>With the above example of two geographic CRS, that have an identified identifier, +(<strong class="program">projinfo</strong> internally resolves NAD27 to EPSG:4267 and NAD83 to EPSG:4269) +the algorithm will first search +in the coordinate operation related tables of the <code class="file docutils literal notranslate"><span class="pre">proj.db</span></code> if there are records +that list direct transformations between the source and the target CRS. The +transformations typically involve <a class="reference internal" href="transformations/helmert.html#helmert"><span class="std std-ref">Helmert</span></a>-style operations or datum shift based on +grids (more esoteric operations are possible).</p> +<p>A request similar to the following will be emitted:</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ sqlite3 proj.db <span class="s2">"SELECT auth_name, code, name, method_name, accuracy FROM \</span> +<span class="s2"> coordinate_operation_view WHERE \</span> +<span class="s2"> source_crs_auth_name = 'EPSG' AND \</span> +<span class="s2"> source_crs_code = '4267' AND \</span> +<span class="s2"> target_crs_auth_name = 'EPSG' AND \</span> +<span class="s2"> target_crs_code = '4269'"</span> + +EPSG<span class="p">|</span><span class="m">1241</span><span class="p">|</span>NAD27 to NAD83 <span class="o">(</span><span class="m">1</span><span class="o">)</span><span class="p">|</span>NADCON<span class="p">|</span><span class="m">0</span>.15 +EPSG<span class="p">|</span><span class="m">1243</span><span class="p">|</span>NAD27 to NAD83 <span class="o">(</span><span class="m">2</span><span class="o">)</span><span class="p">|</span>NADCON<span class="p">|</span><span class="m">0</span>.5 +EPSG<span class="p">|</span><span class="m">1312</span><span class="p">|</span>NAD27 to NAD83 <span class="o">(</span><span class="m">3</span><span class="o">)</span><span class="p">|</span>NTv1<span class="p">|</span><span class="m">1</span>.0 +EPSG<span class="p">|</span><span class="m">1313</span><span class="p">|</span>NAD27 to NAD83 <span class="o">(</span><span class="m">4</span><span class="o">)</span><span class="p">|</span>NTv2<span class="p">|</span><span class="m">1</span>.5 +EPSG<span class="p">|</span><span class="m">1462</span><span class="p">|</span>NAD27 to NAD83 <span class="o">(</span><span class="m">5</span><span class="o">)</span><span class="p">|</span>NTv1<span class="p">|</span><span class="m">1</span>.0 +EPSG<span class="p">|</span><span class="m">1573</span><span class="p">|</span>NAD27 to NAD83 <span class="o">(</span><span class="m">6</span><span class="o">)</span><span class="p">|</span>NTv2<span class="p">|</span><span class="m">1</span>.5 +EPSG<span class="p">|</span><span class="m">8549</span><span class="p">|</span>NAD27 to NAD83 <span class="o">(</span><span class="m">8</span><span class="o">)</span><span class="p">|</span>NADCON5 <span class="o">(</span>2D<span class="o">)</span><span class="p">|</span><span class="m">0</span>.5 +EPSG<span class="p">|</span><span class="m">8555</span><span class="p">|</span>NAD27 to NAD83 <span class="o">(</span><span class="m">7</span><span class="o">)</span><span class="p">|</span>NADCON5 <span class="o">(</span>2D<span class="o">)</span><span class="p">|</span><span class="m">0</span>.15 +EPSG<span class="p">|</span><span class="m">9111</span><span class="p">|</span>NAD27 to NAD83 <span class="o">(</span><span class="m">9</span><span class="o">)</span><span class="p">|</span>NTv2<span class="p">|</span><span class="m">1</span>.5 +ESRI<span class="p">|</span><span class="m">108003</span><span class="p">|</span>NAD_1927_To_NAD_1983_PR_VI<span class="p">|</span>NTv2<span class="p">|</span><span class="m">0</span>.05 +</pre></div> +</div> +<p>As we have found direct transformations, we will not attempt any more complicated +research. +One can note in the above result set that a ESRI:108003 operation was found, +but as the source and target CRS are in the EPSG registry, and there are +operations between those CRS in the EPSG registry itself, transformations from +other authorities will be ignored (except if they are in the PROJ authority, +which can be used as an override).</p> +<p>As those results all involve operations that does not have a perfect accuracy and that +does not cover the area of use of the 2 CRSs, a +‘Ballpark geographic offset from NAD27 to NAD83’ operation is synthetized by PROJ +(see <a class="reference internal" href="../glossary.html#term-Ballpark-transformation"><span class="xref std std-term">Ballpark transformation</span></a>)</p> +</section> +<section id="filtering-and-sorting-of-coordinate-operations"> +<h2>Filtering and sorting of coordinate operations<a class="headerlink" href="#filtering-and-sorting-of-coordinate-operations" title="Permalink to this headline">¶</a></h2> +<p>The last step is to filter and sort results in order of relevance.</p> +<p>The filtering takes into account the following criteria to decide which operations +must be retained or discarded:</p> +<ul class="simple"> +<li><p>a minimum accuracy that the user might have expressed,</p></li> +<li><p>an area of use on which the coordinate operation(s) must apply</p></li> +<li><p>if the absence of grids needed by an operation must result in discarding it.</p></li> +</ul> +<p>The sorting algorithm determines the order of relevance of the operations we got. +A comparison function compares pair of operations to determine which of the +two is the most relevant. This is implemented by the <code class="xref cpp cpp-func docutils literal notranslate"><span class="pre">operator</span> <span class="pre">()</span></code> +method of the SortFunction structure. +When comparing two operations, the following criteria are used. The tests are +performed in the order they are listed below:</p> +<ol class="arabic simple"> +<li><p>consider as more relevant an operation that can be expressed as a PROJ operation string +(the database might list operations whose method is not (yet) implemented by PROJ)</p></li> +<li><p>if both operations evaluate identically with respect to the above criterion, +consider as more relevant an operation that does not include a synthetic +ballpark vertical transformation (occurs when there is a geoid model).</p></li> +<li><p>if both operations evaluate identically with respect to the above criterion, +consider as more relevant an operation that does not include a synthetic +ballpark horizontal transformation.</p></li> +<li><p>consider as more relevant an operation that refers to shift grids that are locally available.</p></li> +<li><p>consider as more relevant an operation that refers to grids that are available +in one of the proj-datumgrid packages, but not necessarily locally available</p></li> +<li><p>consider as more relevant an operation that has a known accuracy.</p></li> +<li><p>if two operations have unknown accuracy, consider as more relevant an operation +that uses grid(s) if the other one does not (grid based operations are assumed +to be more precise than operations relying on a few parameters)</p></li> +<li><p>consider as more relevant an operation whose area of use is larger +(note: the computation of the are of use is approximate, based on a bounding box)</p></li> +<li><p>consider as more relevant an operation that has a better accuracy.</p></li> +<li><p>in case of same accuracy, consider as more relevant an operation that does +not use grids (operations that use only parameters will be faster)</p></li> +<li><p>consider as more relevant an operation that involves less transformation steps +(transformation steps considered are the ones listed in the WKT output, not PROJ pipeline steps)</p></li> +<li><p>and for completeness, if two operations are comparable given all the above criteria, +consider as more relevant the one which has the shorter name, and if they +have the same length, consider as more relevant the one whose name comes last in +lexicographic order (e.g. “FOO to BAR (3)” will have higher precedence than +“FOO to BAR (2)”)</p></li> +</ol> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p><a class="reference internal" href="../development/reference/functions.html#c.proj_trans" title="proj_trans"><code class="xref c c-func docutils literal notranslate"><span class="pre">proj_trans()</span></code></a>, on the results returned by <a class="reference internal" href="../development/reference/functions.html#c.proj_create_crs_to_crs" title="proj_create_crs_to_crs"><code class="xref c c-func docutils literal notranslate"><span class="pre">proj_create_crs_to_crs()</span></code></a>, +will not necessarily use the operation that +is listed in first position due to the above algorithm. <a class="reference internal" href="../development/reference/functions.html#c.proj_trans" title="proj_trans"><code class="xref c c-func docutils literal notranslate"><span class="pre">proj_trans()</span></code></a> +has more context, since it has the coordinate to transform, so it can compare +this coordinate to the area of use of operations. Typically, the above criteria +will favor an operation that has a larger area of use over another one with a +smaller area, due to it being more generally applicable. But once coordinates are known, +<a class="reference internal" href="../development/reference/functions.html#c.proj_trans" title="proj_trans"><code class="xref c c-func docutils literal notranslate"><span class="pre">proj_trans()</span></code></a> can select an operation with a smaller +area of use that applies to the coordinate to transform.</p> +</div> +</section> +<section id="geodetic-geographic-crs-to-geodetic-geographic-crs-without-known-identifiers"> +<h2>Geodetic/geographic CRS to Geodetic/geographic CRS, without known identifiers<a class="headerlink" href="#geodetic-geographic-crs-to-geodetic-geographic-crs-without-known-identifiers" title="Permalink to this headline">¶</a></h2> +<p>In a number of situations, the source and/or target CRS do not have an identifier +(WKT without identifier, PROJ string, ..) +The first step is to try to find in the <code class="file docutils literal notranslate"><span class="pre">proj.db</span></code> a CRS of the same nature of +the CRS to identify and whose name exactly matches the one provided to the +<code class="xref c c-func docutils literal notranslate"><span class="pre">createOperations()</span></code> method. If there is exactly one match and that the CRS are +“computationally” equivalent, then use the code of the CRS for further computations.</p> +<p>If this search did not succeed, or if the previous case with known CRS identifiers +did not result in matches in the database, the search will be based on the +datums. That is, a list of geographic CRS whose datum matches the datum of the +source and target CRS is searched for in the database (by querying the <cite>geodetic_crs</cite> +database table). If the datum has a known +identifier, we will use it, otherwise we will look for an equivalent datum in the +database based on the datum name.</p> +<p>Let’s consider the case where the datum of the source CRS is EPSG:6171 “Reseau +Geodesique Francais 1993” and the datum of the target CRS is EPSG:6258 “European +Terrestrial Reference System 1989”. +For EPSG:6171, there are 10 matching (non-deprecated) geodetic CRSs:</p> +<ul class="simple"> +<li><p>EPSG:4171, RGF93, geographic 2D</p></li> +<li><p>EPSG:4964, RGF93, geocentric</p></li> +<li><p>EPSG:4965, RGF93, geographic 3D</p></li> +<li><p>EPSG:7042, RGF93 (lon-lat), geographic 3D</p></li> +<li><p>EPSG:7084, RGF93 (lon-lat), geographic 2D</p></li> +<li><p>IGNF:RGF93, RGF93 cartesiennes geocentriques, geocentric</p></li> +<li><p>IGNF:RGF93GDD, RGF93 geographiques (dd),geographic 2D</p></li> +<li><p>IGNF:RGF93GEODD, RGF93 geographiques (dd), geographic 3D</p></li> +<li><p>IGNF:RGF93G, RGF93 geographiques (dms), geographic 2D</p></li> +<li><p>IGNF:RGF93GEO, RGF93 geographiques (dms), geographic 3D</p></li> +</ul> +<p>The first three entries from the EPSG dataset are typical: for each datum, +one can define a geographic 2D CRS (latitude, longitude), a geographic 3D crs +(latitude, longitude, ellipsoidal height) and a geocentric one. For that particular +case, the EPSG dataset has also included two extra definitions corresponding to a +longitude, latitude, [ellipsoidal height] coordinate system, as found in the official +French IGNF registry. This IGNF registry has also definitions for a geographic 2D +CRS (with an extra subtlety with an entry using decimal degree as unit and another +one degree-minute-second), geographic 3D and geocentric.</p> +<p>For EPSG:6258, there are 7 matching (non-deprecated) geodetic CRSs:</p> +<ul class="simple"> +<li><p>EPSG:4258, ETRS89, geographic 2D</p></li> +<li><p>EPSG:4936, ETRS89, geocentric</p></li> +<li><p>EPSG:4937, ETRS89, geographic 3D</p></li> +<li><p>IGNF:ETRS89, ETRS89 cartesiennes geocentriques, geocentric</p></li> +<li><p>IGNF:ETRS89G, ETRS89 geographiques (dms), geographic 2D</p></li> +<li><p>IGNF:ETRS89GEO, ETRS89 geographiques (dms), geographic 3D</p></li> +<li><p>ESRI:104129, GCS_EUREF_FIN, geographic 2D</p></li> +</ul> +<p>So the 3 typical EPSG entries, 3 equivalent (with long, lat ordering for the +geographic CRS) and one from the ESRI registry;</p> +<p>PROJ can now test 10 x 7 different combinations of source x target CRSs, using +the database searching method explained in the previous section. As soon as +one of this combination returns at least one non-ballpark combination, the result +set coming from that combination is used. PROJ will then add before that +transformation a conversion between the source CRS and the first intermediate CRS, +and will add at the end a conversion between the second intermediate CRS and the +target CRS. Those conversions are conversion between geographic 2D and geographic 3D +CRS or geographic 2D/3D and geocentric CRS.</p> +<p>This is done by the <code class="xref c c-func docutils literal notranslate"><span class="pre">createOperationsWithDatumPivot()</span></code> method.</p> +<p>So if transforming between EPSG:7042, RGF93 (lon-lat), geographic 3D and +EPSG:4936, ETRS89, geocentric, one get the following concatenated operation, +chaining an axis order change, the null geocentric translation between +RGF93 and ETRS89 (EPSG:1591), and a conversion between geographic and geocentric +coordinates. This concatenated operation is assumed to have a perfect accuracy +as both the initial and final operations are conversions, and the middle transformation +accounts for the fact that the RGF93 datum is one realization of ETRS89, so they +are equivalent for most purposes.</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s EPSG:7042 -t EPSG:4936 + +Candidate operations found: <span class="m">1</span> +------------------------------------- +Operation No. <span class="m">1</span>: + +unknown id, axis order change <span class="o">(</span>geographic3D horizontal<span class="o">)</span> + RGF93 to ETRS89 <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Conversion from ETRS89 <span class="o">(</span>geog2D<span class="o">)</span> to ETRS89 <span class="o">(</span>geocentric<span class="o">)</span>, <span class="m">0</span> m, France + +PROJ string: ++proj<span class="o">=</span>pipeline +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>deg +xy_out<span class="o">=</span>rad +step +proj<span class="o">=</span>cart +ellps<span class="o">=</span>GRS80 + +WKT2:2019 string: +CONCATENATEDOPERATION<span class="o">[</span><span class="s2">"axis order change (geographic3D horizontal) + RGF93 to ETRS89 (1) + Conversion from ETRS89 (geog2D) to ETRS89 (geocentric)"</span>, + SOURCECRS<span class="o">[</span> + GEOGCRS<span class="o">[</span><span class="s2">"RGF93 (lon-lat)"</span>, + <span class="o">[</span>...<span class="o">]</span> + ID<span class="o">[</span><span class="s2">"EPSG"</span>,7042<span class="o">]]]</span>, + TARGETCRS<span class="o">[</span> + GEODCRS<span class="o">[</span><span class="s2">"ETRS89"</span>, + <span class="o">[</span>...<span class="o">]</span> + ID<span class="o">[</span><span class="s2">"EPSG"</span>,4936<span class="o">]]]</span>, + STEP<span class="o">[</span> + CONVERSION<span class="o">[</span><span class="s2">"axis order change (geographic3D horizontal)"</span>, + METHOD<span class="o">[</span><span class="s2">"Axis Order Reversal (Geographic3D horizontal)"</span>, + ID<span class="o">[</span><span class="s2">"EPSG"</span>,9844<span class="o">]]</span>, + ID<span class="o">[</span><span class="s2">"EPSG"</span>,15499<span class="o">]]]</span>, + STEP<span class="o">[</span> + COORDINATEOPERATION<span class="o">[</span><span class="s2">"RGF93 to ETRS89 (1)"</span>, + <span class="o">[</span>...<span class="o">]</span> + METHOD<span class="o">[</span><span class="s2">"Geocentric translations (geog2D domain)"</span>, + ID<span class="o">[</span><span class="s2">"EPSG"</span>,9603<span class="o">]]</span>, + PARAMETER<span class="o">[</span><span class="s2">"X-axis translation"</span>,0, + LENGTHUNIT<span class="o">[</span><span class="s2">"metre"</span>,1<span class="o">]</span>, + ID<span class="o">[</span><span class="s2">"EPSG"</span>,8605<span class="o">]]</span>, + PARAMETER<span class="o">[</span><span class="s2">"Y-axis translation"</span>,0, + LENGTHUNIT<span class="o">[</span><span class="s2">"metre"</span>,1<span class="o">]</span>, + ID<span class="o">[</span><span class="s2">"EPSG"</span>,8606<span class="o">]]</span>, + PARAMETER<span class="o">[</span><span class="s2">"Z-axis translation"</span>,0, + LENGTHUNIT<span class="o">[</span><span class="s2">"metre"</span>,1<span class="o">]</span>, + ID<span class="o">[</span><span class="s2">"EPSG"</span>,8607<span class="o">]]</span>, + OPERATIONACCURACY<span class="o">[</span><span class="m">0</span>.0<span class="o">]</span>, + ID<span class="o">[</span><span class="s2">"EPSG"</span>,1591<span class="o">]</span>, + REMARK<span class="o">[</span><span class="s2">"May be taken as approximate transformation RGF93 to WGS 84 - see code 1671."</span><span class="o">]]]</span>, + STEP<span class="o">[</span> + CONVERSION<span class="o">[</span><span class="s2">"Conversion from ETRS89 (geog2D) to ETRS89 (geocentric)"</span>, + METHOD<span class="o">[</span><span class="s2">"Geographic/geocentric conversions"</span>, + ID<span class="o">[</span><span class="s2">"EPSG"</span>,9602<span class="o">]]]]</span>, + USAGE<span class="o">[</span> + SCOPE<span class="o">[</span><span class="s2">"unknown"</span><span class="o">]</span>, + AREA<span class="o">[</span><span class="s2">"France"</span><span class="o">]</span>, + BBOX<span class="o">[</span><span class="m">41</span>.15,-9.86,51.56,10.38<span class="o">]]]</span> +</pre></div> +</div> +</section> +<section id="geodetic-geographic-crs-to-geodetic-geographic-crs-without-direct-transformation"> +<h2>Geodetic/geographic CRS to Geodetic/geographic CRS, without direct transformation<a class="headerlink" href="#geodetic-geographic-crs-to-geodetic-geographic-crs-without-direct-transformation" title="Permalink to this headline">¶</a></h2> +<p>Still considering transformations between geodetic/geographic CRS, but let’s +consider that the lookup in the database for a transformation between +the source and target CRS (possibly going through the “equivalent” CRS based on +the same datum as detailed in the previous section) leads to an empty set.</p> +<p>Of course, as most operations are invertible, one first tries to do a lookup switching +the source and target CRS, and inverting the resulting operation(s):</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s NAD83 -t NAD27 --spatial-test intersects --summary + +Candidate operations found: <span class="m">10</span> +INVERSE<span class="o">(</span>DERIVED_FROM<span class="o">(</span>EPSG<span class="o">))</span>:1312, Inverse of NAD27 to NAD83 <span class="o">(</span><span class="m">3</span><span class="o">)</span>, <span class="m">2</span>.0 m, Canada +INVERSE<span class="o">(</span>DERIVED_FROM<span class="o">(</span>EPSG<span class="o">))</span>:1313, Inverse of NAD27 to NAD83 <span class="o">(</span><span class="m">4</span><span class="o">)</span>, <span class="m">1</span>.5 m, Canada - NAD27 +INVERSE<span class="o">(</span>DERIVED_FROM<span class="o">(</span>EPSG<span class="o">))</span>:1241, Inverse of NAD27 to NAD83 <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">0</span>.15 m, USA - CONUS including EEZ +INVERSE<span class="o">(</span>DERIVED_FROM<span class="o">(</span>EPSG<span class="o">))</span>:1243, Inverse of NAD27 to NAD83 <span class="o">(</span><span class="m">2</span><span class="o">)</span>, <span class="m">0</span>.5 m, USA - Alaska including EEZ +INVERSE<span class="o">(</span>DERIVED_FROM<span class="o">(</span>EPSG<span class="o">))</span>:1573, Inverse of NAD27 to NAD83 <span class="o">(</span><span class="m">6</span><span class="o">)</span>, <span class="m">1</span>.5 m, Canada - Quebec, at least one grid missing +INVERSE<span class="o">(</span>EPSG<span class="o">)</span>:1462, Inverse of NAD27 to NAD83 <span class="o">(</span><span class="m">5</span><span class="o">)</span>, <span class="m">2</span>.0 m, Canada - Quebec, at least one grid missing +INVERSE<span class="o">(</span>EPSG<span class="o">)</span>:9111, Inverse of NAD27 to NAD83 <span class="o">(</span><span class="m">9</span><span class="o">)</span>, <span class="m">1</span>.5 m, Canada - Saskatchewan, at least one grid missing +unknown id, Ballpark geographic offset from NAD83 to NAD27, unknown accuracy, World, has ballpark transformation +INVERSE<span class="o">(</span>EPSG<span class="o">)</span>:8555, Inverse of NAD27 to NAD83 <span class="o">(</span><span class="m">7</span><span class="o">)</span>, <span class="m">0</span>.15 m, USA - CONUS and GoM, at least one grid missing +INVERSE<span class="o">(</span>EPSG<span class="o">)</span>:8549, Inverse of NAD27 to NAD83 <span class="o">(</span><span class="m">8</span><span class="o">)</span>, <span class="m">0</span>.5 m, USA - Alaska, at least one grid missing +</pre></div> +</div> +<p>That was an easy case. Now let’s consider the transformation between the Australian +CRS AGD84 and GDA2020. There is no direct transformation from AGD84 to GDA2020, or +in the reverse direction, even when considering alternative geodetic CRS based on +the underlying datums. PROJ will then do a cross-join in the coordinate_operation_view +table to find the tuples (op1, op2) of coordinate operations such that:</p> +<ul class="simple"> +<li><p>SOURCE_CRS = op1.source_crs AND op1.target_crs = op2.source_crs AND op2.target_crs = TARGET_CRS or</p></li> +<li><p>SOURCE_CRS = op1.source_crs AND op1.target_crs = op2.target_crs AND op2.source_crs = TARGET_CRS or</p></li> +<li><p>SOURCE_CRS = op1.target_crs AND op1.source_crs = op2.source_crs AND op2.target_crs = TARGET_CRS or</p></li> +<li><p>SOURCE_CRS = op1.target_crs AND op1.source_crs = op2.target_crs AND op2.source_crs = TARGET_CRS</p></li> +</ul> +<p>Depending on which case is selected, op1 and op2 should be reversed, before +being concatenated.</p> +<p>This logic is implement by the <code class="docutils literal notranslate"><span class="pre">findsOpsInRegistryWithIntermediate()</span></code> method.</p> +<p>Assuming that the proj-datumgrid-oceania package is installed, we get the +following results for the AGD84 to GDA2020 coordinate operations lookup:</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s AGD84 -t GDA2020 --spatial-test intersects -o PROJ + +Candidate operations found: <span class="m">4</span> +------------------------------------- +Operation No. <span class="m">1</span>: + +unknown id, AGD84 to GDA94 <span class="o">(</span><span class="m">5</span><span class="o">)</span> + GDA94 to GDA2020 <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">0</span>.11 m, Australia - AGD84 + +PROJ string: ++proj<span class="o">=</span>pipeline +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>deg +xy_out<span class="o">=</span>rad <span class="se">\</span> + +step +proj<span class="o">=</span>hgridshift +grids<span class="o">=</span>National_84_02_07_01.gsb <span class="se">\</span> + +step +proj<span class="o">=</span>push +v_3 <span class="se">\</span> + +step +proj<span class="o">=</span>cart +ellps<span class="o">=</span>GRS80 <span class="se">\</span> + +step +proj<span class="o">=</span>helmert +x<span class="o">=</span><span class="m">0</span>.06155 +y<span class="o">=</span>-0.01087 +z<span class="o">=</span>-0.04019 <span class="se">\</span> + +rx<span class="o">=</span>-0.0394924 +ry<span class="o">=</span>-0.0327221 +rz<span class="o">=</span>-0.0328979 <span class="se">\</span> + +s<span class="o">=</span>-0.009994 +convention<span class="o">=</span>coordinate_frame <span class="se">\</span> + +step +inv +proj<span class="o">=</span>cart +ellps<span class="o">=</span>GRS80 <span class="se">\</span> + +step +proj<span class="o">=</span>pop +v_3 <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>rad +xy_out<span class="o">=</span>deg <span class="se">\</span> + +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 + +------------------------------------- +Operation No. <span class="m">2</span>: + +unknown id, AGD84 to GDA94 <span class="o">(</span><span class="m">2</span><span class="o">)</span> + GDA94 to GDA2020 <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">1</span>.01 m, Australia - AGD84 + +PROJ string: ++proj<span class="o">=</span>pipeline +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>deg +xy_out<span class="o">=</span>rad <span class="se">\</span> + +step +proj<span class="o">=</span>push +v_3 <span class="se">\</span> + +step +proj<span class="o">=</span>cart +ellps<span class="o">=</span>aust_SA <span class="se">\</span> + +step +proj<span class="o">=</span>helmert +x<span class="o">=</span>-117.763 +y<span class="o">=</span>-51.51 +z<span class="o">=</span><span class="m">139</span>.061 <span class="se">\</span> + +rx<span class="o">=</span>-0.292 +ry<span class="o">=</span>-0.443 +rz<span class="o">=</span>-0.277 +s<span class="o">=</span>-0.191 <span class="se">\</span> + +convention<span class="o">=</span>coordinate_frame <span class="se">\</span> + +step +proj<span class="o">=</span>helmert +x<span class="o">=</span><span class="m">0</span>.06155 +y<span class="o">=</span>-0.01087 +z<span class="o">=</span>-0.04019 <span class="se">\</span> + +rx<span class="o">=</span>-0.0394924 +ry<span class="o">=</span>-0.0327221 +rz<span class="o">=</span>-0.0328979 <span class="se">\</span> + +s<span class="o">=</span>-0.009994 +convention<span class="o">=</span>coordinate_frame <span class="se">\</span> + +step +inv +proj<span class="o">=</span>cart +ellps<span class="o">=</span>GRS80 <span class="se">\</span> + +step +proj<span class="o">=</span>pop +v_3 <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>rad +xy_out<span class="o">=</span>deg <span class="se">\</span> + +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 + +------------------------------------- +Operation No. <span class="m">3</span>: + +unknown id, AGD84 to GDA94 <span class="o">(</span><span class="m">5</span><span class="o">)</span> + GDA94 to GDA2020 <span class="o">(</span><span class="m">2</span><span class="o">)</span>, <span class="m">0</span>.15 m, unknown domain of validity + +PROJ string: ++proj<span class="o">=</span>pipeline +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>deg +xy_out<span class="o">=</span>rad <span class="se">\</span> + +step +proj<span class="o">=</span>hgridshift +grids<span class="o">=</span>National_84_02_07_01.gsb <span class="se">\</span> + +step +proj<span class="o">=</span>hgridshift +grids<span class="o">=</span>GDA94_GDA2020_conformal_and_distortion.gsb <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>rad +xy_out<span class="o">=</span>deg <span class="se">\</span> + +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 + +------------------------------------- +Operation No. <span class="m">4</span>: + +unknown id, AGD84 to GDA94 <span class="o">(</span><span class="m">5</span><span class="o">)</span> + GDA94 to GDA2020 <span class="o">(</span><span class="m">3</span><span class="o">)</span>, <span class="m">0</span>.15 m, unknown domain of validity + +PROJ string: ++proj<span class="o">=</span>pipeline +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>deg +xy_out<span class="o">=</span>rad <span class="se">\</span> + +step +proj<span class="o">=</span>hgridshift +grids<span class="o">=</span>National_84_02_07_01.gsb <span class="se">\</span> + +step +proj<span class="o">=</span>hgridshift +grids<span class="o">=</span>GDA94_GDA2020_conformal.gsb <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>rad +xy_out<span class="o">=</span>deg <span class="se">\</span> + +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 +</pre></div> +</div> +<p>One can see that the selected intermediate CRS that has been used is GDA94. +This is a completely novel behavior of PROJ 6 as opposed to the logic of PROJ.4 +where datum transformations implied using EPSG:4326 / WGS 84 has the mandatory +datum hub. PROJ 6 no longer hardcodes it as the mandatory datum hub, and relies +on the database to find the appropriate hub(s). +Actually, WGS 84 has been considered during the above lookup, because there are +transformations between AGD84 and WGS 84 and WGS 84 and GDA2020. However those +have been discarded in a step which we did not mention previously: just after +the initial filtering of results and their sorting, there is a final filtering +that is done. In the list of sorted results, given two operations A and B that +have the same area of use, if B has an accuracy lower than A, and A does not use +grids, or all the needed grids are available, then B is discarded.</p> +<p>If one forces the datum hub to be considered to be EPSG:4326, ones gets:</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s AGD84 -t GDA2020 --spatial-test intersects --pivot-crs EPSG:4326 -o PROJ + +Candidate operations found: <span class="m">2</span> +------------------------------------- +Operation No. <span class="m">1</span>: + +unknown id, AGD84 to WGS <span class="m">84</span> <span class="o">(</span><span class="m">7</span><span class="o">)</span> + Inverse of GDA2020 to WGS <span class="m">84</span> <span class="o">(</span><span class="m">2</span><span class="o">)</span>, <span class="m">4</span> m, Australia - AGD84 + +PROJ string: ++proj<span class="o">=</span>pipeline +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>deg +xy_out<span class="o">=</span>rad <span class="se">\</span> + +step +proj<span class="o">=</span>push +v_3 <span class="se">\</span> + +step +proj<span class="o">=</span>cart +ellps<span class="o">=</span>aust_SA <span class="se">\</span> + +step +proj<span class="o">=</span>helmert +x<span class="o">=</span>-117.763 +y<span class="o">=</span>-51.51 +z<span class="o">=</span><span class="m">139</span>.061 <span class="se">\</span> + +rx<span class="o">=</span>-0.292 +ry<span class="o">=</span>-0.443 +rz<span class="o">=</span>-0.277 <span class="se">\</span> + +s<span class="o">=</span>-0.191 +convention<span class="o">=</span>coordinate_frame <span class="se">\</span> + +step +inv +proj<span class="o">=</span>cart +ellps<span class="o">=</span>GRS80 <span class="se">\</span> + +step +proj<span class="o">=</span>pop +v_3 <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>rad +xy_out<span class="o">=</span>deg <span class="se">\</span> + +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 + +------------------------------------- +Operation No. <span class="m">2</span>: + +unknown id, AGD84 to WGS <span class="m">84</span> <span class="o">(</span><span class="m">9</span><span class="o">)</span> + Inverse of GDA2020 to WGS <span class="m">84</span> <span class="o">(</span><span class="m">2</span><span class="o">)</span>, <span class="m">4</span> m, Australia - AGD84 + +PROJ string: ++proj<span class="o">=</span>pipeline +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>deg +xy_out<span class="o">=</span>rad <span class="se">\</span> + +step +proj<span class="o">=</span>hgridshift +grids<span class="o">=</span>National_84_02_07_01.gsb <span class="se">\</span> + +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>rad +xy_out<span class="o">=</span>deg <span class="se">\</span> + +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 +</pre></div> +</div> +<p>Those operations are less accurate, since WGS 84 is assumed to be equivalent to +GDA2020 with an accuracy of 4 metre. This is an instance demonstrating that using +WGS 84 as a hub systematically can be sub-optimal.</p> +<p>There are still situations where the attempt to find a hub CRS does not work, +because there is no such hub. This can occur for example when transforming from +GDA94 to the latest realization at time of writing of WGS 84, WGS 84 (G1762). +There are transformations between WGS 84 (G1762). Using the above described +techniques, we would only find one non-ballpark operation taking the route: +1. Conversion from GDA94 (geog2D) to GDA94 (geocentric): synthetized by PROJ +2. Inverse of ITRF2008 to GDA94 (1): from EPSG +3. Inverse of WGS 84 (G1762) to ITRF2008 (1): from EPSG +4. Conversion from WGS 84 (G1762) (geocentric) to WGS 84 (G1762): synthetized by PROJ</p> +<p>This is not bad, but the global validity area of use is “Australia - onshore and EEZ”, +whereas GDA94 has a larger area of use. +There is another road that can be taken by going through GDA2020 instead of ITRF2008. +The GDA94 to GDA2020 transformations operate on the respective geographic CRS, +whereas GDA2020 to WGS 84 (G1762) operate on the geocentric CRS. Consequently, +GDA2020 cannot be identifier as a hub by a “simple” self-join SQL request on +the coordinate operation table. This requires to do the join based on the datum +referenced by the source and target CRS of each operation rather than the +source and target CRS themselves. When there is a match, PROJ inserts the required +conversions between geographic and geocentric CRS to have a consistent concatenated +operation, like the following: +1. GDA94 to GDA2020 (1): from EPSG +2. Conversion from GDA2020 (geog2D) to GDA2020 (geocentric): synthetized by PROJ +3. GDA2020 to WGS 84 (G1762) (1): from EPSG +4. Conversion from WGS 84 (G1762) (geocentric) to WGS 84 (G1762) (geog2D): synthetized by PROJ</p> +</section> +<section id="projected-crs-to-any-target-crs"> +<h2>Projected CRS to any target CRS<a class="headerlink" href="#projected-crs-to-any-target-crs" title="Permalink to this headline">¶</a></h2> +<p>This actually extends to any Derived CRS, whose Projected CRS is a well-known +particular case. Such transformations are done in 2 steps:</p> +<ol class="arabic simple"> +<li><p>Use the inverse conversion of the derived CRS to its base CRS, typically an +inverse map projection.</p></li> +<li><p>Find transformations from this base CRS to the target CRS. If the base CRS +is the target CRS, this step can be skipped.</p></li> +</ol> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s EPSG:32631 -t RGF93 + +Candidate operations found: <span class="m">1</span> +------------------------------------- +Operation No. <span class="m">1</span>: + +unknown id, Inverse of UTM zone 31N + Inverse of RGF93 to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">1</span> m, France + +PROJ string: ++proj<span class="o">=</span>pipeline +step +inv +proj<span class="o">=</span>utm +zone<span class="o">=</span><span class="m">31</span> +ellps<span class="o">=</span>WGS84 +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>rad +xy_out<span class="o">=</span>deg +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 +</pre></div> +</div> +<p>This is implemented by the <code class="docutils literal notranslate"><span class="pre">createOperationsDerivedTo</span></code> method</p> +<p>For the symmetric case, source CRS to a derived CRS, the above algorithm is applied +by switching the source and target CRS, and then inverting the resulting operation(s). +This is mostly a matter of avoiding to write very similar code twice. This logic +is also applied to all below cases when considering the transformation between 2 different +types of objects.</p> +</section> +<section id="vertical-crs-to-a-geographic-crs"> +<span id="verttogeog"></span><h2>Vertical CRS to a Geographic CRS<a class="headerlink" href="#vertical-crs-to-a-geographic-crs" title="Permalink to this headline">¶</a></h2> +<p>Such transformation is normally not meant as being used as standalone by PROJ +users, but as an internal computation step of a Compound CRS to a target CRS.</p> +<p>In cases where we are lucky, the PROJ database will have a transformation registered +between those:</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s <span class="s2">"NAVD88 height"</span> -t <span class="s2">"NAD83(2011)"</span> -o PROJ --spatial-test intersects +Candidate operations found: <span class="m">11</span> +------------------------------------- +Operation No. <span class="m">1</span>: + +INVERSE<span class="o">(</span>DERIVED_FROM<span class="o">(</span>EPSG<span class="o">))</span>:9229, Inverse of NAD83<span class="o">(</span><span class="m">2011</span><span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span>, <span class="m">0</span>.015 m, USA - CONUS - onshore + +PROJ string: ++proj<span class="o">=</span>vgridshift +grids<span class="o">=</span>g2018u0.gtx +multiplier<span class="o">=</span><span class="m">1</span> +</pre></div> +</div> +<p>But in cases where there is no match, the <code class="docutils literal notranslate"><span class="pre">createOperationsVertToGeog</span></code> method +will be used to synthetize a ballpark vertical transformation, just taking care +of unit changes, and axis reversal in case the vertical CRS was a depth rather than +a height. Of course the results of such an operation are questionable, hence the +ballpark qualifier and a unknown accuracy advertized for such an operation.</p> +</section> +<section id="vertical-crs-to-a-vertical-crs"> +<h2>Vertical CRS to a Vertical CRS<a class="headerlink" href="#vertical-crs-to-a-vertical-crs" title="Permalink to this headline">¶</a></h2> +<p>Overall logic is similar to the above case. There might be direct operations in +the PROJ database, involving grid transformations or simple offsets. The fallback +case is to synthetize a ballpark transformation.</p> +<p>This is implemented by the <code class="docutils literal notranslate"><span class="pre">createOperationsVertToVert</span></code> method</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s <span class="s2">"NGVD29 depth (ftUS)"</span> -t <span class="s2">"NAVD88 height"</span> --spatial-test intersects -o PROJ + +Candidate operations found: <span class="m">3</span> +------------------------------------- +Operation No. <span class="m">1</span>: + +unknown id, Inverse of NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NGVD29 depth <span class="o">(</span>ftUS<span class="o">)</span> + NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NGVD29 height <span class="o">(</span>m<span class="o">)</span> + NGVD29 height <span class="o">(</span>m<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span>, <span class="m">0</span>.02 m, USA - CONUS east of <span class="m">89</span>°W - onshore + +PROJ string: ++proj<span class="o">=</span>pipeline +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">1</span>,2,-3 +step +proj<span class="o">=</span>unitconvert +z_in<span class="o">=</span>us-ft +z_out<span class="o">=</span>m +step +proj<span class="o">=</span>vgridshift +grids<span class="o">=</span>vertcone.gtx +multiplier<span class="o">=</span><span class="m">0</span>.001 + +------------------------------------- +Operation No. <span class="m">2</span>: + +unknown id, Inverse of NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NGVD29 depth <span class="o">(</span>ftUS<span class="o">)</span> + NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NGVD29 height <span class="o">(</span>m<span class="o">)</span> + NGVD29 height <span class="o">(</span>m<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">2</span><span class="o">)</span>, <span class="m">0</span>.02 m, USA - CONUS <span class="m">89</span>°W-107°W - onshore + +PROJ string: ++proj<span class="o">=</span>pipeline +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">1</span>,2,-3 +step +proj<span class="o">=</span>unitconvert +z_in<span class="o">=</span>us-ft +z_out<span class="o">=</span>m +step +proj<span class="o">=</span>vgridshift +grids<span class="o">=</span>vertconc.gtx +multiplier<span class="o">=</span><span class="m">0</span>.001 + +------------------------------------- +Operation No. <span class="m">3</span>: + +unknown id, Inverse of NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NGVD29 depth <span class="o">(</span>ftUS<span class="o">)</span> + NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NGVD29 height <span class="o">(</span>m<span class="o">)</span> + NGVD29 height <span class="o">(</span>m<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">0</span>.02 m, USA - CONUS west of <span class="m">107</span>°W - onshore + +PROJ string: ++proj<span class="o">=</span>pipeline +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">1</span>,2,-3 +step +proj<span class="o">=</span>unitconvert +z_in<span class="o">=</span>us-ft +z_out<span class="o">=</span>m +step +proj<span class="o">=</span>vgridshift +grids<span class="o">=</span>vertconw.gtx +multiplier<span class="o">=</span><span class="m">0</span>.001 +</pre></div> +</div> +</section> +<section id="compound-crs-to-a-geographic-crs"> +<h2>Compound CRS to a Geographic CRS<a class="headerlink" href="#compound-crs-to-a-geographic-crs" title="Permalink to this headline">¶</a></h2> +<p>A typical example of a Compound CRS is a CRS made of a geographic or projected CRS +as the horizontal component, and a vertical CRS. E.g. “NAD83 + NAVD88 height”</p> +<p>When the horizontal component of the compound source CRS is a projected CRS, we +first look for the operation from this source CRS to another compound CRS made +of the geographic CRS base of the projected CRS, +like “NAD83 / California zone 1 (ftUS) + NAVD88 height” to “NAD83 + NAVD88 height”, +which ultimately goes to one of the above described case. Then we can consider +the transformation from a compound CRS made of a geographic CRS to another geographic CRS.</p> +<p>It first starts by the vertical transformations from the vertical CRS of the +source compound CRS to the target geographic CRS, using the strategy detailed +in <a class="reference internal" href="#verttogeog"><span class="std std-ref">Vertical CRS to a Geographic CRS</span></a></p> +<p>What we did not mention is that when there is not a transformation registered +between the vertical CRS and the target geographic CRS, PROJ attempts to find +transformations between that vertical CRS and any other geographic CRS. This is +clearly an approximation. +If the research of the vertical CRS to the target geographic CRS resulted in +operations that use grids that are not available, as another approximation, we +research operations from the vertical CRS to the source geographic CRS for the +vertical component.</p> +<p>Once we got those more or less accurate vertical transformations, we must consider +the horizontal transformation(s). The algorithm iterates over all found vertical +transformations and look for their target geographic CRS. This will be used as +the interpolation CRS for horizontal transformations. PROJ will then look for +available transformations from the source geographic CRS to the interpolation CRS +and from the interpolation CRS to the target geographic CRS. There is then a +3-level loop to create the final set of operations chaining together:</p> +<ul class="simple"> +<li><p>the horizontal transformation from the source geographic CRS to the interpolation CRS</p></li> +<li><p>the vertical transformation from the source vertical CRS to the interpolation CRS</p></li> +<li><p>the horizontal transformation from the interpolation CRS to the target geographic CRS.</p></li> +</ul> +<p>This is implemented by the <code class="docutils literal notranslate"><span class="pre">createOperationsCompoundToGeog</span></code> method</p> +<p>Example:</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s <span class="s2">"NAD83(NSRS2007) + NAVD88 height"</span> -t <span class="s2">"WGS 84 (G1762)"</span> --spatial-test intersects --summary + +Candidate operations found: <span class="m">21</span> +unknown id, Inverse of NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.05 m, USA - CONUS - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">7</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS south of <span class="m">41</span>°N, <span class="m">95</span>°W to <span class="m">78</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">7</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">3</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS south of <span class="m">41</span>°N, <span class="m">95</span>°W to <span class="m">78</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">6</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS south of <span class="m">41</span>°N, <span class="m">112</span>°W to <span class="m">95</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">6</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">3</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS south of <span class="m">41</span>°N, <span class="m">112</span>°W to <span class="m">95</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">2</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS north of <span class="m">41</span>°N, <span class="m">112</span>°W to <span class="m">95</span>°W +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">2</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">3</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS north of <span class="m">41</span>°N, <span class="m">112</span>°W to <span class="m">95</span>°W +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS north of <span class="m">41</span>°N, <span class="m">95</span>°W to <span class="m">78</span>°W +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">3</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS north of <span class="m">41</span>°N, <span class="m">95</span>°W to <span class="m">78</span>°W +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">5</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS south of <span class="m">41</span>°N, west of <span class="m">112</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">5</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">3</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS south of <span class="m">41</span>°N, west of <span class="m">112</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS north of <span class="m">41</span>°N, west of <span class="m">112</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">3</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS north of <span class="m">41</span>°N, west of <span class="m">112</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">4</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS north of <span class="m">41</span>°N, east of <span class="m">78</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">4</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">3</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS north of <span class="m">41</span>°N, east of <span class="m">78</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">8</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS south of <span class="m">41</span>°N, east of <span class="m">78</span>°W - onshore +unknown id, Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span>HARN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">8</span><span class="o">)</span> + NAD83<span class="o">(</span>HARN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">3</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, <span class="m">3</span>.15 m, USA - CONUS south of <span class="m">41</span>°N, east of <span class="m">78</span>°W - onshore +unknown id, Ballpark geographic offset from NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> to NAD83<span class="o">(</span>FBN<span class="o">)</span> + Inverse of NAD83<span class="o">(</span>FBN<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Ballpark geographic offset from NAD83<span class="o">(</span>FBN<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, unknown accuracy, USA - CONUS - onshore, has ballpark transformation +unknown id, Ballpark geographic offset from NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> to NAD83<span class="o">(</span><span class="m">2011</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span><span class="m">2011</span><span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span> + Ballpark geographic offset from NAD83<span class="o">(</span><span class="m">2011</span><span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span>, unknown accuracy, USA - CONUS - onshore, has ballpark transformation +unknown id, Ballpark geographic offset from NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> to NAD83<span class="o">(</span><span class="m">2011</span><span class="o">)</span> + Inverse of NAD83<span class="o">(</span><span class="m">2011</span><span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span> + Conversion from NAD83<span class="o">(</span><span class="m">2011</span><span class="o">)</span> <span class="o">(</span>geog2D<span class="o">)</span> to NAD83<span class="o">(</span><span class="m">2011</span><span class="o">)</span> <span class="o">(</span>geocentric<span class="o">)</span> + Inverse of ITRF2008 to NAD83<span class="o">(</span><span class="m">2011</span><span class="o">)</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span> to ITRF2008 <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Conversion from WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span> <span class="o">(</span>geocentric<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span> <span class="o">(</span>geog2D<span class="o">)</span>, unknown accuracy, USA - CONUS - onshore, has ballpark transformation +unknown id, NAD83<span class="o">(</span>NSRS2007<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + WGS <span class="m">84</span> to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span> + Transformation from NAVD88 height to WGS <span class="m">84</span> <span class="o">(</span>G1762<span class="o">)</span> <span class="o">(</span>ballpark vertical transformation, without ellipsoid height to vertical height correction<span class="o">)</span>, unknown accuracy, USA - CONUS and Alaska<span class="p">;</span> PRVI, has ballpark transformation +</pre></div> +</div> +</section> +<section id="compoundcrs-to-compoundcrs"> +<h2>CompoundCRS to CompoundCRS<a class="headerlink" href="#compoundcrs-to-compoundcrs" title="Permalink to this headline">¶</a></h2> +<p>There is some similarity with the previous paragraph. We first research the +vertical transformations between the two vertical CRS.</p> +<ol class="arabic"> +<li><p>If there is such a transformation, be it direct, or if both vertical CRS +relate to a common intermediate CRS. +If it has a registered interpolation geographic CRS, then it is used. +Otherwise we fallback to the geographic CRS of the source CRS.</p> +<p>Finally, a 3-level loop to create the final set of operations chaining together:</p> +<ul class="simple"> +<li><p>the horizontal transformation from the source CRS to the interpolation CRS</p></li> +<li><p>the vertical transformation</p></li> +<li><p>the horizontal transformation from the interpolation CRS to the target CRS.</p></li> +</ul> +<blockquote> +<div><p>Example:</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s <span class="s2">"NAD27 + NGVD29 height (ftUS)"</span> -t <span class="s2">"NAD83 + NAVD88 height"</span> --spatial-test intersects --summary + +Candidate operations found: <span class="m">20</span> +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">0</span>.17 m, USA - CONUS east of <span class="m">89</span>°W - onshore +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">2</span><span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">0</span>.17 m, USA - CONUS <span class="m">89</span>°W-107°W - onshore +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">0</span>.17 m, USA - CONUS west of <span class="m">107</span>°W - onshore +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">3</span><span class="o">)</span>, <span class="m">1</span>.02 m, unknown domain of validity +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">2</span><span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">3</span><span class="o">)</span>, <span class="m">1</span>.02 m, unknown domain of validity +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">3</span><span class="o">)</span>, <span class="m">1</span>.02 m, unknown domain of validity +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">5</span><span class="o">)</span>, <span class="m">1</span>.02 m, unknown domain of validity, at least one grid missing +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">6</span><span class="o">)</span>, <span class="m">1</span>.52 m, unknown domain of validity, at least one grid missing +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">2</span><span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">9</span><span class="o">)</span>, <span class="m">1</span>.52 m, unknown domain of validity, at least one grid missing +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">9</span><span class="o">)</span>, <span class="m">1</span>.52 m, unknown domain of validity, at least one grid missing +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">3</span><span class="o">)</span> + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, USA - CONUS east of <span class="m">89</span>°W - onshore, has ballpark transformation +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">2</span><span class="o">)</span> + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, USA - CONUS <span class="m">89</span>°W-107°W - onshore, has ballpark transformation +unknown id, NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, USA - CONUS west of <span class="m">107</span>°W - onshore, has ballpark transformation +unknown id, Transformation from NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span>ballpark vertical transformation<span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">1</span><span class="o">)</span>, unknown accuracy, USA - CONUS including EEZ, has ballpark transformation +unknown id, Transformation from NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span>ballpark vertical transformation<span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">3</span><span class="o">)</span>, unknown accuracy, Canada, has ballpark transformation +unknown id, Transformation from NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span>ballpark vertical transformation<span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">4</span><span class="o">)</span>, unknown accuracy, Canada - NAD27, has ballpark transformation +unknown id, Transformation from NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span>ballpark vertical transformation<span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">5</span><span class="o">)</span>, unknown accuracy, Canada - Quebec, has ballpark transformation, at least one grid missing +unknown id, Transformation from NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span>ballpark vertical transformation<span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">6</span><span class="o">)</span>, unknown accuracy, Canada - Quebec, has ballpark transformation, at least one grid missing +unknown id, Transformation from NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span>ballpark vertical transformation<span class="o">)</span> + NAD27 to NAD83 <span class="o">(</span><span class="m">9</span><span class="o">)</span>, unknown accuracy, Canada - Saskatchewan, has ballpark transformation, at least one grid missing +unknown id, Transformation from NGVD29 height <span class="o">(</span>ftUS<span class="o">)</span> to NAVD88 height <span class="o">(</span>ballpark vertical transformation<span class="o">)</span> + Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, World, has ballpark transformation +</pre></div> +</div> +</div></blockquote> +</li> +<li><p>Otherwise, when there is no such transformation, we decompose into 3 steps:</p> +<ul class="simple"> +<li><p>transform from the source CRS to the geographic 3D CRS corresponding to it</p></li> +<li><p>transform from the geographic 3D CRS corresponding to the source CRS to the +geographic 3D CRS corresponding to the target CRS</p></li> +<li><p>transform from the geographic 3D CRS corresponding to the target CRS to the +target CRS.</p></li> +</ul> +<blockquote> +<div><p>Example:</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s <span class="s2">"WGS 84 + EGM96 height"</span> -t <span class="s2">"ETRS89 + Belfast height"</span> --spatial-test intersects --summary + +Candidate operations found: <span class="m">7</span> +unknown id, Inverse of WGS <span class="m">84</span> to EGM96 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of ETRS89 to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + ETRS89 to Belfast height <span class="o">(</span><span class="m">2</span><span class="o">)</span>, <span class="m">2</span>.014 m, UK - Northern Ireland - onshore +unknown id, Inverse of WGS <span class="m">84</span> to EGM96 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Inverse of ETRS89 to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + ETRS89 to Belfast height <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">2</span>.03 m, UK - Northern Ireland - onshore, at least one grid missing +unknown id, Inverse of WGS <span class="m">84</span> to EGM96 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Null geographic offset from WGS <span class="m">84</span> <span class="o">(</span>geog3D<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span>geog2D<span class="o">)</span> + Inverse of OSGB <span class="m">1936</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">4</span><span class="o">)</span> + OSGB <span class="m">1936</span> to ETRS89 <span class="o">(</span><span class="m">2</span><span class="o">)</span> + Null geographic offset from ETRS89 <span class="o">(</span>geog2D<span class="o">)</span> to ETRS89 <span class="o">(</span>geog3D<span class="o">)</span> + ETRS89 to Belfast height <span class="o">(</span><span class="m">2</span><span class="o">)</span>, <span class="m">19</span>.044 m, unknown domain of validity +unknown id, Inverse of WGS <span class="m">84</span> to EGM96 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Null geographic offset from WGS <span class="m">84</span> <span class="o">(</span>geog3D<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span>geog2D<span class="o">)</span> + Inverse of OSGB <span class="m">1936</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">2</span><span class="o">)</span> + OSGB <span class="m">1936</span> to ETRS89 <span class="o">(</span><span class="m">2</span><span class="o">)</span> + Null geographic offset from ETRS89 <span class="o">(</span>geog2D<span class="o">)</span> to ETRS89 <span class="o">(</span>geog3D<span class="o">)</span> + ETRS89 to Belfast height <span class="o">(</span><span class="m">2</span><span class="o">)</span>, <span class="m">11</span>.044 m, unknown domain of validity +unknown id, Inverse of WGS <span class="m">84</span> to EGM96 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Null geographic offset from WGS <span class="m">84</span> <span class="o">(</span>geog3D<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span>geog2D<span class="o">)</span> + Inverse of TM75 to WGS <span class="m">84</span> <span class="o">(</span><span class="m">2</span><span class="o">)</span> + TM75 to ETRS89 <span class="o">(</span><span class="m">3</span><span class="o">)</span> + Null geographic offset from ETRS89 <span class="o">(</span>geog2D<span class="o">)</span> to ETRS89 <span class="o">(</span>geog3D<span class="o">)</span> + ETRS89 to Belfast height <span class="o">(</span><span class="m">2</span><span class="o">)</span>, <span class="m">2</span>.424 m, UK - Northern Ireland - onshore, at least one grid missing +unknown id, Inverse of WGS <span class="m">84</span> to EGM96 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Null geographic offset from WGS <span class="m">84</span> <span class="o">(</span>geog3D<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span>geog2D<span class="o">)</span> + Inverse of TM75 to WGS <span class="m">84</span> <span class="o">(</span><span class="m">2</span><span class="o">)</span> + TM75 to ETRS89 <span class="o">(</span><span class="m">3</span><span class="o">)</span> + Null geographic offset from ETRS89 <span class="o">(</span>geog2D<span class="o">)</span> to ETRS89 <span class="o">(</span>geog3D<span class="o">)</span> + ETRS89 to Belfast height <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">2</span>.44 m, UK - Northern Ireland - onshore, at least one grid missing +unknown id, Inverse of WGS <span class="m">84</span> to EGM96 height <span class="o">(</span><span class="m">1</span><span class="o">)</span> + Null geographic offset from WGS <span class="m">84</span> <span class="o">(</span>geog3D<span class="o">)</span> to WGS <span class="m">84</span> <span class="o">(</span>geog2D<span class="o">)</span> + Inverse of OSGB <span class="m">1936</span> to WGS <span class="m">84</span> <span class="o">(</span><span class="m">4</span><span class="o">)</span> + OSGB <span class="m">1936</span> to ETRS89 <span class="o">(</span><span class="m">2</span><span class="o">)</span> + Null geographic offset from ETRS89 <span class="o">(</span>geog2D<span class="o">)</span> to ETRS89 <span class="o">(</span>geog3D<span class="o">)</span> + ETRS89 to Belfast height <span class="o">(</span><span class="m">1</span><span class="o">)</span>, <span class="m">19</span>.06 m, unknown domain of validity, at least one grid missing +</pre></div> +</div> +</div></blockquote> +</li> +</ol> +<p>This is implemented by the <code class="docutils literal notranslate"><span class="pre">createOperationsCompoundToCompound</span></code> method</p> +</section> +<section id="when-the-source-or-target-crs-is-a-boundcrs"> +<h2>When the source or target CRS is a BoundCRS<a class="headerlink" href="#when-the-source-or-target-crs-is-a-boundcrs" title="Permalink to this headline">¶</a></h2> +<p>The BoundCRS concept is an hybrid concept where a CRS is linked to a transformation +from it to a hub CRS, typically WGS 84. This is a long-time practice in PROJ.4 +strings with the <code class="docutils literal notranslate"><span class="pre">+towgs84</span></code>, <code class="docutils literal notranslate"><span class="pre">+nadgrids</span></code> and <code class="docutils literal notranslate"><span class="pre">+geoidgrids</span></code> keywords, or the +<code class="docutils literal notranslate"><span class="pre">TOWGS84[]</span></code> node of WKT 1. When encountering those attributes when parsing +a CRS string, PROJ will create a BoundCRS object capturing this transformation. +A BoundCRS object can also be provided with a WKT2 string, and in that case with +a hub CRS being potentially different from WGS 84.</p> +<p>Let’s consider the case of a transformation between a BoundCRS +(“+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 ++ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m” +which used to be the PROJ.4 definition of “OSGB 1936 / British National Grid”) +and a target Geographic CRS, ETRS89.</p> +<p>We apply the following steps:</p> +<ul class="simple"> +<li><p>transform from the base of the source CRS (that is the CRS wrapped by BoundCRS, +here a ProjectedCRS) to the geographic CRS of this base CRS</p></li> +<li><p>apply the transformation of the BoundCRS to go from the geographic CRS of this base CRS +to the hub CRS of the BoundCRS, in that instance WGS 84.</p></li> +<li><p>apply a transformation from the hub CRS to the target CRS.</p></li> +</ul> +<p>This is implemented by the <code class="docutils literal notranslate"><span class="pre">createOperationsBoundToGeog</span></code> method</p> +<p>Example:</p> +<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>$ projinfo -s <span class="s2">"+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +type=crs"</span> -t ETRS89 -o PROJ + +Candidate operations found: <span class="m">1</span> +------------------------------------- +Operation No. <span class="m">1</span>: + +unknown id, Inverse of unknown + Transformation from unknown to WGS84 + Inverse of ETRS89 to WGS <span class="m">84</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span>, unknown accuracy, Europe - ETRS89 + +PROJ string: ++proj<span class="o">=</span>pipeline +step +inv +proj<span class="o">=</span>tmerc +lat_0<span class="o">=</span><span class="m">49</span> +lon_0<span class="o">=</span>-2 +k<span class="o">=</span><span class="m">0</span>.9996012717 +x_0<span class="o">=</span><span class="m">400000</span> +y_0<span class="o">=</span>-100000 +ellps<span class="o">=</span>airy +step +proj<span class="o">=</span>push +v_3 +step +proj<span class="o">=</span>cart +ellps<span class="o">=</span>airy +step +proj<span class="o">=</span>helmert +x<span class="o">=</span><span class="m">446</span>.448 +y<span class="o">=</span>-125.157 +z<span class="o">=</span><span class="m">542</span>.06 +rx<span class="o">=</span><span class="m">0</span>.15 +ry<span class="o">=</span><span class="m">0</span>.247 +rz<span class="o">=</span><span class="m">0</span>.842 +s<span class="o">=</span>-20.489 +convention<span class="o">=</span>position_vector +step +inv +proj<span class="o">=</span>cart +ellps<span class="o">=</span>GRS80 +step +proj<span class="o">=</span>pop +v_3 +step +proj<span class="o">=</span>unitconvert +xy_in<span class="o">=</span>rad +xy_out<span class="o">=</span>deg +step +proj<span class="o">=</span>axisswap +order<span class="o">=</span><span class="m">2</span>,1 +</pre></div> +</div> +<p>There are other situations with BoundCRS, involving vertical transformations, +or transforming to other objects than a geographic CRS, but the curious reader +will have to inspect the code for the actual gory details.</p> +</section> +</section> + + + </div> + </div> + <footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer"> + <a href="pipeline.html" class="btn btn-neutral float-left" title="The pipeline operator" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a> + <a href="../resource_files.html" class="btn btn-neutral float-right" title="Resource files" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a> + </div> + + <hr/> + + <div role="contentinfo"> + <p>© Copyright 1983-2022. + <span class="lastupdated">Last updated on 22 Mar 2022. + </span></p> + </div> + + Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a + <a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a> + provided by <a href="https://readthedocs.org">Read the Docs</a>. + + +</footer> + </div> + </div> + </section> + </div> + <script> + jQuery(function () { + SphinxRtdTheme.Navigation.enable(true); + }); + </script> + +</body> +</html>
\ No newline at end of file |
