diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2020-12-02 14:42:33 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-12-02 14:42:33 +0100 |
| commit | b7fb045ebde07ca461b08269697b25128ed503a1 (patch) | |
| tree | e0ca10fc65208fec18ca1311f896809a5ca12428 | |
| parent | f9655458a951b3a0704b5465ea0b1af3c7b8ba09 (diff) | |
| parent | d8fe9964bcbc6d1eeaddf6f5d47ca6d444dc8744 (diff) | |
| download | PROJ-b7fb045ebde07ca461b08269697b25128ed503a1.tar.gz PROJ-b7fb045ebde07ca461b08269697b25128ed503a1.zip | |
Merge pull request #2444 from rouault/topocentric
Add +proj=topocentric geocentric->topocentric conversion (fixes #500)
| -rw-r--r-- | docs/source/operations/conversions/images/ECEF_ENU_Longitude_Latitude_relationships.png | bin | 0 -> 30731 bytes | |||
| -rw-r--r-- | docs/source/operations/conversions/images/ECEF_ENU_Longitude_Latitude_relationships.svg | 348 | ||||
| -rw-r--r-- | docs/source/operations/conversions/index.rst | 1 | ||||
| -rw-r--r-- | docs/source/operations/conversions/topocentric.rst | 115 | ||||
| -rw-r--r-- | include/proj/internal/coordinateoperation_constants.hpp | 41 | ||||
| -rw-r--r-- | src/Makefile.am | 1 | ||||
| -rw-r--r-- | src/conversions/topocentric.cpp | 165 | ||||
| -rw-r--r-- | src/iso19111/coordinateoperation.cpp | 55 | ||||
| -rw-r--r-- | src/lib_proj.cmake | 1 | ||||
| -rw-r--r-- | src/pj_list.h | 1 | ||||
| -rw-r--r-- | src/proj_constants.h | 20 | ||||
| -rw-r--r-- | test/gie/builtins.gie | 42 | ||||
| -rw-r--r-- | test/unit/test_operation.cpp | 122 |
13 files changed, 904 insertions, 8 deletions
diff --git a/docs/source/operations/conversions/images/ECEF_ENU_Longitude_Latitude_relationships.png b/docs/source/operations/conversions/images/ECEF_ENU_Longitude_Latitude_relationships.png Binary files differnew file mode 100644 index 00000000..224ff738 --- /dev/null +++ b/docs/source/operations/conversions/images/ECEF_ENU_Longitude_Latitude_relationships.png diff --git a/docs/source/operations/conversions/images/ECEF_ENU_Longitude_Latitude_relationships.svg b/docs/source/operations/conversions/images/ECEF_ENU_Longitude_Latitude_relationships.svg new file mode 100644 index 00000000..c97ca181 --- /dev/null +++ b/docs/source/operations/conversions/images/ECEF_ENU_Longitude_Latitude_relationships.svg @@ -0,0 +1,348 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="520" + height="500" + id="svg2" + xml:space="preserve"><defs + id="defs6"><marker + refX="0" + refY="0" + orient="auto" + id="Arrow2Mend" + style="overflow:visible"><path + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="scale(-0.6,-0.6)" + id="path3759" + style="font-size:12px;fill:#0d6299;fill-rule:evenodd;stroke:#0d6299;stroke-width:0.625;stroke-linejoin:round" /></marker><marker + refX="0" + refY="0" + orient="auto" + id="Arrow1Mstart" + style="overflow:visible"><path + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + transform="matrix(0.4,0,0,0.4,4,0)" + id="path3738" + style="fill:#00b800;fill-rule:evenodd;stroke:#00b800;stroke-width:1pt;marker-start:none" /></marker><marker + refX="0" + refY="0" + orient="auto" + id="Arrow1Mend" + style="overflow:visible"><path + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + transform="matrix(-0.4,0,0,-0.4,-4,0)" + id="path3741" + style="fill:#00b800;fill-rule:evenodd;stroke:#00b800;stroke-width:1pt;marker-start:none" /></marker><marker + refX="0" + refY="0" + orient="auto" + id="Arrow2Mstart" + style="overflow:visible"><path + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="scale(0.6,0.6)" + id="path3756" + style="font-size:12px;fill:#0d6299;fill-rule:evenodd;stroke:#0d6299;stroke-width:0.625;stroke-linejoin:round" /></marker><marker + refX="0" + refY="0" + orient="auto" + id="Arrow1Lstart" + style="overflow:visible"><path + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + transform="matrix(0.8,0,0,0.8,10,0)" + id="path3732" + style="fill:#ff0000;fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;marker-start:none" /></marker><marker + refX="0" + refY="0" + orient="auto" + id="Arrow1Lend" + style="overflow:visible"><path + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + transform="matrix(-0.8,0,0,-0.8,-10,0)" + id="path3735" + style="fill:#ff0000;fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;marker-start:none" /></marker></defs><g + transform="matrix(1.25,0,0,-1.25,-136.61316,788.09803)" + id="g12"><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g18" + style="stroke:#808080;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 8;stroke-dashoffset:0"><path + d="m 283.32,265.39 1.08,0 0.96,0.36 1.08,0.48 0.96,0.72 0.96,0.84 0.96,1.08 1.08,1.32 0.96,1.44 0.96,1.68 0.96,1.8 0.96,2.04 0.84,2.28 0.96,2.4 0.96,2.64 0.84,2.76 0.84,2.88 0.84,3.12 0.84,3.24 0.84,3.48 0.84,3.48 0.72,3.84 0.72,3.84 0.72,4.08 0.72,4.08 0.72,4.32 0.6,4.44 0.6,4.56 0.6,4.68 0.6,4.8 0.6,4.93 0.48,5.03 0.48,5.16 0.48,5.16 0.36,5.28 0.36,5.4 0.36,5.4 0.36,5.52 0.24,5.52 0.24,5.64 0.24,5.64 0.24,5.76 0.12,5.76 0.12,5.76 0.12,5.76 0,5.76 0,5.88 0,5.76 0,5.76 -0.12,5.76 -0.12,5.88 -0.12,5.64 -0.24,5.77 -0.24,5.63 -0.24,5.64 -0.24,5.52 -0.36,5.52 -0.36,5.4 -0.36,5.4 -0.36,5.28 -0.48,5.16 -0.48,5.16 -0.48,5.04 -0.6,4.92 -0.6,4.8 -0.6,4.68 -0.6,4.56 -0.6,4.44 -0.72,4.32 -0.72,4.08 -0.72,4.08 -0.72,3.96 -0.72,3.72 -0.84,3.6 -0.84,3.36 -0.84,3.24 -0.84,3.12 -0.84,3 -0.84,2.76 -0.96,2.52 -0.96,2.4 -0.84,2.28 -0.96,2.04 -0.96,1.8 -0.96,1.68 -0.96,1.44 -1.08,1.32 -0.96,1.08 -0.96,0.84 -0.96,0.72 -1.08,0.49 -0.96,0.35 -1.08,0" + id="path20" + style="fill:none;stroke:#808080;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 8;stroke-dashoffset:0" /></g><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g22" + style="stroke:#808080;stroke-width:1.60000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 283.32,605.35 -0.96,0 -0.96,-0.35 -1.08,-0.49 -0.96,-0.72 -0.96,-0.84 -1.08,-1.08 -0.96,-1.32 -0.96,-1.44 -0.96,-1.68 -0.96,-1.8 -0.96,-2.04 -0.96,-2.28 -0.84,-2.4 -0.96,-2.52 -0.84,-2.76 -0.84,-3 -0.96,-3.12 -0.72,-3.24 -0.84,-3.36 -0.84,-3.6 -0.72,-3.72 -0.84,-3.96 -0.72,-4.08 -0.6,-4.08 -0.72,-4.32 -0.6,-4.44 -0.72,-4.56 -0.6,-4.68 -0.48,-4.8 -0.6,-4.92 -0.48,-5.04 -0.48,-5.16 -0.48,-5.16 -0.36,-5.28 -0.36,-5.4 -0.36,-5.4 -0.36,-5.52 -0.24,-5.52 -0.36,-5.64 -0.12,-5.63 -0.24,-5.77 -0.12,-5.64 -0.12,-5.88 -0.12,-5.76 0,-5.76 -0.12,-5.76 0.12,-5.88 0,-5.76 0.12,-5.76 0.12,-5.76 0.12,-5.76 0.24,-5.76 0.12,-5.64 0.36,-5.64 0.24,-5.52 0.36,-5.52 0.36,-5.4 0.36,-5.4 0.36,-5.28 0.48,-5.16 0.48,-5.16 0.48,-5.03 0.6,-4.93 0.48,-4.8 0.6,-4.68 0.72,-4.56 0.6,-4.44 0.72,-4.32 0.6,-4.08 0.72,-4.08 0.84,-3.84 0.72,-3.84 0.84,-3.48 0.84,-3.48 0.72,-3.24 0.96,-3.12 0.84,-2.88 0.84,-2.76 0.96,-2.64 0.84,-2.4 0.96,-2.28 0.96,-2.04 0.96,-1.8 0.96,-1.68 0.96,-1.44 0.96,-1.32 1.08,-1.08 0.96,-0.84 0.96,-0.72 1.08,-0.48 0.96,-0.36 0.96,0" + id="path24" + style="fill:none;stroke:#808080;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g26" + style="stroke:#808080;stroke-width:1.60000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 283.32,605.35 -8.88,-0.12 -8.88,-0.72 -8.76,-1.2 -8.76,-1.56 -8.64,-2.16 -8.52,-2.52 -8.4,-3 -8.28,-3.36 -8.04,-3.84 -7.8,-4.2 -7.56,-4.67 -7.32,-5.05 -7.08,-5.4 -6.84,-5.76 -6.36,-6.24 -6.24,-6.36 -5.76,-6.84 -5.4,-7.08 -5.04,-7.32 -4.68,-7.56 -4.2,-7.8 -3.84,-8.04 -3.36,-8.28 -3,-8.4 -2.52,-8.52 -2.16,-8.64 -1.56,-8.76 -1.2,-8.76 -0.72,-8.88 -0.12,-8.88 0.12,-9 0.72,-8.88 1.2,-8.76 1.56,-8.76 2.16,-8.64 2.52,-8.52 3,-8.4 3.36,-8.28 3.84,-8.04 4.2,-7.8 4.68,-7.56 5.04,-7.32 5.4,-7.08 5.76,-6.84 6.24,-6.36 6.36,-6.12 6.84,-5.88 7.08,-5.4 7.32,-5.04 7.56,-4.68 7.8,-4.2 8.04,-3.84 8.28,-3.36 8.4,-3 8.52,-2.52 8.64,-2.04 8.76,-1.68 8.76,-1.2 8.88,-0.6 8.88,-0.24" + id="path28" + style="fill:none;stroke:#808080;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g30" + style="stroke:#808080;stroke-width:1.60000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 453.36,435.43 0,0.96 -0.36,0.96 -0.48,1.08 -0.72,0.96 -0.84,0.96 -1.08,1.08 -1.32,0.96 -1.44,0.96 -1.68,0.96 -1.92,0.96 -2.04,0.96 -2.16,0.96 -2.52,0.84 -2.52,0.97 -2.76,0.83 -3,0.96 -3.12,0.84 -3.24,0.84 -3.48,0.84 -3.6,0.72 -3.72,0.84 -3.96,0.72 -3.96,0.72 -4.2,0.72 -4.32,0.6 -4.56,0.72 -4.56,0.6 -4.68,0.6 -4.8,0.6 -4.92,0.48 -5.04,0.48 -5.16,0.48 -5.16,0.48 -5.28,0.48 -5.4,0.36 -5.52,0.36 -5.52,0.24 -5.52,0.36 -5.64,0.24 -5.64,0.24 -5.76,0.12 -5.76,0.12 -5.76,0.12 -5.88,0.12 -5.76,0.12 -5.76,0 -5.88,-0.12 -5.76,0 -5.88,-0.12 -5.76,-0.12 -5.76,-0.12" + id="path32" + style="fill:none;stroke:#808080;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g46" + style="stroke:#ff9900;stroke-width:1.60000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 270.6,392.35 -0.24,3.48 -0.36,3.6 -0.24,3.6 -0.24,3.72 -0.24,3.72 -0.24,3.84 -0.12,3.96 -0.12,3.84 -0.12,3.96 0,3.96 -0.12,4.08 0,3.96 0.12,3.96 0,3.97 0.12,3.95" + id="path48" + style="fill:none;stroke:#ff9900;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path + d="m 481.68,406.99 -198.36,0 -33.84,-34.44" + id="path16" + style="fill:none;stroke:#0d6299;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend)" /><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g34" + style="stroke:#ff9900;stroke-width:1.60000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 363,358.51 -79.68,76.92" + id="path36" + style="fill:none;stroke:#ff9900;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g38" + style="stroke:#808080;stroke-width:1.60000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 453.36,435.43 -0.12,-3.96 -0.48,-3.96 -0.96,-3.96 -1.2,-3.84 -1.56,-3.84 -1.92,-3.84 -2.16,-3.84 -2.64,-3.72 -2.88,-3.6 -3.24,-3.6 -3.48,-3.48 -3.84,-3.48 -4.08,-3.24 -4.44,-3.24 -4.68,-3.12 -5.04,-3 -5.16,-2.88 -5.52,-2.88 -5.76,-2.64 -5.88,-2.4 -6.24,-2.4 -6.36,-2.28 -6.48,-2.04 -6.84,-1.92 -6.84,-1.68 -7.08,-1.68 -7.2,-1.44 -7.2,-1.2 -7.44,-1.08 -7.44,-0.96 -7.56,-0.72 -7.68,-0.6 -7.68,-0.36 -7.68,-0.24 -7.68,0 -7.68,0.12 -7.68,0.36 -7.68,0.48" + id="path40" + style="fill:none;stroke:#808080;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g42" + style="stroke:#ff9900;stroke-width:1.60000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 257.88,349.27 25.44,86.16" + id="path44" + style="fill:none;stroke:#ff9900;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g50" + style="stroke:#ff9900;stroke-width:1.60000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 274.92,406.63 4.44,-0.24 4.56,0 4.44,0.12 4.56,0.24 4.32,0.48 4.44,0.72 4.2,0.84 4.08,0.96" + id="path52" + style="fill:none;stroke:#ff9900;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g58"><path + d="m 337.92,327.31 0,48.72 50.16,13.56 0,-48.72 -50.16,-13.56 z" + id="path60" + style="fill:none;stroke:#00b800;stroke-width:0.72000003;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /></g><path + d="m 342.72,461.59 -13.56,50.16 48.72,0 13.56,-50.16 -48.72,0 z" + id="path72" + style="fill:#ffffff;fill-opacity:1;stroke:#00b800;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none" /><g + transform="matrix(0,1,-1,0,718.75,123.67)" + id="g74" + style="fill:none;stroke:#808080;stroke-width:1.60000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"><path + d="m 283.32,265.39 9,0.24 8.88,0.6 8.76,1.2 8.76,1.68 8.64,2.04 8.52,2.52 8.4,3 8.28,3.36 8.04,3.84 7.8,4.2 7.56,4.68 7.32,5.04 7.08,5.4 6.84,5.88 6.36,6.12 6.12,6.36 5.88,6.84 5.4,7.08 5.04,7.32 4.68,7.56 4.2,7.8 3.84,8.04 3.36,8.28 3,8.4 2.52,8.52 2.04,8.64 1.68,8.76 1.2,8.76 0.6,8.88 0.24,9 -0.24,8.88 -0.6,8.88 -1.2,8.76 -1.68,8.76 -2.04,8.64 -2.52,8.52 -3,8.4 -3.36,8.28 -3.84,8.04 -4.2,7.8 -4.68,7.56 -5.04,7.32 -5.4,7.08 -5.88,6.84 -6.12,6.36 -6.36,6.24 -6.84,5.76 -7.08,5.4 -7.32,5.05 -7.56,4.67 -7.8,4.2 -8.04,3.84 -8.28,3.36 -8.4,3 -8.52,2.52 -8.64,2.16 -8.76,1.56 -8.76,1.2 -8.88,0.72 -9,0.12" + id="path76" + style="fill:none;stroke:#808080;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path + d="m 283.32,406.99 0,198.36" + id="path56" + style="fill:none;stroke:#0d6299;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-mid:none;marker-end:url(#Arrow2Mend)" /><path + d="m 360.24,486.67 97.44,0" + id="path68" + style="fill:none;stroke:#00b800;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)" /><path + d="M 411.6,539.71 360.24,486.67 333.12,586.75" + id="path64" + style="fill:none;stroke:#00b800;stroke-width:1.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-mid:none;marker-end:url(#Arrow1Mend)" /><text + x="277.50943" + y="-613.04358" + transform="scale(1,-1)" + id="text3721" + xml:space="preserve" + style="font-size:19.20000076px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="277.50943" + y="-613.04358" + id="tspan3727">Z</tspan></text> + + + + + + +<text + x="488.21332" + y="-399.92703" + transform="scale(1,-1)" + id="text3721-4" + xml:space="preserve" + style="font-size:19.20000076px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="488.21332" + y="-399.92703" + id="tspan4386">Y</tspan></text> + + + + + + +<text + x="238.70586" + y="-350.1015" + transform="scale(1,-1)" + id="text3721-4-9" + xml:space="preserve" + style="font-size:19.20000076px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="238.70586" + y="-350.1015" + id="tspan4386-4">X</tspan></text> + + + + + + +<text + x="341.8902" + y="-575.89331" + transform="scale(1,-1)" + id="text3721-8" + xml:space="preserve" + style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="341.8902" + y="-575.89331" + id="tspan3727-8">North</tspan></text> + + + + + + +<text + x="435.72714" + y="-491.9921" + transform="scale(1,-1)" + id="text3721-8-2" + xml:space="preserve" + style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="435.72714" + y="-491.9921" + id="tspan3727-8-4">East</tspan></text> + + + + + + +<text + x="414.17657" + y="-527.3114" + transform="scale(1,-1)" + id="text3721-8-2-5" + xml:space="preserve" + style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="414.17657" + y="-527.3114" + id="tspan3727-8-4-5">Up</tspan></text> + + + + + + +<text + x="290.61334" + y="-612.47876" + transform="scale(1,-1)" + id="text3721-0" + xml:space="preserve" + style="font-size:19.20000076px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="290.61334" + y="-612.47876" + id="tspan3727-9" + style="font-size:11.19999981px">ecef</tspan></text> + + +<text + x="501.1416" + y="-400.26947" + transform="scale(1,-1)" + id="text3721-0-4" + xml:space="preserve" + style="font-size:19.20000076px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="501.1416" + y="-400.26947" + id="tspan3727-9-8" + style="font-size:11.19999981px">ecef</tspan></text> + + +<text + x="253.27524" + y="-351.07462" + transform="scale(1,-1)" + id="text3721-0-2" + xml:space="preserve" + style="font-size:19.20000076px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="253.27524" + y="-351.07462" + id="tspan3727-9-4" + style="font-size:11.19999981px">ecef</tspan></text> + + +<g + transform="matrix(-1,0,0,1,314.12527,7.5684393)" + id="text4066" + style="font-size:32px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><path + d="m 63.106132,443.21212 11.436928,0.6089 -0.229685,4.3142 c -0.04044,0.75933 -0.107718,1.3373 -0.201842,1.73391 -0.123533,0.55671 -0.326725,1.01791 -0.609576,1.38361 -0.282873,0.36568 -0.667108,0.65164 -1.152709,0.8579 -0.485618,0.20623 -1.011876,0.29427 -1.578773,0.26409 -0.972587,-0.0518 -1.779164,-0.40506 -2.419733,-1.05983 -0.64058,-0.65478 -0.917809,-1.79091 -0.83169,-3.40841 l 0.156169,-2.93334 -4.649665,-0.24755 z m 5.91874,1.83288 -0.157415,2.95675 c -0.05206,0.97778 0.09301,1.6818 0.435204,2.11206 0.342187,0.43025 0.843544,0.66297 1.504074,0.69814 0.478481,0.0255 0.894495,-0.0737 1.248042,-0.29735 0.35353,-0.22372 0.595432,-0.5303 0.725706,-0.91975 0.08117,-0.25125 0.140036,-0.72014 0.176596,-1.40666 l 0.155754,-2.92555 z" + id="path5083" + style="font-size:16px" /><path + d="m 62.494207,453.68612 8.288312,0.37685 -0.05748,1.26431 -1.256515,-0.0571 c 0.57326,0.34932 0.947337,0.66481 1.122232,0.94648 0.174879,0.28167 0.254871,0.5864 0.239976,0.91419 -0.02154,0.47347 -0.194303,0.94788 -0.518304,1.42324 l -1.28134,-0.54313 c 0.218521,-0.33418 0.335592,-0.67296 0.351212,-1.01635 0.01395,-0.30698 -0.06587,-0.58693 -0.239444,-0.83987 -0.173593,-0.25294 -0.421914,-0.43889 -0.744963,-0.55785 -0.492393,-0.1788 -1.035154,-0.28168 -1.628287,-0.30865 l -4.339267,-0.19729 z" + id="path5085" + style="font-size:16px" /><path + d="m 72.064046,459.64751 1.614176,0.0987 -0.08579,1.40363 -1.614175,-0.0987 z m -9.817616,-0.60005 8.281421,0.50616 -0.08579,1.40363 -8.281421,-0.50616 z" + id="path5087" + style="font-size:16px" /><path + d="m 62.072234,462.59865 8.273028,0.62861 -0.0953,1.25419 -1.160717,-0.0882 c 0.385345,0.29044 0.684987,0.66056 0.898927,1.11036 0.213925,0.44979 0.299977,0.94993 0.258157,1.50043 -0.04657,0.61282 -0.211986,1.1056 -0.496246,1.47837 -0.284275,0.37276 -0.661032,0.62228 -1.130274,0.74855 0.916238,0.72776 1.334505,1.61616 1.254803,2.66523 -0.06235,0.82054 -0.337508,1.43427 -0.825462,1.84119 -0.487967,0.4069 -1.204542,0.57444 -2.149729,0.50264 l -5.678943,-0.4315 0.105951,-1.39442 5.211541,0.39598 c 0.560878,0.0426 0.968114,0.0279 1.22171,-0.0443 0.253584,-0.0721 0.464759,-0.22195 0.633524,-0.44939 0.168753,-0.22746 0.265366,-0.50218 0.289837,-0.82416 0.04419,-0.58166 -0.112565,-1.07934 -0.470263,-1.49304 -0.357711,-0.41372 -0.962419,-0.65293 -1.814125,-0.71763 l -4.806458,-0.36521 0.106543,-1.40221 5.375131,0.40842 c 0.623198,0.0473 1.099282,-0.0314 1.428252,-0.23622 0.328958,-0.20484 0.51317,-0.56692 0.552637,-1.08625 0.02998,-0.3947 -0.04616,-0.76743 -0.228439,-1.11818 -0.182289,-0.35076 -0.467653,-0.61664 -0.856092,-0.79762 -0.388451,-0.181 -0.959192,-0.3001 -1.712225,-0.35732 l -4.292315,-0.32614 z" + id="path5089" + style="font-size:16px" /><path + d="m 63.112413,481.79967 -0.313505,1.43024 c -0.824084,-0.30683 -1.44079,-0.7905 -1.850119,-1.45104 -0.409329,-0.66055 -0.569371,-1.47052 -0.480128,-2.42991 0.1124,-1.20833 0.573615,-2.13182 1.383645,-2.77048 0.81003,-0.63866 1.886624,-0.89551 3.229786,-0.77057 1.389828,0.12928 2.435218,0.58745 3.136173,1.37451 0.700941,0.78705 0.998351,1.75103 0.89223,2.89194 -0.102759,1.1046 -0.562677,1.97198 -1.379756,2.60215 -0.817092,0.63014 -1.907586,0.88178 -3.271484,0.75492 -0.08298,-0.008 -0.2072,-0.0219 -0.372665,-0.0425 l 0.573093,-6.1609 c -0.912368,-0.0326 -1.631162,0.15949 -2.156387,0.57618 -0.525227,0.41669 -0.81992,0.96989 -0.884079,1.65963 -0.04776,0.5134 0.04631,0.96416 0.282215,1.35226 0.2359,0.3881 0.63956,0.71596 1.210981,0.98358 z m 2.691313,-4.38677 -0.429095,4.6129 c 0.7007,0.002 1.238289,-0.12544 1.612767,-0.38353 0.580818,-0.39582 0.904275,-0.94897 0.97037,-1.65944 0.05981,-0.64306 -0.105115,-1.20372 -0.494779,-1.68196 -0.389676,-0.47826 -0.942763,-0.77425 -1.659263,-0.88797 z" + id="path5091" + style="font-size:16px" /><path + d="m 59.792635,489.32216 11.383842,1.25786 -0.250542,2.26745 -8.358053,1.80391 c -0.778099,0.16554 -1.359804,0.28467 -1.745119,0.35738 0.399845,0.17517 0.985813,0.44428 1.757906,0.80732 l 7.61938,3.60078 -0.223944,2.02673 -11.383842,-1.25786 0.16045,-1.4521 9.52795,1.0528 -9.162433,-4.36079 0.150154,-1.35892 10.05482,-2.22165 -9.691019,-1.07081 z" + id="path5093" + style="font-size:16px" /><path + d="m 60.144362,508.42535 -0.370341,1.41658 c -0.811182,-0.33947 -1.40809,-0.84738 -1.790728,-1.52372 -0.382638,-0.67637 -0.510223,-1.49208 -0.382757,-2.44715 0.16054,-1.20288 0.658247,-2.10722 1.493124,-2.71304 0.834875,-0.60581 1.920864,-0.81949 3.257969,-0.64104 1.38356,0.18465 2.409829,0.68418 3.078811,1.49859 0.668967,0.8144 0.927664,1.78949 0.77609,2.92526 -0.146767,1.09961 -0.64094,1.94794 -1.48252,2.54499 -0.841592,0.59703 -1.941261,0.80495 -3.29901,0.62374 -0.0826,-0.011 -0.20616,-0.0301 -0.37067,-0.0573 l 0.818543,-6.13312 c -0.91034,-0.0689 -1.636228,0.0943 -2.177666,0.48966 -0.54144,0.39539 -0.857979,0.93639 -0.949617,1.62302 -0.06821,0.51108 0.0078,0.96523 0.228016,1.36245 0.220221,0.3972 0.610473,0.74091 1.170756,1.03113 z m 2.864264,-4.27586 -0.612874,4.5921 c 0.700046,0.0304 1.242309,-0.0759 1.62679,-0.31885 0.596155,-0.37233 0.941432,-0.91212 1.035833,-1.61939 0.08543,-0.64016 -0.05699,-1.20695 -0.427251,-1.70037 -0.370277,-0.49343 -0.911109,-0.81126 -1.622498,-0.95349 z" + id="path5095" + style="font-size:16px" /><path + d="m 57.017756,511.24663 8.203227,1.24307 -0.18962,1.25134 -1.243615,-0.18845 c 0.533511,0.40744 0.872478,0.76039 1.016902,1.05883 0.144409,0.29844 0.192036,0.60987 0.142884,0.9343 -0.07102,0.4686 -0.292535,0.92231 -0.664552,1.36111 l -1.217391,-0.67438 c 0.252328,-0.30944 0.404246,-0.63409 0.455754,-0.97396 0.04603,-0.30383 -0.004,-0.5906 -0.150142,-0.86033 -0.146139,-0.26973 -0.373613,-0.48067 -0.682422,-0.63282 -0.470952,-0.2294 -0.999949,-0.38858 -1.586992,-0.47754 l -4.294722,-0.65079 z" + id="path5097" + style="font-size:16px" /><path + d="m 65.979482,517.99715 1.599793,0.23656 -0.2057,1.39112 -1.599793,-0.23655 z m -9.730141,-1.43875 8.207633,1.21362 -0.2057,1.39113 -8.207633,-1.21363 z" + id="path5099" + style="font-size:16px" /><path + d="m 54.756351,525.36962 1.030675,0.18346 c -0.717999,-0.66212 -0.987553,-1.49569 -0.808664,-2.50072 0.115914,-0.65123 0.401943,-1.21795 0.858087,-1.70016 0.456144,-0.48221 1.032678,-0.81604 1.729604,-1.00146 0.696922,-0.18543 1.463294,-0.20376 2.299118,-0.055 0.815306,0.14512 1.530795,0.41266 2.14647,0.80263 0.615662,0.38996 1.052029,0.8882 1.309104,1.49472 0.25706,0.60651 0.325811,1.24564 0.206255,1.91738 -0.08763,0.49225 -0.2695,0.91219 -0.545619,1.25982 -0.276133,0.34761 -0.602296,0.61225 -0.97849,0.79394 l 4.045786,0.72012 -0.245061,1.3768 -11.275898,-2.00704 z m 4.855545,-3.65092 c -1.046061,-0.1862 -1.867287,-0.10489 -2.463679,0.24391 -0.596395,0.3488 -0.947985,0.82318 -1.054771,1.42313 -0.107699,0.60506 0.04821,1.16316 0.467744,1.67428 0.419528,0.51111 1.136938,0.85702 2.152232,1.03774 1.117843,0.19896 1.976614,0.12963 2.576315,-0.20799 0.599691,-0.33765 0.95567,-0.82182 1.067939,-1.45252 0.109518,-0.61534 -0.05024,-1.17412 -0.479282,-1.67634 -0.429051,-0.50223 -1.18455,-0.84963 -2.266498,-1.04221 z" + id="path5101" + style="font-size:16px" /><path + d="m 63.779537,530.73494 1.587034,0.31084 -0.270294,1.38003 -1.587034,-0.31084 z m -9.652536,-1.89055 8.142171,1.59473 -0.270294,1.38003 -8.142171,-1.59474 z" + id="path5103" + style="font-size:16px" /><path + d="m 53.252402,537.8338 c -0.320697,-0.60367 -0.518405,-1.16033 -0.593125,-1.66998 -0.07472,-0.50966 -0.05287,-1.03411 0.06554,-1.57334 0.19549,-0.89025 0.563212,-1.52671 1.103167,-1.90939 0.539954,-0.38268 1.148225,-0.49974 1.824814,-0.35116 0.396794,0.0871 0.739423,0.25702 1.027889,0.50966 0.28846,0.25265 0.499774,0.54701 0.633944,0.88308 0.134161,0.33608 0.211901,0.69842 0.233217,1.08704 0.01201,0.28525 -0.0087,0.7073 -0.06207,1.26615 -0.111262,1.13803 -0.132853,1.98915 -0.06477,2.55334 0.187103,0.0464 0.306091,0.0752 0.356967,0.0864 0.559579,0.12287 0.982317,0.0797 1.268214,-0.12945 0.387387,-0.28287 0.657045,-0.77023 0.808974,-1.46207 0.141864,-0.64607 0.133401,-1.14785 -0.02539,-1.50532 -0.1588,-0.35749 -0.509421,-0.67311 -1.051865,-0.94686 l 0.478046,-1.30279 c 0.548029,0.24832 0.968104,0.5512 1.260227,0.90863 0.292108,0.35742 0.475461,0.82161 0.550059,1.39257 0.07458,0.57095 0.03536,1.20489 -0.117674,1.90183 -0.15193,0.69184 -0.356761,1.2361 -0.614495,1.63276 -0.257746,0.39666 -0.53232,0.66964 -0.823723,0.81895 -0.291415,0.1493 -0.634279,0.22332 -1.028595,0.22206 -0.244066,-0.006 -0.671324,-0.0754 -1.281775,-0.20948 l -1.831366,-0.40215 c -1.276869,-0.28039 -2.090873,-0.42848 -2.442014,-0.44426 -0.351139,-0.0158 -0.700857,0.0287 -1.049154,0.13356 l 0.315017,-1.43457 c 0.316159,-0.0799 0.669473,-0.0983 1.059943,-0.0552 z m 3.092672,0.55914 c -0.09401,-0.54323 -0.102764,-1.32902 -0.02625,-2.35737 0.0426,-0.58254 0.03872,-1.00199 -0.01163,-1.25834 -0.05036,-0.25635 -0.159502,-0.46962 -0.327427,-0.6398 -0.167929,-0.17019 -0.37144,-0.28153 -0.610533,-0.33403 -0.366275,-0.0804 -0.701942,-0.009 -1.007004,0.2148 -0.305064,0.22362 -0.516242,0.60251 -0.633535,1.13666 -0.116177,0.52906 -0.103775,1.02503 0.03721,1.48792 0.14098,0.46288 0.391187,0.8351 0.750622,1.11667 0.277918,0.21566 0.719561,0.38996 1.324931,0.5229 z" + id="path5105" + style="font-size:16px" /><path + d="m 51.49594,541.02374 8.010721,2.16021 -0.329523,1.22197 -1.139001,-0.30714 c 0.721357,0.82566 0.932194,1.79416 0.632512,2.90551 -0.130189,0.48275 -0.336606,0.90314 -0.619253,1.26118 -0.282659,0.35801 -0.59191,0.59963 -0.927754,0.72485 -0.335855,0.1252 -0.708232,0.17582 -1.117135,0.15187 -0.264999,-0.0175 -0.711791,-0.11103 -1.340375,-0.28053 l -4.925613,-1.32826 0.366137,-1.35775 4.872811,1.31402 c 0.553154,0.14916 0.981002,0.20789 1.283548,0.17621 0.302537,-0.0317 0.571798,-0.16004 0.807785,-0.385 0.235977,-0.22497 0.402786,-0.51848 0.500428,-0.88054 0.155941,-0.57831 0.106983,-1.1269 -0.146876,-1.64579 -0.253869,-0.51889 -0.893728,-0.91665 -1.919578,-1.19328 l -4.37497,-1.17978 z" + id="path5107" + style="font-size:16px" /></g> +<text + x="315.86932" + y="-413.14511" + transform="scale(1,-1)" + id="text3721-8-2-5-1" + xml:space="preserve" + style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="315.86932" + y="-413.14511" + id="tspan3727-8-4-5-7">φ</tspan></text> + +<text + x="292.20465" + y="-379.30618" + transform="scale(1,-1)" + id="text3721-8-2-5-1-1" + xml:space="preserve" + style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:sans-serif"><tspan + x="292.20465" + y="-379.30618" + id="tspan3727-8-4-5-7-1">λ</tspan></text> + +</g></svg>
\ No newline at end of file diff --git a/docs/source/operations/conversions/index.rst b/docs/source/operations/conversions/index.rst index b0bd0d37..b335c2ac 100644 --- a/docs/source/operations/conversions/index.rst +++ b/docs/source/operations/conversions/index.rst @@ -19,4 +19,5 @@ conversions. pop push set + topocentric unitconvert diff --git a/docs/source/operations/conversions/topocentric.rst b/docs/source/operations/conversions/topocentric.rst new file mode 100644 index 00000000..828ef4e0 --- /dev/null +++ b/docs/source/operations/conversions/topocentric.rst @@ -0,0 +1,115 @@ +.. _topocentric: + +================================================================================ +Geocentric to topocentric conversion +================================================================================ + +.. versionadded:: 8.0.0 + +Convert geocentric coordinates to topocentric coordinates (in the forward path). + ++---------------------+--------------------------------------------------------+ +| **Alias** | topocentric | ++---------------------+--------------------------------------------------------+ +| **Domain** | 3D | ++---------------------+--------------------------------------------------------+ +| **Input type** | Geocentric cartesian coordinates | ++---------------------+--------------------------------------------------------+ +| **Output type** | Topocentric cartesian coordinates | ++---------------------+--------------------------------------------------------+ + +This operation converts geocentric coordinate values (X, Y, Z) to topocentric +(E/East, N/North, U/Up) values. This is also sometimes known as the ECEF (Earth +Centered Earth Fixed) to ENU conversion. + +Topocentric coordinates are expressed in a frame whose East and North axis form +a local tangent plane to the Earth's ellipsoidal surface fixed to a specific +location (the topocentric origin), and the Up axis points upwards along the +normal to that plane. + +.. image:: ./images/ECEF_ENU_Longitude_Latitude_relationships.png + :align: center + :scale: 100% + :alt: ENU coordinate frame + +.. + Source : https://en.wikipedia.org/wiki/Local_tangent_plane_coordinates#/media/File:ECEF_ENU_Longitude_Latitude_relationships.svg + Public domain +.. + +The topocentric origin is a required parameter of the conversion, and can be +expressed either as geocentric coordinates (``X_0``, ``Y_0`` and ``Z_0``) or +as geographic coordinates (``lat_0``, ``lon_0``, ``h_0``). + +When conversion between geographic and topocentric coordinates is desired, the +topocentric conversion must be preceded by the :ref:`cart` conversion to +perform the initial geographic to geocentric coordinates conversion. + +The formulas used come from the "Geocentric/topocentric conversions" paragraph +of :cite:`IOGP2018` + +Usage +################################################################################ + +Convert geocentric coordinates to topocentric coordinates, with the topocentric +origin specified in geocentric coordinates:: + + echo 3771793.968 140253.342 5124304.349 2020 | \ + cct -d 3 +proj=topocentric +ellps=WGS84 +X_0=3652755.3058 +Y_0=319574.6799 +Z_0=5201547.3536 + + -189013.869 -128642.040 -4220.171 2020.0000 + +Convert geographic coordinates to topocentric coordinates, with the topocentric +origin specified in geographic coordinates:: + + echo 2.12955 53.80939444 73 2020 | cct -d 3 +proj=pipeline \ + +step +proj=cart +ellps=WGS84 \ + +step +proj=topocentric +ellps=WGS84 +lon_0=5 +lat_0=55 +h_0=200 + + -189013.869 -128642.040 -4220.171 2020.0000 + + +Parameters +################################################################################ + +.. include:: ../options/ellps.rst + +Topocentric origin described as geocentric coordinates +------------------------------------------------------ + +.. note:: + + The below options are mutually exclusive with the ones to express the origin as geographic coordinates. + +.. option:: +X_0=<value> + + Geocentric X value of the topocentric origin (in metre) + +.. option:: +Y_0=<value> + + Geocentric Y value of the topocentric origin (in metre) + +.. option:: +Z_0=<value> + + Geocentric Z value of the topocentric origin (in metre) + +Topocentric origin described as geographic coordinates +------------------------------------------------------ + +.. note:: + + The below options are mutually exclusive with the ones to express the origin as geocentric coordinates. + +.. option:: +lat_0=<value> + + Latitude of topocentric origin (in degree) + +.. option:: +lon_0=<value> + + Longitude of topocentric origin (in degree) + +.. option:: +h_0=<value> + + Ellipsoidal height of topocentric origin (in metre) + + *Defaults to 0.0.* diff --git a/include/proj/internal/coordinateoperation_constants.hpp b/include/proj/internal/coordinateoperation_constants.hpp index 592f6263..0ed3a027 100644 --- a/include/proj/internal/coordinateoperation_constants.hpp +++ b/include/proj/internal/coordinateoperation_constants.hpp @@ -532,6 +532,34 @@ static const ParamMapping *const paramsColombiaUrban[] = { ¶mProjectionPlaneOriginHeight, nullptr}; +static const ParamMapping paramGeocentricXTopocentricOrigin = { + EPSG_NAME_PARAMETER_GEOCENTRIC_X_TOPOCENTRIC_ORIGIN, + EPSG_CODE_PARAMETER_GEOCENTRIC_X_TOPOCENTRIC_ORIGIN, nullptr, + common::UnitOfMeasure::Type::LINEAR, "X_0"}; + +static const ParamMapping paramGeocentricYTopocentricOrigin = { + EPSG_NAME_PARAMETER_GEOCENTRIC_Y_TOPOCENTRIC_ORIGIN, + EPSG_CODE_PARAMETER_GEOCENTRIC_Y_TOPOCENTRIC_ORIGIN, nullptr, + common::UnitOfMeasure::Type::LINEAR, "Y_0"}; + +static const ParamMapping paramGeocentricZTopocentricOrigin = { + EPSG_NAME_PARAMETER_GEOCENTRIC_Z_TOPOCENTRIC_ORIGIN, + EPSG_CODE_PARAMETER_GEOCENTRIC_Z_TOPOCENTRIC_ORIGIN, nullptr, + common::UnitOfMeasure::Type::LINEAR, "Z_0"}; + +static const ParamMapping *const paramsGeocentricTopocentric[] = { + ¶mGeocentricXTopocentricOrigin, ¶mGeocentricYTopocentricOrigin, + ¶mGeocentricZTopocentricOrigin, nullptr}; + +static const ParamMapping paramHeightTopoOriginWithH0 = { + EPSG_NAME_PARAMETER_ELLIPSOIDAL_HEIGHT_TOPOCENTRIC_ORIGIN, + EPSG_CODE_PARAMETER_ELLIPSOIDAL_HEIGHT_TOPOCENTRIC_ORIGIN, nullptr, + common::UnitOfMeasure::Type::LINEAR, "h_0"}; + +static const ParamMapping *const paramsGeographicTopocentric[] = { + ¶mLatTopoOrigin, ¶mLonTopoOrigin, ¶mHeightTopoOriginWithH0, + nullptr}; + static const MethodMapping projectionMethodMappings[] = { {EPSG_NAME_METHOD_TRANSVERSE_MERCATOR, EPSG_CODE_METHOD_TRANSVERSE_MERCATOR, "Transverse_Mercator", "tmerc", nullptr, paramsNatOriginScaleK}, @@ -836,6 +864,14 @@ static const MethodMapping projectionMethodMappings[] = { {EPSG_NAME_METHOD_COLOMBIA_URBAN, EPSG_CODE_METHOD_COLOMBIA_URBAN, nullptr, "col_urban", nullptr, paramsColombiaUrban}, + + {EPSG_NAME_METHOD_GEOCENTRIC_TOPOCENTRIC, + EPSG_CODE_METHOD_GEOCENTRIC_TOPOCENTRIC, nullptr, "topocentric", nullptr, + paramsGeocentricTopocentric}, + + {EPSG_NAME_METHOD_GEOGRAPHIC_TOPOCENTRIC, + EPSG_CODE_METHOD_GEOGRAPHIC_TOPOCENTRIC, nullptr, nullptr, nullptr, + paramsGeographicTopocentric}, }; #define METHOD_NAME_CODE(method) \ @@ -878,6 +914,8 @@ static const struct MethodNameCode { METHOD_NAME_CODE(AXIS_ORDER_REVERSAL_2D), METHOD_NAME_CODE(AXIS_ORDER_REVERSAL_3D), METHOD_NAME_CODE(GEOGRAPHIC_GEOCENTRIC), + METHOD_NAME_CODE(GEOCENTRIC_TOPOCENTRIC), + METHOD_NAME_CODE(GEOGRAPHIC_TOPOCENTRIC), // Transformations METHOD_NAME_CODE(LONGITUDE_ROTATION), METHOD_NAME_CODE(AFFINE_PARAMETRIC_TRANSFORMATION), @@ -943,6 +981,9 @@ static const struct ParamNameCode { PARAM_NAME_CODE(LONGITUDE_OF_ORIGIN), PARAM_NAME_CODE(ELLIPSOID_SCALE_FACTOR), PARAM_NAME_CODE(PROJECTION_PLANE_ORIGIN_HEIGHT), + PARAM_NAME_CODE(GEOCENTRIC_X_TOPOCENTRIC_ORIGIN), + PARAM_NAME_CODE(GEOCENTRIC_Y_TOPOCENTRIC_ORIGIN), + PARAM_NAME_CODE(GEOCENTRIC_Z_TOPOCENTRIC_ORIGIN), // Parameters of transformations PARAM_NAME_CODE(SEMI_MAJOR_AXIS_DIFFERENCE), PARAM_NAME_CODE(FLATTENING_DIFFERENCE), diff --git a/src/Makefile.am b/src/Makefile.am index 1d772207..83ee4adc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -185,6 +185,7 @@ libproj_la_SOURCES = \ conversions/geoc.cpp \ conversions/geocent.cpp \ conversions/noop.cpp \ + conversions/topocentric.cpp \ conversions/set.cpp \ conversions/unitconvert.cpp \ \ diff --git a/src/conversions/topocentric.cpp b/src/conversions/topocentric.cpp new file mode 100644 index 00000000..214f8221 --- /dev/null +++ b/src/conversions/topocentric.cpp @@ -0,0 +1,165 @@ +/****************************************************************************** + * Project: PROJ + * Purpose: Convert between geocentric coordinates and topocentric (ENU) coordinates + * + ****************************************************************************** + * Copyright (c) 2020, Even Rouault <even.rouault at spatialys.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#define PJ_LIB__ + +#include "proj_internal.h" +#include <errno.h> +#include <math.h> + +PROJ_HEAD(topocentric, "Geocentric/Topocentric conversion"); + +// Notations and formulas taken from IOGP Publication 373-7-2 - +// Geomatics Guidance Note number 7, part 2 - October 2020 + +namespace { // anonymous namespace +struct pj_opaque { + double X0; + double Y0; + double Z0; + double sinphi0; + double cosphi0; + double sinlam0; + double coslam0; +}; +} // anonymous namespace + +// Convert from geocentric to topocentric +static PJ_COORD topocentric_fwd(PJ_COORD in, PJ * P) +{ + struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque); + PJ_COORD out; + const double dX = in.xyz.x - Q->X0; + const double dY = in.xyz.y - Q->Y0; + const double dZ = in.xyz.z - Q->Z0; + out.xyz.x = -dX * Q->sinlam0 + dY * Q->coslam0; + out.xyz.y = -dX * Q->sinphi0 * Q->coslam0 - dY * Q->sinphi0 * Q->sinlam0 + dZ * Q->cosphi0; + out.xyz.z = dX * Q->cosphi0 * Q->coslam0 + dY * Q->cosphi0 * Q->sinlam0 + dZ * Q->sinphi0; + return out; +} + +// Convert from topocentric to geocentric +static PJ_COORD topocentric_inv(PJ_COORD in, PJ * P) +{ + struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque); + PJ_COORD out; + out.xyz.x = Q->X0 - in.xyz.x * Q->sinlam0 - in.xyz.y * Q->sinphi0 * Q->coslam0 + in.xyz.z * Q->cosphi0 * Q->coslam0; + out.xyz.y = Q->Y0 + in.xyz.x * Q->coslam0 - in.xyz.y * Q->sinphi0 * Q->sinlam0 + in.xyz.z * Q->cosphi0 * Q->sinlam0; + out.xyz.z = Q->Z0 + in.xyz.y * Q->cosphi0 + in.xyz.z * Q->sinphi0; + return out; +} + + +/*********************************************************************/ +PJ *CONVERSION(topocentric,1) { +/*********************************************************************/ + struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque))); + if (nullptr==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = static_cast<void *>(Q); + + // The topocentric origin can be specified either in geocentric coordinates + // (X_0,Y_0,Z_0) or as geographic coordinates (lon_0,lat_0,h_0) + // Checks: + // - X_0 or lon_0 must be specified + // - If X_0 is specified, the Y_0 and Z_0 must also be + // - If lon_0 is specified, then lat_0 must also be + // - If any of X_0, Y_0, Z_0 is specified, then any of lon_0,lat_0,h_0 must + // not be, and vice versa. + const auto hasX0 = pj_param_exists(P->params, "X_0"); + const auto hasY0 = pj_param_exists(P->params, "Y_0"); + const auto hasZ0 = pj_param_exists(P->params, "Z_0"); + const auto hasLon0 = pj_param_exists(P->params, "lon_0"); + const auto hasLat0 = pj_param_exists(P->params, "lat_0"); + const auto hash0 = pj_param_exists(P->params, "h_0"); + if( !hasX0 && !hasLon0 ) + { + return pj_default_destructor(P, PJD_ERR_MISSING_ARGS); + } + if ( (hasX0 || hasY0 || hasZ0) && + (hasLon0 || hasLat0 || hash0) ) + { + return pj_default_destructor(P, PJD_ERR_MUTUALLY_EXCLUSIVE_ARGS); + } + if( hasX0 && (!hasY0 || !hasZ0) ) + { + return pj_default_destructor(P, PJD_ERR_MISSING_ARGS); + } + if( hasLon0 && !hasLat0 ) // allow missing h_0 + { + return pj_default_destructor(P, PJD_ERR_MISSING_ARGS); + } + + // Pass a dummy ellipsoid definition that will be overridden just afterwards + PJ* cart = proj_create(P->ctx, "+proj=cart +a=1"); + if (cart == nullptr) + return pj_default_destructor(P, ENOMEM); + /* inherit ellipsoid definition from P to cart */ + pj_inherit_ellipsoid_def (P, cart); + + if( hasX0 ) + { + Q->X0 = pj_param(P->ctx, P->params, "dX_0").f; + Q->Y0 = pj_param(P->ctx, P->params, "dY_0").f; + Q->Z0 = pj_param(P->ctx, P->params, "dZ_0").f; + + // Compute lam0, phi0 from X0,Y0,Z0 + PJ_XYZ xyz; + xyz.x = Q->X0; + xyz.y = Q->Y0; + xyz.z = Q->Z0; + const auto lpz = pj_inv3d(xyz, cart); + Q->sinphi0 = sin(lpz.phi); + Q->cosphi0 = cos(lpz.phi); + Q->sinlam0 = sin(lpz.lam); + Q->coslam0 = cos(lpz.lam); + } + else + { + // Compute X0,Y0,Z0 from lam0, phi0, h0 + PJ_LPZ lpz; + lpz.lam = P->lam0; + lpz.phi = P->phi0; + lpz.z = pj_param(P->ctx, P->params, "dh_0").f; + const auto xyz = pj_fwd3d(lpz, cart); + Q->X0 = xyz.x; + Q->Y0 = xyz.y; + Q->Z0 = xyz.z; + + Q->sinphi0 = sin(P->phi0); + Q->cosphi0 = cos(P->phi0); + Q->sinlam0 = sin(P->lam0); + Q->coslam0 = cos(P->lam0); + } + + proj_destroy(cart); + + P->fwd4d = topocentric_fwd; + P->inv4d = topocentric_inv; + P->left = PJ_IO_UNITS_CARTESIAN; + P->right = PJ_IO_UNITS_CARTESIAN; + return P; +} diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 5ef32d31..83b626b3 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -6034,28 +6034,39 @@ void Conversion::_exportToPROJString( !isHeightDepthReversal; bool applyTargetCRSModifiers = applySourceCRSModifiers; + if (formatter->getCRSExport()) { + if (methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TOPOCENTRIC || + methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC_TOPOCENTRIC) { + throw io::FormattingException("Transformation cannot be exported " + "as a PROJ.4 string (but can be part " + "of a PROJ pipeline)"); + } + } + auto l_sourceCRS = sourceCRS(); + crs::GeographicCRSPtr srcGeogCRS; if (!formatter->getCRSExport() && l_sourceCRS && applySourceCRSModifiers) { - crs::CRS *horiz = l_sourceCRS.get(); - const auto compound = dynamic_cast<const crs::CompoundCRS *>(horiz); + crs::CRSPtr horiz = l_sourceCRS; + const auto compound = + dynamic_cast<const crs::CompoundCRS *>(l_sourceCRS.get()); if (compound) { const auto &components = compound->componentReferenceSystems(); if (!components.empty()) { - horiz = components.front().get(); + horiz = components.front().as_nullable(); } } - auto geogCRS = dynamic_cast<const crs::GeographicCRS *>(horiz); - if (geogCRS) { + srcGeogCRS = std::dynamic_pointer_cast<crs::GeographicCRS>(horiz); + if (srcGeogCRS) { formatter->setOmitProjLongLatIfPossible(true); formatter->startInversion(); - geogCRS->_exportToPROJString(formatter); + srcGeogCRS->_exportToPROJString(formatter); formatter->stopInversion(); formatter->setOmitProjLongLatIfPossible(false); } - auto projCRS = dynamic_cast<const crs::ProjectedCRS *>(horiz); + auto projCRS = dynamic_cast<const crs::ProjectedCRS *>(horiz.get()); if (projCRS) { formatter->startInversion(); formatter->pushOmitZUnitConversion(); @@ -6325,6 +6336,30 @@ void Conversion::_exportToPROJString( } bConversionDone = true; bEllipsoidParametersDone = true; + } else if (methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC_TOPOCENTRIC) { + if (!srcGeogCRS) { + throw io::FormattingException( + "Export of Geographic/Topocentric conversion to a PROJ string " + "requires an input geographic CRS"); + } + + formatter->addStep("cart"); + srcGeogCRS->ellipsoid()->_exportToPROJString(formatter); + + formatter->addStep("topocentric"); + const auto latOrigin = parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_TOPOGRAPHIC_ORIGIN, + common::UnitOfMeasure::DEGREE); + const auto lonOrigin = parameterValueNumeric( + EPSG_CODE_PARAMETER_LONGITUDE_TOPOGRAPHIC_ORIGIN, + common::UnitOfMeasure::DEGREE); + const auto heightOrigin = parameterValueNumeric( + EPSG_CODE_PARAMETER_ELLIPSOIDAL_HEIGHT_TOPOCENTRIC_ORIGIN, + common::UnitOfMeasure::METRE); + formatter->addParam("lat_0", latOrigin); + formatter->addParam("lon_0", lonOrigin); + formatter->addParam("h_0", heightOrigin); + bConversionDone = true; } auto l_targetCRS = targetCRS(); @@ -6449,7 +6484,9 @@ void Conversion::_exportToPROJString( } if (!bEllipsoidParametersDone) { - auto targetGeogCRS = horiz->extractGeographicCRS(); + auto targetGeodCRS = horiz->extractGeodeticCRS(); + auto targetGeogCRS = + std::dynamic_pointer_cast<crs::GeographicCRS>(targetGeodCRS); if (targetGeogCRS) { if (formatter->getCRSExport()) { targetGeogCRS->addDatumInfoToPROJString(formatter); @@ -6458,6 +6495,8 @@ void Conversion::_exportToPROJString( targetGeogCRS->primeMeridian()->_exportToPROJString( formatter); } + } else if (targetGeodCRS) { + targetGeodCRS->ellipsoid()->_exportToPROJString(formatter); } } diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index 7ca7f3a4..3a4880c6 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -173,6 +173,7 @@ set(SRC_LIBPROJ_CONVERSIONS conversions/geoc.cpp conversions/geocent.cpp conversions/noop.cpp + conversions/topocentric.cpp conversions/set.cpp conversions/unitconvert.cpp ) diff --git a/src/pj_list.h b/src/pj_list.h index bcdc189e..d00e780f 100644 --- a/src/pj_list.h +++ b/src/pj_list.h @@ -155,6 +155,7 @@ PROJ_HEAD(tinshift, "Triangulation based transformation") PROJ_HEAD(tissot, "Tissot Conic") PROJ_HEAD(tmerc, "Transverse Mercator") PROJ_HEAD(tobmerc, "Tobler-Mercator") +PROJ_HEAD(topocentric, "Geocentric/Topocentric conversion") PROJ_HEAD(tpeqd, "Two Point Equidistant") PROJ_HEAD(tpers, "Tilted perspective") PROJ_HEAD(unitconvert, "Unit conversion") diff --git a/src/proj_constants.h b/src/proj_constants.h index a3da2c10..ce3b2157 100644 --- a/src/proj_constants.h +++ b/src/proj_constants.h @@ -651,4 +651,24 @@ #define EPSG_CODE_METHOD_HEIGHT_DEPTH_REVERSAL 1068 #define EPSG_NAME_METHOD_HEIGHT_DEPTH_REVERSAL "Height Depth Reversal" +/* ------------------------------------------------------------------------ */ + +#define EPSG_NAME_METHOD_GEOCENTRIC_TOPOCENTRIC "Geocentric/topocentric conversions" +#define EPSG_CODE_METHOD_GEOCENTRIC_TOPOCENTRIC 9836 + +#define EPSG_NAME_PARAMETER_GEOCENTRIC_X_TOPOCENTRIC_ORIGIN "Geocentric X of topocentric origin" +#define EPSG_CODE_PARAMETER_GEOCENTRIC_X_TOPOCENTRIC_ORIGIN 8837 + +#define EPSG_NAME_PARAMETER_GEOCENTRIC_Y_TOPOCENTRIC_ORIGIN "Geocentric Y of topocentric origin" +#define EPSG_CODE_PARAMETER_GEOCENTRIC_Y_TOPOCENTRIC_ORIGIN 8838 + +#define EPSG_NAME_PARAMETER_GEOCENTRIC_Z_TOPOCENTRIC_ORIGIN "Geocentric Z of topocentric origin" +#define EPSG_CODE_PARAMETER_GEOCENTRIC_Z_TOPOCENTRIC_ORIGIN 8839 + +/* ------------------------------------------------------------------------ */ + +#define EPSG_NAME_METHOD_GEOGRAPHIC_TOPOCENTRIC "Geographic/topocentric conversions" +#define EPSG_CODE_METHOD_GEOGRAPHIC_TOPOCENTRIC 9837 + + #endif /* PROJ_CONSTANTS_INCLUDED */ diff --git a/test/gie/builtins.gie b/test/gie/builtins.gie index e870f1d4..813b67bb 100644 --- a/test/gie/builtins.gie +++ b/test/gie/builtins.gie @@ -6873,4 +6873,46 @@ accept -74.25 4.8 expect 80859.033 122543.174 roundtrip 1 +=============================================================================== +# Geocentric/topocentric conversion +=============================================================================== + +# Test parameter and point from IOGP Publication 373-7-2 - Geomatics Guidance Note number 7, part 2 - October 2020 +operation +proj=topocentric +ellps=WGS84 +X_0=3652755.3058 +Y_0=319574.6799 +Z_0=5201547.3536 +tolerance 1 mm +accept 3771793.968 140253.342 5124304.349 +expect -189013.869 -128642.040 -4220.171 +roundtrip 1 + +=============================================================================== +# Geographic/topocentric conversion +=============================================================================== + +# Test parameter and point from IOGP Publication 373-7-2 - Geomatics Guidance Note number 7, part 2 - October 2020 +operation +proj=pipeline +step +proj=cart +ellps=WGS84 +step +proj=topocentric +ellps=WGS84 +lon_0=5 +lat_0=55 +h_0=200 +tolerance 1 mm +accept 2.12955 53.80939444444444 73 +expect -189013.869 -128642.040 -4220.171 +roundtrip 1 + +=============================================================================== +# Error cases of topocentric +=============================================================================== + +# missing X_0,Y_0,Z_0 or lon_0,lat_0 +operation +proj=topocentric +ellps=WGS84 +expect failure errno missing_args + +# missing Z_0 +operation +proj=topocentric +ellps=WGS84 +X_0=0 +Y_0=0 +expect failure errno missing_args + +# missing lat_0 +operation +proj=topocentric +ellps=WGS84 +lon_0=0 +expect failure errno missing_args + +# X_0 and lon_0 are mutually exclusive +operation +proj=topocentric +ellps=WGS84 +X_0=0 +lon_0=0 +expect failure errno mutually_exclusive_args + </gie-strict> diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 9bf883d9..d8e7347c 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -10098,6 +10098,128 @@ TEST(operation, derivedGeographicCRS_with_to_wgs84_to_geographicCRS) { // --------------------------------------------------------------------------- +TEST(operation, geographic_topocentric) { + auto wkt = + "PROJCRS[\"EPSG topocentric example A\",\n" + " BASEGEOGCRS[\"WGS 84\",\n" + " ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n" + " MEMBER[\"World Geodetic System 1984 (Transit)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G730)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G873)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1150)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1674)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1762)\"],\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ENSEMBLEACCURACY[2.0]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4979]],\n" + " CONVERSION[\"EPSG topocentric example A\",\n" + " METHOD[\"Geographic/topocentric conversions\",\n" + " ID[\"EPSG\",9837]],\n" + " PARAMETER[\"Latitude of topocentric origin\",55,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8834]],\n" + " PARAMETER[\"Longitude of topocentric origin\",5,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8835]],\n" + " PARAMETER[\"Ellipsoidal height of topocentric origin\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8836]]],\n" + " CS[Cartesian,3],\n" + " AXIS[\"topocentric East (U)\",east,\n" + " ORDER[1],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"topocentric North (V)\",north,\n" + " ORDER[2],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"topocentric height (W)\",up,\n" + " ORDER[3],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " USAGE[\n" + " SCOPE[\"Example only (fictitious).\"],\n" + " AREA[\"Description of the extent of the CRS.\"],\n" + " BBOX[-90,-180,90,180]],\n" + " ID[\"EPSG\",5819]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto dst = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(dst != nullptr); + auto op = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4979, NN_CHECK_ASSERT(dst)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=cart +ellps=WGS84 " + "+step +proj=topocentric +lat_0=55 +lon_0=5 +h_0=0 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geocentric_topocentric) { + auto wkt = + "PROJCRS[\"EPSG topocentric example B\",\n" + " BASEGEODCRS[\"WGS 84\",\n" + " ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n" + " MEMBER[\"World Geodetic System 1984 (Transit)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G730)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G873)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1150)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1674)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1762)\"],\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ENSEMBLEACCURACY[2.0]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4978]],\n" + " CONVERSION[\"EPSG topocentric example B\",\n" + " METHOD[\"Geocentric/topocentric conversions\",\n" + " ID[\"EPSG\",9836]],\n" + " PARAMETER[\"Geocentric X of topocentric origin\",3771793.97,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8837]],\n" + " PARAMETER[\"Geocentric Y of topocentric origin\",140253.34,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8838]],\n" + " PARAMETER[\"Geocentric Z of topocentric origin\",5124304.35,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8839]]],\n" + " CS[Cartesian,3],\n" + " AXIS[\"topocentric East (U)\",east,\n" + " ORDER[1],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"topocentric North (V)\",north,\n" + " ORDER[2],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"topocentric height (W)\",up,\n" + " ORDER[3],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " USAGE[\n" + " SCOPE[\"Example only (fictitious).\"],\n" + " AREA[\"Description of the extent of the CRS.\"],\n" + " BBOX[-90,-180,90,180]],\n" + " ID[\"EPSG\",5820]]"; + auto dbContext = DatabaseContext::create(); + // Need a database so that EPSG:4978 is resolved + auto obj = WKTParser().attachDatabaseContext(dbContext).createFromWKT(wkt); + auto dst = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(dst != nullptr); + auto f(NS_PROJ::io::WKTFormatter::create( + NS_PROJ::io::WKTFormatter::Convention::WKT2_2019)); + std::cerr << GeodeticCRS::EPSG_4978->exportToWKT(f.get()) << std::endl; + auto op = CoordinateOperationFactory::create()->createOperation( + GeodeticCRS::EPSG_4978, NN_CHECK_ASSERT(dst)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=topocentric +X_0=3771793.97 +Y_0=140253.34 " + "+Z_0=5124304.35 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + TEST(operation, mercator_variant_A_to_variant_B) { auto projCRS = ProjectedCRS::create( PropertyMap(), GeographicCRS::EPSG_4326, |
