LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/libkml - ogrlibkmllayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 451 503 89.7 %
Date: 2024-05-02 22:57:13 Functions: 24 27 88.9 %

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

Generated by: LCOV version 1.14