Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/gpx/ogrgpxlayer.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GPX Translator
4
 * Purpose:  Implements OGRGPXLayer class.
5
 * Author:   Even Rouault, even dot rouault at spatialys.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "ogr_gpx.h"
15
16
#include <cstdio>
17
#include <cstdlib>
18
#include <cstring>
19
20
#include "cpl_conv.h"
21
#include "cpl_error.h"
22
#include "cpl_minixml.h"
23
#include "cpl_string.h"
24
#include "cpl_vsi.h"
25
#ifdef HAVE_EXPAT
26
#include "expat.h"
27
#endif
28
#include "ogr_core.h"
29
#include "ogr_expat.h"
30
#include "ogr_feature.h"
31
#include "ogr_geometry.h"
32
#include "ogr_p.h"
33
#include "ogr_spatialref.h"
34
35
constexpr int FLD_TRACK_FID = 0;
36
constexpr int FLD_TRACK_SEG_ID = 1;
37
#ifdef HAVE_EXPAT
38
constexpr int FLD_TRACK_PT_ID = 2;
39
#endif
40
constexpr int FLD_TRACK_NAME = 3;
41
42
constexpr int FLD_ROUTE_FID = 0;
43
#ifdef HAVE_EXPAT
44
constexpr int FLD_ROUTE_PT_ID = 1;
45
#endif
46
constexpr int FLD_ROUTE_NAME = 2;
47
48
/************************************************************************/
49
/*                            OGRGPXLayer()                             */
50
/*                                                                      */
51
/*      Note that the OGRGPXLayer assumes ownership of the passed       */
52
/*      file pointer.                                                   */
53
/************************************************************************/
54
55
OGRGPXLayer::OGRGPXLayer(const char *pszFilename, const char *pszLayerName,
56
                         GPXGeometryType gpxGeomTypeIn,
57
                         OGRGPXDataSource *poDSIn, bool bWriteModeIn,
58
                         CSLConstList papszOpenOptions)
59
1.63k
    : m_poDS(poDSIn), m_gpxGeomType(gpxGeomTypeIn), m_bWriteMode(bWriteModeIn)
60
1.63k
{
61
1.63k
#ifdef HAVE_EXPAT
62
1.63k
    const char *gpxVersion = m_poDS->GetVersion();
63
1.63k
#endif
64
65
1.63k
    m_nMaxLinks =
66
1.63k
        atoi(CSLFetchNameValueDef(papszOpenOptions, "N_MAX_LINKS",
67
1.63k
                                  CPLGetConfigOption("GPX_N_MAX_LINKS", "2")));
68
1.63k
    if (m_nMaxLinks < 0)
69
0
        m_nMaxLinks = 2;
70
1.63k
    if (m_nMaxLinks > 100)
71
0
        m_nMaxLinks = 100;
72
73
1.63k
    m_bEleAs25D = CPLTestBool(
74
1.63k
        CSLFetchNameValueDef(papszOpenOptions, "ELE_AS_25D",
75
1.63k
                             CPLGetConfigOption("GPX_ELE_AS_25D", "NO")));
76
77
1.63k
    const bool bShortNames = CPLTestBool(
78
1.63k
        CSLFetchNameValueDef(papszOpenOptions, "SHORT_NAMES",
79
1.63k
                             CPLGetConfigOption("GPX_SHORT_NAMES", "NO")));
80
81
1.63k
    m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
82
1.63k
    SetDescription(m_poFeatureDefn->GetName());
83
1.63k
    m_poFeatureDefn->Reference();
84
85
1.63k
    if (m_gpxGeomType == GPX_TRACK_POINT)
86
313
    {
87
        /* Don't move this code. This fields must be number 0, 1 and 2 */
88
        /* in order to make OGRGPXLayer::startElementCbk work */
89
313
        OGRFieldDefn oFieldTrackFID("track_fid", OFTInteger);
90
313
        m_poFeatureDefn->AddFieldDefn(&oFieldTrackFID);
91
92
313
        OGRFieldDefn oFieldTrackSegID(
93
313
            (bShortNames) ? "trksegid" : "track_seg_id", OFTInteger);
94
313
        m_poFeatureDefn->AddFieldDefn(&oFieldTrackSegID);
95
96
313
        OGRFieldDefn oFieldTrackSegPointID(
97
313
            (bShortNames) ? "trksegptid" : "track_seg_point_id", OFTInteger);
98
313
        m_poFeatureDefn->AddFieldDefn(&oFieldTrackSegPointID);
99
100
313
        if (m_bWriteMode)
101
0
        {
102
0
            OGRFieldDefn oFieldName("track_name", OFTString);
103
0
            m_poFeatureDefn->AddFieldDefn(&oFieldName);
104
0
        }
105
313
    }
106
1.32k
    else if (m_gpxGeomType == GPX_ROUTE_POINT)
107
313
    {
108
        /* Don't move this code. See above */
109
313
        OGRFieldDefn oFieldRouteFID("route_fid", OFTInteger);
110
313
        m_poFeatureDefn->AddFieldDefn(&oFieldRouteFID);
111
112
313
        OGRFieldDefn oFieldRoutePointID(
113
313
            (bShortNames) ? "rteptid" : "route_point_id", OFTInteger);
114
313
        m_poFeatureDefn->AddFieldDefn(&oFieldRoutePointID);
115
116
313
        if (m_bWriteMode)
117
0
        {
118
0
            OGRFieldDefn oFieldName("route_name", OFTString);
119
0
            m_poFeatureDefn->AddFieldDefn(&oFieldName);
120
0
        }
121
313
    }
122
123
1.63k
    m_iFirstGPXField = m_poFeatureDefn->GetFieldCount();
124
125
1.63k
    if (m_gpxGeomType == GPX_WPT || m_gpxGeomType == GPX_TRACK_POINT ||
126
1.01k
        m_gpxGeomType == GPX_ROUTE_POINT)
127
940
    {
128
940
        m_poFeatureDefn->SetGeomType((m_bEleAs25D) ? wkbPoint25D : wkbPoint);
129
        /* Position info */
130
131
940
        OGRFieldDefn oFieldEle("ele", OFTReal);
132
940
        m_poFeatureDefn->AddFieldDefn(&oFieldEle);
133
134
940
        OGRFieldDefn oFieldTime("time", OFTDateTime);
135
940
        m_poFeatureDefn->AddFieldDefn(&oFieldTime);
136
137
940
#ifdef HAVE_EXPAT
138
940
        if (m_gpxGeomType == GPX_TRACK_POINT && strcmp(gpxVersion, "1.0") == 0)
139
0
        {
140
0
            OGRFieldDefn oFieldCourse("course", OFTReal);
141
0
            m_poFeatureDefn->AddFieldDefn(&oFieldCourse);
142
143
0
            OGRFieldDefn oFieldSpeed("speed", OFTReal);
144
0
            m_poFeatureDefn->AddFieldDefn(&oFieldSpeed);
145
0
        }
146
940
#endif
147
148
940
        OGRFieldDefn oFieldMagVar("magvar", OFTReal);
149
940
        m_poFeatureDefn->AddFieldDefn(&oFieldMagVar);
150
151
940
        OGRFieldDefn oFieldGeoidHeight("geoidheight", OFTReal);
152
940
        m_poFeatureDefn->AddFieldDefn(&oFieldGeoidHeight);
153
154
        /* Description info */
155
156
940
        OGRFieldDefn oFieldName("name", OFTString);
157
940
        m_poFeatureDefn->AddFieldDefn(&oFieldName);
158
159
940
        OGRFieldDefn oFieldCmt("cmt", OFTString);
160
940
        m_poFeatureDefn->AddFieldDefn(&oFieldCmt);
161
162
940
        OGRFieldDefn oFieldDesc("desc", OFTString);
163
940
        m_poFeatureDefn->AddFieldDefn(&oFieldDesc);
164
165
940
        OGRFieldDefn oFieldSrc("src", OFTString);
166
940
        m_poFeatureDefn->AddFieldDefn(&oFieldSrc);
167
168
940
#ifdef HAVE_EXPAT
169
940
        if (strcmp(gpxVersion, "1.0") == 0)
170
0
        {
171
0
            OGRFieldDefn oFieldUrl("url", OFTString);
172
0
            m_poFeatureDefn->AddFieldDefn(&oFieldUrl);
173
174
0
            OGRFieldDefn oFieldUrlName("urlname", OFTString);
175
0
            m_poFeatureDefn->AddFieldDefn(&oFieldUrlName);
176
0
        }
177
940
        else
178
940
#endif
179
940
        {
180
2.82k
            for (int i = 1; i <= m_nMaxLinks; i++)
181
1.88k
            {
182
1.88k
                char szFieldName[32];
183
1.88k
                snprintf(szFieldName, sizeof(szFieldName), "link%d_href", i);
184
1.88k
                OGRFieldDefn oFieldLinkHref(szFieldName, OFTString);
185
1.88k
                m_poFeatureDefn->AddFieldDefn(&oFieldLinkHref);
186
187
1.88k
                snprintf(szFieldName, sizeof(szFieldName), "link%d_text", i);
188
1.88k
                OGRFieldDefn oFieldLinkText(szFieldName, OFTString);
189
1.88k
                m_poFeatureDefn->AddFieldDefn(&oFieldLinkText);
190
191
1.88k
                snprintf(szFieldName, sizeof(szFieldName), "link%d_type", i);
192
1.88k
                OGRFieldDefn oFieldLinkType(szFieldName, OFTString);
193
1.88k
                m_poFeatureDefn->AddFieldDefn(&oFieldLinkType);
194
1.88k
            }
195
940
        }
196
197
940
        OGRFieldDefn oFieldSym("sym", OFTString);
198
940
        m_poFeatureDefn->AddFieldDefn(&oFieldSym);
199
200
940
        OGRFieldDefn oFieldType("type", OFTString);
201
940
        m_poFeatureDefn->AddFieldDefn(&oFieldType);
202
203
        /* Accuracy info */
204
205
940
        OGRFieldDefn oFieldFix("fix", OFTString);
206
940
        m_poFeatureDefn->AddFieldDefn(&oFieldFix);
207
208
940
        OGRFieldDefn oFieldSat("sat", OFTInteger);
209
940
        m_poFeatureDefn->AddFieldDefn(&oFieldSat);
210
211
940
        OGRFieldDefn oFieldHdop("hdop", OFTReal);
212
940
        m_poFeatureDefn->AddFieldDefn(&oFieldHdop);
213
214
940
        OGRFieldDefn oFieldVdop("vdop", OFTReal);
215
940
        m_poFeatureDefn->AddFieldDefn(&oFieldVdop);
216
217
940
        OGRFieldDefn oFieldPdop("pdop", OFTReal);
218
940
        m_poFeatureDefn->AddFieldDefn(&oFieldPdop);
219
220
940
        OGRFieldDefn oFieldAgeofgpsdata("ageofdgpsdata", OFTReal);
221
940
        m_poFeatureDefn->AddFieldDefn(&oFieldAgeofgpsdata);
222
223
940
        OGRFieldDefn oFieldDgpsid("dgpsid", OFTInteger);
224
940
        m_poFeatureDefn->AddFieldDefn(&oFieldDgpsid);
225
940
    }
226
699
    else
227
699
    {
228
699
        if (m_gpxGeomType == GPX_TRACK)
229
339
            m_poFeatureDefn->SetGeomType((m_bEleAs25D) ? wkbMultiLineString25D
230
339
                                                       : wkbMultiLineString);
231
360
        else
232
360
            m_poFeatureDefn->SetGeomType((m_bEleAs25D) ? wkbLineString25D
233
360
                                                       : wkbLineString);
234
235
699
        OGRFieldDefn oFieldName("name", OFTString);
236
699
        m_poFeatureDefn->AddFieldDefn(&oFieldName);
237
238
699
        OGRFieldDefn oFieldCmt("cmt", OFTString);
239
699
        m_poFeatureDefn->AddFieldDefn(&oFieldCmt);
240
241
699
        OGRFieldDefn oFieldDesc("desc", OFTString);
242
699
        m_poFeatureDefn->AddFieldDefn(&oFieldDesc);
243
244
699
        OGRFieldDefn oFieldSrc("src", OFTString);
245
699
        m_poFeatureDefn->AddFieldDefn(&oFieldSrc);
246
247
2.09k
        for (int i = 1; i <= m_nMaxLinks; i++)
248
1.39k
        {
249
1.39k
            char szFieldName[32];
250
1.39k
            snprintf(szFieldName, sizeof(szFieldName), "link%d_href", i);
251
1.39k
            OGRFieldDefn oFieldLinkHref(szFieldName, OFTString);
252
1.39k
            m_poFeatureDefn->AddFieldDefn(&oFieldLinkHref);
253
254
1.39k
            snprintf(szFieldName, sizeof(szFieldName), "link%d_text", i);
255
1.39k
            OGRFieldDefn oFieldLinkText(szFieldName, OFTString);
256
1.39k
            m_poFeatureDefn->AddFieldDefn(&oFieldLinkText);
257
258
1.39k
            snprintf(szFieldName, sizeof(szFieldName), "link%d_type", i);
259
1.39k
            OGRFieldDefn oFieldLinkType(szFieldName, OFTString);
260
1.39k
            m_poFeatureDefn->AddFieldDefn(&oFieldLinkType);
261
1.39k
        }
262
263
699
        OGRFieldDefn oFieldNumber("number", OFTInteger);
264
699
        m_poFeatureDefn->AddFieldDefn(&oFieldNumber);
265
266
699
        OGRFieldDefn oFieldType("type", OFTString);
267
699
        m_poFeatureDefn->AddFieldDefn(&oFieldType);
268
699
    }
269
270
    /* Number of 'standard' GPX attributes */
271
1.63k
    m_nGPXFields = m_poFeatureDefn->GetFieldCount();
272
273
1.63k
    m_poSRS = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
274
1.63k
    m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
275
276
1.63k
    if (m_poFeatureDefn->GetGeomFieldCount() != 0)
277
1.63k
        m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
278
279
1.63k
    if (!m_bWriteMode)
280
1.56k
    {
281
1.56k
        m_fpGPX.reset(VSIFOpenL(pszFilename, "r"));
282
1.56k
        if (m_fpGPX == nullptr)
283
0
        {
284
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
285
0
                     pszFilename);
286
0
            return;
287
0
        }
288
289
1.56k
        if (m_poDS->GetUseExtensions() ||
290
315
            CPLTestBool(CPLGetConfigOption("GPX_USE_EXTENSIONS", "FALSE")))
291
1.25k
        {
292
1.25k
            LoadExtensionsSchema();
293
1.25k
        }
294
1.56k
    }
