Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/osm/ogrosmdatasource.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implements OGROSMDataSource class.
5
 * Author:   Even Rouault, <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2012-2014, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gpb.h"
14
#include "ogr_osm.h"
15
16
#include <cassert>
17
#include <cerrno>
18
#include <climits>
19
#include <cmath>
20
#include <cstddef>
21
#include <cstdio>
22
#include <cstdlib>
23
#include <cstring>
24
#include <ctime>
25
#include <algorithm>
26
#include <limits>
27
#include <map>
28
#include <memory>
29
#include <set>
30
#include <string>
31
#include <unordered_set>
32
#include <utility>
33
#include <vector>
34
35
#include "cpl_conv.h"
36
#include "cpl_error.h"
37
#include "cpl_multiproc.h"
38
#include "cpl_port.h"
39
#include "cpl_progress.h"
40
#include "cpl_string.h"
41
#include "cpl_time.h"
42
#include "cpl_vsi.h"
43
#include "ogr_api.h"
44
#include "ogr_core.h"
45
#include "ogr_feature.h"
46
#include "ogr_geometry.h"
47
#include "ogr_p.h"
48
#include "ogrlayerdecorator.h"
49
#include "ogrsf_frmts.h"
50
#include "ogrsqliteexecutesql.h"
51
#include "osm_parser.h"
52
#include "ogr_swq.h"
53
#include "sqlite3.h"
54
55
#ifdef EMBED_RESOURCE_FILES
56
#include "embedded_resources.h"
57
#endif
58
59
#undef SQLITE_STATIC
60
0
#define SQLITE_STATIC (static_cast<sqlite3_destructor_type>(nullptr))
61
62
constexpr int LIMIT_IDS_PER_REQUEST = 200;
63
64
constexpr int IDX_LYR_POINTS = 0;
65
constexpr int IDX_LYR_LINES = 1;
66
constexpr int IDX_LYR_MULTILINESTRINGS = 2;
67
constexpr int IDX_LYR_MULTIPOLYGONS = 3;
68
constexpr int IDX_LYR_OTHER_RELATIONS = 4;
69
70
static int DBL_TO_INT(double x)
71
0
{
72
0
    return static_cast<int>(floor(x * 1.0e7 + 0.5));
73
0
}
74
75
static double INT_TO_DBL(int x)
76
0
{
77
0
    return x / 1.0e7;
78
0
}
79
80
constexpr unsigned int MAX_COUNT_FOR_TAGS_IN_WAY = 255;  // Must fit on 1 byte.
81
82
constexpr int NODE_PER_BUCKET = 65536;
83
84
static bool VALID_ID_FOR_CUSTOM_INDEXING(GIntBig _id)
85
0
{
86
0
    return _id >= 0 && _id / NODE_PER_BUCKET < INT_MAX;
87
0
}
88
89
// Minimum size of data written on disk, in *uncompressed* case.
90
constexpr int SECTOR_SIZE = 512;
91
// Which represents, 64 nodes
92
// constexpr int NODE_PER_SECTOR = SECTOR_SIZE / (2 * 4);
93
constexpr int NODE_PER_SECTOR = 64;
94
constexpr int NODE_PER_SECTOR_SHIFT = 6;
95
96
// Per bucket, we keep track of the absence/presence of sectors
97
// only, to reduce memory usage.
98
// #define BUCKET_BITMAP_SIZE  NODE_PER_BUCKET / (8 * NODE_PER_SECTOR)
99
constexpr int BUCKET_BITMAP_SIZE = 128;
100
101
// #define BUCKET_SECTOR_SIZE_ARRAY_SIZE  NODE_PER_BUCKET / NODE_PER_SECTOR
102
// Per bucket, we keep track of the real size of the sector. Each sector
103
// size is encoded in a single byte, whose value is:
104
// (sector_size in bytes - 8 ) / 2, minus 8. 252 means uncompressed
105
constexpr int BUCKET_SECTOR_SIZE_ARRAY_SIZE = 1024;
106
107
// Must be a multiple of both BUCKET_BITMAP_SIZE and
108
// BUCKET_SECTOR_SIZE_ARRAY_SIZE
109
constexpr int knPAGE_SIZE = 4096;
110
111
// compressSize should not be greater than 512, so COMPRESS_SIZE_TO_BYTE() fits
112
// on a byte.
113
static GByte COMPRESS_SIZE_TO_BYTE(size_t nCompressSize)
114
0
{
115
0
    return static_cast<GByte>((nCompressSize - 8) / 2);
116
0
}
117
118
template <typename T> static T ROUND_COMPRESS_SIZE(T nCompressSize)
119
0
{
120
0
    return ((nCompressSize + 1) / 2) * 2;
121
0
}
Unexecuted instantiation: ogrosmdatasource.cpp:int ROUND_COMPRESS_SIZE<int>(int)
Unexecuted instantiation: ogrosmdatasource.cpp:unsigned long ROUND_COMPRESS_SIZE<unsigned long>(unsigned long)
122
123
static int COMPRESS_SIZE_FROM_BYTE(GByte byte_on_size)
124
0
{
125
0
    return static_cast<int>(byte_on_size) * 2 + 8;
126
0
}
127
128
// Max number of features that are accumulated in pasWayFeaturePairs.
129
constexpr int MAX_DELAYED_FEATURES = 75000;
130
// Max number of tags that are accumulated in pasAccumulatedTags.
131
constexpr int MAX_ACCUMULATED_TAGS = MAX_DELAYED_FEATURES * 5;
132
// Max size of the string with tag values that are accumulated in
133
// pabyNonRedundantValues.
134
constexpr int MAX_NON_REDUNDANT_VALUES = MAX_DELAYED_FEATURES * 10;
135
// Max size of the string with tag values that are accumulated in
136
// pabyNonRedundantKeys.
137
constexpr int MAX_NON_REDUNDANT_KEYS = MAX_DELAYED_FEATURES * 10;
138
// Max number of features that are accumulated in panUnsortedReqIds
139
constexpr int MAX_ACCUMULATED_NODES = 1000000;
140
141
#ifdef ENABLE_NODE_LOOKUP_BY_HASHING
142
// Size of panHashedIndexes array. Must be in the list at
143
// http://planetmath.org/goodhashtableprimes , and greater than
144
// MAX_ACCUMULATED_NODES.
145
constexpr int HASHED_INDEXES_ARRAY_SIZE = 3145739;
146
// #define HASHED_INDEXES_ARRAY_SIZE   1572869
147
constexpr int COLLISION_BUCKET_ARRAY_SIZE = (MAX_ACCUMULATED_NODES / 100) * 40;
148
149
// hash function = identity
150
0
#define HASH_ID_FUNC(x) (static_cast<std::uintptr_t>(x))
151
#endif  // ENABLE_NODE_LOOKUP_BY_HASHING
152
153
// #define FAKE_LOOKUP_NODES
154
155
// #define DEBUG_MEM_USAGE
156
#ifdef DEBUG_MEM_USAGE
157
size_t GetMaxTotalAllocs();
158
#endif
159
160
static void WriteVarSInt64(GIntBig nSVal, GByte **ppabyData);
161
162
class DSToBeOpened
163
{
164
  public:
165
    GIntBig nPID{};
166
    CPLString osDSName{};
167
    CPLString osInterestLayers{};
168
};
169
170
static CPLMutex *hMutex = nullptr;
171
static std::vector<DSToBeOpened> oListDSToBeOpened;
172
173
/************************************************************************/
174
/*                    AddInterestLayersForDSName()                      */
175
/************************************************************************/
176
177
static void AddInterestLayersForDSName(const CPLString &osDSName,
178
                                       const CPLString &osInterestLayers)
179
0
{
180
0
    CPLMutexHolder oMutexHolder(&hMutex);
181
0
    DSToBeOpened oDSToBeOpened;
182
0
    oDSToBeOpened.nPID = CPLGetPID();
183
0
    oDSToBeOpened.osDSName = osDSName;
184
0
    oDSToBeOpened.osInterestLayers = osInterestLayers;
185
0
    oListDSToBeOpened.push_back(std::move(oDSToBeOpened));
186
0
}
187
188
/************************************************************************/
189
/*                    GetInterestLayersForDSName()                      */
190
/************************************************************************/
191
192
static CPLString GetInterestLayersForDSName(const CPLString &osDSName)
193
0
{
194
0
    CPLMutexHolder oMutexHolder(&hMutex);
195
0
    GIntBig nPID = CPLGetPID();
196
0
    for (auto oIter = oListDSToBeOpened.begin();
197
0
         oIter < oListDSToBeOpened.end(); ++oIter)
198
0
    {
199
0
        const auto &ds = *oIter;
200
0
        if (ds.nPID == nPID && ds.osDSName == osDSName)
201
0
        {
202
0
            CPLString osInterestLayers = ds.osInterestLayers;
203
0
            oListDSToBeOpened.erase(oIter);
204
0
            return osInterestLayers;
205
0
        }
206
0
    }
207
0
    return "";
208
0
}
209
210
/************************************************************************/
211
/*                        OGROSMDataSource()                            */
212
/************************************************************************/
213
214
OGROSMDataSource::OGROSMDataSource()
215
1.47k
{
216
1.47k
    m_apsKeys.push_back(nullptr);  // guard to avoid index 0 to be used
217
218
1.47k
    MAX_INDEXED_KEYS = static_cast<unsigned>(
219
1.47k
        atoi(CPLGetConfigOption("OSM_MAX_INDEXED_KEYS", "32768")));
220
1.47k
    MAX_INDEXED_VALUES_PER_KEY = static_cast<unsigned>(
221
1.47k
        atoi(CPLGetConfigOption("OSM_MAX_INDEXED_VALUES_PER_KEY", "1024")));
222
1.47k
}
223
224
/************************************************************************/
225
/*                          ~OGROSMDataSource()                         */
226
/************************************************************************/
227
228
OGROSMDataSource::~OGROSMDataSource()
229
230
1.47k
{
231
1.47k
    m_apoLayers.clear();
232
233
1.47k
    if (m_psParser != nullptr)
234
1.05k
        CPLDebug("OSM", "Number of bytes read in file : " CPL_FRMT_GUIB,
235
1.05k
                 OSM_GetBytesRead(m_psParser));
236
1.47k
    OSM_Close(m_psParser);
237
238
1.47k
    if (m_hDB != nullptr)
239
0
        CloseDB();
240
241
1.47k
    if (m_hDBForComputedAttributes != nullptr)
242
0
        sqlite3_close(m_hDBForComputedAttributes);
243
244
1.47k
    if (m_pMyVFS)
245
0
    {
246
0
        sqlite3_vfs_unregister(m_pMyVFS);
247
0
        CPLFree(m_pMyVFS->pAppData);
248
0
        CPLFree(m_pMyVFS);
249
0
    }
250
251
1.47k
    if (!m_osTmpDBName.empty() && m_bMustUnlink)
252
0
    {
253
0
        const char *pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
254
0
        if (!EQUAL(pszVal, "NOT_EVEN_AT_END"))
255
0
            VSIUnlink(m_osTmpDBName);
256
0
    }
257
258
1.47k
    CPLFree(m_panReqIds);
259
1.47k
#ifdef ENABLE_NODE_LOOKUP_BY_HASHING
260
1.47k
    CPLFree(m_panHashedIndexes);
261
1.47k
    CPLFree(m_psCollisionBuckets);
262
1.47k
#endif
263
1.47k
    CPLFree(m_pasLonLatArray);
264
1.47k
    CPLFree(m_panUnsortedReqIds);
265
266
1.47k
    CPLFree(m_pasAccumulatedTags);
267
1.47k
    CPLFree(pabyNonRedundantKeys);
268
1.47k
    CPLFree(pabyNonRedundantValues);
269
270
#ifdef OSM_DEBUG
271
    FILE *f = fopen("keys.txt", "wt");
272
    for (int i = 1; i < startic_cast<int>(asKeys.size()); i++)
273
    {
274
        KeyDesc *psKD = asKeys[i];
275
        if (psKD)
276
        {
277
            fprintf(f, "%08d idx=%d %s\n", psKD->nOccurrences, psKD->nKeyIndex,
278
                    psKD->pszK);
279
        }
280
    }
281
    fclose(f);
282
#endif
283
284
1.47k
    for (int i = 1; i < static_cast<int>(m_apsKeys.size()); i++)
285
0
    {
286
0
        KeyDesc *psKD = m_apsKeys[i];
287
0
        if (psKD)
288
0
        {
289
0
            CPLFree(psKD->pszK);
290
0
            for (int j = 0; j < static_cast<int>(psKD->apszValues.size()); j++)
291
0
                CPLFree(psKD->apszValues[j]);
292
0
            delete psKD;
293
0
        }
294
0
    }
295
296
1.47k
    if (m_fpNodes)
297
0
        VSIFCloseL(m_fpNodes);
298
1.47k
    if (!m_osNodesFilename.empty() && m_bMustUnlinkNodesFile)
299
0
    {
300
0
        const char *pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
301
0
        if (!EQUAL(pszVal, "NOT_EVEN_AT_END"))
302
0
            VSIUnlink(m_osNodesFilename);
303
0
    }
304
305
1.47k
    CPLFree(m_pabySector);
306
1.47k
    for (auto &oIter : m_oMapBuckets)
307
0
    {
308
0
        if (m_bCompressNodes)
309
0
        {
310
0
            int nRem =
311
0
                oIter.first % (knPAGE_SIZE / BUCKET_SECTOR_SIZE_ARRAY_SIZE);
312
0
            if (nRem == 0)
313
0
                CPLFree(oIter.second.u.panSectorSize);
314
0
        }
315
0
        else
316
0
        {
317
0
            int nRem = oIter.first % (knPAGE_SIZE / BUCKET_BITMAP_SIZE);
318
0
            if (nRem == 0)
319
0
                CPLFree(oIter.second.u.pabyBitmap);
320
0
        }
321
0
    }
322
1.47k
}
323
324
/************************************************************************/
325
/*                             CloseDB()                               */
326
/************************************************************************/
327
328
void OGROSMDataSource::CloseDB()
329
0
{
330
0
    if (m_hInsertNodeStmt != nullptr)
331
0
        sqlite3_finalize(m_hInsertNodeStmt);
332
0
    m_hInsertNodeStmt = nullptr;
333
334
0
    if (m_hInsertWayStmt != nullptr)
335
0
        sqlite3_finalize(m_hInsertWayStmt);
336
0
    m_hInsertWayStmt = nullptr;
337
338
0
    if (m_hInsertPolygonsStandaloneStmt != nullptr)
339
0
        sqlite3_finalize(m_hInsertPolygonsStandaloneStmt);
340
0
    m_hInsertPolygonsStandaloneStmt = nullptr;
341
342
0
    if (m_hDeletePolygonsStandaloneStmt != nullptr)
343
0
        sqlite3_finalize(m_hDeletePolygonsStandaloneStmt);
344
0
    m_hDeletePolygonsStandaloneStmt = nullptr;
345
346
0
    if (m_hSelectPolygonsStandaloneStmt != nullptr)
347
0
        sqlite3_finalize(m_hSelectPolygonsStandaloneStmt);
348
0
    m_hSelectPolygonsStandaloneStmt = nullptr;
349
350
0
    if (m_pahSelectNodeStmt != nullptr)
351
0
    {
352
0
        for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
353
0
        {
354
0
            if (m_pahSelectNodeStmt[i] != nullptr)
355
0
                sqlite3_finalize(m_pahSelectNodeStmt[i]);
356
0
        }
357
0
        CPLFree(m_pahSelectNodeStmt);
358
0
        m_pahSelectNodeStmt = nullptr;
359
0
    }
360
361
0
    if (m_pahSelectWayStmt != nullptr)
362
0
    {
363
0
        for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
364
0
        {
365
0
            if (m_pahSelectWayStmt[i] != nullptr)
366
0
                sqlite3_finalize(m_pahSelectWayStmt[i]);
367
0
        }
368
0
        CPLFree(m_pahSelectWayStmt);
369
0
        m_pahSelectWayStmt = nullptr;
370
0
    }
371
372
0
    if (m_bInTransaction)
373
0
        CommitTransactionCacheDB();
374
375
0
    sqlite3_close(m_hDB);
376
0
    m_hDB = nullptr;
377
0
}
378
379
/************************************************************************/
380
/*                             IndexPoint()                             */
381
/************************************************************************/
382
383
constexpr GByte abyBitsCount[] = {
384
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4,
385
    2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
386
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4,
387
    2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
388
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
389
    4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
390
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5,
391
    3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
392
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
393
    4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
394
    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
395
396
bool OGROSMDataSource::IndexPoint(const OSMNode *psNode)
397
0
{
398
0
    if (!m_bIndexPoints)
399
0
        return true;
400
401
0
    if (m_bCustomIndexing)
402
0
        return IndexPointCustom(psNode);
403
404
0
    return IndexPointSQLite(psNode);
405
0
}
406
407
/************************************************************************/
408
/*                          IndexPointSQLite()                          */
409
/************************************************************************/
410
411
bool OGROSMDataSource::IndexPointSQLite(const OSMNode *psNode)
412
0
{
413
0
    sqlite3_bind_int64(m_hInsertNodeStmt, 1, psNode->nID);
414
415
0
    LonLat sLonLat;
416
0
    sLonLat.nLon = DBL_TO_INT(psNode->dfLon);
417
0
    sLonLat.nLat = DBL_TO_INT(psNode->dfLat);
418
419
0
    sqlite3_bind_blob(m_hInsertNodeStmt, 2, &sLonLat, sizeof(sLonLat),
420
0
                      SQLITE_STATIC);
421
422
0
    const int rc = sqlite3_step(m_hInsertNodeStmt);
423
0
    sqlite3_reset(m_hInsertNodeStmt);
424
0
    if (!(rc == SQLITE_OK || rc == SQLITE_DONE))
425
0
    {
426
0
        CPLError(CE_Failure, CPLE_AppDefined,
427
0
                 "Failed inserting node " CPL_FRMT_GIB ": %s", psNode->nID,
428
0
                 sqlite3_errmsg(m_hDB));
429
0
        return false;
430
0
    }
431
432
0
    return true;
433
0
}
434
435
/************************************************************************/
436
/*                           FlushCurrentSector()                       */
437
/************************************************************************/
438
439
bool OGROSMDataSource::FlushCurrentSector()
440
0
{
441
0
#ifndef FAKE_LOOKUP_NODES
442
0
    if (m_bCompressNodes)
443
0
        return FlushCurrentSectorCompressedCase();
444
445
0
    return FlushCurrentSectorNonCompressedCase();
446
#else
447
    return true;
448
#endif
449
0
}
450
451
/************************************************************************/
452
/*                            AllocBucket()                             */
453
/************************************************************************/
454
455
Bucket *OGROSMDataSource::AllocBucket(int iBucket)
456
0
{
457
0
    if (m_bCompressNodes)
458
0
    {
459
0
        const int nRem =
460
0
            iBucket % (knPAGE_SIZE / BUCKET_SECTOR_SIZE_ARRAY_SIZE);
461
0
        Bucket *psPrevBucket = GetBucket(iBucket - nRem);
462
0
        if (psPrevBucket->u.panSectorSize == nullptr)
463
0
            psPrevBucket->u.panSectorSize =
464
0
                static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, knPAGE_SIZE));
465
0
        GByte *panSectorSize = psPrevBucket->u.panSectorSize;
466
0
        Bucket *psBucket = GetBucket(iBucket);
467
0
        if (panSectorSize != nullptr)
468
0
        {
469
0
            psBucket->u.panSectorSize =
470
0
                panSectorSize + nRem * BUCKET_SECTOR_SIZE_ARRAY_SIZE;
471
0
            return psBucket;
472
0
        }
473
0
        psBucket->u.panSectorSize = nullptr;
474
0
    }
475
0
    else
476
0
    {
477
0
        const int nRem = iBucket % (knPAGE_SIZE / BUCKET_BITMAP_SIZE);
478
0
        Bucket *psPrevBucket = GetBucket(iBucket - nRem);
479
0
        if (psPrevBucket->u.pabyBitmap == nullptr)
480
0
            psPrevBucket->u.pabyBitmap =
481
0
                reinterpret_cast<GByte *>(VSI_CALLOC_VERBOSE(1, knPAGE_SIZE));
482
0
        GByte *pabyBitmap = psPrevBucket->u.pabyBitmap;
483
0
        Bucket *psBucket = GetBucket(iBucket);
484
0
        if (pabyBitmap != nullptr)
485
0
        {
486
0
            psBucket->u.pabyBitmap = pabyBitmap + nRem * BUCKET_BITMAP_SIZE;
487
0
            return psBucket;
488
0
        }
489
0
        psBucket->u.pabyBitmap = nullptr;
490
0
    }
491
492
    // Out of memory.
493
0
    CPLError(CE_Failure, CPLE_AppDefined,
494
0
             "AllocBucket() failed. Use OSM_USE_CUSTOM_INDEXING=NO");
495
0
    m_bStopParsing = true;
496
0
    return nullptr;
497
0
}
498
499
/************************************************************************/
500
/*                             GetBucket()                              */
501
/************************************************************************/
502
503
Bucket *OGROSMDataSource::GetBucket(int nBucketId)
504
0
{
505
0
    auto oIter = m_oMapBuckets.find(nBucketId);
506
0
    if (oIter == m_oMapBuckets.end())
507
0
    {
508
0
        Bucket *psBucket = &m_oMapBuckets[nBucketId];
509
0
        psBucket->nOff = -1;
510
0
        if (m_bCompressNodes)
511
0
            psBucket->u.panSectorSize = nullptr;
512
0
        else
513
0
            psBucket->u.pabyBitmap = nullptr;
514
0
        return psBucket;
515
0
    }
516
0
    return &(oIter->second);
517
0
}
518
519
/************************************************************************/
520
/*                     FlushCurrentSectorCompressedCase()               */
521
/************************************************************************/
522
523
bool OGROSMDataSource::FlushCurrentSectorCompressedCase()
524
0
{
525
0
    GByte abyOutBuffer[2 * SECTOR_SIZE];
526
0
    GByte *pabyOut = abyOutBuffer;
527
0
    LonLat *pasLonLatIn = reinterpret_cast<LonLat *>(m_pabySector);
528
0
    int nLastLon = 0;
529
0
    int nLastLat = 0;
530
0
    bool bLastValid = false;
531
532
0
    CPLAssert((NODE_PER_SECTOR % 8) == 0);
533
0
    memset(abyOutBuffer, 0, NODE_PER_SECTOR / 8);
534
0
    pabyOut += NODE_PER_SECTOR / 8;
535
0
    for (int i = 0; i < NODE_PER_SECTOR; i++)
536
0
    {
537
0
        if (pasLonLatIn[i].nLon || pasLonLatIn[i].nLat)
538
0
        {
539
0
            abyOutBuffer[i >> 3] |= (1 << (i % 8));
540
0
            if (bLastValid)
541
0
            {
542
0
                const GIntBig nDiff64Lon =
543
0
                    static_cast<GIntBig>(pasLonLatIn[i].nLon) -
544
0
                    static_cast<GIntBig>(nLastLon);
545
0
                const GIntBig nDiff64Lat = pasLonLatIn[i].nLat - nLastLat;
546
0
                WriteVarSInt64(nDiff64Lon, &pabyOut);
547
0
                WriteVarSInt64(nDiff64Lat, &pabyOut);
548
0
            }
549
0
            else
550
0
            {
551
0
                memcpy(pabyOut, &pasLonLatIn[i], sizeof(LonLat));
552
0
                pabyOut += sizeof(LonLat);
553
0
            }
554
0
            bLastValid = true;
555
556
0
            nLastLon = pasLonLatIn[i].nLon;
557
0
            nLastLat = pasLonLatIn[i].nLat;
558
0
        }
559
0
    }
560
561
0
    size_t nCompressSize = static_cast<size_t>(pabyOut - abyOutBuffer);
562
0
    CPLAssert(nCompressSize < sizeof(abyOutBuffer) - 1);
563
0
    abyOutBuffer[nCompressSize] = 0;
564
565
0
    nCompressSize = ROUND_COMPRESS_SIZE(nCompressSize);
566
0
    GByte *pabyToWrite = nullptr;
567
0
    if (nCompressSize >= static_cast<size_t>(SECTOR_SIZE))
568
0
    {
569
0
        nCompressSize = SECTOR_SIZE;
570
0
        pabyToWrite = m_pabySector;
571
0
    }
572
0
    else
573
0
        pabyToWrite = abyOutBuffer;
574
575
0
    if (VSIFWriteL(pabyToWrite, 1, nCompressSize, m_fpNodes) == nCompressSize)
576
0
    {
577
0
        memset(m_pabySector, 0, SECTOR_SIZE);
578
0
        m_nNodesFileSize += nCompressSize;
579
580
0
        Bucket *psBucket = GetBucket(m_nBucketOld);
581
0
        if (psBucket->u.panSectorSize == nullptr)
582
0
        {
583
0
            psBucket = AllocBucket(m_nBucketOld);
584
0
            if (psBucket == nullptr)
585
0
                return false;
586
0
        }
587
0
        CPLAssert(psBucket->u.panSectorSize != nullptr);
588
0
        psBucket->u.panSectorSize[m_nOffInBucketReducedOld] =
589
0
            COMPRESS_SIZE_TO_BYTE(nCompressSize);
590
591
0
        return true;
592
0
    }
593
594
0
    CPLError(CE_Failure, CPLE_AppDefined,
595
0
             "Cannot write in temporary node file %s : %s",
596
0
             m_osNodesFilename.c_str(), VSIStrerror(errno));
597
598
0
    return false;
599
0
}
600
601
/************************************************************************/
602
/*                   FlushCurrentSectorNonCompressedCase()              */
603
/************************************************************************/
604
605
bool OGROSMDataSource::FlushCurrentSectorNonCompressedCase()
606
0
{
607
0
    if (VSIFWriteL(m_pabySector, 1, static_cast<size_t>(SECTOR_SIZE),
608
0
                   m_fpNodes) == static_cast<size_t>(SECTOR_SIZE))
609
0
    {
610
0
        memset(m_pabySector, 0, SECTOR_SIZE);
611
0
        m_nNodesFileSize += SECTOR_SIZE;
612
0
        return true;
613
0
    }
614
615
0
    CPLError(CE_Failure, CPLE_AppDefined,
616
0
             "Cannot write in temporary node file %s : %s",
617
0
             m_osNodesFilename.c_str(), VSIStrerror(errno));
618
619
0
    return false;
620
0
}
621
622
/************************************************************************/
623
/*                          IndexPointCustom()                          */
624
/************************************************************************/
625
626
bool OGROSMDataSource::IndexPointCustom(const OSMNode *psNode)
627
0
{
628
0
    if (psNode->nID <= m_nPrevNodeId)
629
0
    {
630
0
        CPLError(CE_Failure, CPLE_AppDefined,
631
0
                 "Non increasing node id. Use OSM_USE_CUSTOM_INDEXING=NO");
632
0
        m_bStopParsing = true;
633
0
        return false;
634
0
    }
635
0
    if (!VALID_ID_FOR_CUSTOM_INDEXING(psNode->nID))
636
0
    {
637
0
        CPLError(CE_Failure, CPLE_AppDefined,
638
0
                 "Unsupported node id value (" CPL_FRMT_GIB
639
0
                 "). Use OSM_USE_CUSTOM_INDEXING=NO",
640
0
                 psNode->nID);
641
0
        m_bStopParsing = true;
642
0
        return false;
643
0
    }
644
645
0
    const int nBucket = static_cast<int>(psNode->nID / NODE_PER_BUCKET);
646
0
    const int nOffInBucket = static_cast<int>(psNode->nID % NODE_PER_BUCKET);
647
0
    const int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
648
0
    const int nOffInBucketReducedRemainder =
649
0
        nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1);
