Line data Source code
1 : /******************************************************************************
2 : * $Id$
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Private definitions for OGR/OpenStreeMap driver.
6 : * Author: Even Rouault, <even dot rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2012-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #ifndef OGR_OSM_H_INCLUDED
31 : #define OGR_OSM_H_INCLUDED
32 :
33 : // replace O(log2(N)) complexity of FindNode() by O(1)
34 : #define ENABLE_NODE_LOOKUP_BY_HASHING 1
35 :
36 : #include "ogrsf_frmts.h"
37 : #include "cpl_string.h"
38 :
39 : #include <array>
40 : #include <set>
41 : #include <unordered_set>
42 : #include <map>
43 : #include <vector>
44 :
45 : #include "osm_parser.h"
46 :
47 : #include "ogrsqlitevfs.h"
48 :
49 : class ConstCharComp
50 : {
51 : public:
52 25544 : bool operator()(const char *a, const char *b) const
53 : {
54 25544 : return strcmp(a, b) < 0;
55 : }
56 : };
57 :
58 : class OGROSMComputedAttribute
59 : {
60 : public:
61 : CPLString osName;
62 : int nIndex;
63 : OGRFieldType eType;
64 : CPLString osSQL;
65 : sqlite3_stmt *hStmt;
66 : std::vector<CPLString> aosAttrToBind;
67 : std::vector<int> anIndexToBind;
68 : bool bHardcodedZOrder;
69 :
70 0 : OGROSMComputedAttribute()
71 0 : : nIndex(-1), eType(OFTString), hStmt(nullptr), bHardcodedZOrder(false)
72 : {
73 0 : }
74 :
75 60 : explicit OGROSMComputedAttribute(const char *pszName)
76 60 : : osName(pszName), nIndex(-1), eType(OFTString), hStmt(nullptr),
77 60 : bHardcodedZOrder(false)
78 : {
79 60 : }
80 : };
81 :
82 : /************************************************************************/
83 : /* OGROSMLayer */
84 : /************************************************************************/
85 :
86 : class OGROSMDataSource;
87 :
88 : class OGROSMLayer final : public OGRLayer
89 : {
90 : friend class OGROSMDataSource;
91 :
92 : OGROSMDataSource *m_poDS = nullptr;
93 : int m_nIdxLayer = 0;
94 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
95 : OGRSpatialReference *m_poSRS = nullptr;
96 : long m_nFeatureCount = 0;
97 :
98 : std::vector<char *>
99 : m_apszNames{}; /* Needed to keep a "reference" to the string inserted
100 : into oMapFieldNameToIndex */
101 : std::map<const char *, int, ConstCharComp> m_oMapFieldNameToIndex{};
102 :
103 : std::vector<OGROSMComputedAttribute> m_oComputedAttributes{};
104 :
105 : bool m_bResetReadingAllowed = false;
106 :
107 : int m_nFeatureArraySize = 0;
108 : int m_nFeatureArrayMaxSize = 0;
109 : int m_nFeatureArrayIndex = 0;
110 : OGRFeature **m_papoFeatures = nullptr;
111 :
112 : bool m_bHasOSMId = false;
113 : int m_nIndexOSMId = -1;
114 : int m_nIndexOSMWayId = -1;
115 : bool m_bHasVersion = false;
116 : bool m_bHasTimestamp = false;
117 : bool m_bHasUID = false;
118 : bool m_bHasUser = false;
119 : bool m_bHasChangeset = false;
120 : bool m_bHasOtherTags = true;
121 : int m_nIndexOtherTags = -1;
122 : bool m_bHasAllTags = false;
123 : int m_nIndexAllTags = -1;
124 :
125 : bool m_bHasWarnedTooManyFeatures = false;
126 :
127 : std::string m_osAllTagsBuffer{};
128 :
129 : bool m_bUserInterested = true;
130 :
131 : bool AddToArray(OGRFeature *poFeature, int bCheckFeatureThreshold);
132 :
133 : int AddInOtherOrAllTags(const char *pszK);
134 :
135 : char szLaunderedFieldName[256];
136 : const char *GetLaunderedFieldName(const char *pszName);
137 :
138 : std::vector<char *> apszInsignificantKeys;
139 : std::map<const char *, int, ConstCharComp> aoSetInsignificantKeys;
140 :
141 : std::vector<char *> apszIgnoreKeys;
142 : std::map<const char *, int, ConstCharComp> aoSetIgnoreKeys;
143 :
144 : std::set<std::string> aoSetWarnKeys;
145 :
146 : public:
147 : OGROSMLayer(OGROSMDataSource *m_poDS, int m_nIdxLayer, const char *pszName);
148 : virtual ~OGROSMLayer();
149 :
150 3266 : virtual OGRFeatureDefn *GetLayerDefn() override
151 : {
152 3266 : return m_poFeatureDefn;
153 : }
154 :
155 : virtual void ResetReading() override;
156 : virtual int TestCapability(const char *) override;
157 :
158 : virtual OGRFeature *GetNextFeature() override;
159 :
160 : OGRFeature *MyGetNextFeature(OGROSMLayer **ppoNewCurLayer,
161 : GDALProgressFunc pfnProgress,
162 : void *pProgressData);
163 :
164 : virtual GIntBig GetFeatureCount(int bForce) override;
165 :
166 : virtual OGRErr SetAttributeFilter(const char *pszAttrQuery) override;
167 :
168 : virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce) override;
169 :
170 3 : virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent,
171 : int bForce) override
172 : {
173 3 : return OGRLayer::GetExtent(iGeomField, psExtent, bForce);
174 : }
175 :
176 : const OGREnvelope *GetSpatialFilterEnvelope();
177 :
178 : int AddFeature(OGRFeature *poFeature, int bAttrFilterAlreadyEvaluated,
179 : int *pbFilteredOut = nullptr,
180 : int bCheckFeatureThreshold = TRUE);
181 : void ForceResetReading();
182 :
183 : void AddField(const char *pszName, OGRFieldType eFieldType,
184 : OGRFieldSubType eSubType = OFSTNone);
185 : int GetFieldIndex(const char *pszName);
186 :
187 : bool HasOSMId() const
188 : {
189 : return m_bHasOSMId;
190 : }
191 :
192 150 : void SetHasOSMId(bool bIn)
193 : {
194 150 : m_bHasOSMId = bIn;
195 150 : }
196 :
197 31 : bool HasVersion() const
198 : {
199 31 : return m_bHasVersion;
200 : }
201 :
202 150 : void SetHasVersion(bool bIn)
203 : {
204 150 : m_bHasVersion = bIn;
205 150 : }
206 :
207 31 : bool HasTimestamp() const
208 : {
209 31 : return m_bHasTimestamp;
210 : }
211 :
212 150 : void SetHasTimestamp(bool bIn)
213 : {
214 150 : m_bHasTimestamp = bIn;
215 150 : }
216 :
217 31 : bool HasUID() const
218 : {
219 31 : return m_bHasUID;
220 : }
221 :
222 150 : void SetHasUID(bool bIn)
223 : {
224 150 : m_bHasUID = bIn;
225 150 : }
226 :
227 31 : bool HasUser() const
228 : {
229 31 : return m_bHasUser;
230 : }
231 :
232 150 : void SetHasUser(bool bIn)
233 : {
234 150 : m_bHasUser = bIn;
235 150 : }
236 :
237 31 : bool HasChangeset() const
238 : {
239 31 : return m_bHasChangeset;
240 : }
241 :
242 150 : void SetHasChangeset(bool bIn)
243 : {
244 150 : m_bHasChangeset = bIn;
245 150 : }
246 :
247 155 : bool HasOtherTags() const
248 : {
249 155 : return m_bHasOtherTags;
250 : }
251 :
252 2 : void SetHasOtherTags(bool bIn)
253 : {
254 2 : m_bHasOtherTags = bIn;
255 2 : }
256 :
257 155 : bool HasAllTags() const
258 : {
259 155 : return m_bHasAllTags;
260 : }
261 :
262 2 : void SetHasAllTags(bool bIn)
263 : {
264 2 : m_bHasAllTags = bIn;
265 2 : }
266 :
267 : void SetFieldsFromTags(OGRFeature *poFeature, GIntBig nID, bool bIsWayID,
268 : unsigned int nTags, OSMTag *pasTags,
269 : OSMInfo *psInfo);
270 :
271 198 : void SetDeclareInterest(bool bIn)
272 : {
273 198 : m_bUserInterested = bIn;
274 198 : }
275 :
276 4917 : bool IsUserInterested() const
277 : {
278 4917 : return m_bUserInterested;
279 : }
280 :
281 453 : int HasAttributeFilter() const
282 : {
283 453 : return m_poAttrQuery != nullptr;
284 : }
285 :
286 : int EvaluateAttributeFilter(OGRFeature *poFeature);
287 :
288 : void AddInsignificantKey(const char *pszK);
289 :
290 475 : int IsSignificantKey(const char *pszK) const
291 : {
292 475 : return aoSetInsignificantKeys.find(pszK) ==
293 950 : aoSetInsignificantKeys.end();
294 : }
295 :
296 : void AddIgnoreKey(const char *pszK);
297 : void AddWarnKey(const char *pszK);
298 :
299 : void AddComputedAttribute(const char *pszName, OGRFieldType eType,
300 : const char *pszSQL);
301 : };
302 :
303 : /************************************************************************/
304 : /* OGROSMDataSource */
305 : /************************************************************************/
306 :
307 : typedef struct
308 : {
309 : char *pszK;
310 : int nKeyIndex;
311 : int nOccurrences;
312 : std::vector<char *> asValues;
313 : std::map<const char *, int, ConstCharComp>
314 : anMapV; /* map that is the reverse of asValues */
315 : } KeyDesc;
316 :
317 : typedef struct
318 : {
319 : short bKIsIndex; /* whether we should use nKeyIndex or
320 : nOffsetInpabyNonRedundantKeys */
321 : short bVIsIndex; /* whether we should use nValueIndex or
322 : nOffsetInpabyNonRedundantValues */
323 :
324 : union
325 : {
326 : int nKeyIndex; /* index of OGROSMDataSource.asKeys */
327 : int nOffsetInpabyNonRedundantKeys; /* offset in
328 : OGROSMDataSource.pabyNonRedundantKeys
329 : */
330 : } uKey;
331 :
332 : union
333 : {
334 : int nValueIndex; /* index of KeyDesc.asValues */
335 : int nOffsetInpabyNonRedundantValues; /* offset in
336 : OGROSMDataSource.pabyNonRedundantValues
337 : */
338 : } uVal;
339 : } IndexedKVP;
340 :
341 : typedef struct
342 : {
343 : GIntBig nOff;
344 :
345 : /* Note: only one of nth bucket pabyBitmap or panSectorSize must be free'd
346 : */
347 : union
348 : {
349 : GByte *pabyBitmap; /* array of BUCKET_BITMAP_SIZE bytes */
350 : GByte *panSectorSize; /* array of BUCKET_SECTOR_SIZE_ARRAY_SIZE bytes.
351 : Each values means (size in bytes - 8 ) / 2,
352 : minus 8. 252 means uncompressed */
353 : } u;
354 : } Bucket;
355 :
356 : typedef struct
357 : {
358 : int nLon;
359 : int nLat;
360 : } LonLat;
361 :
362 : typedef struct
363 : {
364 : GIntBig nWayID;
365 : GIntBig
366 : *panNodeRefs; /* point to a sub-array of OGROSMDataSource.anReqIds */
367 : unsigned int nRefs;
368 : unsigned int nTags;
369 : IndexedKVP *pasTags; /* point to a sub-array of
370 : OGROSMDataSource.pasAccumulatedTags */
371 : OSMInfo sInfo;
372 : OGRFeature *poFeature;
373 : bool bIsArea : 1;
374 : bool bAttrFilterAlreadyEvaluated : 1;
375 : } WayFeaturePair;
376 :
377 : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
378 : typedef struct
379 : {
380 : int nInd; /* values are indexes of panReqIds */
381 : int nNext; /* values are indexes of psCollisionBuckets, or -1 to stop the
382 : chain */
383 : } CollisionBucket;
384 : #endif
385 :
386 : class OGROSMDataSource final : public OGRDataSource
387 : {
388 : friend class OGROSMLayer;
389 :
390 : int m_nLayers = 0;
391 : OGROSMLayer **m_papoLayers = nullptr;
392 : char *m_pszName = nullptr;
393 :
394 : OGREnvelope m_sExtent{};
395 : bool m_bExtentValid = false;
396 :
397 : // Starts off at -1 to indicate that we do not know.
398 : int m_bInterleavedReading = -1;
399 : OGROSMLayer *m_poCurrentLayer = nullptr;
400 :
401 : OSMContext *m_psParser = nullptr;
402 : bool m_bHasParsedFirstChunk = false;
403 : bool m_bStopParsing = false;
404 :
405 : sqlite3_vfs *m_pMyVFS = nullptr;
406 :
407 : sqlite3 *m_hDB = nullptr;
408 : sqlite3_stmt *m_hInsertNodeStmt = nullptr;
409 : sqlite3_stmt *m_hInsertWayStmt = nullptr;
410 : sqlite3_stmt **m_pahSelectNodeStmt = nullptr;
411 : sqlite3_stmt **m_pahSelectWayStmt = nullptr;
412 : sqlite3_stmt *m_hInsertPolygonsStandaloneStmt = nullptr;
413 : sqlite3_stmt *m_hDeletePolygonsStandaloneStmt = nullptr;
414 : sqlite3_stmt *m_hSelectPolygonsStandaloneStmt = nullptr;
415 : bool m_bHasRowInPolygonsStandalone = false;
416 :
417 : sqlite3 *m_hDBForComputedAttributes = nullptr;
418 :
419 : int m_nMaxSizeForInMemoryDBInMB = 0;
420 : bool m_bInMemoryTmpDB = false;
421 : bool m_bMustUnlink = true;
422 : CPLString m_osTmpDBName{};
423 :
424 : std::unordered_set<std::string> aoSetClosedWaysArePolygons{};
425 : int m_nMinSizeKeysInSetClosedWaysArePolygons = 0;
426 : int m_nMaxSizeKeysInSetClosedWaysArePolygons = 0;
427 :
428 : std::vector<LonLat> m_asLonLatCache{};
429 :
430 : std::array<const char *, 7> m_ignoredKeys = {{"area", "created_by",
431 : "converted_by", "note",
432 : "todo", "fixme", "FIXME"}};
433 :
434 : bool m_bReportAllNodes = false;
435 : bool m_bReportAllWays = false;
436 : bool m_bTagsAsHSTORE = true; // if false, as JSON
437 :
438 : bool m_bFeatureAdded = false;
439 :
440 : bool m_bInTransaction = false;
441 :
442 : bool m_bIndexPoints = true;
443 : bool m_bUsePointsIndex = true;
444 : bool m_bIndexWays = true;
445 : bool m_bUseWaysIndex = true;
446 :
447 : std::vector<bool> m_abSavedDeclaredInterest{};
448 : OGRLayer *m_poResultSetLayer = nullptr;
449 : bool m_bIndexPointsBackup = false;
450 : bool m_bUsePointsIndexBackup = false;
451 : bool m_bIndexWaysBackup = false;
452 : bool m_bUseWaysIndexBackup = false;
453 :
454 : bool m_bIsFeatureCountEnabled = false;
455 :
456 : bool m_bAttributeNameLaundering = true;
457 :
458 : std::vector<GByte> m_abyWayBuffer{};
459 :
460 : int m_nWaysProcessed = 0;
461 : int m_nRelationsProcessed = 0;
462 :
463 : bool m_bCustomIndexing = true;
464 : bool m_bCompressNodes = false;
465 :
466 : unsigned int m_nUnsortedReqIds = 0;
467 : GIntBig *m_panUnsortedReqIds = nullptr;
468 :
469 : unsigned int m_nReqIds = 0;
470 : GIntBig *m_panReqIds = nullptr;
471 :
472 : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
473 : bool m_bEnableHashedIndex = true;
474 : /* values >= 0 are indexes of panReqIds. */
475 : /* == -1 for unoccupied */
476 : /* < -1 are expressed as -nIndexToCollisionBuckets-2 where
477 : * nIndexToCollisionBuckets point to psCollisionBuckets */
478 : int *m_panHashedIndexes = nullptr;
479 : CollisionBucket *m_psCollisionBuckets = nullptr;
480 : bool m_bHashedIndexValid = false;
481 : #endif
482 :
483 : LonLat *m_pasLonLatArray = nullptr;
484 :
485 : IndexedKVP *m_pasAccumulatedTags =
486 : nullptr; /* points to content of pabyNonRedundantValues or
487 : aoMapIndexedKeys */
488 : int m_nAccumulatedTags = 0;
489 : unsigned int MAX_INDEXED_KEYS = 0;
490 : GByte *pabyNonRedundantKeys = nullptr;
491 : int nNonRedundantKeysLen = 0;
492 : unsigned int MAX_INDEXED_VALUES_PER_KEY = 0;
493 : GByte *pabyNonRedundantValues = nullptr;
494 : int nNonRedundantValuesLen = 0;
495 : WayFeaturePair *m_pasWayFeaturePairs = nullptr;
496 : int m_nWayFeaturePairs = 0;
497 :
498 : std::vector<KeyDesc *> m_asKeys{};
499 : std::map<const char *, KeyDesc *, ConstCharComp>
500 : m_aoMapIndexedKeys{}; /* map that is the reverse of asKeys */
501 :
502 : CPLString m_osNodesFilename{};
503 : bool m_bInMemoryNodesFile = false;
504 : bool m_bMustUnlinkNodesFile = true;
505 : GIntBig m_nNodesFileSize = 0;
506 : VSILFILE *m_fpNodes = nullptr;
507 :
508 : GIntBig m_nPrevNodeId = -INT_MAX;
509 : int m_nBucketOld = -1;
510 : int m_nOffInBucketReducedOld = -1;
511 : GByte *m_pabySector = nullptr;
512 : std::map<int, Bucket> m_oMapBuckets{};
513 : Bucket *GetBucket(int nBucketId);
514 :
515 : bool m_bNeedsToSaveWayInfo = false;
516 :
517 : static const GIntBig FILESIZE_NOT_INIT = -2;
518 : static const GIntBig FILESIZE_INVALID = -1;
519 : GIntBig m_nFileSize = FILESIZE_NOT_INIT;
520 :
521 : void CompressWay(bool bIsArea, unsigned int nTags, IndexedKVP *pasTags,
522 : int nPoints, LonLat *pasLonLatPairs, OSMInfo *psInfo,
523 : std::vector<GByte> &abyCompressedWay);
524 : void UncompressWay(int nBytes, const GByte *pabyCompressedWay,
525 : bool *pbIsArea, std::vector<LonLat> &asCoords,
526 : unsigned int *pnTags, OSMTag *pasTags, OSMInfo *psInfo);
527 :
528 : bool ParseConf(char **papszOpenOptions);
529 : bool CreateTempDB();
530 : bool SetDBOptions();
531 : void SetCacheSize();
532 : bool CreatePreparedStatements();
533 : void CloseDB();
534 :
535 : bool IndexPoint(OSMNode *psNode);
536 : bool IndexPointSQLite(OSMNode *psNode);
537 : bool FlushCurrentSector();
538 : bool FlushCurrentSectorCompressedCase();
539 : bool FlushCurrentSectorNonCompressedCase();
540 : bool IndexPointCustom(OSMNode *psNode);
541 :
542 : void IndexWay(GIntBig nWayID, bool bIsArea, unsigned int nTags,
543 : IndexedKVP *pasTags, LonLat *pasLonLatPairs, int nPairs,
544 : OSMInfo *psInfo);
545 :
546 : bool StartTransactionCacheDB();
547 : bool CommitTransactionCacheDB();
548 :
549 : int FindNode(GIntBig nID);
550 : void ProcessWaysBatch();
551 :
552 : void ProcessPolygonsStandalone();
553 :
554 : void LookupNodes();
555 : void LookupNodesSQLite();
556 : void LookupNodesCustom();
557 : void LookupNodesCustomCompressedCase();
558 : void LookupNodesCustomNonCompressedCase();
559 :
560 : unsigned int
561 : LookupWays(std::map<GIntBig, std::pair<int, void *>> &aoMapWays,
562 : OSMRelation *psRelation);
563 :
564 : OGRGeometry *BuildMultiPolygon(OSMRelation *psRelation,
565 : unsigned int *pnTags, OSMTag *pasTags);
566 : OGRGeometry *BuildGeometryCollection(OSMRelation *psRelation,
567 : int bMultiLineString);
568 :
569 : bool TransferToDiskIfNecesserary();
570 :
571 : Bucket *AllocBucket(int iBucket);
572 :
573 : void AddComputedAttributes(
574 : int iCurLayer, const std::vector<OGROSMComputedAttribute> &oAttributes);
575 : bool IsClosedWayTaggedAsPolygon(unsigned int nTags, const OSMTag *pasTags);
576 :
577 : public:
578 : OGROSMDataSource();
579 : virtual ~OGROSMDataSource();
580 :
581 32 : virtual const char *GetName() override
582 : {
583 32 : return m_pszName;
584 : }
585 :
586 700 : virtual int GetLayerCount() override
587 : {
588 700 : return m_nLayers;
589 : }
590 :
591 : virtual OGRLayer *GetLayer(int) override;
592 :
593 : virtual int TestCapability(const char *) override;
594 :
595 : virtual OGRLayer *ExecuteSQL(const char *pszSQLCommand,
596 : OGRGeometry *poSpatialFilter,
597 : const char *pszDialect) override;
598 : virtual void ReleaseResultSet(OGRLayer *poLayer) override;
599 :
600 : virtual void ResetReading() override;
601 : virtual OGRFeature *GetNextFeature(OGRLayer **ppoBelongingLayer,
602 : double *pdfProgressPct,
603 : GDALProgressFunc pfnProgress,
604 : void *pProgressData) override;
605 :
606 : int Open(const char *pszFilename, char **papszOpenOptions);
607 :
608 : int MyResetReading();
609 : bool ParseNextChunk(int nIdxLayer, GDALProgressFunc pfnProgress,
610 : void *pProgressData);
611 : OGRErr GetExtent(OGREnvelope *psExtent);
612 : int IsInterleavedReading();
613 :
614 : void NotifyNodes(unsigned int nNodes, OSMNode *pasNodes);
615 : void NotifyWay(OSMWay *psWay);
616 : void NotifyRelation(OSMRelation *psRelation);
617 : void NotifyBounds(double dfXMin, double dfYMin, double dfXMax,
618 : double dfYMax);
619 :
620 312 : OGROSMLayer *GetCurrentLayer()
621 : {
622 312 : return m_poCurrentLayer;
623 : }
624 :
625 237 : void SetCurrentLayer(OGROSMLayer *poLyr)
626 : {
627 237 : m_poCurrentLayer = poLyr;
628 237 : }
629 :
630 0 : bool IsFeatureCountEnabled() const
631 : {
632 0 : return m_bIsFeatureCountEnabled;
633 : }
634 :
635 1566 : bool DoesAttributeNameLaundering() const
636 : {
637 1566 : return m_bAttributeNameLaundering;
638 : }
639 : };
640 :
641 : #endif /* ndef OGR_OSM_H_INCLUDED */
|