LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/libkml - ogrlibkmllayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 532 571 93.2 %
Date: 2025-02-18 14:19:29 Functions: 27 28 96.4 %

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

Generated by: LCOV version 1.14