LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/kml - ogrkmllayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 223 284 78.5 %
Date: 2025-05-31 00:00:17 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  KML Driver
       4             :  * Purpose:  Implementation of OGRKMLLayer class.
       5             :  * Author:   Christopher Condit, condit@sdsc.edu
       6             :  *           Jens Oberender, j.obi@troja.net
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2006, Christopher Condit
      10             :  * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "ogr_kml.h"
      17             : 
      18             : #include <string>
      19             : 
      20             : #include "cpl_conv.h"
      21             : #include "cpl_error.h"
      22             : #include "cpl_string.h"
      23             : #include "cpl_vsi.h"
      24             : #include "kml.h"
      25             : #include "kmlutility.h"
      26             : #include "ogr_api.h"
      27             : #include "ogr_core.h"
      28             : #include "ogr_feature.h"
      29             : #include "ogr_featurestyle.h"
      30             : #include "ogr_geometry.h"
      31             : #include "ogr_p.h"
      32             : #include "ogr_spatialref.h"
      33             : #include "ogrsf_frmts.h"
      34             : 
      35             : /* Function utility to dump OGRGeometry to KML text. */
      36             : char *OGR_G_ExportToKML(OGRGeometryH hGeometry, const char *pszAltitudeMode);
      37             : 
      38             : /************************************************************************/
      39             : /*                           OGRKMLLayer()                              */
      40             : /************************************************************************/
      41             : 
      42         141 : OGRKMLLayer::OGRKMLLayer(const char *pszName,
      43             :                          const OGRSpatialReference *poSRSIn, bool bWriterIn,
      44         141 :                          OGRwkbGeometryType eReqType, OGRKMLDataSource *poDSIn)
      45             :     : poDS_(poDSIn),
      46         141 :       poSRS_(poSRSIn ? new OGRSpatialReference(nullptr) : nullptr),
      47         141 :       poFeatureDefn_(new OGRFeatureDefn(pszName)), bWriter_(bWriterIn),
      48         423 :       pszName_(CPLStrdup(pszName))
      49             : {
      50             :     // KML should be created as WGS84.
      51         141 :     if (poSRSIn != nullptr)
      52             :     {
      53          83 :         poSRS_->SetWellKnownGeogCS("WGS84");
      54          83 :         poSRS_->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      55          83 :         if (!poSRS_->IsSame(poSRSIn))
      56             :         {
      57           1 :             poCT_ = OGRCreateCoordinateTransformation(poSRSIn, poSRS_);
      58           1 :             if (poCT_ == nullptr && poDSIn->IsFirstCTError())
      59             :             {
      60             :                 // If we can't create a transformation, issue a warning - but
      61             :                 // continue the transformation.
      62           0 :                 char *pszWKT = nullptr;
      63             : 
      64           0 :                 poSRSIn->exportToPrettyWkt(&pszWKT, FALSE);
      65             : 
      66           0 :                 CPLError(
      67             :                     CE_Warning, CPLE_AppDefined,
      68             :                     "Failed to create coordinate transformation between the "
      69             :                     "input coordinate system and WGS84.  This may be because "
      70             :                     "they are not transformable.  "
      71             :                     "KML geometries may not render correctly.  "
      72             :                     "This message will not be issued any more."
      73             :                     "\nSource:\n%s\n",
      74             :                     pszWKT);
      75             : 
      76           0 :                 CPLFree(pszWKT);
      77           0 :                 poDSIn->IssuedFirstCTError();
      78             :             }
      79             :         }
      80             :     }
      81             : 
      82         141 :     SetDescription(poFeatureDefn_->GetName());
      83         141 :     poFeatureDefn_->Reference();
      84         141 :     poFeatureDefn_->SetGeomType(eReqType);
      85         141 :     if (poFeatureDefn_->GetGeomFieldCount() != 0)
      86         137 :         poFeatureDefn_->GetGeomFieldDefn(0)->SetSpatialRef(poSRS_);
      87             : 
      88         282 :     OGRFieldDefn oFieldName("Name", OFTString);
      89         141 :     poFeatureDefn_->AddFieldDefn(&oFieldName);
      90             : 
      91         141 :     OGRFieldDefn oFieldDesc("Description", OFTString);
      92         141 :     poFeatureDefn_->AddFieldDefn(&oFieldDesc);
      93             : 
      94         141 :     bClosedForWriting = !bWriterIn;
      95         141 : }
      96             : 
      97             : /************************************************************************/
      98             : /*                           ~OGRKMLLayer()                             */
      99             : /************************************************************************/
     100             : 
     101         282 : OGRKMLLayer::~OGRKMLLayer()
     102             : {
     103         141 :     if (nullptr != poFeatureDefn_)
     104         141 :         poFeatureDefn_->Release();
     105             : 
     106         141 :     if (nullptr != poSRS_)
     107          83 :         poSRS_->Release();
     108             : 
     109         141 :     if (nullptr != poCT_)
     110           1 :         delete poCT_;
     111             : 
     112         141 :     CPLFree(pszName_);
     113         282 : }
     114             : 
     115             : /************************************************************************/
     116             : /*                            GetLayerDefn()                            */
     117             : /************************************************************************/
     118             : 
     119        2360 : OGRFeatureDefn *OGRKMLLayer::GetLayerDefn()
     120             : {
     121        2360 :     return poFeatureDefn_;
     122             : }
     123             : 
     124             : /************************************************************************/
     125             : /*                            ResetReading()                            */
     126             : /************************************************************************/
     127             : 
     128         602 : void OGRKMLLayer::ResetReading()
     129             : {
     130         602 :     iNextKMLId_ = 0;
     131         602 :     nLastAsked = -1;
     132         602 :     nLastCount = -1;
     133         602 : }
     134             : 
     135             : /************************************************************************/
     136             : /*                           GetNextFeature()                           */
     137             : /************************************************************************/
     138             : 
     139         749 : OGRFeature *OGRKMLLayer::GetNextFeature()
     140             : {
     141             : #ifndef HAVE_EXPAT
     142             :     return nullptr;
     143             : #else
     144             :     /* -------------------------------------------------------------------- */
     145             :     /*      Loop till we find a feature matching our criteria.              */
     146             :     /* -------------------------------------------------------------------- */
     147         749 :     KML *poKMLFile = poDS_->GetKMLFile();
     148         749 :     if (poKMLFile == nullptr)
     149          16 :         return nullptr;
     150             : 
     151         733 :     poKMLFile->selectLayer(nLayerNumber_);
     152             : 
     153             :     while (true)
     154             :     {
     155             :         auto poFeatureKML = std::unique_ptr<Feature>(
     156         916 :             poKMLFile->getFeature(iNextKMLId_++, nLastAsked, nLastCount));
     157             : 
     158         916 :         if (poFeatureKML == nullptr)
     159         189 :             return nullptr;
     160             : 
     161         727 :         auto poFeature = std::make_unique<OGRFeature>(poFeatureDefn_);
     162             : 
     163         727 :         if (poFeatureKML->poGeom)
     164             :         {
     165         727 :             poFeature->SetGeometry(std::move(poFeatureKML->poGeom));
     166             :         }
     167             : 
     168             :         // Add fields.
     169        1454 :         poFeature->SetField(poFeatureDefn_->GetFieldIndex("Name"),
     170         727 :                             poFeatureKML->sName.c_str());
     171        1454 :         poFeature->SetField(poFeatureDefn_->GetFieldIndex("Description"),
     172         727 :                             poFeatureKML->sDescription.c_str());
     173         727 :         poFeature->SetFID(iNextKMLId_ - 1);
     174             : 
     175         727 :         if (poFeature->GetGeometryRef() != nullptr && poSRS_ != nullptr)
     176             :         {
     177         727 :             poFeature->GetGeometryRef()->assignSpatialReference(poSRS_);
     178             :         }
     179             : 
     180             :         // Check spatial/attribute filters.
     181        1663 :         if ((m_poFilterGeom == nullptr ||
     182        1378 :              FilterGeometry(poFeature->GetGeometryRef())) &&
     183         651 :             (m_poAttrQuery == nullptr ||
     184         215 :              m_poAttrQuery->Evaluate(poFeature.get())))
     185             :         {
     186         544 :             return poFeature.release();
     187             :         }
     188         183 :     }
     189             : 
     190             : #endif /* HAVE_EXPAT */
     191             : }
     192             : 
     193             : /************************************************************************/
     194             : /*                          GetFeatureCount()                           */
     195             : /************************************************************************/
     196             : 
     197             : #ifndef HAVE_EXPAT
     198             : GIntBig OGRKMLLayer::GetFeatureCount(int /* bForce */)
     199             : {
     200             :     return 0;
     201             : }
     202             : #else
     203             : 
     204          97 : GIntBig OGRKMLLayer::GetFeatureCount(int bForce)
     205             : {
     206          97 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
     207          36 :         return OGRLayer::GetFeatureCount(bForce);
     208             : 
     209          61 :     KML *poKMLFile = poDS_->GetKMLFile();
     210          61 :     if (nullptr == poKMLFile)
     211           0 :         return 0;
     212             : 
     213          61 :     poKMLFile->selectLayer(nLayerNumber_);
     214             : 
     215          61 :     return poKMLFile->getNumFeatures();
     216             : }
     217             : #endif
     218             : 
     219             : /************************************************************************/
     220             : /*                           WriteSchema()                              */
     221             : /************************************************************************/
     222             : 
     223          40 : CPLString OGRKMLLayer::WriteSchema()
     224             : {
     225          40 :     if (bSchemaWritten_)
     226           0 :         return "";
     227             : 
     228          80 :     CPLString osRet;
     229             : 
     230          40 :     OGRFeatureDefn *featureDefinition = GetLayerDefn();
     231         231 :     for (int j = 0; j < featureDefinition->GetFieldCount(); j++)
     232             :     {
     233         191 :         OGRFieldDefn *fieldDefinition = featureDefinition->GetFieldDefn(j);
     234             : 
     235         382 :         if (nullptr != poDS_->GetNameField() &&
     236         191 :             EQUAL(fieldDefinition->GetNameRef(), poDS_->GetNameField()))
     237          40 :             continue;
     238             : 
     239         302 :         if (nullptr != poDS_->GetDescriptionField() &&
     240         151 :             EQUAL(fieldDefinition->GetNameRef(), poDS_->GetDescriptionField()))
     241          40 :             continue;
     242             : 
     243         111 :         if (osRet.empty())
     244             :         {
     245             :             osRet += CPLSPrintf("<Schema name=\"%s\" id=\"%s\">\n", pszName_,
     246          37 :                                 pszName_);
     247             :         }
     248             : 
     249         111 :         const char *pszKMLType = nullptr;
     250         111 :         const char *pszKMLEltName = nullptr;
     251             :         // Match the OGR type to the GDAL type.
     252         111 :         switch (fieldDefinition->GetType())
     253             :         {
     254          20 :             case OFTInteger:
     255          20 :                 pszKMLType = "int";
     256          20 :                 pszKMLEltName = "SimpleField";
     257          20 :                 break;
     258           0 :             case OFTIntegerList:
     259           0 :                 pszKMLType = "int";
     260           0 :                 pszKMLEltName = "SimpleArrayField";
     261           0 :                 break;
     262          21 :             case OFTReal:
     263          21 :                 pszKMLType = "float";
     264          21 :                 pszKMLEltName = "SimpleField";
     265          21 :                 break;
     266           0 :             case OFTRealList:
     267           0 :                 pszKMLType = "float";
     268           0 :                 pszKMLEltName = "SimpleArrayField";
     269           0 :                 break;
     270          38 :             case OFTString:
     271          38 :                 pszKMLType = "string";
     272          38 :                 pszKMLEltName = "SimpleField";
     273          38 :                 break;
     274           0 :             case OFTStringList:
     275           0 :                 pszKMLType = "string";
     276           0 :                 pszKMLEltName = "SimpleArrayField";
     277           0 :                 break;
     278             :                 // TODO: KML doesn't handle these data types yet...
     279          32 :             case OFTDate:
     280             :             case OFTTime:
     281             :             case OFTDateTime:
     282          32 :                 pszKMLType = "string";
     283          32 :                 pszKMLEltName = "SimpleField";
     284          32 :                 break;
     285             : 
     286           0 :             default:
     287           0 :                 pszKMLType = "string";
     288           0 :                 pszKMLEltName = "SimpleField";
     289           0 :                 break;
     290             :         }
     291             :         osRet += CPLSPrintf("\t<%s name=\"%s\" type=\"%s\"></%s>\n",
     292             :                             pszKMLEltName, fieldDefinition->GetNameRef(),
     293         111 :                             pszKMLType, pszKMLEltName);
     294             :     }
     295             : 
     296          40 :     if (!osRet.empty())
     297          37 :         osRet += CPLSPrintf("%s", "</Schema>\n");
     298             : 
     299          40 :     return osRet;
     300             : }
     301             : 
     302             : /************************************************************************/
     303             : /*                           ICreateFeature()                            */
     304             : /************************************************************************/
     305             : 
     306         129 : OGRErr OGRKMLLayer::ICreateFeature(OGRFeature *poFeature)
     307             : {
     308         129 :     CPLAssert(nullptr != poFeature);
     309         129 :     CPLAssert(nullptr != poDS_);
     310             : 
     311         129 :     if (!bWriter_)
     312           0 :         return OGRERR_FAILURE;
     313             : 
     314         129 :     if (bClosedForWriting)
     315             :     {
     316           1 :         CPLError(
     317             :             CE_Failure, CPLE_NotSupported,
     318             :             "Interleaved feature adding to different layers is not supported");
     319           1 :         return OGRERR_FAILURE;
     320             :     }
     321             : 
     322         128 :     VSILFILE *fp = poDS_->GetOutputFP();
     323         128 :     CPLAssert(nullptr != fp);
     324             : 
     325         128 :     if (poDS_->GetLayerCount() == 1 && nWroteFeatureCount_ == 0)
     326             :     {
     327          44 :         CPLString osRet = WriteSchema();
     328          22 :         if (!osRet.empty())
     329          20 :             VSIFPrintfL(fp, "%s", osRet.c_str());
     330          22 :         bSchemaWritten_ = true;
     331             : 
     332          22 :         VSIFPrintfL(fp, "<Folder><name>%s</name>\n", pszName_);
     333             :     }
     334             : 
     335         128 :     ++nWroteFeatureCount_;
     336         128 :     char *pszEscapedLayerName = OGRGetXML_UTF8_EscapedString(GetDescription());
     337         128 :     VSIFPrintfL(fp, "  <Placemark id=\"%s." CPL_FRMT_GIB "\">\n",
     338             :                 pszEscapedLayerName, nWroteFeatureCount_);
     339         128 :     CPLFree(pszEscapedLayerName);
     340             : 
     341         128 :     if (poFeature->GetFID() == OGRNullFID)
     342         128 :         poFeature->SetFID(iNextKMLId_++);
     343             : 
     344             :     // Find and write the name element
     345         128 :     if (nullptr != poDS_->GetNameField())
     346             :     {
     347         834 :         for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
     348             :         {
     349         706 :             OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
     350             : 
     351        1063 :             if (poFeature->IsFieldSetAndNotNull(iField) &&
     352         357 :                 EQUAL(poField->GetNameRef(), poDS_->GetNameField()))
     353             :             {
     354           2 :                 const char *pszRaw = poFeature->GetFieldAsString(iField);
     355           2 :                 while (*pszRaw == ' ')
     356           0 :                     pszRaw++;
     357             : 
     358           2 :                 char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
     359             : 
     360           2 :                 VSIFPrintfL(fp, "\t<name>%s</name>\n", pszEscaped);
     361           2 :                 CPLFree(pszEscaped);
     362             :             }
     363             :         }
     364             :     }
     365             : 
     366         128 :     if (nullptr != poDS_->GetDescriptionField())
     367             :     {
     368         834 :         for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
     369             :         {
     370         706 :             OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
     371             : 
     372        1063 :             if (poFeature->IsFieldSetAndNotNull(iField) &&
     373         357 :                 EQUAL(poField->GetNameRef(), poDS_->GetDescriptionField()))
     374             :             {
     375           1 :                 const char *pszRaw = poFeature->GetFieldAsString(iField);
     376           1 :                 while (*pszRaw == ' ')
     377           0 :                     pszRaw++;
     378             : 
     379           1 :                 char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
     380             : 
     381           1 :                 VSIFPrintfL(fp, "\t<description>%s</description>\n",
     382             :                             pszEscaped);
     383           1 :                 CPLFree(pszEscaped);
     384             :             }
     385             :         }
     386             :     }
     387             : 
     388         128 :     OGRwkbGeometryType eGeomType = wkbNone;
     389         128 :     if (poFeature->GetGeometryRef() != nullptr)
     390          90 :         eGeomType = wkbFlatten(poFeature->GetGeometryRef()->getGeometryType());
     391             : 
     392         128 :     if (wkbPolygon == eGeomType || wkbMultiPolygon == eGeomType ||
     393          72 :         wkbLineString == eGeomType || wkbMultiLineString == eGeomType)
     394             :     {
     395          63 :         OGRStylePen *poPen = nullptr;
     396         126 :         OGRStyleMgr oSM;
     397             : 
     398          63 :         if (poFeature->GetStyleString() != nullptr)
     399             :         {
     400           0 :             oSM.InitFromFeature(poFeature);
     401             : 
     402           0 :             for (int i = 0; i < oSM.GetPartCount(); i++)
     403             :             {
     404           0 :                 OGRStyleTool *poTool = oSM.GetPart(i);
     405           0 :                 if (poTool && poTool->GetType() == OGRSTCPen)
     406             :                 {
     407           0 :                     poPen = cpl::down_cast<OGRStylePen *>(poTool);
     408           0 :                     break;
     409             :                 }
     410           0 :                 delete poTool;
     411             :             }
     412             :         }
     413             : 
     414          63 :         VSIFPrintfL(fp, "\t<Style>");
     415          63 :         if (poPen != nullptr)
     416             :         {
     417           0 :             GBool bDefault = FALSE;
     418             : 
     419             :             /* Require width to be returned in pixel */
     420           0 :             poPen->SetUnit(OGRSTUPixel);
     421           0 :             double fW = poPen->Width(bDefault);
     422           0 :             if (bDefault)
     423           0 :                 fW = 1;
     424           0 :             const char *pszColor = poPen->Color(bDefault);
     425           0 :             const int nColorLen = static_cast<int>(CPLStrnlen(pszColor, 10));
     426           0 :             if (pszColor != nullptr && pszColor[0] == '#' && !bDefault &&
     427             :                 nColorLen >= 7)
     428             :             {
     429           0 :                 char acColor[9] = {0};
     430             :                 /* Order of KML color is aabbggrr, whereas OGR color is
     431             :                  * #rrggbb[aa] ! */
     432           0 :                 if (nColorLen == 9)
     433             :                 {
     434           0 :                     acColor[0] = pszColor[7]; /* A */
     435           0 :                     acColor[1] = pszColor[8];
     436             :                 }
     437             :                 else
     438             :                 {
     439           0 :                     acColor[0] = 'F';
     440           0 :                     acColor[1] = 'F';
     441             :                 }
     442           0 :                 acColor[2] = pszColor[5]; /* B */
     443           0 :                 acColor[3] = pszColor[6];
     444           0 :                 acColor[4] = pszColor[3]; /* G */
     445           0 :                 acColor[5] = pszColor[4];
     446           0 :                 acColor[6] = pszColor[1]; /* R */
     447           0 :                 acColor[7] = pszColor[2];
     448           0 :                 VSIFPrintfL(fp, "<LineStyle><color>%s</color>", acColor);
     449           0 :                 VSIFPrintfL(fp, "<width>%g</width>", fW);
     450           0 :                 VSIFPrintfL(fp, "</LineStyle>");
     451             :             }
     452             :             else
     453             :             {
     454           0 :                 VSIFPrintfL(fp,
     455             :                             "<LineStyle><color>ff0000ff</color></LineStyle>");
     456             :             }
     457             :         }
     458             :         else
     459             :         {
     460          63 :             VSIFPrintfL(fp, "<LineStyle><color>ff0000ff</color></LineStyle>");
     461             :         }
     462          63 :         delete poPen;
     463             :         // If we're dealing with a polygon, add a line style that will stand out
     464             :         // a bit.
     465          63 :         VSIFPrintfL(fp, "<PolyStyle><fill>0</fill></PolyStyle></Style>\n");
     466             :     }
     467             : 
     468         128 :     bool bHasFoundOtherField = false;
     469             : 
     470             :     // Write all fields as SchemaData
     471         834 :     for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
     472             :     {
     473         706 :         OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
     474             : 
     475         706 :         if (poFeature->IsFieldSetAndNotNull(iField))
     476             :         {
     477         714 :             if (nullptr != poDS_->GetNameField() &&
     478         357 :                 EQUAL(poField->GetNameRef(), poDS_->GetNameField()))
     479           2 :                 continue;
     480             : 
     481         710 :             if (nullptr != poDS_->GetDescriptionField() &&
     482         355 :                 EQUAL(poField->GetNameRef(), poDS_->GetDescriptionField()))
     483           1 :                 continue;
     484             : 
     485         354 :             if (!bHasFoundOtherField)
     486             :             {
     487          86 :                 VSIFPrintfL(fp,
     488             :                             "\t<ExtendedData><SchemaData schemaUrl=\"#%s\">\n",
     489             :                             pszName_);
     490          86 :                 bHasFoundOtherField = true;
     491             :             }
     492         354 :             const char *pszRaw = poFeature->GetFieldAsString(iField);
     493             : 
     494         354 :             while (*pszRaw == ' ')
     495           0 :                 pszRaw++;
     496             : 
     497         354 :             char *pszEscaped = nullptr;
     498         354 :             if (poFeatureDefn_->GetFieldDefn(iField)->GetType() == OFTReal)
     499             :             {
     500         152 :                 pszEscaped = CPLStrdup(pszRaw);
     501             :             }
     502             :             else
     503             :             {
     504         202 :                 pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
     505             :             }
     506             : 
     507         354 :             VSIFPrintfL(fp, "\t\t<SimpleData name=\"%s\">%s</SimpleData>\n",
     508             :                         poField->GetNameRef(), pszEscaped);
     509             : 
     510         354 :             CPLFree(pszEscaped);
     511             :         }
     512             :     }
     513             : 
     514         128 :     if (bHasFoundOtherField)
     515             :     {
     516          86 :         VSIFPrintfL(fp, "\t</SchemaData></ExtendedData>\n");
     517             :     }
     518             : 
     519             :     // Write out Geometry - for now it isn't indented properly.
     520         128 :     if (poFeature->GetGeometryRef() != nullptr)
     521             :     {
     522          90 :         char *pszGeometry = nullptr;
     523          90 :         OGREnvelope sGeomBounds;
     524          90 :         OGRGeometry *poWGS84Geom = nullptr;
     525             : 
     526          90 :         if (nullptr != poCT_)
     527             :         {
     528           1 :             poWGS84Geom = poFeature->GetGeometryRef()->clone();
     529           1 :             poWGS84Geom->transform(poCT_);
     530             :         }
     531             :         else
     532             :         {
     533          89 :             poWGS84Geom = poFeature->GetGeometryRef();
     534             :         }
     535             : 
     536          90 :         pszGeometry = OGR_G_ExportToKML(OGRGeometry::ToHandle(poWGS84Geom),
     537          90 :                                         poDS_->GetAltitudeMode());
     538          90 :         if (pszGeometry != nullptr)
     539             :         {
     540          90 :             VSIFPrintfL(fp, "      %s\n", pszGeometry);
     541             :         }
     542             :         else
     543             :         {
     544           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     545             :                      "Export of geometry to KML failed");
     546             :         }
     547          90 :         CPLFree(pszGeometry);
     548             : 
     549          90 :         poWGS84Geom->getEnvelope(&sGeomBounds);
     550          90 :         poDS_->GrowExtents(&sGeomBounds);
     551             : 
     552          90 :         if (nullptr != poCT_)
     553             :         {
     554           1 :             delete poWGS84Geom;
     555             :         }
     556             :     }
     557             : 
     558         128 :     VSIFPrintfL(fp, "  </Placemark>\n");
     559         128 :     return OGRERR_NONE;
     560             : }
     561             : 
     562             : /************************************************************************/
     563             : /*                           TestCapability()                           */
     564             : /************************************************************************/
     565             : 
     566         333 : int OGRKMLLayer::TestCapability(const char *pszCap)
     567             : {
     568         333 :     if (EQUAL(pszCap, OLCSequentialWrite))
     569             :     {
     570          22 :         return bWriter_;
     571             :     }
     572         311 :     else if (EQUAL(pszCap, OLCCreateField))
     573             :     {
     574          28 :         return bWriter_ && iNextKMLId_ == 0;
     575             :     }
     576         283 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
     577             :     {
     578             :         //        if( poFClass == NULL
     579             :         //            || m_poFilterGeom != NULL
     580             :         //            || m_poAttrQuery != NULL )
     581           0 :         return FALSE;
     582             : 
     583             :         //        return poFClass->GetFeatureCount() != -1;
     584             :     }
     585             : 
     586         283 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
     587          78 :         return TRUE;
     588         205 :     else if (EQUAL(pszCap, OLCZGeometries))
     589          18 :         return TRUE;
     590             : 
     591         187 :     return FALSE;
     592             : }
     593             : 
     594             : /************************************************************************/
     595             : /*                            CreateField()                             */
     596             : /************************************************************************/
     597             : 
     598         111 : OGRErr OGRKMLLayer::CreateField(const OGRFieldDefn *poField,
     599             :                                 CPL_UNUSED int bApproxOK)
     600             : {
     601         111 :     if (!bWriter_ || iNextKMLId_ != 0)
     602           0 :         return OGRERR_FAILURE;
     603             : 
     604         111 :     OGRFieldDefn oCleanCopy(poField);
     605         111 :     poFeatureDefn_->AddFieldDefn(&oCleanCopy);
     606             : 
     607         111 :     return OGRERR_NONE;
     608             : }
     609             : 
     610             : /************************************************************************/
     611             : /*                           SetLayerNumber()                           */
     612             : /************************************************************************/
     613             : 
     614          81 : void OGRKMLLayer::SetLayerNumber(int nLayer)
     615             : {
     616          81 :     nLayerNumber_ = nLayer;
     617          81 : }
     618             : 
     619             : /************************************************************************/
     620             : /*                             GetDataset()                             */
     621             : /************************************************************************/
     622             : 
     623          17 : GDALDataset *OGRKMLLayer::GetDataset()
     624             : {
     625          17 :     return poDS_;
     626             : }

Generated by: LCOV version 1.14