LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpx - ogrgpxdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 375 412 91.0 %
Date: 2024-05-04 12:52:34 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GPX Translator
       4             :  * Purpose:  Implements OGRGPXDataSource class
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007-2011, 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 <algorithm>
      33             : #include <cstdarg>
      34             : #include <cstdio>
      35             : #include <cstring>
      36             : 
      37             : #include "cpl_conv.h"
      38             : #include "cpl_csv.h"
      39             : #include "cpl_error.h"
      40             : #include "cpl_string.h"
      41             : #include "cpl_vsi.h"
      42             : #ifdef HAVE_EXPAT
      43             : #include "expat.h"
      44             : #endif
      45             : #include "ogr_core.h"
      46             : #include "ogr_expat.h"
      47             : #include "ogr_spatialref.h"
      48             : #include "ogrsf_frmts.h"
      49             : #include "ogr_p.h"
      50             : 
      51             : constexpr int SPACE_FOR_METADATA_BOUNDS = 160;
      52             : 
      53             : /************************************************************************/
      54             : /*                         ~OGRGPXDataSource()                          */
      55             : /************************************************************************/
      56             : 
      57         108 : OGRGPXDataSource::~OGRGPXDataSource()
      58             : 
      59             : {
      60          54 :     if (m_fpOutput != nullptr)
      61             :     {
      62          24 :         if (m_nLastRteId != -1)
      63           1 :             PrintLine("</rte>");
      64          23 :         else if (m_nLastTrkId != -1)
      65             :         {
      66           2 :             PrintLine("  </trkseg>");
      67           2 :             PrintLine("</trk>");
      68             :         }
      69          24 :         PrintLine("</gpx>");
      70          24 :         if (m_bIsBackSeekable)
      71             :         {
      72             :             /* Write the <bounds> element in the reserved space */
      73          24 :             if (m_dfMinLon <= m_dfMaxLon)
      74             :             {
      75             :                 char szBounds[SPACE_FOR_METADATA_BOUNDS + 1];
      76             :                 int nRet =
      77          11 :                     CPLsnprintf(szBounds, SPACE_FOR_METADATA_BOUNDS,
      78             :                                 "<bounds minlat=\"%.15f\" minlon=\"%.15f\""
      79             :                                 " maxlat=\"%.15f\" maxlon=\"%.15f\"/>",
      80             :                                 m_dfMinLat, m_dfMinLon, m_dfMaxLat, m_dfMaxLon);
      81          11 :                 if (nRet < SPACE_FOR_METADATA_BOUNDS)
      82             :                 {
      83          11 :                     m_fpOutput->Seek(m_nOffsetBounds, SEEK_SET);
      84          11 :                     m_fpOutput->Write(szBounds, 1, strlen(szBounds));
      85             :                 }
      86             :             }
      87             :         }
      88             :     }
      89         108 : }
      90             : 
      91             : /************************************************************************/
      92             : /*                           TestCapability()                           */
      93             : /************************************************************************/
      94             : 
      95          32 : int OGRGPXDataSource::TestCapability(const char *pszCap)
      96             : 
      97             : {
      98          32 :     if (EQUAL(pszCap, ODsCCreateLayer))
      99          23 :         return TRUE;
     100           9 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     101           6 :         return FALSE;
     102           3 :     else if (EQUAL(pszCap, ODsCZGeometries))
     103           0 :         return TRUE;
     104             : 
     105           3 :     return FALSE;
     106             : }
     107             : 
     108             : /************************************************************************/
     109             : /*                              GetLayer()                              */
     110             : /************************************************************************/
     111             : 
     112         149 : OGRLayer *OGRGPXDataSource::GetLayer(int iLayer)
     113             : 
     114             : {
     115         149 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
     116           0 :         return nullptr;
     117             : 
     118         149 :     return m_apoLayers[iLayer].get();
     119             : }
     120             : 
     121             : /************************************************************************/
     122             : /*                           ICreateLayer()                             */
     123             : /************************************************************************/
     124             : 
     125             : OGRLayer *
     126          30 : OGRGPXDataSource::ICreateLayer(const char *pszLayerName,
     127             :                                const OGRGeomFieldDefn *poGeomFieldDefn,
     128             :                                CSLConstList papszOptions)
     129             : {
     130             :     GPXGeometryType gpxGeomType;
     131          30 :     const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     132          30 :     if (eType == wkbPoint || eType == wkbPoint25D)
     133             :     {
     134          10 :         if (EQUAL(pszLayerName, "track_points"))
     135           2 :             gpxGeomType = GPX_TRACK_POINT;
     136           8 :         else if (EQUAL(pszLayerName, "route_points"))
     137           2 :             gpxGeomType = GPX_ROUTE_POINT;
     138             :         else
     139           6 :             gpxGeomType = GPX_WPT;
     140             :     }
     141          20 :     else if (eType == wkbLineString || eType == wkbLineString25D)
     142             :     {
     143             :         const char *pszForceGPXTrack =
     144           5 :             CSLFetchNameValue(papszOptions, "FORCE_GPX_TRACK");
     145           5 :         if (pszForceGPXTrack && CPLTestBool(pszForceGPXTrack))
     146           0 :             gpxGeomType = GPX_TRACK;
     147             :         else
     148           5 :             gpxGeomType = GPX_ROUTE;
     149             :     }
     150          15 :     else if (eType == wkbMultiLineString || eType == wkbMultiLineString25D)
     151             :     {
     152             :         const char *pszForceGPXRoute =
     153           5 :             CSLFetchNameValue(papszOptions, "FORCE_GPX_ROUTE");
     154           5 :         if (pszForceGPXRoute && CPLTestBool(pszForceGPXRoute))
     155           0 :             gpxGeomType = GPX_ROUTE;
     156             :         else
     157           5 :             gpxGeomType = GPX_TRACK;
     158             :     }
     159          10 :     else if (eType == wkbUnknown)
     160             :     {
     161           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     162             :                  "Cannot create GPX layer %s with unknown geometry type",
     163             :                  pszLayerName);
     164           1 :         return nullptr;
     165             :     }
     166             :     else
     167             :     {
     168           9 :         CPLError(CE_Failure, CPLE_NotSupported,
     169             :                  "Geometry type of `%s' not supported in GPX.\n",
     170             :                  OGRGeometryTypeToName(eType));
     171           9 :         return nullptr;
     172             :     }
     173          20 :     m_apoLayers.emplace_back(std::make_unique<OGRGPXLayer>(
     174          20 :         GetDescription(), pszLayerName, gpxGeomType, this, true, nullptr));
     175             : 
     176          20 :     return m_apoLayers.back().get();
     177             : }
     178             : 
     179             : #ifdef HAVE_EXPAT
     180             : 
     181             : /************************************************************************/
     182             : /*                startElementValidateCbk()                             */
     183             : /************************************************************************/
     184             : 
     185        1255 : void OGRGPXDataSource::startElementValidateCbk(const char *pszNameIn,
     186             :                                                const char **ppszAttr)
     187             : {
     188        1255 :     if (m_validity == GPX_VALIDITY_UNKNOWN)
     189             :     {
     190          29 :         if (strcmp(pszNameIn, "gpx") == 0)
     191             :         {
     192          29 :             m_validity = GPX_VALIDITY_VALID;
     193         168 :             for (int i = 0; ppszAttr[i] != nullptr; i += 2)
     194             :             {
     195         139 :                 if (strcmp(ppszAttr[i], "version") == 0)
     196             :                 {
     197          29 :                     m_osVersion = ppszAttr[i + 1];
     198             :                 }
     199         110 :                 else if (strcmp(ppszAttr[i], "xmlns:ogr") == 0)
     200             :                 {
     201           1 :                     m_bUseExtensions = true;
     202             :                 }
     203             :             }
     204             :         }
     205             :         else
     206             :         {
     207           0 :             m_validity = GPX_VALIDITY_INVALID;
     208             :         }
     209             :     }
     210        1226 :     else if (m_validity == GPX_VALIDITY_VALID)
     211             :     {
     212        1226 :         if (m_nDepth == 1 && strcmp(pszNameIn, "metadata") == 0)
     213             :         {
     214          29 :             m_bInMetadata = true;
     215             :         }
     216        1197 :         else if (m_nDepth == 2 && m_bInMetadata)
     217             :         {
     218         159 :             if (strcmp(pszNameIn, "name") == 0)
     219             :             {
     220          17 :                 m_osMetadataKey = "NAME";
     221             :             }
     222         142 :             else if (strcmp(pszNameIn, "desc") == 0)
     223             :             {
     224          17 :                 m_osMetadataKey = "DESCRIPTION";
     225             :             }
     226         125 :             else if (strcmp(pszNameIn, "time") == 0)
     227             :             {
     228          19 :                 m_osMetadataKey = "TIME";
     229             :             }
     230         106 :             else if (strcmp(pszNameIn, "author") == 0)
     231             :             {
     232          14 :                 m_bInMetadataAuthor = true;
     233             :             }
     234          92 :             else if (strcmp(pszNameIn, "keywords") == 0)
     235             :             {
     236          17 :                 m_osMetadataKey = "KEYWORDS";
     237             :             }
     238          75 :             else if (strcmp(pszNameIn, "copyright") == 0)
     239             :             {
     240          14 :                 std::string osAuthor;
     241          28 :                 for (int i = 0; ppszAttr[i] != nullptr; i += 2)
     242             :                 {
     243          14 :                     if (strcmp(ppszAttr[i], "author") == 0)
     244             :                     {
     245          14 :                         osAuthor = ppszAttr[i + 1];
     246             :                     }
     247             :                 }
     248          14 :                 if (!osAuthor.empty())
     249             :                 {
     250          14 :                     SetMetadataItem("COPYRIGHT_AUTHOR", osAuthor.c_str());
     251             :                 }
     252          14 :                 m_bInMetadataCopyright = true;
     253             :             }
     254          61 :             else if (strcmp(pszNameIn, "link") == 0)
     255             :             {
     256          34 :                 ++m_nMetadataLinkCounter;
     257          34 :                 std::string osHref;
     258          68 :                 for (int i = 0; ppszAttr[i] != nullptr; i += 2)
     259             :                 {
     260          34 :                     if (strcmp(ppszAttr[i], "href") == 0)
     261             :                     {
     262          34 :                         osHref = ppszAttr[i + 1];
     263             :                     }
     264             :                 }
     265          34 :                 if (!osHref.empty())
     266             :                 {
     267          34 :                     SetMetadataItem(
     268             :                         CPLSPrintf("LINK_%d_HREF", m_nMetadataLinkCounter),
     269          34 :                         osHref.c_str());
     270             :                 }
     271          34 :                 m_bInMetadataLink = true;
     272         159 :             }
     273             :         }
     274        1038 :         else if (m_nDepth == 3 && m_bInMetadataAuthor)
     275             :         {
     276          42 :             if (strcmp(pszNameIn, "name") == 0)
     277             :             {
     278          14 :                 m_osMetadataKey = "AUTHOR_NAME";
     279             :             }
     280          28 :             else if (strcmp(pszNameIn, "email") == 0)
     281             :             {
     282          28 :                 std::string osId, osDomain;
     283          42 :                 for (int i = 0; ppszAttr[i] != nullptr; i += 2)
     284             :                 {
     285          28 :                     if (strcmp(ppszAttr[i], "id") == 0)
     286             :                     {
     287          14 :                         osId = ppszAttr[i + 1];
     288             :                     }
     289          14 :                     else if (strcmp(ppszAttr[i], "domain") == 0)
     290             :                     {
     291          14 :                         osDomain = ppszAttr[i + 1];
     292             :                     }
     293             :                 }
     294          14 :                 if (!osId.empty() && !osDomain.empty())
     295             :                 {
     296          14 :                     SetMetadataItem("AUTHOR_EMAIL",
     297          14 :                                     osId.append("@").append(osDomain).c_str());
     298             :                 }
     299             :             }
     300          14 :             else if (strcmp(pszNameIn, "link") == 0)
     301             :             {
     302          14 :                 std::string osHref;
     303          28 :                 for (int i = 0; ppszAttr[i] != nullptr; i += 2)
     304             :                 {
     305          14 :                     if (strcmp(ppszAttr[i], "href") == 0)
     306             :                     {
     307          14 :                         osHref = ppszAttr[i + 1];
     308             :                     }
     309             :                 }
     310          14 :                 if (!osHref.empty())
     311             :                 {
     312          14 :                     SetMetadataItem("AUTHOR_LINK_HREF", osHref.c_str());
     313             :                 }
     314          14 :                 m_bInMetadataAuthorLink = true;
     315          42 :             }
     316             :         }
     317         996 :         else if (m_nDepth == 3 && m_bInMetadataCopyright)
     318             :         {
     319          28 :             if (strcmp(pszNameIn, "year") == 0)
     320             :             {
     321          14 :                 m_osMetadataKey = "COPYRIGHT_YEAR";
     322             :             }
     323          14 :             else if (strcmp(pszNameIn, "license") == 0)
     324             :             {
     325          14 :                 m_osMetadataKey = "COPYRIGHT_LICENSE";
     326             :             }
     327             :         }
     328         968 :         else if (m_nDepth == 3 && m_bInMetadataLink)
     329             :         {
     330         314 :             if (strcmp(pszNameIn, "text") == 0)
     331             :             {
     332             :                 m_osMetadataKey =
     333          82 :                     CPLSPrintf("LINK_%d_TEXT", m_nMetadataLinkCounter);
     334             :             }
     335         232 :             else if (strcmp(pszNameIn, "type") == 0)
     336             :             {
     337             :                 m_osMetadataKey =
     338          82 :                     CPLSPrintf("LINK_%d_TYPE", m_nMetadataLinkCounter);
     339             :             }
     340             :         }
     341         654 :         else if (m_nDepth == 4 && m_bInMetadataAuthorLink)
     342             :         {
     343          28 :             if (strcmp(pszNameIn, "text") == 0)
     344             :             {
     345          14 :                 m_osMetadataKey = "AUTHOR_LINK_TEXT";
     346             :             }
     347          14 :             else if (strcmp(pszNameIn, "type") == 0)
     348             :             {
     349          14 :                 m_osMetadataKey = "AUTHOR_LINK_TYPE";
     350             :             }
     351             :         }
     352         626 :         else if (m_nDepth == 2 && strcmp(pszNameIn, "extensions") == 0)
     353             :         {
     354          16 :             m_bUseExtensions = true;
     355             :         }
     356             :     }
     357        1255 :     m_nDepth++;
     358        1255 : }
     359             : 
     360             : /************************************************************************/
     361             : /*                    endElementValidateCbk()                           */
     362             : /************************************************************************/
     363             : 
     364        1255 : void OGRGPXDataSource::endElementValidateCbk(const char * /*pszName */)
     365             : {
     366        1255 :     m_nDepth--;
     367        1255 :     if (m_nDepth == 4 && m_bInMetadataAuthorLink)
     368             :     {
     369          28 :         if (!m_osMetadataKey.empty())
     370             :         {
     371          28 :             SetMetadataItem(m_osMetadataKey.c_str(), m_osMetadataValue.c_str());
     372             :         }
     373          28 :         m_osMetadataKey.clear();
     374          28 :         m_osMetadataValue.clear();
     375             :     }
     376        1227 :     else if (m_nDepth == 3 && (m_bInMetadataAuthor || m_bInMetadataCopyright ||
     377         340 :                                m_bInMetadataLink))
     378             :     {
     379         384 :         if (!m_osMetadataKey.empty())
     380             :         {
     381         206 :             SetMetadataItem(m_osMetadataKey.c_str(), m_osMetadataValue.c_str());
     382             :         }
     383         384 :         m_osMetadataKey.clear();
     384         384 :         m_osMetadataValue.clear();
     385         384 :         m_bInMetadataAuthorLink = false;
     386             :     }
     387         843 :     else if (m_nDepth == 2 && m_bInMetadata)
     388             :     {
     389         159 :         if (!m_osMetadataKey.empty())
     390             :         {
     391          70 :             SetMetadataItem(m_osMetadataKey.c_str(), m_osMetadataValue.c_str());
     392             :         }
     393         159 :         m_osMetadataKey.clear();
     394         159 :         m_osMetadataValue.clear();
     395         159 :         m_bInMetadataAuthor = false;
     396         159 :         m_bInMetadataCopyright = false;
     397             :     }
     398         684 :     else if (m_nDepth == 1 && m_bInMetadata)
     399             :     {
     400          29 :         m_bInMetadata = false;
     401             :     }
     402        1255 : }
     403             : 
     404             : /************************************************************************/
     405             : /*                      dataHandlerValidateCbk()                        */
     406             : /************************************************************************/
     407             : 
     408        3301 : void OGRGPXDataSource::dataHandlerValidateCbk(const char *data, int nLen)
     409             : {
     410        3301 :     if (!m_osMetadataKey.empty())
     411             :     {
     412         304 :         m_osMetadataValue.append(data, nLen);
     413             :     }
     414             : 
     415        3301 :     m_nDataHandlerCounter++;
     416        3301 :     if (m_nDataHandlerCounter >= PARSER_BUF_SIZE)
     417             :     {
     418           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     419             :                  "File probably corrupted (million laugh pattern)");
     420           0 :         XML_StopParser(m_oCurrentParser, XML_FALSE);
     421             :     }
     422        3301 : }
     423             : 
     424        1255 : static void XMLCALL startElementValidateCbk(void *pUserData,
     425             :                                             const char *pszName,
     426             :                                             const char **ppszAttr)
     427             : {
     428        1255 :     OGRGPXDataSource *poDS = static_cast<OGRGPXDataSource *>(pUserData);
     429        1255 :     poDS->startElementValidateCbk(pszName, ppszAttr);
     430        1255 : }
     431             : 
     432        1255 : static void XMLCALL endElementValidateCbk(void *pUserData, const char *pszName)
     433             : {
     434        1255 :     OGRGPXDataSource *poDS = static_cast<OGRGPXDataSource *>(pUserData);
     435        1255 :     poDS->endElementValidateCbk(pszName);
     436        1255 : }
     437             : 
     438        3301 : static void XMLCALL dataHandlerValidateCbk(void *pUserData, const char *data,
     439             :                                            int nLen)
     440             : {
     441        3301 :     OGRGPXDataSource *poDS = static_cast<OGRGPXDataSource *>(pUserData);
     442        3301 :     poDS->dataHandlerValidateCbk(data, nLen);
     443        3301 : }
     444             : #endif
     445             : 
     446             : /************************************************************************/
     447             : /*                                Open()                                */
     448             : /************************************************************************/
     449             : 
     450          29 : int OGRGPXDataSource::Open(GDALOpenInfo *poOpenInfo)
     451             : 
     452             : {
     453          29 :     const char *pszFilename = poOpenInfo->pszFilename;
     454          29 :     if (poOpenInfo->eAccess == GA_Update)
     455             :     {
     456           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     457             :                  "OGR/GPX driver does not support opening a file in "
     458             :                  "update mode");
     459           0 :         return FALSE;
     460             :     }
     461             : #ifdef HAVE_EXPAT
     462          29 :     SetDescription(pszFilename);
     463             : 
     464             :     /* -------------------------------------------------------------------- */
     465             :     /*      Try to open the file.                                           */
     466             :     /* -------------------------------------------------------------------- */
     467          29 :     VSILFILE *fp = VSIFOpenL(pszFilename, "r");
     468          29 :     if (fp == nullptr)
     469           0 :         return FALSE;
     470             : 
     471          29 :     m_validity = GPX_VALIDITY_UNKNOWN;
     472             : 
     473          29 :     XML_Parser oParser = OGRCreateExpatXMLParser();
     474          29 :     m_oCurrentParser = oParser;
     475          29 :     XML_SetUserData(oParser, this);
     476          29 :     XML_SetElementHandler(oParser, ::startElementValidateCbk,
     477             :                           ::endElementValidateCbk);
     478          29 :     XML_SetCharacterDataHandler(oParser, ::dataHandlerValidateCbk);
     479             : 
     480          29 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
     481          29 :     int nDone = 0;
     482          29 :     unsigned int nLen = 0;
     483          29 :     int nCount = 0;
     484             : 
     485             :     /* Begin to parse the file and look for the <gpx> element */
     486             :     /* It *MUST* be the first element of an XML file */
     487             :     /* So once we have read the first element, we know if we can */
     488             :     /* handle the file or not with that driver */
     489          29 :     uint64_t nTotalBytesRead = 0;
     490           0 :     do
     491             :     {
     492          29 :         m_nDataHandlerCounter = 0;
     493          29 :         nLen = static_cast<unsigned int>(
     494          29 :             VSIFReadL(aBuf.data(), 1, aBuf.size(), fp));
     495          29 :         nTotalBytesRead += nLen;
     496          29 :         nDone = VSIFEofL(fp);
     497          29 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
     498             :         {
     499           0 :             if (nLen <= PARSER_BUF_SIZE - 1)
     500           0 :                 aBuf[nLen] = 0;
     501             :             else
     502           0 :                 aBuf[PARSER_BUF_SIZE - 1] = 0;
     503           0 :             if (strstr(aBuf.data(), "<?xml") && strstr(aBuf.data(), "<gpx"))
     504             :             {
     505           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     506             :                          "XML parsing of GPX file failed : %s at line %d, "
     507             :                          "column %d",
     508             :                          XML_ErrorString(XML_GetErrorCode(oParser)),
     509           0 :                          static_cast<int>(XML_GetCurrentLineNumber(oParser)),
     510           0 :                          static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
     511             :             }
     512           0 :             m_validity = GPX_VALIDITY_INVALID;
     513           0 :             break;
     514             :         }
     515          29 :         if (m_validity == GPX_VALIDITY_INVALID)
     516             :         {
     517           0 :             break;
     518             :         }
     519          29 :         else if (m_validity == GPX_VALIDITY_VALID)
     520             :         {
     521             :             /* If we have recognized the <gpx> element, now we try */
     522             :             /* to recognize if they are <extensions> tags */
     523             :             /* But we stop to look for after an arbitrary amount of bytes */
     524          29 :             if (m_bUseExtensions)
     525          15 :                 break;
     526          14 :             else if (nTotalBytesRead > 1024 * 1024)
     527           0 :                 break;
     528             :         }
     529             :         else
     530             :         {
     531             :             // After reading 50 * PARSER_BUF_SIZE bytes, and not finding whether the
     532             :             // file is GPX or not, we give up and fail silently.
     533           0 :             nCount++;
     534           0 :             if (nCount == 50)
     535           0 :                 break;
     536             :         }
     537          14 :     } while (!nDone && nLen > 0);
     538             : 
     539          29 :     XML_ParserFree(oParser);
     540             : 
     541          29 :     VSIFCloseL(fp);
     542             : 
     543          29 :     if (m_validity == GPX_VALIDITY_VALID)
     544             :     {
     545          29 :         CPLDebug("GPX", "%s seems to be a GPX file.", pszFilename);
     546          29 :         if (m_bUseExtensions)
     547          15 :             CPLDebug("GPX", "It uses <extensions>");
     548             : 
     549          29 :         if (m_osVersion.empty())
     550             :         {
     551             :             /* Default to 1.1 */
     552           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     553             :                      "GPX schema version is unknown. "
     554             :                      "The driver may not be able to handle the file correctly "
     555             :                      "and will behave as if it is GPX 1.1.");
     556           0 :             m_osVersion = "1.1";
     557             :         }
     558          29 :         else if (m_osVersion == "1.0" || m_osVersion == "1.1")
     559             :         {
     560             :             /* Fine */
     561             :         }
     562             :         else
     563             :         {
     564           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     565             :                      "GPX schema version '%s' is not handled by the driver. "
     566             :                      "The driver may not be able to handle the file correctly "
     567             :                      "and will behave as if it is GPX 1.1.",
     568             :                      m_osVersion.c_str());
     569             :         }
     570             : 
     571          29 :         m_apoLayers.emplace_back(std::make_unique<OGRGPXLayer>(
     572          29 :             GetDescription(), "waypoints", GPX_WPT, this, false,
     573          58 :             poOpenInfo->papszOpenOptions));
     574          29 :         m_apoLayers.emplace_back(std::make_unique<OGRGPXLayer>(
     575          29 :             GetDescription(), "routes", GPX_ROUTE, this, false,
     576          58 :             poOpenInfo->papszOpenOptions));
     577          29 :         m_apoLayers.emplace_back(std::make_unique<OGRGPXLayer>(
     578          29 :             GetDescription(), "tracks", GPX_TRACK, this, false,
     579          58 :             poOpenInfo->papszOpenOptions));
     580          29 :         m_apoLayers.emplace_back(std::make_unique<OGRGPXLayer>(
     581          29 :             GetDescription(), "route_points", GPX_ROUTE_POINT, this, false,
     582          58 :             poOpenInfo->papszOpenOptions));
     583          29 :         m_apoLayers.emplace_back(std::make_unique<OGRGPXLayer>(
     584          58 :             GetDescription(), "track_points", GPX_TRACK_POINT, this, false,
     585          58 :             poOpenInfo->papszOpenOptions));
     586             :     }
     587             : 
     588          29 :     return m_validity == GPX_VALIDITY_VALID;
     589             : #else
     590             :     VSILFILE *fp = VSIFOpenL(pszFilename, "r");
     591             :     if (fp)
     592             :     {
     593             :         char aBuf[256];
     594             :         unsigned int nLen =
     595             :             static_cast<unsigned int>(VSIFReadL(aBuf, 1, 255, fp));
     596             :         aBuf[nLen] = 0;
     597             :         if (strstr(aBuf, "<?xml") && strstr(aBuf, "<gpx"))
     598             :         {
     599             :             CPLError(CE_Failure, CPLE_NotSupported,
     600             :                      "OGR/GPX driver has not been built with read support. "
     601             :                      "Expat library required");
     602             :         }
     603             :         VSIFCloseL(fp);
     604             :     }
     605             :     return FALSE;
     606             : #endif
     607             : }
     608             : 
     609             : /************************************************************************/
     610             : /*                               Create()                               */
     611             : /************************************************************************/
     612             : 
     613          25 : int OGRGPXDataSource::Create(const char *pszFilename, char **papszOptions)
     614             : {
     615          25 :     if (strcmp(pszFilename, "/dev/stdout") == 0)
     616           0 :         pszFilename = "/vsistdout/";
     617             : 
     618             :     /* -------------------------------------------------------------------- */
     619             :     /*     Do not overwrite exiting file.                                   */
     620             :     /* -------------------------------------------------------------------- */
     621             :     VSIStatBufL sStatBuf;
     622             : 
     623          25 :     if (VSIStatL(pszFilename, &sStatBuf) == 0)
     624             :     {
     625           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     626             :                  "You have to delete %s before being able to create it with "
     627             :                  "the GPX driver",
     628             :                  pszFilename);
     629           0 :         return FALSE;
     630             :     }
     631             : 
     632             :     /* -------------------------------------------------------------------- */
     633             :     /*      Create the output file.                                         */
     634             :     /* -------------------------------------------------------------------- */
     635             : 
     636          25 :     SetDescription(pszFilename);
     637             : 
     638          25 :     if (strcmp(pszFilename, "/vsistdout/") == 0)
     639             :     {
     640           0 :         m_bIsBackSeekable = false;
     641           0 :         m_fpOutput.reset(VSIFOpenL(pszFilename, "w"));
     642             :     }
     643             :     else
     644          25 :         m_fpOutput.reset(VSIFOpenL(pszFilename, "w+"));
     645          25 :     if (m_fpOutput == nullptr)
     646             :     {
     647           1 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create GPX file %s.",
     648             :                  pszFilename);
     649           1 :         return FALSE;
     650             :     }
     651             : 
     652             :     /* -------------------------------------------------------------------- */
     653             :     /*      End of line character.                                          */
     654             :     /* -------------------------------------------------------------------- */
     655          24 :     const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
     656             : 
     657          24 :     bool bUseCRLF =
     658             : #ifdef _WIN32
     659             :         true
     660             : #else
     661             :         false
     662             : #endif
     663             :         ;
     664          24 :     if (pszCRLFFormat == nullptr)
     665             :     {
     666             :         // Use default value for OS.
     667             :     }
     668           1 :     else if (EQUAL(pszCRLFFormat, "CRLF"))
     669           0 :         bUseCRLF = true;
     670           1 :     else if (EQUAL(pszCRLFFormat, "LF"))
     671           1 :         bUseCRLF = false;
     672             :     else
     673             :     {
     674           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     675             :                  "LINEFORMAT=%s not understood, use one of CRLF or LF.",
     676             :                  pszCRLFFormat);
     677             :         // Use default value for OS.
     678             :     }
     679          24 :     m_pszEOL = (bUseCRLF) ? "\r\n" : "\n";
     680             : 
     681             :     /* -------------------------------------------------------------------- */
     682             :     /*      Look at use extensions options.                                 */
     683             :     /* -------------------------------------------------------------------- */
     684             :     const char *pszUseExtensions =
     685          24 :         CSLFetchNameValue(papszOptions, "GPX_USE_EXTENSIONS");
     686          24 :     const char *pszExtensionsNSURL = nullptr;
     687          24 :     if (pszUseExtensions && CPLTestBool(pszUseExtensions))
     688             :     {
     689           1 :         m_bUseExtensions = true;
     690             : 
     691             :         const char *pszExtensionsNSOption =
     692           1 :             CSLFetchNameValue(papszOptions, "GPX_EXTENSIONS_NS");
     693             :         const char *pszExtensionsNSURLOption =
     694           1 :             CSLFetchNameValue(papszOptions, "GPX_EXTENSIONS_NS_URL");
     695           1 :         if (pszExtensionsNSOption && pszExtensionsNSURLOption)
     696             :         {
     697           0 :             m_osExtensionsNS = pszExtensionsNSOption;
     698           0 :             pszExtensionsNSURL = pszExtensionsNSURLOption;
     699             :         }
     700             :         else
     701             :         {
     702           1 :             m_osExtensionsNS = "ogr";
     703           1 :             pszExtensionsNSURL = "http://osgeo.org/gdal";
     704             :         }
     705             :     }
     706             : 
     707             :     /* -------------------------------------------------------------------- */
     708             :     /*     Output header of GPX file.                                       */
     709             :     /* -------------------------------------------------------------------- */
     710          24 :     PrintLine("<?xml version=\"1.0\"?>");
     711          24 :     m_fpOutput->Printf("<gpx version=\"1.1\" creator=\"");
     712          24 :     const char *pszCreator = CSLFetchNameValue(papszOptions, "CREATOR");
     713          24 :     if (pszCreator)
     714             :     {
     715           1 :         char *pszXML = OGRGetXML_UTF8_EscapedString(pszCreator);
     716           1 :         m_fpOutput->Printf("%s", pszXML);
     717           1 :         CPLFree(pszXML);
     718             :     }
     719             :     else
     720             :     {
     721          23 :         m_fpOutput->Printf("GDAL %s", GDALVersionInfo("RELEASE_NAME"));
     722             :     }
     723          24 :     m_fpOutput->Printf(
     724             :         "\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
     725          24 :     if (m_bUseExtensions)
     726           1 :         m_fpOutput->Printf("xmlns:%s=\"%s\" ", m_osExtensionsNS.c_str(),
     727             :                            pszExtensionsNSURL);
     728          24 :     m_fpOutput->Printf("xmlns=\"http://www.topografix.com/GPX/1/1\" ");
     729          24 :     PrintLine("xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 "
     730             :               "http://www.topografix.com/GPX/1/1/gpx.xsd\">");
     731          24 :     PrintLine("<metadata>");
     732             :     /*
     733             :     Something like:
     734             :         <metadata>
     735             :             <name>metadata name</name>
     736             :             <desc>metadata desc</desc>
     737             :             <author>
     738             :                 <name>metadata author name</name>
     739             :                 <email id="foo" domain="example.com"/>
     740             :                 <link href="author_href"><text>author_text</text><type>author_type</type></link>
     741             :             </author>
     742             :             <copyright author="copyright author"><year>2023</year><license>my license</license></copyright>
     743             :             <link href="href"><text>text</text><type>type</type></link>
     744             :             <link href="href2"><text>text2</text><type>type2</type></link>
     745             :             <time>2007-11-25T17:58:00+01:00</time>
     746             :             <keywords>kw</keywords>
     747             :             <bounds minlat="-90" minlon="-180" maxlat="90" maxlon="179.9999999"/>
     748             :         </metadata>
     749             :     */
     750             : 
     751          24 :     if (const char *pszMetadataName =
     752          24 :             CSLFetchNameValue(papszOptions, "METADATA_NAME"))
     753             :     {
     754           1 :         char *pszXML = OGRGetXML_UTF8_EscapedString(pszMetadataName);
     755           1 :         PrintLine("  <name>%s</name>", pszXML);
     756           1 :         CPLFree(pszXML);
     757             :     }
     758             : 
     759          24 :     if (const char *pszMetadataDesc =
     760          24 :             CSLFetchNameValue(papszOptions, "METADATA_DESCRIPTION"))
     761             :     {
     762           1 :         char *pszXML = OGRGetXML_UTF8_EscapedString(pszMetadataDesc);
     763           1 :         PrintLine("  <desc>%s</desc>", pszXML);
     764           1 :         CPLFree(pszXML);
     765             :     }
     766             : 
     767             :     const char *pszMetadataAuthorName =
     768          24 :         CSLFetchNameValue(papszOptions, "METADATA_AUTHOR_NAME");
     769             :     const char *pszMetadataAuthorEmail =
     770          24 :         CSLFetchNameValue(papszOptions, "METADATA_AUTHOR_EMAIL");
     771             :     const char *pszMetadataAuthorLinkHref =
     772          24 :         CSLFetchNameValue(papszOptions, "METADATA_AUTHOR_LINK_HREF");
     773          24 :     if (pszMetadataAuthorName || pszMetadataAuthorEmail ||
     774             :         pszMetadataAuthorLinkHref)
     775             :     {
     776           1 :         PrintLine("  <author>");
     777           1 :         if (pszMetadataAuthorName)
     778             :         {
     779           1 :             char *pszXML = OGRGetXML_UTF8_EscapedString(pszMetadataAuthorName);
     780           1 :             PrintLine("    <name>%s</name>", pszXML);
     781           1 :             CPLFree(pszXML);
     782             :         }
     783           1 :         if (pszMetadataAuthorEmail)
     784             :         {
     785           2 :             std::string osEmail = pszMetadataAuthorEmail;
     786           1 :             auto nPos = osEmail.find('@');
     787           1 :             if (nPos != std::string::npos)
     788             :             {
     789           1 :                 char *pszId = OGRGetXML_UTF8_EscapedString(
     790           2 :                     osEmail.substr(0, nPos).c_str());
     791           1 :                 char *pszDomain = OGRGetXML_UTF8_EscapedString(
     792           2 :                     osEmail.substr(nPos + 1).c_str());
     793           1 :                 PrintLine("    <email id=\"%s\" domain=\"%s\"/>", pszId,
     794             :                           pszDomain);
     795           1 :                 CPLFree(pszId);
     796           1 :                 CPLFree(pszDomain);
     797             :             }
     798             :         }
     799           1 :         if (pszMetadataAuthorLinkHref)
     800             :         {
     801             :             {
     802             :                 char *pszXML =
     803           1 :                     OGRGetXML_UTF8_EscapedString(pszMetadataAuthorLinkHref);
     804           1 :                 PrintLine("    <link href=\"%s\">", pszXML);
     805           1 :                 CPLFree(pszXML);
     806             :             }
     807           1 :             if (const char *pszMetadataAuthorLinkText = CSLFetchNameValue(
     808             :                     papszOptions, "METADATA_AUTHOR_LINK_TEXT"))
     809             :             {
     810             :                 char *pszXML =
     811           1 :                     OGRGetXML_UTF8_EscapedString(pszMetadataAuthorLinkText);
     812           1 :                 PrintLine("      <text>%s</text>", pszXML);
     813           1 :                 CPLFree(pszXML);
     814             :             }
     815           1 :             if (const char *pszMetadataAuthorLinkType = CSLFetchNameValue(
     816             :                     papszOptions, "METADATA_AUTHOR_LINK_TYPE"))
     817             :             {
     818             :                 char *pszXML =
     819           1 :                     OGRGetXML_UTF8_EscapedString(pszMetadataAuthorLinkType);
     820           1 :                 PrintLine("      <type>%s</type>", pszXML);
     821           1 :                 CPLFree(pszXML);
     822             :             }
     823           1 :             PrintLine("    </link>");
     824             :         }
     825           1 :         PrintLine("  </author>");
     826             :     }
     827             : 
     828          24 :     if (const char *pszMetadataCopyrightAuthor =
     829          24 :             CSLFetchNameValue(papszOptions, "METADATA_COPYRIGHT_AUTHOR"))
     830             :     {
     831             :         {
     832             :             char *pszXML =
     833           1 :                 OGRGetXML_UTF8_EscapedString(pszMetadataCopyrightAuthor);
     834           1 :             PrintLine("  <copyright author=\"%s\">", pszXML);
     835           1 :             CPLFree(pszXML);
     836             :         }
     837           1 :         if (const char *pszMetadataCopyrightYear =
     838           1 :                 CSLFetchNameValue(papszOptions, "METADATA_COPYRIGHT_YEAR"))
     839             :         {
     840             :             char *pszXML =
     841           1 :                 OGRGetXML_UTF8_EscapedString(pszMetadataCopyrightYear);
     842           1 :             PrintLine("      <year>%s</year>", pszXML);
     843           1 :             CPLFree(pszXML);
     844             :         }
     845           1 :         if (const char *pszMetadataCopyrightLicense =
     846           1 :                 CSLFetchNameValue(papszOptions, "METADATA_COPYRIGHT_LICENSE"))
     847             :         {
     848             :             char *pszXML =
     849           1 :                 OGRGetXML_UTF8_EscapedString(pszMetadataCopyrightLicense);
     850           1 :             PrintLine("      <license>%s</license>", pszXML);
     851           1 :             CPLFree(pszXML);
     852             :         }
     853           1 :         PrintLine("  </copyright>");
     854             :     }
     855             : 
     856          45 :     for (CSLConstList papszIter = papszOptions; papszIter && *papszIter;
     857             :          ++papszIter)
     858             :     {
     859          21 :         if (STARTS_WITH_CI(*papszIter, "METADATA_LINK_") &&
     860           6 :             strstr(*papszIter, "_HREF"))
     861             :         {
     862           2 :             const int nLinkNum = atoi(*papszIter + strlen("METADATA_LINK_"));
     863           2 :             const char *pszVal = strchr(*papszIter, '=');
     864           2 :             if (pszVal)
     865             :             {
     866             :                 {
     867           2 :                     char *pszXML = OGRGetXML_UTF8_EscapedString(pszVal + 1);
     868           2 :                     PrintLine("  <link href=\"%s\">", pszXML);
     869           2 :                     CPLFree(pszXML);
     870             :                 }
     871           2 :                 if (const char *pszText = CSLFetchNameValue(
     872             :                         papszOptions,
     873             :                         CPLSPrintf("METADATA_LINK_%d_TEXT", nLinkNum)))
     874             :                 {
     875           2 :                     char *pszXML = OGRGetXML_UTF8_EscapedString(pszText);
     876           2 :                     PrintLine("      <text>%s</text>", pszXML);
     877           2 :                     CPLFree(pszXML);
     878             :                 }
     879           2 :                 if (const char *pszType = CSLFetchNameValue(
     880             :                         papszOptions,
     881             :                         CPLSPrintf("METADATA_LINK_%d_TYPE", nLinkNum)))
     882             :                 {
     883           2 :                     char *pszXML = OGRGetXML_UTF8_EscapedString(pszType);
     884           2 :                     PrintLine("      <type>%s</type>", pszXML);
     885           2 :                     CPLFree(pszXML);
     886             :                 }
     887           2 :                 PrintLine("  </link>");
     888             :             }
     889             :         }
     890             :     }
     891             : 
     892          24 :     if (const char *pszMetadataTime =
     893          24 :             CSLFetchNameValue(papszOptions, "METADATA_TIME"))
     894             :     {
     895           1 :         char *pszXML = OGRGetXML_UTF8_EscapedString(pszMetadataTime);
     896           1 :         PrintLine("  <time>%s</time>", pszXML);
     897           1 :         CPLFree(pszXML);
     898             :     }
     899             : 
     900          24 :     if (const char *pszMetadataKeywords =
     901          24 :             CSLFetchNameValue(papszOptions, "METADATA_KEYWORDS"))
     902             :     {
     903           1 :         char *pszXML = OGRGetXML_UTF8_EscapedString(pszMetadataKeywords);
     904           1 :         PrintLine("  <keywords>%s</keywords>", pszXML);
     905           1 :         CPLFree(pszXML);
     906             :     }
     907             : 
     908          24 :     if (m_bIsBackSeekable)
     909             :     {
     910             :         /* Reserve space for <bounds .../> within <metadata> */
     911             :         char szBounds[SPACE_FOR_METADATA_BOUNDS + 1];
     912          24 :         memset(szBounds, ' ', SPACE_FOR_METADATA_BOUNDS);
     913          24 :         szBounds[SPACE_FOR_METADATA_BOUNDS] = '\0';
     914          24 :         m_nOffsetBounds = m_fpOutput->Tell();
     915          24 :         PrintLine("%s", szBounds);
     916             :     }
     917          24 :     PrintLine("</metadata>");
     918             : 
     919          24 :     return TRUE;
     920             : }
     921             : 
     922             : /************************************************************************/
     923             : /*                             AddCoord()                               */
     924             : /************************************************************************/
     925             : 
     926          41 : void OGRGPXDataSource::AddCoord(double dfLon, double dfLat)
     927             : {
     928          41 :     m_dfMinLon = std::min(m_dfMinLon, dfLon);
     929          41 :     m_dfMinLat = std::min(m_dfMinLat, dfLat);
     930          41 :     m_dfMaxLon = std::max(m_dfMaxLon, dfLon);
     931          41 :     m_dfMaxLat = std::max(m_dfMaxLat, dfLat);
     932          41 : }
     933             : 
     934             : /************************************************************************/
     935             : /*                            PrintLine()                               */
     936             : /************************************************************************/
     937             : 
     938         372 : void OGRGPXDataSource::PrintLine(const char *fmt, ...)
     939             : {
     940         744 :     CPLString osWork;
     941             :     va_list args;
     942             : 
     943         372 :     va_start(args, fmt);
     944         372 :     osWork.vPrintf(fmt, args);
     945         372 :     va_end(args);
     946             : 
     947         372 :     m_fpOutput->Write(osWork.c_str(), 1, osWork.size());
     948         372 :     m_fpOutput->Write(m_pszEOL, 1, strlen(m_pszEOL));
     949         372 : }

Generated by: LCOV version 1.14