• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 8872387746

29 Apr 2024 02:16AM UTC coverage: 69.076% (+0.003%) from 69.073%
8872387746

Pull #9801

github

web-flow
Bump actions/upload-artifact from 4.3.2 to 4.3.3

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.2 to 4.3.3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/1746f4ab6...65462800f)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #9801: Bump actions/upload-artifact from 4.3.2 to 4.3.3

534153 of 773282 relevant lines covered (69.08%)

205719.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.0
/ogr/ogr_proj_p.cpp
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
static void osr_proj_logger(void * /* user_data */, int level,
128✔
52
                            const char *message)
53
{
54
    if (level == PJ_LOG_ERROR)
128✔
55
    {
56
        CPLError(CE_Failure, CPLE_AppDefined, "PROJ: %s", message);
128✔
57
    }
58
    else if (level == PJ_LOG_DEBUG)
×
59
    {
60
        CPLDebug("PROJ", "%s", message);
×
61
    }
62
    else if (level == PJ_LOG_TRACE)
×
63
    {
64
        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
static void ForkOccurred(void)
×
82
{
83
    g_bForkOccurred = true;
×
84
}
×
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
    OSRPJContextHolder()
1,167✔
104
        : oCache(init())
1,167✔
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
        std::call_once(
1,167✔
113
            flag,
114
            []()
1,086✔
115
            {
116
                if (pthread_atfork(nullptr, nullptr, ForkOccurred) != 0)
1,086✔
117
                {
118
                    CPLError(CE_Failure, CPLE_OutOfMemory,
×
119
                             "pthread_atfork() in ogr_proj_p failed");
120
                }
121
            });
1,086✔
122
#endif
123
        init();
1,167✔
124
    }
1,167✔
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
static void OSRSetConfigOption(const char *pszKey, const char *pszValue,
55,625✔
142
                               bool bThreadLocal, void *)
143
{
144
    if (!bThreadLocal && pszValue &&
55,625✔
145
        (EQUAL(pszKey, "PROJ_LIB") || EQUAL(pszKey, "PROJ_DATA")))
1,926✔
146
    {
147
        const char *const apszSearchPaths[] = {pszValue, nullptr};
2✔
148
        OSRSetPROJSearchPaths(apszSearchPaths);
2✔
149
    }
150
}
55,625✔
151

152
static void OSRInstallSetConfigOptionCallback()
1,108✔
153
{
154
    static std::once_flag flag;
155
    std::call_once(
1,108✔
156
        flag,
157
        []() { CPLSubscribeToSetConfigOption(OSRSetConfigOption, nullptr); });
1,087✔
158
}
1,108✔
159

160
PJ_CONTEXT *OSRPJContextHolder::init()
2,894,500✔
161
{
162
    if (!context)
2,894,500✔
163
    {
164
        static std::once_flag flag;
165
        std::call_once(
1,191✔
166
            flag,
167
            []()
1,086✔
168
            {
169
                // Initialize g_aosSearchpaths from PROJ_DATA/PROJ_LIB configuration
170
                // option.
171
                std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
2,172✔
172
                if (g_searchPathGenerationCounter == 0)
1,086✔
173
                {
174
                    const char *pszProjData =
175
                        CPLGetConfigOption("PROJ_DATA", nullptr);
1,072✔
176
                    if (pszProjData == nullptr)
1,072✔
177
                        pszProjData = CPLGetConfigOption("PROJ_LIB", nullptr);
1,069✔
178
                    if (pszProjData)
1,072✔
179
                    {
180
                        const char *pszSep =
3✔
181
#ifdef _WIN32
182
                            ";"
183
#else
184
                            ":"
185
#endif
186
                            ;
187
                        g_aosSearchpaths =
188
                            CSLTokenizeString2(pszProjData, pszSep, 0);
3✔
189
                        g_searchPathGenerationCounter = 1;
3✔
190
                    }
191
                }
192

193
                OSRInstallSetConfigOptionCallback();
1,086✔
194
            });
1,086✔
195

196
        context = proj_context_create();
1,191✔
197
        proj_log_func(context, nullptr, osr_proj_logger);
1,191✔
198
    }
199
    return context;
2,894,500✔
200
}
201

202
OSRPJContextHolder::~OSRPJContextHolder()
1,158✔
203
{
204
    deinit();
1,158✔
205
}
1,158✔
206

207
void OSRPJContextHolder::deinit()
2,015✔
208
{
209
    searchPathGenerationCounter = 0;
2,015✔
210
    oCache.clear();
2,015✔
211

212
    // Destroy context in last
213
    proj_context_destroy(context);
2,015✔
214
    context = nullptr;
2,015✔
215
}
2,015✔
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
static OSRPJContextHolder &GetProjTLSContextHolder()
2,950,260✔
252
{
253
    OSRPJContextHolder &l_projContext = g_tls_projContext;
2,950,260✔
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
    if (g_bForkOccurred)
2,950,250✔
261
#else
262
    const pid_t curpid = getpid();
263
    if (curpid != l_projContext.curpid)
264
#endif
265
    {
266
#if defined(HAVE_PTHREAD_ATFORK)
267
        g_bForkOccurred = false;
×
268
#else
269
        l_projContext.curpid = curpid;
270
#endif
271
        const auto osr_proj_logger_none = [](void *, int, const char *) {};
×
272
        proj_log_func(l_projContext.context, nullptr, osr_proj_logger_none);
×
273
        proj_context_set_autoclose_database(l_projContext.context, true);
×
274
        // dummy call to cause the database to be closed
275
        proj_context_get_database_path(l_projContext.context);
×
276
        proj_context_set_autoclose_database(l_projContext.context, false);
×
277
        proj_log_func(l_projContext.context, nullptr, osr_proj_logger);
×
278
    }
279

280
    return l_projContext;
2,950,250✔
281
}
282
#endif
283

284
PJ_CONTEXT *OSRGetProjTLSContext()
2,892,170✔
285
{
286
    auto &l_projContext = GetProjTLSContextHolder();
2,892,170✔
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
    l_projContext.init();
2,892,170✔
291
    {
292
        // If OSRSetPROJSearchPaths() has been called since we created the
293
        // context, set the new search paths on the context.
294
        std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
5,784,340✔
295
        if (l_projContext.searchPathGenerationCounter !=
2,892,170✔
296
            g_searchPathGenerationCounter)
297
        {
298
            l_projContext.searchPathGenerationCounter =
93✔
299
                g_searchPathGenerationCounter;
300
            proj_context_set_search_paths(l_projContext.context,
93✔
301
                                          g_aosSearchpaths.Count(),
302
                                          g_aosSearchpaths.List());
93✔
303
        }
304
        if (l_projContext.auxDbPathsGenerationCounter !=
2,892,170✔
305
            g_auxDbPathsGenerationCounter)
306
        {
307
            l_projContext.auxDbPathsGenerationCounter =
2✔
308
                g_auxDbPathsGenerationCounter;
309
            std::string oMainPath(
310
                proj_context_get_database_path(l_projContext.context));
4✔
311
            proj_context_set_database_path(l_projContext.context,
2✔
312
                                           oMainPath.c_str(),
313
                                           g_aosAuxDbPaths.List(), nullptr);
2✔
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
    return l_projContext.context;
2,892,170✔
327
}
328

329
/************************************************************************/
330
/*                         OSRGetProjTLSCache()                         */
331
/************************************************************************/
332

333
OSRProjTLSCache *OSRGetProjTLSCache()
57,229✔
334
{
335
    auto &l_projContext = GetProjTLSContextHolder();
57,229✔
336
    return &l_projContext.oCache;
57,229✔
337
}
338

339
void OSRProjTLSCache::clear()
2,015✔
340
{
341
    m_oCacheEPSG.clear();
2,015✔
342
    m_oCacheWKT.clear();
2,015✔
343
    m_tlsContext = nullptr;
2,015✔
344
}
2,015✔
345

346
PJ_CONTEXT *OSRProjTLSCache::GetPJContext()
56,388✔
347
{
348
    if (m_tlsContext == nullptr)
56,388✔
349
        m_tlsContext = OSRGetProjTLSContext();
5✔
350
    return m_tlsContext;
56,388✔
351
}
352

353
PJ *OSRProjTLSCache::GetPJForEPSGCode(int nCode, bool bUseNonDeprecated,
30,774✔
354
                                      bool bAddTOWGS84)
355
{
356
    const EPSGCacheKey key(nCode, bUseNonDeprecated, bAddTOWGS84);
30,774✔
357
    auto cached = m_oCacheEPSG.getPtr(key);
30,774✔
358
    if (cached)
30,774✔
359
    {
360
        return proj_clone(GetPJContext(), cached->get());
22,971✔
361
    }
362
    return nullptr;
7,803✔
363
}
364

365
void OSRProjTLSCache::CachePJForEPSGCode(int nCode, bool bUseNonDeprecated,
7,783✔
366
                                         bool bAddTOWGS84, PJ *pj)
367
{
368
    const EPSGCacheKey key(nCode, bUseNonDeprecated, bAddTOWGS84);
7,783✔
369
    m_oCacheEPSG.insert(key, UniquePtrPJ(proj_clone(GetPJContext(), pj)));
7,783✔
370
}
7,783✔
371

372
PJ *OSRProjTLSCache::GetPJForWKT(const std::string &wkt)
25,917✔
373
{
374
    auto cached = m_oCacheWKT.getPtr(wkt);
25,917✔
375
    if (cached)
25,917✔
376
    {
377
        return proj_clone(GetPJContext(), cached->get());
24,255✔
378
    }
379
    return nullptr;
1,662✔
380
}
381

382
void OSRProjTLSCache::CachePJForWKT(const std::string &wkt, PJ *pj)
1,379✔
383
{
384
    m_oCacheWKT.insert(wkt, UniquePtrPJ(proj_clone(GetPJContext(), pj)));
1,379✔
385
}
1,379✔
386

387
/************************************************************************/
388
/*                         OSRCleanupTLSContext()                       */
389
/************************************************************************/
390

391
void OSRCleanupTLSContext()
857✔
392
{
393
    GetProjTLSContextHolder().deinit();
857✔
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
void OSRSetPROJSearchPaths(const char *const *papszPaths)
22✔
411
{
412
    std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
44✔
413
    g_searchPathGenerationCounter++;
22✔
414
    g_aosSearchpaths.Assign(CSLDuplicate(papszPaths), true);
22✔
415
    OSRInstallSetConfigOptionCallback();
22✔
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
char **OSRGetPROJSearchPaths()
24✔
429
{
430
    std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
48✔
431
    if (g_searchPathGenerationCounter > 0 && !g_aosSearchpaths.empty())
24✔
432
    {
433
        return CSLDuplicate(g_aosSearchpaths.List());
8✔
434
    }
435

436
    const char *pszSep =
16✔
437
#ifdef _WIN32
438
        ";"
439
#else
440
        ":"
441
#endif
442
        ;
443
    return CSLTokenizeString2(proj_info().searchpath, pszSep, 0);
16✔
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
void OSRSetPROJAuxDbPaths(const char *const *papszAux)
2✔
458
{
459
    std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
4✔
460
    g_auxDbPathsGenerationCounter++;
2✔
461
    g_aosAuxDbPaths.Assign(CSLDuplicate(papszAux), true);
2✔
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
char **OSRGetPROJAuxDbPaths(void)
1✔
477
{
478
    std::lock_guard<std::mutex> oLock(g_oSearchPathMutex);
2✔
479
    // Unfortunately, there is no getter for auxiliary database list at PROJ.
480
    // So, return our copy for now.
481
    return CSLDuplicate(g_aosAuxDbPaths.List());
2✔
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
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
    if (enabled)
×
506
    {
507
        CPLError(CE_Failure, CPLE_NotSupported,
×
508
                 "OSRSetPROJEnableNetwork() requires PROJ >= 7");
509
    }
510
#endif
511
}
×
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
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
    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
void OSRGetPROJVersion(int *pnMajor, int *pnMinor, int *pnPatch)
183✔
553
{
554
    auto info = proj_info();
183✔
555
    if (pnMajor)
183✔
556
        *pnMajor = info.major;
72✔
557
    if (pnMinor)
183✔
558
        *pnMinor = info.minor;
63✔
559
    if (pnPatch)
183✔
560
        *pnPatch = info.patch;
48✔
561
}
183✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc