minotor 11.1.1 → 11.1.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/CHANGELOG.md +2 -2
- package/dist/cli.mjs +146 -66
- package/dist/cli.mjs.map +1 -1
- package/dist/parser.cjs.js +65 -15
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +65 -15
- 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.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/router.d.ts +23 -10
- package/dist/timetable/route.d.ts +27 -0
- package/package.json +1 -1
- package/src/routing/router.ts +110 -56
- package/src/timetable/route.ts +47 -0
- package/src/timetable/timetable.ts +26 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
## [11.1.
|
|
1
|
+
## [11.1.2](https://github.com/aubryio/minotor/compare/v11.1.1...v11.1.2) (2026-04-18)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Performance Improvements
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* easy performance wins with minor refactorings ([#65](https://github.com/aubryio/minotor/issues/65)) ([b3cfc62](https://github.com/aubryio/minotor/commit/b3cfc62b56cd754aff964333f4296301185e2a87))
|
package/dist/cli.mjs
CHANGED
|
@@ -17263,6 +17263,45 @@ let Route$1 = class Route {
|
|
|
17263
17263
|
}
|
|
17264
17264
|
return arrival;
|
|
17265
17265
|
}
|
|
17266
|
+
/**
|
|
17267
|
+
* Computes the per-trip base offset used by the hot-path scanning methods.
|
|
17268
|
+
*
|
|
17269
|
+
* Cache the result once when boarding a new trip and pass it to
|
|
17270
|
+
* {@link arrivalAtOffset} and {@link dropOffTypeAtOffset} throughout the
|
|
17271
|
+
* scanning loop to avoid recomputing `tripIndex × nbStops` on every stop.
|
|
17272
|
+
*
|
|
17273
|
+
* @param tripIndex - The index of the trip.
|
|
17274
|
+
* @returns `tripIndex × nbStops`.
|
|
17275
|
+
*/
|
|
17276
|
+
tripStopOffset(tripIndex) {
|
|
17277
|
+
return tripIndex * this.nbStops;
|
|
17278
|
+
}
|
|
17279
|
+
/**
|
|
17280
|
+
* Hot-path variant of {@link arrivalAt} that accepts a precomputed base offset.
|
|
17281
|
+
*
|
|
17282
|
+
* @param stopIndex - The index of the stop in the route.
|
|
17283
|
+
* @param offset - Precomputed value from {@link tripStopOffset}.
|
|
17284
|
+
* @returns The arrival time at the specified stop.
|
|
17285
|
+
*/
|
|
17286
|
+
arrivalAtOffset(stopIndex, offset) {
|
|
17287
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17288
|
+
return this.stopTimes[(offset + stopIndex) * 2];
|
|
17289
|
+
}
|
|
17290
|
+
/**
|
|
17291
|
+
* Hot-path variant of {@link dropOffTypeAt} that accepts a precomputed base offset.
|
|
17292
|
+
*
|
|
17293
|
+
* @param stopIndex - The index of the stop in the route.
|
|
17294
|
+
* @param offset - Precomputed value from {@link tripStopOffset}.
|
|
17295
|
+
* @returns The drop-off type at the specified stop.
|
|
17296
|
+
*/
|
|
17297
|
+
dropOffTypeAtOffset(stopIndex, offset) {
|
|
17298
|
+
const globalIndex = offset + stopIndex;
|
|
17299
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17300
|
+
const byte = this.pickupDropOffTypes[globalIndex >> 1];
|
|
17301
|
+
// Bit layout per byte (two pairs): [pickup_1(2)][dropOff_1(2)][pickup_0(2)][dropOff_0(2)]
|
|
17302
|
+
// First pair → drop-off in lower 2 bits; second pair → drop-off in bits 4-5.
|
|
17303
|
+
return (globalIndex & 1 ? (byte >> 4) & 0x03 : byte & 0x03);
|
|
17304
|
+
}
|
|
17266
17305
|
/**
|
|
17267
17306
|
* Retrieves the departure time at a specific stop for a given trip.
|
|
17268
17307
|
*
|
|
@@ -17854,21 +17893,26 @@ class Timetable {
|
|
|
17854
17893
|
*/
|
|
17855
17894
|
findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
|
|
17856
17895
|
const reachableRoutes = new Map();
|
|
17857
|
-
|
|
17858
|
-
|
|
17859
|
-
|
|
17860
|
-
|
|
17861
|
-
|
|
17862
|
-
|
|
17863
|
-
|
|
17864
|
-
for (let
|
|
17865
|
-
const route =
|
|
17866
|
-
|
|
17867
|
-
|
|
17896
|
+
// Skip the per-route mode check entirely when all modes are allowed,
|
|
17897
|
+
// which is the common case.
|
|
17898
|
+
const filterByMode = transportModes !== ALL_TRANSPORT_MODES;
|
|
17899
|
+
for (const originStop of fromStops) {
|
|
17900
|
+
const stopData = this.stopsAdjacency[originStop];
|
|
17901
|
+
if (!stopData)
|
|
17902
|
+
continue;
|
|
17903
|
+
for (let i = 0; i < stopData.routes.length; i++) {
|
|
17904
|
+
const route = this.routesAdjacency[stopData.routes[i]];
|
|
17905
|
+
if (!route)
|
|
17906
|
+
continue;
|
|
17907
|
+
if (filterByMode) {
|
|
17908
|
+
const serviceRoute = this.serviceRoutes[route.serviceRoute()];
|
|
17909
|
+
if (!serviceRoute || !transportModes.has(serviceRoute.type))
|
|
17910
|
+
continue;
|
|
17911
|
+
}
|
|
17912
|
+
const originStopIndex = route.stopRouteIndices(originStop)[0];
|
|
17868
17913
|
const existingHopOnStopIndex = reachableRoutes.get(route);
|
|
17869
17914
|
if (existingHopOnStopIndex !== undefined) {
|
|
17870
17915
|
if (originStopIndex < existingHopOnStopIndex) {
|
|
17871
|
-
// if the current stop is before the existing hop on stop, replace it
|
|
17872
17916
|
reachableRoutes.set(route, originStopIndex);
|
|
17873
17917
|
}
|
|
17874
17918
|
}
|
|
@@ -17893,9 +17937,15 @@ class Timetable {
|
|
|
17893
17937
|
if (!guaranteedTransfers) {
|
|
17894
17938
|
return false;
|
|
17895
17939
|
}
|
|
17896
|
-
|
|
17897
|
-
transfer
|
|
17898
|
-
transfer.
|
|
17940
|
+
for (let i = 0; i < guaranteedTransfers.length; i++) {
|
|
17941
|
+
const transfer = guaranteedTransfers[i];
|
|
17942
|
+
if (transfer.stopIndex === toTripStop.stopIndex &&
|
|
17943
|
+
transfer.routeId === toTripStop.routeId &&
|
|
17944
|
+
transfer.tripIndex === toTripStop.tripIndex) {
|
|
17945
|
+
return true;
|
|
17946
|
+
}
|
|
17947
|
+
}
|
|
17948
|
+
return false;
|
|
17899
17949
|
}
|
|
17900
17950
|
/**
|
|
17901
17951
|
* Retrieves all guaranteed trip transfer options available at the specified stop for a given trip.
|
|
@@ -22041,11 +22091,12 @@ class Router {
|
|
|
22041
22091
|
}
|
|
22042
22092
|
// process in-seat trip continuations
|
|
22043
22093
|
let continuations = this.findTripContinuations(markedStops, edgesAtCurrentRound);
|
|
22094
|
+
const stopsFromContinuations = new Set();
|
|
22044
22095
|
while (continuations.length > 0) {
|
|
22045
|
-
|
|
22096
|
+
stopsFromContinuations.clear();
|
|
22046
22097
|
for (const continuation of continuations) {
|
|
22047
22098
|
const route = this.timetable.getRoute(continuation.routeId);
|
|
22048
|
-
const routeScanResults = this.
|
|
22099
|
+
const routeScanResults = this.scanRouteContinuation(route, continuation.stopIndex, round, routingState, continuation);
|
|
22049
22100
|
for (const newStop of routeScanResults) {
|
|
22050
22101
|
stopsFromContinuations.add(newStop);
|
|
22051
22102
|
}
|
|
@@ -22112,84 +22163,113 @@ class Router {
|
|
|
22112
22163
|
return new RoutingState(origins, destinations, departureTime, this.timetable.nbStops());
|
|
22113
22164
|
}
|
|
22114
22165
|
/**
|
|
22115
|
-
* Scans a route
|
|
22166
|
+
* Scans a route for an in-seat trip continuation.
|
|
22116
22167
|
*
|
|
22117
|
-
*
|
|
22118
|
-
*
|
|
22119
|
-
* maintaining the current best trip and updating arrival times when improvements
|
|
22120
|
-
* are found. The method also handles boarding new trips when earlier departures
|
|
22121
|
-
* are available if no given trip is provided as a parameter.
|
|
22168
|
+
* The boarded trip and entry stop are fixed, so there is no need to probe for
|
|
22169
|
+
* earlier boardings.
|
|
22122
22170
|
*
|
|
22123
|
-
* @param route The route to scan
|
|
22124
|
-
* @param hopOnStopIndex The stop index where
|
|
22125
|
-
* @param round The current
|
|
22126
|
-
* @param routingState
|
|
22171
|
+
* @param route The route to scan
|
|
22172
|
+
* @param hopOnStopIndex The stop index where the continuation begins
|
|
22173
|
+
* @param round The current RAPTOR round
|
|
22174
|
+
* @param routingState Current routing state
|
|
22175
|
+
* @param tripContinuation The in-seat continuation descriptor
|
|
22127
22176
|
*/
|
|
22128
|
-
|
|
22177
|
+
scanRouteContinuation(route, hopOnStopIndex, round, routingState, tripContinuation) {
|
|
22129
22178
|
const newlyMarkedStops = new Set();
|
|
22130
|
-
|
|
22131
|
-
|
|
22132
|
-
|
|
22133
|
-
|
|
22134
|
-
|
|
22179
|
+
const edgesAtCurrentRound = routingState.graph[round];
|
|
22180
|
+
const earliestArrivalAtAnyDestination = this.earliestArrivalAtAnyStop(routingState);
|
|
22181
|
+
const nbStops = route.getNbStops();
|
|
22182
|
+
const routeId = route.id;
|
|
22183
|
+
const tripIndex = tripContinuation.tripIndex;
|
|
22184
|
+
const tripStopOffset = route.tripStopOffset(tripIndex);
|
|
22185
|
+
const previousEdge = tripContinuation.previousEdge;
|
|
22186
|
+
for (let currentStopIndex = hopOnStopIndex; currentStopIndex < nbStops; currentStopIndex++) {
|
|
22187
|
+
const currentStop = route.stops[currentStopIndex];
|
|
22188
|
+
const arrivalTime = route.arrivalAtOffset(currentStopIndex, tripStopOffset);
|
|
22189
|
+
const dropOffType = route.dropOffTypeAtOffset(currentStopIndex, tripStopOffset);
|
|
22190
|
+
const earliestArrivalAtCurrentStop = routingState.arrivalTime(currentStop);
|
|
22191
|
+
if (dropOffType !== NOT_AVAILABLE &&
|
|
22192
|
+
arrivalTime < earliestArrivalAtCurrentStop &&
|
|
22193
|
+
arrivalTime < earliestArrivalAtAnyDestination) {
|
|
22194
|
+
edgesAtCurrentRound[currentStop] = {
|
|
22195
|
+
routeId,
|
|
22196
|
+
stopIndex: hopOnStopIndex,
|
|
22197
|
+
tripIndex,
|
|
22198
|
+
arrival: arrivalTime,
|
|
22199
|
+
hopOffStopIndex: currentStopIndex,
|
|
22200
|
+
continuationOf: previousEdge,
|
|
22201
|
+
};
|
|
22202
|
+
routingState.updateArrival(currentStop, arrivalTime, round);
|
|
22203
|
+
newlyMarkedStops.add(currentStop);
|
|
22135
22204
|
}
|
|
22136
|
-
|
|
22205
|
+
}
|
|
22206
|
+
return newlyMarkedStops;
|
|
22207
|
+
}
|
|
22208
|
+
/**
|
|
22209
|
+
* Scans a route using the standard RAPTOR boarding logic.
|
|
22210
|
+
*
|
|
22211
|
+
* Iterates through all stops from the hop-on point, maintaining the current
|
|
22212
|
+
* best trip and improving arrival times when possible. At each marked stop it
|
|
22213
|
+
* also checks whether an earlier (or first) trip can be boarded, upgrading the
|
|
22214
|
+
* active trip when one is found.
|
|
22215
|
+
*
|
|
22216
|
+
* @param route The route to scan
|
|
22217
|
+
* @param hopOnStopIndex The stop index where passengers can first board
|
|
22218
|
+
* @param round The current RAPTOR round
|
|
22219
|
+
* @param routingState Current routing state
|
|
22220
|
+
* @param options Query options (minTransferTime, etc.)
|
|
22221
|
+
*/
|
|
22222
|
+
scanRoute(route, hopOnStopIndex, round, routingState, options) {
|
|
22223
|
+
const newlyMarkedStops = new Set();
|
|
22137
22224
|
const edgesAtCurrentRound = routingState.graph[round];
|
|
22138
22225
|
const edgesAtPreviousRound = routingState.graph[round - 1];
|
|
22139
|
-
// Compute target pruning criteria only once per route
|
|
22140
22226
|
const earliestArrivalAtAnyDestination = this.earliestArrivalAtAnyStop(routingState);
|
|
22141
|
-
|
|
22227
|
+
const nbStops = route.getNbStops();
|
|
22228
|
+
const routeId = route.id;
|
|
22229
|
+
let activeTripIndex;
|
|
22230
|
+
let activeTripBoardStopIndex = hopOnStopIndex;
|
|
22231
|
+
// tripStopOffset = activeTripIndex * nbStops, precomputed when the trip changes.
|
|
22232
|
+
// Only valid while activeTripIndex !== undefined.
|
|
22233
|
+
let activeTripStopOffset = 0;
|
|
22234
|
+
for (let currentStopIndex = hopOnStopIndex; currentStopIndex < nbStops; currentStopIndex++) {
|
|
22142
22235
|
const currentStop = route.stops[currentStopIndex];
|
|
22143
|
-
// If
|
|
22144
|
-
|
|
22145
|
-
|
|
22146
|
-
const
|
|
22147
|
-
const dropOffType = route.dropOffTypeAt(currentStopIndex, activeTrip.tripIndex);
|
|
22236
|
+
// If on a trip, check whether alighting here improves the global best.
|
|
22237
|
+
if (activeTripIndex !== undefined) {
|
|
22238
|
+
const arrivalTime = route.arrivalAtOffset(currentStopIndex, activeTripStopOffset);
|
|
22239
|
+
const dropOffType = route.dropOffTypeAtOffset(currentStopIndex, activeTripStopOffset);
|
|
22148
22240
|
const earliestArrivalAtCurrentStop = routingState.arrivalTime(currentStop);
|
|
22149
22241
|
if (dropOffType !== NOT_AVAILABLE &&
|
|
22150
22242
|
arrivalTime < earliestArrivalAtCurrentStop &&
|
|
22151
22243
|
arrivalTime < earliestArrivalAtAnyDestination) {
|
|
22152
|
-
|
|
22153
|
-
routeId
|
|
22154
|
-
stopIndex:
|
|
22155
|
-
tripIndex:
|
|
22244
|
+
edgesAtCurrentRound[currentStop] = {
|
|
22245
|
+
routeId,
|
|
22246
|
+
stopIndex: activeTripBoardStopIndex,
|
|
22247
|
+
tripIndex: activeTripIndex,
|
|
22156
22248
|
arrival: arrivalTime,
|
|
22157
22249
|
hopOffStopIndex: currentStopIndex,
|
|
22158
22250
|
};
|
|
22159
|
-
if (tripContinuation) {
|
|
22160
|
-
// In case of continuous trip, we set a pointer to the previous edge
|
|
22161
|
-
edge.continuationOf = tripContinuation.previousEdge;
|
|
22162
|
-
}
|
|
22163
|
-
edgesAtCurrentRound[currentStop] = edge;
|
|
22164
22251
|
routingState.updateArrival(currentStop, arrivalTime, round);
|
|
22165
22252
|
newlyMarkedStops.add(currentStop);
|
|
22166
22253
|
}
|
|
22167
22254
|
}
|
|
22168
|
-
|
|
22169
|
-
// If it's a trip continuation, no need to check for earlier trips
|
|
22170
|
-
continue;
|
|
22171
|
-
}
|
|
22172
|
-
// check if we can board an earlier trip at the current stop
|
|
22173
|
-
// if there was no current trip, find the first one reachable
|
|
22255
|
+
// Check whether we can board an earlier (or first) trip at this stop.
|
|
22174
22256
|
const previousEdge = edgesAtPreviousRound[currentStop];
|
|
22175
22257
|
const earliestArrivalOnPreviousRound = previousEdge === null || previousEdge === void 0 ? void 0 : previousEdge.arrival;
|
|
22176
22258
|
if (earliestArrivalOnPreviousRound !== undefined &&
|
|
22177
|
-
(
|
|
22259
|
+
(activeTripIndex === undefined ||
|
|
22178
22260
|
earliestArrivalOnPreviousRound <=
|
|
22179
|
-
route.departureFrom(currentStopIndex,
|
|
22180
|
-
const earliestTrip = route.findEarliestTrip(currentStopIndex, earliestArrivalOnPreviousRound,
|
|
22261
|
+
route.departureFrom(currentStopIndex, activeTripIndex))) {
|
|
22262
|
+
const earliestTrip = route.findEarliestTrip(currentStopIndex, earliestArrivalOnPreviousRound, activeTripIndex);
|
|
22181
22263
|
if (earliestTrip === undefined) {
|
|
22182
22264
|
continue;
|
|
22183
22265
|
}
|
|
22184
|
-
const firstBoardableTrip = this.findFirstBoardableTrip(currentStopIndex, route, earliestTrip, earliestArrivalOnPreviousRound,
|
|
22185
|
-
// provide the previous
|
|
22266
|
+
const firstBoardableTrip = this.findFirstBoardableTrip(currentStopIndex, route, earliestTrip, earliestArrivalOnPreviousRound, activeTripIndex,
|
|
22267
|
+
// provide the previous edge only if it was a vehicle leg
|
|
22186
22268
|
previousEdge && 'routeId' in previousEdge ? previousEdge : undefined, options.minTransferTime);
|
|
22187
22269
|
if (firstBoardableTrip !== undefined) {
|
|
22188
|
-
|
|
22189
|
-
|
|
22190
|
-
|
|
22191
|
-
stopIndex: currentStopIndex,
|
|
22192
|
-
};
|
|
22270
|
+
activeTripIndex = firstBoardableTrip;
|
|
22271
|
+
activeTripBoardStopIndex = currentStopIndex;
|
|
22272
|
+
activeTripStopOffset = route.tripStopOffset(firstBoardableTrip);
|
|
22193
22273
|
}
|
|
22194
22274
|
}
|
|
22195
22275
|
}
|