UNPKG

minotor

Version:

A lightweight client-side transit routing library.

112 lines (102 loc) 3.88 kB
import { SourceStopId, StopId } from '../stops/stops.js'; import { StopsIndex } from '../stops/stopsIndex.js'; import { Query } from './query.js'; import { Leg, Route } from './route.js'; import { ReachingTime, TripLeg } from './router.js'; export class Result { private readonly query: Query; public readonly earliestArrivals: Map<StopId, ReachingTime>; public readonly earliestArrivalsPerRound: Map<StopId, TripLeg>[]; private readonly stopsIndex: StopsIndex; constructor( query: Query, earliestArrivals: Map<StopId, ReachingTime>, earliestArrivalsPerRound: Map<StopId, TripLeg>[], stopsIndex: StopsIndex, ) { this.query = query; this.earliestArrivals = earliestArrivals; this.earliestArrivalsPerRound = earliestArrivalsPerRound; this.stopsIndex = stopsIndex; } /** * Reconstructs the best route to a stop. * (to any stop reachable in less time / transfers than the destination(s) of the query) * * @param to The destination stop. Defaults to the destination of the original query. * @returns a route to the destination stop if it exists. */ bestRoute(to?: SourceStopId | SourceStopId[]): Route | undefined { const destinationList = Array.isArray(to) ? to : to ? [to] : this.query.to; const destinations = destinationList.flatMap((destination) => this.stopsIndex.equivalentStops(destination), ); let fastestDestination: StopId | undefined = undefined; let fastestTime: ReachingTime | undefined = undefined; for (const destination of destinations) { const arrivalTime = this.earliestArrivals.get(destination.id); if (arrivalTime !== undefined) { if ( fastestTime === undefined || arrivalTime.time.toMinutes() < fastestTime.time.toMinutes() ) { fastestDestination = destination.id; fastestTime = arrivalTime; } } } if (!fastestDestination || !fastestTime) { return undefined; } const route: Leg[] = []; let currentStop = fastestDestination; let round = fastestTime.legNumber; while (fastestTime.origin !== currentStop) { const tripLeg = this.earliestArrivalsPerRound[round]?.get(currentStop); if (!tripLeg?.leg) { throw new Error( `No leg found for a trip leg: start stop=${ tripLeg?.leg?.from.id ?? 'unknown' }, end stop=${currentStop}, round=${round}, origin=${fastestTime.origin}`, ); } route.unshift(tripLeg.leg); currentStop = tripLeg.leg.from.id; if ('route' in tripLeg.leg) { round -= 1; } } return new Route(route); } /** * Returns the arrival time at any stop reachable in less time / transfers than the destination(s) of the query) * * @param stop The target stop for which to return the arrival time. * @param maxTransfers The optional maximum number of transfers allowed. * @returns The arrival time if the target stop is reachable, otherwise undefined. */ arrivalAt( stop: SourceStopId, maxTransfers?: number, ): ReachingTime | undefined { const equivalentStops = this.stopsIndex.equivalentStops(stop); let earliestArrival: ReachingTime | undefined = undefined; const relevantArrivals = maxTransfers !== undefined ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.earliestArrivalsPerRound[maxTransfers - 1]! : this.earliestArrivals; for (const equivalentStop of equivalentStops) { const arrivalTime = relevantArrivals.get(equivalentStop.id); if (arrivalTime !== undefined) { if ( earliestArrival === undefined || arrivalTime.time.toMinutes() < earliestArrival.time.toMinutes() ) { earliestArrival = arrivalTime; } } } return earliestArrival; } }