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