LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/libkml - ogrlibkmldatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 790 1028 76.8 %
Date: 2024-05-02 14:43:06 Functions: 39 43 90.7 %

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

Generated by: LCOV version 1.14