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

Generated by: LCOV version 1.14