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 7650 : static int DBL_TO_INT(double x)
71 : {
72 7650 : return static_cast<int>(floor(x * 1.0e7 + 0.5));
73 : }
74 :
75 10420 : static double INT_TO_DBL(int x)
76 : {
77 10420 : 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 4954 : static bool VALID_ID_FOR_CUSTOM_INDEXING(GIntBig _id)
85 : {
86 4954 : 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 38 : static CPLString GetInterestLayersForDSName(const CPLString &osDSName)
193 : {
194 76 : CPLMutexHolder oMutexHolder(&hMutex);
195 38 : GIntBig nPID = CPLGetPID();
196 57 : for (auto oIter = oListDSToBeOpened.begin();
197 76 : 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 38 : return "";
208 : }
209 :
210 : /************************************************************************/
211 : /* OGROSMDataSource() */
212 : /************************************************************************/
213 :
214 38 : OGROSMDataSource::OGROSMDataSource()
215 : {
216 38 : m_apsKeys.push_back(nullptr); // guard to avoid index 0 to be used
217 :
218 38 : MAX_INDEXED_KEYS = static_cast<unsigned>(
219 38 : atoi(CPLGetConfigOption("OSM_MAX_INDEXED_KEYS", "32768")));
220 38 : MAX_INDEXED_VALUES_PER_KEY = static_cast<unsigned>(
221 38 : atoi(CPLGetConfigOption("OSM_MAX_INDEXED_VALUES_PER_KEY", "1024")));
222 38 : }
223 :
224 : /************************************************************************/
225 : /* ~OGROSMDataSource() */
226 : /************************************************************************/
227 :
228 76 : OGROSMDataSource::~OGROSMDataSource()
229 :
230 : {
231 38 : m_apoLayers.clear();
232 :
233 38 : if (m_psParser != nullptr)
234 38 : CPLDebug("OSM", "Number of bytes read in file : " CPL_FRMT_GUIB,
235 : OSM_GetBytesRead(m_psParser));
236 38 : OSM_Close(m_psParser);
237 :
238 38 : if (m_hDB != nullptr)
239 38 : CloseDB();
240 :
241 38 : if (m_hDBForComputedAttributes != nullptr)
242 37 : sqlite3_close(m_hDBForComputedAttributes);
243 :
244 38 : if (m_pMyVFS)
245 : {
246 38 : sqlite3_vfs_unregister(m_pMyVFS);
247 38 : CPLFree(m_pMyVFS->pAppData);
248 38 : CPLFree(m_pMyVFS);
249 : }
250 :
251 38 : if (!m_osTmpDBName.empty() && m_bMustUnlink)
252 : {
253 38 : const char *pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
254 38 : if (!EQUAL(pszVal, "NOT_EVEN_AT_END"))
255 38 : VSIUnlink(m_osTmpDBName);
256 : }
257 :
258 38 : CPLFree(m_panReqIds);
259 : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
260 38 : CPLFree(m_panHashedIndexes);
261 38 : CPLFree(m_psCollisionBuckets);
262 : #endif
263 38 : CPLFree(m_pasLonLatArray);
264 38 : CPLFree(m_panUnsortedReqIds);
265 :
266 38 : CPLFree(m_pasAccumulatedTags);
267 38 : CPLFree(pabyNonRedundantKeys);
268 38 : 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 109 : for (int i = 1; i < static_cast<int>(m_apsKeys.size()); i++)
285 : {
286 71 : KeyDesc *psKD = m_apsKeys[i];
287 71 : if (psKD)
288 : {
289 70 : CPLFree(psKD->pszK);
290 236 : for (int j = 0; j < static_cast<int>(psKD->apszValues.size()); j++)
291 166 : CPLFree(psKD->apszValues[j]);
292 70 : delete psKD;
293 : }
294 : }
295 :
296 38 : if (m_fpNodes)
297 36 : VSIFCloseL(m_fpNodes);
298 38 : if (!m_osNodesFilename.empty() && m_bMustUnlinkNodesFile)
299 : {
300 36 : const char *pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
301 36 : if (!EQUAL(pszVal, "NOT_EVEN_AT_END"))
302 36 : VSIUnlink(m_osNodesFilename);
303 : }
304 :
305 38 : CPLFree(m_pabySector);
306 68 : for (auto &oIter : m_oMapBuckets)
307 : {
308 30 : 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 29 : int nRem = oIter.first % (knPAGE_SIZE / BUCKET_BITMAP_SIZE);
318 29 : if (nRem == 0)
319 29 : CPLFree(oIter.second.u.pabyBitmap);
320 : }
321 : }
322 76 : }
323 :
324 : /************************************************************************/
325 : /* CloseDB() */
326 : /************************************************************************/
327 :
328 38 : void OGROSMDataSource::CloseDB()
329 : {
330 38 : if (m_hInsertNodeStmt != nullptr)
331 38 : sqlite3_finalize(m_hInsertNodeStmt);
332 38 : m_hInsertNodeStmt = nullptr;
333 :
334 38 : if (m_hInsertWayStmt != nullptr)
335 38 : sqlite3_finalize(m_hInsertWayStmt);
336 38 : m_hInsertWayStmt = nullptr;
337 :
338 38 : if (m_hInsertPolygonsStandaloneStmt != nullptr)
339 38 : sqlite3_finalize(m_hInsertPolygonsStandaloneStmt);
340 38 : m_hInsertPolygonsStandaloneStmt = nullptr;
341 :
342 38 : if (m_hDeletePolygonsStandaloneStmt != nullptr)
343 38 : sqlite3_finalize(m_hDeletePolygonsStandaloneStmt);
344 38 : m_hDeletePolygonsStandaloneStmt = nullptr;
345 :
346 38 : if (m_hSelectPolygonsStandaloneStmt != nullptr)
347 38 : sqlite3_finalize(m_hSelectPolygonsStandaloneStmt);
348 38 : m_hSelectPolygonsStandaloneStmt = nullptr;
349 :
350 38 : if (m_pahSelectNodeStmt != nullptr)
351 : {
352 7638 : for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
353 : {
354 7600 : if (m_pahSelectNodeStmt[i] != nullptr)
355 7600 : sqlite3_finalize(m_pahSelectNodeStmt[i]);
356 : }
357 38 : CPLFree(m_pahSelectNodeStmt);
358 38 : m_pahSelectNodeStmt = nullptr;
359 : }
360 :
361 38 : if (m_pahSelectWayStmt != nullptr)
362 : {
363 7638 : for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
364 : {
365 7600 : if (m_pahSelectWayStmt[i] != nullptr)
366 7600 : sqlite3_finalize(m_pahSelectWayStmt[i]);
367 : }
368 38 : CPLFree(m_pahSelectWayStmt);
369 38 : m_pahSelectWayStmt = nullptr;
370 : }
371 :
372 38 : if (m_bInTransaction)
373 38 : CommitTransactionCacheDB();
374 :
375 38 : sqlite3_close(m_hDB);
376 38 : m_hDB = nullptr;
377 38 : }
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 3870 : bool OGROSMDataSource::IndexPoint(const OSMNode *psNode)
397 : {
398 3870 : if (!m_bIndexPoints)
399 45 : return true;
400 :
401 3825 : if (m_bCustomIndexing)
402 2065 : 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 62 : bool OGROSMDataSource::FlushCurrentSector()
440 : {
441 : #ifndef FAKE_LOOKUP_NODES
442 62 : if (m_bCompressNodes)
443 1 : return FlushCurrentSectorCompressedCase();
444 :
445 61 : return FlushCurrentSectorNonCompressedCase();
446 : #else
447 : return true;
448 : #endif
449 : }
450 :
451 : /************************************************************************/
452 : /* AllocBucket() */
453 : /************************************************************************/
454 :
455 30 : Bucket *OGROSMDataSource::AllocBucket(int iBucket)
456 : {
457 30 : 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 29 : const int nRem = iBucket % (knPAGE_SIZE / BUCKET_BITMAP_SIZE);
478 29 : Bucket *psPrevBucket = GetBucket(iBucket - nRem);
479 29 : if (psPrevBucket->u.pabyBitmap == nullptr)
480 29 : psPrevBucket->u.pabyBitmap =
481 29 : reinterpret_cast<GByte *>(VSI_CALLOC_VERBOSE(1, knPAGE_SIZE));
482 29 : GByte *pabyBitmap = psPrevBucket->u.pabyBitmap;
483 29 : Bucket *psBucket = GetBucket(iBucket);
484 29 : if (pabyBitmap != nullptr)
485 : {
486 29 : psBucket->u.pabyBitmap = pabyBitmap + nRem * BUCKET_BITMAP_SIZE;
487 29 : 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 2126 : Bucket *OGROSMDataSource::GetBucket(int nBucketId)
504 : {
505 2126 : auto oIter = m_oMapBuckets.find(nBucketId);
506 2126 : if (oIter == m_oMapBuckets.end())
507 : {
508 30 : Bucket *psBucket = &m_oMapBuckets[nBucketId];
509 30 : psBucket->nOff = -1;
510 30 : if (m_bCompressNodes)
511 1 : psBucket->u.panSectorSize = nullptr;
512 : else
513 29 : psBucket->u.pabyBitmap = nullptr;
514 30 : return psBucket;
515 : }
516 2096 : 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 61 : bool OGROSMDataSource::FlushCurrentSectorNonCompressedCase()
606 : {
607 61 : if (VSIFWriteL(m_pabySector, 1, static_cast<size_t>(SECTOR_SIZE),
608 61 : m_fpNodes) == static_cast<size_t>(SECTOR_SIZE))
609 : {
610 61 : memset(m_pabySector, 0, SECTOR_SIZE);
611 61 : m_nNodesFileSize += SECTOR_SIZE;
612 61 : 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 2065 : bool OGROSMDataSource::IndexPointCustom(const OSMNode *psNode)
627 : {
628 2065 : 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 2065 : 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 2065 : const int nBucket = static_cast<int>(psNode->nID / NODE_PER_BUCKET);
646 2065 : const int nOffInBucket = static_cast<int>(psNode->nID % NODE_PER_BUCKET);
647 2065 : const int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
648 2065 : const int nOffInBucketReducedRemainder =
649 : nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1);
650 :
651 2065 : Bucket *psBucket = GetBucket(nBucket);
652 :
653 2065 : if (!m_bCompressNodes)
654 : {
655 2056 : const int nBitmapIndex = nOffInBucketReduced / 8;
656 2056 : const int nBitmapRemainder = nOffInBucketReduced % 8;
657 2056 : if (psBucket->u.pabyBitmap == nullptr)
658 : {
659 29 : psBucket = AllocBucket(nBucket);
660 29 : if (psBucket == nullptr)
661 0 : return false;
662 : }
663 2056 : CPLAssert(psBucket->u.pabyBitmap != nullptr);
664 2056 : psBucket->u.pabyBitmap[nBitmapIndex] |= (1 << nBitmapRemainder);
665 : }
666 :
667 2065 : if (nBucket != m_nBucketOld)
668 : {
669 41 : CPLAssert(nBucket > m_nBucketOld);
670 41 : if (m_nBucketOld >= 0)
671 : {
672 1 : if (!FlushCurrentSector())
673 : {
674 0 : m_bStopParsing = true;
675 0 : return false;
676 : }
677 : }
678 41 : m_nBucketOld = nBucket;
679 41 : m_nOffInBucketReducedOld = nOffInBucketReduced;
680 41 : CPLAssert(psBucket->nOff == -1);
681 41 : psBucket->nOff = VSIFTellL(m_fpNodes);
682 : }
683 2024 : 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 2065 : LonLat *psLonLat = reinterpret_cast<LonLat *>(
695 2065 : m_pabySector + sizeof(LonLat) * nOffInBucketReducedRemainder);
696 2065 : psLonLat->nLon = DBL_TO_INT(psNode->dfLon);
697 2065 : psLonLat->nLat = DBL_TO_INT(psNode->dfLat);
698 :
699 2065 : m_nPrevNodeId = psNode->nID;
700 :
701 2065 : return true;
702 : }
703 :
704 : /************************************************************************/
705 : /* NotifyNodes() */
706 : /************************************************************************/
707 :
708 83 : void OGROSMDataSource::NotifyNodes(unsigned int nNodes, const OSMNode *pasNodes)
709 : {
710 : const OGREnvelope *psEnvelope =
711 83 : m_apoLayers[IDX_LYR_POINTS]->GetSpatialFilterEnvelope();
712 :
713 3962 : 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 3879 : if (psEnvelope != nullptr && !(pasNodes[i].dfLon >= psEnvelope->MinX &&
718 27 : pasNodes[i].dfLon <= psEnvelope->MaxX &&
719 18 : pasNodes[i].dfLat >= psEnvelope->MinY &&
720 18 : pasNodes[i].dfLat <= psEnvelope->MaxY))
721 9 : continue;
722 :
723 3870 : if (!IndexPoint(&pasNodes[i]))
724 0 : break;
725 :
726 3870 : if (!m_apoLayers[IDX_LYR_POINTS]->IsUserInterested())
727 84 : continue;
728 :
729 3786 : bool bInterestingTag = m_bReportAllNodes;
730 3786 : const OSMTag *pasTags = pasNodes[i].pasTags;
731 :
732 3786 : if (!m_bReportAllNodes)
733 : {
734 3792 : for (unsigned int j = 0; j < pasNodes[i].nTags; j++)
735 : {
736 70 : const char *pszK = pasTags[j].pszK;
737 70 : if (m_apoLayers[IDX_LYR_POINTS]->IsSignificantKey(pszK))
738 : {
739 64 : bInterestingTag = true;
740 64 : break;
741 : }
742 : }
743 : }
744 :
745 3786 : if (bInterestingTag)
746 : {
747 : auto poFeature = std::make_unique<OGRFeature>(
748 64 : m_apoLayers[IDX_LYR_POINTS]->GetLayerDefn());
749 :
750 128 : poFeature->SetGeometryDirectly(
751 64 : new OGRPoint(pasNodes[i].dfLon, pasNodes[i].dfLat));
752 :
753 128 : m_apoLayers[IDX_LYR_POINTS]->SetFieldsFromTags(
754 64 : poFeature.get(), pasNodes[i].nID, false, pasNodes[i].nTags,
755 64 : pasTags, &pasNodes[i].sInfo);
756 :
757 64 : bool bFilteredOut = false;
758 128 : if (!m_apoLayers[IDX_LYR_POINTS]->AddFeature(std::move(poFeature),
759 : false, &bFilteredOut,
760 64 : !m_bFeatureAdded))
761 : {
762 0 : m_bStopParsing = true;
763 0 : break;
764 : }
765 64 : else if (!bFilteredOut)
766 35 : m_bFeatureAdded = true;
767 : }
768 : }
769 83 : }
770 :
771 83 : static void OGROSMNotifyNodes(unsigned int nNodes, OSMNode *pasNodes,
772 : OSMContext * /* psOSMContext */, void *user_data)
773 : {
774 83 : static_cast<OGROSMDataSource *>(user_data)->NotifyNodes(nNodes, pasNodes);
775 83 : }
776 :
777 : /************************************************************************/
778 : /* LookupNodes() */
779 : /************************************************************************/
780 :
781 : // #define DEBUG_COLLISIONS 1
782 :
783 62 : void OGROSMDataSource::LookupNodes()
784 : {
785 62 : if (m_bCustomIndexing)
786 60 : LookupNodesCustom();
787 : else
788 2 : LookupNodesSQLite();
789 :
790 : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
791 62 : if (m_nReqIds > 1 && m_bEnableHashedIndex)
792 : {
793 37 : memset(m_panHashedIndexes, 0xFF,
794 : HASHED_INDEXES_ARRAY_SIZE * sizeof(int));
795 37 : m_bHashedIndexValid = true;
796 : #ifdef DEBUG_COLLISIONS
797 : int nCollisions = 0;
798 : #endif
799 37 : int iNextFreeBucket = 0;
800 3780 : for (unsigned int i = 0; i < m_nReqIds; i++)
801 : {
802 3743 : int nIndInHashArray = static_cast<int>(
803 3743 : HASH_ID_FUNC(m_panReqIds[i]) % HASHED_INDEXES_ARRAY_SIZE);
804 3743 : int nIdx = m_panHashedIndexes[nIndInHashArray];
805 3743 : if (nIdx == -1)
806 : {
807 3743 : 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 37 : }
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 25 : m_bHashedIndexValid = false;
870 : #endif // ENABLE_NODE_LOOKUP_BY_HASHING
871 62 : }
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 60 : void OGROSMDataSource::LookupNodesCustom()
985 : {
986 60 : m_nReqIds = 0;
987 :
988 60 : if (m_nBucketOld >= 0)
989 : {
990 35 : if (!FlushCurrentSector())
991 : {
992 0 : m_bStopParsing = true;
993 0 : return;
994 : }
995 :
996 35 : m_nBucketOld = -1;
997 : }
998 :
999 60 : CPLAssert(m_nUnsortedReqIds <=
1000 : static_cast<unsigned int>(MAX_ACCUMULATED_NODES));
1001 :
1002 2949 : for (unsigned int i = 0; i < m_nUnsortedReqIds; i++)
1003 : {
1004 2889 : GIntBig id = m_panUnsortedReqIds[i];
1005 :
1006 2889 : if (!VALID_ID_FOR_CUSTOM_INDEXING(id))
1007 187 : continue;
1008 :
1009 2889 : int nBucket = static_cast<int>(id / NODE_PER_BUCKET);
1010 2889 : int nOffInBucket = static_cast<int>(id % NODE_PER_BUCKET);
1011 2889 : int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
1012 :
1013 2889 : const auto oIter = m_oMapBuckets.find(nBucket);
1014 2889 : if (oIter == m_oMapBuckets.end())
1015 27 : continue;
1016 2862 : const Bucket *psBucket = &(oIter->second);
1017 :
1018 2862 : 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 2836 : int nBitmapIndex = nOffInBucketReduced / 8;
1027 2836 : int nBitmapRemainder = nOffInBucketReduced % 8;
1028 2836 : if (psBucket->u.pabyBitmap == nullptr ||
1029 2836 : !(psBucket->u.pabyBitmap[nBitmapIndex] &
1030 : (1 << nBitmapRemainder)))
1031 155 : continue;
1032 : }
1033 :
1034 2702 : m_panReqIds[m_nReqIds++] = id;
1035 : }
1036 :
1037 60 : std::sort(m_panReqIds, m_panReqIds + m_nReqIds);
1038 :
1039 : /* Remove duplicates */
1040 60 : unsigned int j = 0; // Used after for.
1041 2762 : for (unsigned int i = 0; i < m_nReqIds; i++)
1042 : {
1043 2702 : if (!(i > 0 && m_panReqIds[i] == m_panReqIds[i - 1]))
1044 2019 : m_panReqIds[j++] = m_panReqIds[i];
1045 : }
1046 60 : 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 60 : if (m_bCompressNodes)
1056 1 : LookupNodesCustomCompressedCase();
1057 : else
1058 59 : 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 59 : void OGROSMDataSource::LookupNodesCustomNonCompressedCase()
1180 : {
1181 59 : unsigned int j = 0; // Used after for.
1182 :
1183 59 : int l_nBucketOld = -1;
1184 59 : const Bucket *psBucket = nullptr;
1185 : // To be glibc friendly, we will do reads aligned on 4096 byte offsets
1186 59 : const int knDISK_SECTOR_SIZE = 4096;
1187 59 : 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 59 : GIntBig nOldOffset = -knDISK_SECTOR_SIZE - 1;
1191 : // Number of valid bytes in abyDiskSector
1192 59 : size_t nValidBytes = 0;
1193 59 : int k = 0;
1194 59 : int nSectorBase = 0;
1195 2070 : for (unsigned int i = 0; i < m_nReqIds; i++)
1196 : {
1197 2011 : const GIntBig id = m_panReqIds[i];
1198 2011 : const int nBucket = static_cast<int>(id / NODE_PER_BUCKET);
1199 2011 : const int nOffInBucket = static_cast<int>(id % NODE_PER_BUCKET);
1200 2011 : const int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
1201 2011 : const int nOffInBucketReducedRemainder =
1202 : nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1);
1203 :
1204 2011 : const int nBitmapIndex = nOffInBucketReduced / 8;
1205 2011 : const int nBitmapRemainder = nOffInBucketReduced % 8;
1206 :
1207 2011 : if (psBucket == nullptr || nBucket != l_nBucketOld)
1208 : {
1209 58 : const auto oIter = m_oMapBuckets.find(nBucket);
1210 58 : 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 58 : psBucket = &(oIter->second);
1218 58 : 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 58 : l_nBucketOld = nBucket;
1226 58 : nOldOffset = -knDISK_SECTOR_SIZE - 1;
1227 58 : k = 0;
1228 58 : 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 2013 : 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 2011 : int nSector = nSectorBase;
1241 2011 : 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 2011 : const GIntBig nNewOffset = psBucket->nOff + nSector * SECTOR_SIZE;
1249 2011 : if (nNewOffset - nOldOffset >= knDISK_SECTOR_SIZE)
1250 : {
1251 : // Align on 4096 boundary to be glibc caching friendly
1252 61 : const GIntBig nAlignedNewPos =
1253 : nNewOffset & ~(static_cast<GIntBig>(knDISK_SECTOR_SIZE) - 1);
1254 61 : VSIFSeekL(m_fpNodes, nAlignedNewPos, SEEK_SET);
1255 : nValidBytes =
1256 61 : VSIFReadL(abyDiskSector, 1, knDISK_SECTOR_SIZE, m_fpNodes);
1257 61 : nOldOffset = nAlignedNewPos;
1258 : }
1259 :
1260 2011 : const size_t nOffsetInDiskSector =
1261 2011 : static_cast<size_t>(nNewOffset - nOldOffset) +
1262 2011 : nOffInBucketReducedRemainder * sizeof(LonLat);
1263 2011 : if (nValidBytes < sizeof(LonLat) ||
1264 2011 : 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 2011 : memcpy(&m_pasLonLatArray[j], abyDiskSector + nOffsetInDiskSector,
1271 : sizeof(LonLat));
1272 :
1273 2011 : m_panReqIds[j] = id;
1274 2011 : if (m_pasLonLatArray[j].nLon || m_pasLonLatArray[j].nLat)
1275 2011 : j++;
1276 : }
1277 59 : m_nReqIds = j;
1278 59 : }
1279 :
1280 : /************************************************************************/
1281 : /* WriteVarInt() */
1282 : /************************************************************************/
1283 :
1284 442 : static void WriteVarInt(unsigned int nVal, std::vector<GByte> &abyData)
1285 : {
1286 : while (true)
1287 : {
1288 442 : if ((nVal & (~0x7fU)) == 0)
1289 : {
1290 442 : abyData.push_back(static_cast<GByte>(nVal));
1291 442 : 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 8558 : static void WriteVarSInt64(GIntBig nSVal, std::vector<GByte> &abyData)
1323 : {
1324 8558 : GIntBig nVal = nSVal >= 0 ? nSVal << 1 : ((-1 - nSVal) << 1) + 1;
1325 :
1326 : while (true)
1327 : {
1328 17886 : if ((nVal & (~0x7f)) == 0)
1329 : {
1330 8558 : abyData.push_back(static_cast<GByte>(nVal));
1331 8558 : return;
1332 : }
1333 :
1334 9328 : abyData.push_back(0x80 | static_cast<GByte>(nVal & 0x7f));
1335 9328 : 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 645 : 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 645 : abyCompressedWay.clear();
1374 645 : abyCompressedWay.push_back((bIsArea) ? 1 : 0);
1375 645 : CPLAssert(nTags <= MAX_COUNT_FOR_TAGS_IN_WAY);
1376 645 : abyCompressedWay.push_back(static_cast<GByte>(nTags));
1377 :
1378 869 : for (unsigned int iTag = 0; iTag < nTags; iTag++)
1379 : {
1380 224 : if (pasTags[iTag].bKIsIndex)
1381 : {
1382 222 : 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 224 : if (pasTags[iTag].bVIsIndex)
1398 : {
1399 220 : 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 645 : 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 1290 : abyCompressedWay.end(),
1435 : reinterpret_cast<const GByte *>(&(pasLonLatPairs[0])),
1436 645 : reinterpret_cast<const GByte *>(&(pasLonLatPairs[0])) + sizeof(LonLat));
1437 4924 : for (int i = 1; i < nPoints; i++)
1438 : {
1439 4279 : GIntBig nDiff64 = static_cast<GIntBig>(pasLonLatPairs[i].nLon) -
1440 4279 : static_cast<GIntBig>(pasLonLatPairs[i - 1].nLon);
1441 4279 : WriteVarSInt64(nDiff64, abyCompressedWay);
1442 :
1443 4279 : nDiff64 = pasLonLatPairs[i].nLat - pasLonLatPairs[i - 1].nLat;
1444 4279 : WriteVarSInt64(nDiff64, abyCompressedWay);
1445 : }
1446 645 : }
1447 :
1448 : /************************************************************************/
1449 : /* UncompressWay() */
1450 : /************************************************************************/
1451 :
1452 319 : 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 319 : asCoords.clear();
1459 319 : const GByte *pabyPtr = pabyCompressedWay;
1460 319 : if (pbIsArea)
1461 51 : *pbIsArea = (*pabyPtr == 1) ? true : false;
1462 319 : pabyPtr++;
1463 319 : unsigned int nTags = *pabyPtr;
1464 319 : pabyPtr++;
1465 :
1466 319 : if (pnTags)
1467 180 : *pnTags = nTags;
1468 :
1469 : // TODO: Some additional safety checks.
1470 572 : for (unsigned int iTag = 0; iTag < nTags; iTag++)
1471 : {
1472 253 : const int nK = ReadVarInt32(&pabyPtr);
1473 253 : const GByte *pszK = nullptr;
1474 253 : if (nK == 0)
1475 : {
1476 3 : pszK = pabyPtr;
1477 21 : while (*pabyPtr != '\0')
1478 18 : pabyPtr++;
1479 3 : pabyPtr++;
1480 : }
1481 :
1482 253 : const int nV = nK == 0 ? 0 : ReadVarInt32(&pabyPtr);
1483 253 : const GByte *pszV = nullptr;
1484 253 : if (nV == 0)
1485 : {
1486 6 : pszV = pabyPtr;
1487 58 : while (*pabyPtr != '\0')
1488 52 : pabyPtr++;
1489 6 : pabyPtr++;
1490 : }
1491 :
1492 253 : if (pasTags)
1493 : {
1494 224 : CPLAssert(nK >= 0 && static_cast<unsigned>(nK) < m_apsKeys.size());
1495 224 : pasTags[iTag].pszK =
1496 224 : nK ? m_apsKeys[nK]->pszK : reinterpret_cast<const char *>(pszK);
1497 :
1498 224 : CPLAssert(nK == 0 ||
1499 : (nV >= 0 && static_cast<unsigned>(nV) <
1500 : m_apsKeys[nK]->apszValues.size()));
1501 224 : pasTags[iTag].pszV = nV ? m_apsKeys[nK]->apszValues[nV]
1502 : : reinterpret_cast<const char *>(pszV);
1503 : }
1504 : }
1505 :
1506 319 : 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 319 : memcpy(&lonLat.nLon, pabyPtr, sizeof(int));
1530 319 : memcpy(&lonLat.nLat, pabyPtr + sizeof(int), sizeof(int));
1531 319 : asCoords.emplace_back(lonLat);
1532 319 : pabyPtr += 2 * sizeof(int);
1533 1266 : do
1534 : {
1535 1585 : lonLat.nLon = static_cast<int>(lonLat.nLon + ReadVarSInt64(&pabyPtr));
1536 1585 : lonLat.nLat = static_cast<int>(lonLat.nLat + ReadVarSInt64(&pabyPtr));
1537 1585 : asCoords.emplace_back(lonLat);
1538 1585 : } while (pabyPtr < pabyCompressedWay + nBytes);
1539 319 : }
1540 :
1541 : /************************************************************************/
1542 : /* IndexWay() */
1543 : /************************************************************************/
1544 :
1545 648 : 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 648 : if (!m_bIndexWays)
1551 3 : return;
1552 :
1553 645 : sqlite3_bind_int64(m_hInsertWayStmt, 1, nWayID);
1554 :
1555 645 : const unsigned nTagsClamped = std::min(nTags, MAX_COUNT_FOR_TAGS_IN_WAY);
1556 645 : 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 645 : CompressWay(bIsArea, nTagsClamped, pasTags, nPairs, pasLonLatPairs, psInfo,
1564 645 : m_abyWayBuffer);
1565 645 : sqlite3_bind_blob(m_hInsertWayStmt, 2, m_abyWayBuffer.data(),
1566 645 : static_cast<int>(m_abyWayBuffer.size()), SQLITE_STATIC);
1567 :
1568 645 : int rc = sqlite3_step(m_hInsertWayStmt);
1569 645 : sqlite3_reset(m_hInsertWayStmt);
1570 645 : 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 38 : void OGROSMDataSource::ProcessWaysBatch()
1606 : {
1607 38 : if (m_asWayFeaturePairs.empty())
1608 0 : return;
1609 :
1610 : // printf("nodes = %d, features = %d\n", nUnsortedReqIds, int(m_asWayFeaturePairs.size()));
1611 38 : LookupNodes();
1612 :
1613 726 : for (WayFeaturePair &sWayFeaturePairs : m_asWayFeaturePairs)
1614 : {
1615 688 : const bool bIsArea = sWayFeaturePairs.bIsArea;
1616 688 : m_asLonLatCache.clear();
1617 :
1618 : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
1619 688 : if (m_bHashedIndexValid)
1620 : {
1621 5562 : for (unsigned int i = 0; i < sWayFeaturePairs.nRefs; i++)
1622 : {
1623 4881 : int nIndInHashArray = static_cast<int>(
1624 4881 : HASH_ID_FUNC(sWayFeaturePairs.panNodeRefs[i]) %
1625 : HASHED_INDEXES_ARRAY_SIZE);
1626 4881 : int nIdx = m_panHashedIndexes[nIndInHashArray];
1627 4881 : 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 4881 : else if (nIdx >= 0 &&
1645 4716 : m_panReqIds[nIdx] != sWayFeaturePairs.panNodeRefs[i])
1646 0 : nIdx = -1;
1647 :
1648 4881 : if (nIdx >= 0)
1649 : {
1650 4716 : 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 688 : if (!m_asLonLatCache.empty() && bIsArea)
1680 : {
1681 217 : m_asLonLatCache.push_back(m_asLonLatCache[0]);
1682 : }
1683 :
1684 688 : if (m_asLonLatCache.size() < 2)
1685 : {
1686 40 : CPLDebug("OSM",
1687 : "Way " CPL_FRMT_GIB
1688 : " with %d nodes that could be found. Discarding it",
1689 : sWayFeaturePairs.nWayID,
1690 40 : static_cast<int>(m_asLonLatCache.size()));
1691 40 : sWayFeaturePairs.poFeature.reset();
1692 40 : sWayFeaturePairs.bIsArea = false;
1693 301 : continue;
1694 : }
1695 :
1696 648 : if (bIsArea && m_apoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested())
1697 : {
1698 420 : IndexWay(sWayFeaturePairs.nWayID, /*bIsArea = */ true,
1699 210 : sWayFeaturePairs.nTags, sWayFeaturePairs.pasTags,
1700 210 : m_asLonLatCache.data(),
1701 210 : static_cast<int>(m_asLonLatCache.size()),
1702 210 : &sWayFeaturePairs.sInfo);
1703 : }
1704 : else
1705 438 : IndexWay(sWayFeaturePairs.nWayID, bIsArea, 0, nullptr,
1706 438 : m_asLonLatCache.data(),
1707 438 : static_cast<int>(m_asLonLatCache.size()), nullptr);
1708 :
1709 648 : if (sWayFeaturePairs.poFeature == nullptr)
1710 : {
1711 261 : continue;
1712 : }
1713 :
1714 387 : OGRLineString *poLS = new OGRLineString();
1715 387 : OGRGeometry *poGeom = poLS;
1716 :
1717 387 : const int nPoints = static_cast<int>(m_asLonLatCache.size());
1718 387 : poLS->setNumPoints(nPoints, /*bZeroizeNewContent=*/false);
1719 3670 : for (int i = 0; i < nPoints; i++)
1720 : {
1721 3283 : poLS->setPoint(i, INT_TO_DBL(m_asLonLatCache[i].nLon),
1722 3283 : INT_TO_DBL(m_asLonLatCache[i].nLat));
1723 : }
1724 :
1725 387 : sWayFeaturePairs.poFeature->SetGeometryDirectly(poGeom);
1726 :
1727 387 : if (m_asLonLatCache.size() != sWayFeaturePairs.nRefs)
1728 27 : CPLDebug("OSM",
1729 : "For way " CPL_FRMT_GIB
1730 : ", got only %d nodes instead of %d",
1731 : sWayFeaturePairs.nWayID, nPoints, sWayFeaturePairs.nRefs);
1732 :
1733 387 : bool bFilteredOut = false;
1734 774 : if (!m_apoLayers[IDX_LYR_LINES]->AddFeature(
1735 387 : std::move(sWayFeaturePairs.poFeature),
1736 387 : sWayFeaturePairs.bAttrFilterAlreadyEvaluated, &bFilteredOut,
1737 387 : !m_bFeatureAdded))
1738 0 : m_bStopParsing = true;
1739 387 : else if (!bFilteredOut)
1740 386 : m_bFeatureAdded = true;
1741 : }
1742 :
1743 38 : if (m_apoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested())
1744 : {
1745 702 : for (WayFeaturePair &sWayFeaturePairs : m_asWayFeaturePairs)
1746 : {
1747 669 : if (sWayFeaturePairs.bIsArea &&
1748 210 : (sWayFeaturePairs.nTags || m_bReportAllWays))
1749 : {
1750 180 : sqlite3_bind_int64(m_hInsertPolygonsStandaloneStmt, 1,
1751 : sWayFeaturePairs.nWayID);
1752 :
1753 180 : int rc = sqlite3_step(m_hInsertPolygonsStandaloneStmt);
1754 180 : sqlite3_reset(m_hInsertPolygonsStandaloneStmt);
1755 180 : 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 38 : m_asWayFeaturePairs.clear();
1767 38 : m_nUnsortedReqIds = 0;
1768 :
1769 38 : m_nAccumulatedTags = 0;
1770 38 : nNonRedundantKeysLen = 0;
1771 38 : nNonRedundantValuesLen = 0;
1772 : }
1773 :
1774 : /************************************************************************/
1775 : /* IsClosedWayTaggedAsPolygon() */
1776 : /************************************************************************/
1777 :
1778 291 : bool OGROSMDataSource::IsClosedWayTaggedAsPolygon(unsigned int nTags,
1779 : const OSMTag *pasTags)
1780 : {
1781 291 : bool bIsArea = false;
1782 291 : const int nSizeArea = 4;
1783 : const int nStrnlenK =
1784 291 : std::max(nSizeArea, m_nMaxSizeKeysInSetClosedWaysArePolygons) + 1;
1785 291 : std::string oTmpStr;
1786 291 : oTmpStr.reserve(m_nMaxSizeKeysInSetClosedWaysArePolygons);
1787 513 : for (unsigned int i = 0; i < nTags; i++)
1788 : {
1789 335 : const char *pszK = pasTags[i].pszK;
1790 335 : const int nKLen = static_cast<int>(CPLStrnlen(pszK, nStrnlenK));
1791 335 : if (nKLen > m_nMaxSizeKeysInSetClosedWaysArePolygons)
1792 0 : continue;
1793 :
1794 335 : if (nKLen == nSizeArea && strcmp(pszK, "area") == 0)
1795 : {
1796 113 : const char *pszV = pasTags[i].pszV;
1797 113 : if (strcmp(pszV, "yes") == 0)
1798 : {
1799 113 : bIsArea = true;
1800 : // final true. We can't have several area tags...
1801 113 : break;
1802 : }
1803 0 : else if (strcmp(pszV, "no") == 0)
1804 : {
1805 0 : bIsArea = false;
1806 0 : break;
1807 : }
1808 : }
1809 222 : if (bIsArea)
1810 40 : continue;
1811 :
1812 182 : if (nKLen >= m_nMinSizeKeysInSetClosedWaysArePolygons)
1813 : {
1814 182 : oTmpStr.assign(pszK, nKLen);
1815 182 : if (aoSetClosedWaysArePolygons.find(oTmpStr) !=
1816 364 : aoSetClosedWaysArePolygons.end())
1817 : {
1818 118 : bIsArea = true;
1819 118 : continue;
1820 : }
1821 : }
1822 :
1823 64 : const char *pszV = pasTags[i].pszV;
1824 64 : const int nVLen = static_cast<int>(CPLStrnlen(pszV, nStrnlenK));
1825 64 : if (nKLen + 1 + nVLen >= m_nMinSizeKeysInSetClosedWaysArePolygons &&
1826 64 : nKLen + 1 + nVLen <= m_nMaxSizeKeysInSetClosedWaysArePolygons)
1827 : {
1828 64 : oTmpStr.assign(pszK, nKLen);
1829 64 : oTmpStr.append(1, '=');
1830 64 : oTmpStr.append(pszV, nVLen);
1831 64 : if (aoSetClosedWaysArePolygons.find(oTmpStr) !=
1832 128 : aoSetClosedWaysArePolygons.end())
1833 : {
1834 0 : bIsArea = true;
1835 0 : continue;
1836 : }
1837 : }
1838 : }
1839 582 : return bIsArea;
1840 : }
1841 :
1842 : /************************************************************************/
1843 : /* NotifyWay() */
1844 : /************************************************************************/
1845 :
1846 781 : void OGROSMDataSource::NotifyWay(const OSMWay *psWay)
1847 : {
1848 781 : m_nWaysProcessed++;
1849 781 : 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 781 : if (!m_bUsePointsIndex)
1859 93 : return;
1860 :
1861 : // printf("way %d : %d nodes\n", (int)psWay->nID, (int)psWay->nRefs);
1862 :
1863 741 : if (psWay->nRefs < 2)
1864 : {
1865 36 : CPLDebug("OSM", "Way " CPL_FRMT_GIB " with %d nodes. Discarding it",
1866 36 : psWay->nID, psWay->nRefs);
1867 36 : return;
1868 : }
1869 :
1870 : /* Is a closed way a polygon ? */
1871 705 : bool bIsArea = false;
1872 705 : if (psWay->panNodeRefs[0] == psWay->panNodeRefs[psWay->nRefs - 1])
1873 : {
1874 291 : bIsArea = IsClosedWayTaggedAsPolygon(psWay->nTags, psWay->pasTags);
1875 : }
1876 :
1877 705 : bool bInterestingTag = m_bReportAllWays;
1878 705 : if (!bIsArea && !m_bReportAllWays)
1879 : {
1880 476 : for (unsigned int i = 0; i < psWay->nTags; i++)
1881 : {
1882 440 : const char *pszK = psWay->pasTags[i].pszK;
1883 440 : if (m_apoLayers[IDX_LYR_LINES]->IsSignificantKey(pszK))
1884 : {
1885 440 : bInterestingTag = true;
1886 440 : break;
1887 : }
1888 : }
1889 : }
1890 :
1891 0 : std::unique_ptr<OGRFeature> poFeature;
1892 705 : bool bAttrFilterAlreadyEvaluated = false;
1893 705 : if (!bIsArea && m_apoLayers[IDX_LYR_LINES]->IsUserInterested() &&
1894 : bInterestingTag)
1895 : {
1896 422 : poFeature = std::make_unique<OGRFeature>(
1897 422 : m_apoLayers[IDX_LYR_LINES]->GetLayerDefn());
1898 :
1899 844 : m_apoLayers[IDX_LYR_LINES]->SetFieldsFromTags(
1900 422 : 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 422 : if (m_apoLayers[IDX_LYR_LINES]->HasAttributeFilter() &&
1907 9 : !m_apoLayers[IDX_LYR_LINES]
1908 440 : ->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 283 : else if (!m_bIndexWays)
1920 : {
1921 12 : return;
1922 : }
1923 :
1924 1376 : if (m_nUnsortedReqIds + psWay->nRefs >
1925 688 : static_cast<unsigned int>(MAX_ACCUMULATED_NODES) ||
1926 688 : m_asWayFeaturePairs.size() ==
1927 688 : static_cast<size_t>(MAX_DELAYED_FEATURES) ||
1928 688 : m_nAccumulatedTags + psWay->nTags >
1929 688 : static_cast<unsigned int>(MAX_ACCUMULATED_TAGS) ||
1930 2064 : nNonRedundantKeysLen + 1024 > MAX_NON_REDUNDANT_KEYS ||
1931 688 : nNonRedundantValuesLen + 1024 > MAX_NON_REDUNDANT_VALUES)
1932 : {
1933 0 : ProcessWaysBatch();
1934 : }
1935 :
1936 688 : m_asWayFeaturePairs.push_back(WayFeaturePair());
1937 688 : WayFeaturePair &sWayFeaturePairs = m_asWayFeaturePairs.back();
1938 :
1939 688 : sWayFeaturePairs.nWayID = psWay->nID;
1940 688 : sWayFeaturePairs.nRefs = psWay->nRefs - (bIsArea ? 1 : 0);
1941 688 : sWayFeaturePairs.panNodeRefs = m_panUnsortedReqIds + m_nUnsortedReqIds;
1942 688 : sWayFeaturePairs.poFeature = std::move(poFeature);
1943 688 : sWayFeaturePairs.bIsArea = bIsArea;
1944 688 : sWayFeaturePairs.bAttrFilterAlreadyEvaluated = bAttrFilterAlreadyEvaluated;
1945 :
1946 688 : if (bIsArea && m_apoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested())
1947 : {
1948 213 : unsigned int nTagCount = 0;
1949 :
1950 213 : 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 213 : sWayFeaturePairs.sInfo.ts.nTimeStamp = 0;
1984 213 : sWayFeaturePairs.sInfo.nChangeset = 0;
1985 213 : sWayFeaturePairs.sInfo.nVersion = 0;
1986 213 : sWayFeaturePairs.sInfo.nUID = 0;
1987 213 : sWayFeaturePairs.sInfo.bTimeStampIsStr = false;
1988 213 : sWayFeaturePairs.sInfo.pszUserSID = "";
1989 : }
1990 :
1991 213 : sWayFeaturePairs.pasTags = m_pasAccumulatedTags + m_nAccumulatedTags;
1992 :
1993 538 : for (unsigned int iTag = 0; iTag < psWay->nTags; iTag++)
1994 : {
1995 325 : const char *pszK = psWay->pasTags[iTag].pszK;
1996 325 : const char *pszV = psWay->pasTags[iTag].pszV;
1997 :
1998 325 : if (std::any_of(begin(m_ignoredKeys), end(m_ignoredKeys),
1999 1683 : [pszK](const char *pszIgnoredKey)
2000 1683 : { return strcmp(pszK, pszIgnoredKey) == 0; }))
2001 : {
2002 99 : continue;
2003 : }
2004 :
2005 226 : auto oIterK = m_aoMapIndexedKeys.find(pszK);
2006 226 : KeyDesc *psKD = nullptr;
2007 226 : if (oIterK == m_aoMapIndexedKeys.end())
2008 : {
2009 92 : 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 90 : psKD = new KeyDesc();
2037 90 : psKD->pszK = CPLStrdup(pszK);
2038 90 : psKD->nKeyIndex = static_cast<int>(m_apsKeys.size());
2039 90 : psKD->nOccurrences = 0;
2040 90 : psKD->apszValues.push_back(CPLStrdup(
2041 : "")); // guard value to avoid index 0 to be used
2042 90 : m_aoMapIndexedKeys[psKD->pszK] = psKD;
2043 90 : m_apsKeys.push_back(psKD);
2044 : }
2045 : }
2046 : else
2047 : {
2048 134 : psKD = oIterK->second;
2049 : }
2050 :
2051 226 : if (psKD)
2052 : {
2053 224 : psKD->nOccurrences++;
2054 224 : m_pasAccumulatedTags[m_nAccumulatedTags].bKIsIndex = TRUE;
2055 224 : m_pasAccumulatedTags[m_nAccumulatedTags].uKey.nKeyIndex =
2056 224 : psKD->nKeyIndex;
2057 : }
2058 :
2059 450 : if (psKD != nullptr &&
2060 224 : psKD->apszValues.size() < 1 + MAX_INDEXED_VALUES_PER_KEY)
2061 : {
2062 222 : int nValueIndex = 0;
2063 222 : auto oIterV = psKD->anMapV.find(pszV);
2064 222 : if (oIterV == psKD->anMapV.end())
2065 : {
2066 114 : char *pszVDup = CPLStrdup(pszV);
2067 114 : nValueIndex = static_cast<int>(psKD->apszValues.size());
2068 114 : psKD->anMapV[pszVDup] = nValueIndex;
2069 114 : psKD->apszValues.push_back(pszVDup);
2070 : }
2071 : else
2072 108 : nValueIndex = oIterV->second;
2073 :
2074 222 : m_pasAccumulatedTags[m_nAccumulatedTags].bVIsIndex = TRUE;
2075 222 : 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 226 : m_nAccumulatedTags++;
2106 :
2107 226 : nTagCount++;
2108 226 : if (nTagCount == MAX_COUNT_FOR_TAGS_IN_WAY)
2109 0 : break;
2110 : }
2111 :
2112 213 : sWayFeaturePairs.nTags = nTagCount;
2113 : }
2114 : else
2115 : {
2116 475 : sWayFeaturePairs.sInfo.ts.nTimeStamp = 0;
2117 475 : sWayFeaturePairs.sInfo.nChangeset = 0;
2118 475 : sWayFeaturePairs.sInfo.nVersion = 0;
2119 475 : sWayFeaturePairs.sInfo.nUID = 0;
2120 475 : sWayFeaturePairs.sInfo.bTimeStampIsStr = false;
2121 475 : sWayFeaturePairs.sInfo.pszUserSID = "";
2122 :
2123 475 : sWayFeaturePairs.nTags = 0;
2124 475 : sWayFeaturePairs.pasTags = nullptr;
2125 : }
2126 :
2127 688 : memcpy(m_panUnsortedReqIds + m_nUnsortedReqIds, psWay->panNodeRefs,
2128 688 : sizeof(GIntBig) * (psWay->nRefs - (bIsArea ? 1 : 0)));
2129 688 : m_nUnsortedReqIds += (psWay->nRefs - (bIsArea ? 1 : 0));
2130 : }
2131 :
2132 781 : static void OGROSMNotifyWay(OSMWay *psWay, OSMContext * /* psOSMContext */,
2133 : void *user_data)
2134 : {
2135 781 : static_cast<OGROSMDataSource *>(user_data)->NotifyWay(psWay);
2136 781 : }
2137 :
2138 : /************************************************************************/
2139 : /* LookupWays() */
2140 : /************************************************************************/
2141 :
2142 145 : unsigned int OGROSMDataSource::LookupWays(
2143 : std::map<GIntBig, std::pair<int, void *>> &aoMapWays,
2144 : const OSMRelation *psRelation)
2145 : {
2146 145 : unsigned int nFound = 0;
2147 145 : unsigned int iCur = 0;
2148 :
2149 290 : while (iCur < psRelation->nMembers)
2150 : {
2151 145 : unsigned int nToQuery = 0;
2152 145 : unsigned int i = iCur; // Used after for.
2153 406 : for (; i < psRelation->nMembers; i++)
2154 : {
2155 261 : if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
2156 237 : strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
2157 : {
2158 237 : nToQuery++;
2159 237 : if (nToQuery ==
2160 : static_cast<unsigned int>(LIMIT_IDS_PER_REQUEST))
2161 : {
2162 0 : break;
2163 : }
2164 : }
2165 : }
2166 :
2167 145 : if (nToQuery == 0)
2168 0 : break;
2169 :
2170 145 : unsigned int iLastI = (i == psRelation->nMembers) ? i : i + 1;
2171 :
2172 145 : sqlite3_stmt *hStmt = m_pahSelectWayStmt[nToQuery - 1];
2173 145 : unsigned int nBindIndex = 1;
2174 406 : for (i = iCur; i < iLastI; i++)
2175 : {
2176 261 : if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
2177 237 : strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
2178 : {
2179 237 : sqlite3_bind_int64(hStmt, nBindIndex,
2180 237 : psRelation->pasMembers[i].nID);
2181 237 : nBindIndex++;
2182 : }
2183 : }
2184 145 : iCur = iLastI;
2185 :
2186 344 : while (sqlite3_step(hStmt) == SQLITE_ROW)
2187 : {
2188 199 : GIntBig id = sqlite3_column_int64(hStmt, 0);
2189 199 : if (aoMapWays.find(id) == aoMapWays.end())
2190 : {
2191 199 : int nBlobSize = sqlite3_column_bytes(hStmt, 1);
2192 199 : const void *blob = sqlite3_column_blob(hStmt, 1);
2193 199 : void *blob_dup = CPLMalloc(nBlobSize);
2194 199 : memcpy(blob_dup, blob, nBlobSize);
2195 199 : aoMapWays[id] = std::pair(nBlobSize, blob_dup);
2196 : }
2197 199 : nFound++;
2198 : }
2199 :
2200 145 : sqlite3_reset(hStmt);
2201 : }
2202 :
2203 145 : return nFound;
2204 : }
2205 :
2206 : /************************************************************************/
2207 : /* BuildMultiPolygon() */
2208 : /************************************************************************/
2209 :
2210 92 : OGRGeometry *OGROSMDataSource::BuildMultiPolygon(const OSMRelation *psRelation,
2211 : unsigned int *pnTags,
2212 : OSMTag *pasTags)
2213 : {
2214 184 : std::map<GIntBig, std::pair<int, void *>> aoMapWays;
2215 92 : LookupWays(aoMapWays, psRelation);
2216 :
2217 92 : bool bMissing = false;
2218 :
2219 240 : for (unsigned int i = 0; i < psRelation->nMembers; i++)
2220 : {
2221 181 : if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
2222 181 : strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
2223 : {
2224 181 : if (aoMapWays.find(psRelation->pasMembers[i].nID) ==
2225 362 : aoMapWays.end())
2226 : {
2227 33 : CPLDebug("OSM",
2228 : "Relation " CPL_FRMT_GIB
2229 : " has missing ways. Ignoring it",
2230 33 : psRelation->nID);
2231 33 : bMissing = true;
2232 33 : break;
2233 : }
2234 : }
2235 : }
2236 :
2237 92 : if (bMissing)
2238 : {
2239 : // cppcheck-suppress constVariableReference
2240 63 : for (auto &oIter : aoMapWays)
2241 30 : CPLFree(oIter.second.second);
2242 :
2243 33 : return nullptr;
2244 : }
2245 :
2246 118 : OGRMultiLineString oMLS;
2247 118 : std::vector<OGRGeometry *> apoPolygons(psRelation->nMembers);
2248 59 : int nPolys = 0;
2249 :
2250 59 : if (pnTags != nullptr)
2251 30 : *pnTags = 0;
2252 :
2253 177 : for (unsigned int i = 0; i < psRelation->nMembers; i++)
2254 : {
2255 118 : if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
2256 118 : strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
2257 : {
2258 118 : const auto &oGeom = aoMapWays[psRelation->pasMembers[i].nID];
2259 :
2260 118 : if (pnTags != nullptr && *pnTags == 0 &&
2261 30 : 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 30 : m_abyWayBuffer.clear();
2266 0 : m_abyWayBuffer.insert(m_abyWayBuffer.end(),
2267 30 : static_cast<const GByte *>(oGeom.second),
2268 30 : static_cast<const GByte *>(oGeom.second) +
2269 30 : oGeom.first);
2270 :
2271 30 : UncompressWay(oGeom.first, m_abyWayBuffer.data(), nullptr,
2272 30 : m_asLonLatCache, pnTags, pasTags, nullptr);
2273 : }
2274 : else
2275 : {
2276 88 : UncompressWay(oGeom.first,
2277 88 : static_cast<const GByte *>(oGeom.second), nullptr,
2278 88 : m_asLonLatCache, nullptr, nullptr, nullptr);
2279 : }
2280 :
2281 118 : OGRLineString *poLS = nullptr;
2282 :
2283 118 : if (!m_asLonLatCache.empty() &&
2284 236 : m_asLonLatCache.front().nLon == m_asLonLatCache.back().nLon &&
2285 118 : m_asLonLatCache.front().nLat == m_asLonLatCache.back().nLat)
2286 : {
2287 118 : OGRPolygon *poPoly = new OGRPolygon();
2288 118 : OGRLinearRing *poRing = new OGRLinearRing();
2289 118 : poPoly->addRingDirectly(poRing);
2290 118 : apoPolygons[nPolys++] = poPoly;
2291 118 : poLS = poRing;
2292 :
2293 118 : if (strcmp(psRelation->pasMembers[i].pszRole, "outer") == 0)
2294 : {
2295 59 : sqlite3_bind_int64(m_hDeletePolygonsStandaloneStmt, 1,
2296 59 : psRelation->pasMembers[i].nID);
2297 59 : CPL_IGNORE_RET_VAL(
2298 59 : sqlite3_step(m_hDeletePolygonsStandaloneStmt));
2299 59 : sqlite3_reset(m_hDeletePolygonsStandaloneStmt);
2300 : }
2301 : }
2302 : else
2303 : {
2304 0 : poLS = new OGRLineString();
2305 0 : oMLS.addGeometryDirectly(poLS);
2306 : }
2307 :
2308 118 : const int nPoints = static_cast<int>(m_asLonLatCache.size());
2309 118 : poLS->setNumPoints(nPoints, /*bZeroizeNewContent=*/false);
2310 708 : for (int j = 0; j < nPoints; j++)
2311 : {
2312 590 : poLS->setPoint(j, INT_TO_DBL(m_asLonLatCache[j].nLon),
2313 590 : INT_TO_DBL(m_asLonLatCache[j].nLat));
2314 : }
2315 : }
2316 : }
2317 :
2318 59 : 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 59 : std::unique_ptr<OGRGeometry> poRet;
2342 :
2343 59 : if (nPolys > 0)
2344 : {
2345 59 : int bIsValidGeometry = FALSE;
2346 59 : const char *apszOptions[2] = {"METHOD=DEFAULT", nullptr};
2347 : auto poGeom =
2348 : std::unique_ptr<OGRGeometry>(OGRGeometryFactory::organizePolygons(
2349 118 : apoPolygons.data(), nPolys, &bIsValidGeometry, apszOptions));
2350 :
2351 59 : if (poGeom && poGeom->getGeometryType() == wkbPolygon)
2352 : {
2353 118 : auto poMulti = std::make_unique<OGRMultiPolygon>();
2354 59 : poMulti->addGeometryDirectly(poGeom.release());
2355 59 : poGeom = std::move(poMulti);
2356 : }
2357 :
2358 59 : if (poGeom && poGeom->getGeometryType() == wkbMultiPolygon)
2359 : {
2360 59 : 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 177 : for (auto &oIter : aoMapWays)
2376 118 : CPLFree(oIter.second.second);
2377 :
2378 59 : return poRet.release();
2379 : }
2380 :
2381 : /************************************************************************/
2382 : /* BuildGeometryCollection() */
2383 : /************************************************************************/
2384 :
2385 : OGRGeometry *
2386 53 : OGROSMDataSource::BuildGeometryCollection(const OSMRelation *psRelation,
2387 : bool bMultiLineString)
2388 : {
2389 106 : std::map<GIntBig, std::pair<int, void *>> aoMapWays;
2390 53 : LookupWays(aoMapWays, psRelation);
2391 :
2392 : std::unique_ptr<OGRGeometryCollection> poColl =
2393 81 : bMultiLineString ? std::make_unique<OGRMultiLineString>()
2394 134 : : std::make_unique<OGRGeometryCollection>();
2395 :
2396 130 : for (unsigned int i = 0; i < psRelation->nMembers; i++)
2397 : {
2398 77 : if (psRelation->pasMembers[i].eType == MEMBER_NODE && !bMultiLineString)
2399 : {
2400 24 : m_nUnsortedReqIds = 1;
2401 24 : m_panUnsortedReqIds[0] = psRelation->pasMembers[i].nID;
2402 24 : LookupNodes();
2403 24 : if (m_nReqIds == 1)
2404 : {
2405 46 : poColl->addGeometryDirectly(
2406 23 : new OGRPoint(INT_TO_DBL(m_pasLonLatArray[0].nLon),
2407 23 : INT_TO_DBL(m_pasLonLatArray[0].nLat)));
2408 : }
2409 : }
2410 159 : else if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
2411 106 : strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0 &&
2412 53 : aoMapWays.find(psRelation->pasMembers[i].nID) !=
2413 106 : aoMapWays.end())
2414 : {
2415 51 : const auto &oGeom = aoMapWays[psRelation->pasMembers[i].nID];
2416 :
2417 51 : bool bIsArea = false;
2418 51 : UncompressWay(oGeom.first, reinterpret_cast<GByte *>(oGeom.second),
2419 51 : &bIsArea, m_asLonLatCache, nullptr, nullptr, nullptr);
2420 51 : OGRLineString *poLS = nullptr;
2421 51 : 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 50 : poLS = new OGRLineString();
2432 50 : poColl->addGeometryDirectly(poLS);
2433 : }
2434 :
2435 51 : const int nPoints = static_cast<int>(m_asLonLatCache.size());
2436 51 : poLS->setNumPoints(nPoints, /*bZeroizeNewContent=*/false);
2437 155 : for (int j = 0; j < nPoints; j++)
2438 : {
2439 104 : poLS->setPoint(j, INT_TO_DBL(m_asLonLatCache[j].nLon),
2440 104 : INT_TO_DBL(m_asLonLatCache[j].nLat));
2441 : }
2442 : }
2443 : }
2444 :
2445 53 : if (poColl->getNumGeometries() == 0)
2446 : {
2447 2 : poColl.reset();
2448 : }
2449 :
2450 : // cppcheck-suppress constVariableReference
2451 104 : for (auto &oIter : aoMapWays)
2452 51 : CPLFree(oIter.second.second);
2453 :
2454 106 : return poColl.release();
2455 : }
2456 :
2457 : /************************************************************************/
2458 : /* NotifyRelation() */
2459 : /************************************************************************/
2460 :
2461 206 : void OGROSMDataSource::NotifyRelation(const OSMRelation *psRelation)
2462 : {
2463 206 : if (!m_asWayFeaturePairs.empty())
2464 36 : ProcessWaysBatch();
2465 :
2466 206 : m_nRelationsProcessed++;
2467 206 : 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 206 : if (!m_bUseWaysIndex)
2477 61 : return;
2478 :
2479 166 : bool bMultiPolygon = false;
2480 166 : bool bMultiLineString = false;
2481 166 : bool bInterestingTagFound = false;
2482 166 : const char *pszTypeV = nullptr;
2483 365 : for (unsigned int i = 0; i < psRelation->nTags; i++)
2484 : {
2485 199 : const char *pszK = psRelation->pasTags[i].pszK;
2486 199 : if (strcmp(pszK, "type") == 0)
2487 : {
2488 165 : const char *pszV = psRelation->pasTags[i].pszV;
2489 165 : pszTypeV = pszV;
2490 165 : if (strcmp(pszV, "multipolygon") == 0 ||
2491 66 : strcmp(pszV, "boundary") == 0)
2492 : {
2493 99 : bMultiPolygon = true;
2494 : }
2495 66 : else if (strcmp(pszV, "multilinestring") == 0 ||
2496 66 : strcmp(pszV, "route") == 0)
2497 : {
2498 33 : bMultiLineString = true;
2499 : }
2500 : }
2501 34 : else if (strcmp(pszK, "created_by") != 0)
2502 34 : 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 166 : const int iCurLayer = bMultiPolygon ? IDX_LYR_MULTIPOLYGONS
2509 : : bMultiLineString ? IDX_LYR_MULTILINESTRINGS
2510 : : IDX_LYR_OTHER_RELATIONS;
2511 166 : if (!m_apoLayers[iCurLayer]->IsUserInterested())
2512 19 : return;
2513 :
2514 0 : std::unique_ptr<OGRFeature> poFeature;
2515 :
2516 178 : 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 325 : 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 145 : OGRGeometry *poGeom = nullptr;
2536 :
2537 145 : unsigned int nExtraTags = 0;
2538 : OSMTag pasExtraTags[1 + MAX_COUNT_FOR_TAGS_IN_WAY];
2539 :
2540 145 : if (bMultiPolygon)
2541 : {
2542 92 : if (!bInterestingTagFound)
2543 : {
2544 62 : poGeom = BuildMultiPolygon(psRelation, &nExtraTags, pasExtraTags);
2545 62 : CPLAssert(nExtraTags <= MAX_COUNT_FOR_TAGS_IN_WAY);
2546 62 : pasExtraTags[nExtraTags].pszK = "type";
2547 62 : pasExtraTags[nExtraTags].pszV = pszTypeV;
2548 62 : nExtraTags++;
2549 : }
2550 : else
2551 30 : poGeom = BuildMultiPolygon(psRelation, nullptr, nullptr);
2552 : }
2553 : else
2554 53 : poGeom = BuildGeometryCollection(psRelation, bMultiLineString);
2555 :
2556 145 : if (poGeom != nullptr)
2557 : {
2558 110 : bool bAttrFilterAlreadyEvaluated = true;
2559 110 : if (poFeature == nullptr)
2560 : {
2561 107 : poFeature = std::make_unique<OGRFeature>(
2562 107 : m_apoLayers[iCurLayer]->GetLayerDefn());
2563 :
2564 398 : m_apoLayers[iCurLayer]->SetFieldsFromTags(
2565 107 : poFeature.get(), psRelation->nID, false,
2566 107 : nExtraTags ? nExtraTags : psRelation->nTags,
2567 107 : nExtraTags ? pasExtraTags : psRelation->pasTags,
2568 : &psRelation->sInfo);
2569 :
2570 107 : bAttrFilterAlreadyEvaluated = false;
2571 : }
2572 :
2573 110 : poFeature->SetGeometryDirectly(poGeom);
2574 :
2575 110 : bool bFilteredOut = FALSE;
2576 220 : if (!m_apoLayers[iCurLayer]->AddFeature(
2577 110 : std::move(poFeature), bAttrFilterAlreadyEvaluated,
2578 110 : &bFilteredOut, !m_bFeatureAdded))
2579 0 : m_bStopParsing = true;
2580 110 : else if (!bFilteredOut)
2581 108 : m_bFeatureAdded = true;
2582 : }
2583 : }
2584 :
2585 206 : static void OGROSMNotifyRelation(OSMRelation *psRelation,
2586 : OSMContext * /* psOSMContext */,
2587 : void *user_data)
2588 : {
2589 206 : static_cast<OGROSMDataSource *>(user_data)->NotifyRelation(psRelation);
2590 206 : }
2591 :
2592 : /************************************************************************/
2593 : /* ProcessPolygonsStandalone() */
2594 : /************************************************************************/
2595 :
2596 49 : void OGROSMDataSource::ProcessPolygonsStandalone()
2597 : {
2598 49 : unsigned int nTags = 0;
2599 : OSMTag pasTags[MAX_COUNT_FOR_TAGS_IN_WAY];
2600 : OSMInfo sInfo;
2601 :
2602 49 : sInfo.ts.nTimeStamp = 0;
2603 49 : sInfo.nChangeset = 0;
2604 49 : sInfo.nVersion = 0;
2605 49 : sInfo.nUID = 0;
2606 49 : sInfo.bTimeStampIsStr = false;
2607 49 : sInfo.pszUserSID = "";
2608 :
2609 49 : if (!m_bHasRowInPolygonsStandalone)
2610 49 : m_bHasRowInPolygonsStandalone =
2611 49 : sqlite3_step(m_hSelectPolygonsStandaloneStmt) == SQLITE_ROW;
2612 :
2613 49 : bool bFirst = true;
2614 :
2615 349 : while (m_bHasRowInPolygonsStandalone &&
2616 150 : m_apoLayers[IDX_LYR_MULTIPOLYGONS]->m_apoFeatures.size() < 10000)
2617 : {
2618 150 : if (bFirst)
2619 : {
2620 32 : CPLDebug("OSM", "Remaining standalone polygons");
2621 32 : bFirst = false;
2622 : }
2623 :
2624 150 : GIntBig id = sqlite3_column_int64(m_hSelectPolygonsStandaloneStmt, 0);
2625 :
2626 150 : sqlite3_bind_int64(m_pahSelectWayStmt[0], 1, id);
2627 150 : if (sqlite3_step(m_pahSelectWayStmt[0]) == SQLITE_ROW)
2628 : {
2629 150 : int nBlobSize = sqlite3_column_bytes(m_pahSelectWayStmt[0], 1);
2630 150 : const void *blob = sqlite3_column_blob(m_pahSelectWayStmt[0], 1);
2631 :
2632 : // coverity[tainted_data]
2633 150 : UncompressWay(nBlobSize, static_cast<const GByte *>(blob), nullptr,
2634 150 : m_asLonLatCache, &nTags, pasTags, &sInfo);
2635 150 : CPLAssert(nTags <= MAX_COUNT_FOR_TAGS_IN_WAY);
2636 :
2637 150 : OGRMultiPolygon *poMulti = new OGRMultiPolygon();
2638 150 : OGRPolygon *poPoly = new OGRPolygon();
2639 150 : OGRLinearRing *poRing = new OGRLinearRing();
2640 150 : poMulti->addGeometryDirectly(poPoly);
2641 150 : poPoly->addRingDirectly(poRing);
2642 150 : OGRLineString *poLS = poRing;
2643 :
2644 150 : poLS->setNumPoints(static_cast<int>(m_asLonLatCache.size()),
2645 : /*bZeroizeNewContent=*/false);
2646 1360 : for (int j = 0; j < static_cast<int>(m_asLonLatCache.size()); j++)
2647 : {
2648 1210 : poLS->setPoint(j, INT_TO_DBL(m_asLonLatCache[j].nLon),
2649 1210 : INT_TO_DBL(m_asLonLatCache[j].nLat));
2650 : }
2651 :
2652 : auto poFeature = std::make_unique<OGRFeature>(
2653 150 : m_apoLayers[IDX_LYR_MULTIPOLYGONS]->GetLayerDefn());
2654 :
2655 150 : m_apoLayers[IDX_LYR_MULTIPOLYGONS]->SetFieldsFromTags(
2656 : poFeature.get(), id, true, nTags, pasTags, &sInfo);
2657 :
2658 150 : poFeature->SetGeometryDirectly(poMulti);
2659 :
2660 150 : bool bFilteredOut = false;
2661 300 : if (!m_apoLayers[IDX_LYR_MULTIPOLYGONS]->AddFeature(
2662 150 : std::move(poFeature), FALSE, &bFilteredOut,
2663 150 : !m_bFeatureAdded))
2664 : {
2665 0 : m_bStopParsing = true;
2666 0 : break;
2667 : }
2668 150 : else if (!bFilteredOut)
2669 : {
2670 147 : m_bFeatureAdded = true;
2671 : }
2672 : }
2673 : else
2674 : {
2675 0 : CPLAssert(false);
2676 : }
2677 :
2678 150 : sqlite3_reset(m_pahSelectWayStmt[0]);
2679 :
2680 150 : m_bHasRowInPolygonsStandalone =
2681 150 : sqlite3_step(m_hSelectPolygonsStandaloneStmt) == SQLITE_ROW;
2682 : }
2683 49 : }
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 38 : int OGROSMDataSource::Open(const char *pszFilename,
2715 : CSLConstList papszOpenOptionsIn)
2716 :
2717 : {
2718 38 : m_psParser = OSM_Open(pszFilename, OGROSMNotifyNodes, OGROSMNotifyWay,
2719 : OGROSMNotifyRelation, OGROSMNotifyBounds, this);
2720 38 : if (m_psParser == nullptr)
2721 0 : return FALSE;
2722 :
2723 38 : if (CPLFetchBool(papszOpenOptionsIn, "INTERLEAVED_READING", false))
2724 0 : m_bInterleavedReading = TRUE;
2725 :
2726 : /* The following 4 config options are only useful for debugging */
2727 38 : m_bIndexPoints = CPLTestBool(CPLGetConfigOption("OSM_INDEX_POINTS", "YES"));
2728 38 : m_bUsePointsIndex =
2729 38 : CPLTestBool(CPLGetConfigOption("OSM_USE_POINTS_INDEX", "YES"));
2730 38 : m_bIndexWays = CPLTestBool(CPLGetConfigOption("OSM_INDEX_WAYS", "YES"));
2731 38 : m_bUseWaysIndex =
2732 38 : CPLTestBool(CPLGetConfigOption("OSM_USE_WAYS_INDEX", "YES"));
2733 :
2734 38 : m_bCustomIndexing = CPLTestBool(CSLFetchNameValueDef(
2735 : papszOpenOptionsIn, "USE_CUSTOM_INDEXING",
2736 : CPLGetConfigOption("OSM_USE_CUSTOM_INDEXING", "YES")));
2737 38 : if (!m_bCustomIndexing)
2738 2 : CPLDebug("OSM", "Using SQLite indexing for points");
2739 38 : m_bCompressNodes = CPLTestBool(
2740 : CSLFetchNameValueDef(papszOpenOptionsIn, "COMPRESS_NODES",
2741 : CPLGetConfigOption("OSM_COMPRESS_NODES", "NO")));
2742 38 : 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 38 : std::make_unique<OGROSMLayer>(this, IDX_LYR_POINTS, "points"));
2748 38 : m_apoLayers.back()->GetLayerDefn()->SetGeomType(wkbPoint);
2749 :
2750 : m_apoLayers.emplace_back(
2751 38 : std::make_unique<OGROSMLayer>(this, IDX_LYR_LINES, "lines"));
2752 38 : m_apoLayers.back()->GetLayerDefn()->SetGeomType(wkbLineString);
2753 :
2754 38 : m_apoLayers.emplace_back(std::make_unique<OGROSMLayer>(
2755 38 : this, IDX_LYR_MULTILINESTRINGS, "multilinestrings"));
2756 38 : m_apoLayers.back()->GetLayerDefn()->SetGeomType(wkbMultiLineString);
2757 :
2758 38 : m_apoLayers.emplace_back(std::make_unique<OGROSMLayer>(
2759 38 : this, IDX_LYR_MULTIPOLYGONS, "multipolygons"));
2760 38 : m_apoLayers.back()->GetLayerDefn()->SetGeomType(wkbMultiPolygon);
2761 :
2762 38 : m_apoLayers.emplace_back(std::make_unique<OGROSMLayer>(
2763 38 : this, IDX_LYR_OTHER_RELATIONS, "other_relations"));
2764 38 : m_apoLayers.back()->GetLayerDefn()->SetGeomType(wkbGeometryCollection);
2765 :
2766 38 : 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 38 : CSLFetchNameValue(papszOpenOptionsIn, "TAGS_FORMAT");
2775 38 : 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 38 : const auto eTagsSubType = m_bTagsAsHSTORE ? OFSTNone : OFSTJSON;
2790 228 : for (auto &&poLayer : m_apoLayers)
2791 : {
2792 190 : 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 188 : else if (poLayer->HasOtherTags())
2801 188 : poLayer->AddField("other_tags", OFTString, eTagsSubType);
2802 : }
2803 :
2804 38 : m_bNeedsToSaveWayInfo =
2805 76 : (m_apoLayers[IDX_LYR_MULTIPOLYGONS]->HasTimestamp() ||
2806 76 : m_apoLayers[IDX_LYR_MULTIPOLYGONS]->HasChangeset() ||
2807 76 : m_apoLayers[IDX_LYR_MULTIPOLYGONS]->HasVersion() ||
2808 114 : m_apoLayers[IDX_LYR_MULTIPOLYGONS]->HasUID() ||
2809 38 : m_apoLayers[IDX_LYR_MULTIPOLYGONS]->HasUser());
2810 :
2811 38 : m_panReqIds = static_cast<GIntBig *>(
2812 38 : VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_NODES * sizeof(GIntBig)));
2813 : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
2814 38 : m_panHashedIndexes = static_cast<int *>(
2815 38 : VSI_MALLOC_VERBOSE(HASHED_INDEXES_ARRAY_SIZE * sizeof(int)));
2816 38 : m_psCollisionBuckets = static_cast<CollisionBucket *>(VSI_MALLOC_VERBOSE(
2817 : COLLISION_BUCKET_ARRAY_SIZE * sizeof(CollisionBucket)));
2818 : #endif
2819 38 : m_pasLonLatArray = static_cast<LonLat *>(
2820 38 : VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_NODES * sizeof(LonLat)));
2821 38 : m_panUnsortedReqIds = static_cast<GIntBig *>(
2822 38 : VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_NODES * sizeof(GIntBig)));
2823 : try
2824 : {
2825 38 : 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 38 : m_pasAccumulatedTags = static_cast<IndexedKVP *>(
2834 38 : VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_TAGS * sizeof(IndexedKVP)));
2835 38 : pabyNonRedundantValues =
2836 38 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(MAX_NON_REDUNDANT_VALUES));
2837 38 : pabyNonRedundantKeys =
2838 38 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(MAX_NON_REDUNDANT_KEYS));
2839 38 : if (m_panReqIds == nullptr || m_pasLonLatArray == nullptr ||
2840 38 : m_panUnsortedReqIds == nullptr || m_pasAccumulatedTags == nullptr ||
2841 38 : pabyNonRedundantValues == nullptr || pabyNonRedundantKeys == nullptr)
2842 : {
2843 0 : return FALSE;
2844 : }
2845 :
2846 38 : m_nMaxSizeForInMemoryDBInMB = atoi(CSLFetchNameValueDef(
2847 : papszOpenOptionsIn, "MAX_TMPFILE_SIZE",
2848 : CPLGetConfigOption("OSM_MAX_TMPFILE_SIZE", "100")));
2849 38 : if (m_nMaxSizeForInMemoryDBInMB == 0)
2850 0 : m_nMaxSizeForInMemoryDBInMB = 1;
2851 38 : GIntBig nSize =
2852 38 : static_cast<GIntBig>(m_nMaxSizeForInMemoryDBInMB) * 1024 * 1024;
2853 76 : if (nSize < 0 ||
2854 38 : 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 38 : if (m_bCustomIndexing)
2863 : {
2864 36 : m_pabySector = static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, SECTOR_SIZE));
2865 :
2866 36 : if (m_pabySector == nullptr)
2867 : {
2868 0 : return FALSE;
2869 : }
2870 :
2871 36 : m_bInMemoryNodesFile = true;
2872 36 : m_osNodesFilename = VSIMemGenerateHiddenFilename("osm_temp_nodes");
2873 36 : m_fpNodes = VSIFOpenL(m_osNodesFilename, "wb+");
2874 36 : if (m_fpNodes == nullptr)
2875 : {
2876 0 : return FALSE;
2877 : }
2878 :
2879 36 : CPLPushErrorHandler(CPLQuietErrorHandler);
2880 : const bool bSuccess =
2881 72 : VSIFTruncateL(m_fpNodes,
2882 36 : static_cast<vsi_l_offset>(nSize * 3 / 4)) == 0;
2883 36 : CPLPopErrorHandler();
2884 :
2885 36 : if (bSuccess)
2886 : {
2887 36 : 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 = CPLGenerateTempFilenameSafe("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 38 : const bool bRet = CreateTempDB();
2923 38 : if (bRet)
2924 : {
2925 : CPLString osInterestLayers =
2926 76 : GetInterestLayersForDSName(GetDescription());
2927 38 : if (!osInterestLayers.empty())
2928 : {
2929 0 : ReleaseResultSet(ExecuteSQL(osInterestLayers, nullptr, nullptr));
2930 : }
2931 : }
2932 38 : return bRet;
2933 : }
2934 :
2935 : /************************************************************************/
2936 : /* CreateTempDB() */
2937 : /************************************************************************/
2938 :
2939 38 : bool OGROSMDataSource::CreateTempDB()
2940 : {
2941 38 : char *pszErrMsg = nullptr;
2942 :
2943 38 : int rc = 0;
2944 38 : bool bIsExisting = false;
2945 38 : bool bSuccess = false;
2946 :
2947 : const char *pszExistingTmpFile =
2948 38 : CPLGetConfigOption("OSM_EXISTING_TMPFILE", nullptr);
2949 38 : 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 38 : 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 38 : VSILFILE *fp = VSIFOpenL(m_osTmpDBName, "wb");
2964 38 : if (fp)
2965 : {
2966 38 : vsi_l_offset nSize =
2967 38 : static_cast<vsi_l_offset>(m_nMaxSizeForInMemoryDBInMB) * 1024 *
2968 : 1024;
2969 38 : if (m_bCustomIndexing && m_bInMemoryNodesFile)
2970 36 : nSize = nSize / 4;
2971 :
2972 38 : CPLPushErrorHandler(CPLQuietErrorHandler);
2973 38 : bSuccess = VSIFTruncateL(fp, nSize) == 0;
2974 38 : CPLPopErrorHandler();
2975 :
2976 38 : if (bSuccess)
2977 38 : bSuccess = VSIFTruncateL(fp, 0) == 0;
2978 :
2979 38 : VSIFCloseL(fp);
2980 :
2981 38 : 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 38 : if (bSuccess)
2990 : {
2991 38 : m_bInMemoryTmpDB = true;
2992 38 : m_pMyVFS = OGRSQLiteCreateVFS(nullptr, this);
2993 38 : sqlite3_vfs_register(m_pMyVFS, 0);
2994 38 : rc = sqlite3_open_v2(m_osTmpDBName.c_str(), &m_hDB,
2995 : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
2996 : SQLITE_OPEN_NOMUTEX,
2997 38 : m_pMyVFS->zName);
2998 : }
2999 : }
3000 :
3001 38 : if (!bSuccess)
3002 : {
3003 0 : m_osTmpDBName = CPLGenerateTempFilenameSafe("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 38 : 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 38 : if (!SetDBOptions())
3029 : {
3030 0 : return false;
3031 : }
3032 :
3033 38 : if (!bIsExisting)
3034 : {
3035 38 : rc = sqlite3_exec(
3036 : m_hDB, "CREATE TABLE nodes (id INTEGER PRIMARY KEY, coords BLOB)",
3037 : nullptr, nullptr, &pszErrMsg);
3038 38 : 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 38 : rc = sqlite3_exec(
3047 : m_hDB, "CREATE TABLE ways (id INTEGER PRIMARY KEY, data BLOB)",
3048 : nullptr, nullptr, &pszErrMsg);
3049 38 : 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 38 : rc = sqlite3_exec(
3058 : m_hDB, "CREATE TABLE polygons_standalone (id INTEGER PRIMARY KEY)",
3059 : nullptr, nullptr, &pszErrMsg);
3060 38 : 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 38 : return CreatePreparedStatements();
3071 : }
3072 :
3073 : /************************************************************************/
3074 : /* SetDBOptions() */
3075 : /************************************************************************/
3076 :
3077 38 : bool OGROSMDataSource::SetDBOptions()
3078 : {
3079 38 : char *pszErrMsg = nullptr;
3080 38 : int rc = sqlite3_exec(m_hDB, "PRAGMA synchronous = OFF", nullptr, nullptr,
3081 : &pszErrMsg);
3082 38 : 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 38 : rc = sqlite3_exec(m_hDB, "PRAGMA journal_mode = OFF", nullptr, nullptr,
3091 : &pszErrMsg);
3092 38 : 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 38 : rc = sqlite3_exec(m_hDB, "PRAGMA temp_store = MEMORY", nullptr, nullptr,
3101 : &pszErrMsg);
3102 38 : 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 38 : SetCacheSize();
3111 :
3112 38 : if (!StartTransactionCacheDB())
3113 0 : return false;
3114 :
3115 38 : return true;
3116 : }
3117 :
3118 : /************************************************************************/
3119 : /* SetCacheSize() */
3120 : /************************************************************************/
3121 :
3122 38 : void OGROSMDataSource::SetCacheSize()
3123 : {
3124 : const char *pszSqliteCacheMB =
3125 38 : CPLGetConfigOption("OSM_SQLITE_CACHE", nullptr);
3126 :
3127 38 : if (pszSqliteCacheMB == nullptr)
3128 38 : 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 38 : bool OGROSMDataSource::CreatePreparedStatements()
3182 : {
3183 : int rc =
3184 38 : sqlite3_prepare_v2(m_hDB, "INSERT INTO nodes (id, coords) VALUES (?,?)",
3185 : -1, &m_hInsertNodeStmt, nullptr);
3186 38 : 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 38 : m_pahSelectNodeStmt = static_cast<sqlite3_stmt **>(
3194 38 : CPLCalloc(sizeof(sqlite3_stmt *), LIMIT_IDS_PER_REQUEST));
3195 :
3196 : char szTmp[LIMIT_IDS_PER_REQUEST * 2 + 128];
3197 38 : strcpy(szTmp, "SELECT id, coords FROM nodes WHERE id IN (");
3198 38 : int nLen = static_cast<int>(strlen(szTmp));
3199 7638 : for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
3200 : {
3201 7600 : if (i == 0)
3202 : {
3203 38 : strcpy(szTmp + nLen, "?) ORDER BY id ASC");
3204 38 : nLen += 2;
3205 : }
3206 : else
3207 : {
3208 7562 : strcpy(szTmp + nLen - 1, ",?) ORDER BY id ASC");
3209 7562 : nLen += 2;
3210 : }
3211 7600 : rc = sqlite3_prepare_v2(m_hDB, szTmp, -1, &m_pahSelectNodeStmt[i],
3212 : nullptr);
3213 7600 : 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 38 : rc = sqlite3_prepare_v2(m_hDB, "INSERT INTO ways (id, data) VALUES (?,?)",
3223 : -1, &m_hInsertWayStmt, nullptr);
3224 38 : 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 38 : m_pahSelectWayStmt = static_cast<sqlite3_stmt **>(
3232 38 : CPLCalloc(sizeof(sqlite3_stmt *), LIMIT_IDS_PER_REQUEST));
3233 :
3234 38 : strcpy(szTmp, "SELECT id, data FROM ways WHERE id IN (");
3235 38 : nLen = static_cast<int>(strlen(szTmp));
3236 7638 : for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
3237 : {
3238 7600 : if (i == 0)
3239 : {
3240 38 : strcpy(szTmp + nLen, "?)");
3241 38 : nLen += 2;
3242 : }
3243 : else
3244 : {
3245 7562 : strcpy(szTmp + nLen - 1, ",?)");
3246 7562 : nLen += 2;
3247 : }
3248 7600 : rc = sqlite3_prepare_v2(m_hDB, szTmp, -1, &m_pahSelectWayStmt[i],
3249 : nullptr);
3250 7600 : 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 38 : rc = sqlite3_prepare_v2(m_hDB,
3260 : "INSERT INTO polygons_standalone (id) VALUES (?)",
3261 : -1, &m_hInsertPolygonsStandaloneStmt, nullptr);
3262 38 : 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 38 : rc = sqlite3_prepare_v2(m_hDB,
3270 : "DELETE FROM polygons_standalone WHERE id = ?", -1,
3271 : &m_hDeletePolygonsStandaloneStmt, nullptr);
3272 38 : 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 38 : rc = sqlite3_prepare_v2(m_hDB,
3280 : "SELECT id FROM polygons_standalone ORDER BY id",
3281 : -1, &m_hSelectPolygonsStandaloneStmt, nullptr);
3282 38 : 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 38 : return true;
3290 : }
3291 :
3292 : /************************************************************************/
3293 : /* StartTransactionCacheDB() */
3294 : /************************************************************************/
3295 :
3296 38 : bool OGROSMDataSource::StartTransactionCacheDB()
3297 : {
3298 38 : if (m_bInTransaction)
3299 0 : return false;
3300 :
3301 38 : char *pszErrMsg = nullptr;
3302 38 : int rc = sqlite3_exec(m_hDB, "BEGIN", nullptr, nullptr, &pszErrMsg);
3303 38 : 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 38 : m_bInTransaction = true;
3312 :
3313 38 : return true;
3314 : }
3315 :
3316 : /************************************************************************/
3317 : /* CommitTransactionCacheDB() */
3318 : /************************************************************************/
3319 :
3320 38 : bool OGROSMDataSource::CommitTransactionCacheDB()
3321 : {
3322 38 : if (!m_bInTransaction)
3323 0 : return false;
3324 :
3325 38 : m_bInTransaction = false;
3326 :
3327 38 : char *pszErrMsg = nullptr;
3328 38 : int rc = sqlite3_exec(m_hDB, "COMMIT", nullptr, nullptr, &pszErrMsg);
3329 38 : 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 38 : return true;
3338 : }
3339 :
3340 : /************************************************************************/
3341 : /* AddComputedAttributes() */
3342 : /************************************************************************/
3343 :
3344 186 : void OGROSMDataSource::AddComputedAttributes(
3345 : int iCurLayer, const std::vector<OGROSMComputedAttribute> &oAttributes)
3346 : {
3347 223 : for (const auto &oAttribute : oAttributes)
3348 : {
3349 37 : if (!oAttribute.osSQL.empty())
3350 : {
3351 74 : m_apoLayers[iCurLayer]->AddComputedAttribute(
3352 37 : oAttribute.osName, oAttribute.eType, oAttribute.osSQL);
3353 : }
3354 : }
3355 186 : }
3356 :
3357 : /************************************************************************/
3358 : /* ParseConf() */
3359 : /************************************************************************/
3360 :
3361 38 : bool OGROSMDataSource::ParseConf(CSLConstList papszOpenOptionsIn)
3362 : {
3363 38 : VSILFILE *fpConf = nullptr;
3364 :
3365 : const char *pszFilename =
3366 38 : CSLFetchNameValueDef(papszOpenOptionsIn, "CONFIG_FILE",
3367 : CPLGetConfigOption("OSM_CONFIG_FILE", nullptr));
3368 38 : if (pszFilename == nullptr)
3369 : {
3370 : #if !defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
3371 36 : 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 36 : 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 38 : if (pszFilename)
3399 38 : m_osConfigFile = pszFilename;
3400 :
3401 : #if defined(EMBED_RESOURCE_FILES)
3402 : if (!fpConf)
3403 : #endif
3404 : {
3405 38 : fpConf = VSIFOpenL(pszFilename, "rb");
3406 38 : if (fpConf == nullptr)
3407 0 : return false;
3408 : }
3409 :
3410 38 : const char *pszLine = nullptr;
3411 38 : int iCurLayer = -1;
3412 38 : std::vector<OGROSMComputedAttribute> oAttributes;
3413 :
3414 4976 : while ((pszLine = CPLReadLine2L(fpConf, -1, nullptr)) != nullptr)
3415 : {
3416 4938 : if (pszLine[0] == '#')
3417 2345 : continue;
3418 2593 : if (pszLine[0] == '[' && pszLine[strlen(pszLine) - 1] == ']')
3419 : {
3420 222 : if (iCurLayer >= 0)
3421 148 : AddComputedAttributes(iCurLayer, oAttributes);
3422 222 : oAttributes.resize(0);
3423 :
3424 222 : iCurLayer = -1;
3425 222 : pszLine++;
3426 222 : const_cast<char *>(pszLine)[strlen(pszLine) - 1] =
3427 : '\0'; /* Evil but OK */
3428 :
3429 222 : if (strcmp(pszLine, "general") == 0)
3430 : {
3431 36 : continue;
3432 : }
3433 :
3434 186 : int i = 0;
3435 556 : for (auto &&poLayer : m_apoLayers)
3436 : {
3437 556 : if (strcmp(pszLine, poLayer->GetName()) == 0)
3438 : {
3439 186 : iCurLayer = i;
3440 186 : break;
3441 : }
3442 370 : ++i;
3443 : }
3444 186 : 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 186 : continue;
3451 : }
3452 :
3453 2371 : if (STARTS_WITH(pszLine, "closed_ways_are_polygons="))
3454 : {
3455 37 : char **papszTokens2 = CSLTokenizeString2(
3456 : pszLine + strlen("closed_ways_are_polygons="), ",", 0);
3457 37 : m_nMinSizeKeysInSetClosedWaysArePolygons = INT_MAX;
3458 37 : m_nMaxSizeKeysInSetClosedWaysArePolygons = 0;
3459 701 : for (int i = 0; papszTokens2[i] != nullptr; i++)
3460 : {
3461 664 : const int nTokenSize =
3462 664 : static_cast<int>(strlen(papszTokens2[i]));
3463 664 : aoSetClosedWaysArePolygons.insert(papszTokens2[i]);
3464 664 : m_nMinSizeKeysInSetClosedWaysArePolygons = std::min(
3465 664 : m_nMinSizeKeysInSetClosedWaysArePolygons, nTokenSize);
3466 664 : m_nMaxSizeKeysInSetClosedWaysArePolygons = std::max(
3467 664 : m_nMaxSizeKeysInSetClosedWaysArePolygons, nTokenSize);
3468 : }
3469 37 : CSLDestroy(papszTokens2);
3470 : }
3471 :
3472 2334 : 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 2334 : 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 2334 : 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 2334 : 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 2332 : 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 2332 : else if (iCurLayer >= 0)
3537 : {
3538 2074 : char **papszTokens = CSLTokenizeString2(pszLine, "=", 0);
3539 3705 : if (CSLCount(papszTokens) == 2 &&
3540 1631 : 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 3705 : else if (CSLCount(papszTokens) == 2 &&
3548 1631 : 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 3701 : else if (CSLCount(papszTokens) == 2 &&
3556 1629 : strcmp(papszTokens[0], "osm_id") == 0)
3557 : {
3558 185 : if (strcmp(papszTokens[1], "no") == 0)
3559 0 : m_apoLayers[iCurLayer]->SetHasOSMId(false);
3560 185 : else if (strcmp(papszTokens[1], "yes") == 0)
3561 : {
3562 185 : m_apoLayers[iCurLayer]->SetHasOSMId(true);
3563 185 : m_apoLayers[iCurLayer]->AddField("osm_id", OFTString);
3564 :
3565 185 : if (iCurLayer == IDX_LYR_MULTIPOLYGONS)
3566 37 : m_apoLayers[iCurLayer]->AddField("osm_way_id",
3567 : OFTString);
3568 : }
3569 : }
3570 3331 : else if (CSLCount(papszTokens) == 2 &&
3571 1444 : strcmp(papszTokens[0], "osm_version") == 0)
3572 : {
3573 185 : if (strcmp(papszTokens[1], "no") == 0)
3574 185 : 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 2961 : else if (CSLCount(papszTokens) == 2 &&
3582 1259 : strcmp(papszTokens[0], "osm_timestamp") == 0)
3583 : {
3584 185 : if (strcmp(papszTokens[1], "no") == 0)
3585 185 : 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 2591 : else if (CSLCount(papszTokens) == 2 &&
3594 1074 : strcmp(papszTokens[0], "osm_uid") == 0)
3595 : {
3596 185 : if (strcmp(papszTokens[1], "no") == 0)
3597 185 : 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 2221 : else if (CSLCount(papszTokens) == 2 &&
3605 889 : strcmp(papszTokens[0], "osm_user") == 0)
3606 : {
3607 185 : if (strcmp(papszTokens[1], "no") == 0)
3608 185 : 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 1851 : else if (CSLCount(papszTokens) == 2 &&
3616 704 : strcmp(papszTokens[0], "osm_changeset") == 0)
3617 : {
3618 185 : if (strcmp(papszTokens[1], "no") == 0)
3619 185 : 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 1481 : else if (CSLCount(papszTokens) == 2 &&
3628 519 : strcmp(papszTokens[0], "attributes") == 0)
3629 : {
3630 : char **papszTokens2 =
3631 186 : CSLTokenizeString2(papszTokens[1], ",", 0);
3632 1704 : for (int i = 0; papszTokens2[i] != nullptr; i++)
3633 : {
3634 1518 : m_apoLayers[iCurLayer]->AddField(papszTokens2[i],
3635 : OFTString);
3636 12144 : for (const char *&pszIgnoredKey : m_ignoredKeys)
3637 : {
3638 10626 : if (strcmp(papszTokens2[i], pszIgnoredKey) == 0)
3639 0 : pszIgnoredKey = "";
3640 : }
3641 : }
3642 186 : CSLDestroy(papszTokens2);
3643 : }
3644 1109 : else if (CSLCount(papszTokens) == 2 &&
3645 333 : (strcmp(papszTokens[0], "unsignificant") == 0 ||
3646 296 : strcmp(papszTokens[0], "insignificant") == 0))
3647 : {
3648 : char **papszTokens2 =
3649 37 : CSLTokenizeString2(papszTokens[1], ",", 0);
3650 258 : for (int i = 0; papszTokens2[i] != nullptr; i++)
3651 : {
3652 221 : m_apoLayers[iCurLayer]->AddInsignificantKey(
3653 221 : papszTokens2[i]);
3654 : }
3655 37 : CSLDestroy(papszTokens2);
3656 : }
3657 1035 : else if (CSLCount(papszTokens) == 2 &&
3658 296 : strcmp(papszTokens[0], "ignore") == 0)
3659 : {
3660 : char **papszTokens2 =
3661 185 : CSLTokenizeString2(papszTokens[1], ",", 0);
3662 2141 : for (int i = 0; papszTokens2[i] != nullptr; i++)
3663 : {
3664 1956 : m_apoLayers[iCurLayer]->AddIgnoreKey(papszTokens2[i]);
3665 1956 : m_apoLayers[iCurLayer]->AddWarnKey(papszTokens2[i]);
3666 : }
3667 185 : CSLDestroy(papszTokens2);
3668 : }
3669 665 : else if (CSLCount(papszTokens) == 2 &&
3670 111 : strcmp(papszTokens[0], "computed_attributes") == 0)
3671 : {
3672 : char **papszTokens2 =
3673 37 : CSLTokenizeString2(papszTokens[1], ",", 0);
3674 37 : oAttributes.resize(0);
3675 74 : for (int i = 0; papszTokens2[i] != nullptr; i++)
3676 : {
3677 37 : oAttributes.push_back(
3678 74 : OGROSMComputedAttribute(papszTokens2[i]));
3679 : }
3680 37 : CSLDestroy(papszTokens2);
3681 : }
3682 517 : else if (CSLCount(papszTokens) == 2 &&
3683 591 : strlen(papszTokens[0]) >= 5 &&
3684 74 : strcmp(papszTokens[0] + strlen(papszTokens[0]) - 5,
3685 : "_type") == 0)
3686 : {
3687 74 : CPLString osName(papszTokens[0]);
3688 37 : osName.resize(strlen(papszTokens[0]) - 5);
3689 37 : const char *pszType = papszTokens[1];
3690 37 : bool bFound = false;
3691 37 : OGRFieldType eType = OFTString;
3692 37 : if (EQUAL(pszType, "Integer"))
3693 37 : 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 37 : for (size_t i = 0; i < oAttributes.size(); i++)
3707 : {
3708 37 : if (oAttributes[i].osName == osName)
3709 : {
3710 37 : bFound = true;
3711 37 : oAttributes[i].eType = eType;
3712 37 : break;
3713 : }
3714 : }
3715 37 : 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 37 : if (!bFound)
3730 : {
3731 0 : CPLError(CE_Warning, CPLE_AppDefined,
3732 : "Undeclared attribute : %s", osName.c_str());
3733 : }
3734 : }
3735 480 : else if (CSLCount(papszTokens) >= 2 &&
3736 517 : strlen(papszTokens[0]) >= 4 &&
3737 37 : strcmp(papszTokens[0] + strlen(papszTokens[0]) - 4,
3738 : "_sql") == 0)
3739 : {
3740 74 : CPLString osName(papszTokens[0]);
3741 37 : osName.resize(strlen(papszTokens[0]) - 4);
3742 37 : size_t i = 0; // Used after for.
3743 37 : for (; i < oAttributes.size(); i++)
3744 : {
3745 37 : if (oAttributes[i].osName == osName)
3746 : {
3747 37 : const char *pszSQL = strchr(pszLine, '=') + 1;
3748 37 : while (*pszSQL == ' ')
3749 0 : pszSQL++;
3750 37 : bool bInQuotes = false;
3751 37 : if (*pszSQL == '"')
3752 : {
3753 37 : bInQuotes = true;
3754 37 : pszSQL++;
3755 : }
3756 37 : oAttributes[i].osSQL = pszSQL;
3757 74 : if (bInQuotes && oAttributes[i].osSQL.size() > 1 &&
3758 37 : oAttributes[i].osSQL.back() == '"')
3759 74 : oAttributes[i].osSQL.resize(
3760 37 : oAttributes[i].osSQL.size() - 1);
3761 37 : break;
3762 : }
3763 : }
3764 37 : if (i == oAttributes.size())
3765 : {
3766 0 : CPLError(CE_Warning, CPLE_AppDefined,
3767 : "Undeclared attribute : %s", osName.c_str());
3768 : }
3769 : }
3770 2074 : CSLDestroy(papszTokens);
3771 : }
3772 : }
3773 :
3774 38 : if (iCurLayer >= 0)
3775 38 : AddComputedAttributes(iCurLayer, oAttributes);
3776 :
3777 38 : VSIFCloseL(fpConf);
3778 :
3779 38 : return true;
3780 : }
3781 :
3782 : /************************************************************************/
3783 : /* MyResetReading() */
3784 : /************************************************************************/
3785 :
3786 65 : int OGROSMDataSource::MyResetReading()
3787 : {
3788 65 : if (m_hDB == nullptr)
3789 0 : return FALSE;
3790 65 : if (m_bCustomIndexing && m_fpNodes == nullptr)
3791 0 : return FALSE;
3792 :
3793 65 : OSM_ResetReading(m_psParser);
3794 :
3795 65 : char *pszErrMsg = nullptr;
3796 : int rc =
3797 65 : sqlite3_exec(m_hDB, "DELETE FROM nodes", nullptr, nullptr, &pszErrMsg);
3798 65 : 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 65 : rc = sqlite3_exec(m_hDB, "DELETE FROM ways", nullptr, nullptr, &pszErrMsg);
3807 65 : 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 65 : rc = sqlite3_exec(m_hDB, "DELETE FROM polygons_standalone", nullptr,
3816 : nullptr, &pszErrMsg);
3817 65 : 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 65 : m_bHasRowInPolygonsStandalone = false;
3825 :
3826 65 : if (m_hSelectPolygonsStandaloneStmt != nullptr)
3827 65 : sqlite3_reset(m_hSelectPolygonsStandaloneStmt);
3828 :
3829 : {
3830 65 : m_asWayFeaturePairs.clear();
3831 65 : m_nUnsortedReqIds = 0;
3832 65 : m_nReqIds = 0;
3833 65 : m_nAccumulatedTags = 0;
3834 65 : nNonRedundantKeysLen = 0;
3835 65 : nNonRedundantValuesLen = 0;
3836 :
3837 150 : for (KeyDesc *psKD : m_apsKeys)
3838 : {
3839 85 : if (psKD)
3840 : {
3841 20 : CPLFree(psKD->pszK);
3842 60 : for (auto *pszValue : psKD->apszValues)
3843 40 : CPLFree(pszValue);
3844 20 : delete psKD;
3845 : }
3846 : }
3847 65 : m_apsKeys.resize(1); // keep guard to avoid index 0 to be used
3848 65 : m_aoMapIndexedKeys.clear();
3849 : }
3850 :
3851 65 : if (m_bCustomIndexing)
3852 : {
3853 64 : m_nPrevNodeId = -1;
3854 64 : m_nBucketOld = -1;
3855 64 : m_nOffInBucketReducedOld = -1;
3856 :
3857 64 : VSIFSeekL(m_fpNodes, 0, SEEK_SET);
3858 64 : VSIFTruncateL(m_fpNodes, 0);
3859 64 : m_nNodesFileSize = 0;
3860 :
3861 64 : memset(m_pabySector, 0, SECTOR_SIZE);
3862 :
3863 106 : for (auto &oIter : m_oMapBuckets)
3864 : {
3865 42 : Bucket &sBucket = oIter.second;
3866 42 : sBucket.nOff = -1;
3867 42 : 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 42 : if (sBucket.u.pabyBitmap)
3876 42 : memset(sBucket.u.pabyBitmap, 0, BUCKET_BITMAP_SIZE);
3877 : }
3878 : }
3879 : }
3880 :
3881 390 : for (auto &&poLayer : m_apoLayers)
3882 : {
3883 325 : poLayer->ForceResetReading();
3884 : }
3885 :
3886 65 : m_bStopParsing = false;
3887 65 : m_poCurrentLayer = nullptr;
3888 :
3889 65 : return TRUE;
3890 : }
3891 :
3892 : /************************************************************************/
3893 : /* ResetReading() */
3894 : /************************************************************************/
3895 :
3896 7 : void OGROSMDataSource::ResetReading()
3897 : {
3898 7 : MyResetReading();
3899 7 : }
3900 :
3901 : /************************************************************************/
3902 : /* GetNextFeature() */
3903 : /************************************************************************/
3904 :
3905 126 : OGRFeature *OGROSMDataSource::GetNextFeature(OGRLayer **ppoBelongingLayer,
3906 : double *pdfProgressPct,
3907 : GDALProgressFunc pfnProgress,
3908 : void *pProgressData)
3909 : {
3910 126 : m_bInterleavedReading = TRUE;
3911 :
3912 126 : if (m_poCurrentLayer == nullptr)
3913 : {
3914 18 : m_poCurrentLayer = m_apoLayers[0].get();
3915 : }
3916 126 : 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 179 : OGROSMLayer *poNewCurLayer = nullptr;
3935 179 : CPLAssert(m_poCurrentLayer != nullptr);
3936 179 : OGRFeature *poFeature = m_poCurrentLayer->MyGetNextFeature(
3937 : &poNewCurLayer, pfnProgress, pProgressData);
3938 179 : m_poCurrentLayer = poNewCurLayer;
3939 179 : if (poFeature == nullptr)
3940 : {
3941 69 : if (m_poCurrentLayer != nullptr)
3942 53 : continue;
3943 16 : if (ppoBelongingLayer != nullptr)
3944 16 : *ppoBelongingLayer = nullptr;
3945 16 : if (pdfProgressPct != nullptr)
3946 2 : *pdfProgressPct = 1.0;
3947 126 : return nullptr;
3948 : }
3949 110 : if (ppoBelongingLayer != nullptr)
3950 110 : *ppoBelongingLayer = m_poCurrentLayer;
3951 110 : 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 110 : return poFeature;
3965 53 : }
3966 : }
3967 :
3968 : /************************************************************************/
3969 : /* ParseNextChunk() */
3970 : /************************************************************************/
3971 :
3972 156 : bool OGROSMDataSource::ParseNextChunk(int nIdxLayer,
3973 : GDALProgressFunc pfnProgress,
3974 : void *pProgressData)
3975 : {
3976 156 : if (m_bStopParsing)
3977 101 : return false;
3978 :
3979 55 : m_bHasParsedFirstChunk = true;
3980 55 : 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 97 : OSMRetCode eRet = OSM_ProcessBlock(m_psParser);
3992 97 : 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 96 : if (eRet == OSM_EOF || eRet == OSM_ERROR)
4011 : {
4012 52 : if (eRet == OSM_EOF)
4013 : {
4014 49 : if (!m_asWayFeaturePairs.empty())
4015 2 : ProcessWaysBatch();
4016 :
4017 49 : ProcessPolygonsStandalone();
4018 :
4019 49 : if (!m_bHasRowInPolygonsStandalone)
4020 49 : m_bStopParsing = true;
4021 :
4022 49 : if (!m_bInterleavedReading && !m_bFeatureAdded &&
4023 6 : m_bHasRowInPolygonsStandalone &&
4024 : nIdxLayer != IDX_LYR_MULTIPOLYGONS)
4025 : {
4026 0 : return false;
4027 : }
4028 :
4029 49 : 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 44 : if (m_bInMemoryTmpDB)
4045 : {
4046 44 : if (!TransferToDiskIfNecesserary())
4047 0 : return false;
4048 : }
4049 :
4050 44 : if (m_bFeatureAdded)
4051 2 : break;
4052 : }
4053 42 : }
4054 :
4055 2 : return true;
4056 : }
4057 :
4058 : /************************************************************************/
4059 : /* TransferToDiskIfNecesserary() */
4060 : /************************************************************************/
4061 :
4062 44 : bool OGROSMDataSource::TransferToDiskIfNecesserary()
4063 : {
4064 44 : if (m_bInMemoryNodesFile)
4065 : {
4066 42 : if (m_nNodesFileSize / 1024 / 1024 >
4067 42 : 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 : CPLGenerateTempFilenameSafe("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 44 : if (m_bInMemoryTmpDB)
4141 : {
4142 : VSIStatBufL sStat;
4143 :
4144 44 : int nLimitMB = m_nMaxSizeForInMemoryDBInMB;
4145 44 : if (m_bCustomIndexing && m_bInMemoryNodesFile)
4146 42 : nLimitMB = nLimitMB * 1 / 4;
4147 :
4148 88 : if (VSIStatL(m_osTmpDBName, &sStat) == 0 &&
4149 44 : sStat.st_size / 1024 / 1024 > nLimitMB)
4150 : {
4151 0 : m_bInMemoryTmpDB = false;
4152 :
4153 0 : CloseDB();
4154 :
4155 : const std::string osNewTmpDBName(
4156 0 : CPLGenerateTempFilenameSafe("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 44 : return true;
4209 : }
4210 :
4211 : /************************************************************************/
4212 : /* TestCapability() */
4213 : /************************************************************************/
4214 :
4215 11 : int OGROSMDataSource::TestCapability(const char *pszCap)
4216 : {
4217 11 : return EQUAL(pszCap, ODsCRandomLayerRead);
4218 : }
4219 :
4220 : /************************************************************************/
4221 : /* GetLayer() */
4222 : /************************************************************************/
4223 :
4224 441 : OGRLayer *OGROSMDataSource::GetLayer(int iLayer)
4225 :
4226 : {
4227 441 : if (iLayer < 0 || static_cast<size_t>(iLayer) >= m_apoLayers.size())
4228 0 : return nullptr;
4229 :
4230 441 : 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 195 : int OGROSMDataSource::IsInterleavedReading()
4615 : {
4616 195 : 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 195 : return m_bInterleavedReading;
4623 : }
|