Coverage Report

Created: 2026-08-14 09:29

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.86k
    : m_poDS(poDSIn), m_gpxGeomType(gpxGeomTypeIn), m_bWriteMode(bWriteModeIn)
60
1.86k
{
61
1.86k
#ifdef HAVE_EXPAT
62
1.86k
    const char *gpxVersion = m_poDS->GetVersion();
63
1.86k
#endif
64
65
1.86k
    m_nMaxLinks =
66
1.86k
        atoi(CSLFetchNameValueDef(papszOpenOptions, "N_MAX_LINKS",
67
1.86k
                                  CPLGetConfigOption("GPX_N_MAX_LINKS", "2")));
68
1.86k
    if (m_nMaxLinks < 0)
69
0
        m_nMaxLinks = 2;
70
1.86k
    if (m_nMaxLinks > 100)
71
0
        m_nMaxLinks = 100;
72
73
1.86k
    m_bEleAs25D = CPLTestBool(
74
1.86k
        CSLFetchNameValueDef(papszOpenOptions, "ELE_AS_25D",
75
1.86k
                             CPLGetConfigOption("GPX_ELE_AS_25D", "NO")));
76
77
1.86k
    const bool bShortNames = CPLTestBool(
78
1.86k
        CSLFetchNameValueDef(papszOpenOptions, "SHORT_NAMES",
79
1.86k
                             CPLGetConfigOption("GPX_SHORT_NAMES", "NO")));
80
81
1.86k
    m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
82
1.86k
    SetDescription(m_poFeatureDefn->GetName());
83
1.86k
    m_poFeatureDefn->Reference();
84
85
1.86k
    if (m_gpxGeomType == GPX_TRACK_POINT)
86
360
    {
87
        /* Don't move this code. This fields must be number 0, 1 and 2 */
88
        /* in order to make OGRGPXLayer::startElementCbk work */
89
360
        OGRFieldDefn oFieldTrackFID("track_fid", OFTInteger);
90
360
        m_poFeatureDefn->AddFieldDefn(&oFieldTrackFID);
91
92
360
        OGRFieldDefn oFieldTrackSegID(
93
360
            (bShortNames) ? "trksegid" : "track_seg_id", OFTInteger);
94
360
        m_poFeatureDefn->AddFieldDefn(&oFieldTrackSegID);
95
96
360
        OGRFieldDefn oFieldTrackSegPointID(
97
360
            (bShortNames) ? "trksegptid" : "track_seg_point_id", OFTInteger);
98
360
        m_poFeatureDefn->AddFieldDefn(&oFieldTrackSegPointID);
99
100
360
        if (m_bWriteMode)
101
0
        {
102
0
            OGRFieldDefn oFieldName("track_name", OFTString);
103
0
            m_poFeatureDefn->AddFieldDefn(&oFieldName);
104
0
        }
105
360
    }
106
1.50k
    else if (m_gpxGeomType == GPX_ROUTE_POINT)
107
360
    {
108
        /* Don't move this code. See above */
109
360
        OGRFieldDefn oFieldRouteFID("route_fid", OFTInteger);
110
360
        m_poFeatureDefn->AddFieldDefn(&oFieldRouteFID);
111
112
360
        OGRFieldDefn oFieldRoutePointID(
113
360
            (bShortNames) ? "rteptid" : "route_point_id", OFTInteger);
114
360
        m_poFeatureDefn->AddFieldDefn(&oFieldRoutePointID);
115
116
360
        if (m_bWriteMode)
117
0
        {
118
0
            OGRFieldDefn oFieldName("route_name", OFTString);
119
0
            m_poFeatureDefn->AddFieldDefn(&oFieldName);
120
0
        }
121
360
    }
122
123
1.86k
    m_iFirstGPXField = m_poFeatureDefn->GetFieldCount();
124
125
1.86k
    if (m_gpxGeomType == GPX_WPT || m_gpxGeomType == GPX_TRACK_POINT ||
126
1.14k
        m_gpxGeomType == GPX_ROUTE_POINT)
127
1.08k
    {
128
1.08k
        m_poFeatureDefn->SetGeomType((m_bEleAs25D) ? wkbPoint25D : wkbPoint);
129
        /* Position info */
130
131
1.08k
        OGRFieldDefn oFieldEle("ele", OFTReal);
132
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldEle);
133
134
1.08k
        OGRFieldDefn oFieldTime("time", OFTDateTime);
135
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldTime);
136
137
1.08k
#ifdef HAVE_EXPAT
138
1.08k
        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
1.08k
#endif
147
148
1.08k
        OGRFieldDefn oFieldMagVar("magvar", OFTReal);
149
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldMagVar);
150
151
1.08k
        OGRFieldDefn oFieldGeoidHeight("geoidheight", OFTReal);
152
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldGeoidHeight);
153
154
        /* Description info */
155
156
1.08k
        OGRFieldDefn oFieldName("name", OFTString);
157
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldName);
158
159
1.08k
        OGRFieldDefn oFieldCmt("cmt", OFTString);
160
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldCmt);
161
162
1.08k
        OGRFieldDefn oFieldDesc("desc", OFTString);
163
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldDesc);
164
165
1.08k
        OGRFieldDefn oFieldSrc("src", OFTString);
166
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldSrc);
167
168
1.08k
#ifdef HAVE_EXPAT
169
1.08k
        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
1.08k
        else
178
1.08k
#endif
179
1.08k
        {
180
3.24k
            for (int i = 1; i <= m_nMaxLinks; i++)
181
2.16k
            {
182
2.16k
                char szFieldName[32];
183
2.16k
                snprintf(szFieldName, sizeof(szFieldName), "link%d_href", i);
184
2.16k
                OGRFieldDefn oFieldLinkHref(szFieldName, OFTString);
185
2.16k
                m_poFeatureDefn->AddFieldDefn(&oFieldLinkHref);
186
187
2.16k
                snprintf(szFieldName, sizeof(szFieldName), "link%d_text", i);
188
2.16k
                OGRFieldDefn oFieldLinkText(szFieldName, OFTString);
189
2.16k
                m_poFeatureDefn->AddFieldDefn(&oFieldLinkText);
190
191
2.16k
                snprintf(szFieldName, sizeof(szFieldName), "link%d_type", i);
192
2.16k
                OGRFieldDefn oFieldLinkType(szFieldName, OFTString);
193
2.16k
                m_poFeatureDefn->AddFieldDefn(&oFieldLinkType);
194
2.16k
            }
195
1.08k
        }
196
197
1.08k
        OGRFieldDefn oFieldSym("sym", OFTString);
198
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldSym);
199
200
1.08k
        OGRFieldDefn oFieldType("type", OFTString);
201
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldType);
202
203
        /* Accuracy info */
204
205
1.08k
        OGRFieldDefn oFieldFix("fix", OFTString);
206
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldFix);
207
208
1.08k
        OGRFieldDefn oFieldSat("sat", OFTInteger);
209
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldSat);
210
211
1.08k
        OGRFieldDefn oFieldHdop("hdop", OFTReal);