650
651
0
    Bucket *psBucket = GetBucket(nBucket);
652
653
0
    if (!m_bCompressNodes)
654
0
    {
655
0
        const int nBitmapIndex = nOffInBucketReduced / 8;
656
0
        const int nBitmapRemainder = nOffInBucketReduced % 8;
657
0
        if (psBucket->u.pabyBitmap == nullptr)
658
0
        {
659
0
            psBucket = AllocBucket(nBucket);
660
0
            if (psBucket == nullptr)
661
0
                return false;
662
0
        }
663
0
        CPLAssert(psBucket->u.pabyBitmap != nullptr);
664
0
        psBucket->u.pabyBitmap[nBitmapIndex] |= (1 << nBitmapRemainder);
665
0
    }
666
667
0
    if (nBucket != m_nBucketOld)
668
0
    {
669
0
        CPLAssert(nBucket > m_nBucketOld);
670
0
        if (m_nBucketOld >= 0)
671
0
        {
672
0
            if (!FlushCurrentSector())
673
0
            {
674
0
                m_bStopParsing = true;
675
0
                return false;
676
0
            }
677
0
        }
678
0
        m_nBucketOld = nBucket;
679
0
        m_nOffInBucketReducedOld = nOffInBucketReduced;
680
0
        CPLAssert(psBucket->nOff == -1);
681
0
        psBucket->nOff = VSIFTellL(m_fpNodes);
682
0
    }
683
0
    else if (nOffInBucketReduced != m_nOffInBucketReducedOld)
684
0
    {
685
0
        CPLAssert(nOffInBucketReduced > m_nOffInBucketReducedOld);
686
0
        if (!FlushCurrentSector())
687
0
        {
688
0
            m_bStopParsing = true;
689
0
            return false;
690
0
        }
691
0
        m_nOffInBucketReducedOld = nOffInBucketReduced;
692
0
    }
693
694
0
    LonLat *psLonLat = reinterpret_cast<LonLat *>(
695
0
        m_pabySector + sizeof(LonLat) * nOffInBucketReducedRemainder);
696
0
    psLonLat->nLon = DBL_TO_INT(psNode->dfLon);
697
0
    psLonLat->nLat = DBL_TO_INT(psNode->dfLat);
698
699
0
    m_nPrevNodeId = psNode->nID;
700
701
0
    return true;
702
0
}
703
704
/************************************************************************/
705
/*                             NotifyNodes()                            */
706
/************************************************************************/
707
708
void OGROSMDataSource::NotifyNodes(unsigned int nNodes, const OSMNode *pasNodes)
709
0
{
710
0
    const OGREnvelope *psEnvelope =
711
0
        m_apoLayers[IDX_LYR_POINTS]->GetSpatialFilterEnvelope();
712
713
0
    for (unsigned int i = 0; i < nNodes; i++)
714
0
    {
715
        /* If the point doesn't fit into the envelope of the spatial filter */
716
        /* then skip it */
717
0
        if (psEnvelope != nullptr && !(pasNodes[i].dfLon >= psEnvelope->MinX &&
718
0
                                       pasNodes[i].dfLon <= psEnvelope->MaxX &&
719
0
                                       pasNodes[i].dfLat >= psEnvelope->MinY &&
720
0
                                       pasNodes[i].dfLat <= psEnvelope->MaxY))
721
0
            continue;
722
723
0
        if (!IndexPoint(&pasNodes[i]))
724
0
            break;
725
726
0
        if (!m_apoLayers[IDX_LYR_POINTS]->IsUserInterested())
727
0
            continue;
728
729
0
        bool bInterestingTag = m_bReportAllNodes;
730
0
        const OSMTag *pasTags = pasNodes[i].pasTags;
731
732
0
        if (!m_bReportAllNodes)
733
0
        {
734
0
            for (unsigned int j = 0; j < pasNodes[i].nTags; j++)
735
0
            {
736
0
                const char *pszK = pasTags[j].pszK;
737
0
                if (m_apoLayers[IDX_LYR_POINTS]->IsSignificantKey(pszK))
738
0
                {
739
0
                    bInterestingTag = true;
740
0
                    break;
741
0
                }
742
0
            }
743
0
        }
744
745
0
        if (bInterestingTag)
746
0
        {
747
0
            auto poFeature = std::make_unique<OGRFeature>(
748
0
                m_apoLayers[IDX_LYR_POINTS]->GetLayerDefn());
749
750
0
            poFeature->SetGeometryDirectly(
751
0
                new OGRPoint(pasNodes[i].dfLon, pasNodes[i].dfLat));
752
753
0
            m_apoLayers[IDX_LYR_POINTS]->SetFieldsFromTags(
754
0
                poFeature.get(), pasNodes[i].nID, false, pasNodes[i].nTags,
755
0
                pasTags, &pasNodes[i].sInfo);
756
757
0
            bool bFilteredOut = false;
758
0
            if (!m_apoLayers[IDX_LYR_POINTS]->AddFeature(std::move(poFeature),
759
0
                                                         false, &bFilteredOut,
760
0
                                                         !m_bFeatureAdded))
761
0
            {
762
0
                m_bStopParsing = true;
763
0
                break;
764
0
            }
765
0
            else if (!bFilteredOut)
766
0
                m_bFeatureAdded = true;
767
0
        }
768
0
    }
769
0
}
770
771
static void OGROSMNotifyNodes(unsigned int nNodes, OSMNode *pasNodes,
772
                              OSMContext * /* psOSMContext */, void *user_data)
773
0
{
774
0
    static_cast<OGROSMDataSource *>(user_data)->NotifyNodes(nNodes, pasNodes);
775
0
}
776
777
/************************************************************************/
778
/*                            LookupNodes()                             */
779
/************************************************************************/
780
781
// #define DEBUG_COLLISIONS 1
782
783
void OGROSMDataSource::LookupNodes()
784
0
{
785
0
    if (m_bCustomIndexing)
786
0
        LookupNodesCustom();
787
0
    else
788
0
        LookupNodesSQLite();
789
790
0
#ifdef ENABLE_NODE_LOOKUP_BY_HASHING
791
0
    if (m_nReqIds > 1 && m_bEnableHashedIndex)
792
0
    {
793
0
        memset(m_panHashedIndexes, 0xFF,
794
0
               HASHED_INDEXES_ARRAY_SIZE * sizeof(int));
795
0
        m_bHashedIndexValid = true;
796
#ifdef DEBUG_COLLISIONS
797
        int nCollisions = 0;
798
#endif
799
0
        int iNextFreeBucket = 0;
800
0
        for (unsigned int i = 0; i < m_nReqIds; i++)
801
0
        {
802
0
            int nIndInHashArray = static_cast<int>(
803
0
                HASH_ID_FUNC(m_panReqIds[i]) % HASHED_INDEXES_ARRAY_SIZE);
804
0
            int nIdx = m_panHashedIndexes[nIndInHashArray];
805
0
            if (nIdx == -1)
806
0
            {
807
0
                m_panHashedIndexes[nIndInHashArray] = i;
808
0
            }
809
0
            else
810
0
            {
811
#ifdef DEBUG_COLLISIONS
812
                nCollisions++;
813
#endif
814
0
                int iBucket = 0;
815
0
                if (nIdx >= 0)
816
0
                {
817
0
                    if (iNextFreeBucket == COLLISION_BUCKET_ARRAY_SIZE)
818
0
                    {
819
0
                        CPLDebug(
820
0
                            "OSM",
821
0
                            "Too many collisions. Disabling hashed indexing");
822
0
                        m_bHashedIndexValid = false;
823
0
                        m_bEnableHashedIndex = false;
824
0
                        break;
825
0
                    }
826
0
                    iBucket = iNextFreeBucket;
827
0
                    m_psCollisionBuckets[iNextFreeBucket].nInd = nIdx;
828
0
                    m_psCollisionBuckets[iNextFreeBucket].nNext = -1;
829
0
                    m_panHashedIndexes[nIndInHashArray] = -iNextFreeBucket - 2;
830
0
                    iNextFreeBucket++;
831
0
                }
832
0
                else
833
0
                {
834
0
                    iBucket = -nIdx - 2;
835
0
                }
836
0
                if (iNextFreeBucket == COLLISION_BUCKET_ARRAY_SIZE)
837
0
                {
838
0
                    CPLDebug("OSM",
839
0
                             "Too many collisions. Disabling hashed indexing");
840
0
                    m_bHashedIndexValid = false;
841
0
                    m_bEnableHashedIndex = false;
842
0
                    break;
843
0
                }
844
0
                while (true)
845
0
                {
846
0
                    int iNext = m_psCollisionBuckets[iBucket].nNext;
847
0
                    if (iNext < 0)
848
0
                    {
849
0
                        m_psCollisionBuckets[iBucket].nNext = iNextFreeBucket;
850
0
                        m_psCollisionBuckets[iNextFreeBucket].nInd = i;
851
0
                        m_psCollisionBuckets[iNextFreeBucket].nNext = -1;
852
0
                        iNextFreeBucket++;
853
0
                        break;
854
0
                    }
855
0
                    iBucket = iNext;
856
0
                }
857
0
            }
858
0
        }
859
#ifdef DEBUG_COLLISIONS
860
        /* Collision rate in practice is around 12% on France, Germany, ... */
861
        /* Maximum seen ~ 15.9% on a planet file but often much smaller. */
862
        CPLDebug("OSM",
863
                 "nCollisions = %d/%d (%.1f %%), iNextFreeBucket = %d/%d",
864
                 nCollisions, nReqIds, nCollisions * 100.0 / nReqIds,
865
                 iNextFreeBucket, COLLISION_BUCKET_ARRAY_SIZE);
866
#endif
867
0
    }
868
0
    else
869
0
        m_bHashedIndexValid = false;
870
0
#endif  // ENABLE_NODE_LOOKUP_BY_HASHING
871
0
}
872
873
/************************************************************************/
874
/*                           LookupNodesSQLite()                        */
875
/************************************************************************/
876
877
void OGROSMDataSource::LookupNodesSQLite()
878
0
{
879
0
    CPLAssert(m_nUnsortedReqIds <=
880
0
              static_cast<unsigned int>(MAX_ACCUMULATED_NODES));
881
882
0
    m_nReqIds = 0;
883
0
    for (unsigned int i = 0; i < m_nUnsortedReqIds; i++)
884
0
    {
885
0
        GIntBig id = m_panUnsortedReqIds[i];
886
0
        m_panReqIds[m_nReqIds++] = id;
887
0
    }
888
889
0
    std::sort(m_panReqIds, m_panReqIds + m_nReqIds);
890
891
    /* Remove duplicates */
892
0
    unsigned int j = 0;
893
0
    for (unsigned int i = 0; i < m_nReqIds; i++)
894
0
    {
895
0
        if (!(i > 0 && m_panReqIds[i] == m_panReqIds[i - 1]))
896
0
            m_panReqIds[j++] = m_panReqIds[i];
897
0
    }
898
0
    m_nReqIds = j;
899
900
0
    unsigned int iCur = 0;
901
0
    j = 0;
902
0
    while (iCur < m_nReqIds)
903
0
    {
904
0
        unsigned int nToQuery = m_nReqIds - iCur;
905
0
        if (nToQuery > static_cast<unsigned int>(LIMIT_IDS_PER_REQUEST))
906
0
            nToQuery = static_cast<unsigned int>(LIMIT_IDS_PER_REQUEST);
907
908
0
        assert(nToQuery > 0);
909
0
        sqlite3_stmt *hStmt = m_pahSelectNodeStmt[nToQuery - 1];
910
0
        for (unsigned int i = iCur; i < iCur + nToQuery; i++)
911
0
        {
912
0
            sqlite3_bind_int64(hStmt, i - iCur + 1, m_panReqIds[i]);
913
0
        }
914
0
        iCur += nToQuery;
915
916
0
        while (sqlite3_step(hStmt) == SQLITE_ROW)
917
0
        {
918
0
            const GIntBig id = sqlite3_column_int64(hStmt, 0);
919
0
            const LonLat *psLonLat =
920
0
                reinterpret_cast<const LonLat *>(sqlite3_column_blob(hStmt, 1));
921
922
0
            m_panReqIds[j] = id;
923
0
            m_pasLonLatArray[j].nLon = psLonLat->nLon;
924
0
            m_pasLonLatArray[j].nLat = psLonLat->nLat;
925
0
            j++;
926
0
        }
927
928
0
        sqlite3_reset(hStmt);
929
0
    }
930
0
    m_nReqIds = j;
931
0
}
932
933
/************************************************************************/
934
/*                           DecompressSector()                         */
935
/************************************************************************/
936
937
static bool DecompressSector(const GByte *pabyIn, int nSectorSize,
938
                             GByte *pabyOut)
939
0
{
940
0
    const GByte *pabyPtr = pabyIn;
941
0
    LonLat *pasLonLatOut = reinterpret_cast<LonLat *>(pabyOut);
942
0
    int nLastLon = 0;
943
0
    int nLastLat = 0;
944
0
    bool bLastValid = false;
945
946
0
    pabyPtr += NODE_PER_SECTOR / 8;
947
0
    for (int i = 0; i < NODE_PER_SECTOR; i++)
948
0
    {
949
0
        if (pabyIn[i >> 3] & (1 << (i % 8)))
950
0
        {
951
0
            if (bLastValid)
952
0
            {
953
0
                pasLonLatOut[i].nLon =
954
0
                    static_cast<int>(nLastLon + ReadVarSInt64(&pabyPtr));
955
0
                pasLonLatOut[i].nLat =
956
0
                    static_cast<int>(nLastLat + ReadVarSInt64(&pabyPtr));
957
0
            }
958
0
            else
959
0
            {
960
0
                bLastValid = true;
961
0
                memcpy(&(pasLonLatOut[i]), pabyPtr, sizeof(LonLat));
962
0
                pabyPtr += sizeof(LonLat);
963
0
            }
964
965
0
            nLastLon = pasLonLatOut[i].nLon;
966
0
            nLastLat = pasLonLatOut[i].nLat;
967
0
        }
968
0
        else
969
0
        {
970
0
            pasLonLatOut[i].nLon = 0;
971
0
            pasLonLatOut[i].nLat = 0;
972
0
        }
973
0
    }
974
975
0
    int nRead = static_cast<int>(pabyPtr - pabyIn);
976
0
    nRead = ROUND_COMPRESS_SIZE(nRead);
977
0
    return nRead == nSectorSize;
978
0
}
979
980
/************************************************************************/
981
/*                           LookupNodesCustom()                        */
982
/************************************************************************/
983
984
void OGROSMDataSource::LookupNodesCustom()
985
0
{
986
0
    m_nReqIds = 0;
987
988
0
    if (m_nBucketOld >= 0)
989
0
    {
990
0
        if (!FlushCurrentSector())
991
0
        {
992
0
            m_bStopParsing = true;
993
0
            return;
994
0
        }
995
996
0
        m_nBucketOld = -1;
997
0
    }
998
999
0
    CPLAssert(m_nUnsortedReqIds <=
1000
0
              static_cast<unsigned int>(MAX_ACCUMULATED_NODES));
1001
1002
0
    for (unsigned int i = 0; i < m_nUnsortedReqIds; i++)
1003
0
    {
1004
0
        GIntBig id = m_panUnsortedReqIds[i];
1005
1006
0
        if (!VALID_ID_FOR_CUSTOM_INDEXING(id))
1007
0
            continue;
1008
1009
0
        int nBucket = static_cast<int>(id / NODE_PER_BUCKET);
1010
0
        int nOffInBucket = static_cast<int>(id % NODE_PER_BUCKET);
1011
0
        int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
1012
1013
0
        const auto oIter = m_oMapBuckets.find(nBucket);
1014
0
        if (oIter == m_oMapBuckets.end())
1015
0
            continue;
1016
0
        const Bucket *psBucket = &(oIter->second);
1017
1018
0
        if (m_bCompressNodes)
1019
0
        {
1020
0
            if (psBucket->u.panSectorSize == nullptr ||
1021
0
                !(psBucket->u.panSectorSize[nOffInBucketReduced]))
1022
0
                continue;
1023
0
        }
1024
0
        else
1025
0
        {
1026
0
            int nBitmapIndex = nOffInBucketReduced / 8;
1027
0
            int nBitmapRemainder = nOffInBucketReduced % 8;
1028
0
            if (psBucket->u.pabyBitmap == nullptr ||
1029
0
                !(psBucket->u.pabyBitmap[nBitmapIndex] &
1030
0
                  (1 << nBitmapRemainder)))
1031
0
                continue;
1032
0
        }
1033
1034
0
        m_panReqIds[m_nReqIds++] = id;
1035
0
    }
1036
1037
0
    std::sort(m_panReqIds, m_panReqIds + m_nReqIds);
1038
1039
    /* Remove duplicates */
1040
0
    unsigned int j = 0;  // Used after for.
1041
0
    for (unsigned int i = 0; i < m_nReqIds; i++)
1042
0
    {
1043
0
        if (!(i > 0 && m_panReqIds[i] == m_panReqIds[i - 1]))
1044
0
            m_panReqIds[j++] = m_panReqIds[i];
1045
0
    }
1046
0
    m_nReqIds = j;
1047
1048
#ifdef FAKE_LOOKUP_NODES
1049
    for (unsigned int i = 0; i < nReqIds; i++)
1050
    {
1051
        pasLonLatArray[i].nLon = 0;
1052
        pasLonLatArray[i].nLat = 0;
1053
    }
1054
#else
1055
0
    if (m_bCompressNodes)
1056
0
        LookupNodesCustomCompressedCase();
1057
0
    else
1058
0
        LookupNodesCustomNonCompressedCase();
1059
0
#endif
1060
0
}
1061
1062
/************************************************************************/
1063
/*                      LookupNodesCustomCompressedCase()               */
1064
/************************************************************************/
1065
1066
void OGROSMDataSource::LookupNodesCustomCompressedCase()
1067
0
{
1068
0
    constexpr int SECURITY_MARGIN = 8 + 8 + 2 * NODE_PER_SECTOR;
1069
0
    GByte abyRawSector[SECTOR_SIZE + SECURITY_MARGIN];
1070
0
    memset(abyRawSector + SECTOR_SIZE, 0, SECURITY_MARGIN);
1071
1072
0
    int l_nBucketOld = -1;
1073
0
    int l_nOffInBucketReducedOld = -1;
1074
0
    int k = 0;
1075
0
    int nOffFromBucketStart = 0;
1076
1077
0
    unsigned int j = 0;  // Used after for.
1078
0
    for (unsigned int i = 0; i < m_nReqIds; i++)
1079
0
    {
1080
0
        const GIntBig id = m_panReqIds[i];
1081
0
        const int nBucket = static_cast<int>(id / NODE_PER_BUCKET);
1082
0
        const int nOffInBucket = static_cast<int>(id % NODE_PER_BUCKET);
1083
0
        const int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
1084
0
        const int nOffInBucketReducedRemainder =
1085
0
            nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1);
1086
1087
0
        if (nBucket != l_nBucketOld)
1088
0
        {
1089
0
            l_nOffInBucketReducedOld = -1;
1090
0
            k = 0;
1091
0
            nOffFromBucketStart = 0;
1092
0
        }
1093
1094
0
        if (nOffInBucketReduced != l_nOffInBucketReducedOld)
1095
0
        {
1096
0
            const auto oIter = m_oMapBuckets.find(nBucket);
1097
0
            if (oIter == m_oMapBuckets.end())
1098
0
            {
1099
0
                CPLError(CE_Failure, CPLE_AppDefined,
1100
0
                         "Cannot read node " CPL_FRMT_GIB, id);
1101
0
                continue;
1102
                // FIXME ?
1103
0
            }
1104
0
            const Bucket *psBucket = &(oIter->second);
1105
0
            if (psBucket->u.panSectorSize == nullptr)
1106
0
            {
1107
0
                CPLError(CE_Failure, CPLE_AppDefined,
1108
0
                         "Cannot read node " CPL_FRMT_GIB, id);
1109
0
                continue;
1110
                // FIXME ?
1111
0
            }
1112
0
            const int nSectorSize = COMPRESS_SIZE_FROM_BYTE(
1113
0
                psBucket->u.panSectorSize[nOffInBucketReduced]);
1114
1115
            /* If we stay in the same bucket, we can reuse the previously */
1116
            /* computed offset, instead of starting from bucket start */
1117
0
            for (; k < nOffInBucketReduced; k++)
1118
0
            {
1119
0
                if (psBucket->u.panSectorSize[k])
1120
0
                    nOffFromBucketStart +=
1121
0
                        COMPRESS_SIZE_FROM_BYTE(psBucket->u.panSectorSize[k]);
1122
0
            }
1123
1124
0
            VSIFSeekL(m_fpNodes, psBucket->nOff + nOffFromBucketStart,
1125
0
                      SEEK_SET);
1126
0
            if (nSectorSize == SECTOR_SIZE)
1127
0
            {
1128
0
                if (VSIFReadL(m_pabySector, 1, static_cast<size_t>(SECTOR_SIZE),
1129
0
                              m_fpNodes) != static_cast<size_t>(SECTOR_SIZE))
1130
0
                {
1131
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1132
0
                             "Cannot read node " CPL_FRMT_GIB, id);
1133
0
                    continue;
1134
                    // FIXME ?
1135
0
                }
1136
0
            }
1137
0
            else
1138
0
            {
1139
0
                if (static_cast<int>(VSIFReadL(abyRawSector, 1, nSectorSize,
1140
0
                                               m_fpNodes)) != nSectorSize)
1141
0
                {
1142
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1143
0
                             "Cannot read sector for node " CPL_FRMT_GIB, id);
1144
0
                    continue;
1145
                    // FIXME ?
1146
0
                }
1147
0
                abyRawSector[nSectorSize] = 0;
1148
1149
0
                if (!DecompressSector(abyRawSector, nSectorSize, m_pabySector))
1150
0
                {
1151
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1152
0
                             "Error while uncompressing sector for "
1153
0
                             "node " CPL_FRMT_GIB,
1154
0
                             id);
1155
0
                    continue;
1156
                    // FIXME ?
1157
0
                }
1158
0
            }
1159
1160
0
            l_nBucketOld = nBucket;
1161
0
            l_nOffInBucketReducedOld = nOffInBucketReduced;
1162
0
        }
1163
1164
0
        m_panReqIds[j] = id;
1165
0
        memcpy(m_pasLonLatArray + j,
1166
0
               m_pabySector + nOffInBucketReducedRemainder * sizeof(LonLat),
1167
0
               sizeof(LonLat));
1168
1169
0
        if (m_pasLonLatArray[j].nLon || m_pasLonLatArray[j].nLat)
1170
0
            j++;
1171
0
    }
1172
0
    m_nReqIds = j;
