minotor 11.1.0 → 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 +3 -3
- package/dist/cli.mjs +316 -139
- package/dist/cli.mjs.map +1 -1
- package/dist/parser.cjs.js +71 -15
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +71 -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 +28 -42
- package/dist/routing/state.d.ts +119 -0
- package/dist/timetable/route.d.ts +27 -0
- package/dist/timetable/timetable.d.ts +4 -0
- package/package.json +1 -1
- package/src/routing/__tests__/plotter.test.ts +47 -59
- package/src/routing/__tests__/result.test.ts +203 -267
- package/src/routing/plotter.ts +10 -7
- package/src/routing/result.ts +22 -20
- package/src/routing/router.ts +158 -143
- package/src/routing/state.ts +199 -0
- package/src/timetable/route.ts +47 -0
- package/src/timetable/timetable.ts +33 -18
package/dist/cli.mjs
CHANGED
|
@@ -17025,7 +17025,6 @@ function isSet(value) {
|
|
|
17025
17025
|
return value !== null && value !== undefined;
|
|
17026
17026
|
}
|
|
17027
17027
|
|
|
17028
|
-
const TIME_INFINITY = Number.MAX_SAFE_INTEGER;
|
|
17029
17028
|
const TIME_ORIGIN = 0;
|
|
17030
17029
|
const DURATION_ZERO = 0;
|
|
17031
17030
|
/**
|
|
@@ -17264,6 +17263,45 @@ let Route$1 = class Route {
|
|
|
17264
17263
|
}
|
|
17265
17264
|
return arrival;
|
|
17266
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
|
+
}
|
|
17267
17305
|
/**
|
|
17268
17306
|
* Retrieves the departure time at a specific stop for a given trip.
|
|
17269
17307
|
*
|
|
@@ -17763,6 +17801,12 @@ class Timetable {
|
|
|
17763
17801
|
isActive(stopId) {
|
|
17764
17802
|
return this.activeStops.has(stopId);
|
|
17765
17803
|
}
|
|
17804
|
+
/**
|
|
17805
|
+
* Returns the total number of stops in the timetable.
|
|
17806
|
+
*/
|
|
17807
|
+
nbStops() {
|
|
17808
|
+
return this.stopsAdjacency.length;
|
|
17809
|
+
}
|
|
17766
17810
|
/**
|
|
17767
17811
|
* Retrieves the route associated with the given route ID.
|
|
17768
17812
|
*
|
|
@@ -17849,21 +17893,26 @@ class Timetable {
|
|
|
17849
17893
|
*/
|
|
17850
17894
|
findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
|
|
17851
17895
|
const reachableRoutes = new Map();
|
|
17852
|
-
|
|
17853
|
-
|
|
17854
|
-
|
|
17855
|
-
|
|
17856
|
-
|
|
17857
|
-
|
|
17858
|
-
|
|
17859
|
-
for (let
|
|
17860
|
-
const route =
|
|
17861
|
-
|
|
17862
|
-
|
|
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];
|
|
17863
17913
|
const existingHopOnStopIndex = reachableRoutes.get(route);
|
|
17864
17914
|
if (existingHopOnStopIndex !== undefined) {
|
|
17865
17915
|
if (originStopIndex < existingHopOnStopIndex) {
|
|
17866
|
-
// if the current stop is before the existing hop on stop, replace it
|
|
17867
17916
|
reachableRoutes.set(route, originStopIndex);
|
|
17868
17917
|
}
|
|
17869
17918
|
}
|
|
@@ -17888,9 +17937,15 @@ class Timetable {
|
|
|
17888
17937
|
if (!guaranteedTransfers) {
|
|
17889
17938
|
return false;
|
|
17890
17939
|
}
|
|
17891
|
-
|
|
17892
|
-
transfer
|
|
17893
|
-
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;
|
|
17894
17949
|
}
|
|
17895
17950
|
/**
|
|
17896
17951
|
* Retrieves all guaranteed trip transfer options available at the specified stop for a given trip.
|
|
@@ -21224,8 +21279,8 @@ class Plotter {
|
|
|
21224
21279
|
* Determines station type (origin/destination) information.
|
|
21225
21280
|
*/
|
|
21226
21281
|
getStationInfo(stopId) {
|
|
21227
|
-
var _a
|
|
21228
|
-
const isOrigin = (
|
|
21282
|
+
var _a;
|
|
21283
|
+
const isOrigin = ((_a = this.result.routingState.graph[0]) === null || _a === void 0 ? void 0 : _a[stopId]) !== undefined;
|
|
21229
21284
|
const isDestination = this.result.routingState.destinations.includes(stopId);
|
|
21230
21285
|
return { isOrigin, isDestination };
|
|
21231
21286
|
}
|
|
@@ -21357,8 +21412,11 @@ class Plotter {
|
|
|
21357
21412
|
collectStations() {
|
|
21358
21413
|
const stations = new Set();
|
|
21359
21414
|
const graph = this.result.routingState.graph;
|
|
21360
|
-
for (const
|
|
21361
|
-
for (
|
|
21415
|
+
for (const roundEdges of graph) {
|
|
21416
|
+
for (let stopId = 0; stopId < roundEdges.length; stopId++) {
|
|
21417
|
+
const edge = roundEdges[stopId];
|
|
21418
|
+
if (edge === undefined)
|
|
21419
|
+
continue;
|
|
21362
21420
|
stations.add(stopId);
|
|
21363
21421
|
if (isVehicleEdge(edge)) {
|
|
21364
21422
|
const fromStopId = this.getVehicleEdgeFromStopId(edge);
|
|
@@ -21395,14 +21453,17 @@ class Plotter {
|
|
|
21395
21453
|
const continuationEdges = [];
|
|
21396
21454
|
const graph = this.result.routingState.graph;
|
|
21397
21455
|
for (let round = 0; round < graph.length; round++) {
|
|
21398
|
-
const
|
|
21399
|
-
if (!
|
|
21456
|
+
const roundEdges = graph[round];
|
|
21457
|
+
if (!roundEdges)
|
|
21400
21458
|
continue;
|
|
21401
21459
|
// Skip round 0 as it contains only origin nodes
|
|
21402
21460
|
if (round === 0) {
|
|
21403
21461
|
continue;
|
|
21404
21462
|
}
|
|
21405
|
-
for (
|
|
21463
|
+
for (let stopId = 0; stopId < roundEdges.length; stopId++) {
|
|
21464
|
+
const edge = roundEdges[stopId];
|
|
21465
|
+
if (edge === undefined)
|
|
21466
|
+
continue;
|
|
21406
21467
|
if (isVehicleEdge(edge)) {
|
|
21407
21468
|
edges.push(...this.createVehicleEdge(edge, round));
|
|
21408
21469
|
if (edge.continuationOf) {
|
|
@@ -21684,30 +21745,31 @@ class Result {
|
|
|
21684
21745
|
const destinationIterable = to instanceof Set ? to : to ? [to] : this.query.to;
|
|
21685
21746
|
// Find the fastest-reached destination across all equivalent stops.
|
|
21686
21747
|
let fastestDestination = undefined;
|
|
21687
|
-
let
|
|
21748
|
+
let fastestArrivalTime = undefined;
|
|
21749
|
+
let fastestLegNumber = undefined;
|
|
21688
21750
|
for (const sourceDestination of destinationIterable) {
|
|
21689
21751
|
const equivalentStops = this.stopsIndex.equivalentStops(sourceDestination);
|
|
21690
21752
|
for (const destination of equivalentStops) {
|
|
21691
|
-
const
|
|
21692
|
-
if (
|
|
21693
|
-
|
|
21694
|
-
|
|
21695
|
-
|
|
21696
|
-
|
|
21697
|
-
|
|
21753
|
+
const arrivalData = this.routingState.getArrival(destination.id);
|
|
21754
|
+
if (arrivalData !== undefined &&
|
|
21755
|
+
(fastestArrivalTime === undefined ||
|
|
21756
|
+
arrivalData.arrival < fastestArrivalTime)) {
|
|
21757
|
+
fastestDestination = destination.id;
|
|
21758
|
+
fastestArrivalTime = arrivalData.arrival;
|
|
21759
|
+
fastestLegNumber = arrivalData.legNumber;
|
|
21698
21760
|
}
|
|
21699
21761
|
}
|
|
21700
21762
|
}
|
|
21701
|
-
if (
|
|
21763
|
+
if (fastestDestination === undefined || fastestLegNumber === undefined) {
|
|
21702
21764
|
return undefined;
|
|
21703
21765
|
}
|
|
21704
21766
|
// Reconstruct the path by walking backwards through the routing graph.
|
|
21705
21767
|
const route = [];
|
|
21706
21768
|
let currentStop = fastestDestination;
|
|
21707
|
-
let round =
|
|
21769
|
+
let round = fastestLegNumber;
|
|
21708
21770
|
let previousVehicleEdge;
|
|
21709
21771
|
while (round > 0) {
|
|
21710
|
-
const edge = (_a = this.routingState.graph[round]) === null || _a === void 0 ? void 0 : _a
|
|
21772
|
+
const edge = (_a = this.routingState.graph[round]) === null || _a === void 0 ? void 0 : _a[currentStop];
|
|
21711
21773
|
if (!edge) {
|
|
21712
21774
|
throw new Error(`No edge arriving at stop ${currentStop} at round ${round}`);
|
|
21713
21775
|
}
|
|
@@ -21844,19 +21906,21 @@ class Result {
|
|
|
21844
21906
|
* @returns The arrival time if the target stop is reachable, otherwise undefined.
|
|
21845
21907
|
*/
|
|
21846
21908
|
arrivalAt(stop, maxTransfers) {
|
|
21847
|
-
var _a;
|
|
21909
|
+
var _a, _b;
|
|
21848
21910
|
const equivalentStops = this.stopsIndex.equivalentStops(stop);
|
|
21849
21911
|
let earliestArrival = undefined;
|
|
21850
21912
|
for (const equivalentStop of equivalentStops) {
|
|
21851
21913
|
let arrivalTime;
|
|
21852
|
-
if (maxTransfers === undefined
|
|
21853
|
-
|
|
21914
|
+
if (maxTransfers === undefined ||
|
|
21915
|
+
((_a = this.routingState.getArrival(equivalentStop.id)) === null || _a === void 0 ? void 0 : _a.legNumber) ===
|
|
21916
|
+
maxTransfers + 1) {
|
|
21917
|
+
arrivalTime = this.routingState.getArrival(equivalentStop.id);
|
|
21854
21918
|
}
|
|
21855
21919
|
else {
|
|
21856
21920
|
// We have no guarantee that the stop was visited in the last round,
|
|
21857
21921
|
// so we need to check all rounds if it's not found in the last one.
|
|
21858
21922
|
for (let i = maxTransfers + 1; i >= 0; i--) {
|
|
21859
|
-
const arrivalEdge = (
|
|
21923
|
+
const arrivalEdge = (_b = this.routingState.graph[i]) === null || _b === void 0 ? void 0 : _b[equivalentStop.id];
|
|
21860
21924
|
if (arrivalEdge !== undefined) {
|
|
21861
21925
|
arrivalTime = {
|
|
21862
21926
|
arrival: arrivalEdge.arrival,
|
|
@@ -21877,7 +21941,117 @@ class Result {
|
|
|
21877
21941
|
}
|
|
21878
21942
|
}
|
|
21879
21943
|
|
|
21880
|
-
|
|
21944
|
+
/**
|
|
21945
|
+
* Sentinel value used in the internal arrival-time array to mark stops not yet reached.
|
|
21946
|
+
* 0xFFFF = 65 535 minutes ≈ 45.5 days, safely beyond any realistic transit arrival time.
|
|
21947
|
+
*/
|
|
21948
|
+
const UNREACHED_TIME = 0xffff;
|
|
21949
|
+
/**
|
|
21950
|
+
* Encapsulates all mutable state for a single RAPTOR routing query.
|
|
21951
|
+
*/
|
|
21952
|
+
class RoutingState {
|
|
21953
|
+
/**
|
|
21954
|
+
* Initializes the routing state for a fresh query.
|
|
21955
|
+
*
|
|
21956
|
+
* All stops start as unreached. Each origin is immediately recorded at the
|
|
21957
|
+
* departure time with leg number 0, and a corresponding OriginNode is placed
|
|
21958
|
+
* in round 0 of the graph.
|
|
21959
|
+
*
|
|
21960
|
+
* @param origins Stop IDs to depart from (may be several equivalent stops).
|
|
21961
|
+
* @param destinations Stop IDs that count as the target of the query.
|
|
21962
|
+
* @param departureTime Earliest departure time in minutes from midnight.
|
|
21963
|
+
* @param nbStops Total number of stops in the timetable (sets array sizes).
|
|
21964
|
+
*/
|
|
21965
|
+
constructor(origins, destinations, departureTime, nbStops) {
|
|
21966
|
+
this.origins = origins;
|
|
21967
|
+
this.destinations = destinations;
|
|
21968
|
+
const earliestArrivalTimes = new Uint16Array(nbStops).fill(UNREACHED_TIME);
|
|
21969
|
+
const earliestArrivalLegs = new Uint8Array(nbStops); // zero-initialized = leg 0
|
|
21970
|
+
const graph0 = new Array(nbStops);
|
|
21971
|
+
for (const stop of origins) {
|
|
21972
|
+
earliestArrivalTimes[stop] = departureTime;
|
|
21973
|
+
graph0[stop] = { arrival: departureTime };
|
|
21974
|
+
}
|
|
21975
|
+
this.earliestArrivalTimes = earliestArrivalTimes;
|
|
21976
|
+
this.earliestArrivalLegs = earliestArrivalLegs;
|
|
21977
|
+
this.graph = [graph0];
|
|
21978
|
+
}
|
|
21979
|
+
/** Total number of stops in the timetable */
|
|
21980
|
+
get nbStops() {
|
|
21981
|
+
return this.earliestArrivalTimes.length;
|
|
21982
|
+
}
|
|
21983
|
+
/**
|
|
21984
|
+
* Returns the earliest known arrival time at a stop.
|
|
21985
|
+
* Returns UNREACHED_TIME if the stop has not been reached yet.
|
|
21986
|
+
*/
|
|
21987
|
+
arrivalTime(stop) {
|
|
21988
|
+
return this.earliestArrivalTimes[stop];
|
|
21989
|
+
}
|
|
21990
|
+
/**
|
|
21991
|
+
* Records a new earliest arrival at a stop.
|
|
21992
|
+
|
|
21993
|
+
*
|
|
21994
|
+
* @param stop The stop that was reached.
|
|
21995
|
+
* @param time The arrival time in minutes from midnight.
|
|
21996
|
+
* @param leg The round number (number of transit legs taken so far).
|
|
21997
|
+
*/
|
|
21998
|
+
updateArrival(stop, time, leg) {
|
|
21999
|
+
this.earliestArrivalTimes[stop] = time;
|
|
22000
|
+
this.earliestArrivalLegs[stop] = leg;
|
|
22001
|
+
}
|
|
22002
|
+
/**
|
|
22003
|
+
* Returns the earliest arrival at a stop as an {@link Arrival} object,
|
|
22004
|
+
* or undefined if the stop has not been reached.
|
|
22005
|
+
*/
|
|
22006
|
+
getArrival(stop) {
|
|
22007
|
+
const time = this.earliestArrivalTimes[stop];
|
|
22008
|
+
if (time >= UNREACHED_TIME)
|
|
22009
|
+
return undefined;
|
|
22010
|
+
return { arrival: time, legNumber: this.earliestArrivalLegs[stop] };
|
|
22011
|
+
}
|
|
22012
|
+
/**
|
|
22013
|
+
* Creates a {@link RoutingState} from fully-specified raw data.
|
|
22014
|
+
*
|
|
22015
|
+
* Use this in tests instead of constructing the object through the production
|
|
22016
|
+
* constructor, which is designed for incremental algorithm state.
|
|
22017
|
+
*
|
|
22018
|
+
* @param nbStops Total number of stops (sets array sizes).
|
|
22019
|
+
* @param origins Origin stop IDs.
|
|
22020
|
+
* @param destinations Destination stop IDs.
|
|
22021
|
+
* @param arrivals Each entry is `[stop, time, leg]` — the earliest arrival
|
|
22022
|
+
* time in minutes and the round number for one stop.
|
|
22023
|
+
* @param graph One element per round. Each round is a sparse list of
|
|
22024
|
+
* `[stop, edge]` pairs; stops absent from the list are
|
|
22025
|
+
* left as `undefined` in the dense output array.
|
|
22026
|
+
*
|
|
22027
|
+
* @internal For use in tests only.
|
|
22028
|
+
*/
|
|
22029
|
+
static fromTestData({ nbStops, origins = [], destinations = [], arrivals = [], graph = [], }) {
|
|
22030
|
+
const state = new RoutingState(origins, destinations, 0, nbStops);
|
|
22031
|
+
// Replace the arrival arrays with freshly built ones so the constructor's
|
|
22032
|
+
// origin-seeding doesn't bleed into the test state.
|
|
22033
|
+
const earliestArrivalTimes = new Uint16Array(nbStops).fill(UNREACHED_TIME);
|
|
22034
|
+
const earliestArrivalLegs = new Uint8Array(nbStops);
|
|
22035
|
+
for (const [stop, time, leg] of arrivals) {
|
|
22036
|
+
earliestArrivalTimes[stop] = time;
|
|
22037
|
+
earliestArrivalLegs[stop] = leg;
|
|
22038
|
+
}
|
|
22039
|
+
state.earliestArrivalTimes = earliestArrivalTimes;
|
|
22040
|
+
state.earliestArrivalLegs = earliestArrivalLegs;
|
|
22041
|
+
// Convert the sparse per-round representation to dense arrays and replace
|
|
22042
|
+
// the graph in-place.
|
|
22043
|
+
const denseRounds = graph.map((round) => {
|
|
22044
|
+
const arr = new Array(nbStops);
|
|
22045
|
+
for (const [stop, edge] of round) {
|
|
22046
|
+
arr[stop] = edge;
|
|
22047
|
+
}
|
|
22048
|
+
return arr;
|
|
22049
|
+
});
|
|
22050
|
+
state.graph.splice(0, state.graph.length, ...denseRounds);
|
|
22051
|
+
return state;
|
|
22052
|
+
}
|
|
22053
|
+
}
|
|
22054
|
+
|
|
21881
22055
|
/**
|
|
21882
22056
|
* A public transportation router implementing the RAPTOR algorithm.
|
|
21883
22057
|
* For more information on the RAPTOR algorithm,
|
|
@@ -21897,17 +22071,14 @@ class Router {
|
|
|
21897
22071
|
*/
|
|
21898
22072
|
route(query) {
|
|
21899
22073
|
const routingState = this.initRoutingState(query);
|
|
21900
|
-
const markedStops = new Set();
|
|
21901
|
-
for (const originStop of routingState.graph[0].keys()) {
|
|
21902
|
-
markedStops.add(originStop);
|
|
21903
|
-
}
|
|
22074
|
+
const markedStops = new Set(routingState.origins);
|
|
21904
22075
|
// Initial transfer consideration for origins
|
|
21905
22076
|
const newlyMarkedStops = this.considerTransfers(query, 0, markedStops, routingState);
|
|
21906
22077
|
for (const newStop of newlyMarkedStops) {
|
|
21907
22078
|
markedStops.add(newStop);
|
|
21908
22079
|
}
|
|
21909
22080
|
for (let round = 1; round <= query.options.maxTransfers + 1; round++) {
|
|
21910
|
-
const edgesAtCurrentRound = new
|
|
22081
|
+
const edgesAtCurrentRound = new Array(routingState.nbStops);
|
|
21911
22082
|
routingState.graph.push(edgesAtCurrentRound);
|
|
21912
22083
|
const reachableRoutes = this.timetable.findReachableRoutes(markedStops, query.options.transportModes);
|
|
21913
22084
|
markedStops.clear();
|
|
@@ -21920,11 +22091,12 @@ class Router {
|
|
|
21920
22091
|
}
|
|
21921
22092
|
// process in-seat trip continuations
|
|
21922
22093
|
let continuations = this.findTripContinuations(markedStops, edgesAtCurrentRound);
|
|
22094
|
+
const stopsFromContinuations = new Set();
|
|
21923
22095
|
while (continuations.length > 0) {
|
|
21924
|
-
|
|
22096
|
+
stopsFromContinuations.clear();
|
|
21925
22097
|
for (const continuation of continuations) {
|
|
21926
22098
|
const route = this.timetable.getRoute(continuation.routeId);
|
|
21927
|
-
const routeScanResults = this.
|
|
22099
|
+
const routeScanResults = this.scanRouteContinuation(route, continuation.stopIndex, round, routingState, continuation);
|
|
21928
22100
|
for (const newStop of routeScanResults) {
|
|
21929
22101
|
stopsFromContinuations.add(newStop);
|
|
21930
22102
|
}
|
|
@@ -21946,13 +22118,13 @@ class Router {
|
|
|
21946
22118
|
/**
|
|
21947
22119
|
* Finds trip continuations for the given marked stops and edges at the current round.
|
|
21948
22120
|
* @param markedStops The set of marked stops.
|
|
21949
|
-
* @param edgesAtCurrentRound The
|
|
22121
|
+
* @param edgesAtCurrentRound The array of edges at the current round, indexed by stop ID.
|
|
21950
22122
|
* @returns An array of trip continuations.
|
|
21951
22123
|
*/
|
|
21952
22124
|
findTripContinuations(markedStops, edgesAtCurrentRound) {
|
|
21953
22125
|
const continuations = [];
|
|
21954
22126
|
for (const stopId of markedStops) {
|
|
21955
|
-
const arrival = edgesAtCurrentRound
|
|
22127
|
+
const arrival = edgesAtCurrentRound[stopId];
|
|
21956
22128
|
if (!arrival || !('routeId' in arrival))
|
|
21957
22129
|
continue;
|
|
21958
22130
|
const continuousTrips = this.timetable.getContinuousTrips(arrival.hopOffStopIndex, arrival.routeId, arrival.tripIndex);
|
|
@@ -21988,106 +22160,116 @@ class Router {
|
|
|
21988
22160
|
const destinations = Array.from(to)
|
|
21989
22161
|
.flatMap((destination) => this.stopsIndex.equivalentStops(destination))
|
|
21990
22162
|
.map((destination) => destination.id);
|
|
21991
|
-
|
|
21992
|
-
const earliestArrivalsWithoutAnyLeg = new Map();
|
|
21993
|
-
const earliestArrivalsPerRound = [earliestArrivalsWithoutAnyLeg];
|
|
21994
|
-
const initialState = {
|
|
21995
|
-
arrival: departureTime,
|
|
21996
|
-
legNumber: 0,
|
|
21997
|
-
};
|
|
21998
|
-
for (const originStop of origins) {
|
|
21999
|
-
earliestArrivals.set(originStop, initialState);
|
|
22000
|
-
earliestArrivalsWithoutAnyLeg.set(originStop, initialState);
|
|
22001
|
-
}
|
|
22002
|
-
return {
|
|
22003
|
-
destinations,
|
|
22004
|
-
earliestArrivals,
|
|
22005
|
-
graph: earliestArrivalsPerRound,
|
|
22006
|
-
};
|
|
22163
|
+
return new RoutingState(origins, destinations, departureTime, this.timetable.nbStops());
|
|
22007
22164
|
}
|
|
22008
22165
|
/**
|
|
22009
|
-
* Scans a route
|
|
22166
|
+
* Scans a route for an in-seat trip continuation.
|
|
22010
22167
|
*
|
|
22011
|
-
*
|
|
22012
|
-
*
|
|
22013
|
-
* maintaining the current best trip and updating arrival times when improvements
|
|
22014
|
-
* are found. The method also handles boarding new trips when earlier departures
|
|
22015
|
-
* 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.
|
|
22016
22170
|
*
|
|
22017
|
-
* @param route The route to scan
|
|
22018
|
-
* @param hopOnStopIndex The stop index where
|
|
22019
|
-
* @param round The current
|
|
22020
|
-
* @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
|
|
22021
22176
|
*/
|
|
22022
|
-
|
|
22023
|
-
var _a, _b;
|
|
22177
|
+
scanRouteContinuation(route, hopOnStopIndex, round, routingState, tripContinuation) {
|
|
22024
22178
|
const newlyMarkedStops = new Set();
|
|
22025
|
-
|
|
22026
|
-
|
|
22027
|
-
|
|
22028
|
-
|
|
22029
|
-
|
|
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);
|
|
22030
22204
|
}
|
|
22031
|
-
|
|
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();
|
|
22032
22224
|
const edgesAtCurrentRound = routingState.graph[round];
|
|
22033
22225
|
const edgesAtPreviousRound = routingState.graph[round - 1];
|
|
22034
|
-
|
|
22035
|
-
const
|
|
22036
|
-
|
|
22226
|
+
const earliestArrivalAtAnyDestination = this.earliestArrivalAtAnyStop(routingState);
|
|
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++) {
|
|
22037
22235
|
const currentStop = route.stops[currentStopIndex];
|
|
22038
|
-
// If
|
|
22039
|
-
|
|
22040
|
-
|
|
22041
|
-
const
|
|
22042
|
-
const
|
|
22043
|
-
const earliestArrivalAtCurrentStop = (_b = (_a = routingState.earliestArrivals.get(currentStop)) === null || _a === void 0 ? void 0 : _a.arrival) !== null && _b !== void 0 ? _b : UNREACHED;
|
|
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);
|
|
22240
|
+
const earliestArrivalAtCurrentStop = routingState.arrivalTime(currentStop);
|
|
22044
22241
|
if (dropOffType !== NOT_AVAILABLE &&
|
|
22045
22242
|
arrivalTime < earliestArrivalAtCurrentStop &&
|
|
22046
22243
|
arrivalTime < earliestArrivalAtAnyDestination) {
|
|
22047
|
-
|
|
22048
|
-
routeId
|
|
22049
|
-
stopIndex:
|
|
22050
|
-
tripIndex:
|
|
22244
|
+
edgesAtCurrentRound[currentStop] = {
|
|
22245
|
+
routeId,
|
|
22246
|
+
stopIndex: activeTripBoardStopIndex,
|
|
22247
|
+
tripIndex: activeTripIndex,
|
|
22051
22248
|
arrival: arrivalTime,
|
|
22052
22249
|
hopOffStopIndex: currentStopIndex,
|
|
22053
22250
|
};
|
|
22054
|
-
|
|
22055
|
-
// In case of continuous trip, we set a pointer to the previous edge
|
|
22056
|
-
edge.continuationOf = tripContinuation.previousEdge;
|
|
22057
|
-
}
|
|
22058
|
-
edgesAtCurrentRound.set(currentStop, edge);
|
|
22059
|
-
routingState.earliestArrivals.set(currentStop, {
|
|
22060
|
-
arrival: arrivalTime,
|
|
22061
|
-
legNumber: round,
|
|
22062
|
-
});
|
|
22251
|
+
routingState.updateArrival(currentStop, arrivalTime, round);
|
|
22063
22252
|
newlyMarkedStops.add(currentStop);
|
|
22064
22253
|
}
|
|
22065
22254
|
}
|
|
22066
|
-
|
|
22067
|
-
|
|
22068
|
-
continue;
|
|
22069
|
-
}
|
|
22070
|
-
// check if we can board an earlier trip at the current stop
|
|
22071
|
-
// if there was no current trip, find the first one reachable
|
|
22072
|
-
const previousEdge = edgesAtPreviousRound.get(currentStop);
|
|
22255
|
+
// Check whether we can board an earlier (or first) trip at this stop.
|
|
22256
|
+
const previousEdge = edgesAtPreviousRound[currentStop];
|
|
22073
22257
|
const earliestArrivalOnPreviousRound = previousEdge === null || previousEdge === void 0 ? void 0 : previousEdge.arrival;
|
|
22074
22258
|
if (earliestArrivalOnPreviousRound !== undefined &&
|
|
22075
|
-
(
|
|
22259
|
+
(activeTripIndex === undefined ||
|
|
22076
22260
|
earliestArrivalOnPreviousRound <=
|
|
22077
|
-
route.departureFrom(currentStopIndex,
|
|
22078
|
-
const earliestTrip = route.findEarliestTrip(currentStopIndex, earliestArrivalOnPreviousRound,
|
|
22261
|
+
route.departureFrom(currentStopIndex, activeTripIndex))) {
|
|
22262
|
+
const earliestTrip = route.findEarliestTrip(currentStopIndex, earliestArrivalOnPreviousRound, activeTripIndex);
|
|
22079
22263
|
if (earliestTrip === undefined) {
|
|
22080
22264
|
continue;
|
|
22081
22265
|
}
|
|
22082
|
-
const firstBoardableTrip = this.findFirstBoardableTrip(currentStopIndex, route, earliestTrip, earliestArrivalOnPreviousRound,
|
|
22083
|
-
// 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
|
|
22084
22268
|
previousEdge && 'routeId' in previousEdge ? previousEdge : undefined, options.minTransferTime);
|
|
22085
22269
|
if (firstBoardableTrip !== undefined) {
|
|
22086
|
-
|
|
22087
|
-
|
|
22088
|
-
|
|
22089
|
-
stopIndex: currentStopIndex,
|
|
22090
|
-
};
|
|
22270
|
+
activeTripIndex = firstBoardableTrip;
|
|
22271
|
+
activeTripBoardStopIndex = currentStopIndex;
|
|
22272
|
+
activeTripStopOffset = route.tripStopOffset(firstBoardableTrip);
|
|
22091
22273
|
}
|
|
22092
22274
|
}
|
|
22093
22275
|
}
|
|
@@ -22146,12 +22328,11 @@ class Router {
|
|
|
22146
22328
|
* @param routingState The current routing state containing arrival times and marked stops
|
|
22147
22329
|
*/
|
|
22148
22330
|
considerTransfers(query, round, markedStops, routingState) {
|
|
22149
|
-
var _a, _b;
|
|
22150
22331
|
const { options } = query;
|
|
22151
22332
|
const arrivalsAtCurrentRound = routingState.graph[round];
|
|
22152
22333
|
const newlyMarkedStops = new Set();
|
|
22153
22334
|
for (const stop of markedStops) {
|
|
22154
|
-
const currentArrival = arrivalsAtCurrentRound
|
|
22335
|
+
const currentArrival = arrivalsAtCurrentRound[stop];
|
|
22155
22336
|
// Skip transfers if the last leg was also a transfer
|
|
22156
22337
|
if (!currentArrival || 'type' in currentArrival)
|
|
22157
22338
|
continue;
|
|
@@ -22170,19 +22351,16 @@ class Router {
|
|
|
22170
22351
|
transferTime = options.minTransferTime;
|
|
22171
22352
|
}
|
|
22172
22353
|
const arrivalAfterTransfer = currentArrival.arrival + transferTime;
|
|
22173
|
-
const originalArrival =
|
|
22354
|
+
const originalArrival = routingState.arrivalTime(transfer.destination);
|
|
22174
22355
|
if (arrivalAfterTransfer < originalArrival) {
|
|
22175
|
-
arrivalsAtCurrentRound
|
|
22356
|
+
arrivalsAtCurrentRound[transfer.destination] = {
|
|
22176
22357
|
arrival: arrivalAfterTransfer,
|
|
22177
22358
|
from: stop,
|
|
22178
22359
|
to: transfer.destination,
|
|
22179
22360
|
minTransferTime: transfer.minTransferTime,
|
|
22180
22361
|
type: transfer.type,
|
|
22181
|
-
}
|
|
22182
|
-
routingState.
|
|
22183
|
-
arrival: arrivalAfterTransfer,
|
|
22184
|
-
legNumber: round,
|
|
22185
|
-
});
|
|
22362
|
+
};
|
|
22363
|
+
routingState.updateArrival(transfer.destination, arrivalAfterTransfer, round);
|
|
22186
22364
|
newlyMarkedStops.add(transfer.destination);
|
|
22187
22365
|
}
|
|
22188
22366
|
}
|
|
@@ -22192,17 +22370,16 @@ class Router {
|
|
|
22192
22370
|
/**
|
|
22193
22371
|
* Finds the earliest arrival time at any stop from a given set of destinations.
|
|
22194
22372
|
*
|
|
22195
|
-
* @param
|
|
22196
|
-
* @param destinations An array of destination stops to evaluate.
|
|
22373
|
+
* @param routingState The routing state containing arrival times and destinations.
|
|
22197
22374
|
* @returns The earliest arrival time among the provided destinations.
|
|
22198
22375
|
*/
|
|
22199
|
-
earliestArrivalAtAnyStop(
|
|
22200
|
-
|
|
22201
|
-
let
|
|
22202
|
-
|
|
22203
|
-
|
|
22204
|
-
|
|
22205
|
-
|
|
22376
|
+
earliestArrivalAtAnyStop(routingState) {
|
|
22377
|
+
let earliestArrivalAtAnyDestination = UNREACHED_TIME;
|
|
22378
|
+
for (let i = 0; i < routingState.destinations.length; i++) {
|
|
22379
|
+
const arrival = routingState.arrivalTime(routingState.destinations[i]);
|
|
22380
|
+
if (arrival < earliestArrivalAtAnyDestination) {
|
|
22381
|
+
earliestArrivalAtAnyDestination = arrival;
|
|
22382
|
+
}
|
|
22206
22383
|
}
|
|
22207
22384
|
return earliestArrivalAtAnyDestination;
|
|
22208
22385
|
}
|