LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpx - ogrgpxlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 835 1083 77.1 %
Date: 2024-11-21 22:18:42 Functions: 27 31 87.1 %

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

Generated by: LCOV version 1.14