295
296
1.63k
    OGRGPXLayer::ResetReading();
297
1.63k
}
298
299
/************************************************************************/
300
/*                            ~OGRGPXLayer()                            */
301
/************************************************************************/
302
303
OGRGPXLayer::~OGRGPXLayer()
304
305
1.63k
{
306
1.63k
#ifdef HAVE_EXPAT
307
1.63k
    if (m_oParser)
308
1.56k
        XML_ParserFree(m_oParser);
309
1.63k
#endif
310
1.63k
    m_poFeatureDefn->Release();
311
312
1.63k
    if (m_poSRS != nullptr)
313
1.63k
        m_poSRS->Release();
314
1.63k
}
315
316
#ifdef HAVE_EXPAT
317
318
static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
319
                                    const char **ppszAttr)
320
280
{
321
280
    static_cast<OGRGPXLayer *>(pUserData)->startElementCbk(pszName, ppszAttr);
322
280
}
323
324
static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
325
280
{
326
280
    static_cast<OGRGPXLayer *>(pUserData)->endElementCbk(pszName);
327
280
}
328
329
static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
330
455
{
331
455
    static_cast<OGRGPXLayer *>(pUserData)->dataHandlerCbk(data, nLen);
332
455
}
333
334
#endif
335
336
/************************************************************************/
337
/*                            ResetReading()                            */
338
/************************************************************************/
339
340
void OGRGPXLayer::ResetReading()
341
342
1.63k
{
343
1.63k
    m_nNextFID = 0;
344
1.63k
    if (m_fpGPX)
345
1.56k
    {
346
1.56k
        m_fpGPX->Seek(0, SEEK_SET);
347
1.56k
#ifdef HAVE_EXPAT
348
1.56k
        if (m_oParser)
349
0
            XML_ParserFree(m_oParser);
350
351
1.56k
        m_oParser = OGRCreateExpatXMLParser();
352
1.56k
        XML_SetElementHandler(m_oParser, ::startElementCbk, ::endElementCbk);
353
1.56k
        XML_SetCharacterDataHandler(m_oParser, ::dataHandlerCbk);
354
1.56k
        XML_SetUserData(m_oParser, this);
355
1.56k
#endif
356
1.56k
    }
357
1.63k
    m_hasFoundLat = false;
358
1.63k
    m_hasFoundLon = false;
359
1.63k
    m_inInterestingElement = false;
360
1.63k
    m_osSubElementName.clear();
361
1.63k
    m_osSubElementValue.clear();
362
363
1.63k
    m_poFeature.reset();
364
1.63k
    m_oFeatureQueue.clear();
365
1.63k
    m_multiLineString.reset();
366
1.63k
    m_lineString.reset();
367
368
1.63k
    m_depthLevel = 0;
369
1.63k
    m_interestingDepthLevel = 0;
370
371
1.63k
    m_trkFID = 0;
372
1.63k
    m_trkSegId = 0;
373
1.63k
    m_trkSegPtId = 0;
374
1.63k
    m_rteFID = 0;
375
1.63k
    m_rtePtId = 0;
376
1.63k
}
377
378
#ifdef HAVE_EXPAT
379
380
/************************************************************************/
381
/*                          startElementCbk()                           */
382
/************************************************************************/
383
384
/** Replace ':' from XML NS element name by '_' more OGR friendly */
385
static char *OGRGPX_GetOGRCompatibleTagName(const char *pszName)
386
98.4k
{
387
98.4k
    char *pszModName = CPLStrdup(pszName);
388
3.20M
    for (int i = 0; pszModName[i] != 0; i++)
389
3.10M
    {
390
3.10M
        if (pszModName[i] == ':')
391
6.84k
            pszModName[i] = '_';
392
3.10M
    }
393
98.4k
    return pszModName;
394
98.4k
}
395
396
void OGRGPXLayer::AddStrToSubElementValue(const char *pszStr)
397
0
{
398
0
    try
399
0
    {
400
0
        m_osSubElementValue.append(pszStr);
401
0
    }
402
0
    catch (const std::bad_alloc &)
403
0
    {
404
0
        CPLError(CE_Failure, CPLE_OutOfMemory,
405
0
                 "Out of memory when parsing GPX file");
406
0
        XML_StopParser(m_oParser, XML_FALSE);
407
0
        m_bStopParsing = true;
408
0
    }
409
0
}
410
411
void OGRGPXLayer::startElementCbk(const char *pszName, const char **ppszAttr)
412
280
{
413
280
    if (m_bStopParsing)
414
0
        return;
415
416
280
    m_nWithoutEventCounter = 0;
417
418
280
    if ((m_gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0) ||
419
244
        (m_gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0) ||
420
244
        (m_gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0))
421
36
    {
422
36
        m_interestingDepthLevel = m_depthLevel;
423
424
36
        m_poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
425
36
        m_inInterestingElement = true;
426
36
        m_hasFoundLat = false;
427
36
        m_hasFoundLon = false;
428
36
        m_inExtensions = false;
429
36
        m_inLink = false;
430
36
        m_iCountLink = 0;
431
432
71
        for (int i = 0; ppszAttr[i]; i += 2)
433
35
        {
434
35
            if (strcmp(ppszAttr[i], "lat") == 0 && ppszAttr[i + 1][0])
435
0
            {
436
0
                m_hasFoundLat = true;
437
0
                m_latVal = CPLAtof(ppszAttr[i + 1]);
438
0
            }
439
35
            else if (strcmp(ppszAttr[i], "lon") == 0 && ppszAttr[i + 1][0])
440
0
            {
441
0
                m_hasFoundLon = true;
442
0
                m_lonVal = CPLAtof(ppszAttr[i + 1]);
443
0
            }
444
35
        }
445
446
36
        m_poFeature->SetFID(m_nNextFID++);
447
448
36
        if (m_hasFoundLat && m_hasFoundLon)
449
0
        {
450
0
            m_poFeature->SetGeometryDirectly(new OGRPoint(m_lonVal, m_latVal));
451
0
        }
452
36
        else
453
36
        {
454
36
            CPLDebug("GPX",
455
36
                     "Skipping %s (FID=" CPL_FRMT_GIB
456
36
                     ") without lat and/or lon",
457
36
                     pszName, m_nNextFID);
458
36
        }
459
460
36
        if (m_gpxGeomType == GPX_ROUTE_POINT)
461
0
        {
462
0
            m_rtePtId++;
463
0
            m_poFeature->SetField(FLD_ROUTE_FID, m_rteFID - 1);
464
0
            m_poFeature->SetField(FLD_ROUTE_PT_ID, m_rtePtId - 1);
465
0
        }
466
36
        else if (m_gpxGeomType == GPX_TRACK_POINT)
467
0
        {
468
0
            m_trkSegPtId++;
469
470
0
            m_poFeature->SetField(FLD_TRACK_FID, m_trkFID - 1);
471
0
            m_poFeature->SetField(FLD_TRACK_SEG_ID, m_trkSegId - 1);
472
0
            m_poFeature->SetField(FLD_TRACK_PT_ID, m_trkSegPtId - 1);
473
0
        }
474
36
    }
475
244
    else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
476
0
    {
477
0
        m_interestingDepthLevel = m_depthLevel;
478
479
0
        m_inExtensions = false;
480
0
        m_inLink = false;
481
0
        m_iCountLink = 0;
482
0
        m_poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
483
0
        m_inInterestingElement = true;
484
485
0
        m_multiLineString = std::make_unique<OGRMultiLineString>();
486
0
        m_lineString.reset();
487
488
0
        m_poFeature->SetFID(m_nNextFID++);
489
0
    }
490
244
    else if (m_gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trk") == 0)
491
0
    {
492
0
        m_trkFID++;
493
0
        m_trkSegId = 0;
494
0
    }
495
244
    else if (m_gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkseg") == 0)
496
0
    {
497
0
        m_trkSegId++;
498
0
        m_trkSegPtId = 0;
499
0
    }
500
244
    else if (m_gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
501
6
    {
502
6
        m_interestingDepthLevel = m_depthLevel;
503
504
6
        m_poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
505
6
        m_inInterestingElement = true;
506
6
        m_inExtensions = false;
507
6
        m_inLink = false;
508
6
        m_iCountLink = 0;
509
510
6
        m_lineString = std::make_unique<OGRLineString>();
511
6
        m_poFeature->SetFID(m_nNextFID++);
512
6
    }
513
238
    else if (m_gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rte") == 0)
514
6
    {
515
6
        m_rteFID++;
516
6
        m_rtePtId = 0;
517
6
    }
518
232
    else if (m_inInterestingElement)
519
45
    {
520
45
        if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trkseg") == 0 &&
521
0
            m_depthLevel == m_interestingDepthLevel + 1)
522
0
        {
523
0
            if (m_multiLineString)
524
0
            {
525
0
                m_lineString = std::make_unique<OGRLineString>();
526
0
            }
527
0
        }
528
45
        else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trkpt") == 0 &&
529
0
                 m_depthLevel == m_interestingDepthLevel + 2)
530
0
        {
531
0
            if (m_lineString)
532
0
            {
533
0
                m_hasFoundLat = false;
534
0
                m_hasFoundLon = false;
535
0
                for (int i = 0; ppszAttr[i]; i += 2)
536
0
                {
537
0
                    if (strcmp(ppszAttr[i], "lat") == 0 && ppszAttr[i + 1][0])
538
0
                    {
539
0
                        m_hasFoundLat = true;
540
0
                        m_latVal = CPLAtof(ppszAttr[i + 1]);
541
0
                    }
542
0
                    else if (strcmp(ppszAttr[i], "lon") == 0 &&
543
0
                             ppszAttr[i + 1][0])
544
0
                    {
545
0
                        m_hasFoundLon = true;
546
0
                        m_lonVal = CPLAtof(ppszAttr[i + 1]);
547
0
                    }
548
0
                }
549
550
0
                if (m_hasFoundLat && m_hasFoundLon)
551
0
                {
552
0
                    m_lineString->addPoint(m_lonVal, m_latVal);
553
0
                }
554
0
                else
555
0
                {
556
0
                    CPLDebug("GPX", "Skipping %s without lat and/or lon",
557
0
                             pszName);
558
0
                }
559
0
            }
560
0
        }
561
45
        else if (m_gpxGeomType == GPX_ROUTE && strcmp(pszName, "rtept") == 0 &&
562
0
                 m_depthLevel == m_interestingDepthLevel + 1)
563
0
        {
564
0
            if (m_lineString)
565
0
            {
566
0
                m_hasFoundLat = false;
567
0
                m_hasFoundLon = false;
568
0
                for (int i = 0; ppszAttr[i]; i += 2)
569
0
                {
570
0
                    if (strcmp(ppszAttr[i], "lat") == 0 && ppszAttr[i + 1][0])
571
0
                    {
572
0
                        m_hasFoundLat = true;
573
0
                        m_latVal = CPLAtof(ppszAttr[i + 1]);
574
0
                    }
575
0
                    else if (strcmp(ppszAttr[i], "lon") == 0 &&
576
0
                             ppszAttr[i + 1][0])
577
0
                    {
578
0
                        m_hasFoundLon = true;
579
0
                        m_lonVal = CPLAtof(ppszAttr[i + 1]);
580
0
                    }
581
0
                }
582
583
0
                if (m_hasFoundLat && m_hasFoundLon)
584
0
                {
585
0
                    m_lineString->addPoint(m_lonVal, m_latVal);
586
0
                }
587
0
                else
588
0
                {
589
0
                    CPLDebug("GPX", "Skipping %s without lat and/or lon",
590
0
                             pszName);
591
0
                }
592
0
            }
593
0
        }
594
45
        else if (m_bEleAs25D && strcmp(pszName, "ele") == 0 &&
595
0
                 m_lineString != nullptr &&
596
0
                 ((m_gpxGeomType == GPX_ROUTE &&
597
0
                   m_depthLevel == m_interestingDepthLevel + 2) ||
598
0
                  (m_gpxGeomType == GPX_TRACK &&
599
0
                   m_depthLevel == m_interestingDepthLevel + 3)))
600
0
        {
601
0
            m_osSubElementName = pszName;
602
0
        }
603
45
        else if (m_depthLevel == m_interestingDepthLevel + 1 &&
604
9
                 strcmp(pszName, "extensions") == 0)
605
8
        {
606
8
            if (m_poDS->GetUseExtensions())
607
8
            {
608
8
                m_inExtensions = true;
609
8
            }
610
8
        }
611
37
        else if (m_depthLevel == m_interestingDepthLevel + 1 ||
612
36
                 (m_inExtensions &&
613
31
                  m_depthLevel == m_interestingDepthLevel + 2))
614
32
        {
615
32
            m_osSubElementName.clear();
616
32
            m_iCurrentField = -1;
617
618
32
            if (strcmp(pszName, "link") == 0)
619
0
            {
620
0
                m_iCountLink++;
621
0
                if (m_iCountLink <= m_nMaxLinks)
622
0
                {
623
0
                    if (ppszAttr[0] && ppszAttr[1] &&
624
0
                        strcmp(ppszAttr[0], "href") == 0)
625
0
                    {
626
0
                        char szFieldName[32];
627
0
                        snprintf(szFieldName, sizeof(szFieldName),
628
0
                                 "link%d_href", m_iCountLink);
629
0
                        m_iCurrentField =
630
0
                            m_poFeatureDefn->GetFieldIndex(szFieldName);
631
0
                        m_poFeature->SetField(m_iCurrentField, ppszAttr[1]);
632
0
                    }
633
0
                }
634
0
                else
635
0
                {
636
0
                    static int once = 1;
637
0
                    if (once)
638
0
                    {
639
0
                        once = 0;
640
0
                        CPLError(CE_Warning, CPLE_AppDefined,
641
0
                                 "GPX driver only reads %d links per element. "
642
0
                                 "Others will be ignored. "
643
0
                                 "This can be changed with the GPX_N_MAX_LINKS "
644
0
                                 "environment variable",
645
0
                                 m_nMaxLinks);
646
0
                    }
647
0
                }
648
0
                m_inLink = true;
649
0
                m_iCurrentField = -1;
650
0
            }
651
32
            else
652
32
            {
653
416
                for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount();
654
384
                     iField++)
655
416
                {
656
416
                    bool bMatch = false;
657
416
                    if (iField >= m_nGPXFields)
658
32
                    {
659
32
                        char *pszCompatibleName =
660
32
                            OGRGPX_GetOGRCompatibleTagName(pszName);
661
32
                        bMatch = strcmp(m_poFeatureDefn->GetFieldDefn(iField)
662
32
                                            ->GetNameRef(),
663
32
                                        pszCompatibleName) == 0;
664
32
                        CPLFree(pszCompatibleName);
665
32
                    }
666
384
                    else
667
384
                    {
668
384
                        bMatch = strcmp(m_poFeatureDefn->GetFieldDefn(iField)
669
384
                                            ->GetNameRef(),
670
384
                                        pszName) == 0;
671
384
                    }
672
673
416
                    if (bMatch)
674
32
                    {
675
32
                        m_iCurrentField = iField;
676
32
                        m_osSubElementName = pszName;
677
32
                        break;
678
32
                    }
679
416
                }
680
32
            }
681
32
        }
682
5
        else if (m_depthLevel == m_interestingDepthLevel + 2 && m_inLink)
683
0
        {
684
0
            m_osSubElementName.clear();
685
0
            m_iCurrentField = -1;
686
0
            if (m_iCountLink <= m_nMaxLinks)
687
0
            {
688
0
                if (strcmp(pszName, "type") == 0)
689
0
                {
690
0
                    char szFieldName[32];
691
0
                    snprintf(szFieldName, sizeof(szFieldName), "link%d_type",
692
0
                             m_iCountLink);
693
0
                    m_iCurrentField =
694
0
                        m_poFeatureDefn->GetFieldIndex(szFieldName);
695
0
                    m_osSubElementName = pszName;
696
0
                }
697
0
                else if (strcmp(pszName, "text") == 0)
698
0
                {
699
0
                    char szFieldName[32];
700
0
                    snprintf(szFieldName, sizeof(szFieldName), "link%d_text",
701
0
                             m_iCountLink);
702
0
                    m_iCurrentField =
703
0
                        m_poFeatureDefn->GetFieldIndex(szFieldName);
704
0
                    m_osSubElementName = pszName;
705
0
                }
706
0
            }
707
0
        }
708
5
        else if (m_inExtensions && m_depthLevel > m_interestingDepthLevel + 2)
709
0
        {
710
0
            AddStrToSubElementValue((ppszAttr[0] == nullptr)
711
0
                                        ? CPLSPrintf("<%s>", pszName)
712
0
                                        : CPLSPrintf("<%s ", pszName));
713
0
            for (int i = 0; ppszAttr[i]; i += 2)
714
0
            {
715
0
                AddStrToSubElementValue(
716
0
                    CPLSPrintf("%s=\"%s\" ", ppszAttr[i], ppszAttr[i + 1]));
717
0
            }
718
0
            if (ppszAttr[0] != nullptr)
719
0
            {
720
0
                AddStrToSubElementValue(">");
721
0
            }
722
0
        }
723
45
    }
724
725
280
    m_depthLevel++;
726
280
}
727
728
/************************************************************************/
729
/*                           endElementCbk()                            */
730
/************************************************************************/
731
732
void OGRGPXLayer::endElementCbk(const char *pszName)
733
280
{
734
280
    if (m_bStopParsing)
735
0
        return;
736
737
280
    m_nWithoutEventCounter = 0;
738
739
280
    m_depthLevel--;
740
741
280
    if (m_inInterestingElement)
742
85
    {
743
85
        if ((m_gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0) ||
744
50
            (m_gpxGeomType == GPX_ROUTE_POINT &&
745
0
             strcmp(pszName, "rtept") == 0) ||
746
50
            (m_gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0))
747
35
        {
748
35
            const bool bIsValid = (m_hasFoundLat && m_hasFoundLon);
749
35
            m_inInterestingElement = false;
750
751
35
            if (bIsValid &&
752
0
                (m_poFilterGeom == nullptr ||
753
0
                 FilterGeometry(m_poFeature->GetGeometryRef())) &&
754
0
                (m_poAttrQuery == nullptr ||
755
0
                 m_poAttrQuery->Evaluate(m_poFeature.get())))
756
0
            {
757
0
                if (auto poGeom = m_poFeature->GetGeometryRef())
758
0
                {
759
0
                    poGeom->assignSpatialReference(m_poSRS);
760
761
0
                    if (m_bEleAs25D)
762
0
                    {
763
0
                        const int iEleField =
764
0
                            m_poFeatureDefn->GetFieldIndex("ele");
765
0
                        if (iEleField >= 0 &&
766
0
                            m_poFeature->IsFieldSetAndNotNull(iEleField))
767
0
                        {
768
0
                            const double val =
769
0
                                m_poFeature->GetFieldAsDouble(iEleField);
770
0
                            poGeom->toPoint()->setZ(val);
771
0
                            poGeom->setCoordinateDimension(3);
772
0
                        }
773
0
                    }
774
0
                }
775
776
0
                m_oFeatureQueue.push_back(std::move(m_poFeature));
777
0
            }
778
35
            else
779
35
            {
780
35
                m_poFeature.reset();
781
35
            }
782
35
        }
783
50
        else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
784
0
        {
785
0
            m_poFeature->SetGeometryDirectly(m_multiLineString.release());
786
0
            m_lineString.reset();
787
0
            m_multiLineString.reset();
788
789
0
            m_inInterestingElement = false;
790
0
            if ((m_poFilterGeom == nullptr ||
791
0
                 FilterGeometry(m_poFeature->GetGeometryRef())) &&
792
0
                (m_poAttrQuery == nullptr ||
793
0
                 m_poAttrQuery->Evaluate(m_poFeature.get())))
794
0
            {
795
0
                if (m_poFeature->GetGeometryRef() != nullptr)
796
0
                {
797
0
                    m_poFeature->GetGeometryRef()->assignSpatialReference(
798
0
                        m_poSRS);
799
0
                }
800
801
0
                m_oFeatureQueue.push_back(std::move(m_poFeature));
802
0
            }
803
0
            else
804
0
            {
805
0
                m_poFeature.reset();
806
0
            }
807
0
        }
808
50
        else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trkseg") == 0 &&
809
0
                 m_depthLevel == m_interestingDepthLevel + 1)
810
0
        {
811
0
            if (m_multiLineString)
812
0
            {
813
0
                m_multiLineString->addGeometry(std::move(m_lineString));
814
0
            }
815
0
            else
816
0
            {
817
0
                m_lineString.reset();
818
0
            }
819
0
        }
820
50
        else if (m_gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
821
6
        {
822
6
            m_poFeature->SetGeometryDirectly(m_lineString.release());
823
6
            m_lineString.reset();
824
825
6
            m_inInterestingElement = false;
826
6
            if ((m_poFilterGeom == nullptr ||
827
0
                 FilterGeometry(m_poFeature->GetGeometryRef())) &&
828
6
                (m_poAttrQuery == nullptr ||
829
0
                 m_poAttrQuery->Evaluate(m_poFeature.get())))
830
6
            {
831
6
                if (m_poFeature->GetGeometryRef() != nullptr)
832
6
                {
833
6
                    m_poFeature->GetGeometryRef()->assignSpatialReference(
834
6
                        m_poSRS);
835
6
                }
836
837
6
                m_oFeatureQueue.push_back(std::move(m_poFeature));
838
6
            }
839
0
            else
840
0
            {
841
0
                m_poFeature.reset();
842
0
            }
843
6
        }
844
44
        else if (m_bEleAs25D && strcmp(pszName, "ele") == 0 &&
845
0
                 m_lineString != nullptr &&
846
0
                 ((m_gpxGeomType == GPX_ROUTE &&
847
0
                   m_depthLevel == m_interestingDepthLevel + 2) ||
848
0
                  (m_gpxGeomType == GPX_TRACK &&
849
0
                   m_depthLevel == m_interestingDepthLevel + 3)))
850
0
        {
851
0
            m_lineString->setCoordinateDimension(3);
852
853
0
            if (!m_osSubElementValue.empty())
854
0
            {
855
0
                const double val = CPLAtof(m_osSubElementValue.c_str());
856
0
                const int i = m_lineString->getNumPoints() - 1;
857
0
                if (i >= 0)
858
0
                    m_lineString->setPoint(i, m_lineString->getX(i),
859
0
                                           m_lineString->getY(i), val);
860
0
            }
861
862
0
            m_osSubElementName.clear();
863
0
            m_osSubElementValue.clear();
864
0
        }
865
44
        else if (m_depthLevel == m_interestingDepthLevel + 1 &&
866
8
                 strcmp(pszName, "extensions") == 0)
867
7
        {
868
7
            m_inExtensions = false;
869
7
        }
870
37
        else if ((m_depthLevel == m_interestingDepthLevel + 1 ||
871
36
                  (m_inExtensions &&
872
31
                   m_depthLevel == m_interestingDepthLevel + 2)) &&
873
32
                 !m_osSubElementName.empty() && m_osSubElementName == pszName)
874
32
        {
875
32
            if (m_poFeature && !m_osSubElementValue.empty())
876
32
            {
877
32
                if (m_osSubElementValue == "time" && m_iCurrentField >= 0 &&
878
0
                    m_poFeature->GetFieldDefnRef(m_iCurrentField)->GetType() ==
879
0
                        OFTDateTime)
880
0
                {
881
0
                    OGRField sField;
882
0
                    if (OGRParseXMLDateTime(m_osSubElementValue.c_str(),
883
0
                                            &sField))
884
0
                    {
885
0
                        m_poFeature->SetField(m_iCurrentField, &sField);
886
0
                    }
887
0
                    else
888
0
                    {
889
0
                        CPLError(CE_Warning, CPLE_AppDefined,
890
0
                                 "Could not parse %s as a valid dateTime",
891
0
                                 m_osSubElementValue.c_str());
892
0
                    }
893
0
                }
894
32
                else
895
32
                {
896
32
                    m_poFeature->SetField(m_iCurrentField,
897
32
                                          m_osSubElementValue.c_str());
898
32
                }
899
32
            }
900
32
            if (strcmp(pszName, "link") == 0)
901
0
                m_inLink = false;
902
903
32
            m_osSubElementName.clear();
904
32
            m_osSubElementValue.clear();
905
32
        }
906
5
        else if (m_inLink && m_depthLevel == m_interestingDepthLevel + 2)
907
0
        {
908
0
            if (m_iCurrentField != -1 && !m_osSubElementName.empty() &&
909
0
                m_osSubElementName == pszName && m_poFeature &&
910
0
                !m_osSubElementValue.empty())
911
0
            {
912
0
                m_poFeature->SetField(m_iCurrentField,
913
0
                                      m_osSubElementValue.c_str());
914
0
            }
915
916
0
            m_osSubElementName.clear();
917
0
            m_osSubElementValue.clear();
918
0
        }
919
5
        else if (m_inExtensions && m_depthLevel > m_interestingDepthLevel + 2)
920
0
        {
921
0
            AddStrToSubElementValue(CPLSPrintf("</%s>", pszName));
922
0
        }
923
85
    }
924
280
}
925
926
/************************************************************************/
927
/*                           dataHandlerCbk()                           */
928
/************************************************************************/
929
930
void OGRGPXLayer::dataHandlerCbk(const char *data, int nLen)
931
455
{
932
455
    if (m_bStopParsing)
933
0
        return;
934
935
455
    m_nDataHandlerCounter++;
936
455
    if (m_nDataHandlerCounter >= PARSER_BUF_SIZE)
937
0
    {
938
0
        CPLError(CE_Failure, CPLE_AppDefined,
939
0
                 "File probably corrupted (million laugh pattern)");
940
0
        XML_StopParser(m_oParser, XML_FALSE);
941
0
        m_bStopParsing = true;
942
0
        return;
943
0
    }
944
945
455
    m_nWithoutEventCounter = 0;
946
947
455
    if (!m_osSubElementName.empty())
948
48
    {
949
48
        if (m_inExtensions && m_depthLevel > m_interestingDepthLevel + 2)
950
41
        {
951
41
            if (data[0] == '\n')
952
5
                return;
953
41
        }
954
43
        try
955
43
        {
956
43
            m_osSubElementValue.append(data, nLen);
957
43
        }
958
43
        catch (const std::bad_alloc &)
959
43
        {
960
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
961
0
                     "Out of memory when parsing GPX file");
962
0
            XML_StopParser(m_oParser, XML_FALSE);
963
0
            m_bStopParsing = true;
964
0
            return;
965
0
        }
966
43
        if (m_osSubElementValue.size() > 100000)
967
0
        {
968
0
            CPLError(CE_Failure, CPLE_AppDefined,
969
0
                     "Too much data inside one element. "
970
0
                     "File probably corrupted");
971
0
            XML_StopParser(m_oParser, XML_FALSE);
972
0
            m_bStopParsing = true;
973
0
        }
974
43
    }
975
455
}
976
#endif
977
978
/************************************************************************/
979
/*                           GetNextFeature()                           */
980
/************************************************************************/
981
982
OGRFeature *OGRGPXLayer::GetNextFeature()
983
1.26k
{
984
1.26k
    if (m_bWriteMode)
985
0
    {
986
0
        CPLError(CE_Failure, CPLE_NotSupported,
987
0
                 "Cannot read features when writing a GPX file");
988
0
        return nullptr;
989
0
    }
990
991
1.26k
    if (m_fpGPX == nullptr)
992
0
        return nullptr;
993
994
1.26k
#ifdef HAVE_EXPAT
995
996
1.26k
    if (m_bStopParsing)
997
1.23k
        return nullptr;
998
999
31
    if (!this->m_oFeatureQueue.empty())
1000
2
    {
1001
2
        OGRFeature *poFeature = std::move(m_oFeatureQueue.front()).release();
1002
2
        m_oFeatureQueue.pop_front();
1003
2
        return poFeature;
1004
2
    }
1005
1006
29
    if (m_fpGPX->Eof() || m_fpGPX->Error())
1007
4
        return nullptr;
1008
1009
25
    std::vector<char> aBuf(PARSER_BUF_SIZE);
1010
25
    m_nWithoutEventCounter = 0;
1011
1012
25
    int nDone = 0;
1013
25
    do
1014
25
    {
1015
25
        m_nDataHandlerCounter = 0;
1016
25
        unsigned int nLen = static_cast<unsigned int>(
1017
25
            m_fpGPX->Read(aBuf.data(), 1, aBuf.size()));
1018
25
        nDone = (nLen < aBuf.size());
1019
25
        if (XML_Parse(m_oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
1020
0
        {
1021
0
            CPLError(CE_Failure, CPLE_AppDefined,
1022
0
                     "XML parsing of GPX file failed : "
1023
0
                     "%s at line %d, column %d",
1024
0
                     XML_ErrorString(XML_GetErrorCode(m_oParser)),
1025
0
                     static_cast<int>(XML_GetCurrentLineNumber(m_oParser)),
1026
0
                     static_cast<int>(XML_GetCurrentColumnNumber(m_oParser)));
1027
0
            m_bStopParsing = true;
1028
0
            break;
1029
0
        }
1030
25
        m_nWithoutEventCounter++;
1031
25
    } while (!nDone && this->m_oFeatureQueue.empty() && !m_bStopParsing &&
1032
0
             m_nWithoutEventCounter < 10);
1033
1034
25
    if (m_nWithoutEventCounter == 10)
1035
0
    {
1036
0
        CPLError(CE_Failure, CPLE_AppDefined,
1037
0
                 "Too much data inside one element. File probably corrupted");
1038
0
        m_bStopParsing = true;
1039
0
    }
1040
1041
25
    if (!this->m_oFeatureQueue.empty())
1042
4
    {
1043
4
        OGRFeature *poFeature = std::move(m_oFeatureQueue.front()).release();
1044
4
        m_oFeatureQueue.pop_front();
1045
4
        return poFeature;
1046
4
    }
1047
21
#endif
1048
21
    return nullptr;
1049
25
}
1050
1051
/************************************************************************/
1052
/*                   OGRGPX_GetXMLCompatibleTagName()                   */
1053
/************************************************************************/
1054
1055
static char *OGRGPX_GetXMLCompatibleTagName(const char *pszExtensionsNS,
1056
                                            const char *pszName)
1057
0
{
1058
    /* Skip "ogr_" for example if NS is "ogr". Useful for GPX -> GPX roundtrip
1059
     */
1060
0
    if (strncmp(pszName, pszExtensionsNS, strlen(pszExtensionsNS)) == 0 &&
1061
0
        pszName[strlen(pszExtensionsNS)] == '_')
1062
0
    {
1063
0
        pszName += strlen(pszExtensionsNS) + 1;
1064
0
    }
1065
1066
0
    char *pszModName = CPLStrdup(pszName);
1067
0
    for (int i = 0; pszModName[i] != 0; i++)
1068
0
    {
1069
0
        if (pszModName[i] == ' ')
1070
0
            pszModName[i] = '_';
1071
0
    }
1072
0
    return pszModName;
1073
0
}
1074
1075
/************************************************************************/
1076
/*                        OGRGPX_GetUTF8String()                        */
1077
/************************************************************************/
1078
1079
static char *OGRGPX_GetUTF8String(const char *pszString)
1080
0
{
1081
0
    char *pszEscaped = nullptr;
1082
0
    if (!CPLIsUTF8(pszString, -1) &&
1083
0
        CPLTestBool(CPLGetConfigOption("OGR_FORCE_ASCII", "YES")))
1084
0
    {
1085
0
        static bool bFirstTime = true;
1086
0
        if (bFirstTime)
1087
0
        {
1088
0
            bFirstTime = false;
1089
0
            CPLError(CE_Warning, CPLE_AppDefined,
1090
0
                     "%s is not a valid UTF-8 string. Forcing it to ASCII.\n"
1091
0
                     "If you still want the original string and change the XML "
1092
0
                     "file encoding\n"
1093
0
                     "afterwards, you can define OGR_FORCE_ASCII=NO as "
1094
0
                     "configuration option.\n"
1095
0
                     "This warning won't be issued anymore",
1096
0
                     pszString);
1097
0
        }
1098
0
        else
1099
0
        {
1100
0
            CPLDebug("OGR",
1101
0
                     "%s is not a valid UTF-8 string. Forcing it to ASCII",
1102
0
                     pszString);
1103
0
        }
1104
0
        pszEscaped = CPLForceToASCII(pszString, -1, '?');
1105
0
    }
1106
0
    else
1107
0
    {
1108
0
        pszEscaped = CPLStrdup(pszString);
1109
0
    }
1110
1111
0
    return pszEscaped;
1112
0
}
1113
1114
/************************************************************************/
1115
/*                      OGRGPX_WriteXMLExtension()                      */
1116
/************************************************************************/
1117
1118
bool OGRGPXLayer::OGRGPX_WriteXMLExtension(const char *pszTagName,
1119
                                           const char *pszContent)
1120
0
{
1121
0
    CPLXMLNode *poXML = CPLParseXMLString(pszContent);
1122
0
    if (poXML)
1123
0
    {
1124
0
        const char *pszUnderscore = strchr(pszTagName, '_');
1125
0
        char *pszTagNameWithNS = CPLStrdup(pszTagName);
1126
0
        if (pszUnderscore)
1127
0
            pszTagNameWithNS[pszUnderscore - pszTagName] = ':';
1128
1129
        /* If we detect a Garmin GPX extension, add its xmlns */
1130
0
        const char *pszXMLNS = nullptr;
1131
0
        if (strcmp(pszTagName, "gpxx_WaypointExtension") == 0)
1132
0
            pszXMLNS = " xmlns:gpxx=\"http://www.garmin.com/xmlschemas/"
1133
0
                       "GpxExtensions/v3\"";
1134
1135
        /* Don't XML escape here */
1136
0
        char *pszUTF8 = OGRGPX_GetUTF8String(pszContent);
1137
0
        m_poDS->PrintLine("    <%s%s>%s</%s>", pszTagNameWithNS,
1138
0
                          (pszXMLNS) ? pszXMLNS : "", pszUTF8,
1139
0
                          pszTagNameWithNS);
1140
0
        CPLFree(pszUTF8);
1141
1142
0
        CPLFree(pszTagNameWithNS);
1143
0
        CPLDestroyXMLNode(poXML);
1144
1145
0
        return true;
1146
0
    }
1147
1148
0
    return false;
1149
0
}
1150
1151
/************************************************************************/
1152
/*                       WriteFeatureAttributes()                       */
1153
/************************************************************************/
1154
1155
static void AddIdent(VSILFILE *fp, int nIdentLevel)
1156
0
{
1157
0
    for (int i = 0; i < nIdentLevel; i++)
1158
0
        VSIFPrintfL(fp, "  ");
1159
0
}
1160
1161
void OGRGPXLayer::WriteFeatureAttributes(const OGRFeature *poFeature,
1162
                                         int nIdentLevel)
1163
48.5k
{
1164
48.5k
    VSILFILE *fp = m_poDS->GetOutputFP();
1165
1166
    /* Begin with standard GPX fields */
1167
48.5k
    int i = m_iFirstGPXField;
1168
630k
    for (; i < m_nGPXFields; i++)
1169
582k
    {
1170
582k
        const OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
1171
582k
        if (poFeature->IsFieldSetAndNotNull(i))
1172
0
        {
1173
0
            const char *pszName = poFieldDefn->GetNameRef();
1174
0
            if (strcmp(pszName, "time") == 0)
1175
0
            {
1176
0
                char *pszDate = OGRGetXMLDateTime(poFeature->GetRawFieldRef(i));
1177
0
                AddIdent(fp, nIdentLevel);
1178
0
                m_poDS->PrintLine("<time>%s</time>", pszDate);
1179
0
                CPLFree(pszDate);
1180
0
            }
1181
0
            else if (STARTS_WITH(pszName, "link"))
1182
0
            {
1183
0
                if (strstr(pszName, "href"))
1184
0
                {
1185
0
                    AddIdent(fp, nIdentLevel);
1186
0
                    VSIFPrintfL(fp, "<link href=\"%s\">",
1187
0
                                poFeature->GetFieldAsString(i));
1188
0
                    if (poFeature->IsFieldSetAndNotNull(i + 1))
1189
0
                        VSIFPrintfL(fp, "<text>%s</text>",
1190
0
                                    poFeature->GetFieldAsString(i + 1));
1191
0
                    if (poFeature->IsFieldSetAndNotNull(i + 2))
1192
0
                        VSIFPrintfL(fp, "<type>%s</type>",
1193
0
                                    poFeature->GetFieldAsString(i + 2));
1194
0
                    m_poDS->PrintLine("</link>");
1195
0
                }
1196
0
            }
1197
0
            else if (poFieldDefn->GetType() == OFTReal)
1198
0
            {
1199
0
                char szValue[64];
1200
0
                OGRFormatDouble(szValue, sizeof(szValue),
1201
0
                                poFeature->GetFieldAsDouble(i), '.');
1202
0
                AddIdent(fp, nIdentLevel);
1203
0
                m_poDS->PrintLine("<%s>%s</%s>", pszName, szValue, pszName);
1204
0
            }
1205
0
            else
1206
0
            {
1207
0
                char *pszValue = OGRGetXML_UTF8_EscapedString(
1208
0
                    poFeature->GetFieldAsString(i));
1209
0
                AddIdent(fp, nIdentLevel);
1210
0
                m_poDS->PrintLine("<%s>%s</%s>", pszName, pszValue, pszName);
1211
0
                CPLFree(pszValue);
1212
0
            }
1213
0
        }
1214
582k
    }
1215
1216
    /* Write "extra" fields within the <extensions> tag */
1217
48.5k
    const int n = m_poFeatureDefn->GetFieldCount();
1218
48.5k
    if (i < n)
1219
0
    {
1220
0
        const std::string &osExtensionsNS = m_poDS->GetExtensionsNS();
1221
0
        AddIdent(fp, nIdentLevel);
1222
0
        m_poDS->PrintLine("<extensions>");
1223
0
        for (; i < n; i++)
1224
0
        {
1225
0
            const OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
1226
0
            if (poFeature->IsFieldSetAndNotNull(i))
1227
0
            {
1228
0
                char *compatibleName = OGRGPX_GetXMLCompatibleTagName(
1229
0
                    osExtensionsNS.c_str(), poFieldDefn->GetNameRef());
1230
1231
0
                if (poFieldDefn->GetType() == OFTReal)
1232
0
                {
1233
0
                    char szValue[64];
1234
0
                    OGRFormatDouble(szValue, sizeof(szValue),
1235
0
                                    poFeature->GetFieldAsDouble(i), '.');
1236
0
                    AddIdent(fp, nIdentLevel + 1);
1237
0
                    m_poDS->PrintLine("<%s:%s>%s</%s:%s>",
1238
0
                                      osExtensionsNS.c_str(), compatibleName,
1239
0
                                      szValue, osExtensionsNS.c_str(),
1240
0
                                      compatibleName);
1241
0
                }
1242
0
                else
1243
0
                {
1244
0
                    const char *pszRaw = poFeature->GetFieldAsString(i);
1245
1246
                    /* Try to detect XML content */
1247
0
                    if (pszRaw[0] == '<' && pszRaw[strlen(pszRaw) - 1] == '>')
1248
0
                    {
1249
0
                        if (OGRGPX_WriteXMLExtension(compatibleName, pszRaw))
1250
0
                        {
1251
0
                            CPLFree(compatibleName);
1252
0
                            continue;
1253
0
                        }
1254
0
                    }
1255
1256
                    /* Try to detect XML escaped content */
1257
0
                    else if (STARTS_WITH(pszRaw, "&lt;") &&
1258
0
                             STARTS_WITH(pszRaw + strlen(pszRaw) - 4, "&gt;"))
1259
0
                    {
1260
0
                        char *pszUnescapedContent =
1261
0
                            CPLUnescapeString(pszRaw, nullptr, CPLES_XML);
1262
1263
0
                        if (OGRGPX_WriteXMLExtension(compatibleName,
1264
0
                                                     pszUnescapedContent))
1265
0
                        {
1266
0
                            CPLFree(pszUnescapedContent);
1267
0
                            CPLFree(compatibleName);
1268
0
                            continue;
1269
0
                        }
1270
1271
0
                        CPLFree(pszUnescapedContent);
1272
0
                    }
1273
1274
                    /* Remove leading spaces for a numeric field */
1275
0
                    if (poFieldDefn->GetType() == OFTInteger)
1276
0
                    {
1277
0
                        while (*pszRaw == ' ')
1278
0
                            pszRaw++;
1279
0
                    }
1280
1281
0
                    char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
1282
0
                    AddIdent(fp, nIdentLevel + 1);
1283
0
                    m_poDS->PrintLine("<%s:%s>%s</%s:%s>",
1284
0
                                      osExtensionsNS.c_str(), compatibleName,
1285
0
                                      pszEscaped, osExtensionsNS.c_str(),
1286
0
                                      compatibleName);
1287
0
                    CPLFree(pszEscaped);
1288
0
                }
1289
0
                CPLFree(compatibleName);
1290
0
            }
1291
0
        }
1292
0
        AddIdent(fp, nIdentLevel);
1293
0
        m_poDS->PrintLine("</extensions>");
1294
0
    }
1295
48.5k
}
1296
1297
/************************************************************************/
1298
/*                   CheckAndFixCoordinatesValidity()                   */
1299
/************************************************************************/
1300
1301
OGRErr OGRGPXLayer::CheckAndFixCoordinatesValidity(double *pdfLatitude,
1302
                                                   double *pdfLongitude)
1303
21
{
1304
21
    if (pdfLatitude != nullptr && (*pdfLatitude < -90 || *pdfLatitude > 90))
1305
5
    {
1306
5
        static bool bFirstWarning = true;
1307
5
        if (bFirstWarning)
1308
1
        {
1309
1
            bFirstWarning = false;
1310
1
            CPLError(CE_Failure, CPLE_AppDefined,
1311
1
                     "Latitude %f is invalid. Valid range is [-90,90]. "
1312
1
                     "This warning will not be issued any more",
1313
1
                     *pdfLatitude);
1314
1
        }
1315
5
        return OGRERR_FAILURE;
1316
5
    }
1317
1318
16
    if (pdfLongitude != nullptr &&
1319
16
        (*pdfLongitude < -180 || *pdfLongitude > 180))
1320
9
    {
1321
9
        static bool bFirstWarning = true;
1322
9
        if (bFirstWarning)
1323
1
        {
1324
1
            bFirstWarning = false;
1325
1
            CPLError(CE_Warning, CPLE_AppDefined,
1326
1
                     "Longitude %f has been modified to fit into "
1327
1
                     "range [-180,180]. This warning will not be "
1328
1
                     "issued any more",
1329
1
                     *pdfLongitude);
1330
1
        }
1331
1332
9
        *pdfLongitude = fmod(*pdfLongitude + 180.0, 360.0) - 180.0;
1333
9
        return OGRERR_NONE;
1334
9
    }
1335
1336
7
    return OGRERR_NONE;
1337
16
}
1338
1339
/************************************************************************/
1340
/*                           ICreateFeature()                           */
1341
/************************************************************************/
1342
1343
OGRErr OGRGPXLayer::ICreateFeature(OGRFeature *poFeature)
1344
1345
48.6k
{
1346
48.6k
    VSILFILE *fp = m_poDS->GetOutputFP();
1347
48.6k
    if (fp == nullptr)
1348
0
        return OGRERR_FAILURE;
1349
1350
48.6k
    char szLat[64];
1351
48.6k
    char szLon[64];
1352
48.6k
    char szAlt[64];
1353
1354
48.6k
    const OGRGeometry *poGeom = poFeature->GetGeometryRef();
1355
1356
48.6k
    if (m_gpxGeomType == GPX_WPT)
1357
0
    {
1358
0
        if (m_poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE)
1359
0
        {
1360
0
            CPLError(CE_Failure, CPLE_NotSupported,
1361
0
                     "Cannot write a 'wpt' element after a 'rte' element.");
1362
0
            return OGRERR_FAILURE;
1363
0
        }
1364
0
        else if (m_poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK)
1365
0
        {
1366
0
            CPLError(CE_Failure, CPLE_NotSupported,
1367
0
                     "Cannot write a 'wpt' element after a 'trk' element.");
1368
0
            return OGRERR_FAILURE;
1369
0
        }
1370
1371
0
        m_poDS->SetLastGPXGeomTypeWritten(m_gpxGeomType);
1372
1373
0
        if (poGeom == nullptr ||
1374
0
            wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
1375
0
        {
1376
0
            CPLError(
1377
0
                CE_Failure, CPLE_AppDefined,
1378
0
                "Features without geometry or with non-ponctual geometries not "
1379
0
                "supported by GPX writer in waypoints layer.");
1380
0
            return OGRERR_FAILURE;
1381
0
        }
1382
1383
0
        if (poGeom->getCoordinateDimension() == 0)
1384
0
        {
1385
0
            CPLError(CE_Failure, CPLE_AppDefined,
1386
0
                     "POINT EMPTY geometries not supported by GPX writer.");
1387
0
            return OGRERR_FAILURE;
1388
0
        }
1389
1390
0
        const OGRPoint *point = poGeom->toPoint();
1391
0
        double lat = point->getY();
1392
0
        double lon = point->getX();
1393
0
        CheckAndFixCoordinatesValidity(&lat, &lon);
1394
0
        m_poDS->AddCoord(lon, lat);
1395
0
        OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1396
0
        OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1397
0
        m_poDS->PrintLine("<wpt lat=\"%s\" lon=\"%s\">", szLat, szLon);
1398
0
        WriteFeatureAttributes(poFeature);
1399
0
        m_poDS->PrintLine("</wpt>");
1400
0
    }
1401
48.6k
    else if (m_gpxGeomType == GPX_ROUTE)
1402
1.28k
    {
1403
1.28k
        if (m_poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK ||
1404
1.20k
            m_poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK_POINT)
1405
79
        {
1406
79
            CPLError(CE_Failure, CPLE_NotSupported,
1407
79
                     "Cannot write a 'rte' element after a 'trk' element.");
1408
79
            return OGRERR_FAILURE;
1409
79
        }
1410
1411
1.20k
        if (m_poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT &&
1412
0
            m_poDS->m_nLastRteId != -1)
1413
0
        {
1414
0
            m_poDS->PrintLine("</rte>");
1415
0
            m_poDS->m_nLastRteId = -1;
1416
0
        }
1417
1418
1.20k
        m_poDS->SetLastGPXGeomTypeWritten(m_gpxGeomType);
1419
1420
1.20k
        const OGRLineString *line = nullptr;
1421
1422
1.20k
        if (poGeom == nullptr)
1423
1.15k
        {
1424
1.15k
            m_poDS->PrintLine("<rte>");
1425
1.15k
            WriteFeatureAttributes(poFeature);
1426
1.15k
            m_poDS->PrintLine("</rte>");
1427
1.15k
            return OGRERR_NONE;
1428
1.15k
        }
1429
1430
48
        switch (poGeom->getGeometryType())
1431
48
        {
1432
33
            case wkbLineString:
1433
33
            case wkbLineString25D:
1434
33
            {
1435
33
                line = poGeom->toLineString();
1436
33
                break;
1437
33
            }
1438
1439
3
            case wkbMultiLineString:
1440
3
            case wkbMultiLineString25D:
1441
3
            {
1442
3
                int nGeometries =
1443
3
                    poGeom->toMultiLineString()->getNumGeometries();
1444
3
                if (nGeometries == 0)
1445
3
                {
1446
3
                    line = nullptr;
1447
3
                }
1448
0
                else if (nGeometries == 1)
1449
0
                {
1450
0
                    line = poGeom->toMultiLineString()->getGeometryRef(0);
1451
0
                }
1452
0
                else
1453
0
                {
1454
0
                    CPLError(CE_Failure, CPLE_NotSupported,
1455
0
                             "Multiline with more than one line is not "
1456
0
                             "supported for 'rte' element.");
1457
0
                    return OGRERR_FAILURE;
1458
0
                }
1459
3
                break;
1460
3
            }
1461
1462
12
            default:
1463
12
            {
1464
12
                CPLError(
1465
12
                    CE_Failure, CPLE_NotSupported,
1466
12
                    "Geometry type of `%s' not supported for 'rte' element.\n",
1467
12
                    OGRGeometryTypeToName(poGeom->getGeometryType()));
1468
12
                return OGRERR_FAILURE;
1469
3
            }
1470
48
        }
1471
1472
36
        m_poDS->PrintLine("<rte>");
1473
36
        WriteFeatureAttributes(poFeature);
1474
36
        if (line)
1475
33
        {
1476
33
            const int n = line->getNumPoints();
1477
54
            for (int i = 0; i < n; i++)
1478
21
            {
1479
21
                double lat = line->getY(i);
1480
21
                double lon = line->getX(i);
1481
21
                CheckAndFixCoordinatesValidity(&lat, &lon);
1482
21
                m_poDS->AddCoord(lon, lat);
1483
21
                OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1484
21
                OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1485
21
                m_poDS->PrintLine("  <rtept lat=\"%s\" lon=\"%s\">", szLat,
1486
21
                                  szLon);
1487
21
                if (poGeom->getGeometryType() == wkbLineString25D ||
1488
21
                    poGeom->getGeometryType() == wkbMultiLineString25D)
1489
0
                {
1490
0
                    OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i), '.');
1491
0
                    m_poDS->PrintLine("    <ele>%s</ele>", szAlt);
1492
0
                }
1493
21
                m_poDS->PrintLine("  </rtept>");
1494
21
            }
1495
33
        }
1496
36
        m_poDS->PrintLine("</rte>");
1497
36
    }
1498
47.3k
    else if (m_gpxGeomType == GPX_TRACK)
1499
47.3k
    {
1500
47.3k
        if (m_poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT &&
1501
0
            m_poDS->m_nLastRteId != -1)
1502
0
        {
1503
0
            m_poDS->PrintLine("</rte>");
1504
0
            m_poDS->m_nLastRteId = -1;
1505
0
        }
1506
47.3k
        if (m_poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK_POINT &&
1507
0
            m_poDS->m_nLastTrkId != -1)
1508
0
        {
1509
0
            m_poDS->PrintLine("  </trkseg>");
1510
0
            m_poDS->PrintLine("</trk>");
1511
0
            m_poDS->m_nLastTrkId = -1;
1512
0
            m_poDS->m_nLastTrkSegId = -1;
1513
0
        }
1514
1515
47.3k
        m_poDS->SetLastGPXGeomTypeWritten(m_gpxGeomType);
1516
1517
47.3k
        if (poGeom == nullptr)
1518
47.3k
        {
1519
47.3k
            m_poDS->PrintLine("<trk>");
1520
47.3k
            WriteFeatureAttributes(poFeature);
1521
47.3k
            m_poDS->PrintLine("</trk>");
1522
47.3k
            return OGRERR_NONE;
1523
47.3k
        }
1524
1525
24
        switch (poGeom->getGeometryType())
1526
24
        {
1527
0
            case wkbLineString:
1528
0
            case wkbLineString25D:
1529
0
            {
1530
0
                const OGRLineString *line = poGeom->toLineString();
1531
0
                const int n = line->getNumPoints();
1532
0
                m_poDS->PrintLine("<trk>");
1533
0
                WriteFeatureAttributes(poFeature);
1534
0
                m_poDS->PrintLine("  <trkseg>");
1535
0
                for (int i = 0; i < n; i++)
1536
0
                {
1537
0
                    double lat = line->getY(i);
1538
0
                    double lon = line->getX(i);
1539
0
                    CheckAndFixCoordinatesValidity(&lat, &lon);
1540
0
                    m_poDS->AddCoord(lon, lat);
1541
0
                    OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1542
0
                    OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1543
0
                    m_poDS->PrintLine("    <trkpt lat=\"%s\" lon=\"%s\">",
1544
0
                                      szLat, szLon);
1545
0
                    if (line->getGeometryType() == wkbLineString25D)
1546
0
                    {
1547
0
                        OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i),
1548
0
                                        '.');
1549
0
                        m_poDS->PrintLine("        <ele>%s</ele>", szAlt);
1550
0
                    }
1551
0
                    m_poDS->PrintLine("    </trkpt>");
1552
0
                }
1553
0
                m_poDS->PrintLine("  </trkseg>");
1554
0
                m_poDS->PrintLine("</trk>");
1555
0
                break;
1556
0
            }
1557
1558
0
            case wkbMultiLineString:
1559
0
            case wkbMultiLineString25D:
1560
0
            {
1561
0
                m_poDS->PrintLine("<trk>");
1562
0
                WriteFeatureAttributes(poFeature);
1563
0
                for (auto &&line : poGeom->toMultiLineString())
1564
0
                {
1565
0
                    const int n = (line) ? line->getNumPoints() : 0;
1566
0
                    m_poDS->PrintLine("  <trkseg>");
1567
0
                    for (int i = 0; i < n; i++)
1568
0
                    {
1569
0
                        double lat = line->getY(i);
1570
0
                        double lon = line->getX(i);
1571
0
                        CheckAndFixCoordinatesValidity(&lat, &lon);
1572
0
                        m_poDS->AddCoord(lon, lat);
1573
0
                        OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1574
0
                        OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1575
0
                        m_poDS->PrintLine("    <trkpt lat=\"%s\" lon=\"%s\">",
1576
0
                                          szLat, szLon);
1577
0
                        if (line->getGeometryType() == wkbLineString25D)
1578
0
                        {
1579
0
                            OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i),
1580
0
                                            '.');
1581
0
                            m_poDS->PrintLine("        <ele>%s</ele>", szAlt);
1582
0
                        }
1583
0
                        m_poDS->PrintLine("    </trkpt>");
1584
0
                    }
1585
0
                    m_poDS->PrintLine("  </trkseg>");
1586
0
                }
1587
0
                m_poDS->PrintLine("</trk>");
1588
0
                break;
1589
0
            }