1173
0
}
1174
1175
/************************************************************************/
1176
/*                    LookupNodesCustomNonCompressedCase()              */
1177
/************************************************************************/
1178
1179
void OGROSMDataSource::LookupNodesCustomNonCompressedCase()
1180
0
{
1181
0
    unsigned int j = 0;  // Used after for.
1182
1183
0
    int l_nBucketOld = -1;
1184
0
    const Bucket *psBucket = nullptr;
1185
    // To be glibc friendly, we will do reads aligned on 4096 byte offsets
1186
0
    const int knDISK_SECTOR_SIZE = 4096;
1187
0
    CPL_STATIC_ASSERT((knDISK_SECTOR_SIZE % SECTOR_SIZE) == 0);
1188
0
    GByte abyDiskSector[knDISK_SECTOR_SIZE];
1189
    // Offset in the nodes files for which abyDiskSector was read
1190
0
    GIntBig nOldOffset = -knDISK_SECTOR_SIZE - 1;
1191
    // Number of valid bytes in abyDiskSector
1192
0
    size_t nValidBytes = 0;
1193
0
    int k = 0;
1194
0
    int nSectorBase = 0;
1195
0
    for (unsigned int i = 0; i < m_nReqIds; i++)
1196
0
    {
1197
0
        const GIntBig id = m_panReqIds[i];
1198
0
        const int nBucket = static_cast<int>(id / NODE_PER_BUCKET);
1199
0
        const int nOffInBucket = static_cast<int>(id % NODE_PER_BUCKET);
1200
0
        const int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
1201
0
        const int nOffInBucketReducedRemainder =
1202
0
            nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1);
1203
1204
0
        const int nBitmapIndex = nOffInBucketReduced / 8;
1205
0
        const int nBitmapRemainder = nOffInBucketReduced % 8;
1206
1207
0
        if (psBucket == nullptr || nBucket != l_nBucketOld)
1208
0
        {
1209
0
            const auto oIter = m_oMapBuckets.find(nBucket);
1210
0
            if (oIter == m_oMapBuckets.end())
1211
0
            {
1212
0
                CPLError(CE_Failure, CPLE_AppDefined,
1213
0
                         "Cannot read node " CPL_FRMT_GIB, id);
1214
0
                continue;
1215
                // FIXME ?
1216
0
            }
1217
0
            psBucket = &(oIter->second);
1218
0
            if (psBucket->u.pabyBitmap == nullptr)
1219
0
            {
1220
0
                CPLError(CE_Failure, CPLE_AppDefined,
1221
0
                         "Cannot read node " CPL_FRMT_GIB, id);
1222
0
                continue;
1223
                // FIXME ?
1224
0
            }
1225
0
            l_nBucketOld = nBucket;
1226
0
            nOldOffset = -knDISK_SECTOR_SIZE - 1;
1227
0
            k = 0;
1228
0
            nSectorBase = 0;
1229
0
        }
1230
1231
        /* If we stay in the same bucket, we can reuse the previously */
1232
        /* computed offset, instead of starting from bucket start */
1233
0
        for (; k < nBitmapIndex; k++)
1234
0
        {
1235
0
            assert(psBucket->u.pabyBitmap);
1236
            // psBucket->u.pabyBitmap cannot be NULL
1237
            // coverity[var_deref_op]
1238
0
            nSectorBase += abyBitsCount[psBucket->u.pabyBitmap[k]];
1239
0
        }
1240
0
        int nSector = nSectorBase;
1241
0
        if (nBitmapRemainder)
1242
0
        {
1243
0
            assert(psBucket->u.pabyBitmap);
1244
0
            nSector += abyBitsCount[psBucket->u.pabyBitmap[nBitmapIndex] &
1245
0
                                    ((1 << nBitmapRemainder) - 1)];
1246
0
        }
1247
1248
0
        const GIntBig nNewOffset = psBucket->nOff + nSector * SECTOR_SIZE;
1249
0
        if (nNewOffset - nOldOffset >= knDISK_SECTOR_SIZE)
1250
0
        {
1251
            // Align on 4096 boundary to be glibc caching friendly
1252
0
            const GIntBig nAlignedNewPos =
1253
0
                nNewOffset & ~(static_cast<GIntBig>(knDISK_SECTOR_SIZE) - 1);
1254
0
            VSIFSeekL(m_fpNodes, nAlignedNewPos, SEEK_SET);
1255
0
            nValidBytes =
1256
0
                VSIFReadL(abyDiskSector, 1, knDISK_SECTOR_SIZE, m_fpNodes);
1257
0
            nOldOffset = nAlignedNewPos;
1258
0
        }
1259
1260
0
        const size_t nOffsetInDiskSector =
1261
0
            static_cast<size_t>(nNewOffset - nOldOffset) +
1262
0
            nOffInBucketReducedRemainder * sizeof(LonLat);
1263
0
        if (nValidBytes < sizeof(LonLat) ||
1264
0
            nOffsetInDiskSector > nValidBytes - sizeof(LonLat))
1265
0
        {
1266
0
            CPLError(CE_Failure, CPLE_AppDefined,
1267
0
                     "Cannot read node " CPL_FRMT_GIB, id);
1268
0
            continue;
1269
0
        }
1270
0
        memcpy(&m_pasLonLatArray[j], abyDiskSector + nOffsetInDiskSector,
1271
0
               sizeof(LonLat));
1272
1273
0
        m_panReqIds[j] = id;
1274
0
        if (m_pasLonLatArray[j].nLon || m_pasLonLatArray[j].nLat)
1275
0
            j++;
1276
0
    }
1277
0
    m_nReqIds = j;
1278
0
}
1279
1280
/************************************************************************/
1281
/*                            WriteVarInt()                             */
1282
/************************************************************************/
1283
1284
static void WriteVarInt(unsigned int nVal, std::vector<GByte> &abyData)
1285
0
{
1286
0
    while (true)
1287
0
    {
1288
0
        if ((nVal & (~0x7fU)) == 0)
1289
0
        {
1290
0
            abyData.push_back(static_cast<GByte>(nVal));
1291
0
            return;
1292
0
        }
1293
1294
0
        abyData.push_back(0x80 | static_cast<GByte>(nVal & 0x7f));
1295
0
        nVal >>= 7;
1296
0
    }
1297
0
}
1298
1299
/************************************************************************/
1300
/*                           WriteVarInt64()                            */
1301
/************************************************************************/
1302
1303
static void WriteVarInt64(GUIntBig nVal, std::vector<GByte> &abyData)
1304
0
{
1305
0
    while (true)
1306
0
    {
1307
0
        if ((static_cast<uint32_t>(nVal) & (~0x7fU)) == 0)
1308
0
        {
1309
0
            abyData.push_back(static_cast<GByte>(nVal));
1310
0
            return;
1311
0
        }
1312
1313
0
        abyData.push_back(0x80 | static_cast<GByte>(nVal & 0x7f));
1314
0
        nVal >>= 7;
1315
0
    }
1316
0
}
1317
1318
/************************************************************************/
1319
/*                           WriteVarSInt64()                           */
1320
/************************************************************************/
1321
1322
static void WriteVarSInt64(GIntBig nSVal, std::vector<GByte> &abyData)
1323
0
{
1324
0
    GIntBig nVal = nSVal >= 0 ? nSVal << 1 : ((-1 - nSVal) << 1) + 1;
1325
1326
0
    while (true)
1327
0
    {
1328
0
        if ((nVal & (~0x7f)) == 0)
1329
0
        {
1330
0
            abyData.push_back(static_cast<GByte>(nVal));
1331
0
            return;
1332
0
        }
1333
1334
0
        abyData.push_back(0x80 | static_cast<GByte>(nVal & 0x7f));
1335
0
        nVal >>= 7;
1336
0
    }
1337
0
}
1338
1339
/************************************************************************/
1340
/*                           WriteVarSInt64()                           */
1341
/************************************************************************/
1342
1343
static void WriteVarSInt64(GIntBig nSVal, GByte **ppabyData)
1344
0
{
1345
0
    GIntBig nVal = nSVal >= 0 ? nSVal << 1 : ((-1 - nSVal) << 1) + 1;
1346
1347
0
    GByte *pabyData = *ppabyData;
1348
0
    while (true)
1349
0
    {
1350
0
        if ((nVal & (~0x7f)) == 0)
1351
0
        {
1352
0
            *pabyData = static_cast<GByte>(nVal);
1353
0
            *ppabyData = pabyData + 1;
1354
0
            return;
1355
0
        }
1356
1357
0
        *pabyData = 0x80 | static_cast<GByte>(nVal & 0x7f);
1358
0
        nVal >>= 7;
1359
0
        pabyData++;
1360
0
    }
1361
0
}
1362
1363
/************************************************************************/
1364
/*                             CompressWay()                            */
1365
/************************************************************************/
1366
1367
void OGROSMDataSource::CompressWay(bool bIsArea, unsigned int nTags,
1368
                                   const IndexedKVP *pasTags, int nPoints,
1369
                                   const LonLat *pasLonLatPairs,
1370
                                   const OSMInfo *psInfo,
1371
                                   std::vector<GByte> &abyCompressedWay)
1372
0
{
1373
0
    abyCompressedWay.clear();
1374
0
    abyCompressedWay.push_back((bIsArea) ? 1 : 0);
1375
0
    CPLAssert(nTags <= MAX_COUNT_FOR_TAGS_IN_WAY);
1376
0
    abyCompressedWay.push_back(static_cast<GByte>(nTags));
1377
1378
0
    for (unsigned int iTag = 0; iTag < nTags; iTag++)
1379
0
    {
1380
0
        if (pasTags[iTag].bKIsIndex)
1381
0
        {
1382
0
            WriteVarInt(pasTags[iTag].uKey.nKeyIndex, abyCompressedWay);
1383
0
        }
1384
0
        else
1385
0
        {
1386
0
            const char *pszK =
1387
0
                reinterpret_cast<const char *>(pabyNonRedundantKeys) +
1388
0
                pasTags[iTag].uKey.nOffsetInpabyNonRedundantKeys;
1389
1390
0
            abyCompressedWay.push_back(0);
1391
1392
0
            abyCompressedWay.insert(
1393
0
                abyCompressedWay.end(), reinterpret_cast<const GByte *>(pszK),
1394
0
                reinterpret_cast<const GByte *>(pszK) + strlen(pszK) + 1);
1395
0
        }
1396
1397
0
        if (pasTags[iTag].bVIsIndex)
1398
0
        {
1399
0
            WriteVarInt(pasTags[iTag].uVal.nValueIndex, abyCompressedWay);
1400
0
        }
1401
0
        else
1402
0
        {
1403
0
            const char *pszV =
1404
0
                reinterpret_cast<const char *>(pabyNonRedundantValues) +
1405
0
                pasTags[iTag].uVal.nOffsetInpabyNonRedundantValues;
1406
1407
0
            if (pasTags[iTag].bKIsIndex)
1408
0
                abyCompressedWay.push_back(0);
1409
1410
0
            abyCompressedWay.insert(
1411
0
                abyCompressedWay.end(), reinterpret_cast<const GByte *>(pszV),
1412
0
                reinterpret_cast<const GByte *>(pszV) + strlen(pszV) + 1);
1413
0
        }
1414
0
    }
1415
1416
0
    if (m_bNeedsToSaveWayInfo)
1417
0
    {
1418
0
        if (psInfo != nullptr)
1419
0
        {
1420
0
            abyCompressedWay.push_back(1);
1421
0
            WriteVarInt64(psInfo->ts.nTimeStamp, abyCompressedWay);
1422
0
            WriteVarInt64(psInfo->nChangeset, abyCompressedWay);
1423
0
            WriteVarInt(psInfo->nVersion, abyCompressedWay);
1424
0
            WriteVarInt(psInfo->nUID, abyCompressedWay);
1425
            // FIXME : do something with pszUserSID
1426
0
        }
1427
0
        else
1428
0
        {
1429
0
            abyCompressedWay.push_back(0);
1430
0
        }
1431
0
    }
1432
1433
0
    abyCompressedWay.insert(
1434
0
        abyCompressedWay.end(),
1435
0
        reinterpret_cast<const GByte *>(&(pasLonLatPairs[0])),
1436
0
        reinterpret_cast<const GByte *>(&(pasLonLatPairs[0])) + sizeof(LonLat));
1437
0
    for (int i = 1; i < nPoints; i++)
1438
0
    {
1439
0
        GIntBig nDiff64 = static_cast<GIntBig>(pasLonLatPairs[i].nLon) -
1440
0
                          static_cast<GIntBig>(pasLonLatPairs[i - 1].nLon);
1441
0
        WriteVarSInt64(nDiff64, abyCompressedWay);
1442
1443
0
        nDiff64 = pasLonLatPairs[i].nLat - pasLonLatPairs[i - 1].nLat;
1444
0
        WriteVarSInt64(nDiff64, abyCompressedWay);
1445
0
    }
1446
0
}
1447
1448
/************************************************************************/
1449
/*                             UncompressWay()                          */
1450
/************************************************************************/
1451
1452
void OGROSMDataSource::UncompressWay(int nBytes, const GByte *pabyCompressedWay,
1453
                                     bool *pbIsArea,
1454
                                     std::vector<LonLat> &asCoords,
1455
                                     unsigned int *pnTags, OSMTag *pasTags,
1456
                                     OSMInfo *psInfo)
1457
0
{
1458
0
    asCoords.clear();
1459
0
    const GByte *pabyPtr = pabyCompressedWay;
1460
0
    if (pbIsArea)
1461
0
        *pbIsArea = (*pabyPtr == 1) ? true : false;
1462
0
    pabyPtr++;
1463
0
    unsigned int nTags = *pabyPtr;
1464
0
    pabyPtr++;
1465
1466
0
    if (pnTags)
1467
0
        *pnTags = nTags;
1468
1469
0
    assert(nTags <= MAX_COUNT_FOR_TAGS_IN_WAY);
1470
0
    for (unsigned int iTag = 0; iTag < nTags; iTag++)
1471
0
    {
1472
0
        const int nK = ReadVarInt32(&pabyPtr);
1473
0
        const GByte *pszK = nullptr;
1474
0
        if (nK == 0)
1475
0
        {
1476
0
            pszK = pabyPtr;
1477
0
            while (*pabyPtr != '\0')
1478
0
                pabyPtr++;
1479
0
            pabyPtr++;
1480
0
        }
1481
1482
0
        const int nV = nK == 0 ? 0 : ReadVarInt32(&pabyPtr);
1483
0
        const GByte *pszV = nullptr;
1484
0
        if (nV == 0)
1485
0
        {
1486
0
            pszV = pabyPtr;
1487
0
            while (*pabyPtr != '\0')
1488
0
                pabyPtr++;
1489
0
            pabyPtr++;
1490
0
        }
1491
1492
0
        if (pasTags)
1493
0
        {
1494
0
            CPLAssert(nK >= 0 && static_cast<unsigned>(nK) < m_apsKeys.size());
1495
0
            pasTags[iTag].pszK =
1496
0
                nK ? m_apsKeys[nK]->pszK : reinterpret_cast<const char *>(pszK);
1497
1498
0
            CPLAssert(nK == 0 ||
1499
0
                      (nV >= 0 && static_cast<unsigned>(nV) <
1500
0
                                      m_apsKeys[nK]->apszValues.size()));
1501
0
            pasTags[iTag].pszV = nV ? m_apsKeys[nK]->apszValues[nV]
1502
0
                                    : reinterpret_cast<const char *>(pszV);
1503
0
        }
1504
0
    }
1505
1506
0
    if (m_bNeedsToSaveWayInfo)
1507
0
    {
1508
0
        if (*pabyPtr)
1509
0
        {
1510
0
            pabyPtr++;
1511
1512
0
            OSMInfo sInfo;
1513
0
            if (psInfo == nullptr)
1514
0
                psInfo = &sInfo;
1515
1516
0
            psInfo->ts.nTimeStamp = ReadVarInt64(&pabyPtr);
1517
0
            psInfo->nChangeset = ReadVarInt64(&pabyPtr);
1518
0
            psInfo->nVersion = ReadVarInt32(&pabyPtr);
1519
0
            psInfo->nUID = ReadVarInt32(&pabyPtr);
1520
1521
0
            psInfo->bTimeStampIsStr = false;
1522
0
            psInfo->pszUserSID = "";  // FIXME
1523
0
        }
1524
0
        else
1525
0
            pabyPtr++;
1526
0
    }
1527
1528
0
    LonLat lonLat;
1529
0
    memcpy(&lonLat.nLon, pabyPtr, sizeof(int));
1530
0
    memcpy(&lonLat.nLat, pabyPtr + sizeof(int), sizeof(int));
1531
0
    asCoords.emplace_back(lonLat);
1532
0
    pabyPtr += 2 * sizeof(int);
1533
0
    do
1534
0
    {
1535
0
        lonLat.nLon = static_cast<int>(lonLat.nLon + ReadVarSInt64(&pabyPtr));
1536
0
        lonLat.nLat = static_cast<int>(lonLat.nLat + ReadVarSInt64(&pabyPtr));
1537
0
        asCoords.emplace_back(lonLat);
1538
0
    } while (pabyPtr < pabyCompressedWay + nBytes);
1539
0
}
1540
1541
/************************************************************************/
1542
/*                              IndexWay()                              */
1543
/************************************************************************/
1544
1545
void OGROSMDataSource::IndexWay(GIntBig nWayID, bool bIsArea,
1546
                                unsigned int nTags, const IndexedKVP *pasTags,
1547
                                const LonLat *pasLonLatPairs, int nPairs,
1548
                                const OSMInfo *psInfo)
1549
0
{
1550
0
    if (!m_bIndexWays)
1551
0
        return;
1552
1553
0
    sqlite3_bind_int64(m_hInsertWayStmt, 1, nWayID);
1554
1555
0
    const unsigned nTagsClamped = std::min(nTags, MAX_COUNT_FOR_TAGS_IN_WAY);
1556
0
    if (nTagsClamped < nTags)
1557
0
    {
1558
0
        CPLDebug("OSM",
1559
0
                 "Too many tags for way " CPL_FRMT_GIB ": %u. "
1560
0
                 "Clamping to %u",
1561
0
                 nWayID, nTags, nTagsClamped);
1562
0
    }
1563
0
    CompressWay(bIsArea, nTagsClamped, pasTags, nPairs, pasLonLatPairs, psInfo,
1564
0
                m_abyWayBuffer);
1565
0
    sqlite3_bind_blob(m_hInsertWayStmt, 2, m_abyWayBuffer.data(),
1566
0
                      static_cast<int>(m_abyWayBuffer.size()), SQLITE_STATIC);
1567
1568
0
    int rc = sqlite3_step(m_hInsertWayStmt);
1569
0
    sqlite3_reset(m_hInsertWayStmt);
1570
0
    if (!(rc == SQLITE_OK || rc == SQLITE_DONE))
1571
0
    {
1572
0
        CPLError(CE_Failure, CPLE_AppDefined,
1573
0
                 "Failed inserting way " CPL_FRMT_GIB ": %s", nWayID,
1574
0
                 sqlite3_errmsg(m_hDB));
1575
0
    }
1576
0
}
1577
1578
/************************************************************************/
1579
/*                              FindNode()                              */
1580
/************************************************************************/
1581
1582
int OGROSMDataSource::FindNode(GIntBig nID)
1583
0
{
1584
0
    if (m_nReqIds == 0)
1585
0
        return -1;
1586
0
    int iFirst = 0;
1587
0
    int iLast = m_nReqIds - 1;
1588
0
    while (iFirst < iLast)
1589
0
    {
1590
0
        int iMid = (iFirst + iLast) / 2;
1591
0
        if (nID > m_panReqIds[iMid])
1592
0
            iFirst = iMid + 1;
1593
0
        else
1594
0
            iLast = iMid;
1595
0
    }
1596
0
    if (iFirst == iLast && nID == m_panReqIds[iFirst])
1597
0
        return iFirst;
1598
0
    return -1;
1599
0
}
1600
1601
/************************************************************************/
1602
/*                         ProcessWaysBatch()                           */
1603
/************************************************************************/
1604
1605
void OGROSMDataSource::ProcessWaysBatch()
1606
0
{
1607
0
    if (m_asWayFeaturePairs.empty())
1608
0
        return;
1609
1610
    // printf("nodes = %d, features = %d\n", nUnsortedReqIds, int(m_asWayFeaturePairs.size()));
1611
0
    LookupNodes();
1612
1613
0
    for (WayFeaturePair &sWayFeaturePairs : m_asWayFeaturePairs)
1614
0
    {
1615
0
        const bool bIsArea = sWayFeaturePairs.bIsArea;
1616
0
        m_asLonLatCache.clear();
1617
1618
0
#ifdef ENABLE_NODE_LOOKUP_BY_HASHING
1619
0
        if (m_bHashedIndexValid)
1620
0
        {
1621
0
            for (unsigned int i = 0; i < sWayFeaturePairs.nRefs; i++)
1622
0
            {
1623
0
                int nIndInHashArray = static_cast<int>(
1624
0
                    HASH_ID_FUNC(sWayFeaturePairs.panNodeRefs[i]) %
1625
0
                    HASHED_INDEXES_ARRAY_SIZE);
1626
0
                int nIdx = m_panHashedIndexes[nIndInHashArray];
1627
0
                if (nIdx < -1)
1628
0
                {
1629
0
                    int iBucket = -nIdx - 2;
1630
0
                    while (true)
1631
0
                    {
1632
0
                        nIdx = m_psCollisionBuckets[iBucket].nInd;
1633
0
                        if (m_panReqIds[nIdx] ==
1634
0
                            sWayFeaturePairs.panNodeRefs[i])
1635
0
                            break;
1636
0
                        iBucket = m_psCollisionBuckets[iBucket].nNext;
1637
0
                        if (iBucket < 0)
1638
0
                        {
1639
0
                            nIdx = -1;
1640
0
                            break;
1641
0
                        }
1642
0
                    }
1643
0
                }
1644
0
                else if (nIdx >= 0 &&
1645
0
                         m_panReqIds[nIdx] != sWayFeaturePairs.panNodeRefs[i])
1646
0
                    nIdx = -1;
1647
1648
0
                if (nIdx >= 0)
1649
0
                {
1650
0
                    m_asLonLatCache.push_back(m_pasLonLatArray[nIdx]);
1651
0
                }
1652
0
            }
1653
0
        }
1654
0
        else
1655
0
#endif  // ENABLE_NODE_LOOKUP_BY_HASHING
1656
0
        {
1657
0
            int nIdx = -1;
1658
0
            for (unsigned int i = 0; i < sWayFeaturePairs.nRefs; i++)
1659
0
            {
1660
0
                if (nIdx >= 0 && sWayFeaturePairs.panNodeRefs[i] ==
1661
0
                                     sWayFeaturePairs.panNodeRefs[i - 1] + 1)
1662
0
                {
1663
0
                    if (static_cast<unsigned>(nIdx + 1) < m_nReqIds &&
1664
0
                        m_panReqIds[nIdx + 1] ==
1665
0
                            sWayFeaturePairs.panNodeRefs[i])
1666
0
                        nIdx++;
1667
0
                    else
1668
0
                        nIdx = -1;
1669
0
                }
1670
0
                else
1671
0
                    nIdx = FindNode(sWayFeaturePairs.panNodeRefs[i]);
1672
0
                if (nIdx >= 0)
1673
0
                {
1674
0
                    m_asLonLatCache.push_back(m_pasLonLatArray[nIdx]);
1675
0
                }
1676
0
            }
1677
0
        }
1678
1679
0
        if (!m_asLonLatCache.empty() && bIsArea)
1680
0
        {
1681
0
            m_asLonLatCache.push_back(m_asLonLatCache[0]);
1682
0
        }
1683
1684
0
        if (m_asLonLatCache.size() < 2)
1685
0
        {
1686
0
            CPLDebug("OSM",
1687
0
                     "Way " CPL_FRMT_GIB
1688
0
                     " with %d nodes that could be found. Discarding it",
1689
0
                     sWayFeaturePairs.nWayID,
1690
0
                     static_cast<int>(m_asLonLatCache.size()));
1691
0
            sWayFeaturePairs.poFeature.reset();
1692
0
            sWayFeaturePairs.bIsArea = false;
1693
0
            continue;
1694
0
        }
1695
1696
0
        if (bIsArea && m_apoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested())
1697
0
        {
1698
0
            IndexWay(sWayFeaturePairs.nWayID, /*bIsArea = */ true,
1699
0
                     sWayFeaturePairs.nTags, sWayFeaturePairs.pasTags,
1700
0
                     m_asLonLatCache.data(),
1701
0
                     static_cast<int>(m_asLonLatCache.size()),
1702
0
                     &sWayFeaturePairs.sInfo);
1703
0
        }
1704
0
        else
1705
0
            IndexWay(sWayFeaturePairs.nWayID, bIsArea, 0, nullptr,
1706
0
                     m_asLonLatCache.data(),
1707
0
                     static_cast<int>(m_asLonLatCache.size()), nullptr);
1708
1709
0
        if (sWayFeaturePairs.poFeature == nullptr)
1710
0
        {
1711
0
            continue;
1712
0
        }
1713
1714
0
        OGRLineString *poLS = new OGRLineString();
1715
0
        OGRGeometry *poGeom = poLS;
1716
1717
0
        const int nPoints = static_cast<int>(m_asLonLatCache.size());
1718
0
        poLS->setNumPoints(nPoints, /*bZeroizeNewContent=*/false);
1719
0
        for (int i = 0; i < nPoints; i++)
1720
0
        {
1721
0
            poLS->setPoint(i, INT_TO_DBL(m_asLonLatCache[i].nLon),
1722
0
                           INT_TO_DBL(m_asLonLatCache[i].nLat));
1723
0
        }
1724
1725
0
        sWayFeaturePairs.poFeature->SetGeometryDirectly(poGeom);
1726
1727
0
        if (m_asLonLatCache.size() != sWayFeaturePairs.nRefs)
1728
0
            CPLDebug("OSM",
1729
0
                     "For way " CPL_FRMT_GIB
1730
0
                     ", got only %d nodes instead of %d",
1731
0
                     sWayFeaturePairs.nWayID, nPoints, sWayFeaturePairs.nRefs);
1732
1733
0
        bool bFilteredOut = false;
1734
0
        if (!m_apoLayers[IDX_LYR_LINES]->AddFeature(
1735
0
                std::move(sWayFeaturePairs.poFeature),
1736
0
                sWayFeaturePairs.bAttrFilterAlreadyEvaluated, &bFilteredOut,
1737
0
                !m_bFeatureAdded))
1738
0
            m_bStopParsing = true;
1739
0
        else if (!bFilteredOut)
1740
0
            m_bFeatureAdded = true;
1741
0
    }
1742
1743
0
    if (m_apoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested())
1744
0
    {
1745
0
        for (WayFeaturePair &sWayFeaturePairs : m_asWayFeaturePairs)
1746
0
        {
1747
0
            if (sWayFeaturePairs.bIsArea &&
1748
0
                (sWayFeaturePairs.nTags || m_bReportAllWays))
1749
0
            {
1750
0
                sqlite3_bind_int64(m_hInsertPolygonsStandaloneStmt, 1,
1751
0
                                   sWayFeaturePairs.nWayID);
1752
1753
0
                int rc = sqlite3_step(m_hInsertPolygonsStandaloneStmt);
1754
0
                sqlite3_reset(m_hInsertPolygonsStandaloneStmt);
1755
0
                if (!(rc == SQLITE_OK || rc == SQLITE_DONE))
1756
0
                {
1757
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1758
0
                             "Failed inserting into "
1759
0
                             "polygons_standalone " CPL_FRMT_GIB ": %s",
1760
0
                             sWayFeaturePairs.nWayID, sqlite3_errmsg(m_hDB));
1761
0
                }
1762
0
            }
1763
0
        }
1764
0
    }
1765
1766
0
    m_asWayFeaturePairs.clear();
1767
0
    m_nUnsortedReqIds = 0;
1768
1769
0
    m_nAccumulatedTags = 0;
1770
0
    nNonRedundantKeysLen = 0;
1771
0
    nNonRedundantValuesLen = 0;
1772
0
}
1773
1774
/************************************************************************/
1775
/*                      IsClosedWayTaggedAsPolygon()                    */
1776
/************************************************************************/
1777
1778
bool OGROSMDataSource::IsClosedWayTaggedAsPolygon(unsigned int nTags,
1779
                                                  const OSMTag *pasTags)
