minotor 3.0.1 → 3.0.2
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 +12 -1
- package/.gitattributes +3 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +3 -0
- package/.github/workflows/minotor.yml +17 -1
- package/CHANGELOG.md +2 -2
- package/README.md +34 -14
- package/dist/__e2e__/router.test.d.ts +1 -0
- package/dist/cli/perf.d.ts +28 -0
- package/dist/cli/utils.d.ts +6 -2
- package/dist/cli.mjs +1967 -823
- package/dist/cli.mjs.map +1 -1
- package/dist/gtfs/trips.d.ts +1 -0
- package/dist/gtfs/utils.d.ts +1 -1
- package/dist/parser.cjs.js +1030 -627
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.d.ts +4 -2
- package/dist/parser.esm.js +1030 -627
- 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 +10 -5
- 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__/result.test.d.ts +1 -0
- package/dist/routing/query.d.ts +27 -6
- package/dist/routing/result.d.ts +1 -1
- package/dist/routing/route.d.ts +47 -2
- package/dist/routing/router.d.ts +15 -1
- package/dist/stops/stopsIndex.d.ts +3 -3
- package/dist/timetable/__tests__/route.test.d.ts +1 -0
- package/dist/timetable/__tests__/time.test.d.ts +1 -0
- package/dist/timetable/io.d.ts +7 -1
- package/dist/timetable/proto/timetable.d.ts +1 -1
- package/dist/timetable/route.d.ts +155 -0
- package/dist/timetable/time.d.ts +21 -0
- package/dist/timetable/timetable.d.ts +41 -61
- package/package.json +36 -34
- package/src/__e2e__/benchmark.json +22 -0
- package/src/__e2e__/router.test.ts +209 -0
- package/src/__e2e__/timetable/stops.bin +3 -0
- package/src/__e2e__/timetable/timetable.bin +3 -0
- package/src/cli/minotor.ts +51 -1
- package/src/cli/perf.ts +136 -0
- package/src/cli/repl.ts +26 -13
- package/src/cli/utils.ts +6 -28
- package/src/gtfs/__tests__/parser.test.ts +12 -15
- package/src/gtfs/__tests__/services.test.ts +1 -0
- package/src/gtfs/__tests__/transfers.test.ts +0 -1
- package/src/gtfs/__tests__/trips.test.ts +67 -74
- package/src/gtfs/profiles/ch.ts +1 -1
- package/src/gtfs/routes.ts +4 -4
- package/src/gtfs/services.ts +15 -2
- package/src/gtfs/stops.ts +7 -3
- package/src/gtfs/transfers.ts +6 -3
- package/src/gtfs/trips.ts +33 -16
- package/src/gtfs/utils.ts +13 -2
- package/src/parser.ts +4 -2
- package/src/router.ts +17 -11
- package/src/routing/__tests__/result.test.ts +392 -0
- package/src/routing/__tests__/router.test.ts +94 -137
- package/src/routing/query.ts +28 -7
- package/src/routing/result.ts +10 -5
- package/src/routing/route.ts +95 -9
- package/src/routing/router.ts +82 -66
- package/src/stops/__tests__/io.test.ts +1 -1
- package/src/stops/__tests__/stopFinder.test.ts +1 -1
- package/src/stops/proto/stops.ts +4 -4
- package/src/stops/stopsIndex.ts +3 -3
- package/src/timetable/__tests__/io.test.ts +16 -23
- package/src/timetable/__tests__/route.test.ts +317 -0
- package/src/timetable/__tests__/time.test.ts +494 -0
- package/src/timetable/__tests__/timetable.test.ts +64 -75
- package/src/timetable/io.ts +32 -26
- package/src/timetable/proto/timetable.proto +1 -1
- package/src/timetable/proto/timetable.ts +13 -13
- package/src/timetable/route.ts +347 -0
- package/src/timetable/time.ts +40 -8
- package/src/timetable/timetable.ts +74 -165
- package/tsconfig.build.json +1 -1
|
@@ -3,6 +3,7 @@ import { Readable } from 'node:stream';
|
|
|
3
3
|
import { describe, it } from 'node:test';
|
|
4
4
|
|
|
5
5
|
import { StopId } from '../../stops/stops.js';
|
|
6
|
+
import { REGULAR, Route } from '../../timetable/route.js';
|
|
6
7
|
import { Time } from '../../timetable/time.js';
|
|
7
8
|
import {
|
|
8
9
|
RoutesAdjacency,
|
|
@@ -24,16 +25,12 @@ describe('buildStopsAdjacencyStructure', () => {
|
|
|
24
25
|
const routesAdjacency: RoutesAdjacency = new Map([
|
|
25
26
|
[
|
|
26
27
|
'routeA',
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
]),
|
|
34
|
-
stopTimes: new Uint16Array(),
|
|
35
|
-
pickUpDropOffTypes: new Uint8Array(),
|
|
36
|
-
},
|
|
28
|
+
new Route(
|
|
29
|
+
new Uint16Array(),
|
|
30
|
+
new Uint8Array(),
|
|
31
|
+
new Uint32Array([0, 1]),
|
|
32
|
+
'service1',
|
|
33
|
+
),
|
|
37
34
|
],
|
|
38
35
|
]);
|
|
39
36
|
const transfersMap: TransfersMap = new Map([
|
|
@@ -62,16 +59,12 @@ describe('buildStopsAdjacencyStructure', () => {
|
|
|
62
59
|
const routesAdjacency: RoutesAdjacency = new Map([
|
|
63
60
|
[
|
|
64
61
|
'routeA',
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
]),
|
|
72
|
-
stopTimes: new Uint16Array(),
|
|
73
|
-
pickUpDropOffTypes: new Uint8Array(),
|
|
74
|
-
},
|
|
62
|
+
new Route(
|
|
63
|
+
new Uint16Array(),
|
|
64
|
+
new Uint8Array(),
|
|
65
|
+
new Uint32Array([0, 1]),
|
|
66
|
+
'service1',
|
|
67
|
+
),
|
|
75
68
|
],
|
|
76
69
|
]);
|
|
77
70
|
const transfersMap: TransfersMap = new Map([
|
|
@@ -222,21 +215,17 @@ describe('GTFS stop times parser', () => {
|
|
|
222
215
|
new Map([
|
|
223
216
|
[
|
|
224
217
|
'routeA_1',
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
stops: new Uint32Array([0, 1]),
|
|
228
|
-
stopIndices: new Map([
|
|
229
|
-
[0, 0],
|
|
230
|
-
[1, 1],
|
|
231
|
-
]),
|
|
232
|
-
stopTimes: new Uint16Array([
|
|
218
|
+
new Route(
|
|
219
|
+
new Uint16Array([
|
|
233
220
|
Time.fromHMS(8, 0, 0).toMinutes(),
|
|
234
221
|
Time.fromHMS(8, 5, 0).toMinutes(),
|
|
235
222
|
Time.fromHMS(8, 10, 0).toMinutes(),
|
|
236
223
|
Time.fromHMS(8, 15, 0).toMinutes(),
|
|
237
224
|
]),
|
|
238
|
-
|
|
239
|
-
|
|
225
|
+
new Uint8Array([REGULAR, REGULAR, REGULAR, REGULAR]),
|
|
226
|
+
new Uint32Array([0, 1]),
|
|
227
|
+
'routeA',
|
|
228
|
+
),
|
|
240
229
|
],
|
|
241
230
|
]),
|
|
242
231
|
);
|
|
@@ -292,14 +281,8 @@ describe('GTFS stop times parser', () => {
|
|
|
292
281
|
new Map([
|
|
293
282
|
[
|
|
294
283
|
'routeA_1',
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
stops: new Uint32Array([0, 1]),
|
|
298
|
-
stopIndices: new Map([
|
|
299
|
-
[0, 0],
|
|
300
|
-
[1, 1],
|
|
301
|
-
]),
|
|
302
|
-
stopTimes: new Uint16Array([
|
|
284
|
+
new Route(
|
|
285
|
+
new Uint16Array([
|
|
303
286
|
Time.fromHMS(8, 0, 0).toMinutes(),
|
|
304
287
|
Time.fromHMS(8, 5, 0).toMinutes(),
|
|
305
288
|
Time.fromHMS(8, 10, 0).toMinutes(),
|
|
@@ -309,8 +292,19 @@ describe('GTFS stop times parser', () => {
|
|
|
309
292
|
Time.fromHMS(9, 10, 0).toMinutes(),
|
|
310
293
|
Time.fromHMS(9, 15, 0).toMinutes(),
|
|
311
294
|
]),
|
|
312
|
-
|
|
313
|
-
|
|
295
|
+
new Uint8Array([
|
|
296
|
+
REGULAR,
|
|
297
|
+
REGULAR,
|
|
298
|
+
REGULAR,
|
|
299
|
+
REGULAR,
|
|
300
|
+
REGULAR,
|
|
301
|
+
REGULAR,
|
|
302
|
+
REGULAR,
|
|
303
|
+
REGULAR,
|
|
304
|
+
]),
|
|
305
|
+
new Uint32Array([0, 1]),
|
|
306
|
+
'routeA',
|
|
307
|
+
),
|
|
314
308
|
],
|
|
315
309
|
]),
|
|
316
310
|
);
|
|
@@ -366,14 +360,8 @@ describe('GTFS stop times parser', () => {
|
|
|
366
360
|
new Map([
|
|
367
361
|
[
|
|
368
362
|
'routeA_1',
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
stops: new Uint32Array([0, 1]),
|
|
372
|
-
stopIndices: new Map([
|
|
373
|
-
[0, 0],
|
|
374
|
-
[1, 1],
|
|
375
|
-
]),
|
|
376
|
-
stopTimes: new Uint16Array([
|
|
363
|
+
new Route(
|
|
364
|
+
new Uint16Array([
|
|
377
365
|
Time.fromHMS(8, 0, 0).toMinutes(),
|
|
378
366
|
Time.fromHMS(8, 5, 0).toMinutes(),
|
|
379
367
|
Time.fromHMS(8, 10, 0).toMinutes(),
|
|
@@ -383,8 +371,19 @@ describe('GTFS stop times parser', () => {
|
|
|
383
371
|
Time.fromHMS(9, 10, 0).toMinutes(),
|
|
384
372
|
Time.fromHMS(9, 15, 0).toMinutes(),
|
|
385
373
|
]),
|
|
386
|
-
|
|
387
|
-
|
|
374
|
+
new Uint8Array([
|
|
375
|
+
REGULAR,
|
|
376
|
+
REGULAR,
|
|
377
|
+
REGULAR,
|
|
378
|
+
REGULAR,
|
|
379
|
+
REGULAR,
|
|
380
|
+
REGULAR,
|
|
381
|
+
REGULAR,
|
|
382
|
+
REGULAR,
|
|
383
|
+
]),
|
|
384
|
+
new Uint32Array([0, 1]),
|
|
385
|
+
'routeA',
|
|
386
|
+
),
|
|
388
387
|
],
|
|
389
388
|
]),
|
|
390
389
|
);
|
|
@@ -439,34 +438,29 @@ describe('GTFS stop times parser', () => {
|
|
|
439
438
|
new Map([
|
|
440
439
|
[
|
|
441
440
|
'routeA_1',
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
stops: new Uint32Array([0, 1]),
|
|
445
|
-
stopIndices: new Map([
|
|
446
|
-
[0, 0],
|
|
447
|
-
[1, 1],
|
|
448
|
-
]),
|
|
449
|
-
stopTimes: new Uint16Array([
|
|
441
|
+
new Route(
|
|
442
|
+
new Uint16Array([
|
|
450
443
|
Time.fromHMS(8, 0, 0).toMinutes(),
|
|
451
444
|
Time.fromHMS(8, 5, 0).toMinutes(),
|
|
452
445
|
Time.fromHMS(8, 10, 0).toMinutes(),
|
|
453
446
|
Time.fromHMS(8, 15, 0).toMinutes(),
|
|
454
447
|
]),
|
|
455
|
-
|
|
456
|
-
|
|
448
|
+
new Uint8Array([REGULAR, REGULAR, REGULAR, REGULAR]),
|
|
449
|
+
new Uint32Array([0, 1]),
|
|
450
|
+
'routeA',
|
|
451
|
+
),
|
|
457
452
|
],
|
|
458
453
|
[
|
|
459
454
|
'routeA_0',
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
stops: new Uint32Array([0]),
|
|
463
|
-
stopIndices: new Map([[0, 0]]),
|
|
464
|
-
stopTimes: new Uint16Array([
|
|
455
|
+
new Route(
|
|
456
|
+
new Uint16Array([
|
|
465
457
|
Time.fromHMS(9, 0, 0).toMinutes(),
|
|
466
458
|
Time.fromHMS(9, 15, 0).toMinutes(),
|
|
467
459
|
]),
|
|
468
|
-
|
|
469
|
-
|
|
460
|
+
new Uint8Array([REGULAR, REGULAR]),
|
|
461
|
+
new Uint32Array([0]),
|
|
462
|
+
'routeA',
|
|
463
|
+
),
|
|
470
464
|
],
|
|
471
465
|
]),
|
|
472
466
|
);
|
|
@@ -517,16 +511,15 @@ describe('GTFS stop times parser', () => {
|
|
|
517
511
|
new Map([
|
|
518
512
|
[
|
|
519
513
|
'routeA_0',
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
stops: new Uint32Array([0]),
|
|
523
|
-
stopIndices: new Map([[0, 0]]),
|
|
524
|
-
stopTimes: new Uint16Array([
|
|
514
|
+
new Route(
|
|
515
|
+
new Uint16Array([
|
|
525
516
|
Time.fromHMS(8, 0, 0).toMinutes(),
|
|
526
517
|
Time.fromHMS(8, 5, 0).toMinutes(),
|
|
527
518
|
]),
|
|
528
|
-
|
|
529
|
-
|
|
519
|
+
new Uint8Array([REGULAR, REGULAR]),
|
|
520
|
+
new Uint32Array([0]),
|
|
521
|
+
'routeA',
|
|
522
|
+
),
|
|
530
523
|
],
|
|
531
524
|
]),
|
|
532
525
|
);
|
package/src/gtfs/profiles/ch.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { Maybe } from '../utils.js';
|
|
|
10
10
|
* @returns The platform corresponding to this stop.
|
|
11
11
|
*/
|
|
12
12
|
const platformParser = (stopEntry: StopEntry): Maybe<Platform> => {
|
|
13
|
-
const stopId =
|
|
13
|
+
const stopId = stopEntry.stop_id;
|
|
14
14
|
const stopParts = stopId.split(':');
|
|
15
15
|
if (stopParts.length > 2) {
|
|
16
16
|
return stopParts[2];
|
package/src/gtfs/routes.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import log from 'loglevel';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ServiceRouteId, ServiceRoutesMap } from '../timetable/timetable.js';
|
|
4
4
|
import { GtfsProfile } from './parser.js';
|
|
5
5
|
import { standardProfile } from './profiles/standard.js';
|
|
6
6
|
import { parseCsv } from './utils.js';
|
|
@@ -10,7 +10,7 @@ import { parseCsv } from './utils.js';
|
|
|
10
10
|
export type GtfsRouteType = number;
|
|
11
11
|
|
|
12
12
|
type RouteEntry = {
|
|
13
|
-
route_id:
|
|
13
|
+
route_id: ServiceRouteId;
|
|
14
14
|
agency_id: string;
|
|
15
15
|
route_short_name: string;
|
|
16
16
|
route_long_name: string;
|
|
@@ -30,7 +30,7 @@ export const parseRoutes = async (
|
|
|
30
30
|
profile: GtfsProfile = standardProfile,
|
|
31
31
|
): Promise<ServiceRoutesMap> => {
|
|
32
32
|
const routes: ServiceRoutesMap = new Map();
|
|
33
|
-
for await (const rawLine of parseCsv(routesStream)) {
|
|
33
|
+
for await (const rawLine of parseCsv(routesStream, ['route_type'])) {
|
|
34
34
|
const line = rawLine as RouteEntry;
|
|
35
35
|
const routeType = profile.routeTypeParser(line.route_type);
|
|
36
36
|
if (routeType === undefined) {
|
|
@@ -40,7 +40,7 @@ export const parseRoutes = async (
|
|
|
40
40
|
continue;
|
|
41
41
|
}
|
|
42
42
|
routes.set(line.route_id, {
|
|
43
|
-
name: line.route_short_name
|
|
43
|
+
name: line.route_short_name,
|
|
44
44
|
type: routeType,
|
|
45
45
|
});
|
|
46
46
|
}
|
package/src/gtfs/services.ts
CHANGED
|
@@ -55,7 +55,17 @@ export const parseCalendar = async (
|
|
|
55
55
|
const activeDate: number = toGtfsDate(date);
|
|
56
56
|
const weekday = date.weekday as Weekday;
|
|
57
57
|
const weekdayIndex = weekdays[weekday] as keyof CalendarEntry;
|
|
58
|
-
for await (const rawLine of parseCsv(calendarStream
|
|
58
|
+
for await (const rawLine of parseCsv(calendarStream, [
|
|
59
|
+
'monday',
|
|
60
|
+
'tuesday',
|
|
61
|
+
'wednesday',
|
|
62
|
+
'thursday',
|
|
63
|
+
'friday',
|
|
64
|
+
'saturday',
|
|
65
|
+
'sunday',
|
|
66
|
+
'start_date',
|
|
67
|
+
'end_date',
|
|
68
|
+
])) {
|
|
59
69
|
const line = rawLine as CalendarEntry;
|
|
60
70
|
if (activeDate < line.start_date || activeDate > line.end_date) {
|
|
61
71
|
// If the service is not valid on this date
|
|
@@ -82,7 +92,10 @@ export const parseCalendarDates = async (
|
|
|
82
92
|
date: DateTime,
|
|
83
93
|
) => {
|
|
84
94
|
const activeDate: number = toGtfsDate(date);
|
|
85
|
-
for await (const rawLine of parseCsv(calendarDatesStream
|
|
95
|
+
for await (const rawLine of parseCsv(calendarDatesStream, [
|
|
96
|
+
'date',
|
|
97
|
+
'exception_type',
|
|
98
|
+
])) {
|
|
86
99
|
const line = rawLine as CalendarDatesEntry;
|
|
87
100
|
if (line.date !== activeDate) {
|
|
88
101
|
// No rule on the active date
|
package/src/gtfs/stops.ts
CHANGED
|
@@ -44,11 +44,15 @@ export const parseStops = async (
|
|
|
44
44
|
): Promise<ParsedStopsMap> => {
|
|
45
45
|
const parsedStops = new Map<SourceStopId, ParsedStop>();
|
|
46
46
|
let i = 0;
|
|
47
|
-
for await (const rawLine of parseCsv(stopsStream
|
|
47
|
+
for await (const rawLine of parseCsv(stopsStream, [
|
|
48
|
+
'stop_lat',
|
|
49
|
+
'stop_lon',
|
|
50
|
+
'location_type',
|
|
51
|
+
])) {
|
|
48
52
|
const line = rawLine as StopEntry;
|
|
49
53
|
const stop: ParsedStop = {
|
|
50
54
|
id: i,
|
|
51
|
-
sourceStopId: line.stop_id
|
|
55
|
+
sourceStopId: line.stop_id,
|
|
52
56
|
name: line.stop_name,
|
|
53
57
|
lat: line.stop_lat,
|
|
54
58
|
lon: line.stop_lon,
|
|
@@ -68,7 +72,7 @@ export const parseStops = async (
|
|
|
68
72
|
console.info(`Could not parse platform for stop ${line.stop_id}.`);
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
|
-
parsedStops.set(line.stop_id
|
|
75
|
+
parsedStops.set(line.stop_id, stop);
|
|
72
76
|
i = i + 1;
|
|
73
77
|
}
|
|
74
78
|
|
package/src/gtfs/transfers.ts
CHANGED
|
@@ -42,7 +42,10 @@ export const parseTransfers = async (
|
|
|
42
42
|
): Promise<TransfersMap> => {
|
|
43
43
|
const transfers: TransfersMap = new Map();
|
|
44
44
|
|
|
45
|
-
for await (const rawLine of parseCsv(transfersStream
|
|
45
|
+
for await (const rawLine of parseCsv(transfersStream, [
|
|
46
|
+
'transfer_type',
|
|
47
|
+
'min_transfer_time',
|
|
48
|
+
])) {
|
|
46
49
|
const transferEntry = rawLine as TransferEntry;
|
|
47
50
|
|
|
48
51
|
if (
|
|
@@ -74,9 +77,9 @@ export const parseTransfers = async (
|
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
77
|
-
const fromStop = stopsMap.get(transferEntry.from_stop_id
|
|
80
|
+
const fromStop = stopsMap.get(transferEntry.from_stop_id)!;
|
|
78
81
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
79
|
-
const toStop = stopsMap.get(transferEntry.to_stop_id
|
|
82
|
+
const toStop = stopsMap.get(transferEntry.to_stop_id)!;
|
|
80
83
|
|
|
81
84
|
const transfer: Transfer = {
|
|
82
85
|
destination: toStop.id,
|
package/src/gtfs/trips.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { SourceStopId, StopId } from '../stops/stops.js';
|
|
2
|
+
import { SerializedRoute } from '../timetable/io.js';
|
|
2
3
|
import {
|
|
3
4
|
MUST_COORDINATE_WITH_DRIVER,
|
|
4
5
|
MUST_PHONE_AGENCY,
|
|
5
6
|
NOT_AVAILABLE,
|
|
6
|
-
PickUpDropOffType,
|
|
7
7
|
REGULAR,
|
|
8
8
|
Route,
|
|
9
9
|
RouteId,
|
|
10
|
+
} from '../timetable/route.js';
|
|
11
|
+
import {
|
|
10
12
|
RoutesAdjacency,
|
|
11
13
|
ServiceRouteId,
|
|
12
14
|
ServiceRoutesMap,
|
|
13
15
|
StopsAdjacency,
|
|
14
16
|
} from '../timetable/timetable.js';
|
|
15
|
-
import { ServiceIds } from './services.js';
|
|
17
|
+
import { ServiceId, ServiceIds } from './services.js';
|
|
16
18
|
import { ParsedStopsMap } from './stops.js';
|
|
17
19
|
import { GtfsTime, toTime } from './time.js';
|
|
18
20
|
import { TransfersMap } from './transfers.js';
|
|
@@ -23,8 +25,8 @@ export type TripId = string;
|
|
|
23
25
|
export type TripIdsMap = Map<TripId, ServiceRouteId>;
|
|
24
26
|
|
|
25
27
|
type TripEntry = {
|
|
26
|
-
route_id:
|
|
27
|
-
service_id:
|
|
28
|
+
route_id: ServiceRouteId;
|
|
29
|
+
service_id: ServiceId;
|
|
28
30
|
trip_id: TripId;
|
|
29
31
|
};
|
|
30
32
|
|
|
@@ -45,6 +47,8 @@ type StopTimeEntry = {
|
|
|
45
47
|
drop_off_type?: GtfsPickupDropOffType;
|
|
46
48
|
};
|
|
47
49
|
|
|
50
|
+
export type SerializedPickUpDropOffType = 0 | 1 | 2 | 3;
|
|
51
|
+
|
|
48
52
|
/**
|
|
49
53
|
* Parses the trips.txt file from a GTFS feed
|
|
50
54
|
*
|
|
@@ -59,7 +63,7 @@ export const parseTrips = async (
|
|
|
59
63
|
routeIds: ServiceRoutesMap,
|
|
60
64
|
): Promise<TripIdsMap> => {
|
|
61
65
|
const trips: TripIdsMap = new Map();
|
|
62
|
-
for await (const rawLine of parseCsv(tripsStream)) {
|
|
66
|
+
for await (const rawLine of parseCsv(tripsStream, ['stop_sequence'])) {
|
|
63
67
|
const line = rawLine as TripEntry;
|
|
64
68
|
if (!serviceIds.has(line.service_id)) {
|
|
65
69
|
// The trip doesn't correspond to an active service
|
|
@@ -81,8 +85,11 @@ export const buildStopsAdjacencyStructure = (
|
|
|
81
85
|
): StopsAdjacency => {
|
|
82
86
|
const stopsAdjacency: StopsAdjacency = new Map();
|
|
83
87
|
for (const routeId of routes.keys()) {
|
|
84
|
-
const route = routes.get(routeId)
|
|
85
|
-
|
|
88
|
+
const route = routes.get(routeId);
|
|
89
|
+
if (!route) {
|
|
90
|
+
throw new Error(`Route ${routeId} not found`);
|
|
91
|
+
}
|
|
92
|
+
for (const stop of route.stopsIterator()) {
|
|
86
93
|
if (!stopsAdjacency.get(stop) && validStops.has(stop)) {
|
|
87
94
|
stopsAdjacency.set(stop, { routes: [], transfers: [] });
|
|
88
95
|
}
|
|
@@ -154,7 +161,6 @@ export const parseStopTimes = async (
|
|
|
154
161
|
route = {
|
|
155
162
|
serviceRouteId: gtfsRouteId,
|
|
156
163
|
stops: stopsArray,
|
|
157
|
-
stopIndices: new Map(stops.map((stop, i) => [stop, i])),
|
|
158
164
|
stopTimes: stopTimesArray,
|
|
159
165
|
pickUpDropOffTypes: pickUpDropOffTypesArray,
|
|
160
166
|
};
|
|
@@ -223,17 +229,17 @@ export const parseStopTimes = async (
|
|
|
223
229
|
dropOffTypes = [];
|
|
224
230
|
};
|
|
225
231
|
|
|
226
|
-
const routes:
|
|
232
|
+
const routes: Map<RouteId, SerializedRoute> = new Map();
|
|
227
233
|
|
|
228
234
|
let previousSeq = 0;
|
|
229
235
|
let stops: StopId[] = [];
|
|
230
236
|
let arrivalTimes: number[] = [];
|
|
231
237
|
let departureTimes: number[] = [];
|
|
232
|
-
let pickUpTypes:
|
|
233
|
-
let dropOffTypes:
|
|
238
|
+
let pickUpTypes: SerializedPickUpDropOffType[] = [];
|
|
239
|
+
let dropOffTypes: SerializedPickUpDropOffType[] = [];
|
|
234
240
|
let currentTripId: TripId | undefined = undefined;
|
|
235
241
|
|
|
236
|
-
for await (const rawLine of parseCsv(stopTimesStream)) {
|
|
242
|
+
for await (const rawLine of parseCsv(stopTimesStream, ['stop_sequence'])) {
|
|
237
243
|
const line = rawLine as StopTimeEntry;
|
|
238
244
|
if (line.trip_id === currentTripId && line.stop_sequence <= previousSeq) {
|
|
239
245
|
console.warn(`Stop sequences not increasing for trip ${line.trip_id}.`);
|
|
@@ -253,7 +259,7 @@ export const parseStopTimes = async (
|
|
|
253
259
|
}
|
|
254
260
|
currentTripId = line.trip_id;
|
|
255
261
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
256
|
-
stops.push(stopsMap.get(line.stop_id
|
|
262
|
+
stops.push(stopsMap.get(line.stop_id)!.id);
|
|
257
263
|
const departure = line.departure_time ?? line.arrival_time;
|
|
258
264
|
const arrival = line.arrival_time ?? line.departure_time;
|
|
259
265
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -269,15 +275,26 @@ export const parseStopTimes = async (
|
|
|
269
275
|
addTrip(currentTripId);
|
|
270
276
|
}
|
|
271
277
|
|
|
272
|
-
|
|
278
|
+
const routesAdjacency: RoutesAdjacency = new Map<RouteId, Route>();
|
|
279
|
+
for (const [routeId, routeData] of routes) {
|
|
280
|
+
routesAdjacency.set(
|
|
281
|
+
routeId,
|
|
282
|
+
new Route(
|
|
283
|
+
routeData.stopTimes,
|
|
284
|
+
routeData.pickUpDropOffTypes,
|
|
285
|
+
routeData.stops,
|
|
286
|
+
routeData.serviceRouteId,
|
|
287
|
+
),
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
return routesAdjacency;
|
|
273
291
|
};
|
|
274
292
|
|
|
275
293
|
const parsePickupDropOffType = (
|
|
276
294
|
gtfsType?: GtfsPickupDropOffType,
|
|
277
|
-
):
|
|
295
|
+
): SerializedPickUpDropOffType => {
|
|
278
296
|
switch (gtfsType) {
|
|
279
297
|
default:
|
|
280
|
-
console.warn(`Unknown pickup/drop-off type ${gtfsType}`);
|
|
281
298
|
return REGULAR;
|
|
282
299
|
case 0:
|
|
283
300
|
return REGULAR;
|
package/src/gtfs/utils.ts
CHANGED
|
@@ -28,12 +28,23 @@ export const hashIds = (ids: number[]): string => {
|
|
|
28
28
|
* @param stream The CSV stream.
|
|
29
29
|
* @returns A parser from the csv-parse library.
|
|
30
30
|
*/
|
|
31
|
-
export const parseCsv = (
|
|
31
|
+
export const parseCsv = (
|
|
32
|
+
stream: NodeJS.ReadableStream,
|
|
33
|
+
numericColumns: string[] = [],
|
|
34
|
+
): Parser => {
|
|
32
35
|
return stream.pipe(
|
|
33
36
|
parse({
|
|
34
37
|
delimiter: ',',
|
|
35
38
|
columns: true,
|
|
36
|
-
cast:
|
|
39
|
+
cast: (value, context) => {
|
|
40
|
+
if (
|
|
41
|
+
typeof context.column === 'string' &&
|
|
42
|
+
numericColumns.includes(context.column)
|
|
43
|
+
) {
|
|
44
|
+
return Number(value);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
},
|
|
37
48
|
bom: true,
|
|
38
49
|
ignore_last_delimiters: true,
|
|
39
50
|
relax_column_count: true,
|
package/src/parser.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { GtfsProfile } from './gtfs/parser.js';
|
|
2
|
+
import { GtfsParser } from './gtfs/parser.js';
|
|
2
3
|
import { chGtfsProfile } from './gtfs/profiles/ch.js';
|
|
3
4
|
|
|
4
|
-
export { chGtfsProfile, GtfsParser
|
|
5
|
+
export { chGtfsProfile, GtfsParser };
|
|
6
|
+
export type { GtfsProfile };
|
package/src/router.ts
CHANGED
|
@@ -1,37 +1,43 @@
|
|
|
1
1
|
import { Plotter } from './routing/plotter.js';
|
|
2
2
|
import { Query } from './routing/query.js';
|
|
3
3
|
import { Result } from './routing/result.js';
|
|
4
|
-
import { Leg,
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import type { Leg, Transfer, VehicleLeg } from './routing/route.js';
|
|
5
|
+
import { Route } from './routing/route.js';
|
|
6
|
+
import type { ReachingTime } from './routing/router.js';
|
|
7
|
+
import { Router } from './routing/router.js';
|
|
8
|
+
import type { LocationType, SourceStopId, StopId } from './stops/stops.js';
|
|
9
|
+
import type { Stop } from './stops/stops.js';
|
|
7
10
|
import { StopsIndex } from './stops/stopsIndex.js';
|
|
8
11
|
import { Duration } from './timetable/duration.js';
|
|
9
12
|
import { Time } from './timetable/time.js';
|
|
10
|
-
import {
|
|
13
|
+
import type {
|
|
11
14
|
RouteType,
|
|
12
15
|
ServiceRoute,
|
|
13
|
-
Timetable,
|
|
14
16
|
TransferType,
|
|
15
17
|
} from './timetable/timetable.js';
|
|
18
|
+
import { Timetable } from './timetable/timetable.js';
|
|
16
19
|
|
|
17
20
|
export {
|
|
18
21
|
Duration,
|
|
19
|
-
Leg,
|
|
20
|
-
LocationType,
|
|
21
22
|
Plotter,
|
|
22
23
|
Query,
|
|
23
|
-
ReachingTime,
|
|
24
24
|
Result,
|
|
25
25
|
Route,
|
|
26
26
|
Router,
|
|
27
|
+
StopsIndex,
|
|
28
|
+
Time,
|
|
29
|
+
Timetable,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type {
|
|
33
|
+
Leg,
|
|
34
|
+
LocationType,
|
|
35
|
+
ReachingTime,
|
|
27
36
|
RouteType,
|
|
28
37
|
ServiceRoute,
|
|
29
38
|
SourceStopId,
|
|
30
39
|
Stop,
|
|
31
40
|
StopId,
|
|
32
|
-
StopsIndex,
|
|
33
|
-
Time,
|
|
34
|
-
Timetable,
|
|
35
41
|
Transfer,
|
|
36
42
|
TransferType,
|
|
37
43
|
VehicleLeg,
|