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 474 70.7 %
Date: 2024-11-21 22:18:42 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         200 : 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         200 :         poOgrFeat->GetFieldIndex(oFC.camera_longitude_field);
      51             :     const int iCameraLatitudeField =
      52         200 :         poOgrFeat->GetFieldIndex(oFC.camera_latitude_field);
      53             :     const int iCameraAltitudeField =
      54         200 :         poOgrFeat->GetFieldIndex(oFC.camera_altitude_field);
      55             :     const int iCameraAltitudeModeField =
      56         200 :         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         204 :         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         200 :     if (!bNeedCamera)
      68         198 :         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         215 : FeaturePtr feat2kml(OGRLIBKMLDataSource *poOgrDS, OGRLIBKMLLayer *poOgrLayer,
     208             :                     OGRFeature *poOgrFeat, KmlFactory *poKmlFactory,
     209             :                     int bUseSimpleField)
     210             : {
     211         430 :     FeaturePtr poKmlFeature = nullptr;
     212         215 :     const auto &oFC = poOgrLayer->GetFieldConfig();
     213             : 
     214             :     /***** geometry *****/
     215         215 :     OGRGeometry *poOgrGeom = poOgrFeat->GetGeometryRef();
     216         215 :     const int iHeading = poOgrFeat->GetFieldIndex(oFC.headingfield);
     217         215 :     const int iTilt = poOgrFeat->GetFieldIndex(oFC.tiltfield);
     218         215 :     const int iRoll = poOgrFeat->GetFieldIndex(oFC.rollfield);
     219         215 :     const int iModel = poOgrFeat->GetFieldIndex(oFC.modelfield);
     220         215 :     const int iNetworkLink = poOgrFeat->GetFieldIndex(oFC.networklinkfield);
     221         215 :     const int iPhotoOverlay = poOgrFeat->GetFieldIndex(oFC.photooverlayfield);
     222         430 :     CameraPtr camera = nullptr;
     223             : 
     224             :     // PhotoOverlay.
     225           2 :     if (iPhotoOverlay >= 0 && poOgrFeat->IsFieldSetAndNotNull(iPhotoOverlay) &&
     226           2 :         poOgrGeom != nullptr && !poOgrGeom->IsEmpty() &&
     227         219 :         wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint &&
     228         217 :         (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 = GDALOpen(osURL, GA_ReadOnly);
     268           0 :                 if (hDS != nullptr)
     269             :                 {
     270           0 :                     nTileSize = GDALGetRasterXSize(hDS);
     271           0 :                     if (nTileSize != GDALGetRasterYSize(hDS))
     272             :                     {
     273           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     274             :                                  "Non square tile : %dx%d",
     275             :                                  GDALGetRasterXSize(hDS),
     276             :                                  GDALGetRasterYSize(hDS));
     277           0 :                         nTileSize = 0;
     278           0 :                         bErrorEmitted = true;
     279             :                     }
     280           0 :                     GDALClose(hDS);
     281             :                 }
     282             :                 else
     283             :                 {
     284           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
     285             :                              osURL.c_str());
     286           0 :                     bErrorEmitted = true;
     287             :                 }
     288             :             }
     289             :             else
     290             :             {
     291           1 :                 nTileSize = poOgrFeat->GetFieldAsInteger(iImagePyramidTileSize);
     292             :             }
     293           1 :             if (!bErrorEmitted && (nTileSize <= 1 || !IsPowerOf2(nTileSize)))
     294             :             {
     295           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     296             :                          "Tile size is not a power of two: %d", nTileSize);
     297           0 :                 nTileSize = 0;
     298             :             }
     299             : 
     300           1 :             if (nTileSize > 0)
     301             :             {
     302           2 :                 if (iImagePyramidMaxWidth < 0 ||
     303           1 :                     !poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxWidth) ||
     304           2 :                     iImagePyramidMaxHeight < 0 ||
     305           1 :                     !poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxHeight))
     306             :                 {
     307           0 :                     CPLDebug("LIBKML",
     308             :                              "Missing ImagePyramid maxWidth and/or maxHeight. "
     309             :                              "Computing it");
     310           0 :                     OGRLIBKMLGetMaxDimensions(pszURL, nTileSize, &nMaxWidth,
     311             :                                               &nMaxHeight);
     312             :                 }
     313             :                 else
     314             :                 {
     315           1 :                     nMaxWidth =
     316           1 :                         poOgrFeat->GetFieldAsInteger(iImagePyramidMaxWidth);
     317           1 :                     nMaxHeight =
     318           1 :                         poOgrFeat->GetFieldAsInteger(iImagePyramidMaxHeight);
     319             :                 }
     320             : 
     321           1 :                 if (nMaxWidth <= 0 || nMaxHeight <= 0)
     322             :                 {
     323           0 :                     CPLError(
     324             :                         CE_Failure, CPLE_AppDefined,
     325             :                         "Cannot generate PhotoOverlay object since there are "
     326             :                         "missing information to generate ImagePyramid element");
     327             :                 }
     328             :             }
     329             : 
     330           1 :             if (iImagePyramidGridOrigin >= 0 &&
     331           0 :                 poOgrFeat->IsFieldSetAndNotNull(iImagePyramidGridOrigin))
     332             :             {
     333             :                 const char *pszGridOrigin =
     334           0 :                     poOgrFeat->GetFieldAsString(iImagePyramidGridOrigin);
     335           0 :                 if (EQUAL(pszGridOrigin, "UpperLeft"))
     336             :                 {
     337           0 :                     bGridOriginIsUpperLeft = true;
     338             :                 }
     339           0 :                 else if (EQUAL(pszGridOrigin, "BottomLeft"))
     340             :                 {
     341           0 :                     bGridOriginIsUpperLeft = false;
     342             :                 }
     343             :                 else
     344             :                 {
     345           0 :                     CPLError(
     346             :                         CE_Failure, CPLE_AppDefined,
     347             :                         "Unhandled value for imagepyramid_gridorigin : %s. "
     348             :                         "Assuming UpperLeft",
     349             :                         pszGridOrigin);
     350             :                 }
     351           1 :             }
     352             :         }
     353             :         else
     354             :         {
     355           2 :             if ((iImagePyramidTileSize >= 0 &&
     356           1 :                  poOgrFeat->IsFieldSetAndNotNull(iImagePyramidTileSize)) ||
     357           1 :                 (iImagePyramidMaxWidth >= 0 &&
     358           1 :                  poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxWidth)) ||
     359           1 :                 (iImagePyramidMaxHeight >= 0 &&
     360           2 :                  poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxHeight)) ||
     361           0 :                 (iImagePyramidGridOrigin >= 0 &&
     362           0 :                  poOgrFeat->IsFieldSetAndNotNull(iImagePyramidGridOrigin)))
     363             :             {
     364           0 :                 CPLError(
     365             :                     CE_Warning, CPLE_AppDefined,
     366             :                     "Ignoring any ImagePyramid information since the URL does "
     367             :                     "not include $[x] and/or $[y] and/or $[level]");
     368             :             }
     369             :         }
     370             : 
     371             :         // OGC KML Abstract Test Case (ATC) 19 & 35.
     372           2 :         double dfNear = 0.0;
     373             : 
     374           1 :         if ((!bIsTiledPhotoOverlay ||
     375           2 :              (nTileSize > 0 && nMaxWidth > 0 && nMaxHeight > 0)) &&
     376           2 :             iLeftFovField >= 0 &&
     377           2 :             poOgrFeat->IsFieldSetAndNotNull(iLeftFovField) &&
     378           2 :             iRightFovField >= 0 &&
     379           2 :             poOgrFeat->IsFieldSetAndNotNull(iRightFovField) &&
     380           2 :             iBottomFovField >= 0 &&
     381           2 :             poOgrFeat->IsFieldSetAndNotNull(iBottomFovField) &&
     382           2 :             iTopFovField >= 0 &&
     383           6 :             poOgrFeat->IsFieldSetAndNotNull(iTopFovField) && iNearField >= 0 &&
     384           2 :             (dfNear = poOgrFeat->GetFieldAsDouble(iNearField)) > 0)
     385             :         {
     386             :             const PhotoOverlayPtr poKmlPhotoOverlay =
     387           2 :                 poKmlFactory->CreatePhotoOverlay();
     388           2 :             poKmlFeature = poKmlPhotoOverlay;
     389             : 
     390           2 :             const IconPtr poKmlIcon = poKmlFactory->CreateIcon();
     391           2 :             poKmlPhotoOverlay->set_icon(poKmlIcon);
     392           2 :             poKmlIcon->set_href(pszURL);
     393             : 
     394             :             const ViewVolumePtr poKmlViewVolume =
     395           2 :                 poKmlFactory->CreateViewVolume();
     396           2 :             poKmlPhotoOverlay->set_viewvolume(poKmlViewVolume);
     397             : 
     398           2 :             const double dfLeftFov = poOgrFeat->GetFieldAsDouble(iLeftFovField);
     399             :             const double dfRightFov =
     400           2 :                 poOgrFeat->GetFieldAsDouble(iRightFovField);
     401             :             const double dfBottomFov =
     402           2 :                 poOgrFeat->GetFieldAsDouble(iBottomFovField);
     403           2 :             const double dfTopFov = poOgrFeat->GetFieldAsDouble(iTopFovField);
     404             : 
     405           2 :             poKmlViewVolume->set_leftfov(dfLeftFov);
     406           2 :             poKmlViewVolume->set_rightfov(dfRightFov);
     407           2 :             poKmlViewVolume->set_bottomfov(dfBottomFov);
     408           2 :             poKmlViewVolume->set_topfov(dfTopFov);
     409           2 :             poKmlViewVolume->set_near(dfNear);
     410             : 
     411           2 :             if (bIsTiledPhotoOverlay)
     412             :             {
     413             :                 const ImagePyramidPtr poKmlImagePyramid =
     414           2 :                     poKmlFactory->CreateImagePyramid();
     415           1 :                 poKmlPhotoOverlay->set_imagepyramid(poKmlImagePyramid);
     416             : 
     417           1 :                 poKmlImagePyramid->set_tilesize(nTileSize);
     418           1 :                 poKmlImagePyramid->set_maxwidth(nMaxWidth);
     419           1 :                 poKmlImagePyramid->set_maxheight(nMaxHeight);
     420           1 :                 poKmlImagePyramid->set_gridorigin(
     421             :                     bGridOriginIsUpperLeft ? kmldom::GRIDORIGIN_UPPERLEFT
     422             :                                            : kmldom::GRIDORIGIN_LOWERLEFT);
     423             :             }
     424             : 
     425             :             const int iPhotoOverlayShapeField =
     426           2 :                 poOgrFeat->GetFieldIndex(oFC.photooverlay_shape_field);
     427           4 :             if (iPhotoOverlayShapeField >= 0 &&
     428           2 :                 poOgrFeat->IsFieldSetAndNotNull(iPhotoOverlayShapeField))
     429             :             {
     430             :                 const char *pszShape =
     431           2 :                     poOgrFeat->GetFieldAsString(iPhotoOverlayShapeField);
     432           2 :                 if (EQUAL(pszShape, "rectangle"))
     433           2 :                     poKmlPhotoOverlay->set_shape(kmldom::SHAPE_RECTANGLE);
     434           0 :                 else if (EQUAL(pszShape, "cylinder"))
     435           0 :                     poKmlPhotoOverlay->set_shape(kmldom::SHAPE_CYLINDER);
     436           0 :                 else if (EQUAL(pszShape, "sphere"))
     437           0 :                     poKmlPhotoOverlay->set_shape(kmldom::SHAPE_SPHERE);
     438             :             }
     439             : 
     440           2 :             ElementPtr poKmlElement = geom2kml(poOgrGeom, -1, poKmlFactory);
     441           2 :             if (!poKmlElement)
     442           0 :                 return nullptr;
     443             : 
     444           2 :             poKmlPhotoOverlay->set_point(AsPoint(std::move(poKmlElement)));
     445             :         }
     446             :     }
     447             : 
     448             :     // NetworkLink.
     449         218 :     if (!poKmlFeature && iNetworkLink >= 0 &&
     450           3 :         poOgrFeat->IsFieldSetAndNotNull(iNetworkLink))
     451             :     {
     452             :         const NetworkLinkPtr poKmlNetworkLink =
     453           6 :             poKmlFactory->CreateNetworkLink();
     454           3 :         poKmlFeature = poKmlNetworkLink;
     455             : 
     456             :         const int iRefreshVisibility =
     457           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_refreshvisibility_field);
     458             : 
     459           6 :         if (iRefreshVisibility >= 0 &&
     460           3 :             poOgrFeat->IsFieldSetAndNotNull(iRefreshVisibility))
     461             :         {
     462           2 :             poKmlNetworkLink->set_refreshvisibility(
     463           1 :                 CPL_TO_BOOL(poOgrFeat->GetFieldAsInteger(iRefreshVisibility)));
     464             :         }
     465             : 
     466             :         const int iFlyToView =
     467           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_flytoview_field);
     468             : 
     469           3 :         if (iFlyToView >= 0 && poOgrFeat->IsFieldSetAndNotNull(iFlyToView))
     470           2 :             poKmlNetworkLink->set_flytoview(
     471           1 :                 CPL_TO_BOOL(poOgrFeat->GetFieldAsInteger(iFlyToView)));
     472             : 
     473           6 :         const LinkPtr poKmlLink = poKmlFactory->CreateLink();
     474           3 :         poKmlLink->set_href(poOgrFeat->GetFieldAsString(iNetworkLink));
     475           3 :         poKmlNetworkLink->set_link(poKmlLink);
     476             : 
     477             :         const int iRefreshMode =
     478           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_refreshMode_field);
     479             :         const int iRefreshInterval =
     480           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_refreshInterval_field);
     481             :         const int iViewRefreshMode =
     482           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_viewRefreshMode_field);
     483             :         const int iViewRefreshTime =
     484           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_viewRefreshTime_field);
     485             :         const int iViewBoundScale =
     486           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_viewBoundScale_field);
     487             :         const int iViewFormat =
     488           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_viewFormat_field);
     489             :         const int iHttpQuery =
     490           3 :             poOgrFeat->GetFieldIndex(oFC.networklink_httpQuery_field);
     491             : 
     492           3 :         double dfRefreshInterval = 0.0;
     493           6 :         if (iRefreshInterval >= 0 &&
     494           3 :             poOgrFeat->IsFieldSetAndNotNull(iRefreshInterval))
     495             :         {
     496           1 :             dfRefreshInterval = poOgrFeat->GetFieldAsDouble(iRefreshInterval);
     497           1 :             if (dfRefreshInterval < 0)
     498           0 :                 dfRefreshInterval = 0.0;
     499             :         }
     500             : 
     501           3 :         double dfViewRefreshTime = 0.0;
     502           6 :         if (iViewRefreshTime >= 0 &&
     503           3 :             poOgrFeat->IsFieldSetAndNotNull(iViewRefreshTime))
     504             :         {
     505           1 :             dfViewRefreshTime = poOgrFeat->GetFieldAsDouble(iViewRefreshTime);
     506           1 :             if (dfViewRefreshTime < 0)
     507           0 :                 dfViewRefreshTime = 0.0;
     508             :         }
     509             : 
     510           3 :         if (dfRefreshInterval > 0)  // ATC 51
     511           1 :             poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONINTERVAL);
     512           4 :         else if (iRefreshMode >= 0 &&
     513           2 :                  poOgrFeat->IsFieldSetAndNotNull(iRefreshMode))
     514             :         {
     515             :             const char *const pszRefreshMode =
     516           1 :                 poOgrFeat->GetFieldAsString(iRefreshMode);
     517           1 :             if (EQUAL(pszRefreshMode, "onChange"))
     518           0 :                 poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONCHANGE);
     519           1 :             else if (EQUAL(pszRefreshMode, "onInterval"))
     520           0 :                 poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONINTERVAL);
     521           1 :             else if (EQUAL(pszRefreshMode, "onExpire"))
     522           1 :                 poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONEXPIRE);
     523             :         }
     524             : 
     525           3 :         if (dfRefreshInterval > 0)  // ATC 9
     526           1 :             poKmlLink->set_refreshinterval(dfRefreshInterval);
     527             : 
     528           3 :         if (dfViewRefreshTime > 0)  // ATC 51
     529           1 :             poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONSTOP);
     530           4 :         else if (iViewRefreshMode >= 0 &&
     531           2 :                  poOgrFeat->IsFieldSetAndNotNull(iViewRefreshMode))
     532             :         {
     533             :             const char *const pszViewRefreshMode =
     534           1 :                 poOgrFeat->GetFieldAsString(iViewRefreshMode);
     535           1 :             if (EQUAL(pszViewRefreshMode, "never"))
     536           0 :                 poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_NEVER);
     537           1 :             else if (EQUAL(pszViewRefreshMode, "onRequest"))
     538           0 :                 poKmlLink->set_viewrefreshmode(
     539             :                     kmldom::VIEWREFRESHMODE_ONREQUEST);
     540           1 :             else if (EQUAL(pszViewRefreshMode, "onStop"))
     541           0 :                 poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONSTOP);
     542           1 :             else if (EQUAL(pszViewRefreshMode, "onRegion"))
     543           1 :                 poKmlLink->set_viewrefreshmode(
     544             :                     kmldom::VIEWREFRESHMODE_ONREGION);
     545             :         }
     546             : 
     547           3 :         if (dfViewRefreshTime > 0)  // ATC 9
     548           1 :             poKmlLink->set_viewrefreshtime(dfViewRefreshTime);
     549             : 
     550           6 :         if (iViewBoundScale >= 0 &&
     551           3 :             poOgrFeat->IsFieldSetAndNotNull(iViewBoundScale))
     552             :         {
     553             :             const double dfViewBoundScale =
     554           1 :                 poOgrFeat->GetFieldAsDouble(iViewBoundScale);
     555           1 :             if (dfViewBoundScale > 0)  // ATC 9
     556           1 :                 poKmlLink->set_viewboundscale(dfViewBoundScale);
     557             :         }
     558             : 
     559           3 :         if (iViewFormat >= 0 && poOgrFeat->IsFieldSetAndNotNull(iViewFormat))
     560             :         {
     561             :             const char *const pszViewFormat =
     562           1 :                 poOgrFeat->GetFieldAsString(iViewFormat);
     563           1 :             if (pszViewFormat[0] != '\0')  // ATC 46
     564           1 :                 poKmlLink->set_viewformat(pszViewFormat);
     565             :         }
     566             : 
     567           3 :         if (iHttpQuery >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHttpQuery))
     568             :         {
     569             :             const char *const pszHttpQuery =
     570           1 :                 poOgrFeat->GetFieldAsString(iHttpQuery);
     571           1 :             if (strstr(pszHttpQuery, "[clientVersion]") != nullptr ||
     572           0 :                 strstr(pszHttpQuery, "[kmlVersion]") != nullptr ||
     573           0 :                 strstr(pszHttpQuery, "[clientName]") != nullptr ||
     574           0 :                 strstr(pszHttpQuery, "[language]") != nullptr)  // ATC 47
     575             :             {
     576           1 :                 poKmlLink->set_httpquery(pszHttpQuery);
     577             :             }
     578             :         }
     579             :     }
     580             : 
     581             :     // Model.
     582         422 :     else if (!poKmlFeature && iModel >= 0 &&
     583           2 :              poOgrFeat->IsFieldSetAndNotNull(iModel) && poOgrGeom != nullptr &&
     584         424 :              !poOgrGeom->IsEmpty() &&
     585           2 :              wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint)
     586             :     {
     587           4 :         const PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
     588           2 :         poKmlFeature = poKmlPlacemark;
     589             : 
     590           2 :         const OGRPoint *const poOgrPoint = poOgrGeom->toPoint();
     591           4 :         ModelPtr model = poKmlFactory->CreateModel();
     592             : 
     593           4 :         LocationPtr location = poKmlFactory->CreateLocation();
     594           2 :         model->set_location(location);
     595           2 :         location->set_latitude(poOgrPoint->getY());
     596           2 :         location->set_longitude(poOgrPoint->getX());
     597           2 :         if (poOgrPoint->getCoordinateDimension() == 3)
     598           1 :             location->set_altitude(poOgrPoint->getZ());
     599             : 
     600           2 :         int isGX = FALSE;
     601             :         const int iAltitudeMode =
     602           2 :             poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
     603           2 :         if (poOgrFeat->IsFieldSetAndNotNull(iAltitudeMode))
     604             :         {
     605           1 :             const int nAltitudeMode = kmlAltitudeModeFromString(
     606             :                 poOgrFeat->GetFieldAsString(iAltitudeMode), isGX);
     607           1 :             model->set_altitudemode(nAltitudeMode);
     608             : 
     609             :             // ATC 55
     610           2 :             if (nAltitudeMode != kmldom::ALTITUDEMODE_CLAMPTOGROUND &&
     611           1 :                 poOgrPoint->getCoordinateDimension() != 3)
     612             :             {
     613           0 :                 if (CPLTestBool(
     614             :                         CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
     615           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     616             :                              "Altitude should be defined");
     617             :             }
     618             :         }
     619             : 
     620           2 :         if ((iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading)) ||
     621           5 :             (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt)) ||
     622           1 :             (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll)))
     623             :         {
     624             :             OrientationPtr const orientation =
     625           2 :                 poKmlFactory->CreateOrientation();
     626           1 :             model->set_orientation(orientation);
     627           1 :             if (iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading))
     628           1 :                 orientation->set_heading(poOgrFeat->GetFieldAsDouble(iHeading));
     629             :             else
     630           0 :                 orientation->set_heading(0);
     631           1 :             if (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt))
     632           1 :                 orientation->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt));
     633             :             else
     634           0 :                 orientation->set_tilt(0);
     635           1 :             if (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))
     636           1 :                 orientation->set_roll(poOgrFeat->GetFieldAsDouble(iRoll));
     637             :             else
     638           0 :                 orientation->set_roll(0);
     639             :         }
     640           2 :         const int iScaleX = poOgrFeat->GetFieldIndex(oFC.scalexfield);
     641           2 :         const int iScaleY = poOgrFeat->GetFieldIndex(oFC.scaleyfield);
     642           2 :         const int iScaleZ = poOgrFeat->GetFieldIndex(oFC.scalezfield);
     643             : 
     644           4 :         const ScalePtr scale = poKmlFactory->CreateScale();
     645           2 :         model->set_scale(scale);
     646           2 :         if (iScaleX >= 0 && poOgrFeat->IsFieldSetAndNotNull(iScaleX))
     647           1 :             scale->set_x(poOgrFeat->GetFieldAsDouble(iScaleX));
     648             :         else
     649           1 :             scale->set_x(1.0);
     650           2 :         if (iScaleY >= 0 && poOgrFeat->IsFieldSetAndNotNull(iScaleY))
     651           1 :             scale->set_y(poOgrFeat->GetFieldAsDouble(iScaleY));
     652             :         else
     653           1 :             scale->set_y(1.0);
     654           2 :         if (iScaleZ >= 0 && poOgrFeat->IsFieldSetAndNotNull(iScaleZ))
     655           1 :             scale->set_z(poOgrFeat->GetFieldAsDouble(iScaleZ));
     656             :         else
     657           1 :             scale->set_z(1.0);
     658             : 
     659           2 :         const LinkPtr link = poKmlFactory->CreateLink();
     660           2 :         model->set_link(link);
     661           2 :         const char *const pszURL = poOgrFeat->GetFieldAsString(oFC.modelfield);
     662           2 :         link->set_href(pszURL);
     663             : 
     664             :         // Collada 3D file?
     665           3 :         if (EQUAL(CPLGetExtension(pszURL), "dae") &&
     666           1 :             CPLTestBool(CPLGetConfigOption("LIBKML_ADD_RESOURCE_MAP", "TRUE")))
     667             :         {
     668           1 :             VSILFILE *fp = nullptr;
     669           1 :             bool bIsURL = false;
     670           1 :             if (STARTS_WITH_CI(pszURL, "http://") ||
     671           0 :                 STARTS_WITH_CI(pszURL, "https://"))
     672             :             {
     673           1 :                 bIsURL = true;
     674           1 :                 fp = VSIFOpenL(CPLSPrintf("/vsicurl/%s", pszURL), "rb");
     675             :             }
     676           0 :             else if (strstr(pszURL, ".kmz/") != nullptr)
     677             :             {
     678           0 :                 fp = VSIFOpenL(CPLSPrintf("/vsizip/%s", pszURL), "rb");
     679             :             }
     680             :             else
     681             :             {
     682           0 :                 fp = VSIFOpenL(pszURL, "rb");
     683             :             }
     684           1 :             if (fp != nullptr)
     685             :             {
     686           0 :                 ResourceMapPtr resourceMap = nullptr;
     687           0 :                 const char *pszLine = nullptr;
     688           0 :                 while ((pszLine = CPLReadLineL(fp)) != nullptr)
     689             :                 {
     690           0 :                     const char *pszInitFrom = strstr(pszLine, "<init_from>");
     691           0 :                     if (pszInitFrom)
     692             :                     {
     693           0 :                         pszInitFrom += strlen("<init_from>");
     694             :                         const char *const pszInitFromEnd =
     695           0 :                             strstr(pszInitFrom, "</init_from>");
     696           0 :                         if (pszInitFromEnd)
     697             :                         {
     698           0 :                             CPLString osImage(pszInitFrom);
     699           0 :                             osImage.resize(pszInitFromEnd - pszInitFrom);
     700             :                             const char *const pszExtension =
     701           0 :                                 CPLGetExtension(osImage);
     702           0 :                             if (EQUAL(pszExtension, "jpg") ||
     703           0 :                                 EQUAL(pszExtension, "jpeg") ||
     704           0 :                                 EQUAL(pszExtension, "png") ||
     705           0 :                                 EQUAL(pszExtension, "gif"))
     706             :                             {
     707           0 :                                 if (!resourceMap)
     708             :                                     resourceMap =
     709           0 :                                         poKmlFactory->CreateResourceMap();
     710             :                                 const AliasPtr alias =
     711           0 :                                     poKmlFactory->CreateAlias();
     712           0 :                                 if (bIsURL && CPLIsFilenameRelative(osImage))
     713             :                                 {
     714           0 :                                     if (STARTS_WITH(pszURL, "http"))
     715           0 :                                         alias->set_targethref(CPLSPrintf(
     716             :                                             "%s/%s", CPLGetPath(pszURL),
     717             :                                             osImage.c_str()));
     718             :                                     else
     719           0 :                                         alias->set_targethref(
     720             :                                             CPLFormFilename(CPLGetPath(pszURL),
     721             :                                                             osImage, nullptr));
     722             :                                 }
     723             :                                 else
     724           0 :                                     alias->set_targethref(osImage);
     725           0 :                                 alias->set_sourcehref(osImage);
     726           0 :                                 resourceMap->add_alias(alias);
     727             :                             }
     728             :                         }
     729             :                     }
     730             :                 }
     731           0 :                 if (resourceMap)
     732           0 :                     model->set_resourcemap(resourceMap);
     733           0 :                 VSIFCloseL(fp);
     734             :             }
     735             :         }
     736             : 
     737           2 :         poKmlPlacemark->set_geometry(AsGeometry(model));
     738             :     }
     739             : 
     740             :     // Camera.
     741         418 :     else if (!poKmlFeature && poOgrGeom != nullptr && !poOgrGeom->IsEmpty() &&
     742         156 :              wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint &&
     743         468 :              poOgrFeat->GetFieldIndex(oFC.camera_longitude_field) < 0 &&
     744          50 :              ((iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading)) ||
     745          48 :               (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt)) ||
     746           0 :               (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))))
     747             :     {
     748           2 :         const PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
     749           2 :         poKmlFeature = poKmlPlacemark;
     750             : 
     751           2 :         const OGRPoint *const poOgrPoint = poOgrGeom->toPoint();
     752           2 :         camera = poKmlFactory->CreateCamera();
     753           2 :         camera->set_latitude(poOgrPoint->getY());
     754           2 :         camera->set_longitude(poOgrPoint->getX());
     755           2 :         int isGX = FALSE;
     756             :         const int iAltitudeMode =
     757           2 :             poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
     758           2 :         if (poOgrFeat->IsFieldSetAndNotNull(iAltitudeMode))
     759             :         {
     760           1 :             const int nAltitudeMode = kmlAltitudeModeFromString(
     761             :                 poOgrFeat->GetFieldAsString(iAltitudeMode), isGX);
     762           1 :             camera->set_altitudemode(nAltitudeMode);
     763             :         }
     764           1 :         else if (CPLTestBool(
     765             :                      CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
     766             :         {
     767           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     768             :                      "Camera should define altitudeMode != 'clampToGround'");
     769             :         }
     770             : 
     771           2 :         if (poOgrPoint->getCoordinateDimension() == 3)
     772             :         {
     773           1 :             camera->set_altitude(poOgrPoint->getZ());
     774             :         }
     775           1 :         else if (CPLTestBool(
     776             :                      CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
     777             :         {
     778           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     779             :                      "Camera should have an altitude/Z");
     780           1 :             camera->set_altitude(0.0);
     781             :         }
     782             : 
     783           2 :         if (iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading))
     784           2 :             camera->set_heading(poOgrFeat->GetFieldAsDouble(iHeading));
     785           2 :         if (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt))
     786           1 :             camera->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt));
     787           2 :         if (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))
     788           1 :             camera->set_roll(poOgrFeat->GetFieldAsDouble(iRoll));
     789           2 :         poKmlPlacemark->set_abstractview(camera);
     790             :     }
     791         208 :     else if (!poKmlFeature)
     792             :     {
     793         206 :         const PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
     794         206 :         poKmlFeature = poKmlPlacemark;
     795             : 
     796         206 :         if (poOgrGeom)
     797             :         {
     798         155 :             ElementPtr poKmlElement = geom2kml(poOgrGeom, -1, poKmlFactory);
     799         155 :             if (!poKmlElement)
     800             :             {
     801          13 :                 CPLError(CE_Failure, CPLE_AppDefined,
     802             :                          "Cannot translate feature: %s",
     803          26 :                          poOgrFeat->DumpReadableAsString().c_str());
     804          13 :                 return nullptr;
     805             :             }
     806             : 
     807         142 :             poKmlPlacemark->set_geometry(AsGeometry(std::move(poKmlElement)));
     808             :         }
     809             :     }
     810             : 
     811         202 :     if (!camera)
     812         396 :         camera = feat2kmlcamera(oFC, iHeading, iTilt, iRoll, poOgrFeat,
     813         198 :                                 poKmlFactory);
     814         202 :     if (camera)
     815           4 :         poKmlFeature->set_abstractview(camera);
     816             : 
     817             :     /***** style *****/
     818         202 :     featurestyle2kml(poOgrDS, poOgrLayer, poOgrFeat, poKmlFactory,
     819             :                      poKmlFeature);
     820             : 
     821             :     /***** fields *****/
     822         202 :     field2kml(poOgrFeat, poOgrLayer, poKmlFactory, poKmlFeature,
     823             :               bUseSimpleField, oFC);
     824             : 
     825         202 :     return poKmlFeature;
     826             : }
     827             : 
     828        1251 : OGRFeature *kml2feat(PlacemarkPtr poKmlPlacemark, OGRLIBKMLDataSource *poOgrDS,
     829             :                      OGRLIBKMLLayer *poOgrLayer, OGRFeatureDefn *poOgrFeatDefn,
     830             :                      OGRSpatialReference *poOgrSRS)
     831             : {
     832        1251 :     OGRFeature *poOgrFeat = new OGRFeature(poOgrFeatDefn);
     833             : 
     834             :     /***** style *****/
     835        1251 :     kml2featurestyle(poKmlPlacemark, poOgrDS, poOgrLayer, poOgrFeat);
     836             : 
     837             :     /***** geometry *****/
     838        1251 :     if (poKmlPlacemark->has_geometry())
     839             :     {
     840             :         OGRGeometry *const poOgrGeom =
     841        1187 :             kml2geom(poKmlPlacemark->get_geometry(), poOgrSRS);
     842        1187 :         poOgrFeat->SetGeometryDirectly(poOgrGeom);
     843             :     }
     844          66 :     else if (poKmlPlacemark->has_abstractview() &&
     845           2 :              poKmlPlacemark->get_abstractview()->IsA(kmldom::Type_Camera))
     846             :     {
     847           4 :         const CameraPtr &camera = AsCamera(poKmlPlacemark->get_abstractview());
     848           2 :         if (camera->has_longitude() && camera->has_latitude())
     849             :         {
     850           2 :             if (camera->has_altitude())
     851           2 :                 poOgrFeat->SetGeometryDirectly(new OGRPoint(
     852           2 :                     camera->get_longitude(), camera->get_latitude(),
     853           2 :                     camera->get_altitude()));
     854             :             else
     855           0 :                 poOgrFeat->SetGeometryDirectly(new OGRPoint(
     856           0 :                     camera->get_longitude(), camera->get_latitude()));
     857           2 :             poOgrFeat->GetGeometryRef()->assignSpatialReference(poOgrSRS);
     858             :         }
     859             :     }
     860             : 
     861             :     /***** fields *****/
     862        1251 :     kml2field(poOgrFeat, AsFeature(poKmlPlacemark),
     863             :               poOgrLayer->GetFieldConfig());
     864             : 
     865        1251 :     return poOgrFeat;
     866             : }
     867             : 
     868          42 : OGRFeature *kmlgroundoverlay2feat(GroundOverlayPtr poKmlOverlay,
     869             :                                   OGRLIBKMLDataSource * /* poOgrDS */,
     870             :                                   OGRLIBKMLLayer *poOgrLayer,
     871             :                                   OGRFeatureDefn *poOgrFeatDefn,
     872             :                                   OGRSpatialReference *poOgrSRS)
     873             : {
     874          42 :     OGRFeature *poOgrFeat = new OGRFeature(poOgrFeatDefn);
     875             : 
     876             :     /***** geometry *****/
     877          42 :     if (poKmlOverlay->has_latlonbox())
     878             :     {
     879             :         OGRGeometry *const poOgrGeom =
     880          42 :             kml2geom_latlonbox(poKmlOverlay->get_latlonbox(), poOgrSRS);
     881          42 :         poOgrFeat->SetGeometryDirectly(poOgrGeom);
     882             :     }
     883           0 :     else if (poKmlOverlay->has_gx_latlonquad())
     884             :     {
     885             :         OGRGeometry *const poOgrGeom =
     886           0 :             kml2geom_latlonquad(poKmlOverlay->get_gx_latlonquad(), poOgrSRS);
     887           0 :         poOgrFeat->SetGeometryDirectly(poOgrGeom);
     888             :     }
     889             : 
     890             :     /***** fields *****/
     891          42 :     kml2field(poOgrFeat, AsFeature(poKmlOverlay), poOgrLayer->GetFieldConfig());
     892             : 
     893          42 :     return poOgrFeat;
     894             : }

Generated by: LCOV version 1.14