minotor 5.0.1 → 7.0.0
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 +8 -3
- package/dist/cli.mjs +282 -654
- package/dist/cli.mjs.map +1 -1
- package/dist/gtfs/parser.d.ts +4 -10
- package/dist/gtfs/routes.d.ts +16 -2
- package/dist/gtfs/stops.d.ts +3 -13
- package/dist/gtfs/transfers.d.ts +2 -2
- package/dist/gtfs/trips.d.ts +12 -8
- package/dist/parser.cjs.js +257 -644
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +257 -644
- 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/stops/io.d.ts +3 -3
- package/dist/stops/proto/stops.d.ts +1 -8
- package/dist/stops/stops.d.ts +0 -4
- package/dist/stops/stopsIndex.d.ts +3 -3
- package/dist/timetable/io.d.ts +6 -6
- package/dist/timetable/proto/timetable.d.ts +5 -27
- package/dist/timetable/route.d.ts +2 -11
- package/dist/timetable/timetable.d.ts +17 -9
- package/package.json +1 -1
- package/src/__e2e__/timetable/stops.bin +2 -2
- package/src/__e2e__/timetable/timetable.bin +2 -2
- package/src/cli/minotor.ts +3 -4
- package/src/gtfs/__tests__/parser.test.ts +5 -6
- package/src/gtfs/__tests__/routes.test.ts +0 -3
- package/src/gtfs/__tests__/stops.test.ts +1 -124
- package/src/gtfs/__tests__/transfers.test.ts +7 -7
- package/src/gtfs/__tests__/trips.test.ts +74 -45
- package/src/gtfs/parser.ts +32 -49
- package/src/gtfs/routes.ts +43 -5
- package/src/gtfs/stops.ts +2 -44
- package/src/gtfs/transfers.ts +2 -2
- package/src/gtfs/trips.ts +66 -43
- package/src/routing/__tests__/result.test.ts +48 -48
- package/src/routing/__tests__/router.test.ts +279 -363
- package/src/routing/router.ts +22 -8
- package/src/stops/__tests__/io.test.ts +25 -31
- package/src/stops/__tests__/stopFinder.test.ts +82 -103
- package/src/stops/io.ts +8 -17
- package/src/stops/proto/stops.proto +3 -3
- package/src/stops/proto/stops.ts +16 -120
- package/src/stops/stops.ts +0 -4
- package/src/stops/stopsIndex.ts +37 -41
- package/src/timetable/__tests__/io.test.ts +44 -54
- package/src/timetable/__tests__/route.test.ts +10 -29
- package/src/timetable/__tests__/timetable.test.ts +29 -37
- package/src/timetable/io.ts +66 -74
- package/src/timetable/proto/timetable.proto +7 -14
- package/src/timetable/proto/timetable.ts +49 -391
- package/src/timetable/route.ts +2 -32
- package/src/timetable/timetable.ts +51 -31
package/src/timetable/route.ts
CHANGED
|
@@ -81,7 +81,7 @@ export class Route {
|
|
|
81
81
|
* A binary array of stopIds in the route.
|
|
82
82
|
* [stop1, stop2, stop3,...]
|
|
83
83
|
*/
|
|
84
|
-
|
|
84
|
+
public readonly stops: Uint32Array;
|
|
85
85
|
/**
|
|
86
86
|
* A reverse mapping of each stop with their index in the route:
|
|
87
87
|
* {
|
|
@@ -269,36 +269,6 @@ export class Route {
|
|
|
269
269
|
return toPickupDropOffType(dropOffValue);
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
/**
|
|
273
|
-
* Iterates over the stops in the route, starting from an optional specified stop.
|
|
274
|
-
* If no start stop is provided, the iteration begins from the first stop in the route.
|
|
275
|
-
*
|
|
276
|
-
* @param [startStopId] - (Optional) The StopId of the stop to start the iteration from.
|
|
277
|
-
* @returns An IterableIterator of StopIds, starting from the specified stop or the first stop.
|
|
278
|
-
* @throws An error if the specified start stop is not found in the route.
|
|
279
|
-
*/
|
|
280
|
-
stopsIterator(startStopId?: StopId): IterableIterator<StopId> {
|
|
281
|
-
const startIndex =
|
|
282
|
-
startStopId !== undefined ? this.stopIndices.get(startStopId) : 0;
|
|
283
|
-
if (startIndex === undefined) {
|
|
284
|
-
throw new Error(
|
|
285
|
-
`Start stop ${startStopId} not found in route ${this.serviceRouteId}`,
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function* generator(
|
|
290
|
-
stops: Uint32Array,
|
|
291
|
-
startIndex: number,
|
|
292
|
-
): IterableIterator<StopId> {
|
|
293
|
-
for (let i = startIndex; i < stops.length; i++) {
|
|
294
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
295
|
-
yield stops[i]!;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return generator(this.stops, startIndex);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
272
|
/**
|
|
303
273
|
* Finds the earliest trip that can be taken from a specific stop on a given route,
|
|
304
274
|
* optionally constrained by a latest trip index and a time before which the trip
|
|
@@ -349,7 +319,7 @@ export class Route {
|
|
|
349
319
|
* @param stopId The StopId of the stop to locate in the route.
|
|
350
320
|
* @returns The index of the stop in the route.
|
|
351
321
|
*/
|
|
352
|
-
|
|
322
|
+
public stopIndex(stopId: StopId): number {
|
|
353
323
|
const stopIndex = this.stopIndices.get(stopId);
|
|
354
324
|
if (stopIndex === undefined) {
|
|
355
325
|
throw new Error(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
1
2
|
import { BinaryReader, BinaryWriter } from '@bufbuild/protobuf/wire';
|
|
2
3
|
|
|
3
4
|
import { StopId } from '../stops/stops.js';
|
|
@@ -25,15 +26,12 @@ export type Transfer = {
|
|
|
25
26
|
minTransferTime?: Duration;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
|
-
export type
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
routes: RouteId[];
|
|
33
|
-
}
|
|
34
|
-
>;
|
|
29
|
+
export type StopAdjacency = {
|
|
30
|
+
transfers: Transfer[];
|
|
31
|
+
routes: RouteId[];
|
|
32
|
+
};
|
|
35
33
|
|
|
36
|
-
export type ServiceRouteId =
|
|
34
|
+
export type ServiceRouteId = number;
|
|
37
35
|
|
|
38
36
|
export type RouteType =
|
|
39
37
|
| 'TRAM'
|
|
@@ -47,18 +45,16 @@ export type RouteType =
|
|
|
47
45
|
| 'TROLLEYBUS'
|
|
48
46
|
| 'MONORAIL';
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
// A service refers to a collection of trips that are displayed to riders as a single service.
|
|
49
|
+
// As opposed to a route which consists of the subset of trips from a service which shares the same list of stops.
|
|
50
|
+
// Service is here a synonym for route in the GTFS sense.
|
|
51
|
+
export type ServiceRoute = {
|
|
51
52
|
type: RouteType;
|
|
52
53
|
name: string;
|
|
53
54
|
routes: RouteId[];
|
|
54
55
|
};
|
|
55
56
|
export type ServiceRouteInfo = Omit<ServiceRoute, 'routes'>;
|
|
56
57
|
|
|
57
|
-
// A service refers to a collection of trips that are displayed to riders as a single service.
|
|
58
|
-
// As opposed to a route which consists of the subset of trips from a service which shares the same list of stops.
|
|
59
|
-
// Service is here a synonym for route in the GTFS sense.
|
|
60
|
-
export type ServiceRoutesMap = Map<ServiceRouteId, ServiceRoute>;
|
|
61
|
-
|
|
62
58
|
export const ALL_TRANSPORT_MODES: Set<RouteType> = new Set([
|
|
63
59
|
'TRAM',
|
|
64
60
|
'SUBWAY',
|
|
@@ -72,24 +68,33 @@ export const ALL_TRANSPORT_MODES: Set<RouteType> = new Set([
|
|
|
72
68
|
'MONORAIL',
|
|
73
69
|
]);
|
|
74
70
|
|
|
75
|
-
export const CURRENT_VERSION = '0.0.
|
|
71
|
+
export const CURRENT_VERSION = '0.0.7';
|
|
76
72
|
|
|
77
73
|
/**
|
|
78
74
|
* The internal transit timetable format.
|
|
79
75
|
*/
|
|
80
76
|
export class Timetable {
|
|
81
|
-
private readonly stopsAdjacency:
|
|
77
|
+
private readonly stopsAdjacency: StopAdjacency[];
|
|
82
78
|
private readonly routesAdjacency: Route[];
|
|
83
|
-
private readonly
|
|
79
|
+
private readonly serviceRoutes: ServiceRoute[];
|
|
80
|
+
private readonly activeStops: Set<StopId>;
|
|
84
81
|
|
|
85
82
|
constructor(
|
|
86
|
-
stopsAdjacency:
|
|
83
|
+
stopsAdjacency: StopAdjacency[],
|
|
87
84
|
routesAdjacency: Route[],
|
|
88
|
-
routes:
|
|
85
|
+
routes: ServiceRoute[],
|
|
89
86
|
) {
|
|
90
87
|
this.stopsAdjacency = stopsAdjacency;
|
|
91
88
|
this.routesAdjacency = routesAdjacency;
|
|
92
|
-
this.
|
|
89
|
+
this.serviceRoutes = routes;
|
|
90
|
+
this.activeStops = new Set<StopId>();
|
|
91
|
+
for (let i = 0; i < stopsAdjacency.length; i++) {
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
93
|
+
const stop = stopsAdjacency[i]!;
|
|
94
|
+
if (stop.routes.length > 0 || stop.transfers.length > 0) {
|
|
95
|
+
this.activeStops.add(i);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
/**
|
|
@@ -102,7 +107,7 @@ export class Timetable {
|
|
|
102
107
|
version: CURRENT_VERSION,
|
|
103
108
|
stopsAdjacency: serializeStopsAdjacency(this.stopsAdjacency),
|
|
104
109
|
routesAdjacency: serializeRoutesAdjacency(this.routesAdjacency),
|
|
105
|
-
|
|
110
|
+
serviceRoutes: serializeServiceRoutesMap(this.serviceRoutes),
|
|
106
111
|
};
|
|
107
112
|
const writer = new BinaryWriter();
|
|
108
113
|
ProtoTimetable.encode(protoTimetable, writer);
|
|
@@ -124,14 +129,25 @@ export class Timetable {
|
|
|
124
129
|
);
|
|
125
130
|
}
|
|
126
131
|
return new Timetable(
|
|
127
|
-
|
|
128
|
-
deserializeStopsAdjacency(protoTimetable.stopsAdjacency!),
|
|
132
|
+
deserializeStopsAdjacency(protoTimetable.stopsAdjacency),
|
|
129
133
|
deserializeRoutesAdjacency(protoTimetable.routesAdjacency),
|
|
130
|
-
|
|
131
|
-
deserializeServiceRoutesMap(protoTimetable.
|
|
134
|
+
|
|
135
|
+
deserializeServiceRoutesMap(protoTimetable.serviceRoutes),
|
|
132
136
|
);
|
|
133
137
|
}
|
|
134
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Checks if the given stop is active on the timetable.
|
|
141
|
+
* An active stop is a stop reached by a route that is active on the timetable
|
|
142
|
+
* or by a transfer reachable from an active route.
|
|
143
|
+
*
|
|
144
|
+
* @param stopId - The ID of the stop to check.
|
|
145
|
+
* @returns True if the stop is active, false otherwise.
|
|
146
|
+
*/
|
|
147
|
+
isActive(stopId: StopId): boolean {
|
|
148
|
+
return this.activeStops.has(stopId);
|
|
149
|
+
}
|
|
150
|
+
|
|
135
151
|
/**
|
|
136
152
|
* Retrieves the route associated with the given route ID.
|
|
137
153
|
*
|
|
@@ -150,7 +166,7 @@ export class Timetable {
|
|
|
150
166
|
* @returns An array of transfer options available at the stop.
|
|
151
167
|
*/
|
|
152
168
|
getTransfers(stopId: StopId): Transfer[] {
|
|
153
|
-
return this.stopsAdjacency
|
|
169
|
+
return this.stopsAdjacency[stopId]?.transfers ?? [];
|
|
154
170
|
}
|
|
155
171
|
|
|
156
172
|
/**
|
|
@@ -162,7 +178,7 @@ export class Timetable {
|
|
|
162
178
|
* @returns The service route corresponding to the provided route.
|
|
163
179
|
*/
|
|
164
180
|
getServiceRouteInfo(route: Route): ServiceRouteInfo {
|
|
165
|
-
const serviceRoute = this.
|
|
181
|
+
const serviceRoute = this.serviceRoutes[route.serviceRoute()];
|
|
166
182
|
if (!serviceRoute) {
|
|
167
183
|
throw new Error(
|
|
168
184
|
`Service route not found for route ID: ${route.serviceRoute()}`,
|
|
@@ -180,12 +196,13 @@ export class Timetable {
|
|
|
180
196
|
* @returns An array of routes passing through the specified stop.
|
|
181
197
|
*/
|
|
182
198
|
routesPassingThrough(stopId: StopId): Route[] {
|
|
183
|
-
const stopData = this.stopsAdjacency
|
|
199
|
+
const stopData = this.stopsAdjacency[stopId];
|
|
184
200
|
if (!stopData) {
|
|
185
201
|
return [];
|
|
186
202
|
}
|
|
187
203
|
const routes: Route[] = [];
|
|
188
|
-
for (
|
|
204
|
+
for (let i = 0; i < stopData.routes.length; i++) {
|
|
205
|
+
const routeId = stopData.routes[i]!;
|
|
189
206
|
const route = this.routesAdjacency[routeId];
|
|
190
207
|
if (route) {
|
|
191
208
|
routes.push(route);
|
|
@@ -208,14 +225,17 @@ export class Timetable {
|
|
|
208
225
|
transportModes: Set<RouteType> = ALL_TRANSPORT_MODES,
|
|
209
226
|
): Map<Route, StopId> {
|
|
210
227
|
const reachableRoutes = new Map<Route, StopId>();
|
|
211
|
-
|
|
228
|
+
const fromStopsArray = Array.from(fromStops);
|
|
229
|
+
for (let i = 0; i < fromStopsArray.length; i++) {
|
|
230
|
+
const originStop = fromStopsArray[i]!;
|
|
212
231
|
const validRoutes = this.routesPassingThrough(originStop).filter(
|
|
213
232
|
(route) => {
|
|
214
233
|
const serviceRoute = this.getServiceRouteInfo(route);
|
|
215
234
|
return transportModes.has(serviceRoute.type);
|
|
216
235
|
},
|
|
217
236
|
);
|
|
218
|
-
for (
|
|
237
|
+
for (let j = 0; j < validRoutes.length; j++) {
|
|
238
|
+
const route = validRoutes[j]!;
|
|
219
239
|
const hopOnStop = reachableRoutes.get(route);
|
|
220
240
|
if (hopOnStop) {
|
|
221
241
|
if (route.isBefore(originStop, hopOnStop)) {
|