LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gtfs - ogrgtfsdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 311 326 95.4 %
Date: 2026-03-26 23:25:44 Functions: 24 24 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Implements GTFS driver.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "gdal_priv.h"
      15             : #include "ogrsf_frmts.h"
      16             : 
      17             : #include <cassert>
      18             : #include <map>
      19             : #include <new>
      20             : #include <utility>
      21             : 
      22             : constexpr const char *const apszCSVDriver[] = {"CSV", nullptr};
      23             : 
      24             : /************************************************************************/
      25             : /*                            OGRGTFSDataset                            */
      26             : /************************************************************************/
      27             : 
      28             : class OGRGTFSDataset final : public GDALDataset
      29             : {
      30             :     std::vector<std::unique_ptr<OGRLayer>> m_apoLayers{};
      31             : 
      32             :   public:
      33           9 :     OGRGTFSDataset() = default;
      34             : 
      35         395 :     int GetLayerCount() const override
      36             :     {
      37         395 :         return static_cast<int>(m_apoLayers.size());
      38             :     }
      39             : 
      40             :     const OGRLayer *GetLayer(int nIdx) const override;
      41             : 
      42             :     static int Identify(GDALOpenInfo *poOpenInfo);
      43             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
      44             : };
      45             : 
      46             : /************************************************************************/
      47             : /*                              GetLayer()                              */
      48             : /************************************************************************/
      49             : 
      50         964 : const OGRLayer *OGRGTFSDataset::GetLayer(int nIdx) const
      51             : {
      52         962 :     return nIdx >= 0 && nIdx < static_cast<int>(m_apoLayers.size())
      53        1926 :                ? m_apoLayers[nIdx].get()
      54         964 :                : nullptr;
      55             : }
      56             : 
      57             : /************************************************************************/
      58             : /*                             OGRGTFSLayer                             */
      59             : /************************************************************************/
      60             : 
      61             : class OGRGTFSLayer final : public OGRLayer
      62             : {
      63             :     const std::string m_osDirname;
      64             :     std::unique_ptr<GDALDataset> m_poUnderlyingDS{};
      65             :     OGRLayer *m_poUnderlyingLayer = nullptr;  // owned by m_poUnderlyingDS
      66             :     OGRFeatureDefn *m_poFeatureDefn = nullptr;
      67             :     int m_nTripIdIdx = -1;
      68             :     int m_nLatIdx = -1;
      69             :     int m_nLonIdx = -1;
      70             :     bool m_bIsTrips = false;
      71             :     bool m_bPrepared = false;
      72             :     std::map<std::string, std::pair<double, double>> m_oMapStopIdToLonLat{};
      73             :     std::map<std::string, std::map<int, std::string>> m_oMapTripIdToStopIds{};
      74             : 
      75             :     void PrepareTripsData();
      76             : 
      77             :   public:
      78             :     OGRGTFSLayer(const std::string &osDirname, const char *pszName,
      79             :                  std::unique_ptr<GDALDataset> &&poUnderlyingDS);
      80             :     ~OGRGTFSLayer() override;
      81             : 
      82             :     void ResetReading() override;
      83             :     OGRFeature *GetNextFeature() override;
      84             :     int TestCapability(const char *) const override;
      85             :     GIntBig GetFeatureCount(int bForce) override;
      86             : 
      87        3645 :     const OGRFeatureDefn *GetLayerDefn() const override
      88             :     {
      89        3645 :         return m_poFeatureDefn;
      90             :     }
      91             : };
      92             : 
      93             : /************************************************************************/
      94             : /*                            OGRGTFSLayer()                            */
      95             : /************************************************************************/
      96             : 
      97          64 : OGRGTFSLayer::OGRGTFSLayer(const std::string &osDirname, const char *pszName,
      98          64 :                            std::unique_ptr<GDALDataset> &&poUnderlyingDS)
      99          64 :     : m_osDirname(osDirname), m_poUnderlyingDS(std::move(poUnderlyingDS))
     100             : {
     101          64 :     m_poFeatureDefn = new OGRFeatureDefn(pszName);
     102          64 :     SetDescription(pszName);
     103          64 :     m_poFeatureDefn->SetGeomType(wkbNone);
     104          64 :     m_poFeatureDefn->Reference();
     105             : 
     106          64 :     m_poUnderlyingLayer = m_poUnderlyingDS->GetLayer(0);
     107          64 :     assert(m_poUnderlyingLayer);
     108             : #if defined(__GNUC__)
     109             : #pragma GCC diagnostic push
     110             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     111             : #endif
     112          64 :     auto poSrcLayerDefn = m_poUnderlyingLayer->GetLayerDefn();
     113             : #if defined(__GNUC__)
     114             : #pragma GCC diagnostic pop
     115             : #endif
     116          64 :     const int nFieldCount = poSrcLayerDefn->GetFieldCount();
     117          64 :     m_nTripIdIdx = poSrcLayerDefn->GetFieldIndex("trip_id");
     118          64 :     if (EQUAL(pszName, "stops"))
     119             :     {
     120           8 :         m_nLatIdx = poSrcLayerDefn->GetFieldIndex("stop_lat");
     121           8 :         m_nLonIdx = poSrcLayerDefn->GetFieldIndex("stop_lon");
     122             :     }
     123          56 :     else if (EQUAL(pszName, "shapes"))
     124             :     {
     125           8 :         m_nLatIdx = poSrcLayerDefn->GetFieldIndex("shape_pt_lat");
     126           8 :         m_nLonIdx = poSrcLayerDefn->GetFieldIndex("shape_pt_lon");
     127             :     }
     128          64 :     m_bIsTrips = EQUAL(pszName, "trips") && m_nTripIdIdx >= 0;
     129             : 
     130          64 :     if (m_nLatIdx >= 0 && m_nLonIdx >= 0)
     131          16 :         m_poFeatureDefn->SetGeomType(wkbPoint);
     132          48 :     else if (m_bIsTrips)
     133           8 :         m_poFeatureDefn->SetGeomType(wkbLineString);
     134             : 
     135         592 :     for (int i = 0; i < nFieldCount; ++i)
     136             :     {
     137        1056 :         OGRFieldDefn oFieldDefn(poSrcLayerDefn->GetFieldDefn(i));
     138         528 :         const char *pszFieldName = oFieldDefn.GetNameRef();
     139         528 :         if (i == m_nLatIdx || i == m_nLonIdx ||
     140         496 :             EQUAL(pszFieldName, "shape_dist_traveled"))
     141             :         {
     142          56 :             oFieldDefn.SetType(OFTReal);
     143             :         }
     144         472 :         else if (EQUAL(pszFieldName, "shape_pt_sequence"))
     145             :         {
     146           8 :             oFieldDefn.SetType(OFTInteger);
     147             :         }
     148         464 :         else if (EQUAL(pszFieldName, "date") ||
     149         456 :                  EQUAL(pszFieldName, "start_date") ||
     150         448 :                  EQUAL(pszFieldName, "end_date"))
     151             :         {
     152          24 :             oFieldDefn.SetType(OFTDate);
     153             :         }
     154         440 :         else if (EQUAL(pszFieldName, "arrival_time") ||
     155         424 :                  EQUAL(pszFieldName, "departure_time"))
     156             :         {
     157          32 :             oFieldDefn.SetType(OFTTime);
     158             :         }
     159         408 :         else if (strstr(pszFieldName, "_type") ||
     160         352 :                  EQUAL(pszFieldName, "stop_sequence"))
     161             :         {
     162          72 :             oFieldDefn.SetType(OFTInteger);
     163             :         }
     164         336 :         else if (EQUAL(pszFieldName, "monday") ||
     165         328 :                  EQUAL(pszFieldName, "tuesday") ||
     166         320 :                  EQUAL(pszFieldName, "wednesday") ||
     167         312 :                  EQUAL(pszFieldName, "thursday") ||
     168         304 :                  EQUAL(pszFieldName, "friday") ||
     169         296 :                  EQUAL(pszFieldName, "saturday") ||
     170         288 :                  EQUAL(pszFieldName, "sunday"))
     171             :         {
     172          56 :             oFieldDefn.SetType(OFTInteger);
     173          56 :             oFieldDefn.SetSubType(OFSTBoolean);
     174             :         }
     175         528 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     176             :     }
     177          64 : }
     178             : 
     179             : /************************************************************************/
     180             : /*                           ~OGRGTFSLayer()                            */
     181             : /************************************************************************/
     182             : 
     183         128 : OGRGTFSLayer::~OGRGTFSLayer()
     184             : {
     185          64 :     m_poFeatureDefn->Release();
     186         128 : }
     187             : 
     188             : /************************************************************************/
     189             : /*                            ResetReading()                            */
     190             : /************************************************************************/
     191             : 
     192         618 : void OGRGTFSLayer::ResetReading()
     193             : {
     194         618 :     m_poUnderlyingLayer->ResetReading();
     195         618 : }
     196             : 
     197             : /************************************************************************/
     198             : /*                          PrepareTripsData()                          */
     199             : /************************************************************************/
     200             : 
     201           2 : void OGRGTFSLayer::PrepareTripsData()
     202             : {
     203           2 :     m_bPrepared = true;
     204             :     try
     205             :     {
     206             :         {
     207             :             auto poStopsDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     208           2 :                 std::string(m_osDirname).append("/stops.txt").c_str(),
     209           2 :                 GDAL_OF_VECTOR, apszCSVDriver));
     210           2 :             if (!poStopsDS)
     211           0 :                 return;
     212           2 :             auto poStopsLyr = poStopsDS->GetLayer(0);
     213           2 :             if (!poStopsLyr)
     214           0 :                 return;
     215           2 :             const auto poStopsLyrDefn = poStopsLyr->GetLayerDefn();
     216           2 :             const int nStopIdIdx = poStopsLyrDefn->GetFieldIndex("stop_id");
     217           2 :             const int nStopLatIdx = poStopsLyrDefn->GetFieldIndex("stop_lat");
     218           2 :             const int nStopLonIdx = poStopsLyrDefn->GetFieldIndex("stop_lon");
     219           2 :             if (nStopIdIdx < 0 || nStopLatIdx < 0 || nStopLonIdx < 0)
     220           0 :                 return;
     221          72 :             for (auto &&poFeature : poStopsLyr)
     222             :             {
     223          70 :                 const char *pszStopId = poFeature->GetFieldAsString(nStopIdIdx);
     224          70 :                 if (pszStopId)
     225             :                 {
     226         140 :                     m_oMapStopIdToLonLat[pszStopId] = std::make_pair(
     227          70 :                         poFeature->GetFieldAsDouble(nStopLonIdx),
     228         280 :                         poFeature->GetFieldAsDouble(nStopLatIdx));
     229             :                 }
     230             :             }
     231             :         }
     232             : 
     233             :         auto poStopTimesDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     234           2 :             std::string(m_osDirname).append("/stop_times.txt").c_str(),
     235           2 :             GDAL_OF_VECTOR, apszCSVDriver));
     236           2 :         if (!poStopTimesDS)
     237           0 :             return;
     238           2 :         auto poStopTimesLyr = poStopTimesDS->GetLayer(0);
     239           2 :         if (!poStopTimesLyr)
     240           0 :             return;
     241           2 :         const auto poStopTimesLyrDefn = poStopTimesLyr->GetLayerDefn();
     242           2 :         const int nStopIdIdx = poStopTimesLyrDefn->GetFieldIndex("stop_id");
     243           2 :         const int nTripIdIdx = poStopTimesLyrDefn->GetFieldIndex("trip_id");
     244             :         const int nStopSequenceIdx =
     245           2 :             poStopTimesLyrDefn->GetFieldIndex("stop_sequence");
     246           2 :         if (nStopIdIdx < 0 || nTripIdIdx < 0 || nStopSequenceIdx < 0)
     247           0 :             return;
     248          72 :         for (auto &&poFeature : poStopTimesLyr)
     249             :         {
     250          70 :             const char *pszStopId = poFeature->GetFieldAsString(nStopIdIdx);
     251          70 :             const char *pszTripId = poFeature->GetFieldAsString(nTripIdIdx);
     252             :             const int nStopSequence =
     253          70 :                 poFeature->GetFieldAsInteger(nStopSequenceIdx);
     254          70 :             if (pszStopId && pszTripId)
     255             :             {
     256          70 :                 m_oMapTripIdToStopIds[pszTripId][nStopSequence] = pszStopId;
     257             :             }
     258             :         }
     259             :     }
     260           0 :     catch (const std::bad_alloc &)
     261             :     {
     262           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Not enough memory");
     263             :     }
     264             : }
     265             : 
     266             : /************************************************************************/
     267             : /*                           GetNextFeature()                           */
     268             : /************************************************************************/
     269             : 
     270       15136 : OGRFeature *OGRGTFSLayer::GetNextFeature()
     271             : {
     272       15136 :     if (m_bIsTrips && !m_bPrepared)
     273           2 :         PrepareTripsData();
     274             : 
     275             :     while (true)
     276             :     {
     277             :         auto poSrcFeature =
     278       23008 :             std::unique_ptr<OGRFeature>(m_poUnderlyingLayer->GetNextFeature());
     279       23008 :         if (poSrcFeature == nullptr)
     280         208 :             return nullptr;
     281             : 
     282       22800 :         auto poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
     283       22800 :         const int nFieldCount = poSrcFeature->GetFieldCount();
     284       22800 :         poFeature->SetFID(poSrcFeature->GetFID());
     285       22800 :         auto poSrcLayerDefn = m_poUnderlyingLayer->GetLayerDefn();
     286      157246 :         for (int i = 0; i < nFieldCount; ++i)
     287             :         {
     288      134446 :             const auto eType = m_poFeatureDefn->GetFieldDefn(i)->GetType();
     289      134446 :             if (poSrcLayerDefn->GetFieldDefn(i)->GetType() == eType)
     290             :             {
     291       38519 :                 poFeature->SetField(i, poSrcFeature->GetRawFieldRef(i));
     292             :             }
     293       95927 :             else if (eType == OFTDate)
     294             :             {
     295          86 :                 const char *pszVal = poSrcFeature->GetFieldAsString(i);
     296          86 :                 constexpr char ZERO_DIGIT = '0';
     297          86 :                 if (pszVal && strlen(pszVal) == 8)
     298             :                 {
     299          86 :                     const int nYear = (pszVal[0] - ZERO_DIGIT) * 1000 +
     300          86 :                                       (pszVal[1] - ZERO_DIGIT) * 100 +
     301          86 :                                       (pszVal[2] - ZERO_DIGIT) * 10 +
     302          86 :                                       (pszVal[3] - ZERO_DIGIT);
     303          86 :                     const int nMonth = (pszVal[4] - ZERO_DIGIT) * 10 +
     304          86 :                                        (pszVal[5] - ZERO_DIGIT);
     305          86 :                     const int nDay = (pszVal[6] - ZERO_DIGIT) * 10 +
     306          86 :                                      (pszVal[7] - ZERO_DIGIT);
     307          86 :                     poFeature->SetField(i, nYear, nMonth, nDay, 0, 0, 0, 0);
     308             :                 }
     309             :             }
     310       95841 :             else if (eType == OFTInteger)
     311             :             {
     312       27545 :                 poFeature->SetField(i, poSrcFeature->GetFieldAsInteger(i));
     313             :             }
     314             :             else
     315             :             {
     316       68296 :                 const char *pszVal = poSrcFeature->GetFieldAsString(i);
     317       68296 :                 poFeature->SetField(i, pszVal);
     318             :             }
     319             :         }
     320       22800 :         if (m_nLatIdx >= 0 && m_nLonIdx >= 0)
     321             :         {
     322       42760 :             poFeature->SetGeometryDirectly(
     323       21380 :                 new OGRPoint(poFeature->GetFieldAsDouble(m_nLonIdx),
     324       21380 :                              poFeature->GetFieldAsDouble(m_nLatIdx)));
     325             :         }
     326        1420 :         else if (m_bIsTrips)
     327             :         {
     328          45 :             const char *pszTripId = poFeature->GetFieldAsString(m_nTripIdIdx);
     329          45 :             if (pszTripId)
     330             :             {
     331          45 :                 const auto oIter = m_oMapTripIdToStopIds.find(pszTripId);
     332          45 :                 if (oIter != m_oMapTripIdToStopIds.end())
     333             :                 {
     334          45 :                     OGRLineString *poLS = new OGRLineString();
     335        1620 :                     for (const auto &kv : oIter->second)
     336             :                     {
     337             :                         const auto oIter2 =
     338        1575 :                             m_oMapStopIdToLonLat.find(kv.second);
     339        1575 :                         if (oIter2 != m_oMapStopIdToLonLat.end())
     340        1575 :                             poLS->addPoint(oIter2->second.first,
     341        1575 :                                            oIter2->second.second);
     342             :                     }
     343          45 :                     poFeature->SetGeometryDirectly(poLS);
     344             :                 }
     345             :             }
     346             :         }
     347       42848 :         if ((!m_poFilterGeom || FilterGeometry(poFeature->GetGeometryRef())) &&
     348       20048 :             (!m_poAttrQuery || m_poAttrQuery->Evaluate(poFeature.get())))
     349             :         {
     350       14928 :             return poFeature.release();
     351             :         }
     352        7872 :     }
     353             : }
     354             : 
     355             : /************************************************************************/
     356             : /*                          GetFeatureCount()                           */
     357             : /************************************************************************/
     358             : 
     359          98 : GIntBig OGRGTFSLayer::GetFeatureCount(int bForce)
     360             : {
     361          98 :     if (m_poFilterGeom || m_poAttrQuery)
     362          31 :         return OGRLayer::GetFeatureCount(bForce);
     363          67 :     return m_poUnderlyingLayer->GetFeatureCount(bForce);
     364             : }
     365             : 
     366             : /************************************************************************/
     367             : /*                           TestCapability()                           */
     368             : /************************************************************************/
     369             : 
     370         275 : int OGRGTFSLayer::TestCapability(const char *pszCap) const
     371             : {
     372         275 :     return EQUAL(pszCap, OLCStringsAsUTF8);
     373             : }
     374             : 
     375             : /************************************************************************/
     376             : /*                        OGRGTFSShapesGeomLayer                        */
     377             : /************************************************************************/
     378             : 
     379             : class OGRGTFSShapesGeomLayer final : public OGRLayer
     380             : {
     381             :     std::unique_ptr<GDALDataset> m_poUnderlyingDS{};
     382             :     OGRLayer *m_poUnderlyingLayer = nullptr;  // owned by m_poUnderlyingDS
     383             :     OGRFeatureDefn *m_poFeatureDefn = nullptr;
     384             :     bool m_bPrepared = false;
     385             :     std::vector<std::unique_ptr<OGRFeature>> m_apoFeatures{};
     386             :     size_t m_nIdx = 0;
     387             : 
     388             :     void Prepare();
     389             : 
     390             :   public:
     391             :     explicit OGRGTFSShapesGeomLayer(
     392             :         std::unique_ptr<GDALDataset> &&poUnderlyingDS);
     393             :     ~OGRGTFSShapesGeomLayer() override;
     394             : 
     395             :     void ResetReading() override;
     396             :     OGRFeature *GetNextFeature() override;
     397             :     int TestCapability(const char *) const override;
     398             : 
     399         176 :     const OGRFeatureDefn *GetLayerDefn() const override
     400             :     {
     401         176 :         return m_poFeatureDefn;
     402             :     }
     403             : 
     404             :     GIntBig GetFeatureCount(int bForce) override;
     405             : };
     406             : 
     407             : /************************************************************************/
     408             : /*                       OGRGTFSShapesGeomLayer()                       */
     409             : /************************************************************************/
     410             : 
     411           8 : OGRGTFSShapesGeomLayer::OGRGTFSShapesGeomLayer(
     412           8 :     std::unique_ptr<GDALDataset> &&poUnderlyingDS)
     413           8 :     : m_poUnderlyingDS(std::move(poUnderlyingDS))
     414             : {
     415           8 :     m_poFeatureDefn = new OGRFeatureDefn("shapes_geom");
     416           8 :     SetDescription("shapes_geom");
     417           8 :     m_poFeatureDefn->SetGeomType(wkbLineString);
     418           8 :     m_poFeatureDefn->Reference();
     419           8 :     OGRFieldDefn oField("shape_id", OFTString);
     420           8 :     m_poFeatureDefn->AddFieldDefn(&oField);
     421             : 
     422           8 :     m_poUnderlyingLayer = m_poUnderlyingDS->GetLayer(0);
     423           8 : }
     424             : 
     425             : /************************************************************************/
     426             : /*                      ~OGRGTFSShapesGeomLayer()                       */
     427             : /************************************************************************/
     428             : 
     429          16 : OGRGTFSShapesGeomLayer::~OGRGTFSShapesGeomLayer()
     430             : {
     431           8 :     m_poFeatureDefn->Release();
     432          16 : }
     433             : 
     434             : /************************************************************************/
     435             : /*                            ResetReading()                            */
     436             : /************************************************************************/
     437             : 
     438          98 : void OGRGTFSShapesGeomLayer::ResetReading()
     439             : {
     440          98 :     m_nIdx = 0;
     441          98 : }
     442             : 
     443             : /************************************************************************/
     444             : /*                              Prepare()                               */
     445             : /************************************************************************/
     446             : 
     447           2 : void OGRGTFSShapesGeomLayer::Prepare()
     448             : {
     449           2 :     m_bPrepared = true;
     450           2 :     const auto poSrcLayerDefn = m_poUnderlyingLayer->GetLayerDefn();
     451           2 :     const int nShapeIdIdx = poSrcLayerDefn->GetFieldIndex("shape_id");
     452           2 :     const int nLonIdx = poSrcLayerDefn->GetFieldIndex("shape_pt_lon");
     453           2 :     const int nLatIdx = poSrcLayerDefn->GetFieldIndex("shape_pt_lat");
     454           2 :     const int nSeqIdx = poSrcLayerDefn->GetFieldIndex("shape_pt_sequence");
     455           2 :     if (nShapeIdIdx < 0 || nLonIdx < 0 || nLatIdx < 0 || nSeqIdx < 0)
     456           0 :         return;
     457           4 :     std::map<std::string, std::map<int, std::pair<double, double>>> oMap;
     458             :     try
     459             :     {
     460        1306 :         for (auto &&poFeature : m_poUnderlyingLayer)
     461             :         {
     462        1304 :             const char *pszShapeId = poFeature->GetFieldAsString(nShapeIdIdx);
     463        1304 :             if (pszShapeId)
     464             :             {
     465        1304 :                 const int nSeq = poFeature->GetFieldAsInteger(nSeqIdx);
     466        1304 :                 const double dfLon = poFeature->GetFieldAsDouble(nLonIdx);
     467        1304 :                 const double dfLat = poFeature->GetFieldAsDouble(nLatIdx);
     468        1304 :                 oMap[pszShapeId][nSeq] = std::make_pair(dfLon, dfLat);
     469             :             }
     470             :         }
     471           4 :         for (const auto &kv : oMap)
     472             :         {
     473           2 :             const auto &osShapeId = kv.first;
     474           2 :             const auto &oMapPoints = kv.second;
     475           4 :             auto poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
     476           2 :             poFeature->SetField(0, osShapeId.c_str());
     477           2 :             OGRLineString *poLS = new OGRLineString();
     478        1306 :             for (const auto &kv2 : oMapPoints)
     479             :             {
     480        1304 :                 poLS->addPoint(kv2.second.first, kv2.second.second);
     481             :             }
     482           2 :             poFeature->SetGeometryDirectly(poLS);
     483           2 :             poFeature->SetFID(static_cast<GIntBig>(m_apoFeatures.size()));
     484           2 :             m_apoFeatures.emplace_back(std::move(poFeature));
     485             :         }
     486             :     }
     487           0 :     catch (const std::bad_alloc &)
     488             :     {
     489           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Not enough memory");
     490             :     }
     491             : }
     492             : 
     493             : /************************************************************************/
     494             : /*                           GetNextFeature()                           */
     495             : /************************************************************************/
     496             : 
     497          69 : OGRFeature *OGRGTFSShapesGeomLayer::GetNextFeature()
     498             : {
     499          69 :     if (!m_bPrepared)
     500           0 :         Prepare();
     501             :     while (true)
     502             :     {
     503          80 :         if (m_nIdx >= m_apoFeatures.size())
     504          35 :             return nullptr;
     505         101 :         if ((m_poFilterGeom == nullptr ||
     506          86 :              FilterGeometry(m_apoFeatures[m_nIdx]->GetGeometryRef())) &&
     507          41 :             (m_poAttrQuery == nullptr ||
     508          12 :              m_poAttrQuery->Evaluate(m_apoFeatures[m_nIdx].get())))
     509             :         {
     510          34 :             auto poRet = m_apoFeatures[m_nIdx]->Clone();
     511          34 :             m_nIdx++;
     512          34 :             return poRet;
     513             :         }
     514          11 :         m_nIdx++;
     515          11 :     }
     516             : }
     517             : 
     518             : /************************************************************************/
     519             : /*                           TestCapability()                           */
     520             : /************************************************************************/
     521             : 
     522          38 : int OGRGTFSShapesGeomLayer::TestCapability(const char *pszCap) const
     523             : {
     524          38 :     return EQUAL(pszCap, OLCStringsAsUTF8);
     525             : }
     526             : 
     527             : /************************************************************************/
     528             : /*                          GetFeatureCount()                           */
     529             : /************************************************************************/
     530             : 
     531          16 : GIntBig OGRGTFSShapesGeomLayer::GetFeatureCount(int bForce)
     532             : {
     533          16 :     if (m_poAttrQuery != nullptr || m_poFilterGeom != nullptr)
     534           6 :         return OGRLayer::GetFeatureCount(bForce);
     535          10 :     if (!m_bPrepared)
     536           2 :         Prepare();
     537          10 :     return static_cast<GIntBig>(m_apoFeatures.size());
     538             : }
     539             : 
     540             : /************************************************************************/
     541             : /*                              Identify()                              */
     542             : /************************************************************************/
     543             : 
     544             : static const char *const apszRequiredFiles[] = {"agency.txt", "routes.txt",
     545             :                                                 "trips.txt",  "stop_times.txt",
     546             :                                                 "stops.txt",  "calendar.txt"};
     547             : 
     548       51686 : int OGRGTFSDataset::Identify(GDALOpenInfo *poOpenInfo)
     549             : {
     550       51686 :     if (STARTS_WITH(poOpenInfo->pszFilename, "GTFS:"))
     551           6 :         return TRUE;
     552             : 
     553       51680 :     if (poOpenInfo->IsSingleAllowedDriver("GTFS") && poOpenInfo->bIsDirectory)
     554             :     {
     555           2 :         return TRUE;
     556             :     }
     557             : 
     558       51678 :     if (!poOpenInfo->IsExtensionEqualToCI("zip"))
     559       51618 :         return FALSE;
     560             : 
     561             :     // Check first filename in ZIP
     562             : 
     563          60 :     constexpr int OFFSET_FILENAME_SIZE = 26;
     564          60 :     constexpr int OFFSET_FILENAME_VAL = 30;
     565          60 :     if (poOpenInfo->nHeaderBytes < OFFSET_FILENAME_VAL ||
     566          45 :         memcmp(poOpenInfo->pabyHeader, "PK\x03\x04", 4) != 0)
     567             :     {
     568          15 :         return FALSE;
     569             :     }
     570             : 
     571         255 :     for (const char *pszFilename : apszRequiredFiles)
     572             :     {
     573         220 :         const int nLen = static_cast<int>(strlen(pszFilename));
     574         220 :         if (CPL_LSBSINT16PTR(poOpenInfo->pabyHeader + OFFSET_FILENAME_SIZE) ==
     575          12 :                 nLen &&
     576          12 :             poOpenInfo->nHeaderBytes > OFFSET_FILENAME_VAL + nLen &&
     577          12 :             memcmp(poOpenInfo->pabyHeader + OFFSET_FILENAME_VAL, pszFilename,
     578             :                    nLen) == 0)
     579             :         {
     580          10 :             return TRUE;
     581             :         }
     582             :     }
     583             : 
     584             :     static const char *const apszOptionalFiles[] = {
     585             :         "calendar_dates.txt", "fare_attributes.txt", "fare_rules.txt",
     586             :         "shapes.txt",         "frequencies.txt",     "transfers.txt",
     587             :         "feed_info.txt"};
     588         280 :     for (const char *pszFilename : apszOptionalFiles)
     589             :     {
     590         245 :         const int nLen = static_cast<int>(strlen(pszFilename));
     591         245 :         if (CPL_LSBSINT16PTR(poOpenInfo->pabyHeader + OFFSET_FILENAME_SIZE) ==
     592           2 :                 nLen &&
     593           2 :             poOpenInfo->nHeaderBytes > OFFSET_FILENAME_VAL + nLen &&
     594           2 :             memcmp(poOpenInfo->pabyHeader + OFFSET_FILENAME_VAL, pszFilename,
     595             :                    nLen) == 0)
     596             :         {
     597           0 :             return TRUE;
     598             :         }
     599             :     }
     600          35 :     return FALSE;
     601             : }
     602             : 
     603             : /************************************************************************/
     604             : /*                                Open()                                */
     605             : /************************************************************************/
     606             : 
     607           9 : GDALDataset *OGRGTFSDataset::Open(GDALOpenInfo *poOpenInfo)
     608             : {
     609           9 :     if (!Identify(poOpenInfo))
     610           0 :         return nullptr;
     611             : 
     612           9 :     const char *pszGTFSFilename = poOpenInfo->pszFilename;
     613           9 :     if (STARTS_WITH(pszGTFSFilename, "GTFS:"))
     614           3 :         pszGTFSFilename += strlen("GTFS:");
     615             : 
     616             :     std::string osBaseDir(
     617          23 :         (!STARTS_WITH(pszGTFSFilename, "/vsizip/") &&
     618           7 :          EQUAL(CPLGetExtensionSafe(pszGTFSFilename).c_str(), "zip"))
     619          27 :             ? std::string("/vsizip/{").append(pszGTFSFilename).append("}")
     620          40 :             : std::string(pszGTFSFilename));
     621             : 
     622          18 :     auto poDS = std::make_unique<OGRGTFSDataset>();
     623             : 
     624          18 :     const CPLStringList aosFilenames(VSIReadDir(osBaseDir.c_str()));
     625           9 :     size_t nCountFound = 0;
     626          18 :     std::string osShapesFilename;
     627          81 :     for (const char *pszFilename : cpl::Iterate(aosFilenames))
     628             :     {
     629          72 :         if (!EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "txt"))
     630           0 :             continue;
     631         336 :         for (const char *pszFilenameInDir : apszRequiredFiles)
     632             :         {
     633         312 :             if (EQUAL(pszFilename, pszFilenameInDir))
     634             :             {
     635          48 :                 nCountFound++;
     636          48 :                 break;
     637             :             }
     638             :         }
     639          72 :         if (EQUAL(pszFilename, "shapes.txt"))
     640           8 :             osShapesFilename = pszFilename;
     641             : 
     642             :         auto poCSVDataset = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     643          72 :             std::string(osBaseDir).append("/").append(pszFilename).c_str(),
     644         144 :             GDAL_OF_VERBOSE_ERROR | GDAL_OF_VECTOR, apszCSVDriver));
     645          72 :         if (poCSVDataset)
     646             :         {
     647          72 :             auto poUnderlyingLayer = poCSVDataset->GetLayer(0);
     648          72 :             if (poUnderlyingLayer)
     649             :             {
     650          72 :                 auto poSrcLayerDefn = poUnderlyingLayer->GetLayerDefn();
     651          72 :                 if (poSrcLayerDefn->GetFieldIndex("field_1") < 0)
     652             :                 {
     653          64 :                     poDS->m_apoLayers.emplace_back(
     654          64 :                         std::make_unique<OGRGTFSLayer>(
     655         128 :                             osBaseDir, CPLGetBasenameSafe(pszFilename).c_str(),
     656         128 :                             std::move(poCSVDataset)));
     657             :                 }
     658             :             }
     659             :         }
     660             :     }
     661             : 
     662           9 :     if (nCountFound != sizeof(apszRequiredFiles) / sizeof(apszRequiredFiles[0]))
     663             :     {
     664           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     665             :                  "GTFS: required .txt files missing");
     666           1 :         return nullptr;
     667             :     }
     668             : 
     669           8 :     if (!osShapesFilename.empty())
     670             :     {
     671             :         auto poCSVDataset = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     672           8 :             std::string(osBaseDir).append("/").append(osShapesFilename).c_str(),
     673          16 :             GDAL_OF_VERBOSE_ERROR | GDAL_OF_VECTOR, apszCSVDriver));
     674           8 :         CPL_IGNORE_RET_VAL(osBaseDir);
     675           8 :         if (poCSVDataset)
     676             :         {
     677           8 :             auto poUnderlyingLayer = poCSVDataset->GetLayer(0);
     678           8 :             if (poUnderlyingLayer)
     679             :             {
     680           8 :                 poDS->m_apoLayers.emplace_back(
     681          16 :                     std::make_unique<OGRGTFSShapesGeomLayer>(
     682          16 :                         std::move(poCSVDataset)));
     683             :             }
     684             :         }
     685             :     }
     686             : 
     687           8 :     return poDS.release();
     688             : }
     689             : 
     690             : /************************************************************************/
     691             : /*                          RegisterOGRGTFS()                           */
     692             : /************************************************************************/
     693             : 
     694        2058 : void RegisterOGRGTFS()
     695             : 
     696             : {
     697        2058 :     if (GDALGetDriverByName("GTFS") != nullptr)
     698         263 :         return;
     699             : 
     700        1795 :     GDALDriver *poDriver = new GDALDriver();
     701             : 
     702        1795 :     poDriver->SetDescription("GTFS");
     703        1795 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
     704        1795 :                               "General Transit Feed Specification");
     705        1795 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/gtfs.html");
     706        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     707        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     708        1795 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "zip");
     709             : 
     710        1795 :     poDriver->pfnOpen = OGRGTFSDataset::Open;
     711        1795 :     poDriver->pfnIdentify = OGRGTFSDataset::Identify;
     712             : 
     713        1795 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     714             : }

Generated by: LCOV version 1.14