1 /******************************************************************************
2  *
3  * Project:  KML Driver
4  * Purpose:  Class for building up the node structure of the kml file.
5  * Author:   Jens Oberender, j.obi@troja.net
6  *
7  ******************************************************************************
8  * Copyright (c) 2007, Jens Oberender
9  * Copyright (c) 2007-2012, 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 #include "cpl_port.h"
31 #include "kmlnode.h"
32 
33 #include <cstring>
34 #include <memory>
35 #include <string>
36 #include <vector>
37 
38 #include "cpl_conv.h"
39 #include "cpl_error.h"
40 #include "ogr_geometry.h"
41 
42 CPL_CVSID("$Id: kmlnode.cpp 8ca42e1b9c2e54b75d35e49885df9789a2643aa4 2020-05-17 21:43:40 +0200 Even Rouault $")
43 
44 /************************************************************************/
45 /*                           Help functions                             */
46 /************************************************************************/
47 
Nodetype2String(Nodetype const & type)48 std::string Nodetype2String(Nodetype const& type)
49 {
50     if(type == Empty)
51         return "Empty";
52     else if(type == Rest)
53         return "Rest";
54     else if(type == Mixed)
55         return "Mixed";
56     else if(type == Point)
57         return "Point";
58     else if(type == LineString)
59         return "LineString";
60     else if(type == Polygon)
61         return "Polygon";
62     else if(type == MultiGeometry)
63         return "MultiGeometry";
64     else if(type == MultiPoint)
65         return "MultiPoint";
66     else if(type == MultiLineString)
67         return "MultiLineString";
68     else if(type == MultiPolygon)
69         return "MultiPolygon";
70     else
71         return "Unknown";
72 }
73 
74 static
isNumberDigit(const char cIn)75 bool isNumberDigit(const char cIn)
76 {
77     return ( cIn == '-' || cIn == '+' ||
78             (cIn >= '0' && cIn <= '9') ||
79              cIn == '.' || cIn == 'e' || cIn == 'E' );
80 }
81 
82 static
ParseCoordinate(std::string const & text)83 Coordinate* ParseCoordinate(std::string const& text)
84 {
85     int pos = 0;
86     const char* pszStr = text.c_str();
87     Coordinate *psTmp = new Coordinate();
88 
89     // X coordinate
90     psTmp->dfLongitude = CPLAtof(pszStr);
91     while(isNumberDigit(pszStr[pos++]));
92 
93     // Y coordinate
94     if(pszStr[pos - 1] != ',')
95     {
96         delete psTmp;
97         return nullptr;
98     }
99 
100     psTmp->dfLatitude = CPLAtof(pszStr + pos);
101     while(isNumberDigit(pszStr[pos++]));
102 
103     // Z coordinate
104     if(pszStr[pos - 1] != ',')
105     {
106         psTmp->bHasZ = false;
107         psTmp->dfAltitude = 0;
108         return psTmp;
109     }
110 
111     psTmp->bHasZ = true;
112     psTmp->dfAltitude = CPLAtof(pszStr + pos);
113 
114     return psTmp;
115 }
116 
117 /************************************************************************/
118 /*                         KMLNode methods                              */
119 /************************************************************************/
120 
KMLNode()121 KMLNode::KMLNode() :
122     pvpoChildren_(new std::vector<KMLNode*>),
123     pvsContent_(new std::vector<std::string>),
124     pvoAttributes_(new std::vector<Attribute*>),
125     poParent_(nullptr),
126     nLevel_(0),
127     eType_(Unknown),
128     b25D_(false),
129     nLayerNumber_(-1),
130     nNumFeatures_(-1)
131 {}
132 
~KMLNode()133 KMLNode::~KMLNode()
134 {
135     CPLAssert( nullptr != pvpoChildren_ );
136     CPLAssert( nullptr != pvoAttributes_ );
137 
138     kml_nodes_t::iterator itChild;
139     for( itChild = pvpoChildren_->begin();
140          itChild != pvpoChildren_->end(); ++itChild)
141     {
142         delete (*itChild);
143     }
144     delete pvpoChildren_;
145 
146     kml_attributes_t::iterator itAttr;
147     for( itAttr = pvoAttributes_->begin();
148          itAttr != pvoAttributes_->end(); ++itAttr)
149     {
150         delete (*itAttr);
151     }
152     delete pvoAttributes_;
153 
154     delete pvsContent_;
155 }
156 
print(unsigned int what)157 void KMLNode::print(unsigned int what)
158 {
159     std::string indent;
160     for(std::size_t l = 0; l < nLevel_; l++)
161         indent += " ";
162 
163     if(nLevel_ > 0)
164     {
165         if(nLayerNumber_ > -1)
166         {
167             CPLDebug( "KML",
168                       "%s%s (nLevel: %d Type: %s poParent: %s "
169                       "pvpoChildren_: %d pvsContent_: %d pvoAttributes_: %d) "
170                       "<--- Layer #%d",
171                       indent.c_str(), sName_.c_str(),
172                       static_cast<int>(nLevel_),
173                       Nodetype2String(eType_).c_str(),
174                       poParent_->sName_.c_str(),
175                       static_cast<int>(pvpoChildren_->size()),
176                       static_cast<int>(pvsContent_->size()),
177                       static_cast<int>(pvoAttributes_->size()),
178                       nLayerNumber_ );
179         }
180         else
181         {
182             CPLDebug( "KML",
183                       "%s%s (nLevel: %d Type: %s poParent: %s "
184                       "pvpoChildren_: %d pvsContent_: %d pvoAttributes_: %d)",
185                       indent.c_str(), sName_.c_str(), static_cast<int>(nLevel_),
186                       Nodetype2String(eType_).c_str(),
187                       poParent_->sName_.c_str(),
188                       static_cast<int>(pvpoChildren_->size()),
189                       static_cast<int>(pvsContent_->size()),
190                       static_cast<int>(pvoAttributes_->size()) );
191         }
192     }
193     else
194     {
195         CPLDebug( "KML",
196                   "%s%s (nLevel: %d Type: %s pvpoChildren_: %d "
197                   "pvsContent_: %d pvoAttributes_: %d)",
198                   indent.c_str(), sName_.c_str(), static_cast<int>(nLevel_),
199                   Nodetype2String(eType_).c_str(),
200                   static_cast<int>(pvpoChildren_->size()),
201                   static_cast<int>(pvsContent_->size()),
202                   static_cast<int>(pvoAttributes_->size()) );
203     }
204 
205     if(what == 1 || what == 3)
206     {
207         for(kml_content_t::size_type z = 0; z < pvsContent_->size(); z++)
208             CPLDebug( "KML", "%s|->pvsContent_: '%s'",
209                       indent.c_str(), (*pvsContent_)[z].c_str() );
210     }
211 
212     if(what == 2 || what == 3)
213     {
214         for(kml_attributes_t::size_type z = 0; z < pvoAttributes_->size(); z++)
215             CPLDebug( "KML", "%s|->pvoAttributes_: %s = '%s'",
216                       indent.c_str(), (*pvoAttributes_)[z]->sName.c_str(),
217                       (*pvoAttributes_)[z]->sValue.c_str() );
218     }
219 
220     for(kml_nodes_t::size_type z = 0; z < pvpoChildren_->size(); z++)
221         (*pvpoChildren_)[z]->print(what);
222 }
223 
classify(KML * poKML,int nRecLevel)224 int KMLNode::classify(KML* poKML, int nRecLevel)
225 {
226     Nodetype all = Empty;
227 
228     /* Arbitrary value, but certainly large enough for reasonable usages ! */
229     if( nRecLevel == 32 )
230     {
231         CPLError( CE_Failure, CPLE_AppDefined,
232                   "Too many recursion levels (%d) while parsing KML geometry.",
233                   nRecLevel );
234         return FALSE;
235     }
236 
237     if(sName_.compare("Point") == 0)
238         eType_ = Point;
239     else if(sName_.compare("LineString") == 0)
240         eType_ = LineString;
241     else if(sName_.compare("Polygon") == 0)
242         eType_ = Polygon;
243     else if(poKML->isRest(sName_))
244         eType_ = Empty;
245     else if(sName_.compare("coordinates") == 0)
246     {
247         for( unsigned int nCountP = 0;
248              nCountP < pvsContent_->size();
249              nCountP++ )
250         {
251             const char* pszCoord = (*pvsContent_)[nCountP].c_str();
252             int nComma = 0;
253             while( true )
254             {
255                 pszCoord = strchr(pszCoord, ',');
256                 if (pszCoord)
257                 {
258                     nComma ++;
259                     pszCoord ++;
260                 }
261                 else
262                     break;
263             }
264             if (nComma == 2)
265                 b25D_ = true;
266         }
267     }
268 
269     const kml_nodes_t::size_type size = pvpoChildren_->size();
270     for(kml_nodes_t::size_type z = 0; z < size; z++)
271     {
272         // Classify pvpoChildren_
273         if (!(*pvpoChildren_)[z]->classify(poKML, nRecLevel + 1))
274             return FALSE;
275 
276         Nodetype curr = (*pvpoChildren_)[z]->eType_;
277         b25D_ |= (*pvpoChildren_)[z]->b25D_;
278 
279         // Compare and return if it is mixed
280         if(curr != all && all != Empty && curr != Empty)
281         {
282             if (sName_.compare("MultiGeometry") == 0)
283                 eType_ = MultiGeometry;
284             else
285                 eType_ = Mixed;
286         }
287         else if(curr != Empty)
288         {
289             all = curr;
290         }
291     }
292 
293     if(eType_ == Unknown)
294     {
295         if (sName_.compare("MultiGeometry") == 0)
296         {
297             if (all == Point)
298                 eType_ = MultiPoint;
299             else if (all == LineString)
300                 eType_ = MultiLineString;
301             else if (all == Polygon)
302                 eType_ = MultiPolygon;
303             else
304                 eType_ = MultiGeometry;
305         }
306         else
307             eType_ = all;
308     }
309 
310     return TRUE;
311 }
312 
313 
unregisterLayerIfMatchingThisNode(KML * poKML)314 void KMLNode::unregisterLayerIfMatchingThisNode(KML* poKML)
315 {
316     for(std::size_t z = 0; z < countChildren(); z++)
317     {
318         getChild(z)->unregisterLayerIfMatchingThisNode(poKML);
319     }
320     poKML->unregisterLayerIfMatchingThisNode(this);
321 }
322 
eliminateEmpty(KML * poKML)323 void KMLNode::eliminateEmpty(KML* poKML)
324 {
325     for(kml_nodes_t::size_type z = 0; z < pvpoChildren_->size();)
326     {
327         if((*pvpoChildren_)[z]->eType_ == Empty
328            && (poKML->isContainer((*pvpoChildren_)[z]->sName_)
329                || poKML->isFeatureContainer((*pvpoChildren_)[z]->sName_)))
330         {
331             (*pvpoChildren_)[z]->unregisterLayerIfMatchingThisNode(poKML);
332             delete (*pvpoChildren_)[z];
333             pvpoChildren_->erase(pvpoChildren_->begin() + z);
334         }
335         else
336         {
337             (*pvpoChildren_)[z]->eliminateEmpty(poKML);
338             ++z;
339         }
340     }
341 }
342 
hasOnlyEmpty() const343 bool KMLNode::hasOnlyEmpty() const
344 {
345     for(kml_nodes_t::size_type z = 0; z < pvpoChildren_->size(); z++)
346     {
347         if((*pvpoChildren_)[z]->eType_ != Empty)
348         {
349             return false;
350         }
351         else
352         {
353             if (!(*pvpoChildren_)[z]->hasOnlyEmpty())
354                 return false;
355         }
356     }
357 
358     return true;
359 }
360 
setType(Nodetype oNotet)361 void KMLNode::setType(Nodetype oNotet)
362 {
363     eType_ = oNotet;
364 }
365 
getType() const366 Nodetype KMLNode::getType() const
367 {
368     return eType_;
369 }
370 
setName(std::string const & sIn)371 void KMLNode::setName(std::string const& sIn)
372 {
373     sName_ = sIn;
374 }
375 
getName() const376 const std::string& KMLNode::getName() const
377 {
378     return sName_;
379 }
380 
setLevel(std::size_t nLev)381 void KMLNode::setLevel(std::size_t nLev)
382 {
383     nLevel_ = nLev;
384 }
385 
getLevel() const386 std::size_t KMLNode::getLevel() const
387 {
388     return nLevel_;
389 }
390 
addAttribute(Attribute * poAttr)391 void KMLNode::addAttribute(Attribute *poAttr)
392 {
393     pvoAttributes_->push_back(poAttr);
394 }
395 
setParent(KMLNode * poPar)396 void KMLNode::setParent(KMLNode* poPar)
397 {
398     poParent_ = poPar;
399 }
400 
getParent() const401 KMLNode* KMLNode::getParent() const
402 {
403     return poParent_;
404 }
405 
addChildren(KMLNode * poChil)406 void KMLNode::addChildren(KMLNode *poChil)
407 {
408     pvpoChildren_->push_back(poChil);
409 }
410 
countChildren() const411 std::size_t KMLNode::countChildren() const
412 {
413     return pvpoChildren_->size();
414 }
415 
getChild(std::size_t index) const416 KMLNode* KMLNode::getChild(std::size_t index) const
417 {
418     return (*pvpoChildren_)[index];
419 }
420 
addContent(std::string const & text)421 void KMLNode::addContent(std::string const& text)
422 {
423     pvsContent_->push_back(text);
424 }
425 
appendContent(std::string const & text)426 void KMLNode::appendContent(std::string const& text)
427 {
428     pvsContent_->back() += text;
429 }
430 
getContent(std::size_t index) const431 std::string KMLNode::getContent(std::size_t index) const
432 {
433     return (*pvsContent_)[index];
434 }
435 
deleteContent(std::size_t index)436 void KMLNode::deleteContent(std::size_t index)
437 {
438     if( index < pvsContent_->size() )
439     {
440         pvsContent_->erase(pvsContent_->begin() + index);
441     }
442 }
443 
numContent() const444 std::size_t KMLNode::numContent() const
445 {
446     return pvsContent_->size();
447 }
448 
setLayerNumber(int nNum)449 void KMLNode::setLayerNumber(int nNum)
450 {
451     nLayerNumber_ = nNum;
452 }
453 
getLayerNumber() const454 int KMLNode::getLayerNumber() const
455 {
456     return nLayerNumber_;
457 }
458 
getNameElement() const459 std::string KMLNode::getNameElement() const
460 {
461     const kml_nodes_t::size_type size = pvpoChildren_->size();
462 
463     for( kml_nodes_t::size_type i = 0; i < size; ++i )
464     {
465         if( (*pvpoChildren_)[i]->sName_.compare("name") == 0 )
466         {
467             const auto subsize = (*pvpoChildren_)[i]->pvsContent_->size();
468             if( subsize > 0 )
469             {
470                 return (*(*pvpoChildren_)[i]->pvsContent_)[0];
471             }
472             break;
473         }
474     }
475     return "";
476 }
477 
getDescriptionElement() const478 std::string KMLNode::getDescriptionElement() const
479 {
480     const kml_nodes_t::size_type size = pvpoChildren_->size();
481     for( kml_nodes_t::size_type i = 0; i < size; ++i )
482     {
483         if( (*pvpoChildren_)[i]->sName_.compare("description") == 0 )
484         {
485             const auto subsize = (*pvpoChildren_)[i]->pvsContent_->size();
486             if ( subsize > 0 )
487             {
488                 return (*(*pvpoChildren_)[i]->pvsContent_)[0];
489             }
490             break;
491         }
492     }
493     return "";
494 }
495 
getNumFeatures()496 std::size_t KMLNode::getNumFeatures()
497 {
498     if (nNumFeatures_ < 0)
499     {
500         std::size_t nNum = 0;
501         kml_nodes_t::size_type size = pvpoChildren_->size();
502 
503         for( kml_nodes_t::size_type i = 0; i < size; ++i )
504         {
505             if( (*pvpoChildren_)[i]->sName_ == "Placemark" )
506                 nNum++;
507         }
508         nNumFeatures_ = (int)nNum;
509     }
510     return nNumFeatures_;
511 }
512 
getGeometry(Nodetype eType)513 OGRGeometry* KMLNode::getGeometry(Nodetype eType)
514 {
515     OGRGeometry* poGeom = nullptr;
516     KMLNode* poCoor = nullptr;
517     Coordinate* psCoord = nullptr;
518 
519     if (sName_.compare("Point") == 0)
520     {
521         // Search coordinate Element
522         for( unsigned int nCount = 0; nCount < pvpoChildren_->size(); nCount++)
523         {
524             if((*pvpoChildren_)[nCount]->sName_.compare("coordinates") == 0)
525             {
526                 poCoor = (*pvpoChildren_)[nCount];
527                 for( unsigned int nCountP = 0;
528                      nCountP < poCoor->pvsContent_->size();
529                      nCountP++)
530                 {
531                     psCoord = ParseCoordinate((*poCoor->pvsContent_)[nCountP]);
532                     if(psCoord != nullptr)
533                     {
534                         if( psCoord->bHasZ )
535                             poGeom = new OGRPoint(psCoord->dfLongitude,
536                                                   psCoord->dfLatitude,
537                                                   psCoord->dfAltitude);
538                         else
539                             poGeom = new OGRPoint(psCoord->dfLongitude,
540                                                   psCoord->dfLatitude);
541                         delete psCoord;
542                         return poGeom;
543                     }
544                 }
545             }
546         }
547         poGeom = new OGRPoint();
548     }
549     else if (sName_.compare("LineString") == 0)
550     {
551         // Search coordinate Element
552         poGeom = new OGRLineString();
553         for( unsigned int nCount = 0; nCount < pvpoChildren_->size(); nCount++)
554         {
555             if((*pvpoChildren_)[nCount]->sName_.compare("coordinates") == 0)
556             {
557                 poCoor = (*pvpoChildren_)[nCount];
558                 for( unsigned int nCountP = 0;
559                      nCountP < poCoor->pvsContent_->size();
560                      nCountP++ )
561                 {
562                     psCoord = ParseCoordinate((*poCoor->pvsContent_)[nCountP]);
563                     if(psCoord != nullptr)
564                     {
565                         if( psCoord->bHasZ )
566                             poGeom->toLineString()->addPoint(psCoord->dfLongitude,
567                                                                psCoord->dfLatitude,
568                                                                psCoord->dfAltitude);
569                         else
570                             poGeom->toLineString()->addPoint(psCoord->dfLongitude,
571                                                                psCoord->dfLatitude);
572                         delete psCoord;
573                     }
574                 }
575             }
576         }
577     }
578     else if (sName_.compare("Polygon") == 0)
579     {
580         //*********************************
581         // Search outerBoundaryIs Element
582         //*********************************
583         poGeom = new OGRPolygon();
584         for(unsigned int nCount = 0; nCount < pvpoChildren_->size(); nCount++)
585         {
586             if((*pvpoChildren_)[nCount]->sName_.compare("outerBoundaryIs") == 0 &&
587                !(*pvpoChildren_)[nCount]->pvpoChildren_->empty())
588             {
589                 poCoor = (*(*pvpoChildren_)[nCount]->pvpoChildren_)[0];
590             }
591         }
592         // No outer boundary found
593         if(poCoor == nullptr)
594         {
595             return poGeom;
596         }
597         // Search coordinate Element
598         OGRLinearRing* poLinearRing = nullptr;
599         for( unsigned int nCount = 0;
600              nCount < poCoor->pvpoChildren_->size();
601              nCount++)
602         {
603             if((*poCoor->pvpoChildren_)[nCount]->sName_.compare("coordinates") == 0)
604             {
605                 for( unsigned int nCountP = 0;
606                      nCountP <
607                        (*poCoor->pvpoChildren_)[nCount]->pvsContent_->size();
608                      nCountP++)
609                 {
610                     psCoord = ParseCoordinate((*(*poCoor->pvpoChildren_)[nCount]->pvsContent_)[nCountP]);
611                     if(psCoord != nullptr)
612                     {
613                         if (poLinearRing == nullptr)
614                         {
615                             poLinearRing = new OGRLinearRing();
616                         }
617                         if( psCoord->bHasZ )
618                             poLinearRing->addPoint(psCoord->dfLongitude,
619                                                    psCoord->dfLatitude,
620                                                    psCoord->dfAltitude);
621                         else
622                             poLinearRing->addPoint(psCoord->dfLongitude,
623                                                    psCoord->dfLatitude);
624                         delete psCoord;
625                     }
626                 }
627             }
628         }
629         // No outer boundary coordinates found
630         if(poLinearRing == nullptr)
631         {
632             return poGeom;
633         }
634 
635         poGeom->toPolygon()->addRingDirectly(poLinearRing);
636         poLinearRing = nullptr;
637 
638         //*********************************
639         // Search innerBoundaryIs Elements
640         //*********************************
641 
642         for( unsigned int nCount2 = 0;
643              nCount2 < pvpoChildren_->size();
644              nCount2++ )
645         {
646             if((*pvpoChildren_)[nCount2]->sName_.compare("innerBoundaryIs") == 0)
647             {
648                 if (poLinearRing)
649                     poGeom->toPolygon()->addRingDirectly(poLinearRing);
650                 poLinearRing = nullptr;
651 
652                 if ((*pvpoChildren_)[nCount2]->pvpoChildren_->empty())
653                     continue;
654 
655                 poLinearRing = new OGRLinearRing();
656 
657                 poCoor = (*(*pvpoChildren_)[nCount2]->pvpoChildren_)[0];
658                 // Search coordinate Element
659                 for( unsigned int nCount = 0;
660                      nCount < poCoor->pvpoChildren_->size();
661                      nCount++ )
662                 {
663                     if((*poCoor->pvpoChildren_)[nCount]->sName_.compare("coordinates") == 0)
664                     {
665                         for( unsigned int nCountP = 0;
666                              nCountP < (*poCoor->pvpoChildren_)[nCount]->pvsContent_->size();
667                              nCountP++)
668                         {
669                             psCoord = ParseCoordinate((*(*poCoor->pvpoChildren_)[nCount]->pvsContent_)[nCountP]);
670                             if(psCoord != nullptr)
671                             {
672                                 if( psCoord->bHasZ )
673                                     poLinearRing->addPoint(psCoord->dfLongitude,
674                                                         psCoord->dfLatitude,
675                                                         psCoord->dfAltitude);
676                                 else
677                                     poLinearRing->addPoint(psCoord->dfLongitude,
678                                                         psCoord->dfLatitude);
679                                 delete psCoord;
680                             }
681                         }
682                     }
683                 }
684             }
685         }
686 
687         if( poLinearRing )
688             poGeom->toPolygon()->addRingDirectly(poLinearRing);
689     }
690     else if (sName_.compare("MultiGeometry") == 0)
691     {
692         if (eType == MultiPoint)
693             poGeom = new OGRMultiPoint();
694         else if (eType == MultiLineString)
695             poGeom = new OGRMultiLineString();
696         else if (eType == MultiPolygon)
697             poGeom = new OGRMultiPolygon();
698         else
699             poGeom = new OGRGeometryCollection();
700         for(unsigned int nCount = 0; nCount < pvpoChildren_->size(); nCount++)
701         {
702             OGRGeometry* poSubGeom = (*pvpoChildren_)[nCount]->getGeometry();
703             if (poSubGeom)
704                 poGeom->toGeometryCollection()->addGeometryDirectly(poSubGeom);
705         }
706     }
707 
708     return poGeom;
709 }
710 
getFeature(std::size_t nNum,int & nLastAsked,int & nLastCount)711 Feature* KMLNode::getFeature(std::size_t nNum, int& nLastAsked, int &nLastCount)
712 {
713     if( nNum >= getNumFeatures() )
714         return nullptr;
715 
716     unsigned int nCount = 0;
717     unsigned int nCountP = 0;
718     KMLNode* poFeat = nullptr;
719     KMLNode* poTemp = nullptr;
720 
721     if (nLastAsked + 1 != static_cast<int>(nNum ))
722     {
723         // nCount = 0;
724         // nCountP = 0;
725     }
726     else
727     {
728         nCount = nLastCount + 1;
729         nCountP = nLastAsked + 1;
730     }
731 
732     for(; nCount < pvpoChildren_->size(); nCount++)
733     {
734         if((*pvpoChildren_)[nCount]->sName_.compare("Placemark") == 0)
735         {
736             if(nCountP == nNum)
737             {
738                 poFeat = (*pvpoChildren_)[nCount];
739                 break;
740             }
741             nCountP++;
742         }
743     }
744 
745     nLastAsked = static_cast<int>(nNum);
746     nLastCount = nCount;
747 
748     if(poFeat == nullptr)
749         return nullptr;
750 
751     // Create a feature structure
752     Feature *psReturn = new Feature;
753     // Build up the name
754     psReturn->sName = poFeat->getNameElement();
755     // Build up the description
756     psReturn->sDescription = poFeat->getDescriptionElement();
757     // the type
758     psReturn->eType = poFeat->eType_;
759 
760     std::string sElementName;
761     if(poFeat->eType_ == Point ||
762        poFeat->eType_ == LineString ||
763        poFeat->eType_ == Polygon)
764         sElementName = Nodetype2String(poFeat->eType_);
765     else if (poFeat->eType_ == MultiGeometry ||
766              poFeat->eType_ == MultiPoint ||
767              poFeat->eType_ == MultiLineString ||
768              poFeat->eType_ == MultiPolygon)
769         sElementName = "MultiGeometry";
770     else
771     {
772         delete psReturn;
773         return nullptr;
774     }
775 
776     for(nCount = 0; nCount < poFeat->pvpoChildren_->size(); nCount++)
777     {
778         if((*poFeat->pvpoChildren_)[nCount]->sName_.compare(sElementName) == 0)
779         {
780             poTemp = (*poFeat->pvpoChildren_)[nCount];
781             psReturn->poGeom = poTemp->getGeometry(poFeat->eType_);
782             if(psReturn->poGeom)
783                 return psReturn;
784             else
785             {
786                 delete psReturn;
787                 return nullptr;
788             }
789         }
790     }
791 
792     delete psReturn;
793     return nullptr;
794 }
795