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