212
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldHdop);
213
214
1.08k
        OGRFieldDefn oFieldVdop("vdop", OFTReal);
215
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldVdop);
216
217
1.08k
        OGRFieldDefn oFieldPdop("pdop", OFTReal);
218
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldPdop);
219
220
1.08k
        OGRFieldDefn oFieldAgeofgpsdata("ageofdgpsdata", OFTReal);
221
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldAgeofgpsdata);
222
223
1.08k
        OGRFieldDefn oFieldDgpsid("dgpsid", OFTInteger);
224
1.08k
        m_poFeatureDefn->AddFieldDefn(&oFieldDgpsid);
225
1.08k
    }
226
786
    else
227
786
    {
228
786
        if (m_gpxGeomType == GPX_TRACK)
229
386
            m_poFeatureDefn->SetGeomType((m_bEleAs25D) ? wkbMultiLineString25D
230
386
                                                       : wkbMultiLineString);
231
400
        else
232
400
            m_poFeatureDefn->SetGeomType((m_bEleAs25D) ? wkbLineString25D
233
400
                                                       : wkbLineString);
234
235
786
        OGRFieldDefn oFieldName("name", OFTString);
236
786
        m_poFeatureDefn->AddFieldDefn(&oFieldName);
237
238
786
        OGRFieldDefn oFieldCmt("cmt", OFTString);
239
786
        m_poFeatureDefn->AddFieldDefn(&oFieldCmt);
240
241
786
        OGRFieldDefn oFieldDesc("desc", OFTString);
242
786
        m_poFeatureDefn->AddFieldDefn(&oFieldDesc);
243
244
786
        OGRFieldDefn oFieldSrc("src", OFTString);
245
786
        m_poFeatureDefn->AddFieldDefn(&oFieldSrc);
246
247
2.35k
        for (int i = 1; i <= m_nMaxLinks; i++)
248
1.57k
        {
249
1.57k
            char szFieldName[32];
250
1.57k
            snprintf(szFieldName, sizeof(szFieldName), "link%d_href", i);
251
1.57k
            OGRFieldDefn oFieldLinkHref(szFieldName, OFTString);
252
1.57k
            m_poFeatureDefn->AddFieldDefn(&oFieldLinkHref);
253
254
1.57k
            snprintf(szFieldName, sizeof(szFieldName), "link%d_text", i);
255
1.57k
            OGRFieldDefn oFieldLinkText(szFieldName, OFTString);
256
1.57k
            m_poFeatureDefn->AddFieldDefn(&oFieldLinkText);
257
258
1.57k
            snprintf(szFieldName, sizeof(szFieldName), "link%d_type", i);
259
1.57k
            OGRFieldDefn oFieldLinkType(szFieldName, OFTString);
260
1.57k
            m_poFeatureDefn->AddFieldDefn(&oFieldLinkType);
261
1.57k
        }
262
263
786
        OGRFieldDefn oFieldNumber("number", OFTInteger);
264
786
        m_poFeatureDefn->AddFieldDefn(&oFieldNumber);
265
266
786
        OGRFieldDefn oFieldType("type", OFTString);
267
786
        m_poFeatureDefn->AddFieldDefn(&oFieldType);
268
786
    }
269
270
    /* Number of 'standard' GPX attributes */
271
1.86k
    m_nGPXFields = m_poFeatureDefn->GetFieldCount();
272
273
1.86k
    m_poSRS = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
274
1.86k
    m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
275
276
1.86k
    if (m_poFeatureDefn->GetGeomFieldCount() != 0)
277
1.86k
        m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
278
279
1.86k
    if (!m_bWriteMode)
280
1.80k
    {
281
1.80k
        m_fpGPX.reset(VSIFOpenL(pszFilename, "r"));
282
1.80k
        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.80k
        if (m_poDS->GetUseExtensions() ||
290
280
            CPLTestBool(CPLGetConfigOption("GPX_USE_EXTENSIONS", "FALSE")))
291
1.52k
        {
292
1.52k
            LoadExtensionsSchema();
293
1.52k
        }
294
1.80k
    }
295
296
1.86k
    OGRGPXLayer::ResetReading();
297
1.86k
}
298
299
/************************************************************************/
300
/*                            ~OGRGPXLayer()                            */
301
/************************************************************************/
302
303
OGRGPXLayer::~OGRGPXLayer()
304
305
1.86k
{
306
1.86k
#ifdef HAVE_EXPAT
307
1.86k
    if (m_oParser)
308
1.80k
        XML_ParserFree(m_oParser);
309
1.86k
#endif
310
1.86k
    m_poFeatureDefn->Release();
311
312
1.86k
    if (m_poSRS != nullptr)
313
1.86k
        m_poSRS->Release();
314
1.86k
}
315
316
#ifdef HAVE_EXPAT
317
318
static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
319
                                    const char **ppszAttr)
