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