"Fossies" - the Fresh Open Source Software Archive

Member "proj-6.2.1/test/unit/test_io.cpp" (28 Oct 2019, 479918 Bytes) of package /linux/privat/proj-6.2.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_io.cpp": 6.2.0_vs_6.2.1.

    1 /******************************************************************************
    2  *
    3  * Project:  PROJ
    4  * Purpose:  Test ISO19111:2018 implementation
    5  * Author:   Even Rouault <even dot rouault at spatialys dot com>
    6  *
    7  ******************************************************************************
    8  * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
    9  *
   10  * Permission is hereby granted, free of charge, to any person obtaining a
   11  * copy of this software and associated documentation files (the "Software"),
   12  * to deal in the Software without restriction, including without limitation
   13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   14  * and/or sell copies of the Software, and to permit persons to whom the
   15  * Software is furnished to do so, subject to the following conditions:
   16  *
   17  * The above copyright notice and this permission notice shall be included
   18  * in all copies or substantial portions of the Software.
   19  *
   20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   26  * DEALINGS IN THE SOFTWARE.
   27  ****************************************************************************/
   28 
   29 #include "gtest_include.h"
   30 
   31 // to be able to use internal::toString
   32 #define FROM_PROJ_CPP
   33 
   34 #include "proj/common.hpp"
   35 #include "proj/coordinateoperation.hpp"
   36 #include "proj/coordinatesystem.hpp"
   37 #include "proj/crs.hpp"
   38 #include "proj/datum.hpp"
   39 #include "proj/io.hpp"
   40 #include "proj/metadata.hpp"
   41 #include "proj/util.hpp"
   42 
   43 #include "proj/internal/internal.hpp"
   44 
   45 #include "proj_constants.h"
   46 
   47 #include <string>
   48 
   49 using namespace osgeo::proj::common;
   50 using namespace osgeo::proj::crs;
   51 using namespace osgeo::proj::cs;
   52 using namespace osgeo::proj::datum;
   53 using namespace osgeo::proj::internal;
   54 using namespace osgeo::proj::io;
   55 using namespace osgeo::proj::metadata;
   56 using namespace osgeo::proj::operation;
   57 using namespace osgeo::proj::util;
   58 
   59 // ---------------------------------------------------------------------------
   60 
   61 TEST(io, wkt_parsing) {
   62     {
   63         auto n = WKTNode::createFrom("MYNODE[]");
   64         EXPECT_EQ(n->value(), "MYNODE");
   65         EXPECT_TRUE(n->children().empty());
   66         EXPECT_EQ(n->toString(), "MYNODE");
   67     }
   68     {
   69         auto n = WKTNode::createFrom("  MYNODE  [  ]  ");
   70         EXPECT_EQ(n->value(), "MYNODE");
   71         EXPECT_TRUE(n->children().empty());
   72     }
   73     EXPECT_THROW(WKTNode::createFrom(""), ParsingException);
   74     EXPECT_THROW(WKTNode::createFrom("x"), ParsingException);
   75     EXPECT_THROW(WKTNode::createFrom("x,"), ParsingException);
   76     EXPECT_THROW(WKTNode::createFrom("x["), ParsingException);
   77     EXPECT_THROW(WKTNode::createFrom("["), ParsingException);
   78 
   79     {
   80         auto n = WKTNode::createFrom("MYNODE[\"x\"]");
   81         EXPECT_EQ(n->value(), "MYNODE");
   82         ASSERT_EQ(n->children().size(), 1U);
   83         EXPECT_EQ(n->children()[0]->value(), "\"x\"");
   84         EXPECT_EQ(n->toString(), "MYNODE[\"x\"]");
   85     }
   86 
   87     EXPECT_THROW(WKTNode::createFrom("MYNODE[\"x\""), ParsingException);
   88 
   89     {
   90         auto n = WKTNode::createFrom("MYNODE[  \"x\"   ]");
   91         EXPECT_EQ(n->value(), "MYNODE");
   92         ASSERT_EQ(n->children().size(), 1U);
   93         EXPECT_EQ(n->children()[0]->value(), "\"x\"");
   94     }
   95 
   96     {
   97         auto n = WKTNode::createFrom("MYNODE[\"x[\",1]");
   98         EXPECT_EQ(n->value(), "MYNODE");
   99         ASSERT_EQ(n->children().size(), 2U);
  100         EXPECT_EQ(n->children()[0]->value(), "\"x[\"");
  101         EXPECT_EQ(n->children()[1]->value(), "1");
  102         EXPECT_EQ(n->toString(), "MYNODE[\"x[\",1]");
  103     }
  104 
  105     EXPECT_THROW(WKTNode::createFrom("MYNODE[\"x\","), ParsingException);
  106 
  107     {
  108         auto n = WKTNode::createFrom("A[B[y]]");
  109         EXPECT_EQ(n->value(), "A");
  110         ASSERT_EQ(n->children().size(), 1U);
  111         EXPECT_EQ(n->children()[0]->value(), "B");
  112         ASSERT_EQ(n->children()[0]->children().size(), 1U);
  113         EXPECT_EQ(n->children()[0]->children()[0]->value(), "y");
  114         EXPECT_EQ(n->toString(), "A[B[y]]");
  115     }
  116 
  117     EXPECT_THROW(WKTNode::createFrom("A[B["), ParsingException);
  118 
  119     std::string str;
  120     for (int i = 0; i < 17; i++) {
  121         str = "A[" + str + "]";
  122     }
  123     EXPECT_THROW(WKTNode::createFrom(str), ParsingException);
  124 
  125     {
  126         auto wkt = "A[\"a\",B[\"b\",C[\"c\"]],D[\"d\"]]";
  127         EXPECT_EQ(WKTNode::createFrom(wkt)->toString(), wkt);
  128     }
  129 }
  130 
  131 // ---------------------------------------------------------------------------
  132 
  133 TEST(io, wkt_parsing_with_parenthesis) {
  134 
  135     auto n = WKTNode::createFrom("A(\"x\",B(\"y\"))");
  136     EXPECT_EQ(n->toString(), "A[\"x\",B[\"y\"]]");
  137 }
  138 
  139 // ---------------------------------------------------------------------------
  140 
  141 TEST(io, wkt_parsing_with_double_quotes_inside) {
  142 
  143     auto n = WKTNode::createFrom("A[\"xy\"\"z\"]");
  144     EXPECT_EQ(n->children()[0]->value(), "\"xy\"z\"");
  145     EXPECT_EQ(n->toString(), "A[\"xy\"\"z\"]");
  146 
  147     EXPECT_THROW(WKTNode::createFrom("A[\"x\""), ParsingException);
  148 }
  149 
  150 // ---------------------------------------------------------------------------
  151 
  152 TEST(io, wkt_parsing_with_printed_quotes) {
  153     static const std::string startPrintedQuote("\xE2\x80\x9C");
  154     static const std::string endPrintedQuote("\xE2\x80\x9D");
  155 
  156     auto n = WKTNode::createFrom("A[" + startPrintedQuote + "x" +
  157                                  endPrintedQuote + "]");
  158     EXPECT_EQ(n->children()[0]->value(), "\"x\"");
  159     EXPECT_EQ(n->toString(), "A[\"x\"]");
  160 }
  161 
  162 // ---------------------------------------------------------------------------
  163 
  164 TEST(wkt_parse, sphere) {
  165     auto obj = WKTParser().createFromWKT(
  166         "ELLIPSOID[\"Sphere\",6378137,0,LENGTHUNIT[\"metre\",1]]");
  167     auto ellipsoid = nn_dynamic_pointer_cast<Ellipsoid>(obj);
  168     ASSERT_TRUE(ellipsoid != nullptr);
  169     EXPECT_TRUE(ellipsoid->isSphere());
  170 }
  171 
  172 // ---------------------------------------------------------------------------
  173 
  174 TEST(wkt_parse, datum_with_ANCHOR) {
  175     auto obj = WKTParser().createFromWKT(
  176         "DATUM[\"WGS_1984 with anchor\",\n"
  177         "    ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
  178         "        LENGTHUNIT[\"metre\",1,\n"
  179         "            ID[\"EPSG\",9001]],\n"
  180         "        ID[\"EPSG\",7030]],\n"
  181         "    ANCHOR[\"My anchor\"]]");
  182     auto datum = nn_dynamic_pointer_cast<GeodeticReferenceFrame>(obj);
  183     ASSERT_TRUE(datum != nullptr);
  184     EXPECT_EQ(datum->ellipsoid()->celestialBody(), "Earth");
  185     EXPECT_EQ(datum->primeMeridian()->nameStr(), "Greenwich");
  186     auto anchor = datum->anchorDefinition();
  187     EXPECT_TRUE(anchor.has_value());
  188     EXPECT_EQ(*anchor, "My anchor");
  189 }
  190 
  191 // ---------------------------------------------------------------------------
  192 
  193 TEST(wkt_parse, datum_no_pm_not_earth) {
  194     auto obj = WKTParser().createFromWKT("DATUM[\"unnamed\",\n"
  195                                          "    ELLIPSOID[\"unnamed\",1,0,\n"
  196                                          "        LENGTHUNIT[\"metre\",1]]]");
  197     auto datum = nn_dynamic_pointer_cast<GeodeticReferenceFrame>(obj);
  198     ASSERT_TRUE(datum != nullptr);
  199     EXPECT_EQ(datum->ellipsoid()->celestialBody(), "Non-Earth body");
  200     EXPECT_EQ(datum->primeMeridian()->nameStr(), "Reference meridian");
  201 }
  202 
  203 // ---------------------------------------------------------------------------
  204 
  205 TEST(wkt_parse, dynamic_geodetic_reference_frame) {
  206     auto obj = WKTParser().createFromWKT(
  207         "GEOGCRS[\"WGS 84 (G1762)\","
  208         "DYNAMIC[FRAMEEPOCH[2005.0]],"
  209         "TRF[\"World Geodetic System 1984 (G1762)\","
  210         "    ELLIPSOID[\"WGS 84\",6378137,298.257223563],"
  211         "    ANCHOR[\"My anchor\"]],"
  212         "CS[ellipsoidal,3],"
  213         "    AXIS[\"(lat)\",north,ANGLEUNIT[\"degree\",0.0174532925199433]],"
  214         "    AXIS[\"(lon)\",east,ANGLEUNIT[\"degree\",0.0174532925199433]],"
  215         "    AXIS[\"ellipsoidal height (h)\",up,LENGTHUNIT[\"metre\",1.0]]"
  216         "]");
  217     auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
  218     ASSERT_TRUE(crs != nullptr);
  219     auto dgrf =
  220         std::dynamic_pointer_cast<DynamicGeodeticReferenceFrame>(crs->datum());
  221     ASSERT_TRUE(dgrf != nullptr);
  222     auto anchor = dgrf->anchorDefinition();
  223     EXPECT_TRUE(anchor.has_value());
  224     EXPECT_EQ(*anchor, "My anchor");
  225     EXPECT_TRUE(dgrf->frameReferenceEpoch() ==
  226                 Measure(2005.0, UnitOfMeasure::YEAR));
  227     auto model = dgrf->deformationModelName();
  228     EXPECT_TRUE(!model.has_value());
  229 }
  230 
  231 // ---------------------------------------------------------------------------
  232 
  233 TEST(wkt_parse, dynamic_geodetic_reference_frame_with_model) {
  234     auto obj = WKTParser().createFromWKT(
  235         "GEOGCRS[\"WGS 84 (G1762)\","
  236         "DYNAMIC[FRAMEEPOCH[2005.0],MODEL[\"my_model\"]],"
  237         "TRF[\"World Geodetic System 1984 (G1762)\","
  238         "    ELLIPSOID[\"WGS 84\",6378137,298.257223563],"
  239         "    ANCHOR[\"My anchor\"]],"
  240         "CS[ellipsoidal,3],"
  241         "    AXIS[\"(lat)\",north,ANGLEUNIT[\"degree\",0.0174532925199433]],"
  242         "    AXIS[\"(lon)\",east,ANGLEUNIT[\"degree\",0.0174532925199433]],"
  243         "    AXIS[\"ellipsoidal height (h)\",up,LENGTHUNIT[\"metre\",1.0]]"
  244         "]");
  245     auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
  246     ASSERT_TRUE(crs != nullptr);
  247     auto dgrf =
  248         std::dynamic_pointer_cast<DynamicGeodeticReferenceFrame>(crs->datum());
  249     ASSERT_TRUE(dgrf != nullptr);
  250     auto anchor = dgrf->anchorDefinition();
  251     EXPECT_TRUE(anchor.has_value());
  252     EXPECT_EQ(*anchor, "My anchor");
  253     EXPECT_TRUE(dgrf->frameReferenceEpoch() ==
  254                 Measure(2005.0, UnitOfMeasure::YEAR));
  255     auto model = dgrf->deformationModelName();
  256     EXPECT_TRUE(model.has_value());
  257     EXPECT_EQ(*model, "my_model");
  258 }
  259 
  260 // ---------------------------------------------------------------------------
  261 
  262 TEST(wkt_parse, dynamic_geodetic_reference_frame_with_velocitygrid) {
  263     auto obj = WKTParser().createFromWKT(
  264         "GEOGCRS[\"WGS 84 (G1762)\","
  265         "DYNAMIC[FRAMEEPOCH[2005.0],VELOCITYGRID[\"my_model\"]],"
  266         "TRF[\"World Geodetic System 1984 (G1762)\","
  267         "    ELLIPSOID[\"WGS 84\",6378137,298.257223563],"
  268         "    ANCHOR[\"My anchor\"]],"
  269         "CS[ellipsoidal,3],"
  270         "    AXIS[\"(lat)\",north,ANGLEUNIT[\"degree\",0.0174532925199433]],"
  271         "    AXIS[\"(lon)\",east,ANGLEUNIT[\"degree\",0.0174532925199433]],"
  272         "    AXIS[\"ellipsoidal height (h)\",up,LENGTHUNIT[\"metre\",1.0]]"
  273         "]");
  274     auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
  275     ASSERT_TRUE(crs != nullptr);
  276     auto dgrf =
  277         std::dynamic_pointer_cast<DynamicGeodeticReferenceFrame>(crs->datum());
  278     ASSERT_TRUE(dgrf != nullptr);
  279     auto anchor = dgrf->anchorDefinition();
  280     EXPECT_TRUE(anchor.has_value());
  281     EXPECT_EQ(*anchor, "My anchor");
  282     EXPECT_TRUE(dgrf->frameReferenceEpoch() ==
  283                 Measure(2005.0, UnitOfMeasure::YEAR));
  284     auto model = dgrf->deformationModelName();
  285     EXPECT_TRUE(model.has_value());
  286     EXPECT_EQ(*model, "my_model");
  287 }
  288 
  289 // ---------------------------------------------------------------------------
  290 
  291 TEST(wkt_parse, geogcrs_with_ensemble) {
  292     auto obj = WKTParser().createFromWKT(
  293         "GEOGCRS[\"WGS 84\","
  294         "ENSEMBLE[\"WGS 84 ensemble\","
  295         "    MEMBER[\"WGS 84 (TRANSIT)\"],"
  296         "    MEMBER[\"WGS 84 (G730)\"],"
  297         "    MEMBER[\"WGS 84 (G834)\"],"
  298         "    MEMBER[\"WGS 84 (G1150)\"],"
  299         "    MEMBER[\"WGS 84 (G1674)\"],"
  300         "    MEMBER[\"WGS 84 (G1762)\"],"
  301         "    ELLIPSOID[\"WGS "
  302         "84\",6378137,298.2572236,LENGTHUNIT[\"metre\",1.0]],"
  303         "    ENSEMBLEACCURACY[2]"
  304         "],"
  305         "CS[ellipsoidal,3],"
  306         "    AXIS[\"(lat)\",north,ANGLEUNIT[\"degree\",0.0174532925199433]],"
  307         "    AXIS[\"(lon)\",east,ANGLEUNIT[\"degree\",0.0174532925199433]],"
  308         "    AXIS[\"ellipsoidal height (h)\",up,LENGTHUNIT[\"metre\",1.0]]"
  309         "]");
  310     auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
  311     ASSERT_TRUE(crs != nullptr);
  312     ASSERT_TRUE(crs->datum() == nullptr);
  313     ASSERT_TRUE(crs->datumEnsemble() != nullptr);
  314     EXPECT_EQ(crs->datumEnsemble()->datums().size(), 6U);
  315 }
  316 
  317 // ---------------------------------------------------------------------------
  318 
  319 TEST(wkt_parse, invalid_geogcrs_with_ensemble) {
  320     auto wkt =
  321         "GEOGCRS[\"WGS 84\","
  322         "ENSEMBLE[\"WGS 84 ensemble\","
  323         "    MEMBER[\"WGS 84 (TRANSIT)\"],"
  324         "    MEMBER[\"WGS 84 (G730)\"],"
  325         "    MEMBER[\"WGS 84 (G834)\"],"
  326         "    MEMBER[\"WGS 84 (G1150)\"],"
  327         "    MEMBER[\"WGS 84 (G1674)\"],"
  328         "    MEMBER[\"WGS 84 (G1762)\"],"
  329         "    ENSEMBLEACCURACY[2]"
  330         "],"
  331         "CS[ellipsoidal,3],"
  332         "    AXIS[\"(lat)\",north,ANGLEUNIT[\"degree\",0.0174532925199433]],"
  333         "    AXIS[\"(lon)\",east,ANGLEUNIT[\"degree\",0.0174532925199433]],"
  334         "    AXIS[\"ellipsoidal height (h)\",up,LENGTHUNIT[\"metre\",1.0]]"
  335         "]";
  336     EXPECT_THROW(WKTParser().createFromWKT(wkt), ParsingException);
  337 }
  338 
  339 // ---------------------------------------------------------------------------
  340 
  341 static void checkEPSG_4326(GeographicCRSPtr crs, bool latLong = true,
  342                            bool checkEPSGCodes = true) {
  343     if (checkEPSGCodes) {
  344         ASSERT_EQ(crs->identifiers().size(), 1U);
  345         EXPECT_EQ(crs->identifiers()[0]->code(), "4326");
  346         EXPECT_EQ(*(crs->identifiers()[0]->codeSpace()), "EPSG");
  347     }
  348     EXPECT_EQ(crs->nameStr(), "WGS 84");
  349 
  350     auto cs = crs->coordinateSystem();
  351     ASSERT_EQ(cs->axisList().size(), 2U);
  352     if (latLong) {
  353         EXPECT_TRUE(cs->axisList()[0]->nameStr() == "Latitude" ||
  354                     cs->axisList()[0]->nameStr() == "Geodetic latitude")
  355             << cs->axisList()[0]->nameStr();
  356         EXPECT_EQ(tolower(cs->axisList()[0]->abbreviation()), "lat");
  357         EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::NORTH);
  358 
  359         EXPECT_TRUE(cs->axisList()[1]->nameStr() == "Longitude" ||
  360                     cs->axisList()[1]->nameStr() == "Geodetic longitude")
  361             << cs->axisList()[1]->nameStr();
  362         EXPECT_EQ(tolower(cs->axisList()[1]->abbreviation()), "lon");
  363         EXPECT_EQ(cs->axisList()[1]->direction(), AxisDirection::EAST);
  364     } else {
  365         EXPECT_EQ(cs->axisList()[0]->nameStr(), "Longitude");
  366         EXPECT_EQ(cs->axisList()[0]->abbreviation(), "lon");
  367         EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::EAST);
  368 
  369         EXPECT_EQ(cs->axisList()[1]->nameStr(), "Latitude");
  370         EXPECT_EQ(cs->axisList()[1]->abbreviation(), "lat");
  371         EXPECT_EQ(cs->axisList()[1]->direction(), AxisDirection::NORTH);
  372     }
  373 
  374     auto datum = crs->datum();
  375     if (checkEPSGCodes) {
  376         ASSERT_EQ(datum->identifiers().size(), 1U);
  377         EXPECT_EQ(datum->identifiers()[0]->code(), "6326");
  378         EXPECT_EQ(*(datum->identifiers()[0]->codeSpace()), "EPSG");
  379     }
  380     EXPECT_EQ(datum->nameStr(), "World Geodetic System 1984");
  381 
  382     auto ellipsoid = datum->ellipsoid();
  383     EXPECT_EQ(ellipsoid->semiMajorAxis().value(), 6378137.0);
  384     EXPECT_EQ(ellipsoid->semiMajorAxis().unit(), UnitOfMeasure::METRE);
  385     EXPECT_EQ(ellipsoid->inverseFlattening()->value(), 298.257223563);
  386     if (checkEPSGCodes) {
  387         ASSERT_EQ(ellipsoid->identifiers().size(), 1U);
  388         EXPECT_EQ(ellipsoid->identifiers()[0]->code(), "7030");
  389         EXPECT_EQ(*(ellipsoid->identifiers()[0]->codeSpace()), "EPSG");
  390     }
  391     EXPECT_EQ(ellipsoid->nameStr(), "WGS 84");
  392 }
  393 
  394 // ---------------------------------------------------------------------------
  395 
  396 TEST(wkt_parse, wkt1_EPSG_4326) {
  397     auto obj = WKTParser().createFromWKT(
  398         "GEOGCS[\"WGS 84\","
  399         "    DATUM[\"WGS_1984\","
  400         "        SPHEROID[\"WGS 84\",6378137,298.257223563,"
  401         "            AUTHORITY[\"EPSG\",\"7030\"]],"
  402         "        AUTHORITY[\"EPSG\",\"6326\"]],"
  403         "    PRIMEM[\"Greenwich\",0,"
  404         "        AUTHORITY[\"EPSG\",\"8901\"]],"
  405         "    UNIT[\"degree\",0.0174532925199433,"
  406         "        AUTHORITY[\"EPSG\",\"9122\"]],"
  407         "    AUTHORITY[\"EPSG\",\"4326\"]]");
  408     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  409     ASSERT_TRUE(crs != nullptr);
  410     checkEPSG_4326(crs, false /* longlat order */);
  411 }
  412 
  413 // ---------------------------------------------------------------------------
  414 
  415 TEST(wkt_parse, wkt1_EPSG_4267) {
  416     auto obj =
  417         WKTParser()
  418             .attachDatabaseContext(DatabaseContext::create())
  419             .createFromWKT(
  420                 "GEOGCS[\"NAD27\","
  421                 "    DATUM[\"North_American_Datum_1927\","
  422                 "        SPHEROID[\"Clarke 1866\",6378206.4,294.978698213898,"
  423                 "            AUTHORITY[\"EPSG\",\"7008\"]],"
  424                 "        AUTHORITY[\"EPSG\",\"6267\"]],"
  425                 "    PRIMEM[\"Greenwich\",0,"
  426                 "        AUTHORITY[\"EPSG\",\"8901\"]],"
  427                 "    UNIT[\"degree\",0.0174532925199433,"
  428                 "        AUTHORITY[\"EPSG\",\"9122\"]],"
  429                 "    AUTHORITY[\"EPSG\",\"4267\"]]");
  430     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  431     ASSERT_TRUE(crs != nullptr);
  432 
  433     auto datum = crs->datum();
  434     ASSERT_EQ(datum->identifiers().size(), 1U);
  435     EXPECT_EQ(datum->identifiers()[0]->code(), "6267");
  436     EXPECT_EQ(*(datum->identifiers()[0]->codeSpace()), "EPSG");
  437     EXPECT_EQ(datum->nameStr(), "North American Datum 1927");
  438 }
  439 
  440 // ---------------------------------------------------------------------------
  441 
  442 TEST(wkt_parse, wkt1_EPSG_4807_grad_mess) {
  443     auto obj = WKTParser().createFromWKT(
  444         "GEOGCS[\"NTF (Paris)\",\n"
  445         "    DATUM[\"Nouvelle_Triangulation_Francaise_Paris\",\n"
  446         "        SPHEROID[\"Clarke 1880 (IGN)\",6378249.2,293.466021293627,\n"
  447         "            AUTHORITY[\"EPSG\",\"6807\"]],\n"
  448         "        AUTHORITY[\"EPSG\",\"6807\"]],\n"
  449         /* WKT1_GDAL weirdness: PRIMEM is converted to degree */
  450         "    PRIMEM[\"Paris\",2.33722917,\n"
  451         "        AUTHORITY[\"EPSG\",\"8903\"]],\n"
  452         "    UNIT[\"grad\",0.015707963267949,\n"
  453         "        AUTHORITY[\"EPSG\",\"9105\"]],\n"
  454         "    AXIS[\"latitude\",NORTH],\n"
  455         "    AXIS[\"longitude\",EAST],\n"
  456         "    AUTHORITY[\"EPSG\",\"4807\"]]");
  457     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  458     ASSERT_TRUE(crs != nullptr);
  459 
  460     auto datum = crs->datum();
  461     auto primem = datum->primeMeridian();
  462     EXPECT_EQ(primem->longitude().unit(), UnitOfMeasure::GRAD);
  463     // Check that we have corrected the value that was in degree into grad.
  464     EXPECT_EQ(primem->longitude().value(), 2.5969213);
  465 }
  466 
  467 // ---------------------------------------------------------------------------
  468 
  469 TEST(wkt_parse, wkt1_geographic_old_datum_name_from_EPSG_code) {
  470     auto wkt =
  471         "GEOGCS[\"S-JTSK (Ferro)\",\n"
  472         "    "
  473         "DATUM[\"System_Jednotne_Trigonometricke_Site_Katastralni_Ferro\",\n"
  474         "        SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,\n"
  475         "            AUTHORITY[\"EPSG\",\"7004\"]],\n"
  476         "        AUTHORITY[\"EPSG\",\"6818\"]],\n"
  477         "    PRIMEM[\"Ferro\",-17.66666666666667,\n"
  478         "       AUTHORITY[\"EPSG\",\"8909\"]],\n"
  479         "    UNIT[\"degree\",0.0174532925199433,\n"
  480         "        AUTHORITY[\"EPSG\",\"9122\"]],\n"
  481         "    AUTHORITY[\"EPSG\",\"4818\"]]";
  482     auto obj = WKTParser()
  483                    .attachDatabaseContext(DatabaseContext::create())
  484                    .createFromWKT(wkt);
  485     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  486     ASSERT_TRUE(crs != nullptr);
  487 
  488     auto datum = crs->datum();
  489     EXPECT_EQ(
  490         datum->nameStr(),
  491         "System of the Unified Trigonometrical Cadastral Network (Ferro)");
  492 }
  493 
  494 // ---------------------------------------------------------------------------
  495 
  496 TEST(wkt_parse, wkt1_geographic_old_datum_name_witout_EPSG_code) {
  497     auto wkt =
  498         "GEOGCS[\"S-JTSK (Ferro)\",\n"
  499         "    "
  500         "DATUM[\"System_Jednotne_Trigonometricke_Site_Katastralni_Ferro\",\n"
  501         "        SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,\n"
  502         "            AUTHORITY[\"EPSG\",\"7004\"]]],\n"
  503         "    PRIMEM[\"Ferro\",-17.66666666666667,\n"
  504         "       AUTHORITY[\"EPSG\",\"8909\"]],\n"
  505         "    UNIT[\"degree\",0.0174532925199433,\n"
  506         "        AUTHORITY[\"EPSG\",\"9122\"]],\n"
  507         "    AUTHORITY[\"EPSG\",\"4818\"]]";
  508     auto obj = WKTParser()
  509                    .attachDatabaseContext(DatabaseContext::create())
  510                    .createFromWKT(wkt);
  511     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  512     ASSERT_TRUE(crs != nullptr);
  513 
  514     auto datum = crs->datum();
  515     EXPECT_EQ(
  516         datum->nameStr(),
  517         "System of the Unified Trigonometrical Cadastral Network (Ferro)");
  518 }
  519 
  520 // ---------------------------------------------------------------------------
  521 
  522 TEST(wkt_parse, wkt1_geographic_deprecated) {
  523     auto wkt = "GEOGCS[\"SAD69 (deprecated)\",\n"
  524                "    DATUM[\"South_American_Datum_1969\",\n"
  525                "        SPHEROID[\"GRS 1967\",6378160,298.247167427,\n"
  526                "            AUTHORITY[\"EPSG\",\"7036\"]],\n"
  527                "        AUTHORITY[\"EPSG\",\"6291\"]],\n"
  528                "    PRIMEM[\"Greenwich\",0,\n"
  529                "        AUTHORITY[\"EPSG\",\"8901\"]],\n"
  530                "    UNIT[\"degree\",0.0174532925199433,\n"
  531                "        AUTHORITY[\"EPSG\",\"9108\"]],\n"
  532                "    AUTHORITY[\"EPSG\",\"4291\"]]";
  533     auto obj = WKTParser()
  534                    .attachDatabaseContext(DatabaseContext::create())
  535                    .createFromWKT(wkt);
  536     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  537     ASSERT_TRUE(crs != nullptr);
  538 
  539     EXPECT_EQ(crs->nameStr(), "SAD69");
  540     EXPECT_TRUE(crs->isDeprecated());
  541 }
  542 
  543 // ---------------------------------------------------------------------------
  544 
  545 static std::string contentWKT2_EPSG_4326(
  546     "[\"WGS 84\",\n"
  547     "    DATUM[\"World Geodetic System 1984\",\n"
  548     "        ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
  549     "            LENGTHUNIT[\"metre\",1,\n"
  550     "                ID[\"EPSG\",9001]],\n"
  551     "            ID[\"EPSG\",7030]],\n"
  552     "        ID[\"EPSG\",6326]],\n"
  553     "    PRIMEM[\"Greenwich\",0,\n"
  554     "        ANGLEUNIT[\"degree\",0.0174532925199433,\n"
  555     "            ID[\"EPSG\",9122]],\n"
  556     "        ID[\"EPSG\",8901]],\n"
  557     "    CS[ellipsoidal,2],\n"
  558     "        AXIS[\"latitude\",north,\n"
  559     "            ORDER[1],\n"
  560     "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
  561     "                ID[\"EPSG\",9122]]],\n"
  562     "        AXIS[\"longitude\",east,\n"
  563     "            ORDER[2],\n"
  564     "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
  565     "                ID[\"EPSG\",9122]]],\n"
  566     "    ID[\"EPSG\",4326]]");
  567 
  568 // ---------------------------------------------------------------------------
  569 
  570 TEST(wkt_parse, wkt1_geographic_with_PROJ4_extension) {
  571     auto wkt = "GEOGCS[\"WGS 84\",\n"
  572                "    DATUM[\"unknown\",\n"
  573                "        SPHEROID[\"WGS84\",6378137,298.257223563]],\n"
  574                "    PRIMEM[\"Greenwich\",0],\n"
  575                "    UNIT[\"degree\",0.0174532925199433],\n"
  576                "    EXTENSION[\"PROJ4\",\"+proj=longlat +foo=bar +wktext\"]]";
  577     auto obj = WKTParser().createFromWKT(wkt);
  578 
  579     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  580     ASSERT_TRUE(crs != nullptr);
  581     EXPECT_EQ(
  582         crs->exportToWKT(
  583             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
  584         wkt);
  585 
  586     EXPECT_EQ(
  587         crs->exportToPROJString(
  588             PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
  589                 .get()),
  590         "+proj=longlat +foo=bar +wktext +type=crs");
  591 
  592     EXPECT_TRUE(
  593         crs->exportToWKT(WKTFormatter::create().get()).find("EXTENSION") ==
  594         std::string::npos);
  595 
  596     EXPECT_TRUE(
  597         crs->exportToWKT(
  598                WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI).get())
  599             .find("EXTENSION") == std::string::npos);
  600 }
  601 
  602 // ---------------------------------------------------------------------------
  603 
  604 TEST(wkt_parse, wkt1_geocentric_with_PROJ4_extension) {
  605     auto wkt = "GEOCCS[\"WGS 84\",\n"
  606                "    DATUM[\"unknown\",\n"
  607                "        SPHEROID[\"WGS84\",6378137,298.257223563]],\n"
  608                "    PRIMEM[\"Greenwich\",0],\n"
  609                "    UNIT[\"Meter\",1],\n"
  610                "    AXIS[\"Geocentric X\",OTHER],\n"
  611                "    AXIS[\"Geocentric Y\",OTHER],\n"
  612                "    AXIS[\"Geocentric Z\",NORTH],\n"
  613                "    EXTENSION[\"PROJ4\",\"+proj=geocent +foo=bar +wktext\"]]";
  614     auto obj = WKTParser().createFromWKT(wkt);
  615 
  616     auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
  617     ASSERT_TRUE(crs != nullptr);
  618     EXPECT_EQ(
  619         crs->exportToWKT(
  620             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
  621         wkt);
  622 
  623     EXPECT_EQ(
  624         crs->exportToPROJString(
  625             PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
  626                 .get()),
  627         "+proj=geocent +foo=bar +wktext +type=crs");
  628 
  629     EXPECT_TRUE(
  630         crs->exportToWKT(WKTFormatter::create().get()).find("EXTENSION") ==
  631         std::string::npos);
  632 }
  633 
  634 // ---------------------------------------------------------------------------
  635 
  636 TEST(wkt_parse, wkt2_GEODCRS_EPSG_4326) {
  637     auto obj = WKTParser().createFromWKT("GEODCRS" + contentWKT2_EPSG_4326);
  638     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  639     ASSERT_TRUE(crs != nullptr);
  640     checkEPSG_4326(crs);
  641 }
  642 
  643 // ---------------------------------------------------------------------------
  644 
  645 TEST(wkt_parse, wkt2_long_GEODETICCRS_EPSG_4326) {
  646     auto obj = WKTParser().createFromWKT("GEODETICCRS" + contentWKT2_EPSG_4326);
  647     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  648     ASSERT_TRUE(crs != nullptr);
  649     checkEPSG_4326(crs);
  650 }
  651 
  652 // ---------------------------------------------------------------------------
  653 
  654 TEST(wkt_parse, wkt2_2018_GEOGCRS_EPSG_4326) {
  655     auto obj = WKTParser().createFromWKT("GEOGCRS" + contentWKT2_EPSG_4326);
  656     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  657     ASSERT_TRUE(crs != nullptr);
  658     checkEPSG_4326(crs);
  659 }
  660 
  661 // ---------------------------------------------------------------------------
  662 
  663 TEST(wkt_parse, wkt2_2018_long_GEOGRAPHICCRS_EPSG_4326) {
  664     auto obj =
  665         WKTParser().createFromWKT("GEOGRAPHICCRS" + contentWKT2_EPSG_4326);
  666     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  667     ASSERT_TRUE(crs != nullptr);
  668     checkEPSG_4326(crs);
  669 }
  670 
  671 // ---------------------------------------------------------------------------
  672 
  673 TEST(wkt_parse, wkt2_simplified_EPSG_4326) {
  674     auto obj = WKTParser().createFromWKT(
  675         "GEODCRS[\"WGS 84\",\n"
  676         "    DATUM[\"World Geodetic System 1984\",\n"
  677         "        ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
  678         "    CS[ellipsoidal,2],\n"
  679         "        AXIS[\"latitude (lat)\",north],\n" // test "name
  680                                                     // (abbreviation)"
  681         "        AXIS[\"longitude (lon)\",east],\n"
  682         "        UNIT[\"degree\",0.0174532925199433],\n"
  683         "    ID[\"EPSG\",4326]]");
  684     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  685     ASSERT_TRUE(crs != nullptr);
  686     checkEPSG_4326(crs, true /* latlong */, false /* no EPSG codes */);
  687 }
  688 
  689 // ---------------------------------------------------------------------------
  690 
  691 TEST(wkt_parse, wkt2_GEODETICDATUM) {
  692     auto obj = WKTParser().createFromWKT(
  693         "GEODCRS[\"WGS 84\",\n"
  694         "    GEODETICDATUM[\"World Geodetic System 1984\",\n"
  695         "        ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
  696         "    CS[ellipsoidal,2],\n"
  697         "        AXIS[\"(lat)\",north],\n" // test "(abbreviation)"
  698         "        AXIS[\"(lon)\",east],\n"
  699         "        UNIT[\"degree\",0.0174532925199433],\n"
  700         "    ID[\"EPSG\",4326]]");
  701     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  702     ASSERT_TRUE(crs != nullptr);
  703     checkEPSG_4326(crs, true /* latlong */, false /* no EPSG codes */);
  704 }
  705 
  706 // ---------------------------------------------------------------------------
  707 
  708 TEST(wkt_parse, wkt2_TRF) {
  709     auto obj = WKTParser().createFromWKT(
  710         "GEODCRS[\"WGS 84\",\n"
  711         "    TRF[\"World Geodetic System 1984\",\n"
  712         "        ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
  713         "    CS[ellipsoidal,2],\n"
  714         "        AXIS[\"latitude\",north],\n"
  715         "        AXIS[\"longitude\",east],\n"
  716         "        UNIT[\"degree\",0.0174532925199433],\n"
  717         "    ID[\"EPSG\",4326]]");
  718     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  719     ASSERT_TRUE(crs != nullptr);
  720     checkEPSG_4326(crs, true /* latlong */, false /* no EPSG codes */);
  721 }
  722 
  723 // ---------------------------------------------------------------------------
  724 
  725 static void checkEPSG_4979(GeographicCRSPtr crs) {
  726     ASSERT_EQ(crs->identifiers().size(), 1U);
  727     EXPECT_EQ(crs->identifiers()[0]->code(), "4979");
  728     EXPECT_EQ(*(crs->identifiers()[0]->codeSpace()), "EPSG");
  729     EXPECT_EQ(crs->nameStr(), "WGS 84");
  730 
  731     auto cs = crs->coordinateSystem();
  732     ASSERT_EQ(cs->axisList().size(), 3U);
  733     EXPECT_EQ(cs->axisList()[0]->nameStr(), "Latitude");
  734     EXPECT_EQ(cs->axisList()[0]->abbreviation(), "lat");
  735     EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::NORTH);
  736 
  737     EXPECT_EQ(cs->axisList()[1]->nameStr(), "Longitude");
  738     EXPECT_EQ(cs->axisList()[1]->abbreviation(), "lon");
  739     EXPECT_EQ(cs->axisList()[1]->direction(), AxisDirection::EAST);
  740 
  741     EXPECT_EQ(cs->axisList()[2]->nameStr(), "Ellipsoidal height");
  742     EXPECT_EQ(cs->axisList()[2]->abbreviation(), "h");
  743     EXPECT_EQ(cs->axisList()[2]->direction(), AxisDirection::UP);
  744 
  745     auto datum = crs->datum();
  746     EXPECT_EQ(datum->nameStr(), "World Geodetic System 1984");
  747 
  748     auto ellipsoid = datum->ellipsoid();
  749     EXPECT_EQ(ellipsoid->semiMajorAxis().value(), 6378137.0);
  750     EXPECT_EQ(ellipsoid->semiMajorAxis().unit(), UnitOfMeasure::METRE);
  751     EXPECT_EQ(ellipsoid->inverseFlattening()->value(), 298.257223563);
  752     EXPECT_EQ(ellipsoid->nameStr(), "WGS 84");
  753 }
  754 
  755 // ---------------------------------------------------------------------------
  756 
  757 TEST(wkt_parse, wkt2_EPSG_4979) {
  758     auto obj = WKTParser().createFromWKT(
  759         "GEODCRS[\"WGS 84\",\n"
  760         "    DATUM[\"World Geodetic System 1984\",\n"
  761         "        ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
  762         "    CS[ellipsoidal,3],\n"
  763         "        AXIS[\"latitude\",north,\n"
  764         "            UNIT[\"degree\",0.0174532925199433]],\n"
  765         "        AXIS[\"longitude\",east,\n"
  766         "            UNIT[\"degree\",0.0174532925199433]],\n"
  767         "        AXIS[\"ellipsoidal height\",up,\n"
  768         "            UNIT[\"metre\",1]],\n"
  769         "    ID[\"EPSG\",4979]]");
  770     auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
  771     ASSERT_TRUE(crs != nullptr);
  772     checkEPSG_4979(crs);
  773 }
  774 
  775 // ---------------------------------------------------------------------------
  776 
  777 static void checkGeocentric(GeodeticCRSPtr crs) {
  778     // Explicitly check this is NOT a GeographicCRS
  779     EXPECT_TRUE(!dynamic_cast<GeographicCRS *>(crs.get()));
  780 
  781     EXPECT_EQ(crs->nameStr(), "WGS 84 (geocentric)");
  782 
  783     EXPECT_TRUE(crs->isGeocentric());
  784     auto cs = crs->coordinateSystem();
  785     ASSERT_EQ(cs->axisList().size(), 3U);
  786 
  787     EXPECT_EQ(cs->axisList()[0]->nameStr(), "Geocentric X");
  788     EXPECT_EQ(cs->axisList()[0]->abbreviation(), "X");
  789     EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::GEOCENTRIC_X);
  790 
  791     EXPECT_EQ(cs->axisList()[1]->nameStr(), "Geocentric Y");
  792     EXPECT_EQ(cs->axisList()[1]->abbreviation(), "Y");
  793     EXPECT_EQ(cs->axisList()[1]->direction(), AxisDirection::GEOCENTRIC_Y);
  794 
  795     EXPECT_EQ(cs->axisList()[2]->nameStr(), "Geocentric Z");
  796     EXPECT_EQ(cs->axisList()[2]->abbreviation(), "Z");
  797     EXPECT_EQ(cs->axisList()[2]->direction(), AxisDirection::GEOCENTRIC_Z);
  798 
  799     auto datum = crs->datum();
  800     EXPECT_EQ(datum->nameStr(), "World Geodetic System 1984");
  801 
  802     auto ellipsoid = datum->ellipsoid();
  803     EXPECT_EQ(ellipsoid->semiMajorAxis().value(), 6378137.0);
  804     EXPECT_EQ(ellipsoid->semiMajorAxis().unit(), UnitOfMeasure::METRE);
  805     EXPECT_EQ(ellipsoid->inverseFlattening()->value(), 298.257223563);
  806     EXPECT_EQ(ellipsoid->nameStr(), "WGS 84");
  807 
  808     auto primem = datum->primeMeridian();
  809     ASSERT_EQ(primem->longitude().unit(), UnitOfMeasure::DEGREE);
  810 }
  811 
  812 // ---------------------------------------------------------------------------
  813 
  814 TEST(wkt_parse, wkt2_geocentric) {
  815     auto wkt = "GEODCRS[\"WGS 84 (geocentric)\",\n"
  816                "    DATUM[\"World Geodetic System 1984\",\n"
  817                "        ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
  818                "            LENGTHUNIT[\"metre\",1,\n"
  819                "                ID[\"EPSG\",9001]],\n"
  820                "            ID[\"EPSG\",7030]],\n"
  821                "        ID[\"EPSG\",6326]],\n"
  822                "    PRIMEM[\"Greenwich\",0,\n"
  823                "        ANGLEUNIT[\"degree\",0.0174532925199433,\n"
  824                "            ID[\"EPSG\",9122]],\n"
  825                "        ID[\"EPSG\",8901]],\n"
  826                "    CS[Cartesian,3],\n"
  827                "        AXIS[\"(X)\",geocentricX,\n"
  828                "            ORDER[1],\n"
  829                "            LENGTHUNIT[\"metre\",1,\n"
  830                "                ID[\"EPSG\",9001]]],\n"
  831                "        AXIS[\"(Y)\",geocentricY,\n"
  832                "            ORDER[2],\n"
  833                "            LENGTHUNIT[\"metre\",1,\n"
  834                "                ID[\"EPSG\",9001]]],\n"
  835                "        AXIS[\"(Z)\",geocentricZ,\n"
  836                "            ORDER[3],\n"
  837                "            LENGTHUNIT[\"metre\",1,\n"
  838                "                ID[\"EPSG\",9001]]],\n"
  839                "    ID[\"EPSG\",4328]]";
  840     auto obj = WKTParser().createFromWKT(wkt);
  841     auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
  842     ASSERT_TRUE(crs != nullptr);
  843     checkGeocentric(crs);
  844 }
  845 
  846 // ---------------------------------------------------------------------------
  847 
  848 TEST(wkt_parse, wkt2_simplified_geocentric) {
  849     auto wkt = "GEODCRS[\"WGS 84 (geocentric)\",\n"
  850                "    DATUM[\"World Geodetic System 1984\",\n"
  851                "        ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
  852                "    CS[Cartesian,3],\n"
  853                "        AXIS[\"(X)\",geocentricX],\n"
  854                "        AXIS[\"(Y)\",geocentricY],\n"
  855                "        AXIS[\"(Z)\",geocentricZ],\n"
  856                "        UNIT[\"metre\",1],\n"
  857                "    ID[\"EPSG\",4328]]";
  858     auto obj = WKTParser().createFromWKT(wkt);
  859     auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
  860     ASSERT_TRUE(crs != nullptr);
  861     checkGeocentric(crs);
  862 }
  863 
  864 // ---------------------------------------------------------------------------
  865 
  866 TEST(wkt_parse, wkt1_geocentric) {
  867     auto wkt = "GEOCCS[\"WGS 84 (geocentric)\",\n"
  868                "    DATUM[\"WGS_1984\",\n"
  869                "        SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
  870                "            AUTHORITY[\"EPSG\",\"7030\"]],\n"
  871                "        AUTHORITY[\"EPSG\",\"6326\"]],\n"
  872                "    PRIMEM[\"Greenwich\",0,\n"
  873                "        AUTHORITY[\"EPSG\",\"8901\"]],\n"
  874                "    UNIT[\"metre\",1,\n"
  875                "        AUTHORITY[\"EPSG\",\"9001\"]],\n"
  876                "    AXIS[\"Geocentric X\",OTHER],\n"
  877                "    AXIS[\"Geocentric Y\",OTHER],\n"
  878                "    AXIS[\"Geocentric Z\",NORTH],\n"
  879                "    AUTHORITY[\"EPSG\",\"4328\"]]";
  880     auto obj = WKTParser().createFromWKT(wkt);
  881     auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
  882     ASSERT_TRUE(crs != nullptr);
  883     checkGeocentric(crs);
  884 }
  885 
  886 // ---------------------------------------------------------------------------
  887 
  888 TEST(wkt_parse, wkt1_geocentric_with_z_OTHER) {
  889     auto wkt = "GEOCCS[\"WGS 84 (geocentric)\",\n"
  890                "    DATUM[\"WGS_1984\",\n"
  891                "        SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
  892                "            AUTHORITY[\"EPSG\",\"7030\"]],\n"
  893                "        AUTHORITY[\"EPSG\",\"6326\"]],\n"
  894                "    PRIMEM[\"Greenwich\",0,\n"
  895                "        AUTHORITY[\"EPSG\",\"8901\"]],\n"
  896                "    UNIT[\"metre\",1,\n"
  897                "        AUTHORITY[\"EPSG\",\"9001\"]],\n"
  898                "    AXIS[\"Geocentric X\",OTHER],\n"
  899                "    AXIS[\"Geocentric Y\",OTHER],\n"
  900                "    AXIS[\"Geocentric Z\",OTHER],\n"
  901                "    AUTHORITY[\"EPSG\",\"4328\"]]";
  902     auto obj = WKTParser().createFromWKT(wkt);
  903     auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
  904     ASSERT_TRUE(crs != nullptr);
  905     checkGeocentric(crs);
  906 }
  907 
  908 // ---------------------------------------------------------------------------
  909 
  910 static void checkProjected(ProjectedCRSPtr crs, bool checkEPSGCodes = true) {
  911     EXPECT_EQ(crs->nameStr(), "WGS 84 / UTM zone 31N");
  912     ASSERT_EQ(crs->identifiers().size(), 1U);
  913     EXPECT_EQ(crs->identifiers()[0]->code(), "32631");
  914     EXPECT_EQ(*(crs->identifiers()[0]->codeSpace()), "EPSG");
  915 
  916     auto geogCRS = nn_dynamic_pointer_cast<GeographicCRS>(crs->baseCRS());
  917     ASSERT_TRUE(geogCRS != nullptr);
  918     checkEPSG_4326(NN_CHECK_ASSERT(geogCRS), true, checkEPSGCodes);
  919 
  920     auto conversion = crs->derivingConversion();
  921     EXPECT_EQ(conversion->nameStr(), "UTM zone 31N");
  922     auto method = conversion->method();
  923     EXPECT_EQ(method->nameStr(), "Transverse Mercator");
  924     auto values = conversion->parameterValues();
  925     ASSERT_EQ(values.size(), 5U);
  926     {
  927         const auto &opParamvalue =
  928             nn_dynamic_pointer_cast<OperationParameterValue>(values[0]);
  929         ASSERT_TRUE(opParamvalue);
  930         const auto &paramName = opParamvalue->parameter()->nameStr();
  931         const auto &parameterValue = opParamvalue->parameterValue();
  932         EXPECT_EQ(paramName, "Latitude of natural origin");
  933         EXPECT_EQ(parameterValue->type(), ParameterValue::Type::MEASURE);
  934         auto measure = parameterValue->value();
  935         EXPECT_EQ(measure.unit(), UnitOfMeasure::DEGREE);
  936         EXPECT_EQ(measure.value(), 0);
  937     }
  938     {
  939         const auto &opParamvalue =
  940             nn_dynamic_pointer_cast<OperationParameterValue>(values[1]);
  941         ASSERT_TRUE(opParamvalue);
  942         const auto &paramName = opParamvalue->parameter()->nameStr();
  943         const auto &parameterValue = opParamvalue->parameterValue();
  944         EXPECT_EQ(paramName, "Longitude of natural origin");
  945         EXPECT_EQ(parameterValue->type(), ParameterValue::Type::MEASURE);
  946         auto measure = parameterValue->value();
  947         EXPECT_EQ(measure.unit(), UnitOfMeasure::DEGREE);
  948         EXPECT_EQ(measure.value(), 3);
  949     }
  950     {
  951         const auto &opParamvalue =
  952             nn_dynamic_pointer_cast<OperationParameterValue>(values[2]);
  953         ASSERT_TRUE(opParamvalue);
  954         const auto &paramName = opParamvalue->parameter()->nameStr();
  955         const auto &parameterValue = opParamvalue->parameterValue();
  956         EXPECT_EQ(paramName, "Scale factor at natural origin");
  957         EXPECT_EQ(parameterValue->type(), ParameterValue::Type::MEASURE);
  958         auto measure = parameterValue->value();
  959         EXPECT_EQ(measure.unit(), UnitOfMeasure::SCALE_UNITY);
  960         EXPECT_EQ(measure.value(), 0.9996);
  961     }
  962     {
  963         const auto &opParamvalue =
  964             nn_dynamic_pointer_cast<OperationParameterValue>(values[3]);
  965         ASSERT_TRUE(opParamvalue);
  966         const auto &paramName = opParamvalue->parameter()->nameStr();
  967         const auto &parameterValue = opParamvalue->parameterValue();
  968         EXPECT_EQ(paramName, "False easting");
  969         EXPECT_EQ(parameterValue->type(), ParameterValue::Type::MEASURE);
  970         auto measure = parameterValue->value();
  971         EXPECT_EQ(measure.unit(), UnitOfMeasure::METRE);
  972         EXPECT_EQ(measure.value(), 500000);
  973     }
  974     {
  975         const auto &opParamvalue =
  976             nn_dynamic_pointer_cast<OperationParameterValue>(values[4]);
  977         ASSERT_TRUE(opParamvalue);
  978         const auto &paramName = opParamvalue->parameter()->nameStr();
  979         const auto &parameterValue = opParamvalue->parameterValue();
  980         EXPECT_EQ(paramName, "False northing");
  981         EXPECT_EQ(parameterValue->type(), ParameterValue::Type::MEASURE);
  982         auto measure = parameterValue->value();
  983         EXPECT_EQ(measure.unit(), UnitOfMeasure::METRE);
  984         EXPECT_EQ(measure.value(), 0);
  985     }
  986 
  987     auto cs = crs->coordinateSystem();
  988     ASSERT_EQ(cs->axisList().size(), 2U);
  989 
  990     EXPECT_EQ(cs->axisList()[0]->nameStr(), "Easting");
  991     EXPECT_EQ(cs->axisList()[0]->abbreviation(), "E");
  992     EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::EAST);
  993 
  994     EXPECT_EQ(cs->axisList()[1]->nameStr(), "Northing");
  995     EXPECT_EQ(cs->axisList()[1]->abbreviation(), "N");
  996     EXPECT_EQ(cs->axisList()[1]->direction(), AxisDirection::NORTH);
  997 }
  998 
  999 // ---------------------------------------------------------------------------
 1000 
 1001 TEST(wkt_parse, wkt1_projected) {
 1002     auto wkt = "PROJCS[\"WGS 84 / UTM zone 31N\",\n"
 1003                "    GEOGCS[\"WGS 84\",\n"
 1004                "        DATUM[\"WGS_1984\",\n"
 1005                "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 1006                "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
 1007                "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
 1008                "        PRIMEM[\"Greenwich\",0,\n"
 1009                "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
 1010                "        UNIT[\"degree\",0.0174532925199433,\n"
 1011                "            AUTHORITY[\"EPSG\",\"9122\"]],\n"
 1012                "        AUTHORITY[\"EPSG\",\"4326\"]],\n"
 1013                "    PROJECTION[\"Transverse_Mercator\"],\n"
 1014                "    PARAMETER[\"latitude_of_origin\",0],\n"
 1015                "    PARAMETER[\"central_meridian\",3],\n"
 1016                "    PARAMETER[\"scale_factor\",0.9996],\n"
 1017                "    PARAMETER[\"false_easting\",500000],\n"
 1018                "    PARAMETER[\"false_northing\",0],\n"
 1019                "    UNIT[\"metre\",1,\n"
 1020                "        AUTHORITY[\"EPSG\",\"9001\"]],\n"
 1021                "    AXIS[\"(E)\",East],\n" // should normally be uppercase
 1022                "    AXIS[\"(N)\",NORTH],\n"
 1023                "    AUTHORITY[\"EPSG\",\"32631\"]]";
 1024     auto obj = WKTParser()
 1025                    .attachDatabaseContext(DatabaseContext::create())
 1026                    .createFromWKT(wkt);
 1027     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1028     ASSERT_TRUE(crs != nullptr);
 1029     checkProjected(crs);
 1030 
 1031     EXPECT_TRUE(!crs->baseCRS()->identifiers().empty());
 1032 }
 1033 
 1034 // ---------------------------------------------------------------------------
 1035 
 1036 TEST(wkt_parse, wkt1_projected_no_axis) {
 1037     auto wkt = "PROJCS[\"WGS 84 / UTM zone 31N\",\n"
 1038                "    GEOGCS[\"WGS 84\",\n"
 1039                "        DATUM[\"WGS_1984\",\n"
 1040                "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 1041                "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
 1042                "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
 1043                "        PRIMEM[\"Greenwich\",0,\n"
 1044                "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
 1045                "        UNIT[\"degree\",0.0174532925199433,\n"
 1046                "            AUTHORITY[\"EPSG\",\"9122\"]],\n"
 1047                "        AXIS[\"latitude\",NORTH],\n"
 1048                "        AXIS[\"longitude\",EAST],\n"
 1049                "        AUTHORITY[\"EPSG\",\"4326\"]],\n"
 1050                "    PROJECTION[\"Transverse_Mercator\"],\n"
 1051                "    PARAMETER[\"latitude_of_origin\",0],\n"
 1052                "    PARAMETER[\"central_meridian\",3],\n"
 1053                "    PARAMETER[\"scale_factor\",0.9996],\n"
 1054                "    PARAMETER[\"false_easting\",500000],\n"
 1055                "    PARAMETER[\"false_northing\",0],\n"
 1056                "    UNIT[\"metre\",1,\n"
 1057                "        AUTHORITY[\"EPSG\",\"9001\"]],\n"
 1058                "    AUTHORITY[\"EPSG\",\"32631\"]]";
 1059     auto obj = WKTParser().createFromWKT(wkt);
 1060     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1061     ASSERT_TRUE(crs != nullptr);
 1062     checkProjected(crs);
 1063 }
 1064 
 1065 // ---------------------------------------------------------------------------
 1066 
 1067 TEST(wkt_parse, wkt1_projected_wrong_axis_geogcs) {
 1068     auto wkt = "PROJCS[\"WGS 84 / UTM zone 31N\",\n"
 1069                "    GEOGCS[\"WGS 84\",\n"
 1070                "        DATUM[\"WGS_1984\",\n"
 1071                "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 1072                "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
 1073                "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
 1074                "        PRIMEM[\"Greenwich\",0,\n"
 1075                "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
 1076                "        UNIT[\"degree\",0.0174532925199433,\n"
 1077                "            AUTHORITY[\"EPSG\",\"9122\"]],\n"
 1078                "        AXIS[\"longitude\",EAST],\n"
 1079                "        AXIS[\"latitude\",NORTH],\n"
 1080                "        AUTHORITY[\"EPSG\",\"4326\"]],\n"
 1081                "    PROJECTION[\"Transverse_Mercator\"],\n"
 1082                "    PARAMETER[\"latitude_of_origin\",0],\n"
 1083                "    PARAMETER[\"central_meridian\",3],\n"
 1084                "    PARAMETER[\"scale_factor\",0.9996],\n"
 1085                "    PARAMETER[\"false_easting\",500000],\n"
 1086                "    PARAMETER[\"false_northing\",0],\n"
 1087                "    UNIT[\"metre\",1,\n"
 1088                "        AUTHORITY[\"EPSG\",\"9001\"]],\n"
 1089                "    AUTHORITY[\"EPSG\",\"32631\"]]";
 1090     WKTParser parser;
 1091     parser.setStrict(false).attachDatabaseContext(DatabaseContext::create());
 1092     auto obj = parser.createFromWKT(wkt);
 1093     EXPECT_TRUE(!parser.warningList().empty());
 1094     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1095     ASSERT_TRUE(crs != nullptr);
 1096 
 1097     EXPECT_TRUE(crs->baseCRS()->identifiers().empty());
 1098 
 1099     auto cs = crs->baseCRS()->coordinateSystem();
 1100     ASSERT_EQ(cs->axisList().size(), 2U);
 1101     EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::EAST);
 1102     EXPECT_EQ(cs->axisList()[1]->direction(), AxisDirection::NORTH);
 1103 }
 1104 
 1105 // ---------------------------------------------------------------------------
 1106 
 1107 TEST(wkt_parse, wkt1_projected_with_PROJ4_extension) {
 1108     auto wkt = "PROJCS[\"unnamed\",\n"
 1109                "    GEOGCS[\"WGS 84\",\n"
 1110                "        DATUM[\"unknown\",\n"
 1111                "            SPHEROID[\"WGS84\",6378137,298.257223563]],\n"
 1112                "        PRIMEM[\"Greenwich\",0],\n"
 1113                "        UNIT[\"degree\",0.0174532925199433]],\n"
 1114                "    PROJECTION[\"Mercator_1SP\"],\n"
 1115                "    PARAMETER[\"central_meridian\",0],\n"
 1116                "    PARAMETER[\"scale_factor\",1],\n"
 1117                "    PARAMETER[\"false_easting\",0],\n"
 1118                "    PARAMETER[\"false_northing\",0],\n"
 1119                "    UNIT[\"Meter\",1],\n"
 1120                "    AXIS[\"Easting\",EAST],\n"
 1121                "    AXIS[\"Northing\",NORTH],\n"
 1122                "    EXTENSION[\"PROJ4\",\"+proj=merc +wktext\"]]";
 1123     auto obj = WKTParser().createFromWKT(wkt);
 1124 
 1125     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1126     ASSERT_TRUE(crs != nullptr);
 1127     EXPECT_EQ(
 1128         crs->exportToWKT(
 1129             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
 1130         wkt);
 1131 
 1132     EXPECT_EQ(
 1133         crs->exportToPROJString(
 1134             PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
 1135                 .get()),
 1136         "+proj=merc +wktext +type=crs");
 1137 
 1138     EXPECT_TRUE(
 1139         crs->exportToWKT(WKTFormatter::create().get()).find("EXTENSION") ==
 1140         std::string::npos);
 1141 
 1142     EXPECT_TRUE(
 1143         crs->exportToWKT(
 1144                WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI).get())
 1145             .find("EXTENSION") == std::string::npos);
 1146 }
 1147 
 1148 // ---------------------------------------------------------------------------
 1149 
 1150 TEST(wkt_parse, wkt1_Mercator_1SP_with_latitude_origin_0) {
 1151     auto wkt = "PROJCS[\"unnamed\",\n"
 1152                "    GEOGCS[\"WGS 84\",\n"
 1153                "        DATUM[\"unknown\",\n"
 1154                "            SPHEROID[\"WGS84\",6378137,298.257223563]],\n"
 1155                "        PRIMEM[\"Greenwich\",0],\n"
 1156                "        UNIT[\"degree\",0.0174532925199433]],\n"
 1157                "    PROJECTION[\"Mercator_1SP\"],\n"
 1158                "    PARAMETER[\"latitude_of_origin\",0],\n"
 1159                "    PARAMETER[\"central_meridian\",0],\n"
 1160                "    PARAMETER[\"scale_factor\",1],\n"
 1161                "    PARAMETER[\"false_easting\",0],\n"
 1162                "    PARAMETER[\"false_northing\",0],\n"
 1163                "    UNIT[\"Meter\",1],\n"
 1164                "    AXIS[\"Easting\",EAST],\n"
 1165                "    AXIS[\"Northing\",NORTH]]";
 1166     auto obj = WKTParser().createFromWKT(wkt);
 1167 
 1168     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1169     ASSERT_TRUE(crs != nullptr);
 1170     auto got_wkt = crs->exportToWKT(
 1171         WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get());
 1172     EXPECT_TRUE(got_wkt.find("Mercator_1SP") != std::string::npos) << got_wkt;
 1173 }
 1174 
 1175 // ---------------------------------------------------------------------------
 1176 
 1177 TEST(wkt_parse, wkt1_krovak_south_west) {
 1178     auto wkt =
 1179         "PROJCS[\"S-JTSK / Krovak\","
 1180         "    GEOGCS[\"S-JTSK\","
 1181         "        DATUM[\"System_Jednotne_Trigonometricke_Site_Katastralni\","
 1182         "            SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,"
 1183         "                AUTHORITY[\"EPSG\",\"7004\"]],"
 1184         "            AUTHORITY[\"EPSG\",\"6156\"]],"
 1185         "        PRIMEM[\"Greenwich\",0,"
 1186         "            AUTHORITY[\"EPSG\",\"8901\"]],"
 1187         "        UNIT[\"degree\",0.0174532925199433,"
 1188         "            AUTHORITY[\"EPSG\",\"9122\"]],"
 1189         "        AUTHORITY[\"EPSG\",\"4156\"]],"
 1190         "    PROJECTION[\"Krovak\"],"
 1191         "    PARAMETER[\"latitude_of_center\",49.5],"
 1192         "    PARAMETER[\"longitude_of_center\",24.83333333333333],"
 1193         "    PARAMETER[\"azimuth\",30.28813972222222],"
 1194         "    PARAMETER[\"pseudo_standard_parallel_1\",78.5],"
 1195         "    PARAMETER[\"scale_factor\",0.9999],"
 1196         "    PARAMETER[\"false_easting\",0],"
 1197         "    PARAMETER[\"false_northing\",0],"
 1198         "    UNIT[\"metre\",1,"
 1199         "        AUTHORITY[\"EPSG\",\"9001\"]],"
 1200         "    AXIS[\"X\",SOUTH],"
 1201         "    AXIS[\"Y\",WEST],"
 1202         "    AUTHORITY[\"EPSG\",\"5513\"]]";
 1203 
 1204     auto obj = WKTParser().createFromWKT(wkt);
 1205     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1206     ASSERT_TRUE(crs != nullptr);
 1207 
 1208     EXPECT_EQ(crs->derivingConversion()->method()->nameStr(), "Krovak");
 1209 
 1210     auto expected_wkt2 =
 1211         "PROJCRS[\"S-JTSK / Krovak\",\n"
 1212         "    BASEGEODCRS[\"S-JTSK\",\n"
 1213         "        DATUM[\"System_Jednotne_Trigonometricke_Site_Katastralni\",\n"
 1214         "            ELLIPSOID[\"Bessel 1841\",6377397.155,299.1528128,\n"
 1215         "                LENGTHUNIT[\"metre\",1]]],\n"
 1216         "        PRIMEM[\"Greenwich\",0,\n"
 1217         "            ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 1218         "    CONVERSION[\"unnamed\",\n"
 1219         "        METHOD[\"Krovak\",\n"
 1220         "            ID[\"EPSG\",9819]],\n"
 1221         "        PARAMETER[\"Latitude of projection centre\",49.5,\n"
 1222         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 1223         "            ID[\"EPSG\",8811]],\n"
 1224         "        PARAMETER[\"Longitude of origin\",24.8333333333333,\n"
 1225         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 1226         "            ID[\"EPSG\",8833]],\n"
 1227         "        PARAMETER[\"Co-latitude of cone axis\",30.2881397222222,\n"
 1228         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 1229         "            ID[\"EPSG\",1036]],\n"
 1230         "        PARAMETER[\"Latitude of pseudo standard parallel\",78.5,\n"
 1231         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 1232         "            ID[\"EPSG\",8818]],\n"
 1233         "        PARAMETER[\"Scale factor on pseudo standard "
 1234         "parallel\",0.9999,\n"
 1235         "            SCALEUNIT[\"unity\",1],\n"
 1236         "            ID[\"EPSG\",8819]],\n"
 1237         "        PARAMETER[\"False easting\",0,\n"
 1238         "            LENGTHUNIT[\"metre\",1],\n"
 1239         "            ID[\"EPSG\",8806]],\n"
 1240         "        PARAMETER[\"False northing\",0,\n"
 1241         "            LENGTHUNIT[\"metre\",1],\n"
 1242         "            ID[\"EPSG\",8807]]],\n"
 1243         "    CS[Cartesian,2],\n"
 1244         "        AXIS[\"x\",south,\n"
 1245         "            ORDER[1],\n"
 1246         "            LENGTHUNIT[\"metre\",1]],\n"
 1247         "        AXIS[\"y\",west,\n"
 1248         "            ORDER[2],\n"
 1249         "            LENGTHUNIT[\"metre\",1]],\n"
 1250         "    ID[\"EPSG\",5513]]";
 1251 
 1252     EXPECT_EQ(crs->exportToWKT(WKTFormatter::create().get()), expected_wkt2);
 1253 
 1254     auto projString =
 1255         crs->exportToPROJString(PROJStringFormatter::create().get());
 1256     auto expectedPROJString = "+proj=krovak +axis=swu +lat_0=49.5 "
 1257                               "+lon_0=24.8333333333333 +alpha=30.2881397222222 "
 1258                               "+k=0.9999 +x_0=0 +y_0=0 +ellps=bessel +units=m "
 1259                               "+no_defs +type=crs";
 1260     EXPECT_EQ(projString, expectedPROJString);
 1261 
 1262     obj = PROJStringParser().createFromPROJString(projString);
 1263     crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1264     ASSERT_TRUE(crs != nullptr);
 1265     auto wkt2 = crs->exportToWKT(WKTFormatter::create().get());
 1266     EXPECT_TRUE(wkt2.find("METHOD[\"Krovak\"") != std::string::npos) << wkt2;
 1267     EXPECT_TRUE(
 1268         wkt2.find("PARAMETER[\"Latitude of pseudo standard parallel\",78.5,") !=
 1269         std::string::npos)
 1270         << wkt2;
 1271     EXPECT_TRUE(
 1272         wkt2.find("PARAMETER[\"Co-latitude of cone axis\",30.2881397222222,") !=
 1273         std::string::npos)
 1274         << wkt2;
 1275     EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()),
 1276               expectedPROJString);
 1277 
 1278     obj = PROJStringParser().createFromPROJString(
 1279         "+type=crs +proj=pipeline +step +proj=unitconvert +xy_in=deg "
 1280         "+xy_out=rad "
 1281         "+step +proj=krovak +lat_0=49.5 "
 1282         "+lon_0=24.8333333333333 +alpha=30.2881397222222 "
 1283         "+k=0.9999 +x_0=0 +y_0=0 +ellps=bessel "
 1284         "+step +proj=axisswap +order=-2,-1");
 1285     crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1286     ASSERT_TRUE(crs != nullptr);
 1287     wkt2 = crs->exportToWKT(WKTFormatter::create().get());
 1288     EXPECT_TRUE(wkt2.find("METHOD[\"Krovak\"") != std::string::npos) << wkt2;
 1289 }
 1290 
 1291 // ---------------------------------------------------------------------------
 1292 
 1293 TEST(wkt_parse, wkt1_krovak_north_oriented) {
 1294     auto wkt =
 1295         "PROJCS[\"S-JTSK / Krovak East North\","
 1296         "    GEOGCS[\"S-JTSK\","
 1297         "        DATUM[\"System_Jednotne_Trigonometricke_Site_Katastralni\","
 1298         "            SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,"
 1299         "                AUTHORITY[\"EPSG\",\"7004\"]],"
 1300         "            AUTHORITY[\"EPSG\",\"6156\"]],"
 1301         "        PRIMEM[\"Greenwich\",0,"
 1302         "            AUTHORITY[\"EPSG\",\"8901\"]],"
 1303         "        UNIT[\"degree\",0.0174532925199433,"
 1304         "            AUTHORITY[\"EPSG\",\"9122\"]],"
 1305         "        AUTHORITY[\"EPSG\",\"4156\"]],"
 1306         "    PROJECTION[\"Krovak\"],"
 1307         "    PARAMETER[\"latitude_of_center\",49.5],"
 1308         "    PARAMETER[\"longitude_of_center\",24.83333333333333],"
 1309         "    PARAMETER[\"azimuth\",30.28813972222222],"
 1310         "    PARAMETER[\"pseudo_standard_parallel_1\",78.5],"
 1311         "    PARAMETER[\"scale_factor\",0.9999],"
 1312         "    PARAMETER[\"false_easting\",0],"
 1313         "    PARAMETER[\"false_northing\",0],"
 1314         "    UNIT[\"metre\",1,"
 1315         "        AUTHORITY[\"EPSG\",\"9001\"]],"
 1316         "    AXIS[\"X\",EAST],"
 1317         "    AXIS[\"Y\",NORTH],"
 1318         "    AUTHORITY[\"EPSG\",\"5514\"]]";
 1319 
 1320     auto obj = WKTParser().createFromWKT(wkt);
 1321     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1322     ASSERT_TRUE(crs != nullptr);
 1323 
 1324     EXPECT_EQ(crs->derivingConversion()->method()->nameStr(),
 1325               "Krovak (North Orientated)");
 1326 
 1327     EXPECT_EQ(
 1328         crs->exportToWKT(WKTFormatter::create().get()),
 1329         "PROJCRS[\"S-JTSK / Krovak East North\",\n"
 1330         "    BASEGEODCRS[\"S-JTSK\",\n"
 1331         "        DATUM[\"System_Jednotne_Trigonometricke_Site_Katastralni\",\n"
 1332         "            ELLIPSOID[\"Bessel 1841\",6377397.155,299.1528128,\n"
 1333         "                LENGTHUNIT[\"metre\",1]]],\n"
 1334         "        PRIMEM[\"Greenwich\",0,\n"
 1335         "            ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 1336         "    CONVERSION[\"unnamed\",\n"
 1337         "        METHOD[\"Krovak (North Orientated)\",\n"
 1338         "            ID[\"EPSG\",1041]],\n"
 1339         "        PARAMETER[\"Latitude of projection centre\",49.5,\n"
 1340         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 1341         "            ID[\"EPSG\",8811]],\n"
 1342         "        PARAMETER[\"Longitude of origin\",24.8333333333333,\n"
 1343         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 1344         "            ID[\"EPSG\",8833]],\n"
 1345         "        PARAMETER[\"Co-latitude of cone axis\",30.2881397222222,\n"
 1346         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 1347         "            ID[\"EPSG\",1036]],\n"
 1348         "        PARAMETER[\"Latitude of pseudo standard parallel\",78.5,\n"
 1349         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 1350         "            ID[\"EPSG\",8818]],\n"
 1351         "        PARAMETER[\"Scale factor on pseudo standard "
 1352         "parallel\",0.9999,\n"
 1353         "            SCALEUNIT[\"unity\",1],\n"
 1354         "            ID[\"EPSG\",8819]],\n"
 1355         "        PARAMETER[\"False easting\",0,\n"
 1356         "            LENGTHUNIT[\"metre\",1],\n"
 1357         "            ID[\"EPSG\",8806]],\n"
 1358         "        PARAMETER[\"False northing\",0,\n"
 1359         "            LENGTHUNIT[\"metre\",1],\n"
 1360         "            ID[\"EPSG\",8807]]],\n"
 1361         "    CS[Cartesian,2],\n"
 1362         "        AXIS[\"x\",east,\n"
 1363         "            ORDER[1],\n"
 1364         "            LENGTHUNIT[\"metre\",1]],\n"
 1365         "        AXIS[\"y\",north,\n"
 1366         "            ORDER[2],\n"
 1367         "            LENGTHUNIT[\"metre\",1]],\n"
 1368         "    ID[\"EPSG\",5514]]");
 1369 
 1370     EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()),
 1371               "+proj=krovak +lat_0=49.5 +lon_0=24.8333333333333 "
 1372               "+alpha=30.2881397222222 +k=0.9999 +x_0=0 +y_0=0 +ellps=bessel "
 1373               "+units=m +no_defs +type=crs");
 1374 }
 1375 
 1376 // ---------------------------------------------------------------------------
 1377 
 1378 TEST(wkt_parse, wkt1_polar_stereographic_latitude_of_origin_70) {
 1379     auto wkt = "PROJCS[\"unknown\",\n"
 1380                "    GEOGCS[\"unknown\",\n"
 1381                "        DATUM[\"WGS_1984\",\n"
 1382                "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 1383                "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
 1384                "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
 1385                "        PRIMEM[\"Greenwich\",0,\n"
 1386                "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
 1387                "        UNIT[\"degree\",0.0174532925199433,\n"
 1388                "            AUTHORITY[\"EPSG\",\"9122\"]]],\n"
 1389                "    PROJECTION[\"Polar_Stereographic\"],\n"
 1390                "    PARAMETER[\"latitude_of_origin\",70],\n"
 1391                "    PARAMETER[\"central_meridian\",2],\n"
 1392                "    PARAMETER[\"false_easting\",3],\n"
 1393                "    PARAMETER[\"false_northing\",4],\n"
 1394                "    UNIT[\"metre\",1,\n"
 1395                "        AUTHORITY[\"EPSG\",\"9001\"]]]";
 1396 
 1397     auto obj = WKTParser().createFromWKT(wkt);
 1398     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1399     ASSERT_TRUE(crs != nullptr);
 1400 
 1401     auto projString = crs->exportToPROJString(
 1402         PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
 1403             .get());
 1404     auto expectedPROJString =
 1405         "+proj=stere +lat_0=90 +lat_ts=70 +lon_0=2 "
 1406         "+x_0=3 +y_0=4 +datum=WGS84 +units=m +no_defs +type=crs";
 1407     EXPECT_EQ(projString, expectedPROJString);
 1408 
 1409     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->nameStr(), "Easting");
 1410     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->direction(),
 1411               AxisDirection::SOUTH);
 1412     EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->nameStr(), "Northing");
 1413     EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->direction(),
 1414               AxisDirection::SOUTH);
 1415 }
 1416 
 1417 // ---------------------------------------------------------------------------
 1418 
 1419 TEST(wkt_parse, wkt1_polar_stereographic_latitude_of_origin_minus_70) {
 1420     auto wkt = "PROJCS[\"unknown\",\n"
 1421                "    GEOGCS[\"unknown\",\n"
 1422                "        DATUM[\"WGS_1984\",\n"
 1423                "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 1424                "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
 1425                "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
 1426                "        PRIMEM[\"Greenwich\",0,\n"
 1427                "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
 1428                "        UNIT[\"degree\",0.0174532925199433,\n"
 1429                "            AUTHORITY[\"EPSG\",\"9122\"]]],\n"
 1430                "    PROJECTION[\"Polar_Stereographic\"],\n"
 1431                "    PARAMETER[\"latitude_of_origin\",-70],\n"
 1432                "    PARAMETER[\"central_meridian\",2],\n"
 1433                "    PARAMETER[\"false_easting\",3],\n"
 1434                "    PARAMETER[\"false_northing\",4],\n"
 1435                "    UNIT[\"metre\",1,\n"
 1436                "        AUTHORITY[\"EPSG\",\"9001\"]]]";
 1437 
 1438     auto obj = WKTParser().createFromWKT(wkt);
 1439     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1440     ASSERT_TRUE(crs != nullptr);
 1441 
 1442     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->nameStr(), "Easting");
 1443     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->direction(),
 1444               AxisDirection::NORTH);
 1445     EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->nameStr(), "Northing");
 1446     EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->direction(),
 1447               AxisDirection::NORTH);
 1448 }
 1449 
 1450 // ---------------------------------------------------------------------------
 1451 
 1452 TEST(wkt_parse, wkt1_polar_stereographic_latitude_of_origin_90) {
 1453     auto wkt = "PROJCS[\"unknown\",\n"
 1454                "    GEOGCS[\"unknown\",\n"
 1455                "        DATUM[\"WGS_1984\",\n"
 1456                "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 1457                "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
 1458                "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
 1459                "        PRIMEM[\"Greenwich\",0,\n"
 1460                "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
 1461                "        UNIT[\"degree\",0.0174532925199433,\n"
 1462                "            AUTHORITY[\"EPSG\",\"9122\"]]],\n"
 1463                "    PROJECTION[\"Polar_Stereographic\"],\n"
 1464                "    PARAMETER[\"latitude_of_origin\",90],\n"
 1465                "    PARAMETER[\"central_meridian\",2],\n"
 1466                "    PARAMETER[\"false_easting\",3],\n"
 1467                "    PARAMETER[\"false_northing\",4],\n"
 1468                "    UNIT[\"metre\",1,\n"
 1469                "        AUTHORITY[\"EPSG\",\"9001\"]]]";
 1470 
 1471     auto obj = WKTParser().createFromWKT(wkt);
 1472     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1473     ASSERT_TRUE(crs != nullptr);
 1474 
 1475     auto projString = crs->exportToPROJString(
 1476         PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
 1477             .get());
 1478     auto expectedPROJString =
 1479         "+proj=stere +lat_0=90 +lat_ts=90 +lon_0=2 "
 1480         "+x_0=3 +y_0=4 +datum=WGS84 +units=m +no_defs +type=crs";
 1481     EXPECT_EQ(projString, expectedPROJString);
 1482 }
 1483 
 1484 // ---------------------------------------------------------------------------
 1485 
 1486 TEST(wkt_parse, wkt1_polar_stereographic_latitude_of_origin_90_scale_factor_1) {
 1487     auto wkt = "PROJCS[\"unknown\",\n"
 1488                "    GEOGCS[\"unknown\",\n"
 1489                "        DATUM[\"WGS_1984\",\n"
 1490                "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 1491                "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
 1492                "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
 1493                "        PRIMEM[\"Greenwich\",0,\n"
 1494                "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
 1495                "        UNIT[\"degree\",0.0174532925199433,\n"
 1496                "            AUTHORITY[\"EPSG\",\"9122\"]]],\n"
 1497                "    PROJECTION[\"Polar_Stereographic\"],\n"
 1498                "    PARAMETER[\"latitude_of_origin\",90],\n"
 1499                "    PARAMETER[\"central_meridian\",2],\n"
 1500                "    PARAMETER[\"scale_factor\",1],\n"
 1501                "    PARAMETER[\"false_easting\",3],\n"
 1502                "    PARAMETER[\"false_northing\",4],\n"
 1503                "    UNIT[\"metre\",1,\n"
 1504                "        AUTHORITY[\"EPSG\",\"9001\"]]]";
 1505 
 1506     auto obj = WKTParser().createFromWKT(wkt);
 1507     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1508     ASSERT_TRUE(crs != nullptr);
 1509 
 1510     auto projString = crs->exportToPROJString(
 1511         PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
 1512             .get());
 1513     auto expectedPROJString =
 1514         "+proj=stere +lat_0=90 +lat_ts=90 +lon_0=2 "
 1515         "+x_0=3 +y_0=4 +datum=WGS84 +units=m +no_defs +type=crs";
 1516     EXPECT_EQ(projString, expectedPROJString);
 1517 }
 1518 
 1519 // ---------------------------------------------------------------------------
 1520 
 1521 TEST(wkt_parse, wkt1_polar_stereographic_scale_factor) {
 1522     auto wkt = "PROJCS[\"unknown\",\n"
 1523                "    GEOGCS[\"unknown\",\n"
 1524                "        DATUM[\"WGS_1984\",\n"
 1525                "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 1526                "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
 1527                "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
 1528                "        PRIMEM[\"Greenwich\",0,\n"
 1529                "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
 1530                "        UNIT[\"degree\",0.0174532925199433,\n"
 1531                "            AUTHORITY[\"EPSG\",\"9122\"]]],\n"
 1532                "    PROJECTION[\"Polar_Stereographic\"],\n"
 1533                "    PARAMETER[\"latitude_of_origin\",90],\n"
 1534                "    PARAMETER[\"central_meridian\",2],\n"
 1535                "    PARAMETER[\"scale_factor\",0.99],\n"
 1536                "    PARAMETER[\"false_easting\",3],\n"
 1537                "    PARAMETER[\"false_northing\",4],\n"
 1538                "    UNIT[\"metre\",1,\n"
 1539                "        AUTHORITY[\"EPSG\",\"9001\"]]]";
 1540 
 1541     auto obj = WKTParser().createFromWKT(wkt);
 1542     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1543     ASSERT_TRUE(crs != nullptr);
 1544 
 1545     auto projString = crs->exportToPROJString(
 1546         PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
 1547             .get());
 1548     auto expectedPROJString = "+proj=stere +lat_0=90 +lon_0=2 +k=0.99 +x_0=3 "
 1549                               "+y_0=4 +datum=WGS84 +units=m +no_defs +type=crs";
 1550     EXPECT_EQ(projString, expectedPROJString);
 1551 }
 1552 
 1553 // ---------------------------------------------------------------------------
 1554 
 1555 TEST(wkt_parse, wkt1_Spherical_Cross_Track_Height) {
 1556     auto wkt = "PROJCS[\"unknown\",\n"
 1557                "    GEOGCS[\"unknown\",\n"
 1558                "        DATUM[\"WGS_1984\",\n"
 1559                "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 1560                "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
 1561                "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
 1562                "        PRIMEM[\"Greenwich\",0,\n"
 1563                "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
 1564                "        UNIT[\"degree\",0.0174532925199433,\n"
 1565                "            AUTHORITY[\"EPSG\",\"9122\"]]],\n"
 1566                "    PROJECTION[\"Spherical_Cross_Track_Height\"],\n"
 1567                "    PARAMETER[\"peg_point_latitude\",1],\n"
 1568                "    PARAMETER[\"peg_point_longitude\",2],\n"
 1569                "    PARAMETER[\"peg_point_heading\",3],\n"
 1570                "    PARAMETER[\"peg_point_height\",4],\n"
 1571                "    UNIT[\"metre\",1,\n"
 1572                "        AUTHORITY[\"EPSG\",\"9001\"]]]";
 1573 
 1574     auto obj = WKTParser().createFromWKT(wkt);
 1575     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1576     ASSERT_TRUE(crs != nullptr);
 1577 
 1578     auto projString = crs->exportToPROJString(
 1579         PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
 1580             .get());
 1581     auto expectedPROJString = "+proj=sch +plat_0=1 +plon_0=2 +phdg_0=3 +h_0=4 "
 1582                               "+datum=WGS84 +units=m +no_defs +type=crs";
 1583     EXPECT_EQ(projString, expectedPROJString);
 1584 }
 1585 
 1586 // ---------------------------------------------------------------------------
 1587 
 1588 TEST(wkt_parse, wkt2_projected) {
 1589     auto wkt = "PROJCRS[\"WGS 84 / UTM zone 31N\",\n"
 1590                "    BASEGEODCRS[\"WGS 84\",\n"
 1591                "        DATUM[\"World Geodetic System 1984\",\n"
 1592                "            ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 1593                "                LENGTHUNIT[\"metre\",1,\n"
 1594                "                    ID[\"EPSG\",9001]],\n"
 1595                "                ID[\"EPSG\",7030]],\n"
 1596                "            ID[\"EPSG\",6326]],\n"
 1597                "        PRIMEM[\"Greenwich\",0,\n"
 1598                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 1599                "                ID[\"EPSG\",9122]],\n"
 1600                "            ID[\"EPSG\",8901]]],\n"
 1601                "    CONVERSION[\"UTM zone 31N\",\n"
 1602                "        METHOD[\"Transverse Mercator\",\n"
 1603                "            ID[\"EPSG\",9807]],\n"
 1604                "        PARAMETER[\"Latitude of natural origin\",0,\n"
 1605                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 1606                "                ID[\"EPSG\",9122]],\n"
 1607                "            ID[\"EPSG\",8801]],\n"
 1608                "        PARAMETER[\"Longitude of natural origin\",3,\n"
 1609                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 1610                "                ID[\"EPSG\",9122]],\n"
 1611                "            ID[\"EPSG\",8802]],\n"
 1612                "        PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 1613                "            SCALEUNIT[\"unity\",1,\n"
 1614                "                ID[\"EPSG\",9201]],\n"
 1615                "            ID[\"EPSG\",8805]],\n"
 1616                "        PARAMETER[\"False easting\",500000,\n"
 1617                "            LENGTHUNIT[\"metre\",1,\n"
 1618                "                ID[\"EPSG\",9001]],\n"
 1619                "            ID[\"EPSG\",8806]],\n"
 1620                "        PARAMETER[\"False northing\",0,\n"
 1621                "            LENGTHUNIT[\"metre\",1,\n"
 1622                "                ID[\"EPSG\",9001]],\n"
 1623                "            ID[\"EPSG\",8807]],\n"
 1624                "        ID[\"EPSG\",16031]],\n"
 1625                "    CS[Cartesian,2],\n"
 1626                "        AXIS[\"(E)\",east,\n"
 1627                "            ORDER[1],\n"
 1628                "            LENGTHUNIT[\"metre\",1,\n"
 1629                "                ID[\"EPSG\",9001]]],\n"
 1630                "        AXIS[\"(N)\",north,\n"
 1631                "            ORDER[2],\n"
 1632                "            LENGTHUNIT[\"metre\",1,\n"
 1633                "                ID[\"EPSG\",9001]]],\n"
 1634                "    ID[\"EPSG\",32631]]";
 1635     auto obj = WKTParser().createFromWKT(wkt);
 1636     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1637     ASSERT_TRUE(crs != nullptr);
 1638     checkProjected(crs, /*checkEPSGCodes = */ false);
 1639 }
 1640 
 1641 // ---------------------------------------------------------------------------
 1642 
 1643 TEST(wkt_parse, wkt2_2018_projected_with_id_in_basegeodcrs) {
 1644     auto wkt = "PROJCRS[\"WGS 84 / UTM zone 31N\",\n"
 1645                "    BASEGEOGCRS[\"WGS 84\",\n"
 1646                "        DATUM[\"World Geodetic System 1984\",\n"
 1647                "            ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
 1648                "        ID[\"EPSG\",4326]],\n"
 1649                "    CONVERSION[\"UTM zone 31N\",\n"
 1650                "        METHOD[\"Transverse Mercator\"],\n"
 1651                "        PARAMETER[\"Latitude of natural origin\",0],\n"
 1652                "        PARAMETER[\"Longitude of natural origin\",3],\n"
 1653                "        PARAMETER[\"Scale factor at natural origin\",0.9996],\n"
 1654                "        PARAMETER[\"False easting\",500000],\n"
 1655                "        PARAMETER[\"False northing\",0]],\n"
 1656                "    CS[Cartesian,2],\n"
 1657                "        AXIS[\"(E)\",east],\n"
 1658                "        AXIS[\"(N)\",north],\n"
 1659                "        UNIT[\"metre\",1],\n"
 1660                "    ID[\"EPSG\",32631]]";
 1661     auto obj = WKTParser().createFromWKT(wkt);
 1662     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1663     ASSERT_TRUE(crs != nullptr);
 1664     ASSERT_EQ(crs->baseCRS()->identifiers().size(), 1U);
 1665     EXPECT_EQ(crs->baseCRS()->identifiers().front()->code(), "4326");
 1666 
 1667     {
 1668         auto got_wkt = crs->exportToWKT(
 1669             WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get());
 1670         EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") != std::string::npos)
 1671             << got_wkt;
 1672     }
 1673 
 1674     {
 1675         auto got_wkt = crs->exportToWKT(
 1676             WKTFormatter::create(WKTFormatter::Convention::WKT2_2018_SIMPLIFIED)
 1677                 .get());
 1678         EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") == std::string::npos)
 1679             << got_wkt;
 1680     }
 1681 }
 1682 
 1683 // ---------------------------------------------------------------------------
 1684 
 1685 TEST(wkt_parse, wkt2_2018_projected_no_id_but_id_in_basegeodcrs) {
 1686     auto wkt = "PROJCRS[\"WGS 84 / UTM zone 31N\",\n"
 1687                "    BASEGEOGCRS[\"WGS 84\",\n"
 1688                "        DATUM[\"World Geodetic System 1984\",\n"
 1689                "            ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
 1690                "        ID[\"EPSG\",4326]],\n"
 1691                "    CONVERSION[\"UTM zone 31N\",\n"
 1692                "        METHOD[\"Transverse Mercator\"],\n"
 1693                "        PARAMETER[\"Latitude of natural origin\",0],\n"
 1694                "        PARAMETER[\"Longitude of natural origin\",3],\n"
 1695                "        PARAMETER[\"Scale factor at natural origin\",0.9996],\n"
 1696                "        PARAMETER[\"False easting\",500000],\n"
 1697                "        PARAMETER[\"False northing\",0]],\n"
 1698                "    CS[Cartesian,2],\n"
 1699                "        AXIS[\"(E)\",east],\n"
 1700                "        AXIS[\"(N)\",north],\n"
 1701                "        UNIT[\"metre\",1]]";
 1702     auto obj = WKTParser().createFromWKT(wkt);
 1703     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1704     ASSERT_TRUE(crs != nullptr);
 1705 
 1706     auto got_wkt = crs->exportToWKT(
 1707         WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get());
 1708     EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") != std::string::npos)
 1709         << got_wkt;
 1710 }
 1711 
 1712 // ---------------------------------------------------------------------------
 1713 
 1714 TEST(wkt_parse, wkt2_2018_simplified_projected) {
 1715     auto wkt = "PROJCRS[\"WGS 84 / UTM zone 31N\",\n"
 1716                "    BASEGEOGCRS[\"WGS 84\",\n"
 1717                "        DATUM[\"World Geodetic System 1984\",\n"
 1718                "            ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
 1719                "        UNIT[\"degree\",0.0174532925199433]],\n"
 1720                "    CONVERSION[\"UTM zone 31N\",\n"
 1721                "        METHOD[\"Transverse Mercator\"],\n"
 1722                "        PARAMETER[\"Latitude of natural origin\",0],\n"
 1723                "        PARAMETER[\"Longitude of natural origin\",3],\n"
 1724                "        PARAMETER[\"Scale factor at natural origin\",0.9996],\n"
 1725                "        PARAMETER[\"False easting\",500000],\n"
 1726                "        PARAMETER[\"False northing\",0]],\n"
 1727                "    CS[Cartesian,2],\n"
 1728                "        AXIS[\"(E)\",east],\n"
 1729                "        AXIS[\"(N)\",north],\n"
 1730                "        UNIT[\"metre\",1],\n"
 1731                "    ID[\"EPSG\",32631]]";
 1732     auto obj = WKTParser().createFromWKT(wkt);
 1733     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1734     ASSERT_TRUE(crs != nullptr);
 1735     checkProjected(crs, /*checkEPSGCodes = */ false);
 1736 }
 1737 
 1738 // ---------------------------------------------------------------------------
 1739 
 1740 TEST(wkt_parse, wkt2_2018_projected_3D) {
 1741     auto wkt =
 1742         "PROJCRS[\"WGS 84 (G1762) / UTM zone 31N 3D\","
 1743         "    BASEGEOGCRS[\"WGS 84\","
 1744         "        DATUM[\"World Geodetic System of 1984 (G1762)\","
 1745         "        ELLIPSOID[\"WGS 84\",6378137,298.257223563,"
 1746         "           LENGTHUNIT[\"metre\",1.0]]]],"
 1747         "    CONVERSION[\"Some conversion 3D\","
 1748         "        METHOD[\"Transverse Mercator (3D)\"],"
 1749         "        PARAMETER[\"Latitude of origin\",0.0,"
 1750         "            ANGLEUNIT[\"degree\",0.0174532925199433]],"
 1751         "        PARAMETER[\"Longitude of origin\",3.0,"
 1752         "            ANGLEUNIT[\"degree\",0.0174532925199433]],"
 1753         "        PARAMETER[\"Scale factor\",1,SCALEUNIT[\"unity\",1.0]],"
 1754         "        PARAMETER[\"False easting\",0.0,"
 1755         "           LENGTHUNIT[\"metre\",1.0]],"
 1756         "        PARAMETER[\"False northing\",0.0,LENGTHUNIT[\"metre\",1.0]]],"
 1757         "        CS[Cartesian,3],"
 1758         "            AXIS[\"(E)\",east,ORDER[1]],"
 1759         "            AXIS[\"(N)\",north,ORDER[2]],"
 1760         "            AXIS[\"ellipsoidal height (h)\",up,ORDER[3]],"
 1761         "            LENGTHUNIT[\"metre\",1.0]"
 1762         "]";
 1763     auto obj = WKTParser().createFromWKT(wkt);
 1764     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1765     ASSERT_TRUE(crs != nullptr);
 1766 
 1767     EXPECT_EQ(
 1768         crs->exportToPROJString(
 1769             PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
 1770                 .get()),
 1771         "+proj=tmerc +lat_0=0 +lon_0=3 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 "
 1772         "+units=m +no_defs +type=crs");
 1773 
 1774     EXPECT_THROW(
 1775         crs->exportToWKT(
 1776             WKTFormatter::create(WKTFormatter::Convention::WKT2_2015).get()),
 1777         FormattingException);
 1778 
 1779     EXPECT_NO_THROW(crs->exportToWKT(
 1780         WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()));
 1781 }
 1782 
 1783 // ---------------------------------------------------------------------------
 1784 
 1785 TEST(wkt_parse, wkt2_2018_projected_utm_3D) {
 1786     // Example from WKT2:2018
 1787     auto wkt =
 1788         "PROJCRS[\"WGS 84 (G1762) / UTM zone 31N 3D\","
 1789         "    BASEGEOGCRS[\"WGS 84\","
 1790         "        DATUM[\"World Geodetic System of 1984 (G1762)\","
 1791         "        ELLIPSOID[\"WGS 84\",6378137,298.257223563,"
 1792         "           LENGTHUNIT[\"metre\",1.0]]]],"
 1793         "    CONVERSION[\"UTM zone 31N 3D\","
 1794         "        METHOD[\"Transverse Mercator (3D)\"],"
 1795         "        PARAMETER[\"Latitude of origin\",0.0,"
 1796         "            ANGLEUNIT[\"degree\",0.0174532925199433]],"
 1797         "        PARAMETER[\"Longitude of origin\",3.0,"
 1798         "            ANGLEUNIT[\"degree\",0.0174532925199433]],"
 1799         "        PARAMETER[\"Scale factor\",0.9996,SCALEUNIT[\"unity\",1.0]],"
 1800         "        PARAMETER[\"False easting\",500000.0,"
 1801         "           LENGTHUNIT[\"metre\",1.0]],"
 1802         "        PARAMETER[\"False northing\",0.0,LENGTHUNIT[\"metre\",1.0]]],"
 1803         "        CS[Cartesian,3],"
 1804         "            AXIS[\"(E)\",east,ORDER[1]],"
 1805         "            AXIS[\"(N)\",north,ORDER[2]],"
 1806         "            AXIS[\"ellipsoidal height (h)\",up,ORDER[3]],"
 1807         "            LENGTHUNIT[\"metre\",1.0]"
 1808         "]";
 1809     auto obj = WKTParser().createFromWKT(wkt);
 1810     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1811     ASSERT_TRUE(crs != nullptr);
 1812 
 1813     EXPECT_EQ(
 1814         crs->exportToPROJString(
 1815             PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
 1816                 .get()),
 1817         "+proj=utm +zone=31 +ellps=WGS84 +units=m +no_defs +type=crs");
 1818 
 1819     EXPECT_THROW(
 1820         crs->exportToWKT(
 1821             WKTFormatter::create(WKTFormatter::Convention::WKT2_2015).get()),
 1822         FormattingException);
 1823 
 1824     EXPECT_NO_THROW(crs->exportToWKT(
 1825         WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()));
 1826 }
 1827 
 1828 // ---------------------------------------------------------------------------
 1829 
 1830 TEST(crs, projected_angular_unit_from_primem) {
 1831     auto obj = WKTParser().createFromWKT(
 1832         "PROJCRS[\"NTF (Paris) / Lambert Nord France\",\n"
 1833         "  BASEGEODCRS[\"NTF (Paris)\",\n"
 1834         "    DATUM[\"Nouvelle Triangulation Francaise (Paris)\",\n"
 1835         "      ELLIPSOID[\"Clarke 1880 "
 1836         "(IGN)\",6378249.2,293.4660213,LENGTHUNIT[\"metre\",1.0]]],\n"
 1837         "    PRIMEM[\"Paris\",2.5969213,ANGLEUNIT[\"grad\",0.015707963268]]],\n"
 1838         "  CONVERSION[\"Lambert Nord France\",\n"
 1839         "    METHOD[\"Lambert Conic Conformal (1SP)\",ID[\"EPSG\",9801]],\n"
 1840         "    PARAMETER[\"Latitude of natural "
 1841         "origin\",55,ANGLEUNIT[\"grad\",0.015707963268]],\n"
 1842         "    PARAMETER[\"Longitude of natural "
 1843         "origin\",0,ANGLEUNIT[\"grad\",0.015707963268]],\n"
 1844         "    PARAMETER[\"Scale factor at natural "
 1845         "origin\",0.999877341,SCALEUNIT[\"unity\",1.0]],\n"
 1846         "    PARAMETER[\"False easting\",600000,LENGTHUNIT[\"metre\",1.0]],\n"
 1847         "    PARAMETER[\"False northing\",200000,LENGTHUNIT[\"metre\",1.0]]],\n"
 1848         "  CS[cartesian,2],\n"
 1849         "    AXIS[\"easting (X)\",east,ORDER[1]],\n"
 1850         "    AXIS[\"northing (Y)\",north,ORDER[2]],\n"
 1851         "    LENGTHUNIT[\"metre\",1.0],\n"
 1852         "  ID[\"EPSG\",27561]]");
 1853     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1854     ASSERT_TRUE(crs != nullptr);
 1855     EXPECT_EQ(crs->baseCRS()->coordinateSystem()->axisList()[0]->unit(),
 1856               UnitOfMeasure::GRAD);
 1857 }
 1858 
 1859 // ---------------------------------------------------------------------------
 1860 
 1861 TEST(wkt_parse, cs_with_MERIDIAN) {
 1862     auto wkt =
 1863         "PROJCRS[\"dummy\",\n"
 1864         "    BASEGEOGCRS[\"WGS 84\",\n"
 1865         "        DATUM[\"World Geodetic System 1984\",\n"
 1866         "            ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
 1867         "        UNIT[\"degree\",0.0174532925199433]],\n"
 1868         "    CONVERSION[\"dummy\",\n"
 1869         "        METHOD[\"dummy\"],\n"
 1870         "        PARAMETER[\"dummy\",1.0]],\n"
 1871         "    CS[Cartesian,2],\n"
 1872         "        AXIS[\"easting "
 1873         "(X)\",south,MERIDIAN[90,ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 1874         "        AXIS[\"northing (Y)\",north],\n"
 1875         "        UNIT[\"metre\",1],\n"
 1876         "    ID[\"EPSG\",32631]]";
 1877     auto obj = WKTParser().createFromWKT(wkt);
 1878     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
 1879     ASSERT_TRUE(crs != nullptr);
 1880     ASSERT_EQ(crs->coordinateSystem()->axisList().size(), 2U);
 1881     auto axis = crs->coordinateSystem()->axisList()[0];
 1882     auto meridian = axis->meridian();
 1883     ASSERT_TRUE(meridian != nullptr);
 1884     EXPECT_EQ(meridian->longitude().value(), 90.0);
 1885     EXPECT_EQ(meridian->longitude().unit(), UnitOfMeasure::DEGREE);
 1886     ASSERT_TRUE(crs->coordinateSystem()->axisList()[1]->meridian() == nullptr);
 1887 }
 1888 
 1889 // ---------------------------------------------------------------------------
 1890 
 1891 TEST(wkt_parse, cs_with_multiple_ID) {
 1892     auto wkt = "GEODCRS[\"WGS 84\",\n"
 1893                "    DATUM[\"World Geodetic System 1984\",\n"
 1894                "        ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
 1895                "    CS[Cartesian,3],\n"
 1896                "        AXIS[\"(X)\",geocentricX],\n"
 1897                "        AXIS[\"(Y)\",geocentricY],\n"
 1898                "        AXIS[\"(Z)\",geocentricZ],\n"
 1899                "        UNIT[\"metre\",1],\n"
 1900                "    ID[\"authorityA\",\"codeA\"],\n"
 1901                "    ID[\"authorityB\",\"codeB\"]]";
 1902 
 1903     auto obj = WKTParser().createFromWKT(wkt);
 1904     auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
 1905     ASSERT_TRUE(crs != nullptr);
 1906     EXPECT_EQ(crs->nameStr(), "WGS 84");
 1907     ASSERT_EQ(crs->identifiers().size(), 2U);
 1908     EXPECT_EQ(crs->identifiers()[0]->code(), "codeA");
 1909     EXPECT_EQ(*(crs->identifiers()[0]->codeSpace()), "authorityA");
 1910     EXPECT_EQ(crs->identifiers()[1]->code(), "codeB");
 1911     EXPECT_EQ(*(crs->identifiers()[1]->codeSpace()), "authorityB");
 1912 }
 1913 
 1914 // ---------------------------------------------------------------------------
 1915 
 1916 TEST(wkt_parse, vertcrs_WKT2) {
 1917     auto wkt = "VERTCRS[\"ODN height\",\n"
 1918                "    VDATUM[\"Ordnance Datum Newlyn\"],\n"
 1919                "    CS[vertical,1],\n"
 1920                "        AXIS[\"gravity-related height (H)\",up,\n"
 1921                "            LENGTHUNIT[\"metre\",1]],\n"
 1922                "    ID[\"EPSG\",5701]]";
 1923 
 1924     auto obj = WKTParser().createFromWKT(wkt);
 1925     auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj);
 1926     ASSERT_TRUE(crs != nullptr);
 1927     EXPECT_EQ(crs->nameStr(), "ODN height");
 1928     ASSERT_EQ(crs->identifiers().size(), 1U);
 1929     EXPECT_EQ(crs->identifiers()[0]->code(), "5701");
 1930     EXPECT_EQ(*(crs->identifiers()[0]->codeSpace()), "EPSG");
 1931 
 1932     auto datum = crs->datum();
 1933     EXPECT_EQ(datum->nameStr(), "Ordnance Datum Newlyn");
 1934     // ASSERT_EQ(datum->identifiers().size(), 1U);
 1935     // EXPECT_EQ(datum->identifiers()[0]->code(), "5101");
 1936     // EXPECT_EQ(*(datum->identifiers()[0]->codeSpace()), "EPSG");
 1937 
 1938     auto cs = crs->coordinateSystem();
 1939     ASSERT_EQ(cs->axisList().size(), 1U);
 1940     EXPECT_EQ(cs->axisList()[0]->nameStr(), "Gravity-related height");
 1941     EXPECT_EQ(cs->axisList()[0]->abbreviation(), "H");
 1942     EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::UP);
 1943 }
 1944 
 1945 // ---------------------------------------------------------------------------
 1946 
 1947 TEST(wkt_parse, vertcrs_VRF_WKT2) {
 1948     auto wkt = "VERTCRS[\"ODN height\",\n"
 1949                "    VRF[\"Ordnance Datum Newlyn\"],\n"
 1950                "    CS[vertical,1],\n"
 1951                "        AXIS[\"gravity-related height (H)\",up,\n"
 1952                "            LENGTHUNIT[\"metre\",1]],\n"
 1953                "    ID[\"EPSG\",5701]]";
 1954 
 1955     auto obj = WKTParser().createFromWKT(wkt);
 1956     auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj);
 1957     ASSERT_TRUE(crs != nullptr);
 1958 }
 1959 
 1960 // ---------------------------------------------------------------------------
 1961 
 1962 TEST(wkt_parse, vertcrs_with_GEOIDMODEL) {
 1963     auto wkt = "VERTCRS[\"CGVD2013\","
 1964                "    VRF[\"Canadian Geodetic Vertical Datum of 2013\"],"
 1965                "    CS[vertical,1],"
 1966                "        AXIS[\"gravity-related height (H)\",up],"
 1967                "        LENGTHUNIT[\"metre\",1.0],"
 1968                "    GEOIDMODEL[\"CGG2013\",ID[\"EPSG\",6648]]]";
 1969 
 1970     auto obj = WKTParser().createFromWKT(wkt);
 1971     auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj);
 1972     ASSERT_TRUE(crs != nullptr);
 1973 }
 1974 
 1975 // ---------------------------------------------------------------------------
 1976 
 1977 TEST(wkt_parse, vertcrs_WKT1_GDAL) {
 1978     auto wkt = "VERT_CS[\"ODN height\",\n"
 1979                "    VERT_DATUM[\"Ordnance Datum Newlyn\",2005,\n"
 1980                "        AUTHORITY[\"EPSG\",\"5101\"]],\n"
 1981                "    UNIT[\"metre\",1,\n"
 1982                "        AUTHORITY[\"EPSG\",\"9001\"]],\n"
 1983                "    AXIS[\"gravity-related height\",UP],\n"
 1984                "    AUTHORITY[\"EPSG\",\"5701\"]]";
 1985 
 1986     auto obj = WKTParser().createFromWKT(wkt);
 1987     auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj);
 1988     ASSERT_TRUE(crs != nullptr);
 1989     EXPECT_EQ(crs->nameStr(), "ODN height");
 1990     ASSERT_EQ(crs->identifiers().size(), 1U);
 1991     EXPECT_EQ(crs->identifiers()[0]->code(), "5701");
 1992     EXPECT_EQ(*(crs->identifiers()[0]->codeSpace()), "EPSG");
 1993 
 1994     auto datum = crs->datum();
 1995     EXPECT_EQ(datum->nameStr(), "Ordnance Datum Newlyn");
 1996     ASSERT_EQ(datum->identifiers().size(), 1U);
 1997     EXPECT_EQ(datum->identifiers()[0]->code(), "5101");
 1998     EXPECT_EQ(*(datum->identifiers()[0]->codeSpace()), "EPSG");
 1999 
 2000     auto cs = crs->coordinateSystem();
 2001     ASSERT_EQ(cs->axisList().size(), 1U);
 2002     EXPECT_EQ(cs->axisList()[0]->nameStr(), "Gravity-related height");
 2003     EXPECT_EQ(cs->axisList()[0]->abbreviation(), ""); // "H" in WKT2
 2004     EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::UP);
 2005 }
 2006 
 2007 // ---------------------------------------------------------------------------
 2008 
 2009 TEST(wkt_parse, vertcrs_WKT1_GDAL_minimum) {
 2010     auto wkt = "VERT_CS[\"ODN height\",\n"
 2011                "    VERT_DATUM[\"Ordnance Datum Newlyn\",2005],\n"
 2012                "    UNIT[\"metre\",1]]";
 2013 
 2014     auto obj = WKTParser().createFromWKT(wkt);
 2015     auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj);
 2016     EXPECT_EQ(crs->nameStr(), "ODN height");
 2017 
 2018     auto datum = crs->datum();
 2019     EXPECT_EQ(datum->nameStr(), "Ordnance Datum Newlyn");
 2020 
 2021     auto cs = crs->coordinateSystem();
 2022     ASSERT_EQ(cs->axisList().size(), 1U);
 2023     EXPECT_EQ(cs->axisList()[0]->nameStr(), "Gravity-related height");
 2024     EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::UP);
 2025 }
 2026 
 2027 // ---------------------------------------------------------------------------
 2028 
 2029 TEST(wkt_parse, dynamic_vertical_reference_frame) {
 2030     auto obj = WKTParser().createFromWKT(
 2031         "VERTCRS[\"RH2000\","
 2032         "  DYNAMIC[FRAMEEPOCH[2000.0],MODEL[\"NKG2016LU\"]],"
 2033         "  VDATUM[\"Rikets Hojdsystem 2000\",ANCHOR[\"my anchor\"]],"
 2034         "  CS[vertical,1],"
 2035         "    AXIS[\"gravity-related height (H)\",up],"
 2036         "    LENGTHUNIT[\"metre\",1.0]"
 2037         "]");
 2038     auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj);
 2039     ASSERT_TRUE(crs != nullptr);
 2040     auto dgrf =
 2041         std::dynamic_pointer_cast<DynamicVerticalReferenceFrame>(crs->datum());
 2042     ASSERT_TRUE(dgrf != nullptr);
 2043     auto anchor = dgrf->anchorDefinition();
 2044     EXPECT_TRUE(anchor.has_value());
 2045     EXPECT_EQ(*anchor, "my anchor");
 2046     EXPECT_TRUE(dgrf->frameReferenceEpoch() ==
 2047                 Measure(2000.0, UnitOfMeasure::YEAR));
 2048     auto model = dgrf->deformationModelName();
 2049     EXPECT_TRUE(model.has_value());
 2050     EXPECT_EQ(*model, "NKG2016LU");
 2051 }
 2052 
 2053 // ---------------------------------------------------------------------------
 2054 
 2055 TEST(wkt_parse, vertcrs_with_ensemble) {
 2056     auto obj = WKTParser().createFromWKT(
 2057         "VERTCRS[\"unnamed\",\n"
 2058         "    ENSEMBLE[\"unnamed\",\n"
 2059         "        MEMBER[\"vdatum1\"],\n"
 2060         "        MEMBER[\"vdatum2\"],\n"
 2061         "        ENSEMBLEACCURACY[100]],\n"
 2062         "    CS[vertical,1],\n"
 2063         "        AXIS[\"gravity-related height (H)\",up,\n"
 2064         "            LENGTHUNIT[\"metre\",1]]]");
 2065     auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj);
 2066     ASSERT_TRUE(crs != nullptr);
 2067     ASSERT_TRUE(crs->datum() == nullptr);
 2068     ASSERT_TRUE(crs->datumEnsemble() != nullptr);
 2069     EXPECT_EQ(crs->datumEnsemble()->datums().size(), 2U);
 2070 }
 2071 
 2072 // ---------------------------------------------------------------------------
 2073 
 2074 TEST(wkt_parse, vdatum_with_ANCHOR) {
 2075     auto obj = WKTParser().createFromWKT("VDATUM[\"Ordnance Datum Newlyn\",\n"
 2076                                          "    ANCHOR[\"my anchor\"],\n"
 2077                                          "    ID[\"EPSG\",5101]]");
 2078     auto datum = nn_dynamic_pointer_cast<VerticalReferenceFrame>(obj);
 2079     ASSERT_TRUE(datum != nullptr);
 2080     auto anchor = datum->anchorDefinition();
 2081     EXPECT_TRUE(anchor.has_value());
 2082     EXPECT_EQ(*anchor, "my anchor");
 2083 }
 2084 
 2085 // ---------------------------------------------------------------------------
 2086 
 2087 TEST(wkt_parse, COMPOUNDCRS) {
 2088     auto obj = WKTParser().createFromWKT(
 2089         "COMPOUNDCRS[\"horizontal + vertical\",\n"
 2090         "    PROJCRS[\"WGS 84 / UTM zone 31N\",\n"
 2091         "        BASEGEODCRS[\"WGS 84\",\n"
 2092         "            DATUM[\"World Geodetic System 1984\",\n"
 2093         "                ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2094         "                    LENGTHUNIT[\"metre\",1]]],\n"
 2095         "            PRIMEM[\"Greenwich\",0,\n"
 2096         "                ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2097         "        CONVERSION[\"UTM zone 31N\",\n"
 2098         "            METHOD[\"Transverse Mercator\",\n"
 2099         "                ID[\"EPSG\",9807]],\n"
 2100         "            PARAMETER[\"Latitude of natural origin\",0,\n"
 2101         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2102         "                ID[\"EPSG\",8801]],\n"
 2103         "            PARAMETER[\"Longitude of natural origin\",3,\n"
 2104         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2105         "                ID[\"EPSG\",8802]],\n"
 2106         "            PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2107         "                SCALEUNIT[\"unity\",1],\n"
 2108         "                ID[\"EPSG\",8805]],\n"
 2109         "            PARAMETER[\"False easting\",500000,\n"
 2110         "                LENGTHUNIT[\"metre\",1],\n"
 2111         "                ID[\"EPSG\",8806]],\n"
 2112         "            PARAMETER[\"False northing\",0,\n"
 2113         "                LENGTHUNIT[\"metre\",1],\n"
 2114         "                ID[\"EPSG\",8807]]],\n"
 2115         "        CS[Cartesian,2],\n"
 2116         "            AXIS[\"(E)\",east,\n"
 2117         "                ORDER[1],\n"
 2118         "                LENGTHUNIT[\"metre\",1]],\n"
 2119         "            AXIS[\"(N)\",north,\n"
 2120         "                ORDER[2],\n"
 2121         "                LENGTHUNIT[\"metre\",1]]],\n"
 2122         "    VERTCRS[\"ODN height\",\n"
 2123         "        VDATUM[\"Ordnance Datum Newlyn\"],\n"
 2124         "        CS[vertical,1],\n"
 2125         "            AXIS[\"gravity-related height (H)\",up,\n"
 2126         "                LENGTHUNIT[\"metre\",1]]],\n"
 2127         "    ID[\"codespace\",\"code\"]]");
 2128     auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
 2129     ASSERT_TRUE(crs != nullptr);
 2130     EXPECT_EQ(crs->nameStr(), "horizontal + vertical");
 2131     EXPECT_EQ(crs->componentReferenceSystems().size(), 2U);
 2132     ASSERT_EQ(crs->identifiers().size(), 1U);
 2133     EXPECT_EQ(crs->identifiers()[0]->code(), "code");
 2134     EXPECT_EQ(*(crs->identifiers()[0]->codeSpace()), "codespace");
 2135 }
 2136 
 2137 // ---------------------------------------------------------------------------
 2138 
 2139 TEST(wkt_parse, COMPOUNDCRS_spatio_parametric_2015) {
 2140     auto obj = WKTParser().createFromWKT(
 2141         "COMPOUNDCRS[\"ICAO layer 0\",\n"
 2142         "    GEODETICCRS[\"WGS 84\",\n"
 2143         "        DATUM[\"World Geodetic System 1984\",\n"
 2144         "            ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2145         "                LENGTHUNIT[\"metre\",1]]],\n"
 2146         "        PRIMEM[\"Greenwich\",0,\n"
 2147         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2148         "            ID[\"EPSG\",8901]],\n"
 2149         "        CS[ellipsoidal,2],\n"
 2150         "            AXIS[\"latitude\",north,\n"
 2151         "                ORDER[1],\n"
 2152         "                ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2153         "            AXIS[\"longitude\",east,\n"
 2154         "                ORDER[2],\n"
 2155         "                ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2156         "    PARAMETRICCRS[\"WMO standard atmosphere\",\n"
 2157         "        PARAMETRICDATUM[\"Mean Sea Level\",\n"
 2158         "            ANCHOR[\"Mean Sea Level = 1013.25 hPa\"]],\n"
 2159         "        CS[parametric,1],\n"
 2160         "            AXIS[\"pressure (P)\",unspecified,\n"
 2161         "                PARAMETRICUNIT[\"HectoPascal\",100]]]]");
 2162     auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
 2163     ASSERT_TRUE(crs != nullptr);
 2164 }
 2165 
 2166 // ---------------------------------------------------------------------------
 2167 
 2168 TEST(wkt_parse, COMPOUNDCRS_spatio_parametric_2018) {
 2169     auto obj = WKTParser().createFromWKT(
 2170         "COMPOUNDCRS[\"ICAO layer 0\",\n"
 2171         "    GEOGRAPHICCRS[\"WGS 84\",\n"
 2172         "    DYNAMIC[FRAMEEPOCH[2005]],\n"
 2173         "        DATUM[\"World Geodetic System 1984\",\n"
 2174         "            ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2175         "                LENGTHUNIT[\"metre\",1]]],\n"
 2176         "        PRIMEM[\"Greenwich\",0,\n"
 2177         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2178         "            ID[\"EPSG\",8901]],\n"
 2179         "        CS[ellipsoidal,2],\n"
 2180         "            AXIS[\"latitude\",north,\n"
 2181         "                ORDER[1],\n"
 2182         "                ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2183         "            AXIS[\"longitude\",east,\n"
 2184         "                ORDER[2],\n"
 2185         "                ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2186         "    PARAMETRICCRS[\"WMO standard atmosphere\",\n"
 2187         "        PARAMETRICDATUM[\"Mean Sea Level\",\n"
 2188         "            ANCHOR[\"Mean Sea Level = 1013.25 hPa\"]],\n"
 2189         "        CS[parametric,1],\n"
 2190         "            AXIS[\"pressure (P)\",unspecified,\n"
 2191         "                PARAMETRICUNIT[\"HectoPascal\",100]]]]");
 2192     auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
 2193     ASSERT_TRUE(crs != nullptr);
 2194 }
 2195 
 2196 // ---------------------------------------------------------------------------
 2197 
 2198 TEST(wkt_parse, COMPOUNDCRS_spatio_temporal_2015) {
 2199     auto obj = WKTParser().createFromWKT(
 2200         "COMPOUNDCRS[\"GPS position and time\",\n"
 2201         "    GEODCRS[\"WGS 84 (G1762)\",\n"
 2202         "        DATUM[\"World Geodetic System 1984 (G1762)\",\n"
 2203         "            ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2204         "                LENGTHUNIT[\"metre\",1,\n"
 2205         "                    ID[\"EPSG\",9001]]]],\n"
 2206         "        CS[ellipsoidal,2],\n"
 2207         "            AXIS[\"latitude\",north,\n"
 2208         "                ORDER[1],\n"
 2209         "                ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2210         "            AXIS[\"longitude\",east,\n"
 2211         "                ORDER[2],\n"
 2212         "                ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2213         "    TIMECRS[\"GPS Time\",\n"
 2214         "        TIMEDATUM[\"Time origin\",TIMEORIGIN[1980-01-01]],\n"
 2215         "        CS[temporal,1],\n"
 2216         "            AXIS[\"time (T)\",future]]]");
 2217     auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
 2218     ASSERT_TRUE(crs != nullptr);
 2219 }
 2220 
 2221 // ---------------------------------------------------------------------------
 2222 
 2223 TEST(wkt_parse, COMPOUNDCRS_spatio_temporal_2018) {
 2224     auto obj = WKTParser().createFromWKT(
 2225         "COMPOUNDCRS[\"2D GPS position with civil time in ISO 8601 format\",\n"
 2226         "    GEOGCRS[\"WGS 84 (G1762)\",\n"
 2227         "        DATUM[\"World Geodetic System 1984 (G1762)\",\n"
 2228         "            ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2229         "                LENGTHUNIT[\"metre\",1,\n"
 2230         "                    ID[\"EPSG\",9001]]]],\n"
 2231         "        CS[ellipsoidal,2],\n"
 2232         "            AXIS[\"latitude\",north,\n"
 2233         "                ORDER[1],\n"
 2234         "                ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2235         "            AXIS[\"longitude\",east,\n"
 2236         "                ORDER[2],\n"
 2237         "                ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2238         "    TIMECRS[\"DateTime\",\n"
 2239         "        TDATUM[\"Gregorian Calendar\"],\n"
 2240         "        CS[TemporalDateTime,1],\n"
 2241         "            AXIS[\"time (T)\",future]]]");
 2242     auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
 2243     ASSERT_TRUE(crs != nullptr);
 2244 }
 2245 
 2246 // ---------------------------------------------------------------------------
 2247 
 2248 TEST(wkt_parse, COMPD_CS) {
 2249     auto obj = WKTParser().createFromWKT(
 2250         "COMPD_CS[\"horizontal + vertical\",\n"
 2251         "    PROJCS[\"WGS 84 / UTM zone 31N\",\n"
 2252         "        GEOGCS[\"WGS 84\",\n"
 2253         "            DATUM[\"World Geodetic System 1984\",\n"
 2254         "                SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 2255         "                    AUTHORITY[\"EPSG\",\"7030\"]],\n"
 2256         "                AUTHORITY[\"EPSG\",\"6326\"]],\n"
 2257         "            PRIMEM[\"Greenwich\",0,\n"
 2258         "                AUTHORITY[\"EPSG\",\"8901\"]],\n"
 2259         "            UNIT[\"degree\",0.0174532925199433,\n"
 2260         "                AUTHORITY[\"EPSG\",\"9122\"]],\n"
 2261         "            AXIS[\"Latitude\",NORTH],\n"
 2262         "            AXIS[\"Longitude\",EAST],\n"
 2263         "            AUTHORITY[\"EPSG\",\"4326\"]],\n"
 2264         "        PROJECTION[\"Transverse_Mercator\"],\n"
 2265         "        PARAMETER[\"latitude_of_origin\",0],\n"
 2266         "        PARAMETER[\"central_meridian\",3],\n"
 2267         "        PARAMETER[\"scale_factor\",0.9996],\n"
 2268         "        PARAMETER[\"false_easting\",500000],\n"
 2269         "        PARAMETER[\"false_northing\",0],\n"
 2270         "        UNIT[\"metre\",1,\n"
 2271         "            AUTHORITY[\"EPSG\",\"9001\"]],\n"
 2272         "        AXIS[\"Easting\",EAST],\n"
 2273         "        AXIS[\"Northing\",NORTH],\n"
 2274         "        AUTHORITY[\"EPSG\",\"32631\"]],\n"
 2275         "    VERT_CS[\"ODN height\",\n"
 2276         "        VERT_DATUM[\"Ordnance Datum Newlyn\",2005,\n"
 2277         "            AUTHORITY[\"EPSG\",\"5101\"]],\n"
 2278         "        UNIT[\"metre\",1,\n"
 2279         "            AUTHORITY[\"EPSG\",\"9001\"]],\n"
 2280         "        AXIS[\"Gravity-related height\",UP],\n"
 2281         "        AUTHORITY[\"EPSG\",\"5701\"]],\n"
 2282         "    AUTHORITY[\"codespace\",\"code\"]]");
 2283     auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
 2284     ASSERT_TRUE(crs != nullptr);
 2285     EXPECT_EQ(crs->nameStr(), "horizontal + vertical");
 2286     EXPECT_EQ(crs->componentReferenceSystems().size(), 2U);
 2287     ASSERT_EQ(crs->identifiers().size(), 1U);
 2288     EXPECT_EQ(crs->identifiers()[0]->code(), "code");
 2289     EXPECT_EQ(*(crs->identifiers()[0]->codeSpace()), "codespace");
 2290 }
 2291 
 2292 // ---------------------------------------------------------------------------
 2293 
 2294 TEST(wkt_parse, COORDINATEOPERATION) {
 2295 
 2296     std::string src_wkt;
 2297     {
 2298         auto formatter = WKTFormatter::create();
 2299         formatter->setOutputId(false);
 2300         src_wkt = GeographicCRS::EPSG_4326->exportToWKT(formatter.get());
 2301     }
 2302 
 2303     std::string dst_wkt;
 2304     {
 2305         auto formatter = WKTFormatter::create();
 2306         formatter->setOutputId(false);
 2307         dst_wkt = GeographicCRS::EPSG_4807->exportToWKT(formatter.get());
 2308     }
 2309 
 2310     std::string interpolation_wkt;
 2311     {
 2312         auto formatter = WKTFormatter::create();
 2313         formatter->setOutputId(false);
 2314         interpolation_wkt =
 2315             GeographicCRS::EPSG_4979->exportToWKT(formatter.get());
 2316     }
 2317 
 2318     auto wkt =
 2319         "COORDINATEOPERATION[\"transformationName\",\n"
 2320         "    SOURCECRS[" +
 2321         src_wkt + "],\n"
 2322                   "    TARGETCRS[" +
 2323         dst_wkt +
 2324         "],\n"
 2325         "    METHOD[\"operationMethodName\",\n"
 2326         "        ID[\"codeSpaceOperationMethod\",\"codeOperationMethod\"]],\n"
 2327         "    PARAMETERFILE[\"paramName\",\"foo.bin\"],\n"
 2328         "    INTERPOLATIONCRS[" +
 2329         interpolation_wkt +
 2330         "],\n"
 2331         "    OPERATIONACCURACY[0.1],\n"
 2332         "    ID[\"codeSpaceTransformation\",\"codeTransformation\"],\n"
 2333         "    REMARK[\"my remarks\"]]";
 2334 
 2335     auto obj = WKTParser().createFromWKT(wkt);
 2336     auto transf = nn_dynamic_pointer_cast<Transformation>(obj);
 2337     ASSERT_TRUE(transf != nullptr);
 2338     EXPECT_EQ(transf->nameStr(), "transformationName");
 2339     ASSERT_EQ(transf->identifiers().size(), 1U);
 2340     EXPECT_EQ(transf->identifiers()[0]->code(), "codeTransformation");
 2341     EXPECT_EQ(*(transf->identifiers()[0]->codeSpace()),
 2342               "codeSpaceTransformation");
 2343     ASSERT_EQ(transf->coordinateOperationAccuracies().size(), 1U);
 2344     EXPECT_EQ(transf->coordinateOperationAccuracies()[0]->value(), "0.1");
 2345     EXPECT_EQ(transf->sourceCRS()->nameStr(),
 2346               GeographicCRS::EPSG_4326->nameStr());
 2347     EXPECT_EQ(transf->targetCRS()->nameStr(),
 2348               GeographicCRS::EPSG_4807->nameStr());
 2349     ASSERT_TRUE(transf->interpolationCRS() != nullptr);
 2350     EXPECT_EQ(transf->interpolationCRS()->nameStr(),
 2351               GeographicCRS::EPSG_4979->nameStr());
 2352     EXPECT_EQ(transf->method()->nameStr(), "operationMethodName");
 2353     EXPECT_EQ(transf->parameterValues().size(), 1U);
 2354 
 2355     {
 2356         auto outWkt = transf->exportToWKT(WKTFormatter::create().get());
 2357         EXPECT_EQ(replaceAll(replaceAll(outWkt, "\n", ""), " ", ""),
 2358                   replaceAll(replaceAll(wkt, "\n", ""), " ", ""));
 2359     }
 2360 }
 2361 
 2362 // ---------------------------------------------------------------------------
 2363 
 2364 TEST(wkt_parse, COORDINATEOPERATION_wkt2018) {
 2365 
 2366     std::string src_wkt;
 2367     {
 2368         auto formatter =
 2369             WKTFormatter::create(WKTFormatter::Convention::WKT2_2018);
 2370         formatter->setOutputId(false);
 2371         src_wkt = GeographicCRS::EPSG_4326->exportToWKT(formatter.get());
 2372     }
 2373 
 2374     std::string dst_wkt;
 2375     {
 2376         auto formatter =
 2377             WKTFormatter::create(WKTFormatter::Convention::WKT2_2018);
 2378         formatter->setOutputId(false);
 2379         dst_wkt = GeographicCRS::EPSG_4807->exportToWKT(formatter.get());
 2380     }
 2381 
 2382     std::string interpolation_wkt;
 2383     {
 2384         auto formatter =
 2385             WKTFormatter::create(WKTFormatter::Convention::WKT2_2018);
 2386         formatter->setOutputId(false);
 2387         interpolation_wkt =
 2388             GeographicCRS::EPSG_4979->exportToWKT(formatter.get());
 2389     }
 2390 
 2391     auto wkt =
 2392         "COORDINATEOPERATION[\"transformationName\",\n"
 2393         "    VERSION[\"my version\"],\n"
 2394         "    SOURCECRS[" +
 2395         src_wkt + "],\n"
 2396                   "    TARGETCRS[" +
 2397         dst_wkt +
 2398         "],\n"
 2399         "    METHOD[\"operationMethodName\",\n"
 2400         "        ID[\"codeSpaceOperationMethod\",\"codeOperationMethod\"]],\n"
 2401         "    PARAMETERFILE[\"paramName\",\"foo.bin\"],\n"
 2402         "    INTERPOLATIONCRS[" +
 2403         interpolation_wkt +
 2404         "],\n"
 2405         "    OPERATIONACCURACY[0.1],\n"
 2406         "    ID[\"codeSpaceTransformation\",\"codeTransformation\"],\n"
 2407         "    REMARK[\"my remarks\"]]";
 2408 
 2409     auto obj = WKTParser().createFromWKT(wkt);
 2410     auto transf = nn_dynamic_pointer_cast<Transformation>(obj);
 2411     ASSERT_TRUE(transf != nullptr);
 2412     EXPECT_EQ(transf->nameStr(), "transformationName");
 2413     EXPECT_EQ(*transf->operationVersion(), "my version");
 2414     ASSERT_EQ(transf->identifiers().size(), 1U);
 2415     EXPECT_EQ(transf->identifiers()[0]->code(), "codeTransformation");
 2416     EXPECT_EQ(*(transf->identifiers()[0]->codeSpace()),
 2417               "codeSpaceTransformation");
 2418     ASSERT_EQ(transf->coordinateOperationAccuracies().size(), 1U);
 2419     EXPECT_EQ(transf->coordinateOperationAccuracies()[0]->value(), "0.1");
 2420     EXPECT_EQ(transf->sourceCRS()->nameStr(),
 2421               GeographicCRS::EPSG_4326->nameStr());
 2422     EXPECT_EQ(transf->targetCRS()->nameStr(),
 2423               GeographicCRS::EPSG_4807->nameStr());
 2424     ASSERT_TRUE(transf->interpolationCRS() != nullptr);
 2425     EXPECT_EQ(transf->interpolationCRS()->nameStr(),
 2426               GeographicCRS::EPSG_4979->nameStr());
 2427     EXPECT_EQ(transf->method()->nameStr(), "operationMethodName");
 2428     EXPECT_EQ(transf->parameterValues().size(), 1U);
 2429 
 2430     {
 2431         auto outWkt = transf->exportToWKT(
 2432             WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get());
 2433         EXPECT_EQ(replaceAll(replaceAll(outWkt, "\n", ""), " ", ""),
 2434                   replaceAll(replaceAll(wkt, "\n", ""), " ", ""));
 2435     }
 2436 
 2437     {
 2438         auto outWkt = transf->exportToWKT(
 2439             WKTFormatter::create(WKTFormatter::Convention::WKT2_2015).get());
 2440         EXPECT_FALSE(outWkt.find("VERSION[\"my version\"],") !=
 2441                      std::string::npos);
 2442     }
 2443 }
 2444 
 2445 // ---------------------------------------------------------------------------
 2446 
 2447 TEST(wkt_parse, conversion_proj_based) {
 2448 
 2449     auto wkt = "CONVERSION[\"PROJ-based coordinate operation\",\n"
 2450                "    METHOD[\"PROJ-based operation method: +proj=merc\"]]";
 2451 
 2452     auto obj = WKTParser().createFromWKT(wkt);
 2453     auto transf = nn_dynamic_pointer_cast<SingleOperation>(obj);
 2454     ASSERT_TRUE(transf != nullptr);
 2455     EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()),
 2456               "+proj=merc");
 2457 }
 2458 
 2459 // ---------------------------------------------------------------------------
 2460 
 2461 TEST(wkt_parse, CONCATENATEDOPERATION) {
 2462 
 2463     auto transf_1 = Transformation::create(
 2464         PropertyMap().set(IdentifiedObject::NAME_KEY, "transf_1"),
 2465         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326),
 2466         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), nullptr,
 2467         PropertyMap().set(IdentifiedObject::NAME_KEY, "operationMethodName"),
 2468         std::vector<OperationParameterNNPtr>{OperationParameter::create(
 2469             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))},
 2470         std::vector<ParameterValueNNPtr>{
 2471             ParameterValue::createFilename("foo.bin")},
 2472         std::vector<PositionalAccuracyNNPtr>());
 2473 
 2474     auto transf_2 = Transformation::create(
 2475         PropertyMap().set(IdentifiedObject::NAME_KEY, "transf_2"),
 2476         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807),
 2477         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4979), nullptr,
 2478         PropertyMap().set(IdentifiedObject::NAME_KEY, "operationMethodName"),
 2479         std::vector<OperationParameterNNPtr>{OperationParameter::create(
 2480             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))},
 2481         std::vector<ParameterValueNNPtr>{
 2482             ParameterValue::createFilename("foo.bin")},
 2483         std::vector<PositionalAccuracyNNPtr>());
 2484 
 2485     auto concat_in = ConcatenatedOperation::create(
 2486         PropertyMap()
 2487             .set(Identifier::CODESPACE_KEY, "codeSpace")
 2488             .set(Identifier::CODE_KEY, "code")
 2489             .set(IdentifiedObject::NAME_KEY, "name")
 2490             .set(IdentifiedObject::REMARKS_KEY, "my remarks"),
 2491         std::vector<CoordinateOperationNNPtr>{transf_1, transf_2},
 2492         std::vector<PositionalAccuracyNNPtr>{
 2493             PositionalAccuracy::create("0.1")});
 2494 
 2495     auto wkt = concat_in->exportToWKT(
 2496         WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get());
 2497 
 2498     auto obj = WKTParser().createFromWKT(wkt);
 2499     auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj);
 2500     ASSERT_TRUE(concat != nullptr);
 2501     EXPECT_EQ(concat->nameStr(), "name");
 2502     EXPECT_FALSE(concat->operationVersion().has_value());
 2503     ASSERT_EQ(concat->identifiers().size(), 1U);
 2504     EXPECT_EQ(concat->identifiers()[0]->code(), "code");
 2505     EXPECT_EQ(*(concat->identifiers()[0]->codeSpace()), "codeSpace");
 2506     ASSERT_EQ(concat->operations().size(), 2U);
 2507     ASSERT_EQ(concat->operations()[0]->nameStr(), transf_1->nameStr());
 2508     ASSERT_EQ(concat->operations()[1]->nameStr(), transf_2->nameStr());
 2509     ASSERT_TRUE(concat->sourceCRS() != nullptr);
 2510     ASSERT_TRUE(concat->targetCRS() != nullptr);
 2511     ASSERT_EQ(concat->sourceCRS()->nameStr(), transf_1->sourceCRS()->nameStr());
 2512     ASSERT_EQ(concat->targetCRS()->nameStr(), transf_2->targetCRS()->nameStr());
 2513 }
 2514 
 2515 // ---------------------------------------------------------------------------
 2516 
 2517 TEST(wkt_parse, CONCATENATEDOPERATION_with_conversion_and_conversion) {
 2518 
 2519     auto wkt =
 2520         "CONCATENATEDOPERATION[\"Inverse of UTM zone 31N + UTM zone 32N\",\n"
 2521         "    SOURCECRS[\n"
 2522         "        PROJCRS[\"WGS 84 / UTM zone 31N\",\n"
 2523         "            BASEGEOGCRS[\"WGS 84\",\n"
 2524         "                DATUM[\"World Geodetic System 1984\",\n"
 2525         "                    ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2526         "                        LENGTHUNIT[\"metre\",1]]],\n"
 2527         "                PRIMEM[\"Greenwich\",0,\n"
 2528         "                    ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2529         "            CONVERSION[\"UTM zone 31N\",\n"
 2530         "                METHOD[\"Transverse Mercator\",\n"
 2531         "                    ID[\"EPSG\",9807]],\n"
 2532         "                PARAMETER[\"Latitude of natural origin\",0,\n"
 2533         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2534         "                    ID[\"EPSG\",8801]],\n"
 2535         "                PARAMETER[\"Longitude of natural origin\",3,\n"
 2536         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2537         "                    ID[\"EPSG\",8802]],\n"
 2538         "                PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2539         "                    SCALEUNIT[\"unity\",1],\n"
 2540         "                    ID[\"EPSG\",8805]],\n"
 2541         "                PARAMETER[\"False easting\",500000,\n"
 2542         "                    LENGTHUNIT[\"metre\",1],\n"
 2543         "                    ID[\"EPSG\",8806]],\n"
 2544         "                PARAMETER[\"False northing\",0,\n"
 2545         "                    LENGTHUNIT[\"metre\",1],\n"
 2546         "                    ID[\"EPSG\",8807]]],\n"
 2547         "            CS[Cartesian,2],\n"
 2548         "                AXIS[\"(E)\",east,\n"
 2549         "                    ORDER[1],\n"
 2550         "                    LENGTHUNIT[\"metre\",1]],\n"
 2551         "                AXIS[\"(N)\",north,\n"
 2552         "                    ORDER[2],\n"
 2553         "                    LENGTHUNIT[\"metre\",1]],\n"
 2554         "            ID[\"EPSG\",32631]]],\n"
 2555         "    TARGETCRS[\n"
 2556         "        PROJCRS[\"WGS 84 / UTM zone 32N\",\n"
 2557         "            BASEGEOGCRS[\"WGS 84\",\n"
 2558         "                DATUM[\"World Geodetic System 1984\",\n"
 2559         "                    ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2560         "                        LENGTHUNIT[\"metre\",1]]],\n"
 2561         "                PRIMEM[\"Greenwich\",0,\n"
 2562         "                    ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2563         "            CONVERSION[\"UTM zone 32N\",\n"
 2564         "                METHOD[\"Transverse Mercator\",\n"
 2565         "                    ID[\"EPSG\",9807]],\n"
 2566         "                PARAMETER[\"Latitude of natural origin\",0,\n"
 2567         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2568         "                    ID[\"EPSG\",8801]],\n"
 2569         "                PARAMETER[\"Longitude of natural origin\",9,\n"
 2570         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2571         "                    ID[\"EPSG\",8802]],\n"
 2572         "                PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2573         "                    SCALEUNIT[\"unity\",1],\n"
 2574         "                    ID[\"EPSG\",8805]],\n"
 2575         "                PARAMETER[\"False easting\",500000,\n"
 2576         "                    LENGTHUNIT[\"metre\",1],\n"
 2577         "                    ID[\"EPSG\",8806]],\n"
 2578         "                PARAMETER[\"False northing\",0,\n"
 2579         "                    LENGTHUNIT[\"metre\",1],\n"
 2580         "                    ID[\"EPSG\",8807]]],\n"
 2581         "            CS[Cartesian,2],\n"
 2582         "                AXIS[\"(E)\",east,\n"
 2583         "                    ORDER[1],\n"
 2584         "                    LENGTHUNIT[\"metre\",1]],\n"
 2585         "                AXIS[\"(N)\",north,\n"
 2586         "                    ORDER[2],\n"
 2587         "                    LENGTHUNIT[\"metre\",1]],\n"
 2588         "            ID[\"EPSG\",32632]]],\n"
 2589         "    STEP[\n"
 2590         "        CONVERSION[\"Inverse of UTM zone 31N\",\n"
 2591         "            METHOD[\"Inverse of Transverse Mercator\",\n"
 2592         "                ID[\"INVERSE(EPSG)\",9807]],\n"
 2593         "            PARAMETER[\"Latitude of natural origin\",0,\n"
 2594         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2595         "                ID[\"EPSG\",8801]],\n"
 2596         "            PARAMETER[\"Longitude of natural origin\",3,\n"
 2597         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2598         "                ID[\"EPSG\",8802]],\n"
 2599         "            PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2600         "                SCALEUNIT[\"unity\",1],\n"
 2601         "                ID[\"EPSG\",8805]],\n"
 2602         "            PARAMETER[\"False easting\",500000,\n"
 2603         "                LENGTHUNIT[\"metre\",1],\n"
 2604         "                ID[\"EPSG\",8806]],\n"
 2605         "            PARAMETER[\"False northing\",0,\n"
 2606         "                LENGTHUNIT[\"metre\",1],\n"
 2607         "                ID[\"EPSG\",8807]],\n"
 2608         "            ID[\"INVERSE(EPSG)\",16031]]],\n"
 2609         "    STEP[\n"
 2610         "        CONVERSION[\"UTM zone 32N\",\n"
 2611         "            METHOD[\"Transverse Mercator\",\n"
 2612         "                ID[\"EPSG\",9807]],\n"
 2613         "            PARAMETER[\"Latitude of natural origin\",0,\n"
 2614         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2615         "                ID[\"EPSG\",8801]],\n"
 2616         "            PARAMETER[\"Longitude of natural origin\",9,\n"
 2617         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2618         "                ID[\"EPSG\",8802]],\n"
 2619         "            PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2620         "                SCALEUNIT[\"unity\",1],\n"
 2621         "                ID[\"EPSG\",8805]],\n"
 2622         "            PARAMETER[\"False easting\",500000,\n"
 2623         "                LENGTHUNIT[\"metre\",1],\n"
 2624         "                ID[\"EPSG\",8806]],\n"
 2625         "            PARAMETER[\"False northing\",0,\n"
 2626         "                LENGTHUNIT[\"metre\",1],\n"
 2627         "                ID[\"EPSG\",8807]],\n"
 2628         "            ID[\"EPSG\",16032]]]]";
 2629 
 2630     auto obj = WKTParser().createFromWKT(wkt);
 2631     auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj);
 2632     ASSERT_TRUE(concat != nullptr);
 2633 
 2634     EXPECT_EQ(concat->exportToPROJString(PROJStringFormatter::create().get()),
 2635               "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=WGS84 "
 2636               "+step +proj=utm +zone=32 +ellps=WGS84");
 2637 
 2638     auto outWkt = concat->exportToWKT(
 2639         WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get());
 2640     EXPECT_EQ(wkt, outWkt);
 2641 }
 2642 
 2643 // ---------------------------------------------------------------------------
 2644 
 2645 TEST(wkt_parse,
 2646      CONCATENATEDOPERATION_with_conversion_coordinateoperation_conversion) {
 2647 
 2648     auto wkt =
 2649         "CONCATENATEDOPERATION[\"Inverse of UTM zone 11N + NAD27 to WGS 84 "
 2650         "(79) + UTM zone 11N\",\n"
 2651         "    VERSION[\"my version\"],\n"
 2652         "    SOURCECRS[\n"
 2653         "        PROJCRS[\"NAD27 / UTM zone 11N\",\n"
 2654         "            BASEGEOGCRS[\"NAD27\",\n"
 2655         "                DATUM[\"North American Datum 1927\",\n"
 2656         "                    ELLIPSOID[\"Clarke "
 2657         "1866\",6378206.4,294.978698213898,\n"
 2658         "                        LENGTHUNIT[\"metre\",1]]],\n"
 2659         "                PRIMEM[\"Greenwich\",0,\n"
 2660         "                    ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2661         "            CONVERSION[\"UTM zone 11N\",\n"
 2662         "                METHOD[\"Transverse Mercator\",\n"
 2663         "                    ID[\"EPSG\",9807]],\n"
 2664         "                PARAMETER[\"Latitude of natural origin\",0,\n"
 2665         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2666         "                    ID[\"EPSG\",8801]],\n"
 2667         "                PARAMETER[\"Longitude of natural origin\",-117,\n"
 2668         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2669         "                    ID[\"EPSG\",8802]],\n"
 2670         "                PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2671         "                    SCALEUNIT[\"unity\",1],\n"
 2672         "                    ID[\"EPSG\",8805]],\n"
 2673         "                PARAMETER[\"False easting\",500000,\n"
 2674         "                    LENGTHUNIT[\"metre\",1],\n"
 2675         "                    ID[\"EPSG\",8806]],\n"
 2676         "                PARAMETER[\"False northing\",0,\n"
 2677         "                    LENGTHUNIT[\"metre\",1],\n"
 2678         "                    ID[\"EPSG\",8807]]],\n"
 2679         "            CS[Cartesian,2],\n"
 2680         "                AXIS[\"(E)\",east,\n"
 2681         "                    ORDER[1],\n"
 2682         "                    LENGTHUNIT[\"metre\",1]],\n"
 2683         "                AXIS[\"(N)\",north,\n"
 2684         "                    ORDER[2],\n"
 2685         "                    LENGTHUNIT[\"metre\",1]],\n"
 2686         "            ID[\"EPSG\",26711]]],\n"
 2687         "    TARGETCRS[\n"
 2688         "        PROJCRS[\"WGS 84 / UTM zone 11N\",\n"
 2689         "            BASEGEOGCRS[\"WGS 84\",\n"
 2690         "                DATUM[\"World Geodetic System 1984\",\n"
 2691         "                    ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2692         "                        LENGTHUNIT[\"metre\",1]]],\n"
 2693         "                PRIMEM[\"Greenwich\",0,\n"
 2694         "                    ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2695         "            CONVERSION[\"UTM zone 11N\",\n"
 2696         "                METHOD[\"Transverse Mercator\",\n"
 2697         "                    ID[\"EPSG\",9807]],\n"
 2698         "                PARAMETER[\"Latitude of natural origin\",0,\n"
 2699         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2700         "                    ID[\"EPSG\",8801]],\n"
 2701         "                PARAMETER[\"Longitude of natural origin\",-117,\n"
 2702         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2703         "                    ID[\"EPSG\",8802]],\n"
 2704         "                PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2705         "                    SCALEUNIT[\"unity\",1],\n"
 2706         "                    ID[\"EPSG\",8805]],\n"
 2707         "                PARAMETER[\"False easting\",500000,\n"
 2708         "                    LENGTHUNIT[\"metre\",1],\n"
 2709         "                    ID[\"EPSG\",8806]],\n"
 2710         "                PARAMETER[\"False northing\",0,\n"
 2711         "                    LENGTHUNIT[\"metre\",1],\n"
 2712         "                    ID[\"EPSG\",8807]]],\n"
 2713         "            CS[Cartesian,2],\n"
 2714         "                AXIS[\"(E)\",east,\n"
 2715         "                    ORDER[1],\n"
 2716         "                    LENGTHUNIT[\"metre\",1]],\n"
 2717         "                AXIS[\"(N)\",north,\n"
 2718         "                    ORDER[2],\n"
 2719         "                    LENGTHUNIT[\"metre\",1]],\n"
 2720         "            ID[\"EPSG\",32611]]],\n"
 2721         "    STEP[\n"
 2722         "        CONVERSION[\"Inverse of UTM zone 11N\",\n"
 2723         "            METHOD[\"Inverse of Transverse Mercator\",\n"
 2724         "                ID[\"INVERSE(EPSG)\",9807]],\n"
 2725         "            PARAMETER[\"Latitude of natural origin\",0,\n"
 2726         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2727         "                ID[\"EPSG\",8801]],\n"
 2728         "            PARAMETER[\"Longitude of natural origin\",-117,\n"
 2729         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2730         "                ID[\"EPSG\",8802]],\n"
 2731         "            PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2732         "                SCALEUNIT[\"unity\",1],\n"
 2733         "                ID[\"EPSG\",8805]],\n"
 2734         "            PARAMETER[\"False easting\",500000,\n"
 2735         "                LENGTHUNIT[\"metre\",1],\n"
 2736         "                ID[\"EPSG\",8806]],\n"
 2737         "            PARAMETER[\"False northing\",0,\n"
 2738         "                LENGTHUNIT[\"metre\",1],\n"
 2739         "                ID[\"EPSG\",8807]],\n"
 2740         "            ID[\"INVERSE(EPSG)\",16011]]],\n"
 2741         "    STEP[\n"
 2742         "        COORDINATEOPERATION[\"NAD27 to WGS 84 (79)\",\n"
 2743         "            SOURCECRS[\n"
 2744         "                GEOGCRS[\"NAD27\",\n"
 2745         "                    DATUM[\"North American Datum 1927\",\n"
 2746         "                        ELLIPSOID[\"Clarke "
 2747         "1866\",6378206.4,294.978698213898,\n"
 2748         "                            LENGTHUNIT[\"metre\",1]]],\n"
 2749         "                    PRIMEM[\"Greenwich\",0,\n"
 2750         "                        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2751         "                    CS[ellipsoidal,2],\n"
 2752         "                        AXIS[\"geodetic latitude (Lat)\",north,\n"
 2753         "                            ORDER[1],\n"
 2754         "                            "
 2755         "ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2756         "                        AXIS[\"geodetic longitude (Lon)\",east,\n"
 2757         "                            ORDER[2],\n"
 2758         "                            "
 2759         "ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n"
 2760         "            TARGETCRS[\n"
 2761         "                GEOGCRS[\"WGS 84\",\n"
 2762         "                    DATUM[\"World Geodetic System 1984\",\n"
 2763         "                        ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2764         "                            LENGTHUNIT[\"metre\",1]]],\n"
 2765         "                    PRIMEM[\"Greenwich\",0,\n"
 2766         "                        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2767         "                    CS[ellipsoidal,2],\n"
 2768         "                        AXIS[\"geodetic latitude (Lat)\",north,\n"
 2769         "                            ORDER[1],\n"
 2770         "                            "
 2771         "ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2772         "                        AXIS[\"geodetic longitude (Lon)\",east,\n"
 2773         "                            ORDER[2],\n"
 2774         "                            "
 2775         "ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n"
 2776         "            METHOD[\"CTABLE2\"],\n"
 2777         "            PARAMETERFILE[\"Latitude and longitude difference "
 2778         "file\",\"conus\"],\n"
 2779         "            ID[\"DERIVED_FROM(EPSG)\",15851]]],\n"
 2780         "    STEP[\n"
 2781         "        CONVERSION[\"UTM zone 11N\",\n"
 2782         "            METHOD[\"Transverse Mercator\",\n"
 2783         "                ID[\"EPSG\",9807]],\n"
 2784         "            PARAMETER[\"Latitude of natural origin\",0,\n"
 2785         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2786         "                ID[\"EPSG\",8801]],\n"
 2787         "            PARAMETER[\"Longitude of natural origin\",-117,\n"
 2788         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2789         "                ID[\"EPSG\",8802]],\n"
 2790         "            PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2791         "                SCALEUNIT[\"unity\",1],\n"
 2792         "                ID[\"EPSG\",8805]],\n"
 2793         "            PARAMETER[\"False easting\",500000,\n"
 2794         "                LENGTHUNIT[\"metre\",1],\n"
 2795         "                ID[\"EPSG\",8806]],\n"
 2796         "            PARAMETER[\"False northing\",0,\n"
 2797         "                LENGTHUNIT[\"metre\",1],\n"
 2798         "                ID[\"EPSG\",8807]],\n"
 2799         "            ID[\"EPSG\",16011]]]]";
 2800 
 2801     auto obj = WKTParser().createFromWKT(wkt);
 2802     auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj);
 2803     ASSERT_TRUE(concat != nullptr);
 2804     EXPECT_EQ(*concat->operationVersion(), "my version");
 2805 
 2806     EXPECT_EQ(concat->exportToPROJString(PROJStringFormatter::create().get()),
 2807               "+proj=pipeline +step +inv +proj=utm +zone=11 +ellps=clrk66 "
 2808               "+step +proj=hgridshift +grids=conus +step +proj=utm "
 2809               "+zone=11 +ellps=WGS84");
 2810 
 2811     auto outWkt = concat->exportToWKT(
 2812         WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get());
 2813     EXPECT_EQ(wkt, outWkt);
 2814 }
 2815 
 2816 // ---------------------------------------------------------------------------
 2817 
 2818 TEST(
 2819     wkt_parse,
 2820     CONCATENATEDOPERATION_with_conversion_coordinateoperation_to_inverse_conversion) {
 2821 
 2822     auto wkt =
 2823         "CONCATENATEDOPERATION[\"Inverse of UTM zone 11N + NAD27 to WGS 84 "
 2824         "(79) + UTM zone 11N\",\n"
 2825         "    SOURCECRS[\n"
 2826         "        PROJCRS[\"WGS 84 / UTM zone 11N\",\n"
 2827         "            BASEGEOGCRS[\"WGS 84\",\n"
 2828         "                DATUM[\"World Geodetic System 1984\",\n"
 2829         "                    ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2830         "                        LENGTHUNIT[\"metre\",1]]],\n"
 2831         "                PRIMEM[\"Greenwich\",0,\n"
 2832         "                    ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2833         "            CONVERSION[\"UTM zone 11N\",\n"
 2834         "                METHOD[\"Transverse Mercator\",\n"
 2835         "                    ID[\"EPSG\",9807]],\n"
 2836         "                PARAMETER[\"Latitude of natural origin\",0,\n"
 2837         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2838         "                    ID[\"EPSG\",8801]],\n"
 2839         "                PARAMETER[\"Longitude of natural origin\",-117,\n"
 2840         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2841         "                    ID[\"EPSG\",8802]],\n"
 2842         "                PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2843         "                    SCALEUNIT[\"unity\",1],\n"
 2844         "                    ID[\"EPSG\",8805]],\n"
 2845         "                PARAMETER[\"False easting\",500000,\n"
 2846         "                    LENGTHUNIT[\"metre\",1],\n"
 2847         "                    ID[\"EPSG\",8806]],\n"
 2848         "                PARAMETER[\"False northing\",0,\n"
 2849         "                    LENGTHUNIT[\"metre\",1],\n"
 2850         "                    ID[\"EPSG\",8807]]],\n"
 2851         "            CS[Cartesian,2],\n"
 2852         "                AXIS[\"(E)\",east,\n"
 2853         "                    ORDER[1],\n"
 2854         "                    LENGTHUNIT[\"metre\",1]],\n"
 2855         "                AXIS[\"(N)\",north,\n"
 2856         "                    ORDER[2],\n"
 2857         "                    LENGTHUNIT[\"metre\",1]],\n"
 2858         "            ID[\"EPSG\",32611]]],\n"
 2859         "    TARGETCRS[\n"
 2860         "        PROJCRS[\"NAD27 / UTM zone 11N\",\n"
 2861         "            BASEGEOGCRS[\"NAD27\",\n"
 2862         "                DATUM[\"North American Datum 1927\",\n"
 2863         "                    ELLIPSOID[\"Clarke "
 2864         "1866\",6378206.4,294.978698213898,\n"
 2865         "                        LENGTHUNIT[\"metre\",1]]],\n"
 2866         "                PRIMEM[\"Greenwich\",0,\n"
 2867         "                    ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 2868         "            CONVERSION[\"UTM zone 11N\",\n"
 2869         "                METHOD[\"Transverse Mercator\",\n"
 2870         "                    ID[\"EPSG\",9807]],\n"
 2871         "                PARAMETER[\"Latitude of natural origin\",0,\n"
 2872         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2873         "                    ID[\"EPSG\",8801]],\n"
 2874         "                PARAMETER[\"Longitude of natural origin\",-117,\n"
 2875         "                    ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2876         "                    ID[\"EPSG\",8802]],\n"
 2877         "                PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2878         "                    SCALEUNIT[\"unity\",1],\n"
 2879         "                    ID[\"EPSG\",8805]],\n"
 2880         "                PARAMETER[\"False easting\",500000,\n"
 2881         "                    LENGTHUNIT[\"metre\",1],\n"
 2882         "                    ID[\"EPSG\",8806]],\n"
 2883         "                PARAMETER[\"False northing\",0,\n"
 2884         "                    LENGTHUNIT[\"metre\",1],\n"
 2885         "                    ID[\"EPSG\",8807]]],\n"
 2886         "            CS[Cartesian,2],\n"
 2887         "                AXIS[\"(E)\",east,\n"
 2888         "                    ORDER[1],\n"
 2889         "                    LENGTHUNIT[\"metre\",1]],\n"
 2890         "                AXIS[\"(N)\",north,\n"
 2891         "                    ORDER[2],\n"
 2892         "                    LENGTHUNIT[\"metre\",1]],\n"
 2893         "            ID[\"EPSG\",26711]]],\n"
 2894         "    STEP[\n"
 2895         "        CONVERSION[\"Inverse of UTM zone 11N\",\n"
 2896         "            METHOD[\"Inverse of Transverse Mercator\",\n"
 2897         "                ID[\"INVERSE(EPSG)\",9807]],\n"
 2898         "            PARAMETER[\"Latitude of natural origin\",0,\n"
 2899         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2900         "                ID[\"EPSG\",8801]],\n"
 2901         "            PARAMETER[\"Longitude of natural origin\",-117,\n"
 2902         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2903         "                ID[\"EPSG\",8802]],\n"
 2904         "            PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2905         "                SCALEUNIT[\"unity\",1],\n"
 2906         "                ID[\"EPSG\",8805]],\n"
 2907         "            PARAMETER[\"False easting\",500000,\n"
 2908         "                LENGTHUNIT[\"metre\",1],\n"
 2909         "                ID[\"EPSG\",8806]],\n"
 2910         "            PARAMETER[\"False northing\",0,\n"
 2911         "                LENGTHUNIT[\"metre\",1],\n"
 2912         "                ID[\"EPSG\",8807]],\n"
 2913         "            ID[\"INVERSE(EPSG)\",16011]]],\n"
 2914         "    STEP[\n"
 2915         "        COORDINATEOPERATION[\"NAD27 to WGS 84 (79)\",\n"
 2916         "            SOURCECRS[\n"
 2917         "                GEOGCRS[\"NAD27\",\n"
 2918         "                    DATUM[\"North American Datum 1927\",\n"
 2919         "                        ELLIPSOID[\"Clarke "
 2920         "1866\",6378206.4,294.978698213898,\n"
 2921         "                            LENGTHUNIT[\"metre\",1]]],\n"
 2922         "                    PRIMEM[\"Greenwich\",0,\n"
 2923         "                        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2924         "                    CS[ellipsoidal,2],\n"
 2925         "                        AXIS[\"geodetic latitude (Lat)\",north,\n"
 2926         "                            ORDER[1],\n"
 2927         "                            "
 2928         "ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2929         "                        AXIS[\"geodetic longitude (Lon)\",east,\n"
 2930         "                            ORDER[2],\n"
 2931         "                            "
 2932         "ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n"
 2933         "            TARGETCRS[\n"
 2934         "                GEOGCRS[\"WGS 84\",\n"
 2935         "                    DATUM[\"World Geodetic System 1984\",\n"
 2936         "                        ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 2937         "                            LENGTHUNIT[\"metre\",1]]],\n"
 2938         "                    PRIMEM[\"Greenwich\",0,\n"
 2939         "                        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2940         "                    CS[ellipsoidal,2],\n"
 2941         "                        AXIS[\"geodetic latitude (Lat)\",north,\n"
 2942         "                            ORDER[1],\n"
 2943         "                            "
 2944         "ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 2945         "                        AXIS[\"geodetic longitude (Lon)\",east,\n"
 2946         "                            ORDER[2],\n"
 2947         "                            "
 2948         "ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n"
 2949         "            METHOD[\"CTABLE2\"],\n"
 2950         "            PARAMETERFILE[\"Latitude and longitude difference "
 2951         "file\",\"conus\"],\n"
 2952         "            ID[\"DERIVED_FROM(EPSG)\",15851]]],\n"
 2953         "    STEP[\n"
 2954         "        CONVERSION[\"UTM zone 11N\",\n"
 2955         "            METHOD[\"Transverse Mercator\",\n"
 2956         "                ID[\"EPSG\",9807]],\n"
 2957         "            PARAMETER[\"Latitude of natural origin\",0,\n"
 2958         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2959         "                ID[\"EPSG\",8801]],\n"
 2960         "            PARAMETER[\"Longitude of natural origin\",-117,\n"
 2961         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 2962         "                ID[\"EPSG\",8802]],\n"
 2963         "            PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 2964         "                SCALEUNIT[\"unity\",1],\n"
 2965         "                ID[\"EPSG\",8805]],\n"
 2966         "            PARAMETER[\"False easting\",500000,\n"
 2967         "                LENGTHUNIT[\"metre\",1],\n"
 2968         "                ID[\"EPSG\",8806]],\n"
 2969         "            PARAMETER[\"False northing\",0,\n"
 2970         "                LENGTHUNIT[\"metre\",1],\n"
 2971         "                ID[\"EPSG\",8807]],\n"
 2972         "            ID[\"EPSG\",16011]]]]";
 2973 
 2974     auto obj = WKTParser().createFromWKT(wkt);
 2975     auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj);
 2976     ASSERT_TRUE(concat != nullptr);
 2977 
 2978     EXPECT_EQ(concat->exportToPROJString(PROJStringFormatter::create().get()),
 2979               "+proj=pipeline +step +inv +proj=utm +zone=11 +ellps=WGS84 "
 2980               "+step +inv +proj=hgridshift +grids=conus +step "
 2981               "+proj=utm +zone=11 +ellps=clrk66");
 2982 }
 2983 
 2984 // ---------------------------------------------------------------------------
 2985 
 2986 TEST(wkt_parse, BOUNDCRS_transformation_from_names) {
 2987 
 2988     auto projcrs = ProjectedCRS::create(
 2989         PropertyMap().set(IdentifiedObject::NAME_KEY, "my PROJCRS"),
 2990         GeographicCRS::create(
 2991             PropertyMap().set(IdentifiedObject::NAME_KEY, "my GEOGCRS"),
 2992             GeodeticReferenceFrame::EPSG_6326,
 2993             EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)),
 2994         Conversion::createUTM(PropertyMap(), 31, true),
 2995         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
 2996 
 2997     auto wkt =
 2998         "BOUNDCRS[SOURCECRS[" +
 2999         projcrs->exportToWKT(WKTFormatter::create().get()) + "],\n" +
 3000         "TARGETCRS[" +
 3001         GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) +
 3002         "],\n"
 3003         "    ABRIDGEDTRANSFORMATION[\"Transformation to WGS84\",\n"
 3004         "        METHOD[\"Coordinate Frame\"],\n"
 3005         "        PARAMETER[\"X-axis translation\",1],\n"
 3006         "        PARAMETER[\"Y-axis translation\",2],\n"
 3007         "        PARAMETER[\"Z-axis translation\",3],\n"
 3008         "        PARAMETER[\"X-axis rotation\",-4],\n"
 3009         "        PARAMETER[\"Y-axis rotation\",-5],\n"
 3010         "        PARAMETER[\"Z-axis rotation\",-6],\n"
 3011         "        PARAMETER[\"Scale difference\",1.000007]]]";
 3012 
 3013     auto obj = WKTParser().createFromWKT(wkt);
 3014     auto crs = nn_dynamic_pointer_cast<BoundCRS>(obj);
 3015     ASSERT_TRUE(crs != nullptr);
 3016 
 3017     EXPECT_EQ(crs->baseCRS()->nameStr(), projcrs->nameStr());
 3018 
 3019     EXPECT_EQ(crs->hubCRS()->nameStr(), GeographicCRS::EPSG_4326->nameStr());
 3020 
 3021     ASSERT_TRUE(crs->transformation()->sourceCRS() != nullptr);
 3022     EXPECT_EQ(crs->transformation()->sourceCRS()->nameStr(),
 3023               projcrs->baseCRS()->nameStr());
 3024 
 3025     auto params = crs->transformation()->getTOWGS84Parameters();
 3026     auto expected = std::vector<double>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0};
 3027     ASSERT_EQ(params.size(), expected.size());
 3028     for (int i = 0; i < 7; i++) {
 3029         EXPECT_NEAR(params[i], expected[i], 1e-10);
 3030     }
 3031 }
 3032 
 3033 // ---------------------------------------------------------------------------
 3034 
 3035 TEST(wkt_parse, BOUNDCRS_transformation_from_codes) {
 3036 
 3037     auto projcrs = ProjectedCRS::create(
 3038         PropertyMap().set(IdentifiedObject::NAME_KEY, "my PROJCRS"),
 3039         GeographicCRS::create(
 3040             PropertyMap().set(IdentifiedObject::NAME_KEY, "my GEOGCRS"),
 3041             GeodeticReferenceFrame::EPSG_6326,
 3042             EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)),
 3043         Conversion::createUTM(PropertyMap(), 31, true),
 3044         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
 3045 
 3046     auto wkt =
 3047         "BOUNDCRS[SOURCECRS[" +
 3048         projcrs->exportToWKT(WKTFormatter::create().get()) + "],\n" +
 3049         "TARGETCRS[" +
 3050         GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) +
 3051         "],\n"
 3052         "    ABRIDGEDTRANSFORMATION[\"Transformation to WGS84\",\n"
 3053         "        METHOD[\"bla\",ID[\"EPSG\",1032]],\n"
 3054         "        PARAMETER[\"tx\",1,ID[\"EPSG\",8605]],\n"
 3055         "        PARAMETER[\"ty\",2,ID[\"EPSG\",8606]],\n"
 3056         "        PARAMETER[\"tz\",3,ID[\"EPSG\",8607]],\n"
 3057         "        PARAMETER[\"rotx\",-4,ID[\"EPSG\",8608]],\n"
 3058         "        PARAMETER[\"roty\",-5,ID[\"EPSG\",8609]],\n"
 3059         "        PARAMETER[\"rotz\",-6,ID[\"EPSG\",8610]],\n"
 3060         "        PARAMETER[\"scale\",1.000007,ID[\"EPSG\",8611]]]]";
 3061 
 3062     auto obj = WKTParser().createFromWKT(wkt);
 3063     auto crs = nn_dynamic_pointer_cast<BoundCRS>(obj);
 3064     ASSERT_TRUE(crs != nullptr);
 3065 
 3066     EXPECT_EQ(crs->baseCRS()->nameStr(), projcrs->nameStr());
 3067 
 3068     EXPECT_EQ(crs->hubCRS()->nameStr(), GeographicCRS::EPSG_4326->nameStr());
 3069 
 3070     ASSERT_TRUE(crs->transformation()->sourceCRS() != nullptr);
 3071     EXPECT_EQ(crs->transformation()->sourceCRS()->nameStr(),
 3072               projcrs->baseCRS()->nameStr());
 3073 
 3074     auto params = crs->transformation()->getTOWGS84Parameters();
 3075     auto expected = std::vector<double>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0};
 3076     ASSERT_EQ(params.size(), expected.size());
 3077     for (int i = 0; i < 7; i++) {
 3078         EXPECT_NEAR(params[i], expected[i], 1e-10);
 3079     }
 3080 }
 3081 
 3082 // ---------------------------------------------------------------------------
 3083 
 3084 TEST(wkt_parse, boundcrs_of_verticalcrs_to_geog3Dcrs) {
 3085     auto wkt =
 3086         "BOUNDCRS[\n"
 3087         "    SOURCECRS[\n"
 3088         "        VERTCRS[\"my_height\",\n"
 3089         "            VDATUM[\"my_height\"],\n"
 3090         "            CS[vertical,1],\n"
 3091         "                AXIS[\"up\",up,\n"
 3092         "                    LENGTHUNIT[\"metre\",1,\n"
 3093         "                        ID[\"EPSG\",9001]]]]],\n"
 3094         "    TARGETCRS[\n"
 3095         "        GEODCRS[\"WGS 84\",\n"
 3096         "            DATUM[\"World Geodetic System 1984\",\n"
 3097         "                ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 3098         "                    LENGTHUNIT[\"metre\",1]]],\n"
 3099         "            PRIMEM[\"Greenwich\",0,\n"
 3100         "                ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 3101         "            CS[ellipsoidal,3],\n"
 3102         "                AXIS[\"latitude\",north,\n"
 3103         "                    ORDER[1],\n"
 3104         "                    ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 3105         "                AXIS[\"longitude\",east,\n"
 3106         "                    ORDER[2],\n"
 3107         "                    ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
 3108         "                AXIS[\"ellipsoidal height\",up,\n"
 3109         "                    ORDER[3],\n"
 3110         "                    LENGTHUNIT[\"metre\",1]],\n"
 3111         "            ID[\"EPSG\",4979]]],\n"
 3112         "    ABRIDGEDTRANSFORMATION[\"my_height height to WGS84 ellipsoidal "
 3113         "height\",\n"
 3114         "        METHOD[\"GravityRelatedHeight to Geographic3D\"],\n"
 3115         "        PARAMETERFILE[\"Geoid (height correction) model file\","
 3116         "                      \"./tmp/fake.gtx\",\n"
 3117         "            ID[\"EPSG\",8666]]]]";
 3118 
 3119     auto obj = WKTParser().createFromWKT(wkt);
 3120     auto crs = nn_dynamic_pointer_cast<BoundCRS>(obj);
 3121     ASSERT_TRUE(crs != nullptr);
 3122 
 3123     EXPECT_EQ(crs->baseCRS()->nameStr(), "my_height");
 3124 
 3125     EXPECT_EQ(crs->hubCRS()->nameStr(), GeographicCRS::EPSG_4979->nameStr());
 3126 }
 3127 
 3128 // ---------------------------------------------------------------------------
 3129 
 3130 TEST(wkt_parse, geogcs_TOWGS84_3terms) {
 3131     auto wkt = "GEOGCS[\"my GEOGCRS\",\n"
 3132                "    DATUM[\"WGS_1984\",\n"
 3133                "        SPHEROID[\"WGS 84\",6378137,298.257223563],\n"
 3134                "        TOWGS84[1,2,3]],\n"
 3135                "    PRIMEM[\"Greenwich\",0],\n"
 3136                "    UNIT[\"degree\",0.0174532925199433]]";
 3137 
 3138     auto obj = WKTParser().createFromWKT(wkt);
 3139     auto crs = nn_dynamic_pointer_cast<BoundCRS>(obj);
 3140     ASSERT_TRUE(crs != nullptr);
 3141 
 3142     EXPECT_EQ(crs->baseCRS()->nameStr(), "my GEOGCRS");
 3143 
 3144     EXPECT_EQ(crs->hubCRS()->nameStr(), GeographicCRS::EPSG_4326->nameStr());
 3145 
 3146     ASSERT_TRUE(crs->transformation()->sourceCRS() != nullptr);
 3147     EXPECT_EQ(crs->transformation()->sourceCRS()->nameStr(), "my GEOGCRS");
 3148 
 3149     auto params = crs->transformation()->getTOWGS84Parameters();
 3150     auto expected = std::vector<double>{1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 0.0};
 3151     ASSERT_EQ(params.size(), expected.size());
 3152     for (int i = 0; i < 7; i++) {
 3153         EXPECT_NEAR(params[i], expected[i], 1e-10);
 3154     }
 3155 }
 3156 
 3157 // ---------------------------------------------------------------------------
 3158 
 3159 TEST(wkt_parse, projcs_TOWGS84_7terms) {
 3160     auto wkt = "PROJCS[\"my PROJCRS\",\n"
 3161                "    GEOGCS[\"my GEOGCRS\",\n"
 3162                "        DATUM[\"WGS_1984\",\n"
 3163                "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
 3164                "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
 3165                "            TOWGS84[1,2,3,4,5,6,7],\n"
 3166                "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
 3167                "        PRIMEM[\"Greenwich\",0,\n"
 3168                "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
 3169                "        UNIT[\"degree\",0.0174532925199433,\n"
 3170                "            AUTHORITY[\"EPSG\",\"9122\"]],\n"
 3171                "        AXIS[\"Latitude\",NORTH],\n"
 3172                "        AXIS[\"Longitude\",EAST]],\n"
 3173                "    PROJECTION[\"Transverse_Mercator\"],\n"
 3174                "    PARAMETER[\"latitude_of_origin\",0],\n"
 3175                "    PARAMETER[\"central_meridian\",3],\n"
 3176                "    PARAMETER[\"scale_factor\",0.9996],\n"
 3177                "    PARAMETER[\"false_easting\",500000],\n"
 3178                "    PARAMETER[\"false_northing\",0],\n"
 3179                "    UNIT[\"metre\",1,\n"
 3180                "        AUTHORITY[\"EPSG\",\"9001\"]],\n"
 3181                "    AXIS[\"Easting\",EAST],\n"
 3182                "    AXIS[\"Northing\",NORTH]]";
 3183 
 3184     auto obj = WKTParser().createFromWKT(wkt);
 3185     auto crs = nn_dynamic_pointer_cast<BoundCRS>(obj);
 3186     ASSERT_TRUE(crs != nullptr);
 3187 
 3188     EXPECT_EQ(crs->baseCRS()->nameStr(), "my PROJCRS");
 3189 
 3190     EXPECT_EQ(crs->hubCRS()->nameStr(), GeographicCRS::EPSG_4326->nameStr());
 3191 
 3192     ASSERT_TRUE(crs->transformation()->sourceCRS() != nullptr);
 3193     EXPECT_EQ(crs->transformation()->sourceCRS()->nameStr(), "my GEOGCRS");
 3194 
 3195     auto params = crs->transformation()->getTOWGS84Parameters();
 3196     auto expected = std::vector<double>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0};
 3197     ASSERT_EQ(params.size(), expected.size());
 3198     for (int i = 0; i < 7; i++) {
 3199         EXPECT_NEAR(params[i], expected[i], 1e-10);
 3200     }
 3201 }
 3202 
 3203 // ---------------------------------------------------------------------------
 3204 
 3205 TEST(wkt_parse, WKT1_VERT_DATUM_EXTENSION) {
 3206     auto wkt = "VERT_CS[\"EGM2008 geoid height\",\n"
 3207                "    VERT_DATUM[\"EGM2008 geoid\",2005,\n"
 3208                "        EXTENSION[\"PROJ4_GRIDS\",\"egm08_25.gtx\"],\n"
 3209                "        AUTHORITY[\"EPSG\",\"1027\"]],\n"
 3210                "    UNIT[\"metre\",1,\n"
 3211                "        AUTHORITY[\"EPSG\",\"9001\"]],\n"
 3212                "    AXIS[\"Up\",UP],\n"
 3213                "    AUTHORITY[\"EPSG\",\"3855\"]]";
 3214 
 3215     auto obj = WKTParser().createFromWKT(wkt);
 3216     auto crs = nn_dynamic_pointer_cast<BoundCRS>(obj);
 3217     ASSERT_TRUE(crs != nullptr);
 3218 
 3219     EXPECT_EQ(crs->baseCRS()->nameStr(), "EGM2008 geoid height");
 3220 
 3221     EXPECT_EQ(crs->hubCRS()->nameStr(), GeographicCRS::EPSG_4979->nameStr());
 3222 
 3223     ASSERT_TRUE(crs->transformation()->sourceCRS() != nullptr);
 3224     EXPECT_EQ(crs->transformation()->sourceCRS()->nameStr(),
 3225               crs->baseCRS()->nameStr());
 3226 
 3227     ASSERT_TRUE(crs->transformation()->targetCRS() != nullptr);
 3228     EXPECT_EQ(crs->transformation()->targetCRS()->nameStr(),
 3229               crs->hubCRS()->nameStr());
 3230 
 3231     EXPECT_EQ(crs->transformation()->nameStr(),
 3232               "EGM2008 geoid height to WGS84 ellipsoidal height");
 3233     EXPECT_EQ(crs->transformation()->method()->nameStr(),
 3234               "GravityRelatedHeight to Geographic3D");
 3235     ASSERT_EQ(crs->transformation()->parameterValues().size(), 1U);
 3236     {
 3237         const auto &opParamvalue =
 3238             nn_dynamic_pointer_cast<OperationParameterValue>(
 3239                 crs->transformation()->parameterValues()[0]);
 3240         ASSERT_TRUE(opParamvalue);
 3241         const auto &paramName = opParamvalue->parameter()->nameStr();
 3242         const auto &parameterValue = opParamvalue->parameterValue();
 3243         EXPECT_TRUE(opParamvalue->parameter()->getEPSGCode() == 8666);
 3244         EXPECT_EQ(paramName, "Geoid (height correction) model file");
 3245         EXPECT_EQ(parameterValue->type(), ParameterValue::Type::FILENAME);
 3246         EXPECT_EQ(parameterValue->valueFile(), "egm08_25.gtx");
 3247     }
 3248 }
 3249 
 3250 // ---------------------------------------------------------------------------
 3251 
 3252 TEST(wkt_parse, WKT1_DATUM_EXTENSION) {
 3253     auto wkt =
 3254         "PROJCS[\"unnamed\",\n"
 3255         "    GEOGCS[\"International 1909 (Hayford)\",\n"
 3256         "        DATUM[\"unknown\",\n"
 3257         "            SPHEROID[\"intl\",6378388,297],\n"
 3258         "            EXTENSION[\"PROJ4_GRIDS\",\"nzgd2kgrid0005.gsb\"]],\n"
 3259         "        PRIMEM[\"Greenwich\",0],\n"
 3260         "        UNIT[\"degree\",0.0174532925199433]],\n"
 3261         "    PROJECTION[\"New_Zealand_Map_Grid\"],\n"
 3262         "    PARAMETER[\"latitude_of_origin\",-41],\n"
 3263         "    PARAMETER[\"central_meridian\",173],\n"
 3264         "    PARAMETER[\"false_easting\",2510000],\n"
 3265         "    PARAMETER[\"false_northing\",6023150],\n"
 3266         "    UNIT[\"Meter\",1]]";
 3267 
 3268     auto obj = WKTParser().createFromWKT(wkt);
 3269     auto crs = nn_dynamic_pointer_cast<BoundCRS>(obj);
 3270     ASSERT_TRUE(crs != nullptr);
 3271 
 3272     EXPECT_EQ(crs->baseCRS()->nameStr(), "unnamed");
 3273 
 3274     EXPECT_EQ(crs->hubCRS()->nameStr(), GeographicCRS::EPSG_4326->nameStr());
 3275 
 3276     ASSERT_TRUE(crs->transformation()->sourceCRS() != nullptr);
 3277     EXPECT_EQ(crs->transformation()->sourceCRS()->nameStr(),
 3278               "International 1909 (Hayford)");
 3279 
 3280     ASSERT_TRUE(crs->transformation()->targetCRS() != nullptr);
 3281     EXPECT_EQ(crs->transformation()->targetCRS()->nameStr(),
 3282               crs->hubCRS()->nameStr());
 3283 
 3284     EXPECT_EQ(crs->transformation()->nameStr(),
 3285               "International 1909 (Hayford) to WGS84");
 3286     EXPECT_EQ(crs->transformation()->method()->nameStr(), "NTv2");
 3287     ASSERT_EQ(crs->transformation()->parameterValues().size(), 1U);
 3288     {
 3289         const auto &opParamvalue =
 3290             nn_dynamic_pointer_cast<OperationParameterValue>(
 3291                 crs->transformation()->parameterValues()[0]);
 3292         ASSERT_TRUE(opParamvalue);
 3293         const auto &paramName = opParamvalue->parameter()->nameStr();
 3294         const auto &parameterValue = opParamvalue->parameterValue();
 3295         EXPECT_TRUE(opParamvalue->parameter()->getEPSGCode() == 8656);
 3296         EXPECT_EQ(paramName, "Latitude and longitude difference file");
 3297         EXPECT_EQ(parameterValue->type(), ParameterValue::Type::FILENAME);
 3298         EXPECT_EQ(parameterValue->valueFile(), "nzgd2kgrid0005.gsb");
 3299     }
 3300 }
 3301 
 3302 // ---------------------------------------------------------------------------
 3303 
 3304 TEST(wkt_parse, DerivedGeographicCRS_WKT2) {
 3305     auto wkt = "GEODCRS[\"WMO Atlantic Pole\",\n"
 3306                "    BASEGEODCRS[\"WGS 84\",\n"
 3307                "        DATUM[\"World Geodetic System 1984\",\n"
 3308                "            ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 3309                "                LENGTHUNIT[\"metre\",1]]],\n"
 3310                "        PRIMEM[\"Greenwich\",0,\n"
 3311                "            ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 3312                "    DERIVINGCONVERSION[\"Atlantic pole\",\n"
 3313                "        METHOD[\"Pole rotation\"],\n"
 3314                "        PARAMETER[\"Latitude of rotated pole\",52,\n"
 3315                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 3316                "                ID[\"EPSG\",9122]]],\n"
 3317                "        PARAMETER[\"Longitude of rotated pole\",-30,\n"
 3318                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 3319                "                ID[\"EPSG\",9122]]],\n"
 3320                "        PARAMETER[\"Axis rotation\",-25,\n"
 3321                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 3322                "                ID[\"EPSG\",9122]]]],\n"
 3323                "    CS[ellipsoidal,2],\n"
 3324                "        AXIS[\"latitude\",north,\n"
 3325                "            ORDER[1],\n"
 3326                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 3327                "                ID[\"EPSG\",9122]]],\n"
 3328                "        AXIS[\"longitude\",east,\n"
 3329                "            ORDER[2],\n"
 3330                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 3331                "                ID[\"EPSG\",9122]]]]";
 3332 
 3333     auto obj = WKTParser().createFromWKT(wkt);
 3334     auto crs = nn_dynamic_pointer_cast<DerivedGeographicCRS>(obj);
 3335     ASSERT_TRUE(crs != nullptr);
 3336 
 3337     EXPECT_EQ(crs->nameStr(), "WMO Atlantic Pole");
 3338 
 3339     EXPECT_EQ(crs->baseCRS()->nameStr(), "WGS 84");
 3340     EXPECT_TRUE(nn_dynamic_pointer_cast<GeographicCRS>(crs->baseCRS()) !=
 3341                 nullptr);
 3342 
 3343     EXPECT_EQ(crs->derivingConversion()->nameStr(), "Atlantic pole");
 3344 
 3345     EXPECT_TRUE(nn_dynamic_pointer_cast<EllipsoidalCS>(
 3346                     crs->coordinateSystem()) != nullptr);
 3347 }
 3348 
 3349 // ---------------------------------------------------------------------------
 3350 
 3351 TEST(wkt_parse, DerivedGeographicCRS_WKT2_2018) {
 3352     auto wkt = "GEOGCRS[\"WMO Atlantic Pole\",\n"
 3353                "    BASEGEOGCRS[\"WGS 84\",\n"
 3354                "        DATUM[\"World Geodetic System 1984\",\n"
 3355                "            ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 3356                "                LENGTHUNIT[\"metre\",1]]],\n"
 3357                "        PRIMEM[\"Greenwich\",0,\n"
 3358                "            ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 3359                "    DERIVINGCONVERSION[\"Atlantic pole\",\n"
 3360                "        METHOD[\"Pole rotation\"],\n"
 3361                "        PARAMETER[\"Latitude of rotated pole\",52,\n"
 3362                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 3363                "                ID[\"EPSG\",9122]]],\n"
 3364                "        PARAMETER[\"Longitude of rotated pole\",-30,\n"
 3365                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 3366                "                ID[\"EPSG\",9122]]],\n"
 3367                "        PARAMETER[\"Axis rotation\",-25,\n"
 3368                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 3369                "                ID[\"EPSG\",9122]]]],\n"
 3370                "    CS[ellipsoidal,2],\n"
 3371                "        AXIS[\"latitude\",north,\n"
 3372                "            ORDER[1],\n"
 3373                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 3374                "                ID[\"EPSG\",9122]]],\n"
 3375                "        AXIS[\"longitude\",east,\n"
 3376                "            ORDER[2],\n"
 3377                "            ANGLEUNIT[\"degree\",0.0174532925199433,\n"
 3378                "                ID[\"EPSG\",9122]]]]";
 3379 
 3380     auto obj = WKTParser().createFromWKT(wkt);
 3381     auto crs = nn_dynamic_pointer_cast<DerivedGeographicCRS>(obj);
 3382     ASSERT_TRUE(crs != nullptr);
 3383 
 3384     EXPECT_EQ(crs->nameStr(), "WMO Atlantic Pole");
 3385 
 3386     EXPECT_EQ(crs->baseCRS()->nameStr(), "WGS 84");
 3387     EXPECT_TRUE(nn_dynamic_pointer_cast<GeographicCRS>(crs->baseCRS()) !=
 3388                 nullptr);
 3389 
 3390     EXPECT_EQ(crs->derivingConversion()->nameStr(), "Atlantic pole");
 3391 
 3392     EXPECT_TRUE(nn_dynamic_pointer_cast<EllipsoidalCS>(
 3393                     crs->coordinateSystem()) != nullptr);
 3394 }
 3395 
 3396 // ---------------------------------------------------------------------------
 3397 
 3398 TEST(wkt_parse, DerivedGeodeticCRS) {
 3399     auto wkt = "GEODCRS[\"Derived geodetic CRS\",\n"
 3400                "    BASEGEODCRS[\"WGS 84\",\n"
 3401                "        DATUM[\"World Geodetic System 1984\",\n"
 3402                "            ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 3403                "                LENGTHUNIT[\"metre\",1]]],\n"
 3404                "        PRIMEM[\"Greenwich\",0,\n"
 3405                "            ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 3406                "    DERIVINGCONVERSION[\"Some conversion\",\n"
 3407                "        METHOD[\"Some method\"],\n"
 3408                "        PARAMETER[\"foo\",1.0,UNIT[\"metre\",1]]],\n"
 3409                "    CS[Cartesian,3],\n"
 3410                "        AXIS[\"(X)\",geocentricX,\n"
 3411                "            ORDER[1],\n"
 3412                "            LENGTHUNIT[\"metre\",1,\n"
 3413                "                ID[\"EPSG\",9001]]],\n"
 3414                "        AXIS[\"(Y)\",geocentricY,\n"
 3415                "            ORDER[2],\n"
 3416                "            LENGTHUNIT[\"metre\",1,\n"
 3417                "                ID[\"EPSG\",9001]]],\n"
 3418                "        AXIS[\"(Z)\",geocentricZ,\n"
 3419                "            ORDER[3],\n"
 3420                "            LENGTHUNIT[\"metre\",1,\n"
 3421                "                ID[\"EPSG\",9001]]]]";
 3422     ;
 3423 
 3424     auto obj = WKTParser().createFromWKT(wkt);
 3425     auto crs = nn_dynamic_pointer_cast<DerivedGeodeticCRS>(obj);
 3426     ASSERT_TRUE(crs != nullptr);
 3427 
 3428     EXPECT_EQ(crs->nameStr(), "Derived geodetic CRS");
 3429 
 3430     EXPECT_EQ(crs->baseCRS()->nameStr(), "WGS 84");
 3431     EXPECT_TRUE(nn_dynamic_pointer_cast<GeographicCRS>(crs->baseCRS()) !=
 3432                 nullptr);
 3433 
 3434     EXPECT_EQ(crs->derivingConversion()->nameStr(), "Some conversion");
 3435 
 3436     EXPECT_TRUE(nn_dynamic_pointer_cast<CartesianCS>(crs->coordinateSystem()) !=
 3437                 nullptr);
 3438 }
 3439 
 3440 // ---------------------------------------------------------------------------
 3441 
 3442 TEST(wkt_parse, DerivedProjectedCRS) {
 3443     auto wkt =
 3444         "DERIVEDPROJCRS[\"derived projectedCRS\",\n"
 3445         "    BASEPROJCRS[\"WGS 84 / UTM zone 31N\",\n"
 3446         "        BASEGEOGCRS[\"WGS 84\",\n"
 3447         "            DATUM[\"World Geodetic System 1984\",\n"
 3448         "                ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 3449         "                    LENGTHUNIT[\"metre\",1]]],\n"
 3450         "            PRIMEM[\"Greenwich\",0,\n"
 3451         "                ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
 3452         "        CONVERSION[\"UTM zone 31N\",\n"
 3453         "            METHOD[\"Transverse Mercator\",\n"
 3454         "                ID[\"EPSG\",9807]],\n"
 3455         "            PARAMETER[\"Latitude of natural origin\",0,\n"
 3456         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 3457         "                ID[\"EPSG\",8801]],\n"
 3458         "            PARAMETER[\"Longitude of natural origin\",3,\n"
 3459         "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 3460         "                ID[\"EPSG\",8802]],\n"
 3461         "            PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
 3462         "                SCALEUNIT[\"unity\",1],\n"
 3463         "                ID[\"EPSG\",8805]],\n"
 3464         "            PARAMETER[\"False easting\",500000,\n"
 3465         "                LENGTHUNIT[\"metre\",1],\n"
 3466         "                ID[\"EPSG\",8806]],\n"
 3467         "            PARAMETER[\"False northing\",0,\n"
 3468         "                LENGTHUNIT[\"metre\",1],\n"
 3469         "                ID[\"EPSG\",8807]]]],\n"
 3470         "    DERIVINGCONVERSION[\"unnamed\",\n"
 3471         "        METHOD[\"PROJ unimplemented\"],\n"
 3472         "        PARAMETER[\"foo\",1.0,UNIT[\"metre\",1]]],\n"
 3473         "    CS[Cartesian,2],\n"
 3474         "        AXIS[\"(E)\",east,\n"
 3475         "            ORDER[1],\n"
 3476         "            LENGTHUNIT[\"metre\",1,\n"
 3477         "                ID[\"EPSG\",9001]]],\n"
 3478         "        AXIS[\"(N)\",north,\n"
 3479         "            ORDER[2],\n"
 3480         "            LENGTHUNIT[\"metre\",1,\n"
 3481         "                ID[\"EPSG\",9001]]]]";
 3482 
 3483     auto obj = WKTParser().createFromWKT(wkt);
 3484     auto crs = nn_dynamic_pointer_cast<DerivedProjectedCRS>(obj);
 3485     ASSERT_TRUE(crs != nullptr);
 3486 
 3487     EXPECT_EQ(crs->nameStr(), "derived projectedCRS");
 3488 
 3489     EXPECT_EQ(crs->baseCRS()->nameStr(), "WGS 84 / UTM zone 31N");
 3490     EXPECT_TRUE(nn_dynamic_pointer_cast<ProjectedCRS>(crs->baseCRS()) !=
 3491                 nullptr);
 3492 
 3493     EXPECT_EQ(crs->derivingConversion()->nameStr(), "unnamed");
 3494 
 3495     EXPECT_TRUE(nn_dynamic_pointer_cast<CartesianCS>(crs->coordinateSystem()) !=
 3496                 nullptr);
 3497 }
 3498 
 3499 // ---------------------------------------------------------------------------
 3500 
 3501 TEST(wkt_parse, DerivedProjectedCRS_ordinal) {
 3502     auto wkt = "DERIVEDPROJCRS[\"derived projectedCRS\",\n"
 3503                "    BASEPROJCRS[\"BASEPROJCRS\",\n"
 3504                "        BASEGEOGCRS[\"WGS 84\",\n"
 3505                "            DATUM[\"World Geodetic System 1984\",\n"
 3506                "                ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
 3507                "                    LENGTHUNIT[\"metre\",1]]],\n"
 3508                "            PRIMEM[\"Greenwich\",0,\n"
 3509                "                ANGLEUNIT[\"degree\",0.0174532925199433],\n"
 3510                "                ID[\"EPSG\",8901]]],\n"
 3511                "        CONVERSION[\"unnamed\",\n"
 3512                "            METHOD[\"PROJ unimplemented\"],\n"
 3513                "            PARAMETER[\"foo\",1,\n"
 3514                "                LENGTHUNIT[\"metre\",1,\n"
 3515                "                    ID[\"EPSG\",9001]]]]],\n"
 3516                "    DERIVINGCONVERSION[\"unnamed\",\n"
 3517                "        METHOD[\"PROJ unimplemented\"],\n"
 3518                "        PARAMETER[\"foo\",1,\n"
 3519                "            LENGTHUNIT[\"metre\",1,\n"
 3520                "                ID[\"EPSG\",9001]]]],\n"
 3521                "    CS[ordinal,2],\n"
 3522                "        AXIS[\"inline (I)\",northNorthWest,\n"
 3523                "            ORDER[1]],\n"
 3524                "        AXIS[\"crossline (J)\",westSouthWest,\n"
 3525                "            ORDER[2]]]";
 3526 
 3527     auto obj = WKTParser().createFromWKT(wkt);
 3528     auto crs = nn_dynamic_pointer_cast<DerivedProjectedCRS>(obj);
 3529     ASSERT_TRUE(crs != nullptr);
 3530     EXPECT_TRUE(nn_dynamic_pointer_cast<OrdinalCS>(crs->coordinateSystem()) !=
 3531                 nullptr);
 3532 
 3533     EXPECT_EQ(
 3534         crs->exportToWKT(
 3535             WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()),
 3536         wkt);
 3537 }
 3538 
 3539 // ---------------------------------------------------------------------------
 3540 
 3541 TEST(wkt_parse, TemporalDatum) {
 3542     auto wkt = "TDATUM[\"Gregorian calendar\",\n"
 3543                "    CALENDAR[\"my calendar\"],\n"
 3544                "    TIMEORIGIN[0000-01-01]]";
 3545 
 3546     auto obj = WKTParser().createFromWKT(wkt);
 3547     auto tdatum = nn_dynamic_pointer_cast<TemporalDatum>(obj);
 3548     ASSERT_TRUE(tdatum != nullptr);
 3549 
 3550     EXPECT_EQ(tdatum->nameStr(), "Gregorian calendar");
 3551     EXPECT_EQ(tdatum->temporalOrigin().toString(), "0000-01-01");
 3552     EXPECT_EQ(tdatum->calendar(), "my calendar");
 3553 }
 3554 
 3555 // ---------------------------------------------------------------------------
 3556 
 3557 TEST(wkt_parse, TemporalDatum_no_calendar) {
 3558     auto wkt = "TDATUM[\"Gregorian calendar\",\n"
 3559                "    TIMEORIGIN[0000-01-01]]";
 3560 
 3561     auto obj = WKTParser().createFromWKT(wkt);
 3562     auto tdatum = nn_dynamic_pointer_cast<TemporalDatum>(obj);
 3563     ASSERT_TRUE(tdatum != nullptr);
 3564 
 3565     EXPECT_EQ(tdatum->nameStr(), "Gregorian calendar");
 3566     EXPECT_EQ(tdatum->temporalOrigin().toString(), "0000-01-01");
 3567     EXPECT_EQ(tdatum->calendar(), "proleptic Gregorian");
 3568 }
 3569 
 3570 // ---------------------------------------------------------------------------
 3571 
 3572 TEST(wkt_parse, dateTimeTemporalCRS_WKT2) {
 3573     auto wkt = "TIMECRS[\"Temporal CRS\",\n"
 3574                "    TDATUM[\"Gregorian calendar\",\n"
 3575                "        TIMEORIGIN[0000-01-01]],\n"
 3576                "    CS[temporal,1],\n"
 3577                "        AXIS[\"time (T)\",future]]";
 3578 
 3579     auto obj = WKTParser().createFromWKT(wkt);
 3580     auto crs = nn_dynamic_pointer_cast<TemporalCRS>(obj);
 3581     ASSERT_TRUE(crs != nullptr);
 3582 
 3583     EXPECT_EQ(crs->nameStr(), "Temporal CRS");
 3584     auto tdatum = crs->datum();
 3585     EXPECT_EQ(tdatum->nameStr(), "Gregorian calendar");
 3586     EXPECT_EQ(tdatum->temporalOrigin().toString(), "0000-01-01");
 3587     EXPECT_EQ(tdatum->calendar(), "proleptic Gregorian");
 3588     EXPECT_TRUE(nn_dynamic_pointer_cast<DateTimeTemporalCS>(
 3589                     crs->coordinateSystem()) != nullptr);
 3590     ASSERT_EQ(crs->coordinateSystem()->axisList().size(), 1U);
 3591     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->unit().type(),
 3592               UnitOfMeasure::Type::NONE);
 3593     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->unit().name(), "");
 3594 }
 3595 
 3596 // ---------------------------------------------------------------------------
 3597 
 3598 TEST(wkt_parse, dateTimeTemporalCRS_WKT2_2018) {
 3599     auto wkt = "TIMECRS[\"Temporal CRS\",\n"
 3600                "    TDATUM[\"Gregorian calendar\",\n"
 3601                "        CALENDAR[\"proleptic Gregorian\"],\n"
 3602                "        TIMEORIGIN[0000-01-01]],\n"
 3603                "    CS[TemporalDateTime,1],\n"
 3604                "        AXIS[\"time (T)\",future]]";
 3605 
 3606     auto obj = WKTParser().createFromWKT(wkt);
 3607     auto crs = nn_dynamic_pointer_cast<TemporalCRS>(obj);
 3608     ASSERT_TRUE(crs != nullptr);
 3609 
 3610     EXPECT_EQ(crs->nameStr(), "Temporal CRS");
 3611     auto tdatum = crs->datum();
 3612     EXPECT_EQ(tdatum->nameStr(), "Gregorian calendar");
 3613     EXPECT_EQ(tdatum->temporalOrigin().toString(), "0000-01-01");
 3614     EXPECT_EQ(tdatum->calendar(), "proleptic Gregorian");
 3615     EXPECT_TRUE(nn_dynamic_pointer_cast<DateTimeTemporalCS>(
 3616                     crs->coordinateSystem()) != nullptr);
 3617     ASSERT_EQ(crs->coordinateSystem()->axisList().size(), 1U);
 3618     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->unit().type(),
 3619               UnitOfMeasure::Type::NONE);
 3620     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->unit().name(), "");
 3621 }
 3622 
 3623 // ---------------------------------------------------------------------------
 3624 
 3625 TEST(wkt_parse, temporalCountCRSWithConvFactor_WKT2_2018) {
 3626     auto wkt = "TIMECRS[\"GPS milliseconds\",\n"
 3627                "    TDATUM[\"GPS time origin\",\n"
 3628                "        TIMEORIGIN[1980-01-01T00:00:00.0Z]],\n"
 3629                "    CS[TemporalCount,1],\n"
 3630                "        AXIS[\"(T)\",future,\n"
 3631                "            TIMEUNIT[\"milliseconds (ms)\",0.001]]]";
 3632 
 3633     auto obj = WKTParser().createFromWKT(wkt);
 3634     auto crs = nn_dynamic_pointer_cast<TemporalCRS>(obj);
 3635     ASSERT_TRUE(crs != nullptr);
 3636 
 3637     EXPECT_EQ(crs->nameStr(), "GPS milliseconds");
 3638     auto tdatum = crs->datum();
 3639     EXPECT_EQ(tdatum->nameStr(), "GPS time origin");
 3640     EXPECT_EQ(tdatum->temporalOrigin().toString(), "1980-01-01T00:00:00.0Z");
 3641     EXPECT_EQ(tdatum->calendar(), "proleptic Gregorian");
 3642     EXPECT_TRUE(nn_dynamic_pointer_cast<TemporalCountCS>(
 3643                     crs->coordinateSystem()) != nullptr);
 3644     ASSERT_EQ(crs->coordinateSystem()->axisList().size(), 1U);
 3645     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->unit().name(),
 3646               "milliseconds (ms)");
 3647     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->unit().conversionToSI(),
 3648               0.001);
 3649 }
 3650 
 3651 // ---------------------------------------------------------------------------
 3652 
 3653 TEST(wkt_parse, temporalCountCRSWithoutConvFactor_WKT2_2018) {
 3654     auto wkt = "TIMECRS[\"Calendar hours from 1979-12-29\",\n"
 3655                "    TDATUM[\"29 December 1979\",\n"
 3656                "        CALENDAR[\"proleptic Gregorian\"],\n"
 3657                "        TIMEORIGIN[1979-12-29T00Z]],\n"
 3658                "    CS[TemporalCount,1],\n"
 3659                "        AXIS[\"time\",future,\n"
 3660                "            TIMEUNIT[\"hour\"]]]";
 3661 
 3662     auto obj = WKTParser().createFromWKT(wkt);
 3663     auto crs = nn_dynamic_pointer_cast<TemporalCRS>(obj);
 3664     ASSERT_TRUE(crs != nullptr);
 3665 
 3666     EXPECT_EQ(crs->nameStr(), "Calendar hours from 1979-12-29");
 3667     auto tdatum = crs->datum();
 3668     EXPECT_EQ(tdatum->nameStr(), "29 December 1979");
 3669     EXPECT_EQ(tdatum->temporalOrigin().toString(), "1979-12-29T00Z");
 3670     EXPECT_TRUE(nn_dynamic_pointer_cast<TemporalCountCS>(
 3671                     crs->coordinateSystem()) != nullptr);
 3672     ASSERT_EQ(crs->coordinateSystem()->axisList().size(), 1U);
 3673     EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->unit().name(), "hour");
 3674     EXPECT_EQ(crs->coordinateSystem()->axisList()[0