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

Generated by: LCOV version 1.14