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