Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/kml/kml.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  KML Driver
4
 * Purpose:  Class for reading, parsing and handling a kmlfile.
5
 * Author:   Jens Oberender, j.obi@troja.net
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2007, Jens Oberender
9
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
#include "kmlnode.h"
14
#include "kml.h"
15
16
#include <cstring>
17
#include <cstdio>
18
#include <exception>
19
#include <iostream>
20
#include <string>
21
22
#include "cpl_conv.h"
23
#include "cpl_error.h"
24
#ifdef HAVE_EXPAT
25
#include "expat.h"
26
#endif
27
28
constexpr int PARSER_BUF_SIZE = 8192;
29
30
3.55k
KML::KML() = default;
31
32
KML::~KML()
33
3.55k
{
34
3.55k
    if (nullptr != pKMLFile_)
35
3.55k
        VSIFCloseL(pKMLFile_);
36
3.55k
    CPLFree(papoLayers_);
37
38
3.55k
    delete poTrunk_;
39
3.55k
}
40
41
bool KML::open(const char *pszFilename)
42
3.55k
{
43
3.55k
    if (nullptr != pKMLFile_)
44
0
        VSIFCloseL(pKMLFile_);
45
46
3.55k
    pKMLFile_ = VSIFOpenL(pszFilename, "r");
47
3.55k
    return pKMLFile_ != nullptr;
48
3.55k
}
49
50
bool KML::parse()
51
744
{
52
744
    if (nullptr == pKMLFile_)
53
0
    {
54
0
        sError_ = "No file given";
55
0
        return false;
56
0
    }
57
58
744
    if (poTrunk_ != nullptr)
59
0
    {
60
0
        delete poTrunk_;
61
0
        poTrunk_ = nullptr;
62
0
    }
63
64
744
    if (poCurrent_ != nullptr)
65
0
    {
66
0
        delete poCurrent_;
67
0
        poCurrent_ = nullptr;
68
0
    }
69
70
744
    XML_Parser oParser = OGRCreateExpatXMLParser();
71
744
    XML_SetUserData(oParser, this);
72
744
    XML_SetElementHandler(oParser, startElement, endElement);
73
744
    XML_SetCharacterDataHandler(oParser, dataHandler);
74
744
    oCurrentParser = oParser;
75
744
    nWithoutEventCounter = 0;
76
77
744
    int nDone = 0;
78
744
    unsigned nLen = 0;
79
744
    std::vector<char> aBuf(PARSER_BUF_SIZE);
80
744
    bool bError = false;
81
82
744
    do
83
16.7k
    {
84
16.7k
        nDataHandlerCounter = 0;
85
16.7k
        nLen = static_cast<unsigned>(
86
16.7k
            VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_));
87
16.7k
        nDone = nLen < aBuf.size();
88
16.7k
        if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
89
261
        {
90
261
            CPLError(CE_Failure, CPLE_AppDefined,
91
261
                     "XML parsing of KML file failed : %s at line %d, "
92
261
                     "column %d",
93
261
                     XML_ErrorString(XML_GetErrorCode(oParser)),
94
261
                     static_cast<int>(XML_GetCurrentLineNumber(oParser)),
95
261
                     static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
96
261
            bError = true;
97
261
            break;
98
261
        }
99
16.4k
        nWithoutEventCounter++;
100
16.4k
    } while (!nDone && nLen > 0 && nWithoutEventCounter < 10);
101
102
744
    XML_ParserFree(oParser);
103
744
    VSIRewindL(pKMLFile_);
104
105
744
    if (nWithoutEventCounter == 10)
106
0
    {
107
0
        CPLError(CE_Failure, CPLE_AppDefined,
108
0
                 "Too much data inside one element. File probably corrupted");
109
0
        bError = true;
110
0
    }
111
112
744
    if (bError)
113
261
    {
114
261
        if (poCurrent_ != nullptr)
115
259
        {
116
3.75k
            while (poCurrent_)
117
3.49k
            {
118
3.49k
                KMLNode *poTemp = poCurrent_->getParent();
119
3.49k
                delete poCurrent_;
120
3.49k
                poCurrent_ = poTemp;
121
3.49k
            }
122
            // No need to destroy poTrunk_ : it has been destroyed in
123
            // the last iteration
124
259
        }
125
2
        else
126
2
        {
127
            // Case of invalid content after closing element matching
128
            // first <kml> element
129
2
            delete poTrunk_;
130
2
        }
131
261
        poTrunk_ = nullptr;
132
261
        return false;
133
261
    }
134
135
483
    poCurrent_ = nullptr;
136
483
    return true;
137
744
}
138
139
void KML::checkValidity()
140
3.55k
{
141
3.55k
    if (poTrunk_ != nullptr)
142
0
    {
143
0
        delete poTrunk_;
144
0
        poTrunk_ = nullptr;
145
0
    }
146
147
3.55k
    if (poCurrent_ != nullptr)
148
0
    {
149
0
        delete poCurrent_;
150
0
        poCurrent_ = nullptr;
151
0
    }
152
153
3.55k
    if (pKMLFile_ == nullptr)
154
0
    {
155
0
        sError_ = "No file given";
156
0
        return;
157
0
    }
158
159
3.55k
    XML_Parser oParser = OGRCreateExpatXMLParser();
160
3.55k
    XML_SetUserData(oParser, this);
161
3.55k
    XML_SetElementHandler(oParser, startElementValidate, nullptr);
162
3.55k
    XML_SetCharacterDataHandler(oParser, dataHandlerValidate);
163
3.55k
    int nCount = 0;
164
165
3.55k
    oCurrentParser = oParser;
166
167
3.55k
    int nDone = 0;
168
3.55k
    unsigned nLen = 0;
169
3.55k
    std::vector<char> aBuf(PARSER_BUF_SIZE);
170
171
    // Parses the file until we find the first element.
172
3.55k
    do
173
3.56k
    {
174
3.56k
        nDataHandlerCounter = 0;
175
3.56k
        nLen = static_cast<unsigned>(
176
3.56k
            VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_));
177
3.56k
        nDone = nLen < aBuf.size();
178
3.56k
        if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
179
2.78k
        {
180
2.78k
            if (nLen <= PARSER_BUF_SIZE - 1)
181
1.24k
                aBuf[nLen] = 0;
182
1.53k
            else
183
1.53k
                aBuf[PARSER_BUF_SIZE - 1] = 0;
184
2.78k
            if (strstr(aBuf.data(), "<?xml") &&
185
716
                (strstr(aBuf.data(), "<kml") ||
186
1
                 (strstr(aBuf.data(), "<Document") &&
187
0
                  strstr(aBuf.data(), "/kml/2."))))
188
715
            {
189
715
                CPLError(
190
715
                    CE_Failure, CPLE_AppDefined,
191
715
                    "XML parsing of KML file failed : %s at line %d, column %d",
192
715
                    XML_ErrorString(XML_GetErrorCode(oParser)),
193
715
                    static_cast<int>(XML_GetCurrentLineNumber(oParser)),
194
715
                    static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
195
715
            }
196
197
2.78k
            validity = KML_VALIDITY_INVALID;
198
2.78k
            XML_ParserFree(oParser);
199
2.78k
            VSIRewindL(pKMLFile_);
200
2.78k
            return;
201
2.78k
        }
202
203
786
        nCount++;
204
        /* After reading 50 * PARSER_BUF_SIZE bytes, and not finding whether the file */
205
        /* is KML or not, we give up and fail silently */
206
786
    } while (!nDone && nLen > 0 && validity == KML_VALIDITY_UNKNOWN &&
207
16
             nCount < 50);
208
209
770
    XML_ParserFree(oParser);
210
770
    VSIRewindL(pKMLFile_);
211
770
    poCurrent_ = nullptr;
212
770
}
213
214
void XMLCALL KML::startElement(void *pUserData, const char *pszName,
215
                               const char **ppszAttr)
