minotor 7.0.2 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cspell.json +11 -1
- package/CHANGELOG.md +8 -3
- package/README.md +26 -24
- package/dist/cli.mjs +1243 -267
- package/dist/cli.mjs.map +1 -1
- package/dist/gtfs/transfers.d.ts +13 -4
- package/dist/gtfs/trips.d.ts +12 -7
- package/dist/parser.cjs.js +494 -71
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +494 -71
- package/dist/parser.esm.js.map +1 -1
- package/dist/router.cjs.js +1 -1
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +2 -2
- package/dist/router.esm.js +1 -1
- package/dist/router.esm.js.map +1 -1
- package/dist/router.umd.js +1 -1
- package/dist/router.umd.js.map +1 -1
- package/dist/routing/__tests__/plotter.test.d.ts +1 -0
- package/dist/routing/plotter.d.ts +42 -3
- package/dist/routing/result.d.ts +23 -7
- package/dist/routing/route.d.ts +2 -0
- package/dist/routing/router.d.ts +78 -19
- package/dist/timetable/__tests__/tripId.test.d.ts +1 -0
- package/dist/timetable/io.d.ts +4 -2
- package/dist/timetable/proto/timetable.d.ts +13 -1
- package/dist/timetable/route.d.ts +41 -8
- package/dist/timetable/timetable.d.ts +18 -3
- package/dist/timetable/tripId.d.ts +15 -0
- package/package.json +1 -1
- package/src/__e2e__/router.test.ts +114 -105
- package/src/__e2e__/timetable/stops.bin +2 -2
- package/src/__e2e__/timetable/timetable.bin +2 -2
- package/src/cli/repl.ts +259 -1
- package/src/gtfs/__tests__/transfers.test.ts +468 -12
- package/src/gtfs/__tests__/trips.test.ts +350 -28
- package/src/gtfs/parser.ts +16 -4
- package/src/gtfs/transfers.ts +61 -18
- package/src/gtfs/trips.ts +97 -22
- package/src/router.ts +2 -2
- package/src/routing/__tests__/plotter.test.ts +230 -0
- package/src/routing/__tests__/result.test.ts +486 -125
- package/src/routing/__tests__/route.test.ts +7 -3
- package/src/routing/__tests__/router.test.ts +378 -172
- package/src/routing/plotter.ts +279 -48
- package/src/routing/result.ts +114 -34
- package/src/routing/route.ts +0 -3
- package/src/routing/router.ts +332 -211
- package/src/timetable/__tests__/io.test.ts +33 -1
- package/src/timetable/__tests__/route.test.ts +10 -3
- package/src/timetable/__tests__/timetable.test.ts +225 -57
- package/src/timetable/__tests__/tripId.test.ts +27 -0
- package/src/timetable/io.ts +71 -10
- package/src/timetable/proto/timetable.proto +14 -2
- package/src/timetable/proto/timetable.ts +218 -20
- package/src/timetable/route.ts +152 -19
- package/src/timetable/timetable.ts +45 -6
- package/src/timetable/tripId.ts +29 -0
|
@@ -9,37 +9,56 @@ import { ServiceRoute } from '../../timetable/timetable.js';
|
|
|
9
9
|
import { GtfsRoutesMap } from '../routes.js';
|
|
10
10
|
import { ServiceIds } from '../services.js';
|
|
11
11
|
import { GtfsStopsMap } from '../stops.js';
|
|
12
|
-
import { TransfersMap } from '../transfers.js';
|
|
12
|
+
import { TransfersMap, TripContinuationsMap } from '../transfers.js';
|
|
13
13
|
import {
|
|
14
14
|
buildStopsAdjacencyStructure,
|
|
15
15
|
encodePickUpDropOffTypes,
|
|
16
|
+
GtfsTripIdsMap,
|
|
16
17
|
parseStopTimes,
|
|
17
18
|
parseTrips,
|
|
18
|
-
|
|
19
|
+
TripsMapping,
|
|
19
20
|
} from '../trips.js';
|
|
20
21
|
|
|
21
22
|
describe('buildStopsAdjacencyStructure', () => {
|
|
22
23
|
it('should correctly build stops adjacency for valid routes and transfers', () => {
|
|
23
24
|
const validStops: Set<StopId> = new Set([0]);
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
const routes = [
|
|
26
|
+
Route.of({
|
|
27
|
+
id: 0,
|
|
28
|
+
serviceRouteId: 0,
|
|
29
|
+
trips: [
|
|
30
|
+
{
|
|
31
|
+
stops: [
|
|
32
|
+
{
|
|
33
|
+
id: 0,
|
|
34
|
+
arrivalTime: Time.fromHMS(8, 0, 0),
|
|
35
|
+
departureTime: Time.fromHMS(8, 0, 0),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 1,
|
|
39
|
+
arrivalTime: Time.fromHMS(8, 5, 0),
|
|
40
|
+
departureTime: Time.fromHMS(8, 5, 0),
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
}),
|
|
31
46
|
];
|
|
32
47
|
const transfersMap: TransfersMap = new Map([
|
|
33
48
|
[0, [{ destination: 1, type: 'RECOMMENDED' }]],
|
|
34
49
|
]);
|
|
50
|
+
const tripContinuationsMap: TripContinuationsMap = new Map();
|
|
35
51
|
const serviceRoutes: ServiceRoute[] = [
|
|
36
52
|
{ type: 'BUS', name: 'B1', routes: [] },
|
|
37
53
|
];
|
|
54
|
+
const tripsMapping: TripsMapping = new Map();
|
|
38
55
|
|
|
39
56
|
const stopsAdjacency = buildStopsAdjacencyStructure(
|
|
57
|
+
tripsMapping,
|
|
40
58
|
serviceRoutes,
|
|
41
|
-
|
|
59
|
+
routes,
|
|
42
60
|
transfersMap,
|
|
61
|
+
tripContinuationsMap,
|
|
43
62
|
2,
|
|
44
63
|
validStops,
|
|
45
64
|
);
|
|
@@ -61,7 +80,6 @@ describe('buildStopsAdjacencyStructure', () => {
|
|
|
61
80
|
1,
|
|
62
81
|
{
|
|
63
82
|
routes: [],
|
|
64
|
-
transfers: [],
|
|
65
83
|
},
|
|
66
84
|
],
|
|
67
85
|
]);
|
|
@@ -70,25 +88,43 @@ describe('buildStopsAdjacencyStructure', () => {
|
|
|
70
88
|
|
|
71
89
|
it('should ignore transfers to invalid stops', () => {
|
|
72
90
|
const validStops: Set<StopId> = new Set([0, 1]);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
const routes = [
|
|
92
|
+
Route.of({
|
|
93
|
+
id: 0,
|
|
94
|
+
serviceRouteId: 0,
|
|
95
|
+
trips: [
|
|
96
|
+
{
|
|
97
|
+
stops: [
|
|
98
|
+
{
|
|
99
|
+
id: 0,
|
|
100
|
+
arrivalTime: Time.fromHMS(8, 0, 0),
|
|
101
|
+
departureTime: Time.fromHMS(8, 0, 0),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 1,
|
|
105
|
+
arrivalTime: Time.fromHMS(8, 5, 0),
|
|
106
|
+
departureTime: Time.fromHMS(8, 5, 0),
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
}),
|
|
80
112
|
];
|
|
81
113
|
const transfersMap: TransfersMap = new Map([
|
|
82
114
|
[3, [{ destination: 2, type: 'RECOMMENDED' }]],
|
|
83
115
|
]);
|
|
116
|
+
const tripContinuationsMap: TripContinuationsMap = new Map();
|
|
84
117
|
const serviceRoutes: ServiceRoute[] = [
|
|
85
118
|
{ type: 'BUS', name: 'B1', routes: [] },
|
|
86
119
|
];
|
|
120
|
+
const tripsMapping: TripsMapping = new Map();
|
|
87
121
|
|
|
88
122
|
const stopsAdjacency = buildStopsAdjacencyStructure(
|
|
123
|
+
tripsMapping,
|
|
89
124
|
serviceRoutes,
|
|
90
|
-
|
|
125
|
+
routes,
|
|
91
126
|
transfersMap,
|
|
127
|
+
tripContinuationsMap,
|
|
92
128
|
4,
|
|
93
129
|
validStops,
|
|
94
130
|
);
|
|
@@ -98,33 +134,227 @@ describe('buildStopsAdjacencyStructure', () => {
|
|
|
98
134
|
0,
|
|
99
135
|
{
|
|
100
136
|
routes: [0],
|
|
101
|
-
transfers: [],
|
|
102
137
|
},
|
|
103
138
|
],
|
|
104
139
|
[
|
|
105
140
|
1,
|
|
106
141
|
{
|
|
107
142
|
routes: [0],
|
|
108
|
-
transfers: [],
|
|
109
143
|
},
|
|
110
144
|
],
|
|
111
145
|
[
|
|
112
146
|
2,
|
|
113
147
|
{
|
|
114
148
|
routes: [],
|
|
115
|
-
transfers: [],
|
|
116
149
|
},
|
|
117
150
|
],
|
|
118
151
|
[
|
|
119
152
|
3,
|
|
120
153
|
{
|
|
121
154
|
routes: [],
|
|
122
|
-
transfers: [],
|
|
123
155
|
},
|
|
124
156
|
],
|
|
125
157
|
]);
|
|
126
158
|
assert.deepEqual(serviceRoutes[0]?.routes, [0]);
|
|
127
159
|
});
|
|
160
|
+
|
|
161
|
+
it('should correctly handle trip continuations', () => {
|
|
162
|
+
const validStops: Set<StopId> = new Set([0, 1]);
|
|
163
|
+
const routes = [
|
|
164
|
+
Route.of({
|
|
165
|
+
id: 0,
|
|
166
|
+
serviceRouteId: 0,
|
|
167
|
+
trips: [
|
|
168
|
+
{
|
|
169
|
+
stops: [
|
|
170
|
+
{
|
|
171
|
+
id: 0,
|
|
172
|
+
arrivalTime: Time.fromHMS(8, 0, 0),
|
|
173
|
+
departureTime: Time.fromHMS(8, 0, 0),
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
}),
|
|
179
|
+
Route.of({
|
|
180
|
+
id: 1,
|
|
181
|
+
serviceRouteId: 0,
|
|
182
|
+
trips: [
|
|
183
|
+
{
|
|
184
|
+
stops: [
|
|
185
|
+
{
|
|
186
|
+
id: 1,
|
|
187
|
+
arrivalTime: Time.fromHMS(8, 30, 0),
|
|
188
|
+
departureTime: Time.fromHMS(8, 30, 0),
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
}),
|
|
194
|
+
];
|
|
195
|
+
const transfersMap: TransfersMap = new Map();
|
|
196
|
+
const tripContinuationsMap: TripContinuationsMap = new Map([
|
|
197
|
+
[0, [{ fromTrip: 'trip1', toTrip: 'trip2', hopOnStop: 1 }]],
|
|
198
|
+
]);
|
|
199
|
+
const serviceRoutes: ServiceRoute[] = [
|
|
200
|
+
{ type: 'BUS', name: 'B1', routes: [] },
|
|
201
|
+
];
|
|
202
|
+
const tripsMapping: TripsMapping = new Map([
|
|
203
|
+
['trip1', { routeId: 0, tripRouteIndex: 0 }],
|
|
204
|
+
['trip2', { routeId: 1, tripRouteIndex: 0 }],
|
|
205
|
+
]);
|
|
206
|
+
|
|
207
|
+
const stopsAdjacency = buildStopsAdjacencyStructure(
|
|
208
|
+
tripsMapping,
|
|
209
|
+
serviceRoutes,
|
|
210
|
+
routes,
|
|
211
|
+
transfersMap,
|
|
212
|
+
tripContinuationsMap,
|
|
213
|
+
2,
|
|
214
|
+
validStops,
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
assert.deepEqual(Array.from(stopsAdjacency.entries()), [
|
|
218
|
+
[
|
|
219
|
+
0,
|
|
220
|
+
{
|
|
221
|
+
routes: [0],
|
|
222
|
+
tripContinuations: new Map([
|
|
223
|
+
[0, [{ hopOnStop: 1, routeId: 1, tripIndex: 0 }]],
|
|
224
|
+
]),
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
[
|
|
228
|
+
1,
|
|
229
|
+
{
|
|
230
|
+
routes: [1],
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
]);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should ignore trip continuations with invalid trip IDs', () => {
|
|
237
|
+
const validStops: Set<StopId> = new Set([0]);
|
|
238
|
+
const routes = [
|
|
239
|
+
Route.of({
|
|
240
|
+
id: 0,
|
|
241
|
+
serviceRouteId: 0,
|
|
242
|
+
trips: [
|
|
243
|
+
{
|
|
244
|
+
stops: [
|
|
245
|
+
{
|
|
246
|
+
id: 0,
|
|
247
|
+
arrivalTime: Time.fromHMS(8, 0, 0),
|
|
248
|
+
departureTime: Time.fromHMS(8, 0, 0),
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
}),
|
|
254
|
+
];
|
|
255
|
+
const transfersMap: TransfersMap = new Map();
|
|
256
|
+
const tripContinuationsMap: TripContinuationsMap = new Map([
|
|
257
|
+
[
|
|
258
|
+
0,
|
|
259
|
+
[{ fromTrip: 'invalid_trip', toTrip: 'invalid_trip2', hopOnStop: 1 }],
|
|
260
|
+
],
|
|
261
|
+
]);
|
|
262
|
+
const serviceRoutes: ServiceRoute[] = [
|
|
263
|
+
{ type: 'BUS', name: 'B1', routes: [] },
|
|
264
|
+
];
|
|
265
|
+
const tripsMapping: TripsMapping = new Map([
|
|
266
|
+
['trip1', { routeId: 0, tripRouteIndex: 0 }],
|
|
267
|
+
]);
|
|
268
|
+
|
|
269
|
+
const stopsAdjacency = buildStopsAdjacencyStructure(
|
|
270
|
+
tripsMapping,
|
|
271
|
+
serviceRoutes,
|
|
272
|
+
routes,
|
|
273
|
+
transfersMap,
|
|
274
|
+
tripContinuationsMap,
|
|
275
|
+
1,
|
|
276
|
+
validStops,
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
assert.deepEqual(Array.from(stopsAdjacency.entries()), [
|
|
280
|
+
[
|
|
281
|
+
0,
|
|
282
|
+
{
|
|
283
|
+
routes: [0],
|
|
284
|
+
tripContinuations: new Map(),
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
]);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should ignore trip continuations for inactive stops', () => {
|
|
291
|
+
const validStops: Set<StopId> = new Set([0]); // Only stop 0 is active
|
|
292
|
+
const routes = [
|
|
293
|
+
Route.of({
|
|
294
|
+
id: 0,
|
|
295
|
+
serviceRouteId: 0,
|
|
296
|
+
trips: [
|
|
297
|
+
{
|
|
298
|
+
stops: [
|
|
299
|
+
{
|
|
300
|
+
id: 0,
|
|
301
|
+
arrivalTime: Time.fromHMS(8, 0, 0),
|
|
302
|
+
departureTime: Time.fromHMS(8, 0, 0),
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
}),
|
|
308
|
+
];
|
|
309
|
+
const transfersMap: TransfersMap = new Map();
|
|
310
|
+
const tripContinuationsMap: TripContinuationsMap = new Map([
|
|
311
|
+
[2, [{ fromTrip: 'trip1', toTrip: 'trip2', hopOnStop: 3 }]], // Stop 2 and 3 are inactive
|
|
312
|
+
]);
|
|
313
|
+
const serviceRoutes: ServiceRoute[] = [
|
|
314
|
+
{ type: 'BUS', name: 'B1', routes: [] },
|
|
315
|
+
];
|
|
316
|
+
const tripsMapping: TripsMapping = new Map([
|
|
317
|
+
['trip1', { routeId: 0, tripRouteIndex: 0 }],
|
|
318
|
+
['trip2', { routeId: 0, tripRouteIndex: 1 }],
|
|
319
|
+
]);
|
|
320
|
+
|
|
321
|
+
const stopsAdjacency = buildStopsAdjacencyStructure(
|
|
322
|
+
tripsMapping,
|
|
323
|
+
serviceRoutes,
|
|
324
|
+
routes,
|
|
325
|
+
transfersMap,
|
|
326
|
+
tripContinuationsMap,
|
|
327
|
+
4, // 4 stops total but only stop 0 is active
|
|
328
|
+
validStops,
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
assert.deepEqual(Array.from(stopsAdjacency.entries()), [
|
|
332
|
+
[
|
|
333
|
+
0,
|
|
334
|
+
{
|
|
335
|
+
routes: [0],
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
[
|
|
339
|
+
1,
|
|
340
|
+
{
|
|
341
|
+
routes: [],
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
[
|
|
345
|
+
2,
|
|
346
|
+
{
|
|
347
|
+
routes: [],
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
[
|
|
351
|
+
3,
|
|
352
|
+
{
|
|
353
|
+
routes: [],
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
]);
|
|
357
|
+
});
|
|
128
358
|
});
|
|
129
359
|
describe('GTFS trips parser', () => {
|
|
130
360
|
it('should correctly parse valid trips', async () => {
|
|
@@ -207,7 +437,7 @@ describe('GTFS stop times parser', () => {
|
|
|
207
437
|
mockedStream.push('"tripA","08:10:00","08:15:00","stop2","2","0","0"\n');
|
|
208
438
|
mockedStream.push(null);
|
|
209
439
|
|
|
210
|
-
const validTripIds:
|
|
440
|
+
const validTripIds: GtfsTripIdsMap = new Map([['tripA', 'routeA']]);
|
|
211
441
|
const validStopIds: Set<StopId> = new Set([0, 1]);
|
|
212
442
|
const stopsMap: GtfsStopsMap = new Map([
|
|
213
443
|
[
|
|
@@ -243,6 +473,7 @@ describe('GTFS stop times parser', () => {
|
|
|
243
473
|
);
|
|
244
474
|
assert.deepEqual(result.routes, [
|
|
245
475
|
new Route(
|
|
476
|
+
0,
|
|
246
477
|
new Uint16Array([
|
|
247
478
|
Time.fromHMS(8, 0, 0).toMinutes(),
|
|
248
479
|
Time.fromHMS(8, 5, 0).toMinutes(),
|
|
@@ -254,6 +485,10 @@ describe('GTFS stop times parser', () => {
|
|
|
254
485
|
0,
|
|
255
486
|
),
|
|
256
487
|
]);
|
|
488
|
+
assert.deepEqual(
|
|
489
|
+
result.tripsMapping,
|
|
490
|
+
new Map([['tripA', { routeId: 0, tripRouteIndex: 0 }]]),
|
|
491
|
+
);
|
|
257
492
|
});
|
|
258
493
|
|
|
259
494
|
it('should create same route for same GTFS route with same stops', async () => {
|
|
@@ -267,7 +502,7 @@ describe('GTFS stop times parser', () => {
|
|
|
267
502
|
mockedStream.push('"tripB","09:10:00","09:15:00","stop2","2","0","0"\n');
|
|
268
503
|
mockedStream.push(null);
|
|
269
504
|
|
|
270
|
-
const validTripIds:
|
|
505
|
+
const validTripIds: GtfsTripIdsMap = new Map([
|
|
271
506
|
['tripA', 'routeA'],
|
|
272
507
|
['tripB', 'routeA'],
|
|
273
508
|
]);
|
|
@@ -303,6 +538,7 @@ describe('GTFS stop times parser', () => {
|
|
|
303
538
|
);
|
|
304
539
|
assert.deepEqual(result.routes, [
|
|
305
540
|
new Route(
|
|
541
|
+
0,
|
|
306
542
|
new Uint16Array([
|
|
307
543
|
Time.fromHMS(8, 0, 0).toMinutes(),
|
|
308
544
|
Time.fromHMS(8, 5, 0).toMinutes(),
|
|
@@ -321,6 +557,13 @@ describe('GTFS stop times parser', () => {
|
|
|
321
557
|
0,
|
|
322
558
|
),
|
|
323
559
|
]);
|
|
560
|
+
assert.deepEqual(
|
|
561
|
+
result.tripsMapping,
|
|
562
|
+
new Map([
|
|
563
|
+
['tripA', { routeId: 0, tripRouteIndex: 0 }],
|
|
564
|
+
['tripB', { routeId: 0, tripRouteIndex: 1 }],
|
|
565
|
+
]),
|
|
566
|
+
);
|
|
324
567
|
});
|
|
325
568
|
|
|
326
569
|
it('should support unsorted trips within a route', async () => {
|
|
@@ -334,7 +577,7 @@ describe('GTFS stop times parser', () => {
|
|
|
334
577
|
mockedStream.push('"tripA","08:10:00","08:15:00","stop2","2","0","0"\n');
|
|
335
578
|
mockedStream.push(null);
|
|
336
579
|
|
|
337
|
-
const validTripIds:
|
|
580
|
+
const validTripIds: GtfsTripIdsMap = new Map([
|
|
338
581
|
['tripA', 'routeA'],
|
|
339
582
|
['tripB', 'routeA'],
|
|
340
583
|
]);
|
|
@@ -370,6 +613,7 @@ describe('GTFS stop times parser', () => {
|
|
|
370
613
|
);
|
|
371
614
|
assert.deepEqual(result.routes, [
|
|
372
615
|
new Route(
|
|
616
|
+
0,
|
|
373
617
|
new Uint16Array([
|
|
374
618
|
Time.fromHMS(8, 0, 0).toMinutes(),
|
|
375
619
|
Time.fromHMS(8, 5, 0).toMinutes(),
|
|
@@ -400,7 +644,7 @@ describe('GTFS stop times parser', () => {
|
|
|
400
644
|
mockedStream.push('"tripB","09:00:00","09:15:00","stop1","1","0","0"\n');
|
|
401
645
|
mockedStream.push(null);
|
|
402
646
|
|
|
403
|
-
const validTripIds:
|
|
647
|
+
const validTripIds: GtfsTripIdsMap = new Map([
|
|
404
648
|
['tripA', 'routeA'],
|
|
405
649
|
['tripB', 'routeA'],
|
|
406
650
|
]);
|
|
@@ -436,6 +680,7 @@ describe('GTFS stop times parser', () => {
|
|
|
436
680
|
);
|
|
437
681
|
assert.deepEqual(result.routes, [
|
|
438
682
|
new Route(
|
|
683
|
+
0,
|
|
439
684
|
new Uint16Array([
|
|
440
685
|
Time.fromHMS(8, 0, 0).toMinutes(),
|
|
441
686
|
Time.fromHMS(8, 5, 0).toMinutes(),
|
|
@@ -447,6 +692,7 @@ describe('GTFS stop times parser', () => {
|
|
|
447
692
|
0,
|
|
448
693
|
),
|
|
449
694
|
new Route(
|
|
695
|
+
1,
|
|
450
696
|
new Uint16Array([
|
|
451
697
|
Time.fromHMS(9, 0, 0).toMinutes(),
|
|
452
698
|
Time.fromHMS(9, 15, 0).toMinutes(),
|
|
@@ -467,7 +713,7 @@ describe('GTFS stop times parser', () => {
|
|
|
467
713
|
mockedStream.push('"tripA","08:10:00","08:15:00","stop2","1","0","0"\n');
|
|
468
714
|
mockedStream.push(null);
|
|
469
715
|
|
|
470
|
-
const validTripIds:
|
|
716
|
+
const validTripIds: GtfsTripIdsMap = new Map([['tripA', 'routeA']]);
|
|
471
717
|
const validStopIds: Set<StopId> = new Set([0, 1]);
|
|
472
718
|
const stopsMap: GtfsStopsMap = new Map([
|
|
473
719
|
[
|
|
@@ -500,6 +746,7 @@ describe('GTFS stop times parser', () => {
|
|
|
500
746
|
);
|
|
501
747
|
assert.deepEqual(result.routes, [
|
|
502
748
|
new Route(
|
|
749
|
+
0,
|
|
503
750
|
new Uint16Array([
|
|
504
751
|
Time.fromHMS(8, 0, 0).toMinutes(),
|
|
505
752
|
Time.fromHMS(8, 5, 0).toMinutes(),
|
|
@@ -509,5 +756,80 @@ describe('GTFS stop times parser', () => {
|
|
|
509
756
|
0,
|
|
510
757
|
),
|
|
511
758
|
]);
|
|
759
|
+
assert.deepEqual(
|
|
760
|
+
result.tripsMapping,
|
|
761
|
+
new Map([['tripA', { routeId: 0, tripRouteIndex: 0 }]]),
|
|
762
|
+
);
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
it('should create trip continuations mapping correctly', async () => {
|
|
766
|
+
const mockedStream = new Readable();
|
|
767
|
+
mockedStream.push(
|
|
768
|
+
'trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type\n',
|
|
769
|
+
);
|
|
770
|
+
mockedStream.push('"trip1","08:00:00","08:05:00","stop1","1","0","0"\n');
|
|
771
|
+
mockedStream.push('"trip1","08:10:00","08:15:00","stop2","2","0","0"\n');
|
|
772
|
+
mockedStream.push('"trip2","09:00:00","09:05:00","stop3","1","0","0"\n');
|
|
773
|
+
mockedStream.push(null);
|
|
774
|
+
|
|
775
|
+
const validTripIds: GtfsTripIdsMap = new Map([
|
|
776
|
+
['trip1', 'routeA'],
|
|
777
|
+
['trip2', 'routeB'],
|
|
778
|
+
]);
|
|
779
|
+
const validStopIds: Set<StopId> = new Set([0, 1, 2]);
|
|
780
|
+
const stopsMap: GtfsStopsMap = new Map([
|
|
781
|
+
[
|
|
782
|
+
'stop1',
|
|
783
|
+
{
|
|
784
|
+
id: 0,
|
|
785
|
+
sourceStopId: 'stop1',
|
|
786
|
+
name: 'Stop 1',
|
|
787
|
+
children: [],
|
|
788
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
789
|
+
},
|
|
790
|
+
],
|
|
791
|
+
[
|
|
792
|
+
'stop2',
|
|
793
|
+
{
|
|
794
|
+
id: 1,
|
|
795
|
+
sourceStopId: 'stop2',
|
|
796
|
+
name: 'Stop 2',
|
|
797
|
+
children: [],
|
|
798
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
799
|
+
},
|
|
800
|
+
],
|
|
801
|
+
[
|
|
802
|
+
'stop3',
|
|
803
|
+
{
|
|
804
|
+
id: 2,
|
|
805
|
+
sourceStopId: 'stop3',
|
|
806
|
+
name: 'Stop 3',
|
|
807
|
+
children: [],
|
|
808
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
809
|
+
},
|
|
810
|
+
],
|
|
811
|
+
]);
|
|
812
|
+
|
|
813
|
+
const result = await parseStopTimes(
|
|
814
|
+
mockedStream,
|
|
815
|
+
stopsMap,
|
|
816
|
+
validTripIds,
|
|
817
|
+
validStopIds,
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
assert.deepEqual(
|
|
821
|
+
result.tripsMapping,
|
|
822
|
+
new Map([
|
|
823
|
+
['trip1', { routeId: 0, tripRouteIndex: 0 }],
|
|
824
|
+
['trip2', { routeId: 1, tripRouteIndex: 0 }],
|
|
825
|
+
]),
|
|
826
|
+
);
|
|
827
|
+
assert.deepEqual(
|
|
828
|
+
result.serviceRoutesMap,
|
|
829
|
+
new Map([
|
|
830
|
+
['routeA', 0],
|
|
831
|
+
['routeB', 1],
|
|
832
|
+
]),
|
|
833
|
+
);
|
|
512
834
|
});
|
|
513
835
|
});
|
package/src/gtfs/parser.ts
CHANGED
|
@@ -9,7 +9,11 @@ import { standardProfile } from './profiles/standard.js';
|
|
|
9
9
|
import { indexRoutes, parseRoutes } from './routes.js';
|
|
10
10
|
import { parseCalendar, parseCalendarDates, ServiceIds } from './services.js';
|
|
11
11
|
import { parseStops } from './stops.js';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
parseTransfers,
|
|
14
|
+
TransfersMap,
|
|
15
|
+
TripContinuationsMap,
|
|
16
|
+
} from './transfers.js';
|
|
13
17
|
import {
|
|
14
18
|
buildStopsAdjacencyStructure,
|
|
15
19
|
parseStopTimes,
|
|
@@ -108,21 +112,27 @@ export class GtfsParser {
|
|
|
108
112
|
);
|
|
109
113
|
|
|
110
114
|
let transfers = new Map() as TransfersMap;
|
|
115
|
+
let tripContinuations = new Map() as TripContinuationsMap;
|
|
111
116
|
if (entries[TRANSFERS_FILE]) {
|
|
112
117
|
log.info(`Parsing ${TRANSFERS_FILE}`);
|
|
113
118
|
const transfersStart = performance.now();
|
|
114
119
|
const transfersStream = await zip.stream(TRANSFERS_FILE);
|
|
115
|
-
|
|
120
|
+
const {
|
|
121
|
+
transfers: parsedTransfers,
|
|
122
|
+
tripContinuations: parsedTripContinuations,
|
|
123
|
+
} = await parseTransfers(transfersStream, parsedStops);
|
|
124
|
+
transfers = parsedTransfers;
|
|
125
|
+
tripContinuations = parsedTripContinuations;
|
|
116
126
|
const transfersEnd = performance.now();
|
|
117
127
|
log.info(
|
|
118
|
-
`${transfers.size} valid transfers. (${(transfersEnd - transfersStart).toFixed(2)}ms)`,
|
|
128
|
+
`${transfers.size} valid transfers and ${tripContinuations.size} trip continuations. (${(transfersEnd - transfersStart).toFixed(2)}ms)`,
|
|
119
129
|
);
|
|
120
130
|
}
|
|
121
131
|
|
|
122
132
|
log.info(`Parsing ${STOP_TIMES_FILE}`);
|
|
123
133
|
const stopTimesStart = performance.now();
|
|
124
134
|
const stopTimesStream = await zip.stream(STOP_TIMES_FILE);
|
|
125
|
-
const { routes, serviceRoutesMap } = await parseStopTimes(
|
|
135
|
+
const { routes, serviceRoutesMap, tripsMapping } = await parseStopTimes(
|
|
126
136
|
stopTimesStream,
|
|
127
137
|
parsedStops,
|
|
128
138
|
trips,
|
|
@@ -136,9 +146,11 @@ export class GtfsParser {
|
|
|
136
146
|
log.info('Building stops adjacency structure');
|
|
137
147
|
const stopsAdjacencyStart = performance.now();
|
|
138
148
|
const stopsAdjacency = buildStopsAdjacencyStructure(
|
|
149
|
+
tripsMapping,
|
|
139
150
|
serviceRoutes,
|
|
140
151
|
routes,
|
|
141
152
|
transfers,
|
|
153
|
+
tripContinuations,
|
|
142
154
|
parsedStops.size,
|
|
143
155
|
activeStopIds,
|
|
144
156
|
);
|