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