216
2.27M
{
217
2.27M
    KML *poKML = static_cast<KML *>(pUserData);
218
2.27M
    try
219
2.27M
    {
220
2.27M
        poKML->nWithoutEventCounter = 0;
221
222
2.27M
        const char *pszColumn = strchr(pszName, ':');
223
2.27M
        if (pszColumn)
224
366
            pszName = pszColumn + 1;
225
226
2.27M
        if (poKML->poTrunk_ == nullptr ||
227
2.27M
            (poKML->poCurrent_ != nullptr &&
228
2.27M
             poKML->poCurrent_->getName().compare("description") != 0))
229
2.27M
        {
230
2.27M
            if (poKML->nDepth_ == 1024)
231
0
            {
232
0
                CPLError(CE_Failure, CPLE_AppDefined,
233
0
                         "Too big depth level (%d) while parsing KML.",
234
0
                         poKML->nDepth_);
235
0
                XML_StopParser(poKML->oCurrentParser, XML_FALSE);
236
0
                return;
237
0
            }
238
239
2.27M
            KMLNode *poMynew = new KMLNode();
240
2.27M
            poMynew->setName(pszName);
241
2.27M
            poMynew->setLevel(poKML->nDepth_);
242
243
3.96M
            for (int i = 0; ppszAttr[i]; i += 2)
244
1.68M
            {
245
1.68M
                Attribute *poAtt = new Attribute();
246
1.68M
                poAtt->sName = ppszAttr[i];
247
1.68M
                poAtt->sValue = ppszAttr[i + 1];
248
1.68M
                poMynew->addAttribute(poAtt);
249
1.68M
            }
250
251
2.27M
            if (poKML->poTrunk_ == nullptr)
252
744
                poKML->poTrunk_ = poMynew;
253
2.27M
            if (poKML->poCurrent_ != nullptr)
254
2.27M
                poMynew->setParent(poKML->poCurrent_);
255
2.27M
            poKML->poCurrent_ = poMynew;
256
257
2.27M
            poKML->nDepth_++;
258
2.27M
        }
259
588
        else if (poKML->poCurrent_ != nullptr)
260
588
        {
261
588
            std::string sNewContent = "<";
262
588
            sNewContent += pszName;
263
826
            for (int i = 0; ppszAttr[i]; i += 2)
264
238
            {
265
238
                sNewContent += " ";
266
238
                sNewContent += ppszAttr[i];
267
238
                sNewContent += "=\"";
268
238
                sNewContent += ppszAttr[i + 1];
269
238
                sNewContent += "\"";
270
238
            }
271
588
            sNewContent += ">";
272
588
            if (poKML->poCurrent_->numContent() == 0)
273
0
                poKML->poCurrent_->addContent(sNewContent);
274
588
            else
275
588
                poKML->poCurrent_->appendContent(sNewContent);
276
588
        }
277
2.27M
    }
278
2.27M
    catch (const std::exception &ex)
279
2.27M
    {
280
0
        CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
281
0
                 ex.what());
282
0
        XML_StopParser(poKML->oCurrentParser, XML_FALSE);
283
0
    }