1590
1591
24
            default:
1592
24
            {
1593
24
                CPLError(
1594
24
                    CE_Failure, CPLE_NotSupported,
1595
24
                    "Geometry type of `%s' not supported for 'trk' element.\n",
1596
24
                    OGRGeometryTypeToName(poGeom->getGeometryType()));
1597
24
                return OGRERR_FAILURE;
1598
0
            }
1599
24
        }
1600
24
    }
1601
0
    else if (m_gpxGeomType == GPX_ROUTE_POINT)
1602
0
    {
1603
0
        if (m_poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK ||
1604
0
            m_poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK_POINT)
1605
0
        {
1606
0
            CPLError(CE_Failure, CPLE_NotSupported,
1607
0
                     "Cannot write a 'rte' element after a 'trk' element.");
1608
0
            return OGRERR_FAILURE;
1609
0
        }
1610
1611
0
        if (poGeom == nullptr ||
1612
0
            wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
1613
0
        {
1614
0
            CPLError(
1615
0
                CE_Failure, CPLE_AppDefined,
1616
0
                "Features without geometry or with non-ponctual geometries not "
1617
0
                "supported by GPX writer in route_points layer.");
1618
0
            return OGRERR_FAILURE;
1619
0
        }
1620
1621
0
        if (poGeom->getCoordinateDimension() == 0)
1622
0
        {
1623
0
            CPLError(CE_Failure, CPLE_AppDefined,
1624
0
                     "POINT EMPTY geometries not supported by GPX writer.");
1625
0
            return OGRERR_FAILURE;
1626
0
        }
1627
1628
0
        if (!poFeature->IsFieldSetAndNotNull(FLD_ROUTE_FID))
1629
0
        {
1630
0
            CPLError(
1631
0
                CE_Failure, CPLE_AppDefined, "Field %s must be set.",
1632
0
                m_poFeatureDefn->GetFieldDefn(FLD_ROUTE_FID)->GetNameRef());
1633
0
            return OGRERR_FAILURE;
1634
0
        }
1635
0
        if (poFeature->GetFieldAsInteger(FLD_ROUTE_FID) < 0)
1636
0
        {
1637
0
            CPLError(
1638
0
                CE_Failure, CPLE_AppDefined, "Invalid value for field %s.",
1639
0
                m_poFeatureDefn->GetFieldDefn(FLD_ROUTE_FID)->GetNameRef());
1640
0
            return OGRERR_FAILURE;
1641
0
        }
1642
1643
0
        m_poDS->SetLastGPXGeomTypeWritten(m_gpxGeomType);
1644
1645
0
        if (m_poDS->m_nLastRteId != poFeature->GetFieldAsInteger(FLD_ROUTE_FID))
1646
0
        {
1647
0
            if (m_poDS->m_nLastRteId != -1)
1648
0
            {
1649
0
                m_poDS->PrintLine("</rte>");
1650
0
            }
1651
0
            m_poDS->PrintLine("<rte>");
1652
0
            if (poFeature->IsFieldSetAndNotNull(FLD_ROUTE_NAME))
1653
0
            {
1654
0
                char *pszValue = OGRGetXML_UTF8_EscapedString(
1655
0
                    poFeature->GetFieldAsString(FLD_ROUTE_NAME));
1656
0
                m_poDS->PrintLine("  <%s>%s</%s>", "name", pszValue, "name");
1657
0
                CPLFree(pszValue);
1658
0
            }
1659
0
        }
1660
1661
0
        m_poDS->m_nLastRteId = poFeature->GetFieldAsInteger(FLD_ROUTE_FID);
1662
1663
0
        const OGRPoint *point = poGeom->toPoint();
1664
0
        double lat = point->getY();
1665
0
        double lon = point->getX();
1666
0
        CheckAndFixCoordinatesValidity(&lat, &lon);
1667
0
        m_poDS->AddCoord(lon, lat);
1668
0
        OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1669
0
        OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1670
0
        m_poDS->PrintLine("  <rtept lat=\"%s\" lon=\"%s\">", szLat, szLon);
1671
0
        WriteFeatureAttributes(poFeature, 2);
1672
0
        m_poDS->PrintLine("  </rtept>");
1673
0
    }
1674
0
    else
1675
0
    {
1676
0
        if (m_poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT &&
1677
0
            m_poDS->m_nLastRteId != -1)
1678
0
        {
1679
0
            m_poDS->PrintLine("</rte>");
1680
0
            m_poDS->m_nLastRteId = -1;
1681
0
        }
1682
1683
0
        if (poGeom == nullptr ||
1684
0
            wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
1685
0
        {
1686
0
            CPLError(
1687
0
                CE_Failure, CPLE_AppDefined,
1688
0
                "Features without geometry or with non-ponctual geometries not "
1689
0
                "supported by GPX writer in track_points layer.");
1690
0
            return OGRERR_FAILURE;
1691
0
        }
1692
1693
0
        if (poGeom->getCoordinateDimension() == 0)
1694
0
        {
1695
0
            CPLError(CE_Failure, CPLE_AppDefined,
1696
0
                     "POINT EMPTY geometries not supported by GPX writer.");
1697
0
            return OGRERR_FAILURE;
1698
0
        }
1699
1700
0
        if (!poFeature->IsFieldSetAndNotNull(FLD_TRACK_FID))
1701
0
        {
1702
0
            CPLError(
1703
0
                CE_Failure, CPLE_AppDefined, "Field %s must be set.",
1704
0
                m_poFeatureDefn->GetFieldDefn(FLD_TRACK_FID)->GetNameRef());
1705
0
            return OGRERR_FAILURE;
1706
0
        }
1707
0
        if (poFeature->GetFieldAsInteger(FLD_TRACK_FID) < 0)
1708
0
        {
1709
0
            CPLError(
1710
0
                CE_Failure, CPLE_AppDefined, "Invalid value for field %s.",
1711
0
                m_poFeatureDefn->GetFieldDefn(FLD_TRACK_FID)->GetNameRef());
1712
0
            return OGRERR_FAILURE;
1713
0
        }
1714
0
        if (!poFeature->IsFieldSetAndNotNull(FLD_TRACK_SEG_ID))
1715
0
        {
1716
0
            CPLError(
1717
0
                CE_Failure, CPLE_AppDefined, "Field %s must be set.",
1718
0
                m_poFeatureDefn->GetFieldDefn(FLD_TRACK_SEG_ID)->GetNameRef());
1719
0
            return OGRERR_FAILURE;
1720
0
        }
1721
0
        if (poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID) < 0)
1722
0
        {
1723
0
            CPLError(
1724
0
                CE_Failure, CPLE_AppDefined, "Invalid value for field %s.",
1725
0
                m_poFeatureDefn->GetFieldDefn(FLD_TRACK_SEG_ID)->GetNameRef());
1726
0
            return OGRERR_FAILURE;
1727
0
        }
1728
1729
0
        m_poDS->SetLastGPXGeomTypeWritten(m_gpxGeomType);
1730
1731
0
        if (m_poDS->m_nLastTrkId != poFeature->GetFieldAsInteger(FLD_TRACK_FID))
1732
0
        {
1733
0
            if (m_poDS->m_nLastTrkId != -1)
1734
0
            {
1735
0
                m_poDS->PrintLine("  </trkseg>");
1736
0
                m_poDS->PrintLine("</trk>");
1737
0
            }
1738
0
            m_poDS->PrintLine("<trk>");
1739
1740
0
            if (poFeature->IsFieldSetAndNotNull(FLD_TRACK_NAME))
1741
0
            {
1742
0
                char *pszValue = OGRGetXML_UTF8_EscapedString(
1743
0
                    poFeature->GetFieldAsString(FLD_TRACK_NAME));
1744
0
                m_poDS->PrintLine("  <%s>%s</%s>", "name", pszValue, "name");
1745
0
                CPLFree(pszValue);
1746
0
            }
1747
1748
0
            m_poDS->PrintLine("  <trkseg>");
1749
0
        }
1750
0
        else if (m_poDS->m_nLastTrkSegId !=
1751
0
                 poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID))
1752
0
        {
1753
0
            m_poDS->PrintLine("  </trkseg>");
1754
0
            m_poDS->PrintLine("  <trkseg>");
1755
0
        }
1756
1757
0
        m_poDS->m_nLastTrkId = poFeature->GetFieldAsInteger(FLD_TRACK_FID);
1758
0
        m_poDS->m_nLastTrkSegId =
1759
0
            poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID);