1780
0
{
1781
0
    bool bIsArea = false;
1782
0
    const int nSizeArea = 4;
1783
0
    const int nStrnlenK =
1784
0
        std::max(nSizeArea, m_nMaxSizeKeysInSetClosedWaysArePolygons) + 1;
1785
0
    std::string oTmpStr;
1786
0
    oTmpStr.reserve(m_nMaxSizeKeysInSetClosedWaysArePolygons);
1787
0
    for (unsigned int i = 0; i < nTags; i++)
1788
0
    {
1789
0
        const char *pszK = pasTags[i].pszK;
1790
0
        const int nKLen = static_cast<int>(CPLStrnlen(pszK, nStrnlenK));
1791
0
        if (nKLen > m_nMaxSizeKeysInSetClosedWaysArePolygons)
1792
0
            continue;
1793
1794
0
        if (nKLen == nSizeArea && strcmp(pszK, "area") == 0)
1795
0
        {
1796
0
            const char *pszV = pasTags[i].pszV;
1797
0
            if (strcmp(pszV, "yes") == 0)
1798
0
            {
1799
0
                bIsArea = true;
1800
                // final true. We can't have several area tags...
1801
0
                break;
1802
0
            }
1803
0
            else if (strcmp(pszV, "no") == 0)
1804
0
            {
1805
0
                bIsArea = false;
1806
0
                break;
1807
0
            }
1808
0
        }
1809
0
        if (bIsArea)
1810
0
            continue;
1811
1812
0
        if (nKLen >= m_nMinSizeKeysInSetClosedWaysArePolygons)
1813
0
        {
1814
0
            oTmpStr.assign(pszK, nKLen);
1815
0
            if (aoSetClosedWaysArePolygons.find(oTmpStr) !=
1816
0
                aoSetClosedWaysArePolygons.end())
1817
0
            {
1818
0
                bIsArea = true;
1819
0
                continue;
1820
0
            }
1821
0
        }
1822
1823
0
        const char *pszV = pasTags[i].pszV;
1824
0
        const int nVLen = static_cast<int>(CPLStrnlen(pszV, nStrnlenK));
1825
0
        if (nKLen + 1 + nVLen >= m_nMinSizeKeysInSetClosedWaysArePolygons &&
1826
0
            nKLen + 1 + nVLen <= m_nMaxSizeKeysInSetClosedWaysArePolygons)
1827
0
        {
1828
0
            oTmpStr.assign(pszK, nKLen);
1829
0
            oTmpStr.append(1, '=');
1830
0
            oTmpStr.append(pszV, nVLen);
1831
0
            if (aoSetClosedWaysArePolygons.find(oTmpStr) !=
1832
0
                aoSetClosedWaysArePolygons.end())
1833
0
            {
1834
0
                bIsArea = true;
1835
0
                continue;
1836
0
            }
1837
0
        }
1838
0
    }
1839
0
    return bIsArea;
1840
0
}
1841
1842
/************************************************************************/
1843
/*                              NotifyWay()                             */
1844
/************************************************************************/
1845
1846
void OGROSMDataSource::NotifyWay(const OSMWay *psWay)
1847
0
{
1848
0
    m_nWaysProcessed++;
1849
0
    if (m_nWaysProcessed % 10000 == 0)
1850
0
    {
1851
0
        CPLDebug("OSM", "Ways processed : %d", m_nWaysProcessed);
1852
#ifdef DEBUG_MEM_USAGE
1853
        CPLDebug("OSM", "GetMaxTotalAllocs() = " CPL_FRMT_GUIB,
1854
                 static_cast<GUIntBig>(GetMaxTotalAllocs()));
1855
#endif
1856
0
    }
1857
1858
0
    if (!m_bUsePointsIndex)
1859
0
        return;
1860
1861
    // printf("way %d : %d nodes\n", (int)psWay->nID, (int)psWay->nRefs);
1862
1863
0
    if (psWay->nRefs < 2)
1864
0
    {
1865
0
        CPLDebug("OSM", "Way " CPL_FRMT_GIB " with %d nodes. Discarding it",
1866
0
                 psWay->nID, psWay->nRefs);
1867
0
        return;
1868
0
    }
1869
1870
    /* Is a closed way a polygon ? */
1871
0
    bool bIsArea = false;
1872
0
    if (psWay->panNodeRefs[0] == psWay->panNodeRefs[psWay->nRefs - 1])
1873
0
    {
1874
0
        bIsArea = IsClosedWayTaggedAsPolygon(psWay->nTags, psWay->pasTags);
1875
0
    }
1876
1877
0
    bool bInterestingTag = m_bReportAllWays;
1878
0
    if (!bIsArea && !m_bReportAllWays)
1879
0
    {
1880
0
        for (unsigned int i = 0; i < psWay->nTags; i++)
1881
0
        {
1882
0
            const char *pszK = psWay->pasTags[i].pszK;
1883
0
            if (m_apoLayers[IDX_LYR_LINES]->IsSignificantKey(pszK))
1884
0
            {
1885
0
                bInterestingTag = true;
1886
0
                break;
1887
0
            }
1888
0
        }
1889
0
    }
1890
1891
0
    std::unique_ptr<OGRFeature> poFeature;
1892
0
    bool bAttrFilterAlreadyEvaluated = false;
1893
0
    if (!bIsArea && m_apoLayers[IDX_LYR_LINES]->IsUserInterested() &&
1894
0
        bInterestingTag)
1895
0
    {
1896
0
        poFeature = std::make_unique<OGRFeature>(
1897
0
            m_apoLayers[IDX_LYR_LINES]->GetLayerDefn());
1898
1899
0
        m_apoLayers[IDX_LYR_LINES]->SetFieldsFromTags(
1900
0
            poFeature.get(), psWay->nID, false, psWay->nTags, psWay->pasTags,
1901
0
            &psWay->sInfo);
1902
1903
        // Optimization: if we have an attribute filter, that does not require
1904
        // geometry, and if we don't need to index ways, then we can just
1905
        // evaluate the attribute filter without the geometry.
1906
0
        if (m_apoLayers[IDX_LYR_LINES]->HasAttributeFilter() &&
1907
0
            !m_apoLayers[IDX_LYR_LINES]
1908
0
                 ->AttributeFilterEvaluationNeedsGeometry() &&
1909
0
            !m_bIndexWays)
1910
0
        {
1911
0
            if (!m_apoLayers[IDX_LYR_LINES]->EvaluateAttributeFilter(
1912
0
                    poFeature.get()))
1913
0
            {
1914
0
                return;
1915
0
            }
1916
0
            bAttrFilterAlreadyEvaluated = true;
1917
0
        }
1918
0
    }
1919
0
    else if (!m_bIndexWays)
1920
0
    {
1921
0
        return;
1922
0
    }
1923
1924
0
    if (m_nUnsortedReqIds + psWay->nRefs >
1925
0
            static_cast<unsigned int>(MAX_ACCUMULATED_NODES) ||
1926
0
        m_asWayFeaturePairs.size() ==
1927
0
            static_cast<size_t>(MAX_DELAYED_FEATURES) ||
1928
0
        m_nAccumulatedTags + psWay->nTags >
1929
0
            static_cast<unsigned int>(MAX_ACCUMULATED_TAGS) ||
1930
0
        nNonRedundantKeysLen + 1024 > MAX_NON_REDUNDANT_KEYS ||
1931
0
        nNonRedundantValuesLen + 1024 > MAX_NON_REDUNDANT_VALUES)
1932
0
    {
1933
0
        ProcessWaysBatch();
1934
0
    }
1935
1936
0
    m_asWayFeaturePairs.push_back(WayFeaturePair());
1937
0
    WayFeaturePair &sWayFeaturePairs = m_asWayFeaturePairs.back();
1938
1939
0
    sWayFeaturePairs.nWayID = psWay->nID;
1940
0
    sWayFeaturePairs.nRefs = psWay->nRefs - (bIsArea ? 1 : 0);
1941
0
    sWayFeaturePairs.panNodeRefs = m_panUnsortedReqIds + m_nUnsortedReqIds;
1942
0
    sWayFeaturePairs.poFeature = std::move(poFeature);
1943
0
    sWayFeaturePairs.bIsArea = bIsArea;
1944
0
    sWayFeaturePairs.bAttrFilterAlreadyEvaluated = bAttrFilterAlreadyEvaluated;
1945
1946
0
    if (bIsArea && m_apoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested())
1947
0
    {
1948
0
        unsigned int nTagCount = 0;
1949
1950
0
        if (m_bNeedsToSaveWayInfo)
1951
0
        {
1952
0
            if (!psWay->sInfo.bTimeStampIsStr)
1953
0
                sWayFeaturePairs.sInfo.ts.nTimeStamp =
1954
0
                    psWay->sInfo.ts.nTimeStamp;
1955
0
            else
1956
0
            {
1957
0
                OGRField sField;
1958
0
                if (OGRParseXMLDateTime(psWay->sInfo.ts.pszTimeStamp, &sField))
1959
0
                {
1960
0
                    struct tm brokendown;
1961
0
                    memset(&brokendown, 0, sizeof(brokendown));
1962
0
                    brokendown.tm_year = sField.Date.Year - 1900;
1963
0
                    brokendown.tm_mon = sField.Date.Month - 1;
1964
0
                    brokendown.tm_mday = sField.Date.Day;
1965
0
                    brokendown.tm_hour = sField.Date.Hour;
1966
0
                    brokendown.tm_min = sField.Date.Minute;
1967
0
                    brokendown.tm_sec =
1968
0
                        static_cast<int>(sField.Date.Second + .5);
1969
0
                    sWayFeaturePairs.sInfo.ts.nTimeStamp =
1970
0
                        CPLYMDHMSToUnixTime(&brokendown);
1971
0
                }
1972
0
                else
1973
0
                    sWayFeaturePairs.sInfo.ts.nTimeStamp = 0;
1974
0
            }
1975
0
            sWayFeaturePairs.sInfo.nChangeset = psWay->sInfo.nChangeset;
1976
0
            sWayFeaturePairs.sInfo.nVersion = psWay->sInfo.nVersion;
1977
0
            sWayFeaturePairs.sInfo.nUID = psWay->sInfo.nUID;
1978
0
            sWayFeaturePairs.sInfo.bTimeStampIsStr = false;
1979
0
            sWayFeaturePairs.sInfo.pszUserSID = "";  // FIXME
1980
0
        }
1981
0
        else
1982
0
        {
1983
0
            sWayFeaturePairs.sInfo.ts.nTimeStamp = 0;
1984
0
            sWayFeaturePairs.sInfo.nChangeset = 0;
1985
0
            sWayFeaturePairs.sInfo.nVersion = 0;
1986
0
            sWayFeaturePairs.sInfo.nUID = 0;
1987
0
            sWayFeaturePairs.sInfo.bTimeStampIsStr = false;
1988
0
            sWayFeaturePairs.sInfo.pszUserSID = "";
1989
0
        }
1990
1991
0
        sWayFeaturePairs.pasTags = m_pasAccumulatedTags + m_nAccumulatedTags;
1992
1993
0
        for (unsigned int iTag = 0; iTag < psWay->nTags; iTag++)
1994
0
        {
1995
0
            const char *pszK = psWay->pasTags[iTag].pszK;
1996
0
            const char *pszV = psWay->pasTags[iTag].pszV;
1997
1998
0
            if (std::any_of(begin(m_ignoredKeys), end(m_ignoredKeys),
1999
0
                            [pszK](const char *pszIgnoredKey)
2000
0
                            { return strcmp(pszK, pszIgnoredKey) == 0; }))
2001
0
            {
2002
0
                continue;
2003
0
            }
2004
2005
0
            auto oIterK = m_aoMapIndexedKeys.find(pszK);
2006
0
            KeyDesc *psKD = nullptr;
2007
0
            if (oIterK == m_aoMapIndexedKeys.end())
2008
0
            {
2009
0
                if (m_apsKeys.size() >= 1 + MAX_INDEXED_KEYS)
2010
0
                {
2011
0
                    if (m_apsKeys.size() == 1 + MAX_INDEXED_KEYS)
2012
0
                    {
2013
0
                        CPLDebug("OSM", "More than %d different keys found",
2014
0
                                 MAX_INDEXED_KEYS);
2015
                        // To avoid next warnings.
2016
0
                        m_apsKeys.push_back(nullptr);
2017
0
                    }
2018
2019
0
                    const int nLenK = static_cast<int>(strlen(pszK)) + 1;
2020
0
                    if (nNonRedundantKeysLen + nLenK > MAX_NON_REDUNDANT_KEYS)
2021
0
                    {
2022
0
                        CPLError(CE_Failure, CPLE_AppDefined,
2023
0
                                 "Too many/too long keys found");
2024
0
                        continue;
2025
0
                    }
2026
0
                    memcpy(pabyNonRedundantKeys + nNonRedundantKeysLen, pszK,
2027
0
                           nLenK);
2028
0
                    m_pasAccumulatedTags[m_nAccumulatedTags].bKIsIndex = FALSE;
2029
0
                    m_pasAccumulatedTags[m_nAccumulatedTags]
2030
0
                        .uKey.nOffsetInpabyNonRedundantKeys =
2031
0
                        nNonRedundantKeysLen;
2032
0
                    nNonRedundantKeysLen += nLenK;
2033
0
                }
2034
0
                else
2035
0
                {
2036
0
                    psKD = new KeyDesc();
2037
0
                    psKD->pszK = CPLStrdup(pszK);
2038
0
                    psKD->nKeyIndex = static_cast<int>(m_apsKeys.size());
2039
0
                    psKD->nOccurrences = 0;
2040
0
                    psKD->apszValues.push_back(CPLStrdup(
2041
0
                        ""));  // guard value to avoid index 0 to be used
2042
0
                    m_aoMapIndexedKeys[psKD->pszK] = psKD;
2043
0
                    m_apsKeys.push_back(psKD);
2044
0
                }
2045
0
            }
2046
0
            else
2047
0
            {
2048
0
                psKD = oIterK->second;
2049
0
            }
2050
2051
0
            if (psKD)
2052
0
            {
2053
0
                psKD->nOccurrences++;
2054
0
                m_pasAccumulatedTags[m_nAccumulatedTags].bKIsIndex = TRUE;
2055
0
                m_pasAccumulatedTags[m_nAccumulatedTags].uKey.nKeyIndex =
2056
0
                    psKD->nKeyIndex;
2057
0
            }
2058
2059
0
            if (psKD != nullptr &&
2060
0
                psKD->apszValues.size() < 1 + MAX_INDEXED_VALUES_PER_KEY)
2061
0
            {
2062
0
                int nValueIndex = 0;
2063
0
                auto oIterV = psKD->anMapV.find(pszV);
2064
0
                if (oIterV == psKD->anMapV.end())
2065
0
                {
2066
0
                    char *pszVDup = CPLStrdup(pszV);
2067
0
                    nValueIndex = static_cast<int>(psKD->apszValues.size());
2068
0
                    psKD->anMapV[pszVDup] = nValueIndex;
2069
0
                    psKD->apszValues.push_back(pszVDup);
2070
0
                }
2071
0
                else
2072
0
                    nValueIndex = oIterV->second;
2073
2074
0
                m_pasAccumulatedTags[m_nAccumulatedTags].bVIsIndex = TRUE;
2075
0
                m_pasAccumulatedTags[m_nAccumulatedTags].uVal.nValueIndex =
2076
0
                    nValueIndex;
2077
0
            }
2078
0
            else
2079
0
            {
2080
0
                const int nLenV = static_cast<int>(strlen(pszV)) + 1;
2081
2082
0
                if (psKD != nullptr &&
2083
0
                    psKD->apszValues.size() == 1 + MAX_INDEXED_VALUES_PER_KEY)
2084
0
                {
2085
0
                    CPLDebug("OSM", "More than %d different values for tag %s",
2086
0
                             MAX_INDEXED_VALUES_PER_KEY, pszK);
2087
                    // To avoid next warnings.
2088
0
                    psKD->apszValues.push_back(CPLStrdup(""));
2089
0
                }
2090
2091
0
                if (nNonRedundantValuesLen + nLenV > MAX_NON_REDUNDANT_VALUES)
2092
0
                {
2093
0
                    CPLError(CE_Failure, CPLE_AppDefined,
2094
0
                             "Too many/too long values found");
2095
0
                    continue;
2096
0
                }
2097
0
                memcpy(pabyNonRedundantValues + nNonRedundantValuesLen, pszV,
2098
0
                       nLenV);
2099
0
                m_pasAccumulatedTags[m_nAccumulatedTags].bVIsIndex = FALSE;
2100
0
                m_pasAccumulatedTags[m_nAccumulatedTags]
2101
0
                    .uVal.nOffsetInpabyNonRedundantValues =
2102
0
                    nNonRedundantValuesLen;
2103
0
                nNonRedundantValuesLen += nLenV;
2104
0
            }
2105
0
            m_nAccumulatedTags++;
2106
2107
0
            nTagCount++;
2108
0
            if (nTagCount == MAX_COUNT_FOR_TAGS_IN_WAY)
2109
0
                break;
2110
0
        }
2111
2112
0
        sWayFeaturePairs.nTags = nTagCount;
2113
0
    }
2114
0
    else
2115
0
    {
2116
0
        sWayFeaturePairs.sInfo.ts.nTimeStamp = 0;
2117
0
        sWayFeaturePairs.sInfo.nChangeset = 0;
2118
0
        sWayFeaturePairs.sInfo.nVersion = 0;
2119
0
        sWayFeaturePairs.sInfo.nUID = 0;
2120
0
        sWayFeaturePairs.sInfo.bTimeStampIsStr = false;
2121
0
        sWayFeaturePairs.sInfo.pszUserSID = "";
2122
2123
0
        sWayFeaturePairs.nTags = 0;
2124
0
        sWayFeaturePairs.pasTags = nullptr;
2125
0
    }
2126
2127
0
    memcpy(m_panUnsortedReqIds + m_nUnsortedReqIds, psWay->panNodeRefs,
2128
0
           sizeof(GIntBig) * (psWay->nRefs - (bIsArea ? 1 : 0)));
2129
0
    m_nUnsortedReqIds += (psWay->nRefs - (bIsArea ? 1 : 0));
2130
0
}
2131
2132
static void OGROSMNotifyWay(OSMWay *psWay, OSMContext * /* psOSMContext */,
2133
                            void *user_data)
2134
0
{
2135
0
    static_cast<OGROSMDataSource *>(user_data)->NotifyWay(psWay);
2136
0
}
2137
2138
/************************************************************************/
2139
/*                            LookupWays()                              */
2140
/************************************************************************/
2141
2142
unsigned int OGROSMDataSource::LookupWays(
2143
    std::map<GIntBig, std::pair<int, void *>> &aoMapWays,
2144
    const OSMRelation *psRelation)
2145
0
{
2146
0
    unsigned int nFound = 0;
2147
0
    unsigned int iCur = 0;
2148
2149
0
    while (iCur < psRelation->nMembers)
2150
0
    {
2151
0
        unsigned int nToQuery = 0;
2152
0
        unsigned int i = iCur;  // Used after for.
2153
0
        for (; i < psRelation->nMembers; i++)
2154
0
        {
2155
0
            if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
2156
0
                strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
2157
0
            {
2158
0
                nToQuery++;
2159
0
                if (nToQuery ==
2160
0
                    static_cast<unsigned int>(LIMIT_IDS_PER_REQUEST))
2161
0
                {
2162
0
                    break;
2163
0
                }
2164
0
            }
2165
0
        }
2166
2167
0
        if (nToQuery == 0)
2168
0
            break;
2169
2170
0
        unsigned int iLastI = (i == psRelation->nMembers) ? i : i + 1;
2171
2172
0
        sqlite3_stmt *hStmt = m_pahSelectWayStmt[nToQuery - 1];
2173
0
        unsigned int nBindIndex = 1;
2174
0
        for (i = iCur; i < iLastI; i++)
2175
0
        {
2176
0
            if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
2177
0
                strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
2178
0
            {
2179
0
                sqlite3_bind_int64(hStmt, nBindIndex,
2180
0
                                   psRelation->pasMembers[i].nID);
2181
0
                nBindIndex++;
2182
0
            }
2183
0
        }
2184
0
        iCur = iLastI;
2185
2186
0
        while (sqlite3_step(hStmt) == SQLITE_ROW)
2187
0
        {
2188
0
            GIntBig id = sqlite3_column_int64(hStmt, 0);
2189
0
            if (aoMapWays.find(id) == aoMapWays.end())
2190
0
            {
2191
0
                int nBlobSize = sqlite3_column_bytes(hStmt, 1);
2192
0
                const void *blob = sqlite3_column_blob(hStmt, 1);
2193
0
                void *blob_dup = CPLMalloc(nBlobSize);
2194
0
                memcpy(blob_dup, blob, nBlobSize);
2195
0
                aoMapWays[id] = std::pair(nBlobSize, blob_dup);
2196
0
            }
2197
0
            nFound++;
2198
0
        }
2199
2200
0
        sqlite3_reset(hStmt);
2201
0
    }
2202
2203
0
    return nFound;
2204
0
}
2205
2206
/************************************************************************/
2207
/*                          BuildMultiPolygon()                         */
2208
/************************************************************************/
2209
2210
OGRGeometry *OGROSMDataSource::BuildMultiPolygon(const OSMRelation *psRelation,
2211
                                                 unsigned int *pnTags,
2212
                                                 OSMTag *pasTags)
2213
0
{
2214
0
    std::map<GIntBig, std::pair<int, void *>> aoMapWays;
2215
0
    LookupWays(aoMapWays, psRelation);
2216
2217
0
    bool bMissing = false;
2218
2219
0
    for (unsigned int i = 0; i < psRelation->nMembers; i++)
2220
0
    {
2221
0
        if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
2222
0
            strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
2223
0
        {
2224
0
            if (aoMapWays.find(psRelation->pasMembers[i].nID) ==
2225
0
                aoMapWays.end())
2226
0
            {
2227
0
                CPLDebug("OSM",
2228
0
                         "Relation " CPL_FRMT_GIB
2229
0
                         " has missing ways. Ignoring it",
2230
0
                         psRelation->nID);
2231
0
                bMissing = true;
2232
0
                break;
2233
0
            }
2234
0
        }
2235
0
    }
2236
2237
0
    if (bMissing)
2238
0
    {
2239
        // cppcheck-suppress constVariableReference
2240
0
        for (auto &oIter : aoMapWays)
2241
0
            CPLFree(oIter.second.second);
2242
2243
0
        return nullptr;
2244
0
    }
2245
2246
0
    OGRMultiLineString oMLS;
2247
0
    std::vector<OGRGeometry *> apoPolygons(psRelation->nMembers);
2248
0
    int nPolys = 0;
2249
2250
0
    if (pnTags != nullptr)
2251
0
        *pnTags = 0;
2252
2253
0
    for (unsigned int i = 0; i < psRelation->nMembers; i++)
2254
0
    {
2255
0
        if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
2256
0
            strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
2257
0
        {
2258
0
            const auto &oGeom = aoMapWays[psRelation->pasMembers[i].nID];
2259
2260
0
            if (pnTags != nullptr && *pnTags == 0 &&
2261
0
                strcmp(psRelation->pasMembers[i].pszRole, "outer") == 0)
2262
0
            {
2263
                // This backup in m_abyWayBuffer is crucial for safe memory
2264
                // usage, as pasTags[].pszV will point to it !
2265
0
                m_abyWayBuffer.clear();
2266
0
                m_abyWayBuffer.insert(m_abyWayBuffer.end(),
2267
0
                                      static_cast<const GByte *>(oGeom.second),
2268
0
                                      static_cast<const GByte *>(oGeom.second) +
2269
0
                                          oGeom.first);
2270
2271
0
                UncompressWay(oGeom.first, m_abyWayBuffer.data(), nullptr,
2272
0
                              m_asLonLatCache, pnTags, pasTags, nullptr);
2273
0
            }
2274
0
            else
2275
0
            {
2276
0
                UncompressWay(oGeom.first,
2277
0
                              static_cast<const GByte *>(oGeom.second), nullptr,
2278
0
                              m_asLonLatCache, nullptr, nullptr, nullptr);
2279
0
            }
2280
2281
0
            OGRLineString *poLS = nullptr;
2282
2283
0
            if (!m_asLonLatCache.empty() &&
2284
0
                m_asLonLatCache.front().nLon == m_asLonLatCache.back().nLon &&
2285
0
                m_asLonLatCache.front().nLat == m_asLonLatCache.back().nLat)
2286
0
            {
2287
0
                OGRPolygon *poPoly = new OGRPolygon();
2288
0
                OGRLinearRing *poRing = new OGRLinearRing();
2289
0
                poPoly->addRingDirectly(poRing);
2290
0
                apoPolygons[nPolys++] = poPoly;
2291
0
                poLS = poRing;
2292
2293
0
                if (strcmp(psRelation->pasMembers[i].pszRole, "outer") == 0)
2294
0
                {
2295
0
                    sqlite3_bind_int64(m_hDeletePolygonsStandaloneStmt, 1,
2296
0
                                       psRelation->pasMembers[i].nID);
2297
0
                    CPL_IGNORE_RET_VAL(
2298
0
                        sqlite3_step(m_hDeletePolygonsStandaloneStmt));
2299
0
                    sqlite3_reset(m_hDeletePolygonsStandaloneStmt);
2300
0
                }
2301
0
            }
2302
0
            else
2303
0
            {
2304
0
                poLS = new OGRLineString();
2305
0
                oMLS.addGeometryDirectly(poLS);
2306
0
            }
2307
2308
0
            const int nPoints = static_cast<int>(m_asLonLatCache.size());
2309
0
            poLS->setNumPoints(nPoints, /*bZeroizeNewContent=*/false);
2310
0
            for (int j = 0; j < nPoints; j++)
2311
0
            {
2312
0
                poLS->setPoint(j, INT_TO_DBL(m_asLonLatCache[j].nLon),
2313
0
                               INT_TO_DBL(m_asLonLatCache[j].nLat));
2314
0
            }
2315
0
        }
2316
0
    }
2317
2318
0
    if (oMLS.getNumGeometries() > 0)
2319
0
    {
2320
0
        auto poPolyFromEdges = std::unique_ptr<OGRGeometry>(
2321
0
            OGRGeometry::FromHandle(OGRBuildPolygonFromEdges(
2322
0
                OGRGeometry::ToHandle(&oMLS), TRUE, FALSE, 0, nullptr)));
2323
0
        if (poPolyFromEdges && poPolyFromEdges->getGeometryType() == wkbPolygon)
2324
0
        {
2325
0
            const OGRPolygon *poSuperPoly = poPolyFromEdges->toPolygon();
2326
0
            for (const OGRLinearRing *poRing : *poSuperPoly)
2327
0
            {
2328
0
                if (poRing != nullptr && poRing->getNumPoints() >= 4 &&
2329
0
                    poRing->getX(0) ==
2330
0
                        poRing->getX(poRing->getNumPoints() - 1) &&
2331
0
                    poRing->getY(0) == poRing->getY(poRing->getNumPoints() - 1))
2332
0
                {
2333
0
                    OGRPolygon *poPoly = new OGRPolygon();
2334
0
                    poPoly->addRing(poRing);
2335
0
                    apoPolygons[nPolys++] = poPoly;
2336
0
                }
2337
0
            }
2338
0
        }
2339
0
    }
2340
2341
0
    std::unique_ptr<OGRGeometry> poRet;
2342
2343
0
    if (nPolys > 0)
2344
0
    {
2345
0
        int bIsValidGeometry = FALSE;
2346
0
        const char *apszOptions[2] = {"METHOD=DEFAULT", nullptr};
2347
0
        auto poGeom =
2348
0
            std::unique_ptr<OGRGeometry>(OGRGeometryFactory::organizePolygons(
2349
0
                apoPolygons.data(), nPolys, &bIsValidGeometry, apszOptions));
2350
2351
0
        if (poGeom && poGeom->getGeometryType() == wkbPolygon)
2352
0
        {
2353
0
            auto poMulti = std::make_unique<OGRMultiPolygon>();
2354
0
            poMulti->addGeometryDirectly(poGeom.release());
2355
0
            poGeom = std::move(poMulti);
2356
0
        }
2357
2358
0
        if (poGeom && poGeom->getGeometryType() == wkbMultiPolygon)
2359
0
        {
2360
0
            poRet = std::move(poGeom);
2361
0
        }
2362
0
        else
2363
0
        {
2364
0
            CPLDebug("OSM",
2365
0
                     "Relation " CPL_FRMT_GIB
2366
0
                     ": Geometry has incompatible type : %s",
2367
0
                     psRelation->nID,
2368
0
                     poGeom ? OGR_G_GetGeometryName(
2369
0
                                  OGRGeometry::ToHandle(poGeom.get()))
2370
0
                            : "null");
2371
0
        }
2372
0
    }
2373
2374
    // cppcheck-suppress constVariableReference
2375
0
    for (auto &oIter : aoMapWays)
2376
0
        CPLFree(oIter.second.second);
2377
2378
0
    return poRet.release();
2379
0
}
2380
2381
/************************************************************************/
2382
/*                          BuildGeometryCollection()                   */
2383
/************************************************************************/
2384
2385
OGRGeometry *
2386
OGROSMDataSource::BuildGeometryCollection(const OSMRelation *psRelation,
2387
                                          bool bMultiLineString)
