LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/kml - ogrkmldatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 133 162 82.1 %
Date: 2025-05-31 00:00:17 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  KML Driver
       4             :  * Purpose:  Implementation of OGRKMLDataSource class.
       5             :  * Author:   Christopher Condit, condit@sdsc.edu;
       6             :  *           Jens Oberender, j.obi@troja.net
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2006, Christopher Condit
      10             :  *               2007, Jens Oberender
      11             :  * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : #include "cpl_port.h"
      16             : #include "ogr_kml.h"
      17             : 
      18             : #include <cstring>
      19             : #include <string>
      20             : 
      21             : #include "cpl_conv.h"
      22             : #include "cpl_error.h"
      23             : #include "cpl_minixml.h"
      24             : #include "cpl_string.h"
      25             : #include "cpl_vsi.h"
      26             : #include "cpl_vsi_error.h"
      27             : #include "ogr_core.h"
      28             : #include "ogr_spatialref.h"
      29             : #include "kml.h"
      30             : #include "kmlutility.h"
      31             : #include "kmlvector.h"
      32             : #include "ogrsf_frmts.h"
      33             : 
      34             : /************************************************************************/
      35             : /*                         OGRKMLDataSource()                           */
      36             : /************************************************************************/
      37             : 
      38             : OGRKMLDataSource::OGRKMLDataSource() = default;
      39             : 
      40             : /************************************************************************/
      41             : /*                        ~OGRKMLDataSource()                           */
      42             : /************************************************************************/
      43             : 
      44         134 : OGRKMLDataSource::~OGRKMLDataSource()
      45             : {
      46          67 :     if (fpOutput_ != nullptr)
      47             :     {
      48          41 :         if (nLayers_ > 0)
      49             :         {
      50          41 :             if (nLayers_ == 1 && papoLayers_[0]->nWroteFeatureCount_ == 0)
      51             :             {
      52          17 :                 VSIFPrintfL(fpOutput_, "<Folder><name>%s</name>\n",
      53          17 :                             papoLayers_[0]->GetName());
      54             :             }
      55             : 
      56          41 :             VSIFPrintfL(fpOutput_, "%s", "</Folder>\n");
      57             : 
      58         101 :             for (int i = 0; i < nLayers_; i++)
      59             :             {
      60          60 :                 if (!(papoLayers_[i]->bSchemaWritten_) &&
      61          38 :                     papoLayers_[i]->nWroteFeatureCount_ != 0)
      62             :                 {
      63          36 :                     CPLString osRet = papoLayers_[i]->WriteSchema();
      64          18 :                     if (!osRet.empty())
      65          17 :                         VSIFPrintfL(fpOutput_, "%s", osRet.c_str());
      66             :                 }
      67             :             }
      68             :         }
      69          41 :         VSIFPrintfL(fpOutput_, "%s", "</Document></kml>\n");
      70             : 
      71          41 :         VSIFCloseL(fpOutput_);
      72             :     }
      73             : 
      74          67 :     CSLDestroy(papszCreateOptions_);
      75          67 :     CPLFree(pszNameField_);
      76          67 :     CPLFree(pszDescriptionField_);
      77          67 :     CPLFree(pszAltitudeMode_);
      78             : 
      79         208 :     for (int i = 0; i < nLayers_; i++)
      80             :     {
      81         141 :         delete papoLayers_[i];
      82             :     }
      83             : 
      84          67 :     CPLFree(papoLayers_);
      85             : 
      86             : #ifdef HAVE_EXPAT
      87          67 :     delete poKMLFile_;
      88             : #endif
      89         134 : }
      90             : 
      91             : /************************************************************************/
      92             : /*                                Open()                                */
      93             : /************************************************************************/
      94             : 
      95             : #ifdef HAVE_EXPAT
      96          25 : int OGRKMLDataSource::Open(const char *pszNewName, int bTestOpen)
      97             : {
      98          25 :     CPLAssert(nullptr != pszNewName);
      99             : 
     100             :     /* -------------------------------------------------------------------- */
     101             :     /*      Create a KML object and open the source file.                   */
     102             :     /* -------------------------------------------------------------------- */
     103          25 :     poKMLFile_ = new KMLVector();
     104             : 
     105          25 :     if (!poKMLFile_->open(pszNewName))
     106             :     {
     107           0 :         delete poKMLFile_;
     108           0 :         poKMLFile_ = nullptr;
     109           0 :         return FALSE;
     110             :     }
     111             : 
     112             :     /* -------------------------------------------------------------------- */
     113             :     /*      If we aren't sure it is KML, validate it by start parsing       */
     114             :     /* -------------------------------------------------------------------- */
     115          25 :     if (bTestOpen && !poKMLFile_->isValid())
     116             :     {
     117           1 :         delete poKMLFile_;
     118           1 :         poKMLFile_ = nullptr;
     119           1 :         return FALSE;
     120             :     }
     121             : 
     122             :     /* -------------------------------------------------------------------- */
     123             :     /*      Prescan the KML file so we can later work with the structure    */
     124             :     /* -------------------------------------------------------------------- */
     125          24 :     if (!poKMLFile_->parse())
     126             :     {
     127           1 :         delete poKMLFile_;
     128           1 :         poKMLFile_ = nullptr;
     129           1 :         return FALSE;
     130             :     }
     131             : 
     132             :     /* -------------------------------------------------------------------- */
     133             :     /*      Classify the nodes                                              */
     134             :     /* -------------------------------------------------------------------- */
     135          23 :     if (!poKMLFile_->classifyNodes())
     136             :     {
     137           0 :         delete poKMLFile_;
     138           0 :         poKMLFile_ = nullptr;
     139           0 :         return FALSE;
     140             :     }
     141             : 
     142             :     /* -------------------------------------------------------------------- */
     143             :     /*      Eliminate the empty containers (if there is at least one        */
     144             :     /*      valid container !)                                              */
     145             :     /* -------------------------------------------------------------------- */
     146          23 :     const bool bHasOnlyEmpty = poKMLFile_->hasOnlyEmpty();
     147          23 :     if (bHasOnlyEmpty)
     148           4 :         CPLDebug("KML", "Has only empty containers");
     149             :     else
     150          19 :         poKMLFile_->eliminateEmpty();
     151             : 
     152             :     /* -------------------------------------------------------------------- */
     153             :     /*      Find layers to use in the KML structure                         */
     154             :     /* -------------------------------------------------------------------- */
     155          23 :     poKMLFile_->findLayers(nullptr, bHasOnlyEmpty);
     156             : 
     157             :     /* -------------------------------------------------------------------- */
     158             :     /*      Print the structure                                             */
     159             :     /* -------------------------------------------------------------------- */
     160          23 :     if (CPLGetConfigOption("KML_DEBUG", nullptr) != nullptr)
     161           0 :         poKMLFile_->print(3);
     162             : 
     163          23 :     const int nLayers = poKMLFile_->getNumLayers();
     164             : 
     165             :     /* -------------------------------------------------------------------- */
     166             :     /*      Allocate memory for the Layers                                  */
     167             :     /* -------------------------------------------------------------------- */
     168          23 :     papoLayers_ =
     169          23 :         static_cast<OGRKMLLayer **>(CPLMalloc(sizeof(OGRKMLLayer *) * nLayers));
     170             : 
     171             :     OGRSpatialReference *poSRS =
     172          23 :         new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
     173          23 :     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     174             : 
     175             :     /* -------------------------------------------------------------------- */
     176             :     /*      Create the Layers and fill them                                 */
     177             :     /* -------------------------------------------------------------------- */
     178         104 :     for (int nCount = 0; nCount < nLayers; nCount++)
     179             :     {
     180          81 :         if (!poKMLFile_->selectLayer(nCount))
     181             :         {
     182           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     183             :                      "There are no layers or a layer can not be found!");
     184           0 :             break;
     185             :         }
     186             : 
     187          81 :         OGRwkbGeometryType poGeotype = wkbUnknown;
     188          81 :         if (poKMLFile_->getCurrentType() == Point)
     189          28 :             poGeotype = wkbPoint;
     190          53 :         else if (poKMLFile_->getCurrentType() == LineString)
     191          13 :             poGeotype = wkbLineString;
     192          40 :         else if (poKMLFile_->getCurrentType() == Polygon)
     193          33 :             poGeotype = wkbPolygon;
     194           7 :         else if (poKMLFile_->getCurrentType() == MultiPoint)
     195           0 :             poGeotype = wkbMultiPoint;
     196           7 :         else if (poKMLFile_->getCurrentType() == MultiLineString)
     197           0 :             poGeotype = wkbMultiLineString;
     198           7 :         else if (poKMLFile_->getCurrentType() == MultiPolygon)
     199           0 :             poGeotype = wkbMultiPolygon;
     200           7 :         else if (poKMLFile_->getCurrentType() == MultiGeometry)
     201           0 :             poGeotype = wkbGeometryCollection;
     202             : 
     203          81 :         if (poGeotype != wkbUnknown && poKMLFile_->is25D())
     204          71 :             poGeotype = wkbSetZ(poGeotype);
     205             : 
     206             :         /* --------------------------------------------------------------------
     207             :          */
     208             :         /*      Create the layer object. */
     209             :         /* --------------------------------------------------------------------
     210             :          */
     211          81 :         CPLString sName(poKMLFile_->getCurrentName());
     212             : 
     213          81 :         if (sName.empty())
     214             :         {
     215           4 :             sName.Printf("Layer #%d", nCount);
     216             :         }
     217             :         else
     218             :         {
     219             :             // Build unique layer name
     220          77 :             int nIter = 2;
     221             :             while (true)
     222             :             {
     223          78 :                 if (GetLayerByName(sName) == nullptr)
     224          77 :                     break;
     225             :                 sName = CPLSPrintf("%s (#%d)",
     226           1 :                                    poKMLFile_->getCurrentName().c_str(), nIter);
     227           1 :                 nIter++;
     228             :             }
     229             :         }
     230             : 
     231             :         OGRKMLLayer *poLayer =
     232          81 :             new OGRKMLLayer(sName.c_str(), poSRS, false, poGeotype, this);
     233             : 
     234          81 :         poLayer->SetLayerNumber(nCount);
     235             : 
     236             :         /* --------------------------------------------------------------------
     237             :          */
     238             :         /*      Add layer to data source layer list. */
     239             :         /* --------------------------------------------------------------------
     240             :          */
     241          81 :         papoLayers_[nCount] = poLayer;
     242             : 
     243          81 :         nLayers_ = nCount + 1;
     244             :     }
     245             : 
     246          23 :     poSRS->Release();
     247             : 
     248          23 :     return TRUE;
     249             : }
     250             : #endif /* HAVE_EXPAT */
     251             : 
     252             : /************************************************************************/
     253             : /*                               Create()                               */
     254             : /************************************************************************/
     255             : 
     256          42 : int OGRKMLDataSource::Create(const char *pszName, char **papszOptions)
     257             : {
     258          42 :     CPLAssert(nullptr != pszName);
     259             : 
     260          42 :     if (fpOutput_ != nullptr)
     261             :     {
     262           0 :         CPLAssert(false);
     263             :         return FALSE;
     264             :     }
     265             : 
     266          42 :     if (CSLFetchNameValue(papszOptions, "NameField"))
     267           0 :         pszNameField_ = CPLStrdup(CSLFetchNameValue(papszOptions, "NameField"));
     268             :     else
     269          42 :         pszNameField_ = CPLStrdup("Name");
     270             : 
     271          42 :     if (CSLFetchNameValue(papszOptions, "DescriptionField"))
     272           0 :         pszDescriptionField_ =
     273           0 :             CPLStrdup(CSLFetchNameValue(papszOptions, "DescriptionField"));
     274             :     else
     275          42 :         pszDescriptionField_ = CPLStrdup("Description");
     276             : 
     277          42 :     pszAltitudeMode_ =
     278          42 :         CPLStrdup(CSLFetchNameValue(papszOptions, "AltitudeMode"));
     279          42 :     if ((nullptr != pszAltitudeMode_) && strlen(pszAltitudeMode_) > 0)
     280             :     {
     281             :         // Check to see that the specified AltitudeMode is valid
     282           0 :         if (EQUAL(pszAltitudeMode_, "clampToGround") ||
     283           0 :             EQUAL(pszAltitudeMode_, "relativeToGround") ||
     284           0 :             EQUAL(pszAltitudeMode_, "absolute"))
     285             :         {
     286           0 :             CPLDebug("KML", "Using '%s' for AltitudeMode", pszAltitudeMode_);
     287             :         }
     288             :         else
     289             :         {
     290           0 :             CPLFree(pszAltitudeMode_);
     291           0 :             pszAltitudeMode_ = nullptr;
     292           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     293             :                      "Invalid AltitudeMode specified, ignoring");
     294             :         }
     295             :     }
     296             :     else
     297             :     {
     298          42 :         CPLFree(pszAltitudeMode_);
     299          42 :         pszAltitudeMode_ = nullptr;
     300             :     }
     301             : 
     302             :     /* -------------------------------------------------------------------- */
     303             :     /*      Create the output file.                                         */
     304             :     /* -------------------------------------------------------------------- */
     305             : 
     306          42 :     if (strcmp(pszName, "/dev/stdout") == 0)
     307           0 :         pszName = "/vsistdout/";
     308             : 
     309          42 :     fpOutput_ = VSIFOpenExL(pszName, "wb", true);
     310          42 :     if (fpOutput_ == nullptr)
     311             :     {
     312           1 :         CPLError(CE_Failure, CPLE_OpenFailed,
     313             :                  "Failed to create KML file %s: %s", pszName,
     314             :                  VSIGetLastErrorMsg());
     315           1 :         return FALSE;
     316             :     }
     317             : 
     318             :     /* -------------------------------------------------------------------- */
     319             :     /*      Write out "standard" header.                                    */
     320             :     /* -------------------------------------------------------------------- */
     321          41 :     VSIFPrintfL(fpOutput_, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
     322             : 
     323          41 :     VSIFPrintfL(fpOutput_,
     324             :                 "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
     325             :                 "<Document id=\"%s\">\n",
     326             :                 CSLFetchNameValueDef(papszOptions, "DOCUMENT_ID", "root_doc"));
     327             : 
     328          41 :     return TRUE;
     329             : }
     330             : 
     331             : /************************************************************************/
     332             : /*                           ICreateLayer()                             */
     333             : /************************************************************************/
     334             : 
     335             : OGRLayer *
     336          60 : OGRKMLDataSource::ICreateLayer(const char *pszLayerName,
     337             :                                const OGRGeomFieldDefn *poGeomFieldDefn,
     338             :                                CSLConstList /* papszOptions*/)
     339             : {
     340          60 :     CPLAssert(nullptr != pszLayerName);
     341             : 
     342             :     /* -------------------------------------------------------------------- */
     343             :     /*      Verify we are in update mode.                                   */
     344             :     /* -------------------------------------------------------------------- */
     345          60 :     if (fpOutput_ == nullptr)
     346             :     {
     347           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     348             :                  "Data source %s opened for read access.  "
     349             :                  "New layer %s cannot be created.",
     350           0 :                  GetDescription(), pszLayerName);
     351             : 
     352           0 :         return nullptr;
     353             :     }
     354             : 
     355          60 :     const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     356             :     const auto poSRS =
     357          60 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     358             : 
     359             :     /* -------------------------------------------------------------------- */
     360             :     /*      Close the previous layer (if there is one open)                 */
     361             :     /* -------------------------------------------------------------------- */
     362          60 :     if (GetLayerCount() > 0)
     363             :     {
     364          19 :         if (nLayers_ == 1 && papoLayers_[0]->nWroteFeatureCount_ == 0)
     365             :         {
     366           2 :             VSIFPrintfL(fpOutput_, "<Folder><name>%s</name>\n",
     367           2 :                         papoLayers_[0]->GetName());
     368             :         }
     369             : 
     370          19 :         VSIFPrintfL(fpOutput_, "</Folder>\n");
     371          19 :         papoLayers_[GetLayerCount() - 1]->SetClosedForWriting();
     372             :     }
     373             : 
     374             :     /* -------------------------------------------------------------------- */
     375             :     /*      Ensure name is safe as an element name.                         */
     376             :     /* -------------------------------------------------------------------- */
     377          60 :     char *pszCleanLayerName = CPLStrdup(pszLayerName);
     378             : 
     379          60 :     CPLCleanXMLElementName(pszCleanLayerName);
     380          60 :     if (strcmp(pszCleanLayerName, pszLayerName) != 0)
     381             :     {
     382           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     383             :                  "Layer name '%s' adjusted to '%s' for XML validity.",
     384             :                  pszLayerName, pszCleanLayerName);
     385             :     }
     386             : 
     387          60 :     if (GetLayerCount() > 0)
     388             :     {
     389          19 :         VSIFPrintfL(fpOutput_, "<Folder><name>%s</name>\n", pszCleanLayerName);
     390             :     }
     391             : 
     392             :     /* -------------------------------------------------------------------- */
     393             :     /*      Create the layer object.                                        */
     394             :     /* -------------------------------------------------------------------- */
     395             :     OGRKMLLayer *poLayer =
     396          60 :         new OGRKMLLayer(pszCleanLayerName, poSRS, true, eType, this);
     397             : 
     398          60 :     CPLFree(pszCleanLayerName);
     399             : 
     400             :     /* -------------------------------------------------------------------- */
     401             :     /*      Add layer to data source layer list.                            */
     402             :     /* -------------------------------------------------------------------- */
     403          60 :     papoLayers_ = static_cast<OGRKMLLayer **>(
     404          60 :         CPLRealloc(papoLayers_, sizeof(OGRKMLLayer *) * (nLayers_ + 1)));
     405             : 
     406          60 :     papoLayers_[nLayers_++] = poLayer;
     407             : 
     408          60 :     return poLayer;
     409             : }
     410             : 
     411             : /************************************************************************/
     412             : /*                           TestCapability()                           */
     413             : /************************************************************************/
     414             : 
     415          87 : int OGRKMLDataSource::TestCapability(const char *pszCap)
     416             : 
     417             : {
     418          87 :     if (EQUAL(pszCap, ODsCCreateLayer))
     419          37 :         return TRUE;
     420          50 :     else if (EQUAL(pszCap, ODsCZGeometries))
     421          12 :         return TRUE;
     422             : 
     423          38 :     return FALSE;
     424             : }
     425             : 
     426             : /************************************************************************/
     427             : /*                              GetLayer()                              */
     428             : /************************************************************************/
     429             : 
     430         818 : OGRLayer *OGRKMLDataSource::GetLayer(int iLayer)
     431             : {
     432         818 :     if (iLayer < 0 || iLayer >= nLayers_)
     433           2 :         return nullptr;
     434             : 
     435         816 :     return papoLayers_[iLayer];
     436             : }
     437             : 
     438             : /************************************************************************/
     439             : /*                            GrowExtents()                             */
     440             : /************************************************************************/
     441             : 
     442          90 : void OGRKMLDataSource::GrowExtents(OGREnvelope *psGeomBounds)
     443             : {
     444          90 :     CPLAssert(nullptr != psGeomBounds);
     445             : 
     446          90 :     oEnvelope_.Merge(*psGeomBounds);
     447          90 : }

Generated by: LCOV version 1.14