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 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : *****************************************************************************/
28 :
29 : //#include "cpl_conv.h"
30 : //#include "cpl_string.h"
31 : //#include "cpl_error.h"
32 : #include <iostream>
33 : //#include <sstream>
34 : #include <kml/dom.h>
35 : #include <kml/base/file.h>
36 :
37 : using kmldom::KmlFactory;
38 : using kmldom::DocumentPtr;
39 : using kmldom::FolderPtr;
40 : using kmldom::FeaturePtr;
41 : using kmldom::NetworkLinkPtr;
42 : using kmldom::StyleSelectorPtr;
43 : using kmldom::LinkPtr;
44 : using kmldom::SchemaPtr;
45 : using kmlbase::File;
46 : using kmldom::KmlPtr;
47 :
48 : #include "ogr_libkml.h"
49 : #include "ogrlibkmlstyle.h"
50 :
51 : /***** this was shamelessly swiped from the kml driver *****/
52 :
53 : #define OGRLIBKMLSRSWKT "GEOGCS[\"WGS 84\", "\
54 : " DATUM[\"WGS_1984\","\
55 : " SPHEROID[\"WGS 84\",6378137,298.257223563,"\
56 : " AUTHORITY[\"EPSG\",\"7030\"]],"\
57 : " AUTHORITY[\"EPSG\",\"6326\"]],"\
58 : " PRIMEM[\"Greenwich\",0,"\
59 : " AUTHORITY[\"EPSG\",\"8901\"]],"\
60 : " UNIT[\"degree\",0.01745329251994328,"\
61 : " AUTHORITY[\"EPSG\",\"9122\"]],"\
62 : " AUTHORITY[\"EPSG\",\"4326\"]]"
63 :
64 : /******************************************************************************
65 : OGRLIBKMLDataSource Constructor
66 :
67 : Args: none
68 :
69 : Returns: nothing
70 :
71 : ******************************************************************************/
72 :
73 312 : OGRLIBKMLDataSource::OGRLIBKMLDataSource ( KmlFactory * poKmlFactory )
74 : {
75 312 : pszName = NULL;
76 312 : papoLayers = NULL;
77 312 : nLayers = 0;
78 312 : nAlloced = 0;
79 :
80 312 : bUpdated = FALSE;
81 :
82 312 : m_isKml = FALSE;
83 312 : m_poKmlDSKml = NULL;
84 312 : m_poKmlDSContainer = NULL;
85 :
86 312 : m_isKmz = FALSE;
87 312 : m_poKmlDocKml = NULL;
88 312 : pszStylePath = (char *) "";
89 :
90 312 : m_isDir = FALSE;
91 :
92 312 : m_poKmlFactory = poKmlFactory;
93 :
94 : //m_poStyleTable = NULL;
95 :
96 312 : }
97 :
98 : /******************************************************************************
99 : method to write a single file ds .kml at ds destroy
100 :
101 : Args: none
102 :
103 : Returns: nothing
104 :
105 : ******************************************************************************/
106 :
107 1 : void OGRLIBKMLDataSource::WriteKml (
108 : )
109 : {
110 1 : std::string oKmlFilename = pszName;
111 :
112 3 : if ( m_poKmlDSContainer
113 1 : && m_poKmlDSContainer->IsA ( kmldom::Type_Document ) ) {
114 1 : DocumentPtr poKmlDocument = AsDocument ( m_poKmlDSContainer );
115 : int iLayer;
116 :
117 3 : for ( iLayer = 0; iLayer < nLayers; iLayer++ ) {
118 2 : SchemaPtr poKmlSchema;
119 2 : SchemaPtr poKmlSchema2;
120 :
121 2 : if ( ( poKmlSchema = papoLayers[iLayer]->GetKmlSchema ( ) ) ) {
122 2 : size_t nKmlSchemas = poKmlDocument->get_schema_array_size ( );
123 : size_t iKmlSchema;
124 :
125 3 : for ( iKmlSchema = 0; iKmlSchema < nKmlSchemas; iKmlSchema++ ) {
126 : poKmlSchema2 =
127 1 : poKmlDocument->get_schema_array_at ( iKmlSchema );
128 1 : if ( poKmlSchema2 == poKmlSchema )
129 0 : break;
130 : }
131 :
132 2 : if ( poKmlSchema2 != poKmlSchema )
133 2 : poKmlDocument->add_schema ( poKmlSchema );
134 : }
135 1 : }
136 : }
137 :
138 1 : std::string oKmlOut;
139 1 : if ( m_poKmlDSKml ) {
140 1 : oKmlOut = kmldom::SerializePretty ( m_poKmlDSKml );
141 : }
142 0 : else if ( m_poKmlDSContainer ) {
143 0 : oKmlOut = kmldom::SerializePretty ( m_poKmlDSContainer );
144 : }
145 :
146 1 : if (oKmlOut.size() != 0)
147 : {
148 1 : VSILFILE* fp = VSIFOpenL( oKmlFilename.c_str(), "wb" );
149 1 : if (fp == NULL)
150 : {
151 : CPLError ( CE_Failure, CPLE_FileIO,
152 0 : "ERROR writing %s", oKmlFilename.c_str ( ) );
153 : return;
154 : }
155 :
156 1 : VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp);
157 1 : VSIFCloseL(fp);
158 : }
159 :
160 0 : return;
161 : }
162 :
163 : /******************************************************************************
164 : method to write a ds .kmz at ds destroy
165 :
166 : Args: none
167 :
168 : Returns: nothing
169 :
170 : ******************************************************************************/
171 :
172 1 : void OGRLIBKMLDataSource::WriteKmz (
173 : )
174 : {
175 :
176 1 : void* hZIP = CPLCreateZip( pszName, NULL );
177 :
178 1 : if ( !hZIP ) {
179 : CPLError ( CE_Failure, CPLE_NoWriteAccess, "ERROR creating %s",
180 0 : pszName );
181 0 : return;
182 : }
183 :
184 : /***** write out the doc.kml ****/
185 :
186 : const char *pszUseDocKml =
187 1 : CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" );
188 :
189 1 : if ( EQUAL ( pszUseDocKml, "yes" ) && m_poKmlDocKml ) {
190 :
191 : /***** if we dont have the doc.kml root *****/
192 : /***** make it and add the container *****/
193 :
194 1 : if ( !m_poKmlDocKmlRoot ) {
195 1 : m_poKmlDocKmlRoot = m_poKmlFactory->CreateKml ( );
196 :
197 1 : AsKml( m_poKmlDocKmlRoot )->set_feature ( m_poKmlDocKml );
198 : }
199 :
200 1 : std::string oKmlOut = kmldom::SerializePretty ( m_poKmlDocKmlRoot );
201 :
202 :
203 1 : if ( CPLCreateFileInZip( hZIP, "doc.kml", NULL ) != CE_None ||
204 : CPLWriteFileInZip( hZIP, oKmlOut.data(), oKmlOut.size() ) != CE_None )
205 : CPLError ( CE_Failure, CPLE_FileIO,
206 0 : "ERROR adding %s to %s", "doc.kml", pszName );
207 1 : CPLCloseFileInZip(hZIP);
208 :
209 : }
210 :
211 : /***** loop though the layers and write them *****/
212 :
213 : int iLayer;
214 :
215 3 : for ( iLayer = 0; iLayer < nLayers; iLayer++ ) {
216 2 : ContainerPtr poKlmContainer = papoLayers[iLayer]->GetKmlLayer ( );
217 :
218 2 : if ( poKlmContainer->IsA ( kmldom::Type_Document ) ) {
219 :
220 2 : DocumentPtr poKmlDocument = AsDocument ( poKlmContainer );
221 2 : SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema ( );
222 :
223 2 : if ( !poKmlDocument->get_schema_array_size ( ) &&
224 : poKmlSchema &&
225 : poKmlSchema->get_simplefield_array_size ( ) ) {
226 1 : poKmlDocument->add_schema ( poKmlSchema );
227 2 : }
228 : }
229 :
230 : /***** if we dont have the layers root *****/
231 : /***** make it and add the container *****/
232 :
233 2 : KmlPtr poKmlKml = NULL;
234 :
235 2 : if ( !( poKmlKml = AsKml( papoLayers[iLayer]->GetKmlLayerRoot ( ) ) ) ) {
236 :
237 2 : poKmlKml = m_poKmlFactory->CreateKml ( );
238 :
239 2 : poKmlKml->set_feature ( poKlmContainer );
240 : }
241 :
242 2 : std::string oKmlOut = kmldom::SerializePretty ( poKmlKml );
243 :
244 2 : if ( CPLCreateFileInZip( hZIP, papoLayers[iLayer]->GetFileName ( ), NULL ) != CE_None ||
245 : CPLWriteFileInZip( hZIP, oKmlOut.data(), oKmlOut.size() ) != CE_None )
246 : CPLError ( CE_Failure, CPLE_FileIO,
247 0 : "ERROR adding %s to %s", papoLayers[iLayer]->GetFileName ( ), pszName );
248 2 : CPLCloseFileInZip(hZIP);
249 :
250 : }
251 :
252 : /***** write the style table *****/
253 :
254 1 : if ( m_poKmlStyleKml ) {
255 :
256 0 : KmlPtr poKmlKml = m_poKmlFactory->CreateKml ( );
257 :
258 0 : poKmlKml->set_feature ( m_poKmlStyleKml );
259 0 : std::string oKmlOut = kmldom::SerializePretty ( poKmlKml );
260 :
261 0 : if ( CPLCreateFileInZip( hZIP, "style/style.kml", NULL ) != CE_None ||
262 : CPLWriteFileInZip( hZIP, oKmlOut.data(), oKmlOut.size() ) != CE_None )
263 : CPLError ( CE_Failure, CPLE_FileIO,
264 0 : "ERROR adding %s to %s", "style/style.kml", pszName );
265 0 : CPLCloseFileInZip(hZIP);
266 : }
267 :
268 1 : CPLCloseZip(hZIP);
269 :
270 1 : return;
271 : }
272 :
273 : /******************************************************************************
274 : method to write a dir ds at ds destroy
275 :
276 : Args: none
277 :
278 : Returns: nothing
279 :
280 : ******************************************************************************/
281 :
282 1 : void OGRLIBKMLDataSource::WriteDir (
283 : )
284 : {
285 :
286 : /***** write out the doc.kml ****/
287 :
288 : const char *pszUseDocKml =
289 1 : CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" );
290 :
291 1 : if ( EQUAL ( pszUseDocKml, "yes" ) && m_poKmlDocKml ) {
292 :
293 : /***** if we dont have the doc.kml root *****/
294 : /***** make it and add the container *****/
295 :
296 1 : if ( !m_poKmlDocKmlRoot ) {
297 1 : m_poKmlDocKmlRoot = m_poKmlFactory->CreateKml ( );
298 :
299 1 : AsKml( m_poKmlDocKmlRoot )->set_feature ( m_poKmlDocKml );
300 : }
301 :
302 1 : std::string oKmlOut = kmldom::SerializePretty ( m_poKmlDocKmlRoot );
303 :
304 1 : const char *pszOutfile = CPLFormFilename ( pszName, "doc.kml", NULL );
305 :
306 1 : VSILFILE* fp = VSIFOpenL( pszOutfile, "wb" );
307 1 : if (fp == NULL)
308 : {
309 : CPLError ( CE_Failure, CPLE_FileIO,
310 0 : "ERROR Writing %s to %s", "doc.kml", pszName );
311 : return;
312 : }
313 :
314 1 : VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp);
315 1 : VSIFCloseL(fp);
316 : }
317 :
318 : /***** loop though the layers and write them *****/
319 :
320 : int iLayer;
321 :
322 1 : for ( iLayer = 0; iLayer < nLayers; iLayer++ ) {
323 2 : ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer ( );
324 :
325 2 : if ( poKmlContainer->IsA ( kmldom::Type_Document ) ) {
326 :
327 2 : DocumentPtr poKmlDocument = AsDocument ( poKmlContainer );
328 2 : SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema ( );
329 :
330 2 : if ( !poKmlDocument->get_schema_array_size ( ) &&
331 : poKmlSchema &&
332 : poKmlSchema->get_simplefield_array_size ( ) ) {
333 1 : poKmlDocument->add_schema ( poKmlSchema );
334 2 : };
335 : }
336 :
337 : /***** if we dont have the layers root *****/
338 : /***** make it and add the container *****/
339 :
340 2 : KmlPtr poKmlKml = NULL;
341 :
342 2 : if ( !( poKmlKml = AsKml( papoLayers[iLayer]->GetKmlLayerRoot ( ) ) ) ) {
343 :
344 2 : poKmlKml = m_poKmlFactory->CreateKml ( );
345 :
346 2 : poKmlKml->set_feature ( poKmlContainer );
347 : }
348 :
349 2 : std::string oKmlOut = kmldom::SerializePretty ( poKmlKml );
350 :
351 : const char *pszOutfile = CPLFormFilename ( pszName,
352 2 : papoLayers[iLayer]->
353 : GetFileName ( ),
354 4 : NULL );
355 :
356 2 : VSILFILE* fp = VSIFOpenL( pszOutfile, "wb" );
357 2 : if (fp == NULL)
358 : {
359 : CPLError ( CE_Failure, CPLE_FileIO,
360 : "ERROR Writing %s to %s",
361 0 : papoLayers[iLayer]->GetFileName ( ), pszName );
362 : return;
363 : }
364 :
365 2 : VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp);
366 2 : VSIFCloseL(fp);
367 : }
368 :
369 : /***** write the style table *****/
370 :
371 1 : if ( m_poKmlStyleKml ) {
372 :
373 0 : KmlPtr poKmlKml = m_poKmlFactory->CreateKml ( );
374 :
375 0 : poKmlKml->set_feature ( m_poKmlStyleKml );
376 0 : std::string oKmlOut = kmldom::SerializePretty ( poKmlKml );
377 :
378 : const char *pszOutfile = CPLFormFilename ( pszName,
379 : "style.kml",
380 0 : NULL );
381 :
382 0 : VSILFILE* fp = VSIFOpenL( pszOutfile, "wb" );
383 0 : if (fp == NULL)
384 : {
385 : CPLError ( CE_Failure, CPLE_FileIO,
386 0 : "ERROR Writing %s to %s", "style.kml", pszName );
387 : return;
388 : }
389 :
390 0 : VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp);
391 0 : VSIFCloseL(fp);
392 : }
393 :
394 1 : return;
395 : }
396 :
397 : /******************************************************************************
398 : method to write the datasource to disk
399 :
400 : Args: none
401 :
402 : Returns nothing
403 :
404 : ******************************************************************************/
405 :
406 312 : OGRErr OGRLIBKMLDataSource::SyncToDisk (
407 : )
408 : {
409 :
410 312 : if ( bUpdated ) {
411 :
412 : /***** kml *****/
413 :
414 3 : if ( bUpdate && IsKml ( ) )
415 1 : WriteKml ( );
416 :
417 : /***** kmz *****/
418 :
419 2 : else if ( bUpdate && IsKmz ( ) ) {
420 1 : WriteKmz ( );
421 : }
422 :
423 1 : else if ( bUpdate && IsDir ( ) ) {
424 1 : WriteDir ( );
425 : }
426 :
427 3 : bUpdated = FALSE;
428 : }
429 :
430 312 : return OGRERR_NONE;
431 : }
432 :
433 : /******************************************************************************
434 : OGRLIBKMLDataSource Destructor
435 :
436 : Args: none
437 :
438 : Returns: nothing
439 :
440 : ******************************************************************************/
441 :
442 312 : OGRLIBKMLDataSource::~OGRLIBKMLDataSource ( )
443 : {
444 :
445 :
446 : /***** sync the DS to disk *****/
447 :
448 312 : SyncToDisk ( );
449 :
450 312 : CPLFree ( pszName );
451 :
452 312 : if (! EQUAL(pszStylePath, ""))
453 2 : CPLFree ( pszStylePath );
454 :
455 364 : for ( int i = 0; i < nLayers; i++ )
456 52 : delete papoLayers[i];
457 :
458 312 : CPLFree ( papoLayers );
459 :
460 : //delete m_poStyleTable;
461 :
462 312 : }
463 :
464 :
465 : /******************************************************************************
466 : method to parse a schemas out of a document
467 :
468 : Args: poKmlDocument pointer to the document to parse
469 :
470 : Returns: nothing
471 :
472 : ******************************************************************************/
473 :
474 5 : SchemaPtr OGRLIBKMLDataSource::FindSchema (
475 : const char *pszSchemaUrl )
476 : {
477 5 : char *pszID = NULL;
478 5 : char *pszFile = NULL;
479 5 : char *pszSchemaName = NULL;
480 : char *pszPound;
481 5 : DocumentPtr poKmlDocument = NULL;
482 5 : SchemaPtr poKmlSchemaResult = NULL;
483 :
484 5 : if ( !pszSchemaUrl || !*pszSchemaUrl )
485 0 : return NULL;
486 :
487 5 : if ( *pszSchemaUrl == '#' ) {
488 3 : pszID = CPLStrdup ( pszSchemaUrl + 1 );
489 :
490 : /***** kml *****/
491 :
492 3 : if ( IsKml ( ) && m_poKmlDSContainer->IsA ( kmldom::Type_Document ) )
493 3 : poKmlDocument = AsDocument ( m_poKmlDSContainer );
494 :
495 : /***** kmz *****/
496 :
497 0 : else if ( ( IsKmz ( ) || IsDir ( ) ) && m_poKmlDocKml
498 0 : && m_poKmlDocKml->IsA ( kmldom::Type_Document ) )
499 0 : poKmlDocument = AsDocument ( m_poKmlDocKml );
500 :
501 : }
502 :
503 :
504 2 : else if ( ( pszPound = strchr ( (char *)pszSchemaUrl, '#' ) ) ) {
505 0 : pszFile = CPLStrdup ( pszSchemaUrl );
506 0 : pszID = CPLStrdup ( pszPound + 1 );
507 0 : pszPound = strchr ( pszFile, '#' );
508 0 : *pszPound = '\0';
509 : }
510 :
511 : else {
512 2 : pszSchemaName = CPLStrdup ( pszSchemaUrl );
513 :
514 : /***** kml *****/
515 :
516 2 : if ( IsKml ( ) && m_poKmlDSContainer->IsA ( kmldom::Type_Document ) )
517 2 : poKmlDocument = AsDocument ( m_poKmlDSContainer );
518 :
519 : /***** kmz *****/
520 :
521 0 : else if ( ( IsKmz ( ) || IsDir ( ) ) && m_poKmlDocKml
522 0 : && m_poKmlDocKml->IsA ( kmldom::Type_Document ) )
523 0 : poKmlDocument = AsDocument ( m_poKmlDocKml );
524 :
525 : }
526 :
527 :
528 5 : if ( poKmlDocument) {
529 :
530 5 : size_t nKmlSchemas = poKmlDocument->get_schema_array_size ( );
531 : size_t iKmlSchema;
532 :
533 5 : for ( iKmlSchema = 0; iKmlSchema < nKmlSchemas; iKmlSchema++ ) {
534 : SchemaPtr poKmlSchema =
535 6 : poKmlDocument->get_schema_array_at ( iKmlSchema );
536 6 : if ( poKmlSchema->has_id ( ) && pszID) {
537 4 : if ( EQUAL ( pszID, poKmlSchema->get_id ( ).c_str ( ) ) ) {
538 2 : poKmlSchemaResult = poKmlSchema;
539 : break;
540 : }
541 : }
542 :
543 2 : else if ( poKmlSchema->has_name ( ) && pszSchemaName) {
544 2 : if ( EQUAL ( pszSchemaName, poKmlSchema->get_name ( ).c_str ( ) ) ) {
545 1 : poKmlSchemaResult = poKmlSchema;
546 : break;
547 : }
548 : }
549 :
550 : }
551 : }
552 :
553 5 : if ( pszFile )
554 0 : CPLFree ( pszFile );
555 5 : if ( pszID )
556 3 : CPLFree ( pszID );
557 5 : if ( pszSchemaName )
558 2 : CPLFree ( pszSchemaName );
559 :
560 5 : return poKmlSchemaResult;
561 :
562 : }
563 :
564 : /******************************************************************************
565 : Method to allocate memory for the layer array, create the layer,
566 : and add it to the layer array
567 :
568 : Args: pszLayerName the name of the layer
569 : poSpatialRef the spacial Refrance for the layer
570 : eGType the layers geometry type
571 : poOgrDS pointer to the datasource the layer is in
572 : poKmlRoot pointer to the root kml element of the layer
573 : pszFileName the filename of the layer
574 : bNew true if its a new layer
575 : bUpdate true if the layer is writeable
576 : nGuess a guess at the number of additional layers
577 : we are going to need
578 :
579 : Returns: Pointer to the new layer
580 : ******************************************************************************/
581 :
582 52 : OGRLIBKMLLayer *OGRLIBKMLDataSource::AddLayer (
583 : const char *pszLayerName,
584 : OGRSpatialReference * poSpatialRef,
585 : OGRwkbGeometryType eGType,
586 : OGRLIBKMLDataSource * poOgrDS,
587 : ElementPtr poKmlRoot,
588 : ContainerPtr poKmlContainer,
589 : const char *pszFileName,
590 : int bNew,
591 : int bUpdate,
592 : int nGuess )
593 : {
594 :
595 : /***** check to see if we have enough space to store the layer *****/
596 :
597 52 : if ( nLayers == nAlloced ) {
598 20 : nAlloced += nGuess;
599 : void *tmp = CPLRealloc ( papoLayers,
600 20 : sizeof ( OGRLIBKMLLayer * ) * nAlloced );
601 :
602 20 : papoLayers = ( OGRLIBKMLLayer ** ) tmp;
603 : }
604 :
605 : /***** create the layer *****/
606 :
607 52 : int iLayer = nLayers++;
608 :
609 : OGRLIBKMLLayer *poOgrLayer = new OGRLIBKMLLayer ( pszLayerName,
610 : poSpatialRef,
611 : eGType,
612 : poOgrDS,
613 : poKmlRoot,
614 : poKmlContainer,
615 : pszFileName,
616 : bNew,
617 52 : bUpdate );
618 :
619 : /***** add the layer to the array *****/
620 :
621 52 : papoLayers[iLayer] = poOgrLayer;
622 :
623 52 : return poOgrLayer;
624 : }
625 :
626 : /******************************************************************************
627 : method to parse multiple layers out of a container
628 :
629 : Args: poKmlContainer pointer to the container to parse
630 : poOgrSRS SRS to use when creating the layer
631 :
632 : Returns: number of features in the container that are not another
633 : container
634 :
635 : ******************************************************************************/
636 :
637 56 : int OGRLIBKMLDataSource::ParseLayers (
638 : ContainerPtr poKmlContainer,
639 : OGRSpatialReference * poOgrSRS )
640 : {
641 56 : int nResult = 0;
642 :
643 : /***** if container is null just bail now *****/
644 :
645 56 : if ( !poKmlContainer )
646 0 : return nResult;
647 :
648 56 : size_t nKmlFeatures = poKmlContainer->get_feature_array_size ( );
649 :
650 : /***** loop over the container to seperate the style, layers, etc *****/
651 :
652 : size_t iKmlFeature;
653 :
654 242 : for ( iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++ ) {
655 : FeaturePtr poKmlFeat =
656 186 : poKmlContainer->get_feature_array_at ( iKmlFeature );
657 :
658 : /***** container *****/
659 :
660 186 : if ( poKmlFeat->IsA ( kmldom::Type_Container ) ) {
661 :
662 : /***** see if the container has a name *****/
663 :
664 39 : std::string oKmlFeatName;
665 39 : if ( poKmlFeat->has_name ( ) ) {
666 : /* Strip leading and trailing spaces */
667 39 : const char* pszName = poKmlFeat->get_name ( ).c_str();
668 78 : while(*pszName == ' ' || *pszName == '\n' || *pszName == '\r' || *pszName == '\t' )
669 0 : pszName ++;
670 39 : oKmlFeatName = pszName;
671 39 : int nSize = (int)oKmlFeatName.size();
672 78 : while (nSize > 0 &&
673 : (oKmlFeatName[nSize-1] == ' ' || oKmlFeatName[nSize-1] == '\n' ||
674 : oKmlFeatName[nSize-1] == '\r' || oKmlFeatName[nSize-1] == '\t'))
675 : {
676 0 : nSize --;
677 0 : oKmlFeatName.resize(nSize);
678 : }
679 : }
680 :
681 : /***** use the feature index number as the name *****/
682 : /***** not sure i like this c++ ich *****/
683 :
684 : else {
685 0 : std::stringstream oOut;
686 0 : oOut << iKmlFeature;
687 0 : oKmlFeatName = "Layer";
688 0 : oKmlFeatName.append(oOut.str ( ));
689 : }
690 :
691 : /***** create the layer *****/
692 :
693 : AddLayer ( oKmlFeatName.c_str ( ),
694 : poOgrSRS, wkbUnknown, this,
695 39 : NULL, AsContainer( poKmlFeat ), "", FALSE, bUpdate, nKmlFeatures );
696 :
697 : }
698 :
699 : else
700 147 : nResult++;
701 : }
702 :
703 56 : return nResult;
704 : }
705 :
706 : /******************************************************************************
707 : function to get the container from the kmlroot
708 :
709 : Args: poKmlRoot the root element
710 :
711 : Returns: root if its a container, if its a kml the container it
712 : contains, or NULL
713 :
714 : ******************************************************************************/
715 :
716 16 : static ContainerPtr GetContainerFromRoot (
717 : KmlFactory *m_poKmlFactory, ElementPtr poKmlRoot )
718 : {
719 16 : ContainerPtr poKmlContainer = NULL;
720 :
721 16 : int bReadGroundOverlay = CSLTestBoolean(CPLGetConfigOption("LIBKML_READ_GROUND_OVERLAY", "YES"));
722 :
723 16 : if ( poKmlRoot ) {
724 :
725 : /***** skip over the <kml> we want the container *****/
726 :
727 16 : if ( poKmlRoot->IsA ( kmldom::Type_kml ) ) {
728 :
729 16 : KmlPtr poKmlKml = AsKml ( poKmlRoot );
730 :
731 16 : if ( poKmlKml->has_feature ( ) ) {
732 16 : FeaturePtr poKmlFeat = poKmlKml->get_feature ( );
733 :
734 16 : if ( poKmlFeat->IsA ( kmldom::Type_Container ) )
735 14 : poKmlContainer = AsContainer ( poKmlFeat );
736 2 : else if ( poKmlFeat->IsA ( kmldom::Type_Placemark ) ||
737 0 : (bReadGroundOverlay && poKmlFeat->IsA ( kmldom::Type_GroundOverlay )) )
738 : {
739 2 : poKmlContainer = m_poKmlFactory->CreateDocument ( );
740 2 : poKmlContainer->add_feature ( kmldom::AsFeature(kmlengine::Clone(poKmlFeat)) );
741 16 : }
742 16 : }
743 :
744 : }
745 :
746 0 : else if ( poKmlRoot->IsA ( kmldom::Type_Container ) )
747 0 : poKmlContainer = AsContainer ( poKmlRoot );
748 : }
749 :
750 0 : return poKmlContainer;
751 : }
752 :
753 : /******************************************************************************
754 : method to parse a kml string into the style table
755 : ******************************************************************************/
756 :
757 0 : int OGRLIBKMLDataSource::ParseIntoStyleTable (
758 : std::string *poKmlStyleKml,
759 : const char *pszMyStylePath)
760 : {
761 :
762 : /***** parse the kml into the dom *****/
763 :
764 0 : std::string oKmlErrors;
765 0 : ElementPtr poKmlRoot = kmldom::Parse ( *poKmlStyleKml, &oKmlErrors );
766 :
767 0 : if ( !poKmlRoot ) {
768 : CPLError ( CE_Failure, CPLE_OpenFailed,
769 : "ERROR Parseing style kml %s :%s",
770 0 : pszStylePath, oKmlErrors.c_str ( ) );
771 0 : return false;
772 : }
773 :
774 0 : ContainerPtr poKmlContainer;
775 :
776 0 : if ( !( poKmlContainer = GetContainerFromRoot ( m_poKmlFactory, poKmlRoot ) ) ) {
777 0 : return false;
778 : }
779 :
780 0 : ParseStyles ( AsDocument ( poKmlContainer ), &m_poStyleTable );
781 0 : pszStylePath = CPLStrdup(pszMyStylePath);
782 :
783 :
784 0 : return true;
785 : }
786 :
787 : /******************************************************************************
788 : method to open a kml file
789 :
790 : Args: pszFilename file to open
791 : bUpdate update mode
792 :
793 : Returns: True on success, false on failure
794 :
795 : ******************************************************************************/
796 :
797 10 : int OGRLIBKMLDataSource::OpenKml (
798 : const char *pszFilename,
799 : int bUpdate )
800 : {
801 10 : std::string oKmlKml;
802 : char szBuffer[1024+1];
803 :
804 10 : VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
805 10 : if (fp == NULL)
806 : {
807 : CPLError ( CE_Failure, CPLE_OpenFailed,
808 0 : "Cannot open %s", pszFilename );
809 0 : return FALSE;
810 : }
811 : int nRead;
812 142 : while ((nRead = VSIFReadL(szBuffer, 1, 1024, fp)) != 0)
813 : {
814 : try
815 : {
816 122 : oKmlKml.append(szBuffer, nRead);
817 : }
818 0 : catch(std::bad_alloc& e)
819 : {
820 0 : VSIFCloseL(fp);
821 0 : return FALSE;
822 : }
823 : }
824 10 : VSIFCloseL(fp);
825 :
826 10 : CPLLocaleC oLocaleForcer;
827 :
828 : /***** create a SRS *****/
829 :
830 : OGRSpatialReference *poOgrSRS =
831 10 : new OGRSpatialReference ( OGRLIBKMLSRSWKT );
832 :
833 : /***** parse the kml into the DOM *****/
834 :
835 10 : std::string oKmlErrors;
836 :
837 10 : ElementPtr poKmlRoot = kmldom::Parse ( oKmlKml, &oKmlErrors );
838 :
839 10 : if ( !poKmlRoot ) {
840 : CPLError ( CE_Failure, CPLE_OpenFailed,
841 : "ERROR Parseing kml %s :%s",
842 0 : pszFilename, oKmlErrors.c_str ( ) );
843 0 : delete poOgrSRS;
844 :
845 0 : return FALSE;
846 : }
847 :
848 : /***** get the container from root *****/
849 :
850 10 : if ( !( m_poKmlDSContainer = GetContainerFromRoot ( m_poKmlFactory, poKmlRoot ) ) ) {
851 : CPLError ( CE_Failure, CPLE_OpenFailed,
852 : "ERROR Parseing kml %s :%s %s",
853 : pszFilename, "This file does not fit the OGR model,",
854 0 : "there is no container element at the root." );
855 0 : delete poOgrSRS;
856 :
857 0 : return FALSE;
858 : }
859 :
860 10 : m_isKml = TRUE;
861 :
862 : /***** get the styles *****/
863 :
864 10 : ParseStyles ( AsDocument ( m_poKmlDSContainer ), &m_poStyleTable );
865 :
866 : /***** parse for layers *****/
867 :
868 10 : int nPlacemarks = ParseLayers ( m_poKmlDSContainer, poOgrSRS );
869 :
870 : /***** if there is placemarks in the root its a layer *****/
871 :
872 10 : if ( nPlacemarks && !nLayers ) {
873 : AddLayer ( CPLGetBasename ( pszFilename ),
874 : poOgrSRS, wkbUnknown,
875 2 : this, poKmlRoot, m_poKmlDSContainer, pszFilename, FALSE, bUpdate, 1 );
876 : }
877 :
878 10 : delete poOgrSRS;
879 :
880 10 : return TRUE;
881 : }
882 :
883 : /******************************************************************************
884 : method to open a kmz file
885 :
886 : Args: pszFilename file to open
887 : bUpdate update mode
888 :
889 : Returns: True on success, false on failure
890 :
891 : ******************************************************************************/
892 :
893 :
894 1 : int OGRLIBKMLDataSource::OpenKmz (
895 : const char *pszFilename,
896 : int bUpdate )
897 : {
898 1 : std::string oKmlKmz;
899 : char szBuffer[1024+1];
900 :
901 1 : VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
902 1 : if (fp == NULL)
903 : {
904 : CPLError ( CE_Failure, CPLE_OpenFailed,
905 0 : "Cannot open %s", pszFilename );
906 0 : return FALSE;
907 : }
908 : int nRead;
909 3 : while ((nRead = VSIFReadL(szBuffer, 1, 1024, fp)) != 0)
910 : {
911 : try
912 : {
913 1 : oKmlKmz.append(szBuffer, nRead);
914 : }
915 0 : catch(std::bad_alloc& e)
916 : {
917 0 : VSIFCloseL(fp);
918 0 : return FALSE;
919 : }
920 : }
921 1 : VSIFCloseL(fp);
922 :
923 1 : KmzFile *poKmlKmzfile = KmzFile::OpenFromString ( oKmlKmz );
924 :
925 1 : if ( !poKmlKmzfile ) {
926 : CPLError ( CE_Failure, CPLE_OpenFailed,
927 0 : "%s is not a valid kmz file", pszFilename );
928 0 : return FALSE;
929 : }
930 :
931 1 : CPLLocaleC oLocaleForcer;
932 :
933 : /***** read the doc.kml *****/
934 :
935 1 : std::string oKmlKml;
936 1 : std::string oKmlKmlPath;
937 1 : if ( !poKmlKmzfile->ReadKmlAndGetPath ( &oKmlKml, &oKmlKmlPath ) ) {
938 :
939 0 : return FALSE;
940 : }
941 :
942 : /***** create a SRS *****/
943 :
944 : OGRSpatialReference *poOgrSRS =
945 1 : new OGRSpatialReference ( OGRLIBKMLSRSWKT );
946 :
947 : /***** parse the kml into the DOM *****/
948 :
949 1 : std::string oKmlErrors;
950 1 : ElementPtr poKmlDocKmlRoot = kmldom::Parse ( oKmlKml, &oKmlErrors );
951 :
952 1 : if ( !poKmlDocKmlRoot ) {
953 : CPLError ( CE_Failure, CPLE_OpenFailed,
954 : "ERROR Parseing kml layer %s from %s :%s",
955 : oKmlKmlPath.c_str ( ),
956 0 : pszFilename, oKmlErrors.c_str ( ) );
957 0 : delete poOgrSRS;
958 :
959 0 : return FALSE;
960 : }
961 :
962 : /***** get the child contianer from root *****/
963 :
964 1 : ContainerPtr poKmlContainer;
965 :
966 1 : if (!(poKmlContainer = GetContainerFromRoot ( m_poKmlFactory, poKmlDocKmlRoot ))) {
967 : CPLError ( CE_Failure, CPLE_OpenFailed,
968 : "ERROR Parseing %s from %s :%s",
969 : oKmlKmlPath.c_str ( ),
970 0 : pszFilename, "kml contains no Containers" );
971 0 : delete poOgrSRS;
972 :
973 0 : return FALSE;
974 : }
975 :
976 : /***** loop over the container looking for network links *****/
977 :
978 1 : size_t nKmlFeatures = poKmlContainer->get_feature_array_size ( );
979 : size_t iKmlFeature;
980 1 : int nLinks = 0;
981 :
982 1 : for ( iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++ ) {
983 : FeaturePtr poKmlFeat =
984 2 : poKmlContainer->get_feature_array_at ( iKmlFeature );
985 :
986 : /***** is it a network link? *****/
987 :
988 2 : if ( !poKmlFeat->IsA ( kmldom::Type_NetworkLink ) )
989 0 : continue;
990 :
991 2 : NetworkLinkPtr poKmlNetworkLink = AsNetworkLink ( poKmlFeat );
992 :
993 : /***** does it have a link? *****/
994 :
995 2 : if ( !poKmlNetworkLink->has_link ( ) )
996 0 : continue;
997 :
998 2 : LinkPtr poKmlLink = poKmlNetworkLink->get_link ( );
999 :
1000 : /***** does the link have a href? *****/
1001 :
1002 2 : if ( !poKmlLink->has_href ( ) )
1003 0 : continue;
1004 :
1005 : kmlengine::Href * poKmlHref =
1006 2 : new kmlengine::Href ( poKmlLink->get_href ( ) );
1007 :
1008 : /***** is the link relative? *****/
1009 :
1010 4 : if ( poKmlHref->IsRelativePath ( ) ) {
1011 :
1012 2 : nLinks++;
1013 :
1014 2 : std::string oKml;
1015 2 : if ( poKmlKmzfile->
1016 : ReadFile ( poKmlHref->get_path ( ).c_str ( ), &oKml ) ) {
1017 :
1018 : /***** parse the kml into the DOM *****/
1019 :
1020 2 : std::string oKmlErrors;
1021 2 : ElementPtr poKmlLyrRoot = kmldom::Parse ( oKml, &oKmlErrors );
1022 :
1023 2 : if ( !poKmlLyrRoot ) {
1024 : CPLError ( CE_Failure, CPLE_OpenFailed,
1025 : "ERROR Parseing kml layer %s from %s :%s",
1026 : poKmlHref->get_path ( ).c_str ( ),
1027 0 : pszFilename, oKmlErrors.c_str ( ) );
1028 0 : delete poKmlHref;
1029 :
1030 0 : continue;
1031 : }
1032 :
1033 : /***** get the container from root *****/
1034 :
1035 : ContainerPtr poKmlLyrContainer =
1036 2 : GetContainerFromRoot ( m_poKmlFactory, poKmlLyrRoot );
1037 :
1038 2 : if ( !poKmlLyrContainer )
1039 : {
1040 : CPLError ( CE_Failure, CPLE_OpenFailed,
1041 : "ERROR Parseing kml layer %s from %s :%s",
1042 : poKmlHref->get_path ( ).c_str ( ),
1043 0 : pszFilename, oKmlErrors.c_str ( ) );
1044 0 : delete poKmlHref;
1045 :
1046 0 : continue;
1047 : }
1048 :
1049 : /***** create the layer *****/
1050 :
1051 : AddLayer ( CPLGetBasename
1052 : ( poKmlHref->get_path ( ).c_str ( ) ), poOgrSRS,
1053 : wkbUnknown, this, poKmlLyrRoot, poKmlLyrContainer,
1054 : poKmlHref->get_path ( ).c_str ( ), FALSE, bUpdate,
1055 2 : nKmlFeatures );
1056 :
1057 0 : }
1058 : }
1059 :
1060 : /***** cleanup *****/
1061 :
1062 2 : delete poKmlHref;
1063 : }
1064 :
1065 : /***** if the doc.kml has links store it so if were in update mode we can write it *****/
1066 :
1067 1 : if ( nLinks ) {
1068 1 : m_poKmlDocKml = poKmlContainer;
1069 1 : m_poKmlDocKmlRoot = poKmlDocKmlRoot;
1070 : }
1071 :
1072 : /***** if the doc.kml has no links treat it as a normal kml file *****/
1073 :
1074 : else {
1075 :
1076 : /* todo there could still be a seperate styles file in the kmz
1077 : if there is this would be a layer style table IF its only a single
1078 : layer
1079 : */
1080 :
1081 : /***** get the styles *****/
1082 :
1083 0 : ParseStyles ( AsDocument ( poKmlContainer ), &m_poStyleTable );
1084 :
1085 : /***** parse for layers *****/
1086 :
1087 0 : int nPlacemarks = ParseLayers ( poKmlContainer, poOgrSRS );
1088 :
1089 : /***** if there is placemarks in the root its a layer *****/
1090 :
1091 0 : if ( nPlacemarks && !nLayers ) {
1092 : AddLayer ( CPLGetBasename ( pszFilename ),
1093 : poOgrSRS, wkbUnknown,
1094 0 : this, poKmlDocKmlRoot, poKmlContainer, pszFilename, FALSE, bUpdate, 1 );
1095 : }
1096 : }
1097 :
1098 : /***** read the style table if it has one *****/
1099 :
1100 1 : std::string oKmlStyleKml;
1101 1 : if ( poKmlKmzfile->ReadFile ( "style/style.kml", &oKmlStyleKml ) )
1102 0 : ParseIntoStyleTable ( &oKmlStyleKml, "style/style.kml");
1103 :
1104 : /***** cleanup *****/
1105 :
1106 1 : delete poOgrSRS;
1107 :
1108 1 : delete poKmlKmzfile;
1109 1 : m_isKmz = TRUE;
1110 :
1111 1 : return TRUE;
1112 : }
1113 :
1114 : /******************************************************************************
1115 : method to open a dir
1116 :
1117 : Args: pszFilename Dir to open
1118 : bUpdate update mode
1119 :
1120 : Returns: True on success, false on failure
1121 :
1122 : ******************************************************************************/
1123 :
1124 7 : int OGRLIBKMLDataSource::OpenDir (
1125 : const char *pszFilename,
1126 : int bUpdate )
1127 : {
1128 :
1129 7 : char **papszDirList = NULL;
1130 :
1131 7 : if ( !( papszDirList = VSIReadDir ( pszFilename ) ) )
1132 0 : return FALSE;
1133 :
1134 : /***** create a SRS *****/
1135 :
1136 : OGRSpatialReference *poOgrSRS =
1137 7 : new OGRSpatialReference ( OGRLIBKMLSRSWKT );
1138 :
1139 7 : int nFiles = CSLCount ( papszDirList );
1140 : int iFile;
1141 :
1142 7 : for ( iFile = 0; iFile < nFiles; iFile++ ) {
1143 :
1144 : /***** make sure its a .kml file *****/
1145 :
1146 510 : if ( !EQUAL ( CPLGetExtension ( papszDirList[iFile] ), "kml" ) )
1147 507 : continue;
1148 :
1149 : /***** read the file *****/
1150 3 : std::string oKmlKml;
1151 : char szBuffer[1024+1];
1152 :
1153 : CPLString osFilePath =
1154 3 : CPLFormFilename ( pszFilename, papszDirList[iFile], NULL );
1155 :
1156 3 : VSILFILE* fp = VSIFOpenL(osFilePath, "rb");
1157 3 : if (fp == NULL)
1158 : {
1159 : CPLError ( CE_Failure, CPLE_OpenFailed,
1160 0 : "Cannot open %s", osFilePath.c_str() );
1161 0 : continue;
1162 : }
1163 :
1164 : int nRead;
1165 12 : while ((nRead = VSIFReadL(szBuffer, 1, 1024, fp)) != 0)
1166 : {
1167 : try
1168 : {
1169 6 : oKmlKml.append(szBuffer, nRead);
1170 : }
1171 0 : catch(std::bad_alloc& e)
1172 : {
1173 0 : VSIFCloseL(fp);
1174 0 : CSLDestroy ( papszDirList );
1175 0 : return FALSE;
1176 : }
1177 : }
1178 3 : VSIFCloseL(fp);
1179 :
1180 3 : CPLLocaleC oLocaleForcer;
1181 :
1182 : /***** parse the kml into the DOM *****/
1183 :
1184 3 : std::string oKmlErrors;
1185 3 : ElementPtr poKmlRoot = kmldom::Parse ( oKmlKml, &oKmlErrors );
1186 :
1187 3 : if ( !poKmlRoot ) {
1188 : CPLError ( CE_Failure, CPLE_OpenFailed,
1189 : "ERROR Parseing kml layer %s from %s :%s",
1190 0 : osFilePath.c_str(), pszFilename, oKmlErrors.c_str ( ) );
1191 :
1192 0 : continue;
1193 : }
1194 :
1195 : /***** get the cintainer from the root *****/
1196 :
1197 3 : ContainerPtr poKmlContainer;
1198 :
1199 3 : if ( !( poKmlContainer = GetContainerFromRoot ( m_poKmlFactory, poKmlRoot ) ) ) {
1200 : CPLError ( CE_Failure, CPLE_OpenFailed,
1201 : "ERROR Parseing kml %s :%s %s",
1202 : pszFilename,
1203 : "This file does not fit the OGR model,",
1204 0 : "there is no container element at the root." );
1205 0 : continue;
1206 : }
1207 :
1208 : /***** is it a style table? *****/
1209 :
1210 3 : if ( EQUAL ( papszDirList[iFile], "style.kml" ) ) {
1211 0 : ParseStyles ( AsDocument ( poKmlContainer ), &m_poStyleTable );
1212 0 : pszStylePath = CPLStrdup((char *) "style.kml");
1213 0 : continue;
1214 : }
1215 :
1216 :
1217 : /***** create the layer *****/
1218 :
1219 : AddLayer ( CPLGetBasename ( osFilePath.c_str() ),
1220 : poOgrSRS, wkbUnknown,
1221 3 : this, poKmlRoot, poKmlContainer, osFilePath.c_str(), FALSE, bUpdate, nFiles );
1222 :
1223 : }
1224 :
1225 7 : delete poOgrSRS;
1226 :
1227 7 : CSLDestroy ( papszDirList );
1228 :
1229 7 : if ( nLayers > 0 ) {
1230 1 : m_isDir = TRUE;
1231 1 : return TRUE;
1232 : }
1233 :
1234 6 : return FALSE;
1235 : }
1236 :
1237 : /******************************************************************************
1238 : Method to open a datasource
1239 :
1240 : Args: pszFilename Darasource to open
1241 : bUpdate update mode
1242 :
1243 : Returns: True on success, false on failure
1244 :
1245 : ******************************************************************************/
1246 :
1247 519 : static int CheckIsKMZ(const char *pszFilename)
1248 : {
1249 519 : char** papszFiles = VSIReadDir(pszFilename);
1250 519 : char** papszIter = papszFiles;
1251 519 : int bFoundKML = FALSE;
1252 1533 : while(papszIter && *papszIter)
1253 : {
1254 495 : if (EQUAL(CPLGetExtension(*papszIter), "kml"))
1255 : {
1256 0 : bFoundKML = TRUE;
1257 0 : break;
1258 : }
1259 : else
1260 : {
1261 495 : CPLString osFilename(pszFilename);
1262 495 : osFilename += "/";
1263 495 : osFilename += *papszIter;
1264 495 : if (CheckIsKMZ(osFilename))
1265 : {
1266 0 : bFoundKML = TRUE;
1267 : break;
1268 0 : }
1269 : }
1270 495 : papszIter ++;
1271 : }
1272 519 : CSLDestroy(papszFiles);
1273 519 : return bFoundKML;
1274 : }
1275 :
1276 309 : int OGRLIBKMLDataSource::Open (
1277 : const char *pszFilename,
1278 : int bUpdate )
1279 : {
1280 :
1281 309 : this->bUpdate = bUpdate;
1282 309 : pszName = CPLStrdup ( pszFilename );
1283 :
1284 : /***** dir *****/
1285 :
1286 309 : VSIStatBufL sStatBuf = { };
1287 309 : if ( !VSIStatExL ( pszFilename, &sStatBuf, VSI_STAT_NATURE_FLAG ) &&
1288 : VSI_ISDIR ( sStatBuf.st_mode ) )
1289 7 : return OpenDir ( pszFilename, bUpdate );
1290 :
1291 : /***** kml *****/
1292 :
1293 302 : else if ( EQUAL ( CPLGetExtension ( pszFilename ), "kml" ) )
1294 10 : return OpenKml ( pszFilename, bUpdate );
1295 :
1296 : /***** kmz *****/
1297 :
1298 292 : else if ( EQUAL ( CPLGetExtension ( pszFilename ), "kmz" ) )
1299 1 : return OpenKmz ( pszFilename, bUpdate );
1300 :
1301 : else
1302 : {
1303 : char szBuffer[1024+1];
1304 291 : VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
1305 291 : if (fp == NULL)
1306 173 : return FALSE;
1307 :
1308 118 : int nRead = VSIFReadL(szBuffer, 1, 1024, fp);
1309 118 : szBuffer[nRead] = 0;
1310 :
1311 118 : VSIFCloseL(fp);
1312 :
1313 : /* Does it look like a zip file ? */
1314 288 : if (nRead == 1024 &&
1315 122 : szBuffer[0] == 0x50 && szBuffer[1] == 0x4B &&
1316 48 : szBuffer[2] == 0x03 && szBuffer[3] == 0x04)
1317 : {
1318 24 : CPLString osFilename("/vsizip/");
1319 24 : osFilename += pszFilename;
1320 24 : if (!CheckIsKMZ(osFilename))
1321 24 : return FALSE;
1322 :
1323 0 : return OpenKmz ( pszFilename, bUpdate );
1324 : }
1325 :
1326 94 : if (strstr(szBuffer, "<kml>") || strstr(szBuffer, "<kml xmlns="))
1327 0 : return OpenKml ( pszFilename, bUpdate );
1328 :
1329 94 : return FALSE;
1330 : }
1331 : }
1332 :
1333 : /******************************************************************************
1334 : method to create a single file .kml ds
1335 :
1336 : Args: pszFilename the datasource to create
1337 : papszOptions datasource creation options
1338 :
1339 : Returns: True on success, false on failure
1340 :
1341 : ******************************************************************************/
1342 :
1343 1 : int OGRLIBKMLDataSource::CreateKml (
1344 : const char *pszFilename,
1345 : char **papszOptions )
1346 : {
1347 :
1348 1 : m_poKmlDSKml = m_poKmlFactory->CreateKml ( );
1349 1 : DocumentPtr poKmlDocument = m_poKmlFactory->CreateDocument ( );
1350 :
1351 1 : m_poKmlDSKml->set_feature ( poKmlDocument );
1352 1 : m_poKmlDSContainer = poKmlDocument;
1353 1 : m_isKml = TRUE;
1354 1 : bUpdated = TRUE;
1355 :
1356 1 : return true;
1357 : }
1358 :
1359 : /******************************************************************************
1360 : method to create a .kmz ds
1361 :
1362 : Args: pszFilename the datasource to create
1363 : papszOptions datasource creation options
1364 :
1365 : Returns: True on success, false on failure
1366 :
1367 : ******************************************************************************/
1368 :
1369 1 : int OGRLIBKMLDataSource::CreateKmz (
1370 : const char *pszFilename,
1371 : char **papszOptions )
1372 : {
1373 :
1374 :
1375 : /***** create the doc.kml *****/
1376 :
1377 1 : const char *namefield = CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" );
1378 :
1379 1 : if ( !strcmp ( namefield, "yes" ) ) {
1380 1 : m_poKmlDocKml = m_poKmlFactory->CreateDocument ( );
1381 : }
1382 :
1383 1 : pszStylePath = CPLStrdup((char *) "style/style.kml");
1384 :
1385 1 : m_isKmz = TRUE;
1386 1 : bUpdated = TRUE;
1387 :
1388 1 : return TRUE;
1389 : }
1390 :
1391 : /******************************************************************************
1392 : Method to create a dir datasource
1393 :
1394 : Args: pszFilename the datasource to create
1395 : papszOptions datasource creation options
1396 :
1397 : Returns: True on success, false on failure
1398 :
1399 : ******************************************************************************/
1400 :
1401 1 : int OGRLIBKMLDataSource::CreateDir (
1402 : const char *pszFilename,
1403 : char **papszOptions )
1404 : {
1405 :
1406 1 : if ( VSIMkdir ( pszFilename, 0755 ) ) {
1407 : CPLError ( CE_Failure, CPLE_AppDefined,
1408 0 : "ERROR Creating dir: %s for KML datasource", pszFilename );
1409 0 : return FALSE;
1410 : }
1411 :
1412 1 : m_isDir = TRUE;
1413 1 : bUpdated = TRUE;
1414 :
1415 :
1416 1 : const char *namefield = CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" );
1417 :
1418 1 : if ( !strcmp ( namefield, "yes" ) ) {
1419 1 : m_poKmlDocKml = m_poKmlFactory->CreateDocument ( );
1420 : }
1421 :
1422 1 : pszStylePath = CPLStrdup((char *) "style.kml");
1423 :
1424 1 : return TRUE;
1425 : }
1426 :
1427 :
1428 : /******************************************************************************
1429 : method to create a datasource
1430 :
1431 : Args: pszFilename the datasource to create
1432 : papszOptions datasource creation options
1433 :
1434 : Returns: True on success, false on failure
1435 :
1436 : env vars:
1437 : LIBKML_USE_DOC.KML default: yes
1438 :
1439 : ******************************************************************************/
1440 :
1441 3 : int OGRLIBKMLDataSource::Create (
1442 : const char *pszFilename,
1443 : char **papszOptions )
1444 : {
1445 :
1446 3 : int bResult = FALSE;
1447 :
1448 3 : if (strcmp(pszFilename, "/dev/stdout") == 0)
1449 0 : pszFilename = "/vsistdout/";
1450 :
1451 3 : pszName = CPLStrdup ( pszFilename );
1452 3 : bUpdate = TRUE;
1453 :
1454 : /***** kml *****/
1455 :
1456 3 : if ( strcmp(pszFilename, "/vsistdout/") == 0 ||
1457 : strncmp(pszFilename, "/vsigzip/", 9) == 0 ||
1458 : EQUAL ( CPLGetExtension ( pszFilename ), "kml" ) )
1459 1 : bResult = CreateKml ( pszFilename, papszOptions );
1460 :
1461 : /***** kmz *****/
1462 :
1463 2 : else if ( EQUAL ( CPLGetExtension ( pszFilename ), "kmz" ) )
1464 1 : bResult = CreateKmz ( pszFilename, papszOptions );
1465 :
1466 : /***** dir *****/
1467 :
1468 : else
1469 1 : bResult = CreateDir ( pszFilename, papszOptions );
1470 :
1471 3 : return bResult;
1472 : }
1473 :
1474 : /******************************************************************************
1475 : method to get a layer by index
1476 :
1477 : Args: iLayer the index of the layer to get
1478 :
1479 : Returns: pointer to the layer, or NULL if the layer does not exist
1480 :
1481 : ******************************************************************************/
1482 :
1483 23 : OGRLayer *OGRLIBKMLDataSource::GetLayer (
1484 : int iLayer )
1485 : {
1486 23 : if ( iLayer < 0 || iLayer >= nLayers )
1487 2 : return NULL;
1488 : else
1489 21 : return papoLayers[iLayer];
1490 :
1491 : }
1492 :
1493 : /******************************************************************************
1494 : method to get a layer by name
1495 :
1496 : Args: pszname name of the layer to get
1497 :
1498 : Returns: pointer to the layer, or NULL if the layer does not exist
1499 :
1500 : ******************************************************************************/
1501 :
1502 71 : OGRLayer *OGRLIBKMLDataSource::GetLayerByName (
1503 : const char *pszname )
1504 : {
1505 71 : int iLayer = 0;
1506 :
1507 381 : for ( iLayer = 0; iLayer < nLayers; iLayer++ ) {
1508 380 : if ( EQUAL ( pszname, papoLayers[iLayer]->GetName ( ) ) )
1509 70 : return papoLayers[iLayer];
1510 : }
1511 :
1512 1 : return NULL;
1513 : }
1514 :
1515 :
1516 : /******************************************************************************
1517 : method to DeleteLayers in a .kml datasource
1518 :
1519 : Args: iLayer index of the layer to delete
1520 :
1521 : Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure
1522 :
1523 : ******************************************************************************/
1524 :
1525 0 : OGRErr OGRLIBKMLDataSource::DeleteLayerKml (
1526 : int iLayer )
1527 : {
1528 0 : OGRLIBKMLLayer *poOgrLayer = ( OGRLIBKMLLayer * ) papoLayers[iLayer];
1529 :
1530 : /***** loop over the features *****/
1531 :
1532 0 : size_t nKmlFeatures = m_poKmlDSContainer->get_feature_array_size ( );
1533 : size_t iKmlFeature;
1534 :
1535 0 : for ( iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++ ) {
1536 : FeaturePtr poKmlFeat =
1537 0 : m_poKmlDSContainer->get_feature_array_at ( iKmlFeature );
1538 :
1539 0 : if ( poKmlFeat == poOgrLayer->GetKmlLayer ( ) ) {
1540 0 : m_poKmlDSContainer->DeleteFeatureAt ( iKmlFeature );
1541 : break;
1542 : }
1543 :
1544 : }
1545 :
1546 :
1547 0 : return OGRERR_NONE;
1548 : }
1549 :
1550 : /******************************************************************************
1551 : method to DeleteLayers in a .kmz datasource
1552 :
1553 : Args: iLayer index of the layer to delete
1554 :
1555 : Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure
1556 :
1557 : ******************************************************************************/
1558 :
1559 0 : OGRErr OGRLIBKMLDataSource::DeleteLayerKmz (
1560 : int iLayer )
1561 : {
1562 0 : OGRLIBKMLLayer *poOgrLayer = ( OGRLIBKMLLayer * ) papoLayers[iLayer];
1563 :
1564 : const char *pszUseDocKml =
1565 0 : CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" );
1566 :
1567 0 : if ( EQUAL ( pszUseDocKml, "yes" ) && m_poKmlDocKml ) {
1568 :
1569 : /***** loop over the features *****/
1570 :
1571 0 : size_t nKmlFeatures = m_poKmlDocKml->get_feature_array_size ( );
1572 : size_t iKmlFeature;
1573 :
1574 0 : for ( iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++ ) {
1575 : FeaturePtr poKmlFeat =
1576 0 : m_poKmlDocKml->get_feature_array_at ( iKmlFeature );
1577 :
1578 0 : if ( poKmlFeat->IsA ( kmldom::Type_NetworkLink ) ) {
1579 0 : NetworkLinkPtr poKmlNetworkLink = AsNetworkLink ( poKmlFeat );
1580 :
1581 : /***** does it have a link? *****/
1582 :
1583 0 : if ( poKmlNetworkLink->has_link ( ) ) {
1584 0 : LinkPtr poKmlLink = poKmlNetworkLink->get_link ( );
1585 :
1586 : /***** does the link have a href? *****/
1587 :
1588 0 : if ( poKmlLink->has_href ( ) ) {
1589 : kmlengine::Href * poKmlHref =
1590 0 : new kmlengine::Href ( poKmlLink->get_href ( ) );
1591 :
1592 : /***** is the link relative? *****/
1593 :
1594 0 : if ( poKmlHref->IsRelativePath ( ) ) {
1595 :
1596 : const char *pszLink =
1597 0 : poKmlHref->get_path ( ).c_str ( );
1598 :
1599 0 : if ( EQUAL
1600 : ( pszLink, poOgrLayer->GetFileName ( ) ) ) {
1601 0 : m_poKmlDocKml->DeleteFeatureAt ( iKmlFeature );
1602 : break;
1603 : }
1604 :
1605 :
1606 : }
1607 0 : }
1608 0 : }
1609 : }
1610 : }
1611 :
1612 : }
1613 :
1614 0 : return OGRERR_NONE;
1615 : }
1616 :
1617 : /******************************************************************************
1618 : method to delete a layer in a datasource
1619 :
1620 : Args: iLayer index of the layer to delete
1621 :
1622 : Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure
1623 :
1624 : ******************************************************************************/
1625 :
1626 0 : OGRErr OGRLIBKMLDataSource::DeleteLayer (
1627 : int iLayer )
1628 : {
1629 :
1630 0 : if ( !bUpdate )
1631 0 : return OGRERR_UNSUPPORTED_OPERATION;
1632 :
1633 0 : if ( iLayer >= nLayers )
1634 0 : return OGRERR_FAILURE;
1635 :
1636 0 : if ( IsKml ( ) )
1637 0 : DeleteLayerKml ( iLayer );
1638 :
1639 0 : else if ( IsKmz ( ) )
1640 0 : DeleteLayerKmz ( iLayer );
1641 :
1642 0 : else if ( IsDir ( ) ) {
1643 0 : DeleteLayerKmz ( iLayer );
1644 :
1645 : /***** delete the file the layer corisponds to *****/
1646 :
1647 : const char *pszFilePath =
1648 0 : CPLFormFilename ( pszName, papoLayers[iLayer]->GetFileName ( ),
1649 0 : NULL );
1650 0 : VSIStatBufL oStatBufL = { };
1651 0 : if ( !VSIStatL ( pszFilePath, &oStatBufL ) ) {
1652 0 : if ( VSIUnlink ( pszFilePath ) ) {
1653 : CPLError ( CE_Failure, CPLE_AppDefined,
1654 : "ERROR Deleteing Layer %s from filesystem as %s",
1655 0 : papoLayers[iLayer]->GetName ( ), pszFilePath );
1656 : }
1657 : }
1658 : }
1659 :
1660 :
1661 0 : delete papoLayers[iLayer];
1662 : memmove ( papoLayers + iLayer, papoLayers + iLayer + 1,
1663 0 : sizeof ( void * ) * ( nLayers - iLayer - 1 ) );
1664 0 : nLayers--;
1665 0 : bUpdated = TRUE;
1666 :
1667 0 : return OGRERR_NONE;
1668 : }
1669 :
1670 : /******************************************************************************
1671 : method to create a layer in a single file .kml
1672 :
1673 : Args: pszLayerName name of the layer to create
1674 : poOgrSRS the SRS of the layer
1675 : eGType the layers geometry type
1676 : papszOptions layer creation options
1677 :
1678 : Returns: return a pointer to the new layer or NULL on failure
1679 :
1680 : ******************************************************************************/
1681 :
1682 2 : OGRLayer *OGRLIBKMLDataSource::CreateLayerKml (
1683 : const char *pszLayerName,
1684 : OGRSpatialReference * poOgrSRS,
1685 : OGRwkbGeometryType eGType,
1686 : char **papszOptions )
1687 : {
1688 :
1689 2 : OGRLIBKMLLayer *poOgrLayer = NULL;
1690 :
1691 2 : DocumentPtr poKmlDocument = m_poKmlFactory->CreateDocument ( );
1692 :
1693 2 : m_poKmlDSContainer->add_feature ( poKmlDocument );
1694 :
1695 : /***** create the layer *****/
1696 :
1697 : poOgrLayer = AddLayer ( pszLayerName, poOgrSRS, eGType, this,
1698 2 : NULL, poKmlDocument, "", TRUE, bUpdate, 1 );
1699 :
1700 : /***** add the layer name as a <Name> *****/
1701 :
1702 2 : poKmlDocument->set_name ( pszLayerName );
1703 :
1704 2 : return ( OGRLayer * ) poOgrLayer;
1705 : }
1706 :
1707 : /******************************************************************************
1708 : method to create a layer in a .kmz or dir
1709 :
1710 : Args: pszLayerName name of the layer to create
1711 : poOgrSRS the SRS of the layer
1712 : eGType the layers geometry type
1713 : papszOptions layer creation options
1714 :
1715 : Returns: return a pointer to the new layer or NULL on failure
1716 :
1717 : ******************************************************************************/
1718 :
1719 4 : OGRLayer *OGRLIBKMLDataSource::CreateLayerKmz (
1720 : const char *pszLayerName,
1721 : OGRSpatialReference * poOgrSRS,
1722 : OGRwkbGeometryType eGType,
1723 : char **papszOptions )
1724 : {
1725 :
1726 : /***** add a network link to doc.kml *****/
1727 :
1728 : const char *pszUseDocKml =
1729 4 : CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" );
1730 :
1731 4 : if ( EQUAL ( pszUseDocKml, "yes" ) && m_poKmlDocKml ) {
1732 :
1733 4 : DocumentPtr poKmlDocument = AsDocument ( m_poKmlDocKml );
1734 :
1735 4 : NetworkLinkPtr poKmlNetLink = m_poKmlFactory->CreateNetworkLink ( );
1736 4 : LinkPtr poKmlLink = m_poKmlFactory->CreateLink ( );
1737 :
1738 4 : std::string oHref;
1739 4 : oHref.append ( pszLayerName );
1740 4 : oHref.append ( ".kml" );
1741 4 : poKmlLink->set_href ( oHref );
1742 :
1743 4 : poKmlNetLink->set_link ( poKmlLink );
1744 4 : poKmlDocument->add_feature ( poKmlNetLink );
1745 :
1746 : }
1747 :
1748 : /***** create the layer *****/
1749 :
1750 4 : OGRLIBKMLLayer *poOgrLayer = NULL;
1751 :
1752 4 : DocumentPtr poKmlDocument = m_poKmlFactory->CreateDocument ( );
1753 :
1754 : poOgrLayer = AddLayer ( pszLayerName, poOgrSRS, eGType, this,
1755 : NULL, poKmlDocument,
1756 : CPLFormFilename ( NULL, pszLayerName, ".kml" ),
1757 4 : TRUE, bUpdate, 1 );
1758 :
1759 : /***** add the layer name as a <Name> *****/
1760 :
1761 4 : poKmlDocument->set_name ( pszLayerName );
1762 :
1763 4 : return ( OGRLayer * ) poOgrLayer;
1764 : }
1765 :
1766 : /******************************************************************************
1767 : CreateLayer()
1768 :
1769 : Args: pszLayerName name of the layer to create
1770 : poOgrSRS the SRS of the layer
1771 : eGType the layers geometry type
1772 : papszOptions layer creation options
1773 :
1774 : Returns: return a pointer to the new layer or NULL on failure
1775 :
1776 : ******************************************************************************/
1777 :
1778 6 : OGRLayer *OGRLIBKMLDataSource::CreateLayer (
1779 : const char *pszLayerName,
1780 : OGRSpatialReference * poOgrSRS,
1781 : OGRwkbGeometryType eGType,
1782 : char **papszOptions )
1783 : {
1784 :
1785 6 : if ( !bUpdate )
1786 0 : return NULL;
1787 :
1788 6 : OGRLayer *poOgrLayer = NULL;
1789 :
1790 : /***** kml DS *****/
1791 :
1792 6 : if ( IsKml ( ) ) {
1793 : poOgrLayer = CreateLayerKml ( pszLayerName, poOgrSRS,
1794 2 : eGType, papszOptions );
1795 :
1796 : }
1797 :
1798 4 : else if ( IsKmz ( ) || IsDir ( ) ) {
1799 : poOgrLayer = CreateLayerKmz ( pszLayerName, poOgrSRS,
1800 4 : eGType, papszOptions );
1801 :
1802 : }
1803 :
1804 :
1805 :
1806 :
1807 : /***** mark the dataset as updated *****/
1808 :
1809 6 : if ( poOgrLayer )
1810 6 : bUpdated = TRUE;
1811 :
1812 6 : return poOgrLayer;
1813 : }
1814 :
1815 : /******************************************************************************
1816 : method to get a datasources style table
1817 :
1818 : Args: none
1819 :
1820 : Returns: pointer to the datasources style table, or NULL if it does
1821 : not have one
1822 :
1823 : ******************************************************************************/
1824 :
1825 0 : OGRStyleTable *OGRLIBKMLDataSource::GetStyleTable (
1826 : )
1827 : {
1828 :
1829 0 : return m_poStyleTable;
1830 : }
1831 :
1832 : /******************************************************************************
1833 : method to write a style table to a single file .kml ds
1834 :
1835 : Args: poStyleTable pointer to the style table to add
1836 :
1837 : Returns: nothing
1838 :
1839 : ******************************************************************************/
1840 :
1841 0 : void OGRLIBKMLDataSource::SetStyleTable2Kml (
1842 : OGRStyleTable * poStyleTable )
1843 : {
1844 :
1845 : /***** delete all the styles *****/
1846 :
1847 0 : DocumentPtr poKmlDocument = AsDocument ( m_poKmlDSContainer );
1848 0 : size_t nKmlStyles = poKmlDocument->get_styleselector_array_size ( );
1849 : int iKmlStyle;
1850 :
1851 0 : for ( iKmlStyle = nKmlStyles - 1; iKmlStyle >= 0; iKmlStyle-- ) {
1852 0 : poKmlDocument->DeleteStyleSelectorAt ( iKmlStyle );
1853 : }
1854 :
1855 : /***** add the new style table to the document *****/
1856 :
1857 : styletable2kml ( poStyleTable, m_poKmlFactory,
1858 0 : AsContainer ( poKmlDocument ) );
1859 :
1860 0 : return;
1861 : }
1862 :
1863 : /******************************************************************************
1864 : method to write a style table to a kmz ds
1865 :
1866 : Args: poStyleTable pointer to the style table to add
1867 :
1868 : Returns: nothing
1869 :
1870 : ******************************************************************************/
1871 :
1872 0 : void OGRLIBKMLDataSource::SetStyleTable2Kmz (
1873 : OGRStyleTable * poStyleTable )
1874 : {
1875 :
1876 : /***** replace the style document with a new one *****/
1877 :
1878 0 : m_poKmlStyleKml = m_poKmlFactory->CreateDocument ( );
1879 :
1880 0 : styletable2kml ( poStyleTable, m_poKmlFactory, m_poKmlStyleKml );
1881 :
1882 : return;
1883 : }
1884 :
1885 : /******************************************************************************
1886 : method to write a style table to a datasource
1887 :
1888 : Args: poStyleTable pointer to the style table to add
1889 :
1890 : Returns: nothing
1891 :
1892 : note: this method assumes ownership of the style table
1893 :
1894 : ******************************************************************************/
1895 :
1896 0 : void OGRLIBKMLDataSource::SetStyleTableDirectly (
1897 : OGRStyleTable * poStyleTable )
1898 : {
1899 :
1900 0 : if ( !bUpdate )
1901 0 : return;
1902 :
1903 0 : if ( m_poStyleTable )
1904 0 : delete m_poStyleTable;
1905 :
1906 0 : m_poStyleTable = poStyleTable;
1907 :
1908 : /***** a kml datasource? *****/
1909 :
1910 0 : if ( IsKml ( ) )
1911 0 : SetStyleTable2Kml ( m_poStyleTable );
1912 :
1913 0 : else if ( IsKmz ( ) || IsDir ( ) )
1914 0 : SetStyleTable2Kmz ( m_poStyleTable );
1915 :
1916 0 : bUpdated = TRUE;
1917 :
1918 :
1919 :
1920 :
1921 0 : return;
1922 : }
1923 :
1924 : /******************************************************************************
1925 : method to write a style table to a datasource
1926 :
1927 : Args: poStyleTable pointer to the style table to add
1928 :
1929 : Returns: nothing
1930 :
1931 : note: this method copys the style table, and the user will still be
1932 : responsible for its destruction
1933 :
1934 : ******************************************************************************/
1935 :
1936 0 : void OGRLIBKMLDataSource::SetStyleTable (
1937 : OGRStyleTable * poStyleTable )
1938 : {
1939 :
1940 0 : if ( !bUpdate )
1941 0 : return;
1942 :
1943 0 : if ( poStyleTable )
1944 0 : SetStyleTableDirectly ( poStyleTable->Clone ( ) );
1945 : else
1946 0 : SetStyleTableDirectly ( NULL );
1947 0 : return;
1948 : }
1949 :
1950 :
1951 : /******************************************************************************
1952 : Test if capability is available.
1953 :
1954 : Args: pszCap datasource capability name to test
1955 :
1956 : Returns: nothing
1957 :
1958 : ODsCCreateLayer: True if this datasource can create new layers.
1959 : ODsCDeleteLayer: True if this datasource can delete existing layers.
1960 :
1961 : ******************************************************************************/
1962 :
1963 1 : int OGRLIBKMLDataSource::TestCapability (
1964 : const char *pszCap )
1965 : {
1966 :
1967 1 : if ( EQUAL ( pszCap, ODsCCreateLayer ) )
1968 0 : return bUpdate;
1969 1 : else if ( EQUAL ( pszCap, ODsCDeleteLayer ) )
1970 0 : return bUpdate;
1971 : else
1972 1 : return FALSE;
1973 :
1974 2139 : }
|