2388
0
{
2389
0
    std::map<GIntBig, std::pair<int, void *>> aoMapWays;
2390
0
    LookupWays(aoMapWays, psRelation);
2391
2392
0
    std::unique_ptr<OGRGeometryCollection> poColl =
2393
0
        bMultiLineString ? std::make_unique<OGRMultiLineString>()
2394
0
                         : std::make_unique<OGRGeometryCollection>();
2395
2396
0
    for (unsigned int i = 0; i < psRelation->nMembers; i++)
2397
0
    {
2398
0
        if (psRelation->pasMembers[i].eType == MEMBER_NODE && !bMultiLineString)
2399
0
        {
2400
0
            m_nUnsortedReqIds = 1;
2401
0
            m_panUnsortedReqIds[0] = psRelation->pasMembers[i].nID;
2402
0
            LookupNodes();
2403
0
            if (m_nReqIds == 1)
2404
0
            {
2405
0
                poColl->addGeometryDirectly(
2406
0
                    new OGRPoint(INT_TO_DBL(m_pasLonLatArray[0].nLon),
2407
0
                                 INT_TO_DBL(m_pasLonLatArray[0].nLat)));
2408
0
            }
2409
0
        }
2410
0
        else if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
2411
0
                 strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0 &&
2412
0
                 aoMapWays.find(psRelation->pasMembers[i].nID) !=
2413
0
                     aoMapWays.end())
2414
0
        {
2415
0
            const auto &oGeom = aoMapWays[psRelation->pasMembers[i].nID];
2416
2417
0
            bool bIsArea = false;
2418
0
            UncompressWay(oGeom.first, reinterpret_cast<GByte *>(oGeom.second),
2419
0
                          &bIsArea, m_asLonLatCache, nullptr, nullptr, nullptr);
2420
0
            OGRLineString *poLS = nullptr;
2421
0
            if (bIsArea && !bMultiLineString)
2422
0
            {
2423
0
                OGRLinearRing *poLR = new OGRLinearRing();
2424
0
                OGRPolygon *poPoly = new OGRPolygon();
2425
0
                poPoly->addRingDirectly(poLR);
2426
0
                poColl->addGeometryDirectly(poPoly);
2427
0
                poLS = poLR;
2428
0
            }
2429
0
            else
2430
0
            {
2431
0
                poLS = new OGRLineString();
2432
0
                poColl->addGeometryDirectly(poLS);
2433
0
            }
2434
2435
0
            const int nPoints = static_cast<int>(m_asLonLatCache.size());
2436
0
            poLS->setNumPoints(nPoints, /*bZeroizeNewContent=*/false);
2437
0
            for (int j = 0; j < nPoints; j++)
2438
0
            {
2439
0
                poLS->setPoint(j, INT_TO_DBL(m_asLonLatCache[j].nLon),
2440
0
                               INT_TO_DBL(m_asLonLatCache[j].nLat));
2441
0
            }
2442
0
        }
2443
0
    }
2444
2445
0
    if (poColl->getNumGeometries() == 0)
2446
0
    {
2447
0
        poColl.reset();
2448
0
    }
2449
2450
    // cppcheck-suppress constVariableReference
2451
0
    for (auto &oIter : aoMapWays)
2452
0
        CPLFree(oIter.second.second);
2453
2454
0
    return poColl.release();
2455
0
}
2456
2457
/************************************************************************/
2458
/*                            NotifyRelation()                          */
2459
/************************************************************************/
2460
2461
void OGROSMDataSource::NotifyRelation(const OSMRelation *psRelation)
2462
0
{
2463
0
    if (!m_asWayFeaturePairs.empty())
2464
0
        ProcessWaysBatch();
2465
2466
0
    m_nRelationsProcessed++;
2467
0
    if ((m_nRelationsProcessed % 10000) == 0)
2468
0
    {
2469
0
        CPLDebug("OSM", "Relations processed : %d", m_nRelationsProcessed);
2470
#ifdef DEBUG_MEM_USAGE
2471
        CPLDebug("OSM", "GetMaxTotalAllocs() = " CPL_FRMT_GUIB,
2472
                 static_cast<GUIntBig>(GetMaxTotalAllocs()));
2473
#endif
2474
0
    }
2475
2476
0
    if (!m_bUseWaysIndex)
2477
0
        return;
2478
2479
0
    bool bMultiPolygon = false;
2480
0
    bool bMultiLineString = false;
2481
0
    bool bInterestingTagFound = false;
2482
0
    const char *pszTypeV = nullptr;
2483
0
    for (unsigned int i = 0; i < psRelation->nTags; i++)
2484
0
    {
2485
0
        const char *pszK = psRelation->pasTags[i].pszK;
2486
0
        if (strcmp(pszK, "type") == 0)
2487
0
        {
2488
0
            const char *pszV = psRelation->pasTags[i].pszV;
2489
0
            pszTypeV = pszV;
2490
0
            if (strcmp(pszV, "multipolygon") == 0 ||
2491
0
                strcmp(pszV, "boundary") == 0)
2492
0
            {
2493
0
                bMultiPolygon = true;
2494
0
            }
2495
0
            else if (strcmp(pszV, "multilinestring") == 0 ||
2496
0
                     strcmp(pszV, "route") == 0)
2497
0
            {
2498
0
                bMultiLineString = true;
2499
0
            }
2500
0
        }
2501
0
        else if (strcmp(pszK, "created_by") != 0)
2502
0
            bInterestingTagFound = true;
2503
0
    }
2504
2505
    // Optimization: If we have an attribute filter, that does not require
2506
    // geometry, then we can just evaluate the attribute filter without the
2507
    // geometry.
2508
0
    const int iCurLayer = bMultiPolygon      ? IDX_LYR_MULTIPOLYGONS
2509
0
                          : bMultiLineString ? IDX_LYR_MULTILINESTRINGS
2510
0
                                             : IDX_LYR_OTHER_RELATIONS;
2511
0
    if (!m_apoLayers[iCurLayer]->IsUserInterested())
2512
0
        return;
2513
2514
0
    std::unique_ptr<OGRFeature> poFeature;
2515
2516
0
    if (!(bMultiPolygon && !bInterestingTagFound) &&
2517
        // We cannot do early filtering for multipolygon that has no
2518
        // interesting tag, since we may fetch attributes from ways.
2519
0
        m_apoLayers[iCurLayer]->HasAttributeFilter() &&
2520
0
        !m_apoLayers[iCurLayer]->AttributeFilterEvaluationNeedsGeometry())
2521
0
    {
2522
0
        poFeature = std::make_unique<OGRFeature>(
2523
0
            m_apoLayers[iCurLayer]->GetLayerDefn());
2524
2525
0
        m_apoLayers[iCurLayer]->SetFieldsFromTags(
2526
0
            poFeature.get(), psRelation->nID, false, psRelation->nTags,
2527
0
            psRelation->pasTags, &psRelation->sInfo);
2528
2529
0
        if (!m_apoLayers[iCurLayer]->EvaluateAttributeFilter(poFeature.get()))
2530
0
        {
2531
0
            return;
2532
0
        }
2533
0
    }
2534
2535
0
    OGRGeometry *poGeom = nullptr;
2536
2537
0
    unsigned int nExtraTags = 0;
2538
0
    OSMTag pasExtraTags[1 + MAX_COUNT_FOR_TAGS_IN_WAY];
2539
2540
0
    if (bMultiPolygon)
2541
0
    {
2542
0
        if (!bInterestingTagFound)
2543
0
        {
2544
0
            poGeom = BuildMultiPolygon(psRelation, &nExtraTags, pasExtraTags);
2545
0
            CPLAssert(nExtraTags <= MAX_COUNT_FOR_TAGS_IN_WAY);
2546
0
            pasExtraTags[nExtraTags].pszK = "type";
2547
0
            pasExtraTags[nExtraTags].pszV = pszTypeV;
2548
0
            nExtraTags++;
2549
0
        }
2550
0
        else
2551
0
            poGeom = BuildMultiPolygon(psRelation, nullptr, nullptr);
2552
0
    }
2553
0
    else
2554
0
        poGeom = BuildGeometryCollection(psRelation, bMultiLineString);
2555
2556
0
    if (poGeom != nullptr)
2557
0
    {
2558
0
        bool bAttrFilterAlreadyEvaluated = true;
2559
0
        if (poFeature == nullptr)
2560
0
        {
2561
0
            poFeature = std::make_unique<OGRFeature>(
2562
0
                m_apoLayers[iCurLayer]->GetLayerDefn());
2563
2564
0
            m_apoLayers[iCurLayer]->SetFieldsFromTags(
2565
0
                poFeature.get(), psRelation->nID, false,
2566
0
                nExtraTags ? nExtraTags : psRelation->nTags,
2567
0
                nExtraTags ? pasExtraTags : psRelation->pasTags,
2568
0
                &psRelation->sInfo);
2569
2570
0
            bAttrFilterAlreadyEvaluated = false;
2571
0
        }
2572
2573
0
        poFeature->SetGeometryDirectly(poGeom);
2574
2575
0
        bool bFilteredOut = FALSE;
2576
0
        if (!m_apoLayers[iCurLayer]->AddFeature(
2577
0
                std::move(poFeature), bAttrFilterAlreadyEvaluated,
2578
0
                &bFilteredOut, !m_bFeatureAdded))
2579
0
            m_bStopParsing = true;
2580
0
        else if (!bFilteredOut)
2581
0
            m_bFeatureAdded = true;
2582
0
    }
2583
0
}
2584
2585
static void OGROSMNotifyRelation(OSMRelation *psRelation,
2586
                                 OSMContext * /* psOSMContext */,
2587
                                 void *user_data)
2588
0
{
2589
0
    static_cast<OGROSMDataSource *>(user_data)->NotifyRelation(psRelation);
2590
0
}
2591
2592
/************************************************************************/
2593
/*                      ProcessPolygonsStandalone()                     */
2594
/************************************************************************/
2595
2596
void OGROSMDataSource::ProcessPolygonsStandalone()
2597
0
{
2598
0
    unsigned int nTags = 0;
2599
0
    OSMTag pasTags[MAX_COUNT_FOR_TAGS_IN_WAY];
2600
0
    OSMInfo sInfo;
2601
2602
0
    sInfo.ts.nTimeStamp = 0;
2603
0
    sInfo.nChangeset = 0;
2604
0
    sInfo.nVersion = 0;
2605
0
    sInfo.nUID = 0;
2606
0
    sInfo.bTimeStampIsStr = false;
2607
0
    sInfo.pszUserSID = "";
2608
2609
0
    if (!m_bHasRowInPolygonsStandalone)
2610
0
        m_bHasRowInPolygonsStandalone =
2611
0
            sqlite3_step(m_hSelectPolygonsStandaloneStmt) == SQLITE_ROW;
2612
2613
0
    bool bFirst = true;
2614
2615
0
    while (m_bHasRowInPolygonsStandalone &&
2616
0
           m_apoLayers[IDX_LYR_MULTIPOLYGONS]->m_apoFeatures.size() < 10000)
2617
0
    {
2618
0
        if (bFirst)
2619
0
        {
2620
0
            CPLDebug("OSM", "Remaining standalone polygons");
2621
0
            bFirst = false;
2622
0
        }
2623
2624
0
        GIntBig id = sqlite3_column_int64(m_hSelectPolygonsStandaloneStmt, 0);
2625
2626
0
        sqlite3_bind_int64(m_pahSelectWayStmt[0], 1, id);
2627
0
        if (sqlite3_step(m_pahSelectWayStmt[0]) == SQLITE_ROW)
2628
0
        {
2629
0
            int nBlobSize = sqlite3_column_bytes(m_pahSelectWayStmt[0], 1);
2630
0
            const void *blob = sqlite3_column_blob(m_pahSelectWayStmt[0], 1);
2631
2632
0
            UncompressWay(nBlobSize, static_cast<const GByte *>(blob), nullptr,
2633
0
                          m_asLonLatCache, &nTags, pasTags, &sInfo);
2634
0
            CPLAssert(nTags <= MAX_COUNT_FOR_TAGS_IN_WAY);
2635
2636
0
            OGRMultiPolygon *poMulti = new OGRMultiPolygon();
2637
0
            OGRPolygon *poPoly = new OGRPolygon();
2638
0
            OGRLinearRing *poRing = new OGRLinearRing();
2639
0
            poMulti->addGeometryDirectly(poPoly);
2640
0
            poPoly->addRingDirectly(poRing);
2641
0
            OGRLineString *poLS = poRing;
2642
2643
0
            poLS->setNumPoints(static_cast<int>(m_asLonLatCache.size()),
2644
0
                               /*bZeroizeNewContent=*/false);
2645
0
            for (int j = 0; j < static_cast<int>(m_asLonLatCache.size()); j++)
2646
0
            {
2647
0
                poLS->setPoint(j, INT_TO_DBL(m_asLonLatCache[j].nLon),
2648
0
                               INT_TO_DBL(m_asLonLatCache[j].nLat));
2649
0
            }
2650
2651
0
            auto poFeature = std::make_unique<OGRFeature>(
2652
0
                m_apoLayers[IDX_LYR_MULTIPOLYGONS]->GetLayerDefn());
2653
2654
0
            m_apoLayers[IDX_LYR_MULTIPOLYGONS]->SetFieldsFromTags(
2655
0
                poFeature.get(), id, true, nTags, pasTags, &sInfo);
2656
2657
0
            poFeature->SetGeometryDirectly(poMulti);
2658
2659
0
            bool bFilteredOut = false;
2660
0
            if (!m_apoLayers[IDX_LYR_MULTIPOLYGONS]->AddFeature(
2661
0
                    std::move(poFeature), FALSE, &bFilteredOut,
2662
0
                    !m_bFeatureAdded))
2663
0
            {
2664
0
                m_bStopParsing = true;
2665
0
                break;
2666
0
            }
2667
0
            else if (!bFilteredOut)
2668
0
            {
2669
0
                m_bFeatureAdded = true;
2670
0
            }
2671
0
        }
2672
0
        else
2673
0
        {
2674
0
            CPLAssert(false);
2675
0
        }
2676
2677
0
        sqlite3_reset(m_pahSelectWayStmt[0]);
2678
2679
0
        m_bHasRowInPolygonsStandalone =
2680
0
            sqlite3_step(m_hSelectPolygonsStandaloneStmt) == SQLITE_ROW;
2681
0
    }
2682
0
}
2683
2684
/************************************************************************/
2685
/*                             NotifyBounds()                           */
2686
/************************************************************************/
2687
2688
void OGROSMDataSource::NotifyBounds(double dfXMin, double dfYMin, double dfXMax,
2689
                                    double dfYMax)
2690
0
{
2691
0
    m_sExtent.MinX = dfXMin;
2692
0
    m_sExtent.MinY = dfYMin;
2693
0
    m_sExtent.MaxX = dfXMax;
2694
0
    m_sExtent.MaxY = dfYMax;
2695
0
    m_bExtentValid = true;
2696
2697
0
    CPLDebug("OSM", "Got bounds : minx=%f, miny=%f, maxx=%f, maxy=%f", dfXMin,
2698
0
             dfYMin, dfXMax, dfYMax);
2699
0
}
2700
2701
static void OGROSMNotifyBounds(double dfXMin, double dfYMin, double dfXMax,
2702
                               double dfYMax, OSMContext * /* psCtxt */,
2703
                               void *user_data)
2704
0
{
2705
0
    static_cast<OGROSMDataSource *>(user_data)->NotifyBounds(dfXMin, dfYMin,
2706
0
                                                             dfXMax, dfYMax);
2707
0
}
2708
2709
/************************************************************************/
2710
/*                                Open()                                */
2711
/************************************************************************/
2712
2713
int OGROSMDataSource::Open(const char *pszFilename,
2714
                           CSLConstList papszOpenOptionsIn)
2715
2716
1.47k
{
2717
1.47k
    m_psParser = OSM_Open(pszFilename, OGROSMNotifyNodes, OGROSMNotifyWay,
2718
1.47k
                          OGROSMNotifyRelation, OGROSMNotifyBounds, this);
2719
1.47k
    if (m_psParser == nullptr)
2720
425
        return FALSE;
2721
2722
1.05k
    if (CPLFetchBool(papszOpenOptionsIn, "INTERLEAVED_READING", false))
2723
0
        m_bInterleavedReading = TRUE;
2724
2725
    /* The following 4 config options are only useful for debugging */
2726
1.05k
    m_bIndexPoints = CPLTestBool(CPLGetConfigOption("OSM_INDEX_POINTS", "YES"));
2727
1.05k
    m_bUsePointsIndex =
2728
1.05k
        CPLTestBool(CPLGetConfigOption("OSM_USE_POINTS_INDEX", "YES"));
2729
1.05k
    m_bIndexWays = CPLTestBool(CPLGetConfigOption("OSM_INDEX_WAYS", "YES"));
2730
1.05k
    m_bUseWaysIndex =
2731
1.05k
        CPLTestBool(CPLGetConfigOption("OSM_USE_WAYS_INDEX", "YES"));
2732
2733
1.05k
    m_bCustomIndexing = CPLTestBool(CSLFetchNameValueDef(
2734
1.05k
        papszOpenOptionsIn, "USE_CUSTOM_INDEXING",
2735
1.05k
        CPLGetConfigOption("OSM_USE_CUSTOM_INDEXING", "YES")));
2736
1.05k
    if (!m_bCustomIndexing)
2737
0
        CPLDebug("OSM", "Using SQLite indexing for points");
2738
1.05k
    m_bCompressNodes = CPLTestBool(
2739
1.05k
        CSLFetchNameValueDef(papszOpenOptionsIn, "COMPRESS_NODES",
2740
1.05k
                             CPLGetConfigOption("OSM_COMPRESS_NODES", "NO")));
2741
1.05k
    if (m_bCompressNodes)
2742
0
        CPLDebug("OSM", "Using compression for nodes DB");
2743
2744
    // Do not change the below order without updating the IDX_LYR_ constants!
2745
1.05k
    m_apoLayers.emplace_back(
2746
1.05k
        std::make_unique<OGROSMLayer>(this, IDX_LYR_POINTS, "points"));
2747
1.05k
    m_apoLayers.back()->GetLayerDefn()->SetGeomType(wkbPoint);
2748
2749
1.05k
    m_apoLayers.emplace_back(
2750
1.05k
        std::make_unique<OGROSMLayer>(this, IDX_LYR_LINES, "lines"));
2751
1.05k
    m_apoLayers.back()->GetLayerDefn()->SetGeomType(wkbLineString);
2752
2753
1.05k
    m_apoLayers.emplace_back(std::make_unique<OGROSMLayer>(
2754
1.05k
        this, IDX_LYR_MULTILINESTRINGS, "multilinestrings"));
2755
1.05k
    m_apoLayers.back()->GetLayerDefn()->SetGeomType(wkbMultiLineString);
2756
2757
1.05k
    m_apoLayers.emplace_back(std::make_unique<OGROSMLayer>(
2758
1.05k
        this, IDX_LYR_MULTIPOLYGONS, "multipolygons"));
2759
1.05k
    m_apoLayers.back()->GetLayerDefn()->SetGeomType(wkbMultiPolygon);
2760
2761
1.05k
    m_apoLayers.emplace_back(std::make_unique<OGROSMLayer>(
2762
1.05k
        this, IDX_LYR_OTHER_RELATIONS, "other_relations"));
2763
1.05k
    m_apoLayers.back()->GetLayerDefn()->SetGeomType(wkbGeometryCollection);
2764
2765
1.05k
    if (!ParseConf(papszOpenOptionsIn))
2766
1.05k
    {
2767
1.05k
        CPLError(CE_Failure, CPLE_AppDefined,
2768
1.05k
                 "Could not parse configuration file for OSM import");
2769
1.05k
        return FALSE;
2770
1.05k
    }
2771
2772
0
    const char *pszTagsFormat =
2773
0
        CSLFetchNameValue(papszOpenOptionsIn, "TAGS_FORMAT");
2774
0
    if (pszTagsFormat)
2775
0
    {
2776
0
        if (EQUAL(pszTagsFormat, "JSON"))
2777
0
            m_bTagsAsHSTORE = false;
2778
0
        else if (EQUAL(pszTagsFormat, "HSTORE"))
2779
0
            m_bTagsAsHSTORE = true;
2780
0
        else
2781
0
        {
2782
0
            CPLError(CE_Warning, CPLE_NotSupported,
2783
0
                     "Invalid value for TAGS_FORMAT open option: %s",
2784
0
                     pszTagsFormat);
2785
0
        }
2786
0
    }
2787
2788
0
    const auto eTagsSubType = m_bTagsAsHSTORE ? OFSTNone : OFSTJSON;
2789
0
    for (auto &&poLayer : m_apoLayers)
2790
0
    {
2791
0
        if (poLayer->HasAllTags())
2792
0
        {
2793
0
            poLayer->AddField("all_tags", OFTString, eTagsSubType);
2794
0
            if (poLayer->HasOtherTags())
2795
0
            {
2796
0
                poLayer->SetHasOtherTags(false);
2797
0
            }
2798
0
        }
2799
0
        else if (poLayer->HasOtherTags())
2800
0
            poLayer->AddField("other_tags", OFTString, eTagsSubType);
2801
0
    }
2802
2803
0
    m_bNeedsToSaveWayInfo =
2804
0
        (m_apoLayers[IDX_LYR_MULTIPOLYGONS]->HasTimestamp() ||
2805
0
         m_apoLayers[IDX_LYR_MULTIPOLYGONS]->HasChangeset() ||
2806
0
         m_apoLayers[IDX_LYR_MULTIPOLYGONS]->HasVersion() ||
2807
0
         m_apoLayers[IDX_LYR_MULTIPOLYGONS]->HasUID() ||
2808
0
         m_apoLayers[IDX_LYR_MULTIPOLYGONS]->HasUser());
2809
2810
0
    m_panReqIds = static_cast<GIntBig *>(
2811
0
        VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_NODES * sizeof(GIntBig)));
2812
0
#ifdef ENABLE_NODE_LOOKUP_BY_HASHING
2813
0
    m_panHashedIndexes = static_cast<int *>(
2814
0
        VSI_MALLOC_VERBOSE(HASHED_INDEXES_ARRAY_SIZE * sizeof(int)));
2815
0
    m_psCollisionBuckets = static_cast<CollisionBucket *>(VSI_MALLOC_VERBOSE(
2816
0
        COLLISION_BUCKET_ARRAY_SIZE * sizeof(CollisionBucket)));
2817
0
#endif
2818
0
    m_pasLonLatArray = static_cast<LonLat *>(
2819
0
        VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_NODES * sizeof(LonLat)));
2820
0
    m_panUnsortedReqIds = static_cast<GIntBig *>(
2821
0
        VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_NODES * sizeof(GIntBig)));
2822
0
    try
2823
0
    {
2824
0
        m_asWayFeaturePairs.reserve(MAX_DELAYED_FEATURES);
2825
0
    }
2826
0
    catch (const std::exception &)
2827
0
    {
2828
0
        CPLError(CE_Failure, CPLE_OutOfMemory,
2829
0
                 "OGROSMDataSource::Open(): out of memory");
2830
0
        return FALSE;
2831
0
    }
2832
0
    m_pasAccumulatedTags = static_cast<IndexedKVP *>(
2833
0
        VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_TAGS * sizeof(IndexedKVP)));
2834
0
    pabyNonRedundantValues =
2835
0
        static_cast<GByte *>(VSI_MALLOC_VERBOSE(MAX_NON_REDUNDANT_VALUES));
2836
0
    pabyNonRedundantKeys =
2837
0
        static_cast<GByte *>(VSI_MALLOC_VERBOSE(MAX_NON_REDUNDANT_KEYS));
