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

Generated by: LCOV version 1.14