1760
1761
0
        const OGRPoint *point = poGeom->toPoint();
1762
0
        double lat = point->getY();
1763
0
        double lon = point->getX();
1764
0
        CheckAndFixCoordinatesValidity(&lat, &lon);
1765
0
        m_poDS->AddCoord(lon, lat);
1766
0
        OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1767
0
        OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1768
0
        m_poDS->PrintLine("    <trkpt lat=\"%s\" lon=\"%s\">", szLat, szLon);
1769
0
        WriteFeatureAttributes(poFeature, 3);
1770
0
        m_poDS->PrintLine("    </trkpt>");
1771
0
    }
1772
1773
36
    return OGRERR_NONE;
1774
48.6k
}
1775
1776
/************************************************************************/
1777
/*                            CreateField()                             */
1778
/************************************************************************/
1779
1780
OGRErr OGRGPXLayer::CreateField(const OGRFieldDefn *poField, int /*bApproxOK*/)
1781
1.56k
{
1782
20.4k
    for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
1783
18.8k
    {
1784
18.8k
        if (strcmp(m_poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
1785
18.8k
                   poField->GetNameRef()) == 0)
1786
0
        {
1787
0
            return OGRERR_NONE;
1788
0
        }
1789
18.8k
    }
1790
1.56k
    if (!m_poDS->GetUseExtensions())
1791
1.56k
    {
1792
1.56k
        CPLError(CE_Failure, CPLE_NotSupported,
1793
1.56k
                 "Field of name '%s' is not supported in GPX schema. "
1794
1.56k
                 "Use GPX_USE_EXTENSIONS creation option to allow use of the "
1795
1.56k
                 "<extensions> element.",
1796
1.56k
                 poField->GetNameRef());
1797
1.56k
        return OGRERR_FAILURE;
1798
1.56k
    }
1799
0
    else
1800
0
    {
1801
0
        m_poFeatureDefn->AddFieldDefn(poField);
1802
0
        return OGRERR_NONE;
1803
0
    }
1804
1.56k
}
1805
1806
/************************************************************************/
1807
/*                           TestCapability()                           */
1808
/************************************************************************/
1809
1810
int OGRGPXLayer::TestCapability(const char *pszCap) const
1811
1812
456
{
1813
456
    if (EQUAL(pszCap, OLCSequentialWrite))
1814
0
        return m_bWriteMode;
1815
456
    else if (EQUAL(pszCap, OLCCreateField))
1816
0
        return m_bWriteMode;
1817
456
    else if (EQUAL(pszCap, OLCStringsAsUTF8))
1818
0
        return TRUE;
1819
456
    else if (EQUAL(pszCap, OLCZGeometries))
1820
106
        return TRUE;
1821
1822
350
    else
1823
350
        return FALSE;
1824
456
}
1825
1826
/************************************************************************/
1827
/*                        LoadExtensionsSchema()                        */
1828
/************************************************************************/
1829
1830
#ifdef HAVE_EXPAT
1831
1832
static void XMLCALL startElementLoadSchemaCbk(void *pUserData,
1833
                                              const char *pszName,
1834
                                              const char **ppszAttr)