2838
0
    if (m_panReqIds == nullptr || m_pasLonLatArray == nullptr ||
2839
0
        m_panUnsortedReqIds == nullptr || m_pasAccumulatedTags == nullptr ||
2840
0
        pabyNonRedundantValues == nullptr || pabyNonRedundantKeys == nullptr)
2841
0
    {
2842
0
        return FALSE;
2843
0
    }
2844
2845
0
    m_nMaxSizeForInMemoryDBInMB = atoi(CSLFetchNameValueDef(
2846
0
        papszOpenOptionsIn, "MAX_TMPFILE_SIZE",
2847
0
        CPLGetConfigOption("OSM_MAX_TMPFILE_SIZE", "100")));
2848
0
    if (m_nMaxSizeForInMemoryDBInMB == 0)
2849
0
        m_nMaxSizeForInMemoryDBInMB = 1;
2850
0
    GIntBig nSize =
2851
0
        static_cast<GIntBig>(m_nMaxSizeForInMemoryDBInMB) * 1024 * 1024;
2852
0
    if (nSize < 0 ||
2853
0
        static_cast<GUIntBig>(nSize) > std::numeric_limits<size_t>::max() / 2)
2854
0
    {
2855
0
        CPLError(CE_Failure, CPLE_AppDefined,
2856
0
                 "Invalid value for OSM_MAX_TMPFILE_SIZE. Using 100 instead.");
2857
0
        m_nMaxSizeForInMemoryDBInMB = 100;
2858
0
        nSize = static_cast<GIntBig>(m_nMaxSizeForInMemoryDBInMB) * 1024 * 1024;
2859
0
    }
2860
2861
0
    if (m_bCustomIndexing)
2862
0
    {
2863
0
        m_pabySector = static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, SECTOR_SIZE));
2864
2865
0
        if (m_pabySector == nullptr)
2866
0
        {
2867
0
            return FALSE;
2868
0
        }
2869
2870
0
        m_bInMemoryNodesFile = true;
2871
0
        m_osNodesFilename = VSIMemGenerateHiddenFilename("osm_temp_nodes");
2872
0
        m_fpNodes = VSIFOpenL(m_osNodesFilename, "wb+");
2873
0
        if (m_fpNodes == nullptr)
2874
0
        {
2875
0
            return FALSE;
2876
0
        }
2877
2878
0
        CPLPushErrorHandler(CPLQuietErrorHandler);
2879
0
        const bool bSuccess =
2880
0
            VSIFTruncateL(m_fpNodes,
2881
0
                          static_cast<vsi_l_offset>(nSize * 3 / 4)) == 0;
2882
0
        CPLPopErrorHandler();
2883
2884
0
        if (bSuccess)
2885
0
        {
2886
0
            VSIFTruncateL(m_fpNodes, 0);
2887
0
        }
2888
0
        else
2889
0
        {
2890
0
            CPLDebug("OSM", "Not enough memory for in-memory file. "
2891
0
                            "Using disk temporary file instead.");
2892
2893
0
            VSIFCloseL(m_fpNodes);
2894
0
            m_fpNodes = nullptr;
2895
0
            VSIUnlink(m_osNodesFilename);
2896
2897
0
            m_bInMemoryNodesFile = false;
2898
0
            m_osNodesFilename = CPLGenerateTempFilenameSafe("osm_tmp_nodes");
2899
2900
0
            m_fpNodes = VSIFOpenL(m_osNodesFilename, "wb+");
2901
0
            if (m_fpNodes == nullptr)
2902
0
            {
2903
0
                return FALSE;
2904
0
            }
2905
2906
            /* On Unix filesystems, you can remove a file even if it */
2907
            /* opened */
2908
0
            const char *pszVal =
2909
0
                CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
2910
0
            if (EQUAL(pszVal, "YES"))
2911
0
            {
2912
0
                CPLPushErrorHandler(CPLQuietErrorHandler);
2913
0
                m_bMustUnlinkNodesFile = VSIUnlink(m_osNodesFilename) != 0;
2914
0
                CPLPopErrorHandler();
2915
0
            }
2916
2917
0
            return FALSE;
2918
0
        }
2919
0
    }
2920
2921
0
    const bool bRet = CreateTempDB();
2922
0
    if (bRet)
2923
0
    {
2924
0
        CPLString osInterestLayers =
2925
0
            GetInterestLayersForDSName(GetDescription());
2926
0
        if (!osInterestLayers.empty())
2927
0
        {
2928
0
            ReleaseResultSet(ExecuteSQL(osInterestLayers, nullptr, nullptr));
2929
0
        }
2930
0
    }
2931
0
    return bRet;
2932
0
}
2933
2934
/************************************************************************/
2935
/*                             CreateTempDB()                           */
2936
/************************************************************************/
2937
2938
bool OGROSMDataSource::CreateTempDB()
2939
0
{
2940
0
    char *pszErrMsg = nullptr;
2941
2942
0
    int rc = 0;
2943
0
    bool bIsExisting = false;
2944
0
    bool bSuccess = false;
2945
2946
0
    const char *pszExistingTmpFile =
2947
0
        CPLGetConfigOption("OSM_EXISTING_TMPFILE", nullptr);
2948
0
    if (pszExistingTmpFile != nullptr)
2949
0
    {
2950
0
        bSuccess = true;
2951
0
        bIsExisting = true;
2952
0
        rc = sqlite3_open_v2(pszExistingTmpFile, &m_hDB,
2953
0
                             SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX,
2954
0
                             nullptr);
2955
0
    }
2956
0
    else
2957
0
    {
2958
0
        m_osTmpDBName = VSIMemGenerateHiddenFilename("osm_temp.sqlite");
2959
2960
        // On 32 bit, the virtual memory space is scarce, so we need to
2961
        // reserve it right now. Will not hurt on 64 bit either.
2962
0
        VSILFILE *fp = VSIFOpenL(m_osTmpDBName, "wb");
2963
0
        if (fp)
2964
0
        {
2965
0
            vsi_l_offset nSize =
2966
0
                static_cast<vsi_l_offset>(m_nMaxSizeForInMemoryDBInMB) * 1024 *
2967
0
                1024;
2968
0
            if (m_bCustomIndexing && m_bInMemoryNodesFile)
2969
0
                nSize = nSize / 4;
2970
2971
0
            CPLPushErrorHandler(CPLQuietErrorHandler);
2972
0
            bSuccess = VSIFTruncateL(fp, nSize) == 0;
2973
0
            CPLPopErrorHandler();
2974
2975
0
            if (bSuccess)
2976
0
                bSuccess = VSIFTruncateL(fp, 0) == 0;
2977
2978
0
            VSIFCloseL(fp);
2979
2980
0
            if (!bSuccess)
2981
0
            {
2982
0
                CPLDebug("OSM", "Not enough memory for in-memory file. "
2983
0
                                "Using disk temporary file instead.");
2984
0
                VSIUnlink(m_osTmpDBName);
2985
0
            }
2986
0
        }
2987
2988
0
        if (bSuccess)
2989
0
        {
2990
0
            m_bInMemoryTmpDB = true;
2991
0
            m_pMyVFS = OGRSQLiteCreateVFS(nullptr, this);
2992
0
            sqlite3_vfs_register(m_pMyVFS, 0);
2993
0
            rc = sqlite3_open_v2(m_osTmpDBName.c_str(), &m_hDB,
2994
0
                                 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
2995
0
                                     SQLITE_OPEN_NOMUTEX,
2996
0
                                 m_pMyVFS->zName);
2997
0
        }
2998
0
    }
2999
3000
0
    if (!bSuccess)
3001
0
    {
3002
0
        m_osTmpDBName = CPLGenerateTempFilenameSafe("osm_tmp");
3003
0
        rc = sqlite3_open(m_osTmpDBName.c_str(), &m_hDB);
3004
3005
        /* On Unix filesystems, you can remove a file even if it */
3006
        /* opened */
3007
0
        if (rc == SQLITE_OK)
3008
0
        {
3009
0
            const char *pszVal =
3010
0
                CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
3011
0
            if (EQUAL(pszVal, "YES"))
3012
0
            {
3013
0
                CPLPushErrorHandler(CPLQuietErrorHandler);
3014
0
                m_bMustUnlink = VSIUnlink(m_osTmpDBName) != 0;
3015
0
                CPLPopErrorHandler();
3016
0
            }
3017
0
        }
3018
0
    }
3019
3020
0
    if (rc != SQLITE_OK)
3021
0
    {
3022
0
        CPLError(CE_Failure, CPLE_OpenFailed, "sqlite3_open(%s) failed: %s",
3023
0
                 m_osTmpDBName.c_str(), sqlite3_errmsg(m_hDB));
3024
0
        return false;
3025
0
    }
3026
3027
0
    if (!SetDBOptions())
3028
0
    {
3029
0
        return false;
3030
0
    }
3031
3032
0
    if (!bIsExisting)
3033
0
    {
3034
0
        rc = sqlite3_exec(
3035
0
            m_hDB, "CREATE TABLE nodes (id INTEGER PRIMARY KEY, coords BLOB)",
3036
0
            nullptr, nullptr, &pszErrMsg);
3037
0
        if (rc != SQLITE_OK)
3038
0
        {
3039
0
            CPLError(CE_Failure, CPLE_AppDefined,
3040
0
                     "Unable to create table nodes : %s", pszErrMsg);
3041
0
            sqlite3_free(pszErrMsg);
3042
0
            return false;
3043
0
        }
3044
3045
0
        rc = sqlite3_exec(
3046
0
            m_hDB, "CREATE TABLE ways (id INTEGER PRIMARY KEY, data BLOB)",
3047
0
            nullptr, nullptr, &pszErrMsg);
3048
0
        if (rc != SQLITE_OK)
3049
0
        {
3050
0
            CPLError(CE_Failure, CPLE_AppDefined,
3051
0
                     "Unable to create table ways : %s", pszErrMsg);
3052
0
            sqlite3_free(pszErrMsg);
3053
0
            return false;
3054
0
        }
3055
3056
0
        rc = sqlite3_exec(
3057
0
            m_hDB, "CREATE TABLE polygons_standalone (id INTEGER PRIMARY KEY)",
3058
0
            nullptr, nullptr, &pszErrMsg);
3059
0
        if (rc != SQLITE_OK)
3060
0
        {
3061
0
            CPLError(CE_Failure, CPLE_AppDefined,
3062
0
                     "Unable to create table polygons_standalone : %s",
3063
0
                     pszErrMsg);
3064
0
            sqlite3_free(pszErrMsg);
3065
0
            return false;
3066
0
        }
3067
0
    }
3068
3069
0
    return CreatePreparedStatements();
3070
0
}
3071
3072
/************************************************************************/
3073
/*                            SetDBOptions()                            */
3074
/************************************************************************/
3075
3076
bool OGROSMDataSource::SetDBOptions()
3077
0
{
3078
0
    char *pszErrMsg = nullptr;
3079
0
    int rc = sqlite3_exec(m_hDB, "PRAGMA synchronous = OFF", nullptr, nullptr,
3080
0
                          &pszErrMsg);
3081
0
    if (rc != SQLITE_OK)
3082
0
    {
3083
0
        CPLError(CE_Failure, CPLE_AppDefined,
3084
0
                 "Unable to run PRAGMA synchronous : %s", pszErrMsg);
3085
0
        sqlite3_free(pszErrMsg);
3086
0
        return false;
3087
0
    }
3088
3089
0
    rc = sqlite3_exec(m_hDB, "PRAGMA journal_mode = OFF", nullptr, nullptr,
3090
0
                      &pszErrMsg);
3091
0
    if (rc != SQLITE_OK)
3092
0
    {
3093
0
        CPLError(CE_Failure, CPLE_AppDefined,
3094
0
                 "Unable to run PRAGMA journal_mode : %s", pszErrMsg);
3095
0
        sqlite3_free(pszErrMsg);
3096
0
        return false;
3097
0
    }
3098
3099
0
    rc = sqlite3_exec(m_hDB, "PRAGMA temp_store = MEMORY", nullptr, nullptr,
3100
0
                      &pszErrMsg);
3101
0
    if (rc != SQLITE_OK)
3102
0
    {
3103
0
        CPLError(CE_Failure, CPLE_AppDefined,
3104
0
                 "Unable to run PRAGMA temp_store : %s", pszErrMsg);
3105
0
        sqlite3_free(pszErrMsg);
3106
0
        return false;
3107
0
    }
3108
3109
0
    SetCacheSize();
3110
3111
0
    if (!StartTransactionCacheDB())
3112
0
        return false;
3113
3114
0
    return true;
3115
0
}
3116
3117
/************************************************************************/
3118
/*                              SetCacheSize()                          */
3119
/************************************************************************/
3120
3121
void OGROSMDataSource::SetCacheSize()
3122
0
{
3123
0
    const char *pszSqliteCacheMB =
3124
0
        CPLGetConfigOption("OSM_SQLITE_CACHE", nullptr);
3125
3126
0
    if (pszSqliteCacheMB == nullptr)
3127
0
        return;
3128
3129
0
    char *pszErrMsg = nullptr;
3130
0
    char **papszResult = nullptr;
3131
0
    int nRowCount = 0;
3132
0
    int nColCount = 0;
3133
0
    int iSqlitePageSize = -1;
3134
0
    const GIntBig iSqliteCacheBytes =
3135
0
        static_cast<GIntBig>(atoi(pszSqliteCacheMB)) * 1024 * 1024;
3136
3137
    /* querying the current PageSize */
3138
0
    int rc = sqlite3_get_table(m_hDB, "PRAGMA page_size", &papszResult,
3139
0
                               &nRowCount, &nColCount, &pszErrMsg);
3140
0
    if (rc == SQLITE_OK)
3141
0
    {
3142
0
        for (int iRow = 1; iRow <= nRowCount; iRow++)
3143
0
        {
3144
0
            iSqlitePageSize = atoi(papszResult[(iRow * nColCount) + 0]);
3145
0
        }
3146
0
        sqlite3_free_table(papszResult);
3147
0
    }
3148
0
    if (iSqlitePageSize < 0)
3149
0
    {
3150
0
        CPLError(CE_Failure, CPLE_AppDefined,
3151
0
                 "Unable to run PRAGMA page_size : %s",
3152
0
                 pszErrMsg ? pszErrMsg : sqlite3_errmsg(m_hDB));
3153
0
        sqlite3_free(pszErrMsg);
3154
0
        return;
3155
0
    }
3156
0
    if (iSqlitePageSize == 0)
3157
0
        return;
3158
3159
    /* computing the CacheSize as #Pages */
3160
0
    const int iSqliteCachePages =
3161
0
        static_cast<int>(iSqliteCacheBytes / iSqlitePageSize);
3162
0
    if (iSqliteCachePages <= 0)
3163
0
        return;
3164
3165
0
    rc = sqlite3_exec(m_hDB,
3166
0
                      CPLSPrintf("PRAGMA cache_size = %d", iSqliteCachePages),
3167
0
                      nullptr, nullptr, &pszErrMsg);
3168
0
    if (rc != SQLITE_OK)
3169
0
    {
3170
0
        CPLError(CE_Warning, CPLE_AppDefined,
3171
0
                 "Unrecognized value for PRAGMA cache_size : %s", pszErrMsg);
3172
0
        sqlite3_free(pszErrMsg);
3173
0
    }
3174
0
}
3175
3176
/************************************************************************/
3177
/*                        CreatePreparedStatements()                    */
3178
/************************************************************************/
3179
3180
bool OGROSMDataSource::CreatePreparedStatements()
3181
0
{
3182
0
    int rc =
3183
0
        sqlite3_prepare_v2(m_hDB, "INSERT INTO nodes (id, coords) VALUES (?,?)",
3184
0
                           -1, &m_hInsertNodeStmt, nullptr);
3185
0
    if (rc != SQLITE_OK)
3186
0
    {
3187
0
        CPLError(CE_Failure, CPLE_AppDefined,
3188
0
                 "sqlite3_prepare_v2() failed :  %s", sqlite3_errmsg(m_hDB));
3189
0
        return false;
3190
0
    }
3191
3192
0
    m_pahSelectNodeStmt = static_cast<sqlite3_stmt **>(
3193
0
        CPLCalloc(sizeof(sqlite3_stmt *), LIMIT_IDS_PER_REQUEST));
3194
3195
0
    char szTmp[LIMIT_IDS_PER_REQUEST * 2 + 128];
3196
0
    strcpy(szTmp, "SELECT id, coords FROM nodes WHERE id IN (");
3197
0
    int nLen = static_cast<int>(strlen(szTmp));
3198
0
    for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
3199
0
    {
3200
0
        if (i == 0)
3201
0
        {
3202
0
            strcpy(szTmp + nLen, "?) ORDER BY id ASC");
3203
0
            nLen += 2;
3204
0
        }
3205
0
        else
3206
0
        {
3207
0
            strcpy(szTmp + nLen - 1, ",?) ORDER BY id ASC");
3208
0
            nLen += 2;
3209
0
        }
3210
0
        rc = sqlite3_prepare_v2(m_hDB, szTmp, -1, &m_pahSelectNodeStmt[i],
3211
0
                                nullptr);
3212
0
        if (rc != SQLITE_OK)
3213
0
        {
3214
0
            CPLError(CE_Failure, CPLE_AppDefined,
3215
0
                     "sqlite3_prepare_v2() failed :  %s",
3216
0
                     sqlite3_errmsg(m_hDB));
3217
0
            return false;
3218
0
        }
3219
0
    }
3220
3221
0
    rc = sqlite3_prepare_v2(m_hDB, "INSERT INTO ways (id, data) VALUES (?,?)",
3222
0
                            -1, &m_hInsertWayStmt, nullptr);
3223
0
    if (rc != SQLITE_OK)
3224
0
    {
3225
0
        CPLError(CE_Failure, CPLE_AppDefined,
3226
0
                 "sqlite3_prepare_v2() failed :  %s", sqlite3_errmsg(m_hDB));
3227
0
        return false;
3228
0
    }
3229
3230
0
    m_pahSelectWayStmt = static_cast<sqlite3_stmt **>(
3231
0
        CPLCalloc(sizeof(sqlite3_stmt *), LIMIT_IDS_PER_REQUEST));
3232
3233
0
    strcpy(szTmp, "SELECT id, data FROM ways WHERE id IN (");
3234
0
    nLen = static_cast<int>(strlen(szTmp));
3235
0
    for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
3236
0
    {
3237
0
        if (i == 0)
3238
0
        {
3239
0
            strcpy(szTmp + nLen, "?)");
3240
0
            nLen += 2;
3241
0
        }
3242
0
        else
3243
0
        {
3244
0
            strcpy(szTmp + nLen - 1, ",?)");
3245
0
            nLen += 2;
3246
0
        }
3247
0
        rc = sqlite3_prepare_v2(m_hDB, szTmp, -1, &m_pahSelectWayStmt[i],
3248
0
                                nullptr);
3249
0
        if (rc != SQLITE_OK)
3250
0
        {
3251
0
            CPLError(CE_Failure, CPLE_AppDefined,
3252
0
                     "sqlite3_prepare_v2() failed :  %s",
3253
0
                     sqlite3_errmsg(m_hDB));
3254
0
            return false;
3255
0
        }
3256
0
    }
3257
3258
0
    rc = sqlite3_prepare_v2(m_hDB,
3259
0
                            "INSERT INTO polygons_standalone (id) VALUES (?)",
3260
0
                            -1, &m_hInsertPolygonsStandaloneStmt, nullptr);
3261
0
    if (rc != SQLITE_OK)
3262
0
    {
3263
0
        CPLError(CE_Failure, CPLE_AppDefined,
3264
0
                 "sqlite3_prepare_v2() failed :  %s", sqlite3_errmsg(m_hDB));
3265
0
        return false;
3266
0
    }
3267
3268
0
    rc = sqlite3_prepare_v2(m_hDB,
3269
0
                            "DELETE FROM polygons_standalone WHERE id = ?", -1,
3270
0
                            &m_hDeletePolygonsStandaloneStmt, nullptr);
3271
0
    if (rc != SQLITE_OK)
3272
0
    {
3273
0
        CPLError(CE_Failure, CPLE_AppDefined,
3274
0
                 "sqlite3_prepare_v2() failed :  %s", sqlite3_errmsg(m_hDB));
3275
0
        return false;
3276
0
    }
3277
3278
0
    rc = sqlite3_prepare_v2(m_hDB,
3279
0
                            "SELECT id FROM polygons_standalone ORDER BY id",
3280
0
                            -1, &m_hSelectPolygonsStandaloneStmt, nullptr);
3281
0
    if (rc != SQLITE_OK)
3282
0
    {
3283
0
        CPLError(CE_Failure, CPLE_AppDefined,
3284
0
                 "sqlite3_prepare_v2() failed :  %s", sqlite3_errmsg(m_hDB));
3285
0
        return false;
3286
0
    }
3287
3288
0
    return true;
3289
0
}
3290
3291
/************************************************************************/
3292
/*                      StartTransactionCacheDB()                       */
3293
/************************************************************************/
3294
3295
bool OGROSMDataSource::StartTransactionCacheDB()
3296
0
{
3297
0
    if (m_bInTransaction)
3298
0
        return false;
3299
3300
0
    char *pszErrMsg = nullptr;
3301
0
    int rc = sqlite3_exec(m_hDB, "BEGIN", nullptr, nullptr, &pszErrMsg);
3302
0
    if (rc != SQLITE_OK)
3303
0
    {
3304
0
        CPLError(CE_Failure, CPLE_AppDefined,
3305
0
                 "Unable to start transaction : %s", pszErrMsg);
3306
0
        sqlite3_free(pszErrMsg);
3307
0
        return false;
3308
0
    }
3309
3310
0
    m_bInTransaction = true;
3311
3312
0
    return true;
3313
0
}
3314
3315
/************************************************************************/
3316
/*                        CommitTransactionCacheDB()                    */
3317
/************************************************************************/
3318
3319
bool OGROSMDataSource::CommitTransactionCacheDB()
3320
0
{
3321
0
    if (!m_bInTransaction)
3322
0
        return false;
3323
3324
0
    m_bInTransaction = false;
3325
3326
0
    char *pszErrMsg = nullptr;
3327
0
    int rc = sqlite3_exec(m_hDB, "COMMIT", nullptr, nullptr, &pszErrMsg);
3328
0
    if (rc != SQLITE_OK)
3329
0
    {
3330
0
        CPLError(CE_Failure, CPLE_AppDefined,
3331
0
                 "Unable to commit transaction : %s", pszErrMsg);
3332
0
        sqlite3_free(pszErrMsg);
3333
0
        return false;
3334
0
    }
3335
3336
0
    return true;
3337
0
}
3338
3339
/************************************************************************/
3340
/*                     AddComputedAttributes()                          */
3341
/************************************************************************/
3342
3343
void OGROSMDataSource::AddComputedAttributes(
3344
    int iCurLayer, const std::vector<OGROSMComputedAttribute> &oAttributes)
