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