1 : /******************************************************************************
2 : * $Id: ogrosmlayer.cpp 24950 2012-09-22 13:54:36Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGROSMLayer class
6 : * Author: Even Rouault, <even dot rouault at mines dash paris dot org>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2012, Even Rouault
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 : #include "ogr_osm.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include "cpl_time.h"
34 : #include "ogr_p.h"
35 :
36 : CPL_CVSID("$Id: ogrosmlayer.cpp 24950 2012-09-22 13:54:36Z rouault $");
37 :
38 : #define SWITCH_THRESHOLD 10000
39 : #define MAX_THRESHOLD 100000
40 :
41 : #define ALLTAGS_LENGTH 8192
42 :
43 : /************************************************************************/
44 : /* OGROSMLayer() */
45 : /************************************************************************/
46 :
47 :
48 50 : OGROSMLayer::OGROSMLayer(OGROSMDataSource* poDS, const char* pszName )
49 : {
50 50 : this->poDS = poDS;
51 :
52 50 : poFeatureDefn = new OGRFeatureDefn( pszName );
53 50 : poFeatureDefn->Reference();
54 :
55 50 : poSRS = new OGRSpatialReference();
56 50 : poSRS->SetWellKnownGeogCS("WGS84");
57 :
58 50 : nFeatureArraySize = 0;
59 50 : nFeatureArrayMaxSize = 0;
60 50 : nFeatureArrayIndex = 0;
61 50 : papoFeatures = NULL;
62 :
63 50 : nFeatureCount = 0;
64 :
65 50 : bHasOSMId = FALSE;
66 50 : nIndexOSMId = -1;
67 50 : nIndexOSMWayId = -1;
68 50 : bHasVersion = FALSE;
69 50 : bHasTimestamp = FALSE;
70 50 : bHasUID = FALSE;
71 50 : bHasUser = FALSE;
72 50 : bHasChangeset = FALSE;
73 50 : bHasOtherTags = TRUE;
74 :
75 50 : bResetReadingAllowed = FALSE;
76 50 : bHasWarnedTooManyFeatures = FALSE;
77 :
78 50 : pszAllTags = (char*)CPLMalloc(ALLTAGS_LENGTH);
79 50 : bHasWarnedAllTagsTruncated = FALSE;
80 :
81 50 : bUserInterested = TRUE;
82 50 : }
83 :
84 : /************************************************************************/
85 : /* ~OGROSMLayer() */
86 : /************************************************************************/
87 :
88 50 : OGROSMLayer::~OGROSMLayer()
89 : {
90 : int i;
91 :
92 50 : poFeatureDefn->Release();
93 :
94 50 : if (poSRS)
95 50 : poSRS->Release();
96 :
97 50 : for(i=0;i<nFeatureArraySize;i++)
98 : {
99 0 : if (papoFeatures[i])
100 0 : delete papoFeatures[i];
101 : }
102 :
103 550 : for(i=0;i<(int)apszNames.size();i++)
104 500 : CPLFree(apszNames[i]);
105 :
106 100 : for(i=0;i<(int)apszUnsignificantKeys.size();i++)
107 50 : CPLFree(apszUnsignificantKeys[i]);
108 :
109 530 : for(i=0;i<(int)apszIgnoreKeys.size();i++)
110 480 : CPLFree(apszIgnoreKeys[i]);
111 :
112 50 : CPLFree(pszAllTags);
113 :
114 50 : CPLFree(papoFeatures);
115 50 : }
116 :
117 : /************************************************************************/
118 : /* ResetReading() */
119 : /************************************************************************/
120 :
121 34 : void OGROSMLayer::ResetReading()
122 : {
123 34 : if ( !bResetReadingAllowed || poDS->IsInterleavedReading() )
124 25 : return;
125 :
126 9 : poDS->ResetReading();
127 : }
128 :
129 : /************************************************************************/
130 : /* ForceResetReading() */
131 : /************************************************************************/
132 :
133 115 : void OGROSMLayer::ForceResetReading()
134 : {
135 119 : for(int i=0;i<nFeatureArraySize;i++)
136 : {
137 4 : if (papoFeatures[i])
138 2 : delete papoFeatures[i];
139 : }
140 115 : nFeatureArrayIndex = 0;
141 115 : nFeatureArraySize = 0;
142 115 : nFeatureCount = 0;
143 115 : bResetReadingAllowed = FALSE;
144 115 : }
145 :
146 : /************************************************************************/
147 : /* GetFeatureCount() */
148 : /************************************************************************/
149 :
150 0 : int OGROSMLayer::GetFeatureCount( int bForce )
151 : {
152 0 : if( poDS->IsFeatureCountEnabled() )
153 0 : return OGRLayer::GetFeatureCount(bForce);
154 :
155 0 : return -1;
156 : }
157 :
158 : /************************************************************************/
159 : /* GetNextFeature() */
160 : /************************************************************************/
161 :
162 96 : OGRFeature *OGROSMLayer::GetNextFeature()
163 : {
164 96 : bResetReadingAllowed = TRUE;
165 :
166 96 : if ( nFeatureArraySize == 0)
167 : {
168 62 : if ( poDS->IsInterleavedReading() )
169 : {
170 : int i;
171 :
172 33 : OGRLayer* poCurrentLayer = poDS->GetCurrentLayer();
173 33 : if ( poCurrentLayer == NULL )
174 : {
175 21 : poDS->SetCurrentLayer(this);
176 : }
177 12 : else if( poCurrentLayer != this )
178 : {
179 0 : return NULL;
180 : }
181 :
182 : /* If too many features have been accumulated in */
183 : /* another layer, we force */
184 : /* a switch to that layer, so that it gets emptied */
185 198 : for(i=0;i<poDS->GetLayerCount();i++)
186 : {
187 297 : if (poDS->papoLayers[i] != this &&
188 132 : poDS->papoLayers[i]->nFeatureArraySize > SWITCH_THRESHOLD)
189 : {
190 0 : poDS->SetCurrentLayer(poDS->papoLayers[i]);
191 : CPLDebug("OSM", "Switching to '%s' as they are too many "
192 : "features in '%s'",
193 0 : poDS->papoLayers[i]->GetName(),
194 0 : GetName());
195 0 : return NULL;
196 : }
197 : }
198 :
199 : /* Read some more data and accumulate features */
200 33 : poDS->ParseNextChunk();
201 :
202 33 : if ( nFeatureArraySize == 0 )
203 : {
204 : /* If there are really no more features to read in the */
205 : /* current layer, force a switch to another non-empty layer */
206 :
207 153 : for(i=0;i<poDS->GetLayerCount();i++)
208 : {
209 234 : if (poDS->papoLayers[i] != this &&
210 102 : poDS->papoLayers[i]->nFeatureArraySize > 0)
211 : {
212 9 : poDS->SetCurrentLayer(poDS->papoLayers[i]);
213 : CPLDebug("OSM",
214 : "Switching to '%s' as they are no more feature in '%s'",
215 27 : poDS->papoLayers[i]->GetName(),
216 36 : GetName());
217 9 : return NULL;
218 : }
219 : }
220 :
221 : /* Game over : no more data to read from the stream */
222 21 : poDS->SetCurrentLayer(NULL);
223 21 : return NULL;
224 : }
225 : }
226 : else
227 : {
228 2 : while(TRUE)
229 : {
230 31 : int bRet = poDS->ParseNextChunk();
231 31 : if (nFeatureArraySize != 0)
232 13 : break;
233 18 : if (bRet == FALSE)
234 16 : return NULL;
235 : }
236 : }
237 : }
238 :
239 50 : OGRFeature* poFeature = papoFeatures[nFeatureArrayIndex];
240 :
241 50 : papoFeatures[nFeatureArrayIndex] = NULL;
242 50 : nFeatureArrayIndex++;
243 :
244 50 : if ( nFeatureArrayIndex == nFeatureArraySize)
245 32 : nFeatureArrayIndex = nFeatureArraySize = 0;
246 :
247 50 : return poFeature;
248 : }
249 :
250 : /************************************************************************/
251 : /* TestCapability() */
252 : /************************************************************************/
253 :
254 2 : int OGROSMLayer::TestCapability( const char * pszCap )
255 : {
256 2 : if( EQUAL(pszCap, OLCFastGetExtent) )
257 : {
258 0 : OGREnvelope sExtent;
259 0 : if (poDS->GetExtent(&sExtent) == OGRERR_NONE)
260 0 : return TRUE;
261 : }
262 :
263 2 : return FALSE;
264 : }
265 :
266 : /************************************************************************/
267 : /* AddToArray() */
268 : /************************************************************************/
269 :
270 52 : int OGROSMLayer::AddToArray(OGRFeature* poFeature)
271 : {
272 52 : if( nFeatureArraySize > MAX_THRESHOLD)
273 : {
274 0 : if( !bHasWarnedTooManyFeatures )
275 : {
276 : CPLError(CE_Failure, CPLE_AppDefined,
277 : "Too many features have accumulated in %s layer. "
278 : "Use OGR_INTERLEAVED_READING=YES mode",
279 0 : GetName());
280 : }
281 0 : bHasWarnedTooManyFeatures = TRUE;
282 0 : return FALSE;
283 : }
284 :
285 52 : if (nFeatureArraySize == nFeatureArrayMaxSize)
286 : {
287 31 : nFeatureArrayMaxSize = nFeatureArrayMaxSize + nFeatureArrayMaxSize / 2 + 128;
288 31 : CPLDebug("OSM", "For layer %s, new max size is %d", GetName(), nFeatureArrayMaxSize);
289 : OGRFeature** papoNewFeatures = (OGRFeature**)VSIRealloc(papoFeatures,
290 31 : nFeatureArrayMaxSize * sizeof(OGRFeature*));
291 31 : if (papoNewFeatures == NULL)
292 : {
293 0 : delete poFeature;
294 0 : return FALSE;
295 : }
296 31 : papoFeatures = papoNewFeatures;
297 : }
298 52 : papoFeatures[nFeatureArraySize ++] = poFeature;
299 :
300 52 : return TRUE;
301 : }
302 :
303 : /************************************************************************/
304 : /* EvaluateAttributeFilter() */
305 : /************************************************************************/
306 :
307 11 : int OGROSMLayer::EvaluateAttributeFilter(OGRFeature* poFeature)
308 : {
309 : return (m_poAttrQuery == NULL
310 11 : || m_poAttrQuery->Evaluate( poFeature ));
311 : }
312 :
313 : /************************************************************************/
314 : /* AddFeature() */
315 : /************************************************************************/
316 :
317 55 : int OGROSMLayer::AddFeature(OGRFeature* poFeature,
318 : int bAttrFilterAlreadyEvaluated,
319 : int* pbFilteredOut)
320 : {
321 55 : if( !bUserInterested )
322 : {
323 0 : if (pbFilteredOut)
324 0 : *pbFilteredOut = TRUE;
325 0 : delete poFeature;
326 0 : return TRUE;
327 : }
328 :
329 55 : OGRGeometry* poGeom = poFeature->GetGeometryRef();
330 55 : if (poGeom)
331 55 : poGeom->assignSpatialReference( poSRS );
332 :
333 55 : if( (m_poFilterGeom == NULL
334 : || FilterGeometry( poFeature->GetGeometryRef() ) )
335 : && (m_poAttrQuery == NULL || bAttrFilterAlreadyEvaluated
336 : || m_poAttrQuery->Evaluate( poFeature )) )
337 : {
338 52 : if (!AddToArray(poFeature))
339 : {
340 0 : delete poFeature;
341 0 : return FALSE;
342 : }
343 : }
344 : else
345 : {
346 3 : if (pbFilteredOut)
347 3 : *pbFilteredOut = TRUE;
348 3 : delete poFeature;
349 3 : return TRUE;
350 : }
351 :
352 52 : if (pbFilteredOut)
353 52 : *pbFilteredOut = FALSE;
354 52 : return TRUE;
355 : }
356 :
357 : /************************************************************************/
358 : /* GetExtent() */
359 : /************************************************************************/
360 :
361 1 : OGRErr OGROSMLayer::GetExtent( OGREnvelope *psExtent, int bForce )
362 : {
363 1 : if (poDS->GetExtent(psExtent) == OGRERR_NONE)
364 1 : return OGRERR_NONE;
365 :
366 : /* return OGRLayer::GetExtent(psExtent, bForce);*/
367 0 : return OGRERR_FAILURE;
368 : }
369 :
370 : /************************************************************************/
371 : /* GetLaunderedFieldName() */
372 : /************************************************************************/
373 :
374 500 : const char* OGROSMLayer::GetLaunderedFieldName(const char* pszName)
375 : {
376 500 : if( poDS->DoesAttributeNameLaundering() &&
377 : strchr(pszName, ':') != NULL )
378 : {
379 : size_t i;
380 0 : for( i = 0;
381 0 : pszName[i] != '\0' && i < sizeof(szLaunderedFieldName) - 1; i++ )
382 : {
383 0 : if( pszName[i] == ':' )
384 0 : szLaunderedFieldName[i] = '_';
385 : else
386 0 : szLaunderedFieldName[i] = pszName[i];
387 : }
388 0 : szLaunderedFieldName[i] = '\0';
389 0 : return szLaunderedFieldName;
390 : }
391 : else
392 500 : return pszName;
393 : }
394 :
395 : /************************************************************************/
396 : /* AddField() */
397 : /************************************************************************/
398 :
399 500 : void OGROSMLayer::AddField(const char* pszName, OGRFieldType eFieldType)
400 : {
401 500 : const char* pszLaunderedName = GetLaunderedFieldName(pszName);
402 500 : OGRFieldDefn oField(pszLaunderedName, eFieldType);
403 500 : poFeatureDefn->AddFieldDefn(&oField);
404 :
405 500 : int nIndex = poFeatureDefn->GetFieldCount() - 1;
406 500 : char* pszDupName = CPLStrdup(pszName);
407 500 : apszNames.push_back(pszDupName);
408 500 : oMapFieldNameToIndex[pszDupName] = nIndex;
409 :
410 500 : if( strcmp(pszName, "osm_id") == 0 )
411 50 : nIndexOSMId = nIndex;
412 :
413 450 : else if( strcmp(pszName, "osm_way_id") == 0 )
414 10 : nIndexOSMWayId = nIndex;
415 500 : }
416 :
417 : /************************************************************************/
418 : /* GetFieldIndex() */
419 : /************************************************************************/
420 :
421 86 : int OGROSMLayer::GetFieldIndex(const char* pszName)
422 : {
423 : std::map<const char*, int, ConstCharComp>::iterator oIter =
424 86 : oMapFieldNameToIndex.find(pszName);
425 86 : if( oIter != oMapFieldNameToIndex.end() )
426 86 : return oIter->second;
427 : else
428 0 : return -1;
429 : }
430 :
431 : /************************************************************************/
432 : /* AddInOtherTag() */
433 : /************************************************************************/
434 :
435 0 : int OGROSMLayer::AddInOtherTag(const char* pszK)
436 : {
437 0 : int bAddToOtherTags = FALSE;
438 :
439 0 : if ( aoSetIgnoreKeys.find(pszK) == aoSetIgnoreKeys.end() )
440 : {
441 0 : char* pszColon = strchr((char*) pszK, ':');
442 0 : if( pszColon )
443 : {
444 0 : char chBackup = pszColon[1];
445 0 : pszColon[1] = '\0'; /* Evil but OK */
446 : bAddToOtherTags = ( aoSetIgnoreKeys.find(pszK) ==
447 0 : aoSetIgnoreKeys.end() );
448 0 : pszColon[1] = chBackup;
449 : }
450 : else
451 0 : bAddToOtherTags = TRUE;
452 : }
453 :
454 0 : return bAddToOtherTags;
455 : }
456 :
457 : /************************************************************************/
458 : /* OGROSMFormatForHSTORE() */
459 : /************************************************************************/
460 :
461 0 : static int OGROSMFormatForHSTORE(const char* pszV, char* pszAllTags)
462 : {
463 : int k;
464 0 : int bMustAddDoubleQuotes = FALSE;
465 :
466 0 : int nAllTagsOff = 0;
467 :
468 : /* Follow the quoting rules of http://www.postgresql.org/docs/9.0/static/hstore.html */
469 0 : for(k=0;pszV[k] != '\0'; k++)
470 : {
471 0 : if( pszV[k] == ' ' || pszV[k] == ','||
472 0 : pszV[k] == '=' || pszV[k] == '>' )
473 : {
474 0 : bMustAddDoubleQuotes = TRUE;
475 0 : break;
476 : }
477 : }
478 0 : if( bMustAddDoubleQuotes )
479 : {
480 0 : pszAllTags[nAllTagsOff++] = '"';
481 : }
482 :
483 0 : for(k=0;pszV[k] != '\0'; k++)
484 : {
485 0 : if( pszV[k] == '"' || pszV[k] == '\\' )
486 0 : pszAllTags[nAllTagsOff++] = '\\';
487 0 : pszAllTags[nAllTagsOff++] = pszV[k];
488 : }
489 :
490 0 : if( bMustAddDoubleQuotes )
491 : {
492 0 : pszAllTags[nAllTagsOff++] = '"';
493 : }
494 :
495 0 : return nAllTagsOff;
496 : }
497 :
498 : /************************************************************************/
499 : /* SetFieldsFromTags() */
500 : /************************************************************************/
501 :
502 68 : void OGROSMLayer::SetFieldsFromTags(OGRFeature* poFeature,
503 : GIntBig nID,
504 : int bIsWayID,
505 : unsigned int nTags, OSMTag* pasTags,
506 : OSMInfo* psInfo)
507 : {
508 68 : if( !bIsWayID )
509 : {
510 : if( (long)nID == nID )
511 62 : poFeature->SetFID( (long)nID ); /* Will not work with 32bit GDAL if id doesn't fit into 32 bits */
512 :
513 62 : if( bHasOSMId )
514 : {
515 : char szID[32];
516 62 : sprintf(szID, CPL_FRMT_GIB, nID );
517 62 : poFeature->SetField(nIndexOSMId, szID);
518 : }
519 : }
520 : else
521 : {
522 : if( (long)nID == nID )
523 6 : poFeature->SetFID( (long)nID ); /* Will not work with 32bit GDAL if id doesn't fit into 32 bits */
524 :
525 6 : if( nIndexOSMWayId >= 0 )
526 : {
527 : char szID[32];
528 6 : sprintf(szID, CPL_FRMT_GIB, nID );
529 6 : poFeature->SetField(nIndexOSMWayId, szID );
530 : }
531 : }
532 :
533 68 : if( bHasVersion )
534 : {
535 0 : poFeature->SetField("osm_version", psInfo->nVersion);
536 : }
537 68 : if( bHasTimestamp )
538 : {
539 0 : if( psInfo->bTimeStampIsStr )
540 : {
541 : int year, month, day, hour, minute, TZ;
542 : float second;
543 0 : if (OGRParseXMLDateTime(psInfo->ts.pszTimeStamp, &year, &month, &day,
544 : &hour, &minute, &second, &TZ))
545 : {
546 : poFeature->SetField("osm_timestamp", year, month, day, hour,
547 0 : minute, (int)(second + .5), TZ);
548 : }
549 : }
550 : else
551 : {
552 : struct tm brokendown;
553 0 : CPLUnixTimeToYMDHMS(psInfo->ts.nTimeStamp, &brokendown);
554 : poFeature->SetField("osm_timestamp",
555 : brokendown.tm_year + 1900,
556 : brokendown.tm_mon + 1,
557 : brokendown.tm_mday,
558 : brokendown.tm_hour,
559 : brokendown.tm_min,
560 : brokendown.tm_sec,
561 0 : 0);
562 : }
563 :
564 : }
565 68 : if( bHasUID )
566 : {
567 0 : poFeature->SetField("osm_uid", psInfo->nUID);
568 : }
569 68 : if( bHasUser )
570 : {
571 0 : poFeature->SetField("osm_user", psInfo->pszUserSID);
572 : }
573 68 : if( bHasChangeset )
574 : {
575 0 : poFeature->SetField("osm_changeset", (int) psInfo->nChangeset);
576 : }
577 :
578 68 : int nAllTagsOff = 0;
579 154 : for(unsigned int j = 0; j < nTags; j++)
580 : {
581 86 : const char* pszK = pasTags[j].pszK;
582 86 : const char* pszV = pasTags[j].pszV;
583 86 : int nIndex = GetFieldIndex(pszK);
584 86 : if( nIndex >= 0 )
585 86 : poFeature->SetField(nIndex, pszV);
586 0 : else if ( HasOtherTags() )
587 : {
588 0 : if ( AddInOtherTag(pszK) )
589 : {
590 0 : int nLenK = (int)strlen(pszK);
591 0 : int nLenV = (int)strlen(pszV);
592 0 : if( nAllTagsOff +
593 : 1 + 2 * nLenK + 1 +
594 : 2 +
595 : 1 + 2 * nLenV + 1 +
596 : 1 >= ALLTAGS_LENGTH - 1 )
597 : {
598 0 : if( !bHasWarnedAllTagsTruncated )
599 0 : CPLDebug("OSM", "all_tags field truncated for feature " CPL_FRMT_GIB, nID);
600 0 : bHasWarnedAllTagsTruncated = TRUE;
601 0 : continue;
602 : }
603 :
604 0 : if( nAllTagsOff )
605 0 : pszAllTags[nAllTagsOff++] = ',';
606 :
607 : nAllTagsOff += OGROSMFormatForHSTORE(pszK,
608 0 : pszAllTags + nAllTagsOff);
609 :
610 0 : pszAllTags[nAllTagsOff++] = '=';
611 0 : pszAllTags[nAllTagsOff++] = '>';
612 :
613 : nAllTagsOff += OGROSMFormatForHSTORE(pszV,
614 0 : pszAllTags + nAllTagsOff);
615 : }
616 :
617 : #ifdef notdef
618 : if ( aoSetWarnKeys.find(pszK) ==
619 : aoSetWarnKeys.end() )
620 : {
621 : aoSetWarnKeys.insert(pszK);
622 : CPLDebug("OSM_KEY", "Ignored key : %s", pszK);
623 : }
624 : #endif
625 : }
626 : }
627 :
628 68 : if( nAllTagsOff )
629 : {
630 0 : pszAllTags[nAllTagsOff] = '\0';
631 0 : poFeature->SetField(GetLayerDefn()->GetFieldCount() - 1, pszAllTags);
632 : }
633 68 : }
634 :
635 : /************************************************************************/
636 : /* GetSpatialFilterEnvelope() */
637 : /************************************************************************/
638 :
639 28 : const OGREnvelope* OGROSMLayer::GetSpatialFilterEnvelope()
640 : {
641 28 : if( m_poFilterGeom != NULL )
642 0 : return &m_sFilterEnvelope;
643 : else
644 28 : return NULL;
645 : }
646 :
647 : /************************************************************************/
648 : /* AddUnsignificantKey() */
649 : /************************************************************************/
650 :
651 50 : void OGROSMLayer::AddUnsignificantKey(const char* pszK)
652 : {
653 50 : char* pszKDup = CPLStrdup(pszK);
654 50 : apszUnsignificantKeys.push_back(pszKDup);
655 50 : aoSetUnsignificantKeys[pszKDup] = 1;
656 50 : }
657 :
658 : /************************************************************************/
659 : /* AddIgnoreKey() */
660 : /************************************************************************/
661 :
662 480 : void OGROSMLayer::AddIgnoreKey(const char* pszK)
663 : {
664 480 : char* pszKDup = CPLStrdup(pszK);
665 480 : apszIgnoreKeys.push_back(pszKDup);
666 480 : aoSetIgnoreKeys[pszKDup] = 1;
667 480 : }
668 :
669 : /************************************************************************/
670 : /* AddWarnKey() */
671 : /************************************************************************/
672 :
673 480 : void OGROSMLayer::AddWarnKey(const char* pszK)
674 : {
675 480 : aoSetWarnKeys.insert(pszK);
676 480 : }
|