Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/kml/ogrkmllayer.cpp
Line
Count
Source
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
OGRKMLLayer::OGRKMLLayer(const char *pszName,
43
                         const OGRSpatialReference *poSRSIn, bool bWriterIn,
44
                         OGRwkbGeometryType eReqType, OGRKMLDataSource *poDSIn)
45
2.37k
    : poDS_(poDSIn),
46
2.37k
      poSRS_(poSRSIn ? new OGRSpatialReference(nullptr) : nullptr),
47
2.37k
      poFeatureDefn_(new OGRFeatureDefn(pszName)), bWriter_(bWriterIn),
48
2.37k
      pszName_(CPLStrdup(pszName))
49
2.37k
{
50
    // KML should be created as WGS84.
51
2.37k
    if (poSRSIn != nullptr)
52
986
    {
53
986
        poSRS_->SetWellKnownGeogCS("WGS84");
54
986
        poSRS_->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
55
986
        if (!poSRS_->IsSame(poSRSIn))
56
696
        {
57
696
            poCT_ = OGRCreateCoordinateTransformation(poSRSIn, poSRS_);
58
696
            if (poCT_ == nullptr && poDSIn->IsFirstCTError())
59
155
            {
60
                // If we can't create a transformation, issue a warning - but
61
                // continue the transformation.
62
155
                char *pszWKT = nullptr;
63
64
155
                poSRSIn->exportToPrettyWkt(&pszWKT, FALSE);
65
66
155
                CPLError(
67
155
                    CE_Warning, CPLE_AppDefined,
68
155
                    "Failed to create coordinate transformation between the "
69
155
                    "input coordinate system and WGS84.  This may be because "
70
155
                    "they are not transformable.  "
71
155
                    "KML geometries may not render correctly.  "
72
155
                    "This message will not be issued any more."
73
155
                    "\nSource:\n%s\n",
74
155
                    pszWKT);
75
76
155
                CPLFree(pszWKT);
77
155
                poDSIn->IssuedFirstCTError();
78
155
            }
79
696
        }
80
986
    }
81
82
2.37k
    SetDescription(poFeatureDefn_->GetName());
83
2.37k
    poFeatureDefn_->Reference();
84
2.37k
    poFeatureDefn_->SetGeomType(eReqType);
85
2.37k
    if (poFeatureDefn_->GetGeomFieldCount() != 0)
86
1.16k
        poFeatureDefn_->GetGeomFieldDefn(0)->SetSpatialRef(poSRS_);
87
88
2.37k
    OGRFieldDefn oFieldName("Name", OFTString);
89
2.37k
    poFeatureDefn_->AddFieldDefn(&oFieldName);
90
91
2.37k
    OGRFieldDefn oFieldDesc("Description", OFTString);
92
2.37k
    poFeatureDefn_->AddFieldDefn(&oFieldDesc);
93
94
2.37k
    bClosedForWriting = !bWriterIn;
95
2.37k
}
96
97
/************************************************************************/
98
/*                            ~OGRKMLLayer()                            */
99
/************************************************************************/
100
101
OGRKMLLayer::~OGRKMLLayer()
102
2.37k
{
103
2.37k
    if (nullptr != poFeatureDefn_)
104
2.37k
        poFeatureDefn_->Release();
105
106
2.37k
    if (nullptr != poSRS_)
107
986
        poSRS_->Release();
108
109
2.37k
    if (nullptr != poCT_)
110
425
        delete poCT_;
111
112
2.37k
    CPLFree(pszName_);
113
2.37k
}
114
115
/************************************************************************/
116
/*                            GetLayerDefn()                            */
117
/************************************************************************/
118
119
const OGRFeatureDefn *OGRKMLLayer::GetLayerDefn() const
120
798k
{
121
798k
    return poFeatureDefn_;
122
798k
}
123
124
/************************************************************************/
125
/*                            ResetReading()                            */
126
/************************************************************************/
127
128
void OGRKMLLayer::ResetReading()
129
0
{
130
0
    iNextKMLId_ = 0;
131
0
    nLastAsked = -1;
132
0
    nLastCount = -1;
133
0
}
134
135
/************************************************************************/
136
/*                           GetNextFeature()                           */
137
/************************************************************************/
138
139
OGRFeature *OGRKMLLayer::GetNextFeature()
140
1.00k
{
141
#ifndef HAVE_EXPAT
142
    return nullptr;
143
#else
144
    /* -------------------------------------------------------------------- */
145
    /*      Loop till we find a feature matching our criteria.              */
146
    /* -------------------------------------------------------------------- */
147
1.00k
    KML *poKMLFile = poDS_->GetKMLFile();
148
1.00k
    if (poKMLFile == nullptr)
149
0
        return nullptr;
150
151
1.00k
    poKMLFile->selectLayer(nLayerNumber_);
152
153
1.00k
    while (true)
154
1.00k
    {
155
1.00k
        auto poFeatureKML = std::unique_ptr<Feature>(
156
1.00k
            poKMLFile->getFeature(iNextKMLId_++, nLastAsked, nLastCount));
157
158
1.00k
        if (poFeatureKML == nullptr)
159
142
            return nullptr;
160
161
859
        auto poFeature = std::make_unique<OGRFeature>(poFeatureDefn_);
162
163
859
        if (poFeatureKML->poGeom)
164
859
        {
165
859
            poFeature->SetGeometry(std::move(poFeatureKML->poGeom));
166
859
        }
167
168
        // Add fields.
169
859
        poFeature->SetField(poFeatureDefn_->GetFieldIndex("Name"),
170
859
                            poFeatureKML->sName.c_str());
171
859
        poFeature->SetField(poFeatureDefn_->GetFieldIndex("Description"),
172
859
                            poFeatureKML->sDescription.c_str());
173
859
        poFeature->SetFID(iNextKMLId_ - 1);
174
175
859
        for (const auto &[key, value] : poFeatureKML->oFields)
176
0
        {
177
0
            const int nFieldIdx = poFeatureDefn_->GetFieldIndex(key.c_str());
178
0
            if (nFieldIdx > 0)
179
0
                poFeature->SetField(nFieldIdx, value.c_str());
180
0
        }
181
182
859
        if (poFeature->GetGeometryRef() != nullptr && poSRS_ != nullptr)
183
859
        {
184
859
            poFeature->GetGeometryRef()->assignSpatialReference(poSRS_);
185
859
        }
186
187
        // Check spatial/attribute filters.
188
859
        if ((m_poFilterGeom == nullptr ||
189
0
             FilterGeometry(poFeature->GetGeometryRef())) &&
190
859
            (m_poAttrQuery == nullptr ||
191
0
             m_poAttrQuery->Evaluate(poFeature.get())))
192
859
        {
193
859
            return poFeature.release();
194
859
        }
195
859
    }
196
197
1.00k
#endif /* HAVE_EXPAT */
198
1.00k
}
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
GIntBig OGRKMLLayer::GetFeatureCount(int bForce)
212
0
{
213
0
    if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
214
0
        return OGRLayer::GetFeatureCount(bForce);
215
216
0
    KML *poKMLFile = poDS_->GetKMLFile();
217
0
    if (nullptr == poKMLFile)
218
0
        return 0;
219
220
0
    poKMLFile->selectLayer(nLayerNumber_);
221
222
0
    return poKMLFile->getNumFeatures();
223
0
}
224
#endif
225
226
/************************************************************************/
227
/*                            WriteSchema()                             */
228
/************************************************************************/
229
230
CPLString OGRKMLLayer::WriteSchema()
231
1.51k
{
232
1.51k
    if (bSchemaWritten_)
233
0
        return "";
234
235
1.51k
    CPLString osRet;
236
237
1.51k
    const OGRFeatureDefn *featureDefinition = GetLayerDefn();
238
19.7k
    for (int j = 0; j < featureDefinition->GetFieldCount(); j++)
239
18.2k
    {
240
18.2k
        const OGRFieldDefn *fieldDefinition =
241
18.2k
            featureDefinition->GetFieldDefn(j);
242
243
18.2k
        if (nullptr != poDS_->GetNameField() &&
244
18.2k
            EQUAL(fieldDefinition->GetNameRef(), poDS_->GetNameField()))
245
1.51k
            continue;
246
247
16.7k
        if (nullptr != poDS_->GetDescriptionField() &&
248
16.7k
            EQUAL(fieldDefinition->GetNameRef(), poDS_->GetDescriptionField()))
249
1.51k
            continue;
250
251
15.2k
        if (osRet.empty())
252
1.46k
        {
253
1.46k
            osRet += CPLSPrintf("<Schema name=\"%s\" id=\"%s\">\n", pszName_,
254
1.46k
                                pszName_);
255
1.46k
        }
256
257
15.2k
        const char *pszKMLType = nullptr;
258
15.2k
        const char *pszKMLEltName = nullptr;
259
        // Match the OGR type to the GDAL type.
260
15.2k
        switch (fieldDefinition->GetType())
261
15.2k
        {
262
29
            case OFTInteger:
263
29
                pszKMLType = "int";
264
29
                pszKMLEltName = "SimpleField";
265
29
                break;
266
0
            case OFTIntegerList:
267
0
                pszKMLType = "int";
268
0
                pszKMLEltName = "SimpleArrayField";
269
0
                break;
270
25
            case OFTReal:
271
25
                pszKMLType = "float";
272
25
                pszKMLEltName = "SimpleField";
273
25
                break;
274
0
            case OFTRealList:
275
0
                pszKMLType = "float";
276
0
                pszKMLEltName = "SimpleArrayField";
277
0
                break;
278
15.1k
            case OFTString:
279
15.1k
                pszKMLType = "string";
280
15.1k
                pszKMLEltName = "SimpleField";
281
15.1k
                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
0
            case OFTDate:
288
0
            case OFTTime:
289
0
            case OFTDateTime:
290
0
                pszKMLType = "string";
291
0
                pszKMLEltName = "SimpleField";
292
0
                break;
293
294
0
            default:
295
0
                pszKMLType = "string";
296
0
                pszKMLEltName = "SimpleField";
297
0
                break;
298
15.2k
        }
299
15.2k
        osRet += CPLSPrintf("\t<%s name=\"%s\" type=\"%s\"></%s>\n",
300
15.2k
                            pszKMLEltName, fieldDefinition->GetNameRef(),
301
15.2k
                            pszKMLType, pszKMLEltName);
302
15.2k
    }
303
304
1.51k
    if (!osRet.empty())
305
1.46k
        osRet += CPLSPrintf("%s", "</Schema>\n");
306
307
1.51k
    return osRet;
308
1.51k
}
309
310
/************************************************************************/
311
/*                           ICreateFeature()                           */
312
/************************************************************************/
313
314
OGRErr OGRKMLLayer::ICreateFeature(OGRFeature *poFeature)
315
765k
{
316
765k
    CPLAssert(nullptr != poFeature);
317
765k
    CPLAssert(nullptr != poDS_);
318
319
765k
    if (!bWriter_)
320
0
        return OGRERR_FAILURE;
321
322
765k
    if (bClosedForWriting)
323
20.1k
    {
324
20.1k
        CPLError(
325
20.1k
            CE_Failure, CPLE_NotSupported,
326
20.1k
            "Interleaved feature adding to different layers is not supported");
327
20.1k
        return OGRERR_FAILURE;
328
20.1k
    }
329
330
745k
    VSILFILE *fp = poDS_->GetOutputFP();
331
745k
    CPLAssert(nullptr != fp);
332
333
745k
    if (poDS_->GetLayerCount() == 1 && nWroteFeatureCount_ == 0)
334
394
    {
335
394
        CPLString osRet = WriteSchema();
336
394
        if (!osRet.empty())
337
377
            VSIFPrintfL(fp, "%s", osRet.c_str());
338
394
        bSchemaWritten_ = true;
339
340
394
        VSIFPrintfL(fp, "<Folder><name>%s</name>\n", pszName_);
341
394
    }
342
343
745k
    ++nWroteFeatureCount_;
344
745k
    char *pszEscapedLayerName = OGRGetXML_UTF8_EscapedString(GetDescription());
345
745k
    VSIFPrintfL(fp, "  <Placemark id=\"%s." CPL_FRMT_GIB "\">\n",
346
745k
                pszEscapedLayerName, nWroteFeatureCount_);
347
745k
    CPLFree(pszEscapedLayerName);
348
349
745k
    if (poFeature->GetFID() == OGRNullFID)
350
745k
        poFeature->SetFID(iNextKMLId_++);
351
352
    // Find and write the name element
353
745k
    if (nullptr != poDS_->GetNameField())
354
745k
    {
355
4.19M
        for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
356
3.44M
        {
357
3.44M
            OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
358
359
3.44M
            if (poFeature->IsFieldSetAndNotNull(iField) &&
360
888k
                EQUAL(poField->GetNameRef(), poDS_->GetNameField()))
361
0
            {
362
0
                const char *pszRaw = poFeature->GetFieldAsString(iField);
363
0
                while (*pszRaw == ' ')
364
0
                    pszRaw++;
365
366
0
                char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
367
368
0
                VSIFPrintfL(fp, "\t<name>%s</name>\n", pszEscaped);
369
0
                CPLFree(pszEscaped);
370
0
            }
371
3.44M
        }
372
745k
    }
373
374
745k
    if (nullptr != poDS_->GetDescriptionField())
375
745k
    {
376
4.19M
        for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
377
3.44M
        {
378
3.44M
            OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
379
380
3.44M
            if (poFeature->IsFieldSetAndNotNull(iField) &&
381
888k
                EQUAL(poField->GetNameRef(), poDS_->GetDescriptionField()))
382
0
            {
383
0
                const char *pszRaw = poFeature->GetFieldAsString(iField);
384
0
                while (*pszRaw == ' ')
385
0
                    pszRaw++;
386
387
0
                char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
388
389
0
                VSIFPrintfL(fp, "\t<description>%s</description>\n",
390
0
                            pszEscaped);
391
0
                CPLFree(pszEscaped);
392
0
            }
393
3.44M
        }
394
745k
    }
395
396
745k
    OGRwkbGeometryType eGeomType = wkbNone;
397
745k
    if (poFeature->GetGeometryRef() != nullptr)
398
28.3k
        eGeomType = wkbFlatten(poFeature->GetGeometryRef()->getGeometryType());
399
400
745k
    if (wkbPolygon == eGeomType || wkbMultiPolygon == eGeomType ||
401
743k
        wkbLineString == eGeomType || wkbMultiLineString == eGeomType)
402
15.5k
    {
403
15.5k
        OGRStylePen *poPen = nullptr;
404
15.5k
        OGRStyleMgr oSM;
405
406
15.5k
        if (poFeature->GetStyleString() != nullptr)
407
0
        {
408
0
            oSM.InitFromFeature(poFeature);
409
410
0
            for (int i = 0; i < oSM.GetPartCount(); i++)
411
0
            {
412
0
                OGRStyleTool *poTool = oSM.GetPart(i);
413
0
                if (poTool && poTool->GetType() == OGRSTCPen)
414
0
                {
415
0
                    poPen = cpl::down_cast<OGRStylePen *>(poTool);
416
0
                    break;
417
0
                }
418
0
                delete poTool;
419
0
            }
420
0
        }
421
422
15.5k
        VSIFPrintfL(fp, "\t<Style>");
423
15.5k
        if (poPen != nullptr)
424
0
        {
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
0
                nColorLen >= 7)
436
0
            {
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
0
                {
442
0
                    acColor[0] = pszColor[7]; /* A */
443
0
                    acColor[1] = pszColor[8];
444
0
                }
445
0
                else
446
0
                {
447
0
                    acColor[0] = 'F';
448
0
                    acColor[1] = 'F';
449
0
                }
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
0
            }
460
0
            else
461
0
            {
462
0
                VSIFPrintfL(fp,
463
0
                            "<LineStyle><color>ff0000ff</color></LineStyle>");
464
0
            }
465
0
        }
466
15.5k
        else
467
15.5k
        {
468
15.5k
            VSIFPrintfL(fp, "<LineStyle><color>ff0000ff</color></LineStyle>");
469
15.5k
        }
470
15.5k
        delete poPen;
471
        // If we're dealing with a polygon, add a line style that will stand out
472
        // a bit.
473
15.5k
        VSIFPrintfL(fp, "<PolyStyle><fill>0</fill></PolyStyle></Style>\n");
474
15.5k
    }