284
2.27M
}
285
286
void XMLCALL KML::startElementValidate(void *pUserData, const char *pszName,
287
                                       const char **ppszAttr)
288
99.5k
{
289
99.5k
    KML *poKML = static_cast<KML *>(pUserData);
290
291
99.5k
    if (poKML->validity != KML_VALIDITY_UNKNOWN)
292
98.1k
        return;
293
294
1.40k
    poKML->validity = KML_VALIDITY_INVALID;
295
296
1.40k
    const char *pszColumn = strchr(pszName, ':');
297
1.40k
    if (pszColumn)
298
2
        pszName = pszColumn + 1;
299
300
1.40k
    if (strcmp(pszName, "kml") == 0 || strcmp(pszName, "Document") == 0)
301
1.30k
    {
302
        // Check all Attributes
303
2.59k
        for (int i = 0; ppszAttr[i]; i += 2)
304
1.29k
        {
305
            // Find the namespace and determine the KML version
306
1.29k
            if (strcmp(ppszAttr[i], "xmlns") == 0)
307
1.28k
            {
308
                // Is it KML 2.2?
309
1.28k
                if ((strcmp(ppszAttr[i + 1],
310
1.28k
                            "http://earth.google.com/kml/2.2") == 0) ||
311
1.28k
                    (strcmp(ppszAttr[i + 1],
312
1.28k
                            "http://www.opengis.net/kml/2.2") == 0))
313
1.23k
                {
314
1.23k
                    poKML->validity = KML_VALIDITY_VALID;
315
1.23k
                    poKML->sVersion_ = "2.2";
316
1.23k
                }
317
50
                else if (strcmp(ppszAttr[i + 1],
318
50
                                "http://earth.google.com/kml/2.1") == 0)
319
26
                {
320
26
                    poKML->validity = KML_VALIDITY_VALID;
321
26
                    poKML->sVersion_ = "2.1";
322
26
                }
323
24
                else if (strcmp(ppszAttr[i + 1],
324
24
                                "http://earth.google.com/kml/2.0") == 0)
325
0
                {
326
0
                    poKML->validity = KML_VALIDITY_VALID;
327
0
                    poKML->sVersion_ = "2.0";
328
0
                }
329
24
                else
330
24
                {
331
24
                    CPLDebug("KML",
332
24
                             "Unhandled xmlns value : %s. Going on though...",
333
24
                             ppszAttr[i]);
334
24
                    poKML->validity = KML_VALIDITY_VALID;
335
24
                    poKML->sVersion_ = "?";
336
24
                }
337
1.28k
            }
338
1.29k
        }
339
340
1.30k
        if (poKML->validity == KML_VALIDITY_INVALID)
341
25
        {
342
25
            CPLDebug("KML", "Did not find xmlns attribute in <kml> element. "
343
25
                            "Going on though...");
344
25
            poKML->validity = KML_VALIDITY_VALID;
345
25
            poKML->sVersion_ = "?";
346
25
        }
347
1.30k
    }
348
1.40k
}
349
350
void XMLCALL KML::dataHandlerValidate(void *pUserData,
351
                                      const char * /* pszData */,
352
                                      int /* nLen */)