3345
0
{
3346
0
    for (const auto &oAttribute : oAttributes)
3347
0
    {
3348
0
        if (!oAttribute.osSQL.empty())
3349
0
        {
3350
0
            m_apoLayers[iCurLayer]->AddComputedAttribute(
3351
0
                oAttribute.osName, oAttribute.eType, oAttribute.osSQL);
3352
0
        }
3353
0
    }
3354
0
}
3355
3356
/************************************************************************/
3357
/*                           ParseConf()                                */
3358
/************************************************************************/
3359
3360
bool OGROSMDataSource::ParseConf(CSLConstList papszOpenOptionsIn)
3361
1.05k
{
3362
1.05k
    VSILFILE *fpConf = nullptr;
3363
3364
1.05k
    const char *pszFilename =
3365
1.05k
        CSLFetchNameValueDef(papszOpenOptionsIn, "CONFIG_FILE",
3366
1.05k
                             CPLGetConfigOption("OSM_CONFIG_FILE", nullptr));
3367
1.05k
    if (pszFilename == nullptr)
3368
1.05k
    {
3369
1.05k
#if !defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
3370
1.05k
        pszFilename = CPLFindFile("gdal", "osmconf.ini");
3371
1.05k
#endif
3372
#ifdef EMBED_RESOURCE_FILES
3373
        if (!pszFilename || EQUAL(pszFilename, "osmconf.ini"))
3374
        {
3375
            static const bool bOnce [[maybe_unused]] = []()
3376
            {
3377
                CPLDebug("OSM", "Using embedded osmconf.ini");
3378
                return true;
3379
            }();
3380
            fpConf = VSIFileFromMemBuffer(
3381
                nullptr,
3382
                const_cast<GByte *>(
3383
                    reinterpret_cast<const GByte *>(OSMGetOSMConfIni())),
3384
                static_cast<int>(strlen(OSMGetOSMConfIni())),
3385
                /* bTakeOwnership = */ false);
3386
        }
3387
#else
3388
1.05k
        if (!pszFilename)
3389
1.05k
        {
3390
1.05k
            CPLError(CE_Warning, CPLE_AppDefined,
3391
1.05k
                     "Cannot find osmconf.ini configuration file");
3392
1.05k
            return false;
3393
1.05k
        }
3394
1.05k
#endif
3395
1.05k
    }
3396
3397
0
    if (pszFilename)
3398
0
        m_osConfigFile = pszFilename;
3399
3400
#if defined(EMBED_RESOURCE_FILES)
3401
    if (!fpConf)
3402
#endif
3403
0
    {
3404
0
        fpConf = VSIFOpenL(pszFilename, "rb");
3405
0
        if (fpConf == nullptr)
3406
0
            return false;
3407
0
    }
3408
3409
0
    const char *pszLine = nullptr;
3410
0
    int iCurLayer = -1;
3411
0
    std::vector<OGROSMComputedAttribute> oAttributes;
3412
3413
0
    while ((pszLine = CPLReadLine2L(fpConf, -1, nullptr)) != nullptr)
3414
0
    {
3415
0
        if (pszLine[0] == '#')
3416
0
            continue;
3417
0
        if (pszLine[0] == '[' && pszLine[strlen(pszLine) - 1] == ']')
3418
0
        {
3419
0
            if (iCurLayer >= 0)
3420
0
                AddComputedAttributes(iCurLayer, oAttributes);
3421
0
            oAttributes.resize(0);
3422
3423
0
            iCurLayer = -1;
3424
0
            pszLine++;
3425
0
            const_cast<char *>(pszLine)[strlen(pszLine) - 1] =
3426
0
                '\0'; /* Evil but OK */
3427
3428
0
            if (strcmp(pszLine, "general") == 0)
3429
0
            {
3430
0
                continue;
3431
0
            }
3432
3433
0
            int i = 0;
3434
0
            for (auto &&poLayer : m_apoLayers)
3435
0
            {
3436
0
                if (strcmp(pszLine, poLayer->GetName()) == 0)
3437
0
                {
3438
0
                    iCurLayer = i;
3439
0
                    break;
3440
0
                }
3441
0
                ++i;
3442
0
            }
3443
0
            if (iCurLayer < 0)
3444
0
            {
3445
0
                CPLError(CE_Warning, CPLE_AppDefined,
3446
0
                         "Layer '%s' mentioned in %s is unknown to the driver",
3447
0
                         pszLine, pszFilename);
3448
0
            }
3449
0
            continue;
3450
0
        }
3451
3452
0
        if (STARTS_WITH(pszLine, "closed_ways_are_polygons="))
3453
0
        {
3454
0
            char **papszTokens2 = CSLTokenizeString2(
3455
0
                pszLine + strlen("closed_ways_are_polygons="), ",", 0);
3456
0
            m_nMinSizeKeysInSetClosedWaysArePolygons = INT_MAX;
3457
0
            m_nMaxSizeKeysInSetClosedWaysArePolygons = 0;
3458
0
            for (int i = 0; papszTokens2[i] != nullptr; i++)
3459
0
            {
3460
0
                const int nTokenSize =
3461
0
                    static_cast<int>(strlen(papszTokens2[i]));
3462
0
                aoSetClosedWaysArePolygons.insert(papszTokens2[i]);
3463
0
                m_nMinSizeKeysInSetClosedWaysArePolygons = std::min(
3464
0
                    m_nMinSizeKeysInSetClosedWaysArePolygons, nTokenSize);
3465
0
                m_nMaxSizeKeysInSetClosedWaysArePolygons = std::max(
3466
0
                    m_nMaxSizeKeysInSetClosedWaysArePolygons, nTokenSize);
3467
0
            }
3468
0
            CSLDestroy(papszTokens2);
3469
0
        }
3470
3471
0
        else if (STARTS_WITH(pszLine, "report_all_tags="))
3472
0
        {
3473
0
            if (strcmp(pszLine + strlen("report_all_tags="), "yes") == 0)
3474
0
            {
3475
0
                std::fill(begin(m_ignoredKeys), end(m_ignoredKeys), "");
3476
0
            }
3477
0
        }
3478
3479
0
        else if (STARTS_WITH(pszLine, "report_all_nodes="))
3480
0
        {
3481
0
            if (strcmp(pszLine + strlen("report_all_nodes="), "no") == 0)
3482
0
            {
3483
0
                m_bReportAllNodes = false;
3484
0
            }
3485
0
            else if (strcmp(pszLine + strlen("report_all_nodes="), "yes") == 0)
3486
0
            {
3487
0
                m_bReportAllNodes = true;
3488
0
            }
3489
0
        }
3490
3491
0
        else if (STARTS_WITH(pszLine, "report_all_ways="))
3492
0
        {
3493
0
            if (strcmp(pszLine + strlen("report_all_ways="), "no") == 0)
3494
0
            {
3495
0
                m_bReportAllWays = false;
3496
0
            }
3497
0
            else if (strcmp(pszLine + strlen("report_all_ways="), "yes") == 0)
3498
0
            {
3499
0
                m_bReportAllWays = true;
3500
0
            }
3501
0
        }
3502
3503
0
        else if (STARTS_WITH(pszLine, "attribute_name_laundering="))
3504
0
        {
3505
0
            if (strcmp(pszLine + strlen("attribute_name_laundering="), "no") ==
3506
0
                0)
3507
0
            {
3508
0
                m_bAttributeNameLaundering = false;
3509
0
            }
3510
0
            else if (strcmp(pszLine + strlen("attribute_name_laundering="),
3511
0
                            "yes") == 0)
3512
0
            {
3513
0
                m_bAttributeNameLaundering = true;
3514
0
            }
3515
0
        }
3516
3517
0
        else if (STARTS_WITH(pszLine, "tags_format="))
3518
0
        {
3519
0
            if (EQUAL(pszLine + strlen("tags_format="), "json"))
3520
0
            {
3521
0
                m_bTagsAsHSTORE = false;
3522
0
            }
3523
0
            else if (EQUAL(pszLine + strlen("tags_format="), "hstore"))
3524
0
            {
3525
0
                m_bTagsAsHSTORE = true;
3526
0
            }
3527
0
            else
3528
0
            {
3529
0
                CPLError(CE_Warning, CPLE_NotSupported,
3530
0
                         "Unsupported value for tags_format: %s",
3531
0
                         pszLine + strlen("tags_format="));
3532
0
            }
3533
0
        }
3534
3535
0
        else if (iCurLayer >= 0)
3536
0
        {
3537
0
            char **papszTokens = CSLTokenizeString2(pszLine, "=", 0);
3538
0
            if (CSLCount(papszTokens) == 2 &&
3539
0
                strcmp(papszTokens[0], "other_tags") == 0)
3540
0
            {
3541
0
                if (strcmp(papszTokens[1], "no") == 0)
3542
0
                    m_apoLayers[iCurLayer]->SetHasOtherTags(false);
3543
0
                else if (strcmp(papszTokens[1], "yes") == 0)
3544
0
                    m_apoLayers[iCurLayer]->SetHasOtherTags(true);
3545
0
            }
3546
0
            else if (CSLCount(papszTokens) == 2 &&
3547
0
                     strcmp(papszTokens[0], "all_tags") == 0)
3548
0
            {
3549
0
                if (strcmp(papszTokens[1], "no") == 0)
3550
0
                    m_apoLayers[iCurLayer]->SetHasAllTags(false);
3551
0
                else if (strcmp(papszTokens[1], "yes") == 0)
3552
0
                    m_apoLayers[iCurLayer]->SetHasAllTags(true);
3553
0
            }
3554
0
            else if (CSLCount(papszTokens) == 2 &&
3555
0
                     strcmp(papszTokens[0], "osm_id") == 0)
3556
0
            {
3557
0
                if (strcmp(papszTokens[1], "no") == 0)
3558
0
                    m_apoLayers[iCurLayer]->SetHasOSMId(false);
3559
0
                else if (strcmp(papszTokens[1], "yes") == 0)
3560
0
                {
3561
0
                    m_apoLayers[iCurLayer]->SetHasOSMId(true);
3562
0
                    m_apoLayers[iCurLayer]->AddField("osm_id", OFTString);
3563
3564
0
                    if (iCurLayer == IDX_LYR_MULTIPOLYGONS)
3565
0
                        m_apoLayers[iCurLayer]->AddField("osm_way_id",
3566
0
                                                         OFTString);
3567
0
                }
3568
0
            }
3569
0
            else if (CSLCount(papszTokens) == 2 &&
3570
0
                     strcmp(papszTokens[0], "osm_version") == 0)
3571
0
            {
3572
0
                if (strcmp(papszTokens[1], "no") == 0)
3573
0
                    m_apoLayers[iCurLayer]->SetHasVersion(false);
3574
0
                else if (strcmp(papszTokens[1], "yes") == 0)
3575
0
                {
3576
0
                    m_apoLayers[iCurLayer]->SetHasVersion(true);
3577
0
                    m_apoLayers[iCurLayer]->AddField("osm_version", OFTInteger);
3578
0
                }
3579
0
            }
3580
0
            else if (CSLCount(papszTokens) == 2 &&
3581
0
                     strcmp(papszTokens[0], "osm_timestamp") == 0)
3582
0
            {
3583
0
                if (strcmp(papszTokens[1], "no") == 0)
3584
0
                    m_apoLayers[iCurLayer]->SetHasTimestamp(false);
3585
0
                else if (strcmp(papszTokens[1], "yes") == 0)
3586
0
                {
3587
0
                    m_apoLayers[iCurLayer]->SetHasTimestamp(true);
3588
0
                    m_apoLayers[iCurLayer]->AddField("osm_timestamp",
3589
0
                                                     OFTDateTime);
3590
0
                }
3591
0
            }
3592
0
            else if (CSLCount(papszTokens) == 2 &&
3593
0
                     strcmp(papszTokens[0], "osm_uid") == 0)
3594
0
            {
3595
0
                if (strcmp(papszTokens[1], "no") == 0)
3596
0
                    m_apoLayers[iCurLayer]->SetHasUID(false);
3597
0
                else if (strcmp(papszTokens[1], "yes") == 0)
3598
0
                {
3599
0
                    m_apoLayers[iCurLayer]->SetHasUID(true);
3600
0
                    m_apoLayers[iCurLayer]->AddField("osm_uid", OFTInteger);
3601
0
                }
3602
0
            }
3603
0
            else if (CSLCount(papszTokens) == 2 &&
3604
0
                     strcmp(papszTokens[0], "osm_user") == 0)
3605
0
            {
3606
0
                if (strcmp(papszTokens[1], "no") == 0)
3607
0
                    m_apoLayers[iCurLayer]->SetHasUser(false);
3608
0
                else if (strcmp(papszTokens[1], "yes") == 0)
3609
0
                {
3610
0
                    m_apoLayers[iCurLayer]->SetHasUser(true);
3611
0
                    m_apoLayers[iCurLayer]->AddField("osm_user", OFTString);
3612
0
                }
3613
0
            }
3614
0
            else if (CSLCount(papszTokens) == 2 &&
3615
0
                     strcmp(papszTokens[0], "osm_changeset") == 0)
3616
0
            {
3617
0
                if (strcmp(papszTokens[1], "no") == 0)
3618
0
                    m_apoLayers[iCurLayer]->SetHasChangeset(false);
3619
0
                else if (strcmp(papszTokens[1], "yes") == 0)
3620
0
                {
3621
0
                    m_apoLayers[iCurLayer]->SetHasChangeset(true);
3622
0
                    m_apoLayers[iCurLayer]->AddField("osm_changeset",
3623
0
                                                     OFTInteger);
3624
0
                }
3625
0
            }
3626
0
            else if (CSLCount(papszTokens) == 2 &&
3627
0
                     strcmp(papszTokens[0], "attributes") == 0)
3628
0
            {
3629
0
                char **papszTokens2 =
3630
0
                    CSLTokenizeString2(papszTokens[1], ",", 0);
3631
0
                for (int i = 0; papszTokens2[i] != nullptr; i++)
3632
0
                {
3633
0
                    m_apoLayers[iCurLayer]->AddField(papszTokens2[i],
3634
0
                                                     OFTString);
3635
0
                    for (const char *&pszIgnoredKey : m_ignoredKeys)
3636
0
                    {
3637
0
                        if (strcmp(papszTokens2[i], pszIgnoredKey) == 0)
3638
0
                            pszIgnoredKey = "";
3639
0
                    }
3640
0
                }
3641
0
                CSLDestroy(papszTokens2);
3642
0
            }
3643
0
            else if (CSLCount(papszTokens) == 2 &&
3644
0
                     (strcmp(papszTokens[0], "unsignificant") == 0 ||
3645
0
                      strcmp(papszTokens[0], "insignificant") == 0))
3646
0
            {
3647
0
                char **papszTokens2 =
3648
0
                    CSLTokenizeString2(papszTokens[1], ",", 0);
3649
0
                for (int i = 0; papszTokens2[i] != nullptr; i++)
3650
0
                {
3651
0
                    m_apoLayers[iCurLayer]->AddInsignificantKey(
3652
0
                        papszTokens2[i]);
3653
0
                }
3654
0
                CSLDestroy(papszTokens2);
3655
0
            }
3656
0
            else if (CSLCount(papszTokens) == 2 &&
3657
0
                     strcmp(papszTokens[0], "ignore") == 0)
3658
0
            {
3659
0
                char **papszTokens2 =
3660
0
                    CSLTokenizeString2(papszTokens[1], ",", 0);
3661
0
                for (int i = 0; papszTokens2[i] != nullptr; i++)
3662
0
                {
3663
0
                    m_apoLayers[iCurLayer]->AddIgnoreKey(papszTokens2[i]);
3664
0
                    m_apoLayers[iCurLayer]->AddWarnKey(papszTokens2[i]);
3665
0
                }
3666
0
                CSLDestroy(papszTokens2);
3667
0
            }
3668
0
            else if (CSLCount(papszTokens) == 2 &&
3669
0
                     strcmp(papszTokens[0], "computed_attributes") == 0)
3670
0
            {
3671
0
                char **papszTokens2 =
3672
0
                    CSLTokenizeString2(papszTokens[1], ",", 0);
3673
0
                oAttributes.resize(0);
3674
0
                for (int i = 0; papszTokens2[i] != nullptr; i++)
3675
0
                {
3676
0
                    oAttributes.push_back(
3677
0
                        OGROSMComputedAttribute(papszTokens2[i]));
3678
0
                }
3679
0
                CSLDestroy(papszTokens2);
3680
0
            }
3681
0
            else if (CSLCount(papszTokens) == 2 &&
3682
0
                     strlen(papszTokens[0]) >= 5 &&
3683
0
                     strcmp(papszTokens[0] + strlen(papszTokens[0]) - 5,
3684
0
                            "_type") == 0)
3685
0
            {
3686
0
                CPLString osName(papszTokens[0]);
3687
0
                osName.resize(strlen(papszTokens[0]) - 5);
3688
0
                const char *pszType = papszTokens[1];
3689
0
                bool bFound = false;
3690
0
                OGRFieldType eType = OFTString;
3691
0
                if (EQUAL(pszType, "Integer"))
3692
0
                    eType = OFTInteger;
3693
0
                else if (EQUAL(pszType, "Integer64"))
3694
0
                    eType = OFTInteger64;
3695
0
                else if (EQUAL(pszType, "Real"))
3696
0
                    eType = OFTReal;
3697
0
                else if (EQUAL(pszType, "String"))
3698
0
                    eType = OFTString;
3699
0
                else if (EQUAL(pszType, "DateTime"))
3700
0
                    eType = OFTDateTime;
3701
0
                else
3702
0
                    CPLError(CE_Warning, CPLE_AppDefined,
3703
0
                             "Unhandled type (%s) for attribute %s", pszType,
3704
0
                             osName.c_str());
3705
0
                for (size_t i = 0; i < oAttributes.size(); i++)
3706
0
                {
3707
0
                    if (oAttributes[i].osName == osName)
3708
0
                    {
3709
0
                        bFound = true;
3710
0
                        oAttributes[i].eType = eType;
3711
0
                        break;
3712
0
                    }
3713
0
                }
3714
0
                if (!bFound)
3715
0
                {
3716
0
                    const int idx =
3717
0
                        m_apoLayers[iCurLayer]->GetLayerDefn()->GetFieldIndex(
3718
0
                            osName);
3719
0
                    if (idx >= 0)
3720
0
                    {
3721
0
                        m_apoLayers[iCurLayer]
3722
0
                            ->GetLayerDefn()
3723
0
                            ->GetFieldDefn(idx)
3724
0
                            ->SetType(eType);
3725
0
                        bFound = true;
3726
0
                    }
3727
0
                }
3728
0
                if (!bFound)
3729
0
                {
3730
0
                    CPLError(CE_Warning, CPLE_AppDefined,
3731
0
                             "Undeclared attribute : %s", osName.c_str());
3732
0
                }
3733
0
            }
3734
0
            else if (CSLCount(papszTokens) >= 2 &&
3735
0
                     strlen(papszTokens[0]) >= 4 &&
3736
0
                     strcmp(papszTokens[0] + strlen(papszTokens[0]) - 4,
3737
0
                            "_sql") == 0)
3738
0
            {
3739
0
                CPLString osName(papszTokens[0]);
3740
0
                osName.resize(strlen(papszTokens[0]) - 4);
3741
0
                size_t i = 0;  // Used after for.
3742
0
                for (; i < oAttributes.size(); i++)
3743
0
                {
3744
0
                    if (oAttributes[i].osName == osName)
3745
0
                    {
3746
0
                        const char *pszSQL = strchr(pszLine, '=') + 1;
3747
0
                        while (*pszSQL == ' ')
3748
0
                            pszSQL++;
3749
0
                        bool bInQuotes = false;
3750
0
                        if (*pszSQL == '"')
3751
0
                        {
3752
0
                            bInQuotes = true;
3753
0
                            pszSQL++;
3754
0
                        }
3755
0
                        oAttributes[i].osSQL = pszSQL;
3756
0
                        if (bInQuotes && oAttributes[i].osSQL.size() > 1 &&
3757
0
                            oAttributes[i].osSQL.back() == '"')
3758
0
                            oAttributes[i].osSQL.resize(
3759
0
                                oAttributes[i].osSQL.size() - 1);
3760
0
                        break;
3761
0
                    }
3762
0
                }
3763
0
                if (i == oAttributes.size())
3764
0
                {
3765
0
                    CPLError(CE_Warning, CPLE_AppDefined,
3766
0
                             "Undeclared attribute : %s", osName.c_str());
3767
0
                }
3768
0
            }
3769
0
            CSLDestroy(papszTokens);
3770
0
        }
3771
0
    }
3772
3773
0
    if (iCurLayer >= 0)
3774
0
        AddComputedAttributes(iCurLayer, oAttributes);
3775
3776
0
    VSIFCloseL(fpConf);
3777
3778
0
    return true;
3779
0
}
3780
3781
/************************************************************************/
3782
/*                          MyResetReading()                            */
3783
/************************************************************************/
3784
3785
int OGROSMDataSource::MyResetReading()
3786
0
{
3787
0
    if (m_hDB == nullptr)
3788
0
        return FALSE;
3789
0
    if (m_bCustomIndexing && m_fpNodes == nullptr)
3790
0
        return FALSE;
3791
3792
0
    OSM_ResetReading(m_psParser);
3793
3794
0
    char *pszErrMsg = nullptr;
3795
0
    int rc =
3796
0
        sqlite3_exec(m_hDB, "DELETE FROM nodes", nullptr, nullptr, &pszErrMsg);
3797
0
    if (rc != SQLITE_OK)
3798
0
    {
3799
0
        CPLError(CE_Failure, CPLE_AppDefined,
3800
0
                 "Unable to DELETE FROM nodes : %s", pszErrMsg);
3801
0
        sqlite3_free(pszErrMsg);
3802
0
        return FALSE;
3803
0
    }
3804
3805
0
    rc = sqlite3_exec(m_hDB, "DELETE FROM ways", nullptr, nullptr, &pszErrMsg);
3806
0
    if (rc != SQLITE_OK)
3807
0
    {
3808
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unable to DELETE FROM ways : %s",
3809
0
                 pszErrMsg);
3810
0
        sqlite3_free(pszErrMsg);
3811
0
        return FALSE;
3812
0
    }
3813
3814
0
    rc = sqlite3_exec(m_hDB, "DELETE FROM polygons_standalone", nullptr,
3815
0
                      nullptr, &pszErrMsg);
3816
0
    if (rc != SQLITE_OK)
3817
0
    {
3818
0
        CPLError(CE_Failure, CPLE_AppDefined,
3819
0
                 "Unable to DELETE FROM polygons_standalone : %s", pszErrMsg);
3820
0
        sqlite3_free(pszErrMsg);
3821
0
        return FALSE;
3822
0
    }
3823
0
    m_bHasRowInPolygonsStandalone = false;
3824
3825
0
    if (m_hSelectPolygonsStandaloneStmt != nullptr)
3826
0
        sqlite3_reset(m_hSelectPolygonsStandaloneStmt);
3827
3828
0
    {
3829
0
        m_asWayFeaturePairs.clear();
3830
0
        m_nUnsortedReqIds = 0;
3831
0
        m_nReqIds = 0;
3832
0
        m_nAccumulatedTags = 0;
3833
0
        nNonRedundantKeysLen = 0;
3834
0
        nNonRedundantValuesLen = 0;
3835
3836
0
        for (KeyDesc *psKD : m_apsKeys)
3837
0
        {
3838
0
            if (psKD)
3839
0
            {
3840
0
                CPLFree(psKD->pszK);
3841
0
                for (auto *pszValue : psKD->apszValues)
3842
0
                    CPLFree(pszValue);
3843
0
                delete psKD;
3844
0
            }
3845
0
        }
3846
0
        m_apsKeys.resize(1);  // keep guard to avoid index 0 to be used
3847
0
        m_aoMapIndexedKeys.clear();
3848
0
    }
3849
3850
0
    if (m_bCustomIndexing)
3851
0
    {
3852
0
        m_nPrevNodeId = -1;
3853
0
        m_nBucketOld = -1;
3854
0
        m_nOffInBucketReducedOld = -1;
3855
3856
0
        VSIFSeekL(m_fpNodes, 0, SEEK_SET);
3857
0
        VSIFTruncateL(m_fpNodes, 0);
3858
0
        m_nNodesFileSize = 0;
3859
3860
0
        memset(m_pabySector, 0, SECTOR_SIZE);
3861
3862
0
        for (auto &oIter : m_oMapBuckets)
3863
0
        {
3864
0
            Bucket &sBucket = oIter.second;
3865
0
            sBucket.nOff = -1;
3866
0
            if (m_bCompressNodes)
3867
0
            {
3868
0
                if (sBucket.u.panSectorSize)
3869
0
                    memset(sBucket.u.panSectorSize, 0,
3870
0
                           BUCKET_SECTOR_SIZE_ARRAY_SIZE);
3871
0
            }
3872
0
            else
3873
0
            {
3874
0
                if (sBucket.u.pabyBitmap)
3875
0
                    memset(sBucket.u.pabyBitmap, 0, BUCKET_BITMAP_SIZE);
3876
0
            }
3877
0
        }
3878
0
    }
3879
3880
0
    for (auto &&poLayer : m_apoLayers)
3881
0
    {
3882
0
        poLayer->ForceResetReading();
3883
0
    }
3884
3885
0
    m_bStopParsing = false;
3886
0
    m_poCurrentLayer = nullptr;
3887
3888
0
    return TRUE;
3889
0
}
3890
3891
/************************************************************************/
3892
/*                             ResetReading()                           */
3893
/************************************************************************/
3894
3895
void OGROSMDataSource::ResetReading()
3896
0
{
3897
0
    MyResetReading();
3898
0
}
3899
3900
/************************************************************************/
3901
/*                           GetNextFeature()                           */
3902
/************************************************************************/
3903
3904
OGRFeature *OGROSMDataSource::GetNextFeature(OGRLayer **ppoBelongingLayer,
3905
                                             double *pdfProgressPct,
3906
                                             GDALProgressFunc pfnProgress,
3907
                                             void *pProgressData)
3908
0
{
3909
0
    m_bInterleavedReading = TRUE;
3910
3911
0
    if (m_poCurrentLayer == nullptr)
3912
0
    {
3913
0
        m_poCurrentLayer = m_apoLayers[0].get();
3914
0
    }
3915
0
    if (pdfProgressPct != nullptr || pfnProgress != nullptr)
3916
0
    {
3917
0
        if (m_nFileSize == FILESIZE_NOT_INIT)
3918
0
        {
3919
0
            VSIStatBufL sStat;
3920
0
            if (VSIStatL(GetDescription(), &sStat) == 0)
3921
0
            {
3922
0
                m_nFileSize = static_cast<GIntBig>(sStat.st_size);
3923
0
            }
3924
0
            else
3925
0
            {
3926
0
                m_nFileSize = FILESIZE_INVALID;
3927
0
            }
3928
0
        }
3929
0
    }
3930
3931
0
    while (true)
3932
0
    {
3933
0
        OGROSMLayer *poNewCurLayer = nullptr;
3934
0
        CPLAssert(m_poCurrentLayer != nullptr);
3935
0
        OGRFeature *poFeature = m_poCurrentLayer->MyGetNextFeature(
3936
0
            &poNewCurLayer, pfnProgress, pProgressData);
3937
0
        m_poCurrentLayer = poNewCurLayer;
3938
0
        if (poFeature == nullptr)
3939
0
        {
3940
0
            if (m_poCurrentLayer != nullptr)
3941
0
                continue;
3942
0
            if (ppoBelongingLayer != nullptr)
3943
0
                *ppoBelongingLayer = nullptr;
3944
0
            if (pdfProgressPct != nullptr)
3945
0
                *pdfProgressPct = 1.0;
3946
0
            return nullptr;
3947
0
        }
3948
0
        if (ppoBelongingLayer != nullptr)
3949
0
            *ppoBelongingLayer = m_poCurrentLayer;
3950
0
        if (pdfProgressPct != nullptr)
3951
0
        {
3952
0
            if (m_nFileSize != FILESIZE_INVALID)
3953
0
            {
3954
0
                *pdfProgressPct =
3955
0
                    1.0 * OSM_GetBytesRead(m_psParser) / m_nFileSize;
3956
0
            }
3957
0
            else
3958
0
            {
3959
0
                *pdfProgressPct = -1.0;
3960
0
            }
3961
0
        }
3962
3963
0
        return poFeature;
3964
0
    }
3965
0
}
3966
3967
/************************************************************************/
3968
/*                           ParseNextChunk()                           */
3969
/************************************************************************/
3970
3971
bool OGROSMDataSource::ParseNextChunk(int nIdxLayer,
3972
                                      GDALProgressFunc pfnProgress,
3973
                                      void *pProgressData)
