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