475
476
745k
    bool bHasFoundOtherField = false;
477
478
    // Write all fields as SchemaData
479
4.19M
    for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
480
3.44M
    {
481
3.44M
        OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
482
483
3.44M
        if (poFeature->IsFieldSetAndNotNull(iField))
484
888k
        {
485
888k
            if (nullptr != poDS_->GetNameField() &&
486
888k
                EQUAL(poField->GetNameRef(), poDS_->GetNameField()))
487
0
                continue;
488
489
888k
            if (nullptr != poDS_->GetDescriptionField() &&
490
888k
                EQUAL(poField->GetNameRef(), poDS_->GetDescriptionField()))
491
0
                continue;
492
493
888k
            if (!bHasFoundOtherField)
494
698k
            {
495
698k
                VSIFPrintfL(fp,
496
698k
                            "\t<ExtendedData><SchemaData schemaUrl=\"#%s\">\n",
497
698k
                            pszName_);
498
698k
                bHasFoundOtherField = true;
499
698k
            }
500
888k
            const char *pszRaw = poFeature->GetFieldAsString(iField);
501
502
904k
            while (*pszRaw == ' ')
503
16.2k
                pszRaw++;
504
505
888k
            char *pszEscaped = nullptr;
506
888k
            if (poFeatureDefn_->GetFieldDefn(iField)->GetType() == OFTReal)
507
1.01k
            {
508
1.01k
                pszEscaped = CPLStrdup(pszRaw);
509
1.01k
            }
510
887k
            else
511
887k
            {
512
887k
                pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
513
887k
            }
514
515
888k
            VSIFPrintfL(fp, "\t\t<SimpleData name=\"%s\">%s</SimpleData>\n",
516
888k
                        poField->GetNameRef(), pszEscaped);
517
518
888k
            CPLFree(pszEscaped);
519
888k
        }
520
3.44M
    }
