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

Generated by: LCOV version 1.14