LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/libkml - ogrlibkmlfeature.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 335 477 70.2 %
Date: 2026-06-19 21:24:00 Functions: 5 7 71.4 %

          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) 2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  *****************************************************************************/
      13             : 
      14             : #include "libkml_headers.h"
      15             : #include "ogrlibkmlfeature.h"
      16             : 
      17             : #include "gdal.h"
      18             : #include "ogr_geometry.h"
      19             : #include "ogr_libkml.h"
      20             : #include "ogrlibkmlfield.h"
      21             : #include "ogrlibkmlfeaturestyle.h"
      22             : #include "ogrlibkmlgeometry.h"
      23             : #include "ogrsf_frmts.h"
      24             : 
      25             : using kmldom::AliasPtr;
      26             : using kmldom::CameraPtr;
      27             : using kmldom::ElementPtr;
      28             : using kmldom::FeaturePtr;
      29             : using kmldom::GeometryPtr;
      30             : using kmldom::GroundOverlayPtr;
      31             : using kmldom::IconPtr;
      32             : using kmldom::ImagePyramidPtr;
      33             : using kmldom::KmlFactory;
      34             : using kmldom::LinkPtr;
      35             : using kmldom::LocationPtr;
      36             : using kmldom::ModelPtr;
      37             : using kmldom::NetworkLinkPtr;
      38             : using kmldom::OrientationPtr;
      39             : using kmldom::PhotoOverlayPtr;
      40             : using kmldom::PlacemarkPtr;
      41             : using kmldom::ResourceMapPtr;
      42             : using kmldom::ScalePtr;
      43             : using kmldom::ViewVolumePtr;
      44             : 
      45         203 : static CameraPtr feat2kmlcamera(const struct fieldconfig &oFC, int iHeading,
      46             :                                 int iTilt, int iRoll, OGRFeature *poOgrFeat,
      47             :                                 KmlFactory *poKmlFactory)
      48             : {
      49             :     const int iCameraLongitudeField =
      50         203 :         poOgrFeat->GetFieldIndex(oFC.camera_longitude_field);
      51             :     const int iCameraLatitudeField =
      52         203 :         poOgrFeat->GetFieldIndex(oFC.camera_latitude_field);
      53             :     const int iCameraAltitudeField =
      54         203 :         poOgrFeat->GetFieldIndex(oFC.camera_altitude_field);
      55             :     const int iCameraAltitudeModeField =
      56         203 :         poOgrFeat->GetFieldIndex(oFC.camera_altitudemode_field);
      57             : 
      58             :     const bool bNeedCamera =
      59           2 :         iCameraLongitudeField >= 0 &&
      60           2 :         poOgrFeat->IsFieldSetAndNotNull(iCameraLongitudeField) &&
      61           2 :         iCameraLatitudeField >= 0 &&
      62         207 :         poOgrFeat->IsFieldSetAndNotNull(iCameraLatitudeField) &&
      63           2 :         ((iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading)) ||
      64           0 :          (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt)) ||
      65           0 :          (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll)));
      66             : 
      67         203 :     if (!bNeedCamera)
      68         201 :         return nullptr;
      69             : 
      70           4 :     CameraPtr const camera = poKmlFactory->CreateCamera();
      71           2 :     camera->set_latitude(poOgrFeat->GetFieldAsDouble(iCameraLatitudeField));
      72           2 :     camera->set_longitude(poOgrFeat->GetFieldAsDouble(iCameraLongitudeField));
      73           2 :     int isGX = FALSE;
      74             : 
      75           4 :     if (iCameraAltitudeModeField >= 0 &&
      76           2 :         poOgrFeat->IsFieldSetAndNotNull(iCameraAltitudeModeField))
      77             :     {
      78           2 :         const int nAltitudeMode = kmlAltitudeModeFromString(
      79             :             poOgrFeat->GetFieldAsString(iCameraAltitudeModeField), isGX);
      80           2 :         camera->set_altitudemode(nAltitudeMode);
      81             :     }
      82           0 :     else if (CPLTestBool(
      83             :                  CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
      84             :     {
      85           0 :         CPLError(CE_Warning, CPLE_AppDefined,
      86             :                  "Camera should define altitudeMode != 'clampToGround'");
      87             :     }
      88             : 
      89           4 :     if (iCameraAltitudeField >= 0 &&
      90           2 :         poOgrFeat->IsFieldSetAndNotNull(iCameraAltitudeField))
      91             :     {
      92           2 :         camera->set_altitude(poOgrFeat->GetFieldAsDouble(iCameraAltitudeField));
      93             :     }
      94           0 :     else if (CPLTestBool(
      95             :                  CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
      96             :     {
      97           0 :         CPLError(CE_Warning, CPLE_AppDefined,
      98             :                  "Camera should have an altitude/Z");
      99           0 :         camera->set_altitude(0.0);
     100             :     }
     101             : 
     102           2 :     if (iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading))
     103           2 :         camera->set_heading(poOgrFeat->GetFieldAsDouble(iHeading));
     104           2 :     if (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt))
     105           2 :         camera->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt));
     106           2 :     if (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))
     107           2 :         camera->set_roll(poOgrFeat->GetFieldAsDouble(iRoll));
     108             : 
     109           2 :     return camera;
     110             : }
     111             : 
     112             : /************************************************************************/
     113             : /*                    OGRLIBKMLReplaceXYLevelInURL()                    */
     114             : /************************************************************************/
     115             : 
     116           0 : static CPLString OGRLIBKMLReplaceLevelXYInURL(const char *pszURL, int level,
     117             :                                               int x, int y)
     118             : {
     119           0 :     CPLString osRet(pszURL);
     120           0 :     size_t nPos = osRet.find("$[level]");
     121           0 :     osRet = osRet.substr(0, nPos) + CPLSPrintf("%d", level) +
     122           0 :             osRet.substr(nPos + strlen("$[level]"));
     123             : 
     124           0 :     nPos = osRet.find("$[x]");
     125           0 :     osRet = osRet.substr(0, nPos) + CPLSPrintf("%d", x) +
     126           0 :             osRet.substr(nPos + strlen("$[x]"));
     127             : 
     128           0 :     nPos = osRet.find("$[y]");
     129           0 :     osRet = osRet.substr(0, nPos) + CPLSPrintf("%d", y) +
     130           0 :             osRet.substr(nPos + strlen("$[y]"));
     131             : 
     132           0 :     return osRet;
     133             : }
     134             : 
     135             : /************************************************************************/
     136             : /*                              IsPowerOf2                              */
     137             : /************************************************************************/
     138             : 
     139           1 : static bool IsPowerOf2(int nVal)
     140             : {
     141           1 :     if (nVal < 1)
     142           0 :         return false;
     143             : 
     144           1 :     const unsigned int nTmp = static_cast<unsigned int>(nVal);
     145             : 
     146           1 :     return (nTmp & (nTmp - 1)) == 0;
     147             : }
     148             : 
     149             : /************************************************************************/
     150             : /*                     OGRLIBKMLGetMaxDimensions()                      */
     151             : /************************************************************************/
     152             : 
     153           0 : static void OGRLIBKMLGetMaxDimensions(const char *pszURL, int nTileSize,
     154             :                                       int *panMaxWidth, int *panMaxHeight)
     155             : {
     156             :     VSIStatBufL sStat;
     157           0 :     int nMaxLevel = 0;
     158           0 :     *panMaxWidth = 0;
     159           0 :     *panMaxHeight = 0;
     160             :     while (true)
     161             :     {
     162           0 :         CPLString osURL = OGRLIBKMLReplaceLevelXYInURL(pszURL, nMaxLevel, 0, 0);
     163           0 :         if (strstr(osURL, ".kmz/"))
     164           0 :             osURL = "/vsizip/" + osURL;
     165           0 :         if (VSIStatL(osURL, &sStat) == 0)
     166           0 :             nMaxLevel++;
     167             :         else
     168             :         {
     169           0 :             if (nMaxLevel == 0)
     170           0 :                 return;
     171           0 :             break;
     172             :         }
     173           0 :     }
     174           0 :     nMaxLevel--;
     175             : 
     176             :     {
     177           0 :         int i = 0;  // Used after for.
     178           0 :         for (;; i++)
     179             :         {
     180             :             CPLString osURL =
     181           0 :                 OGRLIBKMLReplaceLevelXYInURL(pszURL, nMaxLevel, i + 1, 0);
     182           0 :             if (strstr(osURL, ".kmz/"))
     183           0 :                 osURL = "/vsizip/" + osURL;
     184           0 :             if (VSIStatL(osURL, &sStat) != 0)
     185           0 :                 break;
     186           0 :         }
     187           0 :         *panMaxWidth = (i + 1) * nTileSize;
     188             :     }
     189             : 
     190           0 :     int i = 0;  // Used after for.
     191           0 :     for (;; i++)
     192             :     {
     193             :         CPLString osURL =
     194           0 :             OGRLIBKMLReplaceLevelXYInURL(pszURL, nMaxLevel, 0, i + 1);
     195           0 :         if (strstr(osURL, ".kmz/"))
     196           0 :             osURL = "/vsizip/" + osURL;
     197           0 :         if (VSIStatL(osURL, &sStat) != 0)
     198           0 :             break;
     199           0 :     }
     200           0 :     *panMaxHeight = (i + 1) * nTileSize;
     201             : }
     202             : 
     203             : /************************************************************************/
     204             : /*                              feat2kml()                              */
     205             : /************************************************************************/
     206             : 
     207         218 : FeaturePtr feat2kml(OGRLIBKMLDataSource *poOgrDS, OGRLIBKMLLayer *poOgrLayer,
     208             :                     OGRFeature *poOgrFeat, KmlFactory *poKmlFactory,
     209             :                     int bUseSimpleField)
     210             : {
     211         436 :     FeaturePtr poKmlFeature = nullptr;
     212         218 :     const auto &oFC = poOgrLayer->GetFieldConfig();
     213             : 
     214             :     /***** geometry *****/
     215         218 :     OGRGeometry *poOgrGeom = poOgrFeat->GetGeometryRef();
     216         218 :     const int iHeading = poOgrFeat->GetFieldIndex(oFC.headingfield);
     217         218 :     const int iTilt = poOgrFeat->GetFieldIndex(oFC.tiltfield);
     218         218 :     const int iRoll = poOgrFeat->GetFieldIndex(oFC.rollfield);
     219         218 :     const int iModel = poOgrFeat->GetFieldIndex(oFC.modelfield);
     220         218 :     const int iNetworkLink = poOgrFeat->GetFieldIndex(oFC.networklinkfield);
     221         218 :     const int iPhotoOverlay = poOgrFeat->GetFieldIndex(oFC.photooverlayfield);
     222         436 :     CameraPtr camera = nullptr;
     223             : 
     224             :     // PhotoOverlay.
     225           2 :     if (iPhotoOverlay >= 0 && poOgrFeat->IsFieldSetAndNotNull(iPhotoOverlay) &&
     226           2 :         poOgrGeom != nullptr && !poOgrGeom->IsEmpty() &&
     227         222 :         wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint &&
     228         220 :         (camera = feat2kmlcamera(oFC, iHeading, iTilt, iRoll, poOgrFeat,
     229           2 :                                  poKmlFactory)))
     230             :     {
     231           2 :         const int iLeftFovField = poOgrFeat->GetFieldIndex(oFC.leftfovfield);
     232           2 :         const int iRightFovField = poOgrFeat->GetFieldIndex(oFC.rightfovfield);
     233             :         const int iBottomFovField =
     234           2 :             poOgrFeat->GetFieldIndex(oFC.bottomfovfield);
     235           2 :         const int iTopFovField = poOgrFeat->GetFieldIndex(oFC.topfovfield);
     236           2 :         const int iNearField = poOgrFeat->GetFieldIndex(oFC.nearfield);
     237             : 
     238           2 :         const char *pszURL = poOgrFeat->GetFieldAsString(iPhotoOverlay);
     239             :         const int iImagePyramidTileSize =
     240           2 :             poOgrFeat->GetFieldIndex(oFC.imagepyramid_tilesize_field);
     241             :         const int iImagePyramidMaxWidth =
     242           2 :             poOgrFeat->GetFieldIndex(oFC.imagepyramid_maxwidth_field);
     243             :         const int iImagePyramidMaxHeight =
     244           2 :             poOgrFeat->GetFieldIndex(oFC.imagepyramid_maxheight_field);
     245             :         const int iImagePyramidGridOrigin =
     246           2 :             poOgrFeat->GetFieldIndex(oFC.imagepyramid_gridorigin_field);
     247             : 
     248           2 :         int nTileSize = 0;
     249           2 :         int nMaxWidth = 0;
     250           2 :         int nMaxHeight = 0;
     251           2 :         bool bIsTiledPhotoOverlay = false;
     252           2 :         bool bGridOriginIsUpperLeft = true;
     253             :         // OGC KML Abstract Test Case (ATC) 52 and 62
     254           2 :         if (strstr(pszURL, "$[x]") && strstr(pszURL, "$[y]") &&
     255           1 :             strstr(pszURL, "$[level]"))
     256             :         {
     257           1 :             bIsTiledPhotoOverlay = true;
     258           1 :             bool bErrorEmitted = false;
     259           2 :             if (iImagePyramidTileSize < 0 ||
     260           1 :                 !poOgrFeat->IsFieldSetAndNotNull(iImagePyramidTileSize))
     261             :             {
     262           0 :                 CPLDebug("LIBKML",
     263             :                          "Missing ImagePyramid tileSize. Computing it");
     264           0 :                 CPLString osURL = OGRLIBKMLReplaceLevelXYInURL(pszURL, 0, 0, 0);
     265           0 :                 if (strstr(osURL, ".kmz/"))
     266           0 :                     osURL = "/vsizip/" + osURL;
     267           0 :                 GDALDatasetH hDS = GDALDataset::ToHandle(GDALDataset::Open(
     268             :                     osURL, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
     269           0 :                 if (hDS != nullptr)
     270             :                 {
     271           0 :                     nTileSize = GDALGetRasterXSize(hDS);
     272           0 :                     if (nTileSize != GDALGetRasterYSize(hDS))
     273             :                     {
     274           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     275             :                                  "Non square tile : %dx%d",
     276             :                                  GDALGetRasterXSize(hDS),
     277             :                                  GDALGetRasterYSize(hDS));
     278           0 :                         nTileSize = 0;
     279           0 :                         bErrorEmitted = true;
     280             :                     }
     281           0 :                     GDALClose(hDS);
     282             :                 }
     283             :                 else
     284             :                 {
     285           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
     286             :                              osURL.c_str());
     287           0 :                     bErrorEmitted = true;
     288             :                 }
     289             :             }
     290             :             else
     291             :             {
     292           1 :                 nTileSize = poOgrFeat->GetFieldAsInteger(iImagePyramidTileSize);
     293             :             }
     294           1 :             if (!bErrorEmitted && (nTileSize <= 1 || !IsPowerOf2(nTileSize)))
     295             :             {
     296           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     297             :                          "Tile size is not a power of two: %d", nTileSize);
     298           0 :                 nTileSize = 0;
     299             :             }
     300             : 
     301           1 :             if (nTileSize > 0)
     302             :             {
     303           2 :                 if (iImagePyramidMaxWidth < 0 ||
     304           1 :                     !poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxWidth) ||
     305           2 :                     iImagePyramidMaxHeight < 0 ||
     306           1 :                     !poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxHeight))
     307             :                 {
     308           0 :                     CPLDebug("LIBKML",
     309             :                              "Missing ImagePyramid maxWidth and/or maxHeight. "
     310             :                              "Computing it");
     311           0 :                     OGRLIBKMLGetMaxDimensions(pszURL, nTileSize, &nMaxWidth,
     312             :                                               &nMaxHeight);
     313             :                 }
     314             :                 else
     315             :                 {
     316           1 :                     nMaxWidth =
     317           1 :                         poOgrFeat->GetFieldAsInteger(iImagePyramidMaxWidth);
     318           1 :                     nMaxHeight =
     319           1 :                         poOgrFeat->GetFieldAsInteger(iImagePyramidMaxHeight);
     320             :                 }
     321             : 
     322           1 :                 if (nMaxWidth <= 0 || nMaxHeight <= 0)
     323             :                 {
     324           0 :                     CPLError(
     325             :                         CE_Failure, CPLE_AppDefined,
     326             :                         "Cannot generate PhotoOverlay object since there are "
     327             :                         "missing information to generate ImagePyramid element");
     328             :                 }
     329             :             }
     330             : 
     331           1 :             if (iImagePyramidGridOrigin >= 0 &&
     332           0 :                 poOgrFeat->IsFieldSetAndNotNull(iImagePyramidGridOrigin))
     333             :             {
     334             :                 const char *pszGridOrigin =
     335           0 :                     poOgrFeat->GetFieldAsString(iImagePyramidGridOrigin);
     336           0 :                 if (EQUAL(pszGridOrigin, "UpperLeft"))
     337             :                 {
     338           0 :                     bGridOriginIsUpperLeft = true;
     339             :                 }
     340           0 :                 else if (EQUAL(pszGridOrigin, "BottomLeft"))
     341             :                 {
     342           0 :                     bGridOriginIsUpperLeft = false;
     343             :                 }
     344             :                 else
     345             :                 {
     346           0 :                     CPLError(
     347             :                         CE_Failure, CPLE_AppDefined,
     348             :                         "Unhandled value for imagepyramid_gridorigin : %s. "
     349             :                         "Assuming UpperLeft",
     350             :                         pszGridOrigin);
     351             :                 }
     352           1 :             }
     353             :         }
     354             :         else
     355             :         {
     356           2 :             if ((iImagePyramidTileSize >= 0 &&
     357           1 :                  poOgrFeat->IsFieldSetAndNotNull(iImagePyramidTileSize)) ||
     358           1 :                 (iImagePyramidMaxWidth >= 0 &&
     359           1 :                  poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxWidth)) ||
     360           1 :                 (iImagePyramidMaxHeight >= 0 &&
     361           2 :                  poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxHeight)) ||
     362           0 :                 (iImagePyramidGridOrigin >= 0 &&
     363           0 :                  poOgrFeat->IsFieldSetAndNotNull(iImagePyramidGridOrigin)))
     364             :             {
     365           0 :                 CPLError(
     366             :                     CE_Warning, CPLE_AppDefined,
     367             :                     "Ignoring any ImagePyramid information since the URL does "
     368             :                     "not include $[x] and/or $[y] and/or $[level]");
     369             :             }
     370             :         }
     371             : 
     372             :         // OGC KML Abstract Test Case (ATC) 19 & 35.
     373           2 :         double dfNear = 0.0;
     374             : 
     375           1 :         if ((!bIsTiledPhotoOverlay ||
     376           2 :              (nTileSize > 0 && nMaxWidth > 0 && nMaxHeight > 0)) &&
     377           2 :             iLeftFovField >= 0 &&
     378           2 :             poOgrFeat->IsFieldSetAndNotNull(iLeftFovField) &&
     379           2 :             iRightFovField >= 0 &&
     380           2 :             poOgrFeat->IsFieldSetAndNotNull(iRightFovField) &&
     381           2 :             iBottomFovField >= 0 &&
     382           2 :             poOgrFeat->IsFieldSetAndNotNull(iBottomFovField) &&
     383           2 :             iTopFovField >= 0 &&
     384           6 :             poOgrFeat->IsFieldSetAndNotNull(iTopFovField) && iNearField >= 0 &&
     385           2 :             (dfNear = poOgrFeat->GetFieldAsDouble(iNearField)) > 0)
     386             :         {
     387             :             const PhotoOverlayPtr poKmlPhotoOverlay =
     388           2 :                 poKmlFactory->CreatePhotoOverlay();
     389           2 :             poKmlFeature = poKmlPhotoOverlay;
     390             : 
     391           2 :             const IconPtr poKmlIcon = poKmlFactory->CreateIcon();
     392           2 :             poKmlPhotoOverlay->set_icon(poKmlIcon);
     393           2 :             poKmlIcon->set_href(pszURL);
     394             : 
     395             :             const ViewVolumePtr poKmlViewVolume =
     396           2 :                 poKmlFactory->CreateViewVolume();
     397           2 :             poKmlPhotoOverlay->set_viewvolume(poKmlViewVolume);
     398             : 
     399           2 :             const double dfLeftFov = poOgrFeat->GetFieldAsDouble(iLeftFovField);
     400             :             const double dfRightFov =
     401           2 :                 poOgrFeat->GetFieldAsDouble(iRightFovField);
     402             :             const double dfBottomFov =
     403           2 :                 poOgrFeat->GetFieldAsDouble(iBottomFovField);
     404           2 :             const double dfTopFov = poOgrFeat->GetFieldAsDouble(iTopFovField);
     405             : 
     406           2 :             poKmlViewVolume->set_leftfov(dfLeftFov);
     407           2 :             poKmlViewVolume->set_rightfov(dfRightFov);
     408           2 :             poKmlViewVolume->set_bottomfov(dfBottomFov);
     409           2 :             poKmlViewVolume->set_topfov(dfTopFov);
     410           2 :             poKmlViewVolume->set_near(dfNear);
     411             : 
     412           2 :             if (bIsTiledPhotoOverlay)
     413             :             {
     414             :                 const ImagePyramidPtr poKmlImagePyramid =
     415           2 :                     poKmlFactory->CreateImagePyramid();
     416           1 :                 poKmlPhotoOverlay->set_imagepyramid(poKmlImagePyramid);
     417             : 
     418           1 :                 poKmlImagePyramid->set_tilesize(nTileSize);
     419           1 :                 poKmlImagePyramid->set_maxwidth(nMaxWidth);
     420           1 :                 poKmlImagePyramid->set_maxheight(nMaxHeight);
     421           1 :                 poKmlImagePyramid->set_gridorigin(
     422             :                     bGridOriginIsUpperLeft ? kmldom::GRIDORIGIN_UPPERLEFT
     423             :                                            : kmldom::GRIDORIGIN_LOWERLEFT);
     424             :             }
     425             : 
     426             :             const int iPhotoOverlayShapeField =
     427           2 :                 poOgrFeat->GetFieldIndex(oFC.photooverlay_shape_field);
     428           4 :             if (iPhotoOverlayShapeField >= 0 &&
     429           2 :                 poOgrFeat->IsFieldSetAndNotNull(iPhotoOverlayShapeField))
     430             :             {
     431             :                 const char *pszShape =
     432           2 :                     poOgrFeat->GetFieldAsString(iPhotoOverlayShapeField);
     433           2 :                 if (EQUAL(pszShape, "rectangle"))
     434           2 :                     poKmlPhotoOverlay->set_shape(kmldom::SHAPE_RECTANGLE);
     435           0 :                 else if (EQUAL(pszShape, "cylinder"))
     436           0 :                     poKmlPhotoOverlay->set_shape(kmldom::SHAPE_CYLINDER);
     437           0 :                 else if (EQUAL(pszShape, "sphere"))
     438           0 :                     poKmlPhotoOverlay->set_shape(kmldom::SHAPE_SPHERE);
     439             :             }
     440             : 
     441           2 :             ElementPtr poKmlElement = geom2kml(poOgrGeom, -1, poKmlFactory);
     442           2 :             if (!poKmlElement)
     443           0 :                 return nullptr;
     444             : 
     445           2 :             poKmlPhotoOverlay->set_point(AsPoint(std::move(poKmlElement)));
     446             :         }
     447             :     }
     448             : 
     449             :     // NetworkLink.
     450         221 :     if (!poKmlFeature && iNetworkLink >= 0 &&
     451           3 :         poOgrFeat->IsFieldSetAndNotNull(iNetworkLink))
     452             :     {
     453             :         const NetworkLinkPtr poKmlNetworkLink =
     454           6 :             poKmlFactory->CreateNetworkLink();
     455           3 :         poKmlFeature = poKmlNetworkLink;
     456             : 
     457             :         const int iRefreshVisibility =
     458           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_refreshvisibility_field);
     459             : 
     460           6 :         if (iRefreshVisibility >= 0 &&
     461           3 :             poOgrFeat->IsFieldSetAndNotNull(iRefreshVisibility))
     462             :         {
     463           2 :             poKmlNetworkLink->set_refreshvisibility(
     464           1 :                 CPL_TO_BOOL(poOgrFeat->GetFieldAsInteger(iRefreshVisibility)));
     465             :         }
     466             : 
     467             :         const int iFlyToView =
     468           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_flytoview_field);
     469             : 
     470           3 :         if (iFlyToView >= 0 && poOgrFeat->IsFieldSetAndNotNull(iFlyToView))
     471           2 :             poKmlNetworkLink->set_flytoview(
     472           1 :                 CPL_TO_BOOL(poOgrFeat->GetFieldAsInteger(iFlyToView)));
     473             : 
     474           6 :         const LinkPtr poKmlLink = poKmlFactory->CreateLink();
     475           3 :         poKmlLink->set_href(poOgrFeat->GetFieldAsString(iNetworkLink));
     476           3 :         poKmlNetworkLink->set_link(poKmlLink);
     477             : 
     478             :         const int iRefreshMode =
     479           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_refreshMode_field);
     480             :         const int iRefreshInterval =
     481           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_refreshInterval_field);
     482             :         const int iViewRefreshMode =
     483           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_viewRefreshMode_field);
     484             :         const int iViewRefreshTime =
     485           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_viewRefreshTime_field);
     486             :         const int iViewBoundScale =
     487           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_viewBoundScale_field);
     488             :         const int iViewFormat =
     489           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_viewFormat_field);
     490             :         const int iHttpQuery =
     491           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_httpQuery_field);
     492             : 
     493           3 :         double dfRefreshInterval = 0.0;
     494           6 :         if (iRefreshInterval >= 0 &&
     495           3 :             poOgrFeat->IsFieldSetAndNotNull(iRefreshInterval))
     496             :         {
     497           1 :             dfRefreshInterval = poOgrFeat->GetFieldAsDouble(iRefreshInterval);
     498           1 :             if (dfRefreshInterval < 0)
     499           0 :                 dfRefreshInterval = 0.0;
     500             :         }
     501             : 
     502           3 :         double dfViewRefreshTime = 0.0;
     503           6 :         if (iViewRefreshTime >= 0 &&
     504           3 :             poOgrFeat->IsFieldSetAndNotNull(iViewRefreshTime))
     505             :         {
     506           1 :             dfViewRefreshTime = poOgrFeat->GetFieldAsDouble(iViewRefreshTime);
     507           1 :             if (dfViewRefreshTime < 0)
     508           0 :                 dfViewRefreshTime = 0.0;
     509             :         }
     510             : 
     511           3 :         if (dfRefreshInterval > 0)  // ATC 51
     512           1 :             poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONINTERVAL);
     513           4 :         else if (iRefreshMode >= 0 &&
     514           2 :                  poOgrFeat->IsFieldSetAndNotNull(iRefreshMode))
     515             :         {
     516             :             const char *const pszRefreshMode =
     517           1 :                 poOgrFeat->GetFieldAsString(iRefreshMode);
     518           1 :             if (EQUAL(pszRefreshMode, "onChange"))
     519           0 :                 poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONCHANGE);
     520           1 :             else if (EQUAL(pszRefreshMode, "onInterval"))
     521           0 :                 poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONINTERVAL);
     522           1 :             else if (EQUAL(pszRefreshMode, "onExpire"))
     523           1 :                 poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONEXPIRE);
     524             :         }
     525             : 
     526           3 :         if (dfRefreshInterval > 0)  // ATC 9
     527           1 :             poKmlLink->set_refreshinterval(dfRefreshInterval);
     528             : 
     529           3 :         if (dfViewRefreshTime > 0)  // ATC 51
     530           1 :             poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONSTOP);
     531           4 :         else if (iViewRefreshMode >= 0 &&
     532           2 :                  poOgrFeat->IsFieldSetAndNotNull(iViewRefreshMode))
     533             :         {
     534             :             const char *const pszViewRefreshMode =
     535           1 :                 poOgrFeat->GetFieldAsString(iViewRefreshMode);
     536           1 :             if (EQUAL(pszViewRefreshMode, "never"))
     537           0 :                 poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_NEVER);
     538           1 :             else if (EQUAL(pszViewRefreshMode, "onRequest"))
     539           0 :                 poKmlLink->set_viewrefreshmode(
     540             :                     kmldom::VIEWREFRESHMODE_ONREQUEST);
     541           1 :             else if (EQUAL(pszViewRefreshMode, "onStop"))
     542           0 :                 poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONSTOP);
     543           1 :             else if (EQUAL(pszViewRefreshMode, "onRegion"))
     544           1 :                 poKmlLink->set_viewrefreshmode(
     545             :                     kmldom::VIEWREFRESHMODE_ONREGION);
     546             :         }
     547             : 
     548           3 :         if (dfViewRefreshTime > 0)  // ATC 9
     549           1 :             poKmlLink->set_viewrefreshtime(dfViewRefreshTime);
     550             : 
     551           6 :         if (iViewBoundScale >= 0 &&
     552           3 :             poOgrFeat->IsFieldSetAndNotNull(iViewBoundScale))
     553             :         {
     554             :             const double dfViewBoundScale =
     555           1 :                 poOgrFeat->GetFieldAsDouble(iViewBoundScale);
     556           1 :             if (dfViewBoundScale > 0)  // ATC 9
     557           1 :                 poKmlLink->set_viewboundscale(dfViewBoundScale);
     558             :         }
     559             : 
     560           3 :         if (iViewFormat >= 0 && poOgrFeat->IsFieldSetAndNotNull(iViewFormat))
     561             :         {
     562             :             const char *const pszViewFormat =
     563           1 :                 poOgrFeat->GetFieldAsString(iViewFormat);
     564           1 :             if (pszViewFormat[0] != '\0')  // ATC 46
     565           1 :                 poKmlLink->set_viewformat(pszViewFormat);
     566             :         }
     567             : 
     568           3 :         if (iHttpQuery >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHttpQuery))
     569             :         {
     570             :             const char *const pszHttpQuery =
     571           1 :                 poOgrFeat->GetFieldAsString(iHttpQuery);
     572           1 :             if (strstr(pszHttpQuery, "[clientVersion]") != nullptr ||
     573           0 :                 strstr(pszHttpQuery, "[kmlVersion]") != nullptr ||
     574           0 :                 strstr(pszHttpQuery, "[clientName]") != nullptr ||
     575           0 :                 strstr(pszHttpQuery, "[language]") != nullptr)  // ATC 47
     576             :             {
     577           1 :                 poKmlLink->set_httpquery(pszHttpQuery);
     578             :             }
     579             :         }
     580             :     }
     581             : 
     582             :     // Model.
     583         428 :     else if (!poKmlFeature && iModel >= 0 &&
     584           2 :              poOgrFeat->IsFieldSetAndNotNull(iModel) && poOgrGeom != nullptr &&
     585         430 :              !poOgrGeom->IsEmpty() &&
     586           2 :              wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint)
     587             :     {
     588           4 :         const PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
     589           2 :         poKmlFeature = poKmlPlacemark;
     590             : 
     591           2 :         const OGRPoint *const poOgrPoint = poOgrGeom->toPoint();
     592           4 :         ModelPtr model = poKmlFactory->CreateModel();
     593             : 
     594           4 :         LocationPtr location = poKmlFactory->CreateLocation();
     595           2 :         model->set_location(location);
     596           2 :         location->set_latitude(poOgrPoint->getY());
     597           2 :         location->set_longitude(poOgrPoint->getX());
     598           2 :         if (poOgrPoint->getCoordinateDimension() == 3)
     599           1 :             location->set_altitude(poOgrPoint->getZ());
     600             : 
     601           2 :         int isGX = FALSE;
     602             :         const int iAltitudeMode =
     603           2 :             poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
     604           2 :         if (poOgrFeat->IsFieldSetAndNotNull(iAltitudeMode))
     605             :         {
     606           1 :             const int nAltitudeMode = kmlAltitudeModeFromString(
     607             :                 poOgrFeat->GetFieldAsString(iAltitudeMode), isGX);
     608           1 :             model->set_altitudemode(nAltitudeMode);
     609             : 
     610             :             // ATC 55
     611           2 :             if (nAltitudeMode != kmldom::ALTITUDEMODE_CLAMPTOGROUND &&
     612           1 :                 poOgrPoint->getCoordinateDimension() != 3)
     613             :             {
     614           0 :                 if (CPLTestBool(
     615             :                         CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
     616           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     617             :                              "Altitude should be defined");
     618             :             }
     619             :         }
     620             : 
     621           2 :         if ((iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading)) ||
     622           5 :             (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt)) ||
     623           1 :             (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll)))
     624             :         {
     625             :             OrientationPtr const orientation =
     626           2 :                 poKmlFactory->CreateOrientation();
     627           1 :             model->set_orientation(orientation);
     628           1 :             if (iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading))
     629           1 :                 orientation->set_heading(poOgrFeat->GetFieldAsDouble(iHeading));
     630             :             else
     631           0 :                 orientation->set_heading(0);
     632           1 :             if (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt))
     633           1 :                 orientation->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt));
     634             :             else
     635           0 :                 orientation->set_tilt(0);
     636           1 :             if (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))
     637           1 :                 orientation->set_roll(poOgrFeat->GetFieldAsDouble(iRoll));
     638             :             else
     639           0 :                 orientation->set_roll(0);
     640             :         }
     641           2 :         const int iScaleX = poOgrFeat->GetFieldIndex(oFC.scalexfield);
     642           2 :         const int iScaleY = poOgrFeat->GetFieldIndex(oFC.scaleyfield);
     643           2 :         const int iScaleZ = poOgrFeat->GetFieldIndex(oFC.scalezfield);
     644             : 
     645           4 :         const ScalePtr scale = poKmlFactory->CreateScale();
     646           2 :         model->set_scale(scale);
     647           2 :         if (iScaleX >= 0 && poOgrFeat->IsFieldSetAndNotNull(iScaleX))
     648           1 :             scale->set_x(poOgrFeat->GetFieldAsDouble(iScaleX));
     649             :         else
     650           1 :             scale->set_x(1.0);
     651           2 :         if (iScaleY >= 0 && poOgrFeat->IsFieldSetAndNotNull(iScaleY))
     652           1 :             scale->set_y(poOgrFeat->GetFieldAsDouble(iScaleY));
     653             :         else
     654           1 :             scale->set_y(1.0);
     655           2 :         if (iScaleZ >= 0 && poOgrFeat->IsFieldSetAndNotNull(iScaleZ))
     656           1 :             scale->set_z(poOgrFeat->GetFieldAsDouble(iScaleZ));
     657             :         else
     658           1 :             scale->set_z(1.0);
     659             : 
     660           2 :         const LinkPtr link = poKmlFactory->CreateLink();
     661           2 :         model->set_link(link);
     662           2 :         const char *const pszURL = poOgrFeat->GetFieldAsString(oFC.modelfield);
     663           2 :         link->set_href(pszURL);
     664             : 
     665             :         // Collada 3D file?
     666           3 :         if (EQUAL(CPLGetExtensionSafe(pszURL).c_str(), "dae") &&
     667           1 :             CPLTestBool(CPLGetConfigOption("LIBKML_ADD_RESOURCE_MAP", "TRUE")))
     668             :         {
     669           1 :             VSILFILE *fp = nullptr;
     670           1 :             bool bIsURL = false;
     671           1 :             if (STARTS_WITH_CI(pszURL, "http://") ||
     672           0 :                 STARTS_WITH_CI(pszURL, "https://"))
     673             :             {
     674           1 :                 bIsURL = true;
     675           1 :                 fp = VSIFOpenL(CPLSPrintf("/vsicurl/%s", pszURL), "rb");
     676             :             }
     677           0 :             else if (strstr(pszURL, ".kmz/") != nullptr)
     678             :             {
     679           0 :                 fp = VSIFOpenL(CPLSPrintf("/vsizip/%s", pszURL), "rb");
     680             :             }
     681             :             else
     682             :             {
     683           0 :                 fp = VSIFOpenL(pszURL, "rb");
     684             :             }
     685           1 :             if (fp != nullptr)
     686             :             {
     687           0 :                 ResourceMapPtr resourceMap = nullptr;
     688           0 :                 const char *pszLine = nullptr;
     689           0 :                 while ((pszLine = CPLReadLineL(fp)) != nullptr)
     690             :                 {
     691           0 :                     const char *pszInitFrom = strstr(pszLine, "<init_from>");
     692           0 :                     if (pszInitFrom)
     693             :                     {
     694           0 :                         pszInitFrom += strlen("<init_from>");
     695             :                         const char *const pszInitFromEnd =
     696           0 :                             strstr(pszInitFrom, "</init_from>");
     697           0 :                         if (pszInitFromEnd)
     698             :                         {
     699           0 :                             CPLString osImage(pszInitFrom);
     700           0 :                             osImage.resize(pszInitFromEnd - pszInitFrom);
     701             :                             const std::string osExtension =
     702           0 :                                 CPLGetExtensionSafe(osImage);
     703           0 :                             if (EQUAL(osExtension.c_str(), "jpg") ||
     704           0 :                                 EQUAL(osExtension.c_str(), "jpeg") ||
     705           0 :                                 EQUAL(osExtension.c_str(), "png") ||
     706           0 :                                 EQUAL(osExtension.c_str(), "gif"))
     707             :                             {
     708           0 :                                 if (!resourceMap)
     709             :                                     resourceMap =
     710           0 :                                         poKmlFactory->CreateResourceMap();
     711             :                                 const AliasPtr alias =
     712           0 :                                     poKmlFactory->CreateAlias();
     713           0 :                                 if (bIsURL && CPLIsFilenameRelative(osImage))
     714             :                                 {
     715           0 :                                     if (STARTS_WITH(pszURL, "http"))
     716           0 :                                         alias->set_targethref(CPLSPrintf(
     717             :                                             "%s/%s",
     718           0 :                                             CPLGetPathSafe(pszURL).c_str(),
     719             :                                             osImage.c_str()));
     720             :                                     else
     721           0 :                                         alias->set_targethref(
     722           0 :                                             CPLFormFilenameSafe(
     723           0 :                                                 CPLGetPathSafe(pszURL).c_str(),
     724             :                                                 osImage, nullptr));
     725             :                                 }
     726             :                                 else
     727           0 :                                     alias->set_targethref(osImage);
     728           0 :                                 alias->set_sourcehref(osImage);
     729           0 :                                 resourceMap->add_alias(alias);
     730             :                             }
     731             :                         }
     732             :                     }
     733             :                 }
     734           0 :                 if (resourceMap)
     735           0 :                     model->set_resourcemap(resourceMap);
     736           0 :                 VSIFCloseL(fp);
     737             :             }
     738             :         }
     739             : 
     740           2 :         poKmlPlacemark->set_geometry(AsGeometry(model));
     741             :     }
     742             : 
     743             :     // Camera.
     744         424 :     else if (!poKmlFeature && poOgrGeom != nullptr && !poOgrGeom->IsEmpty() &&
     745         159 :              wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint &&
     746         477 :              poOgrFeat->GetFieldIndex(oFC.camera_longitude_field) < 0 &&
     747          53 :              ((iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading)) ||
     748          51 :               (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt)) ||
     749           0 :               (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))))
     750             :     {
     751           2 :         const PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
     752           2 :         poKmlFeature = poKmlPlacemark;
     753             : 
     754           2 :         const OGRPoint *const poOgrPoint = poOgrGeom->toPoint();
     755           2 :         camera = poKmlFactory->CreateCamera();
     756           2 :         camera->set_latitude(poOgrPoint->getY());
     757           2 :         camera->set_longitude(poOgrPoint->getX());
     758           2 :         int isGX = FALSE;
     759             :         const int iAltitudeMode =
     760           2 :             poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
     761           2 :         if (poOgrFeat->IsFieldSetAndNotNull(iAltitudeMode))
     762             :         {
     763           1 :             const int nAltitudeMode = kmlAltitudeModeFromString(
     764             :                 poOgrFeat->GetFieldAsString(iAltitudeMode), isGX);
     765           1 :             camera->set_altitudemode(nAltitudeMode);
     766             :         }
     767           1 :         else if (CPLTestBool(
     768             :                      CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
     769             :         {
     770           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     771             :                      "Camera should define altitudeMode != 'clampToGround'");
     772             :         }
     773             : 
     774           2 :         if (poOgrPoint->getCoordinateDimension() == 3)
     775             :         {
     776           1 :             camera->set_altitude(poOgrPoint->getZ());
     777             :         }
     778           1 :         else if (CPLTestBool(
     779             :                      CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
     780             :         {
     781           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     782             :                      "Camera should have an altitude/Z");
     783           1 :             camera->set_altitude(0.0);
     784             :         }
     785             : 
     786           2 :         if (iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading))
     787           2 :             camera->set_heading(poOgrFeat->GetFieldAsDouble(iHeading));
     788           2 :         if (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt))
     789           1 :             camera->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt));
     790           2 :         if (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))
     791           1 :             camera->set_roll(poOgrFeat->GetFieldAsDouble(iRoll));
     792           2 :         poKmlPlacemark->set_abstractview(camera);
     793             :     }
     794         211 :     else if (!poKmlFeature)
     795             :     {
     796         209 :         const PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
     797         209 :         poKmlFeature = poKmlPlacemark;
     798             : 
     799         209 :         if (poOgrGeom)
     800             :         {
     801         158 :             ElementPtr poKmlElement = geom2kml(poOgrGeom, -1, poKmlFactory);
     802         158 :             if (!poKmlElement)
     803             :             {
     804          13 :                 CPLError(CE_Failure, CPLE_AppDefined,
     805             :                          "Cannot translate feature: %s",
     806          26 :                          poOgrFeat->DumpReadableAsString().c_str());
     807          13 :                 return nullptr;
     808             :             }
     809             : 
     810         145 :             poKmlPlacemark->set_geometry(AsGeometry(std::move(poKmlElement)));
     811             :         }
     812             :     }
     813             : 
     814         205 :     if (!camera)
     815         402 :         camera = feat2kmlcamera(oFC, iHeading, iTilt, iRoll, poOgrFeat,
     816         201 :                                 poKmlFactory);
     817         205 :     if (camera)
     818           4 :         poKmlFeature->set_abstractview(camera);
     819             : 
     820             :     /***** style *****/
     821         205 :     featurestyle2kml(poOgrDS, poOgrLayer, poOgrFeat, poKmlFactory,
     822             :                      poKmlFeature);
     823             : 
     824             :     /***** fields *****/
     825         205 :     field2kml(poOgrFeat, poOgrLayer, poKmlFactory, poKmlFeature,
     826             :               bUseSimpleField, oFC);
     827             : 
     828         205 :     return poKmlFeature;
     829             : }
     830             : 
     831             : OGRFeature *
     832        1292 : kml2feat(PlacemarkPtr poKmlPlacemark, OGRLIBKMLDataSource *poOgrDS,
     833             :          OGRLIBKMLLayer *poOgrLayer, OGRFeatureDefn *poOgrFeatDefn,
     834             :          OGRSpatialReference *poOgrSRS,
     835             :          const std::map<std::string, int> &mapSimpleFieldNameToOgrFieldIx)
     836             : {
     837        1292 :     OGRFeature *poOgrFeat = new OGRFeature(poOgrFeatDefn);
     838             : 
     839             :     /***** style *****/
     840        1292 :     kml2featurestyle(poKmlPlacemark, poOgrDS, poOgrLayer, poOgrFeat);
     841             : 
     842             :     /***** geometry *****/
     843        1292 :     if (poKmlPlacemark->has_geometry())
     844             :     {
     845             :         OGRGeometry *const poOgrGeom =
     846        1226 :             kml2geom(poKmlPlacemark->get_geometry(), poOgrSRS);
     847        1226 :         poOgrFeat->SetGeometryDirectly(poOgrGeom);
     848             :     }
     849          68 :     else if (poKmlPlacemark->has_abstractview() &&
     850           2 :              poKmlPlacemark->get_abstractview()->IsA(kmldom::Type_Camera))
     851             :     {
     852           4 :         const CameraPtr &camera = AsCamera(poKmlPlacemark->get_abstractview());
     853           2 :         if (camera->has_longitude() && camera->has_latitude())
     854             :         {
     855           2 :             if (camera->has_altitude())
     856           2 :                 poOgrFeat->SetGeometryDirectly(new OGRPoint(
     857           2 :                     camera->get_longitude(), camera->get_latitude(),
     858           2 :                     camera->get_altitude()));
     859             :             else
     860           0 :                 poOgrFeat->SetGeometryDirectly(new OGRPoint(
     861           0 :                     camera->get_longitude(), camera->get_latitude()));
     862           2 :             poOgrFeat->GetGeometryRef()->assignSpatialReference(poOgrSRS);
     863             :         }
     864             :     }
     865             : 
     866             :     /***** fields *****/
     867        1292 :     kml2field(poOgrFeat, AsFeature(poKmlPlacemark),
     868             :               poOgrLayer->GetFieldConfig(), mapSimpleFieldNameToOgrFieldIx);
     869             : 
     870        1292 :     return poOgrFeat;
     871             : }
     872             : 
     873          44 : OGRFeature *kmlgroundoverlay2feat(
     874             :     GroundOverlayPtr poKmlOverlay, OGRLIBKMLDataSource * /* poOgrDS */,
     875             :     OGRLIBKMLLayer *poOgrLayer, OGRFeatureDefn *poOgrFeatDefn,
     876             :     OGRSpatialReference *poOgrSRS,
     877             :     const std::map<std::string, int> &mapSimpleFieldNameToOgrFieldIx)
     878             : {
     879          44 :     OGRFeature *poOgrFeat = new OGRFeature(poOgrFeatDefn);
     880             : 
     881             :     /***** geometry *****/
     882          44 :     if (poKmlOverlay->has_latlonbox())
     883             :     {
     884             :         OGRGeometry *const poOgrGeom =
     885          44 :             kml2geom_latlonbox(poKmlOverlay->get_latlonbox(), poOgrSRS);
     886          44 :         poOgrFeat->SetGeometryDirectly(poOgrGeom);
     887             :     }
     888           0 :     else if (poKmlOverlay->has_gx_latlonquad())
     889             :     {
     890             :         OGRGeometry *const poOgrGeom =
     891           0 :             kml2geom_latlonquad(poKmlOverlay->get_gx_latlonquad(), poOgrSRS);
     892           0 :         poOgrFeat->SetGeometryDirectly(poOgrGeom);
     893             :     }
     894             : 
     895             :     /***** fields *****/
     896          44 :     kml2field(poOgrFeat, AsFeature(poKmlOverlay), poOgrLayer->GetFieldConfig(),
     897             :               mapSimpleFieldNameToOgrFieldIx);
     898             : 
     899          44 :     return poOgrFeat;
     900             : }

Generated by: LCOV version 1.14