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

Generated by: LCOV version 1.14