Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: KML Driver
4 : * Purpose: Class for reading, parsing and handling a kmlfile.
5 : * Author: Jens Oberender, j.obi@troja.net
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2007, Jens Oberender
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 : #include "kmlnode.h"
14 : #include "kml.h"
15 :
16 : #include <cstring>
17 : #include <cstdio>
18 : #include <exception>
19 : #include <iostream>
20 : #include <string>
21 :
22 : #include "cpl_conv.h"
23 : #include "cpl_error.h"
24 : #ifdef HAVE_EXPAT
25 : #include "expat.h"
26 : #endif
27 :
28 : constexpr int PARSER_BUF_SIZE = 8192;
29 :
30 25 : KML::KML()
31 : : poTrunk_(nullptr), nNumLayers_(-1), papoLayers_(nullptr), nDepth_(0),
32 : validity(KML_VALIDITY_UNKNOWN), pKMLFile_(nullptr), poCurrent_(nullptr),
33 25 : oCurrentParser(nullptr), nDataHandlerCounter(0), nWithoutEventCounter(0)
34 : {
35 25 : }
36 :
37 25 : KML::~KML()
38 : {
39 25 : if (nullptr != pKMLFile_)
40 25 : VSIFCloseL(pKMLFile_);
41 25 : CPLFree(papoLayers_);
42 :
43 25 : delete poTrunk_;
44 25 : }
45 :
46 25 : bool KML::open(const char *pszFilename)
47 : {
48 25 : if (nullptr != pKMLFile_)
49 0 : VSIFCloseL(pKMLFile_);
50 :
51 25 : pKMLFile_ = VSIFOpenL(pszFilename, "r");
52 25 : return pKMLFile_ != nullptr;
53 : }
54 :
55 24 : bool KML::parse()
56 : {
57 24 : if (nullptr == pKMLFile_)
58 : {
59 0 : sError_ = "No file given";
60 0 : return false;
61 : }
62 :
63 24 : if (poTrunk_ != nullptr)
64 : {
65 0 : delete poTrunk_;
66 0 : poTrunk_ = nullptr;
67 : }
68 :
69 24 : if (poCurrent_ != nullptr)
70 : {
71 0 : delete poCurrent_;
72 0 : poCurrent_ = nullptr;
73 : }
74 :
75 24 : XML_Parser oParser = OGRCreateExpatXMLParser();
76 24 : XML_SetUserData(oParser, this);
77 24 : XML_SetElementHandler(oParser, startElement, endElement);
78 24 : XML_SetCharacterDataHandler(oParser, dataHandler);
79 24 : oCurrentParser = oParser;
80 24 : nWithoutEventCounter = 0;
81 :
82 24 : int nDone = 0;
83 24 : unsigned nLen = 0;
84 48 : std::vector<char> aBuf(PARSER_BUF_SIZE);
85 24 : bool bError = false;
86 :
87 45 : do
88 : {
89 69 : nDataHandlerCounter = 0;
90 69 : nLen = (unsigned)VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_);
91 69 : nDone = nLen < aBuf.size();
92 69 : if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
93 : {
94 1 : CPLError(CE_Failure, CPLE_AppDefined,
95 : "XML parsing of KML file failed : %s at line %d, "
96 : "column %d",
97 : XML_ErrorString(XML_GetErrorCode(oParser)),
98 1 : static_cast<int>(XML_GetCurrentLineNumber(oParser)),
99 1 : static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
100 1 : bError = true;
101 1 : break;
102 : }
103 68 : nWithoutEventCounter++;
104 68 : } while (!nDone && nLen > 0 && nWithoutEventCounter < 10);
105 :
106 24 : XML_ParserFree(oParser);
107 24 : VSIRewindL(pKMLFile_);
108 :
109 24 : if (nWithoutEventCounter == 10)
110 : {
111 0 : CPLError(CE_Failure, CPLE_AppDefined,
112 : "Too much data inside one element. File probably corrupted");
113 0 : bError = true;
114 : }
115 :
116 24 : if (bError)
117 : {
118 1 : if (poCurrent_ != nullptr)
119 : {
120 0 : while (poCurrent_)
121 : {
122 0 : KMLNode *poTemp = poCurrent_->getParent();
123 0 : delete poCurrent_;
124 0 : poCurrent_ = poTemp;
125 : }
126 : // No need to destroy poTrunk_ : it has been destroyed in
127 : // the last iteration
128 : }
129 : else
130 : {
131 : // Case of invalid content after closing element matching
132 : // first <kml> element
133 1 : delete poTrunk_;
134 : }
135 1 : poTrunk_ = nullptr;
136 1 : return false;
137 : }
138 :
139 23 : poCurrent_ = nullptr;
140 23 : return true;
141 : }
142 :
143 25 : void KML::checkValidity()
144 : {
145 25 : if (poTrunk_ != nullptr)
146 : {
147 0 : delete poTrunk_;
148 0 : poTrunk_ = nullptr;
149 : }
150 :
151 25 : if (poCurrent_ != nullptr)
152 : {
153 0 : delete poCurrent_;
154 0 : poCurrent_ = nullptr;
155 : }
156 :
157 25 : if (pKMLFile_ == nullptr)
158 : {
159 0 : sError_ = "No file given";
160 1 : return;
161 : }
162 :
163 25 : XML_Parser oParser = OGRCreateExpatXMLParser();
164 25 : XML_SetUserData(oParser, this);
165 25 : XML_SetElementHandler(oParser, startElementValidate, nullptr);
166 25 : XML_SetCharacterDataHandler(oParser, dataHandlerValidate);
167 25 : int nCount = 0;
168 :
169 25 : oCurrentParser = oParser;
170 :
171 25 : int nDone = 0;
172 25 : unsigned nLen = 0;
173 50 : std::vector<char> aBuf(PARSER_BUF_SIZE);
174 :
175 : // Parses the file until we find the first element.
176 0 : do
177 : {
178 25 : nDataHandlerCounter = 0;
179 25 : nLen = static_cast<unsigned>(
180 25 : VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_));
181 25 : nDone = nLen < aBuf.size();
182 25 : if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
183 : {
184 1 : if (nLen <= PARSER_BUF_SIZE - 1)
185 1 : aBuf[nLen] = 0;
186 : else
187 0 : aBuf[PARSER_BUF_SIZE - 1] = 0;
188 2 : if (strstr(aBuf.data(), "<?xml") &&
189 1 : (strstr(aBuf.data(), "<kml") ||
190 0 : (strstr(aBuf.data(), "<Document") &&
191 0 : strstr(aBuf.data(), "/kml/2."))))
192 : {
193 1 : CPLError(
194 : CE_Failure, CPLE_AppDefined,
195 : "XML parsing of KML file failed : %s at line %d, column %d",
196 : XML_ErrorString(XML_GetErrorCode(oParser)),
197 1 : (int)XML_GetCurrentLineNumber(oParser),
198 1 : (int)XML_GetCurrentColumnNumber(oParser));
199 : }
200 :
201 1 : validity = KML_VALIDITY_INVALID;
202 1 : XML_ParserFree(oParser);
203 1 : VSIRewindL(pKMLFile_);
204 1 : return;
205 : }
206 :
207 24 : nCount++;
208 : /* After reading 50 * PARSER_BUF_SIZE bytes, and not finding whether the file */
209 : /* is KML or not, we give up and fail silently */
210 24 : } while (!nDone && nLen > 0 && validity == KML_VALIDITY_UNKNOWN &&
211 : nCount < 50);
212 :
213 24 : XML_ParserFree(oParser);
214 24 : VSIRewindL(pKMLFile_);
215 24 : poCurrent_ = nullptr;
216 : }
217 :
218 5664 : void XMLCALL KML::startElement(void *pUserData, const char *pszName,
219 : const char **ppszAttr)
220 : {
221 5664 : KML *poKML = static_cast<KML *>(pUserData);
222 : try
223 : {
224 5664 : poKML->nWithoutEventCounter = 0;
225 :
226 5664 : const char *pszColumn = strchr(pszName, ':');
227 5664 : if (pszColumn)
228 5 : pszName = pszColumn + 1;
229 :
230 11304 : if (poKML->poTrunk_ == nullptr ||
231 11280 : (poKML->poCurrent_ != nullptr &&
232 5640 : poKML->poCurrent_->getName().compare("description") != 0))
233 : {
234 5659 : if (poKML->nDepth_ == 1024)
235 : {
236 0 : CPLError(CE_Failure, CPLE_AppDefined,
237 : "Too big depth level (%d) while parsing KML.",
238 : poKML->nDepth_);
239 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
240 0 : return;
241 : }
242 :
243 5659 : KMLNode *poMynew = new KMLNode();
244 5659 : poMynew->setName(pszName);
245 5659 : poMynew->setLevel(poKML->nDepth_);
246 :
247 7095 : for (int i = 0; ppszAttr[i]; i += 2)
248 : {
249 1436 : Attribute *poAtt = new Attribute();
250 1436 : poAtt->sName = ppszAttr[i];
251 1436 : poAtt->sValue = ppszAttr[i + 1];
252 1436 : poMynew->addAttribute(poAtt);
253 : }
254 :
255 5659 : if (poKML->poTrunk_ == nullptr)
256 24 : poKML->poTrunk_ = poMynew;
257 5659 : if (poKML->poCurrent_ != nullptr)
258 5635 : poMynew->setParent(poKML->poCurrent_);
259 5659 : poKML->poCurrent_ = poMynew;
260 :
261 5659 : poKML->nDepth_++;
262 : }
263 5 : else if (poKML->poCurrent_ != nullptr)
264 : {
265 10 : std::string sNewContent = "<";
266 5 : sNewContent += pszName;
267 6 : for (int i = 0; ppszAttr[i]; i += 2)
268 : {
269 1 : sNewContent += " ";
270 1 : sNewContent += ppszAttr[i];
271 1 : sNewContent += "=\"";
272 1 : sNewContent += ppszAttr[i + 1];
273 1 : sNewContent += "\"";
274 : }
275 5 : sNewContent += ">";
276 5 : if (poKML->poCurrent_->numContent() == 0)
277 0 : poKML->poCurrent_->addContent(sNewContent);
278 : else
279 5 : poKML->poCurrent_->appendContent(sNewContent);
280 : }
281 : }
282 0 : catch (const std::exception &ex)
283 : {
284 0 : CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
285 0 : ex.what());
286 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
287 : }
288 : }
289 :
290 2053 : void XMLCALL KML::startElementValidate(void *pUserData, const char *pszName,
291 : const char **ppszAttr)
292 : {
293 2053 : KML *poKML = static_cast<KML *>(pUserData);
294 :
295 2053 : if (poKML->validity != KML_VALIDITY_UNKNOWN)
296 2028 : return;
297 :
298 25 : poKML->validity = KML_VALIDITY_INVALID;
299 :
300 25 : const char *pszColumn = strchr(pszName, ':');
301 25 : if (pszColumn)
302 1 : pszName = pszColumn + 1;
303 :
304 25 : if (strcmp(pszName, "kml") == 0 || strcmp(pszName, "Document") == 0)
305 : {
306 : // Check all Attributes
307 53 : for (int i = 0; ppszAttr[i]; i += 2)
308 : {
309 : // Find the namespace and determine the KML version
310 28 : if (strcmp(ppszAttr[i], "xmlns") == 0)
311 : {
312 : // Is it KML 2.2?
313 23 : if ((strcmp(ppszAttr[i + 1],
314 23 : "http://earth.google.com/kml/2.2") == 0) ||
315 23 : (strcmp(ppszAttr[i + 1],
316 : "http://www.opengis.net/kml/2.2") == 0))
317 : {
318 9 : poKML->validity = KML_VALIDITY_VALID;
319 9 : poKML->sVersion_ = "2.2";
320 : }
321 14 : else if (strcmp(ppszAttr[i + 1],
322 : "http://earth.google.com/kml/2.1") == 0)
323 : {
324 14 : poKML->validity = KML_VALIDITY_VALID;
325 14 : poKML->sVersion_ = "2.1";
326 : }
327 0 : else if (strcmp(ppszAttr[i + 1],
328 : "http://earth.google.com/kml/2.0") == 0)
329 : {
330 0 : poKML->validity = KML_VALIDITY_VALID;
331 0 : poKML->sVersion_ = "2.0";
332 : }
333 : else
334 : {
335 0 : CPLDebug("KML",
336 : "Unhandled xmlns value : %s. Going on though...",
337 0 : ppszAttr[i]);
338 0 : poKML->validity = KML_VALIDITY_VALID;
339 0 : poKML->sVersion_ = "?";
340 : }
341 : }
342 : }
343 :
344 25 : if (poKML->validity == KML_VALIDITY_INVALID)
345 : {
346 2 : CPLDebug("KML", "Did not find xmlns attribute in <kml> element. "
347 : "Going on though...");
348 2 : poKML->validity = KML_VALIDITY_VALID;
349 2 : poKML->sVersion_ = "?";
350 : }
351 : }
352 : }
353 :
354 7634 : void XMLCALL KML::dataHandlerValidate(void *pUserData,
355 : const char * /* pszData */,
356 : int /* nLen */)
357 : {
358 7634 : KML *poKML = static_cast<KML *>(pUserData);
359 :
360 7634 : poKML->nDataHandlerCounter++;
361 7634 : if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE)
362 : {
363 0 : CPLError(CE_Failure, CPLE_AppDefined,
364 : "File probably corrupted (million laugh pattern)");
365 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
366 : }
367 7634 : }
368 :
369 5664 : void XMLCALL KML::endElement(void *pUserData, const char *pszName)
370 : {
371 5664 : KML *poKML = static_cast<KML *>(pUserData);
372 :
373 : try
374 : {
375 5664 : poKML->nWithoutEventCounter = 0;
376 :
377 5664 : const char *pszColumn = strchr(pszName, ':');
378 5664 : if (pszColumn)
379 5 : pszName = pszColumn + 1;
380 :
381 11328 : if (poKML->poCurrent_ != nullptr &&
382 5664 : poKML->poCurrent_->getName().compare(pszName) == 0)
383 : {
384 5659 : poKML->nDepth_--;
385 5659 : KMLNode *poTmp = poKML->poCurrent_;
386 : // Split the coordinates
387 5926 : if (poKML->poCurrent_->getName().compare("coordinates") == 0 &&
388 267 : poKML->poCurrent_->numContent() == 1)
389 : {
390 524 : const std::string sData = poKML->poCurrent_->getContent(0);
391 262 : std::size_t nPos = 0;
392 262 : const std::size_t nLength = sData.length();
393 262 : const char *pszData = sData.c_str();
394 : while (true)
395 : {
396 : // Cut off whitespaces
397 33866 : while (nPos < nLength &&
398 33604 : (pszData[nPos] == ' ' || pszData[nPos] == '\n' ||
399 2105 : pszData[nPos] == '\r' || pszData[nPos] == '\t'))
400 31499 : nPos++;
401 :
402 2367 : if (nPos == nLength)
403 262 : break;
404 :
405 2105 : const std::size_t nPosBegin = nPos;
406 :
407 : // Get content
408 81106 : while (nPos < nLength && pszData[nPos] != ' ' &&
409 162061 : pszData[nPos] != '\n' && pszData[nPos] != '\r' &&
410 79087 : pszData[nPos] != '\t')
411 79087 : nPos++;
412 :
413 2105 : if (nPos - nPosBegin > 0)
414 : {
415 4210 : std::string sTmp(pszData + nPosBegin, nPos - nPosBegin);
416 2105 : poKML->poCurrent_->addContent(sTmp);
417 : }
418 2105 : }
419 262 : if (poKML->poCurrent_->numContent() > 1)
420 262 : poKML->poCurrent_->deleteContent(0);
421 : }
422 5397 : else if (poKML->poCurrent_->numContent() == 1)
423 : {
424 9940 : const std::string sData = poKML->poCurrent_->getContent(0);
425 9940 : std::string sDataWithoutNL;
426 4970 : std::size_t nPos = 0;
427 4970 : const std::size_t nLength = sData.length();
428 4970 : const char *pszData = sData.c_str();
429 4970 : std::size_t nLineStartPos = 0;
430 4970 : bool bLineStart = true;
431 :
432 : // Re-assemble multi-line content by removing leading spaces for
433 : // each line. I am not sure why we do that. Should we preserve
434 : // content as such?
435 173819 : while (nPos < nLength)
436 : {
437 168849 : const char ch = pszData[nPos];
438 168849 : if (bLineStart &&
439 11797 : (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'))
440 80456 : nLineStartPos++;
441 88393 : else if (ch == '\n' || ch == '\r')
442 : {
443 1244 : if (!bLineStart)
444 : {
445 : std::string sTmp(pszData + nLineStartPos,
446 1244 : nPos - nLineStartPos);
447 1244 : if (!sDataWithoutNL.empty())
448 1111 : sDataWithoutNL += " ";
449 1244 : sDataWithoutNL += sTmp;
450 1244 : bLineStart = true;
451 : }
452 1244 : nLineStartPos = nPos + 1;
453 : }
454 : else
455 : {
456 87149 : bLineStart = false;
457 : }
458 168849 : nPos++;
459 : }
460 :
461 4970 : if (nLineStartPos > 0)
462 : {
463 1855 : if (nLineStartPos < nPos)
464 : {
465 : std::string sTmp(pszData + nLineStartPos,
466 242 : nPos - nLineStartPos);
467 121 : if (!sDataWithoutNL.empty())
468 121 : sDataWithoutNL += " ";
469 121 : sDataWithoutNL += sTmp;
470 : }
471 :
472 1855 : poKML->poCurrent_->deleteContent(0);
473 1855 : poKML->poCurrent_->addContent(sDataWithoutNL);
474 : }
475 : }
476 :
477 5659 : if (poKML->poCurrent_->getParent() != nullptr)
478 5635 : poKML->poCurrent_ = poKML->poCurrent_->getParent();
479 : else
480 24 : poKML->poCurrent_ = nullptr;
481 :
482 5659 : if (!poKML->isHandled(pszName))
483 : {
484 3553 : CPLDebug("KML", "Not handled: %s", pszName);
485 3553 : delete poTmp;
486 3553 : if (poKML->poCurrent_ == poTmp)
487 0 : poKML->poCurrent_ = nullptr;
488 3553 : if (poKML->poTrunk_ == poTmp)
489 0 : poKML->poTrunk_ = nullptr;
490 : }
491 : else
492 : {
493 2106 : if (poKML->poCurrent_ != nullptr)
494 2082 : poKML->poCurrent_->addChildren(poTmp);
495 : }
496 : }
497 5 : else if (poKML->poCurrent_ != nullptr)
498 : {
499 10 : std::string sNewContent = "</";
500 5 : sNewContent += pszName;
501 5 : sNewContent += ">";
502 5 : if (poKML->poCurrent_->numContent() == 0)
503 0 : poKML->poCurrent_->addContent(sNewContent);
504 : else
505 5 : poKML->poCurrent_->appendContent(sNewContent);
506 : }
507 : }
508 0 : catch (const std::exception &ex)
509 : {
510 0 : CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
511 0 : ex.what());
512 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
513 : }
514 5664 : }
515 :
516 24246 : void XMLCALL KML::dataHandler(void *pUserData, const char *pszData, int nLen)
517 : {
518 24246 : KML *poKML = static_cast<KML *>(pUserData);
519 :
520 24246 : poKML->nWithoutEventCounter = 0;
521 :
522 24246 : if (nLen < 1 || poKML->poCurrent_ == nullptr)
523 0 : return;
524 :
525 24246 : poKML->nDataHandlerCounter++;
526 24246 : if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE)
527 : {
528 0 : CPLError(CE_Failure, CPLE_AppDefined,
529 : "File probably corrupted (million laugh pattern)");
530 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
531 : }
532 :
533 : try
534 : {
535 48492 : std::string sData(pszData, nLen);
536 :
537 24246 : if (poKML->poCurrent_->numContent() == 0)
538 5232 : poKML->poCurrent_->addContent(sData);
539 : else
540 19014 : poKML->poCurrent_->appendContent(sData);
541 : }
542 0 : catch (const std::exception &ex)
543 : {
544 0 : CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
545 0 : ex.what());
546 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
547 : }
548 : }
549 :
550 25 : bool KML::isValid()
551 : {
552 25 : checkValidity();
553 :
554 25 : if (validity == KML_VALIDITY_VALID)
555 24 : CPLDebug("KML", "Valid: 1 Version: %s", sVersion_.c_str());
556 :
557 25 : return validity == KML_VALIDITY_VALID;
558 : }
559 :
560 0 : std::string KML::getError() const
561 : {
562 0 : return sError_;
563 : }
564 :
565 23 : int KML::classifyNodes()
566 : {
567 23 : if (poTrunk_ == nullptr)
568 0 : return false;
569 23 : return poTrunk_->classify(this);
570 : }
571 :
572 19 : void KML::eliminateEmpty()
573 : {
574 19 : if (poTrunk_ != nullptr)
575 19 : poTrunk_->eliminateEmpty(this);
576 19 : }
577 :
578 0 : void KML::print(unsigned short nNum)
579 : {
580 0 : if (poTrunk_ != nullptr)
581 0 : poTrunk_->print(nNum);
582 0 : }
583 :
584 5659 : bool KML::isHandled(std::string const &elem) const
585 : {
586 10176 : return isLeaf(elem) || isFeature(elem) || isFeatureContainer(elem) ||
587 10176 : isContainer(elem) || isRest(elem);
588 : }
589 :
590 0 : bool KML::isLeaf(std::string const & /* elem */) const
591 : {
592 0 : return false;
593 : }
594 :
595 0 : bool KML::isFeature(std::string const & /* elem */) const
596 : {
597 0 : return false;
598 : }
599 :
600 0 : bool KML::isFeatureContainer(std::string const & /* elem */) const
601 : {
602 0 : return false;
603 : }
604 :
605 0 : bool KML::isContainer(std::string const & /* elem */) const
606 : {
607 0 : return false;
608 : }
609 :
610 0 : bool KML::isRest(std::string const & /* elem */) const
611 : {
612 0 : return false;
613 : }
614 :
615 0 : void KML::findLayers(KMLNode * /* poNode */, int /* bKeepEmptyContainers */)
616 : {
617 : // idle
618 0 : }
619 :
620 23 : bool KML::hasOnlyEmpty() const
621 : {
622 23 : return poTrunk_->hasOnlyEmpty();
623 : }
624 :
625 23 : int KML::getNumLayers() const
626 : {
627 23 : return nNumLayers_;
628 : }
629 :
630 875 : bool KML::selectLayer(int nNum)
631 : {
632 875 : if (nNumLayers_ < 1 || nNum >= nNumLayers_)
633 0 : return false;
634 875 : poCurrent_ = papoLayers_[nNum];
635 875 : return true;
636 : }
637 :
638 82 : std::string KML::getCurrentName() const
639 : {
640 82 : std::string tmp;
641 82 : if (poCurrent_ != nullptr)
642 : {
643 82 : tmp = poCurrent_->getNameElement();
644 : }
645 82 : return tmp;
646 : }
647 :
648 202 : Nodetype KML::getCurrentType() const
649 : {
650 202 : if (poCurrent_ != nullptr)
651 202 : return poCurrent_->getType();
652 :
653 0 : return Unknown;
654 : }
655 :
656 74 : int KML::is25D() const
657 : {
658 74 : if (poCurrent_ != nullptr)
659 74 : return poCurrent_->is25D();
660 :
661 0 : return Unknown;
662 : }
663 :
664 61 : int KML::getNumFeatures()
665 : {
666 61 : if (poCurrent_ == nullptr)
667 0 : return -1;
668 :
669 61 : return static_cast<int>(poCurrent_->getNumFeatures());
670 : }
671 :
672 916 : Feature *KML::getFeature(std::size_t nNum, int &nLastAsked, int &nLastCount)
673 : {
674 916 : if (poCurrent_ == nullptr)
675 0 : return nullptr;
676 :
677 916 : return poCurrent_->getFeature(nNum, nLastAsked, nLastCount);
678 : }
679 :
680 116 : void KML::unregisterLayerIfMatchingThisNode(KMLNode *poNode)
681 : {
682 116 : for (int i = 0; i < nNumLayers_;)
683 : {
684 2 : if (papoLayers_[i] == poNode)
685 : {
686 2 : if (i < nNumLayers_ - 1)
687 : {
688 0 : memmove(papoLayers_ + i, papoLayers_ + i + 1,
689 0 : (nNumLayers_ - 1 - i) * sizeof(KMLNode *));
690 : }
691 2 : nNumLayers_--;
692 2 : break;
693 : }
694 0 : i++;
695 : }
696 116 : }
|