LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/libkml - ogrlibkmldatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 874 1064 82.1 %
Date: 2025-01-18 12:42:00 Functions: 41 43 95.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  KML Translator
       4             :  * Purpose:  Implements OGRLIBKMLDriver
       5             :  * Author:   Brian Case, rush at winkey dot org
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010, Brian Case
       9             :  * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  *****************************************************************************/
      13             : 
      14             : #include "libkml_headers.h"
      15             : 
      16             : #include <string>
      17             : #include "ogr_libkml.h"
      18             : #include "ogrlibkmlstyle.h"
      19             : #include "ogr_p.h"
      20             : #include "cpl_vsi_error.h"
      21             : 
      22             : using kmlbase::Attributes;
      23             : using kmldom::ContainerPtr;
      24             : using kmldom::DocumentPtr;
      25             : using kmldom::ElementPtr;
      26             : using kmldom::FeaturePtr;
      27             : using kmldom::FolderPtr;
      28             : using kmldom::KmlFactory;
      29             : using kmldom::KmlPtr;
      30             : using kmldom::LinkPtr;
      31             : using kmldom::LinkSnippetPtr;
      32             : using kmldom::NetworkLinkControlPtr;
      33             : using kmldom::NetworkLinkPtr;
      34             : using kmldom::SchemaPtr;
      35             : using kmldom::SnippetPtr;
      36             : using kmldom::StyleSelectorPtr;
      37             : using kmlengine::KmzFile;
      38             : 
      39             : /************************************************************************/
      40             : /*                           OGRLIBKMLParse()                           */
      41             : /************************************************************************/
      42             : 
      43         138 : static ElementPtr OGRLIBKMLParse(const std::string &oKml, std::string *posError)
      44             : {
      45             :     try
      46             :     {
      47             :         // To allow reading files using an explicit namespace prefix like
      48             :         // <kml:kml> we need to use ParseNS (see #6981). But if we use ParseNS,
      49             :         // we have issues reading gx: elements. So use ParseNS only when we have
      50             :         // errors with Parse. This is not completely satisfactory...
      51         276 :         ElementPtr element = kmldom::Parse(oKml, posError);
      52         138 :         if (!element)
      53           1 :             element = kmldom::ParseNS(oKml, posError);
      54         138 :         return element;
      55             :     }
      56           0 :     catch (const std::exception &ex)
      57             :     {
      58           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      59           0 :                  "LIBKML: libstdc++ exception during Parse() : %s", ex.what());
      60           0 :         return nullptr;
      61             :     }
      62             : }
      63             : 
      64             : /******************************************************************************
      65             :  OGRLIBKMLDataSource Constructor
      66             : 
      67             :  Args:          none
      68             : 
      69             :  Returns:       nothing
      70             : 
      71             : ******************************************************************************/
      72             : 
      73         391 : OGRLIBKMLDataSource::OGRLIBKMLDataSource(KmlFactory *poKmlFactory)
      74             :     : papoLayers(nullptr), nLayers(0), nAllocated(0), bUpdate(false),
      75             :       bUpdated(false), m_papszOptions(nullptr), m_isKml(false),
      76             :       m_poKmlDSKml(nullptr), m_poKmlDSContainer(nullptr),
      77             :       m_poKmlUpdate(nullptr), m_isKmz(false), m_poKmlDocKml(nullptr),
      78             :       m_poKmlDocKmlRoot(nullptr), m_poKmlStyleKml(nullptr), m_isDir(false),
      79         391 :       m_poKmlFactory(poKmlFactory)
      80             : {
      81         391 : }
      82             : 
      83             : /************************************************************************/
      84             : /*                       OGRLIBKMLPreProcessInput()                     */
      85             : /************************************************************************/
      86             : 
      87             : // Substitute <snippet> by deprecated <Snippet> since libkml currently
      88             : // only supports Snippet but ogckml22.xsd has deprecated it in favor of snippet.
      89         119 : static void OGRLIBKMLPreProcessInput(std::string &oKml)
      90             : {
      91         119 :     size_t nPos = 0;
      92             :     while (true)
      93             :     {
      94         120 :         nPos = oKml.find("<snippet>", nPos);
      95         120 :         if (nPos == std::string::npos)
      96             :         {
      97         119 :             break;
      98             :         }
      99           1 :         oKml[nPos + 1] = 'S';
     100           1 :         nPos = oKml.find("</snippet>", nPos);
     101           1 :         if (nPos == std::string::npos)
     102             :         {
     103           0 :             break;
     104             :         }
     105           1 :         oKml[nPos + 2] = 'S';
     106             :     }
     107             : 
     108             :     // Workaround Windows specific issue with libkml (at least the version
     109             :     // used by OSGeo4W at time of writing), where tabulations as
     110             :     // coordinate separators aren't properly handled
     111             :     //(see https://trac.osgeo.org/gdal/ticket/7231)
     112             :     // Another Windows specific issue is that if the content of
     113             :     // <coordinates> is non-empty and does not contain any digit,
     114             :     // libkml hangs (see https://trac.osgeo.org/gdal/ticket/7232)
     115         119 :     nPos = 0;
     116             :     while (true)
     117             :     {
     118         482 :         nPos = oKml.find("<coordinates>", nPos);
     119         482 :         if (nPos == std::string::npos)
     120             :         {
     121         119 :             break;
     122             :         }
     123         363 :         size_t nPosEnd = oKml.find("</coordinates>", nPos);
     124         363 :         if (nPosEnd == std::string::npos)
     125             :         {
     126           0 :             break;
     127             :         }
     128         363 :         nPos += strlen("<coordinates>");
     129         363 :         size_t nPosAfterCoordinates = nPos;
     130         363 :         bool bDigitFound = false;
     131      131550 :         for (; nPos < nPosEnd; nPos++)
     132             :         {
     133      131187 :             char ch = oKml[nPos];
     134      131187 :             if (ch >= '0' && ch <= '9')
     135       78607 :                 bDigitFound = true;
     136       52580 :             else if (ch == '\t' || ch == '\n')
     137        2277 :                 oKml[nPos] = ' ';
     138             :         }
     139         363 :         if (!bDigitFound)
     140             :         {
     141             :             oKml.replace(nPosAfterCoordinates,
     142           4 :                          nPosEnd + strlen("</coordinates>") -
     143             :                              nPosAfterCoordinates,
     144           4 :                          "</coordinates>");
     145           4 :             nPos = nPosAfterCoordinates + strlen("</coordinates>");
     146             :         }
     147         363 :     }
     148             : 
     149             :     // Some non conformant file may contain
     150             :     // MultiPolygon/MultiLineString/MultiPoint See
     151             :     // https://github.com/OSGeo/gdal/issues/4031 Replace them by MultiGeometry.
     152         119 :     nPos = 0;
     153             :     while (true)
     154             :     {
     155         122 :         const char *pszStartTag = "<MultiPolygon>";
     156         122 :         const char *pszEndTag = "";
     157         122 :         CPL_IGNORE_RET_VAL(pszEndTag);  // Make CSA happy
     158         122 :         auto nNewPos = oKml.find(pszStartTag, nPos);
     159         122 :         if (nNewPos != std::string::npos)
     160             :         {
     161           1 :             pszEndTag = "</MultiPolygon>";
     162             :         }
     163             :         else
     164             :         {
     165         121 :             pszStartTag = "<MultiLineString>";
     166         121 :             nNewPos = oKml.find(pszStartTag, nPos);
     167         121 :             if (nNewPos != std::string::npos)
     168             :             {
     169           1 :                 pszEndTag = "</MultiLineString>";
     170             :             }
     171             :             else
     172             :             {
     173         120 :                 pszStartTag = "<MultiPoint>";
     174         120 :                 nNewPos = oKml.find(pszStartTag, nPos);
     175         120 :                 if (nNewPos != std::string::npos)
     176           1 :                     pszEndTag = "</MultiPoint>";
     177             :                 else
     178         119 :                     break;
     179             :             }
     180             :         }
     181           3 :         nPos = nNewPos;
     182           3 :         oKml.replace(nPos, strlen(pszStartTag), "<MultiGeometry>");
     183           3 :         nPos = oKml.find(pszEndTag, nPos);
     184           3 :         if (nPos == std::string::npos)
     185           0 :             break;
     186           3 :         oKml.replace(nPos, strlen(pszEndTag), "</MultiGeometry>");
     187           3 :     }
     188         119 : }
     189             : 
     190             : /************************************************************************/
     191             : /*                       OGRLIBKMLRemoveSpaces()                        */
     192             : /************************************************************************/
     193             : 
     194         456 : static void OGRLIBKMLRemoveSpaces(std::string &osKml,
     195             :                                   const std::string &osNeedle)
     196             : {
     197         456 :     size_t nPos = 0;
     198        1368 :     const std::string osLtNeedle(std::string("<").append(osNeedle));
     199         912 :     std::string osTmp;
     200         912 :     std::string osRet;
     201             :     while (true)
     202             :     {
     203         545 :         auto nPosNew = osKml.find(osLtNeedle, nPos);
     204         545 :         if (nPosNew == std::string::npos)
     205             :         {
     206         456 :             osRet.append(osKml, nPos);
     207         456 :             break;
     208             :         }
     209          89 :         const size_t nPosOri = nPosNew;
     210          89 :         nPosNew = osKml.find(">\n", nPosNew);
     211          89 :         if (nPosNew == std::string::npos || nPosNew + 2 == osKml.size())
     212             :         {
     213           0 :             osRet.append(osKml, nPos);
     214           0 :             break;
     215             :         }
     216             :         // Skip \n character
     217          89 :         osRet.append(osKml, nPos, nPosNew - nPos + 1);
     218          89 :         nPos = nPosNew + 2;
     219             : 
     220             :         // Remove leading spaces of "    </{osNeedle}>"
     221          89 :         osTmp.clear();
     222        1077 :         for (size_t nPosTmp = nPosOri - 1; osKml[nPosTmp] == ' '; nPosTmp--)
     223             :         {
     224         988 :             osTmp += ' ';
     225             :         }
     226          89 :         osTmp += "</";
     227          89 :         osTmp += osNeedle;
     228          89 :         osTmp += '>';
     229          89 :         nPosNew = osKml.find(osTmp, nPos);
     230          89 :         if (nPosNew != std::string::npos)
     231             :         {
     232          89 :             osRet.append(osKml, nPos, nPosNew - nPos);
     233          89 :             osRet += "</";
     234          89 :             osRet += osNeedle;
     235          89 :             osRet += '>';
     236          89 :             nPos = nPosNew + osTmp.size();
     237             :         }
     238             :         else
     239             :         {
     240           0 :             osRet.append(osKml, nPos);
     241           0 :             break;
     242             :         }
     243          89 :     }
     244         456 :     osKml = std::move(osRet);
     245         456 : }
     246             : 
     247             : /************************************************************************/
     248             : /*                      OGRLIBKMLPostProcessOutput()                    */
     249             : /************************************************************************/
     250             : 
     251             : // Substitute deprecated <Snippet> by <snippet> since libkml currently
     252             : // only supports Snippet but ogckml22.xsd has deprecated it in favor of snippet.
     253         152 : static void OGRLIBKMLPostProcessOutput(std::string &oKml)
     254             : {
     255             :     // Manually add <?xml> node since libkml does not produce it currently
     256             :     // and this is useful in some circumstances (#5407).
     257         152 :     if (!(oKml.size() >= 2 && oKml[0] == '<' && oKml[1] == '?'))
     258         152 :         oKml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + oKml;
     259             : 
     260         152 :     size_t nPos = 0;
     261             :     while (true)
     262             :     {
     263         155 :         nPos = oKml.find("<Snippet>", nPos);
     264         155 :         if (nPos == std::string::npos)
     265             :         {
     266         152 :             break;
     267             :         }
     268           3 :         oKml[nPos + 1] = 's';
     269           3 :         nPos = oKml.find("</Snippet>", nPos);
     270           3 :         if (nPos == std::string::npos)
     271             :         {
     272           0 :             break;
     273             :         }
     274           3 :         oKml[nPos + 2] = 's';
     275             :     }
     276             : 
     277             :     // Fix indentation problems.
     278         152 :     OGRLIBKMLRemoveSpaces(oKml, "snippet");
     279         152 :     OGRLIBKMLRemoveSpaces(oKml, "linkSnippet");
     280         152 :     OGRLIBKMLRemoveSpaces(oKml, "SimpleData");
     281         152 : }
     282             : 
     283             : /******************************************************************************
     284             :  Method to write a single file ds .kml at ds destroy.
     285             : 
     286             :  Args:          none
     287             : 
     288             :  Returns:       nothing
     289             : 
     290             : ******************************************************************************/
     291             : 
     292          61 : bool OGRLIBKMLDataSource::WriteKml()
     293             : {
     294         122 :     std::string oKmlFilename = GetDescription();
     295             : 
     296          61 :     if (m_poKmlDSContainer && m_poKmlDSContainer->IsA(kmldom::Type_Document))
     297             :     {
     298         120 :         DocumentPtr poKmlDocument = AsDocument(m_poKmlDSContainer);
     299             : 
     300          60 :         ParseDocumentOptions(m_poKmlDSKml, poKmlDocument);
     301             : 
     302         124 :         for (int iLayer = 0; iLayer < nLayers; iLayer++)
     303             :         {
     304          64 :             SchemaPtr poKmlSchema = nullptr;
     305             : 
     306          64 :             if ((poKmlSchema = papoLayers[iLayer]->GetKmlSchema()))
     307             :             {
     308             :                 const size_t nKmlSchemas =
     309           4 :                     poKmlDocument->get_schema_array_size();
     310           8 :                 SchemaPtr poKmlSchema2 = nullptr;
     311             : 
     312           4 :                 for (size_t iKmlSchema = 0; iKmlSchema < nKmlSchemas;
     313             :                      iKmlSchema++)
     314             :                 {
     315             :                     poKmlSchema2 =
     316           1 :                         poKmlDocument->get_schema_array_at(iKmlSchema);
     317           1 :                     if (poKmlSchema2 == poKmlSchema)
     318           1 :                         break;
     319             :                 }
     320             : 
     321           4 :                 if (poKmlSchema2 != poKmlSchema)
     322           3 :                     poKmlDocument->add_schema(poKmlSchema);
     323             :             }
     324             : 
     325          64 :             papoLayers[iLayer]->Finalize(poKmlDocument);
     326             :         }
     327             :     }
     328             :     else
     329             :     {
     330           1 :         ParseDocumentOptions(m_poKmlDSKml, nullptr);
     331             :     }
     332             : 
     333         122 :     std::string oKmlOut;
     334          61 :     oKmlOut = kmldom::SerializePretty(m_poKmlDSKml);
     335          61 :     OGRLIBKMLPostProcessOutput(oKmlOut);
     336             : 
     337          61 :     bool bRet = true;
     338          61 :     if (!oKmlOut.empty())
     339             :     {
     340          61 :         VSILFILE *fp = VSIFOpenExL(oKmlFilename.c_str(), "wb", true);
     341          61 :         if (fp == nullptr)
     342             :         {
     343           0 :             CPLError(CE_Failure, CPLE_FileIO, "Error writing %s: %s",
     344             :                      oKmlFilename.c_str(), VSIGetLastErrorMsg());
     345           0 :             return false;
     346             :         }
     347             : 
     348          61 :         if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
     349           0 :             bRet = false;
     350          61 :         if (VSIFCloseL(fp) != 0)
     351           0 :             bRet = false;
     352             :     }
     353          61 :     return bRet;
     354             : }
     355             : 
     356             : /******************************************************************************/
     357             : /*                      OGRLIBKMLCreateOGCKml22()                             */
     358             : /******************************************************************************/
     359             : 
     360         148 : static KmlPtr OGRLIBKMLCreateOGCKml22(KmlFactory *poFactory,
     361             :                                       char **papszOptions = nullptr)
     362             : {
     363         148 :     const char *pszAuthorName = CSLFetchNameValue(papszOptions, "AUTHOR_NAME");
     364         148 :     const char *pszAuthorURI = CSLFetchNameValue(papszOptions, "AUTHOR_URI");
     365             :     const char *pszAuthorEmail =
     366         148 :         CSLFetchNameValue(papszOptions, "AUTHOR_EMAIL");
     367         148 :     const char *pszLink = CSLFetchNameValue(papszOptions, "LINK");
     368         147 :     const bool bWithAtom = pszAuthorName != nullptr ||
     369         147 :                            pszAuthorURI != nullptr ||
     370         295 :                            pszAuthorEmail != nullptr || pszLink != nullptr;
     371             : 
     372         148 :     KmlPtr kml = poFactory->CreateKml();
     373         148 :     if (bWithAtom)
     374             :     {
     375           2 :         const char *kAttrs[] = {"xmlns", "http://www.opengis.net/kml/2.2",
     376             :                                 "xmlns:atom", "http://www.w3.org/2005/Atom",
     377             :                                 nullptr};
     378           2 :         kml->AddUnknownAttributes(Attributes::Create(kAttrs));
     379             :     }
     380             :     else
     381             :     {
     382         146 :         const char *kAttrs[] = {"xmlns", "http://www.opengis.net/kml/2.2",
     383             :                                 nullptr};
     384         146 :         kml->AddUnknownAttributes(Attributes::Create(kAttrs));
     385             :     }
     386         148 :     return kml;
     387             : }
     388             : 
     389             : /******************************************************************************
     390             :  Method to write a ds .kmz at ds destroy.
     391             : 
     392             :  Args:          none
     393             : 
     394             :  Returns:       nothing
     395             : 
     396             : ******************************************************************************/
     397             : 
     398           8 : bool OGRLIBKMLDataSource::WriteKmz()
     399             : {
     400          16 :     std::string osTmpFilename;
     401          15 :     if (!VSISupportsRandomWrite(GetDescription(), false) ||
     402           7 :         EQUAL(CPLGetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", ""),
     403             :               "FORCED"))
     404             :     {
     405           4 :         osTmpFilename = CPLGenerateTempFilenameSafe(
     406           6 :             CPLGetBasenameSafe(GetDescription()).c_str());
     407             :     }
     408             : 
     409          10 :     void *hZIP = CPLCreateZip(osTmpFilename.empty() ? GetDescription()
     410           2 :                                                     : osTmpFilename.c_str(),
     411             :                               nullptr);
     412             : 
     413           8 :     if (!hZIP)
     414             :     {
     415           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess, "Error creating %s: %s",
     416           0 :                  GetDescription(), VSIGetLastErrorMsg());
     417           0 :         return false;
     418             :     }
     419             : 
     420           8 :     bool bRet = true;
     421             : 
     422             :     /***** write out the doc.kml ****/
     423           8 :     const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
     424             : 
     425           8 :     if (CPLTestBool(pszUseDocKml) && (m_poKmlDocKml || m_poKmlUpdate))
     426             :     {
     427             :         // If we do not have the doc.kmlroot
     428             :         // make it and add the container.
     429           7 :         if (!m_poKmlDocKmlRoot)
     430             :         {
     431             :             m_poKmlDocKmlRoot =
     432           7 :                 OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions);
     433             : 
     434           7 :             auto kml = AsKml(m_poKmlDocKmlRoot);
     435           7 :             if (m_poKmlDocKml)
     436             :             {
     437           6 :                 if (kml)
     438             :                 {
     439           6 :                     kml->set_feature(m_poKmlDocKml);
     440             :                 }
     441             :             }
     442             : 
     443           7 :             ParseDocumentOptions(std::move(kml), AsDocument(m_poKmlDocKml));
     444             :         }
     445             : 
     446          14 :         std::string oKmlOut = kmldom::SerializePretty(m_poKmlDocKmlRoot);
     447           7 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     448             : 
     449          14 :         if (CPLCreateFileInZip(hZIP, "doc.kml", nullptr) != CE_None ||
     450           7 :             CPLWriteFileInZip(hZIP, oKmlOut.data(),
     451           7 :                               static_cast<int>(oKmlOut.size())) != CE_None)
     452             :         {
     453           0 :             bRet = false;
     454           0 :             CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
     455           0 :                      "doc.kml", GetDescription());
     456             :         }
     457           7 :         CPLCloseFileInZip(hZIP);
     458             :     }
     459             : 
     460             :     /***** loop though the layers and write them *****/
     461          17 :     for (int iLayer = 0; iLayer < nLayers && !m_poKmlUpdate; iLayer++)
     462             :     {
     463          18 :         ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer();
     464             : 
     465           9 :         if (poKmlContainer->IsA(kmldom::Type_Document))
     466             :         {
     467          18 :             DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
     468           9 :             SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema();
     469             : 
     470          13 :             if (!poKmlDocument->get_schema_array_size() && poKmlSchema &&
     471           4 :                 poKmlSchema->get_simplefield_array_size())
     472             :             {
     473           4 :                 poKmlDocument->add_schema(std::move(poKmlSchema));
     474             :             }
     475             : 
     476           9 :             papoLayers[iLayer]->Finalize(std::move(poKmlDocument));
     477             :         }
     478             : 
     479             :         // If we do not have the layers root
     480             :         // make it and add the container.
     481          18 :         KmlPtr poKmlKml = nullptr;
     482             : 
     483           9 :         if (!(poKmlKml = AsKml(papoLayers[iLayer]->GetKmlLayerRoot())))
     484             :         {
     485           9 :             poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
     486             : 
     487           9 :             poKmlKml->set_feature(poKmlContainer);
     488             :         }
     489             : 
     490          18 :         std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
     491           9 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     492             : 
     493           9 :         if (iLayer == 0 && CPLTestBool(pszUseDocKml))
     494             :         {
     495           5 :             CPL_IGNORE_RET_VAL(CPLCreateFileInZip(hZIP, "layers/", nullptr));
     496             :         }
     497             : 
     498           9 :         const char *pszLayerFileName = nullptr;
     499           9 :         if (CPLTestBool(pszUseDocKml))
     500             :             pszLayerFileName =
     501           8 :                 CPLSPrintf("layers/%s", papoLayers[iLayer]->GetFileName());
     502             :         else
     503           1 :             pszLayerFileName = papoLayers[iLayer]->GetFileName();
     504             : 
     505          18 :         if (CPLCreateFileInZip(hZIP, pszLayerFileName, nullptr) != CE_None ||
     506           9 :             CPLWriteFileInZip(hZIP, oKmlOut.data(),
     507           9 :                               static_cast<int>(oKmlOut.size())) != CE_None)
     508           0 :             CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
     509           0 :                      papoLayers[iLayer]->GetFileName(), GetDescription());
     510           9 :         CPLCloseFileInZip(hZIP);
     511             :     }
     512             : 
     513             :     /***** write the style table *****/
     514           8 :     if (m_poKmlStyleKml)
     515             :     {
     516           2 :         KmlPtr poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
     517             : 
     518           1 :         poKmlKml->set_feature(m_poKmlStyleKml);
     519           2 :         std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
     520           1 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     521             : 
     522           1 :         if (CPLCreateFileInZip(hZIP, "style/", nullptr) != CE_None ||
     523           2 :             CPLCreateFileInZip(hZIP, "style/style.kml", nullptr) != CE_None ||
     524           1 :             CPLWriteFileInZip(hZIP, oKmlOut.data(),
     525           1 :                               static_cast<int>(oKmlOut.size())) != CE_None)
     526             :         {
     527           0 :             bRet = false;
     528           0 :             CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
     529           0 :                      "style/style.kml", GetDescription());
     530             :         }
     531           1 :         CPLCloseFileInZip(hZIP);
     532             :     }
     533             : 
     534           8 :     CPLCloseZip(hZIP);
     535             : 
     536           8 :     if (!osTmpFilename.empty())
     537             :     {
     538           2 :         if (bRet)
     539             :         {
     540           2 :             bRet = CPLCopyFile(GetDescription(), osTmpFilename.c_str()) == 0;
     541           2 :             if (!bRet)
     542           1 :                 CPLError(CE_Failure, CPLE_FileIO,
     543           1 :                          "Cannot copy temporary file to %s", GetDescription());
     544             :         }
     545           2 :         VSIUnlink(osTmpFilename.c_str());
     546             :     }
     547             : 
     548           8 :     return bRet;
     549             : }
     550             : 
     551             : /******************************************************************************
     552             :  Method to write a dir ds at ds destroy.
     553             : 
     554             :  Args:          none
     555             : 
     556             :  Returns:       nothing
     557             : 
     558             : ******************************************************************************/
     559             : 
     560          37 : bool OGRLIBKMLDataSource::WriteDir()
     561             : {
     562             :     /***** write out the doc.kml ****/
     563          37 :     const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
     564             : 
     565          37 :     bool bRet = true;
     566          37 :     if (CPLTestBool(pszUseDocKml) && (m_poKmlDocKml || m_poKmlUpdate))
     567             :     {
     568             :         // If we don't have the doc.kml root
     569             :         // make it and add the container.
     570          37 :         if (!m_poKmlDocKmlRoot)
     571             :         {
     572             :             m_poKmlDocKmlRoot =
     573          37 :                 OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions);
     574          37 :             auto kml = AsKml(m_poKmlDocKmlRoot);
     575          37 :             if (kml)
     576             :             {
     577          37 :                 if (m_poKmlDocKml)
     578          36 :                     kml->set_feature(m_poKmlDocKml);
     579             :             }
     580             : 
     581          37 :             ParseDocumentOptions(std::move(kml), AsDocument(m_poKmlDocKml));
     582             :         }
     583             : 
     584          37 :         std::string oKmlOut = kmldom::SerializePretty(m_poKmlDocKmlRoot);
     585          37 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     586             : 
     587             :         const std::string osOutfile =
     588          37 :             CPLFormFilenameSafe(GetDescription(), "doc.kml", nullptr);
     589             : 
     590          37 :         VSILFILE *fp = VSIFOpenExL(osOutfile.c_str(), "wb", true);
     591          37 :         if (fp == nullptr)
     592             :         {
     593           0 :             CPLError(CE_Failure, CPLE_FileIO, "Error writing %s to %s: %s",
     594           0 :                      "doc.kml", GetDescription(), VSIGetLastErrorMsg());
     595           0 :             return false;
     596             :         }
     597             : 
     598          37 :         if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
     599           0 :             bRet = false;
     600          37 :         if (VSIFCloseL(fp) != 0)
     601           0 :             bRet = false;
     602             :     }
     603             : 
     604             :     /***** loop though the layers and write them *****/
     605          74 :     for (int iLayer = 0; iLayer < nLayers && !m_poKmlUpdate; iLayer++)
     606             :     {
     607          37 :         ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer();
     608             : 
     609          37 :         if (poKmlContainer->IsA(kmldom::Type_Document))
     610             :         {
     611          74 :             DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
     612          37 :             SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema();
     613             : 
     614          54 :             if (!poKmlDocument->get_schema_array_size() && poKmlSchema &&
     615          17 :                 poKmlSchema->get_simplefield_array_size())
     616             :             {
     617          17 :                 poKmlDocument->add_schema(std::move(poKmlSchema));
     618             :             }
     619             : 
     620          37 :             papoLayers[iLayer]->Finalize(std::move(poKmlDocument));
     621             :         }
     622             : 
     623             :         // If we do not have the layers root
     624             :         // make it and add the container.
     625          37 :         KmlPtr poKmlKml = nullptr;
     626             : 
     627          37 :         if (!(poKmlKml = AsKml(papoLayers[iLayer]->GetKmlLayerRoot())))
     628             :         {
     629          37 :             poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
     630             : 
     631          37 :             poKmlKml->set_feature(poKmlContainer);
     632             :         }
     633             : 
     634          37 :         std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
     635          37 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     636             : 
     637             :         const std::string osOutfile = CPLFormFilenameSafe(
     638          37 :             GetDescription(), papoLayers[iLayer]->GetFileName(), nullptr);
     639             : 
     640          37 :         VSILFILE *fp = VSIFOpenL(osOutfile.c_str(), "wb");
     641          37 :         if (fp == nullptr)
     642             :         {
     643           0 :             CPLError(CE_Failure, CPLE_FileIO, "ERROR Writing %s to %s",
     644           0 :                      papoLayers[iLayer]->GetFileName(), GetDescription());
     645           0 :             return false;
     646             :         }
     647             : 
     648          37 :         if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
     649           0 :             bRet = false;
     650          37 :         if (VSIFCloseL(fp) != 0)
     651           0 :             bRet = false;
     652             :     }
     653             : 
     654             :     /***** write the style table *****/
     655          37 :     if (m_poKmlStyleKml)
     656             :     {
     657           0 :         KmlPtr poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
     658             : 
     659           0 :         poKmlKml->set_feature(m_poKmlStyleKml);
     660           0 :         std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
     661           0 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     662             : 
     663             :         const std::string osOutfile =
     664           0 :             CPLFormFilenameSafe(GetDescription(), "style.kml", nullptr);
     665             : 
     666           0 :         VSILFILE *fp = VSIFOpenL(osOutfile.c_str(), "wb");
     667           0 :         if (fp == nullptr)
     668             :         {
     669           0 :             CPLError(CE_Failure, CPLE_FileIO, "ERROR Writing %s to %s",
     670           0 :                      "style.kml", GetDescription());
     671           0 :             return false;
     672             :         }
     673             : 
     674           0 :         if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
     675           0 :             bRet = false;
     676           0 :         if (VSIFCloseL(fp) != 0)
     677           0 :             bRet = false;
     678             :     }
     679          37 :     return bRet;
     680             : }
     681             : 
     682             : /******************************************************************************
     683             :  Method to write the datasource to disk.
     684             : 
     685             :  Args:      none
     686             : 
     687             :  Returns    nothing
     688             : 
     689             : ******************************************************************************/
     690             : 
     691         398 : CPLErr OGRLIBKMLDataSource::FlushCache(bool /* bAtClosing */)
     692             : {
     693         398 :     if (!bUpdated)
     694         292 :         return CE_None;
     695             : 
     696         106 :     CPLErr eErr = CE_None;
     697         106 :     if (bUpdate && IsKml())
     698             :     {
     699          61 :         if (!WriteKml())
     700           0 :             eErr = CE_Failure;
     701             :     }
     702          45 :     else if (bUpdate && IsKmz())
     703             :     {
     704           8 :         if (!WriteKmz())
     705           1 :             eErr = CE_Failure;
     706             :     }
     707          37 :     else if (bUpdate && IsDir())
     708             :     {
     709          37 :         if (!WriteDir())
     710           0 :             eErr = CE_Failure;
     711             :     }
     712             : 
     713         106 :     bUpdated = false;
     714         106 :     return eErr;
     715             : }
     716             : 
     717             : /******************************************************************************
     718             :  OGRLIBKMLDataSource Destructor
     719             : 
     720             :  Args:          none
     721             : 
     722             :  Returns:       nothing
     723             : 
     724             : ******************************************************************************/
     725             : 
     726         782 : OGRLIBKMLDataSource::~OGRLIBKMLDataSource()
     727             : {
     728             :     /***** sync the DS to disk *****/
     729         391 :     OGRLIBKMLDataSource::FlushCache(true);
     730             : 
     731         736 :     for (int i = 0; i < nLayers; i++)
     732         345 :         delete papoLayers[i];
     733             : 
     734         391 :     CPLFree(papoLayers);
     735             : 
     736         391 :     CSLDestroy(m_papszOptions);
     737         782 : }
     738             : 
     739             : /******************************************************************************
     740             :  Method to parse a schemas out of a document.
     741             : 
     742             :  Args:          poKmlDocument   pointer to the document to parse
     743             : 
     744             :  Returns:       nothing
     745             : 
     746             : ******************************************************************************/
     747             : 
     748          26 : SchemaPtr OGRLIBKMLDataSource::FindSchema(const char *pszSchemaUrl)
     749             : {
     750          26 :     if (!pszSchemaUrl || !*pszSchemaUrl)
     751           0 :         return nullptr;
     752             : 
     753          26 :     char *pszID = nullptr;
     754          26 :     char *pszFile = nullptr;
     755          26 :     char *pszSchemaName = nullptr;
     756          52 :     DocumentPtr poKmlDocument = nullptr;
     757          52 :     SchemaPtr poKmlSchemaResult = nullptr;
     758             : 
     759          26 :     if (*pszSchemaUrl == '#')
     760             :     {
     761          24 :         pszID = CPLStrdup(pszSchemaUrl + 1);
     762             : 
     763             :         /***** kml *****/
     764          24 :         if (IsKml() && m_poKmlDSContainer->IsA(kmldom::Type_Document))
     765          24 :             poKmlDocument = AsDocument(m_poKmlDSContainer);
     766             : 
     767             :         /***** kmz *****/
     768           0 :         else if ((IsKmz() || IsDir()) && m_poKmlDocKml &&
     769           0 :                  m_poKmlDocKml->IsA(kmldom::Type_Document))
     770           0 :             poKmlDocument = AsDocument(m_poKmlDocKml);
     771             :     }
     772           2 :     else if (const char *pszPound = strchr(pszSchemaUrl, '#'))
     773             :     {
     774           0 :         pszFile = CPLStrdup(pszSchemaUrl);
     775           0 :         pszID = CPLStrdup(pszPound + 1);
     776           0 :         pszFile[pszPound - pszSchemaUrl] = '\0';
     777             :     }
     778             :     else
     779             :     {
     780           2 :         pszSchemaName = CPLStrdup(pszSchemaUrl);
     781             : 
     782             :         /***** kml *****/
     783           2 :         if (IsKml() && m_poKmlDSContainer->IsA(kmldom::Type_Document))
     784           2 :             poKmlDocument = AsDocument(m_poKmlDSContainer);
     785             : 
     786             :         /***** kmz *****/
     787             : 
     788           0 :         else if ((IsKmz() || IsDir()) && m_poKmlDocKml &&
     789           0 :                  m_poKmlDocKml->IsA(kmldom::Type_Document))
     790           0 :             poKmlDocument = AsDocument(m_poKmlDocKml);
     791             :     }
     792             : 
     793          26 :     if (poKmlDocument)
     794             :     {
     795          26 :         size_t nKmlSchemas = poKmlDocument->get_schema_array_size();
     796             : 
     797          29 :         for (size_t iKmlSchema = 0; iKmlSchema < nKmlSchemas; iKmlSchema++)
     798             :         {
     799             :             SchemaPtr poKmlSchema =
     800          27 :                 poKmlDocument->get_schema_array_at(iKmlSchema);
     801          27 :             if (poKmlSchema->has_id() && pszID)
     802             :             {
     803          25 :                 if (EQUAL(pszID, poKmlSchema->get_id().c_str()))
     804             :                 {
     805          23 :                     poKmlSchemaResult = std::move(poKmlSchema);
     806          23 :                     break;
     807             :                 }
     808             :             }
     809             : 
     810           2 :             else if (poKmlSchema->has_name() && pszSchemaName)
     811             :             {
     812           2 :                 if (EQUAL(pszSchemaName, poKmlSchema->get_name().c_str()))
     813             :                 {
     814           1 :                     poKmlSchemaResult = std::move(poKmlSchema);
     815           1 :                     break;
     816             :                 }
     817             :             }
     818             :         }
     819             :     }
     820             : 
     821          26 :     CPLFree(pszFile);
     822          26 :     CPLFree(pszID);
     823          26 :     CPLFree(pszSchemaName);
     824             : 
     825          26 :     return poKmlSchemaResult;
     826             : }
     827             : 
     828             : /******************************************************************************
     829             :  Method to allocate memory for the layer array, create the layer,
     830             :  and add it to the layer array.
     831             : 
     832             :  Args:          pszLayerName    the name of the layer
     833             :                 eGType          the layers geometry type
     834             :                 poOgrDS         pointer to the datasource the layer is in
     835             :                 poKmlRoot       pointer to the root kml element of the layer
     836             :                 pszFileName     the filename of the layer
     837             :                 bNew            true if its a new layer
     838             :                 bUpdate         true if the layer is writable
     839             :                 nGuess          a guess at the number of additional layers
     840             :                                 we are going to need
     841             : 
     842             :  Returns:       Pointer to the new layer
     843             : ******************************************************************************/
     844             : 
     845         361 : OGRLIBKMLLayer *OGRLIBKMLDataSource::AddLayer(
     846             :     const char *pszLayerName, OGRwkbGeometryType eGType,
     847             :     const OGRSpatialReference *poSRS, OGRLIBKMLDataSource *poOgrDS,
     848             :     ElementPtr poKmlRoot, ContainerPtr poKmlContainer, const char *pszFileName,
     849             :     int bNew, int bUpdateIn, int nGuess)
     850             : {
     851             :     // Build unique layer name
     852         361 :     CPLString osUniqueLayername(pszLayerName);
     853         361 :     int nIter = 2;
     854             :     while (true)
     855             :     {
     856         362 :         if (GetLayerByName(osUniqueLayername) == nullptr)
     857         361 :             break;
     858           1 :         osUniqueLayername = CPLSPrintf("%s (#%d)", pszLayerName, nIter);
     859           1 :         nIter++;
     860             :     }
     861             : 
     862             :     /***** check to see if we have enough space to store the layer *****/
     863         361 :     if (nLayers == nAllocated)
     864             :     {
     865         257 :         nAllocated += nGuess;
     866         257 :         papoLayers = static_cast<OGRLIBKMLLayer **>(
     867         257 :             CPLRealloc(papoLayers, sizeof(OGRLIBKMLLayer *) * nAllocated));
     868             :     }
     869             : 
     870             :     /***** create the layer *****/
     871             :     OGRLIBKMLLayer *poOgrLayer = new OGRLIBKMLLayer(
     872         361 :         osUniqueLayername, eGType, poSRS, poOgrDS, std::move(poKmlRoot),
     873         722 :         std::move(poKmlContainer), m_poKmlUpdate, pszFileName, bNew, bUpdateIn);
     874             : 
     875             :     /***** add the layer to the array *****/
     876         361 :     const int iLayer = nLayers++;
     877         361 :     papoLayers[iLayer] = poOgrLayer;
     878         361 :     osUniqueLayername.toupper();
     879         361 :     m_oMapLayers[std::move(osUniqueLayername)] = poOgrLayer;
     880             : 
     881         722 :     return poOgrLayer;
     882             : }
     883             : 
     884             : /******************************************************************************
     885             :  Method to parse multiple layers out of a container.
     886             : 
     887             :  Args:          poKmlContainer  pointer to the container to parse
     888             : 
     889             :  Returns:       number of features in the container that are not another
     890             :                 container
     891             : 
     892             : ******************************************************************************/
     893             : 
     894         459 : int OGRLIBKMLDataSource::ParseLayers(ContainerPtr poKmlContainer, bool bRecurse)
     895             : {
     896             :     /***** if container is null just bail now *****/
     897         459 :     if (!poKmlContainer)
     898           0 :         return 0;
     899             : 
     900         459 :     const size_t nKmlFeatures = poKmlContainer->get_feature_array_size();
     901             : 
     902             :     /***** loop over the container to separate the style, layers, etc *****/
     903             : 
     904         459 :     int nResult = 0;
     905        1396 :     for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
     906             :     {
     907             :         FeaturePtr poKmlFeat =
     908        1874 :             poKmlContainer->get_feature_array_at(iKmlFeature);
     909             : 
     910             :         /***** container *****/
     911             : 
     912         937 :         if (poKmlFeat->IsA(kmldom::Type_Container))
     913             :         {
     914         382 :             if (bRecurse)
     915             :             {
     916             :                 /***** see if the container has a name *****/
     917             : 
     918         211 :                 std::string oKmlFeatName;
     919         211 :                 if (poKmlFeat->has_name())
     920             :                 {
     921             :                     /* Strip leading and trailing spaces */
     922         210 :                     const char *l_pszName = poKmlFeat->get_name().c_str();
     923         210 :                     while (*l_pszName == ' ' || *l_pszName == '\n' ||
     924         420 :                            *l_pszName == '\r' || *l_pszName == '\t')
     925           0 :                         l_pszName++;
     926         210 :                     oKmlFeatName = l_pszName;
     927         210 :                     int nSize = (int)oKmlFeatName.size();
     928         420 :                     while (nSize > 0 && (oKmlFeatName[nSize - 1] == ' ' ||
     929         210 :                                          oKmlFeatName[nSize - 1] == '\n' ||
     930         210 :                                          oKmlFeatName[nSize - 1] == '\r' ||
     931         210 :                                          oKmlFeatName[nSize - 1] == '\t'))
     932             :                     {
     933           0 :                         nSize--;
     934           0 :                         oKmlFeatName.resize(nSize);
     935             :                     }
     936             :                 }
     937             :                 /***** use the feature index number as the name *****/
     938             :                 /***** not sure i like this c++ ich *****/
     939             :                 else
     940             :                 {
     941           1 :                     std::stringstream oOut;
     942           1 :                     oOut << iKmlFeature;
     943           1 :                     oKmlFeatName = "Layer";
     944           1 :                     oKmlFeatName.append(oOut.str());
     945             :                 }
     946             : 
     947             :                 /***** create the layer *****/
     948             : 
     949         211 :                 AddLayer(oKmlFeatName.c_str(), wkbUnknown, nullptr, this,
     950         422 :                          nullptr, AsContainer(poKmlFeat), "", FALSE, bUpdate,
     951             :                          static_cast<int>(nKmlFeatures));
     952             : 
     953             :                 /***** check if any features are another layer *****/
     954         211 :                 ParseLayers(AsContainer(poKmlFeat), true);
     955             :             }
     956             :         }
     957             :         else
     958             :         {
     959         555 :             nResult++;
     960             :         }
     961             :     }
     962             : 
     963         459 :     return nResult;
     964             : }
     965             : 
     966             : /******************************************************************************
     967             :  Function to get the container from the kmlroot.
     968             : 
     969             :  Args:          poKmlRoot   the root element
     970             : 
     971             :  Returns:       root if its a container, if its a kml the container it
     972             :                 contains, or NULL
     973             : 
     974             : ******************************************************************************/
     975             : 
     976         138 : static ContainerPtr GetContainerFromRoot(KmlFactory *poKmlFactory,
     977             :                                          ElementPtr poKmlRoot)
     978             : {
     979         138 :     ContainerPtr poKmlContainer = nullptr;
     980             : 
     981             :     const bool bReadGroundOverlay =
     982         138 :         CPLTestBool(CPLGetConfigOption("LIBKML_READ_GROUND_OVERLAY", "YES"));
     983             : 
     984         138 :     if (poKmlRoot)
     985             :     {
     986             :         /***** skip over the <kml> we want the container *****/
     987         138 :         if (poKmlRoot->IsA(kmldom::Type_kml))
     988             :         {
     989         276 :             KmlPtr poKmlKml = AsKml(poKmlRoot);
     990             : 
     991         138 :             if (poKmlKml && poKmlKml->has_feature())
     992             :             {
     993         276 :                 FeaturePtr poKmlFeat = poKmlKml->get_feature();
     994             : 
     995         138 :                 if (poKmlFeat->IsA(kmldom::Type_Container))
     996         135 :                     poKmlContainer = AsContainer(poKmlFeat);
     997           3 :                 else if (poKmlFeat->IsA(kmldom::Type_Placemark) ||
     998           0 :                          (bReadGroundOverlay &&
     999           0 :                           poKmlFeat->IsA(kmldom::Type_GroundOverlay)))
    1000             :                 {
    1001           3 :                     poKmlContainer = poKmlFactory->CreateDocument();
    1002           6 :                     poKmlContainer->add_feature(
    1003           6 :                         kmldom::AsFeature(kmlengine::Clone(poKmlFeat)));
    1004             :                 }
    1005             :             }
    1006             :         }
    1007           0 :         else if (poKmlRoot->IsA(kmldom::Type_Container))
    1008             :         {
    1009           0 :             poKmlContainer = AsContainer(std::move(poKmlRoot));
    1010             :         }
    1011             :     }
    1012             : 
    1013         138 :     return poKmlContainer;
    1014             : }
    1015             : 
    1016             : /******************************************************************************
    1017             :  Method to parse a kml string into the style table.
    1018             : ******************************************************************************/
    1019             : 
    1020           4 : int OGRLIBKMLDataSource::ParseIntoStyleTable(std::string *poKmlStyleKml,
    1021             :                                              const char *pszMyStylePath)
    1022             : {
    1023             :     /***** parse the kml into the dom *****/
    1024           8 :     std::string oKmlErrors;
    1025           8 :     ElementPtr poKmlRoot = OGRLIBKMLParse(*poKmlStyleKml, &oKmlErrors);
    1026             : 
    1027           4 :     if (!poKmlRoot)
    1028             :     {
    1029           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing style kml %s :%s",
    1030             :                  pszMyStylePath, oKmlErrors.c_str());
    1031           0 :         return false;
    1032             :     }
    1033             : 
    1034           8 :     ContainerPtr poKmlContainer = nullptr;
    1035             : 
    1036           4 :     if (!(poKmlContainer =
    1037           8 :               GetContainerFromRoot(m_poKmlFactory, std::move(poKmlRoot))))
    1038             :     {
    1039           0 :         return false;
    1040             :     }
    1041             : 
    1042           4 :     ParseStyles(AsDocument(std::move(poKmlContainer)), &m_poStyleTable);
    1043           4 :     m_osStylePath = pszMyStylePath;
    1044             : 
    1045           4 :     return true;
    1046             : }
    1047             : 
    1048             : /******************************************************************************
    1049             :  Method to open a kml file.
    1050             : 
    1051             :  Args:          pszFilename file to open
    1052             :                 bUpdate     update mode
    1053             : 
    1054             :  Returns:       True on success, false on failure
    1055             : 
    1056             : ******************************************************************************/
    1057             : 
    1058         119 : int OGRLIBKMLDataSource::OpenKml(const char *pszFilename, int bUpdateIn)
    1059             : {
    1060         238 :     std::string oKmlKml;
    1061         238 :     std::string osBuffer;
    1062         119 :     osBuffer.resize(4096);
    1063             : 
    1064         119 :     VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
    1065         119 :     if (fp == nullptr)
    1066             :     {
    1067           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s", pszFilename);
    1068           0 :         return FALSE;
    1069             :     }
    1070         119 :     int nRead = 0;
    1071         208 :     while ((nRead = static_cast<int>(
    1072         327 :                 VSIFReadL(&osBuffer[0], 1, osBuffer.size(), fp))) != 0)
    1073             :     {
    1074             :         try
    1075             :         {
    1076         208 :             oKmlKml.append(osBuffer.c_str(), nRead);
    1077             :         }
    1078           0 :         catch (const std::exception &ex)
    1079             :         {
    1080           0 :             CPLDebug("LIBKML", "libstdc++ exception during ingestion: %s",
    1081           0 :                      ex.what());
    1082           0 :             VSIFCloseL(fp);
    1083           0 :             return FALSE;
    1084             :         }
    1085             :     }
    1086         119 :     OGRLIBKMLPreProcessInput(oKmlKml);
    1087         119 :     VSIFCloseL(fp);
    1088             : 
    1089         238 :     CPLLocaleC oLocaleForcer;
    1090             : 
    1091             :     /***** parse the kml into the DOM *****/
    1092         238 :     std::string oKmlErrors;
    1093             : 
    1094         119 :     m_poKmlDSKml = AsKml(OGRLIBKMLParse(oKmlKml, &oKmlErrors));
    1095             : 
    1096         119 :     if (!m_poKmlDSKml)
    1097             :     {
    1098           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing kml %s :%s",
    1099             :                  pszFilename, oKmlErrors.c_str());
    1100             : 
    1101           0 :         return FALSE;
    1102             :     }
    1103             : 
    1104             :     /***** get the container from root  *****/
    1105         119 :     if (!(m_poKmlDSContainer =
    1106         238 :               GetContainerFromRoot(m_poKmlFactory, m_poKmlDSKml)))
    1107             :     {
    1108           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing kml %s :%s %s",
    1109             :                  pszFilename, "This file does not fit the OGR model,",
    1110             :                  "there is no container element at the root.");
    1111             : 
    1112           0 :         return FALSE;
    1113             :     }
    1114             : 
    1115         119 :     m_isKml = true;
    1116             : 
    1117             :     /***** get the styles *****/
    1118         119 :     ParseStyles(AsDocument(m_poKmlDSContainer), &m_poStyleTable);
    1119             : 
    1120             :     /***** parse for layers *****/
    1121         119 :     int nPlacemarks = ParseLayers(m_poKmlDSContainer, false);
    1122             : 
    1123             :     /***** if there is placemarks in the root its a layer *****/
    1124         119 :     if (nPlacemarks)
    1125             :     {
    1126          16 :         std::string layername_default(CPLGetBasenameSafe(pszFilename));
    1127             : 
    1128          16 :         if (m_poKmlDSContainer->has_name())
    1129             :         {
    1130           6 :             layername_default = m_poKmlDSContainer->get_name();
    1131             :         }
    1132             : 
    1133          16 :         AddLayer(layername_default.c_str(), wkbUnknown, nullptr, this,
    1134          16 :                  m_poKmlDSKml, m_poKmlDSContainer, pszFilename, FALSE,
    1135             :                  bUpdateIn, 1);
    1136             :     }
    1137             : 
    1138         119 :     ParseLayers(m_poKmlDSContainer, true);
    1139             : 
    1140         119 :     return TRUE;
    1141             : }
    1142             : 
    1143             : /******************************************************************************
    1144             :  Method to open a kmz file.
    1145             : 
    1146             :  Args:          pszFilename file to open
    1147             :                 bUpdate     update mode
    1148             : 
    1149             :  Returns:       True on success, false on failure
    1150             : 
    1151             : ******************************************************************************/
    1152             : 
    1153           6 : int OGRLIBKMLDataSource::OpenKmz(const char *pszFilename, int bUpdateIn)
    1154             : {
    1155          12 :     std::string oKmlKmz;
    1156           6 :     char szBuffer[1024 + 1] = {};
    1157             : 
    1158           6 :     VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
    1159           6 :     if (fp == nullptr)
    1160             :     {
    1161           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s", pszFilename);
    1162           0 :         return FALSE;
    1163             :     }
    1164           6 :     int nRead = 0;
    1165          16 :     while ((nRead = static_cast<int>(VSIFReadL(szBuffer, 1, 1024, fp))) != 0)
    1166             :     {
    1167             :         try
    1168             :         {
    1169          10 :             oKmlKmz.append(szBuffer, nRead);
    1170             :         }
    1171           0 :         catch (const std::bad_alloc &)
    1172             :         {
    1173           0 :             VSIFCloseL(fp);
    1174           0 :             return FALSE;
    1175             :         }
    1176             :     }
    1177           6 :     VSIFCloseL(fp);
    1178             : 
    1179           6 :     KmzFile *poKmlKmzfile = KmzFile::OpenFromString(oKmlKmz);
    1180             : 
    1181           6 :     if (!poKmlKmzfile)
    1182             :     {
    1183           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "%s is not a valid kmz file",
    1184             :                  pszFilename);
    1185           0 :         return FALSE;
    1186             :     }
    1187             : 
    1188          12 :     CPLLocaleC oLocaleForcer;
    1189             : 
    1190             :     /***** read the doc.kml *****/
    1191          12 :     std::string oKmlKml;
    1192          12 :     std::string oKmlKmlPath;
    1193           6 :     if (!poKmlKmzfile->ReadKmlAndGetPath(&oKmlKml, &oKmlKmlPath))
    1194             :     {
    1195           0 :         return FALSE;
    1196             :     }
    1197             : 
    1198             :     /***** parse the kml into the DOM *****/
    1199          12 :     std::string oKmlErrors;
    1200          12 :     ElementPtr poKmlDocKmlRoot = OGRLIBKMLParse(oKmlKml, &oKmlErrors);
    1201             : 
    1202           6 :     if (!poKmlDocKmlRoot)
    1203             :     {
    1204           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1205             :                  "ERROR parsing kml layer %s from %s :%s", oKmlKmlPath.c_str(),
    1206             :                  pszFilename, oKmlErrors.c_str());
    1207             : 
    1208           0 :         return FALSE;
    1209             :     }
    1210             : 
    1211             :     /***** Get the child container from root. *****/
    1212          12 :     ContainerPtr poKmlContainer = nullptr;
    1213             : 
    1214           6 :     if (!(poKmlContainer =
    1215          12 :               GetContainerFromRoot(m_poKmlFactory, poKmlDocKmlRoot)))
    1216             :     {
    1217           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing %s from %s :%s",
    1218             :                  oKmlKmlPath.c_str(), pszFilename,
    1219             :                  "kml contains no Containers");
    1220             : 
    1221           0 :         return FALSE;
    1222             :     }
    1223             : 
    1224             :     /***** loop over the container looking for network links *****/
    1225             : 
    1226           6 :     size_t nKmlFeatures = poKmlContainer->get_feature_array_size();
    1227           6 :     int nLinks = 0;
    1228             : 
    1229          21 :     for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
    1230             :     {
    1231             :         FeaturePtr poKmlFeat =
    1232          15 :             poKmlContainer->get_feature_array_at(iKmlFeature);
    1233             : 
    1234             :         /***** is it a network link? *****/
    1235          15 :         if (!poKmlFeat->IsA(kmldom::Type_NetworkLink))
    1236           8 :             continue;
    1237             : 
    1238           7 :         NetworkLinkPtr poKmlNetworkLink = AsNetworkLink(poKmlFeat);
    1239             : 
    1240             :         /***** does it have a link? *****/
    1241           7 :         if (!poKmlNetworkLink->has_link())
    1242           0 :             continue;
    1243             : 
    1244           7 :         LinkPtr poKmlLink = poKmlNetworkLink->get_link();
    1245             : 
    1246             :         /***** does the link have a href? *****/
    1247           7 :         if (!poKmlLink->has_href())
    1248           0 :             continue;
    1249             : 
    1250           7 :         const kmlengine::Href oKmlHref(poKmlLink->get_href());
    1251             : 
    1252             :         /***** is the link relative? *****/
    1253           7 :         if (oKmlHref.IsRelativePath())
    1254             :         {
    1255           7 :             nLinks++;
    1256             : 
    1257           7 :             std::string oKml;
    1258           7 :             if (poKmlKmzfile->ReadFile(oKmlHref.get_path().c_str(), &oKml))
    1259             :             {
    1260             :                 /***** parse the kml into the DOM *****/
    1261           7 :                 oKmlErrors.clear();
    1262           7 :                 ElementPtr poKmlLyrRoot = OGRLIBKMLParse(oKml, &oKmlErrors);
    1263             : 
    1264           7 :                 if (!poKmlLyrRoot)
    1265             :                 {
    1266           0 :                     CPLError(CE_Failure, CPLE_OpenFailed,
    1267             :                              "ERROR parsing kml layer %s from %s :%s",
    1268           0 :                              oKmlHref.get_path().c_str(), pszFilename,
    1269             :                              oKmlErrors.c_str());
    1270             : 
    1271           0 :                     continue;
    1272             :                 }
    1273             : 
    1274             :                 /***** get the container from root  *****/
    1275             :                 ContainerPtr poKmlLyrContainer =
    1276           7 :                     GetContainerFromRoot(m_poKmlFactory, poKmlLyrRoot);
    1277             : 
    1278           7 :                 if (!poKmlLyrContainer)
    1279             :                 {
    1280           0 :                     CPLError(CE_Failure, CPLE_OpenFailed,
    1281             :                              "ERROR parsing kml layer %s from %s :%s",
    1282           0 :                              oKmlHref.get_path().c_str(), pszFilename,
    1283             :                              oKmlErrors.c_str());
    1284             : 
    1285           0 :                     continue;
    1286             :                 }
    1287             : 
    1288             :                 /***** create the layer *****/
    1289             :                 const std::string osLayerName =
    1290           7 :                     poKmlNetworkLink->has_name()
    1291           7 :                         ? poKmlNetworkLink->get_name()
    1292          14 :                         : CPLGetBasenameSafe(oKmlHref.get_path().c_str());
    1293             : 
    1294          14 :                 AddLayer(osLayerName.c_str(), wkbUnknown, nullptr, this,
    1295           7 :                          std::move(poKmlLyrRoot), poKmlLyrContainer,
    1296           7 :                          oKmlHref.get_path().c_str(), FALSE, bUpdateIn,
    1297             :                          static_cast<int>(nKmlFeatures));
    1298             : 
    1299             :                 /***** check if any features are another layer *****/
    1300           7 :                 ParseLayers(std::move(poKmlLyrContainer), true);
    1301             :             }
    1302             :         }
    1303             :     }
    1304             : 
    1305             :     /***** if the doc.kml has links store it so if were in update mode we can
    1306             :      * write it *****/
    1307           6 :     if (nLinks)
    1308             :     {
    1309           5 :         m_poKmlDocKml = std::move(poKmlContainer);
    1310           5 :         m_poKmlDocKmlRoot = std::move(poKmlDocKmlRoot);
    1311             :     }
    1312             :     /***** if the doc.kml has no links treat it as a normal kml file *****/
    1313             :     else
    1314             :     {
    1315             :         /* TODO: There could still be a separate styles file in the KMZ
    1316             :            if there is this would be a layer style table IF its only a single
    1317             :            layer.
    1318             :          */
    1319             : 
    1320             :         /***** get the styles *****/
    1321           1 :         ParseStyles(AsDocument(poKmlContainer), &m_poStyleTable);
    1322             : 
    1323             :         /***** parse for layers *****/
    1324           1 :         const int nPlacemarks = ParseLayers(poKmlContainer, false);
    1325             : 
    1326             :         /***** if there is placemarks in the root its a layer *****/
    1327           1 :         if (nPlacemarks)
    1328             :         {
    1329           1 :             std::string layername_default(CPLGetBasenameSafe(pszFilename));
    1330             : 
    1331           1 :             if (poKmlContainer->has_name())
    1332             :             {
    1333           1 :                 layername_default = poKmlContainer->get_name();
    1334             :             }
    1335             : 
    1336           2 :             AddLayer(layername_default.c_str(), wkbUnknown, nullptr, this,
    1337           1 :                      std::move(poKmlDocKmlRoot), poKmlContainer, pszFilename,
    1338             :                      FALSE, bUpdateIn, 1);
    1339             :         }
    1340             : 
    1341           1 :         ParseLayers(std::move(poKmlContainer), true);
    1342             :     }
    1343             : 
    1344             :     /***** read the style table if it has one *****/
    1345           6 :     std::string oKmlStyleKml;
    1346           6 :     if (poKmlKmzfile->ReadFile("style/style.kml", &oKmlStyleKml))
    1347           2 :         ParseIntoStyleTable(&oKmlStyleKml, "style/style.kml");
    1348             : 
    1349             :     /***** cleanup *****/
    1350           6 :     delete poKmlKmzfile;
    1351           6 :     m_isKmz = true;
    1352             : 
    1353           6 :     return TRUE;
    1354             : }
    1355             : 
    1356             : /******************************************************************************
    1357             :  Method to open a dir.
    1358             : 
    1359             :  Args:          pszFilename Dir to open
    1360             :                 bUpdate     update mode
    1361             : 
    1362             :  Returns:       True on success, false on failure
    1363             : 
    1364             : ******************************************************************************/
    1365             : 
    1366         163 : int OGRLIBKMLDataSource::OpenDir(const char *pszFilename, int bUpdateIn)
    1367             : {
    1368         163 :     char **papszDirList = VSIReadDir(pszFilename);
    1369             : 
    1370         163 :     if (papszDirList == nullptr)
    1371           6 :         return FALSE;
    1372             : 
    1373         157 :     const int nFiles = CSLCount(papszDirList);
    1374             : 
    1375        4529 :     for (int iFile = 0; iFile < nFiles; iFile++)
    1376             :     {
    1377             :         /***** make sure its a .kml file *****/
    1378        4372 :         if (!EQUAL(CPLGetExtensionSafe(papszDirList[iFile]).c_str(), "kml"))
    1379        4371 :             continue;
    1380             : 
    1381             :         /***** read the file *****/
    1382           2 :         std::string oKmlKml;
    1383           2 :         char szBuffer[1024 + 1] = {};
    1384             : 
    1385             :         const CPLString osFilePath =
    1386           2 :             CPLFormFilenameSafe(pszFilename, papszDirList[iFile], nullptr);
    1387             : 
    1388           2 :         VSILFILE *fp = VSIFOpenL(osFilePath, "rb");
    1389           2 :         if (fp == nullptr)
    1390             :         {
    1391           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s",
    1392             :                      osFilePath.c_str());
    1393           0 :             continue;
    1394             :         }
    1395             : 
    1396           2 :         int nRead = 0;
    1397           4 :         while ((nRead = static_cast<int>(VSIFReadL(szBuffer, 1, 1024, fp))) !=
    1398             :                0)
    1399             :         {
    1400             :             try
    1401             :             {
    1402           2 :                 oKmlKml.append(szBuffer, nRead);
    1403             :             }
    1404           0 :             catch (const std::bad_alloc &)
    1405             :             {
    1406           0 :                 VSIFCloseL(fp);
    1407           0 :                 CSLDestroy(papszDirList);
    1408           0 :                 return FALSE;
    1409             :             }
    1410             :         }
    1411           2 :         VSIFCloseL(fp);
    1412             : 
    1413           2 :         CPLLocaleC oLocaleForcer;
    1414             : 
    1415             :         /***** parse the kml into the DOM *****/
    1416           2 :         std::string oKmlErrors;
    1417           2 :         ElementPtr poKmlRoot = OGRLIBKMLParse(oKmlKml, &oKmlErrors);
    1418             : 
    1419           2 :         if (!poKmlRoot)
    1420             :         {
    1421           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1422             :                      "ERROR parsing kml layer %s from %s :%s",
    1423             :                      osFilePath.c_str(), pszFilename, oKmlErrors.c_str());
    1424             : 
    1425           0 :             continue;
    1426             :         }
    1427             : 
    1428             :         /***** Get the container from the root *****/
    1429           2 :         ContainerPtr poKmlContainer = nullptr;
    1430             : 
    1431           2 :         if (!(poKmlContainer = GetContainerFromRoot(m_poKmlFactory, poKmlRoot)))
    1432             :         {
    1433           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing kml %s :%s %s",
    1434             :                      pszFilename, "This file does not fit the OGR model,",
    1435             :                      "there is no container element at the root.");
    1436           0 :             continue;
    1437             :         }
    1438             : 
    1439             :         /***** is it a style table? *****/
    1440           2 :         if (EQUAL(papszDirList[iFile], "style.kml"))
    1441             :         {
    1442           0 :             ParseStyles(AsDocument(poKmlContainer), &m_poStyleTable);
    1443           0 :             m_osStylePath = "style.kml";
    1444           0 :             continue;
    1445             :         }
    1446             : 
    1447             :         // Root document generated by ourselves. Contains nothing of interest
    1448           2 :         if (poKmlContainer->has_id() && poKmlContainer->get_id() == "root_doc")
    1449             :         {
    1450           1 :             continue;
    1451             :         }
    1452             : 
    1453             :         const std::string osLayerName =
    1454           1 :             poKmlContainer->has_name()
    1455           1 :                 ? poKmlContainer->get_name()
    1456           2 :                 : std::string(CPLGetBasenameSafe(osFilePath.c_str()));
    1457             : 
    1458             :         /***** create the layer *****/
    1459           2 :         AddLayer(osLayerName.c_str(), wkbUnknown, nullptr, this,
    1460           1 :                  std::move(poKmlRoot), poKmlContainer, osFilePath.c_str(),
    1461             :                  FALSE, bUpdateIn, nFiles);
    1462             : 
    1463             :         /***** check if any features are another layer *****/
    1464           1 :         ParseLayers(std::move(poKmlContainer), true);
    1465             :     }
    1466             : 
    1467         157 :     CSLDestroy(papszDirList);
    1468             : 
    1469         157 :     if (nLayers > 0)
    1470             :     {
    1471           1 :         m_isDir = true;
    1472           1 :         return TRUE;
    1473             :     }
    1474             : 
    1475         156 :     return FALSE;
    1476             : }
    1477             : 
    1478             : /******************************************************************************
    1479             :  Method to open a datasource.
    1480             : 
    1481             :  Args:          pszFilename Darasource to open
    1482             :                 bUpdate     update mode
    1483             : 
    1484             :  Returns:       True on success, false on failure
    1485             : 
    1486             : ******************************************************************************/
    1487             : 
    1488           0 : static bool CheckIsKMZ(const char *pszFilename)
    1489             : {
    1490           0 :     char **papszFiles = VSIReadDir(pszFilename);
    1491           0 :     char **papszIter = papszFiles;
    1492           0 :     bool bFoundKML = false;
    1493           0 :     while (papszIter && *papszIter)
    1494             :     {
    1495           0 :         if (EQUAL(CPLGetExtensionSafe(*papszIter).c_str(), "kml"))
    1496             :         {
    1497           0 :             bFoundKML = true;
    1498           0 :             break;
    1499             :         }
    1500             :         else
    1501             :         {
    1502           0 :             CPLString osFilename(pszFilename);
    1503           0 :             osFilename += "/";
    1504           0 :             osFilename += *papszIter;
    1505           0 :             if (CheckIsKMZ(osFilename))
    1506             :             {
    1507           0 :                 bFoundKML = true;
    1508           0 :                 break;
    1509             :             }
    1510             :         }
    1511           0 :         papszIter++;
    1512             :     }
    1513           0 :     CSLDestroy(papszFiles);
    1514           0 :     return bFoundKML;
    1515             : }
    1516             : 
    1517         288 : int OGRLIBKMLDataSource::Open(const char *pszFilename, int bUpdateIn)
    1518             : {
    1519         288 :     bUpdate = CPL_TO_BOOL(bUpdateIn);
    1520             : 
    1521             :     /***** dir *****/
    1522             :     VSIStatBufL sStatBuf;
    1523         576 :     if (!VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) &&
    1524         288 :         VSI_ISDIR(sStatBuf.st_mode))
    1525             :     {
    1526         163 :         return OpenDir(pszFilename, bUpdate);
    1527             :     }
    1528             : 
    1529             :     /***** kml *****/
    1530         125 :     if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kml"))
    1531             :     {
    1532         119 :         return OpenKml(pszFilename, bUpdate);
    1533             :     }
    1534             : 
    1535             :     /***** kmz *****/
    1536           6 :     if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kmz"))
    1537             :     {
    1538           6 :         return OpenKmz(pszFilename, bUpdate);
    1539             :     }
    1540             : 
    1541           0 :     VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
    1542           0 :     if (fp == nullptr)
    1543           0 :         return FALSE;
    1544             : 
    1545           0 :     char szBuffer[1024 + 1] = {};
    1546           0 :     const int nRead = static_cast<int>(VSIFReadL(szBuffer, 1, 1024, fp));
    1547           0 :     szBuffer[nRead] = 0;
    1548             : 
    1549           0 :     VSIFCloseL(fp);
    1550             : 
    1551             :     // Does it look like a zip file?
    1552           0 :     if (nRead == 1024 && szBuffer[0] == 0x50 && szBuffer[1] == 0x4B &&
    1553           0 :         szBuffer[2] == 0x03 && szBuffer[3] == 0x04)
    1554             :     {
    1555           0 :         CPLString osFilename("/vsizip/");
    1556           0 :         osFilename += pszFilename;
    1557           0 :         if (!CheckIsKMZ(osFilename))
    1558           0 :             return FALSE;
    1559             : 
    1560           0 :         return OpenKmz(pszFilename, bUpdate);
    1561             :     }
    1562             : 
    1563           0 :     if (strstr(szBuffer, "<kml>") || strstr(szBuffer, "<kml xmlns="))
    1564           0 :         return OpenKml(pszFilename, bUpdate);
    1565             : 
    1566           0 :     return FALSE;
    1567             : }
    1568             : 
    1569             : /************************************************************************/
    1570             : /*                         IsValidPhoneNumber()                         */
    1571             : /************************************************************************/
    1572             : 
    1573             : // Very approximative validation of http://tools.ietf.org/html/rfc3966#page-6
    1574           1 : static bool IsValidPhoneNumber(const char *pszPhoneNumber)
    1575             : {
    1576           1 :     if (STARTS_WITH(pszPhoneNumber, "tel:"))
    1577           1 :         pszPhoneNumber += strlen("tel:");
    1578           1 :     char ch = '\0';
    1579           1 :     bool bDigitFound = false;
    1580           1 :     if (*pszPhoneNumber == '+')
    1581           0 :         pszPhoneNumber++;
    1582           4 :     while ((ch = *pszPhoneNumber) != '\0')
    1583             :     {
    1584           3 :         if (ch >= '0' && ch <= '9')
    1585           3 :             bDigitFound = true;
    1586           0 :         else if (ch == ';')
    1587           0 :             break;
    1588           0 :         else if (!(ch == '-' || ch == '.' || ch == '(' || ch == ')'))
    1589           0 :             return false;
    1590           3 :         pszPhoneNumber++;
    1591             :     }
    1592           1 :     return bDigitFound;
    1593             : }
    1594             : 
    1595             : /************************************************************************/
    1596             : /*                           SetCommonOptions()                         */
    1597             : /************************************************************************/
    1598             : 
    1599         224 : void OGRLIBKMLDataSource::SetCommonOptions(ContainerPtr poKmlContainer,
    1600             :                                            CSLConstList papszOptions)
    1601             : {
    1602         224 :     const char *l_pszName = CSLFetchNameValue(papszOptions, "NAME");
    1603         224 :     if (l_pszName != nullptr)
    1604           5 :         poKmlContainer->set_name(l_pszName);
    1605             : 
    1606         224 :     const char *pszVisibilility = CSLFetchNameValue(papszOptions, "VISIBILITY");
    1607         224 :     if (pszVisibilility != nullptr)
    1608           2 :         poKmlContainer->set_visibility(CPLTestBool(pszVisibilility));
    1609             : 
    1610         224 :     const char *pszOpen = CSLFetchNameValue(papszOptions, "OPEN");
    1611         224 :     if (pszOpen != nullptr)
    1612           2 :         poKmlContainer->set_open(CPLTestBool(pszOpen));
    1613             : 
    1614         224 :     const char *pszSnippet = CSLFetchNameValue(papszOptions, "SNIPPET");
    1615         224 :     if (pszSnippet != nullptr)
    1616             :     {
    1617           4 :         SnippetPtr poKmlSnippet = m_poKmlFactory->CreateSnippet();
    1618           2 :         poKmlSnippet->set_text(pszSnippet);
    1619           2 :         poKmlContainer->set_snippet(poKmlSnippet);
    1620             :     }
    1621             : 
    1622         224 :     const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
    1623         224 :     if (pszDescription != nullptr)
    1624           2 :         poKmlContainer->set_description(pszDescription);
    1625         224 : }
    1626             : 
    1627             : /************************************************************************/
    1628             : /*                        ParseDocumentOptions()                        */
    1629             : /************************************************************************/
    1630             : 
    1631         105 : void OGRLIBKMLDataSource::ParseDocumentOptions(KmlPtr poKml,
    1632             :                                                DocumentPtr poKmlDocument)
    1633             : {
    1634         105 :     if (poKmlDocument)
    1635             :     {
    1636             :         const char *pszDocumentId =
    1637         102 :             CSLFetchNameValueDef(m_papszOptions, "DOCUMENT_ID", "root_doc");
    1638         102 :         poKmlDocument->set_id(pszDocumentId);
    1639             : 
    1640             :         const char *pszAuthorName =
    1641         102 :             CSLFetchNameValue(m_papszOptions, "AUTHOR_NAME");
    1642             :         const char *pszAuthorURI =
    1643         102 :             CSLFetchNameValue(m_papszOptions, "AUTHOR_URI");
    1644             :         const char *pszAuthorEmail =
    1645         102 :             CSLFetchNameValue(m_papszOptions, "AUTHOR_EMAIL");
    1646         102 :         const char *pszLink = CSLFetchNameValue(m_papszOptions, "LINK");
    1647             : 
    1648         102 :         if (pszAuthorName != nullptr || pszAuthorURI != nullptr ||
    1649             :             pszAuthorEmail != nullptr)
    1650             :         {
    1651           2 :             kmldom::AtomAuthorPtr author = m_poKmlFactory->CreateAtomAuthor();
    1652           1 :             if (pszAuthorName != nullptr)
    1653           1 :                 author->set_name(pszAuthorName);
    1654           1 :             if (pszAuthorURI != nullptr)
    1655             :             {
    1656             :                 // Ad-hoc validation. The ABNF is horribly complicated:
    1657             :                 // http://tools.ietf.org/search/rfc3987#page-7
    1658           1 :                 if (STARTS_WITH(pszAuthorURI, "http://") ||
    1659           0 :                     STARTS_WITH(pszAuthorURI, "https://"))
    1660             :                 {
    1661           1 :                     author->set_uri(pszAuthorURI);
    1662             :                 }
    1663             :                 else
    1664             :                 {
    1665           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1666             :                              "Invalid URI for AUTHOR_URI");
    1667             :                 }
    1668             :             }
    1669           1 :             if (pszAuthorEmail != nullptr)
    1670             :             {
    1671           1 :                 const char *pszArobase = strchr(pszAuthorEmail, '@');
    1672           1 :                 if (pszArobase != nullptr &&
    1673           1 :                     strchr(pszArobase + 1, '.') != nullptr)
    1674             :                 {
    1675           1 :                     author->set_email(pszAuthorEmail);
    1676             :                 }
    1677             :                 else
    1678             :                 {
    1679           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1680             :                              "Invalid email for AUTHOR_EMAIL");
    1681             :                 }
    1682             :             }
    1683           1 :             poKmlDocument->set_atomauthor(author);
    1684             :         }
    1685             : 
    1686         102 :         if (pszLink != nullptr)
    1687             :         {
    1688           2 :             kmldom::AtomLinkPtr link = m_poKmlFactory->CreateAtomLink();
    1689           1 :             link->set_href(pszLink);
    1690           1 :             link->set_rel("related");
    1691           1 :             poKmlDocument->set_atomlink(link);
    1692             :         }
    1693             : 
    1694             :         const char *pszPhoneNumber =
    1695         102 :             CSLFetchNameValue(m_papszOptions, "PHONENUMBER");
    1696         102 :         if (pszPhoneNumber != nullptr)
    1697             :         {
    1698           1 :             if (IsValidPhoneNumber(pszPhoneNumber))
    1699             :             {
    1700           1 :                 if (!STARTS_WITH(pszPhoneNumber, "tel:"))
    1701           0 :                     poKmlDocument->set_phonenumber(
    1702             :                         CPLSPrintf("tel:%s", pszPhoneNumber));
    1703             :                 else
    1704           1 :                     poKmlDocument->set_phonenumber(pszPhoneNumber);
    1705             :             }
    1706             :             else
    1707             :             {
    1708           0 :                 CPLError(CE_Warning, CPLE_AppDefined, "Invalid phone number");
    1709             :             }
    1710             :         }
    1711             : 
    1712         102 :         SetCommonOptions(poKmlDocument, m_papszOptions);
    1713             : 
    1714             :         CPLString osListStyleType =
    1715         204 :             CSLFetchNameValueDef(m_papszOptions, "LISTSTYLE_TYPE", "");
    1716             :         CPLString osListStyleIconHref =
    1717         102 :             CSLFetchNameValueDef(m_papszOptions, "LISTSTYLE_ICON_HREF", "");
    1718         102 :         createkmlliststyle(m_poKmlFactory, pszDocumentId, poKmlDocument,
    1719             :                            poKmlDocument, osListStyleType, osListStyleIconHref);
    1720             :     }
    1721             : 
    1722         105 :     if (poKml)
    1723             :     {
    1724         105 :         if (m_poKmlUpdate)
    1725             :         {
    1726             :             NetworkLinkControlPtr nlc =
    1727           6 :                 m_poKmlFactory->CreateNetworkLinkControl();
    1728           3 :             poKml->set_networklinkcontrol(nlc);
    1729           3 :             if (m_poKmlUpdate->get_updateoperation_array_size() != 0)
    1730             :             {
    1731           3 :                 nlc->set_update(m_poKmlUpdate);
    1732             :             }
    1733             :         }
    1734             : 
    1735             :         const char *pszNLCMinRefreshPeriod =
    1736         105 :             CSLFetchNameValue(m_papszOptions, "NLC_MINREFRESHPERIOD");
    1737             :         const char *pszNLCMaxSessionLength =
    1738         105 :             CSLFetchNameValue(m_papszOptions, "NLC_MAXSESSIONLENGTH");
    1739             :         const char *pszNLCCookie =
    1740         105 :             CSLFetchNameValue(m_papszOptions, "NLC_COOKIE");
    1741             :         const char *pszNLCMessage =
    1742         105 :             CSLFetchNameValue(m_papszOptions, "NLC_MESSAGE");
    1743             :         const char *pszNLCLinkName =
    1744         105 :             CSLFetchNameValue(m_papszOptions, "NLC_LINKNAME");
    1745             :         const char *pszNLCLinkDescription =
    1746         105 :             CSLFetchNameValue(m_papszOptions, "NLC_LINKDESCRIPTION");
    1747             :         const char *pszNLCLinkSnippet =
    1748         105 :             CSLFetchNameValue(m_papszOptions, "NLC_LINKSNIPPET");
    1749             :         const char *pszNLCExpires =
    1750         105 :             CSLFetchNameValue(m_papszOptions, "NLC_EXPIRES");
    1751             : 
    1752         105 :         if (pszNLCMinRefreshPeriod != nullptr ||
    1753         102 :             pszNLCMaxSessionLength != nullptr || pszNLCCookie != nullptr ||
    1754         102 :             pszNLCMessage != nullptr || pszNLCLinkName != nullptr ||
    1755         102 :             pszNLCLinkDescription != nullptr || pszNLCLinkSnippet != nullptr ||
    1756             :             pszNLCExpires != nullptr)
    1757             :         {
    1758           6 :             NetworkLinkControlPtr nlc = nullptr;
    1759           3 :             if (poKml->has_networklinkcontrol())
    1760             :             {
    1761           0 :                 nlc = poKml->get_networklinkcontrol();
    1762             :             }
    1763             :             else
    1764             :             {
    1765           3 :                 nlc = m_poKmlFactory->CreateNetworkLinkControl();
    1766           3 :                 poKml->set_networklinkcontrol(nlc);
    1767             :             }
    1768           3 :             if (pszNLCMinRefreshPeriod != nullptr)
    1769             :             {
    1770           3 :                 const double dfVal = CPLAtof(pszNLCMinRefreshPeriod);
    1771           3 :                 if (dfVal >= 0)
    1772           3 :                     nlc->set_minrefreshperiod(dfVal);
    1773             :             }
    1774           3 :             if (pszNLCMaxSessionLength != nullptr)
    1775             :             {
    1776           3 :                 const double dfVal = CPLAtof(pszNLCMaxSessionLength);
    1777           3 :                 nlc->set_maxsessionlength(dfVal);
    1778             :             }
    1779           3 :             if (pszNLCCookie != nullptr)
    1780             :             {
    1781           3 :                 nlc->set_cookie(pszNLCCookie);
    1782             :             }
    1783           3 :             if (pszNLCMessage != nullptr)
    1784             :             {
    1785           3 :                 nlc->set_message(pszNLCMessage);
    1786             :             }
    1787           3 :             if (pszNLCLinkName != nullptr)
    1788             :             {
    1789           3 :                 nlc->set_linkname(pszNLCLinkName);
    1790             :             }
    1791           3 :             if (pszNLCLinkDescription != nullptr)
    1792             :             {
    1793           3 :                 nlc->set_linkdescription(pszNLCLinkDescription);
    1794             :             }
    1795           3 :             if (pszNLCLinkSnippet != nullptr)
    1796             :             {
    1797             :                 LinkSnippetPtr linksnippet =
    1798           3 :                     m_poKmlFactory->CreateLinkSnippet();
    1799           3 :                 linksnippet->set_text(pszNLCLinkSnippet);
    1800           3 :                 nlc->set_linksnippet(std::move(linksnippet));
    1801             :             }
    1802           3 :             if (pszNLCExpires != nullptr)
    1803             :             {
    1804             :                 OGRField sField;
    1805           3 :                 if (OGRParseXMLDateTime(pszNLCExpires, &sField))
    1806             :                 {
    1807           3 :                     char *pszXMLDate = OGRGetXMLDateTime(&sField);
    1808           3 :                     nlc->set_expires(pszXMLDate);
    1809           3 :                     CPLFree(pszXMLDate);
    1810             :                 }
    1811             :             }
    1812             :         }
    1813             :     }
    1814         105 : }
    1815             : 
    1816             : /******************************************************************************
    1817             :  Method to create a single file .kml ds.
    1818             : 
    1819             :  Args:          pszFilename     the datasource to create
    1820             :                 papszOptions    datasource creation options
    1821             : 
    1822             :  Returns:       True on success, false on failure
    1823             : 
    1824             : ******************************************************************************/
    1825             : 
    1826          57 : int OGRLIBKMLDataSource::CreateKml(const char * /* pszFilename */,
    1827             :                                    char **papszOptions)
    1828             : {
    1829          57 :     m_poKmlDSKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory, papszOptions);
    1830          57 :     if (osUpdateTargetHref.empty())
    1831             :     {
    1832         112 :         DocumentPtr poKmlDocument = m_poKmlFactory->CreateDocument();
    1833          56 :         m_poKmlDSKml->set_feature(poKmlDocument);
    1834          56 :         m_poKmlDSContainer = poKmlDocument;
    1835             :     }
    1836             : 
    1837          57 :     m_isKml = true;
    1838          57 :     bUpdated = true;
    1839             : 
    1840          57 :     return true;
    1841             : }
    1842             : 
    1843             : /******************************************************************************
    1844             :  Method to create a .kmz ds.
    1845             : 
    1846             :  Args:          pszFilename     the datasource to create
    1847             :                 papszOptions    datasource creation options
    1848             : 
    1849             :  Returns:       True on success, false on failure
    1850             : 
    1851             : ******************************************************************************/
    1852             : 
    1853           8 : int OGRLIBKMLDataSource::CreateKmz(const char * /* pszFilename */,
    1854             :                                    char ** /* papszOptions */)
    1855             : {
    1856             :     /***** create the doc.kml  *****/
    1857           8 :     if (osUpdateTargetHref.empty())
    1858             :     {
    1859             :         const char *pszUseDocKml =
    1860           7 :             CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
    1861             : 
    1862           7 :         if (CPLTestBool(pszUseDocKml))
    1863             :         {
    1864           6 :             m_poKmlDocKml = m_poKmlFactory->CreateDocument();
    1865             :         }
    1866             :     }
    1867             : 
    1868             :     // Layers are written in a layers/ subdirectory, hence ../style
    1869             :     // to access style/style.kml
    1870           8 :     m_osStylePath = "../style/style.kml";
    1871             : 
    1872           8 :     m_isKmz = true;
    1873           8 :     bUpdated = true;
    1874             : 
    1875           8 :     return TRUE;
    1876             : }
    1877             : 
    1878             : /******************************************************************************
    1879             :  Method to create a dir datasource.
    1880             : 
    1881             :  Args:          pszFilename     the datasource to create
    1882             :                 papszOptions    datasource creation options
    1883             : 
    1884             :  Returns:       True on success, false on failure
    1885             : 
    1886             : ******************************************************************************/
    1887             : 
    1888          38 : int OGRLIBKMLDataSource::CreateDir(const char *pszFilename,
    1889             :                                    char ** /* papszOptions */)
    1890             : {
    1891          38 :     if (VSIMkdir(pszFilename, 0755))
    1892             :     {
    1893           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1894             :                  "ERROR Creating dir: %s for KML datasource", pszFilename);
    1895           1 :         return FALSE;
    1896             :     }
    1897             : 
    1898          37 :     m_isDir = true;
    1899          37 :     bUpdated = true;
    1900             : 
    1901          37 :     if (osUpdateTargetHref.empty())
    1902             :     {
    1903             :         const char *pszUseDocKml =
    1904          36 :             CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
    1905             : 
    1906          36 :         if (CPLTestBool(pszUseDocKml))
    1907             :         {
    1908          36 :             m_poKmlDocKml = m_poKmlFactory->CreateDocument();
    1909             :         }
    1910             :     }
    1911             : 
    1912          37 :     m_osStylePath = "style.kml";
    1913             : 
    1914          37 :     return TRUE;
    1915             : }
    1916             : 
    1917             : /******************************************************************************
    1918             :  Method to create a datasource.
    1919             : 
    1920             :  Args:          pszFilename     the datasource to create
    1921             :                 papszOptions    datasource creation options
    1922             : 
    1923             :  Returns:       True on success, false on failure
    1924             : 
    1925             :  env vars:
    1926             :   LIBKML_USE_DOC.KML         default: yes
    1927             : 
    1928             : ******************************************************************************/
    1929             : 
    1930         103 : int OGRLIBKMLDataSource::Create(const char *pszFilename, char **papszOptions)
    1931             : {
    1932         103 :     if (strcmp(pszFilename, "/dev/stdout") == 0)
    1933           0 :         pszFilename = "/vsistdout/";
    1934             : 
    1935         103 :     SetDescription(pszFilename);
    1936         103 :     bUpdate = true;
    1937             : 
    1938             :     osUpdateTargetHref =
    1939         103 :         CSLFetchNameValueDef(papszOptions, "UPDATE_TARGETHREF", "");
    1940         103 :     if (!osUpdateTargetHref.empty())
    1941             :     {
    1942           3 :         m_poKmlUpdate = m_poKmlFactory->CreateUpdate();
    1943           3 :         m_poKmlUpdate->set_targethref(osUpdateTargetHref.c_str());
    1944             :     }
    1945             : 
    1946         103 :     m_papszOptions = CSLDuplicate(papszOptions);
    1947             : 
    1948             :     /***** kml *****/
    1949         309 :     if (strcmp(pszFilename, "/vsistdout/") == 0 ||
    1950         206 :         STARTS_WITH(pszFilename, "/vsigzip/") ||
    1951         206 :         EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kml"))
    1952          57 :         return CreateKml(pszFilename, papszOptions);
    1953             : 
    1954             :     /***** kmz *****/
    1955          46 :     if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kmz"))
    1956           8 :         return CreateKmz(pszFilename, papszOptions);
    1957             : 
    1958             :     /***** dir *****/
    1959          38 :     return CreateDir(pszFilename, papszOptions);
    1960             : }
    1961             : 
    1962             : /******************************************************************************
    1963             :  Method to get a layer by index.
    1964             : 
    1965             :  Args:          iLayer      the index of the layer to get
    1966             : 
    1967             :  Returns:       pointer to the layer, or NULL if the layer does not exist
    1968             : 
    1969             : ******************************************************************************/
    1970             : 
    1971          95 : OGRLayer *OGRLIBKMLDataSource::GetLayer(int iLayer)
    1972             : {
    1973          95 :     if (iLayer < 0 || iLayer >= nLayers)
    1974           4 :         return nullptr;
    1975             : 
    1976          91 :     return papoLayers[iLayer];
    1977             : }
    1978             : 
    1979             : /******************************************************************************
    1980             :  Method to get a layer by name.
    1981             : 
    1982             :  Args:          pszname     name of the layer to get
    1983             : 
    1984             :  Returns:       pointer to the layer, or NULL if the layer does not exist
    1985             : 
    1986             : ******************************************************************************/
    1987             : 
    1988         613 : OGRLayer *OGRLIBKMLDataSource::GetLayerByName(const char *pszName)
    1989             : {
    1990         613 :     auto oIter = m_oMapLayers.find(CPLString(pszName).toupper());
    1991         613 :     if (oIter != m_oMapLayers.end())
    1992         233 :         return oIter->second;
    1993             : 
    1994         380 :     return nullptr;
    1995             : }
    1996             : 
    1997             : /******************************************************************************
    1998             :  Method to DeleteLayers in a .kml datasource.
    1999             : 
    2000             :  Args:          iLayer  index of the layer to delete
    2001             : 
    2002             :  Returns:       OGRERR_NONE on success, OGRERR_FAILURE on failure
    2003             : 
    2004             : ******************************************************************************/
    2005             : 
    2006           0 : OGRErr OGRLIBKMLDataSource::DeleteLayerKml(int iLayer)
    2007             : {
    2008           0 :     OGRLIBKMLLayer *poOgrLayer = (OGRLIBKMLLayer *)papoLayers[iLayer];
    2009             : 
    2010             :     /***** loop over the features *****/
    2011             : 
    2012           0 :     const size_t nKmlFeatures = m_poKmlDSContainer->get_feature_array_size();
    2013             : 
    2014           0 :     for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
    2015             :     {
    2016             :         FeaturePtr poKmlFeat =
    2017           0 :             m_poKmlDSContainer->get_feature_array_at(iKmlFeature);
    2018             : 
    2019           0 :         if (poKmlFeat == poOgrLayer->GetKmlLayer())
    2020             :         {
    2021           0 :             m_poKmlDSContainer->DeleteFeatureAt(iKmlFeature);
    2022           0 :             break;
    2023             :         }
    2024             :     }
    2025             : 
    2026           0 :     return OGRERR_NONE;
    2027             : }
    2028             : 
    2029             : /******************************************************************************
    2030             :  Method to DeleteLayers in a .kmz datasource.
    2031             : 
    2032             :  Args:          iLayer  index of the layer to delete
    2033             : 
    2034             :  Returns:       OGRERR_NONE on success, OGRERR_FAILURE on failure
    2035             : 
    2036             : ******************************************************************************/
    2037             : 
    2038          16 : OGRErr OGRLIBKMLDataSource::DeleteLayerKmz(int iLayer)
    2039             : {
    2040          16 :     OGRLIBKMLLayer *poOgrLayer = papoLayers[iLayer];
    2041             : 
    2042          16 :     const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
    2043             : 
    2044          16 :     if (CPLTestBool(pszUseDocKml) && m_poKmlDocKml)
    2045             :     {
    2046             :         /***** loop over the features *****/
    2047          16 :         const size_t nKmlFeatures = m_poKmlDocKml->get_feature_array_size();
    2048             : 
    2049          16 :         for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
    2050             :         {
    2051             :             FeaturePtr poKmlFeat =
    2052          16 :                 m_poKmlDocKml->get_feature_array_at(iKmlFeature);
    2053             : 
    2054          16 :             if (poKmlFeat->IsA(kmldom::Type_NetworkLink))
    2055             :             {
    2056          16 :                 NetworkLinkPtr poKmlNetworkLink = AsNetworkLink(poKmlFeat);
    2057             : 
    2058             :                 /***** does it have a link? *****/
    2059          16 :                 if (poKmlNetworkLink->has_link())
    2060             :                 {
    2061          16 :                     LinkPtr poKmlLink = poKmlNetworkLink->get_link();
    2062             : 
    2063             :                     /***** does the link have a href? *****/
    2064          16 :                     if (poKmlLink->has_href())
    2065             :                     {
    2066          16 :                         const kmlengine::Href oKmlHref(poKmlLink->get_href());
    2067             : 
    2068             :                         /***** is the link relative? *****/
    2069          16 :                         if (oKmlHref.IsRelativePath())
    2070             :                         {
    2071          16 :                             if (EQUAL(oKmlHref.get_path().c_str(),
    2072             :                                       poOgrLayer->GetFileName()))
    2073             :                             {
    2074          16 :                                 m_poKmlDocKml->DeleteFeatureAt(iKmlFeature);
    2075          16 :                                 break;
    2076             :                             }
    2077             :                         }
    2078             :                     }
    2079             :                 }
    2080             :             }
    2081             :         }
    2082             :     }
    2083             : 
    2084          16 :     return OGRERR_NONE;
    2085             : }
    2086             : 
    2087             : /******************************************************************************
    2088             :  Method to delete a layer in a datasource.
    2089             : 
    2090             :  Args:          iLayer  index of the layer to delete
    2091             : 
    2092             :  Returns:       OGRERR_NONE on success, OGRERR_FAILURE on failure
    2093             : 
    2094             : ******************************************************************************/
    2095             : 
    2096          16 : OGRErr OGRLIBKMLDataSource::DeleteLayer(int iLayer)
    2097             : {
    2098          16 :     if (!bUpdate)
    2099           0 :         return OGRERR_UNSUPPORTED_OPERATION;
    2100             : 
    2101          16 :     if (iLayer >= nLayers)
    2102           0 :         return OGRERR_FAILURE;
    2103             : 
    2104          16 :     if (IsKml())
    2105             :     {
    2106           0 :         DeleteLayerKml(iLayer);
    2107             :     }
    2108          16 :     else if (IsKmz())
    2109             :     {
    2110           0 :         DeleteLayerKmz(iLayer);
    2111             :     }
    2112          16 :     else if (IsDir())
    2113             :     {
    2114          16 :         DeleteLayerKmz(iLayer);
    2115             : 
    2116             :         /***** delete the file the layer corresponds to *****/
    2117             :         const std::string osFilePath = CPLFormFilenameSafe(
    2118          32 :             GetDescription(), papoLayers[iLayer]->GetFileName(), nullptr);
    2119             :         VSIStatBufL oStatBufL;
    2120          16 :         if (!VSIStatL(osFilePath.c_str(), &oStatBufL))
    2121             :         {
    2122           0 :             if (VSIUnlink(osFilePath.c_str()))
    2123             :             {
    2124           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2125             :                          "ERROR Deleting Layer %s from filesystem as %s",
    2126           0 :                          papoLayers[iLayer]->GetName(), osFilePath.c_str());
    2127             :             }
    2128             :         }
    2129             :     }
    2130             : 
    2131          16 :     m_oMapLayers.erase(CPLString(papoLayers[iLayer]->GetName()).toupper());
    2132          16 :     delete papoLayers[iLayer];
    2133          16 :     memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
    2134          16 :             sizeof(void *) * (nLayers - iLayer - 1));
    2135          16 :     nLayers--;
    2136          16 :     bUpdated = true;
    2137             : 
    2138          16 :     return OGRERR_NONE;
    2139             : }
    2140             : 
    2141             : /******************************************************************************
    2142             :  Method to create a layer in a single file .kml.
    2143             : 
    2144             :  Args:          pszLayerName    name of the layer to create
    2145             :                 poOgrSRS        the SRS of the layer
    2146             :                 eGType          the layers geometry type
    2147             :                 papszOptions    layer creation options
    2148             : 
    2149             :  Returns:       return a pointer to the new layer or NULL on failure
    2150             : 
    2151             : ******************************************************************************/
    2152             : 
    2153          61 : OGRLIBKMLLayer *OGRLIBKMLDataSource::CreateLayerKml(
    2154             :     const char *pszLayerName, const OGRSpatialReference *poSRS,
    2155             :     OGRwkbGeometryType eGType, CSLConstList papszOptions)
    2156             : {
    2157          61 :     ContainerPtr poKmlLayerContainer = nullptr;
    2158             : 
    2159          61 :     if (m_poKmlDSContainer)
    2160             :     {
    2161          60 :         if (CPLFetchBool(papszOptions, "FOLDER", false))
    2162           2 :             poKmlLayerContainer = m_poKmlFactory->CreateFolder();
    2163             :         else
    2164          58 :             poKmlLayerContainer = m_poKmlFactory->CreateDocument();
    2165         120 :         poKmlLayerContainer->set_id(
    2166         120 :             OGRLIBKMLGetSanitizedNCName(pszLayerName).c_str());
    2167             : 
    2168          60 :         m_poKmlDSContainer->add_feature(poKmlLayerContainer);
    2169             :     }
    2170             : 
    2171             :     /***** create the layer *****/
    2172             :     OGRLIBKMLLayer *poOgrLayer =
    2173          61 :         AddLayer(pszLayerName, eGType, poSRS, this, nullptr,
    2174          61 :                  poKmlLayerContainer, "", TRUE, bUpdate, 1);
    2175             : 
    2176             :     /***** add the layer name as a <Name> *****/
    2177          61 :     if (poKmlLayerContainer)
    2178          60 :         poKmlLayerContainer->set_name(pszLayerName);
    2179           1 :     else if (CPLFetchBool(papszOptions, "FOLDER", false))
    2180             :     {
    2181           0 :         poOgrLayer->SetUpdateIsFolder(TRUE);
    2182             :     }
    2183             : 
    2184         122 :     return poOgrLayer;
    2185             : }
    2186             : 
    2187             : /******************************************************************************
    2188             :  Method to create a layer in a .kmz or dir.
    2189             : 
    2190             :  Args:          pszLayerName    name of the layer to create
    2191             :                 poOgrSRS        the SRS of the layer
    2192             :                 eGType          the layers geometry type
    2193             :                 papszOptions    layer creation options
    2194             : 
    2195             :  Returns:       return a pointer to the new layer or NULL on failure
    2196             : 
    2197             : ******************************************************************************/
    2198             : 
    2199          64 : OGRLIBKMLLayer *OGRLIBKMLDataSource::CreateLayerKmz(
    2200             :     const char *pszLayerName, const OGRSpatialReference *poSRS,
    2201             :     OGRwkbGeometryType eGType, CSLConstList papszOptions)
    2202             : {
    2203          64 :     DocumentPtr poKmlDocument = nullptr;
    2204             : 
    2205          64 :     if (!m_poKmlUpdate)
    2206             :     {
    2207             :         /***** add a network link to doc.kml *****/
    2208             :         const char *pszUseDocKml =
    2209          62 :             CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
    2210             : 
    2211          62 :         if (CPLTestBool(pszUseDocKml) && m_poKmlDocKml)
    2212             :         {
    2213          61 :             poKmlDocument = AsDocument(m_poKmlDocKml);
    2214             : 
    2215         122 :             NetworkLinkPtr poKmlNetLink = m_poKmlFactory->CreateNetworkLink();
    2216          61 :             poKmlNetLink->set_name(
    2217             :                 CSLFetchNameValueDef(papszOptions, "NAME", pszLayerName));
    2218             : 
    2219         122 :             LinkPtr poKmlLink = m_poKmlFactory->CreateLink();
    2220             : 
    2221          61 :             std::string oHref;
    2222          61 :             if (IsKmz())
    2223           8 :                 oHref.append("layers/");
    2224          61 :             oHref.append(pszLayerName);
    2225          61 :             oHref.append(".kml");
    2226          61 :             poKmlLink->set_href(oHref);
    2227             : 
    2228          61 :             poKmlNetLink->set_link(poKmlLink);
    2229          61 :             poKmlDocument->add_feature(poKmlNetLink);
    2230             :         }
    2231             : 
    2232             :         /***** create the layer *****/
    2233             : 
    2234          62 :         poKmlDocument = m_poKmlFactory->CreateDocument();
    2235         186 :         poKmlDocument->set_id(
    2236         124 :             OGRLIBKMLGetSanitizedNCName(pszLayerName).c_str());
    2237             :     }
    2238             : 
    2239             :     OGRLIBKMLLayer *poOgrLayer =
    2240          64 :         AddLayer(pszLayerName, eGType, poSRS, this, nullptr, poKmlDocument,
    2241          64 :                  CPLFormFilenameSafe(nullptr, pszLayerName, ".kml").c_str(),
    2242          64 :                  TRUE, bUpdate, 1);
    2243             : 
    2244             :     /***** add the layer name as a <Name> *****/
    2245          64 :     if (!m_poKmlUpdate)
    2246             :     {
    2247          62 :         poKmlDocument->set_name(pszLayerName);
    2248             :     }
    2249             : 
    2250         128 :     return poOgrLayer;
    2251             : }
    2252             : 
    2253             : /******************************************************************************
    2254             :  ICreateLayer()
    2255             : 
    2256             :  Args:          pszLayerName    name of the layer to create
    2257             :                 poOgrSRS        the SRS of the layer
    2258             :                 eGType          the layers geometry type
    2259             :                 papszOptions    layer creation options
    2260             : 
    2261             :  Returns:       return a pointer to the new layer or NULL on failure
    2262             : 
    2263             : ******************************************************************************/
    2264             : 
    2265             : OGRLayer *
    2266         125 : OGRLIBKMLDataSource::ICreateLayer(const char *pszLayerName,
    2267             :                                   const OGRGeomFieldDefn *poGeomFieldDefn,
    2268             :                                   CSLConstList papszOptions)
    2269             : {
    2270         125 :     if (!bUpdate)
    2271           0 :         return nullptr;
    2272             : 
    2273         125 :     if ((IsKmz() || IsDir()) && EQUAL(pszLayerName, "doc"))
    2274             :     {
    2275           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2276             :                  "'doc' is an invalid layer name in a KMZ file");
    2277           0 :         return nullptr;
    2278             :     }
    2279             : 
    2280         125 :     const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
    2281             :     const auto poOgrSRS =
    2282         125 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
    2283             : 
    2284         125 :     OGRLIBKMLLayer *poOgrLayer = nullptr;
    2285             : 
    2286             :     /***** kml DS *****/
    2287         125 :     if (IsKml())
    2288             :     {
    2289             :         poOgrLayer =
    2290          61 :             CreateLayerKml(pszLayerName, poOgrSRS, eGType, papszOptions);
    2291             :     }
    2292          64 :     else if (IsKmz() || IsDir())
    2293             :     {
    2294             :         poOgrLayer =
    2295          64 :             CreateLayerKmz(pszLayerName, poOgrSRS, eGType, papszOptions);
    2296             :     }
    2297             : 
    2298             :     const char *pszLookatLongitude =
    2299         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_LONGITUDE");
    2300             :     const char *pszLookatLatitude =
    2301         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_LATITUDE");
    2302             :     const char *pszLookatAltitude =
    2303         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDE");
    2304             :     const char *pszLookatHeading =
    2305         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_HEADING");
    2306         125 :     const char *pszLookatTilt = CSLFetchNameValue(papszOptions, "LOOKAT_TILT");
    2307             :     const char *pszLookatRange =
    2308         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_RANGE");
    2309             :     const char *pszLookatAltitudeMode =
    2310         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDEMODE");
    2311         125 :     if (poOgrLayer != nullptr && pszLookatLongitude != nullptr &&
    2312           2 :         pszLookatLatitude != nullptr && pszLookatRange != nullptr)
    2313             :     {
    2314           2 :         poOgrLayer->SetLookAt(pszLookatLongitude, pszLookatLatitude,
    2315             :                               pszLookatAltitude, pszLookatHeading,
    2316             :                               pszLookatTilt, pszLookatRange,
    2317             :                               pszLookatAltitudeMode);
    2318             :     }
    2319             :     else
    2320             :     {
    2321             :         const char *pszCameraLongitude =
    2322         123 :             CSLFetchNameValue(papszOptions, "CAMERA_LONGITUDE");
    2323             :         const char *pszCameraLatitude =
    2324         123 :             CSLFetchNameValue(papszOptions, "CAMERA_LATITUDE");
    2325             :         const char *pszCameraAltitude =
    2326         123 :             CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDE");
    2327             :         const char *pszCameraHeading =
    2328         123 :             CSLFetchNameValue(papszOptions, "CAMERA_HEADING");
    2329             :         const char *pszCameraTilt =
    2330         123 :             CSLFetchNameValue(papszOptions, "CAMERA_TILT");
    2331             :         const char *pszCameraRoll =
    2332         123 :             CSLFetchNameValue(papszOptions, "CAMERA_ROLL");
    2333             :         const char *pszCameraAltitudeMode =
    2334         123 :             CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDEMODE");
    2335         123 :         if (poOgrLayer != nullptr && pszCameraLongitude != nullptr &&
    2336           1 :             pszCameraLatitude != nullptr && pszCameraAltitude != nullptr &&
    2337             :             pszCameraAltitudeMode != nullptr)
    2338             :         {
    2339           1 :             poOgrLayer->SetCamera(pszCameraLongitude, pszCameraLatitude,
    2340             :                                   pszCameraAltitude, pszCameraHeading,
    2341             :                                   pszCameraTilt, pszCameraRoll,
    2342             :                                   pszCameraAltitudeMode);
    2343             :         }
    2344             :     }
    2345             : 
    2346             :     const char *pszRegionAdd =
    2347         125 :         CSLFetchNameValueDef(papszOptions, "ADD_REGION", "FALSE");
    2348         125 :     const char *pszRegionXMin = CSLFetchNameValue(papszOptions, "REGION_XMIN");
    2349         125 :     const char *pszRegionYMin = CSLFetchNameValue(papszOptions, "REGION_YMIN");
    2350         125 :     const char *pszRegionXMax = CSLFetchNameValue(papszOptions, "REGION_XMAX");
    2351         125 :     const char *pszRegionYMax = CSLFetchNameValue(papszOptions, "REGION_YMAX");
    2352             :     const char *pszRegionMinLodPixels =
    2353         125 :         CSLFetchNameValueDef(papszOptions, "REGION_MIN_LOD_PIXELS", "256");
    2354             :     const char *pszRegionMaxLodPixels =
    2355         125 :         CSLFetchNameValueDef(papszOptions, "REGION_MAX_LOD_PIXELS", "-1");
    2356             :     const char *pszRegionMinFadeExtent =
    2357         125 :         CSLFetchNameValueDef(papszOptions, "REGION_MIN_FADE_EXTENT", "0");
    2358             :     const char *pszRegionMaxFadeExtent =
    2359         125 :         CSLFetchNameValueDef(papszOptions, "REGION_MAX_FADE_EXTENT", "0");
    2360         125 :     if (poOgrLayer != nullptr && CPLTestBool(pszRegionAdd))
    2361             :     {
    2362           2 :         poOgrLayer->SetWriteRegion(
    2363             :             CPLAtof(pszRegionMinLodPixels), CPLAtof(pszRegionMaxLodPixels),
    2364             :             CPLAtof(pszRegionMinFadeExtent), CPLAtof(pszRegionMaxFadeExtent));
    2365           2 :         if (pszRegionXMin != nullptr && pszRegionYMin != nullptr &&
    2366           1 :             pszRegionXMax != nullptr && pszRegionYMax != nullptr)
    2367             :         {
    2368           1 :             const double xmin = CPLAtof(pszRegionXMin);
    2369           1 :             const double ymin = CPLAtof(pszRegionYMin);
    2370           1 :             const double xmax = CPLAtof(pszRegionXMax);
    2371           1 :             const double ymax = CPLAtof(pszRegionYMax);
    2372           1 :             if (xmin < xmax && ymin < ymax)
    2373           1 :                 poOgrLayer->SetRegionBounds(xmin, ymin, xmax, ymax);
    2374             :         }
    2375             :     }
    2376             : 
    2377         125 :     const char *pszSOHref = CSLFetchNameValue(papszOptions, "SO_HREF");
    2378         125 :     const char *pszSOName = CSLFetchNameValue(papszOptions, "SO_NAME");
    2379             :     const char *pszSODescription =
    2380         125 :         CSLFetchNameValue(papszOptions, "SO_DESCRIPTION");
    2381         125 :     const char *pszSOOverlayX = CSLFetchNameValue(papszOptions, "SO_OVERLAY_X");
    2382         125 :     const char *pszSOOverlayY = CSLFetchNameValue(papszOptions, "SO_OVERLAY_Y");
    2383             :     const char *pszSOOverlayXUnits =
    2384         125 :         CSLFetchNameValue(papszOptions, "SO_OVERLAY_XUNITS");
    2385             :     const char *pszSOOverlayYUnits =
    2386         125 :         CSLFetchNameValue(papszOptions, "SO_OVERLAY_YUNITS");
    2387         125 :     const char *pszSOScreenX = CSLFetchNameValue(papszOptions, "SO_SCREEN_X");
    2388         125 :     const char *pszSOScreenY = CSLFetchNameValue(papszOptions, "SO_SCREEN_Y");
    2389             :     const char *pszSOScreenXUnits =
    2390         125 :         CSLFetchNameValue(papszOptions, "SO_SCREEN_XUNITS");
    2391             :     const char *pszSOScreenYUnits =
    2392         125 :         CSLFetchNameValue(papszOptions, "SO_SCREEN_YUNITS");
    2393         125 :     const char *pszSOSizeX = CSLFetchNameValue(papszOptions, "SO_SIZE_X");
    2394         125 :     const char *pszSOSizeY = CSLFetchNameValue(papszOptions, "SO_SIZE_Y");
    2395             :     const char *pszSOSizeXUnits =
    2396         125 :         CSLFetchNameValue(papszOptions, "SO_SIZE_XUNITS");
    2397             :     const char *pszSOSizeYUnits =
    2398         125 :         CSLFetchNameValue(papszOptions, "SO_SIZE_YUNITS");
    2399         125 :     if (poOgrLayer != nullptr && pszSOHref != nullptr)
    2400             :     {
    2401           2 :         poOgrLayer->SetScreenOverlay(
    2402             :             pszSOHref, pszSOName, pszSODescription, pszSOOverlayX,
    2403             :             pszSOOverlayY, pszSOOverlayXUnits, pszSOOverlayYUnits, pszSOScreenX,
    2404             :             pszSOScreenY, pszSOScreenXUnits, pszSOScreenYUnits, pszSOSizeX,
    2405             :             pszSOSizeY, pszSOSizeXUnits, pszSOSizeYUnits);
    2406             :     }
    2407             : 
    2408             :     const char *pszListStyleType =
    2409         125 :         CSLFetchNameValue(papszOptions, "LISTSTYLE_TYPE");
    2410             :     const char *pszListStyleIconHref =
    2411         125 :         CSLFetchNameValue(papszOptions, "LISTSTYLE_ICON_HREF");
    2412         125 :     if (poOgrLayer != nullptr)
    2413             :     {
    2414         125 :         poOgrLayer->SetListStyle(pszListStyleType, pszListStyleIconHref);
    2415             :     }
    2416             : 
    2417         125 :     if (poOgrLayer != nullptr && poOgrLayer->GetKmlLayer())
    2418             :     {
    2419         122 :         SetCommonOptions(poOgrLayer->GetKmlLayer(), papszOptions);
    2420             :     }
    2421             : 
    2422             :     /***** mark the dataset as updated *****/
    2423         125 :     if (poOgrLayer)
    2424         125 :         bUpdated = true;
    2425             : 
    2426         125 :     return poOgrLayer;
    2427             : }
    2428             : 
    2429             : /******************************************************************************
    2430             :  Method to get a datasources style table.
    2431             : 
    2432             :  Args:          none
    2433             : 
    2434             :  Returns:       pointer to the datasources style table, or NULL if it does
    2435             :                 not have one
    2436             : 
    2437             : ******************************************************************************/
    2438             : 
    2439           7 : OGRStyleTable *OGRLIBKMLDataSource::GetStyleTable()
    2440             : {
    2441           7 :     return m_poStyleTable;
    2442             : }
    2443             : 
    2444             : /******************************************************************************
    2445             :   Method to write a style table to a single file .kml ds.
    2446             : 
    2447             :  Args:          poStyleTable    pointer to the style table to add
    2448             : 
    2449             :  Returns:       nothing
    2450             : 
    2451             : ******************************************************************************/
    2452             : 
    2453           4 : void OGRLIBKMLDataSource::SetStyleTable2Kml(OGRStyleTable *poStyleTable)
    2454             : {
    2455           4 :     if (!m_poKmlDSContainer)
    2456           0 :         return;
    2457             : 
    2458             :     /***** delete all the styles *****/
    2459             : 
    2460           4 :     DocumentPtr poKmlDocument = AsDocument(m_poKmlDSContainer);
    2461             :     int nKmlStyles =
    2462           4 :         static_cast<int>(poKmlDocument->get_styleselector_array_size());
    2463             : 
    2464           4 :     for (int iKmlStyle = nKmlStyles - 1; iKmlStyle >= 0; iKmlStyle--)
    2465             :     {
    2466           0 :         poKmlDocument->DeleteStyleSelectorAt(iKmlStyle);
    2467             :     }
    2468             : 
    2469             :     /***** add the new style table to the document *****/
    2470             : 
    2471           4 :     styletable2kml(poStyleTable, m_poKmlFactory, AsContainer(poKmlDocument),
    2472             :                    m_papszOptions);
    2473             : }
    2474             : 
    2475             : /******************************************************************************
    2476             :  Method to write a style table to a kmz ds.
    2477             : 
    2478             :  Args:          poStyleTable    pointer to the style table to add
    2479             : 
    2480             :  Returns:       nothing
    2481             : 
    2482             : ******************************************************************************/
    2483             : 
    2484           1 : void OGRLIBKMLDataSource::SetStyleTable2Kmz(OGRStyleTable *poStyleTable)
    2485             : {
    2486           1 :     if (m_poKmlStyleKml || poStyleTable != nullptr)
    2487             :     {
    2488             :         /***** replace the style document with a new one *****/
    2489             : 
    2490           1 :         m_poKmlStyleKml = m_poKmlFactory->CreateDocument();
    2491           1 :         m_poKmlStyleKml->set_id("styleId");
    2492             : 
    2493           1 :         styletable2kml(poStyleTable, m_poKmlFactory, m_poKmlStyleKml);
    2494             :     }
    2495           1 : }
    2496             : 
    2497             : /******************************************************************************
    2498             :  Method to write a style table to a datasource.
    2499             : 
    2500             :  Args:          poStyleTable    pointer to the style table to add
    2501             : 
    2502             :  Returns:       nothing
    2503             : 
    2504             :  Note: This method assumes ownership of the style table.
    2505             : 
    2506             : ******************************************************************************/
    2507             : 
    2508           5 : void OGRLIBKMLDataSource::SetStyleTableDirectly(OGRStyleTable *poStyleTable)
    2509             : {
    2510           5 :     if (!bUpdate)
    2511           0 :         return;
    2512             : 
    2513           5 :     if (m_poStyleTable)
    2514           0 :         delete m_poStyleTable;
    2515             : 
    2516           5 :     m_poStyleTable = poStyleTable;
    2517             : 
    2518             :     /***** a kml datasource? *****/
    2519           5 :     if (IsKml())
    2520           4 :         SetStyleTable2Kml(m_poStyleTable);
    2521             : 
    2522           1 :     else if (IsKmz() || IsDir())
    2523           1 :         SetStyleTable2Kmz(m_poStyleTable);
    2524             : 
    2525           5 :     bUpdated = true;
    2526             : }
    2527             : 
    2528             : /******************************************************************************
    2529             :  Method to write a style table to a datasource.
    2530             : 
    2531             :  Args:          poStyleTable    pointer to the style table to add
    2532             : 
    2533             :  Returns:       nothing
    2534             : 
    2535             :  Note:  This method copies the style table, and the user will still be
    2536             :         responsible for its destruction.
    2537             : 
    2538             : ******************************************************************************/
    2539             : 
    2540           5 : void OGRLIBKMLDataSource::SetStyleTable(OGRStyleTable *poStyleTable)
    2541             : {
    2542           5 :     if (!bUpdate)
    2543           0 :         return;
    2544             : 
    2545           5 :     if (poStyleTable)
    2546           4 :         SetStyleTableDirectly(poStyleTable->Clone());
    2547             :     else
    2548           1 :         SetStyleTableDirectly(nullptr);
    2549             : }
    2550             : 
    2551             : /******************************************************************************
    2552             :  Test if capability is available.
    2553             : 
    2554             :  Args:          pszCap  datasource capability name to test
    2555             : 
    2556             :  Returns:       TRUE or FALSE
    2557             : 
    2558             :  ODsCCreateLayer: True if this datasource can create new layers.
    2559             :  ODsCDeleteLayer: True if this datasource can delete existing layers.
    2560             : 
    2561             : ******************************************************************************/
    2562             : 
    2563         104 : int OGRLIBKMLDataSource::TestCapability(const char *pszCap)
    2564             : {
    2565         104 :     if (EQUAL(pszCap, ODsCCreateLayer))
    2566          38 :         return bUpdate;
    2567          66 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
    2568          18 :         return bUpdate;
    2569          48 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
    2570           0 :         return bUpdate;
    2571          48 :     else if (EQUAL(pszCap, ODsCZGeometries))
    2572          22 :         return TRUE;
    2573             : 
    2574          26 :     return FALSE;
    2575             : }

Generated by: LCOV version 1.14