3974
0
{
3975
0
    if (m_bStopParsing)
3976
0
        return false;
3977
3978
0
    m_bHasParsedFirstChunk = true;
3979
0
    m_bFeatureAdded = false;
3980
0
    while (true)
3981
0
    {
3982
#ifdef DEBUG_MEM_USAGE
3983
        static int counter = 0;
3984
        counter++;
3985
        if ((counter % 1000) == 0)
3986
            CPLDebug("OSM", "GetMaxTotalAllocs() = " CPL_FRMT_GUIB,
3987
                     static_cast<GUIntBig>(GetMaxTotalAllocs()));
3988
#endif
3989
3990
0
        OSMRetCode eRet = OSM_ProcessBlock(m_psParser);
3991
0
        if (pfnProgress != nullptr)
3992
0
        {
3993
0
            double dfPct = -1.0;
3994
0
            if (m_nFileSize != FILESIZE_INVALID)
3995
0
            {
3996
0
                dfPct = 1.0 * OSM_GetBytesRead(m_psParser) / m_nFileSize;
3997
0
            }
3998
0
            if (!pfnProgress(dfPct, "", pProgressData))
3999
0
            {
4000
0
                m_bStopParsing = true;
4001
0
                for (auto &&poLayer : m_apoLayers)
4002
0
                {
4003
0
                    poLayer->ForceResetReading();
4004
0
                }
4005
0
                return false;
4006
0
            }
4007
0
        }
4008
4009
0
        if (eRet == OSM_EOF || eRet == OSM_ERROR)
4010
0
        {
4011
0
            if (eRet == OSM_EOF)
4012
0
            {
4013
0
                if (!m_asWayFeaturePairs.empty())
4014
0
                    ProcessWaysBatch();
4015
4016
0
                ProcessPolygonsStandalone();
4017
4018
0
                if (!m_bHasRowInPolygonsStandalone)
4019
0
                    m_bStopParsing = true;
4020
4021
0
                if (!m_bInterleavedReading && !m_bFeatureAdded &&
4022
0
                    m_bHasRowInPolygonsStandalone &&
4023
0
                    nIdxLayer != IDX_LYR_MULTIPOLYGONS)
4024
0
                {
4025
0
                    return false;
4026
0
                }
4027
4028
0
                return m_bFeatureAdded || m_bHasRowInPolygonsStandalone;
4029
0
            }
4030
0
            else
4031
0
            {
4032
0
                CPLError(CE_Failure, CPLE_AppDefined,
4033
0
                         "An error occurred during the parsing of data "
4034
0
                         "around byte " CPL_FRMT_GUIB,
4035
0
                         OSM_GetBytesRead(m_psParser));
4036
4037
0
                m_bStopParsing = true;
4038
0
                return false;
4039
0
            }
4040
0
        }
4041
0
        else
4042
0
        {
4043
0
            if (m_bInMemoryTmpDB)
4044
0
            {
4045
0
                if (!TransferToDiskIfNecesserary())
4046
0
                    return false;
4047
0
            }
4048
4049
0
            if (m_bFeatureAdded)
4050
0
                break;
4051
0
        }
4052
0
    }
4053
4054
0
    return true;
4055
0
}
4056
4057
/************************************************************************/
4058
/*                    TransferToDiskIfNecesserary()                     */
4059
/************************************************************************/
4060
4061
bool OGROSMDataSource::TransferToDiskIfNecesserary()
4062
0
{
4063
0
    if (m_bInMemoryNodesFile)
4064
0
    {
4065
0
        if (m_nNodesFileSize / 1024 / 1024 >
4066
0
            3 * m_nMaxSizeForInMemoryDBInMB / 4)
4067
0
        {
4068
0
            m_bInMemoryNodesFile = false;
4069
4070
0
            VSIFCloseL(m_fpNodes);
4071
0
            m_fpNodes = nullptr;
4072
4073
0
            const std::string osNewTmpDBName(
4074
0
                CPLGenerateTempFilenameSafe("osm_tmp_nodes"));
4075
4076
0
            CPLDebug("OSM",
4077
0
                     "%s too big for RAM. Transferring it onto disk in %s",
4078
0
                     m_osNodesFilename.c_str(), osNewTmpDBName.c_str());
4079
4080
0
            if (CPLCopyFile(osNewTmpDBName.c_str(), m_osNodesFilename) != 0)
4081
0
            {
4082
0
                CPLError(CE_Failure, CPLE_AppDefined, "Cannot copy %s to %s",
4083
0
                         m_osNodesFilename.c_str(), osNewTmpDBName.c_str());
4084
0
                VSIUnlink(osNewTmpDBName.c_str());
4085
0
                m_bStopParsing = true;
4086
0
                return false;
4087
0
            }
4088
4089
0
            VSIUnlink(m_osNodesFilename);
4090
4091
0
            if (m_bInMemoryTmpDB)
4092
0
            {
4093
                /* Try to grow the sqlite in memory-db to the full space now */
4094
                /* it has been freed. */
4095
0
                VSILFILE *fp = VSIFOpenL(m_osTmpDBName, "rb+");
4096
0
                if (fp)
4097
0
                {
4098
0
                    VSIFSeekL(fp, 0, SEEK_END);
4099
0
                    vsi_l_offset nCurSize = VSIFTellL(fp);
4100
0
                    vsi_l_offset nNewSize =
4101
0
                        static_cast<vsi_l_offset>(m_nMaxSizeForInMemoryDBInMB) *
4102
0
                        1024 * 1024;
4103
0
                    CPLPushErrorHandler(CPLQuietErrorHandler);
4104
0
                    const bool bSuccess =
4105
0
                        VSIFSeekL(fp, nNewSize, SEEK_SET) == 0;
4106
0
                    CPLPopErrorHandler();
4107
4108
0
                    if (bSuccess)
4109
0
                        VSIFTruncateL(fp, nCurSize);
4110
4111
0
                    VSIFCloseL(fp);
4112
0
                }
4113
0
            }
4114
4115
0
            m_osNodesFilename = osNewTmpDBName;
4116
4117
0
            m_fpNodes = VSIFOpenL(m_osNodesFilename, "rb+");
4118
0
            if (m_fpNodes == nullptr)
4119
0
            {
4120
0
                m_bStopParsing = true;
4121
0
                return false;
4122
0
            }
4123
4124
0
            VSIFSeekL(m_fpNodes, 0, SEEK_END);
4125
4126
            /* On Unix filesystems, you can remove a file even if it */
4127
            /* opened */
4128
0
            const char *pszVal =
4129
0
                CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
4130
0
            if (EQUAL(pszVal, "YES"))
4131
0
            {
4132
0
                CPLPushErrorHandler(CPLQuietErrorHandler);
4133
0
                m_bMustUnlinkNodesFile = VSIUnlink(m_osNodesFilename) != 0;
4134
0
                CPLPopErrorHandler();
4135
0
            }
4136
0
        }
4137
0
    }
4138
4139
0
    if (m_bInMemoryTmpDB)
4140
0
    {
4141
0
        VSIStatBufL sStat;
4142
4143
0
        int nLimitMB = m_nMaxSizeForInMemoryDBInMB;
4144
0
        if (m_bCustomIndexing && m_bInMemoryNodesFile)
4145
0
            nLimitMB = nLimitMB * 1 / 4;
4146
4147
0
        if (VSIStatL(m_osTmpDBName, &sStat) == 0 &&
4148
0
            sStat.st_size / 1024 / 1024 > nLimitMB)
4149
0
        {
4150
0
            m_bInMemoryTmpDB = false;
4151
4152
0
            CloseDB();
4153
4154
0
            const std::string osNewTmpDBName(
4155
0
                CPLGenerateTempFilenameSafe("osm_tmp"));
4156
4157
0
            CPLDebug("OSM",
4158
0
                     "%s too big for RAM. Transferring it onto disk in %s",
4159
0
                     m_osTmpDBName.c_str(), osNewTmpDBName.c_str());
4160
4161
0
            if (CPLCopyFile(osNewTmpDBName.c_str(), m_osTmpDBName) != 0)
4162
0
            {
4163
0
                CPLError(CE_Failure, CPLE_AppDefined, "Cannot copy %s to %s",
4164
0
                         m_osTmpDBName.c_str(), osNewTmpDBName.c_str());
4165
0
                VSIUnlink(osNewTmpDBName.c_str());
4166
0
                m_bStopParsing = true;
4167
0
                return false;
4168
0
            }
4169
4170
0
            VSIUnlink(m_osTmpDBName);
4171
4172
0
            m_osTmpDBName = osNewTmpDBName;
4173
4174
0
            const int rc = sqlite3_open_v2(
4175
0
                m_osTmpDBName.c_str(), &m_hDB,
4176
0
                SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX, nullptr);
4177
0
            if (rc != SQLITE_OK)
4178
0
            {
4179
0
                CPLError(CE_Failure, CPLE_OpenFailed,
4180
0
                         "sqlite3_open(%s) failed: %s", m_osTmpDBName.c_str(),
4181
0
                         sqlite3_errmsg(m_hDB));
4182
0
                m_bStopParsing = true;
4183
0
                CloseDB();
4184
0
                return false;
4185
0
            }
4186
4187
            /* On Unix filesystems, you can remove a file even if it */
4188
            /* opened */
4189
0
            const char *pszVal =
4190
0
                CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
4191
0
            if (EQUAL(pszVal, "YES"))
4192
0
            {
4193
0
                CPLPushErrorHandler(CPLQuietErrorHandler);
4194
0
                m_bMustUnlink = VSIUnlink(m_osTmpDBName) != 0;
4195
0
                CPLPopErrorHandler();
4196
0
            }
4197
4198
0
            if (!SetDBOptions() || !CreatePreparedStatements())
4199
0
            {
4200
0
                m_bStopParsing = true;
4201
0
                CloseDB();
4202
0
                return false;
4203
0
            }
4204
0
        }
4205
0
    }
4206
4207
0
    return true;
4208
0
}
4209
4210
/************************************************************************/
4211
/*                           TestCapability()                           */
4212
/************************************************************************/
4213
4214
int OGROSMDataSource::TestCapability(const char *pszCap)
4215
0
{
4216
0
    return EQUAL(pszCap, ODsCRandomLayerRead);
4217
0
}
4218
4219
/************************************************************************/
4220
/*                              GetLayer()                              */
4221
/************************************************************************/
4222
4223
OGRLayer *OGROSMDataSource::GetLayer(int iLayer)
4224
4225
0
{
4226
0
    if (iLayer < 0 || static_cast<size_t>(iLayer) >= m_apoLayers.size())
4227
0
        return nullptr;
4228
4229
0
    return m_apoLayers[iLayer].get();
4230
0
}
4231
4232
/************************************************************************/
4233
/*                             GetExtent()                              */
4234
/************************************************************************/
4235
4236
OGRErr OGROSMDataSource::GetExtent(OGREnvelope *psExtent)
4237
0
{
4238
0
    if (!m_bHasParsedFirstChunk)
4239
0
    {
4240
0
        m_bHasParsedFirstChunk = true;
4241
0
        OSM_ProcessBlock(m_psParser);
4242
0
    }
4243
4244
0
    if (m_bExtentValid)
4245
0
    {
4246
0
        *psExtent = m_sExtent;
4247
0
        return OGRERR_NONE;
4248
0
    }
4249
4250
0
    return OGRERR_FAILURE;
4251
0
}
4252
4253
/************************************************************************/
4254
/*                   OGROSMSingleFeatureLayer                           */
4255
/************************************************************************/
4256
4257
class OGROSMSingleFeatureLayer final : public OGRLayer
4258
{
4259
  private:
4260
    int nVal;
4261
    char *pszVal;
4262
    OGRFeatureDefn *poFeatureDefn;
4263
    int iNextShapeId;
4264
4265
    OGROSMSingleFeatureLayer(const OGROSMSingleFeatureLayer &) = delete;
4266
    OGROSMSingleFeatureLayer &
4267
    operator=(const OGROSMSingleFeatureLayer &) = delete;
4268
4269
  public:
4270
    OGROSMSingleFeatureLayer(const char *pszLayerName, int nVal);
4271
    OGROSMSingleFeatureLayer(const char *pszLayerName, const char *pszVal);
4272
    virtual ~OGROSMSingleFeatureLayer();
4273
4274
    virtual void ResetReading() override
4275
0
    {
4276
0
        iNextShapeId = 0;
4277
0
    }
4278
4279
    virtual OGRFeature *GetNextFeature() override;
4280
4281
    virtual OGRFeatureDefn *GetLayerDefn() override
4282
0
    {
4283
0
        return poFeatureDefn;
4284
0
    }
4285
4286
    virtual int TestCapability(const char *) override
4287
0
    {
4288
0
        return FALSE;
4289
0
    }
4290
};
4291
4292
/************************************************************************/
4293
/*                    OGROSMSingleFeatureLayer()                        */
4294
/************************************************************************/
4295
4296
OGROSMSingleFeatureLayer::OGROSMSingleFeatureLayer(const char *pszLayerName,
4297
                                                   int nValIn)
4298
0
    : nVal(nValIn), pszVal(nullptr),
4299
0
      poFeatureDefn(new OGRFeatureDefn("SELECT")), iNextShapeId(0)
4300
0
{
4301
0
    poFeatureDefn->Reference();
4302
0
    OGRFieldDefn oField(pszLayerName, OFTInteger);
4303
0
    poFeatureDefn->AddFieldDefn(&oField);
4304
0
}
4305
4306
/************************************************************************/
4307
/*                    OGROSMSingleFeatureLayer()                        */
4308
/************************************************************************/
4309
4310
OGROSMSingleFeatureLayer::OGROSMSingleFeatureLayer(const char *pszLayerName,
4311
                                                   const char *pszValIn)
4312
0
    : nVal(0), pszVal(CPLStrdup(pszValIn)),
4313
0
      poFeatureDefn(new OGRFeatureDefn("SELECT")), iNextShapeId(0)
4314
0
{
4315
0
    poFeatureDefn->Reference();
4316
0
    OGRFieldDefn oField(pszLayerName, OFTString);
4317
0
    poFeatureDefn->AddFieldDefn(&oField);
4318
0
}
4319
4320
/************************************************************************/
4321
/*                    ~OGROSMSingleFeatureLayer()                       */
4322
/************************************************************************/
4323
4324
OGROSMSingleFeatureLayer::~OGROSMSingleFeatureLayer()
4325
0
{
4326
0
    poFeatureDefn->Release();
4327
0
    CPLFree(pszVal);
4328
0
}
4329
4330
/************************************************************************/
4331
/*                           GetNextFeature()                           */
4332
/************************************************************************/
4333
4334
OGRFeature *OGROSMSingleFeatureLayer::GetNextFeature()
4335
0
{
4336
0
    if (iNextShapeId != 0)
4337
0
        return nullptr;
4338
4339
0
    OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
4340
0
    if (pszVal)
4341
0
        poFeature->SetField(0, pszVal);
4342
0
    else
4343
0
        poFeature->SetField(0, nVal);
4344
0
    poFeature->SetFID(iNextShapeId++);
4345
0
    return poFeature;
4346
0
}
4347
4348
/************************************************************************/
4349
/*                      OGROSMResultLayerDecorator                      */
4350
/************************************************************************/
4351
4352
class OGROSMResultLayerDecorator final : public OGRLayerDecorator
4353
{
4354
    std::string osDSName;
4355
    std::string osInterestLayers;
4356
4357
  public:
4358
    OGROSMResultLayerDecorator(OGRLayer *poLayer, const std::string &osDSNameIn,
4359
                               const std::string &osInterestLayersIn)
4360
0
        : OGRLayerDecorator(poLayer, TRUE), osDSName(osDSNameIn),
4361
0
          osInterestLayers(osInterestLayersIn)
4362
0
    {
4363
0
    }
4364
4365
    GIntBig GetFeatureCount(int bForce = TRUE) override;
4366
};
4367
4368
GIntBig OGROSMResultLayerDecorator::GetFeatureCount(int bForce)
4369
0
{
4370
    /* When we run GetFeatureCount() with SQLite SQL dialect, */
4371
    /* the OSM dataset will be re-opened. Make sure that it is */
4372
    /* re-opened with the same interest layers */
4373
0
    AddInterestLayersForDSName(osDSName, osInterestLayers);
4374
0
    return OGRLayerDecorator::GetFeatureCount(bForce);
4375
0
}
4376
4377
/************************************************************************/
4378
/*                             ExecuteSQL()                             */
4379
/************************************************************************/
4380
4381
OGRLayer *OGROSMDataSource::ExecuteSQL(const char *pszSQLCommand,
4382
                                       OGRGeometry *poSpatialFilter,
4383
                                       const char *pszDialect)
4384
4385
0
{
4386
    /* -------------------------------------------------------------------- */
4387
    /*      Special GetBytesRead() command                                  */
4388
    /* -------------------------------------------------------------------- */
4389
0
    if (strcmp(pszSQLCommand, "GetBytesRead()") == 0)
4390
0
    {
4391
0
        char szVal[64] = {};
4392
0
        snprintf(szVal, sizeof(szVal), CPL_FRMT_GUIB,
4393
0
                 OSM_GetBytesRead(m_psParser));
4394
0
        return new OGROSMSingleFeatureLayer("GetBytesRead", szVal);
4395
0
    }
4396
4397
    /* -------------------------------------------------------------------- */
4398
    /*      Special SHOW config_file_path command                           */
4399
    /* -------------------------------------------------------------------- */
4400
0
    if (strcmp(pszSQLCommand, "SHOW config_file_path") == 0)
4401
0
    {
4402
0
        return new OGROSMSingleFeatureLayer("config_file_path",
4403
0
                                            m_osConfigFile.c_str());
4404
0
    }
4405
4406
0
    if (m_poResultSetLayer != nullptr)
4407
0
    {
4408
0
        CPLError(CE_Failure, CPLE_NotSupported,
4409
0
                 "A SQL result layer is still in use. Please delete it first");
4410
0
        return nullptr;
4411
0
    }
4412
4413
    /* -------------------------------------------------------------------- */
4414
    /*      Special SET interest_layers = command                           */
4415
    /* -------------------------------------------------------------------- */
4416
0
    if (STARTS_WITH(pszSQLCommand, "SET interest_layers ="))
4417
0
    {
4418
0
        char **papszTokens =
4419
0
            CSLTokenizeString2(pszSQLCommand + 21, ",",
4420
0
                               CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
4421
0
        for (auto &&poLayer : m_apoLayers)
4422
0
        {
4423
0
            poLayer->SetDeclareInterest(FALSE);
4424
0
        }
4425
4426
0
        for (int i = 0; papszTokens[i] != nullptr; i++)
4427
0
        {
4428
0
            OGROSMLayer *poLayer =
4429
0
                dynamic_cast<OGROSMLayer *>(GetLayerByName(papszTokens[i]));
4430
0
            if (poLayer != nullptr)
4431
0
            {
4432
0
                poLayer->SetDeclareInterest(TRUE);
4433
0
            }
4434
0
        }
4435
4436
0
        if (m_apoLayers[IDX_LYR_POINTS]->IsUserInterested() &&
4437
0
            !m_apoLayers[IDX_LYR_LINES]->IsUserInterested() &&
4438
0
            !m_apoLayers[IDX_LYR_MULTILINESTRINGS]->IsUserInterested() &&
4439
0
            !m_apoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested() &&
4440
0
            !m_apoLayers[IDX_LYR_OTHER_RELATIONS]->IsUserInterested())
4441
0
        {
4442
0
            if (CPLGetConfigOption("OSM_INDEX_POINTS", nullptr) == nullptr)
4443
0
            {
4444
0
                CPLDebug("OSM", "Disabling indexing of nodes");
4445
0
                m_bIndexPoints = false;
4446
0
            }
4447
0
            if (CPLGetConfigOption("OSM_USE_POINTS_INDEX", nullptr) == nullptr)
4448
0
            {
4449
0
                m_bUsePointsIndex = false;
4450
0
            }
4451
0
            if (CPLGetConfigOption("OSM_INDEX_WAYS", nullptr) == nullptr)
4452
0
            {
4453
0
                CPLDebug("OSM", "Disabling indexing of ways");
4454
0
                m_bIndexWays = false;
4455
0
            }
4456
0
            if (CPLGetConfigOption("OSM_USE_WAYS_INDEX", nullptr) == nullptr)
4457
0
            {
4458
0
                m_bUseWaysIndex = false;
4459
0
            }
4460
0
        }
4461
0
        else if (m_apoLayers[IDX_LYR_LINES]->IsUserInterested() &&
4462
0
                 !m_apoLayers[IDX_LYR_MULTILINESTRINGS]->IsUserInterested() &&
4463
0
                 !m_apoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested() &&
4464
0
                 !m_apoLayers[IDX_LYR_OTHER_RELATIONS]->IsUserInterested())
4465
0
        {
4466
0
            if (CPLGetConfigOption("OSM_INDEX_WAYS", nullptr) == nullptr)
4467
0
            {
4468
0
                CPLDebug("OSM", "Disabling indexing of ways");
4469
0
                m_bIndexWays = false;
4470
0
            }
4471
0
            if (CPLGetConfigOption("OSM_USE_WAYS_INDEX", nullptr) == nullptr)
4472
0
            {
4473
0
                m_bUseWaysIndex = false;
4474
0
            }
4475
0
        }
4476
4477
0
        CSLDestroy(papszTokens);
4478
4479
0
        return nullptr;
4480
0
    }
4481
4482
0
    while (*pszSQLCommand == ' ')
4483
0
        pszSQLCommand++;
4484
4485
    /* Try to analyse the SQL command to get the interest table */
4486
0
    if (STARTS_WITH_CI(pszSQLCommand, "SELECT"))
4487
0
    {
4488
0
        bool bLayerAlreadyAdded = false;
4489
0
        CPLString osInterestLayers = "SET interest_layers =";
4490
4491
0
        if (pszDialect != nullptr && EQUAL(pszDialect, "SQLITE"))
4492
0
        {
4493
0
            const auto oSetLayers = OGRSQLiteGetReferencedLayers(pszSQLCommand);
4494
0
            for (const LayerDesc &oLayerDesc : oSetLayers)
4495
0
            {
4496
0
                if (oLayerDesc.osDSName.empty())
4497
0
                {
4498
0
                    if (bLayerAlreadyAdded)
4499
0
                        osInterestLayers += ",";
4500
0
                    bLayerAlreadyAdded = true;
4501
0
                    osInterestLayers += oLayerDesc.osLayerName;
4502
0
                }
4503
0
            }
4504
0
        }
4505
0
        else
4506
0
        {
4507
0
            swq_select sSelectInfo;
4508
4509
0
            CPLPushErrorHandler(CPLQuietErrorHandler);
4510
0
            CPLErr eErr = sSelectInfo.preparse(pszSQLCommand);
4511
0
            CPLPopErrorHandler();
4512
4513
0
            if (eErr == CE_None)
4514
0
            {
4515
0
                swq_select *pCurSelect = &sSelectInfo;
4516
0
                while (pCurSelect != nullptr)
4517
0
                {
4518
0
                    for (int iTable = 0; iTable < pCurSelect->table_count;
4519
0
                         iTable++)
4520
0
                    {
4521
0
                        swq_table_def *psTableDef =
4522
0
                            pCurSelect->table_defs + iTable;
4523
0
                        if (psTableDef->data_source == nullptr)
4524
0
                        {
4525
0
                            if (bLayerAlreadyAdded)
4526
0
                                osInterestLayers += ",";
4527
0
                            bLayerAlreadyAdded = true;
4528
0
                            osInterestLayers += psTableDef->table_name;
4529
0
                        }
4530
0
                    }
4531
0
                    pCurSelect = pCurSelect->poOtherSelect;
4532
0
                }
4533
0
            }
4534
0
        }
4535
4536
0
        if (bLayerAlreadyAdded)
4537
0
        {
4538
            /* Backup current optimization parameters */
4539
0
            m_abSavedDeclaredInterest.resize(0);
4540
0
            for (auto &&poLayer : m_apoLayers)
4541
0
            {
4542
0
                m_abSavedDeclaredInterest.push_back(
4543
0
                    poLayer->IsUserInterested());
4544
0
            }
4545
0
            m_bIndexPointsBackup = m_bIndexPoints;
4546
0
            m_bUsePointsIndexBackup = m_bUsePointsIndex;
4547
0
            m_bIndexWaysBackup = m_bIndexWays;
4548
0
            m_bUseWaysIndexBackup = m_bUseWaysIndex;
4549
4550
            /* Update optimization parameters */
4551
0
            delete ExecuteSQL(osInterestLayers, nullptr, nullptr);
4552
4553
0
            MyResetReading();
4554
4555
            /* Run the request */
4556
0
            m_poResultSetLayer = GDALDataset::ExecuteSQL(
4557
0
                pszSQLCommand, poSpatialFilter, pszDialect);
4558
4559
            /* If the user explicitly run a COUNT() request, then do it ! */
4560
0
            if (m_poResultSetLayer)
4561
0
            {
4562
0
                if (pszDialect != nullptr && EQUAL(pszDialect, "SQLITE"))
4563
0
                {
4564
0
                    m_poResultSetLayer = new OGROSMResultLayerDecorator(
4565
0
                        m_poResultSetLayer, GetDescription(), osInterestLayers);
4566
0
                }
4567
0
                m_bIsFeatureCountEnabled = true;
4568
0
            }
4569
4570
0
            return m_poResultSetLayer;
4571
0
        }
4572
0
    }
4573
4574
0
    return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
4575
0
}
4576
4577
/************************************************************************/
4578
/*                          ReleaseResultSet()                          */
4579
/************************************************************************/
4580
4581
void OGROSMDataSource::ReleaseResultSet(OGRLayer *poLayer)
4582
4583
0
{
4584
0
    if (poLayer != nullptr && poLayer == m_poResultSetLayer)
4585
0
    {
4586
0
        m_poResultSetLayer = nullptr;
4587
4588
0
        m_bIsFeatureCountEnabled = false;
4589
4590
        /* Restore backup'ed optimization parameters */
4591
0
        int i = 0;
4592
0
        for (auto &&poIterLayer : m_apoLayers)
4593
0
        {
4594
0
            poIterLayer->SetDeclareInterest(m_abSavedDeclaredInterest[i]);
4595
0
            ++i;
4596
0
        }
4597
0
        if (m_bIndexPointsBackup && !m_bIndexPoints)
4598
0
            CPLDebug("OSM", "Re-enabling indexing of nodes");
4599
0
        m_bIndexPoints = m_bIndexPointsBackup;
4600
0
        m_bUsePointsIndex = m_bUsePointsIndexBackup;
4601
0
        if (m_bIndexWaysBackup && !m_bIndexWays)
4602
0
            CPLDebug("OSM", "Re-enabling indexing of ways");
4603
0
        m_bIndexWays = m_bIndexWaysBackup;
4604
0
        m_bUseWaysIndex = m_bUseWaysIndexBackup;
4605
0
        m_abSavedDeclaredInterest.clear();
4606
0
    }
4607
4608
0
    delete poLayer;
4609
0
}
4610
4611
/************************************************************************/
4612
/*                         IsInterleavedReading()                       */
4613
/************************************************************************/
4614
4615
int OGROSMDataSource::IsInterleavedReading()
4616
0
{
4617
0
    if (m_bInterleavedReading < 0)
4618
0
    {
4619
0
        m_bInterleavedReading =
4620
0
            CPLTestBool(CPLGetConfigOption("OGR_INTERLEAVED_READING", "NO"));
4621
0
        CPLDebug("OSM", "OGR_INTERLEAVED_READING = %d", m_bInterleavedReading);
4622
0
    }
4623
0
    return m_bInterleavedReading;
4624
0
}