diff options
| -rw-r--r-- | docs/source/development/reference/index.rst | 1 | ||||
| -rw-r--r-- | docs/source/development/reference/macros.rst | 39 | ||||
| -rw-r--r-- | src/4D_api.cpp | 9 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 40 | ||||
| -rw-r--r-- | src/proj.h | 12 | ||||
| -rw-r--r-- | src/proj_internal.h | 49 | ||||
| -rw-r--r-- | test/unit/CMakeLists.txt | 10 | ||||
| -rw-r--r-- | test/unit/Makefile.am | 11 | ||||
| -rw-r--r-- | test/unit/test_c_api.cpp | 38 | ||||
| -rw-r--r-- | test/unit/test_misc.cpp | 48 |
10 files changed, 237 insertions, 20 deletions
diff --git a/docs/source/development/reference/index.rst b/docs/source/development/reference/index.rst index caa893c3..f04f4b3e 100644 --- a/docs/source/development/reference/index.rst +++ b/docs/source/development/reference/index.rst @@ -7,6 +7,7 @@ Reference .. toctree:: :maxdepth: 1 + macros datatypes functions cpp/index.rst diff --git a/docs/source/development/reference/macros.rst b/docs/source/development/reference/macros.rst new file mode 100644 index 00000000..131b8e72 --- /dev/null +++ b/docs/source/development/reference/macros.rst @@ -0,0 +1,39 @@ +.. _macros: + +================================================================================ +Macros +================================================================================ + +.. c:macro:: PROJ_VERSION_MAJOR + + Major version number, e.g 8 for PROJ 8.0.1 + +.. c:macro:: PROJ_VERSION_MINOR + + Minor version number, e.g 0 for PROJ 8.0.1 + +.. c:macro:: PROJ_VERSION_PATCH + + Patch version number, e.g 1 for PROJ 8.0.1 + +.. c:macro:: PROJ_COMPUTE_VERSION(maj,min,patch) + + .. versionadded:: 8.0.1 + + Compute the version number from the major, minor and patch numbers. + +.. c:macro:: PROJ_VERSION_NUMBER + + .. versionadded:: 8.0.1 + + Total version number, equal to + ``PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, PROJ_VERSION_PATCH)`` + +.. c:macro:: PROJ_AT_LEAST_VERSION(maj,min,patch) + + .. versionadded:: 8.0.1 + + Macro that returns true if the current PROJ version is at least the version + specified by (maj,min,patch) + + Equivalent to ``PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj,min,patch)`` diff --git a/src/4D_api.cpp b/src/4D_api.cpp index 9c5b45f5..4ef9b616 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -203,7 +203,7 @@ double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coord) { /**************************************************************************************/ int pj_get_suggested_operation(PJ_CONTEXT*, - const std::vector<CoordOperation>& opList, + const std::vector<PJCoordOperation>& opList, const int iExcluded[2], PJ_DIRECTION direction, PJ_COORD coord) @@ -1034,7 +1034,7 @@ static PJ* add_coord_op_to_list( PJ* pjGeogToSrc, PJ* pjGeogToDst, bool isOffshore, - std::vector<CoordOperation>& altCoordOps) { + std::vector<PJCoordOperation>& altCoordOps) { /*****************************************************************************/ double minxSrc; @@ -1193,7 +1193,7 @@ PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs, const char /*****************************************************************************/ -std::vector<CoordOperation> pj_create_prepared_operations(PJ_CONTEXT *ctx, +std::vector<PJCoordOperation> pj_create_prepared_operations(PJ_CONTEXT *ctx, const PJ *source_crs, const PJ *target_crs, PJ_OBJ_LIST* op_list) @@ -1218,7 +1218,7 @@ std::vector<CoordOperation> pj_create_prepared_operations(PJ_CONTEXT *ctx, try { - std::vector<CoordOperation> preparedOpList; + std::vector<PJCoordOperation> preparedOpList; // Iterate over source->target candidate transformations and reproject // their long-lat bounding box into the source CRS. @@ -1407,6 +1407,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons P->alternativeCoordinateOperations = std::move(preparedOpList); // The returned P is rather dummy + P->descr = "Set of coordinate operations"; P->iso_obj = nullptr; P->fwd = nullptr; P->inv = nullptr; diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 1462dd31..78eb4ac5 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -491,8 +491,11 @@ static const char *getOptionValue(const char *option, /** \brief "Clone" an object. * - * Technically this just increases the reference counter on the object, since - * PJ objects are immutable. + * The object might be used independently of the original object, provided that + * the use of context is compatible. In particular if you intend to use a + * clone in a different thread than the original object, you should pass a + * context that is different from the one of the original object (or later + * assing a different context with proj_assign_context()). * * The returned object must be unreferenced with proj_destroy() after use. * It should be used by at most one thread at a time. @@ -510,6 +513,18 @@ PJ *proj_clone(PJ_CONTEXT *ctx, const PJ *obj) { return nullptr; } if (!obj->iso_obj) { + if (!obj->alternativeCoordinateOperations.empty()) { + auto newPj = pj_new(); + if (newPj) { + newPj->descr = "Set of coordinate operations"; + newPj->ctx = ctx; + for (const auto &altOp : obj->alternativeCoordinateOperations) { + newPj->alternativeCoordinateOperations.emplace_back( + PJCoordOperation(ctx, altOp)); + } + } + return newPj; + } return nullptr; } try { @@ -1303,6 +1318,21 @@ static int proj_is_equivalent_to_internal(PJ_CONTEXT *ctx, const PJ *obj, } return false; } + + if (obj->iso_obj == nullptr && other->iso_obj == nullptr && + !obj->alternativeCoordinateOperations.empty() && + obj->alternativeCoordinateOperations.size() == + other->alternativeCoordinateOperations.size()) { + for (size_t i = 0; i < obj->alternativeCoordinateOperations.size(); + ++i) { + if (obj->alternativeCoordinateOperations[i] != + other->alternativeCoordinateOperations[i]) { + return false; + } + } + return true; + } + if (!obj->iso_obj || !other->iso_obj) { return false; } @@ -7899,7 +7929,7 @@ struct PJ_OPERATION_LIST : PJ_OBJ_LIST { PJ *source_crs; PJ *target_crs; bool hasPreparedOperation = false; - std::vector<CoordOperation> preparedOperations{}; + std::vector<PJCoordOperation> preparedOperations{}; explicit PJ_OPERATION_LIST(PJ_CONTEXT *ctx, const PJ *source_crsIn, const PJ *target_crsIn, @@ -7909,7 +7939,7 @@ struct PJ_OPERATION_LIST : PJ_OBJ_LIST { PJ_OPERATION_LIST(const PJ_OPERATION_LIST &) = delete; PJ_OPERATION_LIST &operator=(const PJ_OPERATION_LIST &) = delete; - const std::vector<CoordOperation> &getPreparedOperations(PJ_CONTEXT *ctx); + const std::vector<PJCoordOperation> &getPreparedOperations(PJ_CONTEXT *ctx); }; // --------------------------------------------------------------------------- @@ -7934,7 +7964,7 @@ PJ_OPERATION_LIST::~PJ_OPERATION_LIST() { // --------------------------------------------------------------------------- -const std::vector<CoordOperation> & +const std::vector<PJCoordOperation> & PJ_OPERATION_LIST::getPreparedOperations(PJ_CONTEXT *ctx) { if (!hasPreparedOperation) { hasPreparedOperation = true; @@ -174,6 +174,18 @@ extern "C" { #define PROJ_VERSION_MINOR 1 #define PROJ_VERSION_PATCH 0 +/* Note: the following 3 defines have been introduced in PROJ 8.0.1 */ +/* Macro to compute a PROJ version number from its components */ +#define PROJ_COMPUTE_VERSION(maj,min,patch) ((maj)*10000+(min)*100+(patch)) + +/* Current PROJ version from the above version numbers */ +#define PROJ_VERSION_NUMBER \ + PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, PROJ_VERSION_PATCH) + +/* Macro that returns true if the current PROJ version is at least the version specified by (maj,min,patch) */ +#define PROJ_AT_LEAST_VERSION(maj,min,patch) \ + (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj,min,patch)) + extern char const PROJ_DLL pj_release[]; /* global release id string */ /* first forward declare everything needed */ diff --git a/src/proj_internal.h b/src/proj_internal.h index e3e28d41..8ff38411 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -282,7 +282,7 @@ typedef PJ_COORD (* PJ_OPERATOR) (PJ_COORD, PJ *); #define PJD_GRIDSHIFT 3 #define PJD_WGS84 4 /* WGS84 (or anything considered equivalent) */ -struct CoordOperation +struct PJCoordOperation { int idxInOriginalList; double minxSrc = 0.0; @@ -298,7 +298,7 @@ struct CoordOperation double accuracy = -1.0; bool isOffshore = false; - CoordOperation(int idxInOriginalListIn, + PJCoordOperation(int idxInOriginalListIn, double minxSrcIn, double minySrcIn, double maxxSrcIn, double maxySrcIn, double minxDstIn, double minyDstIn, double maxxDstIn, double maxyDstIn, PJ* pjIn, const std::string& nameIn, double accuracyIn, bool isOffshoreIn): @@ -311,9 +311,20 @@ struct CoordOperation { } - CoordOperation(const CoordOperation&) = delete; + PJCoordOperation(const PJCoordOperation&) = delete; - CoordOperation(CoordOperation&& other): + PJCoordOperation(PJ_CONTEXT* ctx, const PJCoordOperation& other): + idxInOriginalList(other.idxInOriginalList), + minxSrc(other.minxSrc), minySrc(other.minySrc), maxxSrc(other.maxxSrc), maxySrc(other.maxySrc), + minxDst(other.minxDst), minyDst(other.minyDst), maxxDst(other.maxxDst), maxyDst(other.maxyDst), + pj(proj_clone(ctx, other.pj)), + name(std::move(other.name)), + accuracy(other.accuracy), + isOffshore(other.isOffshore) + { + } + + PJCoordOperation(PJCoordOperation&& other): idxInOriginalList(other.idxInOriginalList), minxSrc(other.minxSrc), minySrc(other.minySrc), maxxSrc(other.maxxSrc), maxySrc(other.maxySrc), minxDst(other.minxDst), minyDst(other.minyDst), maxxDst(other.maxxDst), maxyDst(other.maxyDst), @@ -324,9 +335,29 @@ struct CoordOperation other.pj = nullptr; } - CoordOperation& operator=(const CoordOperation&) = delete; + PJCoordOperation& operator=(const PJCoordOperation&) = delete; + + bool operator == (const PJCoordOperation& other) const { + return idxInOriginalList == other.idxInOriginalList && + minxSrc == other.minxSrc && + minySrc == other.minySrc && + maxxSrc == other.maxxSrc && + maxySrc == other.maxySrc && + minxDst == other.minxDst && + minyDst == other.minyDst && + maxxDst == other.maxxDst && + maxyDst == other.maxyDst && + name == other.name && + proj_is_equivalent_to(pj, other.pj, PJ_COMP_STRICT) && + accuracy == other.accuracy && + isOffshore == other.isOffshore; + } + + bool operator != (const PJCoordOperation& other) const { + return !(operator==(other)); + } - ~CoordOperation() + ~PJCoordOperation() { proj_destroy(pj); } @@ -545,7 +576,7 @@ struct PJconsts { /************************************************************************************* proj_create_crs_to_crs() alternative coordinate operations **************************************************************************************/ - std::vector<CoordOperation> alternativeCoordinateOperations{}; + std::vector<PJCoordOperation> alternativeCoordinateOperations{}; int iCurCoordOp = -1; /************************************************************************************* @@ -820,13 +851,13 @@ std::string PROJ_DLL pj_context_get_grid_cache_filename(PJ_CONTEXT *ctx); void PROJ_DLL pj_context_set_user_writable_directory(PJ_CONTEXT* ctx, const std::string& path); std::string PROJ_DLL pj_get_relative_share_proj(PJ_CONTEXT *ctx); -std::vector<CoordOperation> pj_create_prepared_operations(PJ_CONTEXT *ctx, +std::vector<PJCoordOperation> pj_create_prepared_operations(PJ_CONTEXT *ctx, const PJ *source_crs, const PJ *target_crs, PJ_OBJ_LIST* op_list); int pj_get_suggested_operation(PJ_CONTEXT *ctx, - const std::vector<CoordOperation>& opList, + const std::vector<PJCoordOperation>& opList, const int iExcluded[2], PJ_DIRECTION direction, PJ_COORD coord); diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index b9efd926..1a080ac5 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -179,3 +179,13 @@ target_link_libraries(test_tinshift add_test(NAME test_tinshift COMMAND test_tinshift) set_property(TEST test_tinshift PROPERTY ENVIRONMENT ${PROJ_TEST_ENVIRONMENT}) + +add_executable(test_misc + main.cpp + test_misc.cpp) +target_link_libraries(test_misc + PRIVATE GTest::gtest + PRIVATE ${PROJ_LIBRARIES}) +add_test(NAME test_misc COMMAND test_misc) +set_property(TEST test_misc + PROPERTY ENVIRONMENT ${PROJ_TEST_ENVIRONMENT}) diff --git a/test/unit/Makefile.am b/test/unit/Makefile.am index b073fcf2..1326332b 100644 --- a/test/unit/Makefile.am +++ b/test/unit/Makefile.am @@ -19,7 +19,7 @@ noinst_PROGRAMS += include_proj_h_from_c noinst_PROGRAMS += test_network noinst_PROGRAMS += test_defmodel noinst_PROGRAMS += test_tinshift - +noinst_PROGRAMS += test_misc pj_phi2_test_SOURCES = pj_phi2_test.cpp main.cpp pj_phi2_test_LDADD = ../../src/libproj.la @GTEST_LIBS@ @@ -89,6 +89,13 @@ test_tinshift_LDADD = ../../src/libproj.la @GTEST_LIBS@ test_tinshift-check: test_tinshift PROJ_LIB=$(PROJ_LIB) ./test_tinshift +test_misc_SOURCES = test_misc.cpp main.cpp +test_misc_LDADD = ../../src/libproj.la @GTEST_LIBS@ + +test_misc-check: test_misc + PROJ_LIB=$(PROJ_LIB) ./test_misc + check-local: pj_phi2_test-check proj_errno_string_test-check \ proj_angular_io_test-check proj_context_test-check test_cpp_api-check \ - gie_self_tests-check test_network-check test_defmodel-check test_tinshift-check + gie_self_tests-check test_network-check test_defmodel-check test_tinshift-check \ + test_misc-check diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index 0bdb9287..b624e063 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -127,6 +127,10 @@ class CApi : public ::testing::Test { PJ *m_obj = nullptr; explicit ObjectKeeper(PJ *obj) : m_obj(obj) {} ~ObjectKeeper() { proj_destroy(m_obj); } + void clear() { + proj_destroy(m_obj); + m_obj = nullptr; + } ObjectKeeper(const ObjectKeeper &) = delete; ObjectKeeper &operator=(const ObjectKeeper &) = delete; @@ -2776,6 +2780,40 @@ TEST_F(CApi, proj_clone) { // --------------------------------------------------------------------------- +TEST_F(CApi, proj_clone_of_obj_with_alternative_operations) { + // NAD27 to NAD83 + auto obj = + proj_create_crs_to_crs(m_ctxt, "EPSG:4267", "EPSG:4269", nullptr); + ObjectKeeper keeper(obj); + ASSERT_NE(obj, nullptr); + + PJ_COORD c; + c.xyzt.x = 40.5; + c.xyzt.y = -60; + c.xyzt.z = 0; + c.xyzt.t = 2021; + PJ_COORD c_trans_ref = proj_trans(obj, PJ_FWD, c); + EXPECT_NE(c_trans_ref.xyzt.x, c.xyzt.x); + EXPECT_NEAR(c_trans_ref.xyzt.x, c.xyzt.x, 1e-3); + EXPECT_NEAR(c_trans_ref.xyzt.y, c.xyzt.y, 1e-3); + + auto clone = proj_clone(m_ctxt, obj); + ObjectKeeper keeperClone(clone); + ASSERT_NE(clone, nullptr); + + EXPECT_TRUE(proj_is_equivalent_to(obj, clone, PJ_COMP_STRICT)); + + keeper.clear(); + obj = nullptr; + (void)obj; + + PJ_COORD c_trans = proj_trans(clone, PJ_FWD, c); + EXPECT_EQ(c_trans.xyzt.x, c_trans_ref.xyzt.x); + EXPECT_EQ(c_trans.xyzt.y, c_trans_ref.xyzt.y); +} + +// --------------------------------------------------------------------------- + TEST_F(CApi, proj_crs_alter_geodetic_crs) { auto projCRS = proj_create_from_wkt( m_ctxt, diff --git a/test/unit/test_misc.cpp b/test/unit/test_misc.cpp new file mode 100644 index 00000000..aaf13629 --- /dev/null +++ b/test/unit/test_misc.cpp @@ -0,0 +1,48 @@ +/****************************************************************************** + * + * Project: PROJ + * Purpose: Test + * Author: Even Rouault <even dot rouault at spatialys dot com> + * + ****************************************************************************** + * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys dot com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gtest_include.h" + +#include "proj.h" + +namespace { + +TEST(misc, version) { + EXPECT_EQ(PROJ_COMPUTE_VERSION(2200, 98, 76), 22009876); + EXPECT_EQ(PROJ_VERSION_NUMBER, PROJ_VERSION_MAJOR * 10000 + + PROJ_VERSION_MINOR * 100 + + PROJ_VERSION_PATCH); + EXPECT_TRUE(PROJ_AT_LEAST_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, + PROJ_VERSION_PATCH)); + EXPECT_TRUE(PROJ_AT_LEAST_VERSION(PROJ_VERSION_MAJOR - 1, + PROJ_VERSION_MINOR, PROJ_VERSION_PATCH)); + EXPECT_FALSE(PROJ_AT_LEAST_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, + PROJ_VERSION_PATCH + 1)); +} + +} // namespace |
