LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/kml - kml.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 284 352 80.7 %
Date: 2026-10-10 13:16:24 Functions: 22 25 88.0 %

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

Generated by: LCOV version 1.14