LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/libkml - ogrlibkmldatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 873 1063 82.1 %
Date: 2024-11-21 22:18:42 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 :         auto nNewPos = oKml.find(pszStartTag, nPos);
     158         122 :         if (nNewPos != std::string::npos)
     159             :         {
     160           1 :             pszEndTag = "</MultiPolygon>";
     161             :         }
     162             :         else
     163             :         {
     164         121 :             pszStartTag = "<MultiLineString>";
     165         121 :             nNewPos = oKml.find(pszStartTag, nPos);
     166         121 :             if (nNewPos != std::string::npos)
     167             :             {
     168           1 :                 pszEndTag = "</MultiLineString>";
     169             :             }
     170             :             else
     171             :             {
     172         120 :                 pszStartTag = "<MultiPoint>";
     173         120 :                 nNewPos = oKml.find(pszStartTag, nPos);
     174         120 :                 if (nNewPos != std::string::npos)
     175           1 :                     pszEndTag = "</MultiPoint>";
     176             :                 else
     177         119 :                     break;
     178             :             }
     179             :         }
     180           3 :         nPos = nNewPos;
     181           3 :         oKml.replace(nPos, strlen(pszStartTag), "<MultiGeometry>");
     182           3 :         nPos = oKml.find(pszEndTag, nPos);
     183           3 :         if (nPos == std::string::npos)
     184           0 :             break;
     185           3 :         oKml.replace(nPos, strlen(pszEndTag), "</MultiGeometry>");
     186           3 :     }
     187         119 : }
     188             : 
     189             : /************************************************************************/
     190             : /*                       OGRLIBKMLRemoveSpaces()                        */
     191             : /************************************************************************/
     192             : 
     193         456 : static void OGRLIBKMLRemoveSpaces(std::string &osKml,
     194             :                                   const std::string &osNeedle)
     195             : {
     196         456 :     size_t nPos = 0;
     197        1368 :     const std::string osLtNeedle(std::string("<").append(osNeedle));
     198         912 :     std::string osTmp;
     199         912 :     std::string osRet;
     200             :     while (true)
     201             :     {
     202         545 :         auto nPosNew = osKml.find(osLtNeedle, nPos);
     203         545 :         if (nPosNew == std::string::npos)
     204             :         {
     205         456 :             osRet.append(osKml, nPos);
     206         456 :             break;
     207             :         }
     208          89 :         const size_t nPosOri = nPosNew;
     209          89 :         nPosNew = osKml.find(">\n", nPosNew);
     210          89 :         if (nPosNew == std::string::npos || nPosNew + 2 == osKml.size())
     211             :         {
     212           0 :             osRet.append(osKml, nPos);
     213           0 :             break;
     214             :         }
     215             :         // Skip \n character
     216          89 :         osRet.append(osKml, nPos, nPosNew - nPos + 1);
     217          89 :         nPos = nPosNew + 2;
     218             : 
     219             :         // Remove leading spaces of "    </{osNeedle}>"
     220          89 :         osTmp.clear();
     221        1077 :         for (size_t nPosTmp = nPosOri - 1; osKml[nPosTmp] == ' '; nPosTmp--)
     222             :         {
     223         988 :             osTmp += ' ';
     224             :         }
     225          89 :         osTmp += "</";
     226          89 :         osTmp += osNeedle;
     227          89 :         osTmp += '>';
     228          89 :         nPosNew = osKml.find(osTmp, nPos);
     229          89 :         if (nPosNew != std::string::npos)
     230             :         {
     231          89 :             osRet.append(osKml, nPos, nPosNew - nPos);
     232          89 :             osRet += "</";
     233          89 :             osRet += osNeedle;
     234          89 :             osRet += '>';
     235          89 :             nPos = nPosNew + osTmp.size();
     236             :         }
     237             :         else
     238             :         {
     239           0 :             osRet.append(osKml, nPos);
     240           0 :             break;
     241             :         }
     242          89 :     }
     243         456 :     osKml = std::move(osRet);
     244         456 : }
     245             : 
     246             : /************************************************************************/
     247             : /*                      OGRLIBKMLPostProcessOutput()                    */
     248             : /************************************************************************/
     249             : 
     250             : // Substitute deprecated <Snippet> by <snippet> since libkml currently
     251             : // only supports Snippet but ogckml22.xsd has deprecated it in favor of snippet.
     252         152 : static void OGRLIBKMLPostProcessOutput(std::string &oKml)
     253             : {
     254             :     // Manually add <?xml> node since libkml does not produce it currently
     255             :     // and this is useful in some circumstances (#5407).
     256         152 :     if (!(oKml.size() >= 2 && oKml[0] == '<' && oKml[1] == '?'))
     257         152 :         oKml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + oKml;
     258             : 
     259         152 :     size_t nPos = 0;
     260             :     while (true)
     261             :     {
     262         155 :         nPos = oKml.find("<Snippet>", nPos);
     263         155 :         if (nPos == std::string::npos)
     264             :         {
     265         152 :             break;
     266             :         }
     267           3 :         oKml[nPos + 1] = 's';
     268           3 :         nPos = oKml.find("</Snippet>", nPos);
     269           3 :         if (nPos == std::string::npos)
     270             :         {
     271           0 :             break;
     272             :         }
     273           3 :         oKml[nPos + 2] = 's';
     274             :     }
     275             : 
     276             :     // Fix indentation problems.
     277         152 :     OGRLIBKMLRemoveSpaces(oKml, "snippet");
     278         152 :     OGRLIBKMLRemoveSpaces(oKml, "linkSnippet");
     279         152 :     OGRLIBKMLRemoveSpaces(oKml, "SimpleData");
     280         152 : }
     281             : 
     282             : /******************************************************************************
     283             :  Method to write a single file ds .kml at ds destroy.
     284             : 
     285             :  Args:          none
     286             : 
     287             :  Returns:       nothing
     288             : 
     289             : ******************************************************************************/
     290             : 
     291          61 : bool OGRLIBKMLDataSource::WriteKml()
     292             : {
     293         122 :     std::string oKmlFilename = GetDescription();
     294             : 
     295          61 :     if (m_poKmlDSContainer && m_poKmlDSContainer->IsA(kmldom::Type_Document))
     296             :     {
     297         120 :         DocumentPtr poKmlDocument = AsDocument(m_poKmlDSContainer);
     298             : 
     299          60 :         ParseDocumentOptions(m_poKmlDSKml, poKmlDocument);
     300             : 
     301         124 :         for (int iLayer = 0; iLayer < nLayers; iLayer++)
     302             :         {
     303          64 :             SchemaPtr poKmlSchema = nullptr;
     304             : 
     305          64 :             if ((poKmlSchema = papoLayers[iLayer]->GetKmlSchema()))
     306             :             {
     307             :                 const size_t nKmlSchemas =
     308           4 :                     poKmlDocument->get_schema_array_size();
     309           8 :                 SchemaPtr poKmlSchema2 = nullptr;
     310             : 
     311           4 :                 for (size_t iKmlSchema = 0; iKmlSchema < nKmlSchemas;
     312             :                      iKmlSchema++)
     313             :                 {
     314             :                     poKmlSchema2 =
     315           1 :                         poKmlDocument->get_schema_array_at(iKmlSchema);
     316           1 :                     if (poKmlSchema2 == poKmlSchema)
     317           1 :                         break;
     318             :                 }
     319             : 
     320           4 :                 if (poKmlSchema2 != poKmlSchema)
     321           3 :                     poKmlDocument->add_schema(poKmlSchema);
     322             :             }
     323             : 
     324          64 :             papoLayers[iLayer]->Finalize(poKmlDocument);
     325             :         }
     326             :     }
     327             :     else
     328             :     {
     329           1 :         ParseDocumentOptions(m_poKmlDSKml, nullptr);
     330             :     }
     331             : 
     332         122 :     std::string oKmlOut;
     333          61 :     oKmlOut = kmldom::SerializePretty(m_poKmlDSKml);
     334          61 :     OGRLIBKMLPostProcessOutput(oKmlOut);
     335             : 
     336          61 :     bool bRet = true;
     337          61 :     if (!oKmlOut.empty())
     338             :     {
     339          61 :         VSILFILE *fp = VSIFOpenExL(oKmlFilename.c_str(), "wb", true);
     340          61 :         if (fp == nullptr)
     341             :         {
     342           0 :             CPLError(CE_Failure, CPLE_FileIO, "Error writing %s: %s",
     343             :                      oKmlFilename.c_str(), VSIGetLastErrorMsg());
     344           0 :             return false;
     345             :         }
     346             : 
     347          61 :         if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
     348           0 :             bRet = false;
     349          61 :         if (VSIFCloseL(fp) != 0)
     350           0 :             bRet = false;
     351             :     }
     352          61 :     return bRet;
     353             : }
     354             : 
     355             : /******************************************************************************/
     356             : /*                      OGRLIBKMLCreateOGCKml22()                             */
     357             : /******************************************************************************/
     358             : 
     359         148 : static KmlPtr OGRLIBKMLCreateOGCKml22(KmlFactory *poFactory,
     360             :                                       char **papszOptions = nullptr)
     361             : {
     362         148 :     const char *pszAuthorName = CSLFetchNameValue(papszOptions, "AUTHOR_NAME");
     363         148 :     const char *pszAuthorURI = CSLFetchNameValue(papszOptions, "AUTHOR_URI");
     364             :     const char *pszAuthorEmail =
     365         148 :         CSLFetchNameValue(papszOptions, "AUTHOR_EMAIL");
     366         148 :     const char *pszLink = CSLFetchNameValue(papszOptions, "LINK");
     367         147 :     const bool bWithAtom = pszAuthorName != nullptr ||
     368         147 :                            pszAuthorURI != nullptr ||
     369         295 :                            pszAuthorEmail != nullptr || pszLink != nullptr;
     370             : 
     371         148 :     KmlPtr kml = poFactory->CreateKml();
     372         148 :     if (bWithAtom)
     373             :     {
     374           2 :         const char *kAttrs[] = {"xmlns", "http://www.opengis.net/kml/2.2",
     375             :                                 "xmlns:atom", "http://www.w3.org/2005/Atom",
     376             :                                 nullptr};
     377           2 :         kml->AddUnknownAttributes(Attributes::Create(kAttrs));
     378             :     }
     379             :     else
     380             :     {
     381         146 :         const char *kAttrs[] = {"xmlns", "http://www.opengis.net/kml/2.2",
     382             :                                 nullptr};
     383         146 :         kml->AddUnknownAttributes(Attributes::Create(kAttrs));
     384             :     }
     385         148 :     return kml;
     386             : }
     387             : 
     388             : /******************************************************************************
     389             :  Method to write a ds .kmz at ds destroy.
     390             : 
     391             :  Args:          none
     392             : 
     393             :  Returns:       nothing
     394             : 
     395             : ******************************************************************************/
     396             : 
     397           8 : bool OGRLIBKMLDataSource::WriteKmz()
     398             : {
     399          16 :     std::string osTmpFilename;
     400          15 :     if (!VSISupportsRandomWrite(GetDescription(), false) ||
     401           7 :         EQUAL(CPLGetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", ""),
     402             :               "FORCED"))
     403             :     {
     404             :         osTmpFilename =
     405           2 :             CPLGenerateTempFilename(CPLGetBasename(GetDescription()));
     406             :     }
     407             : 
     408          10 :     void *hZIP = CPLCreateZip(osTmpFilename.empty() ? GetDescription()
     409           2 :                                                     : osTmpFilename.c_str(),
     410             :                               nullptr);
     411             : 
     412           8 :     if (!hZIP)
     413             :     {
     414           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess, "Error creating %s: %s",
     415           0 :                  GetDescription(), VSIGetLastErrorMsg());
     416           0 :         return false;
     417             :     }
     418             : 
     419           8 :     bool bRet = true;
     420             : 
     421             :     /***** write out the doc.kml ****/
     422           8 :     const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
     423             : 
     424           8 :     if (CPLTestBool(pszUseDocKml) && (m_poKmlDocKml || m_poKmlUpdate))
     425             :     {
     426             :         // If we do not have the doc.kmlroot
     427             :         // make it and add the container.
     428           7 :         if (!m_poKmlDocKmlRoot)
     429             :         {
     430             :             m_poKmlDocKmlRoot =
     431           7 :                 OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions);
     432             : 
     433           7 :             auto kml = AsKml(m_poKmlDocKmlRoot);
     434           7 :             if (m_poKmlDocKml)
     435             :             {
     436           6 :                 if (kml)
     437             :                 {
     438           6 :                     kml->set_feature(m_poKmlDocKml);
     439             :                 }
     440             :             }
     441             : 
     442           7 :             ParseDocumentOptions(std::move(kml), AsDocument(m_poKmlDocKml));
     443             :         }
     444             : 
     445          14 :         std::string oKmlOut = kmldom::SerializePretty(m_poKmlDocKmlRoot);
     446           7 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     447             : 
     448          14 :         if (CPLCreateFileInZip(hZIP, "doc.kml", nullptr) != CE_None ||
     449           7 :             CPLWriteFileInZip(hZIP, oKmlOut.data(),
     450           7 :                               static_cast<int>(oKmlOut.size())) != CE_None)
     451             :         {
     452           0 :             bRet = false;
     453           0 :             CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
     454           0 :                      "doc.kml", GetDescription());
     455             :         }
     456           7 :         CPLCloseFileInZip(hZIP);
     457             :     }
     458             : 
     459             :     /***** loop though the layers and write them *****/
     460          17 :     for (int iLayer = 0; iLayer < nLayers && !m_poKmlUpdate; iLayer++)
     461             :     {
     462          18 :         ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer();
     463             : 
     464           9 :         if (poKmlContainer->IsA(kmldom::Type_Document))
     465             :         {
     466          18 :             DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
     467           9 :             SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema();
     468             : 
     469          13 :             if (!poKmlDocument->get_schema_array_size() && poKmlSchema &&
     470           4 :                 poKmlSchema->get_simplefield_array_size())
     471             :             {
     472           4 :                 poKmlDocument->add_schema(std::move(poKmlSchema));
     473             :             }
     474             : 
     475           9 :             papoLayers[iLayer]->Finalize(std::move(poKmlDocument));
     476             :         }
     477             : 
     478             :         // If we do not have the layers root
     479             :         // make it and add the container.
     480          18 :         KmlPtr poKmlKml = nullptr;
     481             : 
     482           9 :         if (!(poKmlKml = AsKml(papoLayers[iLayer]->GetKmlLayerRoot())))
     483             :         {
     484           9 :             poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
     485             : 
     486           9 :             poKmlKml->set_feature(poKmlContainer);
     487             :         }
     488             : 
     489          18 :         std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
     490           9 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     491             : 
     492           9 :         if (iLayer == 0 && CPLTestBool(pszUseDocKml))
     493             :         {
     494           5 :             CPL_IGNORE_RET_VAL(CPLCreateFileInZip(hZIP, "layers/", nullptr));
     495             :         }
     496             : 
     497           9 :         const char *pszLayerFileName = nullptr;
     498           9 :         if (CPLTestBool(pszUseDocKml))
     499             :             pszLayerFileName =
     500           8 :                 CPLSPrintf("layers/%s", papoLayers[iLayer]->GetFileName());
     501             :         else
     502           1 :             pszLayerFileName = papoLayers[iLayer]->GetFileName();
     503             : 
     504          18 :         if (CPLCreateFileInZip(hZIP, pszLayerFileName, nullptr) != CE_None ||
     505           9 :             CPLWriteFileInZip(hZIP, oKmlOut.data(),
     506           9 :                               static_cast<int>(oKmlOut.size())) != CE_None)
     507           0 :             CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
     508           0 :                      papoLayers[iLayer]->GetFileName(), GetDescription());
     509           9 :         CPLCloseFileInZip(hZIP);
     510             :     }
     511             : 
     512             :     /***** write the style table *****/
     513           8 :     if (m_poKmlStyleKml)
     514             :     {
     515           2 :         KmlPtr poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
     516             : 
     517           1 :         poKmlKml->set_feature(m_poKmlStyleKml);
     518           2 :         std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
     519           1 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     520             : 
     521           1 :         if (CPLCreateFileInZip(hZIP, "style/", nullptr) != CE_None ||
     522           2 :             CPLCreateFileInZip(hZIP, "style/style.kml", nullptr) != CE_None ||
     523           1 :             CPLWriteFileInZip(hZIP, oKmlOut.data(),
     524           1 :                               static_cast<int>(oKmlOut.size())) != CE_None)
     525             :         {
     526           0 :             bRet = false;
     527           0 :             CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
     528           0 :                      "style/style.kml", GetDescription());
     529             :         }
     530           1 :         CPLCloseFileInZip(hZIP);
     531             :     }
     532             : 
     533           8 :     CPLCloseZip(hZIP);
     534             : 
     535           8 :     if (!osTmpFilename.empty())
     536             :     {
     537           2 :         if (bRet)
     538             :         {
     539           2 :             bRet = CPLCopyFile(GetDescription(), osTmpFilename.c_str()) == 0;
     540           2 :             if (!bRet)
     541           1 :                 CPLError(CE_Failure, CPLE_FileIO,
     542           1 :                          "Cannot copy temporary file to %s", GetDescription());
     543             :         }
     544           2 :         VSIUnlink(osTmpFilename.c_str());
     545             :     }
     546             : 
     547           8 :     return bRet;
     548             : }
     549             : 
     550             : /******************************************************************************
     551             :  Method to write a dir ds at ds destroy.
     552             : 
     553             :  Args:          none
     554             : 
     555             :  Returns:       nothing
     556             : 
     557             : ******************************************************************************/
     558             : 
     559          37 : bool OGRLIBKMLDataSource::WriteDir()
     560             : {
     561             :     /***** write out the doc.kml ****/
     562          37 :     const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
     563             : 
     564          37 :     bool bRet = true;
     565          37 :     if (CPLTestBool(pszUseDocKml) && (m_poKmlDocKml || m_poKmlUpdate))
     566             :     {
     567             :         // If we don't have the doc.kml root
     568             :         // make it and add the container.
     569          37 :         if (!m_poKmlDocKmlRoot)
     570             :         {
     571             :             m_poKmlDocKmlRoot =
     572          37 :                 OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions);
     573          37 :             auto kml = AsKml(m_poKmlDocKmlRoot);
     574          37 :             if (kml)
     575             :             {
     576          37 :                 if (m_poKmlDocKml)
     577          36 :                     kml->set_feature(m_poKmlDocKml);
     578             :             }
     579             : 
     580          37 :             ParseDocumentOptions(std::move(kml), AsDocument(m_poKmlDocKml));
     581             :         }
     582             : 
     583          37 :         std::string oKmlOut = kmldom::SerializePretty(m_poKmlDocKmlRoot);
     584          37 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     585             : 
     586             :         const char *pszOutfile =
     587          37 :             CPLFormFilename(GetDescription(), "doc.kml", nullptr);
     588             : 
     589          37 :         VSILFILE *fp = VSIFOpenExL(pszOutfile, "wb", true);
     590          37 :         if (fp == nullptr)
     591             :         {
     592           0 :             CPLError(CE_Failure, CPLE_FileIO, "Error writing %s to %s: %s",
     593           0 :                      "doc.kml", GetDescription(), VSIGetLastErrorMsg());
     594           0 :             return false;
     595             :         }
     596             : 
     597          37 :         if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
     598           0 :             bRet = false;
     599          37 :         if (VSIFCloseL(fp) != 0)
     600           0 :             bRet = false;
     601             :     }
     602             : 
     603             :     /***** loop though the layers and write them *****/
     604          74 :     for (int iLayer = 0; iLayer < nLayers && !m_poKmlUpdate; iLayer++)
     605             :     {
     606          37 :         ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer();
     607             : 
     608          37 :         if (poKmlContainer->IsA(kmldom::Type_Document))
     609             :         {
     610          74 :             DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
     611          37 :             SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema();
     612             : 
     613          54 :             if (!poKmlDocument->get_schema_array_size() && poKmlSchema &&
     614          17 :                 poKmlSchema->get_simplefield_array_size())
     615             :             {
     616          17 :                 poKmlDocument->add_schema(std::move(poKmlSchema));
     617             :             }
     618             : 
     619          37 :             papoLayers[iLayer]->Finalize(std::move(poKmlDocument));
     620             :         }
     621             : 
     622             :         // If we do not have the layers root
     623             :         // make it and add the container.
     624          37 :         KmlPtr poKmlKml = nullptr;
     625             : 
     626          37 :         if (!(poKmlKml = AsKml(papoLayers[iLayer]->GetKmlLayerRoot())))
     627             :         {
     628          37 :             poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
     629             : 
     630          37 :             poKmlKml->set_feature(poKmlContainer);
     631             :         }
     632             : 
     633          37 :         std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
     634          37 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     635             : 
     636          37 :         const char *pszOutfile = CPLFormFilename(
     637          37 :             GetDescription(), papoLayers[iLayer]->GetFileName(), nullptr);
     638             : 
     639          37 :         VSILFILE *fp = VSIFOpenL(pszOutfile, "wb");
     640          37 :         if (fp == nullptr)
     641             :         {
     642           0 :             CPLError(CE_Failure, CPLE_FileIO, "ERROR Writing %s to %s",
     643           0 :                      papoLayers[iLayer]->GetFileName(), GetDescription());
     644           0 :             return false;
     645             :         }
     646             : 
     647          37 :         if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
     648           0 :             bRet = false;
     649          37 :         if (VSIFCloseL(fp) != 0)
     650           0 :             bRet = false;
     651             :     }
     652             : 
     653             :     /***** write the style table *****/
     654          37 :     if (m_poKmlStyleKml)
     655             :     {
     656           0 :         KmlPtr poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
     657             : 
     658           0 :         poKmlKml->set_feature(m_poKmlStyleKml);
     659           0 :         std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
     660           0 :         OGRLIBKMLPostProcessOutput(oKmlOut);
     661             : 
     662             :         const char *pszOutfile =
     663           0 :             CPLFormFilename(GetDescription(), "style.kml", nullptr);
     664             : 
     665           0 :         VSILFILE *fp = VSIFOpenL(pszOutfile, "wb");
     666           0 :         if (fp == nullptr)
     667             :         {
     668           0 :             CPLError(CE_Failure, CPLE_FileIO, "ERROR Writing %s to %s",
     669           0 :                      "style.kml", GetDescription());
     670           0 :             return false;
     671             :         }
     672             : 
     673           0 :         if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
     674           0 :             bRet = false;
     675           0 :         if (VSIFCloseL(fp) != 0)
     676           0 :             bRet = false;
     677             :     }
     678          37 :     return bRet;
     679             : }
     680             : 
     681             : /******************************************************************************
     682             :  Method to write the datasource to disk.
     683             : 
     684             :  Args:      none
     685             : 
     686             :  Returns    nothing
     687             : 
     688             : ******************************************************************************/
     689             : 
     690         398 : CPLErr OGRLIBKMLDataSource::FlushCache(bool /* bAtClosing */)
     691             : {
     692         398 :     if (!bUpdated)
     693         292 :         return CE_None;
     694             : 
     695         106 :     CPLErr eErr = CE_None;
     696         106 :     if (bUpdate && IsKml())
     697             :     {
     698          61 :         if (!WriteKml())
     699           0 :             eErr = CE_Failure;
     700             :     }
     701          45 :     else if (bUpdate && IsKmz())
     702             :     {
     703           8 :         if (!WriteKmz())
     704           1 :             eErr = CE_Failure;
     705             :     }
     706          37 :     else if (bUpdate && IsDir())
     707             :     {
     708          37 :         if (!WriteDir())
     709           0 :             eErr = CE_Failure;
     710             :     }
     711             : 
     712         106 :     bUpdated = false;
     713         106 :     return eErr;
     714             : }
     715             : 
     716             : /******************************************************************************
     717             :  OGRLIBKMLDataSource Destructor
     718             : 
     719             :  Args:          none
     720             : 
     721             :  Returns:       nothing
     722             : 
     723             : ******************************************************************************/
     724             : 
     725         782 : OGRLIBKMLDataSource::~OGRLIBKMLDataSource()
     726             : {
     727             :     /***** sync the DS to disk *****/
     728         391 :     OGRLIBKMLDataSource::FlushCache(true);
     729             : 
     730         736 :     for (int i = 0; i < nLayers; i++)
     731         345 :         delete papoLayers[i];
     732             : 
     733         391 :     CPLFree(papoLayers);
     734             : 
     735         391 :     CSLDestroy(m_papszOptions);
     736         782 : }
     737             : 
     738             : /******************************************************************************
     739             :  Method to parse a schemas out of a document.
     740             : 
     741             :  Args:          poKmlDocument   pointer to the document to parse
     742             : 
     743             :  Returns:       nothing
     744             : 
     745             : ******************************************************************************/
     746             : 
     747          26 : SchemaPtr OGRLIBKMLDataSource::FindSchema(const char *pszSchemaUrl)
     748             : {
     749          26 :     if (!pszSchemaUrl || !*pszSchemaUrl)
     750           0 :         return nullptr;
     751             : 
     752          26 :     char *pszID = nullptr;
     753          26 :     char *pszFile = nullptr;
     754          26 :     char *pszSchemaName = nullptr;
     755          52 :     DocumentPtr poKmlDocument = nullptr;
     756          52 :     SchemaPtr poKmlSchemaResult = nullptr;
     757             : 
     758          26 :     if (*pszSchemaUrl == '#')
     759             :     {
     760          24 :         pszID = CPLStrdup(pszSchemaUrl + 1);
     761             : 
     762             :         /***** kml *****/
     763          24 :         if (IsKml() && m_poKmlDSContainer->IsA(kmldom::Type_Document))
     764          24 :             poKmlDocument = AsDocument(m_poKmlDSContainer);
     765             : 
     766             :         /***** kmz *****/
     767           0 :         else if ((IsKmz() || IsDir()) && m_poKmlDocKml &&
     768           0 :                  m_poKmlDocKml->IsA(kmldom::Type_Document))
     769           0 :             poKmlDocument = AsDocument(m_poKmlDocKml);
     770             :     }
     771           2 :     else if (const char *pszPound = strchr(pszSchemaUrl, '#'))
     772             :     {
     773           0 :         pszFile = CPLStrdup(pszSchemaUrl);
     774           0 :         pszID = CPLStrdup(pszPound + 1);
     775           0 :         pszFile[pszPound - pszSchemaUrl] = '\0';
     776             :     }
     777             :     else
     778             :     {
     779           2 :         pszSchemaName = CPLStrdup(pszSchemaUrl);
     780             : 
     781             :         /***** kml *****/
     782           2 :         if (IsKml() && m_poKmlDSContainer->IsA(kmldom::Type_Document))
     783           2 :             poKmlDocument = AsDocument(m_poKmlDSContainer);
     784             : 
     785             :         /***** kmz *****/
     786             : 
     787           0 :         else if ((IsKmz() || IsDir()) && m_poKmlDocKml &&
     788           0 :                  m_poKmlDocKml->IsA(kmldom::Type_Document))
     789           0 :             poKmlDocument = AsDocument(m_poKmlDocKml);
     790             :     }
     791             : 
     792          26 :     if (poKmlDocument)
     793             :     {
     794          26 :         size_t nKmlSchemas = poKmlDocument->get_schema_array_size();
     795             : 
     796          29 :         for (size_t iKmlSchema = 0; iKmlSchema < nKmlSchemas; iKmlSchema++)
     797             :         {
     798             :             SchemaPtr poKmlSchema =
     799          27 :                 poKmlDocument->get_schema_array_at(iKmlSchema);
     800          27 :             if (poKmlSchema->has_id() && pszID)
     801             :             {
     802          25 :                 if (EQUAL(pszID, poKmlSchema->get_id().c_str()))
     803             :                 {
     804          23 :                     poKmlSchemaResult = std::move(poKmlSchema);
     805          23 :                     break;
     806             :                 }
     807             :             }
     808             : 
     809           2 :             else if (poKmlSchema->has_name() && pszSchemaName)
     810             :             {
     811           2 :                 if (EQUAL(pszSchemaName, poKmlSchema->get_name().c_str()))
     812             :                 {
     813           1 :                     poKmlSchemaResult = std::move(poKmlSchema);
     814           1 :                     break;
     815             :                 }
     816             :             }
     817             :         }
     818             :     }
     819             : 
     820          26 :     CPLFree(pszFile);
     821          26 :     CPLFree(pszID);
     822          26 :     CPLFree(pszSchemaName);
     823             : 
     824          26 :     return poKmlSchemaResult;
     825             : }
     826             : 
     827             : /******************************************************************************
     828             :  Method to allocate memory for the layer array, create the layer,
     829             :  and add it to the layer array.
     830             : 
     831             :  Args:          pszLayerName    the name of the layer
     832             :                 eGType          the layers geometry type
     833             :                 poOgrDS         pointer to the datasource the layer is in
     834             :                 poKmlRoot       pointer to the root kml element of the layer
     835             :                 pszFileName     the filename of the layer
     836             :                 bNew            true if its a new layer
     837             :                 bUpdate         true if the layer is writable
     838             :                 nGuess          a guess at the number of additional layers
     839             :                                 we are going to need
     840             : 
     841             :  Returns:       Pointer to the new layer
     842             : ******************************************************************************/
     843             : 
     844         361 : OGRLIBKMLLayer *OGRLIBKMLDataSource::AddLayer(
     845             :     const char *pszLayerName, OGRwkbGeometryType eGType,
     846             :     const OGRSpatialReference *poSRS, OGRLIBKMLDataSource *poOgrDS,
     847             :     ElementPtr poKmlRoot, ContainerPtr poKmlContainer, const char *pszFileName,
     848             :     int bNew, int bUpdateIn, int nGuess)
     849             : {
     850             :     // Build unique layer name
     851         361 :     CPLString osUniqueLayername(pszLayerName);
     852         361 :     int nIter = 2;
     853             :     while (true)
     854             :     {
     855         362 :         if (GetLayerByName(osUniqueLayername) == nullptr)
     856         361 :             break;
     857           1 :         osUniqueLayername = CPLSPrintf("%s (#%d)", pszLayerName, nIter);
     858           1 :         nIter++;
     859             :     }
     860             : 
     861             :     /***** check to see if we have enough space to store the layer *****/
     862         361 :     if (nLayers == nAllocated)
     863             :     {
     864         257 :         nAllocated += nGuess;
     865         257 :         papoLayers = static_cast<OGRLIBKMLLayer **>(
     866         257 :             CPLRealloc(papoLayers, sizeof(OGRLIBKMLLayer *) * nAllocated));
     867             :     }
     868             : 
     869             :     /***** create the layer *****/
     870             :     OGRLIBKMLLayer *poOgrLayer = new OGRLIBKMLLayer(
     871         361 :         osUniqueLayername, eGType, poSRS, poOgrDS, std::move(poKmlRoot),
     872         722 :         std::move(poKmlContainer), m_poKmlUpdate, pszFileName, bNew, bUpdateIn);
     873             : 
     874             :     /***** add the layer to the array *****/
     875         361 :     const int iLayer = nLayers++;
     876         361 :     papoLayers[iLayer] = poOgrLayer;
     877         361 :     osUniqueLayername.toupper();
     878         361 :     m_oMapLayers[std::move(osUniqueLayername)] = poOgrLayer;
     879             : 
     880         722 :     return poOgrLayer;
     881             : }
     882             : 
     883             : /******************************************************************************
     884             :  Method to parse multiple layers out of a container.
     885             : 
     886             :  Args:          poKmlContainer  pointer to the container to parse
     887             : 
     888             :  Returns:       number of features in the container that are not another
     889             :                 container
     890             : 
     891             : ******************************************************************************/
     892             : 
     893         459 : int OGRLIBKMLDataSource::ParseLayers(ContainerPtr poKmlContainer, bool bRecurse)
     894             : {
     895             :     /***** if container is null just bail now *****/
     896         459 :     if (!poKmlContainer)
     897           0 :         return 0;
     898             : 
     899         459 :     const size_t nKmlFeatures = poKmlContainer->get_feature_array_size();
     900             : 
     901             :     /***** loop over the container to separate the style, layers, etc *****/
     902             : 
     903         459 :     int nResult = 0;
     904        1396 :     for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
     905             :     {
     906             :         FeaturePtr poKmlFeat =
     907        1874 :             poKmlContainer->get_feature_array_at(iKmlFeature);
     908             : 
     909             :         /***** container *****/
     910             : 
     911         937 :         if (poKmlFeat->IsA(kmldom::Type_Container))
     912             :         {
     913         382 :             if (bRecurse)
     914             :             {
     915             :                 /***** see if the container has a name *****/
     916             : 
     917         211 :                 std::string oKmlFeatName;
     918         211 :                 if (poKmlFeat->has_name())
     919             :                 {
     920             :                     /* Strip leading and trailing spaces */
     921         210 :                     const char *l_pszName = poKmlFeat->get_name().c_str();
     922         210 :                     while (*l_pszName == ' ' || *l_pszName == '\n' ||
     923         420 :                            *l_pszName == '\r' || *l_pszName == '\t')
     924           0 :                         l_pszName++;
     925         210 :                     oKmlFeatName = l_pszName;
     926         210 :                     int nSize = (int)oKmlFeatName.size();
     927         420 :                     while (nSize > 0 && (oKmlFeatName[nSize - 1] == ' ' ||
     928         210 :                                          oKmlFeatName[nSize - 1] == '\n' ||
     929         210 :                                          oKmlFeatName[nSize - 1] == '\r' ||
     930         210 :                                          oKmlFeatName[nSize - 1] == '\t'))
     931             :                     {
     932           0 :                         nSize--;
     933           0 :                         oKmlFeatName.resize(nSize);
     934             :                     }
     935             :                 }
     936             :                 /***** use the feature index number as the name *****/
     937             :                 /***** not sure i like this c++ ich *****/
     938             :                 else
     939             :                 {
     940           1 :                     std::stringstream oOut;
     941           1 :                     oOut << iKmlFeature;
     942           1 :                     oKmlFeatName = "Layer";
     943           1 :                     oKmlFeatName.append(oOut.str());
     944             :                 }
     945             : 
     946             :                 /***** create the layer *****/
     947             : 
     948         211 :                 AddLayer(oKmlFeatName.c_str(), wkbUnknown, nullptr, this,
     949         422 :                          nullptr, AsContainer(poKmlFeat), "", FALSE, bUpdate,
     950             :                          static_cast<int>(nKmlFeatures));
     951             : 
     952             :                 /***** check if any features are another layer *****/
     953         211 :                 ParseLayers(AsContainer(poKmlFeat), true);
     954             :             }
     955             :         }
     956             :         else
     957             :         {
     958         555 :             nResult++;
     959             :         }
     960             :     }
     961             : 
     962         459 :     return nResult;
     963             : }
     964             : 
     965             : /******************************************************************************
     966             :  Function to get the container from the kmlroot.
     967             : 
     968             :  Args:          poKmlRoot   the root element
     969             : 
     970             :  Returns:       root if its a container, if its a kml the container it
     971             :                 contains, or NULL
     972             : 
     973             : ******************************************************************************/
     974             : 
     975         138 : static ContainerPtr GetContainerFromRoot(KmlFactory *poKmlFactory,
     976             :                                          ElementPtr poKmlRoot)
     977             : {
     978         138 :     ContainerPtr poKmlContainer = nullptr;
     979             : 
     980             :     const bool bReadGroundOverlay =
     981         138 :         CPLTestBool(CPLGetConfigOption("LIBKML_READ_GROUND_OVERLAY", "YES"));
     982             : 
     983         138 :     if (poKmlRoot)
     984             :     {
     985             :         /***** skip over the <kml> we want the container *****/
     986         138 :         if (poKmlRoot->IsA(kmldom::Type_kml))
     987             :         {
     988         276 :             KmlPtr poKmlKml = AsKml(poKmlRoot);
     989             : 
     990         138 :             if (poKmlKml && poKmlKml->has_feature())
     991             :             {
     992         276 :                 FeaturePtr poKmlFeat = poKmlKml->get_feature();
     993             : 
     994         138 :                 if (poKmlFeat->IsA(kmldom::Type_Container))
     995         135 :                     poKmlContainer = AsContainer(poKmlFeat);
     996           3 :                 else if (poKmlFeat->IsA(kmldom::Type_Placemark) ||
     997           0 :                          (bReadGroundOverlay &&
     998           0 :                           poKmlFeat->IsA(kmldom::Type_GroundOverlay)))
     999             :                 {
    1000           3 :                     poKmlContainer = poKmlFactory->CreateDocument();
    1001           6 :                     poKmlContainer->add_feature(
    1002           6 :                         kmldom::AsFeature(kmlengine::Clone(poKmlFeat)));
    1003             :                 }
    1004             :             }
    1005             :         }
    1006           0 :         else if (poKmlRoot->IsA(kmldom::Type_Container))
    1007             :         {
    1008           0 :             poKmlContainer = AsContainer(std::move(poKmlRoot));
    1009             :         }
    1010             :     }
    1011             : 
    1012         138 :     return poKmlContainer;
    1013             : }
    1014             : 
    1015             : /******************************************************************************
    1016             :  Method to parse a kml string into the style table.
    1017             : ******************************************************************************/
    1018             : 
    1019           4 : int OGRLIBKMLDataSource::ParseIntoStyleTable(std::string *poKmlStyleKml,
    1020             :                                              const char *pszMyStylePath)
    1021             : {
    1022             :     /***** parse the kml into the dom *****/
    1023           8 :     std::string oKmlErrors;
    1024           8 :     ElementPtr poKmlRoot = OGRLIBKMLParse(*poKmlStyleKml, &oKmlErrors);
    1025             : 
    1026           4 :     if (!poKmlRoot)
    1027             :     {
    1028           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing style kml %s :%s",
    1029             :                  pszMyStylePath, oKmlErrors.c_str());
    1030           0 :         return false;
    1031             :     }
    1032             : 
    1033           8 :     ContainerPtr poKmlContainer = nullptr;
    1034             : 
    1035           4 :     if (!(poKmlContainer =
    1036           8 :               GetContainerFromRoot(m_poKmlFactory, std::move(poKmlRoot))))
    1037             :     {
    1038           0 :         return false;
    1039             :     }
    1040             : 
    1041           4 :     ParseStyles(AsDocument(std::move(poKmlContainer)), &m_poStyleTable);
    1042           4 :     m_osStylePath = pszMyStylePath;
    1043             : 
    1044           4 :     return true;
    1045             : }
    1046             : 
    1047             : /******************************************************************************
    1048             :  Method to open a kml file.
    1049             : 
    1050             :  Args:          pszFilename file to open
    1051             :                 bUpdate     update mode
    1052             : 
    1053             :  Returns:       True on success, false on failure
    1054             : 
    1055             : ******************************************************************************/
    1056             : 
    1057         119 : int OGRLIBKMLDataSource::OpenKml(const char *pszFilename, int bUpdateIn)
    1058             : {
    1059         238 :     std::string oKmlKml;
    1060         238 :     std::string osBuffer;
    1061         119 :     osBuffer.resize(4096);
    1062             : 
    1063         119 :     VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
    1064         119 :     if (fp == nullptr)
    1065             :     {
    1066           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s", pszFilename);
    1067           0 :         return FALSE;
    1068             :     }
    1069         119 :     int nRead = 0;
    1070         208 :     while ((nRead = static_cast<int>(
    1071         327 :                 VSIFReadL(&osBuffer[0], 1, osBuffer.size(), fp))) != 0)
    1072             :     {
    1073             :         try
    1074             :         {
    1075         208 :             oKmlKml.append(osBuffer.c_str(), nRead);
    1076             :         }
    1077           0 :         catch (const std::exception &ex)
    1078             :         {
    1079           0 :             CPLDebug("LIBKML", "libstdc++ exception during ingestion: %s",
    1080           0 :                      ex.what());
    1081           0 :             VSIFCloseL(fp);
    1082           0 :             return FALSE;
    1083             :         }
    1084             :     }
    1085         119 :     OGRLIBKMLPreProcessInput(oKmlKml);
    1086         119 :     VSIFCloseL(fp);
    1087             : 
    1088         238 :     CPLLocaleC oLocaleForcer;
    1089             : 
    1090             :     /***** parse the kml into the DOM *****/
    1091         238 :     std::string oKmlErrors;
    1092             : 
    1093         119 :     m_poKmlDSKml = AsKml(OGRLIBKMLParse(oKmlKml, &oKmlErrors));
    1094             : 
    1095         119 :     if (!m_poKmlDSKml)
    1096             :     {
    1097           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing kml %s :%s",
    1098             :                  pszFilename, oKmlErrors.c_str());
    1099             : 
    1100           0 :         return FALSE;
    1101             :     }
    1102             : 
    1103             :     /***** get the container from root  *****/
    1104         119 :     if (!(m_poKmlDSContainer =
    1105         238 :               GetContainerFromRoot(m_poKmlFactory, m_poKmlDSKml)))
    1106             :     {
    1107           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing kml %s :%s %s",
    1108             :                  pszFilename, "This file does not fit the OGR model,",
    1109             :                  "there is no container element at the root.");
    1110             : 
    1111           0 :         return FALSE;
    1112             :     }
    1113             : 
    1114         119 :     m_isKml = true;
    1115             : 
    1116             :     /***** get the styles *****/
    1117         119 :     ParseStyles(AsDocument(m_poKmlDSContainer), &m_poStyleTable);
    1118             : 
    1119             :     /***** parse for layers *****/
    1120         119 :     int nPlacemarks = ParseLayers(m_poKmlDSContainer, false);
    1121             : 
    1122             :     /***** if there is placemarks in the root its a layer *****/
    1123         119 :     if (nPlacemarks)
    1124             :     {
    1125          16 :         std::string layername_default(CPLGetBasename(pszFilename));
    1126             : 
    1127          16 :         if (m_poKmlDSContainer->has_name())
    1128             :         {
    1129           6 :             layername_default = m_poKmlDSContainer->get_name();
    1130             :         }
    1131             : 
    1132          16 :         AddLayer(layername_default.c_str(), wkbUnknown, nullptr, this,
    1133          16 :                  m_poKmlDSKml, m_poKmlDSContainer, pszFilename, FALSE,
    1134             :                  bUpdateIn, 1);
    1135             :     }
    1136             : 
    1137         119 :     ParseLayers(m_poKmlDSContainer, true);
    1138             : 
    1139         119 :     return TRUE;
    1140             : }
    1141             : 
    1142             : /******************************************************************************
    1143             :  Method to open a kmz file.
    1144             : 
    1145             :  Args:          pszFilename file to open
    1146             :                 bUpdate     update mode
    1147             : 
    1148             :  Returns:       True on success, false on failure
    1149             : 
    1150             : ******************************************************************************/
    1151             : 
    1152           6 : int OGRLIBKMLDataSource::OpenKmz(const char *pszFilename, int bUpdateIn)
    1153             : {
    1154          12 :     std::string oKmlKmz;
    1155           6 :     char szBuffer[1024 + 1] = {};
    1156             : 
    1157           6 :     VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
    1158           6 :     if (fp == nullptr)
    1159             :     {
    1160           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s", pszFilename);
    1161           0 :         return FALSE;
    1162             :     }
    1163           6 :     int nRead = 0;
    1164          16 :     while ((nRead = static_cast<int>(VSIFReadL(szBuffer, 1, 1024, fp))) != 0)
    1165             :     {
    1166             :         try
    1167             :         {
    1168          10 :             oKmlKmz.append(szBuffer, nRead);
    1169             :         }
    1170           0 :         catch (const std::bad_alloc &)
    1171             :         {
    1172           0 :             VSIFCloseL(fp);
    1173           0 :             return FALSE;
    1174             :         }
    1175             :     }
    1176           6 :     VSIFCloseL(fp);
    1177             : 
    1178           6 :     KmzFile *poKmlKmzfile = KmzFile::OpenFromString(oKmlKmz);
    1179             : 
    1180           6 :     if (!poKmlKmzfile)
    1181             :     {
    1182           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "%s is not a valid kmz file",
    1183             :                  pszFilename);
    1184           0 :         return FALSE;
    1185             :     }
    1186             : 
    1187          12 :     CPLLocaleC oLocaleForcer;
    1188             : 
    1189             :     /***** read the doc.kml *****/
    1190          12 :     std::string oKmlKml;
    1191          12 :     std::string oKmlKmlPath;
    1192           6 :     if (!poKmlKmzfile->ReadKmlAndGetPath(&oKmlKml, &oKmlKmlPath))
    1193             :     {
    1194           0 :         return FALSE;
    1195             :     }
    1196             : 
    1197             :     /***** parse the kml into the DOM *****/
    1198          12 :     std::string oKmlErrors;
    1199          12 :     ElementPtr poKmlDocKmlRoot = OGRLIBKMLParse(oKmlKml, &oKmlErrors);
    1200             : 
    1201           6 :     if (!poKmlDocKmlRoot)
    1202             :     {
    1203           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1204             :                  "ERROR parsing kml layer %s from %s :%s", oKmlKmlPath.c_str(),
    1205             :                  pszFilename, oKmlErrors.c_str());
    1206             : 
    1207           0 :         return FALSE;
    1208             :     }
    1209             : 
    1210             :     /***** Get the child container from root. *****/
    1211          12 :     ContainerPtr poKmlContainer = nullptr;
    1212             : 
    1213           6 :     if (!(poKmlContainer =
    1214          12 :               GetContainerFromRoot(m_poKmlFactory, poKmlDocKmlRoot)))
    1215             :     {
    1216           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing %s from %s :%s",
    1217             :                  oKmlKmlPath.c_str(), pszFilename,
    1218             :                  "kml contains no Containers");
    1219             : 
    1220           0 :         return FALSE;
    1221             :     }
    1222             : 
    1223             :     /***** loop over the container looking for network links *****/
    1224             : 
    1225           6 :     size_t nKmlFeatures = poKmlContainer->get_feature_array_size();
    1226           6 :     int nLinks = 0;
    1227             : 
    1228          21 :     for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
    1229             :     {
    1230             :         FeaturePtr poKmlFeat =
    1231          15 :             poKmlContainer->get_feature_array_at(iKmlFeature);
    1232             : 
    1233             :         /***** is it a network link? *****/
    1234          15 :         if (!poKmlFeat->IsA(kmldom::Type_NetworkLink))
    1235           8 :             continue;
    1236             : 
    1237           7 :         NetworkLinkPtr poKmlNetworkLink = AsNetworkLink(poKmlFeat);
    1238             : 
    1239             :         /***** does it have a link? *****/
    1240           7 :         if (!poKmlNetworkLink->has_link())
    1241           0 :             continue;
    1242             : 
    1243           7 :         LinkPtr poKmlLink = poKmlNetworkLink->get_link();
    1244             : 
    1245             :         /***** does the link have a href? *****/
    1246           7 :         if (!poKmlLink->has_href())
    1247           0 :             continue;
    1248             : 
    1249           7 :         const kmlengine::Href oKmlHref(poKmlLink->get_href());
    1250             : 
    1251             :         /***** is the link relative? *****/
    1252           7 :         if (oKmlHref.IsRelativePath())
    1253             :         {
    1254           7 :             nLinks++;
    1255             : 
    1256           7 :             std::string oKml;
    1257           7 :             if (poKmlKmzfile->ReadFile(oKmlHref.get_path().c_str(), &oKml))
    1258             :             {
    1259             :                 /***** parse the kml into the DOM *****/
    1260           7 :                 oKmlErrors.clear();
    1261           7 :                 ElementPtr poKmlLyrRoot = OGRLIBKMLParse(oKml, &oKmlErrors);
    1262             : 
    1263           7 :                 if (!poKmlLyrRoot)
    1264             :                 {
    1265           0 :                     CPLError(CE_Failure, CPLE_OpenFailed,
    1266             :                              "ERROR parsing kml layer %s from %s :%s",
    1267           0 :                              oKmlHref.get_path().c_str(), pszFilename,
    1268             :                              oKmlErrors.c_str());
    1269             : 
    1270           0 :                     continue;
    1271             :                 }
    1272             : 
    1273             :                 /***** get the container from root  *****/
    1274             :                 ContainerPtr poKmlLyrContainer =
    1275           7 :                     GetContainerFromRoot(m_poKmlFactory, poKmlLyrRoot);
    1276             : 
    1277           7 :                 if (!poKmlLyrContainer)
    1278             :                 {
    1279           0 :                     CPLError(CE_Failure, CPLE_OpenFailed,
    1280             :                              "ERROR parsing kml layer %s from %s :%s",
    1281           0 :                              oKmlHref.get_path().c_str(), pszFilename,
    1282             :                              oKmlErrors.c_str());
    1283             : 
    1284           0 :                     continue;
    1285             :                 }
    1286             : 
    1287             :                 /***** create the layer *****/
    1288             :                 const std::string osLayerName =
    1289           7 :                     poKmlNetworkLink->has_name()
    1290           7 :                         ? poKmlNetworkLink->get_name()
    1291             :                         : std::string(
    1292          14 :                               CPLGetBasename(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(CPLGetBasename(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(CPLGetExtension(papszDirList[iFile]), "kml"))
    1379        4371 :             continue;
    1380             : 
    1381             :         /***** read the file *****/
    1382           2 :         std::string oKmlKml;
    1383           2 :         char szBuffer[1024 + 1] = {};
    1384             : 
    1385             :         CPLString osFilePath =
    1386           2 :             CPLFormFilename(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(CPLGetBasename(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(CPLGetExtension(*papszIter), "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(CPLGetExtension(pszFilename), "kml"))
    1531             :     {
    1532         119 :         return OpenKml(pszFilename, bUpdate);
    1533             :     }
    1534             : 
    1535             :     /***** kmz *****/
    1536           6 :     if (EQUAL(CPLGetExtension(pszFilename), "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         103 :         EQUAL(CPLGetExtension(pszFilename), "kml"))
    1952          57 :         return CreateKml(pszFilename, papszOptions);
    1953             : 
    1954             :     /***** kmz *****/
    1955          46 :     if (EQUAL(CPLGetExtension(pszFilename), "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          16 :         const char *pszFilePath = CPLFormFilename(
    2118          16 :             GetDescription(), papoLayers[iLayer]->GetFileName(), nullptr);
    2119             :         VSIStatBufL oStatBufL;
    2120          16 :         if (!VSIStatL(pszFilePath, &oStatBufL))
    2121             :         {
    2122           0 :             if (VSIUnlink(pszFilePath))
    2123             :             {
    2124           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2125             :                          "ERROR Deleting Layer %s from filesystem as %s",
    2126           0 :                          papoLayers[iLayer]->GetName(), pszFilePath);
    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          64 :     OGRLIBKMLLayer *poOgrLayer = AddLayer(
    2240             :         pszLayerName, eGType, poSRS, this, nullptr, poKmlDocument,
    2241          64 :         CPLFormFilename(nullptr, pszLayerName, ".kml"), TRUE, bUpdate, 1);
    2242             : 
    2243             :     /***** add the layer name as a <Name> *****/
    2244          64 :     if (!m_poKmlUpdate)
    2245             :     {
    2246          62 :         poKmlDocument->set_name(pszLayerName);
    2247             :     }
    2248             : 
    2249         128 :     return poOgrLayer;
    2250             : }
    2251             : 
    2252             : /******************************************************************************
    2253             :  ICreateLayer()
    2254             : 
    2255             :  Args:          pszLayerName    name of the layer to create
    2256             :                 poOgrSRS        the SRS of the layer
    2257             :                 eGType          the layers geometry type
    2258             :                 papszOptions    layer creation options
    2259             : 
    2260             :  Returns:       return a pointer to the new layer or NULL on failure
    2261             : 
    2262             : ******************************************************************************/
    2263             : 
    2264             : OGRLayer *
    2265         125 : OGRLIBKMLDataSource::ICreateLayer(const char *pszLayerName,
    2266             :                                   const OGRGeomFieldDefn *poGeomFieldDefn,
    2267             :                                   CSLConstList papszOptions)
    2268             : {
    2269         125 :     if (!bUpdate)
    2270           0 :         return nullptr;
    2271             : 
    2272         125 :     if ((IsKmz() || IsDir()) && EQUAL(pszLayerName, "doc"))
    2273             :     {
    2274           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2275             :                  "'doc' is an invalid layer name in a KMZ file");
    2276           0 :         return nullptr;
    2277             :     }
    2278             : 
    2279         125 :     const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
    2280             :     const auto poOgrSRS =
    2281         125 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
    2282             : 
    2283         125 :     OGRLIBKMLLayer *poOgrLayer = nullptr;
    2284             : 
    2285             :     /***** kml DS *****/
    2286         125 :     if (IsKml())
    2287             :     {
    2288             :         poOgrLayer =
    2289          61 :             CreateLayerKml(pszLayerName, poOgrSRS, eGType, papszOptions);
    2290             :     }
    2291          64 :     else if (IsKmz() || IsDir())
    2292             :     {
    2293             :         poOgrLayer =
    2294          64 :             CreateLayerKmz(pszLayerName, poOgrSRS, eGType, papszOptions);
    2295             :     }
    2296             : 
    2297             :     const char *pszLookatLongitude =
    2298         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_LONGITUDE");
    2299             :     const char *pszLookatLatitude =
    2300         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_LATITUDE");
    2301             :     const char *pszLookatAltitude =
    2302         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDE");
    2303             :     const char *pszLookatHeading =
    2304         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_HEADING");
    2305         125 :     const char *pszLookatTilt = CSLFetchNameValue(papszOptions, "LOOKAT_TILT");
    2306             :     const char *pszLookatRange =
    2307         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_RANGE");
    2308             :     const char *pszLookatAltitudeMode =
    2309         125 :         CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDEMODE");
    2310         125 :     if (poOgrLayer != nullptr && pszLookatLongitude != nullptr &&
    2311           2 :         pszLookatLatitude != nullptr && pszLookatRange != nullptr)
    2312             :     {
    2313           2 :         poOgrLayer->SetLookAt(pszLookatLongitude, pszLookatLatitude,
    2314             :                               pszLookatAltitude, pszLookatHeading,
    2315             :                               pszLookatTilt, pszLookatRange,
    2316             :                               pszLookatAltitudeMode);
    2317             :     }
    2318             :     else
    2319             :     {
    2320             :         const char *pszCameraLongitude =
    2321         123 :             CSLFetchNameValue(papszOptions, "CAMERA_LONGITUDE");
    2322             :         const char *pszCameraLatitude =
    2323         123 :             CSLFetchNameValue(papszOptions, "CAMERA_LATITUDE");
    2324             :         const char *pszCameraAltitude =
    2325         123 :             CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDE");
    2326             :         const char *pszCameraHeading =
    2327         123 :             CSLFetchNameValue(papszOptions, "CAMERA_HEADING");
    2328             :         const char *pszCameraTilt =
    2329         123 :             CSLFetchNameValue(papszOptions, "CAMERA_TILT");
    2330             :         const char *pszCameraRoll =
    2331         123 :             CSLFetchNameValue(papszOptions, "CAMERA_ROLL");
    2332             :         const char *pszCameraAltitudeMode =
    2333         123 :             CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDEMODE");
    2334         123 :         if (poOgrLayer != nullptr && pszCameraLongitude != nullptr &&
    2335           1 :             pszCameraLatitude != nullptr && pszCameraAltitude != nullptr &&
    2336             :             pszCameraAltitudeMode != nullptr)
    2337             :         {
    2338           1 :             poOgrLayer->SetCamera(pszCameraLongitude, pszCameraLatitude,
    2339             :                                   pszCameraAltitude, pszCameraHeading,
    2340             :                                   pszCameraTilt, pszCameraRoll,
    2341             :                                   pszCameraAltitudeMode);
    2342             :         }
    2343             :     }
    2344             : 
    2345             :     const char *pszRegionAdd =
    2346         125 :         CSLFetchNameValueDef(papszOptions, "ADD_REGION", "FALSE");
    2347         125 :     const char *pszRegionXMin = CSLFetchNameValue(papszOptions, "REGION_XMIN");
    2348         125 :     const char *pszRegionYMin = CSLFetchNameValue(papszOptions, "REGION_YMIN");
    2349         125 :     const char *pszRegionXMax = CSLFetchNameValue(papszOptions, "REGION_XMAX");
    2350         125 :     const char *pszRegionYMax = CSLFetchNameValue(papszOptions, "REGION_YMAX");
    2351             :     const char *pszRegionMinLodPixels =
    2352         125 :         CSLFetchNameValueDef(papszOptions, "REGION_MIN_LOD_PIXELS", "256");
    2353             :     const char *pszRegionMaxLodPixels =
    2354         125 :         CSLFetchNameValueDef(papszOptions, "REGION_MAX_LOD_PIXELS", "-1");
    2355             :     const char *pszRegionMinFadeExtent =
    2356         125 :         CSLFetchNameValueDef(papszOptions, "REGION_MIN_FADE_EXTENT", "0");
    2357             :     const char *pszRegionMaxFadeExtent =
    2358         125 :         CSLFetchNameValueDef(papszOptions, "REGION_MAX_FADE_EXTENT", "0");
    2359         125 :     if (poOgrLayer != nullptr && CPLTestBool(pszRegionAdd))
    2360             :     {
    2361           2 :         poOgrLayer->SetWriteRegion(
    2362             :             CPLAtof(pszRegionMinLodPixels), CPLAtof(pszRegionMaxLodPixels),
    2363             :             CPLAtof(pszRegionMinFadeExtent), CPLAtof(pszRegionMaxFadeExtent));
    2364           2 :         if (pszRegionXMin != nullptr && pszRegionYMin != nullptr &&
    2365           1 :             pszRegionXMax != nullptr && pszRegionYMax != nullptr)
    2366             :         {
    2367           1 :             const double xmin = CPLAtof(pszRegionXMin);
    2368           1 :             const double ymin = CPLAtof(pszRegionYMin);
    2369           1 :             const double xmax = CPLAtof(pszRegionXMax);
    2370           1 :             const double ymax = CPLAtof(pszRegionYMax);
    2371           1 :             if (xmin < xmax && ymin < ymax)
    2372           1 :                 poOgrLayer->SetRegionBounds(xmin, ymin, xmax, ymax);
    2373             :         }
    2374             :     }
    2375             : 
    2376         125 :     const char *pszSOHref = CSLFetchNameValue(papszOptions, "SO_HREF");
    2377         125 :     const char *pszSOName = CSLFetchNameValue(papszOptions, "SO_NAME");
    2378             :     const char *pszSODescription =
    2379         125 :         CSLFetchNameValue(papszOptions, "SO_DESCRIPTION");
    2380         125 :     const char *pszSOOverlayX = CSLFetchNameValue(papszOptions, "SO_OVERLAY_X");
    2381         125 :     const char *pszSOOverlayY = CSLFetchNameValue(papszOptions, "SO_OVERLAY_Y");
    2382             :     const char *pszSOOverlayXUnits =
    2383         125 :         CSLFetchNameValue(papszOptions, "SO_OVERLAY_XUNITS");
    2384             :     const char *pszSOOverlayYUnits =
    2385         125 :         CSLFetchNameValue(papszOptions, "SO_OVERLAY_YUNITS");
    2386         125 :     const char *pszSOScreenX = CSLFetchNameValue(papszOptions, "SO_SCREEN_X");
    2387         125 :     const char *pszSOScreenY = CSLFetchNameValue(papszOptions, "SO_SCREEN_Y");
    2388             :     const char *pszSOScreenXUnits =
    2389         125 :         CSLFetchNameValue(papszOptions, "SO_SCREEN_XUNITS");
    2390             :     const char *pszSOScreenYUnits =
    2391         125 :         CSLFetchNameValue(papszOptions, "SO_SCREEN_YUNITS");
    2392         125 :     const char *pszSOSizeX = CSLFetchNameValue(papszOptions, "SO_SIZE_X");
    2393         125 :     const char *pszSOSizeY = CSLFetchNameValue(papszOptions, "SO_SIZE_Y");
    2394             :     const char *pszSOSizeXUnits =
    2395         125 :         CSLFetchNameValue(papszOptions, "SO_SIZE_XUNITS");
    2396             :     const char *pszSOSizeYUnits =
    2397         125 :         CSLFetchNameValue(papszOptions, "SO_SIZE_YUNITS");
    2398         125 :     if (poOgrLayer != nullptr && pszSOHref != nullptr)
    2399             :     {
    2400           2 :         poOgrLayer->SetScreenOverlay(
    2401             :             pszSOHref, pszSOName, pszSODescription, pszSOOverlayX,
    2402             :             pszSOOverlayY, pszSOOverlayXUnits, pszSOOverlayYUnits, pszSOScreenX,
    2403             :             pszSOScreenY, pszSOScreenXUnits, pszSOScreenYUnits, pszSOSizeX,
    2404             :             pszSOSizeY, pszSOSizeXUnits, pszSOSizeYUnits);
    2405             :     }
    2406             : 
    2407             :     const char *pszListStyleType =
    2408         125 :         CSLFetchNameValue(papszOptions, "LISTSTYLE_TYPE");
    2409             :     const char *pszListStyleIconHref =
    2410         125 :         CSLFetchNameValue(papszOptions, "LISTSTYLE_ICON_HREF");
    2411         125 :     if (poOgrLayer != nullptr)
    2412             :     {
    2413         125 :         poOgrLayer->SetListStyle(pszListStyleType, pszListStyleIconHref);
    2414             :     }
    2415             : 
    2416         125 :     if (poOgrLayer != nullptr && poOgrLayer->GetKmlLayer())
    2417             :     {
    2418         122 :         SetCommonOptions(poOgrLayer->GetKmlLayer(), papszOptions);
    2419             :     }
    2420             : 
    2421             :     /***** mark the dataset as updated *****/
    2422         125 :     if (poOgrLayer)
    2423         125 :         bUpdated = true;
    2424             : 
    2425         125 :     return poOgrLayer;
    2426             : }
    2427             : 
    2428             : /******************************************************************************
    2429             :  Method to get a datasources style table.
    2430             : 
    2431             :  Args:          none
    2432             : 
    2433             :  Returns:       pointer to the datasources style table, or NULL if it does
    2434             :                 not have one
    2435             : 
    2436             : ******************************************************************************/
    2437             : 
    2438           7 : OGRStyleTable *OGRLIBKMLDataSource::GetStyleTable()
    2439             : {
    2440           7 :     return m_poStyleTable;
    2441             : }
    2442             : 
    2443             : /******************************************************************************
    2444             :   Method to write a style table to a single file .kml ds.
    2445             : 
    2446             :  Args:          poStyleTable    pointer to the style table to add
    2447             : 
    2448             :  Returns:       nothing
    2449             : 
    2450             : ******************************************************************************/
    2451             : 
    2452           4 : void OGRLIBKMLDataSource::SetStyleTable2Kml(OGRStyleTable *poStyleTable)
    2453             : {
    2454           4 :     if (!m_poKmlDSContainer)
    2455           0 :         return;
    2456             : 
    2457             :     /***** delete all the styles *****/
    2458             : 
    2459           4 :     DocumentPtr poKmlDocument = AsDocument(m_poKmlDSContainer);
    2460             :     int nKmlStyles =
    2461           4 :         static_cast<int>(poKmlDocument->get_styleselector_array_size());
    2462             : 
    2463           4 :     for (int iKmlStyle = nKmlStyles - 1; iKmlStyle >= 0; iKmlStyle--)
    2464             :     {
    2465           0 :         poKmlDocument->DeleteStyleSelectorAt(iKmlStyle);
    2466             :     }
    2467             : 
    2468             :     /***** add the new style table to the document *****/
    2469             : 
    2470           4 :     styletable2kml(poStyleTable, m_poKmlFactory, AsContainer(poKmlDocument),
    2471             :                    m_papszOptions);
    2472             : }
    2473             : 
    2474             : /******************************************************************************
    2475             :  Method to write a style table to a kmz ds.
    2476             : 
    2477             :  Args:          poStyleTable    pointer to the style table to add
    2478             : 
    2479             :  Returns:       nothing
    2480             : 
    2481             : ******************************************************************************/
    2482             : 
    2483           1 : void OGRLIBKMLDataSource::SetStyleTable2Kmz(OGRStyleTable *poStyleTable)
    2484             : {
    2485           1 :     if (m_poKmlStyleKml || poStyleTable != nullptr)
    2486             :     {
    2487             :         /***** replace the style document with a new one *****/
    2488             : 
    2489           1 :         m_poKmlStyleKml = m_poKmlFactory->CreateDocument();
    2490           1 :         m_poKmlStyleKml->set_id("styleId");
    2491             : 
    2492           1 :         styletable2kml(poStyleTable, m_poKmlFactory, m_poKmlStyleKml);
    2493             :     }
    2494           1 : }
    2495             : 
    2496             : /******************************************************************************
    2497             :  Method to write a style table to a datasource.
    2498             : 
    2499             :  Args:          poStyleTable    pointer to the style table to add
    2500             : 
    2501             :  Returns:       nothing
    2502             : 
    2503             :  Note: This method assumes ownership of the style table.
    2504             : 
    2505             : ******************************************************************************/
    2506             : 
    2507           5 : void OGRLIBKMLDataSource::SetStyleTableDirectly(OGRStyleTable *poStyleTable)
    2508             : {
    2509           5 :     if (!bUpdate)
    2510           0 :         return;
    2511             : 
    2512           5 :     if (m_poStyleTable)
    2513           0 :         delete m_poStyleTable;
    2514             : 
    2515           5 :     m_poStyleTable = poStyleTable;
    2516             : 
    2517             :     /***** a kml datasource? *****/
    2518           5 :     if (IsKml())
    2519           4 :         SetStyleTable2Kml(m_poStyleTable);
    2520             : 
    2521           1 :     else if (IsKmz() || IsDir())
    2522           1 :         SetStyleTable2Kmz(m_poStyleTable);
    2523             : 
    2524           5 :     bUpdated = true;
    2525             : }
    2526             : 
    2527             : /******************************************************************************
    2528             :  Method to write a style table to a datasource.
    2529             : 
    2530             :  Args:          poStyleTable    pointer to the style table to add
    2531             : 
    2532             :  Returns:       nothing
    2533             : 
    2534             :  Note:  This method copies the style table, and the user will still be
    2535             :         responsible for its destruction.
    2536             : 
    2537             : ******************************************************************************/
    2538             : 
    2539           5 : void OGRLIBKMLDataSource::SetStyleTable(OGRStyleTable *poStyleTable)
    2540             : {
    2541           5 :     if (!bUpdate)
    2542           0 :         return;
    2543             : 
    2544           5 :     if (poStyleTable)
    2545           4 :         SetStyleTableDirectly(poStyleTable->Clone());
    2546             :     else
    2547           1 :         SetStyleTableDirectly(nullptr);
    2548             : }
    2549             : 
    2550             : /******************************************************************************
    2551             :  Test if capability is available.
    2552             : 
    2553             :  Args:          pszCap  datasource capability name to test
    2554             : 
    2555             :  Returns:       TRUE or FALSE
    2556             : 
    2557             :  ODsCCreateLayer: True if this datasource can create new layers.
    2558             :  ODsCDeleteLayer: True if this datasource can delete existing layers.
    2559             : 
    2560             : ******************************************************************************/
    2561             : 
    2562         104 : int OGRLIBKMLDataSource::TestCapability(const char *pszCap)
    2563             : {
    2564         104 :     if (EQUAL(pszCap, ODsCCreateLayer))
    2565          38 :         return bUpdate;
    2566          66 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
    2567          18 :         return bUpdate;
    2568          48 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
    2569           0 :         return bUpdate;
    2570          48 :     else if (EQUAL(pszCap, ODsCZGeometries))
    2571          22 :         return TRUE;
    2572             : 
    2573          26 :     return FALSE;
    2574             : }

Generated by: LCOV version 1.14