LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/kml - kml.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 286 366 78.1 %
Date: 2025-08-01 10:10:57 Functions: 22 31 71.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          25 : KML::~KML()
      33             : {
      34          25 :     if (nullptr != pKMLFile_)
      35          25 :         VSIFCloseL(pKMLFile_);
      36          25 :     CPLFree(papoLayers_);
      37             : 
      38          25 :     delete poTrunk_;
      39          25 : }
      40             : 
      41          25 : bool KML::open(const char *pszFilename)
      42             : {
      43          25 :     if (nullptr != pKMLFile_)
      44           0 :         VSIFCloseL(pKMLFile_);
      45             : 
      46          25 :     pKMLFile_ = VSIFOpenL(pszFilename, "r");
      47          25 :     return pKMLFile_ != nullptr;
      48             : }
      49             : 
      50          24 : bool KML::parse()
      51             : {
      52          24 :     if (nullptr == pKMLFile_)
      53             :     {
      54           0 :         sError_ = "No file given";
      55           0 :         return false;
      56             :     }
      57             : 
      58          24 :     if (poTrunk_ != nullptr)
      59             :     {
      60           0 :         delete poTrunk_;
      61           0 :         poTrunk_ = nullptr;
      62             :     }
      63             : 
      64          24 :     if (poCurrent_ != nullptr)
      65             :     {
      66           0 :         delete poCurrent_;
      67           0 :         poCurrent_ = nullptr;
      68             :     }
      69             : 
      70          24 :     XML_Parser oParser = OGRCreateExpatXMLParser();
      71          24 :     XML_SetUserData(oParser, this);
      72          24 :     XML_SetElementHandler(oParser, startElement, endElement);
      73          24 :     XML_SetCharacterDataHandler(oParser, dataHandler);
      74          24 :     oCurrentParser = oParser;
      75          24 :     nWithoutEventCounter = 0;
      76             : 
      77          24 :     int nDone = 0;
      78          24 :     unsigned nLen = 0;
      79          48 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
      80          24 :     bool bError = false;
      81             : 
      82          45 :     do
      83             :     {
      84          69 :         nDataHandlerCounter = 0;
      85          69 :         nLen = static_cast<unsigned>(
      86          69 :             VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_));
      87          69 :         nDone = nLen < aBuf.size();
      88          69 :         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 at line %d, "
      92             :                      "column %d",
      93             :                      XML_ErrorString(XML_GetErrorCode(oParser)),
      94           1 :                      static_cast<int>(XML_GetCurrentLineNumber(oParser)),
      95           1 :                      static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
      96           1 :             bError = true;
      97           1 :             break;
      98             :         }
      99          68 :         nWithoutEventCounter++;
     100          68 :     } while (!nDone && nLen > 0 && nWithoutEventCounter < 10);
     101             : 
     102          24 :     XML_ParserFree(oParser);
     103          24 :     VSIRewindL(pKMLFile_);
     104             : 
     105          24 :     if (nWithoutEventCounter == 10)
     106             :     {
     107           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     108             :                  "Too much data inside one element. File probably corrupted");
     109           0 :         bError = true;
     110             :     }
     111             : 
     112          24 :     if (bError)
     113             :     {
     114           1 :         if (poCurrent_ != nullptr)
     115             :         {
     116           0 :             while (poCurrent_)
     117             :             {
     118           0 :                 KMLNode *poTemp = poCurrent_->getParent();
     119           0 :                 delete poCurrent_;
     120           0 :                 poCurrent_ = poTemp;
     121             :             }
     122             :             // No need to destroy poTrunk_ : it has been destroyed in
     123             :             // the last iteration
     124             :         }
     125             :         else
     126             :         {
     127             :             // Case of invalid content after closing element matching
     128             :             // first <kml> element
     129           1 :             delete poTrunk_;
     130             :         }
     131           1 :         poTrunk_ = nullptr;
     132           1 :         return false;
     133             :     }
     134             : 
     135          23 :     poCurrent_ = nullptr;
     136          23 :     return true;
     137             : }
     138             : 
     139          25 : void KML::checkValidity()
     140             : {
     141          25 :     if (poTrunk_ != nullptr)
     142             :     {
     143           0 :         delete poTrunk_;
     144           0 :         poTrunk_ = nullptr;
     145             :     }
     146             : 
     147          25 :     if (poCurrent_ != nullptr)
     148             :     {
     149           0 :         delete poCurrent_;
     150           0 :         poCurrent_ = nullptr;
     151             :     }
     152             : 
     153          25 :     if (pKMLFile_ == nullptr)
     154             :     {
     155           0 :         sError_ = "No file given";
     156           1 :         return;
     157             :     }
     158             : 
     159          25 :     XML_Parser oParser = OGRCreateExpatXMLParser();
     160          25 :     XML_SetUserData(oParser, this);
     161          25 :     XML_SetElementHandler(oParser, startElementValidate, nullptr);
     162          25 :     XML_SetCharacterDataHandler(oParser, dataHandlerValidate);
     163          25 :     int nCount = 0;
     164             : 
     165          25 :     oCurrentParser = oParser;
     166             : 
     167          25 :     int nDone = 0;
     168          25 :     unsigned nLen = 0;
     169          50 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
     170             : 
     171             :     // Parses the file until we find the first element.
     172           0 :     do
     173             :     {
     174          25 :         nDataHandlerCounter = 0;
     175          25 :         nLen = static_cast<unsigned>(
     176          25 :             VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_));
     177          25 :         nDone = nLen < aBuf.size();
     178          25 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
     179             :         {
     180           1 :             if (nLen <= PARSER_BUF_SIZE - 1)
     181           1 :                 aBuf[nLen] = 0;
     182             :             else
     183           0 :                 aBuf[PARSER_BUF_SIZE - 1] = 0;
     184           2 :             if (strstr(aBuf.data(), "<?xml") &&
     185           1 :                 (strstr(aBuf.data(), "<kml") ||
     186           0 :                  (strstr(aBuf.data(), "<Document") &&
     187           0 :                   strstr(aBuf.data(), "/kml/2."))))
     188             :             {
     189           1 :                 CPLError(
     190             :                     CE_Failure, CPLE_AppDefined,
     191             :                     "XML parsing of KML file failed : %s at line %d, column %d",
     192             :                     XML_ErrorString(XML_GetErrorCode(oParser)),
     193           1 :                     static_cast<int>(XML_GetCurrentLineNumber(oParser)),
     194           1 :                     static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
     195             :             }
     196             : 
     197           1 :             validity = KML_VALIDITY_INVALID;
     198           1 :             XML_ParserFree(oParser);
     199           1 :             VSIRewindL(pKMLFile_);
     200           1 :             return;
     201             :         }
     202             : 
     203          24 :         nCount++;
     204             :         /* After reading 50 * PARSER_BUF_SIZE bytes, and not finding whether the file */
     205             :         /* is KML or not, we give up and fail silently */
     206          24 :     } while (!nDone && nLen > 0 && validity == KML_VALIDITY_UNKNOWN &&
     207             :              nCount < 50);
     208             : 
     209          24 :     XML_ParserFree(oParser);
     210          24 :     VSIRewindL(pKMLFile_);
     211          24 :     poCurrent_ = nullptr;
     212             : }
     213             : 
     214        5664 : void XMLCALL KML::startElement(void *pUserData, const char *pszName,
     215             :                                const char **ppszAttr)
     216             : {
     217        5664 :     KML *poKML = static_cast<KML *>(pUserData);
     218             :     try
     219             :     {
     220        5664 :         poKML->nWithoutEventCounter = 0;
     221             : 
     222        5664 :         const char *pszColumn = strchr(pszName, ':');
     223        5664 :         if (pszColumn)
     224           5 :             pszName = pszColumn + 1;
     225             : 
     226       11304 :         if (poKML->poTrunk_ == nullptr ||
     227       11280 :             (poKML->poCurrent_ != nullptr &&
     228        5640 :              poKML->poCurrent_->getName().compare("description") != 0))
     229             :         {
     230        5659 :             if (poKML->nDepth_ == 1024)
     231             :             {
     232           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     233             :                          "Too big depth level (%d) while parsing KML.",
     234             :                          poKML->nDepth_);
     235           0 :                 XML_StopParser(poKML->oCurrentParser, XML_FALSE);
     236           0 :                 return;
     237             :             }
     238             : 
     239        5659 :             KMLNode *poMynew = new KMLNode();
     240        5659 :             poMynew->setName(pszName);
     241        5659 :             poMynew->setLevel(poKML->nDepth_);
     242             : 
     243        7095 :             for (int i = 0; ppszAttr[i]; i += 2)
     244             :             {
     245        1436 :                 Attribute *poAtt = new Attribute();
     246        1436 :                 poAtt->sName = ppszAttr[i];
     247        1436 :                 poAtt->sValue = ppszAttr[i + 1];
     248        1436 :                 poMynew->addAttribute(poAtt);
     249             :             }
     250             : 
     251        5659 :             if (poKML->poTrunk_ == nullptr)
     252          24 :                 poKML->poTrunk_ = poMynew;
     253        5659 :             if (poKML->poCurrent_ != nullptr)
     254        5635 :                 poMynew->setParent(poKML->poCurrent_);
     255        5659 :             poKML->poCurrent_ = poMynew;
     256             : 
     257        5659 :             poKML->nDepth_++;
     258             :         }
     259           5 :         else if (poKML->poCurrent_ != nullptr)
     260             :         {
     261          10 :             std::string sNewContent = "<";
     262           5 :             sNewContent += pszName;
     263           6 :             for (int i = 0; ppszAttr[i]; i += 2)
     264             :             {
     265           1 :                 sNewContent += " ";
     266           1 :                 sNewContent += ppszAttr[i];
     267           1 :                 sNewContent += "=\"";
     268           1 :                 sNewContent += ppszAttr[i + 1];
     269           1 :                 sNewContent += "\"";
     270             :             }
     271           5 :             sNewContent += ">";
     272           5 :             if (poKML->poCurrent_->numContent() == 0)
     273           0 :                 poKML->poCurrent_->addContent(sNewContent);
     274             :             else
     275           5 :                 poKML->poCurrent_->appendContent(sNewContent);
     276             :         }
     277             :     }
     278           0 :     catch (const std::exception &ex)
     279             :     {
     280           0 :         CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
     281           0 :                  ex.what());
     282           0 :         XML_StopParser(poKML->oCurrentParser, XML_FALSE);
     283             :     }
     284             : }
     285             : 
     286        2053 : void XMLCALL KML::startElementValidate(void *pUserData, const char *pszName,
     287             :                                        const char **ppszAttr)
     288             : {
     289        2053 :     KML *poKML = static_cast<KML *>(pUserData);
     290             : 
     291        2053 :     if (poKML->validity != KML_VALIDITY_UNKNOWN)
     292        2028 :         return;
     293             : 
     294          25 :     poKML->validity = KML_VALIDITY_INVALID;
     295             : 
     296          25 :     const char *pszColumn = strchr(pszName, ':');
     297          25 :     if (pszColumn)
     298           1 :         pszName = pszColumn + 1;
     299             : 
     300          25 :     if (strcmp(pszName, "kml") == 0 || strcmp(pszName, "Document") == 0)
     301             :     {
     302             :         // Check all Attributes
     303          53 :         for (int i = 0; ppszAttr[i]; i += 2)
     304             :         {
     305             :             // Find the namespace and determine the KML version
     306          28 :             if (strcmp(ppszAttr[i], "xmlns") == 0)
     307             :             {
     308             :                 // Is it KML 2.2?
     309          23 :                 if ((strcmp(ppszAttr[i + 1],
     310          23 :                             "http://earth.google.com/kml/2.2") == 0) ||
     311          23 :                     (strcmp(ppszAttr[i + 1],
     312             :                             "http://www.opengis.net/kml/2.2") == 0))
     313             :                 {
     314           9 :                     poKML->validity = KML_VALIDITY_VALID;
     315           9 :                     poKML->sVersion_ = "2.2";
     316             :                 }
     317          14 :                 else if (strcmp(ppszAttr[i + 1],
     318             :                                 "http://earth.google.com/kml/2.1") == 0)
     319             :                 {
     320          14 :                     poKML->validity = KML_VALIDITY_VALID;
     321          14 :                     poKML->sVersion_ = "2.1";
     322             :                 }
     323           0 :                 else if (strcmp(ppszAttr[i + 1],
     324             :                                 "http://earth.google.com/kml/2.0") == 0)
     325             :                 {
     326           0 :                     poKML->validity = KML_VALIDITY_VALID;
     327           0 :                     poKML->sVersion_ = "2.0";
     328             :                 }
     329             :                 else
     330             :                 {
     331           0 :                     CPLDebug("KML",
     332             :                              "Unhandled xmlns value : %s. Going on though...",
     333           0 :                              ppszAttr[i]);
     334           0 :                     poKML->validity = KML_VALIDITY_VALID;
     335           0 :                     poKML->sVersion_ = "?";
     336             :                 }
     337             :             }
     338             :         }
     339             : 
     340          25 :         if (poKML->validity == KML_VALIDITY_INVALID)
     341             :         {
     342           2 :             CPLDebug("KML", "Did not find xmlns attribute in <kml> element. "
     343             :                             "Going on though...");
     344           2 :             poKML->validity = KML_VALIDITY_VALID;
     345           2 :             poKML->sVersion_ = "?";
     346             :         }
     347             :     }
     348             : }
     349             : 
     350        7634 : void XMLCALL KML::dataHandlerValidate(void *pUserData,
     351             :                                       const char * /* pszData */,
     352             :                                       int /* nLen */)
     353             : {
     354        7634 :     KML *poKML = static_cast<KML *>(pUserData);
     355             : 
     356        7634 :     poKML->nDataHandlerCounter++;
     357        7634 :     if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE)
     358             :     {
     359           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     360             :                  "File probably corrupted (million laugh pattern)");
     361           0 :         XML_StopParser(poKML->oCurrentParser, XML_FALSE);
     362             :     }
     363        7634 : }
     364             : 
     365        5664 : void XMLCALL KML::endElement(void *pUserData, const char *pszName)
     366             : {
     367        5664 :     KML *poKML = static_cast<KML *>(pUserData);
     368             : 
     369             :     try
     370             :     {
     371        5664 :         poKML->nWithoutEventCounter = 0;
     372             : 
     373        5664 :         const char *pszColumn = strchr(pszName, ':');
     374        5664 :         if (pszColumn)
     375           5 :             pszName = pszColumn + 1;
     376             : 
     377       11328 :         if (poKML->poCurrent_ != nullptr &&
     378        5664 :             poKML->poCurrent_->getName().compare(pszName) == 0)
     379             :         {
     380        5659 :             poKML->nDepth_--;
     381        5659 :             KMLNode *poTmp = poKML->poCurrent_;
     382             :             // Split the coordinates
     383        5926 :             if (poKML->poCurrent_->getName().compare("coordinates") == 0 &&
     384         267 :                 poKML->poCurrent_->numContent() == 1)
     385             :             {
     386         524 :                 const std::string sData = poKML->poCurrent_->getContent(0);
     387         262 :                 std::size_t nPos = 0;
     388         262 :                 const std::size_t nLength = sData.length();
     389         262 :                 const char *pszData = sData.c_str();
     390             :                 while (true)
     391             :                 {
     392             :                     // Cut off whitespaces
     393       33866 :                     while (nPos < nLength &&
     394       33604 :                            (pszData[nPos] == ' ' || pszData[nPos] == '\n' ||
     395        2105 :                             pszData[nPos] == '\r' || pszData[nPos] == '\t'))
     396       31499 :                         nPos++;
     397             : 
     398        2367 :                     if (nPos == nLength)
     399         262 :                         break;
     400             : 
     401        2105 :                     const std::size_t nPosBegin = nPos;
     402        2105 :                     size_t nContentSize = 0;
     403             : 
     404             :                     // Get content
     405       81106 :                     while (nPos < nLength && pszData[nPos] != ' ' &&
     406      162061 :                            pszData[nPos] != '\n' && pszData[nPos] != '\r' &&
     407       79087 :                            pszData[nPos] != '\t')
     408             :                     {
     409       79087 :                         nContentSize++;
     410       79087 :                         nPos++;
     411             :                     }
     412             : 
     413        2105 :                     if (nContentSize > 0)
     414             :                     {
     415        4210 :                         std::string sTmp(pszData + nPosBegin, nContentSize);
     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 += '\n';
     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 += '\n';
     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 : }

Generated by: LCOV version 1.14