Coverage Report

Created: 2025-12-31 08:30

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