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 "ogr_libkml.h"
31 //#include "cpl_conv.h"
32 //#include "cpl_string.h"
33 #include "cpl_error.h"
34 
35 #include <kml/dom.h>
36 
37 using kmldom::KmlFactory;
38 using kmldom::PlacemarkPtr;
39 using kmldom::Placemark;
40 using kmldom::DocumentPtr;
41 using kmldom::ContainerPtr;
42 using kmldom::FeaturePtr;
43 using kmldom::GroundOverlayPtr;
44 using kmldom::KmlPtr;
45 using kmldom::Kml;
46 using kmlengine::KmzFile;
47 using kmlengine::KmlFile;
48 using kmlengine::Bbox;
49 using kmldom::ExtendedDataPtr;
50 using kmldom::SchemaDataPtr;
51 using kmldom::DataPtr;
52 using kmldom::CameraPtr;
53 using kmldom::LookAtPtr;
54 using kmldom::RegionPtr;
55 using kmldom::LatLonAltBoxPtr;
56 using kmldom::LodPtr;
57 using kmldom::ScreenOverlayPtr;
58 using kmldom::IconPtr;
59 using kmldom::CreatePtr;
60 using kmldom::ChangePtr;
61 using kmldom::DeletePtr;
62 
63 #include "ogrlibkmlfeature.h"
64 #include "ogrlibkmlfield.h"
65 #include "ogrlibkmlstyle.h"
66 
67 /************************************************************************/
68 /*                    OGRLIBKMLGetSanitizedNCName()                     */
69 /************************************************************************/
70 
OGRLIBKMLGetSanitizedNCName(const char * pszName)71 CPLString OGRLIBKMLGetSanitizedNCName(const char* pszName)
72 {
73     CPLString osName(pszName);
74     /* (Approximate) validation rules for a valic NCName */
75     for(size_t i = 0; i < osName.size(); i++)
76     {
77         char ch = osName[i];
78         if( (ch >= 'A' && ch <= 'Z') || ch == '_' || (ch >= 'a' && ch <= 'z') )
79         {
80             /* ok */
81         }
82         else if ( i > 0 && (ch == '-' || ch == '.' || (ch >= '0' && ch <= '9')) )
83         {
84             /* ok */
85         }
86 #if 0
87         /* Always false. */
88         else if ( ch > 127 )
89         {
90             /* ok : this is an approximation */
91         }
92 #endif
93         else
94             osName[i] = '_';
95     }
96     return osName;
97 }
98 
99 /******************************************************************************
100  OGRLIBKMLLayer constructor
101 
102  Args:          pszLayerName    the name of the layer
103                 poSpatialRef    the spacial Refrance for the layer
104                 eGType          the layers geometry type
105                 poOgrDS         pointer to the datasource the layer is in
106                 poKmlRoot       pointer to the root kml element of the layer
107                 poKmlContainer  pointer to the kml container of the layer
108                 pszFileName     the filename of the layer
109                 bNew            true if its a new layer
110                 bUpdate         true if the layer is writeable
111 
112  Returns:       nothing
113 
114 ******************************************************************************/
115 
OGRLIBKMLLayer(const char * pszLayerName,OGRSpatialReference * poSpatialRef,OGRwkbGeometryType eGType,OGRLIBKMLDataSource * poOgrDS,ElementPtr poKmlRoot,ContainerPtr poKmlContainer,UpdatePtr poKmlUpdate,const char * pszFileName,int bNew,int bUpdate)116 OGRLIBKMLLayer::OGRLIBKMLLayer ( const char *pszLayerName,
117                                  OGRSpatialReference * poSpatialRef,
118                                  OGRwkbGeometryType eGType,
119                                  OGRLIBKMLDataSource * poOgrDS,
120                                  ElementPtr poKmlRoot,
121                                  ContainerPtr poKmlContainer,
122                                  UpdatePtr poKmlUpdate,
123                                  const char *pszFileName,
124                                  int bNew,
125                                  int bUpdate )
126 {
127 
128     m_poStyleTable = NULL;
129     iFeature = 0;
130     nFeatures = 0;
131     nFID = 1;
132 
133     this->bUpdate = bUpdate;
134     bUpdated = FALSE;
135     m_pszName = CPLStrdup ( pszLayerName );
136     m_pszFileName = CPLStrdup ( pszFileName );
137     m_poOgrDS = poOgrDS;
138 
139     m_poOgrSRS = new OGRSpatialReference ( NULL );
140     m_poOgrSRS->SetWellKnownGeogCS ( "WGS84" );
141 
142     m_poOgrFeatureDefn = new OGRFeatureDefn ( pszLayerName );
143     SetDescription( m_poOgrFeatureDefn->GetName() );
144     m_poOgrFeatureDefn->Reference (  );
145     m_poOgrFeatureDefn->SetGeomType ( eGType );
146     if( m_poOgrFeatureDefn->GetGeomFieldCount() != 0 )
147         m_poOgrFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poOgrSRS);
148 
149     /***** store the root element pointer *****/
150 
151     m_poKmlLayerRoot = poKmlRoot;
152 
153     /***** store the layers container *****/
154 
155     m_poKmlLayer = poKmlContainer;
156 
157     /* update container */
158     m_poKmlUpdate = poKmlUpdate;
159 
160     m_poKmlSchema = NULL;
161 
162     /***** related to Region *****/
163 
164     m_bWriteRegion = FALSE;
165     m_bRegionBoundsAuto = FALSE;
166     m_dfRegionMinLodPixels = 0;
167     m_dfRegionMaxLodPixels = -1;
168     m_dfRegionMinFadeExtent = 0;
169     m_dfRegionMaxFadeExtent = 0;
170     m_dfRegionMinX = 200;
171     m_dfRegionMinY = 200;
172     m_dfRegionMaxX = -200;
173     m_dfRegionMaxY = -200;
174 
175 
176     m_bReadGroundOverlay = CSLTestBoolean(CPLGetConfigOption("LIBKML_READ_GROUND_OVERLAY", "YES"));
177     m_bUseSimpleField = CSLTestBoolean(CPLGetConfigOption("LIBKML_USE_SIMPLEFIELD", "YES"));
178 
179     m_bUpdateIsFolder = FALSE;
180 
181     /***** was the layer created from a DS::Open *****/
182 
183     if ( !bNew ) {
184 
185         /***** get the number of features on the layer *****/
186 
187         nFeatures = m_poKmlLayer->get_feature_array_size (  );
188 
189         /***** get the field config *****/
190 
191         struct fieldconfig oFC;
192         get_fieldconfig( &oFC );
193 
194         /***** name field *****/
195 
196         OGRFieldDefn oOgrFieldName ( oFC.namefield,OFTString );
197         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldName );
198 
199         /***** descripton field *****/
200 
201         OGRFieldDefn oOgrFieldDesc ( oFC.descfield, OFTString );
202         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldDesc );
203 
204         /***** timestamp field *****/
205 
206         OGRFieldDefn oOgrFieldTs ( oFC.tsfield, OFTDateTime );
207         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldTs );
208 
209         /*****  timespan begin field *****/
210 
211         OGRFieldDefn oOgrFieldBegin ( oFC.beginfield, OFTDateTime );
212         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldBegin );
213 
214         /*****  timespan end field *****/
215 
216         OGRFieldDefn oOgrFieldEnd ( oFC.endfield, OFTDateTime );
217         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldEnd );
218 
219         /*****  altitudeMode field *****/
220 
221         OGRFieldDefn oOgrFieldAltitudeMode ( oFC.altitudeModefield, OFTString );
222         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldAltitudeMode );
223 
224         /***** tessellate field *****/
225 
226         OGRFieldDefn oOgrFieldTessellate ( oFC.tessellatefield, OFTInteger );
227         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldTessellate );
228 
229         /***** extrude field *****/
230 
231         OGRFieldDefn oOgrFieldExtrude ( oFC.extrudefield, OFTInteger );
232         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldExtrude );
233 
234         /***** visibility field *****/
235 
236         OGRFieldDefn oOgrFieldVisibility ( oFC.visibilityfield, OFTInteger );
237         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldVisibility );
238 
239         /***** draw order field *****/
240 
241         OGRFieldDefn oOgrFieldDrawOrder ( oFC.drawOrderfield, OFTInteger );
242         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldDrawOrder );
243 
244         /***** icon field *****/
245 
246         OGRFieldDefn oOgrFieldIcon ( oFC.iconfield, OFTString );
247         m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldIcon );
248 
249         /***** get the styles *****/
250 
251         if ( m_poKmlLayer->IsA ( kmldom::Type_Document ) )
252             ParseStyles ( AsDocument ( m_poKmlLayer ), &m_poStyleTable );
253 
254         /***** get the schema if the layer is a Document *****/
255 
256         if ( m_poKmlLayer->IsA ( kmldom::Type_Document ) ) {
257             DocumentPtr poKmlDocument = AsDocument ( m_poKmlLayer );
258 
259             if ( poKmlDocument->get_schema_array_size (  ) ) {
260                 m_poKmlSchema = poKmlDocument->get_schema_array_at ( 0 );
261                 kml2FeatureDef ( m_poKmlSchema, m_poOgrFeatureDefn );
262             }
263         }
264 
265         /***** the schema is somewhere else *****/
266 
267         if (m_poKmlSchema == NULL) {
268 
269             /***** try to find the correct schema *****/
270 
271             int bHasHeading = FALSE, bHasTilt = FALSE, bHasRoll = FALSE;
272             int bHasSnippet = FALSE;
273             FeaturePtr poKmlFeature;
274 
275             /***** find the first placemark *****/
276 
277             do {
278                 if ( iFeature >= nFeatures )
279                     break;
280 
281                 poKmlFeature =
282                     m_poKmlLayer->get_feature_array_at ( iFeature++ );
283 
284                 if( poKmlFeature->Type() == kmldom::Type_Placemark )
285                 {
286                     PlacemarkPtr poKmlPlacemark = AsPlacemark ( poKmlFeature );
287                     if( !poKmlPlacemark->has_geometry (  ) &&
288                         poKmlPlacemark->has_abstractview (  ) &&
289                         poKmlPlacemark->get_abstractview()->IsA( kmldom::Type_Camera) )
290                     {
291                         const CameraPtr& camera = AsCamera(poKmlPlacemark->get_abstractview());
292                         if( camera->has_heading() && !bHasHeading )
293                         {
294                             bHasHeading = TRUE;
295                             OGRFieldDefn oOgrField ( oFC.headingfield, OFTReal );
296                             m_poOgrFeatureDefn->AddFieldDefn ( &oOgrField );
297                         }
298                         if( camera->has_tilt() && !bHasTilt )
299                         {
300                             bHasTilt = TRUE;
301                             OGRFieldDefn oOgrField ( oFC.tiltfield, OFTReal );
302                             m_poOgrFeatureDefn->AddFieldDefn ( &oOgrField );
303                         }
304                         if( camera->has_roll() && !bHasRoll )
305                         {
306                             bHasRoll = TRUE;
307                             OGRFieldDefn oOgrField ( oFC.rollfield, OFTReal );
308                             m_poOgrFeatureDefn->AddFieldDefn ( &oOgrField );
309                         }
310                     }
311                 }
312                 if( !bHasSnippet && poKmlFeature->has_snippet() )
313                 {
314                     bHasSnippet = TRUE;
315                     OGRFieldDefn oOgrField ( oFC.snippetfield, OFTString );
316                     m_poOgrFeatureDefn->AddFieldDefn ( &oOgrField );
317                 }
318             } while ( poKmlFeature->Type (  ) != kmldom::Type_Placemark );
319 
320             if ( iFeature <= nFeatures && poKmlFeature &&
321                  poKmlFeature->Type (  ) == kmldom::Type_Placemark &&
322                  poKmlFeature->has_extendeddata (  ) ) {
323 
324                 ExtendedDataPtr poKmlExtendedData = poKmlFeature->
325                     get_extendeddata (  );
326 
327                 if ( poKmlExtendedData->get_schemadata_array_size (  ) > 0 ) {
328                     SchemaDataPtr poKmlSchemaData = poKmlExtendedData->
329                         get_schemadata_array_at ( 0 );
330 
331                     if ( poKmlSchemaData->has_schemaurl (  ) ) {
332 
333                         std::string oKmlSchemaUrl = poKmlSchemaData->
334                             get_schemaurl (  );
335                         if ( ( m_poKmlSchema =
336                                m_poOgrDS->FindSchema ( oKmlSchemaUrl.
337                                                        c_str (  ) ) ) ) {
338                             kml2FeatureDef ( m_poKmlSchema,
339                                              m_poOgrFeatureDefn );
340                         }
341                     }
342                 }
343                 else if ( poKmlExtendedData->get_data_array_size() > 0 )
344                 {
345                     /* Use the <Data> of the first placemark to build the feature definition */
346                     /* If others have different fields, too bad... */
347                     int bLaunderFieldNames =
348                         CSLTestBoolean(CPLGetConfigOption("LIBKML_LAUNDER_FIELD_NAMES", "YES"));
349                     size_t nDataArraySize = poKmlExtendedData->get_data_array_size();
350                     for(size_t i=0; i < nDataArraySize; i++)
351                     {
352                         const DataPtr& data = poKmlExtendedData->get_data_array_at(i);
353                         if (data->has_name())
354                         {
355                             CPLString osName = data->get_name();
356                             if (bLaunderFieldNames)
357                                 osName = LaunderFieldNames(osName);
358                             OGRFieldDefn oOgrField ( osName,
359                                                     OFTString );
360                             m_poOgrFeatureDefn->AddFieldDefn ( &oOgrField );
361                         }
362                     }
363                 }
364             }
365 
366             iFeature = 0;
367 
368         }
369 
370 
371 
372         /***** check if any features are another layer *****/
373 
374         m_poOgrDS->ParseLayers ( m_poKmlLayer, poSpatialRef );
375 
376     }
377 
378     /***** it was from a DS::CreateLayer *****/
379 
380     else {
381 
382         /***** mark the layer as updated *****/
383 
384         bUpdated = TRUE;
385     }
386 
387 }
388 
389 /******************************************************************************
390  OGRLIBKMLLayer Destructor
391 
392  Args:          none
393 
394  Returns:       nothing
395 
396 ******************************************************************************/
397 
~OGRLIBKMLLayer()398 OGRLIBKMLLayer::~OGRLIBKMLLayer (  )
399 {
400 
401     CPLFree ( ( void * )m_pszName );
402     CPLFree ( ( void * )m_pszFileName );
403     m_poOgrSRS->Release();
404 
405     m_poOgrFeatureDefn->Release (  );
406 
407 
408 }
409 
410 
411 /******************************************************************************
412  Method to get the next feature on the layer
413 
414  Args:          none
415 
416  Returns:       The next feature, or NULL if there is no more
417 
418  this function copyed from the sqlite driver
419 ******************************************************************************/
420 
GetNextFeature()421 OGRFeature *OGRLIBKMLLayer::GetNextFeature()
422 
423 {
424     for( ; TRUE; )
425     {
426         OGRFeature      *poFeature;
427 
428         poFeature = GetNextRawFeature();
429         if( poFeature == NULL )
430             return NULL;
431 
432         if( (m_poFilterGeom == NULL
433             || FilterGeometry( poFeature->GetGeometryRef() ) )
434             && (m_poAttrQuery == NULL
435                 || m_poAttrQuery->Evaluate( poFeature )) )
436             return poFeature;
437 
438         delete poFeature;
439     }
440 }
441 
442 /******************************************************************************
443  Method to get the next feature on the layer
444 
445  Args:          none
446 
447  Returns:       The next feature, or NULL if there is no more
448 
449 ******************************************************************************/
450 
GetNextRawFeature()451 OGRFeature *OGRLIBKMLLayer::GetNextRawFeature (
452      )
453 {
454     FeaturePtr poKmlFeature;
455     OGRFeature *poOgrFeature = NULL;
456 
457     if( m_poKmlLayer == NULL )
458         return NULL;
459 
460     /***** loop over the kml features to find the next placemark *****/
461 
462     do {
463         if ( iFeature >= nFeatures )
464             break;
465 
466         /***** get the next kml feature in the container *****/
467 
468         poKmlFeature = m_poKmlLayer->get_feature_array_at ( iFeature++ );
469 
470         /***** what type of kml feature in the container? *****/
471 
472         switch (poKmlFeature->Type (  )) {
473 
474             case kmldom::Type_Placemark:
475                 poOgrFeature = kml2feat ( AsPlacemark ( poKmlFeature ),
476                                           m_poOgrDS, this,
477                                           m_poOgrFeatureDefn, m_poOgrSRS );
478                 break;
479 
480             case kmldom::Type_GroundOverlay:
481                 if (m_bReadGroundOverlay) {
482                     poOgrFeature =
483                         kmlgroundoverlay2feat ( AsGroundOverlay ( poKmlFeature ),
484                                                 m_poOgrDS, this,
485                                                 m_poOgrFeatureDefn,
486                                                 m_poOgrSRS );
487                 }
488                 break;
489 
490             default:
491                 break;
492 
493         }
494 
495     } while ( !poOgrFeature );
496 
497     /***** set the FID on the ogr feature *****/
498 
499     if (poOgrFeature)
500         poOgrFeature->SetFID(nFID ++);
501 
502     return poOgrFeature;
503 }
504 
505 /******************************************************************************
506  method to add a feature to a layer
507 
508  Args:          poOgrFeat   pointer to the feature to add
509 
510  Returns:       OGRERR_NONE, or OGRERR_UNSUPPORTED_OPERATION of the layer is
511                 not writeable
512 
513 ******************************************************************************/
514 
ICreateFeature(OGRFeature * poOgrFeat)515 OGRErr OGRLIBKMLLayer::ICreateFeature (
516     OGRFeature * poOgrFeat )
517 {
518 
519     if ( !bUpdate )
520         return OGRERR_UNSUPPORTED_OPERATION;
521 
522     if( m_bRegionBoundsAuto && poOgrFeat->GetGeometryRef() != NULL &&
523         !(poOgrFeat->GetGeometryRef()->IsEmpty()) )
524     {
525         OGREnvelope sEnvelope;
526         poOgrFeat->GetGeometryRef()->getEnvelope(&sEnvelope);
527         m_dfRegionMinX = MIN(m_dfRegionMinX, sEnvelope.MinX);
528         m_dfRegionMinY = MIN(m_dfRegionMinY, sEnvelope.MinY);
529         m_dfRegionMaxX = MAX(m_dfRegionMaxX, sEnvelope.MaxX);
530         m_dfRegionMaxY = MAX(m_dfRegionMaxY, sEnvelope.MaxY);
531     }
532 
533     FeaturePtr poKmlFeature =
534         feat2kml ( m_poOgrDS, this, poOgrFeat, m_poOgrDS->GetKmlFactory (  ),
535                    m_bUseSimpleField );
536 
537     if( m_poKmlLayer != NULL )
538         m_poKmlLayer->add_feature ( poKmlFeature );
539     else
540     {
541         CPLAssert( m_poKmlUpdate != NULL );
542         KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory (  );
543         CreatePtr poCreate = poKmlFactory->CreateCreate();
544         ContainerPtr poContainer;
545         if( m_bUpdateIsFolder )
546             poContainer = poKmlFactory->CreateFolder();
547         else
548             poContainer = poKmlFactory->CreateDocument();
549         poContainer->set_targetid(OGRLIBKMLGetSanitizedNCName(GetName()));
550         poContainer->add_feature ( poKmlFeature );
551         poCreate->add_container(poContainer);
552         m_poKmlUpdate->add_updateoperation(poCreate);
553     }
554 
555     /***** update the layer class count of features  *****/
556 
557     if( m_poKmlLayer != NULL )
558     {
559         nFeatures++;
560 
561         const char* pszId = CPLSPrintf("%s.%d",
562                         OGRLIBKMLGetSanitizedNCName(GetName()).c_str(), nFeatures);
563         poOgrFeat->SetFID(nFeatures);
564         poKmlFeature->set_id(pszId);
565     }
566     else
567     {
568         if( poOgrFeat->GetFID() < 0 )
569         {
570             static int bAlreadyWarned = FALSE;
571             if( !bAlreadyWarned )
572             {
573                 bAlreadyWarned = TRUE;
574                 CPLError(CE_Warning, CPLE_AppDefined,
575                          "It is recommended to define a FID when calling CreateFeature() in a update document");
576             }
577         }
578         else
579         {
580             const char* pszId = CPLSPrintf("%s." CPL_FRMT_GIB,
581                     OGRLIBKMLGetSanitizedNCName(GetName()).c_str(), poOgrFeat->GetFID());
582             poOgrFeat->SetFID(nFeatures);
583             poKmlFeature->set_id(pszId);
584         }
585     }
586 
587     /***** mark the layer as updated *****/
588 
589     bUpdated = TRUE;
590     m_poOgrDS->Updated (  );
591 
592     return OGRERR_NONE;
593 }
594 
595 
596 /******************************************************************************
597  method to update a feature to a layer. Only work on a NetworkLinkControl/Update
598 
599  Args:          poOgrFeat   pointer to the feature to update
600 
601  Returns:       OGRERR_NONE, or OGRERR_UNSUPPORTED_OPERATION of the layer is
602                 not writeable
603 
604 ******************************************************************************/
605 
ISetFeature(OGRFeature * poOgrFeat)606 OGRErr OGRLIBKMLLayer::ISetFeature ( OGRFeature * poOgrFeat )
607 {
608     if( !bUpdate || m_poKmlUpdate == NULL )
609         return OGRERR_UNSUPPORTED_OPERATION;
610     if( poOgrFeat->GetFID() == OGRNullFID )
611         return OGRERR_FAILURE;
612 
613     FeaturePtr poKmlFeature =
614         feat2kml ( m_poOgrDS, this, poOgrFeat, m_poOgrDS->GetKmlFactory (  ),
615                    m_bUseSimpleField );
616 
617     KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory (  );
618     ChangePtr poChange = poKmlFactory->CreateChange();
619     poChange->add_object(poKmlFeature);
620     m_poKmlUpdate->add_updateoperation(poChange);
621 
622     const char* pszId = CPLSPrintf("%s." CPL_FRMT_GIB,
623                     OGRLIBKMLGetSanitizedNCName(GetName()).c_str(), poOgrFeat->GetFID());
624     poKmlFeature->set_targetid(pszId);
625 
626     /***** mark the layer as updated *****/
627 
628     bUpdated = TRUE;
629     m_poOgrDS->Updated (  );
630 
631     return OGRERR_NONE;
632 }
633 
634 /******************************************************************************
635  method to delete a feature to a layer. Only work on a NetworkLinkControl/Update
636 
637  Args:          nFID   id of the feature to delete
638 
639  Returns:       OGRERR_NONE, or OGRERR_UNSUPPORTED_OPERATION of the layer is
640                 not writeable
641 
642 ******************************************************************************/
643 
DeleteFeature(GIntBig nFID)644 OGRErr OGRLIBKMLLayer::DeleteFeature( GIntBig nFID )
645 {
646     if( !bUpdate || m_poKmlUpdate == NULL )
647         return OGRERR_UNSUPPORTED_OPERATION;
648 
649     KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory (  );
650     DeletePtr poDelete = poKmlFactory->CreateDelete();
651     m_poKmlUpdate->add_updateoperation(poDelete);
652     PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
653     poDelete->add_feature(poKmlPlacemark);
654 
655     const char* pszId = CPLSPrintf("%s." CPL_FRMT_GIB,
656                     OGRLIBKMLGetSanitizedNCName(GetName()).c_str(), nFID);
657     poKmlPlacemark->set_targetid(pszId);
658 
659     /***** mark the layer as updated *****/
660 
661     bUpdated = TRUE;
662     m_poOgrDS->Updated (  );
663 
664     return OGRERR_NONE;
665 }
666 
667 /******************************************************************************
668  method to get the number of features on the layer
669 
670  Args:          bForce      no effect as of now
671 
672  Returns:       the number of features on the layer
673 
674  Note:          the result can include links, folders and other items that are
675                 not supported by OGR
676 
677 ******************************************************************************/
678 
GetFeatureCount(int bForce)679 GIntBig OGRLIBKMLLayer::GetFeatureCount (
680                                      int bForce )
681 {
682 
683 
684     int i = 0;
685     if (m_poFilterGeom != NULL || m_poAttrQuery != NULL ) {
686         i = OGRLayer::GetFeatureCount( bForce );
687     }
688 
689     else if( m_poKmlLayer != NULL ) {
690         size_t iKmlFeature;
691         size_t nKmlFeatures = m_poKmlLayer->get_feature_array_size (  );
692         FeaturePtr poKmlFeature;
693 
694         /***** loop over the kml features in the container *****/
695 
696         for ( iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++ ) {
697             poKmlFeature = m_poKmlLayer->get_feature_array_at ( iKmlFeature );
698 
699             /***** what type of kml feature? *****/
700 
701             switch (poKmlFeature->Type (  )) {
702 
703                 case kmldom::Type_Placemark:
704                     i++;
705                     break;
706 
707                 case kmldom::Type_GroundOverlay:
708                     if (m_bReadGroundOverlay)
709                         i++;
710                     break;
711 
712                 default:
713                     break;
714 
715             }
716         }
717     }
718 
719     return i;
720 }
721 
722 /******************************************************************************
723  GetExtent()
724 
725  Args:          psExtent    pointer to the Envelope to store the result in
726                 bForce      no effect as of now
727 
728  Returns:       nothing
729 
730 ******************************************************************************/
731 
GetExtent(OGREnvelope * psExtent,int bForce)732 OGRErr OGRLIBKMLLayer::GetExtent (
733     OGREnvelope * psExtent,
734     int bForce )
735 {
736     Bbox oKmlBbox;
737 
738     if ( m_poKmlLayer != NULL &&
739         kmlengine::
740          GetFeatureBounds ( AsFeature ( m_poKmlLayer ), &oKmlBbox ) ) {
741         psExtent->MinX = oKmlBbox.get_west (  );
742         psExtent->MinY = oKmlBbox.get_south (  );
743         psExtent->MaxX = oKmlBbox.get_east (  );
744         psExtent->MaxY = oKmlBbox.get_north (  );
745 
746         return OGRERR_NONE;
747     }
748     else
749         return OGRLayer::GetExtent(psExtent, bForce);
750 }
751 
752 
753 
754 
755 /******************************************************************************
756  Method to create a field on a layer
757 
758  Args:          poField     pointer to the Field Definition to add
759                 bApproxOK   no effect as of now
760 
761  Returns:       OGRERR_NONE on success or OGRERR_UNSUPPORTED_OPERATION if the
762                 layer is not writeable
763 
764 ******************************************************************************/
765 
CreateField(OGRFieldDefn * poField,CPL_UNUSED int bApproxOK)766 OGRErr OGRLIBKMLLayer::CreateField (
767     OGRFieldDefn * poField,
768     CPL_UNUSED int bApproxOK )
769 {
770     if ( !bUpdate )
771         return OGRERR_UNSUPPORTED_OPERATION;
772 
773     if( m_bUseSimpleField )
774     {
775         SimpleFieldPtr poKmlSimpleField = NULL;
776 
777         if ( (poKmlSimpleField =
778             FieldDef2kml ( poField, m_poOgrDS->GetKmlFactory (  ) )) != NULL )
779         {
780             if( m_poKmlSchema == NULL )
781             {
782                 /***** create a new schema *****/
783 
784                 KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory (  );
785 
786                 m_poKmlSchema = poKmlFactory->CreateSchema (  );
787 
788                 /***** set the id on the new schema *****/
789 
790                 std::string oKmlSchemaID = OGRLIBKMLGetSanitizedNCName(m_pszName);
791                 oKmlSchemaID.append ( ".schema" );
792                 m_poKmlSchema->set_id ( oKmlSchemaID );
793             }
794 
795             m_poKmlSchema->add_simplefield ( poKmlSimpleField );
796         }
797     }
798 
799     m_poOgrFeatureDefn->AddFieldDefn ( poField );
800 
801     /***** mark the layer as updated *****/
802 
803     bUpdated = TRUE;
804     m_poOgrDS->Updated (  );
805 
806     return OGRERR_NONE;
807 }
808 
809 
810 /******************************************************************************
811  method to write the datasource to disk
812 
813  Args:      none
814 
815  Returns    nothing
816 
817 ******************************************************************************/
818 
SyncToDisk()819 OGRErr OGRLIBKMLLayer::SyncToDisk (
820      )
821 {
822 
823     return OGRERR_NONE;
824 }
825 
826 /******************************************************************************
827  method to get a layers style table
828 
829  Args:          none
830 
831  Returns:       pointer to the layers style table, or NULL if it does
832                 not have one
833 
834 ******************************************************************************/
835 
GetStyleTable()836 OGRStyleTable *OGRLIBKMLLayer::GetStyleTable (
837      )
838 {
839 
840     return m_poStyleTable;
841 }
842 
843 /******************************************************************************
844  method to write a style table to a layer
845 
846  Args:          poStyleTable    pointer to the style table to add
847 
848  Returns:       nothing
849 
850  note: this method assumes ownership of the style table
851 ******************************************************************************/
852 
SetStyleTableDirectly(OGRStyleTable * poStyleTable)853 void OGRLIBKMLLayer::SetStyleTableDirectly (
854     OGRStyleTable * poStyleTable )
855 {
856 
857     if ( !bUpdate || m_poKmlLayer == NULL )
858         return;
859 
860     KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory (  );
861 
862     if ( m_poStyleTable )
863         delete m_poStyleTable;
864 
865     m_poStyleTable = poStyleTable;
866 
867     if ( m_poKmlLayer->IsA ( kmldom::Type_Document ) ) {
868 
869         /***** delete all the styles *****/
870 
871         DocumentPtr poKmlDocument = AsDocument ( m_poKmlLayer );
872         size_t nKmlStyles = poKmlDocument->get_schema_array_size (  );
873         int iKmlStyle;
874 
875         for ( iKmlStyle = nKmlStyles - 1; iKmlStyle >= 0; iKmlStyle-- ) {
876             poKmlDocument->DeleteStyleSelectorAt ( iKmlStyle );
877         }
878 
879         /***** add the new style table to the document *****/
880 
881         styletable2kml ( poStyleTable, poKmlFactory,
882                          AsContainer ( poKmlDocument ) );
883 
884     }
885 
886     /***** mark the layer as updated *****/
887 
888     bUpdated = TRUE;
889     m_poOgrDS->Updated (  );
890 
891     return;
892 }
893 
894 /******************************************************************************
895  method to write a style table to a layer
896 
897  Args:          poStyleTable    pointer to the style table to add
898 
899  Returns:       nothing
900 
901  note:  this method copys the style table, and the user will still be
902         responsible for its destruction
903 ******************************************************************************/
904 
SetStyleTable(OGRStyleTable * poStyleTable)905 void OGRLIBKMLLayer::SetStyleTable (
906     OGRStyleTable * poStyleTable )
907 {
908 
909     if ( !bUpdate || m_poKmlLayer == NULL )
910         return;
911 
912     if ( poStyleTable )
913         SetStyleTableDirectly ( poStyleTable->Clone (  ) );
914     else
915         SetStyleTableDirectly ( NULL );
916     return;
917 }
918 
919 /******************************************************************************
920  Test if capability is available.
921 
922  Args:          pszCap  layer capability name to test
923 
924  Returns:       True if the layer supports the capability, otherwise false
925 
926 ******************************************************************************/
927 
TestCapability(const char * pszCap)928 int OGRLIBKMLLayer::TestCapability (
929     const char *pszCap )
930 {
931     int result = FALSE;
932 
933     if ( EQUAL ( pszCap, OLCRandomRead ) )
934         result = FALSE;
935     else if ( EQUAL ( pszCap, OLCSequentialWrite ) )
936         result = bUpdate;
937     else if ( EQUAL ( pszCap, OLCRandomWrite ) )
938         result = FALSE;
939     else if ( EQUAL ( pszCap, OLCFastFeatureCount ) )
940         result = FALSE;
941     else if ( EQUAL ( pszCap, OLCFastSetNextByIndex ) )
942         result = FALSE;
943     else if ( EQUAL ( pszCap, OLCCreateField ) )
944         result = bUpdate;
945     else if ( EQUAL ( pszCap, OLCDeleteFeature ) )
946         result = FALSE;
947     else if ( EQUAL(pszCap, OLCStringsAsUTF8) )
948         result = TRUE;
949 
950     return result;
951 }
952 
953 /************************************************************************/
954 /*                        LaunderFieldNames()                           */
955 /************************************************************************/
956 
LaunderFieldNames(CPLString osName)957 CPLString OGRLIBKMLLayer::LaunderFieldNames(CPLString osName)
958 {
959     CPLString osLaunderedName;
960     for(int i=0;i<(int)osName.size();i++)
961     {
962         char ch = osName[i];
963         if ((ch >= '0' && ch <= '9') ||
964             (ch >= 'a' && ch <= 'z') ||
965             (ch >= 'A' && ch <= 'Z') ||
966             (ch == '_'))
967             osLaunderedName += ch;
968         else
969             osLaunderedName += "_";
970     }
971     return osLaunderedName;
972 }
973 
974 /************************************************************************/
975 /*                            SetLookAt()                               */
976 /************************************************************************/
977 
SetLookAt(const char * pszLookatLongitude,const char * pszLookatLatitude,const char * pszLookatAltitude,const char * pszLookatHeading,const char * pszLookatTilt,const char * pszLookatRange,const char * pszLookatAltitudeMode)978 void OGRLIBKMLLayer::SetLookAt( const char* pszLookatLongitude,
979                                 const char* pszLookatLatitude,
980                                 const char* pszLookatAltitude,
981                                 const char* pszLookatHeading,
982                                 const char* pszLookatTilt,
983                                 const char* pszLookatRange,
984                                 const char* pszLookatAltitudeMode )
985 {
986     KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory (  );
987     LookAtPtr lookAt = poKmlFactory->CreateLookAt();
988     lookAt->set_latitude(CPLAtof(pszLookatLatitude));
989     lookAt->set_longitude(CPLAtof(pszLookatLongitude));
990     if( pszLookatAltitude != NULL )
991         lookAt->set_altitude(CPLAtof(pszLookatAltitude));
992     if( pszLookatHeading != NULL )
993         lookAt->set_heading(CPLAtof(pszLookatHeading));
994     if( pszLookatTilt != NULL )
995     {
996         double dfTilt = CPLAtof(pszLookatTilt);
997         if( dfTilt >= 0 && dfTilt <= 90 )
998             lookAt->set_tilt(dfTilt);
999         else
1000             CPLError(CE_Warning, CPLE_AppDefined, "Invalid value for tilt: %s",
1001                      pszLookatTilt);
1002     }
1003     lookAt->set_range(CPLAtof(pszLookatRange));
1004     if( pszLookatAltitudeMode != NULL )
1005     {
1006         int isGX = FALSE;
1007         int iAltitudeMode = kmlAltitudeModeFromString(pszLookatAltitudeMode, isGX);
1008         if( iAltitudeMode != kmldom::ALTITUDEMODE_CLAMPTOGROUND &&
1009             pszLookatAltitude == NULL )
1010         {
1011             CPLError(CE_Warning, CPLE_AppDefined, "Lookat altitude should be present for altitudeMode = %s",
1012                      pszLookatAltitudeMode);
1013         }
1014         else if( isGX )
1015             lookAt->set_gx_altitudemode(iAltitudeMode);
1016         else
1017             lookAt->set_altitudemode(iAltitudeMode);
1018     }
1019 
1020     m_poKmlLayer->set_abstractview(lookAt);
1021 }
1022 
1023 /************************************************************************/
1024 /*                            SetCamera()                               */
1025 /************************************************************************/
1026 
SetCamera(const char * pszCameraLongitude,const char * pszCameraLatitude,const char * pszCameraAltitude,const char * pszCameraHeading,const char * pszCameraTilt,const char * pszCameraRoll,const char * pszCameraAltitudeMode)1027 void OGRLIBKMLLayer::SetCamera( const char* pszCameraLongitude,
1028                                 const char* pszCameraLatitude,
1029                                 const char* pszCameraAltitude,
1030                                 const char* pszCameraHeading,
1031                                 const char* pszCameraTilt,
1032                                 const char* pszCameraRoll,
1033                                 const char* pszCameraAltitudeMode )
1034 {
1035     int isGX = FALSE;
1036     int iAltitudeMode = kmlAltitudeModeFromString(pszCameraAltitudeMode, isGX);
1037     if( isGX == FALSE && iAltitudeMode == kmldom::ALTITUDEMODE_CLAMPTOGROUND )
1038     {
1039         CPLError(CE_Warning, CPLE_AppDefined, "Camera altitudeMode should be different from %s",
1040                     pszCameraAltitudeMode);
1041         return;
1042     }
1043     KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory (  );
1044     CameraPtr camera = poKmlFactory->CreateCamera();
1045     camera->set_latitude(CPLAtof(pszCameraLatitude));
1046     camera->set_longitude(CPLAtof(pszCameraLongitude));
1047     camera->set_altitude(CPLAtof(pszCameraAltitude));
1048     if( pszCameraHeading != NULL )
1049         camera->set_heading(CPLAtof(pszCameraHeading));
1050     if( pszCameraTilt != NULL )
1051     {
1052         double dfTilt = CPLAtof(pszCameraTilt);
1053         if( dfTilt >= 0 && dfTilt <= 90 )
1054             camera->set_tilt(dfTilt);
1055         else
1056             CPLError(CE_Warning, CPLE_AppDefined, "Invalid value for tilt: %s",
1057                      pszCameraTilt);
1058     }
1059     if( pszCameraRoll != NULL )
1060         camera->set_roll(CPLAtof(pszCameraRoll));
1061     if( isGX )
1062         camera->set_gx_altitudemode(iAltitudeMode);
1063     else
1064         camera->set_altitudemode(iAltitudeMode);
1065 
1066     m_poKmlLayer->set_abstractview(camera);
1067 }
1068 
1069 /************************************************************************/
1070 /*                         SetWriteRegion()                             */
1071 /************************************************************************/
1072 
SetWriteRegion(double dfMinLodPixels,double dfMaxLodPixels,double dfMinFadeExtent,double dfMaxFadeExtent)1073 void OGRLIBKMLLayer::SetWriteRegion(double dfMinLodPixels,
1074                                     double dfMaxLodPixels,
1075                                     double dfMinFadeExtent,
1076                                     double dfMaxFadeExtent)
1077 {
1078     m_bWriteRegion = TRUE;
1079     m_bRegionBoundsAuto = TRUE;
1080     m_dfRegionMinLodPixels = dfMinLodPixels;
1081     m_dfRegionMaxLodPixels = dfMaxLodPixels;
1082     m_dfRegionMinFadeExtent = dfMinFadeExtent;
1083     m_dfRegionMaxFadeExtent = dfMaxFadeExtent;
1084 }
1085 
1086 /************************************************************************/
1087 /*                          SetRegionBounds()                           */
1088 /************************************************************************/
1089 
SetRegionBounds(double dfMinX,double dfMinY,double dfMaxX,double dfMaxY)1090 void OGRLIBKMLLayer::SetRegionBounds(double dfMinX, double dfMinY,
1091                                      double dfMaxX, double dfMaxY)
1092 {
1093     m_bRegionBoundsAuto = FALSE;
1094     m_dfRegionMinX = dfMinX;
1095     m_dfRegionMinY = dfMinY;
1096     m_dfRegionMaxX = dfMaxX;
1097     m_dfRegionMaxY = dfMaxY;
1098 }
1099 
1100 /************************************************************************/
1101 /*                            Finalize()                                */
1102 /************************************************************************/
1103 
Finalize(DocumentPtr poKmlDocument)1104 void OGRLIBKMLLayer::Finalize(DocumentPtr poKmlDocument)
1105 {
1106     KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory (  );
1107 
1108     if( m_bWriteRegion && m_dfRegionMinX < m_dfRegionMaxX )
1109     {
1110         RegionPtr region = poKmlFactory->CreateRegion();
1111 
1112         LatLonAltBoxPtr box = poKmlFactory->CreateLatLonAltBox();
1113         box->set_west(m_dfRegionMinX);
1114         box->set_east(m_dfRegionMaxX);
1115         box->set_south(m_dfRegionMinY);
1116         box->set_north(m_dfRegionMaxY);
1117         region->set_latlonaltbox(box);
1118 
1119         LodPtr lod = poKmlFactory->CreateLod();
1120         lod->set_minlodpixels(m_dfRegionMinLodPixels);
1121         lod->set_maxlodpixels(m_dfRegionMaxLodPixels);
1122         if( (m_dfRegionMinFadeExtent != 0 || m_dfRegionMaxFadeExtent != 0) &&
1123             m_dfRegionMinFadeExtent + m_dfRegionMaxFadeExtent <
1124                 m_dfRegionMaxLodPixels - m_dfRegionMinLodPixels )
1125         {
1126             lod->set_minfadeextent(m_dfRegionMinFadeExtent);
1127             lod->set_maxfadeextent(m_dfRegionMaxFadeExtent);
1128         }
1129 
1130         region->set_lod(lod);
1131         m_poKmlLayer->set_region(region);
1132     }
1133 
1134     createkmlliststyle (poKmlFactory,
1135                         GetName(),
1136                         m_poKmlLayer,
1137                         poKmlDocument,
1138                         osListStyleType,
1139                         osListStyleIconHref);
1140 }
1141 
1142 /************************************************************************/
1143 /*                             LIBKMLGetUnits()                         */
1144 /************************************************************************/
1145 
LIBKMLGetUnits(const char * pszUnits)1146 static int LIBKMLGetUnits(const char* pszUnits)
1147 {
1148     if( EQUAL(pszUnits, "fraction") )
1149         return kmldom::UNITS_FRACTION;
1150     if( EQUAL(pszUnits, "pixels") )
1151         return  kmldom::UNITS_PIXELS;
1152     if( EQUAL(pszUnits, "insetPixels") )
1153         return  kmldom::UNITS_INSETPIXELS;
1154     return  kmldom::UNITS_FRACTION;
1155 }
1156 
1157 /************************************************************************/
1158 /*                         LIBKMLSetVec2()                              */
1159 /************************************************************************/
1160 
LIBKMLSetVec2(kmldom::Vec2Ptr vec2,const char * pszX,const char * pszY,const char * pszXUnits,const char * pszYUnits)1161 static void LIBKMLSetVec2(kmldom::Vec2Ptr vec2, const char* pszX, const char* pszY,
1162                     const char* pszXUnits, const char* pszYUnits)
1163 {
1164     double dfX = CPLAtof(pszX);
1165     double dfY = CPLAtof(pszY);
1166     vec2->set_x(dfX);
1167     vec2->set_y(dfY);
1168     if( dfX <= 1 && dfY <= 1 )
1169     {
1170         if( pszXUnits == NULL ) pszXUnits = "fraction";
1171         if( pszYUnits == NULL ) pszYUnits = "fraction";
1172     }
1173     else
1174     {
1175         if( pszXUnits == NULL ) pszXUnits = "pixels";
1176         if( pszYUnits == NULL ) pszYUnits = "pixels";
1177     }
1178     vec2->set_xunits(LIBKMLGetUnits(pszXUnits));
1179     vec2->set_yunits(LIBKMLGetUnits(pszYUnits));
1180 }
1181 
1182 /************************************************************************/
1183 /*                         SetScreenOverlay()                           */
1184 /************************************************************************/
1185 
SetScreenOverlay(const char * pszSOHref,const char * pszSOName,const char * pszSODescription,const char * pszSOOverlayX,const char * pszSOOverlayY,const char * pszSOOverlayXUnits,const char * pszSOOverlayYUnits,const char * pszSOScreenX,const char * pszSOScreenY,const char * pszSOScreenXUnits,const char * pszSOScreenYUnits,const char * pszSOSizeX,const char * pszSOSizeY,const char * pszSOSizeXUnits,const char * pszSOSizeYUnits)1186 void OGRLIBKMLLayer::SetScreenOverlay(const char* pszSOHref,
1187                                       const char* pszSOName,
1188                                       const char* pszSODescription,
1189                                       const char* pszSOOverlayX,
1190                                       const char* pszSOOverlayY,
1191                                       const char* pszSOOverlayXUnits,
1192                                       const char* pszSOOverlayYUnits,
1193                                       const char* pszSOScreenX,
1194                                       const char* pszSOScreenY,
1195                                       const char* pszSOScreenXUnits,
1196                                       const char* pszSOScreenYUnits,
1197                                       const char* pszSOSizeX,
1198                                       const char* pszSOSizeY,
1199                                       const char* pszSOSizeXUnits,
1200                                       const char* pszSOSizeYUnits)
1201 {
1202     KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory (  );
1203     ScreenOverlayPtr so = poKmlFactory->CreateScreenOverlay();
1204 
1205     if( pszSOName != NULL )
1206         so->set_name(pszSOName);
1207     if( pszSODescription != NULL )
1208         so->set_description(pszSODescription);
1209 
1210     IconPtr icon = poKmlFactory->CreateIcon();
1211     icon->set_href(pszSOHref);
1212     so->set_icon(icon);
1213 
1214     if( pszSOOverlayX != NULL && pszSOOverlayY != NULL )
1215     {
1216         kmldom::OverlayXYPtr overlayxy = poKmlFactory->CreateOverlayXY();
1217         LIBKMLSetVec2(overlayxy, pszSOOverlayX, pszSOOverlayY,
1218                       pszSOOverlayXUnits, pszSOOverlayYUnits);
1219         so->set_overlayxy(overlayxy);
1220     }
1221 
1222     if( pszSOScreenX != NULL && pszSOScreenY != NULL )
1223     {
1224         kmldom::ScreenXYPtr screenxy = poKmlFactory->CreateScreenXY();
1225         LIBKMLSetVec2(screenxy, pszSOScreenX, pszSOScreenY,
1226                       pszSOScreenXUnits, pszSOScreenYUnits);
1227         so->set_screenxy(screenxy);
1228     }
1229     else
1230     {
1231         kmldom::ScreenXYPtr screenxy = poKmlFactory->CreateScreenXY();
1232         LIBKMLSetVec2(screenxy, "0.05", "0.05", NULL, NULL);
1233         so->set_screenxy(screenxy);
1234     }
1235 
1236     if( pszSOSizeX != NULL && pszSOSizeY != NULL )
1237     {
1238         kmldom::SizePtr sizexy = poKmlFactory->CreateSize();
1239         LIBKMLSetVec2(sizexy, pszSOSizeX, pszSOSizeY,
1240                       pszSOSizeXUnits, pszSOSizeYUnits);
1241         so->set_size(sizexy);
1242     }
1243 
1244     m_poKmlLayer->add_feature(so);
1245 }
1246 
1247 /************************************************************************/
1248 /*                           SetListStyle()                              */
1249 /************************************************************************/
1250 
SetListStyle(const char * pszListStyleType,const char * pszListStyleIconHref)1251 void OGRLIBKMLLayer::SetListStyle(const char* pszListStyleType,
1252                                   const char* pszListStyleIconHref)
1253 {
1254     osListStyleType = (pszListStyleType) ? pszListStyleType : "";
1255     osListStyleIconHref = (pszListStyleIconHref) ? pszListStyleIconHref : "";
1256 }
1257