minotor
Version:
A lightweight client-side transit routing library.
216 lines (195 loc) • 6.82 kB
text/typescript
import { StopId } from '../stops/stops.js';
import { Duration, durationFromSeconds, Time } from '../timetable/time.js';
import { ALL_TRANSPORT_MODES, RouteType } from '../timetable/timetable.js';
export type QueryOptions = {
maxTransfers: number;
minTransferTime: Duration;
transportModes: Set<RouteType>;
/**
* Maximum total journey duration (in minutes) from the query departure time.
*
* When set, arrivals after `departureTime + maxDuration` are skipped. The
* duration includes initial access, waiting time, transit legs, and transfers.
* Undefined means no limit.
*/
maxDuration?: Duration;
/**
* Maximum time (in minutes) the traveler is willing to wait at the first
* boarding stop before the first transit vehicle departs.
*
* When set, any trip that would require waiting longer than this duration
* after arriving at the stop is skipped for the first boarding leg.
* Undefined means no limit.
*/
maxInitialWaitingTime?: Duration;
};
/**
* A routing query for standard RAPTOR.
*
* Finds the earliest-arrival journey from `from` to `to` for a single
* departure time. Use {@link RangeQuery} (and `router.rangeRoute()`) when
* you want all Pareto-optimal journeys within a departure-time window.
*/
export class Query {
from: StopId;
to: Set<StopId>;
departureTime: Time;
options: QueryOptions;
constructor(builder: typeof Query.Builder.prototype) {
this.from = builder.fromValue;
this.to = builder.toValue;
this.departureTime = builder.departureTimeValue;
this.options = builder.optionsValue;
}
static Builder = class {
fromValue!: StopId;
toValue: Set<StopId> = new Set();
departureTimeValue!: Time;
optionsValue: QueryOptions = {
maxTransfers: 5,
minTransferTime: durationFromSeconds(120),
transportModes: ALL_TRANSPORT_MODES,
};
/**
* Sets the starting stop.
*/
from(from: StopId): this {
this.fromValue = from;
return this;
}
/**
* Sets the destination stop(s).
* Routing stops as soon as all provided stops have been reached.
*/
to(to: StopId | Set<StopId>): this {
this.toValue = to instanceof Set ? to : new Set([to]);
return this;
}
/**
* Sets the departure time in minutes from midnight.
* The router favours trips departing shortly after this time.
*/
departureTime(departureTime: Time): this {
this.departureTimeValue = departureTime;
return this;
}
/**
* Sets the maximum number of transfers allowed.
*/
maxTransfers(maxTransfers: number): this {
this.optionsValue.maxTransfers = maxTransfers;
return this;
}
/**
* Sets the fallback minimum transfer time (in minutes) used when the
* timetable data does not specify one for a particular transfer.
*/
minTransferTime(minTransferTime: Duration): this {
this.optionsValue.minTransferTime = minTransferTime;
return this;
}
/**
* Restricts routing to the given transport modes.
*/
transportModes(transportModes: Set<RouteType>): this {
this.optionsValue.transportModes = transportModes;
return this;
}
/**
* Sets the maximum total journey duration (in minutes) from the query
* departure time. The limit includes initial access, waiting time, transit
* legs, and transfers.
*/
maxDuration(maxDuration: Duration): this {
this.optionsValue.maxDuration = maxDuration;
return this;
}
/**
* Sets the maximum time (in minutes) the traveler is willing to wait at
* the first boarding stop before the first transit vehicle departs.
*
* When set, any trip that would require waiting longer than this duration
* after arriving at the stop is not considered for the first boarding leg.
*/
maxInitialWaitingTime(maxInitialWaitingTime: Duration): this {
this.optionsValue.maxInitialWaitingTime = maxInitialWaitingTime;
return this;
}
build(): Query {
return new Query(this);
}
};
}
/**
* Options specific to a {@link RangeQuery}.
*/
export type RangeQueryOptions = {
/**
* When `true`, a full RAPTOR pass is run at `lastDepartureTime + 1` before
* the main departure-time loop (the *boundary run*).
*
* The boundary run seeds the shared Pareto labels with the best arrival
* achievable by departing just after the window closes. Any in-window
* journey whose arrival is no better than what that post-window departure
* achieves is therefore suppressed — you only see journeys that are still
* worth taking given that a later departure exists.
*
* **Timetable use-case** (`boundaryRun: true`): the window is a *display
* filter*. A journey at 10:55 that arrives at 12:30 is hidden when an
* 11:05 departure arrives at 12:00 — the router pre-empts the dominated
* option on the caller's behalf.
*
* **Isochrone / accessibility use-case** (`boundaryRun: false`, the
* default): the window is a hard constraint. Every Pareto-optimal journey
* whose departure falls strictly within `[departureTime, lastDepartureTime]`
* is returned, regardless of what might be available just outside the
* window.
*
* @default false
*/
optimizeBeyondLatestDeparture: boolean;
};
/**
* A routing query for Range RAPTOR.
*
* Extends {@link Query} with a required `lastDepartureTime` that defines the
* upper bound of the departure-time window. `router.rangeRoute()` returns
* all Pareto-optimal journeys departing in
* `[departureTime, lastDepartureTime]`.
*
*/
export class RangeQuery extends Query {
/** Upper bound of the departure-time window (minutes from midnight). */
readonly lastDepartureTime: Time;
/** Options specific to Range RAPTOR behavior. */
readonly rangeOptions: RangeQueryOptions;
constructor(builder: typeof RangeQuery.Builder.prototype) {
super(builder);
this.lastDepartureTime = builder.lastDepartureTimeValue;
this.rangeOptions = builder.rangeOptionsValue;
}
static Builder = class extends Query.Builder {
lastDepartureTimeValue!: Time;
rangeOptionsValue: RangeQueryOptions = {
optimizeBeyondLatestDeparture: true,
};
/**
* Sets the upper bound of the departure-time window.
*/
lastDepartureTime(time: Time): this {
this.lastDepartureTimeValue = time;
return this;
}
/**
* Overrides individual Range RAPTOR options.
* Unspecified fields keep their defaults.
*/
rangeOptions(options: Partial<RangeQueryOptions>): this {
this.rangeOptionsValue = { ...this.rangeOptionsValue, ...options };
return this;
}
build(): RangeQuery {
return new RangeQuery(this);
}
};
}