521
522
745k
    if (bHasFoundOtherField)
523
698k
    {
524
698k
        VSIFPrintfL(fp, "\t</SchemaData></ExtendedData>\n");
525
698k
    }
526
527
    // Write out Geometry - for now it isn't indented properly.
528
745k
    if (poFeature->GetGeometryRef() != nullptr)
529
28.3k
    {
530
28.3k
        char *pszGeometry = nullptr;
531
28.3k
        OGREnvelope sGeomBounds;
532
28.3k
        OGRGeometry *poWGS84Geom = nullptr;
533
534
28.3k
        if (nullptr != poCT_)
535
1.86k
        {
536
1.86k
            poWGS84Geom = poFeature->GetGeometryRef()->clone();
537
1.86k
            poWGS84Geom->transform(poCT_);
538
1.86k
        }
539
26.4k
        else
540
26.4k
        {
541
26.4k
            poWGS84Geom = poFeature->GetGeometryRef();
542
26.4k
        }
543
544
28.3k
        pszGeometry = OGR_G_ExportToKML(OGRGeometry::ToHandle(poWGS84Geom),
545
28.3k
                                        poDS_->GetAltitudeMode());
546
28.3k
        if (pszGeometry != nullptr)
547
24.2k
        {
548
24.2k
            VSIFPrintfL(fp, "      %s\n", pszGeometry);
549
24.2k
        }
550
4.12k
        else
551
4.12k
        {
552
4.12k
            CPLError(CE_Failure, CPLE_AppDefined,
553
4.12k
                     "Export of geometry to KML failed");
554
4.12k
        }
555
28.3k
        CPLFree(pszGeometry);
556
557
28.3k
        poWGS84Geom->getEnvelope(&sGeomBounds);
558
28.3k
        poDS_->GrowExtents(&sGeomBounds);
559
560
28.3k
        if (nullptr != poCT_)
561
1.86k
        {
562
1.86k
            delete poWGS84Geom;
563
1.86k
        }
564
28.3k
    }
