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

Generated by: LCOV version 1.14