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 : }
|