565
566
745k
    VSIFPrintfL(fp, "  </Placemark>\n");
567
745k
    return OGRERR_NONE;
568
765k
}
569
570
/************************************************************************/
571
/*                           TestCapability()                           */
572
/************************************************************************/
573
574
int OGRKMLLayer::TestCapability(const char *pszCap) const
575
11.9k
{
576
11.9k
    if (EQUAL(pszCap, OLCSequentialWrite))
577
0
    {
578
0
        return bWriter_;
579
0
    }
580
11.9k
    else if (EQUAL(pszCap, OLCCreateField))
581
0
    {
582
0
        return bWriter_ && iNextKMLId_ == 0;
583
0
    }
584
11.9k
    else if (EQUAL(pszCap, OLCFastFeatureCount))
585
0
    {
586
        //        if( poFClass == NULL
587
        //            || m_poFilterGeom != NULL
588
        //            || m_poAttrQuery != NULL )
589
0
        return FALSE;
590
591
        //        return poFClass->GetFeatureCount() != -1;
592
0
    }
593
594
11.9k
    else if (EQUAL(pszCap, OLCStringsAsUTF8))
595
0
        return TRUE;
596
11.9k
    else if (EQUAL(pszCap, OLCZGeometries))
597
2.96k
        return TRUE;
598
599
8.96k
    return FALSE;
600
11.9k
}
601
602
/************************************************************************/
603
/*                            CreateField()                             */
604
/************************************************************************/
605
606
OGRErr OGRKMLLayer::CreateField(const OGRFieldDefn *poField,
607
                                CPL_UNUSED int bApproxOK)
608
20.0k
{
609
20.0k
    if (!bWriter_ || iNextKMLId_ != 0)
610
156
        return OGRERR_FAILURE;
611
612
19.9k
    OGRFieldDefn oCleanCopy(poField);
613
19.9k
    poFeatureDefn_->AddFieldDefn(&oCleanCopy);
614
615
19.9k
    return OGRERR_NONE;
616
20.0k
}
617
618
/************************************************************************/
619
/*                           SetLayerNumber()                           */
620
/************************************************************************/
621
622
void OGRKMLLayer::SetLayerNumber(int nLayer)
623
290
{
624
290
    nLayerNumber_ = nLayer;
625
290
}
626
627
/************************************************************************/
628
/*                             GetDataset()                             */
629
/************************************************************************/
630
631
GDALDataset *OGRKMLLayer::GetDataset()
632
0
{
633
0
    return poDS_;
634
0
}