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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1180 void OGRLIBKMLLayer::SetListStyle( const char* pszListStyleType, 1181 const char* pszListStyleIconHref ) 1182 { 1183 osListStyleType = pszListStyleType ? pszListStyleType : ""; 1184 osListStyleIconHref = pszListStyleIconHref ? pszListStyleIconHref : ""; 1185 } 1186