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

Generated by: LCOV version 1.14