353
290k
{
354
290k
    KML *poKML = static_cast<KML *>(pUserData);
355
356
290k
    poKML->nDataHandlerCounter++;
357
290k
    if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE)
358
0
    {
359
0
        CPLError(CE_Failure, CPLE_AppDefined,
360
0
                 "File probably corrupted (million laugh pattern)");
361
0
        XML_StopParser(poKML->oCurrentParser, XML_FALSE);
362
0
    }
363
290k
}
364
365
void XMLCALL KML::endElement(void *pUserData, const char *pszName)
366
2.26M
{
367
2.26M
    KML *poKML = static_cast<KML *>(pUserData);
368
369
2.26M
    try
370
2.26M
    {
371
2.26M
        poKML->nWithoutEventCounter = 0;
372
373
2.26M
        const char *pszColumn = strchr(pszName, ':');
374
2.26M
        if (pszColumn)
375
352
            pszName = pszColumn + 1;
376
377
2.26M
        if (poKML->poCurrent_ != nullptr &&
378
2.26M
            poKML->poCurrent_->getName().compare(pszName) == 0)
379
2.26M
        {
380
2.26M
            poKML->nDepth_--;
381
2.26M
            KMLNode *poTmp = poKML->poCurrent_;
382
            // Split the coordinates
383
2.26M
            if (poKML->poCurrent_->getName().compare("coordinates") == 0 &&
384
20.5k
                poKML->poCurrent_->numContent() == 1)
385
15.1k
            {
386
15.1k
                const std::string sData = poKML->poCurrent_->getContent(0);
387
15.1k
                std::size_t nPos = 0;
388
15.1k
                const std::size_t nLength = sData.length();
389
15.1k
                const char *pszData = sData.c_str();
390
416k
                while (true)
391
416k
                {
392
                    // Cut off whitespaces
393
837k
                    while (nPos < nLength &&
394
821k
                           (pszData[nPos] == ' ' || pszData[nPos] == '\n' ||
395
401k
                            pszData[nPos] == '\r' || pszData[nPos] == '\t'))
396
420k
                        nPos++;
397
398
416k
                    if (nPos == nLength)
399
15.1k
                        break;
400
401
401k
                    const std::size_t nPosBegin = nPos;
402
401k
                    size_t nContentSize = 0;
403
404
                    // Get content
405
3.73M
                    while (nPos < nLength && pszData[nPos] != ' ' &&
406
3.39M
                           pszData[nPos] != '\n' && pszData[nPos] != '\r' &&
407
3.33M
                           pszData[nPos] != '\t')
408
3.33M
                    {
409
3.33M
                        nContentSize++;
410
3.33M
                        nPos++;
411
3.33M
                    }
412
413
401k
                    if (nContentSize > 0)
414
401k
                    {
415
401k
                        std::string sTmp(pszData + nPosBegin, nContentSize);
416
401k
                        poKML->poCurrent_->addContent(sTmp);
417
401k
                    }
418
401k
                }
419
15.1k
                if (poKML->poCurrent_->numContent() > 1)
420
15.1k
                    poKML->poCurrent_->deleteContent(0);
421
15.1k
            }
422
2.25M
            else if (poKML->poCurrent_->numContent() == 1)
423
1.63M
            {
424
1.63M
                const std::string sData = poKML->poCurrent_->getContent(0);
425
1.63M
                std::string sDataWithoutNL;
426
1.63M
                std::size_t nPos = 0;
427
1.63M
                const std::size_t nLength = sData.length();
428
1.63M
                const char *pszData = sData.c_str();
429
1.63M
                std::size_t nLineStartPos = 0;
430
1.63M
                bool bLineStart = true;
431
432
                // Re-assemble multi-line content by removing leading spaces for
433
                // each line.  I am not sure why we do that. Should we preserve
434
                // content as such?
435
34.6M
                while (nPos < nLength)
436
33.0M
                {
437
33.0M
                    const char ch = pszData[nPos];
438
33.0M
                    if (bLineStart &&
439
8.29M
                        (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'))
440
7.48M
                        nLineStartPos++;
441
25.5M
                    else if (ch == '\n' || ch == '\r')
442
217k
                    {
443
217k
                        if (!bLineStart)
444
217k
                        {
445
217k
                            std::string sTmp(pszData + nLineStartPos,
446
217k
                                             nPos - nLineStartPos);
447
217k
                            if (!sDataWithoutNL.empty())
448
174k
                                sDataWithoutNL += '\n';
449
217k
                            sDataWithoutNL += sTmp;
450
217k
                            bLineStart = true;
451
217k
                        }
452
217k
                        nLineStartPos = nPos + 1;
453
217k
                    }
454
25.3M
                    else
455
25.3M
                    {
456
25.3M
                        bLineStart = false;
457
25.3M
                    }
458
33.0M
                    nPos++;
459
33.0M
                }
460
461
1.63M
                if (nLineStartPos > 0)
462
1.07M
                {
463
1.07M
                    if (nLineStartPos < nPos)
464
44.1k
                    {
465
44.1k
                        std::string sTmp(pszData + nLineStartPos,
466
44.1k
                                         nPos - nLineStartPos);
467
44.1k
                        if (!sDataWithoutNL.empty())
468
42.9k
                            sDataWithoutNL += '\n';
469
44.1k
                        sDataWithoutNL += sTmp;
470
44.1k
                    }
471
472
1.07M
                    poKML->poCurrent_->deleteContent(0);
473
1.07M
                    poKML->poCurrent_->addContent(sDataWithoutNL);
474
1.07M
                }
475
1.63M
            }
476
477
2.26M
            if (poKML->poCurrent_->getParent() != nullptr)
478
2.26M
                poKML->poCurrent_ = poKML->poCurrent_->getParent();
479
485
            else
480
485
                poKML->poCurrent_ = nullptr;
481
482
2.26M
            if (!poKML->isHandled(pszName))
483
1.69M
            {
484
1.69M
                CPLDebug("KML", "Not handled: %s", pszName);
485
1.69M
                delete poTmp;
486
1.69M
                if (poKML->poCurrent_ == poTmp)
487
0
                    poKML->poCurrent_ = nullptr;
488
1.69M
                if (poKML->poTrunk_ == poTmp)
489
0
                    poKML->poTrunk_ = nullptr;
490
1.69M
            }
491
576k
            else
492
576k
            {
493
576k
                if (poKML->poCurrent_ != nullptr)
494
576k
                    poKML->poCurrent_->addChildren(poTmp);
495
576k
            }
496
2.26M
        }
497
523
        else if (poKML->poCurrent_ != nullptr)
498
523
        {
499
523
            std::string sNewContent = "</";
500
523
            sNewContent += pszName;
501
523
            sNewContent += ">";
502
523
            if (poKML->poCurrent_->numContent() == 0)
503
0
                poKML->poCurrent_->addContent(sNewContent);
504
523
            else
505
523
                poKML->poCurrent_->appendContent(sNewContent);
506
523
        }
507
2.26M
    }
508
2.26M
    catch (const std::exception &ex)
509
2.26M
    {
510
0
        CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
511
0
                 ex.what());
512
0
        XML_StopParser(poKML->oCurrentParser, XML_FALSE);
513
0
    }
514
2.26M
}
515
516
void XMLCALL KML::dataHandler(void *pUserData, const char *pszData, int nLen)
517
6.98M
{
518
6.98M
    KML *poKML = static_cast<KML *>(pUserData);
519
520
6.98M
    poKML->nWithoutEventCounter = 0;
521
522
6.98M
    if (nLen < 1 || poKML->poCurrent_ == nullptr)
523
0
        return;
524
525
6.98M
    poKML->nDataHandlerCounter++;
526
6.98M
    if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE)
527
0
    {
528
0
        CPLError(CE_Failure, CPLE_AppDefined,
529
0
                 "File probably corrupted (million laugh pattern)");
530
0
        XML_StopParser(poKML->oCurrentParser, XML_FALSE);
531
0
    }
532
533
6.98M
    try
534
6.98M
    {
535
6.98M
        std::string sData(pszData, nLen);
536
537
6.98M
        if (poKML->poCurrent_->numContent() == 0)
538
1.64M
            poKML->poCurrent_->addContent(sData);
539
5.33M
        else
540
5.33M
            poKML->poCurrent_->appendContent(sData);
541
6.98M
    }
542
6.98M
    catch (const std::exception &ex)
543
6.98M
    {
544
0
        CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
545
0
                 ex.what());
546
0
        XML_StopParser(poKML->oCurrentParser, XML_FALSE);
547
0
    }