1835
1.59M
{
1836
1.59M
    static_cast<OGRGPXLayer *>(pUserData)->startElementLoadSchemaCbk(pszName,
1837
1.59M
                                                                     ppszAttr);
1838
1.59M
}
1839
1840
static void XMLCALL endElementLoadSchemaCbk(void *pUserData,
1841
                                            const char *pszName)
1842
299k
{
1843
299k
    static_cast<OGRGPXLayer *>(pUserData)->endElementLoadSchemaCbk(pszName);
1844
299k
}
1845
1846
static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, const char *data,
1847
                                             int nLen)
1848
821k
{
1849
821k
    static_cast<OGRGPXLayer *>(pUserData)->dataHandlerLoadSchemaCbk(data, nLen);
1850
821k
}
1851
1852
/** This function parses the whole file to detect the extensions fields */
1853
void OGRGPXLayer::LoadExtensionsSchema()
1854
1.25k
{
1855
1.25k
    m_oSchemaParser = OGRCreateExpatXMLParser();
1856
1.25k
    XML_SetElementHandler(m_oSchemaParser, ::startElementLoadSchemaCbk,
1857
1.25k
                          ::endElementLoadSchemaCbk);
1858
1.25k
    XML_SetCharacterDataHandler(m_oSchemaParser, ::dataHandlerLoadSchemaCbk);
1859
1.25k
    XML_SetUserData(m_oSchemaParser, this);
1860
1861
1.25k
    m_fpGPX->Seek(0, SEEK_SET);
1862
1863
1.25k
    m_inInterestingElement = false;
1864
1.25k
    m_inExtensions = false;
1865
1.25k
    m_depthLevel = 0;
1866
1.25k
    m_currentFieldDefn = nullptr;
1867
1.25k
    m_osSubElementName.clear();
1868
1.25k
    m_osSubElementValue.clear();
1869
1.25k
    m_nWithoutEventCounter = 0;
1870
1.25k
    m_bStopParsing = false;
1871
1872
1.25k
    std::vector<char> aBuf(PARSER_BUF_SIZE);
1873
1.25k
    int nDone = 0;
1874
1.25k
    do
1875
5.15k
    {
1876
5.15k
        m_nDataHandlerCounter = 0;
1877
5.15k
        unsigned int nLen = static_cast<unsigned int>(
1878
5.15k
            m_fpGPX->Read(aBuf.data(), 1, aBuf.size()));
1879
5.15k
        nDone = (nLen < aBuf.size());
1880
5.15k
        if (XML_Parse(m_oSchemaParser, aBuf.data(), nLen, nDone) ==
1881
5.15k
            XML_STATUS_ERROR)
1882
1.23k
        {
1883
1.23k
            CPLError(
1884
1.23k
                CE_Failure, CPLE_AppDefined,
1885
1.23k
                "XML parsing of GPX file failed : "
1886
1.23k
                "%s at line %d, column %d",
1887
1.23k
                XML_ErrorString(XML_GetErrorCode(m_oSchemaParser)),
1888
1.23k
                static_cast<int>(XML_GetCurrentLineNumber(m_oSchemaParser)),
1889
1.23k
                static_cast<int>(XML_GetCurrentColumnNumber(m_oSchemaParser)));
1890
1.23k
            m_bStopParsing = true;
1891
1.23k
            break;
1892
1.23k
        }
1893
3.92k
        m_nWithoutEventCounter++;
1894
3.92k
    } while (!nDone && !m_bStopParsing && m_nWithoutEventCounter < 10);
1895
1896
1.25k
    if (m_nWithoutEventCounter == 10)
1897
0
    {
1898
0
        CPLError(CE_Failure, CPLE_AppDefined,
1899
0
                 "Too much data inside one element. File probably corrupted");
1900
0
        m_bStopParsing = true;
1901
0
    }
1902
1903
1.25k
    XML_ParserFree(m_oSchemaParser);
1904
1.25k
    m_oSchemaParser = nullptr;
1905
1906
1.25k
    m_fpGPX->Seek(0, SEEK_SET);
1907
1.25k
}
1908
1909
/************************************************************************/
1910
/*                     startElementLoadSchemaCbk()                      */
1911
/************************************************************************/
1912
1913
void OGRGPXLayer::startElementLoadSchemaCbk(const char *pszName,
1914
                                            CPL_UNUSED const char **ppszAttr)
