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