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