1915
1.59M
{
1916
1.59M
    if (m_bStopParsing)
1917
0
        return;
1918
1919
1.59M
    m_nWithoutEventCounter = 0;
1920
1921
1.59M
    if (m_gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0)
1922
10.4k
    {
1923
10.4k
        m_inInterestingElement = true;
1924
10.4k
        m_inExtensions = false;
1925
10.4k
        m_interestingDepthLevel = m_depthLevel;
1926
10.4k
    }
1927
1.58M
    else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
1928
12.6k
    {
1929
12.6k
        m_inInterestingElement = true;
1930
12.6k
        m_inExtensions = false;
1931
12.6k
        m_interestingDepthLevel = m_depthLevel;
1932
12.6k
    }
1933
1.57M
    else if (m_gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
1934
237
    {
1935
237
        m_inInterestingElement = true;
1936
237
        m_inExtensions = false;
1937
237
        m_interestingDepthLevel = m_depthLevel;
1938
237
    }
1939
1.57M
    else if (m_gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0)
1940
6.19k
    {
1941
6.19k
        m_inInterestingElement = true;
1942
6.19k
        m_inExtensions = false;
1943
6.19k
        m_interestingDepthLevel = m_depthLevel;
1944
6.19k
    }
1945
1.56M
    else if (m_gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0)
1946
475
    {
1947
475
        m_inInterestingElement = true;
1948
475
        m_inExtensions = false;
1949
475
        m_interestingDepthLevel = m_depthLevel;
1950
475
    }
1951
1.56M
    else if (m_inInterestingElement)
1952
584k
    {
1953
584k
        if (m_depthLevel == m_interestingDepthLevel + 1 &&
1954
21.2k
            strcmp(pszName, "extensions") == 0)
1955
8.56k
        {
1956
8.56k
            m_inExtensions = true;
1957
8.56k
            m_extensionsDepthLevel = m_depthLevel;
1958
8.56k
        }
1959
576k
        else if (m_inExtensions && m_depthLevel == m_extensionsDepthLevel + 1)
1960
28.0k
        {
1961
28.0k
            m_osSubElementName = pszName;
1962
1963
28.0k
            int iField = 0;  // Used after for.
1964
423k
            for (; iField < m_poFeatureDefn->GetFieldCount(); iField++)
1965
421k
            {
1966
421k
                bool bMatch = false;
1967
421k
                if (iField >= m_nGPXFields)
1968
96.6k
                {
1969
96.6k
                    char *pszCompatibleName =
1970
96.6k
                        OGRGPX_GetOGRCompatibleTagName(pszName);
1971
96.6k
                    bMatch =
1972
96.6k
                        strcmp(
1973
96.6k
                            m_poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
1974
96.6k
                            pszCompatibleName) == 0;
1975
96.6k
                    CPLFree(pszCompatibleName);
1976
96.6k
                }
1977
324k
                else
1978
324k
                {
1979
324k
                    bMatch =
1980
324k
                        strcmp(
1981
324k
                            m_poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
1982
324k
                            pszName) == 0;
1983
324k
                }
1984
1985
421k
                if (bMatch)
1986
26.3k
                {
1987
26.3k
                    m_currentFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
1988
26.3k
                    break;
1989
26.3k
                }
1990
421k
            }
1991
28.0k
            if (iField == m_poFeatureDefn->GetFieldCount())
1992
1.69k
            {
1993
1.69k
                char *pszCompatibleName =
1994
1.69k
                    OGRGPX_GetOGRCompatibleTagName(pszName);
1995
1.69k
                OGRFieldDefn newFieldDefn(pszCompatibleName, OFTInteger);
1996
1.69k
                CPLFree(pszCompatibleName);
1997
1998
1.69k
                m_poFeatureDefn->AddFieldDefn(&newFieldDefn);
1999
1.69k
                m_currentFieldDefn = m_poFeatureDefn->GetFieldDefn(
2000
1.69k
                    m_poFeatureDefn->GetFieldCount() - 1);
2001
2002
1.69k
                if (m_poFeatureDefn->GetFieldCount() == 100)
2003
0
                {
2004
0
                    CPLError(CE_Failure, CPLE_AppDefined,
2005
0
                             "Too many fields. File probably corrupted");
2006
0
                    XML_StopParser(m_oSchemaParser, XML_FALSE);
2007
0
                    m_bStopParsing = true;
2008
0
                }
2009
1.69k
            }
2010
28.0k
        }
2011
584k
    }
2012
2013
1.59M
    m_depthLevel++;
2014
1.59M
}
2015
2016
/************************************************************************/
2017
/*                      endElementLoadSchemaCbk()                       */
2018
/************************************************************************/
2019
2020
static bool OGRGPXIsInt(const char *pszStr)
2021
5.58k
{
2022
5.58k
    while (*pszStr == ' ')
2023
0
        pszStr++;
2024
2025
16.8k
    for (int i = 0; pszStr[i]; i++)
2026
11.2k
    {
2027
11.2k
        if (pszStr[i] == '+' || pszStr[i] == '-')
2028
1
        {
2029
1
            if (i != 0)
2030
0
                return false;
2031
1
        }
2032
11.2k
        else if (!(pszStr[i] >= '0' && pszStr[i] <= '9'))
2033
25
            return false;
2034
11.2k
    }
2035
5.55k
    return true;
2036
5.58k
}
2037
2038
void OGRGPXLayer::endElementLoadSchemaCbk(const char *pszName)
2039
299k
{
2040
299k
    if (m_bStopParsing)
2041
0
        return;
2042
2043
299k
    m_nWithoutEventCounter = 0;
2044
2045
299k
    m_depthLevel--;
2046
2047
299k
    if (m_inInterestingElement)
2048
95.2k
    {
2049
95.2k
        if (m_gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0)
2050
10.2k
        {
2051
10.2k
            m_inInterestingElement = false;
2052
10.2k
            m_inExtensions = false;
2053
10.2k
        }
2054
84.9k
        else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
2055
2.50k
        {
2056
2.50k
            m_inInterestingElement = false;
2057
2.50k
            m_inExtensions = false;
2058
2.50k
        }
2059
82.4k
        else if (m_gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
2060
6
        {
2061
6
            m_inInterestingElement = false;
2062
6
            m_inExtensions = false;
2063
6
        }
2064
82.4k
        else if (m_gpxGeomType == GPX_TRACK_POINT &&
2065
26.2k
                 strcmp(pszName, "trkpt") == 0)
2066
1.50k
        {
2067
1.50k
            m_inInterestingElement = false;
2068
1.50k
            m_inExtensions = false;
2069
1.50k
        }
2070
80.9k
        else if (m_gpxGeomType == GPX_ROUTE_POINT &&
2071
1.01k
                 strcmp(pszName, "rtept") == 0)
2072
468
        {
2073
468
            m_inInterestingElement = false;
2074
468
            m_inExtensions = false;
2075
468
        }
2076
80.5k
        else if (m_depthLevel == m_interestingDepthLevel + 1 &&
2077
6.41k
                 strcmp(pszName, "extensions") == 0)
2078
644
        {
2079
644
            m_inExtensions = false;
2080
644
        }
2081
79.8k
        else if (m_inExtensions && m_depthLevel == m_extensionsDepthLevel + 1 &&
2082
20.7k
                 !m_osSubElementName.empty() && m_osSubElementName == pszName)
2083
20.7k
        {
2084
20.7k
            if (!m_osSubElementValue.empty() && m_currentFieldDefn)
2085
14.2k
            {
2086
14.2k
                if (m_currentFieldDefn->GetType() == OFTInteger ||
2087
8.34k
                    m_currentFieldDefn->GetType() == OFTReal)
2088
6.15k
                {
2089
6.15k
                    char *pszRemainingStr = nullptr;
2090
6.15k
                    CPLStrtod(m_osSubElementValue.c_str(), &pszRemainingStr);
2091
6.15k
                    if (pszRemainingStr == nullptr || *pszRemainingStr == 0 ||
2092
555
                        *pszRemainingStr == ' ')
2093
5.80k
                    {
2094
5.80k
                        if (m_currentFieldDefn->GetType() == OFTInteger)
2095
5.58k
                        {
2096
5.58k
                            if (!OGRGPXIsInt(m_osSubElementValue.c_str()))
2097
25
                            {
2098
25
                                m_currentFieldDefn->SetType(OFTReal);
2099
25
                            }
2100
5.58k
                        }
2101
5.80k
                    }
2102
353
                    else
2103
353
                    {
2104
353
                        m_currentFieldDefn->SetType(OFTString);
2105
353
                    }
2106
6.15k
                }
2107
14.2k
            }
2108
2109
20.7k
            m_osSubElementName.clear();
2110
20.7k
            m_osSubElementValue.clear();
2111
20.7k
            m_currentFieldDefn = nullptr;
2112
20.7k
        }
2113
95.2k
    }
2114
299k
}
2115
2116
/************************************************************************/
2117
/*                      dataHandlerLoadSchemaCbk()                      */
2118
/************************************************************************/
2119
2120
void OGRGPXLayer::dataHandlerLoadSchemaCbk(const char *data, int nLen)
2121
821k
{
2122
821k
    if (m_bStopParsing)
2123
0
        return;
2124
2125
821k
    m_nDataHandlerCounter++;
2126
821k
    if (m_nDataHandlerCounter >= PARSER_BUF_SIZE)
2127
0
    {
2128
0
        CPLError(CE_Failure, CPLE_AppDefined,
2129
0
                 "File probably corrupted (million laugh pattern)");
2130
0
        XML_StopParser(m_oSchemaParser, XML_FALSE);
2131
0
        m_bStopParsing = true;
2132
0
        return;
2133
0
    }
2134
2135
821k
    m_nWithoutEventCounter = 0;
2136
2137
821k
    if (!m_osSubElementName.empty())
2138
114k
    {
2139
114k
        try
2140
114k
        {
2141
114k
            m_osSubElementValue.append(data, nLen);
2142
114k
        }
2143
114k
        catch (const std::bad_alloc &)
2144
114k
        {
2145
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
2146
0
                     "Out of memory when parsing GPX file");
2147
0
            XML_StopParser(m_oSchemaParser, XML_FALSE);
2148
0
            m_bStopParsing = true;
2149
0
            return;
2150
0
        }
2151
114k
        if (m_osSubElementValue.size() > 100000)
2152
0
        {
2153
0
            CPLError(CE_Failure, CPLE_AppDefined,
2154
0
                     "Too much data inside one element. "
2155
0
                     "File probably corrupted");
2156
0
            XML_StopParser(m_oSchemaParser, XML_FALSE);
2157
0
            m_bStopParsing = true;
2158
0
        }
2159
114k
    }
2160
821k
}
2161
#else
2162
void OGRGPXLayer::LoadExtensionsSchema()
2163
{
2164
}
2165
#endif
2166
2167
/************************************************************************/
2168
/*                             GetDataset()                             */
2169
/************************************************************************/
2170
2171
GDALDataset *OGRGPXLayer::GetDataset()
2172
0
{
2173
0
    return m_poDS;
2174
0
}