LCOV - code coverage report
Current view: top level - ogr - ogr_proj_p.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 129 150 86.0 %
Date: 2024-11-21 22:18:42 Functions: 25 29 86.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  PROJ-related functionality
       5             :  * Author:   Even Rouault <even dot rouault at spatialys dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_error.h"
      14             : #include "cpl_multiproc.h"
      15             : #include "cpl_string.h"
      16             : 
      17             : #include "ogr_proj_p.h"
      18             : #include "ogr_srs_api.h"
      19             : 
      20             : #include "proj.h"
      21             : 
      22             : #ifndef _WIN32
      23             : #include <sys/types.h>
      24             : #include <unistd.h>
      25             : #if defined(HAVE_PTHREAD_ATFORK)
      26             : #include <pthread.h>
      27             : #endif
      28             : #endif
      29             : 
      30             : #include <mutex>
      31             : #include <vector>
      32             : 
      33             : /*! @cond Doxygen_Suppress */
      34             : 
      35         134 : static void osr_proj_logger(void * /* user_data */, int level,
      36             :                             const char *message)
      37             : {
      38         134 :     if (level == PJ_LOG_ERROR)
      39             :     {
      40         134 :         CPLError(CE_Failure, CPLE_AppDefined, "PROJ: %s", message);
      41             :     }
      42           0 :     else if (level == PJ_LOG_DEBUG)
      43             :     {
      44           0 :         CPLDebug("PROJ", "%s", message);
      45             :     }
      46           0 :     else if (level == PJ_LOG_TRACE)
      47             :     {
      48           0 :         CPLDebug("PROJ_TRACE", "%s", message);
      49             :     }
      50         134 : }
      51             : 
      52             : static unsigned g_searchPathGenerationCounter = 0;
      53             : static unsigned g_auxDbPathsGenerationCounter = 0;
      54             : static std::mutex g_oSearchPathMutex;
      55             : static CPLStringList g_aosSearchpaths;
      56             : static CPLStringList g_aosAuxDbPaths;
      57             : #if PROJ_VERSION_MAJOR >= 7
      58             : static int g_projNetworkEnabled = -1;
      59             : static unsigned g_projNetworkEnabledGenerationCounter = 0;
      60             : #endif
      61             : 
      62             : #if !defined(_WIN32) && defined(HAVE_PTHREAD_ATFORK)
      63             : static bool g_bForkOccurred = false;
      64             : 
      65           0 : static void ForkOccurred(void)
      66             : {
      67           0 :     g_bForkOccurred = true;
      68           0 : }
      69             : #endif
      70             : 
      71             : struct OSRPJContextHolder
      72             : {
      73             :     unsigned searchPathGenerationCounter = 0;
      74             :     unsigned auxDbPathsGenerationCounter = 0;
      75             : #if PROJ_VERSION_MAJOR >= 7
      76             :     unsigned projNetworkEnabledGenerationCounter = 0;
      77             : #endif
      78             :     PJ_CONTEXT *context = nullptr;
      79             :     OSRProjTLSCache oCache;
      80             : #if !defined(_WIN32)
      81             : #if !defined(HAVE_PTHREAD_ATFORK)
      82             :     pid_t curpid = 0;
      83             : #endif
      84             : #endif
      85             : 
      86             : #if !defined(_WIN32)
      87        1296 :     OSRPJContextHolder()
      88        1296 :         : oCache(init())
      89             : #if !defined(HAVE_PTHREAD_ATFORK)
      90             :           ,
      91             :           curpid(getpid())
      92             : #endif
      93             :     {
      94             : #if HAVE_PTHREAD_ATFORK
      95             :         static std::once_flag flag;
      96        1295 :         std::call_once(
      97             :             flag,
      98        1159 :             []()
      99             :             {
     100        1159 :                 if (pthread_atfork(nullptr, nullptr, ForkOccurred) != 0)
     101             :                 {
     102           0 :                     CPLError(CE_Failure, CPLE_OutOfMemory,
     103             :                              "pthread_atfork() in ogr_proj_p failed");
     104             :                 }
     105        1159 :             });
     106             : #endif
     107        1296 :         init();
     108        1295 :     }
     109             : #else
     110             :     OSRPJContextHolder() : oCache(init())
     111             :     {
     112             :     }
     113             : #endif
     114             : 
     115             :     ~OSRPJContextHolder();
     116             : 
     117             :     PJ_CONTEXT *init();
     118             :     void deinit();
     119             : 
     120             :   private:
     121             :     OSRPJContextHolder(const OSRPJContextHolder &) = delete;
     122             :     OSRPJContextHolder &operator=(const OSRPJContextHolder &) = delete;
     123             : };
     124             : 
     125       60024 : static void OSRSetConfigOption(const char *pszKey, const char *pszValue,
     126             :                                bool bThreadLocal, void *)
     127             : {
     128       60024 :     if (!bThreadLocal && pszValue &&
     129        2324 :         (EQUAL(pszKey, "PROJ_LIB") || EQUAL(pszKey, "PROJ_DATA")))
     130             :     {
     131           2 :         const char *const apszSearchPaths[] = {pszValue, nullptr};
     132           2 :         OSRSetPROJSearchPaths(apszSearchPaths);
     133             :     }
     134       60024 : }
     135             : 
     136        1181 : static void OSRInstallSetConfigOptionCallback()
     137             : {
     138             :     static std::once_flag flag;
     139        1181 :     std::call_once(
     140             :         flag,
     141        1160 :         []() { CPLSubscribeToSetConfigOption(OSRSetConfigOption, nullptr); });
     142        1181 : }
     143             : 
     144     1023630 : PJ_CONTEXT *OSRPJContextHolder::init()
     145             : {
     146     1023630 :     if (!context)
     147             :     {
     148             :         static std::once_flag flag;
     149        1358 :         std::call_once(
     150             :             flag,
     151        1159 :             []()
     152             :             {
     153             :                 // Initialize g_aosSearchpaths from PROJ_DATA/PROJ_LIB configuration
     154             :                 // option.
     155        2318 :                 std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
     156        1159 :                 if (g_searchPathGenerationCounter == 0)
     157             :                 {
     158             :                     const char *pszProjData =
     159        1145 :                         CPLGetConfigOption("PROJ_DATA", nullptr);
     160        1145 :                     if (pszProjData == nullptr)
     161        1142 :                         pszProjData = CPLGetConfigOption("PROJ_LIB", nullptr);
     162        1145 :                     if (pszProjData)
     163             :                     {
     164           3 :                         const char *pszSep =
     165             : #ifdef _WIN32
     166             :                             ";"
     167             : #else
     168             :                             ":"
     169             : #endif
     170             :                             ;
     171             :                         g_aosSearchpaths =
     172           3 :                             CSLTokenizeString2(pszProjData, pszSep, 0);
     173           3 :                         g_searchPathGenerationCounter = 1;
     174             :                     }
     175             :                 }
     176             : 
     177        1159 :                 OSRInstallSetConfigOptionCallback();
     178        1159 :             });
     179             : 
     180        1358 :         context = proj_context_create();
     181        1358 :         proj_log_func(context, nullptr, osr_proj_logger);
     182             :     }
     183     1023620 :     return context;
     184             : }
     185             : 
     186        1276 : OSRPJContextHolder::~OSRPJContextHolder()
     187             : {
     188        1276 :     deinit();
     189        1276 : }
     190             : 
     191        2214 : void OSRPJContextHolder::deinit()
     192             : {
     193        2214 :     searchPathGenerationCounter = 0;
     194        2214 :     oCache.clear();
     195             : 
     196             :     // Destroy context in last
     197        2214 :     proj_context_destroy(context);
     198        2214 :     context = nullptr;
     199        2214 : }
     200             : 
     201             : #ifdef _WIN32
     202             : // Currently thread_local and C++ objects don't work well with DLL on Windows
     203             : static void FreeProjTLSContextHolder(void *pData)
     204             : {
     205             :     delete static_cast<OSRPJContextHolder *>(pData);
     206             : }
     207             : 
     208             : static OSRPJContextHolder &GetProjTLSContextHolder()
     209             : {
     210             :     static OSRPJContextHolder dummy;
     211             :     int bMemoryErrorOccurred = false;
     212             :     void *pData = CPLGetTLSEx(CTLS_PROJCONTEXTHOLDER, &bMemoryErrorOccurred);
     213             :     if (bMemoryErrorOccurred)
     214             :     {
     215             :         return dummy;
     216             :     }
     217             :     if (pData == nullptr)
     218             :     {
     219             :         auto pHolder = new OSRPJContextHolder();
     220             :         CPLSetTLSWithFreeFuncEx(CTLS_PROJCONTEXTHOLDER, pHolder,
     221             :                                 FreeProjTLSContextHolder,
     222             :                                 &bMemoryErrorOccurred);
     223             :         if (bMemoryErrorOccurred)
     224             :         {
     225             :             delete pHolder;
     226             :             return dummy;
     227             :         }
     228             :         return *pHolder;
     229             :     }
     230             :     return *static_cast<OSRPJContextHolder *>(pData);
     231             : }
     232             : #else
     233             : static thread_local OSRPJContextHolder g_tls_projContext;
     234             : 
     235     1073990 : static OSRPJContextHolder &GetProjTLSContextHolder()
     236             : {
     237     1073990 :     OSRPJContextHolder &l_projContext = g_tls_projContext;
     238             : 
     239             :     // Detect if we are now running in a child process created by fork()
     240             :     // In that situation we must make sure *not* to use the same underlying
     241             :     // file open descriptor to the sqlite3 database, since seeks&reads in one
     242             :     // of the parent or child will affect the other end.
     243             : #if defined(HAVE_PTHREAD_ATFORK)
     244     1073980 :     if (g_bForkOccurred)
     245             : #else
     246             :     const pid_t curpid = getpid();
     247             :     if (curpid != l_projContext.curpid)
     248             : #endif
     249             :     {
     250             : #if defined(HAVE_PTHREAD_ATFORK)
     251           0 :         g_bForkOccurred = false;
     252             : #else
     253             :         l_projContext.curpid = curpid;
     254             : #endif
     255           0 :         const auto osr_proj_logger_none = [](void *, int, const char *) {};
     256           0 :         proj_log_func(l_projContext.context, nullptr, osr_proj_logger_none);
     257           0 :         proj_context_set_autoclose_database(l_projContext.context, true);
     258             :         // dummy call to cause the database to be closed
     259           0 :         proj_context_get_database_path(l_projContext.context);
     260           0 :         proj_context_set_autoclose_database(l_projContext.context, false);
     261           0 :         proj_log_func(l_projContext.context, nullptr, osr_proj_logger);
     262             :     }
     263             : 
     264     1073990 :     return l_projContext;
     265             : }
     266             : #endif
     267             : 
     268     1020960 : PJ_CONTEXT *OSRGetProjTLSContext()
     269             : {
     270     1020960 :     auto &l_projContext = GetProjTLSContextHolder();
     271             :     // This .init() must be kept, even if OSRPJContextHolder constructor
     272             :     // calls it. The reason is that OSRCleanupTLSContext() calls deinit(),
     273             :     // so if reusing the object, we must re-init again.
     274     1021030 :     l_projContext.init();
     275             :     {
     276             :         // If OSRSetPROJSearchPaths() has been called since we created the
     277             :         // context, set the new search paths on the context.
     278     2042080 :         std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
     279     1021050 :         if (l_projContext.searchPathGenerationCounter !=
     280             :             g_searchPathGenerationCounter)
     281             :         {
     282         172 :             l_projContext.searchPathGenerationCounter =
     283             :                 g_searchPathGenerationCounter;
     284         172 :             proj_context_set_search_paths(l_projContext.context,
     285             :                                           g_aosSearchpaths.Count(),
     286         172 :                                           g_aosSearchpaths.List());
     287             :         }
     288     1021050 :         if (l_projContext.auxDbPathsGenerationCounter !=
     289             :             g_auxDbPathsGenerationCounter)
     290             :         {
     291           2 :             l_projContext.auxDbPathsGenerationCounter =
     292             :                 g_auxDbPathsGenerationCounter;
     293             :             std::string oMainPath(
     294           4 :                 proj_context_get_database_path(l_projContext.context));
     295           2 :             proj_context_set_database_path(l_projContext.context,
     296             :                                            oMainPath.c_str(),
     297           2 :                                            g_aosAuxDbPaths.List(), nullptr);
     298             :         }
     299             : #if PROJ_VERSION_MAJOR >= 7
     300             :         if (l_projContext.projNetworkEnabledGenerationCounter !=
     301             :             g_projNetworkEnabledGenerationCounter)
     302             :         {
     303             :             l_projContext.projNetworkEnabledGenerationCounter =
     304             :                 g_projNetworkEnabledGenerationCounter;
     305             :             proj_context_set_enable_network(l_projContext.context,
     306             :                                             g_projNetworkEnabled);
     307             :         }
     308             : #endif
     309             :     }
     310     1021050 :     return l_projContext.context;
     311             : }
     312             : 
     313             : /************************************************************************/
     314             : /*                         OSRGetProjTLSCache()                         */
     315             : /************************************************************************/
     316             : 
     317       52016 : OSRProjTLSCache *OSRGetProjTLSCache()
     318             : {
     319       52016 :     auto &l_projContext = GetProjTLSContextHolder();
     320       52016 :     return &l_projContext.oCache;
     321             : }
     322             : 
     323        2214 : void OSRProjTLSCache::clear()
     324             : {
     325        2214 :     m_oCacheEPSG.clear();
     326        2214 :     m_oCacheWKT.clear();
     327        2214 :     m_tlsContext = nullptr;
     328        2214 : }
     329             : 
     330       51171 : PJ_CONTEXT *OSRProjTLSCache::GetPJContext()
     331             : {
     332       51171 :     if (m_tlsContext == nullptr)
     333           5 :         m_tlsContext = OSRGetProjTLSContext();
     334       51171 :     return m_tlsContext;
     335             : }
     336             : 
     337       34380 : PJ *OSRProjTLSCache::GetPJForEPSGCode(int nCode, bool bUseNonDeprecated,
     338             :                                       bool bAddTOWGS84)
     339             : {
     340       34380 :     const EPSGCacheKey key(nCode, bUseNonDeprecated, bAddTOWGS84);
     341       34379 :     auto cached = m_oCacheEPSG.getPtr(key);
     342       34380 :     if (cached)
     343             :     {
     344       26442 :         return proj_clone(GetPJContext(), cached->get());
     345             :     }
     346        7938 :     return nullptr;
     347             : }
     348             : 
     349        7917 : void OSRProjTLSCache::CachePJForEPSGCode(int nCode, bool bUseNonDeprecated,
     350             :                                          bool bAddTOWGS84, PJ *pj)
     351             : {
     352        7917 :     const EPSGCacheKey key(nCode, bUseNonDeprecated, bAddTOWGS84);
     353        7917 :     m_oCacheEPSG.insert(key, UniquePtrPJ(proj_clone(GetPJContext(), pj)));
     354        7917 : }
     355             : 
     356       17099 : PJ *OSRProjTLSCache::GetPJForWKT(const std::string &wkt)
     357             : {
     358       17099 :     auto cached = m_oCacheWKT.getPtr(wkt);
     359       17099 :     if (cached)
     360             :     {
     361       15403 :         return proj_clone(GetPJContext(), cached->get());
     362             :     }
     363        1696 :     return nullptr;
     364             : }
     365             : 
     366        1410 : void OSRProjTLSCache::CachePJForWKT(const std::string &wkt, PJ *pj)
     367             : {
     368        1410 :     m_oCacheWKT.insert(wkt, UniquePtrPJ(proj_clone(GetPJContext(), pj)));
     369        1410 : }
     370             : 
     371             : /************************************************************************/
     372             : /*                         OSRCleanupTLSContext()                       */
     373             : /************************************************************************/
     374             : 
     375         938 : void OSRCleanupTLSContext()
     376             : {
     377         938 :     GetProjTLSContextHolder().deinit();
     378         938 : }
     379             : 
     380             : /*! @endcond */
     381             : 
     382             : /************************************************************************/
     383             : /*                        OSRSetPROJSearchPaths()                       */
     384             : /************************************************************************/
     385             : 
     386             : /** \brief Set the search path(s) for PROJ resource files.
     387             :  *
     388             :  * Note: starting with GDAL 3.7, CPLSetConfigOption("PROJ_DATA", ...) can
     389             :  * also been used for the same effect.
     390             :  *
     391             :  * @param papszPaths NULL terminated list of directory paths.
     392             :  * @since GDAL 3.0
     393             :  */
     394          22 : void OSRSetPROJSearchPaths(const char *const *papszPaths)
     395             : {
     396          44 :     std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
     397          22 :     g_searchPathGenerationCounter++;
     398          22 :     g_aosSearchpaths.Assign(CSLDuplicate(papszPaths), true);
     399          22 :     OSRInstallSetConfigOptionCallback();
     400          22 : }
     401             : 
     402             : /************************************************************************/
     403             : /*                        OSRGetPROJSearchPaths()                       */
     404             : /************************************************************************/
     405             : 
     406             : /** \brief Get the search path(s) for PROJ resource files.
     407             :  *
     408             :  * @return NULL terminated list of directory paths. To be freed with
     409             :  * CSLDestroy()
     410             :  * @since GDAL 3.0.3
     411             :  */
     412          24 : char **OSRGetPROJSearchPaths()
     413             : {
     414          48 :     std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
     415          24 :     if (g_searchPathGenerationCounter > 0 && !g_aosSearchpaths.empty())
     416             :     {
     417           8 :         return CSLDuplicate(g_aosSearchpaths.List());
     418             :     }
     419             : 
     420          16 :     const char *pszSep =
     421             : #ifdef _WIN32
     422             :         ";"
     423             : #else
     424             :         ":"
     425             : #endif
     426             :         ;
     427          16 :     return CSLTokenizeString2(proj_info().searchpath, pszSep, 0);
     428             : }
     429             : 
     430             : /************************************************************************/
     431             : /*                        OSRSetPROJAuxDbPaths()                        */
     432             : /************************************************************************/
     433             : 
     434             : /** \brief Set list of PROJ auxiliary database filenames.
     435             :  *
     436             :  * @param papszAux NULL-terminated list of auxiliary database filenames, or NULL
     437             :  * @since GDAL 3.3
     438             :  *
     439             :  * @see OSRGetPROJAuxDbPaths, proj_context_set_database_path
     440             :  */
     441           2 : void OSRSetPROJAuxDbPaths(const char *const *papszAux)
     442             : {
     443           4 :     std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
     444           2 :     g_auxDbPathsGenerationCounter++;
     445           2 :     g_aosAuxDbPaths.Assign(CSLDuplicate(papszAux), true);
     446           2 : }
     447             : 
     448             : /************************************************************************/
     449             : /*                        OSRGetPROJAuxDbPaths()                        */
     450             : /************************************************************************/
     451             : 
     452             : /** \brief Get PROJ auxiliary database filenames.
     453             :  *
     454             :  * @return NULL terminated list of PROJ auxiliary database filenames. To be
     455             :  * freed with CSLDestroy()
     456             :  * @since GDAL 3.3.0
     457             :  *
     458             :  * @see OSRSetPROJAuxDbPaths, proj_context_set_database_path
     459             :  */
     460           1 : char **OSRGetPROJAuxDbPaths(void)
     461             : {
     462           2 :     std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
     463             :     // Unfortunately, there is no getter for auxiliary database list at PROJ.
     464             :     // So, return our copy for now.
     465           2 :     return CSLDuplicate(g_aosAuxDbPaths.List());
     466             : }
     467             : 
     468             : /************************************************************************/
     469             : /*                       OSRSetPROJEnableNetwork()                      */
     470             : /************************************************************************/
     471             : 
     472             : /** \brief Enable or disable PROJ networking capabilities.
     473             :  *
     474             :  * @param enabled Set to TRUE to enable networking capabilities.
     475             :  * @since GDAL 3.4 and PROJ 7
     476             :  *
     477             :  * @see OSRGetPROJEnableNetwork, proj_context_set_enable_network
     478             :  */
     479           0 : void OSRSetPROJEnableNetwork(int enabled)
     480             : {
     481             : #if PROJ_VERSION_MAJOR >= 7
     482             :     std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
     483             :     if (g_projNetworkEnabled != enabled)
     484             :     {
     485             :         g_projNetworkEnabled = enabled;
     486             :         g_projNetworkEnabledGenerationCounter++;
     487             :     }
     488             : #else
     489           0 :     if (enabled)
     490             :     {
     491           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     492             :                  "OSRSetPROJEnableNetwork() requires PROJ >= 7");
     493             :     }
     494             : #endif
     495           0 : }
     496             : 
     497             : /************************************************************************/
     498             : /*                        OSRGetPROJEnableNetwork()                     */
     499             : /************************************************************************/
     500             : 
     501             : /** \brief Get whether PROJ networking capabilities are enabled.
     502             :  *
     503             :  * @return TRUE if PROJ networking capabilities are enabled.
     504             :  * @since GDAL 3.4 and PROJ 7
     505             :  *
     506             :  * @see OSRSetPROJEnableNetwork, proj_context_is_network_enabled
     507             :  */
     508           0 : int OSRGetPROJEnableNetwork(void)
     509             : {
     510             : #if PROJ_VERSION_MAJOR >= 7
     511             :     std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
     512             :     if (g_projNetworkEnabled < 0)
     513             :     {
     514             :         g_oSearchPathMutex.unlock();
     515             :         const int ret = proj_context_is_network_enabled(OSRGetProjTLSContext());
     516             :         g_oSearchPathMutex.lock();
     517             :         g_projNetworkEnabled = ret;
     518             :     }
     519             :     return g_projNetworkEnabled;
     520             : #else
     521           0 :     return FALSE;
     522             : #endif
     523             : }
     524             : 
     525             : /************************************************************************/
     526             : /*                         OSRGetPROJVersion()                          */
     527             : /************************************************************************/
     528             : 
     529             : /** \brief Get the PROJ version
     530             :  *
     531             :  * @param pnMajor Pointer to major version number, or NULL
     532             :  * @param pnMinor Pointer to minor version number, or NULL
     533             :  * @param pnPatch Pointer to patch version number, or NULL
     534             :  * @since GDAL 3.0.1
     535             :  */
     536         184 : void OSRGetPROJVersion(int *pnMajor, int *pnMinor, int *pnPatch)
     537             : {
     538         184 :     auto info = proj_info();
     539         184 :     if (pnMajor)
     540          71 :         *pnMajor = info.major;
     541         184 :     if (pnMinor)
     542          64 :         *pnMinor = info.minor;
     543         184 :     if (pnPatch)
     544          49 :         *pnPatch = info.patch;
     545         184 : }

Generated by: LCOV version 1.14