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