LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/libkml - ogrlibkmlfield.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 674 899 75.0 %
Date: 2026-01-23 20:24:11 Functions: 15 16 93.8 %

          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             : #include "ogrlibkmlfield.h"
      16             : 
      17             : #include <set>
      18             : #include <string>
      19             : 
      20             : #include "ogr_feature.h"
      21             : #include "ogr_p.h"
      22             : #include "ogrsf_frmts.h"
      23             : 
      24             : using kmldom::CameraPtr;
      25             : using kmldom::DataPtr;
      26             : using kmldom::ExtendedDataPtr;
      27             : using kmldom::FeaturePtr;
      28             : using kmldom::GeometryPtr;
      29             : using kmldom::GroundOverlayPtr;
      30             : using kmldom::GxMultiTrackPtr;
      31             : using kmldom::GxTrackPtr;
      32             : using kmldom::IconPtr;
      33             : using kmldom::KmlFactory;
      34             : using kmldom::LineStringPtr;
      35             : using kmldom::MultiGeometryPtr;
      36             : using kmldom::PlacemarkPtr;
      37             : using kmldom::PointPtr;
      38             : using kmldom::PolygonPtr;
      39             : using kmldom::SchemaDataPtr;
      40             : using kmldom::SchemaPtr;
      41             : using kmldom::SimpleDataPtr;
      42             : using kmldom::SimpleFieldPtr;
      43             : using kmldom::SnippetPtr;
      44             : using kmldom::TimePrimitivePtr;
      45             : using kmldom::TimeSpanPtr;
      46             : using kmldom::TimeStampPtr;
      47             : 
      48           1 : static void ogr2altitudemode_rec(const GeometryPtr &poKmlGeometry,
      49             :                                  int iAltitudeMode, int isGX)
      50             : {
      51           1 :     switch (poKmlGeometry->Type())
      52             :     {
      53           0 :         case kmldom::Type_Point:
      54             :         {
      55           0 :             PointPtr poKmlPoint = AsPoint(poKmlGeometry);
      56             : 
      57           0 :             if (!isGX)
      58           0 :                 poKmlPoint->set_altitudemode(iAltitudeMode);
      59             :             else
      60           0 :                 poKmlPoint->set_gx_altitudemode(iAltitudeMode);
      61             : 
      62           0 :             break;
      63             :         }
      64           0 :         case kmldom::Type_LineString:
      65             :         {
      66           0 :             LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
      67             : 
      68           0 :             if (!isGX)
      69           0 :                 poKmlLineString->set_altitudemode(iAltitudeMode);
      70             :             else
      71           0 :                 poKmlLineString->set_gx_altitudemode(iAltitudeMode);
      72             : 
      73           0 :             break;
      74             :         }
      75           0 :         case kmldom::Type_LinearRing:
      76             :         {
      77           0 :             break;
      78             :         }
      79           0 :         case kmldom::Type_Polygon:
      80             :         {
      81           0 :             PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
      82             : 
      83           0 :             if (!isGX)
      84           0 :                 poKmlPolygon->set_altitudemode(iAltitudeMode);
      85             :             else
      86           0 :                 poKmlPolygon->set_gx_altitudemode(iAltitudeMode);
      87             : 
      88           0 :             break;
      89             :         }
      90           0 :         case kmldom::Type_MultiGeometry:
      91             :         {
      92             :             MultiGeometryPtr poKmlMultiGeometry =
      93           0 :                 AsMultiGeometry(poKmlGeometry);
      94             : 
      95           0 :             const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
      96           0 :             for (size_t i = 0; i < nGeom; i++)
      97             :             {
      98           0 :                 ogr2altitudemode_rec(
      99             :                     poKmlMultiGeometry->get_geometry_array_at(i), iAltitudeMode,
     100             :                     isGX);
     101             :             }
     102             : 
     103           0 :             break;
     104             :         }
     105           1 :         default:
     106             :         {
     107           1 :             break;
     108             :         }
     109             :     }
     110           1 : }
     111             : 
     112           9 : static void ogr2extrude_rec(bool bExtrude, const GeometryPtr &poKmlGeometry)
     113             : {
     114           9 :     switch (poKmlGeometry->Type())
     115             :     {
     116           2 :         case kmldom::Type_Point:
     117             :         {
     118           4 :             PointPtr const poKmlPoint = AsPoint(poKmlGeometry);
     119           2 :             poKmlPoint->set_extrude(bExtrude);
     120           2 :             break;
     121             :         }
     122           0 :         case kmldom::Type_LineString:
     123             :         {
     124           0 :             LineStringPtr const poKmlLineString = AsLineString(poKmlGeometry);
     125           0 :             poKmlLineString->set_extrude(bExtrude);
     126           0 :             break;
     127             :         }
     128           0 :         case kmldom::Type_LinearRing:
     129             :         {
     130           0 :             break;
     131             :         }
     132           7 :         case kmldom::Type_Polygon:
     133             :         {
     134          14 :             PolygonPtr const poKmlPolygon = AsPolygon(poKmlGeometry);
     135           7 :             poKmlPolygon->set_extrude(bExtrude);
     136           7 :             break;
     137             :         }
     138           0 :         case kmldom::Type_MultiGeometry:
     139             :         {
     140             :             MultiGeometryPtr const poKmlMultiGeometry =
     141           0 :                 AsMultiGeometry(poKmlGeometry);
     142             : 
     143           0 :             const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
     144           0 :             for (size_t i = 0; i < nGeom; i++)
     145             :             {
     146           0 :                 ogr2extrude_rec(bExtrude,
     147             :                                 poKmlMultiGeometry->get_geometry_array_at(i));
     148             :             }
     149           0 :             break;
     150             :         }
     151           0 :         default:
     152             :         {
     153           0 :             break;
     154             :         }
     155             :     }
     156           9 : }
     157             : 
     158           0 : static void ogr2tessellate_rec(bool bTessellate,
     159             :                                const GeometryPtr &poKmlGeometry)
     160             : {
     161           0 :     switch (poKmlGeometry->Type())
     162             :     {
     163           0 :         case kmldom::Type_Point:
     164             :         {
     165           0 :             break;
     166             :         }
     167           0 :         case kmldom::Type_LineString:
     168             :         {
     169           0 :             LineStringPtr const poKmlLineString = AsLineString(poKmlGeometry);
     170           0 :             poKmlLineString->set_tessellate(bTessellate);
     171           0 :             break;
     172             :         }
     173           0 :         case kmldom::Type_LinearRing:
     174             :         {
     175           0 :             break;
     176             :         }
     177           0 :         case kmldom::Type_Polygon:
     178             :         {
     179           0 :             PolygonPtr const poKmlPolygon = AsPolygon(poKmlGeometry);
     180             : 
     181           0 :             poKmlPolygon->set_tessellate(bTessellate);
     182           0 :             break;
     183             :         }
     184           0 :         case kmldom::Type_MultiGeometry:
     185             :         {
     186             :             MultiGeometryPtr const poKmlMultiGeometry =
     187           0 :                 AsMultiGeometry(poKmlGeometry);
     188             : 
     189           0 :             const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
     190           0 :             for (size_t i = 0; i < nGeom; i++)
     191             :             {
     192           0 :                 ogr2tessellate_rec(
     193             :                     bTessellate, poKmlMultiGeometry->get_geometry_array_at(i));
     194             :             }
     195             : 
     196           0 :             break;
     197             :         }
     198           0 :         default:
     199             :         {
     200           0 :             break;
     201             :         }
     202             :     }
     203           0 : }
     204             : 
     205             : /************************************************************************/
     206             : /*                 OGRLIBKMLSanitizeUTF8String()                        */
     207             : /************************************************************************/
     208             : 
     209         130 : static char *OGRLIBKMLSanitizeUTF8String(const char *pszString)
     210             : {
     211         130 :     if (!CPLIsUTF8(pszString, -1) &&
     212           0 :         CPLTestBool(CPLGetConfigOption("OGR_FORCE_ASCII", "YES")))
     213             :     {
     214             :         static bool bFirstTime = true;
     215           0 :         if (bFirstTime)
     216             :         {
     217           0 :             bFirstTime = false;
     218           0 :             CPLError(
     219             :                 CE_Warning, CPLE_AppDefined,
     220             :                 "%s is not a valid UTF-8 string. Forcing it to ASCII.  "
     221             :                 "If you still want the original string and change the XML file "
     222             :                 "encoding afterwards, you can define OGR_FORCE_ASCII=NO as "
     223             :                 "  configuration option.  This warning won't be issued anymore",
     224             :                 pszString);
     225             :         }
     226             :         else
     227             :         {
     228           0 :             CPLDebug("OGR",
     229             :                      "%s is not a valid UTF-8 string. Forcing it to ASCII",
     230             :                      pszString);
     231             :         }
     232           0 :         return CPLForceToASCII(pszString, -1, '?');
     233             :     }
     234             : 
     235         130 :     return CPLStrdup(pszString);
     236             : }
     237             : 
     238             : /******************************************************************************
     239             :  Function to output ogr fields in kml.
     240             : 
     241             :  Args:
     242             :         poOgrFeat       pointer to the feature the field is in
     243             :         poOgrLayer      pointer to the layer the feature is in
     244             :         poKmlFactory    pointer to the libkml dom factory
     245             :         poKmlPlacemark  pointer to the placemark to add to
     246             : 
     247             :  Returns:
     248             :         nothing
     249             : 
     250             :  env vars:
     251             :   LIBKML_TIMESTAMP_FIELD         default: OFTDate or OFTDateTime named timestamp
     252             :   LIBKML_TIMESPAN_BEGIN_FIELD    default: OFTDate or OFTDateTime named begin
     253             :   LIBKML_TIMESPAN_END_FIELD      default: OFTDate or OFTDateTime named end
     254             :   LIBKML_DESCRIPTION_FIELD       default: none
     255             :   LIBKML_NAME_FIELD              default: OFTString field named name
     256             : 
     257             : ******************************************************************************/
     258             : 
     259         205 : void field2kml(OGRFeature *poOgrFeat, OGRLIBKMLLayer *poOgrLayer,
     260             :                KmlFactory *poKmlFactory, FeaturePtr poKmlFeature,
     261             :                int bUseSimpleFieldIn, const fieldconfig &oFC)
     262             : {
     263         205 :     const bool bUseSimpleField = CPL_TO_BOOL(bUseSimpleFieldIn);
     264         410 :     SchemaDataPtr poKmlSchemaData = nullptr;
     265         205 :     if (bUseSimpleField)
     266             :     {
     267         204 :         poKmlSchemaData = poKmlFactory->CreateSchemaData();
     268         408 :         SchemaPtr poKmlSchema = poOgrLayer->GetKmlSchema();
     269             : 
     270             :         /***** set the url to the schema *****/
     271         204 :         if (poKmlSchema && poKmlSchema->has_id())
     272             :         {
     273         296 :             std::string oKmlSchemaID = poKmlSchema->get_id();
     274         296 :             std::string oKmlSchemaURL = "#";
     275         148 :             oKmlSchemaURL.append(oKmlSchemaID);
     276             : 
     277         148 :             poKmlSchemaData->set_schemaurl(oKmlSchemaURL);
     278             :         }
     279             :     }
     280             : 
     281         410 :     TimeSpanPtr poKmlTimeSpan = nullptr;
     282             : 
     283         205 :     const int nFields = poOgrFeat->GetFieldCount();
     284         205 :     int iSkip1 = -1;
     285         205 :     int iSkip2 = -1;
     286         205 :     int iAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND;
     287         205 :     int isGX = false;
     288             : 
     289         410 :     ExtendedDataPtr poKmlExtendedData = nullptr;
     290             : 
     291        1063 :     for (int i = 0; i < nFields; i++)
     292             :     {
     293             :         /***** If the field is set to skip, do so *****/
     294         858 :         if (i == iSkip1 || i == iSkip2)
     295         544 :             continue;
     296             : 
     297             :         /***** If the field isn't set just bail now *****/
     298         858 :         if (!poOgrFeat->IsFieldSetAndNotNull(i))
     299         427 :             continue;
     300             : 
     301         431 :         const OGRFieldDefn *poOgrFieldDef = poOgrFeat->GetFieldDefnRef(i);
     302         431 :         const OGRFieldType type = poOgrFieldDef->GetType();
     303         431 :         const char *name = poOgrFieldDef->GetNameRef();
     304             : 
     305         431 :         SimpleDataPtr poKmlSimpleData = nullptr;
     306         431 :         DataPtr poKmlData = nullptr;
     307             :         OGRField sFieldDT;
     308             : 
     309             :         // TODO(schwehr): Refactor to get rid of gotos.
     310         431 :         switch (type)
     311             :         {
     312         130 :             case OFTString:  //     String of ASCII chars
     313             :             {
     314             :                 char *const pszUTF8String =
     315         130 :                     OGRLIBKMLSanitizeUTF8String(poOgrFeat->GetFieldAsString(i));
     316         130 :                 if (pszUTF8String[0] == '\0')
     317             :                 {
     318           0 :                     CPLFree(pszUTF8String);
     319           0 :                     continue;
     320             :                 }
     321             : 
     322             :                 /***** id *****/
     323         130 :                 if (EQUAL(name, oFC.idfield))
     324             :                 {
     325          11 :                     poKmlFeature->set_id(pszUTF8String);
     326          11 :                     CPLFree(pszUTF8String);
     327          11 :                     continue;
     328             :                 }
     329             :                 /***** name *****/
     330         119 :                 if (EQUAL(name, oFC.namefield))
     331             :                 {
     332          15 :                     poKmlFeature->set_name(pszUTF8String);
     333          15 :                     CPLFree(pszUTF8String);
     334          15 :                     continue;
     335             :                 }
     336             :                 /***** description *****/
     337         104 :                 else if (EQUAL(name, oFC.descfield))
     338             :                 {
     339           6 :                     poKmlFeature->set_description(pszUTF8String);
     340           6 :                     CPLFree(pszUTF8String);
     341           6 :                     continue;
     342             :                 }
     343             :                 /***** altitudemode *****/
     344          98 :                 else if (EQUAL(name, oFC.altitudeModefield))
     345             :                 {
     346           2 :                     const char *pszAltitudeMode = pszUTF8String;
     347             : 
     348             :                     iAltitudeMode =
     349           2 :                         kmlAltitudeModeFromString(pszAltitudeMode, isGX);
     350             : 
     351           2 :                     if (poKmlFeature->IsA(kmldom::Type_Placemark))
     352             :                     {
     353             :                         PlacemarkPtr const poKmlPlacemark =
     354           4 :                             AsPlacemark(poKmlFeature);
     355           2 :                         if (poKmlPlacemark->has_geometry())
     356             :                         {
     357             :                             GeometryPtr poKmlGeometry =
     358           2 :                                 poKmlPlacemark->get_geometry();
     359             : 
     360           1 :                             ogr2altitudemode_rec(poKmlGeometry, iAltitudeMode,
     361             :                                                  isGX);
     362             :                         }
     363             :                     }
     364             : 
     365           2 :                     CPLFree(pszUTF8String);
     366             : 
     367           2 :                     continue;
     368             :                 }
     369             :                 /***** timestamp *****/
     370          96 :                 else if (EQUAL(name, oFC.tsfield))
     371             :                 {
     372             :                     TimeStampPtr poKmlTimeStamp =
     373           0 :                         poKmlFactory->CreateTimeStamp();
     374           0 :                     poKmlTimeStamp->set_when(pszUTF8String);
     375           0 :                     poKmlFeature->set_timeprimitive(poKmlTimeStamp);
     376             : 
     377           0 :                     CPLFree(pszUTF8String);
     378             : 
     379           0 :                     continue;
     380             :                 }
     381             :                 /***** begin *****/
     382          96 :                 if (EQUAL(name, oFC.beginfield))
     383             :                 {
     384           0 :                     if (!poKmlTimeSpan)
     385             :                     {
     386           0 :                         poKmlTimeSpan = poKmlFactory->CreateTimeSpan();
     387           0 :                         poKmlFeature->set_timeprimitive(poKmlTimeSpan);
     388             :                     }
     389             : 
     390           0 :                     poKmlTimeSpan->set_begin(pszUTF8String);
     391             : 
     392           0 :                     CPLFree(pszUTF8String);
     393             : 
     394           0 :                     continue;
     395             :                 }
     396             :                 /***** end *****/
     397          96 :                 else if (EQUAL(name, oFC.endfield))
     398             :                 {
     399           0 :                     if (!poKmlTimeSpan)
     400             :                     {
     401           0 :                         poKmlTimeSpan = poKmlFactory->CreateTimeSpan();
     402           0 :                         poKmlFeature->set_timeprimitive(poKmlTimeSpan);
     403             :                     }
     404             : 
     405           0 :                     poKmlTimeSpan->set_end(pszUTF8String);
     406             : 
     407           0 :                     CPLFree(pszUTF8String);
     408             : 
     409           0 :                     continue;
     410             :                 }
     411             :                 /***** snippet *****/
     412          96 :                 else if (EQUAL(name, oFC.snippetfield))
     413             :                 {
     414           2 :                     SnippetPtr snippet = poKmlFactory->CreateSnippet();
     415           1 :                     snippet->set_text(pszUTF8String);
     416           1 :                     poKmlFeature->set_snippet(snippet);
     417             : 
     418           1 :                     CPLFree(pszUTF8String);
     419             : 
     420           1 :                     continue;
     421             :                 }
     422             :                 /***** other special fields *****/
     423          95 :                 else if (EQUAL(name, oFC.iconfield) ||
     424          95 :                          EQUAL(name, oFC.modelfield) ||
     425          93 :                          EQUAL(name, oFC.networklinkfield) ||
     426          90 :                          EQUAL(name, oFC.networklink_refreshMode_field) ||
     427          89 :                          EQUAL(name, oFC.networklink_viewRefreshMode_field) ||
     428          88 :                          EQUAL(name, oFC.networklink_viewFormat_field) ||
     429          87 :                          EQUAL(name, oFC.networklink_httpQuery_field) ||
     430          86 :                          EQUAL(name, oFC.camera_altitudemode_field) ||
     431          84 :                          EQUAL(name, oFC.photooverlayfield) ||
     432          82 :                          EQUAL(name, oFC.photooverlay_shape_field) ||
     433          80 :                          EQUAL(name, oFC.imagepyramid_gridorigin_field))
     434             :                 {
     435          15 :                     CPLFree(pszUTF8String);
     436             : 
     437          15 :                     continue;
     438             :                 }
     439             : 
     440             :                 /***** other *****/
     441             : 
     442          80 :                 if (bUseSimpleField)
     443             :                 {
     444          79 :                     poKmlSimpleData = poKmlFactory->CreateSimpleData();
     445          79 :                     poKmlSimpleData->set_name(name);
     446          79 :                     poKmlSimpleData->set_text(pszUTF8String);
     447             :                 }
     448             :                 else
     449             :                 {
     450           1 :                     poKmlData = poKmlFactory->CreateData();
     451           1 :                     poKmlData->set_name(name);
     452           1 :                     poKmlData->set_value(pszUTF8String);
     453             :                 }
     454             : 
     455          80 :                 CPLFree(pszUTF8String);
     456             : 
     457          80 :                 break;
     458             :             }
     459             : 
     460             :             // This code checks if there's a OFTTime field with the same name
     461             :             // that could be used to compose a DateTime. Not sure this is really
     462             :             // supported in OGR data model to have 2 fields with same name.
     463          48 :             case OFTDate:  //   Date
     464             :             {
     465          48 :                 memcpy(&sFieldDT, poOgrFeat->GetRawFieldRef(i),
     466             :                        sizeof(OGRField));
     467             : 
     468          96 :                 for (int iTimeField = i + 1; iTimeField < nFields; iTimeField++)
     469             :                 {
     470          48 :                     if (iTimeField == iSkip1 || iTimeField == iSkip2)
     471           0 :                         continue;
     472             : 
     473             :                     const OGRFieldDefn *poOgrFieldDef2 =
     474          48 :                         poOgrFeat->GetFieldDefnRef(i);
     475          48 :                     const OGRFieldType type2 = poOgrFieldDef2->GetType();
     476          48 :                     const char *name2 = poOgrFieldDef2->GetNameRef();
     477             : 
     478          48 :                     if (EQUAL(name2, name) && type2 == OFTTime &&
     479           0 :                         (EQUAL(name, oFC.tsfield) ||
     480           0 :                          EQUAL(name, oFC.beginfield) ||
     481           0 :                          EQUAL(name, oFC.endfield)))
     482             :                     {
     483             :                         const OGRField *const psField2 =
     484           0 :                             poOgrFeat->GetRawFieldRef(iTimeField);
     485           0 :                         sFieldDT.Date.Hour = psField2->Date.Hour;
     486           0 :                         sFieldDT.Date.Minute = psField2->Date.Minute;
     487           0 :                         sFieldDT.Date.Second = psField2->Date.Second;
     488           0 :                         sFieldDT.Date.TZFlag = psField2->Date.TZFlag;
     489             : 
     490           0 :                         if (0 > iSkip1)
     491           0 :                             iSkip1 = iTimeField;
     492             :                         else
     493           0 :                             iSkip2 = iTimeField;
     494             :                     }
     495             :                 }
     496             : 
     497          48 :                 goto Do_DateTime;
     498             :             }
     499             : 
     500             :             // This code checks if there's a OFTTime field with the same name
     501             :             // that could be used to compose a DateTime. Not sure this is really
     502             :             // supported in OGR data model to have 2 fields with same name.
     503           6 :             case OFTTime:  //   Time
     504             :             {
     505           6 :                 memcpy(&sFieldDT, poOgrFeat->GetRawFieldRef(i),
     506             :                        sizeof(OGRField));
     507             : 
     508          12 :                 for (int iTimeField = i + 1; iTimeField < nFields; iTimeField++)
     509             :                 {
     510           6 :                     if (iTimeField == iSkip1 || iTimeField == iSkip2)
     511           0 :                         continue;
     512             : 
     513             :                     const OGRFieldDefn *const poOgrFieldDef2 =
     514           6 :                         poOgrFeat->GetFieldDefnRef(i);
     515           6 :                     OGRFieldType type2 = poOgrFieldDef2->GetType();
     516           6 :                     const char *const name2 = poOgrFieldDef2->GetNameRef();
     517             : 
     518           6 :                     if (EQUAL(name2, name) && type2 == OFTDate &&
     519           0 :                         (EQUAL(name, oFC.tsfield) ||
     520           0 :                          EQUAL(name, oFC.beginfield) ||
     521           0 :                          EQUAL(name, oFC.endfield)))
     522             :                     {
     523             :                         const OGRField *psField2 =
     524           0 :                             poOgrFeat->GetRawFieldRef(iTimeField);
     525           0 :                         sFieldDT.Date.Year = psField2->Date.Year;
     526           0 :                         sFieldDT.Date.Month = psField2->Date.Month;
     527           0 :                         sFieldDT.Date.Day = psField2->Date.Day;
     528             : 
     529           0 :                         if (0 > iSkip1)
     530           0 :                             iSkip1 = iTimeField;
     531             :                         else
     532           0 :                             iSkip2 = iTimeField;
     533             :                     }
     534             :                 }
     535             : 
     536           6 :                 goto Do_DateTime;
     537             :             }
     538             : 
     539          54 :             case OFTDateTime:  //  Date and Time
     540             :             {
     541          54 :                 memcpy(&sFieldDT, poOgrFeat->GetRawFieldRef(i),
     542             :                        sizeof(OGRField));
     543             : 
     544         108 :             Do_DateTime:
     545             :                 /***** timestamp *****/
     546         108 :                 if (EQUAL(name, oFC.tsfield))
     547             :                 {
     548           0 :                     char *const timebuf = OGRGetXMLDateTime(&sFieldDT);
     549             : 
     550             :                     TimeStampPtr poKmlTimeStamp =
     551           0 :                         poKmlFactory->CreateTimeStamp();
     552           0 :                     poKmlTimeStamp->set_when(timebuf);
     553           0 :                     poKmlFeature->set_timeprimitive(poKmlTimeStamp);
     554           0 :                     CPLFree(timebuf);
     555             : 
     556           0 :                     continue;
     557             :                 }
     558             : 
     559             :                 /***** begin *****/
     560         108 :                 if (EQUAL(name, oFC.beginfield))
     561             :                 {
     562           0 :                     char *const timebuf = OGRGetXMLDateTime(&sFieldDT);
     563             : 
     564           0 :                     if (!poKmlTimeSpan)
     565             :                     {
     566           0 :                         poKmlTimeSpan = poKmlFactory->CreateTimeSpan();
     567           0 :                         poKmlFeature->set_timeprimitive(poKmlTimeSpan);
     568             :                     }
     569             : 
     570           0 :                     poKmlTimeSpan->set_begin(timebuf);
     571           0 :                     CPLFree(timebuf);
     572             : 
     573           0 :                     continue;
     574             :                 }
     575             : 
     576             :                 /***** end *****/
     577         108 :                 else if (EQUAL(name, oFC.endfield))
     578             :                 {
     579           0 :                     char *const timebuf = OGRGetXMLDateTime(&sFieldDT);
     580             : 
     581           0 :                     if (!poKmlTimeSpan)
     582             :                     {
     583           0 :                         poKmlTimeSpan = poKmlFactory->CreateTimeSpan();
     584           0 :                         poKmlFeature->set_timeprimitive(poKmlTimeSpan);
     585             :                     }
     586             : 
     587           0 :                     poKmlTimeSpan->set_end(timebuf);
     588           0 :                     CPLFree(timebuf);
     589             : 
     590           0 :                     continue;
     591             :                 }
     592             : 
     593             :                 /***** other *****/
     594             :                 const char *pszVal =
     595             :                     type == OFTDateTime
     596         108 :                         ? poOgrFeat->GetFieldAsISO8601DateTime(i, nullptr)
     597          54 :                         : poOgrFeat->GetFieldAsString(i);
     598         108 :                 if (bUseSimpleField)
     599             :                 {
     600         108 :                     poKmlSimpleData = poKmlFactory->CreateSimpleData();
     601         108 :                     poKmlSimpleData->set_name(name);
     602         108 :                     poKmlSimpleData->set_text(pszVal);
     603             :                 }
     604             :                 else
     605             :                 {
     606           0 :                     poKmlData = poKmlFactory->CreateData();
     607           0 :                     poKmlData->set_name(name);
     608           0 :                     poKmlData->set_value(pszVal);
     609             :                 }
     610             : 
     611         108 :                 break;
     612             :             }
     613             : 
     614          83 :             case OFTInteger:  //    Simple 32bit integer
     615             :             {
     616             :                 /***** extrude *****/
     617          83 :                 if (EQUAL(name, oFC.extrudefield))
     618             :                 {
     619           9 :                     if (poKmlFeature->IsA(kmldom::Type_Placemark))
     620             :                     {
     621          18 :                         PlacemarkPtr poKmlPlacemark = AsPlacemark(poKmlFeature);
     622          18 :                         if (poKmlPlacemark->has_geometry() &&
     623           9 :                             -1 < poOgrFeat->GetFieldAsInteger(i))
     624             :                         {
     625             :                             const int iExtrude =
     626           9 :                                 poOgrFeat->GetFieldAsInteger(i);
     627           0 :                             if (iExtrude && !isGX &&
     628             :                                 iAltitudeMode ==
     629           9 :                                     kmldom::ALTITUDEMODE_CLAMPTOGROUND &&
     630           0 :                                 CPLTestBool(CPLGetConfigOption(
     631             :                                     "LIBKML_STRICT_COMPLIANCE", "TRUE")))
     632             :                             {
     633           0 :                                 CPLError(CE_Warning, CPLE_NotSupported,
     634             :                                          "altitudeMode=clampToGround "
     635             :                                          "unsupported with "
     636             :                                          "extrude=1");
     637             :                             }
     638             :                             else
     639             :                             {
     640             :                                 GeometryPtr poKmlGeometry =
     641          18 :                                     poKmlPlacemark->get_geometry();
     642           9 :                                 ogr2extrude_rec(CPL_TO_BOOL(iExtrude),
     643             :                                                 poKmlGeometry);
     644             :                             }
     645             :                         }
     646             :                     }
     647           9 :                     continue;
     648             :                 }
     649             : 
     650             :                 /***** tessellate *****/
     651          74 :                 if (EQUAL(name, oFC.tessellatefield))
     652             :                 {
     653           9 :                     if (poKmlFeature->IsA(kmldom::Type_Placemark))
     654             :                     {
     655          18 :                         PlacemarkPtr poKmlPlacemark = AsPlacemark(poKmlFeature);
     656          18 :                         if (poKmlPlacemark->has_geometry() &&
     657           9 :                             -1 < poOgrFeat->GetFieldAsInteger(i))
     658             :                         {
     659             :                             const int iTessellate =
     660           0 :                                 poOgrFeat->GetFieldAsInteger(i);
     661           0 :                             if (iTessellate &&
     662           0 :                                 !(!isGX &&
     663             :                                   static_cast<kmldom::AltitudeModeEnum>(
     664             :                                       iAltitudeMode) ==
     665           0 :                                       kmldom::ALTITUDEMODE_CLAMPTOGROUND) &&
     666           0 :                                 !(isGX &&
     667             :                                   static_cast<kmldom::GxAltitudeModeEnum>(
     668             :                                       iAltitudeMode) ==
     669             :                                       kmldom::
     670           0 :                                           GX_ALTITUDEMODE_CLAMPTOSEAFLOOR) &&
     671           0 :                                 CPLTestBool(CPLGetConfigOption(
     672             :                                     "LIBKML_STRICT_COMPLIANCE", "TRUE")))
     673             :                             {
     674           0 :                                 CPLError(CE_Warning, CPLE_NotSupported,
     675             :                                          "altitudeMode!=clampToGround && "
     676             :                                          "altitudeMode!=clampToSeaFloor "
     677             :                                          "unsupported with tessellate=1");
     678             :                             }
     679             :                             else
     680             :                             {
     681             :                                 GeometryPtr poKmlGeometry =
     682           0 :                                     poKmlPlacemark->get_geometry();
     683           0 :                                 ogr2tessellate_rec(CPL_TO_BOOL(iTessellate),
     684             :                                                    poKmlGeometry);
     685           0 :                                 if (!isGX &&
     686             :                                     iAltitudeMode ==
     687             :                                         kmldom::ALTITUDEMODE_CLAMPTOGROUND)
     688           0 :                                     ogr2altitudemode_rec(poKmlGeometry,
     689             :                                                          iAltitudeMode, isGX);
     690             :                             }
     691             :                         }
     692             :                     }
     693             : 
     694           9 :                     continue;
     695             :                 }
     696             : 
     697             :                 /***** visibility *****/
     698          65 :                 if (EQUAL(name, oFC.visibilityfield))
     699             :                 {
     700           9 :                     if (-1 < poOgrFeat->GetFieldAsInteger(i))
     701           0 :                         poKmlFeature->set_visibility(
     702           0 :                             CPL_TO_BOOL(poOgrFeat->GetFieldAsInteger(i)));
     703             : 
     704           9 :                     continue;
     705             :                 }
     706             :                 /***** other special fields *****/
     707          56 :                 else if (EQUAL(name, oFC.drawOrderfield) ||
     708          56 :                          EQUAL(name, oFC.networklink_refreshvisibility_field) ||
     709          55 :                          EQUAL(name, oFC.networklink_flytoview_field) ||
     710          54 :                          EQUAL(name, oFC.networklink_refreshInterval_field) ||
     711          54 :                          EQUAL(name, oFC.networklink_viewRefreshMode_field) ||
     712          54 :                          EQUAL(name, oFC.networklink_viewRefreshTime_field) ||
     713          54 :                          EQUAL(name, oFC.imagepyramid_tilesize_field) ||
     714          53 :                          EQUAL(name, oFC.imagepyramid_maxwidth_field) ||
     715          52 :                          EQUAL(name, oFC.imagepyramid_maxheight_field))
     716             :                 {
     717           5 :                     continue;
     718             :                 }
     719             : 
     720             :                 /***** other *****/
     721             :                 const char *value =
     722          51 :                     poOgrFieldDef->GetSubType() == OFSTBoolean
     723          51 :                         ? (poOgrFeat->GetFieldAsInteger(i) ? "true" : "false")
     724          49 :                         : poOgrFeat->GetFieldAsString(i);
     725          51 :                 if (bUseSimpleField)
     726             :                 {
     727          51 :                     poKmlSimpleData = poKmlFactory->CreateSimpleData();
     728          51 :                     poKmlSimpleData->set_name(name);
     729          51 :                     poKmlSimpleData->set_text(value);
     730             :                 }
     731             :                 else
     732             :                 {
     733           0 :                     poKmlData = poKmlFactory->CreateData();
     734           0 :                     poKmlData->set_name(name);
     735           0 :                     poKmlData->set_value(value);
     736             :                 }
     737             : 
     738          51 :                 break;
     739             :             }
     740             : 
     741         100 :             case OFTReal:  //   Double Precision floating point
     742             :             {
     743         100 :                 if (EQUAL(name, oFC.headingfield) ||
     744          95 :                     EQUAL(name, oFC.tiltfield) || EQUAL(name, oFC.rollfield) ||
     745          87 :                     EQUAL(name, oFC.scalexfield) ||
     746          86 :                     EQUAL(name, oFC.scaleyfield) ||
     747          85 :                     EQUAL(name, oFC.scalezfield) ||
     748          84 :                     EQUAL(name, oFC.networklink_refreshInterval_field) ||
     749          83 :                     EQUAL(name, oFC.networklink_viewRefreshMode_field) ||
     750          83 :                     EQUAL(name, oFC.networklink_viewRefreshTime_field) ||
     751          82 :                     EQUAL(name, oFC.networklink_viewBoundScale_field) ||
     752          81 :                     EQUAL(name, oFC.camera_longitude_field) ||
     753          79 :                     EQUAL(name, oFC.camera_latitude_field) ||
     754          77 :                     EQUAL(name, oFC.camera_altitude_field) ||
     755          75 :                     EQUAL(name, oFC.leftfovfield) ||
     756          73 :                     EQUAL(name, oFC.rightfovfield) ||
     757          71 :                     EQUAL(name, oFC.bottomfovfield) ||
     758          69 :                     EQUAL(name, oFC.topfovfield) ||
     759          67 :                     EQUAL(name, oFC.nearfield) ||
     760          65 :                     EQUAL(name, oFC.camera_altitude_field))
     761             :                 {
     762          35 :                     continue;
     763             :                 }
     764             : 
     765          65 :                 char *pszStr = CPLStrdup(poOgrFeat->GetFieldAsString(i));
     766             : 
     767          65 :                 if (bUseSimpleField)
     768             :                 {
     769          65 :                     poKmlSimpleData = poKmlFactory->CreateSimpleData();
     770          65 :                     poKmlSimpleData->set_name(name);
     771          65 :                     poKmlSimpleData->set_text(pszStr);
     772             :                 }
     773             :                 else
     774             :                 {
     775           0 :                     poKmlData = poKmlFactory->CreateData();
     776           0 :                     poKmlData->set_name(name);
     777           0 :                     poKmlData->set_value(pszStr);
     778             :                 }
     779             : 
     780          65 :                 CPLFree(pszStr);
     781             : 
     782          65 :                 break;
     783             :             }
     784             : 
     785          10 :             case OFTStringList:      //     Array of strings
     786             :             case OFTIntegerList:     //    List of 32bit integers
     787             :             case OFTRealList:        //    List of doubles
     788             :             case OFTBinary:          //     Raw Binary data
     789             :             case OFTWideStringList:  //     deprecated
     790             :             default:
     791             : 
     792             :                 /***** other *****/
     793             : 
     794          10 :                 if (bUseSimpleField)
     795             :                 {
     796          10 :                     poKmlSimpleData = poKmlFactory->CreateSimpleData();
     797          10 :                     poKmlSimpleData->set_name(name);
     798          10 :                     poKmlSimpleData->set_text(poOgrFeat->GetFieldAsString(i));
     799             :                 }
     800             :                 else
     801             :                 {
     802           0 :                     poKmlData = poKmlFactory->CreateData();
     803           0 :                     poKmlData->set_name(name);
     804           0 :                     poKmlData->set_value(poOgrFeat->GetFieldAsString(i));
     805             :                 }
     806             : 
     807          10 :                 break;
     808             :         }
     809             : 
     810         314 :         if (poKmlSimpleData)
     811             :         {
     812         313 :             poKmlSchemaData->add_simpledata(poKmlSimpleData);
     813             :         }
     814           1 :         else if (poKmlData)
     815             :         {
     816           1 :             if (!poKmlExtendedData)
     817           1 :                 poKmlExtendedData = poKmlFactory->CreateExtendedData();
     818             : #if defined(__GNUC__)
     819             : #pragma GCC diagnostic push
     820             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     821             : #endif
     822           1 :             poKmlExtendedData->add_data(poKmlData);
     823             : #if defined(__GNUC__)
     824             : #pragma GCC diagnostic pop
     825             : #endif
     826             :         }
     827             :     }
     828             : 
     829             :     // Do not add it to the placemark unless there is data.
     830         205 :     if (bUseSimpleField && poKmlSchemaData->get_simpledata_array_size() > 0)
     831             :     {
     832          75 :         poKmlExtendedData = poKmlFactory->CreateExtendedData();
     833          75 :         poKmlExtendedData->add_schemadata(poKmlSchemaData);
     834             :     }
     835         205 :     if (poKmlExtendedData)
     836             :     {
     837          76 :         poKmlFeature->set_extendeddata(poKmlExtendedData);
     838             :     }
     839         205 : }
     840             : 
     841             : /******************************************************************************
     842             :  Recursive function to read altitude mode from the geometry.
     843             : ******************************************************************************/
     844             : 
     845        1270 : static bool kml2altitudemode_rec(GeometryPtr poKmlGeometry, int *pnAltitudeMode,
     846             :                                  int *pbIsGX)
     847             : {
     848        1270 :     switch (poKmlGeometry->Type())
     849             :     {
     850         221 :         case kmldom::Type_Point:
     851             :         {
     852         221 :             PointPtr poKmlPoint = AsPoint(poKmlGeometry);
     853             : 
     854         221 :             if (poKmlPoint->has_altitudemode())
     855             :             {
     856          62 :                 *pnAltitudeMode = poKmlPoint->get_altitudemode();
     857          62 :                 return true;
     858             :             }
     859         159 :             else if (poKmlPoint->has_gx_altitudemode())
     860             :             {
     861           0 :                 *pnAltitudeMode = poKmlPoint->get_gx_altitudemode();
     862           0 :                 *pbIsGX = true;
     863           0 :                 return true;
     864             :             }
     865             : 
     866         159 :             break;
     867             :         }
     868         247 :         case kmldom::Type_LineString:
     869             :         {
     870         247 :             LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
     871             : 
     872         247 :             if (poKmlLineString->has_altitudemode())
     873             :             {
     874         132 :                 *pnAltitudeMode = poKmlLineString->get_altitudemode();
     875         132 :                 return true;
     876             :             }
     877         115 :             else if (poKmlLineString->has_gx_altitudemode())
     878             :             {
     879           0 :                 *pnAltitudeMode = poKmlLineString->get_gx_altitudemode();
     880           0 :                 *pbIsGX = true;
     881           0 :                 return true;
     882             :             }
     883         115 :             break;
     884             :         }
     885           3 :         case kmldom::Type_LinearRing:
     886             :         {
     887           3 :             break;
     888             :         }
     889         768 :         case kmldom::Type_Polygon:
     890             :         {
     891         768 :             PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
     892             : 
     893         768 :             if (poKmlPolygon->has_altitudemode())
     894             :             {
     895         308 :                 *pnAltitudeMode = poKmlPolygon->get_altitudemode();
     896         308 :                 return true;
     897             :             }
     898         460 :             else if (poKmlPolygon->has_gx_altitudemode())
     899             :             {
     900           0 :                 *pnAltitudeMode = poKmlPolygon->get_gx_altitudemode();
     901           0 :                 *pbIsGX = true;
     902           0 :                 return true;
     903             :             }
     904             : 
     905         460 :             break;
     906             :         }
     907          28 :         case kmldom::Type_MultiGeometry:
     908             :         {
     909             :             MultiGeometryPtr poKmlMultiGeometry =
     910          28 :                 AsMultiGeometry(poKmlGeometry);
     911             : 
     912          28 :             const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
     913          72 :             for (size_t i = 0; i < nGeom; i++)
     914             :             {
     915          44 :                 if (kml2altitudemode_rec(
     916          44 :                         poKmlMultiGeometry->get_geometry_array_at(i),
     917             :                         pnAltitudeMode, pbIsGX))
     918           0 :                     return true;
     919             :             }
     920             : 
     921          28 :             break;
     922             :         }
     923           3 :         default:
     924             :         {
     925           3 :             break;
     926             :         }
     927             :     }
     928             : 
     929         768 :     return false;
     930             : }
     931             : 
     932             : /******************************************************************************
     933             :  Recursive function to read extrude from the geometry.
     934             : ******************************************************************************/
     935             : 
     936        1270 : static bool kml2extrude_rec(GeometryPtr poKmlGeometry, bool *pbExtrude)
     937             : {
     938        1270 :     switch (poKmlGeometry->Type())
     939             :     {
     940         221 :         case kmldom::Type_Point:
     941             :         {
     942         221 :             PointPtr poKmlPoint = AsPoint(poKmlGeometry);
     943             : 
     944         221 :             if (poKmlPoint->has_extrude())
     945             :             {
     946          33 :                 *pbExtrude = poKmlPoint->get_extrude();
     947          33 :                 return true;
     948             :             }
     949             : 
     950         188 :             break;
     951             :         }
     952         247 :         case kmldom::Type_LineString:
     953             :         {
     954         247 :             LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
     955             : 
     956         247 :             if (poKmlLineString->has_extrude())
     957             :             {
     958          62 :                 *pbExtrude = poKmlLineString->get_extrude();
     959          62 :                 return true;
     960             :             }
     961             : 
     962         185 :             break;
     963             :         }
     964           3 :         case kmldom::Type_LinearRing:
     965             :         {
     966           3 :             break;
     967             :         }
     968         768 :         case kmldom::Type_Polygon:
     969             :         {
     970         768 :             PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
     971             : 
     972         768 :             if (poKmlPolygon->has_extrude())
     973             :             {
     974         293 :                 *pbExtrude = poKmlPolygon->get_extrude();
     975         293 :                 return true;
     976             :             }
     977             : 
     978         475 :             break;
     979             :         }
     980          28 :         case kmldom::Type_MultiGeometry:
     981             :         {
     982             :             MultiGeometryPtr poKmlMultiGeometry =
     983          28 :                 AsMultiGeometry(poKmlGeometry);
     984             : 
     985          28 :             const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
     986          72 :             for (size_t i = 0; i < nGeom; i++)
     987             :             {
     988          44 :                 if (kml2extrude_rec(
     989          44 :                         poKmlMultiGeometry->get_geometry_array_at(i),
     990             :                         pbExtrude))
     991           0 :                     return true;
     992             :             }
     993             : 
     994          28 :             break;
     995             :         }
     996           3 :         default:
     997             :         {
     998           3 :             break;
     999             :         }
    1000             :     }
    1001             : 
    1002         882 :     return false;
    1003             : }
    1004             : 
    1005             : /******************************************************************************
    1006             :  Recursive function to read tessellate from the geometry.
    1007             : ******************************************************************************/
    1008             : 
    1009        1270 : static bool kml2tessellate_rec(GeometryPtr poKmlGeometry, int *pnTessellate)
    1010             : {
    1011        1270 :     switch (poKmlGeometry->Type())
    1012             :     {
    1013         221 :         case kmldom::Type_Point:
    1014             :         {
    1015         221 :             break;
    1016             :         }
    1017         247 :         case kmldom::Type_LineString:
    1018             :         {
    1019         247 :             LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
    1020             : 
    1021         247 :             if (poKmlLineString->has_tessellate())
    1022             :             {
    1023         221 :                 *pnTessellate = poKmlLineString->get_tessellate();
    1024         221 :                 return true;
    1025             :             }
    1026             : 
    1027          26 :             break;
    1028             :         }
    1029           3 :         case kmldom::Type_LinearRing:
    1030             :         {
    1031           3 :             break;
    1032             :         }
    1033         768 :         case kmldom::Type_Polygon:
    1034             :         {
    1035         768 :             PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
    1036             : 
    1037         768 :             if (poKmlPolygon->has_tessellate())
    1038             :             {
    1039         129 :                 *pnTessellate = poKmlPolygon->get_tessellate();
    1040         129 :                 return true;
    1041             :             }
    1042             : 
    1043         639 :             break;
    1044             :         }
    1045          28 :         case kmldom::Type_MultiGeometry:
    1046             :         {
    1047             :             MultiGeometryPtr poKmlMultiGeometry =
    1048          28 :                 AsMultiGeometry(poKmlGeometry);
    1049             : 
    1050          28 :             const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
    1051          72 :             for (size_t i = 0; i < nGeom; i++)
    1052             :             {
    1053          44 :                 if (kml2tessellate_rec(
    1054          44 :                         poKmlMultiGeometry->get_geometry_array_at(i),
    1055             :                         pnTessellate))
    1056           0 :                     return true;
    1057             :             }
    1058             : 
    1059          28 :             break;
    1060             :         }
    1061           3 :         default:
    1062           3 :             break;
    1063             :     }
    1064             : 
    1065         920 :     return false;
    1066             : }
    1067             : 
    1068             : /************************************************************************/
    1069             : /*                     ogrkmlSetAltitudeMode()                          */
    1070             : /************************************************************************/
    1071             : 
    1072         503 : static void ogrkmlSetAltitudeMode(OGRFeature *poOgrFeat, int iField,
    1073             :                                   int nAltitudeMode, bool bIsGX)
    1074             : {
    1075         503 :     if (!bIsGX)
    1076             :     {
    1077         503 :         switch (nAltitudeMode)
    1078             :         {
    1079           0 :             case kmldom::ALTITUDEMODE_CLAMPTOGROUND:
    1080           0 :                 poOgrFeat->SetField(iField, "clampToGround");
    1081           0 :                 break;
    1082             : 
    1083         362 :             case kmldom::ALTITUDEMODE_RELATIVETOGROUND:
    1084         362 :                 poOgrFeat->SetField(iField, "relativeToGround");
    1085         362 :                 break;
    1086             : 
    1087         141 :             case kmldom::ALTITUDEMODE_ABSOLUTE:
    1088         141 :                 poOgrFeat->SetField(iField, "absolute");
    1089         141 :                 break;
    1090             :         }
    1091             :     }
    1092             :     else
    1093             :     {
    1094           0 :         switch (nAltitudeMode)
    1095             :         {
    1096           0 :             case kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR:
    1097           0 :                 poOgrFeat->SetField(iField, "relativeToSeaFloor");
    1098           0 :                 break;
    1099             : 
    1100           0 :             case kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR:
    1101           0 :                 poOgrFeat->SetField(iField, "clampToSeaFloor");
    1102           0 :                 break;
    1103             :         }
    1104             :     }
    1105         503 : }
    1106             : 
    1107             : /************************************************************************/
    1108             : /*                            TrimSpaces()                              */
    1109             : /************************************************************************/
    1110             : 
    1111        1332 : static const char *TrimSpaces(CPLString &oText)
    1112             : {
    1113             :     // SerializePretty() adds a new line before the data
    1114             :     // ands trailing spaces. I believe this is wrong
    1115             :     // as it breaks round-tripping.
    1116             : 
    1117             :     // Trim trailing spaces.
    1118        1332 :     while (!oText.empty() && oText.back() == ' ')
    1119           0 :         oText.pop_back();
    1120             : 
    1121             :     // Skip leading newline and spaces.
    1122        1332 :     const char *pszText = oText.c_str();
    1123        1332 :     if (pszText[0] == '\n')
    1124           0 :         pszText++;
    1125        1332 :     while (pszText[0] == ' ')
    1126           0 :         pszText++;
    1127             : 
    1128        1332 :     return pszText;
    1129             : }
    1130             : 
    1131             : /************************************************************************/
    1132             : /*                            kmldatetime2ogr()                         */
    1133             : /************************************************************************/
    1134             : 
    1135           7 : static void kmldatetime2ogr(OGRFeature *poOgrFeat, const char *pszOGRField,
    1136             :                             const std::string &osKmlDateTime)
    1137             : {
    1138           7 :     const int iField = poOgrFeat->GetFieldIndex(pszOGRField);
    1139             : 
    1140           7 :     if (iField > -1)
    1141             :     {
    1142             :         OGRField sField;
    1143             : 
    1144           7 :         if (OGRParseXMLDateTime(osKmlDateTime.c_str(), &sField))
    1145           7 :             poOgrFeat->SetField(iField, &sField);
    1146             :     }
    1147           7 : }
    1148             : 
    1149             : /******************************************************************************
    1150             :  function to read kml into ogr fields
    1151             : ******************************************************************************/
    1152             : 
    1153        1336 : void kml2field(OGRFeature *poOgrFeat, FeaturePtr poKmlFeature,
    1154             :                const fieldconfig &oFC,
    1155             :                const std::map<std::string, int> &mapSimpleFieldNameToOgrFieldIx)
    1156             : {
    1157             :     /***** id *****/
    1158             : 
    1159        1336 :     if (poKmlFeature->has_id())
    1160             :     {
    1161        1076 :         const std::string oKmlId = poKmlFeature->get_id();
    1162         538 :         int iField = poOgrFeat->GetFieldIndex(oFC.idfield);
    1163             : 
    1164         538 :         if (iField > -1)
    1165         522 :             poOgrFeat->SetField(iField, oKmlId.c_str());
    1166             :     }
    1167             :     /***** name *****/
    1168             : 
    1169        1336 :     if (poKmlFeature->has_name())
    1170             :     {
    1171        1576 :         const std::string oKmlName = poKmlFeature->get_name();
    1172         788 :         int iField = poOgrFeat->GetFieldIndex(oFC.namefield);
    1173             : 
    1174         788 :         if (iField > -1)
    1175         788 :             poOgrFeat->SetField(iField, oKmlName.c_str());
    1176             :     }
    1177             : 
    1178             :     /***** description *****/
    1179             : 
    1180        1336 :     if (poKmlFeature->has_description())
    1181             :     {
    1182         818 :         const std::string oKmlDesc = poKmlFeature->get_description();
    1183         409 :         int iField = poOgrFeat->GetFieldIndex(oFC.descfield);
    1184             : 
    1185         409 :         if (iField > -1)
    1186         409 :             poOgrFeat->SetField(iField, oKmlDesc.c_str());
    1187             :     }
    1188             : 
    1189        1336 :     if (poKmlFeature->has_timeprimitive())
    1190             :     {
    1191           4 :         TimePrimitivePtr poKmlTimePrimitive = poKmlFeature->get_timeprimitive();
    1192             : 
    1193             :         /***** timestamp *****/
    1194             : 
    1195           2 :         if (poKmlTimePrimitive->IsA(kmldom::Type_TimeStamp))
    1196             :         {
    1197             :             // Probably a libkml bug: AsTimeStamp should really return not NULL
    1198             :             // on a gx:TimeStamp.
    1199           2 :             TimeStampPtr poKmlTimeStamp = AsTimeStamp(poKmlTimePrimitive);
    1200           1 :             if (!poKmlTimeStamp)
    1201           1 :                 poKmlTimeStamp = AsGxTimeStamp(poKmlTimePrimitive);
    1202             : 
    1203           1 :             if (poKmlTimeStamp && poKmlTimeStamp->has_when())
    1204             :             {
    1205           2 :                 const std::string oKmlWhen = poKmlTimeStamp->get_when();
    1206           1 :                 kmldatetime2ogr(poOgrFeat, oFC.tsfield, oKmlWhen);
    1207             :             }
    1208             :         }
    1209             : 
    1210             :         /***** timespan *****/
    1211             : 
    1212           2 :         if (poKmlTimePrimitive->IsA(kmldom::Type_TimeSpan))
    1213             :         {
    1214             :             // Probably a libkml bug: AsTimeSpan should really return not NULL
    1215             :             // on a gx:TimeSpan.
    1216           2 :             TimeSpanPtr poKmlTimeSpan = AsTimeSpan(poKmlTimePrimitive);
    1217           1 :             if (!poKmlTimeSpan)
    1218           1 :                 poKmlTimeSpan = AsGxTimeSpan(poKmlTimePrimitive);
    1219             : 
    1220             :             /***** begin *****/
    1221             : 
    1222           1 :             if (poKmlTimeSpan && poKmlTimeSpan->has_begin())
    1223             :             {
    1224           2 :                 const std::string oKmlWhen = poKmlTimeSpan->get_begin();
    1225           1 :                 kmldatetime2ogr(poOgrFeat, oFC.beginfield, oKmlWhen);
    1226             :             }
    1227             : 
    1228             :             /***** end *****/
    1229             : 
    1230           1 :             if (poKmlTimeSpan && poKmlTimeSpan->has_end())
    1231             :             {
    1232           2 :                 const std::string oKmlWhen = poKmlTimeSpan->get_end();
    1233           1 :                 kmldatetime2ogr(poOgrFeat, oFC.endfield, oKmlWhen);
    1234             :             }
    1235             :         }
    1236             :     }
    1237             : 
    1238             :     /***** placemark *****/
    1239        2672 :     PlacemarkPtr poKmlPlacemark = AsPlacemark(poKmlFeature);
    1240        2672 :     GroundOverlayPtr poKmlGroundOverlay = AsGroundOverlay(poKmlFeature);
    1241        1336 :     if (poKmlPlacemark && poKmlPlacemark->has_geometry())
    1242             :     {
    1243        2452 :         GeometryPtr poKmlGeometry = poKmlPlacemark->get_geometry();
    1244             : 
    1245             :         /***** altitudeMode *****/
    1246        1226 :         int bIsGX = false;
    1247        1226 :         int nAltitudeMode = -1;
    1248             : 
    1249        1226 :         int iField = poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
    1250             : 
    1251        1226 :         if (iField > -1)
    1252             :         {
    1253        1226 :             if (kml2altitudemode_rec(poKmlGeometry, &nAltitudeMode, &bIsGX))
    1254             :             {
    1255         502 :                 ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode,
    1256         502 :                                       CPL_TO_BOOL(bIsGX));
    1257             :             }
    1258             :         }
    1259             : 
    1260             :         /***** tessellate *****/
    1261        1226 :         int nTessellate = -1;
    1262             : 
    1263        1226 :         kml2tessellate_rec(poKmlGeometry, &nTessellate);
    1264             : 
    1265        1226 :         iField = poOgrFeat->GetFieldIndex(oFC.tessellatefield);
    1266        1226 :         if (iField > -1)
    1267        1226 :             poOgrFeat->SetField(iField, nTessellate);
    1268             : 
    1269             :         /***** extrude *****/
    1270             : 
    1271        1226 :         bool bExtrude = false;
    1272             : 
    1273        1226 :         kml2extrude_rec(poKmlGeometry, &bExtrude);
    1274             : 
    1275        1226 :         iField = poOgrFeat->GetFieldIndex(oFC.extrudefield);
    1276        1226 :         if (iField > -1)
    1277        1226 :             poOgrFeat->SetField(iField, bExtrude ? 1 : 0);
    1278             : 
    1279             :         /***** special case for gx:Track ******/
    1280             :         /* we set the first timestamp as begin and the last one as end */
    1281        1228 :         if (poKmlGeometry->Type() == kmldom::Type_GxTrack &&
    1282           2 :             !poKmlFeature->has_timeprimitive())
    1283             :         {
    1284           4 :             GxTrackPtr poKmlGxTrack = AsGxTrack(poKmlGeometry);
    1285           2 :             if (poKmlGxTrack)
    1286             :             {
    1287           2 :                 const size_t nCoords = poKmlGxTrack->get_when_array_size();
    1288           2 :                 if (nCoords > 0)
    1289             :                 {
    1290           2 :                     kmldatetime2ogr(poOgrFeat, oFC.beginfield,
    1291           1 :                                     poKmlGxTrack->get_when_array_at(0).c_str());
    1292           3 :                     kmldatetime2ogr(
    1293           1 :                         poOgrFeat, oFC.endfield,
    1294           1 :                         poKmlGxTrack->get_when_array_at(nCoords - 1).c_str());
    1295             :                 }
    1296             :             }
    1297             :         }
    1298             : 
    1299             :         /***** special case for gx:MultiTrack ******/
    1300             :         /* we set the first timestamp as begin and the last one as end */
    1301        1225 :         else if (poKmlGeometry->Type() == kmldom::Type_GxMultiTrack &&
    1302           1 :                  !poKmlFeature->has_timeprimitive())
    1303             :         {
    1304           2 :             GxMultiTrackPtr poKmlGxMultiTrack = AsGxMultiTrack(poKmlGeometry);
    1305           1 :             if (poKmlGxMultiTrack)
    1306             :             {
    1307             :                 const size_t nGeom =
    1308           1 :                     poKmlGxMultiTrack->get_gx_track_array_size();
    1309           1 :                 if (nGeom >= 1)
    1310             :                 {
    1311             :                     {
    1312             :                         GxTrackPtr poKmlGxTrack =
    1313           2 :                             poKmlGxMultiTrack->get_gx_track_array_at(0);
    1314             :                         const size_t nCoords =
    1315           1 :                             poKmlGxTrack->get_when_array_size();
    1316           1 :                         if (nCoords > 0)
    1317             :                         {
    1318           3 :                             kmldatetime2ogr(
    1319           1 :                                 poOgrFeat, oFC.beginfield,
    1320           1 :                                 poKmlGxTrack->get_when_array_at(0).c_str());
    1321             :                         }
    1322             :                     }
    1323             : 
    1324             :                     {
    1325             :                         GxTrackPtr poKmlGxTrack =
    1326           2 :                             poKmlGxMultiTrack->get_gx_track_array_at(nGeom - 1);
    1327             :                         const size_t nCoords =
    1328           1 :                             poKmlGxTrack->get_when_array_size();
    1329           1 :                         if (nCoords > 0)
    1330             :                         {
    1331           3 :                             kmldatetime2ogr(
    1332           1 :                                 poOgrFeat, oFC.endfield,
    1333           1 :                                 poKmlGxTrack->get_when_array_at(nCoords - 1)
    1334             :                                     .c_str());
    1335             :                         }
    1336             :                     }
    1337             :                 }
    1338             :             }
    1339             :         }
    1340             :     }
    1341             : 
    1342             :     /***** camera *****/
    1343             : 
    1344         112 :     else if (poKmlPlacemark && poKmlPlacemark->has_abstractview() &&
    1345           2 :              poKmlPlacemark->get_abstractview()->IsA(kmldom::Type_Camera))
    1346             :     {
    1347           4 :         const CameraPtr &camera = AsCamera(poKmlPlacemark->get_abstractview());
    1348             : 
    1349           2 :         if (camera->has_heading())
    1350             :         {
    1351           2 :             int iField = poOgrFeat->GetFieldIndex(oFC.headingfield);
    1352           2 :             if (iField > -1)
    1353           2 :                 poOgrFeat->SetField(iField, camera->get_heading());
    1354             :         }
    1355             : 
    1356           2 :         if (camera->has_tilt())
    1357             :         {
    1358           1 :             int iField = poOgrFeat->GetFieldIndex(oFC.tiltfield);
    1359           1 :             if (iField > -1)
    1360           1 :                 poOgrFeat->SetField(iField, camera->get_tilt());
    1361             :         }
    1362             : 
    1363           2 :         if (camera->has_roll())
    1364             :         {
    1365           1 :             int iField = poOgrFeat->GetFieldIndex(oFC.rollfield);
    1366           1 :             if (iField > -1)
    1367           1 :                 poOgrFeat->SetField(iField, camera->get_roll());
    1368             :         }
    1369             : 
    1370           2 :         int iField = poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
    1371             : 
    1372           2 :         if (iField > -1)
    1373             :         {
    1374           2 :             if (camera->has_altitudemode())
    1375             :             {
    1376           1 :                 const int nAltitudeMode = camera->get_altitudemode();
    1377           1 :                 ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, false);
    1378             :             }
    1379           1 :             else if (camera->has_gx_altitudemode())
    1380             :             {
    1381           0 :                 const int nAltitudeMode = camera->get_gx_altitudemode();
    1382           0 :                 ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, true);
    1383             :             }
    1384             :         }
    1385             :     }
    1386             :     /***** ground overlay *****/
    1387         108 :     else if (poKmlGroundOverlay)
    1388             :     {
    1389             :         /***** icon *****/
    1390          44 :         int iField = poOgrFeat->GetFieldIndex(oFC.iconfield);
    1391          44 :         if (iField > -1)
    1392             :         {
    1393          44 :             if (poKmlGroundOverlay->has_icon())
    1394             :             {
    1395          88 :                 IconPtr icon = poKmlGroundOverlay->get_icon();
    1396          44 :                 if (icon->has_href())
    1397             :                 {
    1398          44 :                     poOgrFeat->SetField(iField, icon->get_href().c_str());
    1399             :                 }
    1400             :             }
    1401             :         }
    1402             : 
    1403             :         /***** drawOrder *****/
    1404          44 :         iField = poOgrFeat->GetFieldIndex(oFC.drawOrderfield);
    1405          44 :         if (iField > -1)
    1406             :         {
    1407          44 :             if (poKmlGroundOverlay->has_draworder())
    1408             :             {
    1409           0 :                 poOgrFeat->SetField(iField,
    1410           0 :                                     poKmlGroundOverlay->get_draworder());
    1411             :             }
    1412             :         }
    1413             : 
    1414             :         /***** altitudeMode *****/
    1415             : 
    1416          44 :         iField = poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
    1417             : 
    1418          44 :         if (iField > -1)
    1419             :         {
    1420          44 :             if (poKmlGroundOverlay->has_altitudemode())
    1421             :             {
    1422           0 :                 switch (poKmlGroundOverlay->get_altitudemode())
    1423             :                 {
    1424           0 :                     case kmldom::ALTITUDEMODE_CLAMPTOGROUND:
    1425           0 :                         poOgrFeat->SetField(iField, "clampToGround");
    1426           0 :                         break;
    1427             : 
    1428           0 :                     case kmldom::ALTITUDEMODE_RELATIVETOGROUND:
    1429           0 :                         poOgrFeat->SetField(iField, "relativeToGround");
    1430           0 :                         break;
    1431             : 
    1432           0 :                     case kmldom::ALTITUDEMODE_ABSOLUTE:
    1433           0 :                         poOgrFeat->SetField(iField, "absolute");
    1434           0 :                         break;
    1435             :                 }
    1436             :             }
    1437          44 :             else if (poKmlGroundOverlay->has_gx_altitudemode())
    1438             :             {
    1439           0 :                 switch (poKmlGroundOverlay->get_gx_altitudemode())
    1440             :                 {
    1441           0 :                     case kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR:
    1442           0 :                         poOgrFeat->SetField(iField, "relativeToSeaFloor");
    1443           0 :                         break;
    1444             : 
    1445           0 :                     case kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR:
    1446           0 :                         poOgrFeat->SetField(iField, "clampToSeaFloor");
    1447           0 :                         break;
    1448             :                 }
    1449             :             }
    1450             :         }
    1451             :     }
    1452             : 
    1453             :     /***** visibility *****/
    1454             :     const int nVisibility =
    1455        1336 :         poKmlFeature->has_visibility() ? poKmlFeature->get_visibility() : -1;
    1456             : 
    1457        1336 :     int iField = poOgrFeat->GetFieldIndex(oFC.visibilityfield);
    1458             : 
    1459        1336 :     if (iField > -1)
    1460        1320 :         poOgrFeat->SetField(iField, nVisibility);
    1461             : 
    1462             :     /***** snippet *****/
    1463             : 
    1464        1336 :     if (poKmlFeature->has_snippet())
    1465             :     {
    1466           2 :         CPLString oText = poKmlFeature->get_snippet()->get_text();
    1467             : 
    1468           1 :         iField = poOgrFeat->GetFieldIndex(oFC.snippetfield);
    1469             : 
    1470           1 :         if (iField > -1)
    1471           1 :             poOgrFeat->SetField(iField, TrimSpaces(oText));
    1472             :     }
    1473             : 
    1474             :     /***** extended schema *****/
    1475        2672 :     ExtendedDataPtr poKmlExtendedData = nullptr;
    1476             : 
    1477        1336 :     if (poKmlFeature->has_extendeddata())
    1478             :     {
    1479         453 :         poKmlExtendedData = poKmlFeature->get_extendeddata();
    1480             : 
    1481             :         /***** loop over the schemadata_arrays *****/
    1482             : 
    1483             :         const size_t nSchemaData =
    1484         453 :             poKmlExtendedData->get_schemadata_array_size();
    1485             : 
    1486         903 :         for (size_t iSchemaData = 0; iSchemaData < nSchemaData; iSchemaData++)
    1487             :         {
    1488             :             SchemaDataPtr poKmlSchemaData =
    1489         900 :                 poKmlExtendedData->get_schemadata_array_at(iSchemaData);
    1490             : 
    1491             :             /***** loop over the simpledata array *****/
    1492             : 
    1493             :             const size_t nSimpleData =
    1494         450 :                 poKmlSchemaData->get_simpledata_array_size();
    1495             : 
    1496        1782 :             for (size_t iSimpleData = 0; iSimpleData < nSimpleData;
    1497             :                  iSimpleData++)
    1498             :             {
    1499             :                 SimpleDataPtr poKmlSimpleData =
    1500        2664 :                     poKmlSchemaData->get_simpledata_array_at(iSimpleData);
    1501             : 
    1502             :                 /***** find the field index *****/
    1503             : 
    1504        1332 :                 iField = -1;
    1505             : 
    1506        1332 :                 if (poKmlSimpleData->has_name())
    1507             :                 {
    1508        2664 :                     const string oName = poKmlSimpleData->get_name();
    1509             :                     const auto oIter =
    1510        1332 :                         mapSimpleFieldNameToOgrFieldIx.find(oName);
    1511        1332 :                     if (oIter != mapSimpleFieldNameToOgrFieldIx.end())
    1512        1331 :                         iField = oIter->second;
    1513             :                 }
    1514             : 
    1515             :                 /***** if it has trxt set the field *****/
    1516             : 
    1517        1332 :                 if (iField > -1 && poKmlSimpleData->has_text())
    1518             :                 {
    1519        2662 :                     CPLString oText = poKmlSimpleData->get_text();
    1520             : 
    1521        1331 :                     poOgrFeat->SetField(iField, TrimSpaces(oText));
    1522             :                 }
    1523             :             }
    1524             :         }
    1525             : 
    1526         453 :         if (nSchemaData == 0 && poKmlExtendedData->get_data_array_size() > 0)
    1527             :         {
    1528           3 :             const bool bLaunderFieldNames = CPLTestBool(
    1529             :                 CPLGetConfigOption("LIBKML_LAUNDER_FIELD_NAMES", "YES"));
    1530             :             const size_t nDataArraySize =
    1531           3 :                 poKmlExtendedData->get_data_array_size();
    1532           8 :             for (size_t i = 0; i < nDataArraySize; i++)
    1533             :             {
    1534           5 :                 const DataPtr &data = poKmlExtendedData->get_data_array_at(i);
    1535           5 :                 if (data->has_name() && data->has_value())
    1536             :                 {
    1537          10 :                     CPLString osName = std::string(data->get_name());
    1538           5 :                     if (bLaunderFieldNames)
    1539           5 :                         osName = OGRLIBKMLLayer::LaunderFieldNames(osName);
    1540           5 :                     iField = poOgrFeat->GetFieldIndex(osName);
    1541           5 :                     if (iField >= 0)
    1542             :                     {
    1543           5 :                         poOgrFeat->SetField(iField, data->get_value().c_str());
    1544             :                     }
    1545             :                 }
    1546             :             }
    1547             :         }
    1548             :     }
    1549        1336 : }
    1550             : 
    1551             : /******************************************************************************
    1552             :  function create a simplefield from a FieldDefn
    1553             : ******************************************************************************/
    1554             : 
    1555         179 : SimpleFieldPtr FieldDef2kml(const OGRFieldDefn *poOgrFieldDef,
    1556             :                             KmlFactory *poKmlFactory, bool bApproxOK,
    1557             :                             const fieldconfig &oFC)
    1558             : {
    1559         179 :     const char *pszFieldName = poOgrFieldDef->GetNameRef();
    1560             : 
    1561         179 :     if (EQUAL(pszFieldName, oFC.idfield) ||
    1562         176 :         EQUAL(pszFieldName, oFC.namefield) ||
    1563         165 :         EQUAL(pszFieldName, oFC.descfield) ||
    1564         159 :         EQUAL(pszFieldName, oFC.tsfield) ||
    1565         159 :         EQUAL(pszFieldName, oFC.beginfield) ||
    1566         159 :         EQUAL(pszFieldName, oFC.endfield) ||
    1567         159 :         EQUAL(pszFieldName, oFC.altitudeModefield) ||
    1568         157 :         EQUAL(pszFieldName, oFC.tessellatefield) ||
    1569         157 :         EQUAL(pszFieldName, oFC.extrudefield) ||
    1570         157 :         EQUAL(pszFieldName, oFC.visibilityfield) ||
    1571         157 :         EQUAL(pszFieldName, oFC.drawOrderfield) ||
    1572         157 :         EQUAL(pszFieldName, oFC.iconfield) ||
    1573         157 :         EQUAL(pszFieldName, oFC.headingfield) ||
    1574         154 :         EQUAL(pszFieldName, oFC.tiltfield) ||
    1575         151 :         EQUAL(pszFieldName, oFC.rollfield) ||
    1576         148 :         EQUAL(pszFieldName, oFC.snippetfield) ||
    1577         147 :         EQUAL(pszFieldName, oFC.modelfield) ||
    1578         146 :         EQUAL(pszFieldName, oFC.scalexfield) ||
    1579         145 :         EQUAL(pszFieldName, oFC.scaleyfield) ||
    1580         144 :         EQUAL(pszFieldName, oFC.scalezfield) ||
    1581         143 :         EQUAL(pszFieldName, oFC.networklinkfield) ||
    1582         142 :         EQUAL(pszFieldName, oFC.networklink_refreshvisibility_field) ||
    1583         141 :         EQUAL(pszFieldName, oFC.networklink_flytoview_field) ||
    1584         140 :         EQUAL(pszFieldName, oFC.networklink_refreshMode_field) ||
    1585         139 :         EQUAL(pszFieldName, oFC.networklink_refreshInterval_field) ||
    1586         138 :         EQUAL(pszFieldName, oFC.networklink_viewRefreshMode_field) ||
    1587         137 :         EQUAL(pszFieldName, oFC.networklink_viewRefreshTime_field) ||
    1588         136 :         EQUAL(pszFieldName, oFC.networklink_viewBoundScale_field) ||
    1589         135 :         EQUAL(pszFieldName, oFC.networklink_viewFormat_field) ||
    1590         134 :         EQUAL(pszFieldName, oFC.networklink_httpQuery_field) ||
    1591         133 :         EQUAL(pszFieldName, oFC.camera_longitude_field) ||
    1592         132 :         EQUAL(pszFieldName, oFC.camera_latitude_field) ||
    1593         131 :         EQUAL(pszFieldName, oFC.camera_altitude_field) ||
    1594         130 :         EQUAL(pszFieldName, oFC.camera_altitudemode_field) ||
    1595         129 :         EQUAL(pszFieldName, oFC.photooverlayfield) ||
    1596         128 :         EQUAL(pszFieldName, oFC.leftfovfield) ||
    1597         127 :         EQUAL(pszFieldName, oFC.rightfovfield) ||
    1598         126 :         EQUAL(pszFieldName, oFC.bottomfovfield) ||
    1599         125 :         EQUAL(pszFieldName, oFC.topfovfield) ||
    1600         124 :         EQUAL(pszFieldName, oFC.nearfield) ||
    1601         123 :         EQUAL(pszFieldName, oFC.photooverlay_shape_field) ||
    1602         122 :         EQUAL(pszFieldName, oFC.imagepyramid_tilesize_field) ||
    1603         121 :         EQUAL(pszFieldName, oFC.imagepyramid_maxwidth_field) ||
    1604         120 :         EQUAL(pszFieldName, oFC.imagepyramid_maxheight_field) ||
    1605         119 :         EQUAL(pszFieldName, oFC.imagepyramid_gridorigin_field))
    1606             :     {
    1607          60 :         return nullptr;
    1608             :     }
    1609             : 
    1610         238 :     SimpleFieldPtr poKmlSimpleField = poKmlFactory->CreateSimpleField();
    1611         119 :     poKmlSimpleField->set_name(pszFieldName);
    1612             : 
    1613         119 :     switch (poOgrFieldDef->GetType())
    1614             :     {
    1615          17 :         case OFTInteger:
    1616             :         case OFTIntegerList:
    1617          34 :             poKmlSimpleField->set_type(
    1618          17 :                 poOgrFieldDef->GetSubType() == OFSTBoolean ? "bool" : "int");
    1619          17 :             return poKmlSimpleField;
    1620             : 
    1621          17 :         case OFTReal:
    1622             :         case OFTRealList:
    1623          34 :             poKmlSimpleField->set_type(
    1624          17 :                 poOgrFieldDef->GetSubType() == OFSTFloat32 ? "float"
    1625             :                                                            : "double");
    1626          17 :             return poKmlSimpleField;
    1627             : 
    1628          40 :         case OFTString:
    1629             :         case OFTStringList:
    1630          40 :             poKmlSimpleField->set_type("string");
    1631          40 :             return poKmlSimpleField;
    1632             : 
    1633           1 :         case OFTInteger64:
    1634           1 :             if (bApproxOK)
    1635             :             {
    1636           1 :                 poKmlSimpleField->set_type("string");
    1637           1 :                 return poKmlSimpleField;
    1638             :             }
    1639           0 :             break;
    1640             : 
    1641             :             /***** kml has these types but as timestamp/timespan *****/
    1642          44 :         case OFTDate:
    1643             :         case OFTTime:
    1644             :         case OFTDateTime:
    1645          44 :             if (bApproxOK)
    1646             :             {
    1647          44 :                 poKmlSimpleField->set_type("string");
    1648          44 :                 return poKmlSimpleField;
    1649             :             }
    1650           0 :             break;
    1651             : 
    1652           0 :         default:
    1653           0 :             poKmlSimpleField->set_type("string");
    1654           0 :             return poKmlSimpleField;
    1655             :     }
    1656             : 
    1657           0 :     return nullptr;
    1658             : }
    1659             : 
    1660             : /******************************************************************************
    1661             :  function to add the simpleFields in a schema to a featuredefn
    1662             : ******************************************************************************/
    1663             : 
    1664          36 : void kml2FeatureDef(SchemaPtr poKmlSchema, OGRFeatureDefn *poOgrFeatureDefn,
    1665             :                     std::map<std::string, int> &mapSimpleFieldNameToOgrFieldIx,
    1666             :                     std::set<std::string> &oSetSimpleFields)
    1667             : {
    1668          36 :     const size_t nSimpleFields = poKmlSchema->get_simplefield_array_size();
    1669             : 
    1670         158 :     for (size_t iSimpleField = 0; iSimpleField < nSimpleFields; iSimpleField++)
    1671             :     {
    1672             :         SimpleFieldPtr poKmlSimpleField =
    1673         122 :             poKmlSchema->get_simplefield_array_at(iSimpleField);
    1674             : 
    1675         122 :         const char *pszType = "string";
    1676         122 :         string osType;
    1677             : 
    1678         122 :         if (poKmlSimpleField->has_type())
    1679             :         {
    1680         122 :             osType = poKmlSimpleField->get_type();
    1681             : 
    1682         122 :             pszType = osType.c_str();
    1683             :         }
    1684             : 
    1685         122 :         std::string osName;
    1686         122 :         if (poKmlSimpleField->has_name())
    1687             :         {
    1688         122 :             osName = poKmlSimpleField->get_name();
    1689             :         }
    1690             :         else
    1691           0 :             continue;
    1692             : 
    1693         122 :         int counter = 2;
    1694         122 :         const std::string osNameRadix = osName;
    1695         122 :         const CPLString osNameLower = CPLString(osName).tolower();
    1696         122 :         if (cpl::contains(oSetSimpleFields, osNameLower))
    1697           4 :             continue;
    1698         118 :         oSetSimpleFields.insert(osNameLower);
    1699             : 
    1700         118 :         mapSimpleFieldNameToOgrFieldIx[osNameRadix] =
    1701         118 :             poOgrFeatureDefn->GetFieldCount();
    1702             : 
    1703         120 :         while (poOgrFeatureDefn->GetFieldIndex(osName.c_str()) >= 0)
    1704             :         {
    1705           2 :             osName = osNameRadix + std::to_string(counter);
    1706           2 :             ++counter;
    1707             :         }
    1708             : 
    1709         118 :         if (EQUAL(pszType, "bool") || EQUAL(pszType, "boolean"))
    1710             :         {
    1711           2 :             OGRFieldDefn ogrFieldDefn(osName.c_str(), OFTInteger);
    1712           1 :             ogrFieldDefn.SetSubType(OFSTBoolean);
    1713           2 :             poOgrFeatureDefn->AddFieldDefn(&ogrFieldDefn);
    1714             :         }
    1715         117 :         else if (EQUAL(pszType, "int") || EQUAL(pszType, "short") ||
    1716         100 :                  EQUAL(pszType, "ushort"))
    1717             :         {
    1718          34 :             OGRFieldDefn ogrFieldDefn(osName.c_str(), OFTInteger);
    1719          34 :             poOgrFeatureDefn->AddFieldDefn(&ogrFieldDefn);
    1720             :         }
    1721         100 :         else if (EQUAL(pszType, "uint"))
    1722             :         {
    1723           0 :             OGRFieldDefn ogrFieldDefn(osName.c_str(), OFTInteger64);
    1724           0 :             poOgrFeatureDefn->AddFieldDefn(&ogrFieldDefn);
    1725             :         }
    1726         100 :         else if (EQUAL(pszType, "float") || EQUAL(pszType, "double"))
    1727             :         {
    1728             :             // We write correctly 'double' for 64-bit since GDAL 3.11.1.
    1729             :             // In prior versions we wrote 'float', so it is premature
    1730             :             // on reading to set OFSTFloat32 when reading 'float'
    1731          42 :             OGRFieldDefn ogrFieldDefn(osName.c_str(), OFTReal);
    1732          42 :             poOgrFeatureDefn->AddFieldDefn(&ogrFieldDefn);
    1733             :         }
    1734             :         else  // string, or any other unrecognized type.
    1735             :         {
    1736         158 :             OGRFieldDefn ogrFieldDefn(osName.c_str(), OFTString);
    1737          79 :             poOgrFeatureDefn->AddFieldDefn(&ogrFieldDefn);
    1738             :         }
    1739             :     }
    1740          36 : }
    1741             : 
    1742             : /*******************************************************************************
    1743             :  * function to fetch the field config options
    1744             :  *
    1745             :  *******************************************************************************/
    1746             : 
    1747         367 : void get_fieldconfig(struct fieldconfig *oFC)
    1748             : {
    1749         367 :     oFC->idfield = CPLGetConfigOption("LIBKML_ID_FIELD", "id");
    1750         367 :     oFC->namefield = CPLGetConfigOption("LIBKML_NAME_FIELD", "Name");
    1751         367 :     oFC->descfield =
    1752         367 :         CPLGetConfigOption("LIBKML_DESCRIPTION_FIELD", "description");
    1753         367 :     oFC->tsfield = CPLGetConfigOption("LIBKML_TIMESTAMP_FIELD", "timestamp");
    1754         367 :     oFC->beginfield = CPLGetConfigOption("LIBKML_BEGIN_FIELD", "begin");
    1755         367 :     oFC->endfield = CPLGetConfigOption("LIBKML_END_FIELD", "end");
    1756         367 :     oFC->altitudeModefield =
    1757         367 :         CPLGetConfigOption("LIBKML_ALTITUDEMODE_FIELD", "altitudeMode");
    1758         367 :     oFC->tessellatefield =
    1759         367 :         CPLGetConfigOption("LIBKML_TESSELLATE_FIELD", "tessellate");
    1760         367 :     oFC->extrudefield = CPLGetConfigOption("LIBKML_EXTRUDE_FIELD", "extrude");
    1761         367 :     oFC->visibilityfield =
    1762         367 :         CPLGetConfigOption("LIBKML_VISIBILITY_FIELD", "visibility");
    1763         367 :     oFC->drawOrderfield =
    1764         367 :         CPLGetConfigOption("LIBKML_DRAWORDER_FIELD", "drawOrder");
    1765         367 :     oFC->iconfield = CPLGetConfigOption("LIBKML_ICON_FIELD", "icon");
    1766         367 :     oFC->headingfield = CPLGetConfigOption("LIBKML_HEADING_FIELD", "heading");
    1767         367 :     oFC->tiltfield = CPLGetConfigOption("LIBKML_TILT_FIELD", "tilt");
    1768         367 :     oFC->rollfield = CPLGetConfigOption("LIBKML_ROLL_FIELD", "roll");
    1769         367 :     oFC->snippetfield = CPLGetConfigOption("LIBKML_SNIPPET_FIELD", "snippet");
    1770         367 :     oFC->modelfield = CPLGetConfigOption("LIBKML_MODEL_FIELD", "model");
    1771         367 :     oFC->scalexfield = CPLGetConfigOption("LIBKML_SCALE_X_FIELD", "scale_x");
    1772         367 :     oFC->scaleyfield = CPLGetConfigOption("LIBKML_SCALE_Y_FIELD", "scale_y");
    1773         367 :     oFC->scalezfield = CPLGetConfigOption("LIBKML_SCALE_Z_FIELD", "scale_z");
    1774         367 :     oFC->networklinkfield =
    1775         367 :         CPLGetConfigOption("LIBKML_NETWORKLINK_FIELD", "networklink");
    1776         367 :     oFC->networklink_refreshvisibility_field =
    1777         367 :         CPLGetConfigOption("LIBKML_NETWORKLINK_REFRESHVISIBILITY_FIELD",
    1778             :                            "networklink_refreshvisibility");
    1779         367 :     oFC->networklink_flytoview_field = CPLGetConfigOption(
    1780             :         "LIBKML_NETWORKLINK_FLYTOVIEW_FIELD", "networklink_flytoview");
    1781         367 :     oFC->networklink_refreshMode_field = CPLGetConfigOption(
    1782             :         "LIBKML_NETWORKLINK_REFRESHMODE_FIELD", "networklink_refreshmode");
    1783         367 :     oFC->networklink_refreshInterval_field =
    1784         367 :         CPLGetConfigOption("LIBKML_NETWORKLINK_REFRESHINTERVAL_FIELD",
    1785             :                            "networklink_refreshinterval");
    1786         367 :     oFC->networklink_viewRefreshMode_field =
    1787         367 :         CPLGetConfigOption("LIBKML_NETWORKLINK_VIEWREFRESHMODE_FIELD",
    1788             :                            "networklink_viewrefreshmode");
    1789         367 :     oFC->networklink_viewRefreshTime_field =
    1790         367 :         CPLGetConfigOption("LIBKML_NETWORKLINK_VIEWREFRESHTIME_FIELD",
    1791             :                            "networklink_viewrefreshtime");
    1792         367 :     oFC->networklink_viewBoundScale_field =
    1793         367 :         CPLGetConfigOption("LIBKML_NETWORKLINK_VIEWBOUNDSCALE_FIELD",
    1794             :                            "networklink_viewboundscale");
    1795         367 :     oFC->networklink_viewFormat_field = CPLGetConfigOption(
    1796             :         "LIBKML_NETWORKLINK_VIEWFORMAT_FIELD", "networklink_viewformat");
    1797         367 :     oFC->networklink_httpQuery_field = CPLGetConfigOption(
    1798             :         "LIBKML_NETWORKLINK_HTTPQUERY_FIELD", "networklink_httpquery");
    1799         367 :     oFC->camera_longitude_field =
    1800         367 :         CPLGetConfigOption("LIBKML_CAMERA_LONGITUDE_FIELD", "camera_longitude");
    1801         367 :     oFC->camera_latitude_field =
    1802         367 :         CPLGetConfigOption("LIBKML_CAMERA_LATITUDE_FIELD", "camera_latitude");
    1803         367 :     oFC->camera_altitude_field =
    1804         367 :         CPLGetConfigOption("LIBKML_CAMERA_ALTITUDE_FIELD", "camera_altitude");
    1805         367 :     oFC->camera_altitudemode_field = CPLGetConfigOption(
    1806             :         "LIBKML_CAMERA_ALTITUDEMODE_FIELD", "camera_altitudemode");
    1807         367 :     oFC->photooverlayfield =
    1808         367 :         CPLGetConfigOption("LIBKML_PHOTOOVERLAY_FIELD", "photooverlay");
    1809         367 :     oFC->leftfovfield = CPLGetConfigOption("LIBKML_LEFTFOV_FIELD", "leftfov");
    1810         367 :     oFC->rightfovfield =
    1811         367 :         CPLGetConfigOption("LIBKML_RIGHTFOV_FIELD", "rightfov");
    1812         367 :     oFC->bottomfovfield =
    1813         367 :         CPLGetConfigOption("LIBKML_BOTTOMFOV_FIELD", "bottomfov");
    1814         367 :     oFC->topfovfield = CPLGetConfigOption("LIBKML_TOPFOV_FIELD", "topfov");
    1815         367 :     oFC->nearfield = CPLGetConfigOption("LIBKML_NEARFOV_FIELD", "near");
    1816         367 :     oFC->photooverlay_shape_field = CPLGetConfigOption(
    1817             :         "LIBKML_PHOTOOVERLAY_SHAPE_FIELD", "photooverlay_shape");
    1818         367 :     oFC->imagepyramid_tilesize_field = CPLGetConfigOption(
    1819             :         "LIBKML_IMAGEPYRAMID_TILESIZE", "imagepyramid_tilesize");
    1820         367 :     oFC->imagepyramid_maxwidth_field = CPLGetConfigOption(
    1821             :         "LIBKML_IMAGEPYRAMID_MAXWIDTH", "imagepyramid_maxwidth");
    1822         367 :     oFC->imagepyramid_maxheight_field = CPLGetConfigOption(
    1823             :         "LIBKML_IMAGEPYRAMID_MAXHEIGHT", "imagepyramid_maxheight");
    1824         367 :     oFC->imagepyramid_gridorigin_field = CPLGetConfigOption(
    1825             :         "LIBKML_IMAGEPYRAMID_GRIDORIGIN", "imagepyramid_gridorigin");
    1826         367 : }
    1827             : 
    1828             : /************************************************************************/
    1829             : /*                 kmlAltitudeModeFromString()                          */
    1830             : /************************************************************************/
    1831             : 
    1832           8 : int kmlAltitudeModeFromString(const char *pszAltitudeMode, int &isGX)
    1833             : {
    1834           8 :     isGX = FALSE;
    1835           8 :     int iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_CLAMPTOGROUND);
    1836             : 
    1837           8 :     if (EQUAL(pszAltitudeMode, "clampToGround"))
    1838             :     {
    1839           0 :         iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_CLAMPTOGROUND);
    1840             :     }
    1841           8 :     else if (EQUAL(pszAltitudeMode, "relativeToGround"))
    1842             :     {
    1843           8 :         iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_RELATIVETOGROUND);
    1844             :     }
    1845           0 :     else if (EQUAL(pszAltitudeMode, "absolute"))
    1846             :     {
    1847           0 :         iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_ABSOLUTE);
    1848             :     }
    1849           0 :     else if (EQUAL(pszAltitudeMode, "relativeToSeaFloor"))
    1850             :     {
    1851           0 :         iAltitudeMode =
    1852             :             static_cast<int>(kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR);
    1853           0 :         isGX = TRUE;
    1854             :     }
    1855           0 :     else if (EQUAL(pszAltitudeMode, "clampToSeaFloor"))
    1856             :     {
    1857           0 :         iAltitudeMode =
    1858             :             static_cast<int>(kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR);
    1859           0 :         isGX = TRUE;
    1860             :     }
    1861             :     else
    1862             :     {
    1863           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    1864             :                  "Unrecognized value for altitudeMode: %s", pszAltitudeMode);
    1865             :     }
    1866             : 
    1867           8 :     return iAltitudeMode;
    1868             : }

Generated by: LCOV version 1.14