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