LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/libkml - ogrlibkmlfield.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 645 874 73.8 %
Date: 2024-11-21 22:18:42 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         123 : static char *OGRLIBKMLSanitizeUTF8String(const char *pszString)
     209             : {
     210         123 :     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         123 :     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         202 : void field2kml(OGRFeature *poOgrFeat, OGRLIBKMLLayer *poOgrLayer,
     259             :                KmlFactory *poKmlFactory, FeaturePtr poKmlFeature,
     260             :                int bUseSimpleFieldIn, const fieldconfig &oFC)
     261             : {
     262         202 :     const bool bUseSimpleField = CPL_TO_BOOL(bUseSimpleFieldIn);
     263         404 :     SchemaDataPtr poKmlSchemaData = nullptr;
     264         202 :     if (bUseSimpleField)
     265             :     {
     266         201 :         poKmlSchemaData = poKmlFactory->CreateSchemaData();
     267         402 :         SchemaPtr poKmlSchema = poOgrLayer->GetKmlSchema();
     268             : 
     269             :         /***** set the url to the schema *****/
     270         201 :         if (poKmlSchema && poKmlSchema->has_id())
     271             :         {
     272         292 :             std::string oKmlSchemaID = poKmlSchema->get_id();
     273         292 :             std::string oKmlSchemaURL = "#";
     274         146 :             oKmlSchemaURL.append(oKmlSchemaID);
     275             : 
     276         146 :             poKmlSchemaData->set_schemaurl(oKmlSchemaURL);
     277             :         }
     278             :     }
     279             : 
     280         404 :     TimeSpanPtr poKmlTimeSpan = nullptr;
     281             : 
     282         202 :     const int nFields = poOgrFeat->GetFieldCount();
     283         202 :     int iSkip1 = -1;
     284         202 :     int iSkip2 = -1;
     285         202 :     int iAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND;
     286         202 :     int isGX = false;
     287             : 
     288         404 :     ExtendedDataPtr poKmlExtendedData = nullptr;
     289             : 
     290        1056 :     for (int i = 0; i < nFields; i++)
     291             :     {
     292             :         /***** If the field is set to skip, do so *****/
     293         854 :         if (i == iSkip1 || i == iSkip2)
     294         543 :             continue;
     295             : 
     296             :         /***** If the field isn't set just bail now *****/
     297         854 :         if (!poOgrFeat->IsFieldSetAndNotNull(i))
     298         426 :             continue;
     299             : 
     300         428 :         OGRFieldDefn *poOgrFieldDef = poOgrFeat->GetFieldDefnRef(i);
     301         428 :         const OGRFieldType type = poOgrFieldDef->GetType();
     302         428 :         const char *name = poOgrFieldDef->GetNameRef();
     303             : 
     304         428 :         SimpleDataPtr poKmlSimpleData = nullptr;
     305         428 :         DataPtr poKmlData = nullptr;
     306             :         OGRField sFieldDT;
     307             : 
     308             :         // TODO(schwehr): Refactor to get rid of gotos.
     309         428 :         switch (type)
     310             :         {
     311         123 :             case OFTString:  //     String of ASCII chars
     312             :             {
     313             :                 char *const pszUTF8String =
     314         123 :                     OGRLIBKMLSanitizeUTF8String(poOgrFeat->GetFieldAsString(i));
     315         123 :                 if (pszUTF8String[0] == '\0')
     316             :                 {
     317           0 :                     CPLFree(pszUTF8String);
     318           0 :                     continue;
     319             :                 }
     320             : 
     321             :                 /***** id *****/
     322         123 :                 if (EQUAL(name, oFC.idfield))
     323             :                 {
     324          11 :                     poKmlFeature->set_id(pszUTF8String);
     325          11 :                     CPLFree(pszUTF8String);
     326          11 :                     continue;
     327             :                 }
     328             :                 /***** name *****/
     329         112 :                 if (EQUAL(name, oFC.namefield))
     330             :                 {
     331          15 :                     poKmlFeature->set_name(pszUTF8String);
     332          15 :                     CPLFree(pszUTF8String);
     333          15 :                     continue;
     334             :                 }
     335             :                 /***** description *****/
     336          97 :                 else if (EQUAL(name, oFC.descfield))
     337             :                 {
     338           6 :                     poKmlFeature->set_description(pszUTF8String);
     339           6 :                     CPLFree(pszUTF8String);
     340           6 :                     continue;
     341             :                 }
     342             :                 /***** altitudemode *****/
     343          91 :                 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          89 :                 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          89 :                 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          89 :                 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          89 :                 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          88 :                 else if (EQUAL(name, oFC.iconfield) ||
     423          88 :                          EQUAL(name, oFC.modelfield) ||
     424          86 :                          EQUAL(name, oFC.networklinkfield) ||
     425          83 :                          EQUAL(name, oFC.networklink_refreshMode_field) ||
     426          82 :                          EQUAL(name, oFC.networklink_viewRefreshMode_field) ||
     427          81 :                          EQUAL(name, oFC.networklink_viewFormat_field) ||
     428          80 :                          EQUAL(name, oFC.networklink_httpQuery_field) ||
     429          79 :                          EQUAL(name, oFC.camera_altitudemode_field) ||
     430          77 :                          EQUAL(name, oFC.photooverlayfield) ||
     431          75 :                          EQUAL(name, oFC.photooverlay_shape_field) ||
     432          73 :                          EQUAL(name, oFC.imagepyramid_gridorigin_field))
     433             :                 {
     434          15 :                     CPLFree(pszUTF8String);
     435             : 
     436          15 :                     continue;
     437             :                 }
     438             : 
     439             :                 /***** other *****/
     440             : 
     441          73 :                 if (bUseSimpleField)
     442             :                 {
     443          72 :                     poKmlSimpleData = poKmlFactory->CreateSimpleData();
     444          72 :                     poKmlSimpleData->set_name(name);
     445          72 :                     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          73 :                 CPLFree(pszUTF8String);
     455             : 
     456          73 :                 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          80 :             case OFTInteger:  //    Simple 32bit integer
     614             : 
     615             :                 /***** extrude *****/
     616          80 :                 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          71 :                 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          62 :                 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          53 :                 else if (EQUAL(name, oFC.drawOrderfield) ||
     707          53 :                          EQUAL(name, oFC.networklink_refreshvisibility_field) ||
     708          52 :                          EQUAL(name, oFC.networklink_flytoview_field) ||
     709          51 :                          EQUAL(name, oFC.networklink_refreshInterval_field) ||
     710          51 :                          EQUAL(name, oFC.networklink_viewRefreshMode_field) ||
     711          51 :                          EQUAL(name, oFC.networklink_viewRefreshTime_field) ||
     712          51 :                          EQUAL(name, oFC.imagepyramid_tilesize_field) ||
     713          50 :                          EQUAL(name, oFC.imagepyramid_maxwidth_field) ||
     714          49 :                          EQUAL(name, oFC.imagepyramid_maxheight_field))
     715             :                 {
     716           5 :                     continue;
     717             :                 }
     718             : 
     719             :                 /***** other *****/
     720          48 :                 if (bUseSimpleField)
     721             :                 {
     722          48 :                     poKmlSimpleData = poKmlFactory->CreateSimpleData();
     723          48 :                     poKmlSimpleData->set_name(name);
     724          48 :                     poKmlSimpleData->set_text(poOgrFeat->GetFieldAsString(i));
     725             :                 }
     726             :                 else
     727             :                 {
     728           0 :                     poKmlData = poKmlFactory->CreateData();
     729           0 :                     poKmlData->set_name(name);
     730           0 :                     poKmlData->set_value(poOgrFeat->GetFieldAsString(i));
     731             :                 }
     732             : 
     733          48 :                 break;
     734             : 
     735         117 :             case OFTReal:  //   Double Precision floating point
     736             :             {
     737         117 :                 if (EQUAL(name, oFC.headingfield) ||
     738         112 :                     EQUAL(name, oFC.tiltfield) || EQUAL(name, oFC.rollfield) ||
     739         104 :                     EQUAL(name, oFC.scalexfield) ||
     740         103 :                     EQUAL(name, oFC.scaleyfield) ||
     741         102 :                     EQUAL(name, oFC.scalezfield) ||
     742         101 :                     EQUAL(name, oFC.networklink_refreshInterval_field) ||
     743         100 :                     EQUAL(name, oFC.networklink_viewRefreshMode_field) ||
     744         100 :                     EQUAL(name, oFC.networklink_viewRefreshTime_field) ||
     745          99 :                     EQUAL(name, oFC.networklink_viewBoundScale_field) ||
     746          98 :                     EQUAL(name, oFC.camera_longitude_field) ||
     747          96 :                     EQUAL(name, oFC.camera_latitude_field) ||
     748          94 :                     EQUAL(name, oFC.camera_altitude_field) ||
     749          92 :                     EQUAL(name, oFC.leftfovfield) ||
     750          90 :                     EQUAL(name, oFC.rightfovfield) ||
     751          88 :                     EQUAL(name, oFC.bottomfovfield) ||
     752          86 :                     EQUAL(name, oFC.topfovfield) ||
     753          84 :                     EQUAL(name, oFC.nearfield) ||
     754          82 :                     EQUAL(name, oFC.camera_altitude_field))
     755             :                 {
     756          35 :                     continue;
     757             :                 }
     758             : 
     759          82 :                 char *pszStr = CPLStrdup(poOgrFeat->GetFieldAsString(i));
     760             : 
     761          82 :                 if (bUseSimpleField)
     762             :                 {
     763          82 :                     poKmlSimpleData = poKmlFactory->CreateSimpleData();
     764          82 :                     poKmlSimpleData->set_name(name);
     765          82 :                     poKmlSimpleData->set_text(pszStr);
     766             :                 }
     767             :                 else
     768             :                 {
     769           0 :                     poKmlData = poKmlFactory->CreateData();
     770           0 :                     poKmlData->set_name(name);
     771           0 :                     poKmlData->set_value(pszStr);
     772             :                 }
     773             : 
     774          82 :                 CPLFree(pszStr);
     775             : 
     776          82 :                 break;
     777             :             }
     778             : 
     779           0 :             case OFTStringList:      //     Array of strings
     780             :             case OFTIntegerList:     //    List of 32bit integers
     781             :             case OFTRealList:        //    List of doubles
     782             :             case OFTBinary:          //     Raw Binary data
     783             :             case OFTWideStringList:  //     deprecated
     784             :             default:
     785             : 
     786             :                 /***** other *****/
     787             : 
     788           0 :                 if (bUseSimpleField)
     789             :                 {
     790           0 :                     poKmlSimpleData = poKmlFactory->CreateSimpleData();
     791           0 :                     poKmlSimpleData->set_name(name);
     792           0 :                     poKmlSimpleData->set_text(poOgrFeat->GetFieldAsString(i));
     793             :                 }
     794             :                 else
     795             :                 {
     796           0 :                     poKmlData = poKmlFactory->CreateData();
     797           0 :                     poKmlData->set_name(name);
     798           0 :                     poKmlData->set_value(poOgrFeat->GetFieldAsString(i));
     799             :                 }
     800             : 
     801           0 :                 break;
     802             :         }
     803             : 
     804         311 :         if (poKmlSimpleData)
     805             :         {
     806         310 :             poKmlSchemaData->add_simpledata(poKmlSimpleData);
     807             :         }
     808           1 :         else if (poKmlData)
     809             :         {
     810           1 :             if (!poKmlExtendedData)
     811           1 :                 poKmlExtendedData = poKmlFactory->CreateExtendedData();
     812             : #if defined(__GNUC__)
     813             : #pragma GCC diagnostic push
     814             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     815             : #endif
     816           1 :             poKmlExtendedData->add_data(poKmlData);
     817             : #if defined(__GNUC__)
     818             : #pragma GCC diagnostic pop
     819             : #endif
     820             :         }
     821             :     }
     822             : 
     823             :     // Do not add it to the placemark unless there is data.
     824         202 :     if (bUseSimpleField && poKmlSchemaData->get_simpledata_array_size() > 0)
     825             :     {
     826          72 :         poKmlExtendedData = poKmlFactory->CreateExtendedData();
     827          72 :         poKmlExtendedData->add_schemadata(poKmlSchemaData);
     828             :     }
     829         202 :     if (poKmlExtendedData)
     830             :     {
     831          73 :         poKmlFeature->set_extendeddata(poKmlExtendedData);
     832             :     }
     833         202 : }
     834             : 
     835             : /******************************************************************************
     836             :  Recursive function to read altitude mode from the geometry.
     837             : ******************************************************************************/
     838             : 
     839        1230 : static bool kml2altitudemode_rec(GeometryPtr poKmlGeometry, int *pnAltitudeMode,
     840             :                                  int *pbIsGX)
     841             : {
     842        1230 :     switch (poKmlGeometry->Type())
     843             :     {
     844         211 :         case kmldom::Type_Point:
     845             :         {
     846         211 :             PointPtr poKmlPoint = AsPoint(poKmlGeometry);
     847             : 
     848         211 :             if (poKmlPoint->has_altitudemode())
     849             :             {
     850          60 :                 *pnAltitudeMode = poKmlPoint->get_altitudemode();
     851          60 :                 return true;
     852             :             }
     853         151 :             else if (poKmlPoint->has_gx_altitudemode())
     854             :             {
     855           0 :                 *pnAltitudeMode = poKmlPoint->get_gx_altitudemode();
     856           0 :                 *pbIsGX = true;
     857           0 :                 return true;
     858             :             }
     859             : 
     860         151 :             break;
     861             :         }
     862         240 :         case kmldom::Type_LineString:
     863             :         {
     864         240 :             LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
     865             : 
     866         240 :             if (poKmlLineString->has_altitudemode())
     867             :             {
     868         128 :                 *pnAltitudeMode = poKmlLineString->get_altitudemode();
     869         128 :                 return true;
     870             :             }
     871         112 :             else if (poKmlLineString->has_gx_altitudemode())
     872             :             {
     873           0 :                 *pnAltitudeMode = poKmlLineString->get_gx_altitudemode();
     874           0 :                 *pbIsGX = true;
     875           0 :                 return true;
     876             :             }
     877         112 :             break;
     878             :         }
     879           3 :         case kmldom::Type_LinearRing:
     880             :         {
     881           3 :             break;
     882             :         }
     883         745 :         case kmldom::Type_Polygon:
     884             :         {
     885         745 :             PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
     886             : 
     887         745 :             if (poKmlPolygon->has_altitudemode())
     888             :             {
     889         296 :                 *pnAltitudeMode = poKmlPolygon->get_altitudemode();
     890         296 :                 return true;
     891             :             }
     892         449 :             else if (poKmlPolygon->has_gx_altitudemode())
     893             :             {
     894           0 :                 *pnAltitudeMode = poKmlPolygon->get_gx_altitudemode();
     895           0 :                 *pbIsGX = true;
     896           0 :                 return true;
     897             :             }
     898             : 
     899         449 :             break;
     900             :         }
     901          28 :         case kmldom::Type_MultiGeometry:
     902             :         {
     903             :             MultiGeometryPtr poKmlMultiGeometry =
     904          28 :                 AsMultiGeometry(poKmlGeometry);
     905             : 
     906          28 :             const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
     907          72 :             for (size_t i = 0; i < nGeom; i++)
     908             :             {
     909          44 :                 if (kml2altitudemode_rec(
     910          44 :                         poKmlMultiGeometry->get_geometry_array_at(i),
     911             :                         pnAltitudeMode, pbIsGX))
     912           0 :                     return true;
     913             :             }
     914             : 
     915          28 :             break;
     916             :         }
     917           3 :         default:
     918             :         {
     919           3 :             break;
     920             :         }
     921             :     }
     922             : 
     923         746 :     return false;
     924             : }
     925             : 
     926             : /******************************************************************************
     927             :  Recursive function to read extrude from the geometry.
     928             : ******************************************************************************/
     929             : 
     930        1231 : static bool kml2extrude_rec(GeometryPtr poKmlGeometry, bool *pbExtrude)
     931             : {
     932        1231 :     switch (poKmlGeometry->Type())
     933             :     {
     934         212 :         case kmldom::Type_Point:
     935             :         {
     936         212 :             PointPtr poKmlPoint = AsPoint(poKmlGeometry);
     937             : 
     938         212 :             if (poKmlPoint->has_extrude())
     939             :             {
     940          32 :                 *pbExtrude = poKmlPoint->get_extrude();
     941          32 :                 return true;
     942             :             }
     943             : 
     944         180 :             break;
     945             :         }
     946         240 :         case kmldom::Type_LineString:
     947             :         {
     948         240 :             LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
     949             : 
     950         240 :             if (poKmlLineString->has_extrude())
     951             :             {
     952          60 :                 *pbExtrude = poKmlLineString->get_extrude();
     953          60 :                 return true;
     954             :             }
     955             : 
     956         180 :             break;
     957             :         }
     958           3 :         case kmldom::Type_LinearRing:
     959             :         {
     960           3 :             break;
     961             :         }
     962         745 :         case kmldom::Type_Polygon:
     963             :         {
     964         745 :             PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
     965             : 
     966         745 :             if (poKmlPolygon->has_extrude())
     967             :             {
     968         280 :                 *pbExtrude = poKmlPolygon->get_extrude();
     969         280 :                 return true;
     970             :             }
     971             : 
     972         465 :             break;
     973             :         }
     974          28 :         case kmldom::Type_MultiGeometry:
     975             :         {
     976             :             MultiGeometryPtr poKmlMultiGeometry =
     977          28 :                 AsMultiGeometry(poKmlGeometry);
     978             : 
     979          28 :             const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
     980          72 :             for (size_t i = 0; i < nGeom; i++)
     981             :             {
     982          44 :                 if (kml2extrude_rec(
     983          44 :                         poKmlMultiGeometry->get_geometry_array_at(i),
     984             :                         pbExtrude))
     985           0 :                     return true;
     986             :             }
     987             : 
     988          28 :             break;
     989             :         }
     990           3 :         default:
     991             :         {
     992           3 :             break;
     993             :         }
     994             :     }
     995             : 
     996         859 :     return false;
     997             : }
     998             : 
     999             : /******************************************************************************
    1000             :  Recursive function to read tessellate from the geometry.
    1001             : ******************************************************************************/
    1002             : 
    1003        1231 : static bool kml2tessellate_rec(GeometryPtr poKmlGeometry, int *pnTessellate)
    1004             : {
    1005        1231 :     switch (poKmlGeometry->Type())
    1006             :     {
    1007         212 :         case kmldom::Type_Point:
    1008             :         {
    1009         212 :             break;
    1010             :         }
    1011         240 :         case kmldom::Type_LineString:
    1012             :         {
    1013         240 :             LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
    1014             : 
    1015         240 :             if (poKmlLineString->has_tessellate())
    1016             :             {
    1017         214 :                 *pnTessellate = poKmlLineString->get_tessellate();
    1018         214 :                 return true;
    1019             :             }
    1020             : 
    1021          26 :             break;
    1022             :         }
    1023           3 :         case kmldom::Type_LinearRing:
    1024             :         {
    1025           3 :             break;
    1026             :         }
    1027         745 :         case kmldom::Type_Polygon:
    1028             :         {
    1029         745 :             PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
    1030             : 
    1031         745 :             if (poKmlPolygon->has_tessellate())
    1032             :             {
    1033         124 :                 *pnTessellate = poKmlPolygon->get_tessellate();
    1034         124 :                 return true;
    1035             :             }
    1036             : 
    1037         621 :             break;
    1038             :         }
    1039          28 :         case kmldom::Type_MultiGeometry:
    1040             :         {
    1041             :             MultiGeometryPtr poKmlMultiGeometry =
    1042          28 :                 AsMultiGeometry(poKmlGeometry);
    1043             : 
    1044          28 :             const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
    1045          72 :             for (size_t i = 0; i < nGeom; i++)
    1046             :             {
    1047          44 :                 if (kml2tessellate_rec(
    1048          44 :                         poKmlMultiGeometry->get_geometry_array_at(i),
    1049             :                         pnTessellate))
    1050           0 :                     return true;
    1051             :             }
    1052             : 
    1053          28 :             break;
    1054             :         }
    1055           3 :         default:
    1056           3 :             break;
    1057             :     }
    1058             : 
    1059         893 :     return false;
    1060             : }
    1061             : 
    1062             : /************************************************************************/
    1063             : /*                     ogrkmlSetAltitudeMode()                          */
    1064             : /************************************************************************/
    1065             : 
    1066         485 : static void ogrkmlSetAltitudeMode(OGRFeature *poOgrFeat, int iField,
    1067             :                                   int nAltitudeMode, bool bIsGX)
    1068             : {
    1069         485 :     if (!bIsGX)
    1070             :     {
    1071         485 :         switch (nAltitudeMode)
    1072             :         {
    1073           0 :             case kmldom::ALTITUDEMODE_CLAMPTOGROUND:
    1074           0 :                 poOgrFeat->SetField(iField, "clampToGround");
    1075           0 :                 break;
    1076             : 
    1077         349 :             case kmldom::ALTITUDEMODE_RELATIVETOGROUND:
    1078         349 :                 poOgrFeat->SetField(iField, "relativeToGround");
    1079         349 :                 break;
    1080             : 
    1081         136 :             case kmldom::ALTITUDEMODE_ABSOLUTE:
    1082         136 :                 poOgrFeat->SetField(iField, "absolute");
    1083         136 :                 break;
    1084             :         }
    1085             :     }
    1086             :     else
    1087             :     {
    1088           0 :         switch (nAltitudeMode)
    1089             :         {
    1090           0 :             case kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR:
    1091           0 :                 poOgrFeat->SetField(iField, "relativeToSeaFloor");
    1092           0 :                 break;
    1093             : 
    1094           0 :             case kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR:
    1095           0 :                 poOgrFeat->SetField(iField, "clampToSeaFloor");
    1096           0 :                 break;
    1097             :         }
    1098             :     }
    1099         485 : }
    1100             : 
    1101             : /************************************************************************/
    1102             : /*                            TrimSpaces()                              */
    1103             : /************************************************************************/
    1104             : 
    1105        1296 : static const char *TrimSpaces(CPLString &oText)
    1106             : {
    1107             :     // SerializePretty() adds a new line before the data
    1108             :     // ands trailing spaces. I believe this is wrong
    1109             :     // as it breaks round-tripping.
    1110             : 
    1111             :     // Trim trailing spaces.
    1112        1296 :     while (!oText.empty() && oText.back() == ' ')
    1113           0 :         oText.pop_back();
    1114             : 
    1115             :     // Skip leading newline and spaces.
    1116        1296 :     const char *pszText = oText.c_str();
    1117        1296 :     if (pszText[0] == '\n')
    1118           0 :         pszText++;
    1119        1296 :     while (pszText[0] == ' ')
    1120           0 :         pszText++;
    1121             : 
    1122        1296 :     return pszText;
    1123             : }
    1124             : 
    1125             : /************************************************************************/
    1126             : /*                            kmldatetime2ogr()                         */
    1127             : /************************************************************************/
    1128             : 
    1129           7 : static void kmldatetime2ogr(OGRFeature *poOgrFeat, const char *pszOGRField,
    1130             :                             const std::string &osKmlDateTime)
    1131             : {
    1132           7 :     const int iField = poOgrFeat->GetFieldIndex(pszOGRField);
    1133             : 
    1134           7 :     if (iField > -1)
    1135             :     {
    1136             :         OGRField sField;
    1137             : 
    1138           7 :         if (OGRParseXMLDateTime(osKmlDateTime.c_str(), &sField))
    1139           7 :             poOgrFeat->SetField(iField, &sField);
    1140             :     }
    1141           7 : }
    1142             : 
    1143             : /******************************************************************************
    1144             :  function to read kml into ogr fields
    1145             : ******************************************************************************/
    1146             : 
    1147        1293 : void kml2field(OGRFeature *poOgrFeat, FeaturePtr poKmlFeature,
    1148             :                const fieldconfig &oFC)
    1149             : {
    1150             :     /***** id *****/
    1151             : 
    1152        1293 :     if (poKmlFeature->has_id())
    1153             :     {
    1154        1048 :         const std::string oKmlId = poKmlFeature->get_id();
    1155         524 :         int iField = poOgrFeat->GetFieldIndex(oFC.idfield);
    1156             : 
    1157         524 :         if (iField > -1)
    1158         508 :             poOgrFeat->SetField(iField, oKmlId.c_str());
    1159             :     }
    1160             :     /***** name *****/
    1161             : 
    1162        1293 :     if (poKmlFeature->has_name())
    1163             :     {
    1164        1520 :         const std::string oKmlName = poKmlFeature->get_name();
    1165         760 :         int iField = poOgrFeat->GetFieldIndex(oFC.namefield);
    1166             : 
    1167         760 :         if (iField > -1)
    1168         760 :             poOgrFeat->SetField(iField, oKmlName.c_str());
    1169             :     }
    1170             : 
    1171             :     /***** description *****/
    1172             : 
    1173        1293 :     if (poKmlFeature->has_description())
    1174             :     {
    1175         788 :         const std::string oKmlDesc = poKmlFeature->get_description();
    1176         394 :         int iField = poOgrFeat->GetFieldIndex(oFC.descfield);
    1177             : 
    1178         394 :         if (iField > -1)
    1179         394 :             poOgrFeat->SetField(iField, oKmlDesc.c_str());
    1180             :     }
    1181             : 
    1182        1293 :     if (poKmlFeature->has_timeprimitive())
    1183             :     {
    1184           4 :         TimePrimitivePtr poKmlTimePrimitive = poKmlFeature->get_timeprimitive();
    1185             : 
    1186             :         /***** timestamp *****/
    1187             : 
    1188           2 :         if (poKmlTimePrimitive->IsA(kmldom::Type_TimeStamp))
    1189             :         {
    1190             :             // Probably a libkml bug: AsTimeStamp should really return not NULL
    1191             :             // on a gx:TimeStamp.
    1192           2 :             TimeStampPtr poKmlTimeStamp = AsTimeStamp(poKmlTimePrimitive);
    1193           1 :             if (!poKmlTimeStamp)
    1194           1 :                 poKmlTimeStamp = AsGxTimeStamp(poKmlTimePrimitive);
    1195             : 
    1196           1 :             if (poKmlTimeStamp && poKmlTimeStamp->has_when())
    1197             :             {
    1198           2 :                 const std::string oKmlWhen = poKmlTimeStamp->get_when();
    1199           1 :                 kmldatetime2ogr(poOgrFeat, oFC.tsfield, oKmlWhen);
    1200             :             }
    1201             :         }
    1202             : 
    1203             :         /***** timespan *****/
    1204             : 
    1205           2 :         if (poKmlTimePrimitive->IsA(kmldom::Type_TimeSpan))
    1206             :         {
    1207             :             // Probably a libkml bug: AsTimeSpan should really return not NULL
    1208             :             // on a gx:TimeSpan.
    1209           2 :             TimeSpanPtr poKmlTimeSpan = AsTimeSpan(poKmlTimePrimitive);
    1210           1 :             if (!poKmlTimeSpan)
    1211           1 :                 poKmlTimeSpan = AsGxTimeSpan(poKmlTimePrimitive);
    1212             : 
    1213             :             /***** begin *****/
    1214             : 
    1215           1 :             if (poKmlTimeSpan && poKmlTimeSpan->has_begin())
    1216             :             {
    1217           2 :                 const std::string oKmlWhen = poKmlTimeSpan->get_begin();
    1218           1 :                 kmldatetime2ogr(poOgrFeat, oFC.beginfield, oKmlWhen);
    1219             :             }
    1220             : 
    1221             :             /***** end *****/
    1222             : 
    1223           1 :             if (poKmlTimeSpan && poKmlTimeSpan->has_end())
    1224             :             {
    1225           2 :                 const std::string oKmlWhen = poKmlTimeSpan->get_end();
    1226           1 :                 kmldatetime2ogr(poOgrFeat, oFC.endfield, oKmlWhen);
    1227             :             }
    1228             :         }
    1229             :     }
    1230             : 
    1231             :     /***** placemark *****/
    1232        2586 :     PlacemarkPtr poKmlPlacemark = AsPlacemark(poKmlFeature);
    1233        2586 :     GroundOverlayPtr poKmlGroundOverlay = AsGroundOverlay(poKmlFeature);
    1234        1293 :     if (poKmlPlacemark && poKmlPlacemark->has_geometry())
    1235             :     {
    1236        2374 :         GeometryPtr poKmlGeometry = poKmlPlacemark->get_geometry();
    1237             : 
    1238             :         /***** altitudeMode *****/
    1239        1187 :         int bIsGX = false;
    1240        1187 :         int nAltitudeMode = -1;
    1241             : 
    1242        1187 :         int iField = poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
    1243             : 
    1244        1187 :         if (iField > -1)
    1245             :         {
    1246        1186 :             if (kml2altitudemode_rec(poKmlGeometry, &nAltitudeMode, &bIsGX))
    1247             :             {
    1248         484 :                 ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode,
    1249         484 :                                       CPL_TO_BOOL(bIsGX));
    1250             :             }
    1251             :         }
    1252             : 
    1253             :         /***** tessellate *****/
    1254        1187 :         int nTessellate = -1;
    1255             : 
    1256        1187 :         kml2tessellate_rec(poKmlGeometry, &nTessellate);
    1257             : 
    1258        1187 :         iField = poOgrFeat->GetFieldIndex(oFC.tessellatefield);
    1259        1187 :         if (iField > -1)
    1260        1186 :             poOgrFeat->SetField(iField, nTessellate);
    1261             : 
    1262             :         /***** extrude *****/
    1263             : 
    1264        1187 :         bool bExtrude = false;
    1265             : 
    1266        1187 :         kml2extrude_rec(poKmlGeometry, &bExtrude);
    1267             : 
    1268        1187 :         iField = poOgrFeat->GetFieldIndex(oFC.extrudefield);
    1269        1187 :         if (iField > -1)
    1270        1186 :             poOgrFeat->SetField(iField, bExtrude ? 1 : 0);
    1271             : 
    1272             :         /***** special case for gx:Track ******/
    1273             :         /* we set the first timestamp as begin and the last one as end */
    1274        1189 :         if (poKmlGeometry->Type() == kmldom::Type_GxTrack &&
    1275           2 :             !poKmlFeature->has_timeprimitive())
    1276             :         {
    1277           4 :             GxTrackPtr poKmlGxTrack = AsGxTrack(poKmlGeometry);
    1278           2 :             if (poKmlGxTrack)
    1279             :             {
    1280           2 :                 const size_t nCoords = poKmlGxTrack->get_when_array_size();
    1281           2 :                 if (nCoords > 0)
    1282             :                 {
    1283           2 :                     kmldatetime2ogr(poOgrFeat, oFC.beginfield,
    1284           1 :                                     poKmlGxTrack->get_when_array_at(0).c_str());
    1285           3 :                     kmldatetime2ogr(
    1286           1 :                         poOgrFeat, oFC.endfield,
    1287           1 :                         poKmlGxTrack->get_when_array_at(nCoords - 1).c_str());
    1288             :                 }
    1289             :             }
    1290             :         }
    1291             : 
    1292             :         /***** special case for gx:MultiTrack ******/
    1293             :         /* we set the first timestamp as begin and the last one as end */
    1294        1186 :         else if (poKmlGeometry->Type() == kmldom::Type_GxMultiTrack &&
    1295           1 :                  !poKmlFeature->has_timeprimitive())
    1296             :         {
    1297           2 :             GxMultiTrackPtr poKmlGxMultiTrack = AsGxMultiTrack(poKmlGeometry);
    1298           1 :             if (poKmlGxMultiTrack)
    1299             :             {
    1300             :                 const size_t nGeom =
    1301           1 :                     poKmlGxMultiTrack->get_gx_track_array_size();
    1302           1 :                 if (nGeom >= 1)
    1303             :                 {
    1304             :                     {
    1305             :                         GxTrackPtr poKmlGxTrack =
    1306           2 :                             poKmlGxMultiTrack->get_gx_track_array_at(0);
    1307             :                         const size_t nCoords =
    1308           1 :                             poKmlGxTrack->get_when_array_size();
    1309           1 :                         if (nCoords > 0)
    1310             :                         {
    1311           3 :                             kmldatetime2ogr(
    1312           1 :                                 poOgrFeat, oFC.beginfield,
    1313           1 :                                 poKmlGxTrack->get_when_array_at(0).c_str());
    1314             :                         }
    1315             :                     }
    1316             : 
    1317             :                     {
    1318             :                         GxTrackPtr poKmlGxTrack =
    1319           2 :                             poKmlGxMultiTrack->get_gx_track_array_at(nGeom - 1);
    1320             :                         const size_t nCoords =
    1321           1 :                             poKmlGxTrack->get_when_array_size();
    1322           1 :                         if (nCoords > 0)
    1323             :                         {
    1324           3 :                             kmldatetime2ogr(
    1325           1 :                                 poOgrFeat, oFC.endfield,
    1326           1 :                                 poKmlGxTrack->get_when_array_at(nCoords - 1)
    1327             :                                     .c_str());
    1328             :                         }
    1329             :                     }
    1330             :                 }
    1331             :             }
    1332             :         }
    1333             :     }
    1334             : 
    1335             :     /***** camera *****/
    1336             : 
    1337         108 :     else if (poKmlPlacemark && poKmlPlacemark->has_abstractview() &&
    1338           2 :              poKmlPlacemark->get_abstractview()->IsA(kmldom::Type_Camera))
    1339             :     {
    1340           4 :         const CameraPtr &camera = AsCamera(poKmlPlacemark->get_abstractview());
    1341             : 
    1342           2 :         if (camera->has_heading())
    1343             :         {
    1344           2 :             int iField = poOgrFeat->GetFieldIndex(oFC.headingfield);
    1345           2 :             if (iField > -1)
    1346           2 :                 poOgrFeat->SetField(iField, camera->get_heading());
    1347             :         }
    1348             : 
    1349           2 :         if (camera->has_tilt())
    1350             :         {
    1351           1 :             int iField = poOgrFeat->GetFieldIndex(oFC.tiltfield);
    1352           1 :             if (iField > -1)
    1353           1 :                 poOgrFeat->SetField(iField, camera->get_tilt());
    1354             :         }
    1355             : 
    1356           2 :         if (camera->has_roll())
    1357             :         {
    1358           1 :             int iField = poOgrFeat->GetFieldIndex(oFC.rollfield);
    1359           1 :             if (iField > -1)
    1360           1 :                 poOgrFeat->SetField(iField, camera->get_roll());
    1361             :         }
    1362             : 
    1363           2 :         int iField = poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
    1364             : 
    1365           2 :         if (iField > -1)
    1366             :         {
    1367           2 :             if (camera->has_altitudemode())
    1368             :             {
    1369           1 :                 const int nAltitudeMode = camera->get_altitudemode();
    1370           1 :                 ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, false);
    1371             :             }
    1372           1 :             else if (camera->has_gx_altitudemode())
    1373             :             {
    1374           0 :                 const int nAltitudeMode = camera->get_gx_altitudemode();
    1375           0 :                 ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, true);
    1376             :             }
    1377             :         }
    1378             :     }
    1379             :     /***** ground overlay *****/
    1380         104 :     else if (poKmlGroundOverlay)
    1381             :     {
    1382             :         /***** icon *****/
    1383          42 :         int iField = poOgrFeat->GetFieldIndex(oFC.iconfield);
    1384          42 :         if (iField > -1)
    1385             :         {
    1386          42 :             if (poKmlGroundOverlay->has_icon())
    1387             :             {
    1388          84 :                 IconPtr icon = poKmlGroundOverlay->get_icon();
    1389          42 :                 if (icon->has_href())
    1390             :                 {
    1391          42 :                     poOgrFeat->SetField(iField, icon->get_href().c_str());
    1392             :                 }
    1393             :             }
    1394             :         }
    1395             : 
    1396             :         /***** drawOrder *****/
    1397          42 :         iField = poOgrFeat->GetFieldIndex(oFC.drawOrderfield);
    1398          42 :         if (iField > -1)
    1399             :         {
    1400          42 :             if (poKmlGroundOverlay->has_draworder())
    1401             :             {
    1402           0 :                 poOgrFeat->SetField(iField,
    1403           0 :                                     poKmlGroundOverlay->get_draworder());
    1404             :             }
    1405             :         }
    1406             : 
    1407             :         /***** altitudeMode *****/
    1408             : 
    1409          42 :         iField = poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
    1410             : 
    1411          42 :         if (iField > -1)
    1412             :         {
    1413          42 :             if (poKmlGroundOverlay->has_altitudemode())
    1414             :             {
    1415           0 :                 switch (poKmlGroundOverlay->get_altitudemode())
    1416             :                 {
    1417           0 :                     case kmldom::ALTITUDEMODE_CLAMPTOGROUND:
    1418           0 :                         poOgrFeat->SetField(iField, "clampToGround");
    1419           0 :                         break;
    1420             : 
    1421           0 :                     case kmldom::ALTITUDEMODE_RELATIVETOGROUND:
    1422           0 :                         poOgrFeat->SetField(iField, "relativeToGround");
    1423           0 :                         break;
    1424             : 
    1425           0 :                     case kmldom::ALTITUDEMODE_ABSOLUTE:
    1426           0 :                         poOgrFeat->SetField(iField, "absolute");
    1427           0 :                         break;
    1428             :                 }
    1429             :             }
    1430          42 :             else if (poKmlGroundOverlay->has_gx_altitudemode())
    1431             :             {
    1432           0 :                 switch (poKmlGroundOverlay->get_gx_altitudemode())
    1433             :                 {
    1434           0 :                     case kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR:
    1435           0 :                         poOgrFeat->SetField(iField, "relativeToSeaFloor");
    1436           0 :                         break;
    1437             : 
    1438           0 :                     case kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR:
    1439           0 :                         poOgrFeat->SetField(iField, "clampToSeaFloor");
    1440           0 :                         break;
    1441             :                 }
    1442             :             }
    1443             :         }
    1444             :     }
    1445             : 
    1446             :     /***** visibility *****/
    1447             :     const int nVisibility =
    1448        1293 :         poKmlFeature->has_visibility() ? poKmlFeature->get_visibility() : -1;
    1449             : 
    1450        1293 :     int iField = poOgrFeat->GetFieldIndex(oFC.visibilityfield);
    1451             : 
    1452        1293 :     if (iField > -1)
    1453        1276 :         poOgrFeat->SetField(iField, nVisibility);
    1454             : 
    1455             :     /***** snippet *****/
    1456             : 
    1457        1293 :     if (poKmlFeature->has_snippet())
    1458             :     {
    1459           2 :         CPLString oText = poKmlFeature->get_snippet()->get_text();
    1460             : 
    1461           1 :         iField = poOgrFeat->GetFieldIndex(oFC.snippetfield);
    1462             : 
    1463           1 :         if (iField > -1)
    1464           1 :             poOgrFeat->SetField(iField, TrimSpaces(oText));
    1465             :     }
    1466             : 
    1467             :     /***** extended schema *****/
    1468        2586 :     ExtendedDataPtr poKmlExtendedData = nullptr;
    1469             : 
    1470        1293 :     if (poKmlFeature->has_extendeddata())
    1471             :     {
    1472         438 :         poKmlExtendedData = poKmlFeature->get_extendeddata();
    1473             : 
    1474             :         /***** loop over the schemadata_arrays *****/
    1475             : 
    1476             :         const size_t nSchemaData =
    1477         438 :             poKmlExtendedData->get_schemadata_array_size();
    1478             : 
    1479         873 :         for (size_t iSchemaData = 0; iSchemaData < nSchemaData; iSchemaData++)
    1480             :         {
    1481             :             SchemaDataPtr poKmlSchemaData =
    1482         870 :                 poKmlExtendedData->get_schemadata_array_at(iSchemaData);
    1483             : 
    1484             :             /***** loop over the simpledata array *****/
    1485             : 
    1486             :             const size_t nSimpleData =
    1487         435 :                 poKmlSchemaData->get_simpledata_array_size();
    1488             : 
    1489        1730 :             for (size_t iSimpleData = 0; iSimpleData < nSimpleData;
    1490             :                  iSimpleData++)
    1491             :             {
    1492             :                 SimpleDataPtr poKmlSimpleData =
    1493        2590 :                     poKmlSchemaData->get_simpledata_array_at(iSimpleData);
    1494             : 
    1495             :                 /***** find the field index *****/
    1496             : 
    1497        1295 :                 iField = -1;
    1498             : 
    1499        1295 :                 if (poKmlSimpleData->has_name())
    1500             :                 {
    1501        1295 :                     const string oName = poKmlSimpleData->get_name();
    1502        1295 :                     const char *pszName = oName.c_str();
    1503             : 
    1504        1295 :                     iField = poOgrFeat->GetFieldIndex(pszName);
    1505             :                 }
    1506             : 
    1507             :                 /***** if it has trxt set the field *****/
    1508             : 
    1509        1295 :                 if (iField > -1 && poKmlSimpleData->has_text())
    1510             :                 {
    1511        2590 :                     CPLString oText = poKmlSimpleData->get_text();
    1512             : 
    1513        1295 :                     poOgrFeat->SetField(iField, TrimSpaces(oText));
    1514             :                 }
    1515             :             }
    1516             :         }
    1517             : 
    1518         438 :         if (nSchemaData == 0 && poKmlExtendedData->get_data_array_size() > 0)
    1519             :         {
    1520           3 :             const bool bLaunderFieldNames = CPLTestBool(
    1521             :                 CPLGetConfigOption("LIBKML_LAUNDER_FIELD_NAMES", "YES"));
    1522             :             const size_t nDataArraySize =
    1523           3 :                 poKmlExtendedData->get_data_array_size();
    1524           8 :             for (size_t i = 0; i < nDataArraySize; i++)
    1525             :             {
    1526           5 :                 const DataPtr &data = poKmlExtendedData->get_data_array_at(i);
    1527           5 :                 if (data->has_name() && data->has_value())
    1528             :                 {
    1529          10 :                     CPLString osName = std::string(data->get_name());
    1530           5 :                     if (bLaunderFieldNames)
    1531           5 :                         osName = OGRLIBKMLLayer::LaunderFieldNames(osName);
    1532           5 :                     iField = poOgrFeat->GetFieldIndex(osName);
    1533           5 :                     if (iField >= 0)
    1534             :                     {
    1535           5 :                         poOgrFeat->SetField(iField, data->get_value().c_str());
    1536             :                     }
    1537             :                 }
    1538             :             }
    1539             :         }
    1540             :     }
    1541        1293 : }
    1542             : 
    1543             : /******************************************************************************
    1544             :  function create a simplefield from a FieldDefn
    1545             : ******************************************************************************/
    1546             : 
    1547         176 : SimpleFieldPtr FieldDef2kml(const OGRFieldDefn *poOgrFieldDef,
    1548             :                             KmlFactory *poKmlFactory, bool bApproxOK,
    1549             :                             const fieldconfig &oFC)
    1550             : {
    1551         176 :     const char *pszFieldName = poOgrFieldDef->GetNameRef();
    1552             : 
    1553         176 :     if (EQUAL(pszFieldName, oFC.idfield) ||
    1554         174 :         EQUAL(pszFieldName, oFC.namefield) ||
    1555         164 :         EQUAL(pszFieldName, oFC.descfield) ||
    1556         158 :         EQUAL(pszFieldName, oFC.tsfield) ||
    1557         158 :         EQUAL(pszFieldName, oFC.beginfield) ||
    1558         158 :         EQUAL(pszFieldName, oFC.endfield) ||
    1559         158 :         EQUAL(pszFieldName, oFC.altitudeModefield) ||
    1560         156 :         EQUAL(pszFieldName, oFC.tessellatefield) ||
    1561         156 :         EQUAL(pszFieldName, oFC.extrudefield) ||
    1562         156 :         EQUAL(pszFieldName, oFC.visibilityfield) ||
    1563         156 :         EQUAL(pszFieldName, oFC.drawOrderfield) ||
    1564         156 :         EQUAL(pszFieldName, oFC.iconfield) ||
    1565         156 :         EQUAL(pszFieldName, oFC.headingfield) ||
    1566         153 :         EQUAL(pszFieldName, oFC.tiltfield) ||
    1567         150 :         EQUAL(pszFieldName, oFC.rollfield) ||
    1568         147 :         EQUAL(pszFieldName, oFC.snippetfield) ||
    1569         146 :         EQUAL(pszFieldName, oFC.modelfield) ||
    1570         145 :         EQUAL(pszFieldName, oFC.scalexfield) ||
    1571         144 :         EQUAL(pszFieldName, oFC.scaleyfield) ||
    1572         143 :         EQUAL(pszFieldName, oFC.scalezfield) ||
    1573         142 :         EQUAL(pszFieldName, oFC.networklinkfield) ||
    1574         141 :         EQUAL(pszFieldName, oFC.networklink_refreshvisibility_field) ||
    1575         140 :         EQUAL(pszFieldName, oFC.networklink_flytoview_field) ||
    1576         139 :         EQUAL(pszFieldName, oFC.networklink_refreshMode_field) ||
    1577         138 :         EQUAL(pszFieldName, oFC.networklink_refreshInterval_field) ||
    1578         137 :         EQUAL(pszFieldName, oFC.networklink_viewRefreshMode_field) ||
    1579         136 :         EQUAL(pszFieldName, oFC.networklink_viewRefreshTime_field) ||
    1580         135 :         EQUAL(pszFieldName, oFC.networklink_viewBoundScale_field) ||
    1581         134 :         EQUAL(pszFieldName, oFC.networklink_viewFormat_field) ||
    1582         133 :         EQUAL(pszFieldName, oFC.networklink_httpQuery_field) ||
    1583         132 :         EQUAL(pszFieldName, oFC.camera_longitude_field) ||
    1584         131 :         EQUAL(pszFieldName, oFC.camera_latitude_field) ||
    1585         130 :         EQUAL(pszFieldName, oFC.camera_altitude_field) ||
    1586         129 :         EQUAL(pszFieldName, oFC.camera_altitudemode_field) ||
    1587         128 :         EQUAL(pszFieldName, oFC.photooverlayfield) ||
    1588         127 :         EQUAL(pszFieldName, oFC.leftfovfield) ||
    1589         126 :         EQUAL(pszFieldName, oFC.rightfovfield) ||
    1590         125 :         EQUAL(pszFieldName, oFC.bottomfovfield) ||
    1591         124 :         EQUAL(pszFieldName, oFC.topfovfield) ||
    1592         123 :         EQUAL(pszFieldName, oFC.nearfield) ||
    1593         122 :         EQUAL(pszFieldName, oFC.photooverlay_shape_field) ||
    1594         121 :         EQUAL(pszFieldName, oFC.imagepyramid_tilesize_field) ||
    1595         120 :         EQUAL(pszFieldName, oFC.imagepyramid_maxwidth_field) ||
    1596         119 :         EQUAL(pszFieldName, oFC.imagepyramid_maxheight_field) ||
    1597         118 :         EQUAL(pszFieldName, oFC.imagepyramid_gridorigin_field))
    1598             :     {
    1599          58 :         return nullptr;
    1600             :     }
    1601             : 
    1602         236 :     SimpleFieldPtr poKmlSimpleField = poKmlFactory->CreateSimpleField();
    1603         118 :     poKmlSimpleField->set_name(pszFieldName);
    1604             : 
    1605         118 :     switch (poOgrFieldDef->GetType())
    1606             :     {
    1607          16 :         case OFTInteger:
    1608             :         case OFTIntegerList:
    1609          16 :             poKmlSimpleField->set_type("int");
    1610          16 :             return poKmlSimpleField;
    1611             : 
    1612          18 :         case OFTReal:
    1613             :         case OFTRealList:
    1614          18 :             poKmlSimpleField->set_type("float");
    1615          18 :             return poKmlSimpleField;
    1616             : 
    1617          40 :         case OFTString:
    1618             :         case OFTStringList:
    1619          40 :             poKmlSimpleField->set_type("string");
    1620          40 :             return poKmlSimpleField;
    1621             : 
    1622             :             /***** kml has these types but as timestamp/timespan *****/
    1623             : 
    1624          44 :         case OFTDate:
    1625             :         case OFTTime:
    1626             :         case OFTDateTime:
    1627          44 :             if (bApproxOK)
    1628             :             {
    1629          44 :                 poKmlSimpleField->set_type("string");
    1630          44 :                 return poKmlSimpleField;
    1631             :             }
    1632           0 :             break;
    1633             : 
    1634           0 :         default:
    1635           0 :             poKmlSimpleField->set_type("string");
    1636           0 :             return poKmlSimpleField;
    1637             :     }
    1638             : 
    1639           0 :     return nullptr;
    1640             : }
    1641             : 
    1642             : /******************************************************************************
    1643             :  function to add the simpleFields in a schema to a featuredefn
    1644             : ******************************************************************************/
    1645             : 
    1646          33 : void kml2FeatureDef(SchemaPtr poKmlSchema, OGRFeatureDefn *poOgrFeatureDefn)
    1647             : {
    1648          33 :     const size_t nSimpleFields = poKmlSchema->get_simplefield_array_size();
    1649             : 
    1650         150 :     for (size_t iSimpleField = 0; iSimpleField < nSimpleFields; iSimpleField++)
    1651             :     {
    1652             :         SimpleFieldPtr poKmlSimpleField =
    1653         234 :             poKmlSchema->get_simplefield_array_at(iSimpleField);
    1654             : 
    1655         117 :         const char *pszType = "string";
    1656         234 :         string osName = "Unknown";
    1657         234 :         string osType;
    1658             : 
    1659         117 :         if (poKmlSimpleField->has_type())
    1660             :         {
    1661         117 :             osType = poKmlSimpleField->get_type();
    1662             : 
    1663         117 :             pszType = osType.c_str();
    1664             :         }
    1665             : 
    1666             :         // TODO: We cannot set displayname as the field name because in
    1667             :         // kml2field() we make the lookup on fields based on their name. We
    1668             :         // would need some map if we really want to use displayname, but that
    1669             :         // might not be a good idea because displayname may have HTML
    1670             :         // formatting, which makes it impractical when converting to other
    1671             :         // drivers or to make requests.
    1672             :         // Example: http://www.jasonbirch.com/files/newt_combined.kml
    1673             : 
    1674             :         // if( poKmlSimpleField->has_displayname() )
    1675             :         //   {
    1676             :         //       osName = poKmlSimpleField->get_displayname();
    1677             :         //   }
    1678             :         //   else
    1679         117 :         if (poKmlSimpleField->has_name())
    1680             :         {
    1681         117 :             osName = poKmlSimpleField->get_name();
    1682             :         }
    1683         117 :         if (poOgrFeatureDefn->GetFieldIndex(osName.c_str()) < 0)
    1684             :         {
    1685         113 :             if (EQUAL(pszType, "bool") || EQUAL(pszType, "boolean") ||
    1686         113 :                 EQUAL(pszType, "int") || EQUAL(pszType, "short") ||
    1687          97 :                 EQUAL(pszType, "ushort"))
    1688             :             {
    1689          32 :                 OGRFieldDefn oOgrFieldName(osName.c_str(), OFTInteger);
    1690          32 :                 poOgrFeatureDefn->AddFieldDefn(&oOgrFieldName);
    1691             :             }
    1692          97 :             else if (EQUAL(pszType, "uint"))
    1693             :             {
    1694           0 :                 OGRFieldDefn oOgrFieldName(osName.c_str(), OFTInteger64);
    1695           0 :                 poOgrFeatureDefn->AddFieldDefn(&oOgrFieldName);
    1696             :             }
    1697          97 :             else if (EQUAL(pszType, "float") || EQUAL(pszType, "double"))
    1698             :             {
    1699          40 :                 OGRFieldDefn oOgrFieldName(osName.c_str(), OFTReal);
    1700          40 :                 poOgrFeatureDefn->AddFieldDefn(&oOgrFieldName);
    1701             :             }
    1702             :             else  // string, or any other unrecognized type.
    1703             :             {
    1704         154 :                 OGRFieldDefn oOgrFieldName(osName.c_str(), OFTString);
    1705          77 :                 poOgrFeatureDefn->AddFieldDefn(&oOgrFieldName);
    1706             :             }
    1707             :         }
    1708             :     }
    1709          33 : }
    1710             : 
    1711             : /*******************************************************************************
    1712             :  * function to fetch the field config options
    1713             :  *
    1714             :  *******************************************************************************/
    1715             : 
    1716         361 : void get_fieldconfig(struct fieldconfig *oFC)
    1717             : {
    1718         361 :     oFC->idfield = CPLGetConfigOption("LIBKML_ID_FIELD", "id");
    1719         361 :     oFC->namefield = CPLGetConfigOption("LIBKML_NAME_FIELD", "Name");
    1720         361 :     oFC->descfield =
    1721         361 :         CPLGetConfigOption("LIBKML_DESCRIPTION_FIELD", "description");
    1722         361 :     oFC->tsfield = CPLGetConfigOption("LIBKML_TIMESTAMP_FIELD", "timestamp");
    1723         361 :     oFC->beginfield = CPLGetConfigOption("LIBKML_BEGIN_FIELD", "begin");
    1724         361 :     oFC->endfield = CPLGetConfigOption("LIBKML_END_FIELD", "end");
    1725         361 :     oFC->altitudeModefield =
    1726         361 :         CPLGetConfigOption("LIBKML_ALTITUDEMODE_FIELD", "altitudeMode");
    1727         361 :     oFC->tessellatefield =
    1728         361 :         CPLGetConfigOption("LIBKML_TESSELLATE_FIELD", "tessellate");
    1729         361 :     oFC->extrudefield = CPLGetConfigOption("LIBKML_EXTRUDE_FIELD", "extrude");
    1730         361 :     oFC->visibilityfield =
    1731         361 :         CPLGetConfigOption("LIBKML_VISIBILITY_FIELD", "visibility");
    1732         361 :     oFC->drawOrderfield =
    1733         361 :         CPLGetConfigOption("LIBKML_DRAWORDER_FIELD", "drawOrder");
    1734         361 :     oFC->iconfield = CPLGetConfigOption("LIBKML_ICON_FIELD", "icon");
    1735         361 :     oFC->headingfield = CPLGetConfigOption("LIBKML_HEADING_FIELD", "heading");
    1736         361 :     oFC->tiltfield = CPLGetConfigOption("LIBKML_TILT_FIELD", "tilt");
    1737         361 :     oFC->rollfield = CPLGetConfigOption("LIBKML_ROLL_FIELD", "roll");
    1738         361 :     oFC->snippetfield = CPLGetConfigOption("LIBKML_SNIPPET_FIELD", "snippet");
    1739         361 :     oFC->modelfield = CPLGetConfigOption("LIBKML_MODEL_FIELD", "model");
    1740         361 :     oFC->scalexfield = CPLGetConfigOption("LIBKML_SCALE_X_FIELD", "scale_x");
    1741         361 :     oFC->scaleyfield = CPLGetConfigOption("LIBKML_SCALE_Y_FIELD", "scale_y");
    1742         361 :     oFC->scalezfield = CPLGetConfigOption("LIBKML_SCALE_Z_FIELD", "scale_z");
    1743         361 :     oFC->networklinkfield =
    1744         361 :         CPLGetConfigOption("LIBKML_NETWORKLINK_FIELD", "networklink");
    1745         361 :     oFC->networklink_refreshvisibility_field =
    1746         361 :         CPLGetConfigOption("LIBKML_NETWORKLINK_REFRESHVISIBILITY_FIELD",
    1747             :                            "networklink_refreshvisibility");
    1748         361 :     oFC->networklink_flytoview_field = CPLGetConfigOption(
    1749             :         "LIBKML_NETWORKLINK_FLYTOVIEW_FIELD", "networklink_flytoview");
    1750         361 :     oFC->networklink_refreshMode_field = CPLGetConfigOption(
    1751             :         "LIBKML_NETWORKLINK_REFRESHMODE_FIELD", "networklink_refreshmode");
    1752         361 :     oFC->networklink_refreshInterval_field =
    1753         361 :         CPLGetConfigOption("LIBKML_NETWORKLINK_REFRESHINTERVAL_FIELD",
    1754             :                            "networklink_refreshinterval");
    1755         361 :     oFC->networklink_viewRefreshMode_field =
    1756         361 :         CPLGetConfigOption("LIBKML_NETWORKLINK_VIEWREFRESHMODE_FIELD",
    1757             :                            "networklink_viewrefreshmode");
    1758         361 :     oFC->networklink_viewRefreshTime_field =
    1759         361 :         CPLGetConfigOption("LIBKML_NETWORKLINK_VIEWREFRESHTIME_FIELD",
    1760             :                            "networklink_viewrefreshtime");
    1761         361 :     oFC->networklink_viewBoundScale_field =
    1762         361 :         CPLGetConfigOption("LIBKML_NETWORKLINK_VIEWBOUNDSCALE_FIELD",
    1763             :                            "networklink_viewboundscale");
    1764         361 :     oFC->networklink_viewFormat_field = CPLGetConfigOption(
    1765             :         "LIBKML_NETWORKLINK_VIEWFORMAT_FIELD", "networklink_viewformat");
    1766         361 :     oFC->networklink_httpQuery_field = CPLGetConfigOption(
    1767             :         "LIBKML_NETWORKLINK_HTTPQUERY_FIELD", "networklink_httpquery");
    1768         361 :     oFC->camera_longitude_field =
    1769         361 :         CPLGetConfigOption("LIBKML_CAMERA_LONGITUDE_FIELD", "camera_longitude");
    1770         361 :     oFC->camera_latitude_field =
    1771         361 :         CPLGetConfigOption("LIBKML_CAMERA_LATITUDE_FIELD", "camera_latitude");
    1772         361 :     oFC->camera_altitude_field =
    1773         361 :         CPLGetConfigOption("LIBKML_CAMERA_ALTITUDE_FIELD", "camera_altitude");
    1774         361 :     oFC->camera_altitudemode_field = CPLGetConfigOption(
    1775             :         "LIBKML_CAMERA_ALTITUDEMODE_FIELD", "camera_altitudemode");
    1776         361 :     oFC->photooverlayfield =
    1777         361 :         CPLGetConfigOption("LIBKML_PHOTOOVERLAY_FIELD", "photooverlay");
    1778         361 :     oFC->leftfovfield = CPLGetConfigOption("LIBKML_LEFTFOV_FIELD", "leftfov");
    1779         361 :     oFC->rightfovfield =
    1780         361 :         CPLGetConfigOption("LIBKML_RIGHTFOV_FIELD", "rightfov");
    1781         361 :     oFC->bottomfovfield =
    1782         361 :         CPLGetConfigOption("LIBKML_BOTTOMFOV_FIELD", "bottomfov");
    1783         361 :     oFC->topfovfield = CPLGetConfigOption("LIBKML_TOPFOV_FIELD", "topfov");
    1784         361 :     oFC->nearfield = CPLGetConfigOption("LIBKML_NEARFOV_FIELD", "near");
    1785         361 :     oFC->photooverlay_shape_field = CPLGetConfigOption(
    1786             :         "LIBKML_PHOTOOVERLAY_SHAPE_FIELD", "photooverlay_shape");
    1787         361 :     oFC->imagepyramid_tilesize_field = CPLGetConfigOption(
    1788             :         "LIBKML_IMAGEPYRAMID_TILESIZE", "imagepyramid_tilesize");
    1789         361 :     oFC->imagepyramid_maxwidth_field = CPLGetConfigOption(
    1790             :         "LIBKML_IMAGEPYRAMID_MAXWIDTH", "imagepyramid_maxwidth");
    1791         361 :     oFC->imagepyramid_maxheight_field = CPLGetConfigOption(
    1792             :         "LIBKML_IMAGEPYRAMID_MAXHEIGHT", "imagepyramid_maxheight");
    1793         361 :     oFC->imagepyramid_gridorigin_field = CPLGetConfigOption(
    1794             :         "LIBKML_IMAGEPYRAMID_GRIDORIGIN", "imagepyramid_gridorigin");
    1795         361 : }
    1796             : 
    1797             : /************************************************************************/
    1798             : /*                 kmlAltitudeModeFromString()                          */
    1799             : /************************************************************************/
    1800             : 
    1801           8 : int kmlAltitudeModeFromString(const char *pszAltitudeMode, int &isGX)
    1802             : {
    1803           8 :     isGX = FALSE;
    1804           8 :     int iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_CLAMPTOGROUND);
    1805             : 
    1806           8 :     if (EQUAL(pszAltitudeMode, "clampToGround"))
    1807             :     {
    1808           0 :         iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_CLAMPTOGROUND);
    1809             :     }
    1810           8 :     else if (EQUAL(pszAltitudeMode, "relativeToGround"))
    1811             :     {
    1812           8 :         iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_RELATIVETOGROUND);
    1813             :     }
    1814           0 :     else if (EQUAL(pszAltitudeMode, "absolute"))
    1815             :     {
    1816           0 :         iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_ABSOLUTE);
    1817             :     }
    1818           0 :     else if (EQUAL(pszAltitudeMode, "relativeToSeaFloor"))
    1819             :     {
    1820           0 :         iAltitudeMode =
    1821             :             static_cast<int>(kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR);
    1822           0 :         isGX = TRUE;
    1823             :     }
    1824           0 :     else if (EQUAL(pszAltitudeMode, "clampToSeaFloor"))
    1825             :     {
    1826           0 :         iAltitudeMode =
    1827             :             static_cast<int>(kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR);
    1828           0 :         isGX = TRUE;
    1829             :     }
    1830             :     else
    1831             :     {
    1832           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    1833             :                  "Unrecognized value for altitudeMode: %s", pszAltitudeMode);
    1834             :     }
    1835             : 
    1836           8 :     return iAltitudeMode;
    1837             : }

Generated by: LCOV version 1.14