548
6.98M
}
549
550
bool KML::isValid()
551
3.55k
{
552
3.55k
    checkValidity();
553
554
3.55k
    if (validity == KML_VALIDITY_VALID)
555
744
        CPLDebug("KML", "Valid: 1 Version: %s", sVersion_.c_str());
556
557
3.55k
    return validity == KML_VALIDITY_VALID;
558
3.55k
}
559
560
std::string KML::getError() const
561
0
{
562
0
    return sError_;
563
0
}
564
565
int KML::classifyNodes()
566
483
{
567
483
    if (poTrunk_ == nullptr)
568
0
        return false;
569
483
    return poTrunk_->classify(this);
570
483
}
571
572
void KML::eliminateEmpty()
573
186
{
574
186
    if (poTrunk_ != nullptr)
575
186
        poTrunk_->eliminateEmpty(this);
576
186
}
577
578
void KML::print(unsigned short nNum)
579
0
{
580
0
    if (poTrunk_ != nullptr)
581
0
        poTrunk_->print(nNum);
582
0
}
583
584
bool KML::isHandled(std::string const &elem) const
585
2.26M
{
586
2.26M
    return isLeaf(elem) || isFeature(elem) || isFeatureContainer(elem) ||
587
1.69M
           isContainer(elem) || isRest(elem);
588
2.26M
}
589
590
bool KML::isLeaf(std::string const & /* elem */) const
591
0
{
592
0
    return false;
593
0
}
594
595
bool KML::isFeature(std::string const & /* elem */) const
596
0
{
597
0
    return false;
598
0
}
599
600
bool KML::isFeatureContainer(std::string const & /* elem */) const
601
0
{
602
0
    return false;
603
0
}
604
605
bool KML::isContainer(std::string const & /* elem */) const
606
0
{
607
0
    return false;
608
0
}
609
610
bool KML::isRest(std::string const & /* elem */) const
611
0
{
612
0
    return false;
613
0
}
614
615
void KML::findLayers(KMLNode * /* poNode */, int /* bKeepEmptyContainers */)
616
0
{
617
    // idle
618
0
}
619
620
bool KML::hasOnlyEmpty() const
621
480
{
622
480
    return poTrunk_->hasOnlyEmpty();
623
480
}
624
625
int KML::getNumLayers() const
626
480
{
627
480
    return nNumLayers_;
628
480
}
629
630
bool KML::selectLayer(int nNum)
631
1.29k
{
632
1.29k
    if (nNumLayers_ < 1 || nNum >= nNumLayers_)
633
0
        return false;
634
1.29k
    poCurrent_ = papoLayers_[nNum];
635
1.29k
    return true;
636
1.29k
}
637
638
std::string KML::getCurrentName() const
639
681
{
640
681
    std::string tmp;
641
681
    if (poCurrent_ != nullptr)
642
681
    {
643
681
        tmp = poCurrent_->getNameElement();
644
681
    }
645
681
    return tmp;
646
681
}
647
648
Nodetype KML::getCurrentType() const
649
4.21k
{
650
4.21k
    if (poCurrent_ != nullptr)
651
4.21k
        return poCurrent_->getType();
652
653
0
    return Unknown;
654
4.21k
}
655
656
int KML::is25D() const
657
103
{
658
103
    if (poCurrent_ != nullptr)
659
103
        return poCurrent_->is25D();
660
661
0
    return Unknown;
662
103
}
663
664
int KML::getNumFeatures()
665
0
{
666
0
    if (poCurrent_ == nullptr)
667
0
        return -1;
668
669
0
    return static_cast<int>(poCurrent_->getNumFeatures());
670
0
}
671
672
Feature *KML::getFeature(std::size_t nNum, int &nLastAsked, int &nLastCount)
673
626
{
674
626
    if (poCurrent_ == nullptr)
675
0
        return nullptr;
676
677
626
    return poCurrent_->getFeature(nNum, nLastAsked, nLastCount);
678
626
}
679
680
void KML::unregisterLayerIfMatchingThisNode(KMLNode *poNode)
681
485k
{
682
489k
    for (int i = 0; i < nNumLayers_;)
683
3.62k
    {
684
3.62k
        if (papoLayers_[i] == poNode)
685
0
        {
686
0
            if (i < nNumLayers_ - 1)
687
0
            {
688
0
                memmove(papoLayers_ + i, papoLayers_ + i + 1,
689
0
                        (nNumLayers_ - 1 - i) * sizeof(KMLNode *));
690
0
            }
691
0
            nNumLayers_--;
692
0
            break;
693
0
        }
694
3.62k
        i++;
695
3.62k
    }
696
485k
}