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