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