diff options
Diffstat (limited to 'test/unit')
| -rw-r--r-- | test/unit/CMakeLists.txt | 72 | ||||
| -rw-r--r-- | test/unit/gie_self_tests.cpp | 53 | ||||
| -rw-r--r-- | test/unit/proj_errno_string_test.cpp | 1 | ||||
| -rw-r--r-- | test/unit/test_c_api.cpp | 150 | ||||
| -rw-r--r-- | test/unit/test_crs.cpp | 184 | ||||
| -rw-r--r-- | test/unit/test_datum.cpp | 4 | ||||
| -rw-r--r-- | test/unit/test_factory.cpp | 127 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 271 | ||||
| -rw-r--r-- | test/unit/test_operation.cpp | 1072 |
9 files changed, 1624 insertions, 310 deletions
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 58638fa4..40a3dd06 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -2,32 +2,26 @@ option(USE_EXTERNAL_GTEST "Compile against external GTest" OFF) -if (USE_EXTERNAL_GTEST) +if(USE_EXTERNAL_GTEST) message(STATUS "Using external GTest") find_package(GTest 1.8.1 CONFIG REQUIRED) -else (USE_EXTERNAL_GTEST) +else() message(STATUS "Using internal GTest") -# FIXME: Deal with our old-school CMakeLists.txt behaving badly -set(_save_c_flags "${CMAKE_C_FLAGS}") -set(_save_cxx_flags "${CMAKE_CXX_FLAGS}") -string(REGEX REPLACE "\\-W[a-z\\-]+" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) -string(REGEX REPLACE "\\-W[a-z\\-]+" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - # # Build Google Test # # Source https://github.com/google/googletest/blob/master/googletest/README.md # Download and unpack googletest at configure time configure_file( - ${CMAKE_SOURCE_DIR}/test/googletest/CMakeLists.txt.in - ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt) + ${CMAKE_SOURCE_DIR}/test/googletest/CMakeLists.txt.in + ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt) execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) if(result) message(FATAL_ERROR "CMake step for googletest failed: ${result}") endif() @@ -43,31 +37,31 @@ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Add googletest directly to our build. This defines # the gtest and gtest_main targets. option(INSTALL_GTEST "Enable installation of googletest" OFF) -add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src - ${CMAKE_BINARY_DIR}/googletest-build - EXCLUDE_FROM_ALL) - -# FIXME: Deal with our old-school CMakeLists.txt behaving badly -set(CMAKE_C_FLAGS "${_save_c_flags}") -set(CMAKE_CXX_FLAGS "${_save_cxx_flags}") -unset(_save_c_flags) -unset(_save_cxx_flags) +add_subdirectory( + ${CMAKE_BINARY_DIR}/googletest-src + ${CMAKE_BINARY_DIR}/googletest-build + EXCLUDE_FROM_ALL) # Provide the same target name as find_package(GTest) add_library(GTest::gtest ALIAS gtest) -endif(USE_EXTERNAL_GTEST) +endif() # USE_EXTERNAL_GTEST # # Build PROJ unit tests # if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC" AND BUILD_LIBPROJ_SHARED) -add_definitions(-DPROJ_MSVC_DLL_IMPORT=1) + add_definitions(-DPROJ_MSVC_DLL_IMPORT=1) endif() include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${SQLITE3_INCLUDE_DIR}) +# Add the directory containing proj_config.h +include_directories(${CMAKE_BINARY_DIR}/src) + +# Apply to targets in the current directory and below +add_compile_options(${PROJ_CXX_WARN_FLAGS}) add_executable(proj_pj_transform_test main.cpp @@ -86,31 +80,31 @@ target_link_libraries(proj_errno_string_test add_test(NAME proj_errno_string_test COMMAND proj_errno_string_test) add_executable(proj_angular_io_test - main.cpp - proj_angular_io_test.cpp) + main.cpp + proj_angular_io_test.cpp) target_link_libraries(proj_angular_io_test - GTest::gtest - ${PROJ_LIBRARIES}) + GTest::gtest + ${PROJ_LIBRARIES}) add_test(NAME proj_angular_io_test COMMAND proj_angular_io_test) add_executable(proj_context_test - main.cpp - proj_context_test.cpp) + main.cpp + proj_context_test.cpp) target_link_libraries(proj_context_test - GTest::gtest - ${PROJ_LIBRARIES}) + GTest::gtest + ${PROJ_LIBRARIES}) add_test(NAME proj_context_test COMMAND proj_context_test) -if (MSVC AND BUILD_LIBPROJ_SHARED) -# ph_phi2_test not compatible of a .dll build +if(MSVC AND BUILD_LIBPROJ_SHARED) + # ph_phi2_test not compatible of a .dll build else() -add_executable(pj_phi2_test - main.cpp - pj_phi2_test.cpp) -target_link_libraries(pj_phi2_test - GTest::gtest - ${PROJ_LIBRARIES}) -add_test(NAME pj_phi2_test COMMAND pj_phi2_test) + add_executable(pj_phi2_test + main.cpp + pj_phi2_test.cpp) + target_link_libraries(pj_phi2_test + GTest::gtest + ${PROJ_LIBRARIES}) + add_test(NAME pj_phi2_test COMMAND pj_phi2_test) endif() add_executable(proj_test_cpp_api diff --git a/test/unit/gie_self_tests.cpp b/test/unit/gie_self_tests.cpp index ad637786..399f51e5 100644 --- a/test/unit/gie_self_tests.cpp +++ b/test/unit/gie_self_tests.cpp @@ -421,9 +421,11 @@ TEST(gie, info_functions) { /* check a few key characteristics of the Mercator projection */ EXPECT_NEAR(factors.angular_distortion, 0.0, 1e-7) << factors.angular_distortion; /* angular distortion should be 0 */ + + /* Meridian/parallel angle should be 90 deg */ EXPECT_NEAR(factors.meridian_parallel_angle, M_PI_2, 1e-7) - << factors.meridian_parallel_angle; /* Meridian/parallel angle should be - 90 deg */ + << factors.meridian_parallel_angle; + EXPECT_EQ(factors.meridian_convergence, 0.0); /* meridian convergence should be 0 */ @@ -701,13 +703,16 @@ TEST(gie, proj_create_crs_to_crs_PULKOVO42_ETRS89) { EXPECT_NEAR(c.xy.x, 44.999701238, 1e-9); EXPECT_NEAR(c.xy.y, 24.998474948, 1e-9); EXPECT_EQ(std::string(proj_pj_info(P).definition), - "proj=pipeline step proj=push v_3 step proj=axisswap order=2,1 " - "step proj=unitconvert xy_in=deg xy_out=rad step proj=cart " + "proj=pipeline step proj=axisswap order=2,1 " + "step proj=unitconvert xy_in=deg xy_out=rad " + "step proj=push v_3 " + "step proj=cart " "ellps=krass step proj=helmert x=2.3287 y=-147.0425 z=-92.0802 " "rx=0.3092483 ry=-0.32482185 rz=-0.49729934 s=5.68906266 " - "convention=coordinate_frame step inv proj=cart ellps=GRS80 step " - "proj=unitconvert xy_in=rad xy_out=deg step proj=axisswap " - "order=2,1 step proj=pop v_3"); + "convention=coordinate_frame step inv proj=cart ellps=GRS80 " + "step proj=pop v_3 " + "step proj=unitconvert xy_in=rad xy_out=deg step proj=axisswap " + "order=2,1"); c = proj_trans(P, PJ_INV, c); EXPECT_NEAR(c.xy.x, 45, 1e-8); @@ -730,12 +735,38 @@ TEST(gie, proj_create_crs_to_crs_PULKOVO42_ETRS89) { EXPECT_NEAR(c.xy.x, 51.999714150, 1e-9); EXPECT_NEAR(c.xy.y, 19.998187811, 1e-9); EXPECT_EQ(std::string(proj_pj_info(P).definition), - "proj=pipeline step proj=push v_3 step proj=axisswap order=2,1 " - "step proj=unitconvert xy_in=deg xy_out=rad step proj=cart " + "proj=pipeline step proj=axisswap order=2,1 " + "step proj=unitconvert xy_in=deg xy_out=rad " + "step proj=push v_3 " + "step proj=cart " "ellps=krass step proj=helmert x=33.4 y=-146.6 z=-76.3 rx=-0.359 " "ry=-0.053 rz=0.844 s=-0.84 convention=position_vector step inv " - "proj=cart ellps=GRS80 step proj=unitconvert xy_in=rad " - "xy_out=deg step proj=axisswap order=2,1 step proj=pop v_3"); + "proj=cart ellps=GRS80 step proj=pop v_3 " + "step proj=unitconvert xy_in=rad " + "xy_out=deg step proj=axisswap order=2,1"); + + proj_destroy(P); +} + +// --------------------------------------------------------------------------- + +TEST(gie, proj_create_crs_to_crs_outside_area_of_use) { + + // See https://github.com/OSGeo/proj.4/issues/1329 + auto P = proj_create_crs_to_crs(PJ_DEFAULT_CTX, "EPSG:4275", "EPSG:4807", + nullptr); + ASSERT_TRUE(P != nullptr); + PJ_COORD c; + + EXPECT_EQ(P->fwd, nullptr); + + // Test point outside area of use of both candidate coordinate operations + c.xyz.x = 58; // Lat in deg + c.xyz.y = 5; // Long in deg + c.xyz.z = 0; + c = proj_trans(P, PJ_FWD, c); + EXPECT_NEAR(c.xy.x, 64.44444444444444, 1e-9); // Lat in grad + EXPECT_NEAR(c.xy.y, 2.958634259259258, 1e-9); // Long in grad proj_destroy(P); } diff --git a/test/unit/proj_errno_string_test.cpp b/test/unit/proj_errno_string_test.cpp index dcdff5c4..a592b31f 100644 --- a/test/unit/proj_errno_string_test.cpp +++ b/test/unit/proj_errno_string_test.cpp @@ -29,6 +29,7 @@ #include <cstring> #include "proj.h" +#include "proj_config.h" #include "gtest_include.h" diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index 6205a9b8..b9ea0bd5 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -838,14 +838,16 @@ TEST_F(CApi, proj_create_from_database) { ASSERT_NE(info.definition, nullptr); EXPECT_EQ( info.definition, - std::string("proj=pipeline step proj=push v_3 step proj=axisswap " + std::string("proj=pipeline step proj=axisswap " "order=2,1 step proj=unitconvert xy_in=deg xy_out=rad " + "step proj=push v_3 " "step proj=cart ellps=bessel step proj=helmert " "x=601.705 y=84.263 z=485.227 rx=-4.7354 ry=-1.3145 " - "rz=-5.393 s=-2.3887 convention=coordinate_frame step " - "inv proj=cart ellps=GRS80 step proj=unitconvert " - "xy_in=rad xy_out=deg step proj=axisswap order=2,1 " - "step proj=pop v_3")); + "rz=-5.393 s=-2.3887 convention=coordinate_frame " + "step inv proj=cart ellps=GRS80 " + "step proj=pop v_3 " + "step proj=unitconvert xy_in=rad xy_out=deg " + "step proj=axisswap order=2,1")); EXPECT_EQ(info.accuracy, 1); } } @@ -1300,13 +1302,13 @@ TEST_F(CApi, proj_coordoperation_get_grid_used) { // --------------------------------------------------------------------------- -TEST_F(CApi, proj_coordoperation_is_instanciable) { +TEST_F(CApi, proj_coordoperation_is_instantiable) { auto op = proj_create_from_database(m_ctxt, "EPSG", "1671", PJ_CATEGORY_COORDINATE_OPERATION, true, nullptr); ASSERT_NE(op, nullptr); ObjectKeeper keeper(op); - EXPECT_EQ(proj_coordoperation_is_instanciable(m_ctxt, op), 1); + EXPECT_EQ(proj_coordoperation_is_instantiable(m_ctxt, op), 1); } // --------------------------------------------------------------------------- @@ -1343,6 +1345,7 @@ TEST_F(CApi, proj_create_operations) { auto op = proj_list_get(m_ctxt, res, 0); ASSERT_NE(op, nullptr); ObjectKeeper keeper_op(op); + EXPECT_FALSE(proj_coordoperation_has_ballpark_transformation(m_ctxt, op)); EXPECT_EQ(proj_get_name(op), std::string("NAD27 to NAD83 (3)")); } @@ -1399,8 +1402,9 @@ TEST_F(CApi, proj_create_operations_with_pivot) { ASSERT_NE(op, nullptr); ObjectKeeper keeper_op(op); - EXPECT_EQ(proj_get_name(op), - std::string("Null geographic offset from WGS 84 to JGD2011")); + EXPECT_EQ( + proj_get_name(op), + std::string("Ballpark geographic offset from WGS 84 to JGD2011")); } // Restrict pivot to Tokyo CRS @@ -1421,7 +1425,7 @@ TEST_F(CApi, proj_create_operations_with_pivot) { ASSERT_NE(res, nullptr); ObjListKeeper keeper_res(res); EXPECT_EQ(proj_list_get_count(res), 7); - auto op = proj_list_get(m_ctxt, res, 1); + auto op = proj_list_get(m_ctxt, res, 0); ASSERT_NE(op, nullptr); ObjectKeeper keeper_op(op); @@ -1451,7 +1455,7 @@ TEST_F(CApi, proj_create_operations_with_pivot) { ASSERT_NE(res, nullptr); ObjListKeeper keeper_res(res); // includes results from ESRI - EXPECT_EQ(proj_list_get_count(res), 5); + EXPECT_EQ(proj_list_get_count(res), 4); auto op = proj_list_get(m_ctxt, res, 0); ASSERT_NE(op, nullptr); ObjectKeeper keeper_op(op); @@ -1646,6 +1650,38 @@ TEST_F(CApi, proj_identify) { ObjListKeeper keeper_res(res); EXPECT_EQ(res, nullptr); } + { + auto obj2 = proj_create( + m_ctxt, "+proj=longlat +datum=WGS84 +no_defs +type=crs"); + ObjectKeeper keeper2(obj2); + ASSERT_NE(obj2, nullptr); + int *confidence = nullptr; + auto res = proj_identify(m_ctxt, obj2, nullptr, nullptr, &confidence); + ObjListKeeper keeper_res(res); + EXPECT_EQ(proj_list_get_count(res), 4); + proj_int_list_destroy(confidence); + } + { + auto obj2 = proj_create_from_database(m_ctxt, "IGNF", "ETRS89UTM28", + PJ_CATEGORY_CRS, false, nullptr); + ObjectKeeper keeper2(obj2); + ASSERT_NE(obj2, nullptr); + int *confidence = nullptr; + auto res = proj_identify(m_ctxt, obj2, "EPSG", nullptr, &confidence); + ObjListKeeper keeper_res(res); + EXPECT_EQ(proj_list_get_count(res), 1); + auto gotCRS = proj_list_get(m_ctxt, res, 0); + ASSERT_NE(gotCRS, nullptr); + ObjectKeeper keeper_gotCRS(gotCRS); + auto auth = proj_get_id_auth_name(gotCRS, 0); + ASSERT_TRUE(auth != nullptr); + EXPECT_EQ(auth, std::string("EPSG")); + auto code = proj_get_id_code(gotCRS, 0); + ASSERT_TRUE(code != nullptr); + EXPECT_EQ(code, std::string("25828")); + EXPECT_EQ(confidence[0], 70); + proj_int_list_destroy(confidence); + } } // --------------------------------------------------------------------------- @@ -3216,4 +3252,96 @@ TEST_F(CApi, proj_get_crs_info_list_from_database) { proj_crs_info_list_destroy(list); } } + +// --------------------------------------------------------------------------- + +TEST_F(CApi, proj_normalize_for_visualization) { + + { + auto P = proj_create(m_ctxt, "+proj=utm +zone=31 +ellps=WGS84"); + ObjectKeeper keeper_P(P); + ASSERT_NE(P, nullptr); + auto Pnormalized = proj_normalize_for_visualization(m_ctxt, P); + ObjectKeeper keeper_Pnormalized(Pnormalized); + EXPECT_EQ(Pnormalized, nullptr); + } + + auto P = proj_create_crs_to_crs(m_ctxt, "EPSG:4326", "EPSG:32631", nullptr); + ObjectKeeper keeper_P(P); + ASSERT_NE(P, nullptr); + auto Pnormalized = proj_normalize_for_visualization(m_ctxt, P); + ObjectKeeper keeper_Pnormalized(Pnormalized); + ASSERT_NE(Pnormalized, nullptr); + auto projstr = proj_as_proj_string(m_ctxt, Pnormalized, PJ_PROJ_5, nullptr); + ASSERT_NE(projstr, nullptr); + EXPECT_EQ(std::string(projstr), + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=utm +zone=31 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST_F(CApi, proj_normalize_for_visualization_with_alternatives) { + + auto P = proj_create_crs_to_crs(m_ctxt, "EPSG:4326", "EPSG:3003", nullptr); + ObjectKeeper keeper_P(P); + ASSERT_NE(P, nullptr); + auto Pnormalized = proj_normalize_for_visualization(m_ctxt, P); + ObjectKeeper keeper_Pnormalized(Pnormalized); + ASSERT_NE(Pnormalized, nullptr); + + { + PJ_COORD c; + // Approximately Roma + c.lpz.lam = 12.5; + c.lpz.phi = 42; + c.lpz.z = 0; + c = proj_trans(Pnormalized, PJ_FWD, c); + EXPECT_NEAR(c.xy.x, 1789912.46264783037, 1e-8); + EXPECT_NEAR(c.xy.y, 4655716.25402576849, 1e-8); + auto projstr = proj_pj_info(Pnormalized).definition; + ASSERT_NE(projstr, nullptr); + EXPECT_EQ(std::string(projstr), + "proj=pipeline step proj=unitconvert xy_in=deg xy_out=rad " + "step proj=push v_3 step proj=cart ellps=WGS84 " + "step inv proj=helmert x=-104.1 y=-49.1 z=-9.9 rx=0.971 " + "ry=-2.917 rz=0.714 s=-11.68 convention=position_vector " + "step inv proj=cart ellps=intl step proj=pop v_3 " + "step proj=tmerc lat_0=0 lon_0=9 k=0.9996 x_0=1500000 " + "y_0=0 ellps=intl"); + } + + { + PJ_COORD c; + // Approximately Roma + c.xyz.x = 1789912.46264783037; + c.xyz.y = 4655716.25402576849; + c.xyz.z = 0; + c = proj_trans(Pnormalized, PJ_INV, c); + EXPECT_NEAR(c.lp.lam, 12.5, 1e-8); + EXPECT_NEAR(c.lp.phi, 42, 1e-8); + } +} + +// --------------------------------------------------------------------------- + +TEST_F(CApi, proj_normalize_for_visualization_with_alternatives_reverse) { + + auto P = proj_create_crs_to_crs(m_ctxt, "EPSG:3003", "EPSG:4326", nullptr); + ObjectKeeper keeper_P(P); + ASSERT_NE(P, nullptr); + auto Pnormalized = proj_normalize_for_visualization(m_ctxt, P); + ObjectKeeper keeper_Pnormalized(Pnormalized); + ASSERT_NE(Pnormalized, nullptr); + + PJ_COORD c; + // Approximately Roma + c.xyz.x = 1789912.46264783037; + c.xyz.y = 4655716.25402576849; + c.xyz.z = 0; + c = proj_trans(Pnormalized, PJ_FWD, c); + EXPECT_NEAR(c.lp.lam, 12.5, 1e-8); + EXPECT_NEAR(c.lp.phi, 42, 1e-8); +} + } // namespace diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index 4953529c..0f0304b1 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -148,32 +148,72 @@ TEST(crs, GeographicCRS_datum_ensemble) { std::vector<DatumNNPtr>{GeodeticReferenceFrame::EPSG_6326, GeodeticReferenceFrame::EPSG_6326}, PositionalAccuracy::create("100")); - auto crs = GeographicCRS::create( - PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), nullptr, - ensemble_vdatum, - EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); - WKTFormatterNNPtr f( - WKTFormatter::create(WKTFormatter::Convention::WKT2_2018)); - f->simulCurNodeHasId(); - crs->exportToWKT(f.get()); - auto expected = "GEOGCRS[\"unnamed\",\n" - " ENSEMBLE[\"unnamed\",\n" - " MEMBER[\"World Geodetic System 1984\"],\n" - " MEMBER[\"World Geodetic System 1984\"],\n" - " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" - " LENGTHUNIT[\"metre\",1]],\n" - " ENSEMBLEACCURACY[100]],\n" - " PRIMEM[\"Greenwich\",0,\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" - " CS[ellipsoidal,2],\n" - " AXIS[\"latitude\",north,\n" - " ORDER[1],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" - " AXIS[\"longitude\",east,\n" - " ORDER[2],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]]]"; + { + auto crs = GeographicCRS::create( + PropertyMap() + .set(IdentifiedObject::NAME_KEY, "unnamed") + .set(Identifier::CODESPACE_KEY, "MY_CODESPACE") + .set(Identifier::CODE_KEY, "MY_ID"), + nullptr, ensemble_vdatum, + EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); + WKTFormatterNNPtr f( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018)); + crs->exportToWKT(f.get()); + auto expected = + "GEOGCRS[\"unnamed\",\n" + " ENSEMBLE[\"unnamed\",\n" + " MEMBER[\"World Geodetic System 1984\"],\n" + " MEMBER[\"World Geodetic System 1984\"],\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ENSEMBLEACCURACY[100]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"latitude\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"longitude\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"MY_CODESPACE\",\"MY_ID\"]]"; - EXPECT_EQ(f->toString(), expected); + EXPECT_EQ(f->toString(), expected); + } + + { + auto crs = GeographicCRS::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), nullptr, + ensemble_vdatum, + EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); + WKTFormatterNNPtr f( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018)); + crs->exportToWKT(f.get()); + auto expected = "GEOGCRS[\"unnamed\",\n" + " ENSEMBLE[\"unnamed\",\n" + " MEMBER[\"World Geodetic System 1984\",\n" + " ID[\"EPSG\",6326]],\n" + " MEMBER[\"World Geodetic System 1984\",\n" + " ID[\"EPSG\",6326]],\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",7030]],\n" + " ENSEMBLEACCURACY[100]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8901]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"latitude\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433,\n" + " ID[\"EPSG\",9122]]],\n" + " AXIS[\"longitude\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433,\n" + " ID[\"EPSG\",9122]]]]"; + + EXPECT_EQ(f->toString(), expected); + } } // --------------------------------------------------------------------------- @@ -1458,6 +1498,19 @@ TEST(crs, geodeticcrs_identify_db) { ->identify(factory); ASSERT_EQ(res.size(), 0U); } + { + // Test identification from PROJ string + auto obj = PROJStringParser().createFromPROJString( + "+proj=longlat +datum=WGS84 +type=crs"); + auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj); + ASSERT_TRUE(crs != nullptr); + auto res = crs->identify(factory); + ASSERT_EQ(res.size(), 1U); + ASSERT_TRUE(!res.front().first->identifiers().empty()); + EXPECT_EQ(*res.front().first->identifiers()[0]->codeSpace(), "EPSG"); + EXPECT_EQ(res.front().first->identifiers()[0]->code(), "4326"); + EXPECT_EQ(res.front().second, 70); + } } // --------------------------------------------------------------------------- @@ -1561,6 +1614,52 @@ TEST(crs, projectedCRS_as_WKT2) { // --------------------------------------------------------------------------- +TEST(crs, projectedCRS_as_WKT2_2018) { + auto crs = createProjected(); + + auto expected = + "PROJCRS[\"WGS 84 / UTM zone 31N\",\n" + " BASEGEOGCRS[\"WGS 84\",\n" + " DATUM[\"World Geodetic System 1984\",\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4326]],\n" + " CONVERSION[\"UTM zone 31N\",\n" + " METHOD[\"Transverse Mercator\",\n" + " ID[\"EPSG\",9807]],\n" + " PARAMETER[\"Latitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",0.9996,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",500000,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"(E)\",east,\n" + " ORDER[1],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"(N)\",north,\n" + " ORDER[2],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",32631]]"; + + EXPECT_EQ( + crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()), + expected); +} +// --------------------------------------------------------------------------- + TEST(crs, projectedCRS_as_WKT2_simplified) { auto crs = createProjected(); @@ -1885,13 +1984,35 @@ TEST(crs, projectedCRS_identify_no_db) { TEST(crs, projectedCRS_identify_db) { auto dbContext = DatabaseContext::create(); auto factoryEPSG = AuthorityFactory::create(dbContext, "EPSG"); + auto factoryIGNF = AuthorityFactory::create(dbContext, "IGNF"); + auto factoryAnonymous = AuthorityFactory::create(dbContext, std::string()); { // Identify by existing code - auto res = - factoryEPSG->createProjectedCRS("2172")->identify(factoryEPSG); - ASSERT_EQ(res.size(), 1U); - EXPECT_EQ(res.front().first->getEPSGCode(), 2172); - EXPECT_EQ(res.front().second, 100); + auto crs = factoryEPSG->createProjectedCRS("2172"); + { + auto res = crs->identify(factoryEPSG); + ASSERT_EQ(res.size(), 1U); + EXPECT_EQ(res.front().first->getEPSGCode(), 2172); + EXPECT_EQ(res.front().second, 100); + } + { + auto res = crs->identify(factoryAnonymous); + ASSERT_EQ(res.size(), 1U); + } + { + auto res = crs->identify(factoryIGNF); + ASSERT_EQ(res.size(), 0U); + } + } + { + // Identify by existing code + auto crs = factoryIGNF->createProjectedCRS("ETRS89UTM28"); + { + auto res = crs->identify(factoryEPSG); + ASSERT_EQ(res.size(), 1U); + EXPECT_EQ(res.front().first->getEPSGCode(), 25828); + EXPECT_EQ(res.front().second, 70); + } } { // Non-existing code @@ -4522,7 +4643,8 @@ TEST(crs, DerivedVerticalCRS_WKT2) { auto expected = "VERTCRS[\"Derived vertCRS\",\n" " BASEVERTCRS[\"ODN height\",\n" - " VDATUM[\"Ordnance Datum Newlyn\"]],\n" + " VDATUM[\"Ordnance Datum Newlyn\",\n" + " ID[\"EPSG\",5101]]],\n" " DERIVINGCONVERSION[\"unnamed\",\n" " METHOD[\"PROJ unimplemented\"]],\n" " CS[vertical,1],\n" diff --git a/test/unit/test_datum.cpp b/test/unit/test_datum.cpp index 23c14f67..fa53ff85 100644 --- a/test/unit/test_datum.cpp +++ b/test/unit/test_datum.cpp @@ -175,7 +175,7 @@ TEST(datum, prime_meridian_to_PROJString) { EXPECT_EQ(PrimeMeridian::GREENWICH->exportToPROJString( PROJStringFormatter::create().get()), - ""); + "+proj=noop"); EXPECT_EQ(PrimeMeridian::PARIS->exportToPROJString( PROJStringFormatter::create().get()), @@ -195,7 +195,7 @@ TEST(datum, prime_meridian_to_PROJString) { PropertyMap().set(IdentifiedObject::NAME_KEY, "Origin meridian"), Angle(0)) ->exportToPROJString(PROJStringFormatter::create().get()), - ""); + "+proj=noop"); EXPECT_TRUE(PrimeMeridian::GREENWICH->isEquivalentTo( PrimeMeridian::GREENWICH.get())); diff --git a/test/unit/test_factory.cpp b/test/unit/test_factory.cpp index 80de017f..91024d62 100644 --- a/test/unit/test_factory.cpp +++ b/test/unit/test_factory.cpp @@ -150,7 +150,8 @@ TEST(factory, AuthorityFactory_createPrimeMeridian) { EXPECT_THROW(factory->createPrimeMeridian("-1"), NoSuchAuthorityCodeException); EXPECT_TRUE(nn_dynamic_pointer_cast<PrimeMeridian>( - factory->createObject("8903")) != nullptr); + AuthorityFactory::create(DatabaseContext::create(), "ESRI") + ->createObject("108900")) != nullptr); auto pm = factory->createPrimeMeridian("8903"); ASSERT_EQ(pm->identifiers().size(), 1U); EXPECT_EQ(pm->identifiers()[0]->code(), "8903"); @@ -660,13 +661,13 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_helmert_3) { NoSuchAuthorityCodeException); auto op = factory->createCoordinateOperation("1113", false); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+inv +proj=longlat +a=6378249.145 +rf=293.4663077 +step " - "+proj=cart +a=6378249.145 +rf=293.4663077 +step +proj=helmert " - "+x=-143 +y=-90 +z=-294 +step +inv +proj=cart +ellps=WGS84 +step " - "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " - "+order=2,1 +step +proj=pop +v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +inv " + "+proj=longlat +a=6378249.145 +rf=293.4663077 +step +proj=push " + "+v_3 +step +proj=cart +a=6378249.145 +rf=293.4663077 +step " + "+proj=helmert +x=-143 +y=-90 +z=-294 +step +inv +proj=cart " + "+ellps=WGS84 +step +proj=pop +v_3 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); } // --------------------------------------------------------------------------- @@ -675,13 +676,13 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_helmert_7_CF) { auto factory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); auto op = factory->createCoordinateOperation("7676", false); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=bessel +step +proj=helmert +x=577.88891 " + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=bessel +step +proj=helmert +x=577.88891 " "+y=165.22205 +z=391.18289 +rx=-4.9145 +ry=0.94729 +rz=13.05098 " "+s=7.78664 +convention=coordinate_frame +step +inv +proj=cart " - "+ellps=WGS84 +step +proj=unitconvert +xy_in=rad +xy_out=deg " - "+step +proj=axisswap +order=2,1 +step +proj=pop +v_3"); + "+ellps=WGS84 +step +proj=pop +v_3 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); } // --------------------------------------------------------------------------- @@ -717,6 +718,7 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_helmert_15_CF) { auto op = factory->createCoordinateOperation("6276", false); auto expected = "COORDINATEOPERATION[\"ITRF2008 to GDA94 (1)\",\n" + " VERSION[\"GA-Aus 2010\"],\n" " SOURCECRS[\n" " GEODCRS[\"ITRF2008\",\n" " DATUM[\"International Terrestrial Reference Frame " @@ -734,7 +736,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_helmert_15_CF) { " LENGTHUNIT[\"metre\",1]],\n" " AXIS[\"(Z)\",geocentricZ,\n" " ORDER[3],\n" - " LENGTHUNIT[\"metre\",1]]]],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",5332]]],\n" " TARGETCRS[\n" " GEODCRS[\"GDA94\",\n" " DATUM[\"Geocentric Datum of Australia 1994\",\n" @@ -751,7 +754,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_helmert_15_CF) { " LENGTHUNIT[\"metre\",1]],\n" " AXIS[\"(Z)\",geocentricZ,\n" " ORDER[3],\n" - " LENGTHUNIT[\"metre\",1]]]],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",4938]]],\n" " METHOD[\"Time-dependent Coordinate Frame rotation (geocen)\",\n" " ID[\"EPSG\",1056]],\n" " PARAMETER[\"X-axis translation\",-84.68,\n" @@ -852,15 +856,15 @@ TEST(factory, EXPECT_TRUE(so->validateParameters().empty()); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=bessel +step +proj=molobadekas +x=593.032 " - "+y=26 +z=478.741 +rx=0.409394387439237 +ry=-0.359705195614311 " - "+rz=1.86849100035057 +s=4.0772 +px=3903453.148 +py=368135.313 " - "+pz=5012970.306 +convention=coordinate_frame +step +inv " - "+proj=cart +ellps=GRS80 +step +proj=unitconvert +xy_in=rad " - "+xy_out=deg +step +proj=axisswap +order=2,1 +step +proj=pop " - "+v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=bessel +step +proj=molobadekas " + "+x=593.032 +y=26 +z=478.741 +rx=0.409394387439237 " + "+ry=-0.359705195614311 +rz=1.86849100035057 +s=4.0772 " + "+px=3903453.148 +py=368135.313 +pz=5012970.306 " + "+convention=coordinate_frame +step +inv +proj=cart +ellps=GRS80 " + "+step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); } // --------------------------------------------------------------------------- @@ -872,6 +876,7 @@ TEST( auto op = factory->createCoordinateOperation("1295", false); auto expected = "COORDINATEOPERATION[\"RGNC91-93 to NEA74 Noumea (4)\",\n" + " VERSION[\"ESRI-Ncl 0.05m\"],\n" " SOURCECRS[\n" " GEOGCRS[\"RGNC91-93\",\n" " DATUM[\"Reseau Geodesique de Nouvelle Caledonie 91-93\",\n" @@ -885,7 +890,8 @@ TEST( " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" " AXIS[\"geodetic longitude (Lon)\",east,\n" " ORDER[2],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4749]]],\n" " TARGETCRS[\n" " GEOGCRS[\"NEA74 Noumea\",\n" " DATUM[\"NEA74 Noumea\",\n" @@ -899,7 +905,8 @@ TEST( " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" " AXIS[\"geodetic longitude (Lon)\",east,\n" " ORDER[2],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4644]]],\n" " METHOD[\"NTv2\",\n" " ID[\"EPSG\",9615]],\n" " PARAMETERFILE[\"Latitude and longitude difference " @@ -939,6 +946,7 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_other_transformation) { auto op = factory->createCoordinateOperation("1884", false); auto expected = "COORDINATEOPERATION[\"S-JTSK (Ferro) to S-JTSK (1)\",\n" + " VERSION[\"EPSG-Cze\"],\n" " SOURCECRS[\n" " GEOGCRS[\"S-JTSK (Ferro)\",\n" " DATUM[\"System of the Unified Trigonometrical Cadastral " @@ -953,7 +961,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_other_transformation) { " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" " AXIS[\"geodetic longitude (Lon)\",east,\n" " ORDER[2],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4818]]],\n" " TARGETCRS[\n" " GEOGCRS[\"S-JTSK\",\n" " DATUM[\"System of the Unified Trigonometrical Cadastral " @@ -968,7 +977,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_other_transformation) { " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" " AXIS[\"geodetic longitude (Lon)\",east,\n" " ORDER[2],\n" - " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",4156]]],\n" " METHOD[\"Longitude rotation\",\n" " ID[\"EPSG\",9601]],\n" " PARAMETER[\"Longitude offset\",-17.6666666666667,\n" @@ -1465,7 +1475,7 @@ class FactoryWithTmpDatabase : public ::testing::Test { "'EPSG','4326','EPSG','1262',44.0,-143." "0,-90.0,-294.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," - "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); ASSERT_TRUE(execute( @@ -1474,7 +1484,8 @@ class FactoryWithTmpDatabase : public ::testing::Test { "'EPSG','9615'" ",'NTv2','EPSG','4326','EPSG','4326','EPSG','1262',1.0,'EPSG','" "8656','Latitude and longitude difference " - "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL,NULL," + "0);")) << last_error(); ASSERT_TRUE(execute( @@ -1491,7 +1502,7 @@ class FactoryWithTmpDatabase : public ::testing::Test { "offset',-17.4,'EPSG','9110',NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," - "NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); ASSERT_TRUE(execute( @@ -1499,7 +1510,7 @@ class FactoryWithTmpDatabase : public ::testing::Test { "VALUES('EPSG','DUMMY_CONCATENATED','name',NULL,NULL," "'EPSG','4326','EPSG'" ",'4326','EPSG','1262',NULL,'EPSG','DUMMY_OTHER_TRANSFORMATION'" - ",'EPSG','DUMMY_OTHER_TRANSFORMATION',NULL,NULL,0);")) + ",'EPSG','DUMMY_OTHER_TRANSFORMATION',NULL,NULL,NULL,0);")) << last_error(); } @@ -1507,12 +1518,20 @@ class FactoryWithTmpDatabase : public ::testing::Test { const auto vals = std::vector<std::string>{"SOURCE", "TARGET", "PIVOT"}; for (const auto &val : vals) { + ASSERT_TRUE( + execute("INSERT INTO geodetic_datum " + "VALUES('FOO','" + + val + "','" + val + + "','',NULL," + "'EPSG','7030','EPSG','8901','EPSG','1262',0);")) + << last_error(); ASSERT_TRUE(execute("INSERT INTO geodetic_crs " "VALUES('NS_" + val + "','" + val + "','" + val + "',NULL,NULL,'geographic 2D','EPSG','6422'," - "'EPSG','6326'," - "'EPSG','1262',NULL,0);")) + "'FOO','" + + val + "'," + "'EPSG','1262',NULL,0);")) << last_error(); } } @@ -1530,7 +1549,7 @@ class FactoryWithTmpDatabase : public ::testing::Test { "','EPSG'" ",'1262',1.0,0,0,0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," - "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); } @@ -1838,7 +1857,7 @@ TEST_F(FactoryWithTmpDatabase, "'EPSG','9615'" ",'NTv2','EPSG','4326','OTHER','OTHER_4326','EPSG','1262',1.0,'EPSG','" "8656','Latitude and longitude difference " - "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); { auto res = factoryGeneral->createFromCoordinateReferenceSystemCodes( @@ -1870,7 +1889,7 @@ TEST_F(FactoryWithTmpDatabase, "'EPSG','9615'" ",'NTv2','EPSG','4326','EPSG','4326','EPSG','1262',10.0,'EPSG','" "8656','Latitude and longitude difference " - "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); ASSERT_TRUE( @@ -1879,7 +1898,8 @@ TEST_F(FactoryWithTmpDatabase, "TRANSFORMATION_1M_SMALL_EXTENT',NULL,NULL,'EPSG','9615'" ",'NTv2','EPSG','4326','EPSG','4326','EPSG','2060',1.0,'EPSG','" "8656','Latitude and longitude difference " - "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL," + "NULL,0);")) << last_error(); ASSERT_TRUE(execute( @@ -1888,7 +1908,7 @@ TEST_F(FactoryWithTmpDatabase, "'EPSG','9615'" ",'NTv2','EPSG','4326','EPSG','4326','EPSG','1262',1.0,'EPSG','" "8656','Latitude and longitude difference " - "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); ASSERT_TRUE( @@ -1897,7 +1917,8 @@ TEST_F(FactoryWithTmpDatabase, "TRANSFORMATION_0.5M_DEPRECATED',NULL,NULL,'EPSG','9615'" ",'NTv2','EPSG','4326','EPSG','4326','EPSG','1262',1.0,'EPSG','" "8656','Latitude and longitude difference " - "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL,1);")) + "file','nzgd2kgrid0005.gsb',NULL,NULL,NULL,NULL,NULL,NULL," + "NULL,1);")) << last_error(); auto factoryOTHER = @@ -2000,7 +2021,7 @@ TEST_F(FactoryWithTmpDatabase, AuthorityFactory_proj_based_transformation) { "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," - "NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); auto factoryOTHER = @@ -2061,7 +2082,7 @@ TEST_F(FactoryWithTmpDatabase, AuthorityFactory_wkt_based_transformation) { "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," - "NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); auto factoryOTHER = @@ -2071,12 +2092,12 @@ TEST_F(FactoryWithTmpDatabase, AuthorityFactory_wkt_based_transformation) { ASSERT_EQ(res.size(), 1U); EXPECT_EQ(res[0]->nameStr(), "My WKT string based op"); EXPECT_EQ(res[0]->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=WGS84 +step +proj=helmert +x=1 +y=2 +z=3 " - "+step +inv +proj=cart +ellps=WGS84 +step +proj=unitconvert " - "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1 +step " - "+proj=pop +v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=WGS84 +step +proj=helmert +x=1 +y=2 " + "+z=3 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg +step " + "+proj=axisswap +order=2,1"); } // --------------------------------------------------------------------------- @@ -2096,7 +2117,7 @@ TEST_F(FactoryWithTmpDatabase, "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," - "NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); auto factoryOTHER = @@ -2123,7 +2144,7 @@ TEST_F(FactoryWithTmpDatabase, "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," - "NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); auto factoryOTHER = @@ -2613,7 +2634,7 @@ TEST_F(FactoryWithTmpDatabase, "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," - "NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); ASSERT_TRUE( @@ -2625,7 +2646,7 @@ TEST_F(FactoryWithTmpDatabase, "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL," - "NULL,NULL,NULL,NULL,NULL,NULL,0);")) + "NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);")) << last_error(); auto dbContext = DatabaseContext::create(m_ctxt); @@ -2792,7 +2813,7 @@ TEST(factory, getMetadata) { EXPECT_EQ(ctxt->getMetadata("i_do_not_exist"), nullptr); const char *IGNF_VERSION = ctxt->getMetadata("IGNF.VERSION"); ASSERT_TRUE(IGNF_VERSION != nullptr); - EXPECT_EQ(std::string(IGNF_VERSION), "3.0.2"); + EXPECT_EQ(std::string(IGNF_VERSION), "3.0.3"); } // --------------------------------------------------------------------------- diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 3d015e0c..294e765d 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -1640,6 +1640,77 @@ TEST(wkt_parse, wkt2_projected) { // --------------------------------------------------------------------------- +TEST(wkt_parse, wkt2_2018_projected_with_id_in_basegeodcrs) { + auto wkt = "PROJCRS[\"WGS 84 / UTM zone 31N\",\n" + " BASEGEOGCRS[\"WGS 84\",\n" + " DATUM[\"World Geodetic System 1984\",\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n" + " ID[\"EPSG\",4326]],\n" + " CONVERSION[\"UTM zone 31N\",\n" + " METHOD[\"Transverse Mercator\"],\n" + " PARAMETER[\"Latitude of natural origin\",0],\n" + " PARAMETER[\"Longitude of natural origin\",3],\n" + " PARAMETER[\"Scale factor at natural origin\",0.9996],\n" + " PARAMETER[\"False easting\",500000],\n" + " PARAMETER[\"False northing\",0]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"(E)\",east],\n" + " AXIS[\"(N)\",north],\n" + " UNIT[\"metre\",1],\n" + " ID[\"EPSG\",32631]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + ASSERT_EQ(crs->baseCRS()->identifiers().size(), 1U); + EXPECT_EQ(crs->baseCRS()->identifiers().front()->code(), "4326"); + + { + auto got_wkt = crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()); + EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") != std::string::npos) + << got_wkt; + } + + { + auto got_wkt = crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018_SIMPLIFIED) + .get()); + EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") == std::string::npos) + << got_wkt; + } +} + +// --------------------------------------------------------------------------- + +TEST(wkt_parse, wkt2_2018_projected_no_id_but_id_in_basegeodcrs) { + auto wkt = "PROJCRS[\"WGS 84 / UTM zone 31N\",\n" + " BASEGEOGCRS[\"WGS 84\",\n" + " DATUM[\"World Geodetic System 1984\",\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n" + " ID[\"EPSG\",4326]],\n" + " CONVERSION[\"UTM zone 31N\",\n" + " METHOD[\"Transverse Mercator\"],\n" + " PARAMETER[\"Latitude of natural origin\",0],\n" + " PARAMETER[\"Longitude of natural origin\",3],\n" + " PARAMETER[\"Scale factor at natural origin\",0.9996],\n" + " PARAMETER[\"False easting\",500000],\n" + " PARAMETER[\"False northing\",0]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"(E)\",east],\n" + " AXIS[\"(N)\",north],\n" + " UNIT[\"metre\",1]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + auto got_wkt = crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()); + EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") != std::string::npos) + << got_wkt; +} + +// --------------------------------------------------------------------------- + TEST(wkt_parse, wkt2_2018_simplified_projected) { auto wkt = "PROJCRS[\"WGS 84 / UTM zone 31N\",\n" " BASEGEOGCRS[\"WGS 84\",\n" @@ -2280,6 +2351,95 @@ TEST(wkt_parse, COORDINATEOPERATION) { GeographicCRS::EPSG_4979->nameStr()); EXPECT_EQ(transf->method()->nameStr(), "operationMethodName"); EXPECT_EQ(transf->parameterValues().size(), 1U); + + { + auto outWkt = transf->exportToWKT(WKTFormatter::create().get()); + EXPECT_EQ(replaceAll(replaceAll(outWkt, "\n", ""), " ", ""), + replaceAll(replaceAll(wkt, "\n", ""), " ", "")); + } +} + +// --------------------------------------------------------------------------- + +TEST(wkt_parse, COORDINATEOPERATION_wkt2018) { + + std::string src_wkt; + { + auto formatter = + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); + formatter->setOutputId(false); + src_wkt = GeographicCRS::EPSG_4326->exportToWKT(formatter.get()); + } + + std::string dst_wkt; + { + auto formatter = + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); + formatter->setOutputId(false); + dst_wkt = GeographicCRS::EPSG_4807->exportToWKT(formatter.get()); + } + + std::string interpolation_wkt; + { + auto formatter = + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); + formatter->setOutputId(false); + interpolation_wkt = + GeographicCRS::EPSG_4979->exportToWKT(formatter.get()); + } + + auto wkt = + "COORDINATEOPERATION[\"transformationName\",\n" + " VERSION[\"my version\"],\n" + " SOURCECRS[" + + src_wkt + "],\n" + " TARGETCRS[" + + dst_wkt + + "],\n" + " METHOD[\"operationMethodName\",\n" + " ID[\"codeSpaceOperationMethod\",\"codeOperationMethod\"]],\n" + " PARAMETERFILE[\"paramName\",\"foo.bin\"],\n" + " INTERPOLATIONCRS[" + + interpolation_wkt + + "],\n" + " OPERATIONACCURACY[0.1],\n" + " ID[\"codeSpaceTransformation\",\"codeTransformation\"],\n" + " REMARK[\"my remarks\"]]"; + + auto obj = WKTParser().createFromWKT(wkt); + auto transf = nn_dynamic_pointer_cast<Transformation>(obj); + ASSERT_TRUE(transf != nullptr); + EXPECT_EQ(transf->nameStr(), "transformationName"); + EXPECT_EQ(*transf->operationVersion(), "my version"); + ASSERT_EQ(transf->identifiers().size(), 1U); + EXPECT_EQ(transf->identifiers()[0]->code(), "codeTransformation"); + EXPECT_EQ(*(transf->identifiers()[0]->codeSpace()), + "codeSpaceTransformation"); + ASSERT_EQ(transf->coordinateOperationAccuracies().size(), 1U); + EXPECT_EQ(transf->coordinateOperationAccuracies()[0]->value(), "0.1"); + EXPECT_EQ(transf->sourceCRS()->nameStr(), + GeographicCRS::EPSG_4326->nameStr()); + EXPECT_EQ(transf->targetCRS()->nameStr(), + GeographicCRS::EPSG_4807->nameStr()); + ASSERT_TRUE(transf->interpolationCRS() != nullptr); + EXPECT_EQ(transf->interpolationCRS()->nameStr(), + GeographicCRS::EPSG_4979->nameStr()); + EXPECT_EQ(transf->method()->nameStr(), "operationMethodName"); + EXPECT_EQ(transf->parameterValues().size(), 1U); + + { + auto outWkt = transf->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()); + EXPECT_EQ(replaceAll(replaceAll(outWkt, "\n", ""), " ", ""), + replaceAll(replaceAll(wkt, "\n", ""), " ", "")); + } + + { + auto outWkt = transf->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2015).get()); + EXPECT_FALSE(outWkt.find("VERSION[\"my version\"],") != + std::string::npos); + } } // --------------------------------------------------------------------------- @@ -2339,6 +2499,7 @@ TEST(wkt_parse, CONCATENATEDOPERATION) { auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj); ASSERT_TRUE(concat != nullptr); EXPECT_EQ(concat->nameStr(), "name"); + EXPECT_FALSE(concat->operationVersion().has_value()); ASSERT_EQ(concat->identifiers().size(), 1U); EXPECT_EQ(concat->identifiers()[0]->code(), "code"); EXPECT_EQ(*(concat->identifiers()[0]->codeSpace()), "codeSpace"); @@ -2487,6 +2648,7 @@ TEST(wkt_parse, auto wkt = "CONCATENATEDOPERATION[\"Inverse of UTM zone 11N + NAD27 to WGS 84 " "(79) + UTM zone 11N\",\n" + " VERSION[\"my version\"],\n" " SOURCECRS[\n" " PROJCRS[\"NAD27 / UTM zone 11N\",\n" " BASEGEOGCRS[\"NAD27\",\n" @@ -2639,6 +2801,7 @@ TEST(wkt_parse, auto obj = WKTParser().createFromWKT(wkt); auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj); ASSERT_TRUE(concat != nullptr); + EXPECT_EQ(*concat->operationVersion(), "my version"); EXPECT_EQ(concat->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +inv +proj=utm +zone=11 +ellps=clrk66 " @@ -5017,6 +5180,43 @@ TEST(wkt_parse, wkt1_esri_ups_south) { // --------------------------------------------------------------------------- +TEST(wkt_parse, wkt1_esri_gauss_kruger) { + auto wkt = "PROJCS[\"ETRS_1989_UWPP_2000_PAS_8\",GEOGCS[\"GCS_ETRS_1989\"," + "DATUM[\"D_ETRS_1989\"," + "SPHEROID[\"GRS_1980\",6378137.0,298.257222101]]," + "PRIMEM[\"Greenwich\",0.0]," + "UNIT[\"Degree\",0.0174532925199433]]," + "PROJECTION[\"Gauss_Kruger\"]," + "PARAMETER[\"False_Easting\",8500000.0]," + "PARAMETER[\"False_Northing\",0.0]," + "PARAMETER[\"Central_Meridian\",24.0]," + "PARAMETER[\"Scale_Factor\",0.999923]," + "PARAMETER[\"Latitude_Of_Origin\",0.0]," + "UNIT[\"Meter\",1.0]]"; + + auto dbContext = DatabaseContext::create(); + auto obj = WKTParser().attachDatabaseContext(dbContext).createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + EXPECT_EQ( + crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI, dbContext) + .get()), + wkt); + + auto crs2 = AuthorityFactory::create(dbContext, "ESRI") + ->createProjectedCRS("102177"); + + EXPECT_EQ( + crs2->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI, dbContext) + .get()), + wkt); +} + +// --------------------------------------------------------------------------- + TEST(wkt_parse, invalid) { EXPECT_THROW(WKTParser().createFromWKT(""), ParsingException); EXPECT_THROW(WKTParser().createFromWKT("A"), ParsingException); @@ -6378,7 +6578,7 @@ TEST(io, projstringformatter_helmert_3_param_noop) { fmt->addParam("x", 0); fmt->addParam("y", 0); fmt->addParam("z", 0); - EXPECT_EQ(fmt->toString(), ""); + EXPECT_EQ(fmt->toString(), "+proj=noop"); } // --------------------------------------------------------------------------- @@ -6394,7 +6594,7 @@ TEST(io, projstringformatter_helmert_7_param_noop) { fmt->addParam("rz", 0); fmt->addParam("s", 0); fmt->addParam("convention", "position_vector"); - EXPECT_EQ(fmt->toString(), ""); + EXPECT_EQ(fmt->toString(), "+proj=noop"); } // --------------------------------------------------------------------------- @@ -6424,7 +6624,7 @@ TEST(io, projstringformatter_merge_consecutive_helmert_3_param_noop) { fmt->addParam("x", -10); fmt->addParam("y", -20); fmt->addParam("z", -30); - EXPECT_EQ(fmt->toString(), ""); + EXPECT_EQ(fmt->toString(), "+proj=noop"); } // --------------------------------------------------------------------------- @@ -6436,7 +6636,7 @@ TEST(io, projstringformatter_cart_grs80_wgs84) { fmt->addStep("cart"); fmt->setCurrentStepInverted(true); fmt->addParam("ellps", "GRS80"); - EXPECT_EQ(fmt->toString(), ""); + EXPECT_EQ(fmt->toString(), "+proj=noop"); } // --------------------------------------------------------------------------- @@ -7171,7 +7371,8 @@ TEST(io, projparse_longlat_axis_neu) { auto op = CoordinateOperationFactory::create()->createOperation( GeographicCRS::EPSG_4326, NN_NO_CHECK(crs)); ASSERT_TRUE(op != nullptr); - EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), ""); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=noop"); } // --------------------------------------------------------------------------- @@ -7274,7 +7475,7 @@ TEST(io, projparse_longlat_axisswap) { EXPECT_EQ( op->exportToPROJString(PROJStringFormatter::create().get()), (atoi(order1) == 2 && atoi(order2) == 1) - ? "" + ? "+proj=noop" : "+proj=pipeline +step +proj=axisswap +order=2,1 " "+step +proj=axisswap +order=" + std::string(order1) + "," + order2); @@ -8611,6 +8812,39 @@ TEST(io, createFromUserInput) { EXPECT_THROW( createFromUserInput("urn:ogc:def:unhandled:EPSG::4326", dbContext), ParsingException); + EXPECT_THROW(createFromUserInput("urn:ogc:def:crs:non_existing_auth::4326", + dbContext), + NoSuchAuthorityCodeException); + EXPECT_THROW(createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393,unhandled_type:EPSG::5717", + dbContext), + ParsingException); + EXPECT_THROW(createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::unexisting_code", + dbContext), + NoSuchAuthorityCodeException); + EXPECT_THROW( + createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393::extra_element,crs:EPSG::EPSG", + dbContext), + ParsingException); + EXPECT_THROW(createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895," + "unhandled_type:EPSG::1618", + dbContext), + ParsingException); + EXPECT_THROW( + createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895," + "coordinateOperation:EPSG::unexisting_code", + dbContext), + NoSuchAuthorityCodeException); + EXPECT_THROW( + createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895::extra_element," + "coordinateOperation:EPSG::1618", + dbContext), + ParsingException); EXPECT_NO_THROW(createFromUserInput("+proj=longlat", nullptr)); EXPECT_NO_THROW(createFromUserInput("EPSG:4326", dbContext)); @@ -8625,6 +8859,31 @@ TEST(io, createFromUserInput) { createFromUserInput("urn:ogc:def:meridian:EPSG::8901", dbContext)); EXPECT_NO_THROW( createFromUserInput("urn:ogc:def:ellipsoid:EPSG::7030", dbContext)); + { + auto obj = createFromUserInput("EPSG:2393+5717", dbContext); + auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->nameStr(), + "KKJ / Finland Uniform Coordinate System + N60 height"); + } + { + auto obj = createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717", dbContext); + auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->nameStr(), + "KKJ / Finland Uniform Coordinate System + N60 height"); + } + { + auto obj = createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895," + "coordinateOperation:EPSG::1618", + dbContext); + auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj); + ASSERT_TRUE(concat != nullptr); + EXPECT_EQ(concat->nameStr(), + "MGI (Ferro) to MGI (1) + MGI to WGS 84 (3)"); + } EXPECT_NO_THROW(createFromUserInput( "GEOGCRS[\"WGS 84\",\n" " DATUM[\"World Geodetic System 1984\",\n" diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 90deb661..5ac1609d 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -218,6 +218,11 @@ TEST(operation, SingleOperation) { EXPECT_TRUE(sop1->isEquivalentTo(sop1.get())); EXPECT_FALSE(sop1->isEquivalentTo(createUnrelatedObject().get())); + EXPECT_TRUE( + sop1->isEquivalentTo(sop1->CoordinateOperation::shallowClone().get())); + EXPECT_TRUE(sop1->inverse()->isEquivalentTo( + sop1->inverse()->CoordinateOperation::shallowClone().get())); + auto sop2 = Transformation::create( PropertyMap(), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), @@ -442,7 +447,6 @@ TEST(operation, concatenated_operation) { { auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); - formatter->setOutputId(false); src_wkt = GeographicCRS::EPSG_4326->exportToWKT(formatter.get()); } @@ -450,7 +454,6 @@ TEST(operation, concatenated_operation) { { auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); - formatter->setOutputId(false); dst_wkt = GeographicCRS::EPSG_4979->exportToWKT(formatter.get()); } @@ -458,7 +461,6 @@ TEST(operation, concatenated_operation) { { auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); - formatter->setOutputId(false); step1_wkt = transf_1->exportToWKT(formatter.get()); } @@ -466,7 +468,6 @@ TEST(operation, concatenated_operation) { { auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); - formatter->setOutputId(false); step2_wkt = transf_2->exportToWKT(formatter.get()); } @@ -514,6 +515,8 @@ TEST(operation, concatenated_operation) { EXPECT_TRUE(concat->isEquivalentTo(concat.get())); EXPECT_FALSE(concat->isEquivalentTo(createUnrelatedObject().get())); + EXPECT_TRUE(concat->isEquivalentTo( + concat->CoordinateOperation::shallowClone().get())); EXPECT_FALSE( ConcatenatedOperation::create(PropertyMap(), std::vector<CoordinateOperationNNPtr>{ @@ -565,12 +568,12 @@ TEST(operation, transformation_createGeocentricTranslations) { EXPECT_EQ(inv_transf_as_transf->getTOWGS84Parameters(), expected_inv); EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 +z=3 " - "+step +inv +proj=cart +ellps=WGS84 +step +proj=unitconvert " - "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1 +step " - "+proj=pop +v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 " + "+z=3 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg +step " + "+proj=axisswap +order=2,1"); } // --------------------------------------------------------------------------- @@ -640,7 +643,7 @@ TEST(operation, transformation_createGeocentricTranslations_null) { EXPECT_EQ(transf->inverse()->exportToPROJString( PROJStringFormatter::create().get()), - ""); + "+proj=noop"); } // --------------------------------------------------------------------------- @@ -673,13 +676,13 @@ TEST(operation, transformation_createPositionVector) { EXPECT_EQ(transf->getTOWGS84Parameters(), expected); EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 +z=3 " - "+rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step +inv " - "+proj=cart +ellps=WGS84 +step +proj=unitconvert +xy_in=rad " - "+xy_out=deg +step +proj=axisswap +order=2,1 +step +proj=pop " - "+v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 " + "+z=3 +rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step " + "+inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); auto inv_transf = transf->inverse(); ASSERT_EQ(inv_transf->coordinateOperationAccuracies().size(), 1U); @@ -696,12 +699,12 @@ TEST(operation, transformation_createPositionVector) { #else EXPECT_EQ( inv_transf->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap +order=2,1 " - "+step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " - "+ellps=WGS84 +step +inv +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 " - "+rz=6 +s=7 +convention=position_vector +step +inv +proj=cart " - "+ellps=GRS80 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step " - "+proj=axisswap +order=2,1 +step +proj=pop +v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 +step " + "+proj=cart +ellps=WGS84 +step +inv +proj=helmert +x=1 +y=2 +z=3 +rx=4 " + "+ry=5 +rz=6 +s=7 +convention=position_vector +step +inv +proj=cart " + "+ellps=GRS80 +step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); // In WKT, use approximate formula auto wkt = inv_transf->exportToWKT(WKTFormatter::create().get()); @@ -742,13 +745,13 @@ TEST(operation, transformation_createCoordinateFrameRotation) { EXPECT_EQ(params, expected); EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 +z=3 " - "+rx=-4 +ry=-5 +rz=-6 +s=7 +convention=coordinate_frame +step " - "+inv +proj=cart +ellps=WGS84 +step +proj=unitconvert +xy_in=rad " - "+xy_out=deg +step +proj=axisswap +order=2,1 +step +proj=pop " - "+v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 " + "+z=3 +rx=-4 +ry=-5 +rz=-6 +s=7 +convention=coordinate_frame " + "+step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); auto inv_transf = transf->inverse(); ASSERT_EQ(inv_transf->coordinateOperationAccuracies().size(), 0U); @@ -765,12 +768,12 @@ TEST(operation, transformation_createCoordinateFrameRotation) { #else EXPECT_EQ( inv_transf->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap +order=2,1 " - "+step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " - "+ellps=WGS84 +step +inv +proj=helmert +x=1 +y=2 +z=3 +rx=-4 +ry=-5 " - "+rz=-6 +s=7 +convention=coordinate_frame +step +inv +proj=cart " - "+ellps=GRS80 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step " - "+proj=axisswap +order=2,1 +step +proj=pop +v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 +step " + "+proj=cart +ellps=WGS84 +step +inv +proj=helmert +x=1 +y=2 +z=3 " + "+rx=-4 +ry=-5 +rz=-6 +s=7 +convention=coordinate_frame +step +inv " + "+proj=cart +ellps=GRS80 +step +proj=pop +v_3 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); // In WKT, use approximate formula auto wkt = inv_transf->exportToWKT(WKTFormatter::create().get()); @@ -960,7 +963,7 @@ TEST(operation, transformation_successive_helmert_noop) { std::vector<PositionalAccuracyNNPtr>{}); EXPECT_EQ(concat->exportToPROJString(PROJStringFormatter::create().get()), - ""); + "+proj=noop"); } // --------------------------------------------------------------------------- @@ -4054,6 +4057,11 @@ TEST(operation, conversion_inverse) { EXPECT_TRUE(inv->isEquivalentTo(inv.get())); EXPECT_FALSE(inv->isEquivalentTo(createUnrelatedObject().get())); + + EXPECT_TRUE( + conv->isEquivalentTo(conv->CoordinateOperation::shallowClone().get())); + EXPECT_TRUE( + inv->isEquivalentTo(inv->CoordinateOperation::shallowClone().get())); } // --------------------------------------------------------------------------- @@ -4135,7 +4143,7 @@ TEST(operation, PROJ_based_empty) { SingleOperation::createPROJBased(PropertyMap(), "", nullptr, nullptr); EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), - ""); + "+proj=noop"); EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), "CONVERSION[\"PROJ-based coordinate operation\",\n" @@ -4148,7 +4156,7 @@ TEST(operation, PROJ_based_empty) { EXPECT_EQ(conv->inverse()->exportToPROJString( PROJStringFormatter::create().get()), - ""); + "+proj=noop"); } // --------------------------------------------------------------------------- @@ -4199,17 +4207,18 @@ TEST(operation, geogCRS_to_geogCRS_context_default) { EXPECT_EQ(list[0]->getEPSGCode(), 15994); // Romania - 3m EXPECT_EQ(list[1]->getEPSGCode(), 1644); // Poland - 1m EXPECT_EQ(list[2]->nameStr(), - "Null geographic offset from Pulkovo 1942(58) to ETRS89"); + "Ballpark geographic offset from Pulkovo 1942(58) to ETRS89"); EXPECT_EQ( list[0]->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=krass +step +proj=helmert +x=2.3287 " + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=krass +step +proj=helmert +x=2.3287 " "+y=-147.0425 +z=-92.0802 +rx=0.3092483 +ry=-0.32482185 " "+rz=-0.49729934 +s=5.68906266 +convention=coordinate_frame +step " - "+inv +proj=cart +ellps=GRS80 +step +proj=unitconvert +xy_in=rad " - "+xy_out=deg +step +proj=axisswap +order=2,1 +step +proj=pop +v_3"); + "+inv +proj=cart +ellps=GRS80 +step +proj=pop +v_3 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); } // Reverse case @@ -4224,13 +4233,14 @@ TEST(operation, geogCRS_to_geogCRS_context_default) { EXPECT_EQ( list[0]->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=GRS80 +step +inv +proj=helmert +x=2.3287 " + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=GRS80 +step +inv +proj=helmert +x=2.3287 " "+y=-147.0425 +z=-92.0802 +rx=0.3092483 +ry=-0.32482185 " "+rz=-0.49729934 +s=5.68906266 +convention=coordinate_frame +step " - "+inv +proj=cart +ellps=krass +step +proj=unitconvert +xy_in=rad " - "+xy_out=deg +step +proj=axisswap +order=2,1 +step +proj=pop +v_3"); + "+inv +proj=cart +ellps=krass +step +proj=pop +v_3 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); } } @@ -4330,7 +4340,7 @@ TEST(operation, geogCRS_to_geogCRS_context_filter_bbox) { ASSERT_EQ(list.size(), 1U); EXPECT_EQ( list[0]->exportToPROJString(PROJStringFormatter::create().get()), - ""); + "+proj=noop"); } } @@ -4346,7 +4356,7 @@ TEST(operation, geogCRS_to_geogCRS_context_incompatible_area) { ctxt); ASSERT_EQ(list.size(), 1U); EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), - ""); + "+proj=noop"); } // --------------------------------------------------------------------------- @@ -4362,26 +4372,20 @@ TEST(operation, geogCRS_to_geogCRS_context_inverse_needed) { authFactory->createCoordinateReferenceSystem("4275"), // NTF authFactory->createCoordinateReferenceSystem("4258"), // ETRS89 ctxt); - ASSERT_EQ(list.size(), 3U); + ASSERT_EQ(list.size(), 2U); EXPECT_EQ( list[0]->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=clrk80ign +step +proj=helmert +x=-168 +y=-60 " - "+z=320 +step +inv +proj=cart +ellps=GRS80 +step +proj=unitconvert " - "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1 +step " - "+proj=pop +v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=clrk80ign +step +proj=helmert +x=-168 " + "+y=-60 +z=320 +step +inv +proj=cart +ellps=GRS80 +step +proj=pop " + "+v_3 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step " + "+proj=axisswap +order=2,1"); EXPECT_EQ(list[1]->exportToPROJString( PROJStringFormatter::create( PROJStringFormatter::Convention::PROJ_5, authFactory->databaseContext()) .get()), - ""); - EXPECT_EQ(list[2]->exportToPROJString( - PROJStringFormatter::create( - PROJStringFormatter::Convention::PROJ_5, - authFactory->databaseContext()) - .get()), "+proj=pipeline +step +proj=axisswap +order=2,1 +step " "+proj=unitconvert +xy_in=deg +xy_out=rad +step " "+proj=hgridshift +grids=ntf_r93.gsb +step +proj=unitconvert " @@ -4512,7 +4516,8 @@ TEST(operation, geogCRS_to_geogCRS_noop) { GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4326); ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->nameStr(), "Null geographic offset from WGS 84 to WGS 84"); - EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), ""); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=noop"); EXPECT_EQ(op->inverse()->nameStr(), op->nameStr()); } @@ -4659,7 +4664,7 @@ TEST(operation, geogCRS_to_geogCRS_CH1903_to_CH1903plus_context) { EXPECT_EQ(list[0]->nameStr(), "CH1903 to ETRS89 (1) + Inverse of CH1903+ to ETRS89 (1)"); EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), - ""); + "+proj=noop"); EXPECT_EQ(list[1]->nameStr(), "CH1903 to CH1903+ (1)"); EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()), @@ -4754,6 +4759,52 @@ TEST(operation, geogCRS_to_geogCRS_3D) { // --------------------------------------------------------------------------- +TEST(operation, geogCRS_without_id_to_geogCRS_3D_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto src = + authFactory->createCoordinateReferenceSystem("4289"); // Amersfoort + auto dst = + authFactory->createCoordinateReferenceSystem("4937"); // ETRS89 3D + auto list = + CoordinateOperationFactory::create()->createOperations(src, dst, ctxt); + ASSERT_GE(list.size(), 1U); + auto wkt2 = "GEOGCRS[\"unnamed\",\n" + " DATUM[\"Amersfoort\",\n" + " ELLIPSOID[\"Bessel 1841\",6377397.155,299.1528128,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]]," + " USAGE[\n" + " SCOPE[\"unknown\"],\n" + " AREA[\"Netherlands - onshore\"],\n" + " BBOX[50.75,3.2,53.7,7.22]]]\n"; + + auto obj = WKTParser().createFromWKT(wkt2); + auto src_from_wkt2 = nn_dynamic_pointer_cast<CRS>(obj); + ASSERT_TRUE(src_from_wkt2 != nullptr); + auto list2 = CoordinateOperationFactory::create()->createOperations( + NN_NO_CHECK(src_from_wkt2), dst, ctxt); + ASSERT_GE(list.size(), list2.size()); + for (size_t i = 0; i < list.size(); i++) { + const auto &op = list[i]; + const auto &op2 = list2[i]; + EXPECT_TRUE( + op->isEquivalentTo(op2.get(), IComparable::Criterion::EQUIVALENT)) + << op->nameStr() << " " << op2->nameStr(); + } +} + +// --------------------------------------------------------------------------- + TEST(operation, geocentricCRS_to_geogCRS_same_datum) { auto op = CoordinateOperationFactory::create()->createOperation( @@ -4772,9 +4823,10 @@ TEST(operation, geocentricCRS_to_geogCRS_different_datum) { auto op = CoordinateOperationFactory::create()->createOperation( createGeocentricDatumWGS84(), GeographicCRS::EPSG_4269); ASSERT_TRUE(op != nullptr); - EXPECT_EQ(op->nameStr(), "Null geocentric translation from WGS 84 to NAD83 " - "(geocentric) + Conversion from NAD83 " - "(geocentric) to NAD83"); + EXPECT_EQ(op->nameStr(), + "Ballpark geocentric translation from WGS 84 to NAD83 " + "(geocentric) + Conversion from NAD83 " + "(geocentric) to NAD83"); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +inv +proj=cart +ellps=GRS80 +step " "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " @@ -4789,7 +4841,7 @@ TEST(operation, geogCRS_to_geocentricCRS_different_datum) { GeographicCRS::EPSG_4269, createGeocentricDatumWGS84()); ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->nameStr(), "Conversion from NAD83 to NAD83 (geocentric) + " - "Null geocentric translation from NAD83 " + "Ballpark geocentric translation from NAD83 " "(geocentric) to WGS 84"); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +proj=axisswap +order=2,1 +step " @@ -4805,8 +4857,9 @@ TEST(operation, geocentricCRS_to_geocentricCRS_noop) { createGeocentricDatumWGS84(), createGeocentricDatumWGS84()); ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->nameStr(), - "Null geocentric translation from WGS 84 to WGS 84"); - EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), ""); + "Ballpark geocentric translation from WGS 84 to WGS 84"); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=noop"); EXPECT_EQ(op->inverse()->nameStr(), op->nameStr()); } @@ -4902,6 +4955,24 @@ TEST(operation, geogCRS_geocentricCRS_same_datum_to_context) { // --------------------------------------------------------------------------- TEST(operation, + geog2D_to_geog3D_same_datum_but_with_potential_other_pivot_context) { + // Check that when going from geog2D to geog3D of same datum, we don't + // try to go through a WGS84 pivot... + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("5365"), // CR 05 2D + authFactory->createCoordinateReferenceSystem("5364"), // CR 05 3D + ctxt); + ASSERT_EQ(list.size(), 1U); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=noop"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_different_datum_though_geocentric_transform_context) { auto authFactory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); @@ -5179,6 +5250,50 @@ TEST(operation, projCRS_to_geogCRS) { // --------------------------------------------------------------------------- +TEST(operation, projCRS_no_id_to_geogCRS_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto src = authFactory->createCoordinateReferenceSystem( + "28992"); // Amersfoort / RD New + auto dst = + authFactory->createCoordinateReferenceSystem("4258"); // ETRS89 2D + auto list = + CoordinateOperationFactory::create()->createOperations(src, dst, ctxt); + ASSERT_GE(list.size(), 1U); + auto wkt2 = + "PROJCRS[\"unknown\",\n" + " BASEGEOGCRS[\"Amersfoort\",\n" + " DATUM[\"Amersfoort\",\n" + " ELLIPSOID[\"Bessel 1841\",6377397.155,299.1528128]]],\n" + " CONVERSION[\"unknown\",\n" + " METHOD[\"Oblique Stereographic\"],\n" + " PARAMETER[\"Latitude of natural origin\",52.1561605555556],\n" + " PARAMETER[\"Longitude of natural origin\",5.38763888888889],\n" + " PARAMETER[\"Scale factor at natural origin\",0.9999079],\n" + " PARAMETER[\"False easting\",155000],\n" + " PARAMETER[\"False northing\",463000]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"(E)\",east],\n" + " AXIS[\"(N)\",north],\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",28992]]"; + auto obj = WKTParser().createFromWKT(wkt2); + auto src_from_wkt2 = nn_dynamic_pointer_cast<CRS>(obj); + ASSERT_TRUE(src_from_wkt2 != nullptr); + auto list2 = CoordinateOperationFactory::create()->createOperations( + NN_NO_CHECK(src_from_wkt2), dst, ctxt); + ASSERT_GE(list.size(), list2.size() - 1); + for (size_t i = 0; i < list.size(); i++) { + const auto &op = list[i]; + const auto &op2 = list2[i]; + EXPECT_TRUE( + op->isEquivalentTo(op2.get(), IComparable::Criterion::EQUIVALENT)); + } +} + +// --------------------------------------------------------------------------- + TEST(operation, projCRS_to_projCRS) { auto op = CoordinateOperationFactory::create()->createOperation( @@ -5281,6 +5396,39 @@ TEST(operation, projCRS_to_projCRS_context_incompatible_areas) { // --------------------------------------------------------------------------- +TEST(operation, projCRS_to_projCRS_context_incompatible_areas_ballpark) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("26711"), // UTM 11 NAD27 + authFactory->createCoordinateReferenceSystem( + "3034"), // ETRS89 / LCC Europe + ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_TRUE(list[0]->hasBallparkTransformation()); +} + +// --------------------------------------------------------------------------- + +TEST( + operation, + projCRS_to_projCRS_context_incompatible_areas_crs_extent_use_intersection) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setSourceAndTargetCRSExtentUse( + CoordinateOperationContext::SourceTargetCRSExtentUse::INTERSECTION); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("26711"), // UTM 11 NAD27 + authFactory->createCoordinateReferenceSystem( + "3034"), // ETRS89 / LCC Europe + ctxt); + ASSERT_GE(list.size(), 0U); +} + +// --------------------------------------------------------------------------- + TEST(operation, projCRS_to_projCRS_north_pole_inverted_axis) { auto authFactory = @@ -5317,6 +5465,37 @@ TEST(operation, projCRS_to_projCRS_south_pole_inverted_axis) { // --------------------------------------------------------------------------- +TEST(operation, projCRS_to_projCRS_through_geog3D) { + // Check that when going from projCRS to projCRS, using + // geog2D-->geog3D-->geog3D-->geog2D we do not have issues with + // inconsistent CRS chaining, due to how we 'hack' a bit some intermediate + // steps + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("5367"), // CR05 / CRTM05 + authFactory->createCoordinateReferenceSystem( + "8908"), // CR-SIRGAS / CRTM05 + ctxt); + ASSERT_EQ(list.size(), 1U); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 " + "+step +inv +proj=tmerc +lat_0=0 +lon_0=-84 +k=0.9999 " + "+x_0=500000 +y_0=0 +ellps=WGS84 " + "+step +proj=push +v_3 " + "+step +proj=cart +ellps=WGS84 " + "+step +proj=helmert +x=-0.16959 +y=0.35312 +z=0.51846 " + "+rx=-0.03385 +ry=0.16325 +rz=-0.03446 +s=0.03693 " + "+convention=position_vector " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=pop +v_3 " + "+step +proj=tmerc +lat_0=0 +lon_0=-84 +k=0.9999 +x_0=500000 " + "+y_0=0 +ellps=GRS80"); +} + +// --------------------------------------------------------------------------- + TEST(operation, boundCRS_of_geogCRS_to_geogCRS) { auto boundCRS = BoundCRS::createFromTOWGS84( GeographicCRS::EPSG_4807, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); @@ -5324,14 +5503,14 @@ TEST(operation, boundCRS_of_geogCRS_to_geogCRS) { boundCRS, GeographicCRS::EPSG_4326); ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=grad +xy_out=rad " - "+step +inv +proj=longlat +ellps=clrk80ign +pm=paris +step " - "+proj=cart +ellps=clrk80ign +step +proj=helmert +x=1 +y=2 +z=3 " - "+rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step +inv " - "+proj=cart +ellps=WGS84 +step +proj=unitconvert +xy_in=rad " - "+xy_out=deg +step +proj=axisswap +order=2,1 +step +proj=pop " - "+v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv " + "+proj=longlat +ellps=clrk80ign +pm=paris +step +proj=push +v_3 " + "+step +proj=cart +ellps=clrk80ign +step +proj=helmert +x=1 +y=2 " + "+z=3 +rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step " + "+inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); } // --------------------------------------------------------------------------- @@ -5345,13 +5524,13 @@ TEST(operation, boundCRS_of_geogCRS_to_geogCRS_with_area) { boundCRS, authFactory->createCoordinateReferenceSystem("4326")); ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=clrk66 +step +proj=helmert +x=1 +y=2 +z=3 " - "+rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step +inv " - "+proj=cart +ellps=WGS84 +step +proj=unitconvert +xy_in=rad " - "+xy_out=deg +step +proj=axisswap +order=2,1 +step +proj=pop " - "+v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=clrk66 +step +proj=helmert +x=1 +y=2 " + "+z=3 +rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step " + "+inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); } // --------------------------------------------------------------------------- @@ -5387,8 +5566,8 @@ TEST(operation, createOperation_boundCRS_identified_by_datum) { NN_CHECK_ASSERT(src), NN_CHECK_ASSERT(dest)); ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=unitconvert " - "+xy_in=deg +xy_out=rad +step +proj=cart +ellps=WGS84 +step " + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=push +v_3 +step +proj=cart +ellps=WGS84 +step " "+proj=helmert +x=263 +y=-6 +z=-431 +step +inv +proj=cart " "+ellps=clrk80ign +step +proj=pop +v_3 +step +proj=utm +zone=32 " "+ellps=clrk80ign"); @@ -5461,13 +5640,11 @@ TEST(operation, boundCRS_of_projCRS_to_geogCRS) { ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=clrk80ign " - "+pm=paris +step +proj=longlat +ellps=clrk80ign +pm=paris +step " - "+proj=push +v_3 +step +inv +proj=longlat +ellps=clrk80ign " - "+pm=paris +step +proj=cart +ellps=clrk80ign +step +proj=helmert " - "+x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 +s=7 " - "+convention=position_vector +step +inv +proj=cart +ellps=WGS84 " - "+step +proj=unitconvert +xy_in=rad +xy_out=deg +step " - "+proj=axisswap +order=2,1 +step +proj=pop +v_3"); + "+pm=paris +step +proj=push +v_3 +step +proj=cart " + "+ellps=clrk80ign +step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 " + "+rz=6 +s=7 +convention=position_vector +step +inv +proj=cart " + "+ellps=WGS84 +step +proj=pop +v_3 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); } // --------------------------------------------------------------------------- @@ -5483,13 +5660,45 @@ TEST(operation, boundCRS_of_geogCRS_to_projCRS) { CoordinateOperationFactory::create()->createOperation(boundCRS, utm31); ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=grad +xy_out=rad " - "+step +inv +proj=longlat +ellps=clrk80ign +pm=paris +step " - "+proj=cart +ellps=clrk80ign +step +proj=helmert +x=1 +y=2 +z=3 " - "+rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step +inv " - "+proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step +proj=utm " - "+zone=31 +ellps=WGS84"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv " + "+proj=longlat +ellps=clrk80ign +pm=paris +step +proj=push +v_3 " + "+step +proj=cart +ellps=clrk80ign +step +proj=helmert +x=1 +y=2 " + "+z=3 +rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step " + "+inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step " + "+proj=utm +zone=31 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, boundCRS_of_geogCRS_to_unrelated_geogCRS_context) { + auto src = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4807, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + // ETRS89 + auto dst = authFactory->createCoordinateReferenceSystem("4258"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = + CoordinateOperationFactory::create()->createOperations(src, dst, ctxt); + ASSERT_EQ(list.size(), 1U); + // Check with it is a concatenated operation, since it doesn't particularly + // show up in the PROJ string + EXPECT_TRUE(dynamic_cast<ConcatenatedOperation *>(list[0].get()) != + nullptr); + EXPECT_EQ(list[0]->nameStr(), "Transformation from NTF (Paris) to WGS84 + " + "Inverse of ETRS89 to WGS 84 (1)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=grad +xy_out=rad " + "+step +inv +proj=longlat +ellps=clrk80ign +pm=paris " + "+step +proj=push +v_3 +step +proj=cart +ellps=clrk80ign " + "+step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 +s=7 " + "+convention=position_vector " + "+step +inv +proj=cart +ellps=GRS80 +step +proj=pop +v_3 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); } // --------------------------------------------------------------------------- @@ -5501,14 +5710,14 @@ TEST(operation, geogCRS_to_boundCRS_of_geogCRS) { GeographicCRS::EPSG_4326, boundCRS); ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=WGS84 +step +inv +proj=helmert +x=1 +y=2 +z=3 " - "+rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step +inv " - "+proj=cart +ellps=clrk80ign +step +proj=longlat " - "+ellps=clrk80ign +pm=paris +step +proj=unitconvert +xy_in=rad " - "+xy_out=grad +step +proj=axisswap +order=2,1 +step +proj=pop " - "+v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=WGS84 +step +inv +proj=helmert +x=1 " + "+y=2 +z=3 +rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector " + "+step +inv +proj=cart +ellps=clrk80ign +step +proj=pop +v_3 " + "+step +proj=longlat +ellps=clrk80ign +pm=paris +step " + "+proj=unitconvert +xy_in=rad +xy_out=grad +step +proj=axisswap " + "+order=2,1"); } // --------------------------------------------------------------------------- @@ -5531,14 +5740,12 @@ TEST(operation, boundCRS_to_boundCRS) { ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=clrk80ign " - "+pm=paris +step +proj=longlat +ellps=clrk80ign +pm=paris +step " - "+proj=push +v_3 +step +inv +proj=longlat +ellps=clrk80ign " - "+pm=paris +step +proj=cart +ellps=clrk80ign +step +proj=helmert " - "+x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 +s=7 " - "+convention=position_vector +step +inv +proj=helmert +x=8 +y=9 " - "+z=10 +rx=11 +ry=12 +rz=13 +s=14 +convention=position_vector " - "+step +inv +proj=cart +ellps=GRS80 +step +proj=pop +v_3 +step " - "+proj=utm +zone=32 +ellps=GRS80"); + "+pm=paris +step +proj=push +v_3 +step +proj=cart " + "+ellps=clrk80ign +step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 " + "+rz=6 +s=7 +convention=position_vector +step +inv +proj=helmert " + "+x=8 +y=9 +z=10 +rx=11 +ry=12 +rz=13 +s=14 " + "+convention=position_vector +step +inv +proj=cart +ellps=GRS80 " + "+step +proj=pop +v_3 +step +proj=utm +zone=32 +ellps=GRS80"); } // --------------------------------------------------------------------------- @@ -5552,12 +5759,12 @@ TEST(operation, boundCRS_to_boundCRS_noop_for_TOWGS84) { boundCRS2); ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=grad +xy_out=rad " - "+step +inv +proj=longlat +ellps=clrk80ign +pm=paris +step " - "+proj=cart +ellps=clrk80ign +step +inv +proj=cart +ellps=GRS80 " - "+step +proj=unitconvert +xy_in=rad +xy_out=deg +step " - "+proj=axisswap +order=2,1 +step +proj=pop +v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv " + "+proj=longlat +ellps=clrk80ign +pm=paris +step +proj=push +v_3 " + "+step +proj=cart +ellps=clrk80ign +step +inv +proj=cart " + "+ellps=GRS80 +step +proj=pop +v_3 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); } // --------------------------------------------------------------------------- @@ -5696,6 +5903,35 @@ TEST(operation, boundCRS_with_basecrs_with_extent_to_geogCRS) { // --------------------------------------------------------------------------- +TEST(operation, ETRS89_3D_to_proj_string_with_geoidgrids_nadgrids) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + // ETRS89 3D + auto src = authFactory->createCoordinateReferenceSystem("4937"); + auto objDst = PROJStringParser().createFromPROJString( + "+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 " + "+k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel " + "+nadgrids=rdtrans2008.gsb +geoidgrids=naptrans2008.gtx +units=m " + "+type=crs"); + auto dst = nn_dynamic_pointer_cast<CRS>(objDst); + ASSERT_TRUE(dst != nullptr); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + src, NN_NO_CHECK(dst), ctxt); + ASSERT_EQ(list.size(), 1U); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=axisswap +order=2,1 " + "+step +inv +proj=vgridshift +grids=naptrans2008.gtx " + "+multiplier=1 " + "+step +inv +proj=hgridshift +grids=rdtrans2008.gsb " + "+step +proj=sterea +lat_0=52.1561605555556 " + "+lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 " + "+y_0=463000 +ellps=bessel"); +} + +// --------------------------------------------------------------------------- + static VerticalCRSNNPtr createVerticalCRS() { PropertyMap propertiesVDatum; propertiesVDatum.set(Identifier::CODESPACE_KEY, "EPSG") @@ -5734,8 +5970,8 @@ static BoundCRSNNPtr createBoundVerticalCRS() { auto vertCRS = createVerticalCRS(); auto transformation = Transformation::createGravityRelatedHeightToGeographic3D( - PropertyMap(), vertCRS, GeographicCRS::EPSG_4979, "egm08_25.gtx", - std::vector<PositionalAccuracyNNPtr>()); + PropertyMap(), vertCRS, GeographicCRS::EPSG_4979, nullptr, + "egm08_25.gtx", std::vector<PositionalAccuracyNNPtr>()); return BoundCRS::create(vertCRS, GeographicCRS::EPSG_4979, transformation); } @@ -5769,6 +6005,59 @@ TEST(operation, transformation_height_to_PROJ_string) { // --------------------------------------------------------------------------- +TEST(operation, transformation_Geographic3D_to_GravityRelatedHeight_gtx) { + auto wkt = + "COORDINATEOPERATION[\"ETRS89 to NAP height (1)\",\n" + " VERSION[\"RDNAP-Nld 2008\"],\n" + " SOURCECRS[\n" + " GEOGCRS[\"ETRS89\",\n" + " DATUM[\"European Terrestrial Reference System 1989\",\n" + " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,3],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"ellipsoidal height (h)\",up,\n" + " ORDER[3],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",4937]]],\n" + " TARGETCRS[\n" + " VERTCRS[\"NAP height\",\n" + " VDATUM[\"Normaal Amsterdams Peil\"],\n" + " CS[vertical,1],\n" + " AXIS[\"gravity-related height (H)\",up,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",5709]]],\n" + " METHOD[\"Geographic3D to GravityRelatedHeight (US .gtx)\",\n" + " ID[\"EPSG\",9665]],\n" + " PARAMETERFILE[\"Geoid (height correction) model " + "file\",\"naptrans2008.gtx\"],\n" + " OPERATIONACCURACY[0.01],\n" + " USAGE[\n" + " SCOPE[\"unknown\"],\n" + " AREA[\"Netherlands - onshore\"],\n" + " BBOX[50.75,3.2,53.7,7.22]],\n" + " ID[\"EPSG\",7001]]"; + ; + auto obj = WKTParser().createFromWKT(wkt); + auto transf = nn_dynamic_pointer_cast<Transformation>(obj); + ASSERT_TRUE(transf != nullptr); + + // Check that we correctly inverse files in the case of + // "Geographic3D to GravityRelatedHeight (US .gtx)" + EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=vgridshift " + "+grids=naptrans2008.gtx +multiplier=1"); +} + +// --------------------------------------------------------------------------- + TEST(operation, transformation_ntv2_to_PROJ_string) { auto transformation = Transformation::createNTv2( PropertyMap(), GeographicCRS::EPSG_4807, GeographicCRS::EPSG_4326, @@ -5955,13 +6244,13 @@ TEST(operation, compoundCRS_with_boundGeogCRS_to_geogCRS) { compound, GeographicCRS::EPSG_4979); ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " - "+proj=cart +ellps=WGS84 +step +proj=helmert +x=1 +y=2 +z=3 " - "+rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step +inv " - "+proj=cart +ellps=WGS84 +step +proj=unitconvert +xy_in=rad " - "+xy_out=deg +step +proj=axisswap +order=2,1 +step +proj=pop " - "+v_3"); + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=WGS84 +step +proj=helmert +x=1 +y=2 " + "+z=3 +rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step " + "+inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); } // --------------------------------------------------------------------------- @@ -5979,12 +6268,12 @@ TEST(operation, compoundCRS_with_boundGeogCRS_and_boundVerticalCRS_to_geogCRS) { // Not completely sure the order of horizontal and vertical operations // makes sense EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), - "+proj=pipeline +step +proj=push +v_3 +step +proj=axisswap " - "+order=2,1 +step +proj=unitconvert +xy_in=grad +xy_out=rad " - "+step +inv +proj=longlat +ellps=clrk80ign +pm=paris +step " - "+proj=cart +ellps=clrk80ign +step +proj=helmert +x=1 +y=2 +z=3 " - "+rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step +inv " - "+proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step " + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv " + "+proj=longlat +ellps=clrk80ign +pm=paris +step +proj=push +v_3 " + "+step +proj=cart +ellps=clrk80ign +step +proj=helmert +x=1 +y=2 " + "+z=3 +rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step " + "+inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step " "+proj=vgridshift +grids=egm08_25.gtx +multiplier=1 +step " "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " "+order=2,1"); @@ -6018,14 +6307,12 @@ TEST(operation, compoundCRS_with_boundProjCRS_and_boundVerticalCRS_to_geogCRS) { // makes sense EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=clrk80ign " - "+pm=paris +step +proj=longlat +ellps=clrk80ign +pm=paris +step " - "+proj=push +v_3 +step +inv +proj=longlat +ellps=clrk80ign " - "+pm=paris +step +proj=cart +ellps=clrk80ign +step +proj=helmert " - "+x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 +s=7 " - "+convention=position_vector +step +inv +proj=cart +ellps=WGS84 " - "+step +proj=pop +v_3 +step +proj=vgridshift +grids=egm08_25.gtx " - "+multiplier=1 +step +proj=unitconvert +xy_in=rad +xy_out=deg " - "+step +proj=axisswap +order=2,1"); + "+pm=paris +step +proj=push +v_3 +step +proj=cart " + "+ellps=clrk80ign +step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 " + "+rz=6 +s=7 +convention=position_vector +step +inv +proj=cart " + "+ellps=WGS84 +step +proj=pop +v_3 +step +proj=vgridshift " + "+grids=egm08_25.gtx +multiplier=1 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); auto opInverse = CoordinateOperationFactory::create()->createOperation( GeographicCRS::EPSG_4979, compound); @@ -6035,6 +6322,51 @@ TEST(operation, compoundCRS_with_boundProjCRS_and_boundVerticalCRS_to_geogCRS) { // --------------------------------------------------------------------------- +TEST(operation, geocent_to_compoundCRS) { + auto objSrc = PROJStringParser().createFromPROJString( + "+proj=geocent +datum=WGS84 +units=m +type=crs"); + auto src = nn_dynamic_pointer_cast<CRS>(objSrc); + ASSERT_TRUE(src != nullptr); + auto objDst = PROJStringParser().createFromPROJString( + "+proj=longlat +ellps=GRS67 +nadgrids=@foo.gsb +geoidgrids=@foo.gtx " + "+type=crs"); + auto dst = nn_dynamic_pointer_cast<CRS>(objDst); + ASSERT_TRUE(dst != nullptr); + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(src), NN_CHECK_ASSERT(dst)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=cart +ellps=WGS84 +step +inv " + "+proj=vgridshift +grids=@foo.gtx +multiplier=1 +step +inv " + "+proj=hgridshift +grids=@foo.gsb +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geocent_to_compoundCRS_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + // WGS84 geocentric + auto src = authFactory->createCoordinateReferenceSystem("4978"); + auto objDst = PROJStringParser().createFromPROJString( + "+proj=longlat +ellps=GRS67 +nadgrids=@foo.gsb +geoidgrids=@foo.gtx " + "+type=crs"); + auto dst = nn_dynamic_pointer_cast<CRS>(objDst); + ASSERT_TRUE(dst != nullptr); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + src, NN_CHECK_ASSERT(dst), ctxt); + ASSERT_EQ(list.size(), 1U); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=cart +ellps=WGS84 +step +inv " + "+proj=vgridshift +grids=@foo.gtx +multiplier=1 +step +inv " + "+proj=hgridshift +grids=@foo.gsb +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg"); +} + +// --------------------------------------------------------------------------- + TEST(operation, compoundCRS_to_compoundCRS) { auto compound1 = CompoundCRS::create( PropertyMap(), @@ -6125,6 +6457,100 @@ TEST(operation, compoundCRS_to_compoundCRS_with_vertical_transform) { // --------------------------------------------------------------------------- +TEST(operation, compoundCRS_to_compoundCRS_with_bound_crs_in_horiz_and_vert) { + auto objSrc = PROJStringParser().createFromPROJString( + "+proj=longlat +ellps=GRS67 +nadgrids=@foo.gsb +geoidgrids=@foo.gtx " + "+type=crs"); + auto src = nn_dynamic_pointer_cast<CRS>(objSrc); + ASSERT_TRUE(src != nullptr); + auto objDst = PROJStringParser().createFromPROJString( + "+proj=longlat +ellps=GRS80 +nadgrids=@bar.gsb +geoidgrids=@bar.gtx " + "+type=crs"); + auto dst = nn_dynamic_pointer_cast<CRS>(objDst); + ASSERT_TRUE(dst != nullptr); + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(src), NN_CHECK_ASSERT(dst)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=hgridshift +grids=@foo.gsb " + "+step +proj=vgridshift +grids=@foo.gtx +multiplier=1 " + "+step +inv +proj=vgridshift +grids=@bar.gtx +multiplier=1 " + "+step +inv +proj=hgridshift +grids=@bar.gsb " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_to_compoundCRS_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + auto list = CoordinateOperationFactory::create()->createOperations( + // NAD27 + NGVD29 height (ftUS) + authFactory->createCoordinateReferenceSystem("7406"), + // NAD83(NSRS2007) + NAVD88 height + authFactory->createCoordinateReferenceSystem("5500"), ctxt); + // 152 or 155 depending if the VERTCON grids are there + ASSERT_GE(list.size(), 152U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->nameStr(), "NGVD29 height (ftUS) to NAVD88 height (3) + " + "NAD27 to WGS 84 (79) + Inverse of " + "NAD83(NSRS2007) to WGS 84 (1)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +z_in=us-ft +xy_out=rad +z_out=m " + "+step +proj=vgridshift +grids=vertcone.gtx +multiplier=0.001 " + "+step +proj=hgridshift +grids=conus +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); + { + // Test that we can round-trip this through WKT and still get the same + // PROJ string. + auto wkt = list[0]->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()); + auto obj = WKTParser().createFromWKT(wkt); + auto co = nn_dynamic_pointer_cast<CoordinateOperation>(obj); + ASSERT_TRUE(co != nullptr); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + co->exportToPROJString(PROJStringFormatter::create().get())); + } + + bool foundApprox = false; + for (size_t i = 0; i < list.size(); i++) { + auto projString = + list[i]->exportToPROJString(PROJStringFormatter::create().get()); + EXPECT_TRUE( + projString.find("+proj=pipeline +step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +z_in=us-ft " + "+xy_out=rad +z_out=m") == 0) + << list[i]->nameStr(); + if (list[i]->nameStr().find("Transformation from NGVD29 height (ftUS) " + "to NAVD88 height (ballpark vertical " + "transformation)") == 0) { + EXPECT_TRUE(list[i]->hasBallparkTransformation()); + EXPECT_EQ(list[i]->nameStr(), + "Transformation from NGVD29 height (ftUS) to NAVD88 " + "height (ballpark vertical transformation) + NAD27 to " + "WGS 84 (79) + Inverse of NAD83(NSRS2007) to WGS 84 (1)"); + EXPECT_EQ(projString, + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +z_in=us-ft +xy_out=rad " + "+z_out=m +step +proj=hgridshift +grids=conus " + "+step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + foundApprox = true; + break; + } + } + EXPECT_TRUE(foundApprox); +} + +// --------------------------------------------------------------------------- + TEST(operation, vertCRS_to_vertCRS) { auto vertcrs_m_obj = PROJStringParser().createFromPROJString("+vunits=m"); @@ -6174,6 +6600,25 @@ TEST(operation, vertCRS_to_vertCRS) { // --------------------------------------------------------------------------- +TEST(operation, vertCRS_to_vertCRS_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + auto list = CoordinateOperationFactory::create()->createOperations( + // NGVD29 height (m) + authFactory->createCoordinateReferenceSystem("7968"), + // NAVD88 height (1) + authFactory->createCoordinateReferenceSystem("5703"), ctxt); + ASSERT_EQ(list.size(), 3U); + EXPECT_EQ(list[0]->nameStr(), "NGVD29 height (m) to NAVD88 height (3)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=vgridshift +grids=vertcone.gtx +multiplier=0.001"); +} + +// --------------------------------------------------------------------------- + TEST(operation, compoundCRS_to_geogCRS_3D) { auto compoundcrs_ft_obj = PROJStringParser().createFromPROJString( @@ -6190,6 +6635,7 @@ TEST(operation, compoundCRS_to_geogCRS_3D) { auto op = CoordinateOperationFactory::create()->createOperation( NN_CHECK_ASSERT(compoundcrs_ft), NN_CHECK_ASSERT(geogcrs_m)); ASSERT_TRUE(op != nullptr); + EXPECT_TRUE(op->hasBallparkTransformation()); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +inv +proj=merc +lon_0=0 +k=1 +x_0=0 " "+y_0=0 +ellps=WGS84 +step +proj=unitconvert +xy_in=rad " @@ -6200,6 +6646,7 @@ TEST(operation, compoundCRS_to_geogCRS_3D) { auto op = CoordinateOperationFactory::create()->createOperation( NN_CHECK_ASSERT(geogcrs_m), NN_CHECK_ASSERT(compoundcrs_ft)); ASSERT_TRUE(op != nullptr); + EXPECT_TRUE(op->hasBallparkTransformation()); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +proj=unitconvert +xy_in=deg +z_in=m " "+xy_out=rad +z_out=ft +step +proj=merc +lon_0=0 +k=1 +x_0=0 " @@ -6209,6 +6656,195 @@ TEST(operation, compoundCRS_to_geogCRS_3D) { // --------------------------------------------------------------------------- +TEST(operation, compoundCRS_to_geogCRS_3D_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + // CompoundCRS to Geog3DCRS, with vertical unit change, but without + // ellipsoid height <--> vertical height correction + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem( + "7406"), // NAD27 + NGVD29 height (ftUS) + authFactory->createCoordinateReferenceSystem("4979"), // WGS 84 + ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_TRUE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->nameStr(), + "NAD27 to WGS 84 (79) + Transformation from NGVD29 height " + "(ftUS) to WGS 84 (ballpark vertical transformation, without " + "ellipsoid height to vertical height correction)"); + EXPECT_EQ(list[0]->exportToPROJString( + PROJStringFormatter::create( + PROJStringFormatter::Convention::PROJ_5, + authFactory->databaseContext()) + .get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=hgridshift +grids=conus +step +proj=unitconvert " + "+xy_in=rad +z_in=us-ft +xy_out=deg +z_out=m +step " + "+proj=axisswap +order=2,1"); + } + + // CompoundCRS to Geog3DCRS, with same vertical unit, but without + // ellipsoid height <--> vertical height correction + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem( + "5500"), // NAD83(NSRS2007) + NAVD88 height + authFactory->createCoordinateReferenceSystem("4979"), // WGS 84 + ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_TRUE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->nameStr(), + "NAD83(NSRS2007) to WGS 84 (1) + Transformation from NAVD88 " + "height to WGS 84 (ballpark vertical transformation, without " + "ellipsoid height to vertical height correction)"); + EXPECT_EQ(list[0]->exportToPROJString( + PROJStringFormatter::create( + PROJStringFormatter::Convention::PROJ_5, + authFactory->databaseContext()) + .get()), + "+proj=noop"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_from_WKT2_to_geogCRS_3D_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto src = authFactory->createCoordinateReferenceSystem( + "7415"); // Amersfoort / RD New + NAP height + auto dst = + authFactory->createCoordinateReferenceSystem("4937"); // ETRS89 3D + auto list = + CoordinateOperationFactory::create()->createOperations(src, dst, ctxt); + ASSERT_GE(list.size(), 1U); + auto wkt2 = src->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()); + auto obj = WKTParser().createFromWKT(wkt2); + auto src_from_wkt2 = nn_dynamic_pointer_cast<CRS>(obj); + ASSERT_TRUE(src_from_wkt2 != nullptr); + auto list2 = CoordinateOperationFactory::create()->createOperations( + NN_NO_CHECK(src_from_wkt2), dst, ctxt); + ASSERT_GE(list.size(), list2.size()); + for (size_t i = 0; i < list.size(); i++) { + const auto &op = list[i]; + const auto &op2 = list2[i]; + EXPECT_TRUE( + op->isEquivalentTo(op2.get(), IComparable::Criterion::EQUIVALENT)); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_from_WKT2_no_id_to_geogCRS_3D_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + auto src = authFactory->createCoordinateReferenceSystem( + "7415"); // Amersfoort / RD New + NAP height + auto dst = + authFactory->createCoordinateReferenceSystem("4937"); // ETRS89 3D + auto list = + CoordinateOperationFactory::create()->createOperations(src, dst, ctxt); + ASSERT_GE(list.size(), 1U); + + { + // Important here is vgridshift before hgridshift + auto op_proj = + list[0]->exportToPROJString(PROJStringFormatter::create().get()); + EXPECT_EQ( + op_proj, + "+proj=pipeline +step +inv +proj=sterea +lat_0=52.1561605555556 " + "+lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 " + "+ellps=bessel " + "+step +proj=vgridshift +grids=naptrans2008.gtx +multiplier=1 " + "+step +proj=hgridshift +grids=rdtrans2008.gsb " + "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m " + "+step +proj=axisswap +order=2,1"); + } + + auto wkt2 = + "COMPOUNDCRS[\"unknown\",\n" + " PROJCRS[\"unknown\",\n" + " BASEGEOGCRS[\"Amersfoort\",\n" + " DATUM[\"Amersfoort\",\n" + " ELLIPSOID[\"Bessel " + "1841\",6377397.155,299.1528128]]],\n" + " CONVERSION[\"unknown\",\n" + " METHOD[\"Oblique Stereographic\"],\n" + " PARAMETER[\"Latitude of natural origin\",52.1561605555556],\n" + " PARAMETER[\"Longitude of natural origin\",5.38763888888889],\n" + " PARAMETER[\"Scale factor at natural origin\",0.9999079],\n" + " PARAMETER[\"False easting\",155000],\n" + " PARAMETER[\"False northing\",463000]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"(E)\",east],\n" + " AXIS[\"(N)\",north],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " VERTCRS[\"NAP height\",\n" + " VDATUM[\"Normaal Amsterdams Peil\"],\n" + " CS[vertical,1],\n" + " AXIS[\"gravity-related height (H)\",up,\n" + " LENGTHUNIT[\"metre\",1]]]]"; + auto obj = WKTParser().createFromWKT(wkt2); + auto src_from_wkt2 = nn_dynamic_pointer_cast<CRS>(obj); + ASSERT_TRUE(src_from_wkt2 != nullptr); + auto list2 = CoordinateOperationFactory::create()->createOperations( + NN_NO_CHECK(src_from_wkt2), dst, ctxt); + ASSERT_GE(list.size(), list2.size() - 1); + for (size_t i = 0; i < list.size(); i++) { + const auto &op = list[i]; + const auto &op2 = list2[i]; + auto op_proj = + op->exportToPROJString(PROJStringFormatter::create().get()); + auto op2_proj = + op2->exportToPROJString(PROJStringFormatter::create().get()); + EXPECT_EQ(op_proj, op2_proj) << "op=" << op->nameStr() + << " op2=" << op2->nameStr(); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, boundCRS_to_compoundCRS) { + auto objSrc = PROJStringParser().createFromPROJString( + "+proj=longlat +ellps=GRS67 +nadgrids=@foo.gsb +type=crs"); + auto src = nn_dynamic_pointer_cast<CRS>(objSrc); + ASSERT_TRUE(src != nullptr); + auto objDst = PROJStringParser().createFromPROJString( + "+proj=longlat +ellps=GRS80 +nadgrids=@bar.gsb +geoidgrids=@bar.gtx " + "+type=crs"); + auto dst = nn_dynamic_pointer_cast<CRS>(objDst); + ASSERT_TRUE(dst != nullptr); + + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(src), NN_CHECK_ASSERT(dst)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=hgridshift +grids=@foo.gsb " + "+step +inv +proj=vgridshift +grids=@bar.gtx +multiplier=1 " + "+step +inv +proj=hgridshift +grids=@bar.gsb " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg"); + + auto opInverse = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(dst), NN_CHECK_ASSERT(src)); + ASSERT_TRUE(opInverse != nullptr); + EXPECT_TRUE(opInverse->inverse()->_isEquivalentTo(op.get())); +} + +// --------------------------------------------------------------------------- + TEST(operation, IGNF_LAMB1_TO_EPSG_4326) { auto authFactory = AuthorityFactory::create(DatabaseContext::create(), std::string()); @@ -6223,6 +6859,7 @@ TEST(operation, IGNF_LAMB1_TO_EPSG_4326) { ctxt); ASSERT_EQ(list.size(), 2U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +inv +proj=lcc +lat_1=49.5 +lat_0=49.5 " "+lon_0=0 +k_0=0.99987734 +x_0=600000 +y_0=200000 " @@ -6230,14 +6867,15 @@ TEST(operation, IGNF_LAMB1_TO_EPSG_4326) { "+grids=ntf_r93.gsb +step +proj=unitconvert +xy_in=rad " "+xy_out=deg +step +proj=axisswap +order=2,1"); + EXPECT_FALSE(list[1]->hasBallparkTransformation()); EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +inv +proj=lcc +lat_1=49.5 +lat_0=49.5 " "+lon_0=0 +k_0=0.99987734 +x_0=600000 +y_0=200000 " "+ellps=clrk80ign +pm=paris +step +proj=push +v_3 +step " "+proj=cart +ellps=clrk80ign +step +proj=helmert +x=-168 +y=-60 " - "+z=320 +step +inv +proj=cart +ellps=WGS84 +step " - "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " - "+order=2,1 +step +proj=pop +v_3"); + "+z=320 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg +step " + "+proj=axisswap +order=2,1"); auto list2 = CoordinateOperationFactory::create()->createOperations( AuthorityFactory::create(DatabaseContext::create(), "EPSG") @@ -6264,14 +6902,14 @@ TEST(operation, IGNF_LAMB1_TO_EPSG_4326) { // --------------------------------------------------------------------------- -TEST(operation, isPROJInstanciable) { +TEST(operation, isPROJInstantiable) { { auto transformation = Transformation::createGeocentricTranslations( PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0, 2.0, 3.0, {}); EXPECT_TRUE( - transformation->isPROJInstanciable(DatabaseContext::create())); + transformation->isPROJInstantiable(DatabaseContext::create())); } // Missing grid @@ -6280,7 +6918,7 @@ TEST(operation, isPROJInstanciable) { PropertyMap(), GeographicCRS::EPSG_4807, GeographicCRS::EPSG_4326, "foo.gsb", std::vector<PositionalAccuracyNNPtr>()); EXPECT_FALSE( - transformation->isPROJInstanciable(DatabaseContext::create())); + transformation->isPROJInstantiable(DatabaseContext::create())); } // Unsupported method @@ -6292,7 +6930,7 @@ TEST(operation, isPROJInstanciable) { std::vector<GeneralParameterValueNNPtr>{}, std::vector<PositionalAccuracyNNPtr>{}); EXPECT_FALSE( - transformation->isPROJInstanciable(DatabaseContext::create())); + transformation->isPROJInstantiable(DatabaseContext::create())); } } @@ -7370,3 +8008,123 @@ TEST(operation, validateParameters) { EXPECT_EQ(validation, expected); } } + +// --------------------------------------------------------------------------- + +TEST(operation, normalizeForVisualization) { + + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + + // Source(geographic) must be inverted + { + auto src = authFactory->createCoordinateReferenceSystem("4326"); + auto dst = authFactory->createCoordinateReferenceSystem("32631"); + auto op = + CoordinateOperationFactory::create()->createOperation(src, dst); + ASSERT_TRUE(op != nullptr); + auto opNormalized = op->normalizeForVisualization(); + EXPECT_FALSE(opNormalized->_isEquivalentTo(op.get())); + EXPECT_EQ(opNormalized->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=utm +zone=31 +ellps=WGS84"); + } + + // Target(geographic) must be inverted + { + auto src = authFactory->createCoordinateReferenceSystem("32631"); + auto dst = authFactory->createCoordinateReferenceSystem("4326"); + auto op = + CoordinateOperationFactory::create()->createOperation(src, dst); + ASSERT_TRUE(op != nullptr); + auto opNormalized = op->normalizeForVisualization(); + EXPECT_FALSE(opNormalized->_isEquivalentTo(op.get())); + EXPECT_EQ(opNormalized->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=utm +zone=31 +ellps=WGS84 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg"); + } + + // Source(geographic) and target(projected) must be inverted + { + auto src = authFactory->createCoordinateReferenceSystem("4326"); + auto dst = authFactory->createCoordinateReferenceSystem("3040"); + auto op = + CoordinateOperationFactory::create()->createOperation(src, dst); + ASSERT_TRUE(op != nullptr); + auto opNormalized = op->normalizeForVisualization(); + EXPECT_FALSE(opNormalized->_isEquivalentTo(op.get())); + EXPECT_EQ(opNormalized->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=utm +zone=28 +ellps=GRS80"); + } + + // No inversion + { + auto src = authFactory->createCoordinateReferenceSystem("32631"); + auto dst = authFactory->createCoordinateReferenceSystem("32632"); + auto op = + CoordinateOperationFactory::create()->createOperation(src, dst); + ASSERT_TRUE(op != nullptr); + auto opNormalized = op->normalizeForVisualization(); + EXPECT_TRUE(opNormalized->_isEquivalentTo(op.get())); + } + + // Source(compoundCRS) and target(geographic 3D) must be inverted + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setUsePROJAlternativeGridNames(false); + auto src = CompoundCRS::create( + PropertyMap(), + std::vector<CRSNNPtr>{ + authFactory->createCoordinateReferenceSystem("4326"), + // EGM2008 height + authFactory->createCoordinateReferenceSystem("3855")}); + auto list = CoordinateOperationFactory::create()->createOperations( + src, + authFactory->createCoordinateReferenceSystem("4979"), // WGS 84 3D + ctxt); + ASSERT_EQ(list.size(), 2U); + auto op = list[1]; + auto opNormalized = op->normalizeForVisualization(); + EXPECT_FALSE(opNormalized->_isEquivalentTo(op.get())); + EXPECT_EQ(opNormalized->exportToPROJString( + PROJStringFormatter::create( + PROJStringFormatter::Convention::PROJ_5, + authFactory->databaseContext()) + .get()), + "+proj=pipeline " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=vgridshift +grids=egm08_25.gtx +multiplier=1 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg"); + } + + // Source(boundCRS) and target(geographic) must be inverted + { + auto src = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4269, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto dst = authFactory->createCoordinateReferenceSystem("4326"); + auto op = + CoordinateOperationFactory::create()->createOperation(src, dst); + ASSERT_TRUE(op != nullptr); + auto opNormalized = op->normalizeForVisualization(); + EXPECT_FALSE(opNormalized->_isEquivalentTo(op.get())); + EXPECT_EQ(opNormalized->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=push +v_3 " + "+step +proj=cart +ellps=GRS80 " + "+step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 +s=7 " + "+convention=position_vector " + "+step +inv +proj=cart +ellps=WGS84 " + "+step +proj=pop +v_3 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg"); + } +} |
