diff options
| -rw-r--r-- | include/proj/internal/io_internal.hpp | 44 | ||||
| -rw-r--r-- | scripts/reference_exported_symbols.txt | 1 | ||||
| -rw-r--r-- | src/4D_api.cpp | 12 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 191 | ||||
| -rw-r--r-- | src/iso19111/coordinateoperation.cpp | 2 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 13 | ||||
| -rw-r--r-- | src/proj.h | 3 | ||||
| -rw-r--r-- | test/unit/test_c_api.cpp | 191 | ||||
| -rw-r--r-- | test/unit/test_factory.cpp | 3 |
9 files changed, 378 insertions, 82 deletions
diff --git a/include/proj/internal/io_internal.hpp b/include/proj/internal/io_internal.hpp index b7f514d5..bd6d441d 100644 --- a/include/proj/internal/io_internal.hpp +++ b/include/proj/internal/io_internal.hpp @@ -166,24 +166,46 @@ NS_PROJ_END /** Auxiliary structure to PJ_CONTEXT storing C++ context stuff. */ struct projCppContext { - NS_PROJ::io::DatabaseContextNNPtr databaseContext; + private: + NS_PROJ::io::DatabaseContextPtr databaseContext_{}; + PJ_CONTEXT *ctx_ = nullptr; + std::string dbPath_{}; + std::vector<std::string> auxDbPaths_{}; + bool autoCloseDb_ = false; + + projCppContext(const projCppContext &) = delete; + projCppContext &operator=(const projCppContext &) = delete; + + public: + std::string lastDbPath_{}; + std::string lastDbMetadataItem_{}; std::string lastUOMName_{}; std::string lastGridFullName_{}; std::string lastGridPackageName_{}; std::string lastGridUrl_{}; + static std::vector<std::string> toVector(const char *const *auxDbPaths); + explicit projCppContext(PJ_CONTEXT *ctx, const char *dbPath = nullptr, - const char *const *auxDbPaths = nullptr) - : databaseContext(NS_PROJ::io::DatabaseContext::create( - dbPath ? dbPath : std::string(), toVector(auxDbPaths), ctx)) {} - - static std::vector<std::string> toVector(const char *const *auxDbPaths) { - std::vector<std::string> res; - for (auto iter = auxDbPaths; iter && *iter; ++iter) { - res.emplace_back(std::string(*iter)); - } - return res; + const std::vector<std::string> &auxDbPaths = {}); + + // cppcheck-suppress functionStatic + inline const std::string &getDbPath() const { return dbPath_; } + + // cppcheck-suppress functionStatic + inline const std::vector<std::string> &getAuxDbPaths() const { + return auxDbPaths_; } + + void setAutoCloseDb(bool autoClose) { autoCloseDb_ = autoClose; } + inline bool getAutoCloseDb() const { return autoCloseDb_; } + + // cppcheck-suppress functionStatic + void closeDb(); + + void autoCloseDbIfNeeded(); + + NS_PROJ::io::DatabaseContextNNPtr getDatabaseContext(); }; //! @endcond diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index 4910b822..fa6b16a4 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -784,6 +784,7 @@ proj_context_get_database_metadata proj_context_get_database_path proj_context_get_use_proj4_init_rules proj_context_guess_wkt_dialect +proj_context_set_autoclose_database proj_context_set_database_path proj_context_set_file_finder proj_context_set(PJconsts*, projCtx_t*) diff --git a/src/4D_api.cpp b/src/4D_api.cpp index 9f445d88..07ccfd91 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -231,13 +231,19 @@ similarly, but prefers the 2D resp. 3D interfaces if available. // with the input coordinate, then goes through again the list, and // use the first operation that does not require grids. i = 0; + NS_PROJ::io::DatabaseContextPtr dbContext; + try + { + if( P->ctx->cpp_context ) { + dbContext = P->ctx->cpp_context->getDatabaseContext().as_nullable(); + } + } + catch( const std::exception& ) {} for( const auto &alt: P->alternativeCoordinateOperations ) { auto coordOperation = dynamic_cast< NS_PROJ::operation::CoordinateOperation*>(alt.pj->iso_obj.get()); if( coordOperation ) { - if( coordOperation->gridsNeeded(P->ctx->cpp_context ? - P->ctx->cpp_context->databaseContext.as_nullable() : - nullptr).empty() ) { + if( coordOperation->gridsNeeded(dbContext).empty() ) { if( P->iCurCoordOp != i ) { std::string msg("Using coordinate operation "); msg += alt.name; diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index f40cf241..312daeab 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -105,19 +105,55 @@ static void PROJ_NO_INLINE proj_log_debug(PJ_CONTEXT *ctx, const char *function, void proj_context_delete_cpp_context(struct projCppContext *cppContext) { delete cppContext; } +// --------------------------------------------------------------------------- -//! @endcond +projCppContext::projCppContext(PJ_CONTEXT *ctx, const char *dbPath, + const std::vector<std::string> &auxDbPaths) + : ctx_(ctx), dbPath_(dbPath ? dbPath : std::string()), + auxDbPaths_(auxDbPaths) {} // --------------------------------------------------------------------------- -//! @cond Doxygen_Suppress +std::vector<std::string> +projCppContext::toVector(const char *const *auxDbPaths) { + std::vector<std::string> res; + for (auto iter = auxDbPaths; iter && *iter; ++iter) { + res.emplace_back(std::string(*iter)); + } + return res; +} + +// --------------------------------------------------------------------------- + +void projCppContext::closeDb() { databaseContext_ = nullptr; } + +// --------------------------------------------------------------------------- + +void projCppContext::autoCloseDbIfNeeded() { + if (getAutoCloseDb()) { + closeDb(); + } +} + +// --------------------------------------------------------------------------- -static PROJ_NO_INLINE const DatabaseContextNNPtr & -getDBcontext(PJ_CONTEXT *ctx) { +NS_PROJ::io::DatabaseContextNNPtr projCppContext::getDatabaseContext() { + if (databaseContext_) { + return NN_NO_CHECK(databaseContext_); + } + auto dbContext = + NS_PROJ::io::DatabaseContext::create(dbPath_, auxDbPaths_, ctx_); + databaseContext_ = dbContext; + return dbContext; +} + +// --------------------------------------------------------------------------- + +static PROJ_NO_INLINE DatabaseContextNNPtr getDBcontext(PJ_CONTEXT *ctx) { if (ctx->cpp_context == nullptr) { ctx->cpp_context = new projCppContext(ctx); } - return ctx->cpp_context->databaseContext; + return ctx->cpp_context->getDatabaseContext(); } // --------------------------------------------------------------------------- @@ -144,6 +180,9 @@ static PJ *pj_obj_create(PJ_CONTEXT *ctx, const IdentifiedObjectNNPtr &objIn) { auto pj = pj_create_internal(ctx, projString.c_str()); if (pj) { pj->iso_obj = objIn; + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return pj; } } catch (const std::exception &) { @@ -157,6 +196,9 @@ static PJ *pj_obj_create(PJ_CONTEXT *ctx, const IdentifiedObjectNNPtr &objIn) { pj->descr = "ISO-19111 object"; pj->iso_obj = objIn; } + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return pj; } //! @endcond @@ -191,6 +233,26 @@ struct PJ_OBJ_LIST { // --------------------------------------------------------------------------- +/** \brief Set if the database must be closed after each C API call where it + * has been openeded, and automatically re-openeded when needed. + * + * The default value is FALSE, that is the database remains open until the + * context is destroyed. + * + * @param ctx PROJ context, or NULL for default context + * @param autoclose Boolean parameter + * @since 6.2 + */ +void proj_context_set_autoclose_database(PJ_CONTEXT *ctx, int autoclose) { + SANITIZE_CTX(ctx); + if (ctx->cpp_context == nullptr) { + ctx->cpp_context = new projCppContext(ctx); + } + ctx->cpp_context->setAutoCloseDb(autoclose != FALSE); +} + +// --------------------------------------------------------------------------- + /** \brief Explicitly point to the main PROJ CRS and coordinate operation * definition database ("proj.db"), and potentially auxiliary databases with * same structure. @@ -207,13 +269,29 @@ int proj_context_set_database_path(PJ_CONTEXT *ctx, const char *dbPath, const char *const *options) { SANITIZE_CTX(ctx); (void)options; + std::string osPrevDbPath; + std::vector<std::string> osPrevAuxDbPaths; + bool autoCloseDb = false; + if (ctx->cpp_context) { + osPrevDbPath = ctx->cpp_context->getDbPath(); + osPrevAuxDbPaths = ctx->cpp_context->getAuxDbPaths(); + autoCloseDb = ctx->cpp_context->getAutoCloseDb(); + } delete ctx->cpp_context; ctx->cpp_context = nullptr; try { - ctx->cpp_context = new projCppContext(ctx, dbPath, auxDbPaths); + ctx->cpp_context = new projCppContext( + ctx, dbPath, projCppContext::toVector(auxDbPaths)); + ctx->cpp_context->setAutoCloseDb(autoCloseDb); + ctx->cpp_context->getDatabaseContext(); + ctx->cpp_context->autoCloseDbIfNeeded(); return true; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); + delete ctx->cpp_context; + ctx->cpp_context = + new projCppContext(ctx, osPrevDbPath.c_str(), osPrevAuxDbPaths); + ctx->cpp_context->setAutoCloseDb(autoCloseDb); return false; } } @@ -231,7 +309,12 @@ int proj_context_set_database_path(PJ_CONTEXT *ctx, const char *dbPath, const char *proj_context_get_database_path(PJ_CONTEXT *ctx) { SANITIZE_CTX(ctx); try { - return getDBcontext(ctx)->getPath().c_str(); + // temporary variable must be used as getDBcontext() might create + // ctx->cpp_context + auto osPath(getDBcontext(ctx)->getPath()); + ctx->cpp_context->lastDbPath_ = osPath; + ctx->cpp_context->autoCloseDbIfNeeded(); + return ctx->cpp_context->lastDbPath_.c_str(); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; @@ -253,7 +336,12 @@ const char *proj_context_get_database_metadata(PJ_CONTEXT *ctx, const char *key) { SANITIZE_CTX(ctx); try { - return getDBcontext(ctx)->getMetadata(key); + // temporary variable must be used as getDBcontext() might create + // ctx->cpp_context + auto osVal(getDBcontext(ctx)->getMetadata(key)); + ctx->cpp_context->lastDbMetadataItem_ = osVal; + ctx->cpp_context->autoCloseDbIfNeeded(); + return ctx->cpp_context->lastDbMetadataItem_.c_str(); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; @@ -359,6 +447,9 @@ PJ *proj_create(PJ_CONTEXT *ctx, const char *text) { } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return nullptr; } @@ -487,6 +578,9 @@ PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, proj_log_error(ctx, __FUNCTION__, e.what()); } } + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return nullptr; } @@ -545,6 +639,7 @@ PJ *proj_create_from_database(PJ_CONTEXT *ctx, const char *auth_name, } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } @@ -617,10 +712,12 @@ int proj_uom_get_info_from_database(PJ_CONTEXT *ctx, const char *auth_name, if (out_category) { *out_category = get_unit_category(obj->type()); } + ctx->cpp_context->autoCloseDbIfNeeded(); return true; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + ctx->cpp_context->autoCloseDbIfNeeded(); return false; } @@ -660,8 +757,10 @@ int PROJ_DLL proj_grid_get_info_from_database( grid_name, ctx->cpp_context->lastGridFullName_, ctx->cpp_context->lastGridPackageName_, ctx->cpp_context->lastGridUrl_, direct_download, open_license, - available)) + available)) { + ctx->cpp_context->autoCloseDbIfNeeded(); return false; + } if (out_full_name) *out_full_name = ctx->cpp_context->lastGridFullName_.c_str(); @@ -676,10 +775,12 @@ int PROJ_DLL proj_grid_get_info_from_database( if (out_available) *out_available = available ? 1 : 0; + ctx->cpp_context->autoCloseDbIfNeeded(); return true; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + ctx->cpp_context->autoCloseDbIfNeeded(); return false; } @@ -712,10 +813,12 @@ PJ_OBJ_LIST *proj_query_geodetic_crs_from_datum(PJ_CONTEXT *ctx, for (const auto &obj : res) { objects.push_back(obj); } + ctx->cpp_context->autoCloseDbIfNeeded(); return new PJ_OBJ_LIST(std::move(objects)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } @@ -872,10 +975,12 @@ PJ_OBJ_LIST *proj_create_from_name(PJ_CONTEXT *ctx, const char *auth_name, for (const auto &obj : res) { objects.push_back(obj); } + ctx->cpp_context->autoCloseDbIfNeeded(); return new PJ_OBJ_LIST(std::move(objects)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } @@ -1014,10 +1119,12 @@ PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj) { for (const auto &resObj : res) { objects.push_back(resObj); } + ctx->cpp_context->autoCloseDbIfNeeded(); return new PJ_OBJ_LIST(std::move(objects)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } @@ -1235,13 +1342,22 @@ const char *proj_as_wkt(PJ_CONTEXT *ctx, const PJ *obj, PJ_WKT_TYPE type, std::string msg("Unknown option :"); msg += *iter; proj_log_error(ctx, __FUNCTION__, msg.c_str()); + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return nullptr; } } obj->lastWKT = obj->iso_obj->exportToWKT(formatter.get()); + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return obj->lastWKT.c_str(); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return nullptr; } } @@ -1305,9 +1421,15 @@ const char *proj_as_proj_string(PJ_CONTEXT *ctx, const PJ *obj, } } obj->lastPROJString = exportable->exportToPROJString(formatter.get()); + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return obj->lastPROJString.c_str(); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return nullptr; } } @@ -1669,6 +1791,9 @@ PJ *proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, const PJ *crs, std::string msg("Unknown option :"); msg += *iter; proj_log_error(ctx, __FUNCTION__, msg.c_str()); + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return nullptr; } } @@ -1676,6 +1801,9 @@ PJ *proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, const PJ *crs, dbContext, allowIntermediateCRS)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return nullptr; } } @@ -2020,12 +2148,14 @@ PJ_OBJ_LIST *proj_identify(PJ_CONTEXT *ctx, const PJ *obj, *out_confidence = confidenceTemp; confidenceTemp = nullptr; } + ctx->cpp_context->autoCloseDbIfNeeded(); return ret.release(); } catch (const std::exception &e) { delete[] confidenceTemp; proj_log_error(ctx, __FUNCTION__, e.what()); } } + ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } @@ -2049,10 +2179,13 @@ void proj_int_list_destroy(int *list) { delete[] list; } PROJ_STRING_LIST proj_get_authorities_from_database(PJ_CONTEXT *ctx) { SANITIZE_CTX(ctx); try { - return to_string_list(getDBcontext(ctx)->getAuthorities()); + auto ret = to_string_list(getDBcontext(ctx)->getAuthorities()); + ctx->cpp_context->autoCloseDbIfNeeded(); + return ret; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } @@ -2086,12 +2219,15 @@ PROJ_STRING_LIST proj_get_codes_from_database(PJ_CONTEXT *ctx, if (!valid) { return nullptr; } - return to_string_list( + auto ret = to_string_list( factory->getAuthorityCodes(typeInternal, allow_deprecated != 0)); + ctx->cpp_context->autoCloseDbIfNeeded(); + return ret; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } @@ -2284,6 +2420,7 @@ proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name, ret[i] = nullptr; if (out_result_count) *out_result_count = i; + ctx->cpp_context->autoCloseDbIfNeeded(); return ret; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); @@ -2294,6 +2431,7 @@ proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name, if (out_result_count) *out_result_count = 0; } + ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } @@ -2569,6 +2707,9 @@ PJ *proj_create_geographic_crs(PJ_CONTEXT *ctx, const char *crs_name, } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return nullptr; } @@ -2611,6 +2752,9 @@ PJ *proj_create_geographic_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name, } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return nullptr; } @@ -5905,8 +6049,15 @@ int proj_coordoperation_is_instantiable(PJ_CONTEXT *ctx, } auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); try { - return op->isPROJInstantiable(dbContext) ? 1 : 0; + auto ret = op->isPROJInstantiable(dbContext) ? 1 : 0; + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } + return ret; } catch (const std::exception &) { + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return 0; } } @@ -6214,9 +6365,15 @@ int proj_coordoperation_get_grid_used_count(PJ_CONTEXT *ctx, coordoperation->gridsNeeded.emplace_back(gridDesc); } } + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return static_cast<int>(coordoperation->gridsNeeded.size()); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return 0; } } @@ -6344,6 +6501,7 @@ proj_create_operation_factory_context(PJ_CONTEXT *ctx, const char *authority) { std::string(authority ? authority : "")); auto operationContext = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctx->cpp_context->autoCloseDbIfNeeded(); return new PJ_OPERATION_FACTORY_CONTEXT( std::move(operationContext)); } else { @@ -6355,6 +6513,9 @@ proj_create_operation_factory_context(PJ_CONTEXT *ctx, const char *authority) { } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } + if (ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } return nullptr; } @@ -6652,18 +6813,16 @@ void proj_operation_factory_context_set_allowed_intermediate_crs( * @param discard superseded crs or not */ void PROJ_DLL proj_operation_factory_context_set_discard_superseded( - PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, - int discard) { + PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, int discard) { SANITIZE_CTX(ctx); assert(factory_ctx); - try { + try { factory_ctx->operationContext->setDiscardSuperseded(discard != 0); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } } - // --------------------------------------------------------------------------- /** \brief Find a list of CoordinateOperation from source_crs to target_crs. diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 2dd77f1c..dc700445 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -10446,7 +10446,7 @@ struct FilterResults { // ... removeSyntheticNullTransforms(); if (context->getDiscardSuperseded()) - removeUninterestingOps(); + removeUninterestingOps(); removeDuplicateOps(); removeSyntheticNullTransforms(); return *this; diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 5ccd9642..cbf5e150 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -5881,11 +5881,14 @@ BaseObjectNNPtr createFromUserInput(const std::string &text, * @throw ParsingException */ BaseObjectNNPtr createFromUserInput(const std::string &text, PJ_CONTEXT *ctx) { - return createFromUserInput( - text, ctx != nullptr && ctx->cpp_context - ? ctx->cpp_context->databaseContext.as_nullable() - : nullptr, - false, ctx); + DatabaseContextPtr dbContext; + try { + if (ctx != nullptr && ctx->cpp_context) { + dbContext = ctx->cpp_context->getDatabaseContext().as_nullable(); + } + } catch (const std::exception &) { + } + return createFromUserInput(text, dbContext, false, ctx); } // --------------------------------------------------------------------------- @@ -755,6 +755,9 @@ typedef struct PJ_OBJ_LIST PJ_OBJ_LIST; void PROJ_DLL proj_string_list_destroy(PROJ_STRING_LIST list); +void PROJ_DLL proj_context_set_autoclose_database(PJ_CONTEXT *ctx, + int autoclose); + int PROJ_DLL proj_context_set_database_path(PJ_CONTEXT *ctx, const char *dbPath, const char *const *auxDbPaths, diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index 813b9f01..74377738 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -28,6 +28,7 @@ #include "gtest_include.h" +#include <cstdio> #include <limits> #include "proj.h" @@ -43,6 +44,8 @@ #include "proj/metadata.hpp" #include "proj/util.hpp" +#include <sqlite3.h> + using namespace osgeo::proj::common; using namespace osgeo::proj::crs; using namespace osgeo::proj::cs; @@ -64,7 +67,10 @@ class CApi : public ::testing::Test { proj_log_func(m_ctxt, nullptr, DummyLogFunction); } - void TearDown() override { proj_context_destroy(m_ctxt); } + void TearDown() override { + if (m_ctxt) + proj_context_destroy(m_ctxt); + } static BoundCRSNNPtr createBoundCRS() { return BoundCRS::create( @@ -1353,68 +1359,66 @@ TEST_F(CApi, proj_create_operations) { // --------------------------------------------------------------------------- TEST_F(CApi, proj_create_operations_discard_superseded) { - auto ctxt = proj_create_operation_factory_context(m_ctxt, nullptr); - ASSERT_NE(ctxt, nullptr); - ContextKeeper keeper_ctxt(ctxt); + auto ctxt = proj_create_operation_factory_context(m_ctxt, nullptr); + ASSERT_NE(ctxt, nullptr); + ContextKeeper keeper_ctxt(ctxt); - auto source_crs = proj_create_from_database( - m_ctxt, "EPSG", "4203", PJ_CATEGORY_CRS, false, nullptr); // AGD84 - ASSERT_NE(source_crs, nullptr); - ObjectKeeper keeper_source_crs(source_crs); + auto source_crs = proj_create_from_database( + m_ctxt, "EPSG", "4203", PJ_CATEGORY_CRS, false, nullptr); // AGD84 + ASSERT_NE(source_crs, nullptr); + ObjectKeeper keeper_source_crs(source_crs); - auto target_crs = proj_create_from_database( - m_ctxt, "EPSG", "4326", PJ_CATEGORY_CRS, false, nullptr); // WGS84 - ASSERT_NE(target_crs, nullptr); - ObjectKeeper keeper_target_crs(target_crs); + auto target_crs = proj_create_from_database( + m_ctxt, "EPSG", "4326", PJ_CATEGORY_CRS, false, nullptr); // WGS84 + ASSERT_NE(target_crs, nullptr); + ObjectKeeper keeper_target_crs(target_crs); - proj_operation_factory_context_set_spatial_criterion( - m_ctxt, ctxt, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); + proj_operation_factory_context_set_spatial_criterion( + m_ctxt, ctxt, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); - proj_operation_factory_context_set_grid_availability_use( - m_ctxt, ctxt, PROJ_GRID_AVAILABILITY_IGNORED); + proj_operation_factory_context_set_grid_availability_use( + m_ctxt, ctxt, PROJ_GRID_AVAILABILITY_IGNORED); - proj_operation_factory_context_set_discard_superseded( - m_ctxt, ctxt, true); + proj_operation_factory_context_set_discard_superseded(m_ctxt, ctxt, true); - auto res = proj_create_operations(m_ctxt, source_crs, target_crs, ctxt); - ASSERT_NE(res, nullptr); - ObjListKeeper keeper_res(res); + auto res = proj_create_operations(m_ctxt, source_crs, target_crs, ctxt); + ASSERT_NE(res, nullptr); + ObjListKeeper keeper_res(res); - EXPECT_EQ(proj_list_get_count(res), 2); - } + EXPECT_EQ(proj_list_get_count(res), 2); +} // --------------------------------------------------------------------------- TEST_F(CApi, proj_create_operations_dont_discard_superseded) { - auto ctxt = proj_create_operation_factory_context(m_ctxt, nullptr); - ASSERT_NE(ctxt, nullptr); - ContextKeeper keeper_ctxt(ctxt); + auto ctxt = proj_create_operation_factory_context(m_ctxt, nullptr); + ASSERT_NE(ctxt, nullptr); + ContextKeeper keeper_ctxt(ctxt); - auto source_crs = proj_create_from_database( - m_ctxt, "EPSG", "4203", PJ_CATEGORY_CRS, false, nullptr); // AGD84 - ASSERT_NE(source_crs, nullptr); - ObjectKeeper keeper_source_crs(source_crs); + auto source_crs = proj_create_from_database( + m_ctxt, "EPSG", "4203", PJ_CATEGORY_CRS, false, nullptr); // AGD84 + ASSERT_NE(source_crs, nullptr); + ObjectKeeper keeper_source_crs(source_crs); - auto target_crs = proj_create_from_database( - m_ctxt, "EPSG", "4326", PJ_CATEGORY_CRS, false, nullptr); // WGS84 - ASSERT_NE(target_crs, nullptr); - ObjectKeeper keeper_target_crs(target_crs); + auto target_crs = proj_create_from_database( + m_ctxt, "EPSG", "4326", PJ_CATEGORY_CRS, false, nullptr); // WGS84 + ASSERT_NE(target_crs, nullptr); + ObjectKeeper keeper_target_crs(target_crs); - proj_operation_factory_context_set_spatial_criterion( - m_ctxt, ctxt, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); + proj_operation_factory_context_set_spatial_criterion( + m_ctxt, ctxt, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); - proj_operation_factory_context_set_grid_availability_use( - m_ctxt, ctxt, PROJ_GRID_AVAILABILITY_IGNORED); + proj_operation_factory_context_set_grid_availability_use( + m_ctxt, ctxt, PROJ_GRID_AVAILABILITY_IGNORED); - proj_operation_factory_context_set_discard_superseded( - m_ctxt, ctxt, false); + proj_operation_factory_context_set_discard_superseded(m_ctxt, ctxt, false); - auto res = proj_create_operations(m_ctxt, source_crs, target_crs, ctxt); - ASSERT_NE(res, nullptr); - ObjListKeeper keeper_res(res); + auto res = proj_create_operations(m_ctxt, source_crs, target_crs, ctxt); + ASSERT_NE(res, nullptr); + ObjListKeeper keeper_res(res); - EXPECT_EQ(proj_list_get_count(res), 5); - } + EXPECT_EQ(proj_list_get_count(res), 5); +} // --------------------------------------------------------------------------- @@ -3610,4 +3614,101 @@ TEST_F(CApi, proj_as_projjson) { } } +// --------------------------------------------------------------------------- + +struct Fixture_proj_context_set_autoclose_database : public CApi { + void test(bool autoclose) { + proj_context_set_autoclose_database(m_ctxt, autoclose); + + auto c_path = proj_context_get_database_path(m_ctxt); + ASSERT_TRUE(c_path != nullptr); + std::string path(c_path); + + FILE *f = fopen(path.c_str(), "rb"); + ASSERT_NE(f, nullptr); + fseek(f, 0, SEEK_END); + auto length = ftell(f); + std::string content; + content.resize(static_cast<size_t>(length)); + fseek(f, 0, SEEK_SET); + auto read_bytes = fread(&content[0], 1, content.size(), f); + ASSERT_EQ(read_bytes, content.size()); + fclose(f); + const char *tempdir = getenv("TEMP"); + if (!tempdir) { + tempdir = getenv("TMP"); + } + if (!tempdir) { + tempdir = "/tmp"; + } + std::string tmp_filename( + std::string(tempdir) + + "/test_proj_context_set_autoclose_database.db"); + f = fopen(tmp_filename.c_str(), "wb"); + if (!f) { + std::cerr << "Cannot create " << tmp_filename << std::endl; + return; + } + fwrite(content.data(), 1, content.size(), f); + fclose(f); + + { + sqlite3 *db = nullptr; + sqlite3_open_v2(tmp_filename.c_str(), &db, SQLITE_OPEN_READWRITE, + nullptr); + ASSERT_NE(db, nullptr); + ASSERT_TRUE(sqlite3_exec(db, "UPDATE geodetic_crs SET name = 'foo' " + "WHERE auth_name = 'EPSG' and code = " + "'4326'", + nullptr, nullptr, nullptr) == SQLITE_OK); + sqlite3_close(db); + } + + EXPECT_TRUE(proj_context_set_database_path(m_ctxt, tmp_filename.c_str(), + nullptr, nullptr)); + { + auto crs = proj_create_from_database( + m_ctxt, "EPSG", "4326", PJ_CATEGORY_CRS, false, nullptr); + ObjectKeeper keeper(crs); + ASSERT_NE(crs, nullptr); + EXPECT_EQ(proj_get_name(crs), std::string("foo")); + } + + { + sqlite3 *db = nullptr; + sqlite3_open_v2(tmp_filename.c_str(), &db, SQLITE_OPEN_READWRITE, + nullptr); + ASSERT_NE(db, nullptr); + ASSERT_TRUE(sqlite3_exec(db, "UPDATE geodetic_crs SET name = 'bar' " + "WHERE auth_name = 'EPSG' and code = " + "'4326'", + nullptr, nullptr, nullptr) == SQLITE_OK); + sqlite3_close(db); + } + { + auto crs = proj_create_from_database( + m_ctxt, "EPSG", "4326", PJ_CATEGORY_CRS, false, nullptr); + ObjectKeeper keeper(crs); + ASSERT_NE(crs, nullptr); + EXPECT_EQ(proj_get_name(crs), + std::string(autoclose ? "bar" : "foo")); + } + + if (!autoclose) { + proj_context_destroy(m_ctxt); + m_ctxt = nullptr; + } + std::remove(tmp_filename.c_str()); + } +}; + +TEST_F(Fixture_proj_context_set_autoclose_database, + proj_context_set_autoclose_database_true) { + test(true); +} + +TEST_F(Fixture_proj_context_set_autoclose_database, + proj_context_set_autoclose_database_false) { + test(false); +} } // namespace diff --git a/test/unit/test_factory.cpp b/test/unit/test_factory.cpp index e23d1421..e68b42fc 100644 --- a/test/unit/test_factory.cpp +++ b/test/unit/test_factory.cpp @@ -2695,7 +2695,8 @@ TEST(factory, createObjectsFromName) { { auto res = factoryEPSG->createObjectsFromName( "WGS84", {AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS}, true); - EXPECT_EQ(res.size(), 8U); // EPSG:4326 and EPSG:4030 and the 6 WGS84 realizations + EXPECT_EQ(res.size(), + 8U); // EPSG:4326 and EPSG:4030 and the 6 WGS84 realizations if (!res.empty()) { EXPECT_EQ(res.front()->getEPSGCode(), 4326); } |
