minotor 6.0.0 → 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.
@@ -44,7 +44,7 @@ export declare class Route {
44
44
  * A binary array of stopIds in the route.
45
45
  * [stop1, stop2, stop3,...]
46
46
  */
47
- private readonly stops;
47
+ readonly stops: Uint32Array;
48
48
  /**
49
49
  * A reverse mapping of each stop with their index in the route:
50
50
  * {
@@ -126,15 +126,6 @@ export declare class Route {
126
126
  * @returns The drop-off type at the specified stop and trip.
127
127
  */
128
128
  dropOffTypeAt(stopId: StopId, tripIndex: TripIndex): PickUpDropOffType;
129
- /**
130
- * Iterates over the stops in the route, starting from an optional specified stop.
131
- * If no start stop is provided, the iteration begins from the first stop in the route.
132
- *
133
- * @param [startStopId] - (Optional) The StopId of the stop to start the iteration from.
134
- * @returns An IterableIterator of StopIds, starting from the specified stop or the first stop.
135
- * @throws An error if the specified start stop is not found in the route.
136
- */
137
- stopsIterator(startStopId?: StopId): IterableIterator<StopId>;
138
129
  /**
139
130
  * Finds the earliest trip that can be taken from a specific stop on a given route,
140
131
  * optionally constrained by a latest trip index and a time before which the trip
@@ -153,5 +144,5 @@ export declare class Route {
153
144
  * @param stopId The StopId of the stop to locate in the route.
154
145
  * @returns The index of the stop in the route.
155
146
  */
156
- private stopIndex;
147
+ stopIndex(stopId: StopId): number;
157
148
  }
@@ -20,7 +20,7 @@ export type ServiceRoute = {
20
20
  };
21
21
  export type ServiceRouteInfo = Omit<ServiceRoute, 'routes'>;
22
22
  export declare const ALL_TRANSPORT_MODES: Set<RouteType>;
