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.
Files changed (81) hide show
  1. package/.cspell.json +12 -1
  2. package/.gitattributes +3 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +3 -0
  4. package/.github/workflows/minotor.yml +17 -1
  5. package/CHANGELOG.md +2 -2
  6. package/README.md +34 -14
  7. package/dist/__e2e__/router.test.d.ts +1 -0
  8. package/dist/cli/perf.d.ts +28 -0
  9. package/dist/cli/utils.d.ts +6 -2
  10. package/dist/cli.mjs +1967 -823
  11. package/dist/cli.mjs.map +1 -1
  12. package/dist/gtfs/trips.d.ts +1 -0
  13. package/dist/gtfs/utils.d.ts +1 -1
  14. package/dist/parser.cjs.js +1030 -627
  15. package/dist/parser.cjs.js.map +1 -1
  16. package/dist/parser.d.ts +4 -2
  17. package/dist/parser.esm.js +1030 -627
  18. package/dist/parser.esm.js.map +1 -1
  19. package/dist/router.cjs.js +1 -1
  20. package/dist/router.cjs.js.map +1 -1
  21. package/dist/router.d.ts +10 -5
  22. package/dist/router.esm.js +1 -1
  23. package/dist/router.esm.js.map +1 -1
  24. package/dist/router.umd.js +1 -1
  25. package/dist/router.umd.js.map +1 -1
  26. package/dist/routing/__tests__/result.test.d.ts +1 -0
  27. package/dist/routing/query.d.ts +27 -6
  28. package/dist/routing/result.d.ts +1 -1
  29. package/dist/routing/route.d.ts +47 -2
  30. package/dist/routing/router.d.ts +15 -1
  31. package/dist/stops/stopsIndex.d.ts +3 -3
  32. package/dist/timetable/__tests__/route.test.d.ts +1 -0
  33. package/dist/timetable/__tests__/time.test.d.ts +1 -0
  34. package/dist/timetable/io.d.ts +7 -1
  35. package/dist/timetable/proto/timetable.d.ts +1 -1
  36. package/dist/timetable/route.d.ts +155 -0
  37. package/dist/timetable/time.d.ts +21 -0
  38. package/dist/timetable/timetable.d.ts +41 -61
  39. package/package.json +36 -34
  40. package/src/__e2e__/benchmark.json +22 -0
  41. package/src/__e2e__/router.test.ts +209 -0
  42. package/src/__e2e__/timetable/stops.bin +3 -0
  43. package/src/__e2e__/timetable/timetable.bin +3 -0
  44. package/src/cli/minotor.ts +51 -1
  45. package/src/cli/perf.ts +136 -0
  46. package/src/cli/repl.ts +26 -13
  47. package/src/cli/utils.ts +6 -28
  48. package/src/gtfs/__tests__/parser.test.ts +12 -15
  49. package/src/gtfs/__tests__/services.test.ts +1 -0
  50. package/src/gtfs/__tests__/transfers.test.ts +0 -1
  51. package/src/gtfs/__tests__/trips.test.ts +67 -74
  52. package/src/gtfs/profiles/ch.ts +1 -1
  53. package/src/gtfs/routes.ts +4 -4
  54. package/src/gtfs/services.ts +15 -2
  55. package/src/gtfs/stops.ts +7 -3
  56. package/src/gtfs/transfers.ts +6 -3
  57. package/src/gtfs/trips.ts +33 -16
  58. package/src/gtfs/utils.ts +13 -2
  59. package/src/parser.ts +4 -2
  60. package/src/router.ts +17 -11
  61. package/src/routing/__tests__/result.test.ts +392 -0
  62. package/src/routing/__tests__/router.test.ts +94 -137
  63. package/src/routing/query.ts +28 -7
  64. package/src/routing/result.ts +10 -5
  65. package/src/routing/route.ts +95 -9
  66. package/src/routing/router.ts +82 -66
  67. package/src/stops/__tests__/io.test.ts +1 -1
  68. package/src/stops/__tests__/stopFinder.test.ts +1 -1
  69. package/src/stops/proto/stops.ts +4 -4
  70. package/src/stops/stopsIndex.ts +3 -3
  71. package/src/timetable/__tests__/io.test.ts +16 -23
  72. package/src/timetable/__tests__/route.test.ts +317 -0
  73. package/src/timetable/__tests__/time.test.ts +494 -0
  74. package/src/timetable/__tests__/timetable.test.ts +64 -75
  75. package/src/timetable/io.ts +32 -26
  76. package/src/timetable/proto/timetable.proto +1 -1
  77. package/src/timetable/proto/timetable.ts +13 -13
  78. package/src/timetable/route.ts +347 -0
  79. package/src/timetable/time.ts +40 -8
  80. package/src/timetable/timetable.ts +74 -165
  81. 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
- serviceRouteId: 'service1',
29
- stops: new Uint32Array([0, 1]),
30
- stopIndices: new Map([
31
- [0, 0],
32
- [1, 1],
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
- serviceRouteId: 'service1',
67
- stops: new Uint32Array([0, 1]),
68
- stopIndices: new Map([
69
- [0, 0],
70
- [1, 1],
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
- serviceRouteId: 'routeA',
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
- pickUpDropOffTypes: new Uint8Array([0, 0, 0, 0]),
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
- serviceRouteId: 'routeA',
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
- pickUpDropOffTypes: new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]),
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
- serviceRouteId: 'routeA',
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
- pickUpDropOffTypes: new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]),
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
- serviceRouteId: 'routeA',
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
- pickUpDropOffTypes: new Uint8Array([0, 0, 0, 0]),
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
- serviceRouteId: 'routeA',
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
- pickUpDropOffTypes: new Uint8Array([0, 0]),
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
- serviceRouteId: 'routeA',
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
- pickUpDropOffTypes: new Uint8Array([0, 0]),
529
- },
519
+ new Uint8Array([REGULAR, REGULAR]),
520
+ new Uint32Array([0]),
521
+ 'routeA',
522
+ ),
530
523
  ],
531
524
  ]),
532
525
  );
@@ -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 = String(stopEntry.stop_id);
13
+ const stopId = stopEntry.stop_id;
14
14
  const stopParts = stopId.split(':');
15
15
  if (stopParts.length > 2) {
16
16
  return stopParts[2];
@@ -1,6 +1,6 @@
1
1
  import log from 'loglevel';
2
2
 
3
- import { RouteId, ServiceRoutesMap } from '../timetable/timetable.js';
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: RouteId;
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
  }
@@ -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 + '', stop);
75
+ parsedStops.set(line.stop_id, stop);
72
76
  i = i + 1;
73
77
  }
74
78
 
@@ -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: RouteId;
27
- service_id: ServiceRouteId;
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) as Route;
85
- for (const stop of route.stops) {
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: RoutesAdjacency = new Map();
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: PickUpDropOffType[] = [];
233
- let dropOffTypes: PickUpDropOffType[] = [];
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 + '')!.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
- return routes;
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
- ): PickUpDropOffType => {
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 = (stream: NodeJS.ReadableStream): Parser => {
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: true,
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 { GtfsParser, GtfsProfile } from './gtfs/parser.js';
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, GtfsProfile };
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, Route, Transfer, VehicleLeg } from './routing/route.js';
5
- import { ReachingTime, Router } from './routing/router.js';
6
- import { LocationType, SourceStopId, Stop, StopId } from './stops/stops.js';
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,