minotor 7.0.2 → 9.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.
Files changed (60) hide show
  1. package/.cspell.json +11 -1
  2. package/CHANGELOG.md +8 -3
  3. package/README.md +26 -24
  4. package/dist/cli.mjs +1786 -791
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/gtfs/transfers.d.ts +29 -5
  7. package/dist/gtfs/trips.d.ts +10 -5
  8. package/dist/parser.cjs.js +972 -525
  9. package/dist/parser.cjs.js.map +1 -1
  10. package/dist/parser.esm.js +972 -525
  11. package/dist/parser.esm.js.map +1 -1
  12. package/dist/router.cjs.js +1 -1
  13. package/dist/router.cjs.js.map +1 -1
  14. package/dist/router.d.ts +2 -2
  15. package/dist/router.esm.js +1 -1
  16. package/dist/router.esm.js.map +1 -1
  17. package/dist/router.umd.js +1 -1
  18. package/dist/router.umd.js.map +1 -1
  19. package/dist/routing/__tests__/plotter.test.d.ts +1 -0
  20. package/dist/routing/plotter.d.ts +42 -3
  21. package/dist/routing/result.d.ts +23 -7
  22. package/dist/routing/route.d.ts +2 -0
  23. package/dist/routing/router.d.ts +78 -19
  24. package/dist/timetable/__tests__/tripBoardingId.test.d.ts +1 -0
  25. package/dist/timetable/io.d.ts +4 -2
  26. package/dist/timetable/proto/timetable.d.ts +15 -1
  27. package/dist/timetable/route.d.ts +48 -23
  28. package/dist/timetable/timetable.d.ts +24 -7
  29. package/dist/timetable/tripBoardingId.d.ts +34 -0
  30. package/package.json +1 -1
  31. package/src/__e2e__/router.test.ts +114 -105
  32. package/src/__e2e__/timetable/stops.bin +2 -2
  33. package/src/__e2e__/timetable/timetable.bin +2 -2
  34. package/src/cli/repl.ts +245 -1
  35. package/src/gtfs/__tests__/parser.test.ts +19 -4
  36. package/src/gtfs/__tests__/transfers.test.ts +773 -37
  37. package/src/gtfs/__tests__/trips.test.ts +308 -27
  38. package/src/gtfs/parser.ts +36 -6
  39. package/src/gtfs/transfers.ts +193 -19
  40. package/src/gtfs/trips.ts +58 -21
  41. package/src/router.ts +2 -2
  42. package/src/routing/__tests__/plotter.test.ts +230 -0
  43. package/src/routing/__tests__/result.test.ts +486 -125
  44. package/src/routing/__tests__/route.test.ts +7 -3
  45. package/src/routing/__tests__/router.test.ts +380 -172
  46. package/src/routing/plotter.ts +279 -48
  47. package/src/routing/result.ts +114 -34
  48. package/src/routing/route.ts +0 -3
  49. package/src/routing/router.ts +344 -211
  50. package/src/timetable/__tests__/io.test.ts +34 -1
  51. package/src/timetable/__tests__/route.test.ts +74 -81
  52. package/src/timetable/__tests__/timetable.test.ts +232 -61
  53. package/src/timetable/__tests__/tripBoardingId.test.ts +57 -0
  54. package/src/timetable/io.ts +72 -10
  55. package/src/timetable/proto/timetable.proto +16 -2
  56. package/src/timetable/proto/timetable.ts +256 -22
  57. package/src/timetable/route.ts +174 -58
  58. package/src/timetable/timetable.ts +66 -16
  59. package/src/timetable/tripBoardingId.ts +94 -0
  60. package/tsconfig.json +2 -2
@@ -1,12 +1,17 @@
1
1
  import { SourceStopId, StopId } from '../stops/stops.js';
2
2
  import { Duration } from '../timetable/duration.js';