320
10.4k
{
321
10.4k
    static_cast<OGRGPXLayer *>(pUserData)->startElementCbk(pszName, ppszAttr);
322
10.4k
}
323
324
static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
325
10.4k
{
326
10.4k
    static_cast<OGRGPXLayer *>(pUserData)->endElementCbk(pszName);
327
10.4k
}
328
329
static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
330
3.13k
{
331
3.13k
    static_cast<OGRGPXLayer *>(pUserData)->dataHandlerCbk(data, nLen);
332
3.13k
}
333
334
#endif
335
336
/************************************************************************/
337
/*                            ResetReading()                            */
338
/************************************************************************/
339
340
void OGRGPXLayer::ResetReading()
341
342
1.86k
{
343
1.86k
    m_nNextFID = 0;
344
1.86k
    if (m_fpGPX)
345
1.80k
    {
346
1.80k
        m_fpGPX->Seek(0, SEEK_SET);
347
1.80k
#ifdef HAVE_EXPAT
348
1.80k
        if (m_oParser)
349
0
            XML_ParserFree(m_oParser);
350
351
1.80k
        m_oParser = OGRCreateExpatXMLParser();
352
1.80k
        XML_SetElementHandler(m_oParser, ::startElementCbk, ::endElementCbk);
353
1.80k
        XML_SetCharacterDataHandler(m_oParser, ::dataHandlerCbk);
354
1.80k
        XML_SetUserData(m_oParser, this);
355
1.80k
#endif
356
1.80k
    }
357
1.86k
    m_hasFoundLat = false;
358
1.86k
    m_hasFoundLon = false;
359
1.86k
    m_inInterestingElement = false;
360
1.86k
    m_osSubElementName.clear();
361
1.86k
    m_osSubElementValue.clear();
362
363
1.86k
    m_poFeature.reset();
364
1.86k
    m_oFeatureQueue.clear();
365
1.86k
    m_multiLineString.reset();
366
1.86k
    m_lineString.reset();
367
368
1.86k
    m_depthLevel = 0;
369
1.86k
    m_interestingDepthLevel = 0;
370
371
1.86k
    m_trkFID = 0;
372
1.86k
    m_trkSegId = 0;
373
1.86k
    m_trkSegPtId = 0;
374
1.86k
    m_rteFID = 0;
375
1.86k
    m_rtePtId = 0;
376
1.86k
}
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
126k
{
387
126k
    char *pszModName = CPLStrdup(pszName);
388
3.51M
    for (int i = 0; pszModName[i] != 0; i++)
389
3.38M
    {
390
3.38M
        if (pszModName[i] == ':')
391
6.98k
            pszModName[i] = '_';
392
3.38M
    }
393
126k
    return pszModName;
394
126k
}
395
396
void OGRGPXLayer::AddStrToSubElementValue(const char *pszStr)
397
128
{
398
128
    try
399
128
    {
400
128
        m_osSubElementValue.append(pszStr);
401
128
    }
402
128
    catch (const std::bad_alloc &)
403
128
    {
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
128
}
410
411
void OGRGPXLayer::startElementCbk(const char *pszName, const char **ppszAttr)
412
10.4k
{
413
10.4k
    if (m_bStopParsing)
414
0
        return;
415
416
10.4k
    m_nWithoutEventCounter = 0;
417
418
10.4k
    if ((m_gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0) ||
419
10.4k
        (m_gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0) ||
420
10.4k
        (m_gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0))
421
52
    {
422
52
        m_interestingDepthLevel = m_depthLevel;
423
424
52
        m_poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
425
52
        m_inInterestingElement = true;
426
52
        m_hasFoundLat = false;
427
52
        m_hasFoundLon = false;
428
52
        m_inExtensions = false;
429
52
        m_inLink = false;
430
52
        m_iCountLink = 0;
431
432
113
        for (int i = 0; ppszAttr[i]; i += 2)
433
61
        {
434
61
            if (strcmp(ppszAttr[i], "lat") == 0 && ppszAttr[i + 1][0])
435
10
            {
436
10
                m_hasFoundLat = true;
437
10
                m_latVal = CPLAtof(ppszAttr[i + 1]);
438
10
            }
439
51
            else if (strcmp(ppszAttr[i], "lon") == 0 && ppszAttr[i + 1][0])
440
10
            {
441
10
                m_hasFoundLon = true;
442
10
                m_lonVal = CPLAtof(ppszAttr[i + 1]);
443
10
            }
444
61
        }
445
446
52
        m_poFeature->SetFID(m_nNextFID++);
447
448
52
        if (m_hasFoundLat && m_hasFoundLon)
449
10
        {
450
10
            m_poFeature->SetGeometryDirectly(new OGRPoint(m_lonVal, m_latVal));
451
10
        }
452
42
        else
453
42
        {
454
42
            CPLDebug("GPX",
455
42
                     "Skipping %s (FID=" CPL_FRMT_GIB
456
42
                     ") without lat and/or lon",
457
42
                     pszName, m_nNextFID);
458
42
        }
459
460
52
        if (m_gpxGeomType == GPX_ROUTE_POINT)
461
3
        {
462
3
            m_rtePtId++;
463
3
            m_poFeature->SetField(FLD_ROUTE_FID, m_rteFID - 1);
464
3
            m_poFeature->SetField(FLD_ROUTE_PT_ID, m_rtePtId - 1);
465
3
        }
466
49
        else if (m_gpxGeomType == GPX_TRACK_POINT)
467
5
        {
468
5
            m_trkSegPtId++;
469
470
5
            m_poFeature->SetField(FLD_TRACK_FID, m_trkFID - 1);
471
5
            m_poFeature->SetField(FLD_TRACK_SEG_ID, m_trkSegId - 1);
472
5
            m_poFeature->SetField(FLD_TRACK_PT_ID, m_trkSegPtId - 1);
473
5
        }
474
52
    }
475
10.4k
    else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
476
65
    {
477
65
        m_interestingDepthLevel = m_depthLevel;
478
479
65
        m_inExtensions = false;
480
65
        m_inLink = false;
481
65
        m_iCountLink = 0;
482
65
        m_poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
483
65
        m_inInterestingElement = true;
484
485
65
        m_multiLineString = std::make_unique<OGRMultiLineString>();
486
65
        m_lineString.reset();
487
488
65
        m_poFeature->SetFID(m_nNextFID++);
489
65
    }
490
10.3k
    else if (m_gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trk") == 0)
491
65
    {
492
65
        m_trkFID++;
493
65
        m_trkSegId = 0;
494
65
    }
495
10.2k
    else if (m_gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkseg") == 0)
496
3
    {
497
3
        m_trkSegId++;
498
3
        m_trkSegPtId = 0;
499
3
    }
500
10.2k
    else if (m_gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
501
10
    {
502
10
        m_interestingDepthLevel = m_depthLevel;
503
504
10
        m_poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
505
10
        m_inInterestingElement = true;
506
10
        m_inExtensions = false;
507
10
        m_inLink = false;
508
10
        m_iCountLink = 0;
509
510
10
        m_lineString = std::make_unique<OGRLineString>();
511
10
        m_poFeature->SetFID(m_nNextFID++);
512
10
    }
513
10.2k
    else if (m_gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rte") == 0)
514
10
    {
515
10
        m_rteFID++;
516
10
        m_rtePtId = 0;
517
10
    }
518
10.2k
    else if (m_inInterestingElement)
519
1.99k
    {
520
1.99k
        if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trkseg") == 0 &&
521
3
            m_depthLevel == m_interestingDepthLevel + 1)
522
3
        {
523
3
            if (m_multiLineString)
524
3
            {
525
3
                m_lineString = std::make_unique<OGRLineString>();
526
3
            }
527
3
        }
528
1.98k
        else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trkpt") == 0 &&
529
5
                 m_depthLevel == m_interestingDepthLevel + 2)
530
5
        {
531
5
            if (m_lineString)
532
5
            {
533
5
                m_hasFoundLat = false;
534
5
                m_hasFoundLon = false;
535
15
                for (int i = 0; ppszAttr[i]; i += 2)
536
10
                {
537
10
                    if (strcmp(ppszAttr[i], "lat") == 0 && ppszAttr[i + 1][0])
538
5
                    {
539
5
                        m_hasFoundLat = true;
540
5
                        m_latVal = CPLAtof(ppszAttr[i + 1]);
541
5
                    }
542
5
                    else if (strcmp(ppszAttr[i], "lon") == 0 &&
543
5
                             ppszAttr[i + 1][0])
544
5
                    {
545
5
                        m_hasFoundLon = true;
546
5
                        m_lonVal = CPLAtof(ppszAttr[i + 1]);
547
5
                    }
548
10
                }
549
550
5
                if (m_hasFoundLat && m_hasFoundLon)
551
5
                {
552
5
                    m_lineString->addPoint(m_lonVal, m_latVal);
553
5
                }
554
0
                else
555
0
                {
556
0
                    CPLDebug("GPX", "Skipping %s without lat and/or lon",
557
0
                             pszName);
558
0
                }
559
5
            }
560
5
        }
561
1.98k
        else if (m_gpxGeomType == GPX_ROUTE && strcmp(pszName, "rtept") == 0 &&
562
3
                 m_depthLevel == m_interestingDepthLevel + 1)
563
3
        {
564
3
            if (m_lineString)
565
3
            {
566
3
                m_hasFoundLat = false;
567
3
                m_hasFoundLon = false;
568
9
                for (int i = 0; ppszAttr[i]; i += 2)
569
6
                {
570
6
                    if (strcmp(ppszAttr[i], "lat") == 0 && ppszAttr[i + 1][0])
571
3
                    {
572
3
                        m_hasFoundLat = true;
573
3
                        m_latVal = CPLAtof(ppszAttr[i + 1]);
574
3
                    }
575
3
                    else if (strcmp(ppszAttr[i], "lon") == 0 &&
576
3
                             ppszAttr[i + 1][0])
577
3
                    {
578
3
                        m_hasFoundLon = true;
579
3
                        m_lonVal = CPLAtof(ppszAttr[i + 1]);
580
3
                    }
581
6
                }
582
583
3
                if (m_hasFoundLat && m_hasFoundLon)
584
3
                {
585
3
                    m_lineString->addPoint(m_lonVal, m_latVal);
586
3
                }
587
0
                else
588
0
                {
589
0
                    CPLDebug("GPX", "Skipping %s without lat and/or lon",
590
0
                             pszName);
591
0
                }
592
3
            }
593
3
        }
594
1.98k
        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
1.98k
        else if (m_depthLevel == m_interestingDepthLevel + 1 &&
604
98
                 strcmp(pszName, "extensions") == 0)
605
18
        {
606
18
            if (m_poDS->GetUseExtensions())
607
17
            {
608
17
                m_inExtensions = true;
609
17
            }
610
18
        }
611
1.96k
        else if (m_depthLevel == m_interestingDepthLevel + 1 ||
612
1.88k
                 (m_inExtensions &&
613
1.85k
                  m_depthLevel == m_interestingDepthLevel + 2))
614
1.87k
        {
615
1.87k
            m_osSubElementName.clear();
616
1.87k
            m_iCurrentField = -1;
617
618
1.87k
            if (strcmp(pszName, "link") == 0)
619
3
            {
620
3
                m_iCountLink++;
621
3
                if (m_iCountLink <= m_nMaxLinks)
622
2
                {
623
2
                    if (ppszAttr[0] && ppszAttr[1] &&
624
2
                        strcmp(ppszAttr[0], "href") == 0)
625
2
                    {
626
2
                        char szFieldName[32];
627
2
                        snprintf(szFieldName, sizeof(szFieldName),
628
2
                                 "link%d_href", m_iCountLink);
629
2
                        m_iCurrentField =
630
2
                            m_poFeatureDefn->GetFieldIndex(szFieldName);
631
2
                        m_poFeature->SetField(m_iCurrentField, ppszAttr[1]);
632
2
                    }
633
2
                }
634
1
                else
635
1
                {
636
1
                    static int once = 1;
637
1
                    if (once)
638
1
                    {
639
1
                        once = 0;
640
1
                        CPLError(CE_Warning, CPLE_AppDefined,
641
1
                                 "GPX driver only reads %d links per element. "
642
1
                                 "Others will be ignored. "
643
1
                                 "This can be changed with the GPX_N_MAX_LINKS "
644
1
                                 "environment variable",
645
1
                                 m_nMaxLinks);
646
1
                    }
647
1
                }
648
3
                m_inLink = true;
649
3
                m_iCurrentField = -1;
650
3
            }
651
1.87k
            else
652
1.87k
            {
653
23.4k
                for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount();
654
21.6k
                     iField++)
655
23.4k
                {
656
23.4k
                    bool bMatch = false;
657
23.4k
                    if (iField >= m_nGPXFields)
658
1.79k
                    {
659
1.79k
                        char *pszCompatibleName =
660
1.79k
                            OGRGPX_GetOGRCompatibleTagName(pszName);
661
1.79k
                        bMatch = strcmp(m_poFeatureDefn->GetFieldDefn(iField)
662
1.79k
                                            ->GetNameRef(),
663
1.79k
                                        pszCompatibleName) == 0;
664
1.79k
                        CPLFree(pszCompatibleName);
665
1.79k
                    }
666
21.6k
                    else
667
21.6k
                    {
668
21.6k
                        bMatch = strcmp(m_poFeatureDefn->GetFieldDefn(iField)
669
21.6k
                                            ->GetNameRef(),
670
21.6k
                                        pszName) == 0;
671
21.6k
                    }
672
673
23.4k
                    if (bMatch)
674
1.86k
                    {
675
1.86k
                        m_iCurrentField = iField;
676
1.86k
                        m_osSubElementName = pszName;
677
1.86k
                        break;
678
1.86k
                    }
679
23.4k
                }
680
1.87k
            }
681
1.87k
        }
682
89
        else if (m_depthLevel == m_interestingDepthLevel + 2 && m_inLink)
683
7
        {
684
7
            m_osSubElementName.clear();
685
7
            m_iCurrentField = -1;
686
7
            if (m_iCountLink <= m_nMaxLinks)
687
4
            {
688
4
                if (strcmp(pszName, "type") == 0)
689
2
                {
690
2
                    char szFieldName[32];
691
2
                    snprintf(szFieldName, sizeof(szFieldName), "link%d_type",
692
2
                             m_iCountLink);
693
2
                    m_iCurrentField =
694
2
                        m_poFeatureDefn->GetFieldIndex(szFieldName);
695
2
                    m_osSubElementName = pszName;
696
2
                }
697
2
                else if (strcmp(pszName, "text") == 0)
698
2
                {
699
2
                    char szFieldName[32];
700
2
                    snprintf(szFieldName, sizeof(szFieldName), "link%d_text",
701
2
                             m_iCountLink);
702
2
                    m_iCurrentField =
703
2
                        m_poFeatureDefn->GetFieldIndex(szFieldName);
704
2
                    m_osSubElementName = pszName;
705
2
                }
706
4
            }
707
7
        }
708
82
        else if (m_inExtensions && m_depthLevel > m_interestingDepthLevel + 2)
709
64
        {
710
64
            AddStrToSubElementValue((ppszAttr[0] == nullptr)
711
64
                                        ? CPLSPrintf("<%s>", pszName)
712
64
                                        : CPLSPrintf("<%s ", pszName));
713
64
            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
64
            if (ppszAttr[0] != nullptr)
719
0
            {
720
0
                AddStrToSubElementValue(">");
721
0
            }
722
64
        }
723
1.99k
    }
724
725
10.4k
    m_depthLevel++;
726
10.4k
}
727
728
/************************************************************************/
729
/*                           endElementCbk()                            */
730
/************************************************************************/
731
732
void OGRGPXLayer::endElementCbk(const char *pszName)
733
10.4k
{
734
10.4k
    if (m_bStopParsing)
735
0
        return;
736
737
10.4k
    m_nWithoutEventCounter = 0;
738
739
10.4k
    m_depthLevel--;
740
741
10.4k
    if (m_inInterestingElement)
742
2.06k
    {
743
2.06k
        if ((m_gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0) ||
744
2.02k
            (m_gpxGeomType == GPX_ROUTE_POINT &&
745
8
             strcmp(pszName, "rtept") == 0) ||
746
2.02k
            (m_gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0))
747
51
        {
748
51
            const bool bIsValid = (m_hasFoundLat && m_hasFoundLon);
749
51
            m_inInterestingElement = false;
750
751
51
            if (bIsValid &&
752
10
                (m_poFilterGeom == nullptr ||
753
0
                 FilterGeometry(m_poFeature->GetGeometryRef())) &&
754
10
                (m_poAttrQuery == nullptr ||
755
0
                 m_poAttrQuery->Evaluate(m_poFeature.get())))
756
10
            {
757
10
                if (auto poGeom = m_poFeature->GetGeometryRef())
758
10
                {
759
10
                    poGeom->assignSpatialReference(m_poSRS);
760
761
10
                    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
10
                }
775
776
10
                m_oFeatureQueue.push_back(std::move(m_poFeature));
777
10
            }
778
41
            else
779
41
            {
780
41
                m_poFeature.reset();
781
41
            }
782
51
        }
783
2.01k
        else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
784
15
        {
785
15
            m_poFeature->SetGeometryDirectly(m_multiLineString.release());
786
15
            m_lineString.reset();
787
15
            m_multiLineString.reset();
788
789
15
            m_inInterestingElement = false;
790
15
            if ((m_poFilterGeom == nullptr ||
791
0
                 FilterGeometry(m_poFeature->GetGeometryRef())) &&
792
15
                (m_poAttrQuery == nullptr ||
793
0
                 m_poAttrQuery->Evaluate(m_poFeature.get())))
794
15
            {
795
15
                if (m_poFeature->GetGeometryRef() != nullptr)
796
15
                {
797
15
                    m_poFeature->GetGeometryRef()->assignSpatialReference(
798
15
                        m_poSRS);
799
15
                }
800
801
15
                m_oFeatureQueue.push_back(std::move(m_poFeature));
802
15
            }
803
0
            else
804
0
            {
805
0
                m_poFeature.reset();
806
0
            }
807
15
        }
808
2.00k
        else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trkseg") == 0 &&
809
3
                 m_depthLevel == m_interestingDepthLevel + 1)
810
3
        {
811
3
            if (m_multiLineString)
812
3
            {
813
3
                m_multiLineString->addGeometry(std::move(m_lineString));
814
3
            }
815
0
            else
816
0
            {
817
0
                m_lineString.reset();
818
0
            }
819
3
        }
820
1.99k
        else if (m_gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
821
10
        {
822
10
            m_poFeature->SetGeometryDirectly(m_lineString.release());
823
10
            m_lineString.reset();
824
825
10
            m_inInterestingElement = false;
826
10
            if ((m_poFilterGeom == nullptr ||
827
0
                 FilterGeometry(m_poFeature->GetGeometryRef())) &&
828
10
                (m_poAttrQuery == nullptr ||
829
0
                 m_poAttrQuery->Evaluate(m_poFeature.get())))
830
10
            {
831
10
                if (m_poFeature->GetGeometryRef() != nullptr)
832
10
                {
833
10
                    m_poFeature->GetGeometryRef()->assignSpatialReference(
834
10
                        m_poSRS);
835
10
                }
836
837
10
                m_oFeatureQueue.push_back(std::move(m_poFeature));
838
10
            }
839
0
            else
840
0
            {
841
0
                m_poFeature.reset();
842
0
            }
843
10
        }
844
1.98k
        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
1.98k
        else if (m_depthLevel == m_interestingDepthLevel + 1 &&
866
100
                 strcmp(pszName, "extensions") == 0)
867
17
        {
868
17
            m_inExtensions = false;
869
17
        }
870
1.97k
        else if ((m_depthLevel == m_interestingDepthLevel + 1 ||
871
1.88k
                  (m_inExtensions &&
872
1.85k
                   m_depthLevel == m_interestingDepthLevel + 2)) &&
873
1.87k
                 !m_osSubElementName.empty() && m_osSubElementName == pszName)
874
1.86k
        {
875
1.86k
            if (m_poFeature && !m_osSubElementValue.empty())
876
114
            {
877
114
                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
114
                else
895
114
                {
896
114
                    m_poFeature->SetField(m_iCurrentField,
897
114
                                          m_osSubElementValue.c_str());
898
114
                }
899
114
            }
900
1.86k
            if (strcmp(pszName, "link") == 0)
901
0
                m_inLink = false;
902
903
1.86k
            m_osSubElementName.clear();
904
1.86k
            m_osSubElementValue.clear();
905
1.86k
        }
906
101
        else if (m_inLink && m_depthLevel == m_interestingDepthLevel + 2)
907
7
        {
908
7
            if (m_iCurrentField != -1 && !m_osSubElementName.empty() &&
909
4
                m_osSubElementName == pszName && m_poFeature &&
910
4
                !m_osSubElementValue.empty())
911
4
            {
912
4
                m_poFeature->SetField(m_iCurrentField,
913
4
                                      m_osSubElementValue.c_str());
914
4
            }
915
916
7
            m_osSubElementName.clear();
917
7
            m_osSubElementValue.clear();
918
7
        }
919
94
        else if (m_inExtensions && m_depthLevel > m_interestingDepthLevel + 2)
920
64
        {
921
64
            AddStrToSubElementValue(CPLSPrintf("</%s>", pszName));
922
64
        }
923
2.06k
    }
924
10.4k
}
925
926
/************************************************************************/
927
/*                           dataHandlerCbk()                           */
928
/************************************************************************/
929
930
void OGRGPXLayer::dataHandlerCbk(const char *data, int nLen)
931
3.13k
{
932
3.13k
    if (m_bStopParsing)
933
0
        return;
934
935
3.13k
    m_nDataHandlerCounter++;
936
3.13k
    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
3.13k
    m_nWithoutEventCounter = 0;
946
947
3.13k
    if (!m_osSubElementName.empty())
948
146
    {
949
146
        if (m_inExtensions && m_depthLevel > m_interestingDepthLevel + 2)
950
61
        {
951
61
            if (data[0] == '\n')
952
7
                return;
953
61
        }
954
139
        try
955
139
        {
956
139
            m_osSubElementValue.append(data, nLen);
957
139
        }
958
139
        catch (const std::bad_alloc &)
959
139
        {
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
139
        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
139
    }
975
3.13k
}
976
#endif
977
978
/************************************************************************/
979
/*                           GetNextFeature()                           */
980
/************************************************************************/
981
982
OGRFeature *OGRGPXLayer::GetNextFeature()
983
1.56k
{
984
1.56k
    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.56k
    if (m_fpGPX == nullptr)
992
0
        return nullptr;
993
994
1.56k
#ifdef HAVE_EXPAT
995
996
1.56k
    if (m_bStopParsing)
997
1.48k
        return nullptr;
998
999
80
    if (!this->m_oFeatureQueue.empty())
1000
23
    {
1001
23
        OGRFeature *poFeature = std::move(m_oFeatureQueue.front()).release();
1002
23
        m_oFeatureQueue.pop_front();
1003
23
        return poFeature;
1004
23
    }
1005
1006
57
    if (m_fpGPX->Eof() || m_fpGPX->Error())
1007
12
        return nullptr;
1008
1009
45
    std::vector<char> aBuf(PARSER_BUF_SIZE);
1010
45
    m_nWithoutEventCounter = 0;
1011
1012
45
    int nDone = 0;
1013
45
    do
1014
55
    {
1015
55
        m_nDataHandlerCounter = 0;
1016
55
        unsigned int nLen = static_cast<unsigned int>(
1017
55
            m_fpGPX->Read(aBuf.data(), 1, aBuf.size()));
1018
55
        nDone = (nLen < aBuf.size());
1019
55
        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
55
        m_nWithoutEventCounter++;
1031
55
    } while (!nDone && this->m_oFeatureQueue.empty() && !m_bStopParsing &&
1032
10
             m_nWithoutEventCounter < 10);
1033
1034
45
    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
45
    if (!this->m_oFeatureQueue.empty())
1042
12
    {
1043
12
        OGRFeature *poFeature = std::move(m_oFeatureQueue.front()).release();
1044
12
        m_oFeatureQueue.pop_front();
1045
12
        return poFeature;
1046
12
    }
1047
33
#endif
1048
33
    return nullptr;
1049
45
}
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.3k
{
1164
48.3k
    VSILFILE *fp = m_poDS->GetOutputFP();
1165
1166
    /* Begin with standard GPX fields */
1167
48.3k
    int i = m_iFirstGPXField;
1168
628k
    for (; i < m_nGPXFields; i++)
1169
580k
    {
1170
580k
        const OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
1171
580k
        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
580k
    }
1215
1216
    /* Write "extra" fields within the <extensions> tag */
1217
48.3k
    const int n = m_poFeatureDefn->GetFieldCount();
1218
48.3k
    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.3k
}
1296
1297
/************************************************************************/
1298
/*                   CheckAndFixCoordinatesValidity()                   */
1299
/************************************************************************/
1300
1301
OGRErr OGRGPXLayer::CheckAndFixCoordinatesValidity(double *pdfLatitude,
1302
                                                   double *pdfLongitude)
1303
16
{
1304
16
    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
11
    if (pdfLongitude != nullptr &&
1319
11
        (*pdfLongitude < -180 || *pdfLongitude > 180))
1320
8
    {
1321
8
        static bool bFirstWarning = true;
1322
8
        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
8
        *pdfLongitude = fmod(*pdfLongitude + 180.0, 360.0) - 180.0;
1333
8
        return OGRERR_NONE;
1334
8
    }
1335
1336
3
    return OGRERR_NONE;
1337
11
}
1338
1339
/************************************************************************/
1340
/*                           ICreateFeature()                           */
1341
/************************************************************************/
1342
1343
OGRErr OGRGPXLayer::ICreateFeature(OGRFeature *poFeature)
1344
1345
48.4k
{
1346
48.4k
    VSILFILE *fp = m_poDS->GetOutputFP();
1347
48.4k
    if (fp == nullptr)
1348
0
        return OGRERR_FAILURE;
1349
1350
48.4k
    char szLat[64];
1351
48.4k
    char szLon[64];
1352
48.4k
    char szAlt[64];
1353
1354
48.4k
    const OGRGeometry *poGeom = poFeature->GetGeometryRef();
1355
1356
48.4k
    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.4k
    else if (m_gpxGeomType == GPX_ROUTE)
1402
1.16k
    {
1403
1.16k
        if (m_poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK ||
1404
1.08k
            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.08k
        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.08k
        m_poDS->SetLastGPXGeomTypeWritten(m_gpxGeomType);
1419
1420
1.08k
        const OGRLineString *line = nullptr;
1421
1422
1.08k
        if (poGeom == nullptr)
1423
1.03k
        {
1424
1.03k
            m_poDS->PrintLine("<rte>");
1425
1.03k
            WriteFeatureAttributes(poFeature);
1426
1.03k
            m_poDS->PrintLine("</rte>");
1427
1.03k
            return OGRERR_NONE;
1428
1.03k
        }
1429
1430
43
        switch (poGeom->getGeometryType())
1431
43
        {
1432
29
            case wkbLineString:
1433
29
            case wkbLineString25D:
1434
29
            {
1435
29
                line = poGeom->toLineString();
1436
29
                break;
1437
29
            }
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
11
            default:
1463
11
            {
1464
11
                CPLError(
1465
11
                    CE_Failure, CPLE_NotSupported,
1466
11
                    "Geometry type of `%s' not supported for 'rte' element.\n",
1467
11
                    OGRGeometryTypeToName(poGeom->getGeometryType()));
1468
11
                return OGRERR_FAILURE;
1469
3
            }
1470
43
        }
1471
1472
32
        m_poDS->PrintLine("<rte>");
1473
32
        WriteFeatureAttributes(poFeature);
1474
32
        if (line)
1475
29
        {
1476
29
            const int n = line->getNumPoints();
1477
45
            for (int i = 0; i < n; i++)
1478
16
            {
1479
16
                double lat = line->getY(i);
1480
16
                double lon = line->getX(i);
1481
16
                CheckAndFixCoordinatesValidity(&lat, &lon);
1482
16
                m_poDS->AddCoord(lon, lat);
1483
16
                OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1484
16
                OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1485
16
                m_poDS->PrintLine("  <rtept lat=\"%s\" lon=\"%s\">", szLat,
1486
16
                                  szLon);
1487
16
                if (poGeom->getGeometryType() == wkbLineString25D ||
1488
16
                    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
16
                m_poDS->PrintLine("  </rtept>");
1494
16
            }
1495
29
        }
1496
32
        m_poDS->PrintLine("</rte>");
1497
32
    }
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
32
    return OGRERR_NONE;
1774
48.4k
}
1775
1776
/************************************************************************/
1777
/*                            CreateField()                             */
1778
/************************************************************************/
1779
1780
OGRErr OGRGPXLayer::CreateField(const OGRFieldDefn *poField, int /*bApproxOK*/)
1781
1.55k
{
1782
20.2k
    for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
1783
18.6k
    {
1784
18.6k
        if (strcmp(m_poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
1785
18.6k
                   poField->GetNameRef()) == 0)
1786
0
        {
1787
0
            return OGRERR_NONE;
1788
0
        }
1789
18.6k
    }
1790
1.55k
    if (!m_poDS->GetUseExtensions())
1791
1.55k
    {
1792
1.55k
        CPLError(CE_Failure, CPLE_NotSupported,
1793
1.55k
                 "Field of name '%s' is not supported in GPX schema. "
1794
1.55k
                 "Use GPX_USE_EXTENSIONS creation option to allow use of the "
1795
1.55k
                 "<extensions> element.",
1796
1.55k
                 poField->GetNameRef());
1797
1.55k
        return OGRERR_FAILURE;
1798
1.55k
    }
1799
0
    else
1800
0
    {
1801
0
        m_poFeatureDefn->AddFieldDefn(poField);
1802
0
        return OGRERR_NONE;
1803
0
    }
1804
1.55k
}
1805
1806
/************************************************************************/
1807
/*                           TestCapability()                           */
1808
/************************************************************************/
1809
1810
int OGRGPXLayer::TestCapability(const char *pszCap) const
1811
1812
423
{
1813
423
    if (EQUAL(pszCap, OLCSequentialWrite))
1814
0
        return m_bWriteMode;
1815
423
    else if (EQUAL(pszCap, OLCCreateField))
1816
0
        return m_bWriteMode;
1817
423
    else if (EQUAL(pszCap, OLCStringsAsUTF8))
1818
0
        return TRUE;
1819
423
    else if (EQUAL(pszCap, OLCZGeometries))
1820
99
        return TRUE;
1821
1822
324
    else
1823
324
        return FALSE;
1824
423
}
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.93M
{
1836
1.93M
    static_cast<OGRGPXLayer *>(pUserData)->startElementLoadSchemaCbk(pszName,
1837
1.93M
                                                                     ppszAttr);
1838
1.93M
}
1839
1840
static void XMLCALL endElementLoadSchemaCbk(void *pUserData,
1841
                                            const char *pszName)
1842
398k
{
1843
398k
    static_cast<OGRGPXLayer *>(pUserData)->endElementLoadSchemaCbk(pszName);
1844
398k
}
1845
1846
static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, const char *data,
1847
                                             int nLen)
1848
1.13M
{
1849
1.13M
    static_cast<OGRGPXLayer *>(pUserData)->dataHandlerLoadSchemaCbk(data, nLen);
1850
1.13M
}
1851
1852
/** This function parses the whole file to detect the extensions fields */
1853
void OGRGPXLayer::LoadExtensionsSchema()
1854
1.52k
{
1855
1.52k
    m_oSchemaParser = OGRCreateExpatXMLParser();
1856
1.52k
    XML_SetElementHandler(m_oSchemaParser, ::startElementLoadSchemaCbk,
1857
1.52k
                          ::endElementLoadSchemaCbk);
1858
1.52k
    XML_SetCharacterDataHandler(m_oSchemaParser, ::dataHandlerLoadSchemaCbk);
1859
1.52k
    XML_SetUserData(m_oSchemaParser, this);
1860
1861
1.52k
    m_fpGPX->Seek(0, SEEK_SET);
1862
1863
1.52k
    m_inInterestingElement = false;
1864
1.52k
    m_inExtensions = false;
1865
1.52k
    m_depthLevel = 0;
1866
1.52k
    m_currentFieldDefn = nullptr;
1867
1.52k
    m_osSubElementName.clear();
1868
1.52k
    m_osSubElementValue.clear();
1869
1.52k
    m_nWithoutEventCounter = 0;
1870
1.52k
    m_bStopParsing = false;
1871
1872
1.52k
    std::vector<char> aBuf(PARSER_BUF_SIZE);
1873
1.52k
    int nDone = 0;
1874
1.52k
    do
1875
6.38k
    {
1876
6.38k
        m_nDataHandlerCounter = 0;
1877
6.38k
        unsigned int nLen = static_cast<unsigned int>(
1878
6.38k
            m_fpGPX->Read(aBuf.data(), 1, aBuf.size()));
1879
6.38k
        nDone = (nLen < aBuf.size());
1880
6.38k
        if (XML_Parse(m_oSchemaParser, aBuf.data(), nLen, nDone) ==
1881
6.38k
            XML_STATUS_ERROR)
1882
1.48k
        {
1883
1.48k
            CPLError(
1884
1.48k
                CE_Failure, CPLE_AppDefined,
1885
1.48k
                "XML parsing of GPX file failed : "
1886
1.48k
                "%s at line %d, column %d",
1887
1.48k
                XML_ErrorString(XML_GetErrorCode(m_oSchemaParser)),
1888
1.48k
                static_cast<int>(XML_GetCurrentLineNumber(m_oSchemaParser)),
1889
1.48k
                static_cast<int>(XML_GetCurrentColumnNumber(m_oSchemaParser)));
1890
1.48k
            m_bStopParsing = true;
1891
1.48k
            break;
1892
1.48k
        }
1893
4.90k
        m_nWithoutEventCounter++;
1894
4.90k
    } while (!nDone && !m_bStopParsing && m_nWithoutEventCounter < 10);
1895
1896
1.52k
    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.52k
    XML_ParserFree(m_oSchemaParser);
1904
1.52k
    m_oSchemaParser = nullptr;
1905
1906
1.52k
    m_fpGPX->Seek(0, SEEK_SET);
1907
1.52k
}
1908
1909
/************************************************************************/
1910
/*                     startElementLoadSchemaCbk()                      */
1911
/************************************************************************/
1912
1913
void OGRGPXLayer::startElementLoadSchemaCbk(const char *pszName,
1914
                                            CPL_UNUSED const char **ppszAttr)
1915
1.93M
{
1916
1.93M
    if (m_bStopParsing)
1917
0
        return;
1918
1919
1.93M
    m_nWithoutEventCounter = 0;
1920
1921
1.93M
    if (m_gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0)
1922
11.9k
    {
1923
11.9k
        m_inInterestingElement = true;
1924
11.9k
        m_inExtensions = false;
1925
11.9k
        m_interestingDepthLevel = m_depthLevel;
1926
11.9k
    }
1927
1.91M
    else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
1928
17.5k
    {
1929
17.5k
        m_inInterestingElement = true;
1930
17.5k
        m_inExtensions = false;
1931
17.5k
        m_interestingDepthLevel = m_depthLevel;
1932
17.5k
    }
1933
1.90M
    else if (m_gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
1934
283
    {
1935
283
        m_inInterestingElement = true;
1936
283
        m_inExtensions = false;
1937
283
        m_interestingDepthLevel = m_depthLevel;
1938
283
    }
1939
1.90M
    else if (m_gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0)
1940
9.02k
    {
1941
9.02k
        m_inInterestingElement = true;
1942
9.02k
        m_inExtensions = false;
1943
9.02k
        m_interestingDepthLevel = m_depthLevel;
1944
9.02k
    }
1945
1.89M
    else if (m_gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0)
1946
873
    {
1947
873
        m_inInterestingElement = true;
1948
873
        m_inExtensions = false;
1949
873
        m_interestingDepthLevel = m_depthLevel;
1950
873
    }
1951
1.89M
    else if (m_inInterestingElement)
1952
709k
    {
1953
709k
        if (m_depthLevel == m_interestingDepthLevel + 1 &&
1954
29.9k
            strcmp(pszName, "extensions") == 0)
1955
11.7k
        {
1956
11.7k
            m_inExtensions = true;
1957
11.7k
            m_extensionsDepthLevel = m_depthLevel;
1958
11.7k
        }
1959
698k
        else if (m_inExtensions && m_depthLevel == m_extensionsDepthLevel + 1)
1960
37.6k
        {
1961
37.6k
            m_osSubElementName = pszName;
1962
1963
37.6k
            int iField = 0;  // Used after for.
1964
564k
            for (; iField < m_poFeatureDefn->GetFieldCount(); iField++)
1965
562k
            {
1966
562k
                bool bMatch = false;
1967
562k
                if (iField >= m_nGPXFields)
1968
122k
                {
1969
122k
                    char *pszCompatibleName =
1970
122k
                        OGRGPX_GetOGRCompatibleTagName(pszName);
1971
122k
                    bMatch =
1972
122k
                        strcmp(
1973
122k
                            m_poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
1974
122k
                            pszCompatibleName) == 0;
1975
122k
                    CPLFree(pszCompatibleName);
1976
122k
                }
1977
439k
                else
1978
439k
                {
1979
439k
                    bMatch =
1980
439k
                        strcmp(
1981
439k
                            m_poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
1982
439k
                            pszName) == 0;
1983
439k
                }
1984
1985
562k
                if (bMatch)
1986
35.6k
                {
1987
35.6k
                    m_currentFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
1988
35.6k
                    break;
1989
35.6k
                }
1990
562k
            }
1991
37.6k
            if (iField == m_poFeatureDefn->GetFieldCount())
1992
2.00k
            {
1993
2.00k
                char *pszCompatibleName =
1994
2.00k
                    OGRGPX_GetOGRCompatibleTagName(pszName);
1995
2.00k
                OGRFieldDefn newFieldDefn(pszCompatibleName, OFTInteger);
1996
2.00k
                CPLFree(pszCompatibleName);
1997
1998
2.00k
                m_poFeatureDefn->AddFieldDefn(&newFieldDefn);
1999
2.00k
                m_currentFieldDefn = m_poFeatureDefn->GetFieldDefn(
2000
2.00k
                    m_poFeatureDefn->GetFieldCount() - 1);
2001
2002
2.00k
                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
2.00k
            }
2010
37.6k
        }
2011
709k
    }
2012
2013
1.93M
    m_depthLevel++;
2014
1.93M
}
2015
2016
/************************************************************************/
2017
/*                      endElementLoadSchemaCbk()                       */
2018
/************************************************************************/
2019
2020
static bool OGRGPXIsInt(const char *pszStr)
2021
6.31k
{
2022
6.31k
    while (*pszStr == ' ')
2023
0
        pszStr++;
2024
2025
19.0k
    for (int i = 0; pszStr[i]; i++)
2026
12.7k
    {
2027
12.7k
        if (pszStr[i] == '+' || pszStr[i] == '-')
2028
1
        {
2029
1
            if (i != 0)
2030
0
                return false;
2031
1
        }
2032
12.7k
        else if (!(pszStr[i] >= '0' && pszStr[i] <= '9'))
2033
34
            return false;
2034
12.7k
    }
2035
6.28k
    return true;
2036
6.31k
}
2037
2038
void OGRGPXLayer::endElementLoadSchemaCbk(const char *pszName)
2039
398k
{
2040
398k
    if (m_bStopParsing)
2041
0
        return;
2042
2043
398k
    m_nWithoutEventCounter = 0;
2044
2045
398k
    m_depthLevel--;
2046
2047
398k
    if (m_inInterestingElement)
2048
130k
    {
2049
130k
        if (m_gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0)
2050
11.6k
        {
2051
11.6k
            m_inInterestingElement = false;
2052
11.6k
            m_inExtensions = false;
2053
11.6k
        }
2054
119k
        else if (m_gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
2055
3.50k
        {
2056
3.50k
            m_inInterestingElement = false;
2057
3.50k
            m_inExtensions = false;
2058
3.50k
        }
2059
115k
        else if (m_gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
2060
8
        {
2061
8
            m_inInterestingElement = false;
2062
8
            m_inExtensions = false;
2063
8
        }
2064
115k
        else if (m_gpxGeomType == GPX_TRACK_POINT &&
2065
40.4k
                 strcmp(pszName, "trkpt") == 0)
2066
2.16k
        {
2067
2.16k
            m_inInterestingElement = false;
2068
2.16k
            m_inExtensions = false;
2069
2.16k
        }
2070
113k
        else if (m_gpxGeomType == GPX_ROUTE_POINT &&
2071
1.64k
                 strcmp(pszName, "rtept") == 0)
2072
851
        {
2073
851
            m_inInterestingElement = false;
2074
851
            m_inExtensions = false;
2075
851
        }
2076
112k
        else if (m_depthLevel == m_interestingDepthLevel + 1 &&
2077
8.96k
                 strcmp(pszName, "extensions") == 0)
2078
730
        {
2079
730
            m_inExtensions = false;
2080
730
        }
2081
111k
        else if (m_inExtensions && m_depthLevel == m_extensionsDepthLevel + 1 &&
2082
27.5k
                 !m_osSubElementName.empty() && m_osSubElementName == pszName)
2083
27.5k
        {
2084
27.5k
            if (!m_osSubElementValue.empty() && m_currentFieldDefn)
2085
16.7k
            {
2086
16.7k
                if (m_currentFieldDefn->GetType() == OFTInteger ||
2087
10.0k
                    m_currentFieldDefn->GetType() == OFTReal)
2088
6.90k
                {
2089
6.90k
                    char *pszRemainingStr = nullptr;
2090
6.90k
                    CPLStrtod(m_osSubElementValue.c_str(), &pszRemainingStr);
2091
6.90k
                    if (pszRemainingStr == nullptr || *pszRemainingStr == 0 ||
2092
568
                        *pszRemainingStr == ' ')
2093
6.47k
                    {
2094
6.47k
                        if (m_currentFieldDefn->GetType() == OFTInteger)
2095
6.31k
                        {
2096
6.31k
                            if (!OGRGPXIsInt(m_osSubElementValue.c_str()))
2097
34
                            {
2098
34
                                m_currentFieldDefn->SetType(OFTReal);
2099
34
                            }
2100
6.31k
                        }
2101
6.47k
                    }
2102
434
                    else
2103
434
                    {
2104
434
                        m_currentFieldDefn->SetType(OFTString);
2105
434
                    }
2106
6.90k
                }
2107
16.7k
            }
2108
2109
27.5k
            m_osSubElementName.clear();
2110
27.5k
            m_osSubElementValue.clear();
2111
27.5k
            m_currentFieldDefn = nullptr;
2112
27.5k
        }
2113
130k
    }
2114
398k
}
2115
2116
/************************************************************************/
2117
/*                      dataHandlerLoadSchemaCbk()                      */
2118
/************************************************************************/
2119
2120
void OGRGPXLayer::dataHandlerLoadSchemaCbk(const char *data, int nLen)
2121
1.13M
{
2122
1.13M
    if (m_bStopParsing)
2123
0
        return;
2124
2125
1.13M
    m_nDataHandlerCounter++;
2126
1.13M
    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
1.13M
    m_nWithoutEventCounter = 0;
2136
2137
1.13M
    if (!m_osSubElementName.empty())
2138
153k
    {
2139
153k
        try
2140
153k
        {
2141
153k
            m_osSubElementValue.append(data, nLen);
2142
153k
        }
2143
153k
        catch (const std::bad_alloc &)
2144
153k
        {
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
153k
        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
153k
    }
2160
1.13M
}
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
}