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