3
+ import { Route } from '../timetable/route.js';
3
4
  import {
4
5
  ServiceRouteId,
6
+ Timetable,
5
7
  Transfer,
6
8
  TransferType,
9
+ TripBoarding,
10
+ TripContinuations,
7
11
  } from '../timetable/timetable.js';
12
+ import { encode } from '../timetable/tripBoardingId.js';
8
13
  import { GtfsStopsMap } from './stops.js';
9
- import { TripId } from './trips.js';
14
+ import { GtfsTripId, TripsMapping } from './trips.js';
10
15
  import { parseCsv } from './utils.js';
11
16
 
12
17
  export type GtfsTransferType =
@@ -19,11 +24,18 @@ export type GtfsTransferType =
19
24
 
20
25
  export type TransfersMap = Map<StopId, Transfer[]>;
21
26
 
27
+ export type GtfsTripContinuation = {
28
+ fromStop: StopId;
29
+ fromTrip: GtfsTripId;
30
+ toStop: StopId;
31
+ toTrip: GtfsTripId;
32
+ };
33
+
22
34
  export type TransferEntry = {
23
35
  from_stop_id?: SourceStopId;
24
36
  to_stop_id?: SourceStopId;
25
- from_trip_id?: TripId;
26
- to_trip_id?: TripId;
37
+ from_trip_id?: GtfsTripId;
38
+ to_trip_id?: GtfsTripId;
27
39
  from_route_id?: ServiceRouteId;
28
40
  to_route_id?: ServiceRouteId;
29
41
  transfer_type: GtfsTransferType;
@@ -39,9 +51,12 @@ export type TransferEntry = {
39
51
  export const parseTransfers = async (
40
52
  transfersStream: NodeJS.ReadableStream,
41
53
  stopsMap: GtfsStopsMap,
42
- ): Promise<TransfersMap> => {
54
+ ): Promise<{
55
+ transfers: TransfersMap;
56
+ tripContinuations: GtfsTripContinuation[];
57
+ }> => {
43
58
  const transfers: TransfersMap = new Map();
44
-
59
+ const tripContinuations: GtfsTripContinuation[] = [];
45
60
  for await (const rawLine of parseCsv(transfersStream, [
46
61
  'transfer_type',
47
62
  'min_transfer_time',
@@ -54,37 +69,67 @@ export const parseTransfers = async (
54
69
  ) {
55
70
  continue;
56
71
  }
57
- if (transferEntry.from_trip_id && transferEntry.to_trip_id) {
72
+ if (!transferEntry.from_stop_id || !transferEntry.to_stop_id) {
73
+ console.warn(`Missing transfer origin or destination stop.`);
74
+ continue;
75
+ }
76
+ const fromStop = stopsMap.get(transferEntry.from_stop_id);
77
+ const toStop = stopsMap.get(transferEntry.to_stop_id);
78
+
79
+ if (!fromStop || !toStop) {
58
80
  console.warn(
59
- `Unsupported transfer between trips ${transferEntry.from_trip_id} and ${transferEntry.to_trip_id}.`,
81
+ `Transfer references non-existent stop(s): from_stop_id=${transferEntry.from_stop_id}, to_stop_id=${transferEntry.to_stop_id}`,
60
82
  );
61
83
  continue;
62
84
  }
63
- if (transferEntry.from_route_id && transferEntry.to_route_id) {
85
+
86
+ if (transferEntry.transfer_type === 4) {
87
+ if (
88
+ transferEntry.from_trip_id === undefined ||
89
+ transferEntry.from_trip_id === '' ||
90
+ transferEntry.to_trip_id === undefined ||
91
+ transferEntry.to_trip_id === ''
92
+ ) {
93
+ console.warn(
94
+ `Unsupported in-seat transfer, missing from_trip_id and/or to_trip_id.`,
95
+ );
96
+ continue;
97
+ }
98
+ const tripBoardingEntry: GtfsTripContinuation = {
99
+ fromStop: fromStop.id,
100
+ fromTrip: transferEntry.from_trip_id,
101
+ toStop: toStop.id,
102
+ toTrip: transferEntry.to_trip_id,
103
+ };
104
+ tripContinuations.push(tripBoardingEntry);
105
+ continue;
106
+ }
107
+ if (transferEntry.from_trip_id && transferEntry.to_trip_id) {
64
108
  console.warn(
65
- `Unsupported transfer between routes ${transferEntry.from_route_id} and ${transferEntry.to_route_id}.`,
109
+ `Unsupported transfer of type ${transferEntry.transfer_type} between trips ${transferEntry.from_trip_id} and ${transferEntry.to_trip_id}.`,
66
110
  );
67
111
  continue;
68
112
  }
69
- if (!transferEntry.from_stop_id || !transferEntry.to_stop_id) {
70
- console.warn(`Missing transfer origin or destination stop.`);
113
+ if (transferEntry.from_route_id && transferEntry.to_route_id) {
114
+ console.warn(
115
+ `Unsupported transfer of type ${transferEntry.transfer_type} between routes ${transferEntry.from_route_id} and ${transferEntry.to_route_id}.`,
116
+ );
71
117
  continue;
72
118
  }
73
- if (transferEntry.transfer_type === 2 && !transferEntry.min_transfer_time) {
119
+
120
+ if (
121
+ transferEntry.transfer_type === 2 &&
122
+ transferEntry.min_transfer_time === undefined
123
+ ) {
74
124
  console.info(
75
125
  `Missing minimum transfer time between ${transferEntry.from_stop_id} and ${transferEntry.to_stop_id}.`,
76
126
  );
77
127
  }
78
128
 
79
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
80
- const fromStop = stopsMap.get(transferEntry.from_stop_id)!;
81
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
82
- const toStop = stopsMap.get(transferEntry.to_stop_id)!;
83
-
84
129
  const transfer: Transfer = {
85
130
  destination: toStop.id,
86
131
  type: parseGtfsTransferType(transferEntry.transfer_type),
87
- ...(transferEntry.min_transfer_time && {
132
+ ...(transferEntry.min_transfer_time !== undefined && {
88
133
  minTransferTime: Duration.fromSeconds(transferEntry.min_transfer_time),
89
134
  }),
90
135
  };
@@ -93,7 +138,136 @@ export const parseTransfers = async (
93
138
  fromStopTransfers.push(transfer);
94
139
  transfers.set(fromStop.id, fromStopTransfers);
95
140
  }
96
- return transfers;
141
+ return {
142
+ transfers,
143
+ tripContinuations,
144
+ };
145
+ };
146
+
147
+ /**
148
+ * Disambiguates stops involved in a transfer.
149
+ *
150
+ * The GTFS specification only refers to a stopId in the trip-to-trip transfers and not the
151
+ * specific stop index in the route. For routes that have multiple stops with the same stopId,
152
+ * we need to determine which are the from / to stop indices in respective routes.
153
+ * We do so by picking the stop indices leading to the most coherent transfer.
154
+ * (we pick the closest from stop index happening after the to stop index).
155
+ */
156
+ const disambiguateTransferStopsIndices = (
157
+ fromStop: StopId,
158
+ fromRoute: Route,
159
+ fromTripIndex: number,
160
+ toStop: StopId,
161
+ toRoute: Route,
162
+ toTripIndex: number,
163
+ ): { fromStopIndex: number; toStopIndex: number } | undefined => {
164
+ const fromStopIndices = fromRoute.stopRouteIndices(fromStop);
165
+ const toStopIndices = toRoute.stopRouteIndices(toStop);
166
+ let bestFromStopIndex: number | undefined;
167
+ let bestToStopIndex: number | undefined;
168
+ let bestTimeDifference = Infinity;
169
+
170
+ for (const originStopIndex of fromStopIndices) {
171
+ const fromArrivalTime = fromRoute.arrivalAt(originStopIndex, fromTripIndex);
172
+ for (const toStopIndex of toStopIndices) {
173
+ const toDepartureTime = toRoute.departureFrom(toStopIndex, toTripIndex);
174
+ if (toDepartureTime.isAfter(fromArrivalTime)) {
175
+ const timeDifference =
176
+ toDepartureTime.toMinutes() - fromArrivalTime.toMinutes();
177
+ if (timeDifference < bestTimeDifference) {
178
+ bestTimeDifference = timeDifference;
179
+ bestFromStopIndex = originStopIndex;
180
+ bestToStopIndex = toStopIndex;
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ if (bestFromStopIndex !== undefined && bestToStopIndex !== undefined) {
187
+ return {
188
+ fromStopIndex: bestFromStopIndex,
189
+ toStopIndex: bestToStopIndex,
190
+ };
191
+ }
192
+
193
+ return undefined;
194
+ };
195
+
196
+ /**
197
+ * Builds trip continuations map from GTFS trip continuation data.
198
+ *
199
+ * This function processes GTFS in-seat transfer data and creates a mapping
200
+ * from trip boarding IDs to continuation boarding information. It disambiguates
201
+ * stop indices when routes have multiple stops with the same ID by finding
202
+ * the most coherent transfer timing.
203
+ *
204
+ * @param tripsMapping Mapping from GTFS trip IDs to internal trip representations
205
+ * @param tripContinuations Array of GTFS trip continuation data from transfers.txt
206
+ * @param timetable The timetable containing route and timing information
207
+ * @param activeStopIds Set of stop IDs that are active/enabled in the system
208
+ * @returns A map from trip boarding IDs to arrays of continuation boarding options
209
+ */
210
+ export const buildTripContinuations = (
211
+ tripsMapping: TripsMapping,
212
+ tripContinuations: GtfsTripContinuation[],
213
+ timetable: Timetable,
214
+ activeStopIds: Set<StopId>,
215
+ ): TripContinuations => {
216
+ const continuations: TripContinuations = new Map();
217
+
218
+ for (const gtfsContinuation of tripContinuations) {
219
+ if (
220
+ !activeStopIds.has(gtfsContinuation.fromStop) ||
221
+ !activeStopIds.has(gtfsContinuation.toStop)
222
+ ) {
223
+ continue;
224
+ }
225
+ const fromTripMapping = tripsMapping.get(gtfsContinuation.fromTrip);
226
+ const toTripMapping = tripsMapping.get(gtfsContinuation.toTrip);
227
+
228
+ if (!fromTripMapping || !toTripMapping) {
229
+ continue;
230
+ }
231
+
232
+ const fromRoute = timetable.getRoute(fromTripMapping.routeId);
233
+ const toRoute = timetable.getRoute(toTripMapping.routeId);
234
+
235
+ if (!fromRoute || !toRoute) {
236
+ continue;
237
+ }
238
+
239
+ const bestStopIndices = disambiguateTransferStopsIndices(
240
+ gtfsContinuation.fromStop,
241
+ fromRoute,
242
+ fromTripMapping.tripRouteIndex,
243
+ gtfsContinuation.toStop,
244
+ toRoute,
245
+ toTripMapping.tripRouteIndex,
246
+ );
247
+
248
+ if (!bestStopIndices) {
249
+ // No valid continuation found
250
+ continue;
251
+ }
252
+
253
+ const tripBoardingId = encode(
254
+ bestStopIndices.fromStopIndex,
255
+ fromTripMapping.routeId,
256
+ fromTripMapping.tripRouteIndex,
257
+ );
258
+
259
+ const continuationBoarding: TripBoarding = {
260
+ hopOnStopIndex: bestStopIndices.toStopIndex,
261
+ routeId: toTripMapping.routeId,
262
+ tripIndex: toTripMapping.tripRouteIndex,
263
+ };
264
+
265
+ const existingContinuations = continuations.get(tripBoardingId) || [];
266
+ existingContinuations.push(continuationBoarding);
267
+ continuations.set(tripBoardingId, existingContinuations);
268
+ }
269
+
270
+ return continuations;
97
271
  };
98
272
 
99
273
  const parseGtfsTransferType = (
package/src/gtfs/trips.ts CHANGED
@@ -6,6 +6,8 @@ import {
6
6
  NOT_AVAILABLE,
7
7
  REGULAR,
8
8
  Route,
9
+ RouteId,
10
+ TripRouteIndex,
9
11
  } from '../timetable/route.js';
10
12
  import {
11
13
  ServiceRoute,
@@ -19,14 +21,19 @@ import { GtfsTime, toTime } from './time.js';
19
21
  import { TransfersMap } from './transfers.js';
20
22
  import { hashIds, parseCsv } from './utils.js';
21
23
 
22
- export type TripId = string;
24
+ export type GtfsTripId = string;
23
25
 
24
- export type TripIdsMap = Map<TripId, GtfsRouteId>;
26
+ export type GtfsTripIdsMap = Map<GtfsTripId, GtfsRouteId>;
27
+
28
+ export type TripsMapping = Map<
29
+ GtfsTripId,
30
+ { routeId: RouteId; tripRouteIndex: TripRouteIndex }
31
+ >;
25
32
 
26
33
  type TripEntry = {
27
34
  route_id: GtfsRouteId;
28
35
  service_id: ServiceId;
29
- trip_id: TripId;
36
+ trip_id: GtfsTripId;
30
37
  };
31
38
 
32
39
  export type GtfsPickupDropOffType =
@@ -37,7 +44,7 @@ export type GtfsPickupDropOffType =
37
44
  | '3'; // Must coordinate with driver
38
45
 
39
46
  type StopTimeEntry = {
40
- trip_id: TripId;
47
+ trip_id: GtfsTripId;
41
48
  arrival_time?: GtfsTime;
42
49
  departure_time?: GtfsTime;
43
50
  stop_id: SourceStopId;
@@ -55,6 +62,7 @@ type RouteBuilder = {
55
62
  serviceRouteId: ServiceRouteId;
56
63
  stops: StopId[];
57
64
  trips: Array<{
65
+ gtfsTripId: GtfsTripId;
58
66
  firstDeparture: number;
59
67
  arrivalTimes: number[];
60
68
  departureTimes: number[];
@@ -109,7 +117,9 @@ export const encodePickUpDropOffTypes = (
109
117
  /**
110
118
  * Sorts trips by departure time and creates optimized typed arrays
111
119
  */
112
- const finalizeRouteFromBuilder = (builder: RouteBuilder): SerializedRoute => {
120
+ const finalizeRouteFromBuilder = (
121
+ builder: RouteBuilder,
122
+ ): [SerializedRoute, GtfsTripId[]] => {
113
123
  builder.trips.sort((a, b) => a.firstDeparture - b.firstDeparture);
114
124
 
115
125
  const stopsCount = builder.stops.length;
@@ -119,11 +129,13 @@ const finalizeRouteFromBuilder = (builder: RouteBuilder): SerializedRoute => {
119
129
  const allPickUpTypes: SerializedPickUpDropOffType[] = [];
120
130
  const allDropOffTypes: SerializedPickUpDropOffType[] = [];
121
131
 
132
+ const gtfsTripIds = [];
122
133
  for (let tripIndex = 0; tripIndex < tripsCount; tripIndex++) {
123
134
  const trip = builder.trips[tripIndex];
124
135
  if (!trip) {
125
136
  throw new Error(`Missing trip data at index ${tripIndex}`);
126
137
  }
138
+ gtfsTripIds.push(trip.gtfsTripId);
127
139
  const baseIndex = tripIndex * stopsCount * 2;
128
140
 
129
141
  for (let stopIndex = 0; stopIndex < stopsCount; stopIndex++) {
@@ -155,12 +167,15 @@ const finalizeRouteFromBuilder = (builder: RouteBuilder): SerializedRoute => {
155
167
  allPickUpTypes,
156
168
  allDropOffTypes,
157
169
  );
158
- return {
159
- serviceRouteId: builder.serviceRouteId,
160
- stops: stopsArray,
161
- stopTimes: stopTimesArray,
162
- pickUpDropOffTypes: pickUpDropOffTypesArray,
163
- };
170
+ return [
171
+ {
172
+ serviceRouteId: builder.serviceRouteId,
173
+ stops: stopsArray,
174
+ stopTimes: stopTimesArray,
175
+ pickUpDropOffTypes: pickUpDropOffTypesArray,
176
+ },
177
+ gtfsTripIds,
178
+ ];
164
179
  };
165
180
 
166
181
  /**
@@ -175,8 +190,8 @@ export const parseTrips = async (
175
190
  tripsStream: NodeJS.ReadableStream,
176
191
  serviceIds: ServiceIds,
177
192
  validGtfsRoutes: GtfsRoutesMap,
178
- ): Promise<TripIdsMap> => {
179
- const trips: TripIdsMap = new Map();
193
+ ): Promise<GtfsTripIdsMap> => {
194
+ const trips: GtfsTripIdsMap = new Map();
180
195
  for await (const rawLine of parseCsv(tripsStream, ['stop_sequence'])) {
181
196
  const line = rawLine as TripEntry;
182
197
  if (!serviceIds.has(line.service_id)) {
@@ -199,10 +214,11 @@ export const buildStopsAdjacencyStructure = (
199
214
  nbStops: number,
200
215
  activeStops: Set<StopId>,
201
216
  ): StopAdjacency[] => {
202
- // TODO somehow works when it's a map
203
217
  const stopsAdjacency = new Array<StopAdjacency>(nbStops);
204
218
  for (let i = 0; i < nbStops; i++) {
205
- stopsAdjacency[i] = { routes: [], transfers: [] };
219
+ stopsAdjacency[i] = {
220
+ routes: [],
221
+ };
206
222
  }
207
223
  for (let index = 0; index < routes.length; index++) {
208
224
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -229,7 +245,11 @@ export const buildStopsAdjacencyStructure = (
229
245
  const transfer = transfers[i]!;
230
246
  if (activeStops.has(stop) || activeStops.has(transfer.destination)) {
231
247
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
232
- stopsAdjacency[stop]!.transfers.push(transfer);
248
+ const stopAdj = stopsAdjacency[stop]!;
249
+ if (!stopAdj.transfers) {
250
+ stopAdj.transfers = [];
251
+ }
252
+ stopAdj.transfers.push(transfer);
233
253
  activeStops.add(transfer.destination);
234
254
  activeStops.add(stop);
235
255
  }
@@ -250,16 +270,17 @@ export const buildStopsAdjacencyStructure = (
250
270
  export const parseStopTimes = async (
251
271
  stopTimesStream: NodeJS.ReadableStream,
252
272
  stopsMap: GtfsStopsMap,
253
- activeTripIds: TripIdsMap,
273
+ activeTripIds: GtfsTripIdsMap,
254
274
  activeStopIds: Set<StopId>,
255
275
  ): Promise<{
256
276
  routes: Route[];
257
277
  serviceRoutesMap: Map<GtfsRouteId, ServiceRouteId>;
278
+ tripsMapping: TripsMapping;
258
279
  }> => {
259
280
  /**
260
281
  * Adds a trip to the appropriate route builder
261
282
  */
262
- const addTrip = (currentTripId: TripId) => {
283
+ const addTrip = (currentTripId: GtfsTripId) => {
263
284
  const gtfsRouteId = activeTripIds.get(currentTripId);
264
285
 
265
286
  if (!gtfsRouteId || stops.length === 0) {
@@ -304,6 +325,7 @@ export const parseStopTimes = async (
304
325
 
305
326
  routeBuilder.trips.push({
306
327
  firstDeparture,
328
+ gtfsTripId: currentTripId,
307
329
  arrivalTimes: arrivalTimes,
308
330
  departureTimes: departureTimes,
309
331
  pickUpTypes: pickUpTypes,
@@ -330,7 +352,7 @@ export const parseStopTimes = async (
330
352
  let departureTimes: number[] = [];
331
353
  let pickUpTypes: SerializedPickUpDropOffType[] = [];
332
354
  let dropOffTypes: SerializedPickUpDropOffType[] = [];
333
- let currentTripId: TripId | undefined = undefined;
355
+ let currentTripId: GtfsTripId | undefined = undefined;
334
356
 
335
357
  for await (const rawLine of parseCsv(stopTimesStream, ['stop_sequence'])) {
336
358
  const line = rawLine as StopTimeEntry;
@@ -347,6 +369,9 @@ export const parseStopTimes = async (
347
369
  continue;
348
370
  }
349
371
  if (line.pickup_type === '1' && line.drop_off_type === '1') {
372
+ // Warning: could potentially lead to issues if there is an in-seat transfer
373
+ // at this stop - it can be not boardable nor alightable but still useful for an in-seat transfer.
374
+ // This doesn't seem to happen in practice for now so keeping this condition to save memory.
350
375
  continue;
351
376
  }
352
377
  if (currentTripId && line.trip_id !== currentTripId && stops.length > 0) {
@@ -382,18 +407,30 @@ export const parseStopTimes = async (
382
407
  }
383
408
 
384
409
  const routesAdjacency: Route[] = [];
410
+ const tripsMapping = new Map<
411
+ GtfsTripId,
412
+ { routeId: RouteId; tripRouteIndex: TripRouteIndex }
413
+ >();
385
414
  for (const [, routeBuilder] of routeBuilders) {
386
- const routeData = finalizeRouteFromBuilder(routeBuilder);
415
+ const [routeData, gtfsTripIds] = finalizeRouteFromBuilder(routeBuilder);
416
+ const routeId = routesAdjacency.length;
387
417
  routesAdjacency.push(
388
418
  new Route(
419
+ routeId,
389
420
  routeData.stopTimes,
390
421
  routeData.pickUpDropOffTypes,
391
422
  routeData.stops,
392
423
  routeData.serviceRouteId,
393
424
  ),
394
425
  );
426
+ gtfsTripIds.forEach((tripId, index) => {
427
+ tripsMapping.set(tripId, {
428
+ routeId,
429
+ tripRouteIndex: index,
430
+ });
431
+ });
395
432
  }
396
- return { routes: routesAdjacency, serviceRoutesMap };
433
+ return { routes: routesAdjacency, serviceRoutesMap, tripsMapping };
397
434
  };
398
435
 
399
436
  const parsePickupDropOffType = (
package/src/router.ts CHANGED
@@ -3,7 +3,7 @@ import { Query } from './routing/query.js';
3
3
  import { Result } from './routing/result.js';
4
4
  import type { Leg, Transfer, VehicleLeg } from './routing/route.js';
5
5
  import { Route } from './routing/route.js';
6
- import type { ReachingTime } from './routing/router.js';
6
+ import type { Arrival } from './routing/router.js';
7
7
  import { Router } from './routing/router.js';
8
8
  import type { LocationType, SourceStopId, StopId } from './stops/stops.js';
9
9
  import type { Stop } from './stops/stops.js';
@@ -30,9 +30,9 @@ export {
30
30
  };
31
31
 
32
32
  export type {
33
+ Arrival,
33
34
  Leg,
34
35
  LocationType,
35
- ReachingTime,
36
36
  RouteType,
37
37
  ServiceRouteInfo,
38
38
  SourceStopId,