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

Generated by: LCOV version 1.14