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/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
- const fromStopsArray = Array.from(fromStops);
17853
- for (let i = 0; i < fromStopsArray.length; i++) {
17854
- const originStop = fromStopsArray[i];
17855
- const validRoutes = this.routesPassingThrough(originStop).filter((route) => {
17856
- const serviceRoute = this.getServiceRouteInfo(route);
17857
- return transportModes.has(serviceRoute.type);
17858
- });
17859
- for (let j = 0; j < validRoutes.length; j++) {
17860
- const route = validRoutes[j];
17861
- const originStopIndices = route.stopRouteIndices(originStop);
17862
- const originStopIndex = originStopIndices[0];
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
- return guaranteedTransfers.some((transfer) => transfer.stopIndex === toTripStop.stopIndex &&
17892
- transfer.routeId === toTripStop.routeId &&
17893
- transfer.tripIndex === toTripStop.tripIndex);
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, _b;
21228
- const isOrigin = (_b = (_a = this.result.routingState.graph[0]) === null || _a === void 0 ? void 0 : _a.has(stopId)) !== null && _b !== void 0 ? _b : false;
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 roundMap of graph) {
21361
- for (const [stopId, edge] of roundMap) {
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 roundMap = graph[round];
21399
- if (!roundMap)
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 (const edge of roundMap.values()) {
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 fastestTime = undefined;
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 arrivalTime = this.routingState.earliestArrivals.get(destination.id);
21692
- if (arrivalTime !== undefined) {
21693
- if (fastestTime === undefined ||
21694
- arrivalTime.arrival < fastestTime.arrival) {
21695
- fastestDestination = destination.id;
21696
- fastestTime = arrivalTime;
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 (!fastestDestination || !fastestTime) {
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 = fastestTime.legNumber;
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.get(currentStop);
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
- arrivalTime = this.routingState.earliestArrivals.get(equivalentStop.id);
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 = (_a = this.routingState.graph[i]) === null || _a === void 0 ? void 0 : _a.get(equivalentStop.id);
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
- const UNREACHED = TIME_INFINITY;
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 Map();
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
- const stopsFromContinuations = new Set();
22096
+ stopsFromContinuations.clear();
21925
22097
  for (const continuation of continuations) {
21926
22098
  const route = this.timetable.getRoute(continuation.routeId);
21927
- const routeScanResults = this.scanRoute(route, continuation.stopIndex, round, routingState, query.options, continuation);
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 map of edges at the current round.
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.get(stopId);
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
- const earliestArrivals = new Map();
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 to find the earliest possible trips (if not provided) and updates arrival times.
22166
+ * Scans a route for an in-seat trip continuation.
22010
22167
  *
22011
- * This method implements the core route scanning logic of the RAPTOR algorithm.
22012
- * It iterates through all stops on a given route starting from the hop-on stop,
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 for possible trips
22018
- * @param hopOnStopIndex The stop index where passengers can board the route
22019
- * @param round The current round number in the RAPTOR algorithm
22020
- * @param routingState The current routing state containing arrival times and marked stops
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
- scanRoute(route, hopOnStopIndex, round, routingState, options, tripContinuation) {
22023
- var _a, _b;
22177
+ scanRouteContinuation(route, hopOnStopIndex, round, routingState, tripContinuation) {
22024
22178
  const newlyMarkedStops = new Set();
22025
- let activeTrip = tripContinuation
22026
- ? {
22027
- routeId: route.id,
22028
- stopIndex: hopOnStopIndex,
22029
- tripIndex: tripContinuation.tripIndex,
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
- : undefined;
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
- // Compute target pruning criteria only once per route
22035
- const earliestArrivalAtAnyDestination = this.earliestArrivalAtAnyStop(routingState.earliestArrivals, routingState.destinations);
22036
- for (let currentStopIndex = hopOnStopIndex; currentStopIndex < route.getNbStops(); currentStopIndex++) {
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 we're currently on a trip,
22039
- // check if arrival at the stop improves the earliest arrival time
22040
- if (activeTrip !== undefined) {
22041
- const arrivalTime = route.arrivalAt(currentStopIndex, activeTrip.tripIndex);
22042
- const dropOffType = route.dropOffTypeAt(currentStopIndex, activeTrip.tripIndex);
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
- const edge = {
22048
- routeId: activeTrip.routeId,
22049
- stopIndex: activeTrip.stopIndex,
22050
- tripIndex: activeTrip.tripIndex,
22244
+ edgesAtCurrentRound[currentStop] = {
22245
+ routeId,
22246
+ stopIndex: activeTripBoardStopIndex,
22247
+ tripIndex: activeTripIndex,
22051
22248
  arrival: arrivalTime,
22052
22249
  hopOffStopIndex: currentStopIndex,
22053
22250
  };
22054
- if (tripContinuation) {
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
- if (tripContinuation) {
22067
- // If it's a trip continuation, no need to check for earlier trips
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
- (activeTrip === undefined ||
22259
+ (activeTripIndex === undefined ||
22076
22260
  earliestArrivalOnPreviousRound <=
22077
- route.departureFrom(currentStopIndex, activeTrip.tripIndex))) {
22078
- const earliestTrip = route.findEarliestTrip(currentStopIndex, earliestArrivalOnPreviousRound, activeTrip === null || activeTrip === void 0 ? void 0 : activeTrip.tripIndex);
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, activeTrip === null || activeTrip === void 0 ? void 0 : activeTrip.tripIndex,
22083
- // provide the previous trip if the previous edge was a vehicle
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
- activeTrip = {
22087
- routeId: route.id,
22088
- tripIndex: firstBoardableTrip,
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.get(stop);
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 = (_b = (_a = routingState.earliestArrivals.get(transfer.destination)) === null || _a === void 0 ? void 0 : _a.arrival) !== null && _b !== void 0 ? _b : UNREACHED;
22354
+ const originalArrival = routingState.arrivalTime(transfer.destination);
22174
22355
  if (arrivalAfterTransfer < originalArrival) {
22175
- arrivalsAtCurrentRound.set(transfer.destination, {
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.earliestArrivals.set(transfer.destination, {
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 earliestArrivals A map of stops to their earliest reaching times.
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(earliestArrivals, destinations) {
22200
- var _a, _b;
22201
- let earliestArrivalAtAnyDestination = UNREACHED;
22202
- for (let i = 0; i < destinations.length; i++) {
22203
- const destination = destinations[i];
22204
- const arrival = (_b = (_a = earliestArrivals.get(destination)) === null || _a === void 0 ? void 0 : _a.arrival) !== null && _b !== void 0 ? _b : UNREACHED;
22205
- earliestArrivalAtAnyDestination = Math.min(earliestArrivalAtAnyDestination, arrival);
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
  }