23
- export declare const CURRENT_VERSION = "0.0.6";
23
+ export declare const CURRENT_VERSION = "0.0.7";
24
24
  /**
25
25
  * The internal transit timetable format.
26
26
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minotor",
3
- "version": "6.0.0",
3
+ "version": "7.0.0",
4
4
  "description": "A lightweight client-side transit routing library.",
5
5
  "keywords": [
6
6
  "minotor",
@@ -1,3 +1,3 @@
1
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:c2ba83dc99279de7efd87f05c5b49c16f4dbf509e6c3f77af8f98b3273afb9e2
2
+ oid sha256:c7552c57f15104abaa7a67adc4b2c12177cd5d34f2e7f826860b1d073b1b1f26
3
3
  size 18173715
package/src/gtfs/trips.ts CHANGED
@@ -204,8 +204,12 @@ export const buildStopsAdjacencyStructure = (
204
204
  for (let i = 0; i < nbStops; i++) {
205
205
  stopsAdjacency[i] = { routes: [], transfers: [] };
206
206
  }
207
- routes.forEach((route, index) => {
208
- for (const stop of route.stopsIterator()) {
207
+ for (let index = 0; index < routes.length; index++) {
208
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
209
+ const route = routes[index]!;
210
+ for (let j = 0; j < route.getNbStops(); j++) {
211
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
212
+ const stop = route.stops[j]!;
209
213
  if (activeStops.has(stop)) {
210
214
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
211
215
  stopsAdjacency[stop]!.routes.push(index);
@@ -218,9 +222,11 @@ export const buildStopsAdjacencyStructure = (
218
222
  );
219
223
  }
220
224
  serviceRoute.routes.push(index);
221
- });
225
+ }
222
226
  for (const [stop, transfers] of transfersMap) {
223
- for (const transfer of transfers) {
227
+ for (let i = 0; i < transfers.length; i++) {
228
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
229
+ const transfer = transfers[i]!;
224
230
  if (activeStops.has(stop) || activeStops.has(transfer.destination)) {
225
231
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
226
232
  stopsAdjacency[stop]!.transfers.push(transfer);
@@ -56,7 +56,9 @@ export class Router {
56
56
  ): void {
57
57
  const { options } = query;
58
58
  const newlyMarkedStops: Set<StopId> = new Set();
59
- for (const stop of markedStops) {
59
+ const markedStopsArray = Array.from(markedStops);
60
+ for (let i = 0; i < markedStopsArray.length; i++) {
61
+ const stop = markedStopsArray[i]!;
60
62
  const currentArrival = arrivalsAtCurrentRound.get(stop);
61
63
  if (!currentArrival) continue;
62
64
  // Skip transfers if the last leg was also a transfer
@@ -65,7 +67,9 @@ export class Router {
65
67
  continue;
66
68
  }
67
69
 
68
- for (const transfer of this.timetable.getTransfers(stop)) {
70
+ const transfers = this.timetable.getTransfers(stop);
71
+ for (let j = 0; j < transfers.length; j++) {
72
+ const transfer = transfers[j]!;
69
73
  let transferTime: Duration;
70
74
  if (transfer.minTransferTime) {
71
75
  transferTime = transfer.minTransferTime;
@@ -100,7 +104,9 @@ export class Router {
100
104
  }
101
105
  }
102
106
  }
103
- for (const newStop of newlyMarkedStops) {
107
+ const newlyMarkedStopsArray = Array.from(newlyMarkedStops);
108
+ for (let i = 0; i < newlyMarkedStopsArray.length; i++) {
109
+ const newStop = newlyMarkedStopsArray[i]!;
104
110
  markedStops.add(newStop);
105
111
  }
106
112
  }
@@ -117,7 +123,8 @@ export class Router {
117
123
  destinations: Stop[],
118
124
  ): Time {
119
125
  let earliestArrivalAtAnyDestination = UNREACHED;
120
- for (const destination of destinations) {
126
+ for (let i = 0; i < destinations.length; i++) {
127
+ const destination = destinations[i]!;
121
128
  const arrival =
122
129
  earliestArrivals.get(destination.id)?.arrival ?? UNREACHED;
123
130
  earliestArrivalAtAnyDestination = Time.min(
@@ -149,7 +156,8 @@ export class Router {
149
156
  // Stops that have been improved at round k-1
150
157
  const markedStops = new Set<StopId>();
151
158
 
152
- for (const originStop of origins) {
159
+ for (let i = 0; i < origins.length; i++) {
160
+ const originStop = origins[i]!;
153
161
  markedStops.add(originStop.id);
154
162
  earliestArrivals.set(originStop.id, {
155
163
  arrival: departureTime,
@@ -183,9 +191,13 @@ export class Router {
183
191
  );
184
192
  markedStops.clear();
185
193
  // for each route that can be reached with at least round - 1 trips
186
- for (const [route, hopOnStop] of reachableRoutes.entries()) {
194
+ const reachableRoutesArray = Array.from(reachableRoutes.entries());
195
+ for (let i = 0; i < reachableRoutesArray.length; i++) {
196
+ const [route, hopOnStop] = reachableRoutesArray[i]!;
187
197
  let currentTrip: CurrentTrip | undefined = undefined;
188
- for (const currentStop of route.stopsIterator(hopOnStop)) {
198
+ const startIndex = route.stopIndex(hopOnStop);
199
+ for (let j = startIndex; j < route.getNbStops(); j++) {
200
+ const currentStop = route.stops[j]!;
189
201
  // If we're currently on a trip,
190
202
  // check if arrival at the stop improves the earliest arrival time
191
203
  if (currentTrip !== undefined) {
@@ -25,17 +25,14 @@ export class StopsIndex {
25
25
  constructor(stops: Stop[]) {
26
26
  this.stops = stops;
27
27
  this.sourceStopsMap = new Map<SourceStopId, StopId>();
28
- stops.forEach((stop, id) => {
29
- this.sourceStopsMap.set(stop.sourceStopId, id);
30
- });
31
- this.textIndex = createIndex({
32
- fields: ['name'],
33
- storeFields: ['id'],
34
- searchOptions: { prefix: true, fuzzy: 0.2 },
35
- processTerm: generateAccentVariants,
36
- });
37
28
  const stopsSet = new Map<StopId, { id: StopId; name: string }>();
38
- stops.forEach((stop, id) => {
29
+ this.stopPoints = [];
30
+ for (let id = 0; id < stops.length; id++) {
31
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
32
+ const stop = stops[id]!;
33
+
34
+ this.sourceStopsMap.set(stop.sourceStopId, id);
35
+
39
36
  const effectiveStopId = stop.parent ?? id;
40
37
  if (!stopsSet.has(effectiveStopId)) {
41
38
  stopsSet.set(effectiveStopId, {
@@ -44,22 +41,27 @@ export class StopsIndex {
44
41
  name: stop.parent ? this.stops[stop.parent]!.name : stop.name,
45
42
  });
46
43
  }
44
+
45
+ if (stop.lat && stop.lon) {
46
+ this.stopPoints.push({
47
+ id: id,
48
+ lat: stop.lat,
49
+ lon: stop.lon,
50
+ });
51
+ }
52
+ }
53
+ this.textIndex = createIndex({
54
+ fields: ['name'],
55
+ storeFields: ['id'],
56
+ searchOptions: { prefix: true, fuzzy: 0.2 },
57
+ processTerm: generateAccentVariants,
47
58
  });
48
59
  const stopsArray = Array.from(stopsSet.values());
49
60
  addAll(this.textIndex, stopsArray);
50
-
51
- this.stopPoints = this.stops
52
- .filter((stop) => {
53
- if (stop.lat && stop.lon) return true;
54
- return false;
55
- })
56
- .map((stop, id) => ({
57
- id: id,
58
- lat: stop.lat as number,
59
- lon: stop.lon as number,
60
- }));
61
61
  this.geoIndex = new KDTree(this.stopPoints.length);
62
- for (const { lat, lon } of this.stopPoints) {
62
+ for (let i = 0; i < this.stopPoints.length; i++) {
63
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
64
+ const { lat, lon } = this.stopPoints[i]!;
63
65
  this.geoIndex.add(lon, lat);
64
66
  }
65
67
  this.geoIndex.finish();
@@ -249,25 +249,6 @@ describe('Route', () => {
249
249
  });
250
250
  });
251
251
 
252
- describe('stopsIterator', () => {
253
- it('should iterate over all stops when no start stop is provided', () => {
254
- const stopsList = Array.from(route.stopsIterator());
255
- assert.deepStrictEqual(stopsList, [1001, 1002]);
256
- });
257
-
258
- it('should iterate from specified start stop', () => {
259
- const stopsList = Array.from(route.stopsIterator(1002));
260
- assert.deepStrictEqual(stopsList, [1002]);
261
- });
262
-
263
- it('should throw error for invalid start stop ID', () => {
264
- assert.throws(
265
- () => Array.from(route.stopsIterator(9999)),
266
- /Start stop 9999 not found in route 0/,
267
- );
268
- });
269
- });
270
-
271
252
  describe('findEarliestTrip', () => {
272
253
  it('should find earliest trip without time constraint', () => {
273
254
  const tripIndex = route.findEarliestTrip(1001);
@@ -4,7 +4,6 @@ import {
4
4
  RouteType as ProtoRouteType,
5
5
  ServiceRoute as ProtoServiceRoute,
6
6
  StopAdjacency as ProtoStopAdjacency,
7
- Transfer as ProtoTransfer,
8
7
  TransferType as ProtoTransferType,
9
8
  } from './proto/timetable.js';
10
9
  import { Route } from './route.js';
@@ -171,20 +170,33 @@ export const serializeServiceRoutesMap = (
171
170
  export const deserializeStopsAdjacency = (
172
171
  protoStopsAdjacency: ProtoStopAdjacency[],
173
172
  ): StopAdjacency[] => {
174
- return protoStopsAdjacency.map((value) => {
175
- return {
176
- transfers: value.transfers.map(
177
- (transfer: ProtoTransfer): Transfer => ({
178
- destination: transfer.destination,
179
- type: parseTransferType(transfer.type),
180
- ...(transfer.minTransferTime !== undefined && {
181
- minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
182
- }),
173
+ const result: StopAdjacency[] = [];
174
+
175
+ for (let i = 0; i < protoStopsAdjacency.length; i++) {
176
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
177
+ const value = protoStopsAdjacency[i]!;
178
+ const transfers: Transfer[] = [];
179
+
180
+ for (let j = 0; j < value.transfers.length; j++) {
181
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
182
+ const transfer = value.transfers[j]!;
183
+ const newTransfer: Transfer = {
184
+ destination: transfer.destination,
185
+ type: parseTransferType(transfer.type),
186
+ ...(transfer.minTransferTime !== undefined && {
187
+ minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
183
188
  }),
184
- ),
189
+ };
190
+ transfers.push(newTransfer);
191
+ }
192
+
193
+ result.push({
194
+ transfers: transfers,
185
195
  routes: value.routes,
186
- };
187
- });
196
+ });
197
+ }
198
+
199
+ return result;
188
200
  };
189
201
 
190
202
  export const deserializeRoutesAdjacency = (
@@ -192,7 +204,9 @@ export const deserializeRoutesAdjacency = (
192
204
  ): Route[] => {
193
205
  const routesAdjacency: Route[] = [];
194
206
 
195
- protoRoutesAdjacency.forEach((value) => {
207
+ for (let i = 0; i < protoRoutesAdjacency.length; i++) {
208
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
209
+ const value = protoRoutesAdjacency[i]!;
196
210
  const stops = bytesToUint32Array(value.stops);
197
211
  routesAdjacency.push(
198
212
  new Route(
@@ -202,7 +216,7 @@ export const deserializeRoutesAdjacency = (
202
216
  value.serviceRouteId,
203
217
  ),
204
218
  );
205
- });
219
+ }
206
220
 
207
221
  return routesAdjacency;
208
222
  };
@@ -210,13 +224,19 @@ export const deserializeRoutesAdjacency = (
210
224
  export const deserializeServiceRoutesMap = (
211
225
  protoServiceRoutes: ProtoServiceRoute[],
212
226
  ): ServiceRoute[] => {
213
- return protoServiceRoutes.map((value) => {
214
- return {
227
+ const result: ServiceRoute[] = [];
228
+
229
+ for (let i = 0; i < protoServiceRoutes.length; i++) {
230
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
231
+ const value = protoServiceRoutes[i]!;
232
+ result.push({
215
233
  type: parseRouteType(value.type),
216
234
  name: value.name,
217
235
  routes: value.routes,
218
- };
219
- });
236
+ });
237
+ }
238
+
239
+ return result;
220
240
  };
221
241
 
222
242
  const parseTransferType = (type: ProtoTransferType): TransferType => {
@@ -42,7 +42,7 @@ message Transfer {
42
42
 
43
43
  message StopAdjacency {
44
44
  repeated Transfer transfers = 1;
45
- repeated int32 routes = 2;
45
+ repeated uint32 routes = 2;
46
46
  }
47
47
 
48
48
  enum RouteType {
@@ -61,7 +61,7 @@ enum RouteType {
61
61
  message ServiceRoute {
62
62
  RouteType type = 1;
63
63
  string name = 2;
64
- repeated int32 routes = 3;
64
+ repeated uint32 routes = 3;
65
65
  }
66
66
 
67
67
  message Timetable {
@@ -402,7 +402,7 @@ export const StopAdjacency: MessageFns<StopAdjacency> = {
402
402
  }
403
403
  writer.uint32(18).fork();
404
404
  for (const v of message.routes) {
405
- writer.int32(v);
405
+ writer.uint32(v);
406
406
  }
407
407
  writer.join();
408
408
  return writer;
@@ -425,7 +425,7 @@ export const StopAdjacency: MessageFns<StopAdjacency> = {
425
425
  }
426
426
  case 2: {
427
427
  if (tag === 16) {
428
- message.routes.push(reader.int32());
428
+ message.routes.push(reader.uint32());
429
429
 
430
430
  continue;
431
431
  }
@@ -433,7 +433,7 @@ export const StopAdjacency: MessageFns<StopAdjacency> = {
433
433
  if (tag === 18) {
434
434
  const end2 = reader.uint32() + reader.pos;
435
435
  while (reader.pos < end2) {
436
- message.routes.push(reader.int32());
436
+ message.routes.push(reader.uint32());
437
437
  }
438
438
 
439
439
  continue;
@@ -495,7 +495,7 @@ export const ServiceRoute: MessageFns<ServiceRoute> = {
495
495
  }
496
496
  writer.uint32(26).fork();
497
497
  for (const v of message.routes) {
498
- writer.int32(v);
498
+ writer.uint32(v);
499
499
  }
500
500
  writer.join();
501
501
  return writer;
@@ -526,7 +526,7 @@ export const ServiceRoute: MessageFns<ServiceRoute> = {
526
526
  }
527
527
  case 3: {
528
528
  if (tag === 24) {
529
- message.routes.push(reader.int32());
529
+ message.routes.push(reader.uint32());
530
530
 
531
531
  continue;
532
532
  }
@@ -534,7 +534,7 @@ export const ServiceRoute: MessageFns<ServiceRoute> = {
534
534
  if (tag === 26) {
535
535
  const end2 = reader.uint32() + reader.pos;
536
536
  while (reader.pos < end2) {
537
- message.routes.push(reader.int32());
537
+ message.routes.push(reader.uint32());
538
538
  }
539
539
 
540
540
  continue;
@@ -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
- private readonly stops: Uint32Array;
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
- private stopIndex(stopId: StopId): number {
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';
@@ -67,7 +68,7 @@ export const ALL_TRANSPORT_MODES: Set<RouteType> = new Set([
67
68
  'MONORAIL',
68
69
  ]);
69
70
 
70
- export const CURRENT_VERSION = '0.0.6';
71
+ export const CURRENT_VERSION = '0.0.7';
71
72
 
72
73
  /**
73
74
  * The internal transit timetable format.
@@ -200,7 +201,8 @@ export class Timetable {
200
201
  return [];
201
202
  }
202
203
  const routes: Route[] = [];
203
- for (const routeId of stopData.routes) {
204
+ for (let i = 0; i < stopData.routes.length; i++) {
205
+ const routeId = stopData.routes[i]!;
204
206
  const route = this.routesAdjacency[routeId];
205
207
  if (route) {
206
208
  routes.push(route);
@@ -223,14 +225,17 @@ export class Timetable {
223
225
  transportModes: Set<RouteType> = ALL_TRANSPORT_MODES,
224
226
  ): Map<Route, StopId> {
225
227
  const reachableRoutes = new Map<Route, StopId>();
226
- for (const originStop of fromStops) {
228
+ const fromStopsArray = Array.from(fromStops);
229
+ for (let i = 0; i < fromStopsArray.length; i++) {
230
+ const originStop = fromStopsArray[i]!;
227
231
  const validRoutes = this.routesPassingThrough(originStop).filter(
228
232
  (route) => {
229
233
  const serviceRoute = this.getServiceRouteInfo(route);
230
234
  return transportModes.has(serviceRoute.type);
231
235
  },
232
236
  );
233
- for (const route of validRoutes) {
237
+ for (let j = 0; j < validRoutes.length; j++) {
238
+ const route = validRoutes[j]!;
234
239
  const hopOnStop = reachableRoutes.get(route);
235
240
  if (hopOnStop) {
236
241
  if (route.isBefore(originStop, hopOnStop)) {