LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/libkml - ogrlibkmlfield.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 607 863 70.3 %
Date: 2024-05-02 14:43:06 Functions: 14 16 87.5 %

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

Generated by: LCOV version 1.14