aboutsummaryrefslogtreecommitdiff
path: root/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/CMakeLists.txt72
-rw-r--r--test/unit/gie_self_tests.cpp53
-rw-r--r--test/unit/proj_errno_string_test.cpp1
-rw-r--r--test/unit/test_c_api.cpp150
-rw-r--r--test/unit/test_crs.cpp184
-rw-r--r--test/unit/test_datum.cpp4
-rw-r--r--test/unit/test_factory.cpp127
-rw-r--r--test/unit/test_io.cpp271
-rw-r--r--test/unit/test_operation.cpp1072
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");
+ }
+}