minotor 1.0.7 → 2.0.1
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/CHANGELOG.md +2 -2
- package/README.md +3 -2
- package/dist/cli.mjs +604 -531
- package/dist/cli.mjs.map +1 -1
- package/dist/gtfs/stops.d.ts +19 -5
- package/dist/gtfs/transfers.d.ts +5 -4
- package/dist/gtfs/trips.d.ts +7 -5
- package/dist/gtfs/utils.d.ts +7 -8
- package/dist/parser.cjs.js +569 -501
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +569 -501
- 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 +3 -3
- 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__/route.test.d.ts +1 -0
- package/dist/routing/query.d.ts +7 -7
- package/dist/routing/result.d.ts +3 -3
- package/dist/routing/route.d.ts +1 -0
- package/dist/stops/proto/stops.d.ts +5 -4
- package/dist/stops/stops.d.ts +10 -1
- package/dist/stops/stopsIndex.d.ts +21 -4
- package/dist/timetable/proto/timetable.d.ts +21 -18
- package/dist/timetable/timetable.d.ts +38 -14
- package/package.json +4 -3
- package/src/cli/repl.ts +13 -10
- package/src/gtfs/__tests__/parser.test.ts +50 -579
- package/src/gtfs/__tests__/stops.test.ts +181 -112
- package/src/gtfs/__tests__/transfers.test.ts +170 -12
- package/src/gtfs/__tests__/trips.test.ts +212 -141
- package/src/gtfs/__tests__/utils.test.ts +4 -4
- package/src/gtfs/parser.ts +22 -13
- package/src/gtfs/stops.ts +63 -28
- package/src/gtfs/transfers.ts +14 -6
- package/src/gtfs/trips.ts +110 -47
- package/src/gtfs/utils.ts +11 -11
- package/src/router.ts +2 -3
- package/src/routing/__tests__/route.test.ts +112 -0
- package/src/routing/__tests__/router.test.ts +234 -244
- package/src/routing/query.ts +7 -7
- package/src/routing/result.ts +9 -6
- package/src/routing/route.ts +11 -0
- package/src/routing/router.ts +26 -24
- package/src/stops/__tests__/io.test.ts +9 -8
- package/src/stops/__tests__/stopFinder.test.ts +45 -36
- package/src/stops/io.ts +8 -5
- package/src/stops/proto/stops.proto +8 -7
- package/src/stops/proto/stops.ts +68 -38
- package/src/stops/stops.ts +13 -1
- package/src/stops/stopsIndex.ts +50 -7
- package/src/timetable/__tests__/io.test.ts +40 -49
- package/src/timetable/__tests__/timetable.test.ts +50 -58
- package/src/timetable/io.ts +69 -56
- package/src/timetable/proto/timetable.proto +22 -17
- package/src/timetable/proto/timetable.ts +94 -184
- package/src/timetable/timetable.ts +62 -29
|
@@ -2,13 +2,14 @@ import assert from 'node:assert';
|
|
|
2
2
|
import { Readable } from 'node:stream';
|
|
3
3
|
import { describe, it } from 'node:test';
|
|
4
4
|
|
|
5
|
+
import { StopId } from '../../stops/stops.js';
|
|
5
6
|
import { Time } from '../../timetable/time.js';
|
|
6
7
|
import {
|
|
7
8
|
RoutesAdjacency,
|
|
8
9
|
ServiceRoutesMap,
|
|
9
10
|
} from '../../timetable/timetable.js';
|
|
10
11
|
import { ServiceIds } from '../services.js';
|
|
11
|
-
import {
|
|
12
|
+
import { ParsedStopsMap } from '../stops.js';
|
|
12
13
|
import { TransfersMap } from '../transfers.js';
|
|
13
14
|
import {
|
|
14
15
|
buildStopsAdjacencyStructure,
|
|
@@ -19,23 +20,24 @@ import {
|
|
|
19
20
|
|
|
20
21
|
describe('buildStopsAdjacencyStructure', () => {
|
|
21
22
|
it('should correctly build stops adjacency for valid routes and transfers', () => {
|
|
22
|
-
const validStops:
|
|
23
|
+
const validStops: Set<StopId> = new Set([0]);
|
|
23
24
|
const routesAdjacency: RoutesAdjacency = new Map([
|
|
24
25
|
[
|
|
25
26
|
'routeA',
|
|
26
27
|
{
|
|
27
28
|
serviceRouteId: 'service1',
|
|
28
|
-
stops: [
|
|
29
|
+
stops: new Uint32Array([0, 1]),
|
|
29
30
|
stopIndices: new Map([
|
|
30
|
-
[
|
|
31
|
-
[
|
|
31
|
+
[0, 0],
|
|
32
|
+
[1, 1],
|
|
32
33
|
]),
|
|
33
|
-
stopTimes:
|
|
34
|
+
stopTimes: new Uint32Array(),
|
|
35
|
+
pickUpDropOffTypes: new Uint8Array(),
|
|
34
36
|
},
|
|
35
37
|
],
|
|
36
38
|
]);
|
|
37
39
|
const transfersMap: TransfersMap = new Map([
|
|
38
|
-
[
|
|
40
|
+
[0, [{ destination: 1, type: 'RECOMMENDED' }]],
|
|
39
41
|
]);
|
|
40
42
|
|
|
41
43
|
const stopsAdjacency = buildStopsAdjacencyStructure(
|
|
@@ -46,7 +48,7 @@ describe('buildStopsAdjacencyStructure', () => {
|
|
|
46
48
|
|
|
47
49
|
assert.deepEqual(Array.from(stopsAdjacency.entries()), [
|
|
48
50
|
[
|
|
49
|
-
|
|
51
|
+
0,
|
|
50
52
|
{
|
|
51
53
|
routes: ['routeA'],
|
|
52
54
|
transfers: [],
|
|
@@ -56,23 +58,24 @@ describe('buildStopsAdjacencyStructure', () => {
|
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
it('should ignore transfers to invalid stops', () => {
|
|
59
|
-
const validStops:
|
|
61
|
+
const validStops: Set<StopId> = new Set([0, 1]);
|
|
60
62
|
const routesAdjacency: RoutesAdjacency = new Map([
|
|
61
63
|
[
|
|
62
64
|
'routeA',
|
|
63
65
|
{
|
|
64
66
|
serviceRouteId: 'service1',
|
|
65
|
-
stops: [
|
|
67
|
+
stops: new Uint32Array([0, 1]),
|
|
66
68
|
stopIndices: new Map([
|
|
67
|
-
[
|
|
68
|
-
[
|
|
69
|
+
[0, 0],
|
|
70
|
+
[1, 1],
|
|
69
71
|
]),
|
|
70
|
-
stopTimes:
|
|
72
|
+
stopTimes: new Uint32Array(),
|
|
73
|
+
pickUpDropOffTypes: new Uint8Array(),
|
|
71
74
|
},
|
|
72
75
|
],
|
|
73
76
|
]);
|
|
74
77
|
const transfersMap: TransfersMap = new Map([
|
|
75
|
-
[
|
|
78
|
+
[0, [{ destination: 2, type: 'RECOMMENDED' }]],
|
|
76
79
|
]);
|
|
77
80
|
|
|
78
81
|
const stopsAdjacency = buildStopsAdjacencyStructure(
|
|
@@ -83,14 +86,14 @@ describe('buildStopsAdjacencyStructure', () => {
|
|
|
83
86
|
|
|
84
87
|
assert.deepEqual(Array.from(stopsAdjacency.entries()), [
|
|
85
88
|
[
|
|
86
|
-
|
|
89
|
+
0,
|
|
87
90
|
{
|
|
88
91
|
routes: ['routeA'],
|
|
89
92
|
transfers: [],
|
|
90
93
|
},
|
|
91
94
|
],
|
|
92
95
|
[
|
|
93
|
-
|
|
96
|
+
1,
|
|
94
97
|
{
|
|
95
98
|
routes: ['routeA'],
|
|
96
99
|
transfers: [],
|
|
@@ -181,10 +184,36 @@ describe('GTFS stop times parser', () => {
|
|
|
181
184
|
mockedStream.push(null);
|
|
182
185
|
|
|
183
186
|
const validTripIds: TripIdsMap = new Map([['tripA', 'routeA']]);
|
|
184
|
-
const validStopIds:
|
|
185
|
-
|
|
187
|
+
const validStopIds: Set<StopId> = new Set([0, 1]);
|
|
188
|
+
const stopsMap: ParsedStopsMap = new Map([
|
|
189
|
+
[
|
|
190
|
+
'stop1',
|
|
191
|
+
{
|
|
192
|
+
id: 0,
|
|
193
|
+
sourceStopId: 'stop1',
|
|
194
|
+
name: 'Stop 1',
|
|
195
|
+
children: [],
|
|
196
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
197
|
+
lat: 36.425288,
|
|
198
|
+
lon: -117.133162,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
[
|
|
202
|
+
'stop2',
|
|
203
|
+
{
|
|
204
|
+
id: 1,
|
|
205
|
+
sourceStopId: 'stop2',
|
|
206
|
+
name: 'Stop 2',
|
|
207
|
+
children: [],
|
|
208
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
209
|
+
lat: 36.868446,
|
|
210
|
+
lon: -116.784582,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
]);
|
|
186
214
|
const routes = await parseStopTimes(
|
|
187
215
|
mockedStream,
|
|
216
|
+
stopsMap,
|
|
188
217
|
validTripIds,
|
|
189
218
|
validStopIds,
|
|
190
219
|
);
|
|
@@ -192,28 +221,21 @@ describe('GTFS stop times parser', () => {
|
|
|
192
221
|
routes,
|
|
193
222
|
new Map([
|
|
194
223
|
[
|
|
195
|
-
'
|
|
224
|
+
'routeA_1',
|
|
196
225
|
{
|
|
197
226
|
serviceRouteId: 'routeA',
|
|
198
|
-
stops: [
|
|
227
|
+
stops: new Uint32Array([0, 1]),
|
|
199
228
|
stopIndices: new Map([
|
|
200
|
-
[
|
|
201
|
-
[
|
|
229
|
+
[0, 0],
|
|
230
|
+
[1, 1],
|
|
202
231
|
]),
|
|
203
|
-
stopTimes: [
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
{
|
|
211
|
-
arrival: Time.fromHMS(8, 10, 0),
|
|
212
|
-
departure: Time.fromHMS(8, 15, 0),
|
|
213
|
-
pickUpType: 'REGULAR',
|
|
214
|
-
dropOffType: 'REGULAR',
|
|
215
|
-
},
|
|
216
|
-
],
|
|
232
|
+
stopTimes: new Uint32Array([
|
|
233
|
+
Time.fromHMS(8, 0, 0).toSeconds(),
|
|
234
|
+
Time.fromHMS(8, 5, 0).toSeconds(),
|
|
235
|
+
Time.fromHMS(8, 10, 0).toSeconds(),
|
|
236
|
+
Time.fromHMS(8, 15, 0).toSeconds(),
|
|
237
|
+
]),
|
|
238
|
+
pickUpDropOffTypes: new Uint8Array([0, 0, 0, 0]),
|
|
217
239
|
},
|
|
218
240
|
],
|
|
219
241
|
]),
|
|
@@ -235,10 +257,33 @@ describe('GTFS stop times parser', () => {
|
|
|
235
257
|
['tripA', 'routeA'],
|
|
236
258
|
['tripB', 'routeA'],
|
|
237
259
|
]);
|
|
238
|
-
const validStopIds:
|
|
260
|
+
const validStopIds: Set<StopId> = new Set([0, 1]);
|
|
261
|
+
const stopsMap: ParsedStopsMap = new Map([
|
|
262
|
+
[
|
|
263
|
+
'stop1',
|
|
264
|
+
{
|
|
265
|
+
id: 0,
|
|
266
|
+
sourceStopId: 'stop1',
|
|
267
|
+
name: 'Stop 1',
|
|
268
|
+
children: [],
|
|
269
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
[
|
|
273
|
+
'stop2',
|
|
274
|
+
{
|
|
275
|
+
id: 1,
|
|
276
|
+
sourceStopId: 'stop2',
|
|
277
|
+
name: 'Stop 2',
|
|
278
|
+
children: [],
|
|
279
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
]);
|
|
239
283
|
|
|
240
284
|
const routes = await parseStopTimes(
|
|
241
285
|
mockedStream,
|
|
286
|
+
stopsMap,
|
|
242
287
|
validTripIds,
|
|
243
288
|
validStopIds,
|
|
244
289
|
);
|
|
@@ -246,40 +291,25 @@ describe('GTFS stop times parser', () => {
|
|
|
246
291
|
routes,
|
|
247
292
|
new Map([
|
|
248
293
|
[
|
|
249
|
-
'
|
|
294
|
+
'routeA_1',
|
|
250
295
|
{
|
|
251
296
|
serviceRouteId: 'routeA',
|
|
252
|
-
stops: [
|
|
297
|
+
stops: new Uint32Array([0, 1]),
|
|
253
298
|
stopIndices: new Map([
|
|
254
|
-
[
|
|
255
|
-
[
|
|
299
|
+
[0, 0],
|
|
300
|
+
[1, 1],
|
|
256
301
|
]),
|
|
257
|
-
stopTimes: [
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
dropOffType: 'REGULAR',
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
arrival: Time.fromHMS(9, 0, 0),
|
|
272
|
-
departure: Time.fromHMS(9, 5, 0),
|
|
273
|
-
pickUpType: 'REGULAR',
|
|
274
|
-
dropOffType: 'REGULAR',
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
arrival: Time.fromHMS(9, 10, 0),
|
|
278
|
-
departure: Time.fromHMS(9, 15, 0),
|
|
279
|
-
pickUpType: 'REGULAR',
|
|
280
|
-
dropOffType: 'REGULAR',
|
|
281
|
-
},
|
|
282
|
-
],
|
|
302
|
+
stopTimes: new Uint32Array([
|
|
303
|
+
Time.fromHMS(8, 0, 0).toSeconds(),
|
|
304
|
+
Time.fromHMS(8, 5, 0).toSeconds(),
|
|
305
|
+
Time.fromHMS(8, 10, 0).toSeconds(),
|
|
306
|
+
Time.fromHMS(8, 15, 0).toSeconds(),
|
|
307
|
+
Time.fromHMS(9, 0, 0).toSeconds(),
|
|
308
|
+
Time.fromHMS(9, 5, 0).toSeconds(),
|
|
309
|
+
Time.fromHMS(9, 10, 0).toSeconds(),
|
|
310
|
+
Time.fromHMS(9, 15, 0).toSeconds(),
|
|
311
|
+
]),
|
|
312
|
+
pickUpDropOffTypes: new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]),
|
|
283
313
|
},
|
|
284
314
|
],
|
|
285
315
|
]),
|
|
@@ -301,10 +331,33 @@ describe('GTFS stop times parser', () => {
|
|
|
301
331
|
['tripA', 'routeA'],
|
|
302
332
|
['tripB', 'routeA'],
|
|
303
333
|
]);
|
|
304
|
-
const validStopIds:
|
|
334
|
+
const validStopIds: Set<StopId> = new Set([0, 1]);
|
|
335
|
+
const stopsMap: ParsedStopsMap = new Map([
|
|
336
|
+
[
|
|
337
|
+
'stop1',
|
|
338
|
+
{
|
|
339
|
+
id: 0,
|
|
340
|
+
sourceStopId: 'stop1',
|
|
341
|
+
name: 'Stop 1',
|
|
342
|
+
children: [],
|
|
343
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
[
|
|
347
|
+
'stop2',
|
|
348
|
+
{
|
|
349
|
+
id: 1,
|
|
350
|
+
sourceStopId: 'stop2',
|
|
351
|
+
name: 'Stop 2',
|
|
352
|
+
children: [],
|
|
353
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
]);
|
|
305
357
|
|
|
306
358
|
const routes = await parseStopTimes(
|
|
307
359
|
mockedStream,
|
|
360
|
+
stopsMap,
|
|
308
361
|
validTripIds,
|
|
309
362
|
validStopIds,
|
|
310
363
|
);
|
|
@@ -312,40 +365,25 @@ describe('GTFS stop times parser', () => {
|
|
|
312
365
|
routes,
|
|
313
366
|
new Map([
|
|
314
367
|
[
|
|
315
|
-
'
|
|
368
|
+
'routeA_1',
|
|
316
369
|
{
|
|
317
370
|
serviceRouteId: 'routeA',
|
|
318
|
-
stops: [
|
|
371
|
+
stops: new Uint32Array([0, 1]),
|
|
319
372
|
stopIndices: new Map([
|
|
320
|
-
[
|
|
321
|
-
[
|
|
373
|
+
[0, 0],
|
|
374
|
+
[1, 1],
|
|
322
375
|
]),
|
|
323
|
-
stopTimes: [
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
dropOffType: 'REGULAR',
|
|
335
|
-
},
|
|
336
|
-
{
|
|
337
|
-
arrival: Time.fromHMS(9, 0, 0),
|
|
338
|
-
departure: Time.fromHMS(9, 5, 0),
|
|
339
|
-
pickUpType: 'REGULAR',
|
|
340
|
-
dropOffType: 'REGULAR',
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
arrival: Time.fromHMS(9, 10, 0),
|
|
344
|
-
departure: Time.fromHMS(9, 15, 0),
|
|
345
|
-
pickUpType: 'REGULAR',
|
|
346
|
-
dropOffType: 'REGULAR',
|
|
347
|
-
},
|
|
348
|
-
],
|
|
376
|
+
stopTimes: new Uint32Array([
|
|
377
|
+
Time.fromHMS(8, 0, 0).toSeconds(),
|
|
378
|
+
Time.fromHMS(8, 5, 0).toSeconds(),
|
|
379
|
+
Time.fromHMS(8, 10, 0).toSeconds(),
|
|
380
|
+
Time.fromHMS(8, 15, 0).toSeconds(),
|
|
381
|
+
Time.fromHMS(9, 0, 0).toSeconds(),
|
|
382
|
+
Time.fromHMS(9, 5, 0).toSeconds(),
|
|
383
|
+
Time.fromHMS(9, 10, 0).toSeconds(),
|
|
384
|
+
Time.fromHMS(9, 15, 0).toSeconds(),
|
|
385
|
+
]),
|
|
386
|
+
pickUpDropOffTypes: new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]),
|
|
349
387
|
},
|
|
350
388
|
],
|
|
351
389
|
]),
|
|
@@ -366,10 +404,33 @@ describe('GTFS stop times parser', () => {
|
|
|
366
404
|
['tripA', 'routeA'],
|
|
367
405
|
['tripB', 'routeA'],
|
|
368
406
|
]);
|
|
369
|
-
const validStopIds:
|
|
407
|
+
const validStopIds: Set<StopId> = new Set([0, 1]);
|
|
408
|
+
const stopsMap: ParsedStopsMap = new Map([
|
|
409
|
+
[
|
|
410
|
+
'stop1',
|
|
411
|
+
{
|
|
412
|
+
id: 0,
|
|
413
|
+
sourceStopId: 'stop1',
|
|
414
|
+
name: 'Stop 1',
|
|
415
|
+
children: [],
|
|
416
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
417
|
+
},
|
|
418
|
+
],
|
|
419
|
+
[
|
|
420
|
+
'stop2',
|
|
421
|
+
{
|
|
422
|
+
id: 1,
|
|
423
|
+
sourceStopId: 'stop2',
|
|
424
|
+
name: 'Stop 2',
|
|
425
|
+
children: [],
|
|
426
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
]);
|
|
370
430
|
|
|
371
431
|
const routes = await parseStopTimes(
|
|
372
432
|
mockedStream,
|
|
433
|
+
stopsMap,
|
|
373
434
|
validTripIds,
|
|
374
435
|
validStopIds,
|
|
375
436
|
);
|
|
@@ -377,51 +438,41 @@ describe('GTFS stop times parser', () => {
|
|
|
377
438
|
routes,
|
|
378
439
|
new Map([
|
|
379
440
|
[
|
|
380
|
-
'
|
|
441
|
+
'routeA_1',
|
|
381
442
|
{
|
|
382
443
|
serviceRouteId: 'routeA',
|
|
383
|
-
stops: [
|
|
444
|
+
stops: new Uint32Array([0, 1]),
|
|
384
445
|
stopIndices: new Map([
|
|
385
|
-
[
|
|
386
|
-
[
|
|
446
|
+
[0, 0],
|
|
447
|
+
[1, 1],
|
|
387
448
|
]),
|
|
388
|
-
stopTimes: [
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
{
|
|
396
|
-
arrival: Time.fromHMS(8, 10, 0),
|
|
397
|
-
departure: Time.fromHMS(8, 15, 0),
|
|
398
|
-
pickUpType: 'REGULAR',
|
|
399
|
-
dropOffType: 'REGULAR',
|
|
400
|
-
},
|
|
401
|
-
],
|
|
449
|
+
stopTimes: new Uint32Array([
|
|
450
|
+
Time.fromHMS(8, 0, 0).toSeconds(),
|
|
451
|
+
Time.fromHMS(8, 5, 0).toSeconds(),
|
|
452
|
+
Time.fromHMS(8, 10, 0).toSeconds(),
|
|
453
|
+
Time.fromHMS(8, 15, 0).toSeconds(),
|
|
454
|
+
]),
|
|
455
|
+
pickUpDropOffTypes: new Uint8Array([0, 0, 0, 0]),
|
|
402
456
|
},
|
|
403
457
|
],
|
|
404
458
|
[
|
|
405
|
-
'
|
|
459
|
+
'routeA_0',
|
|
406
460
|
{
|
|
407
461
|
serviceRouteId: 'routeA',
|
|
408
|
-
stops: [
|
|
409
|
-
stopIndices: new Map([[
|
|
410
|
-
stopTimes: [
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
dropOffType: 'REGULAR',
|
|
416
|
-
},
|
|
417
|
-
],
|
|
462
|
+
stops: new Uint32Array([0]),
|
|
463
|
+
stopIndices: new Map([[0, 0]]),
|
|
464
|
+
stopTimes: new Uint32Array([
|
|
465
|
+
Time.fromHMS(9, 0, 0).toSeconds(),
|
|
466
|
+
Time.fromHMS(9, 15, 0).toSeconds(),
|
|
467
|
+
]),
|
|
468
|
+
pickUpDropOffTypes: new Uint8Array([0, 0]),
|
|
418
469
|
},
|
|
419
470
|
],
|
|
420
471
|
]),
|
|
421
472
|
);
|
|
422
473
|
});
|
|
423
474
|
|
|
424
|
-
it('should
|
|
475
|
+
it('should ignore non-increasing stop sequences', async () => {
|
|
425
476
|
const mockedStream = new Readable();
|
|
426
477
|
mockedStream.push(
|
|
427
478
|
'trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type\n',
|
|
@@ -431,10 +482,33 @@ describe('GTFS stop times parser', () => {
|
|
|
431
482
|
mockedStream.push(null);
|
|
432
483
|
|
|
433
484
|
const validTripIds: TripIdsMap = new Map([['tripA', 'routeA']]);
|
|
434
|
-
const validStopIds:
|
|
485
|
+
const validStopIds: Set<StopId> = new Set([0, 1]);
|
|
486
|
+
const stopsMap: ParsedStopsMap = new Map([
|
|
487
|
+
[
|
|
488
|
+
'stop1',
|
|
489
|
+
{
|
|
490
|
+
id: 0,
|
|
491
|
+
sourceStopId: 'stop1',
|
|
492
|
+
name: 'Stop 1',
|
|
493
|
+
children: [],
|
|
494
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
495
|
+
},
|
|
496
|
+
],
|
|
497
|
+
[
|
|
498
|
+
'stop2',
|
|
499
|
+
{
|
|
500
|
+
id: 1,
|
|
501
|
+
sourceStopId: 'stop2',
|
|
502
|
+
name: 'Stop 2',
|
|
503
|
+
children: [],
|
|
504
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
505
|
+
},
|
|
506
|
+
],
|
|
507
|
+
]);
|
|
435
508
|
|
|
436
509
|
const routes = await parseStopTimes(
|
|
437
510
|
mockedStream,
|
|
511
|
+
stopsMap,
|
|
438
512
|
validTripIds,
|
|
439
513
|
validStopIds,
|
|
440
514
|
);
|
|
@@ -442,19 +516,16 @@ describe('GTFS stop times parser', () => {
|
|
|
442
516
|
routes,
|
|
443
517
|
new Map([
|
|
444
518
|
[
|
|
445
|
-
'
|
|
519
|
+
'routeA_0',
|
|
446
520
|
{
|
|
447
521
|
serviceRouteId: 'routeA',
|
|
448
|
-
stops: [
|
|
449
|
-
stopIndices: new Map([[
|
|
450
|
-
stopTimes: [
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
dropOffType: 'REGULAR',
|
|
456
|
-
},
|
|
457
|
-
],
|
|
522
|
+
stops: new Uint32Array([0]),
|
|
523
|
+
stopIndices: new Map([[0, 0]]),
|
|
524
|
+
stopTimes: new Uint32Array([
|
|
525
|
+
Time.fromHMS(8, 0, 0).toSeconds(),
|
|
526
|
+
Time.fromHMS(8, 5, 0).toSeconds(),
|
|
527
|
+
]),
|
|
528
|
+
pickUpDropOffTypes: new Uint8Array([0, 0]),
|
|
458
529
|
},
|
|
459
530
|
],
|
|
460
531
|
]),
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
import { describe, it } from 'node:test';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { hashIds } from '../utils.js';
|
|
5
5
|
|
|
6
|
-
describe('Utility
|
|
6
|
+
describe('Utility hashIds function', () => {
|
|
7
7
|
it('should be consistent for a given input', () => {
|
|
8
|
-
assert.equal(
|
|
8
|
+
assert.equal(hashIds([1, 2, 3]), hashIds([1, 2, 3]));
|
|
9
9
|
});
|
|
10
10
|
it('should not collide with different input', () => {
|
|
11
|
-
assert.
|
|
11
|
+
assert.notEqual(hashIds([1, 2, 3]), hashIds([4, 5, 6]));
|
|
12
12
|
});
|
|
13
13
|
});
|
package/src/gtfs/parser.ts
CHANGED
|
@@ -2,13 +2,13 @@ import log from 'loglevel';
|
|
|
2
2
|
import { DateTime } from 'luxon';
|
|
3
3
|
import StreamZip from 'node-stream-zip';
|
|
4
4
|
|
|
5
|
-
import { Platform } from '../stops/stops.js';
|
|
5
|
+
import { Platform, StopId } from '../stops/stops.js';
|
|
6
6
|
import { StopsIndex } from '../stops/stopsIndex.js';
|
|
7
7
|
import { RouteType, Timetable } from '../timetable/timetable.js';
|
|
8
8
|
import { standardProfile } from './profiles/standard.js';
|
|
9
9
|
import { parseRoutes } from './routes.js';
|
|
10
10
|
import { parseCalendar, parseCalendarDates, ServiceIds } from './services.js';
|
|
11
|
-
import { parseStops, StopEntry
|
|
11
|
+
import { indexStops, parseStops, StopEntry } from './stops.js';
|
|
12
12
|
import { parseTransfers, TransfersMap } from './transfers.js';
|
|
13
13
|
import {
|
|
14
14
|
buildStopsAdjacencyStructure,
|
|
@@ -56,7 +56,15 @@ export class GtfsParser {
|
|
|
56
56
|
const datetime = DateTime.fromJSDate(date);
|
|
57
57
|
|
|
58
58
|
const validServiceIds: ServiceIds = new Set();
|
|
59
|
-
const validStopIds
|
|
59
|
+
const validStopIds = new Set<StopId>();
|
|
60
|
+
|
|
61
|
+
log.info(`Parsing ${STOPS_FILE}`);
|
|
62
|
+
const stopsStream = await zip.stream(STOPS_FILE);
|
|
63
|
+
const parsedStops = await parseStops(
|
|
64
|
+
stopsStream,
|
|
65
|
+
this.profile.platformParser,
|
|
66
|
+
);
|
|
67
|
+
log.info(`${parsedStops.size} parsed stops.`);
|
|
60
68
|
|
|
61
69
|
if (entries[CALENDAR_FILE]) {
|
|
62
70
|
log.info(`Parsing ${CALENDAR_FILE}`);
|
|
@@ -90,7 +98,7 @@ export class GtfsParser {
|
|
|
90
98
|
if (entries[TRANSFERS_FILE]) {
|
|
91
99
|
log.info(`Parsing ${TRANSFERS_FILE}`);
|
|
92
100
|
const transfersStream = await zip.stream(TRANSFERS_FILE);
|
|
93
|
-
transfers = await parseTransfers(transfersStream);
|
|
101
|
+
transfers = await parseTransfers(transfersStream, parsedStops);
|
|
94
102
|
log.info(`${transfers.size} valid transfers.`);
|
|
95
103
|
}
|
|
96
104
|
|
|
@@ -98,6 +106,7 @@ export class GtfsParser {
|
|
|
98
106
|
const stopTimesStream = await zip.stream(STOP_TIMES_FILE);
|
|
99
107
|
const routesAdjacency = await parseStopTimes(
|
|
100
108
|
stopTimesStream,
|
|
109
|
+
parsedStops,
|
|
101
110
|
trips,
|
|
102
111
|
validStopIds,
|
|
103
112
|
);
|
|
@@ -108,14 +117,11 @@ export class GtfsParser {
|
|
|
108
117
|
);
|
|
109
118
|
log.info(`${routesAdjacency.size} valid unique routes.`);
|
|
110
119
|
|
|
111
|
-
log.info(`
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
this.profile.platformParser,
|
|
116
|
-
validStopIds,
|
|
120
|
+
log.info(`Removing unused stops.`);
|
|
121
|
+
const stops = indexStops(parsedStops, validStopIds);
|
|
122
|
+
log.info(
|
|
123
|
+
`${stops.size} used stop stops, ${parsedStops.size - stops.size} unused.`,
|
|
117
124
|
);
|
|
118
|
-
log.info(`${stops.size} valid stops.`);
|
|
119
125
|
|
|
120
126
|
await zip.close();
|
|
121
127
|
|
|
@@ -144,8 +150,11 @@ export class GtfsParser {
|
|
|
144
150
|
|
|
145
151
|
log.info(`Parsing ${STOPS_FILE}`);
|
|
146
152
|
const stopsStream = await zip.stream(STOPS_FILE);
|
|
147
|
-
const stops =
|
|
148
|
-
|
|
153
|
+
const stops = indexStops(
|
|
154
|
+
await parseStops(stopsStream, this.profile.platformParser),
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
log.info(`${stops.size} parsed stops.`);
|
|
149
158
|
|
|
150
159
|
await zip.close();
|
|
151
160
|
|