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

Generated by: LCOV version 1.14