LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/kml - ogrkmldatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 141 170 82.9 %
Date: 2026-06-27 16:33:51 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         136 : OGRKMLDataSource::~OGRKMLDataSource()
      45             : {
      46          68 :     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          68 :     CSLDestroy(papszCreateOptions_);
      75          68 :     CPLFree(pszNameField_);
      76          68 :     CPLFree(pszDescriptionField_);
      77          68 :     CPLFree(pszAltitudeMode_);
      78             : 
      79         210 :     for (int i = 0; i < nLayers_; i++)
      80             :     {
      81         142 :         delete papoLayers_[i];
      82             :     }
      83             : 
      84          68 :     CPLFree(papoLayers_);
      85             : 
      86             : #ifdef HAVE_EXPAT
      87          68 :     delete poKMLFile_;
      88             : #endif
      89         136 : }
      90             : 
      91             : /************************************************************************/
      92             : /*                                Open()                                */
      93             : /************************************************************************/
      94             : 
      95             : #ifdef HAVE_EXPAT
      96          26 : int OGRKMLDataSource::Open(const char *pszNewName, int bTestOpen)
      97             : {
      98          26 :     CPLAssert(nullptr != pszNewName);
      99             : 
     100             :     /* -------------------------------------------------------------------- */
     101             :     /*      Create a KML object and open the source file.                   */
     102             :     /* -------------------------------------------------------------------- */
     103          26 :     poKMLFile_ = new KMLVector();
     104             : 
     105          26 :     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          26 :     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          25 :     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          24 :     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          24 :     const bool bHasOnlyEmpty = poKMLFile_->hasOnlyEmpty();
     147          24 :     if (bHasOnlyEmpty)
     148           4 :         CPLDebug("KML", "Has only empty containers");
     149             :     else
     150          20 :         poKMLFile_->eliminateEmpty();
     151             : 
     152             :     /* -------------------------------------------------------------------- */
     153             :     /*      Find layers to use in the KML structure                         */
     154             :     /* -------------------------------------------------------------------- */
     155          24 :     poKMLFile_->findLayers(nullptr, bHasOnlyEmpty);
     156             : 
     157          24 :     poKMLFile_->findSchemas();
     158             : 
     159             :     /* -------------------------------------------------------------------- */
     160             :     /*      Print the structure                                             */
     161             :     /* -------------------------------------------------------------------- */
     162          24 :     if (CPLGetConfigOption("KML_DEBUG", nullptr) != nullptr)
     163           0 :         poKMLFile_->print(3);
     164             : 
     165          24 :     const int nLayers = poKMLFile_->getNumLayers();
     166             : 
     167             :     /* -------------------------------------------------------------------- */
     168             :     /*      Allocate memory for the Layers                                  */
     169             :     /* -------------------------------------------------------------------- */
     170          24 :     papoLayers_ =
     171          24 :         static_cast<OGRKMLLayer **>(CPLMalloc(sizeof(OGRKMLLayer *) * nLayers));
     172             : 
     173             :     OGRSpatialReference *poSRS =
     174          24 :         new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
     175          24 :     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     176             : 
     177          24 :     const auto &oMapSchemas = poKMLFile_->GetSchemas();
     178             : 
     179             :     /* -------------------------------------------------------------------- */
     180             :     /*      Create the Layers and fill them                                 */
     181             :     /* -------------------------------------------------------------------- */
     182         106 :     for (int nCount = 0; nCount < nLayers; nCount++)
     183             :     {
     184          82 :         if (!poKMLFile_->selectLayer(nCount))
     185             :         {
     186           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     187             :                      "There are no layers or a layer can not be found!");
     188           0 :             break;
     189             :         }
     190             : 
     191          82 :         OGRwkbGeometryType poGeotype = wkbUnknown;
     192          82 :         if (poKMLFile_->getCurrentType() == Point)
     193          29 :             poGeotype = wkbPoint;
     194          53 :         else if (poKMLFile_->getCurrentType() == LineString)
     195          13 :             poGeotype = wkbLineString;
     196          40 :         else if (poKMLFile_->getCurrentType() == Polygon)
     197          33 :             poGeotype = wkbPolygon;
     198           7 :         else if (poKMLFile_->getCurrentType() == MultiPoint)
     199           0 :             poGeotype = wkbMultiPoint;
     200           7 :         else if (poKMLFile_->getCurrentType() == MultiLineString)
     201           0 :             poGeotype = wkbMultiLineString;
     202           7 :         else if (poKMLFile_->getCurrentType() == MultiPolygon)
     203           0 :             poGeotype = wkbMultiPolygon;
     204           7 :         else if (poKMLFile_->getCurrentType() == MultiGeometry)
     205           0 :             poGeotype = wkbGeometryCollection;
     206             : 
     207          82 :         if (poGeotype != wkbUnknown && poKMLFile_->is25D())
     208          71 :             poGeotype = wkbSetZ(poGeotype);
     209             : 
     210             :         /* --------------------------------------------------------------------
     211             :          */
     212             :         /*      Create the layer object. */
     213             :         /* --------------------------------------------------------------------
     214             :          */
     215          82 :         CPLString sName(poKMLFile_->getCurrentName());
     216             : 
     217          82 :         const auto oIterSchema = oMapSchemas.find(sName);
     218             : 
     219          82 :         if (sName.empty())
     220             :         {
     221           4 :             sName.Printf("Layer #%d", nCount);
     222             :         }
     223             :         else
     224             :         {
     225             :             // Build unique layer name
     226          78 :             int nIter = 2;
     227             :             while (true)
     228             :             {
     229          79 :                 if (GetLayerByName(sName) == nullptr)
     230          78 :                     break;
     231             :                 sName = CPLSPrintf("%s (#%d)",
     232           1 :                                    poKMLFile_->getCurrentName().c_str(), nIter);
     233           1 :                 nIter++;
     234             :             }
     235             :         }
     236             : 
     237             :         OGRKMLLayer *poLayer =
     238          82 :             new OGRKMLLayer(sName.c_str(), poSRS, false, poGeotype, this);
     239             : 
     240          82 :         poLayer->SetLayerNumber(nCount);
     241             : 
     242          82 :         if (oIterSchema != oMapSchemas.end())
     243             :         {
     244           4 :             for (const auto &poFieldDefn : oIterSchema->second)
     245             :             {
     246           6 :                 if (strcmp(poFieldDefn->GetNameRef(), "Name") != 0 &&
     247           3 :                     strcmp(poFieldDefn->GetNameRef(), "Description") != 0)
     248             :                 {
     249           3 :                     poLayer->GetLayerDefn()->AddFieldDefn(poFieldDefn.get());
     250             :                 }
     251             :             }
     252             :         }
     253             : 
     254             :         /* --------------------------------------------------------------------
     255             :          */
     256             :         /*      Add layer to data source layer list. */
     257             :         /* --------------------------------------------------------------------
     258             :          */
     259          82 :         papoLayers_[nCount] = poLayer;
     260             : 
     261          82 :         nLayers_ = nCount + 1;
     262             :     }
     263             : 
     264          24 :     poSRS->Release();
     265             : 
     266          24 :     return TRUE;
     267             : }
     268             : #endif /* HAVE_EXPAT */
     269             : 
     270             : /************************************************************************/
     271             : /*                               Create()                               */
     272             : /************************************************************************/
     273             : 
     274          42 : int OGRKMLDataSource::Create(const char *pszName, CSLConstList papszOptions)
     275             : {
     276          42 :     CPLAssert(nullptr != pszName);
     277             : 
     278          42 :     if (fpOutput_ != nullptr)
     279             :     {
     280           0 :         CPLAssert(false);
     281             :         return FALSE;
     282             :     }
     283             : 
     284          42 :     if (CSLFetchNameValue(papszOptions, "NameField"))
     285           0 :         pszNameField_ = CPLStrdup(CSLFetchNameValue(papszOptions, "NameField"));
     286             :     else
     287          42 :         pszNameField_ = CPLStrdup("Name");
     288             : 
     289          42 :     if (CSLFetchNameValue(papszOptions, "DescriptionField"))
     290           0 :         pszDescriptionField_ =
     291           0 :             CPLStrdup(CSLFetchNameValue(papszOptions, "DescriptionField"));
     292             :     else
     293          42 :         pszDescriptionField_ = CPLStrdup("Description");
     294             : 
     295          42 :     pszAltitudeMode_ =
     296          42 :         CPLStrdup(CSLFetchNameValue(papszOptions, "AltitudeMode"));
     297          42 :     if ((nullptr != pszAltitudeMode_) && strlen(pszAltitudeMode_) > 0)
     298             :     {
     299             :         // Check to see that the specified AltitudeMode is valid
     300           0 :         if (EQUAL(pszAltitudeMode_, "clampToGround") ||
     301           0 :             EQUAL(pszAltitudeMode_, "relativeToGround") ||
     302           0 :             EQUAL(pszAltitudeMode_, "absolute"))
     303             :         {
     304           0 :             CPLDebug("KML", "Using '%s' for AltitudeMode", pszAltitudeMode_);
     305             :         }
     306             :         else
     307             :         {
     308           0 :             CPLFree(pszAltitudeMode_);
     309           0 :             pszAltitudeMode_ = nullptr;
     310           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     311             :                      "Invalid AltitudeMode specified, ignoring");
     312             :         }
     313             :     }
     314             :     else
     315             :     {
     316          42 :         CPLFree(pszAltitudeMode_);
     317          42 :         pszAltitudeMode_ = nullptr;
     318             :     }
     319             : 
     320             :     /* -------------------------------------------------------------------- */
     321             :     /*      Create the output file.                                         */
     322             :     /* -------------------------------------------------------------------- */
     323             : 
     324          42 :     if (strcmp(pszName, "/dev/stdout") == 0)
     325           0 :         pszName = "/vsistdout/";
     326             : 
     327          42 :     fpOutput_ = VSIFOpenExL(pszName, "wb", true);
     328          42 :     if (fpOutput_ == nullptr)
     329             :     {
     330           1 :         CPLError(CE_Failure, CPLE_OpenFailed,
     331             :                  "Failed to create KML file %s: %s", pszName,
     332             :                  VSIGetLastErrorMsg());
     333           1 :         return FALSE;
     334             :     }
     335             : 
     336             :     /* -------------------------------------------------------------------- */
     337             :     /*      Write out "standard" header.                                    */
     338             :     /* -------------------------------------------------------------------- */
     339          41 :     VSIFPrintfL(fpOutput_, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
     340             : 
     341          41 :     VSIFPrintfL(fpOutput_,
     342             :                 "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
     343             :                 "<Document id=\"%s\">\n",
     344             :                 CSLFetchNameValueDef(papszOptions, "DOCUMENT_ID", "root_doc"));
     345             : 
     346          41 :     return TRUE;
     347             : }
     348             : 
     349             : /************************************************************************/
     350             : /*                            ICreateLayer()                            */
     351             : /************************************************************************/
     352             : 
     353             : OGRLayer *
     354          60 : OGRKMLDataSource::ICreateLayer(const char *pszLayerName,
     355             :                                const OGRGeomFieldDefn *poGeomFieldDefn,
     356             :                                CSLConstList /* papszOptions*/)
     357             : {
     358          60 :     CPLAssert(nullptr != pszLayerName);
     359             : 
     360             :     /* -------------------------------------------------------------------- */
     361             :     /*      Verify we are in update mode.                                   */
     362             :     /* -------------------------------------------------------------------- */
     363          60 :     if (fpOutput_ == nullptr)
     364             :     {
     365           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     366             :                  "Data source %s opened for read access.  "
     367             :                  "New layer %s cannot be created.",
     368           0 :                  GetDescription(), pszLayerName);
     369             : 
     370           0 :         return nullptr;
     371             :     }
     372             : 
     373          60 :     const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     374             :     const auto poSRS =
     375          60 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     376             : 
     377             :     /* -------------------------------------------------------------------- */
     378             :     /*      Close the previous layer (if there is one open)                 */
     379             :     /* -------------------------------------------------------------------- */
     380          60 :     if (GetLayerCount() > 0)
     381             :     {
     382          19 :         if (nLayers_ == 1 && papoLayers_[0]->nWroteFeatureCount_ == 0)
     383             :         {
     384           2 :             VSIFPrintfL(fpOutput_, "<Folder><name>%s</name>\n",
     385           2 :                         papoLayers_[0]->GetName());
     386             :         }
     387             : 
     388          19 :         VSIFPrintfL(fpOutput_, "</Folder>\n");
     389          19 :         papoLayers_[GetLayerCount() - 1]->SetClosedForWriting();
     390             :     }
     391             : 
     392             :     /* -------------------------------------------------------------------- */
     393             :     /*      Ensure name is safe as an element name.                         */
     394             :     /* -------------------------------------------------------------------- */
     395          60 :     char *pszCleanLayerName = CPLStrdup(pszLayerName);
     396             : 
     397          60 :     CPLCleanXMLElementName(pszCleanLayerName);
     398          60 :     if (strcmp(pszCleanLayerName, pszLayerName) != 0)
     399             :     {
     400           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     401             :                  "Layer name '%s' adjusted to '%s' for XML validity.",
     402             :                  pszLayerName, pszCleanLayerName);
     403             :     }
     404             : 
     405          60 :     if (GetLayerCount() > 0)
     406             :     {
     407          19 :         VSIFPrintfL(fpOutput_, "<Folder><name>%s</name>\n", pszCleanLayerName);
     408             :     }
     409             : 
     410             :     /* -------------------------------------------------------------------- */
     411             :     /*      Create the layer object.                                        */
     412             :     /* -------------------------------------------------------------------- */
     413             :     OGRKMLLayer *poLayer =
     414          60 :         new OGRKMLLayer(pszCleanLayerName, poSRS, true, eType, this);
     415             : 
     416          60 :     CPLFree(pszCleanLayerName);
     417             : 
     418             :     /* -------------------------------------------------------------------- */
     419             :     /*      Add layer to data source layer list.                            */
     420             :     /* -------------------------------------------------------------------- */
     421          60 :     papoLayers_ = static_cast<OGRKMLLayer **>(
     422          60 :         CPLRealloc(papoLayers_, sizeof(OGRKMLLayer *) * (nLayers_ + 1)));
     423             : 
     424          60 :     papoLayers_[nLayers_++] = poLayer;
     425             : 
     426          60 :     return poLayer;
     427             : }
     428             : 
     429             : /************************************************************************/
     430             : /*                           TestCapability()                           */
     431             : /************************************************************************/
     432             : 
     433          87 : int OGRKMLDataSource::TestCapability(const char *pszCap) const
     434             : 
     435             : {
     436          87 :     if (EQUAL(pszCap, ODsCCreateLayer))
     437          37 :         return TRUE;
     438          50 :     else if (EQUAL(pszCap, ODsCZGeometries))
     439          12 :         return TRUE;
     440             : 
     441          38 :     return FALSE;
     442             : }
     443             : 
     444             : /************************************************************************/
     445             : /*                              GetLayer()                              */
     446             : /************************************************************************/
     447             : 
     448         819 : const OGRLayer *OGRKMLDataSource::GetLayer(int iLayer) const
     449             : {
     450         819 :     if (iLayer < 0 || iLayer >= nLayers_)
     451           2 :         return nullptr;
     452             : 
     453         817 :     return papoLayers_[iLayer];
     454             : }
     455             : 
     456             : /************************************************************************/
     457             : /*                            GrowExtents()                             */
     458             : /************************************************************************/
     459             : 
     460          91 : void OGRKMLDataSource::GrowExtents(OGREnvelope *psGeomBounds)
     461             : {
     462          91 :     CPLAssert(nullptr != psGeomBounds);
     463             : 
     464          91 :     oEnvelope_.Merge(*psGeomBounds);
     465          91 : }

Generated by: LCOV version 1.14