minotor 7.0.2 → 9.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.
Files changed (60) hide show
  1. package/.cspell.json +11 -1
  2. package/CHANGELOG.md +8 -3
  3. package/README.md +26 -24
  4. package/dist/cli.mjs +1786 -791
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/gtfs/transfers.d.ts +29 -5
  7. package/dist/gtfs/trips.d.ts +10 -5
  8. package/dist/parser.cjs.js +972 -525
  9. package/dist/parser.cjs.js.map +1 -1
  10. package/dist/parser.esm.js +972 -525
  11. package/dist/parser.esm.js.map +1 -1
  12. package/dist/router.cjs.js +1 -1
  13. package/dist/router.cjs.js.map +1 -1
  14. package/dist/router.d.ts +2 -2
  15. package/dist/router.esm.js +1 -1
  16. package/dist/router.esm.js.map +1 -1
  17. package/dist/router.umd.js +1 -1
  18. package/dist/router.umd.js.map +1 -1
  19. package/dist/routing/__tests__/plotter.test.d.ts +1 -0
  20. package/dist/routing/plotter.d.ts +42 -3
  21. package/dist/routing/result.d.ts +23 -7
  22. package/dist/routing/route.d.ts +2 -0
  23. package/dist/routing/router.d.ts +78 -19
  24. package/dist/timetable/__tests__/tripBoardingId.test.d.ts +1 -0
  25. package/dist/timetable/io.d.ts +4 -2
  26. package/dist/timetable/proto/timetable.d.ts +15 -1
  27. package/dist/timetable/route.d.ts +48 -23
  28. package/dist/timetable/timetable.d.ts +24 -7
  29. package/dist/timetable/tripBoardingId.d.ts +34 -0
  30. package/package.json +1 -1
  31. package/src/__e2e__/router.test.ts +114 -105
  32. package/src/__e2e__/timetable/stops.bin +2 -2
  33. package/src/__e2e__/timetable/timetable.bin +2 -2
  34. package/src/cli/repl.ts +245 -1
  35. package/src/gtfs/__tests__/parser.test.ts +19 -4
  36. package/src/gtfs/__tests__/transfers.test.ts +773 -37
  37. package/src/gtfs/__tests__/trips.test.ts +308 -27
  38. package/src/gtfs/parser.ts +36 -6
  39. package/src/gtfs/transfers.ts +193 -19
  40. package/src/gtfs/trips.ts +58 -21
  41. package/src/router.ts +2 -2
  42. package/src/routing/__tests__/plotter.test.ts +230 -0
  43. package/src/routing/__tests__/result.test.ts +486 -125
  44. package/src/routing/__tests__/route.test.ts +7 -3
  45. package/src/routing/__tests__/router.test.ts +380 -172
  46. package/src/routing/plotter.ts +279 -48
  47. package/src/routing/result.ts +114 -34
  48. package/src/routing/route.ts +0 -3
  49. package/src/routing/router.ts +344 -211
  50. package/src/timetable/__tests__/io.test.ts +34 -1
  51. package/src/timetable/__tests__/route.test.ts +74 -81
  52. package/src/timetable/__tests__/timetable.test.ts +232 -61
  53. package/src/timetable/__tests__/tripBoardingId.test.ts +57 -0
  54. package/src/timetable/io.ts +72 -10
  55. package/src/timetable/proto/timetable.proto +16 -2
  56. package/src/timetable/proto/timetable.ts +256 -22
  57. package/src/timetable/route.ts +174 -58
  58. package/src/timetable/timetable.ts +66 -16
  59. package/src/timetable/tripBoardingId.ts +94 -0
  60. package/tsconfig.json +2 -2
@@ -5,58 +5,6 @@ import require$$3 from 'events';
5
5
  import require$$4 from 'zlib';
6
6
  import require$$5, { Transform } from 'stream';
7
7
 
8
- /******************************************************************************
9
- Copyright (c) Microsoft Corporation.
10
-
11
- Permission to use, copy, modify, and/or distribute this software for any
12
- purpose with or without fee is hereby granted.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
15
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
16
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
17
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
18
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
19
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20
- PERFORMANCE OF THIS SOFTWARE.
21
- ***************************************************************************** */
22
- /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
23
-
24
-
25
- function __awaiter(thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- }
34
-
35
- function __values(o) {
36
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
37
- if (m) return m.call(o);
38
- if (o && typeof o.length === "number") return {
39
- next: function () {
40
- if (o && i >= o.length) o = void 0;
41
- return { value: o && o[i++], done: !o };
42
- }
43
- };
44
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
45
- }
46
-
47
- function __asyncValues(o) {
48
- if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
49
- var m = o[Symbol.asyncIterator], i;
50
- return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
51
- function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
52
- function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
53
- }
54
-
55
- typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
56
- var e = new Error(message);
57
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
58
- };
59
-
60
8
  function getDefaultExportFromCjs (x) {
61
9
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
62
10
  }
@@ -11552,14 +11500,13 @@ const Stop = {
11552
11500
  sourceStopId: isSet$1(object.sourceStopId) ? globalThis.String(object.sourceStopId) : "",
11553
11501
  lat: isSet$1(object.lat) ? globalThis.Number(object.lat) : undefined,
11554
11502
  lon: isSet$1(object.lon) ? globalThis.Number(object.lon) : undefined,
11555
- children: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.children) ? object.children.map((e) => globalThis.Number(e)) : [],
11503
+ children: globalThis.Array.isArray(object?.children) ? object.children.map((e) => globalThis.Number(e)) : [],
11556
11504
  parent: isSet$1(object.parent) ? globalThis.Number(object.parent) : undefined,
11557
11505
  locationType: isSet$1(object.locationType) ? locationTypeFromJSON(object.locationType) : 0,
11558
11506
  platform: isSet$1(object.platform) ? globalThis.String(object.platform) : undefined,
11559
11507
  };
11560
11508
  },
11561
11509
  toJSON(message) {
11562
- var _a;
11563
11510
  const obj = {};
11564
11511
  if (message.name !== "") {
11565
11512
  obj.name = message.name;
@@ -11573,7 +11520,7 @@ const Stop = {
11573
11520
  if (message.lon !== undefined) {
11574
11521
  obj.lon = message.lon;
11575
11522
  }
11576
- if ((_a = message.children) === null || _a === void 0 ? void 0 : _a.length) {
11523
+ if (message.children?.length) {
11577
11524
  obj.children = message.children.map((e) => Math.round(e));
11578
11525
  }
11579
11526
  if (message.parent !== undefined) {
@@ -11588,19 +11535,18 @@ const Stop = {
11588
11535
  return obj;
11589
11536
  },
11590
11537
  create(base) {
11591
- return Stop.fromPartial(base !== null && base !== void 0 ? base : {});
11538
+ return Stop.fromPartial(base ?? {});
11592
11539
  },
11593
11540
  fromPartial(object) {
11594
- var _a, _b, _c, _d, _e, _f, _g, _h;
11595
11541
  const message = createBaseStop();
11596
- message.name = (_a = object.name) !== null && _a !== void 0 ? _a : "";
11597
- message.sourceStopId = (_b = object.sourceStopId) !== null && _b !== void 0 ? _b : "";
11598
- message.lat = (_c = object.lat) !== null && _c !== void 0 ? _c : undefined;
11599
- message.lon = (_d = object.lon) !== null && _d !== void 0 ? _d : undefined;
11600
- message.children = ((_e = object.children) === null || _e === void 0 ? void 0 : _e.map((e) => e)) || [];
11601
- message.parent = (_f = object.parent) !== null && _f !== void 0 ? _f : undefined;
11602
- message.locationType = (_g = object.locationType) !== null && _g !== void 0 ? _g : 0;
11603
- message.platform = (_h = object.platform) !== null && _h !== void 0 ? _h : undefined;
11542
+ message.name = object.name ?? "";
11543
+ message.sourceStopId = object.sourceStopId ?? "";
11544
+ message.lat = object.lat ?? undefined;
11545
+ message.lon = object.lon ?? undefined;
11546
+ message.children = object.children?.map((e) => e) || [];
11547
+ message.parent = object.parent ?? undefined;
11548
+ message.locationType = object.locationType ?? 0;
11549
+ message.platform = object.platform ?? undefined;
11604
11550
  return message;
11605
11551
  },
11606
11552
  };
@@ -11649,28 +11595,26 @@ const StopsMap = {
11649
11595
  fromJSON(object) {
11650
11596
  return {
11651
11597
  version: isSet$1(object.version) ? globalThis.String(object.version) : "",
11652
- stops: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.stops) ? object.stops.map((e) => Stop.fromJSON(e)) : [],
11598
+ stops: globalThis.Array.isArray(object?.stops) ? object.stops.map((e) => Stop.fromJSON(e)) : [],
11653
11599
  };
11654
11600
  },
11655
11601
  toJSON(message) {
11656
- var _a;
11657
11602
  const obj = {};
11658
11603
  if (message.version !== "") {
11659
11604
  obj.version = message.version;
11660
11605
  }
11661
- if ((_a = message.stops) === null || _a === void 0 ? void 0 : _a.length) {
11606
+ if (message.stops?.length) {
11662
11607
  obj.stops = message.stops.map((e) => Stop.toJSON(e));
11663
11608
  }
11664
11609
  return obj;
11665
11610
  },
11666
11611
  create(base) {
11667
- return StopsMap.fromPartial(base !== null && base !== void 0 ? base : {});
11612
+ return StopsMap.fromPartial(base ?? {});
11668
11613
  },
11669
11614
  fromPartial(object) {
11670
- var _a, _b;
11671
11615
  const message = createBaseStopsMap();
11672
- message.version = (_a = object.version) !== null && _a !== void 0 ? _a : "";
11673
- message.stops = ((_b = object.stops) === null || _b === void 0 ? void 0 : _b.map((e) => Stop.fromPartial(e))) || [];
11616
+ message.version = object.version ?? "";
11617
+ message.stops = object.stops?.map((e) => Stop.fromPartial(e)) || [];
11674
11618
  return message;
11675
11619
  },
11676
11620
  };
@@ -11754,8 +11698,12 @@ const serializeLocationType = (locationType) => {
11754
11698
  * to efficiently find stops based on user queries.
11755
11699
  */
11756
11700
  class StopsIndex {
11701
+ stops;
11702
+ sourceStopsMap;
11703
+ textIndex;
11704
+ geoIndex;
11705
+ stopPoints;
11757
11706
  constructor(stops) {
11758
- var _a;
11759
11707
  this.stops = stops;
11760
11708
  this.sourceStopsMap = new Map();
11761
11709
  const stopsSet = new Map();
@@ -11764,7 +11712,7 @@ class StopsIndex {
11764
11712
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
11765
11713
  const stop = stops[id];
11766
11714
  this.sourceStopsMap.set(stop.sourceStopId, id);
11767
- const effectiveStopId = (_a = stop.parent) !== null && _a !== void 0 ? _a : id;
11715
+ const effectiveStopId = stop.parent ?? id;
11768
11716
  if (!stopsSet.has(effectiveStopId)) {
11769
11717
  stopsSet.set(effectiveStopId, {
11770
11718
  id: effectiveStopId,
@@ -11879,7 +11827,6 @@ class StopsIndex {
11879
11827
  * Find ids of all sibling stops.
11880
11828
  */
11881
11829
  equivalentStops(sourceId) {
11882
- var _a, _b;
11883
11830
  const id = this.sourceStopsMap.get(sourceId);
11884
11831
  if (id === undefined) {
11885
11832
  return [];
@@ -11889,13 +11836,14 @@ class StopsIndex {
11889
11836
  return [];
11890
11837
  }
11891
11838
  const equivalentStops = stop.parent
11892
- ? ((_b = (_a = this.stops[stop.parent]) === null || _a === void 0 ? void 0 : _a.children) !== null && _b !== void 0 ? _b : [])
11839
+ ? (this.stops[stop.parent]?.children ?? [])
11893
11840
  : stop.children;
11894
11841
  return Array.from(new Set([id, ...equivalentStops])).map((stopId) => this.stops[stopId]);
11895
11842
  }
11896
11843
  }
11897
11844
 
11898
11845
  class Duration {
11846
+ totalSeconds;
11899
11847
  constructor(totalSeconds) {
11900
11848
  this.totalSeconds = totalSeconds;
11901
11849
  }
@@ -12198,15 +12146,14 @@ const Route$1 = {
12198
12146
  return obj;
12199
12147
  },
12200
12148
  create(base) {
12201
- return Route$1.fromPartial(base !== null && base !== void 0 ? base : {});
12149
+ return Route$1.fromPartial(base ?? {});
12202
12150
  },
12203
12151
  fromPartial(object) {
12204
- var _a, _b, _c, _d;
12205
12152
  const message = createBaseRoute();
12206
- message.stopTimes = (_a = object.stopTimes) !== null && _a !== void 0 ? _a : new Uint8Array(0);
12207
- message.pickUpDropOffTypes = (_b = object.pickUpDropOffTypes) !== null && _b !== void 0 ? _b : new Uint8Array(0);
12208
- message.stops = (_c = object.stops) !== null && _c !== void 0 ? _c : new Uint8Array(0);
12209
- message.serviceRouteId = (_d = object.serviceRouteId) !== null && _d !== void 0 ? _d : 0;
12153
+ message.stopTimes = object.stopTimes ?? new Uint8Array(0);
12154
+ message.pickUpDropOffTypes = object.pickUpDropOffTypes ?? new Uint8Array(0);
12155
+ message.stops = object.stops ?? new Uint8Array(0);
12156
+ message.serviceRouteId = object.serviceRouteId ?? 0;
12210
12157
  return message;
12211
12158
  },
12212
12159
  };
@@ -12283,52 +12230,227 @@ const Transfer = {
12283
12230
  return obj;
12284
12231
  },
12285
12232
  create(base) {
12286
- return Transfer.fromPartial(base !== null && base !== void 0 ? base : {});
12233
+ return Transfer.fromPartial(base ?? {});
12287
12234
  },
12288
12235
  fromPartial(object) {
12289
- var _a, _b, _c;
12290
12236
  const message = createBaseTransfer();
12291
- message.destination = (_a = object.destination) !== null && _a !== void 0 ? _a : 0;
12292
- message.type = (_b = object.type) !== null && _b !== void 0 ? _b : 0;
12293
- message.minTransferTime = (_c = object.minTransferTime) !== null && _c !== void 0 ? _c : undefined;
12237
+ message.destination = object.destination ?? 0;
12238
+ message.type = object.type ?? 0;
12239
+ message.minTransferTime = object.minTransferTime ?? undefined;
12294
12240
  return message;
12295
12241
  },
12296
12242
  };
12297
- function createBaseStopAdjacency() {
12298
- return { transfers: [], routes: [] };
12243
+ function createBaseTripBoarding() {
12244
+ return { hopOnStopIndex: 0, routeId: 0, tripIndex: 0 };
12299
12245
  }
12300
- const StopAdjacency = {
12246
+ const TripBoarding = {
12301
12247
  encode(message, writer = new BinaryWriter()) {
12302
- for (const v of message.transfers) {
12303
- Transfer.encode(v, writer.uint32(10).fork()).join();
12248
+ if (message.hopOnStopIndex !== 0) {
12249
+ writer.uint32(8).uint32(message.hopOnStopIndex);
12304
12250
  }
12305
- writer.uint32(18).fork();
12306
- for (const v of message.routes) {
12307
- writer.uint32(v);
12251
+ if (message.routeId !== 0) {
12252
+ writer.uint32(16).uint32(message.routeId);
12253
+ }
12254
+ if (message.tripIndex !== 0) {
12255
+ writer.uint32(24).uint32(message.tripIndex);
12308
12256
  }
12309
- writer.join();
12310
12257
  return writer;
12311
12258
  },
12312
12259
  decode(input, length) {
12313
12260
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12314
12261
  const end = length === undefined ? reader.len : reader.pos + length;
12315
- const message = createBaseStopAdjacency();
12262
+ const message = createBaseTripBoarding();
12316
12263
  while (reader.pos < end) {
12317
12264
  const tag = reader.uint32();
12318
12265
  switch (tag >>> 3) {
12319
12266
  case 1: {
12320
- if (tag !== 10) {
12267
+ if (tag !== 8) {
12321
12268
  break;
12322
12269
  }
12323
- message.transfers.push(Transfer.decode(reader, reader.uint32()));
12270
+ message.hopOnStopIndex = reader.uint32();
12271
+ continue;
12272
+ }
12273
+ case 2: {
12274
+ if (tag !== 16) {
12275
+ break;
12276
+ }
12277
+ message.routeId = reader.uint32();
12278
+ continue;
12279
+ }
12280
+ case 3: {
12281
+ if (tag !== 24) {
12282
+ break;
12283
+ }
12284
+ message.tripIndex = reader.uint32();
12285
+ continue;
12286
+ }
12287
+ }
12288
+ if ((tag & 7) === 4 || tag === 0) {
12289
+ break;
12290
+ }
12291
+ reader.skip(tag & 7);
12292
+ }
12293
+ return message;
12294
+ },
12295
+ fromJSON(object) {
12296
+ return {
12297
+ hopOnStopIndex: isSet(object.hopOnStopIndex) ? globalThis.Number(object.hopOnStopIndex) : 0,
12298
+ routeId: isSet(object.routeId) ? globalThis.Number(object.routeId) : 0,
12299
+ tripIndex: isSet(object.tripIndex) ? globalThis.Number(object.tripIndex) : 0,
12300
+ };
12301
+ },
12302
+ toJSON(message) {
12303
+ const obj = {};
12304
+ if (message.hopOnStopIndex !== 0) {
12305
+ obj.hopOnStopIndex = Math.round(message.hopOnStopIndex);
12306
+ }
12307
+ if (message.routeId !== 0) {
12308
+ obj.routeId = Math.round(message.routeId);
12309
+ }
12310
+ if (message.tripIndex !== 0) {
12311
+ obj.tripIndex = Math.round(message.tripIndex);
12312
+ }
12313
+ return obj;
12314
+ },
12315
+ create(base) {
12316
+ return TripBoarding.fromPartial(base ?? {});
12317
+ },
12318
+ fromPartial(object) {
12319
+ const message = createBaseTripBoarding();
12320
+ message.hopOnStopIndex = object.hopOnStopIndex ?? 0;
12321
+ message.routeId = object.routeId ?? 0;
12322
+ message.tripIndex = object.tripIndex ?? 0;
12323
+ return message;
12324
+ },
12325
+ };
12326
+ function createBaseTripContinuationEntry() {
12327
+ return { originStopIndex: 0, originRouteId: 0, originTripIndex: 0, continuations: [] };
12328
+ }
12329
+ const TripContinuationEntry = {
12330
+ encode(message, writer = new BinaryWriter()) {
12331
+ if (message.originStopIndex !== 0) {
12332
+ writer.uint32(8).uint32(message.originStopIndex);
12333
+ }
12334
+ if (message.originRouteId !== 0) {
12335
+ writer.uint32(16).uint32(message.originRouteId);
12336
+ }
12337
+ if (message.originTripIndex !== 0) {
12338
+ writer.uint32(24).uint32(message.originTripIndex);
12339
+ }
12340
+ for (const v of message.continuations) {
12341
+ TripBoarding.encode(v, writer.uint32(34).fork()).join();
12342
+ }
12343
+ return writer;
12344
+ },
12345
+ decode(input, length) {
12346
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12347
+ const end = length === undefined ? reader.len : reader.pos + length;
12348
+ const message = createBaseTripContinuationEntry();
12349
+ while (reader.pos < end) {
12350
+ const tag = reader.uint32();
12351
+ switch (tag >>> 3) {
12352
+ case 1: {
12353
+ if (tag !== 8) {
12354
+ break;
12355
+ }
12356
+ message.originStopIndex = reader.uint32();
12324
12357
  continue;
12325
12358
  }
12326
12359
  case 2: {
12327
- if (tag === 16) {
12360
+ if (tag !== 16) {
12361
+ break;
12362
+ }
12363
+ message.originRouteId = reader.uint32();
12364
+ continue;
12365
+ }
12366
+ case 3: {
12367
+ if (tag !== 24) {
12368
+ break;
12369
+ }
12370
+ message.originTripIndex = reader.uint32();
12371
+ continue;
12372
+ }
12373
+ case 4: {
12374
+ if (tag !== 34) {
12375
+ break;
12376
+ }
12377
+ message.continuations.push(TripBoarding.decode(reader, reader.uint32()));
12378
+ continue;
12379
+ }
12380
+ }
12381
+ if ((tag & 7) === 4 || tag === 0) {
12382
+ break;
12383
+ }
12384
+ reader.skip(tag & 7);
12385
+ }
12386
+ return message;
12387
+ },
12388
+ fromJSON(object) {
12389
+ return {
12390
+ originStopIndex: isSet(object.originStopIndex) ? globalThis.Number(object.originStopIndex) : 0,
12391
+ originRouteId: isSet(object.originRouteId) ? globalThis.Number(object.originRouteId) : 0,
12392
+ originTripIndex: isSet(object.originTripIndex) ? globalThis.Number(object.originTripIndex) : 0,
12393
+ continuations: globalThis.Array.isArray(object?.continuations)
12394
+ ? object.continuations.map((e) => TripBoarding.fromJSON(e))
12395
+ : [],
12396
+ };
12397
+ },
12398
+ toJSON(message) {
12399
+ const obj = {};
12400
+ if (message.originStopIndex !== 0) {
12401
+ obj.originStopIndex = Math.round(message.originStopIndex);
12402
+ }
12403
+ if (message.originRouteId !== 0) {
12404
+ obj.originRouteId = Math.round(message.originRouteId);
12405
+ }
12406
+ if (message.originTripIndex !== 0) {
12407
+ obj.originTripIndex = Math.round(message.originTripIndex);
12408
+ }
12409
+ if (message.continuations?.length) {
12410
+ obj.continuations = message.continuations.map((e) => TripBoarding.toJSON(e));
12411
+ }
12412
+ return obj;
12413
+ },
12414
+ create(base) {
12415
+ return TripContinuationEntry.fromPartial(base ?? {});
12416
+ },
12417
+ fromPartial(object) {
12418
+ const message = createBaseTripContinuationEntry();
12419
+ message.originStopIndex = object.originStopIndex ?? 0;
12420
+ message.originRouteId = object.originRouteId ?? 0;
12421
+ message.originTripIndex = object.originTripIndex ?? 0;
12422
+ message.continuations = object.continuations?.map((e) => TripBoarding.fromPartial(e)) || [];
12423
+ return message;
12424
+ },
12425
+ };
12426
+ function createBaseStopAdjacency() {
12427
+ return { routes: [], transfers: [] };
12428
+ }
12429
+ const StopAdjacency = {
12430
+ encode(message, writer = new BinaryWriter()) {
12431
+ writer.uint32(10).fork();
12432
+ for (const v of message.routes) {
12433
+ writer.uint32(v);
12434
+ }
12435
+ writer.join();
12436
+ for (const v of message.transfers) {
12437
+ Transfer.encode(v, writer.uint32(18).fork()).join();
12438
+ }
12439
+ return writer;
12440
+ },
12441
+ decode(input, length) {
12442
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12443
+ const end = length === undefined ? reader.len : reader.pos + length;
12444
+ const message = createBaseStopAdjacency();
12445
+ while (reader.pos < end) {
12446
+ const tag = reader.uint32();
12447
+ switch (tag >>> 3) {
12448
+ case 1: {
12449
+ if (tag === 8) {
12328
12450
  message.routes.push(reader.uint32());
12329
12451
  continue;
12330
12452
  }
12331
- if (tag === 18) {
12453
+ if (tag === 10) {
12332
12454
  const end2 = reader.uint32() + reader.pos;
12333
12455
  while (reader.pos < end2) {
12334
12456
  message.routes.push(reader.uint32());
@@ -12337,6 +12459,13 @@ const StopAdjacency = {
12337
12459
  }
12338
12460
  break;
12339
12461
  }
12462
+ case 2: {
12463
+ if (tag !== 18) {
12464
+ break;
12465
+ }
12466
+ message.transfers.push(Transfer.decode(reader, reader.uint32()));
12467
+ continue;
12468
+ }
12340
12469
  }
12341
12470
  if ((tag & 7) === 4 || tag === 0) {
12342
12471
  break;
@@ -12347,31 +12476,29 @@ const StopAdjacency = {
12347
12476
  },
12348
12477
  fromJSON(object) {
12349
12478
  return {
12350
- transfers: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.transfers)
12479
+ routes: globalThis.Array.isArray(object?.routes) ? object.routes.map((e) => globalThis.Number(e)) : [],
12480
+ transfers: globalThis.Array.isArray(object?.transfers)
12351
12481
  ? object.transfers.map((e) => Transfer.fromJSON(e))
12352
12482
  : [],
12353
- routes: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.routes) ? object.routes.map((e) => globalThis.Number(e)) : [],
12354
12483
  };
12355
12484
  },
12356
12485
  toJSON(message) {
12357
- var _a, _b;
12358
12486
  const obj = {};
12359
- if ((_a = message.transfers) === null || _a === void 0 ? void 0 : _a.length) {
12360
- obj.transfers = message.transfers.map((e) => Transfer.toJSON(e));
12361
- }
12362
- if ((_b = message.routes) === null || _b === void 0 ? void 0 : _b.length) {
12487
+ if (message.routes?.length) {
12363
12488
  obj.routes = message.routes.map((e) => Math.round(e));
12364
12489
  }
12490
+ if (message.transfers?.length) {
12491
+ obj.transfers = message.transfers.map((e) => Transfer.toJSON(e));
12492
+ }
12365
12493
  return obj;
12366
12494
  },
12367
12495
  create(base) {
12368
- return StopAdjacency.fromPartial(base !== null && base !== void 0 ? base : {});
12496
+ return StopAdjacency.fromPartial(base ?? {});
12369
12497
  },
12370
12498
  fromPartial(object) {
12371
- var _a, _b;
12372
12499
  const message = createBaseStopAdjacency();
12373
- message.transfers = ((_a = object.transfers) === null || _a === void 0 ? void 0 : _a.map((e) => Transfer.fromPartial(e))) || [];
12374
- message.routes = ((_b = object.routes) === null || _b === void 0 ? void 0 : _b.map((e) => e)) || [];
12500
+ message.routes = object.routes?.map((e) => e) || [];
12501
+ message.transfers = object.transfers?.map((e) => Transfer.fromPartial(e)) || [];
12375
12502
  return message;
12376
12503
  },
12377
12504
  };
@@ -12440,11 +12567,10 @@ const ServiceRoute = {
12440
12567
  return {
12441
12568
  type: isSet(object.type) ? routeTypeFromJSON(object.type) : 0,
12442
12569
  name: isSet(object.name) ? globalThis.String(object.name) : "",
12443
- routes: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.routes) ? object.routes.map((e) => globalThis.Number(e)) : [],
12570
+ routes: globalThis.Array.isArray(object?.routes) ? object.routes.map((e) => globalThis.Number(e)) : [],
12444
12571
  };
12445
12572
  },
12446
12573
  toJSON(message) {
12447
- var _a;
12448
12574
  const obj = {};
12449
12575
  if (message.type !== 0) {
12450
12576
  obj.type = routeTypeToJSON(message.type);
@@ -12452,25 +12578,24 @@ const ServiceRoute = {
12452
12578
  if (message.name !== "") {
12453
12579
  obj.name = message.name;
12454
12580
  }
12455
- if ((_a = message.routes) === null || _a === void 0 ? void 0 : _a.length) {
12581
+ if (message.routes?.length) {
12456
12582
  obj.routes = message.routes.map((e) => Math.round(e));
12457
12583
  }
12458
12584
  return obj;
12459
12585
  },
12460
12586
  create(base) {
12461
- return ServiceRoute.fromPartial(base !== null && base !== void 0 ? base : {});
12587
+ return ServiceRoute.fromPartial(base ?? {});
12462
12588
  },
12463
12589
  fromPartial(object) {
12464
- var _a, _b, _c;
12465
12590
  const message = createBaseServiceRoute();
12466
- message.type = (_a = object.type) !== null && _a !== void 0 ? _a : 0;
12467
- message.name = (_b = object.name) !== null && _b !== void 0 ? _b : "";
12468
- message.routes = ((_c = object.routes) === null || _c === void 0 ? void 0 : _c.map((e) => e)) || [];
12591
+ message.type = object.type ?? 0;
12592
+ message.name = object.name ?? "";
12593
+ message.routes = object.routes?.map((e) => e) || [];
12469
12594
  return message;
12470
12595
  },
12471
12596
  };
12472
12597
  function createBaseTimetable() {
12473
- return { version: "", stopsAdjacency: [], routesAdjacency: [], serviceRoutes: [] };
12598
+ return { version: "", stopsAdjacency: [], routesAdjacency: [], serviceRoutes: [], tripContinuations: [] };
12474
12599
  }
12475
12600
  const Timetable$1 = {
12476
12601
  encode(message, writer = new BinaryWriter()) {
@@ -12486,6 +12611,9 @@ const Timetable$1 = {
12486
12611
  for (const v of message.serviceRoutes) {
12487
12612
  ServiceRoute.encode(v, writer.uint32(34).fork()).join();
12488
12613
  }
12614
+ for (const v of message.tripContinuations) {
12615
+ TripContinuationEntry.encode(v, writer.uint32(42).fork()).join();
12616
+ }
12489
12617
  return writer;
12490
12618
  },
12491
12619
  decode(input, length) {
@@ -12523,6 +12651,13 @@ const Timetable$1 = {
12523
12651
  message.serviceRoutes.push(ServiceRoute.decode(reader, reader.uint32()));
12524
12652
  continue;
12525
12653
  }
12654
+ case 5: {
12655
+ if (tag !== 42) {
12656
+ break;
12657
+ }
12658
+ message.tripContinuations.push(TripContinuationEntry.decode(reader, reader.uint32()));
12659
+ continue;
12660
+ }
12526
12661
  }
12527
12662
  if ((tag & 7) === 4 || tag === 0) {
12528
12663
  break;
@@ -12534,44 +12669,49 @@ const Timetable$1 = {
12534
12669
  fromJSON(object) {
12535
12670
  return {
12536
12671
  version: isSet(object.version) ? globalThis.String(object.version) : "",
12537
- stopsAdjacency: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.stopsAdjacency)
12672
+ stopsAdjacency: globalThis.Array.isArray(object?.stopsAdjacency)
12538
12673
  ? object.stopsAdjacency.map((e) => StopAdjacency.fromJSON(e))
12539
12674
  : [],
12540
- routesAdjacency: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.routesAdjacency)
12675
+ routesAdjacency: globalThis.Array.isArray(object?.routesAdjacency)
12541
12676
  ? object.routesAdjacency.map((e) => Route$1.fromJSON(e))
12542
12677
  : [],
12543
- serviceRoutes: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.serviceRoutes)
12678
+ serviceRoutes: globalThis.Array.isArray(object?.serviceRoutes)
12544
12679
  ? object.serviceRoutes.map((e) => ServiceRoute.fromJSON(e))
12545
12680
  : [],
12681
+ tripContinuations: globalThis.Array.isArray(object?.tripContinuations)
12682
+ ? object.tripContinuations.map((e) => TripContinuationEntry.fromJSON(e))
12683
+ : [],
12546
12684
  };
12547
12685
  },
12548
12686
  toJSON(message) {
12549
- var _a, _b, _c;
12550
12687
  const obj = {};
12551
12688
  if (message.version !== "") {
12552
12689
  obj.version = message.version;
12553
12690
  }
12554
- if ((_a = message.stopsAdjacency) === null || _a === void 0 ? void 0 : _a.length) {
12691
+ if (message.stopsAdjacency?.length) {
12555
12692
  obj.stopsAdjacency = message.stopsAdjacency.map((e) => StopAdjacency.toJSON(e));
12556
12693
  }
12557
- if ((_b = message.routesAdjacency) === null || _b === void 0 ? void 0 : _b.length) {
12694
+ if (message.routesAdjacency?.length) {
12558
12695
  obj.routesAdjacency = message.routesAdjacency.map((e) => Route$1.toJSON(e));
12559
12696
  }
12560
- if ((_c = message.serviceRoutes) === null || _c === void 0 ? void 0 : _c.length) {
12697
+ if (message.serviceRoutes?.length) {
12561
12698
  obj.serviceRoutes = message.serviceRoutes.map((e) => ServiceRoute.toJSON(e));
12562
12699
  }
12700
+ if (message.tripContinuations?.length) {
12701
+ obj.tripContinuations = message.tripContinuations.map((e) => TripContinuationEntry.toJSON(e));
12702
+ }
12563
12703
  return obj;
12564
12704
  },
12565
12705
  create(base) {
12566
- return Timetable$1.fromPartial(base !== null && base !== void 0 ? base : {});
12706
+ return Timetable$1.fromPartial(base ?? {});
12567
12707
  },
12568
12708
  fromPartial(object) {
12569
- var _a, _b, _c, _d;
12570
12709
  const message = createBaseTimetable();
12571
- message.version = (_a = object.version) !== null && _a !== void 0 ? _a : "";
12572
- message.stopsAdjacency = ((_b = object.stopsAdjacency) === null || _b === void 0 ? void 0 : _b.map((e) => StopAdjacency.fromPartial(e))) || [];
12573
- message.routesAdjacency = ((_c = object.routesAdjacency) === null || _c === void 0 ? void 0 : _c.map((e) => Route$1.fromPartial(e))) || [];
12574
- message.serviceRoutes = ((_d = object.serviceRoutes) === null || _d === void 0 ? void 0 : _d.map((e) => ServiceRoute.fromPartial(e))) || [];
12710
+ message.version = object.version ?? "";
12711
+ message.stopsAdjacency = object.stopsAdjacency?.map((e) => StopAdjacency.fromPartial(e)) || [];
12712
+ message.routesAdjacency = object.routesAdjacency?.map((e) => Route$1.fromPartial(e)) || [];
12713
+ message.serviceRoutes = object.serviceRoutes?.map((e) => ServiceRoute.fromPartial(e)) || [];
12714
+ message.tripContinuations = object.tripContinuations?.map((e) => TripContinuationEntry.fromPartial(e)) || [];
12575
12715
  return message;
12576
12716
  },
12577
12717
  };
@@ -12608,6 +12748,11 @@ function isSet(value) {
12608
12748
  * A class representing a time as minutes since midnight.
12609
12749
  */
12610
12750
  class Time {
12751
+ /*
12752
+ * Number of minutes since midnight.
12753
+ Note that this value can go beyond 3600 to model services overlapping with the next day.
12754
+ */
12755
+ minutesSinceMidnight;
12611
12756
  /**
12612
12757
  * Gets the infinity time as a Time instance.
12613
12758
  * This represents a time that is conceptually beyond any real possible time.
@@ -12860,7 +13005,53 @@ const toPickupDropOffType = (numericalType) => {
12860
13005
  * A route identifies all trips of a given service route sharing the same list of stops.
12861
13006
  */
12862
13007
  class Route {
12863
- constructor(stopTimes, pickUpDropOffTypes, stops, serviceRouteId) {
13008
+ id;
13009
+ /**
13010
+ * Arrivals and departures encoded as minutes from midnight.
13011
+ * Format: [arrival1, departure1, arrival2, departure2, etc.]
13012
+ */
13013
+ stopTimes;
13014
+ /**
13015
+ * PickUp and DropOff types represented as a 2-bit encoded Uint8Array.
13016
+ * Values (2 bits each):
13017
+ * 0: REGULAR
13018
+ * 1: NOT_AVAILABLE
13019
+ * 2: MUST_PHONE_AGENCY
13020
+ * 3: MUST_COORDINATE_WITH_DRIVER
13021
+ *
13022
+ * Encoding format: Each byte contains 2 pickup/drop-off pairs (4 bits each)
13023
+ * Bit layout per byte: [pickup_1 (2 bits)][drop_off_1 (2 bits)][pickup_0 (2 bits)][drop_off_0 (2 bits)]
13024
+ * Example: For stops 0 and 1 in a trip, one byte encodes all 4 values
13025
+ */
13026
+ pickUpDropOffTypes;
13027
+ /**
13028
+ * A binary array of stopIds in the route.
13029
+ * [stop1, stop2, stop3,...]
13030
+ */
13031
+ stops;
13032
+ /**
13033
+ * A reverse mapping of each stop with their index in the route:
13034
+ * {
13035
+ * 4: 0,
13036
+ * 5: 1,
13037
+ * ...
13038
+ * }
13039
+ */
13040
+ stopIndices;
13041
+ /**
13042
+ * The identifier of the route as a service shown to users.
13043
+ */
13044
+ serviceRouteId;
13045
+ /**
13046
+ * The total number of stops in the route.
13047
+ */
13048
+ nbStops;
13049
+ /**
13050
+ * The total number of trips in the route.
13051
+ */
13052
+ nbTrips;
13053
+ constructor(id, stopTimes, pickUpDropOffTypes, stops, serviceRouteId) {
13054
+ this.id = id;
12864
13055
  this.stopTimes = stopTimes;
12865
13056
  this.pickUpDropOffTypes = pickUpDropOffTypes;
12866
13057
  this.stops = stops;
@@ -12870,9 +13061,87 @@ class Route {
12870
13061
  this.stopIndices = new Map();
12871
13062
  for (let i = 0; i < stops.length; i++) {
12872
13063
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
12873
- this.stopIndices.set(stops[i], i);
13064
+ const stopId = stops[i];
13065
+ const existingIndices = this.stopIndices.get(stopId);
13066
+ if (existingIndices) {
13067
+ existingIndices.push(i);
13068
+ }
13069
+ else {
13070
+ this.stopIndices.set(stopId, [i]);
13071
+ }
12874
13072
  }
12875
13073
  }
13074
+ /**
13075
+ * Creates a new route from multiple trips with their stops.
13076
+ *
13077
+ * @param params The route parameters including ID, service route ID, and trips.
13078
+ * @returns The new route.
13079
+ */
13080
+ static of(params) {
13081
+ const { id, serviceRouteId, trips } = params;
13082
+ if (trips.length === 0) {
13083
+ throw new Error('At least one trip must be provided');
13084
+ }
13085
+ // All trips must have the same stops in the same order
13086
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13087
+ const firstTrip = trips[0];
13088
+ const stopIds = new Uint32Array(firstTrip.stops.map((stop) => stop.id));
13089
+ const numStops = stopIds.length;
13090
+ // Validate all trips have the same stops
13091
+ for (let tripIndex = 1; tripIndex < trips.length; tripIndex++) {
13092
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13093
+ const trip = trips[tripIndex];
13094
+ if (trip.stops.length !== numStops) {
13095
+ throw new Error(`Trip ${tripIndex} has ${trip.stops.length} stops, expected ${numStops}`);
13096
+ }
13097
+ for (let stopIndex = 0; stopIndex < numStops; stopIndex++) {
13098
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13099
+ if (trip.stops[stopIndex].id !== stopIds[stopIndex]) {
13100
+ throw new Error(`Trip ${tripIndex} has different stop at index ${stopIndex}`);
13101
+ }
13102
+ }
13103
+ }
13104
+ // Create stopTimes array with arrivals and departures for all trips
13105
+ const stopTimes = new Uint16Array(trips.length * numStops * 2);
13106
+ for (let tripIndex = 0; tripIndex < trips.length; tripIndex++) {
13107
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13108
+ const trip = trips[tripIndex];
13109
+ for (let stopIndex = 0; stopIndex < numStops; stopIndex++) {
13110
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13111
+ const stop = trip.stops[stopIndex];
13112
+ const baseIndex = (tripIndex * numStops + stopIndex) * 2;
13113
+ stopTimes[baseIndex] = stop.arrivalTime.toMinutes();
13114
+ stopTimes[baseIndex + 1] = stop.departureTime.toMinutes();
13115
+ }
13116
+ }
13117
+ // Create pickUpDropOffTypes array (2-bit encoded) for all trips
13118
+ const totalStopEntries = trips.length * numStops;
13119
+ const pickUpDropOffTypes = new Uint8Array(Math.ceil(totalStopEntries / 2));
13120
+ for (let tripIndex = 0; tripIndex < trips.length; tripIndex++) {
13121
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13122
+ const trip = trips[tripIndex];
13123
+ for (let stopIndex = 0; stopIndex < numStops; stopIndex++) {
13124
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13125
+ const stop = trip.stops[stopIndex];
13126
+ const globalIndex = tripIndex * numStops + stopIndex;
13127
+ const pickUp = stop.pickUpType ?? REGULAR;
13128
+ const dropOff = stop.dropOffType ?? REGULAR;
13129
+ const byteIndex = Math.floor(globalIndex / 2);
13130
+ const isSecondPair = globalIndex % 2 === 1;
13131
+ if (isSecondPair) {
13132
+ // Second pair: pickup in upper 2 bits, dropOff in bits 4-5
13133
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13134
+ pickUpDropOffTypes[byteIndex] |= (pickUp << 6) | (dropOff << 4);
13135
+ }
13136
+ else {
13137
+ // First pair: pickup in bits 2-3, dropOff in lower 2 bits
13138
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13139
+ pickUpDropOffTypes[byteIndex] |= (pickUp << 2) | dropOff;
13140
+ }
13141
+ }
13142
+ }
13143
+ return new Route(id, stopTimes, pickUpDropOffTypes, stopIds, serviceRouteId);
13144
+ }
12876
13145
  /**
12877
13146
  * Serializes the Route into binary arrays.
12878
13147
  *
@@ -12886,24 +13155,6 @@ class Route {
12886
13155
  serviceRouteId: this.serviceRouteId,
12887
13156
  };
12888
13157
  }
12889
- /**
12890
- * Checks if stop A is before stop B in the route.
12891
- *
12892
- * @param stopA - The StopId of the first stop.
12893
- * @param stopB - The StopId of the second stop.
12894
- * @returns True if stop A is before stop B, false otherwise.
12895
- */
12896
- isBefore(stopA, stopB) {
12897
- const stopAIndex = this.stopIndices.get(stopA);
12898
- if (stopAIndex === undefined) {
12899
- throw new Error(`Stop index ${stopAIndex} not found in route ${this.serviceRouteId}`);
12900
- }
12901
- const stopBIndex = this.stopIndices.get(stopB);
12902
- if (stopBIndex === undefined) {
12903
- throw new Error(`Stop index ${stopBIndex} not found in route ${this.serviceRouteId}`);
12904
- }
12905
- return stopAIndex < stopBIndex;
12906
- }
12907
13158
  /**
12908
13159
  * Retrieves the number of stops in the route.
12909
13160
  *
@@ -12912,6 +13163,14 @@ class Route {
12912
13163
  getNbStops() {
12913
13164
  return this.nbStops;
12914
13165
  }
13166
+ /**
13167
+ * Retrieves the number of trips in the route.
13168
+ *
13169
+ * @returns The total number of trips in the route.
13170
+ */
13171
+ getNbTrips() {
13172
+ return this.nbTrips;
13173
+ }
12915
13174
  /**
12916
13175
  * Finds the ServiceRouteId of the route. It corresponds the identifier
12917
13176
  * of the service shown to the end user as a route.
@@ -12924,47 +13183,47 @@ class Route {
12924
13183
  /**
12925
13184
  * Retrieves the arrival time at a specific stop for a given trip.
12926
13185
  *
12927
- * @param stopId - The identifier of the stop.
13186
+ * @param stopIndex - The index of the stop in the route.
12928
13187
  * @param tripIndex - The index of the trip.
12929
13188
  * @returns The arrival time at the specified stop and trip as a Time object.
12930
13189
  */
12931
- arrivalAt(stopId, tripIndex) {
12932
- const arrivalIndex = (tripIndex * this.stops.length + this.stopIndex(stopId)) * 2;
13190
+ arrivalAt(stopIndex, tripIndex) {
13191
+ const arrivalIndex = (tripIndex * this.stops.length + stopIndex) * 2;
12933
13192
  const arrival = this.stopTimes[arrivalIndex];
12934
13193
  if (arrival === undefined) {
12935
- throw new Error(`Arrival time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
13194
+ throw new Error(`Arrival time not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
12936
13195
  }
12937
13196
  return Time.fromMinutes(arrival);
12938
13197
  }
12939
13198
  /**
12940
13199
  * Retrieves the departure time at a specific stop for a given trip.
12941
13200
  *
12942
- * @param stopId - The identifier of the stop.
13201
+ * @param stopIndex - The index of the stop in the route.
12943
13202
  * @param tripIndex - The index of the trip.
12944
13203
  * @returns The departure time at the specified stop and trip as a Time object.
12945
13204
  */
12946
- departureFrom(stopId, tripIndex) {
12947
- const departureIndex = (tripIndex * this.stops.length + this.stopIndex(stopId)) * 2 + 1;
13205
+ departureFrom(stopIndex, tripIndex) {
13206
+ const departureIndex = (tripIndex * this.stops.length + stopIndex) * 2 + 1;
12948
13207
  const departure = this.stopTimes[departureIndex];
12949
13208
  if (departure === undefined) {
12950
- throw new Error(`Departure time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
13209
+ throw new Error(`Departure time not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
12951
13210
  }
12952
13211
  return Time.fromMinutes(departure);
12953
13212
  }
12954
13213
  /**
12955
13214
  * Retrieves the pick-up type for a specific stop and trip.
12956
13215
  *
12957
- * @param stopId - The identifier of the stop.
13216
+ * @param stopIndex - The index of the stop in the route.
12958
13217
  * @param tripIndex - The index of the trip.
12959
13218
  * @returns The pick-up type at the specified stop and trip.
12960
13219
  */
12961
- pickUpTypeFrom(stopId, tripIndex) {
12962
- const globalIndex = tripIndex * this.stops.length + this.stopIndex(stopId);
13220
+ pickUpTypeFrom(stopIndex, tripIndex) {
13221
+ const globalIndex = tripIndex * this.stops.length + stopIndex;
12963
13222
  const byteIndex = Math.floor(globalIndex / 2);
12964
13223
  const isSecondPair = globalIndex % 2 === 1;
12965
13224
  const byte = this.pickUpDropOffTypes[byteIndex];
12966
13225
  if (byte === undefined) {
12967
- throw new Error(`Pick up type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
13226
+ throw new Error(`Pick up type not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
12968
13227
  }
12969
13228
  const pickUpValue = isSecondPair
12970
13229
  ? (byte >> 6) & 0x03 // Upper 2 bits for second pair
@@ -12974,17 +13233,17 @@ class Route {
12974
13233
  /**
12975
13234
  * Retrieves the drop-off type for a specific stop and trip.
12976
13235
  *
12977
- * @param stopId - The identifier of the stop.
13236
+ * @param stopIndex - The index of the stop in the route.
12978
13237
  * @param tripIndex - The index of the trip.
12979
13238
  * @returns The drop-off type at the specified stop and trip.
12980
13239
  */
12981
- dropOffTypeAt(stopId, tripIndex) {
12982
- const globalIndex = tripIndex * this.stops.length + this.stopIndex(stopId);
13240
+ dropOffTypeAt(stopIndex, tripIndex) {
13241
+ const globalIndex = tripIndex * this.stops.length + stopIndex;
12983
13242
  const byteIndex = Math.floor(globalIndex / 2);
12984
13243
  const isSecondPair = globalIndex % 2 === 1;
12985
13244
  const byte = this.pickUpDropOffTypes[byteIndex];
12986
13245
  if (byte === undefined) {
12987
- throw new Error(`Drop off type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
13246
+ throw new Error(`Drop off type not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
12988
13247
  }
12989
13248
  const dropOffValue = isSecondPair
12990
13249
  ? (byte >> 4) & 0x03 // Bits 4-5 for second pair
@@ -12996,14 +13255,14 @@ class Route {
12996
13255
  * optionally constrained by a latest trip index and a time before which the trip
12997
13256
  * should not depart.
12998
13257
  * *
12999
- * @param stopId - The StopId of the stop where the trip should be found.
13258
+ * @param stopIndex - The route index of the stop where the trip should be found.
13000
13259
  * @param [after=Time.origin()] - The earliest time after which the trip should depart.
13001
13260
  * If not provided, searches all available trips.
13002
13261
  * @param [beforeTrip] - (Optional) The index of the trip before which the search should be constrained.
13003
13262
  * If not provided, searches all available trips.
13004
13263
  * @returns The index of the earliest trip meeting the criteria, or undefined if no such trip is found.
13005
13264
  */
13006
- findEarliestTrip(stopId, after = Time.origin(), beforeTrip) {
13265
+ findEarliestTrip(stopIndex, after = Time.origin(), beforeTrip) {
13007
13266
  if (this.nbTrips <= 0)
13008
13267
  return undefined;
13009
13268
  let hi = this.nbTrips - 1;
@@ -13015,7 +13274,7 @@ class Route {
13015
13274
  let lb = -1;
13016
13275
  while (lo <= hi) {
13017
13276
  const mid = (lo + hi) >>> 1;
13018
- const depMid = this.departureFrom(stopId, mid);
13277
+ const depMid = this.departureFrom(stopIndex, mid);
13019
13278
  if (depMid.isBefore(after)) {
13020
13279
  lo = mid + 1;
13021
13280
  }
@@ -13026,8 +13285,8 @@ class Route {
13026
13285
  }
13027
13286
  if (lb === -1)
13028
13287
  return undefined;
13029
- for (let t = lb; t < (beforeTrip !== null && beforeTrip !== void 0 ? beforeTrip : this.nbTrips); t++) {
13030
- const pickup = this.pickUpTypeFrom(stopId, t);
13288
+ for (let t = lb; t < (beforeTrip ?? this.nbTrips); t++) {
13289
+ const pickup = this.pickUpTypeFrom(stopIndex, t);
13031
13290
  if (pickup !== 'NOT_AVAILABLE') {
13032
13291
  return t;
13033
13292
  }
@@ -13035,19 +13294,76 @@ class Route {
13035
13294
  return undefined;
13036
13295
  }
13037
13296
  /**
13038
- * Retrieves the index of a stop within the route.
13297
+ * Retrieves the indices of a stop within the route.
13039
13298
  * @param stopId The StopId of the stop to locate in the route.
13040
- * @returns The index of the stop in the route.
13299
+ * @returns An array of indices where the stop appears in the route, or an empty array if the stop is not found.
13041
13300
  */
13042
- stopIndex(stopId) {
13301
+ stopRouteIndices(stopId) {
13043
13302
  const stopIndex = this.stopIndices.get(stopId);
13044
13303
  if (stopIndex === undefined) {
13045
- throw new Error(`Stop index for ${stopId} not found in route ${this.serviceRouteId}`);
13304
+ return [];
13046
13305
  }
13047
13306
  return stopIndex;
13048
13307
  }
13308
+ /**
13309
+ * Retrieves the id of a stop at a given index in a route.
13310
+ * @param stopRouteIndex The route index of the stop.
13311
+ * @returns The id of the stop at the given index in the route.
13312
+ */
13313
+ stopId(stopRouteIndex) {
13314
+ const stopId = this.stops[stopRouteIndex];
13315
+ if (stopId === undefined) {
13316
+ throw new Error(`StopId for stop at index ${stopRouteIndex} not found in route ${this.serviceRouteId}`);
13317
+ }
13318
+ return stopId;
13319
+ }
13049
13320
  }
13050
13321
 
13322
+ // Each value uses 20 bits, allowing values from 0 to 1,048,575 (2^20 - 1)
13323
+ const VALUE_MASK = (1n << 20n) - 1n; // 0xFFFFF
13324
+ const MAX_VALUE = 1_048_575; // 2^20 - 1
13325
+ // Bit positions for each value in the 60-bit bigint
13326
+ const TRIP_INDEX_SHIFT = 0n;
13327
+ const ROUTE_ID_SHIFT = 20n;
13328
+ const STOP_INDEX_SHIFT = 40n;
13329
+ /**
13330
+ * Validates that a value fits within 20 bits (0 to 1,048,575)
13331
+ * @param value - The value to validate
13332
+ * @param name - The name of the value for error reporting
13333
+ * @throws Error if the value is out of range
13334
+ */
13335
+ const validateValue = (value, name) => {
13336
+ if (value < 0 || value > MAX_VALUE) {
13337
+ throw new Error(`${name} must be between 0 and ${MAX_VALUE}, got ${value}`);
13338
+ }
13339
+ };
13340
+ /**
13341
+ * Encodes a stop index, route ID, and trip index into a single trip boarding ID.
13342
+ * @param stopIndex - The index of the stop within the route (0 to 1,048,575)
13343
+ * @param routeId - The route identifier (0 to 1,048,575)
13344
+ * @param tripIndex - The index of the trip within the route (0 to 1,048,575)
13345
+ * @returns The encoded trip ID as a bigint
13346
+ */
13347
+ const encode = (stopIndex, routeId, tripIndex) => {
13348
+ validateValue(stopIndex, 'stopIndex');
13349
+ validateValue(routeId, 'routeId');
13350
+ validateValue(tripIndex, 'tripIndex');
13351
+ return ((BigInt(stopIndex) << STOP_INDEX_SHIFT) |
13352
+ (BigInt(routeId) << ROUTE_ID_SHIFT) |
13353
+ (BigInt(tripIndex) << TRIP_INDEX_SHIFT));
13354
+ };
13355
+ /**
13356
+ * Decodes a trip boarding ID back into its constituent stop index, route ID, and trip index.
13357
+ * @param tripBoardingId - The encoded trip ID
13358
+ * @returns A tuple containing [stopIndex, routeId, tripIndex]
13359
+ */
13360
+ const decode = (tripBoardingId) => {
13361
+ const stopIndex = Number((tripBoardingId >> STOP_INDEX_SHIFT) & VALUE_MASK);
13362
+ const routeId = Number((tripBoardingId >> ROUTE_ID_SHIFT) & VALUE_MASK);
13363
+ const tripIndex = Number((tripBoardingId >> TRIP_INDEX_SHIFT) & VALUE_MASK);
13364
+ return [stopIndex, routeId, tripIndex];
13365
+ };
13366
+
13051
13367
  const isLittleEndian = (() => {
13052
13368
  const buffer = new ArrayBuffer(4);
13053
13369
  const view = new DataView(buffer);
@@ -13116,9 +13432,15 @@ const bytesToUint16Array = (bytes) => {
13116
13432
  const serializeStopsAdjacency = (stopsAdjacency) => {
13117
13433
  return stopsAdjacency.map((value) => {
13118
13434
  return {
13119
- transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: serializeTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
13120
- minTransferTime: transfer.minTransferTime.toSeconds(),
13121
- })))),
13435
+ transfers: value.transfers
13436
+ ? value.transfers.map((transfer) => ({
13437
+ destination: transfer.destination,
13438
+ type: serializeTransferType(transfer.type),
13439
+ ...(transfer.minTransferTime !== undefined && {
13440
+ minTransferTime: transfer.minTransferTime.toSeconds(),
13441
+ }),
13442
+ }))
13443
+ : [],
13122
13444
  routes: value.routes,
13123
13445
  };
13124
13446
  });
@@ -13154,15 +13476,22 @@ const deserializeStopsAdjacency = (protoStopsAdjacency) => {
13154
13476
  for (let j = 0; j < value.transfers.length; j++) {
13155
13477
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13156
13478
  const transfer = value.transfers[j];
13157
- const newTransfer = Object.assign({ destination: transfer.destination, type: parseTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
13158
- minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
13159
- }));
13479
+ const newTransfer = {
13480
+ destination: transfer.destination,
13481
+ type: parseTransferType(transfer.type),
13482
+ ...(transfer.minTransferTime !== undefined && {
13483
+ minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
13484
+ }),
13485
+ };
13160
13486
  transfers.push(newTransfer);
13161
13487
  }
13162
- result.push({
13163
- transfers: transfers,
13488
+ const stopAdjacency = {
13164
13489
  routes: value.routes,
13165
- });
13490
+ };
13491
+ if (transfers.length > 0) {
13492
+ stopAdjacency.transfers = transfers;
13493
+ }
13494
+ result.push(stopAdjacency);
13166
13495
  }
13167
13496
  return result;
13168
13497
  };
@@ -13172,7 +13501,7 @@ const deserializeRoutesAdjacency = (protoRoutesAdjacency) => {
13172
13501
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13173
13502
  const value = protoRoutesAdjacency[i];
13174
13503
  const stops = bytesToUint32Array(value.stops);
13175
- routesAdjacency.push(new Route(bytesToUint16Array(value.stopTimes), value.pickUpDropOffTypes, stops, value.serviceRouteId));
13504
+ routesAdjacency.push(new Route(i, bytesToUint16Array(value.stopTimes), value.pickUpDropOffTypes, stops, value.serviceRouteId));
13176
13505
  }
13177
13506
  return routesAdjacency;
13178
13507
  };
@@ -13266,6 +13595,38 @@ const serializeRouteType = (type) => {
13266
13595
  return RouteType.MONORAIL;
13267
13596
  }
13268
13597
  };
13598
+ const serializeTripContinuations = (tripContinuations) => {
13599
+ const result = [];
13600
+ for (const [tripBoardingId, boardings] of tripContinuations.entries()) {
13601
+ const [originStopIndex, originRouteId, originTripIndex] = decode(tripBoardingId);
13602
+ result.push({
13603
+ originStopIndex,
13604
+ originRouteId,
13605
+ originTripIndex,
13606
+ continuations: boardings.map((tripBoarding) => ({
13607
+ hopOnStopIndex: tripBoarding.hopOnStopIndex,
13608
+ routeId: tripBoarding.routeId,
13609
+ tripIndex: tripBoarding.tripIndex,
13610
+ })),
13611
+ });
13612
+ }
13613
+ return result;
13614
+ };
13615
+ const deserializeTripContinuations = (protoTripContinuations) => {
13616
+ const result = new Map();
13617
+ for (let i = 0; i < protoTripContinuations.length; i++) {
13618
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13619
+ const entry = protoTripContinuations[i];
13620
+ const tripBoardingId = encode(entry.originStopIndex, entry.originRouteId, entry.originTripIndex);
13621
+ const tripBoardings = entry.continuations.map((protoTripBoarding) => ({
13622
+ hopOnStopIndex: protoTripBoarding.hopOnStopIndex,
13623
+ routeId: protoTripBoarding.routeId,
13624
+ tripIndex: protoTripBoarding.tripIndex,
13625
+ }));
13626
+ result.set(tripBoardingId, tripBoardings);
13627
+ }
13628
+ return result;
13629
+ };
13269
13630
 
13270
13631
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
13271
13632
  const ALL_TRANSPORT_MODES = new Set([
@@ -13280,20 +13641,27 @@ const ALL_TRANSPORT_MODES = new Set([
13280
13641
  'TROLLEYBUS',
13281
13642
  'MONORAIL',
13282
13643
  ]);
13283
- const CURRENT_VERSION = '0.0.7';
13644
+ const EMPTY_TRIP_CONTINUATIONS = [];
13645
+ const CURRENT_VERSION = '0.0.9';
13284
13646
  /**
13285
13647
  * The internal transit timetable format.
13286
13648
  */
13287
13649
  class Timetable {
13288
- constructor(stopsAdjacency, routesAdjacency, routes) {
13650
+ stopsAdjacency;
13651
+ routesAdjacency;
13652
+ serviceRoutes;
13653
+ tripContinuations;
13654
+ activeStops;
13655
+ constructor(stopsAdjacency, routesAdjacency, routes, tripContinuations) {
13289
13656
  this.stopsAdjacency = stopsAdjacency;
13290
13657
  this.routesAdjacency = routesAdjacency;
13291
13658
  this.serviceRoutes = routes;
13659
+ this.tripContinuations = tripContinuations;
13292
13660
  this.activeStops = new Set();
13293
13661
  for (let i = 0; i < stopsAdjacency.length; i++) {
13294
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13295
13662
  const stop = stopsAdjacency[i];
13296
- if (stop.routes.length > 0 || stop.transfers.length > 0) {
13663
+ if (stop.routes.length > 0 ||
13664
+ (stop.transfers && stop.transfers.length > 0)) {
13297
13665
  this.activeStops.add(i);
13298
13666
  }
13299
13667
  }
@@ -13309,6 +13677,7 @@ class Timetable {
13309
13677
  stopsAdjacency: serializeStopsAdjacency(this.stopsAdjacency),
13310
13678
  routesAdjacency: serializeRoutesAdjacency(this.routesAdjacency),
13311
13679
  serviceRoutes: serializeServiceRoutesMap(this.serviceRoutes),
13680
+ tripContinuations: serializeTripContinuations(this.tripContinuations || new Map()),
13312
13681
  };
13313
13682
  const writer = new BinaryWriter();
13314
13683
  Timetable$1.encode(protoTimetable, writer);
@@ -13326,7 +13695,7 @@ class Timetable {
13326
13695
  if (protoTimetable.version !== CURRENT_VERSION) {
13327
13696
  throw new Error(`Unsupported timetable version ${protoTimetable.version}`);
13328
13697
  }
13329
- return new Timetable(deserializeStopsAdjacency(protoTimetable.stopsAdjacency), deserializeRoutesAdjacency(protoTimetable.routesAdjacency), deserializeServiceRoutesMap(protoTimetable.serviceRoutes));
13698
+ return new Timetable(deserializeStopsAdjacency(protoTimetable.stopsAdjacency), deserializeRoutesAdjacency(protoTimetable.routesAdjacency), deserializeServiceRoutesMap(protoTimetable.serviceRoutes), deserializeTripContinuations(protoTimetable.tripContinuations));
13330
13699
  }
13331
13700
  /**
13332
13701
  * Checks if the given stop is active on the timetable.
@@ -13356,8 +13725,26 @@ class Timetable {
13356
13725
  * @returns An array of transfer options available at the stop.
13357
13726
  */
13358
13727
  getTransfers(stopId) {
13359
- var _a, _b;
13360
- return (_b = (_a = this.stopsAdjacency[stopId]) === null || _a === void 0 ? void 0 : _a.transfers) !== null && _b !== void 0 ? _b : [];
13728
+ const stopAdjacency = this.stopsAdjacency[stopId];
13729
+ if (!stopAdjacency) {
13730
+ throw new Error(`Stop ID ${stopId} not found`);
13731
+ }
13732
+ return stopAdjacency.transfers || [];
13733
+ }
13734
+ /**
13735
+ * Retrieves all trip continuation options available at the specified stop for a given trip.
13736
+ *
13737
+ * @param stopIndex - The index in the route of the stop to get trip continuations for.
13738
+ * @param routeId - The ID of the route to get continuations for.
13739
+ * @param tripIndex - The index of the trip to get continuations for.
13740
+ * @returns An array of trip continuation options available at the stop for the specified trip.
13741
+ */
13742
+ getContinuousTrips(stopIndex, routeId, tripIndex) {
13743
+ const tripContinuations = this.tripContinuations?.get(encode(stopIndex, routeId, tripIndex));
13744
+ if (!tripContinuations) {
13745
+ return EMPTY_TRIP_CONTINUATIONS;
13746
+ }
13747
+ return tripContinuations;
13361
13748
  }
13362
13749
  /**
13363
13750
  * Retrieves the service route associated with the given route.
@@ -13397,12 +13784,12 @@ class Timetable {
13397
13784
  }
13398
13785
  /**
13399
13786
  * Finds routes that are reachable from a set of stop IDs.
13400
- * Also identifies the first stop available to hop on each route among
13787
+ * Also identifies the first stop index available to hop on each route among
13401
13788
  * the input stops.
13402
13789
  *
13403
13790
  * @param fromStops - The set of stop IDs to find reachable routes from.
13404
13791
  * @param transportModes - The set of transport modes to consider for reachable routes.
13405
- * @returns A map of reachable routes to the first stop available to hop on each route.
13792
+ * @returns A map of reachable routes to the first stop index available to hop on each route.
13406
13793
  */
13407
13794
  findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
13408
13795
  const reachableRoutes = new Map();
@@ -13415,15 +13802,17 @@ class Timetable {
13415
13802
  });
13416
13803
  for (let j = 0; j < validRoutes.length; j++) {
13417
13804
  const route = validRoutes[j];
13418
- const hopOnStop = reachableRoutes.get(route);
13419
- if (hopOnStop) {
13420
- if (route.isBefore(originStop, hopOnStop)) {
13805
+ const originStopIndices = route.stopRouteIndices(originStop);
13806
+ const originStopIndex = originStopIndices[0];
13807
+ const existingHopOnStopIndex = reachableRoutes.get(route);
13808
+ if (existingHopOnStopIndex !== undefined) {
13809
+ if (originStopIndex < existingHopOnStopIndex) {
13421
13810
  // if the current stop is before the existing hop on stop, replace it
13422
- reachableRoutes.set(route, originStop);
13811
+ reachableRoutes.set(route, originStopIndex);
13423
13812
  }
13424
13813
  }
13425
13814
  else {
13426
- reachableRoutes.set(route, originStop);
13815
+ reachableRoutes.set(route, originStopIndex);
13427
13816
  }
13428
13817
  }
13429
13818
  }
@@ -15399,35 +15788,22 @@ const parseCsv = (stream, numericColumns = []) => {
15399
15788
  * @param profile A configuration object defining the specificities of the GTFS feed.
15400
15789
  * @returns A map of all the valid routes.
15401
15790
  */
15402
- const parseRoutes = (routesStream_1, ...args_1) => __awaiter(void 0, [routesStream_1, ...args_1], void 0, function* (routesStream, profile = standardProfile) {
15403
- var _a, e_1, _b, _c;
15791
+ const parseRoutes = async (routesStream, profile = standardProfile) => {
15404
15792
  const routes = new Map();
15405
- try {
15406
- for (var _d = true, _e = __asyncValues(parseCsv(routesStream, ['route_type'])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15407
- _c = _f.value;
15408
- _d = false;
15409
- const rawLine = _c;
15410
- const line = rawLine;
15411
- const routeType = profile.routeTypeParser(line.route_type);
15412
- if (routeType === undefined) {
15413
- log.info(`Unsupported route type ${line.route_type} for route ${line.route_id}.`);
15414
- continue;
15415
- }
15416
- routes.set(line.route_id, {
15417
- name: line.route_short_name,
15418
- type: routeType,
15419
- });
15420
- }
15421
- }
15422
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
15423
- finally {
15424
- try {
15425
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
15793
+ for await (const rawLine of parseCsv(routesStream, ['route_type'])) {
15794
+ const line = rawLine;
15795
+ const routeType = profile.routeTypeParser(line.route_type);
15796
+ if (routeType === undefined) {
15797
+ log.info(`Unsupported route type ${line.route_type} for route ${line.route_id}.`);
15798
+ continue;
15426
15799
  }
15427
- finally { if (e_1) throw e_1.error; }
15800
+ routes.set(line.route_id, {
15801
+ name: line.route_short_name,
15802
+ type: routeType,
15803
+ });
15428
15804
  }
15429
15805
  return routes;
15430
- });
15806
+ };
15431
15807
  /**
15432
15808
  * Creates an array of ServiceRoute objects by combining GTFS route data with service route mappings.
15433
15809
  *
@@ -15490,46 +15866,33 @@ const weekdays = {
15490
15866
  * @param date The active date.
15491
15867
  * @param calendarStream A readable stream for the GTFS calendar.txt file.
15492
15868
  */
15493
- const parseCalendar = (calendarStream, serviceIds, date) => __awaiter(void 0, void 0, void 0, function* () {
15494
- var _a, e_1, _b, _c;
15869
+ const parseCalendar = async (calendarStream, serviceIds, date) => {
15495
15870
  const activeDate = toGtfsDate(date);
15496
15871
  const weekday = date.weekday;
15497
15872
  const weekdayIndex = weekdays[weekday];
15498
- try {
15499
- for (var _d = true, _e = __asyncValues(parseCsv(calendarStream, [
15500
- 'monday',
15501
- 'tuesday',
15502
- 'wednesday',
15503
- 'thursday',
15504
- 'friday',
15505
- 'saturday',
15506
- 'sunday',
15507
- 'start_date',
15508
- 'end_date',
15509
- ])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15510
- _c = _f.value;
15511
- _d = false;
15512
- const rawLine = _c;
15513
- const line = rawLine;
15514
- if (activeDate < line.start_date || activeDate > line.end_date) {
15515
- // If the service is not valid on this date
15516
- continue;
15517
- }
15518
- if (line[weekdayIndex] !== 1) {
15519
- // If the service is not valid on this week day
15520
- continue;
15521
- }
15522
- serviceIds.add(line['service_id']);
15873
+ for await (const rawLine of parseCsv(calendarStream, [
15874
+ 'monday',
15875
+ 'tuesday',
15876
+ 'wednesday',
15877
+ 'thursday',
15878
+ 'friday',
15879
+ 'saturday',
15880
+ 'sunday',
15881
+ 'start_date',
15882
+ 'end_date',
15883
+ ])) {
15884
+ const line = rawLine;
15885
+ if (activeDate < line.start_date || activeDate > line.end_date) {
15886
+ // If the service is not valid on this date
15887
+ continue;
15523
15888
  }
15524
- }
15525
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
15526
- finally {
15527
- try {
15528
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
15889
+ if (line[weekdayIndex] !== 1) {
15890
+ // If the service is not valid on this week day
15891
+ continue;
15529
15892
  }
15530
- finally { if (e_1) throw e_1.error; }
15893
+ serviceIds.add(line['service_id']);
15531
15894
  }
15532
- });
15895
+ };
15533
15896
  /**
15534
15897
  * Parses a gtfs calendar_dates.txt file and finds the service ids valid at a given date.
15535
15898
  *
@@ -15537,39 +15900,24 @@ const parseCalendar = (calendarStream, serviceIds, date) => __awaiter(void 0, vo
15537
15900
  * @param date The active date, in the format "YYYYMMDD".
15538
15901
  * @param calendarDatesStream A readable stream for the GTFS calendar_dates.txt file.
15539
15902
  */
15540
- const parseCalendarDates = (calendarDatesStream, serviceIds, date) => __awaiter(void 0, void 0, void 0, function* () {
15541
- var _a, e_2, _b, _c;
15903
+ const parseCalendarDates = async (calendarDatesStream, serviceIds, date) => {
15542
15904
  const activeDate = toGtfsDate(date);
15543
- try {
15544
- for (var _d = true, _e = __asyncValues(parseCsv(calendarDatesStream, [
15545
- 'date',
15546
- 'exception_type',
15547
- ])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15548
- _c = _f.value;
15549
- _d = false;
15550
- const rawLine = _c;
15551
- const line = rawLine;
15552
- if (line.date !== activeDate) {
15553
- // No rule on the active date
15554
- }
15555
- else if (line.exception_type === 2 && serviceIds.has(line.service_id)) {
15556
- // Service has been removed for the specified date.
15557
- serviceIds.delete(line.service_id);
15558
- }
15559
- else if (line.exception_type === 1) {
15560
- // Service is present on the active date
15561
- serviceIds.add(line.service_id);
15562
- }
15905
+ for await (const rawLine of parseCsv(calendarDatesStream, [
15906
+ 'date',
15907
+ 'exception_type',
15908
+ ])) {
15909
+ const line = rawLine;
15910
+ if (line.date !== activeDate) ;
15911
+ else if (line.exception_type === 2 && serviceIds.has(line.service_id)) {
15912
+ // Service has been removed for the specified date.
15913
+ serviceIds.delete(line.service_id);
15563
15914
  }
15564
- }
15565
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
15566
- finally {
15567
- try {
15568
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
15915
+ else if (line.exception_type === 1) {
15916
+ // Service is present on the active date
15917
+ serviceIds.add(line.service_id);
15569
15918
  }
15570
- finally { if (e_2) throw e_2.error; }
15571
15919
  }
15572
- });
15920
+ };
15573
15921
 
15574
15922
  /**
15575
15923
  * Parses the stops.txt file from a GTFS feed.
@@ -15577,33 +15925,30 @@ const parseCalendarDates = (calendarDatesStream, serviceIds, date) => __awaiter(
15577
15925
  * @param stopsStream The readable stream containing the stops data.
15578
15926
  * @return A mapping of stop IDs to corresponding stop details.
15579
15927
  */
15580
- const parseStops = (stopsStream) => __awaiter(void 0, void 0, void 0, function* () {
15581
- var _a, e_1, _b, _c;
15928
+ const parseStops = async (stopsStream) => {
15582
15929
  const parsedStops = new Map();
15583
15930
  let i = 0;
15584
- try {
15585
- for (var _d = true, _e = __asyncValues(parseCsv(stopsStream, [
15586
- 'stop_lat',
15587
- 'stop_lon',
15588
- 'location_type',
15589
- ])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15590
- _c = _f.value;
15591
- _d = false;
15592
- const rawLine = _c;
15593
- const line = rawLine;
15594
- const stop = Object.assign(Object.assign(Object.assign({ id: i, sourceStopId: line.stop_id, name: line.stop_name, lat: line.stop_lat, lon: line.stop_lon, locationType: line.location_type
15595
- ? parseGtfsLocationType(line.location_type)
15596
- : 'SIMPLE_STOP_OR_PLATFORM' }, (line.platform_code && { platform: line.platform_code })), { children: [] }), (line.parent_station && { parentSourceId: line.parent_station }));
15597
- parsedStops.set(line.stop_id, stop);
15598
- i = i + 1;
15599
- }
15600
- }
15601
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
15602
- finally {
15603
- try {
15604
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
15605
- }
15606
- finally { if (e_1) throw e_1.error; }
15931
+ for await (const rawLine of parseCsv(stopsStream, [
15932
+ 'stop_lat',
15933
+ 'stop_lon',
15934
+ 'location_type',
15935
+ ])) {
15936
+ const line = rawLine;
15937
+ const stop = {
15938
+ id: i,
15939
+ sourceStopId: line.stop_id,
15940
+ name: line.stop_name,
15941
+ lat: line.stop_lat,
15942
+ lon: line.stop_lon,
15943
+ locationType: line.location_type
15944
+ ? parseGtfsLocationType(line.location_type)
15945
+ : 'SIMPLE_STOP_OR_PLATFORM',
15946
+ ...(line.platform_code && { platform: line.platform_code }),
15947
+ children: [],
15948
+ ...(line.parent_station && { parentSourceId: line.parent_station }),
15949
+ };
15950
+ parsedStops.set(line.stop_id, stop);
15951
+ i = i + 1;
15607
15952
  }
15608
15953
  for (const [sourceStopId, stop] of parsedStops) {
15609
15954
  if (stop.parentSourceId) {
@@ -15617,7 +15962,7 @@ const parseStops = (stopsStream) => __awaiter(void 0, void 0, void 0, function*
15617
15962
  }
15618
15963
  }
15619
15964
  return parsedStops;
15620
- });
15965
+ };
15621
15966
  const parseGtfsLocationType = (gtfsLocationType) => {
15622
15967
  switch (gtfsLocationType) {
15623
15968
  case 0:
@@ -15640,58 +15985,158 @@ const parseGtfsLocationType = (gtfsLocationType) => {
15640
15985
  * @param stopsStream The readable stream containing the stops data.
15641
15986
  * @return A mapping of stop IDs to corresponding stop details.
15642
15987
  */
15643
- const parseTransfers = (transfersStream, stopsMap) => __awaiter(void 0, void 0, void 0, function* () {
15644
- var _a, e_1, _b, _c;
15988
+ const parseTransfers = async (transfersStream, stopsMap) => {
15645
15989
  const transfers = new Map();
15646
- try {
15647
- for (var _d = true, _e = __asyncValues(parseCsv(transfersStream, [
15648
- 'transfer_type',
15649
- 'min_transfer_time',
15650
- ])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15651
- _c = _f.value;
15652
- _d = false;
15653
- const rawLine = _c;
15654
- const transferEntry = rawLine;
15655
- if (transferEntry.transfer_type === 3 ||
15656
- transferEntry.transfer_type === 5) {
15657
- continue;
15658
- }
15659
- if (transferEntry.from_trip_id && transferEntry.to_trip_id) {
15660
- console.warn(`Unsupported transfer between trips ${transferEntry.from_trip_id} and ${transferEntry.to_trip_id}.`);
15661
- continue;
15662
- }
15663
- if (transferEntry.from_route_id && transferEntry.to_route_id) {
15664
- console.warn(`Unsupported transfer between routes ${transferEntry.from_route_id} and ${transferEntry.to_route_id}.`);
15665
- continue;
15666
- }
15667
- if (!transferEntry.from_stop_id || !transferEntry.to_stop_id) {
15668
- console.warn(`Missing transfer origin or destination stop.`);
15990
+ const tripContinuations = [];
15991
+ for await (const rawLine of parseCsv(transfersStream, [
15992
+ 'transfer_type',
15993
+ 'min_transfer_time',
15994
+ ])) {
15995
+ const transferEntry = rawLine;
15996
+ if (transferEntry.transfer_type === 3 ||
15997
+ transferEntry.transfer_type === 5) {
15998
+ continue;
15999
+ }
16000
+ if (!transferEntry.from_stop_id || !transferEntry.to_stop_id) {
16001
+ console.warn(`Missing transfer origin or destination stop.`);
16002
+ continue;
16003
+ }
16004
+ const fromStop = stopsMap.get(transferEntry.from_stop_id);
16005
+ const toStop = stopsMap.get(transferEntry.to_stop_id);
16006
+ if (!fromStop || !toStop) {
16007
+ console.warn(`Transfer references non-existent stop(s): from_stop_id=${transferEntry.from_stop_id}, to_stop_id=${transferEntry.to_stop_id}`);
16008
+ continue;
16009
+ }
16010
+ if (transferEntry.transfer_type === 4) {
16011
+ if (transferEntry.from_trip_id === undefined ||
16012
+ transferEntry.from_trip_id === '' ||
16013
+ transferEntry.to_trip_id === undefined ||
16014
+ transferEntry.to_trip_id === '') {
16015
+ console.warn(`Unsupported in-seat transfer, missing from_trip_id and/or to_trip_id.`);
15669
16016
  continue;
15670
16017
  }
15671
- if (transferEntry.transfer_type === 2 && !transferEntry.min_transfer_time) {
15672
- console.info(`Missing minimum transfer time between ${transferEntry.from_stop_id} and ${transferEntry.to_stop_id}.`);
15673
- }
15674
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15675
- const fromStop = stopsMap.get(transferEntry.from_stop_id);
15676
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15677
- const toStop = stopsMap.get(transferEntry.to_stop_id);
15678
- const transfer = Object.assign({ destination: toStop.id, type: parseGtfsTransferType(transferEntry.transfer_type) }, (transferEntry.min_transfer_time && {
16018
+ const tripBoardingEntry = {
16019
+ fromStop: fromStop.id,
16020
+ fromTrip: transferEntry.from_trip_id,
16021
+ toStop: toStop.id,
16022
+ toTrip: transferEntry.to_trip_id,
16023
+ };
16024
+ tripContinuations.push(tripBoardingEntry);
16025
+ continue;
16026
+ }
16027
+ if (transferEntry.from_trip_id && transferEntry.to_trip_id) {
16028
+ console.warn(`Unsupported transfer of type ${transferEntry.transfer_type} between trips ${transferEntry.from_trip_id} and ${transferEntry.to_trip_id}.`);
16029
+ continue;
16030
+ }
16031
+ if (transferEntry.from_route_id && transferEntry.to_route_id) {
16032
+ console.warn(`Unsupported transfer of type ${transferEntry.transfer_type} between routes ${transferEntry.from_route_id} and ${transferEntry.to_route_id}.`);
16033
+ continue;
16034
+ }
16035
+ if (transferEntry.transfer_type === 2 &&
16036
+ transferEntry.min_transfer_time === undefined) {
16037
+ console.info(`Missing minimum transfer time between ${transferEntry.from_stop_id} and ${transferEntry.to_stop_id}.`);
16038
+ }
16039
+ const transfer = {
16040
+ destination: toStop.id,
16041
+ type: parseGtfsTransferType(transferEntry.transfer_type),
16042
+ ...(transferEntry.min_transfer_time !== undefined && {
15679
16043
  minTransferTime: Duration.fromSeconds(transferEntry.min_transfer_time),
15680
- }));
15681
- const fromStopTransfers = transfers.get(fromStop.id) || [];
15682
- fromStopTransfers.push(transfer);
15683
- transfers.set(fromStop.id, fromStopTransfers);
16044
+ }),
16045
+ };
16046
+ const fromStopTransfers = transfers.get(fromStop.id) || [];
16047
+ fromStopTransfers.push(transfer);
16048
+ transfers.set(fromStop.id, fromStopTransfers);
16049
+ }
16050
+ return {
16051
+ transfers,
16052
+ tripContinuations,
16053
+ };
16054
+ };
16055
+ /**
16056
+ * Disambiguates stops involved in a transfer.
16057
+ *
16058
+ * The GTFS specification only refers to a stopId in the trip-to-trip transfers and not the
16059
+ * specific stop index in the route. For routes that have multiple stops with the same stopId,
16060
+ * we need to determine which are the from / to stop indices in respective routes.
16061
+ * We do so by picking the stop indices leading to the most coherent transfer.
16062
+ * (we pick the closest from stop index happening after the to stop index).
16063
+ */
16064
+ const disambiguateTransferStopsIndices = (fromStop, fromRoute, fromTripIndex, toStop, toRoute, toTripIndex) => {
16065
+ const fromStopIndices = fromRoute.stopRouteIndices(fromStop);
16066
+ const toStopIndices = toRoute.stopRouteIndices(toStop);
16067
+ let bestFromStopIndex;
16068
+ let bestToStopIndex;
16069
+ let bestTimeDifference = Infinity;
16070
+ for (const originStopIndex of fromStopIndices) {
16071
+ const fromArrivalTime = fromRoute.arrivalAt(originStopIndex, fromTripIndex);
16072
+ for (const toStopIndex of toStopIndices) {
16073
+ const toDepartureTime = toRoute.departureFrom(toStopIndex, toTripIndex);
16074
+ if (toDepartureTime.isAfter(fromArrivalTime)) {
16075
+ const timeDifference = toDepartureTime.toMinutes() - fromArrivalTime.toMinutes();
16076
+ if (timeDifference < bestTimeDifference) {
16077
+ bestTimeDifference = timeDifference;
16078
+ bestFromStopIndex = originStopIndex;
16079
+ bestToStopIndex = toStopIndex;
16080
+ }
16081
+ }
15684
16082
  }
15685
16083
  }
15686
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
15687
- finally {
15688
- try {
15689
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
16084
+ if (bestFromStopIndex !== undefined && bestToStopIndex !== undefined) {
16085
+ return {
16086
+ fromStopIndex: bestFromStopIndex,
16087
+ toStopIndex: bestToStopIndex,
16088
+ };
16089
+ }
16090
+ return undefined;
16091
+ };
16092
+ /**
16093
+ * Builds trip continuations map from GTFS trip continuation data.
16094
+ *
16095
+ * This function processes GTFS in-seat transfer data and creates a mapping
16096
+ * from trip boarding IDs to continuation boarding information. It disambiguates
16097
+ * stop indices when routes have multiple stops with the same ID by finding
16098
+ * the most coherent transfer timing.
16099
+ *
16100
+ * @param tripsMapping Mapping from GTFS trip IDs to internal trip representations
16101
+ * @param tripContinuations Array of GTFS trip continuation data from transfers.txt
16102
+ * @param timetable The timetable containing route and timing information
16103
+ * @param activeStopIds Set of stop IDs that are active/enabled in the system
16104
+ * @returns A map from trip boarding IDs to arrays of continuation boarding options
16105
+ */
16106
+ const buildTripContinuations = (tripsMapping, tripContinuations, timetable, activeStopIds) => {
16107
+ const continuations = new Map();
16108
+ for (const gtfsContinuation of tripContinuations) {
16109
+ if (!activeStopIds.has(gtfsContinuation.fromStop) ||
16110
+ !activeStopIds.has(gtfsContinuation.toStop)) {
16111
+ continue;
15690
16112
  }
15691
- finally { if (e_1) throw e_1.error; }
16113
+ const fromTripMapping = tripsMapping.get(gtfsContinuation.fromTrip);
16114
+ const toTripMapping = tripsMapping.get(gtfsContinuation.toTrip);
16115
+ if (!fromTripMapping || !toTripMapping) {
16116
+ continue;
16117
+ }
16118
+ const fromRoute = timetable.getRoute(fromTripMapping.routeId);
16119
+ const toRoute = timetable.getRoute(toTripMapping.routeId);
16120
+ if (!fromRoute || !toRoute) {
16121
+ continue;
16122
+ }
16123
+ const bestStopIndices = disambiguateTransferStopsIndices(gtfsContinuation.fromStop, fromRoute, fromTripMapping.tripRouteIndex, gtfsContinuation.toStop, toRoute, toTripMapping.tripRouteIndex);
16124
+ if (!bestStopIndices) {
16125
+ // No valid continuation found
16126
+ continue;
16127
+ }
16128
+ const tripBoardingId = encode(bestStopIndices.fromStopIndex, fromTripMapping.routeId, fromTripMapping.tripRouteIndex);
16129
+ const continuationBoarding = {
16130
+ hopOnStopIndex: bestStopIndices.toStopIndex,
16131
+ routeId: toTripMapping.routeId,
16132
+ tripIndex: toTripMapping.tripRouteIndex,
16133
+ };
16134
+ const existingContinuations = continuations.get(tripBoardingId) || [];
16135
+ existingContinuations.push(continuationBoarding);
16136
+ continuations.set(tripBoardingId, existingContinuations);
15692
16137
  }
15693
- return transfers;
15694
- });
16138
+ return continuations;
16139
+ };
15695
16140
  const parseGtfsTransferType = (gtfsTransferType) => {
15696
16141
  switch (gtfsTransferType) {
15697
16142
  case 0:
@@ -15753,11 +16198,13 @@ const finalizeRouteFromBuilder = (builder) => {
15753
16198
  const stopTimesArray = new Uint16Array(stopsCount * tripsCount * 2);
15754
16199
  const allPickUpTypes = [];
15755
16200
  const allDropOffTypes = [];
16201
+ const gtfsTripIds = [];
15756
16202
  for (let tripIndex = 0; tripIndex < tripsCount; tripIndex++) {
15757
16203
  const trip = builder.trips[tripIndex];
15758
16204
  if (!trip) {
15759
16205
  throw new Error(`Missing trip data at index ${tripIndex}`);
15760
16206
  }
16207
+ gtfsTripIds.push(trip.gtfsTripId);
15761
16208
  const baseIndex = tripIndex * stopsCount * 2;
15762
16209
  for (let stopIndex = 0; stopIndex < stopsCount; stopIndex++) {
15763
16210
  const timeIndex = baseIndex + stopIndex * 2;
@@ -15779,12 +16226,15 @@ const finalizeRouteFromBuilder = (builder) => {
15779
16226
  }
15780
16227
  // Use 2-bit encoding for pickup/drop-off types
15781
16228
  const pickUpDropOffTypesArray = encodePickUpDropOffTypes(allPickUpTypes, allDropOffTypes);
15782
- return {
15783
- serviceRouteId: builder.serviceRouteId,
15784
- stops: stopsArray,
15785
- stopTimes: stopTimesArray,
15786
- pickUpDropOffTypes: pickUpDropOffTypesArray,
15787
- };
16229
+ return [
16230
+ {
16231
+ serviceRouteId: builder.serviceRouteId,
16232
+ stops: stopsArray,
16233
+ stopTimes: stopTimesArray,
16234
+ pickUpDropOffTypes: pickUpDropOffTypesArray,
16235
+ },
16236
+ gtfsTripIds,
16237
+ ];
15788
16238
  };
15789
16239
  /**
15790
16240
  * Parses the trips.txt file from a GTFS feed
@@ -15794,40 +16244,28 @@ const finalizeRouteFromBuilder = (builder) => {
15794
16244
  * @param serviceRoutes A mapping of route IDs to route details.
15795
16245
  * @returns A mapping of trip IDs to corresponding route IDs.
15796
16246
  */
15797
- const parseTrips = (tripsStream, serviceIds, validGtfsRoutes) => __awaiter(void 0, void 0, void 0, function* () {
15798
- var _a, e_1, _b, _c;
16247
+ const parseTrips = async (tripsStream, serviceIds, validGtfsRoutes) => {
15799
16248
  const trips = new Map();
15800
- try {
15801
- for (var _d = true, _e = __asyncValues(parseCsv(tripsStream, ['stop_sequence'])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15802
- _c = _f.value;
15803
- _d = false;
15804
- const rawLine = _c;
15805
- const line = rawLine;
15806
- if (!serviceIds.has(line.service_id)) {
15807
- // The trip doesn't correspond to an active service
15808
- continue;
15809
- }
15810
- if (!validGtfsRoutes.has(line.route_id)) {
15811
- // The trip doesn't correspond to a supported route
15812
- continue;
15813
- }
15814
- trips.set(line.trip_id, line.route_id);
16249
+ for await (const rawLine of parseCsv(tripsStream, ['stop_sequence'])) {
16250
+ const line = rawLine;
16251
+ if (!serviceIds.has(line.service_id)) {
16252
+ // The trip doesn't correspond to an active service
16253
+ continue;
15815
16254
  }
15816
- }
15817
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
15818
- finally {
15819
- try {
15820
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
16255
+ if (!validGtfsRoutes.has(line.route_id)) {
16256
+ // The trip doesn't correspond to a supported route
16257
+ continue;
15821
16258
  }
15822
- finally { if (e_1) throw e_1.error; }
16259
+ trips.set(line.trip_id, line.route_id);
15823
16260
  }
15824
16261
  return trips;
15825
- });
16262
+ };
15826
16263
  const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbStops, activeStops) => {
15827
- // TODO somehow works when it's a map
15828
16264
  const stopsAdjacency = new Array(nbStops);
15829
16265
  for (let i = 0; i < nbStops; i++) {
15830
- stopsAdjacency[i] = { routes: [], transfers: [] };
16266
+ stopsAdjacency[i] = {
16267
+ routes: [],
16268
+ };
15831
16269
  }
15832
16270
  for (let index = 0; index < routes.length; index++) {
15833
16271
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -15852,7 +16290,11 @@ const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbSto
15852
16290
  const transfer = transfers[i];
15853
16291
  if (activeStops.has(stop) || activeStops.has(transfer.destination)) {
15854
16292
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15855
- stopsAdjacency[stop].transfers.push(transfer);
16293
+ const stopAdj = stopsAdjacency[stop];
16294
+ if (!stopAdj.transfers) {
16295
+ stopAdj.transfers = [];
16296
+ }
16297
+ stopAdj.transfers.push(transfer);
15856
16298
  activeStops.add(transfer.destination);
15857
16299
  activeStops.add(stop);
15858
16300
  }
@@ -15869,9 +16311,7 @@ const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbSto
15869
16311
  * @param activeStopIds A set of valid stop IDs.
15870
16312
  * @returns A mapping of route IDs to route details. The routes returned correspond to the set of trips from GTFS that share the same stop list.
15871
16313
  */
15872
- const parseStopTimes = (stopTimesStream, stopsMap, activeTripIds, activeStopIds) => __awaiter(void 0, void 0, void 0, function* () {
15873
- var _a, e_2, _b, _c;
15874
- var _d, _e;
16314
+ const parseStopTimes = async (stopTimesStream, stopsMap, activeTripIds, activeStopIds) => {
15875
16315
  /**
15876
16316
  * Adds a trip to the appropriate route builder
15877
16317
  */
@@ -15916,6 +16356,7 @@ const parseStopTimes = (stopTimesStream, stopsMap, activeTripIds, activeStopIds)
15916
16356
  }
15917
16357
  routeBuilder.trips.push({
15918
16358
  firstDeparture,
16359
+ gtfsTripId: currentTripId,
15919
16360
  arrivalTimes: arrivalTimes,
15920
16361
  departureTimes: departureTimes,
15921
16362
  pickUpTypes: pickUpTypes,
@@ -15938,63 +16379,62 @@ const parseStopTimes = (stopTimesStream, stopsMap, activeTripIds, activeStopIds)
15938
16379
  let pickUpTypes = [];
15939
16380
  let dropOffTypes = [];
15940
16381
  let currentTripId = undefined;
15941
- try {
15942
- for (var _f = true, _g = __asyncValues(parseCsv(stopTimesStream, ['stop_sequence'])), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
15943
- _c = _h.value;
15944
- _f = false;
15945
- const rawLine = _c;
15946
- const line = rawLine;
15947
- if (line.trip_id === currentTripId && line.stop_sequence <= previousSeq) {
15948
- console.warn(`Stop sequences not increasing for trip ${line.trip_id}: ${line.stop_sequence} > ${previousSeq}.`);
15949
- continue;
15950
- }
15951
- if (!line.arrival_time && !line.departure_time) {
15952
- console.warn(`Missing arrival or departure time for ${line.trip_id} at stop ${line.stop_id}.`);
15953
- continue;
15954
- }
15955
- if (line.pickup_type === '1' && line.drop_off_type === '1') {
15956
- continue;
15957
- }
15958
- if (currentTripId && line.trip_id !== currentTripId && stops.length > 0) {
15959
- addTrip(currentTripId);
15960
- }
15961
- currentTripId = line.trip_id;
15962
- const stopData = stopsMap.get(line.stop_id);
15963
- if (!stopData) {
15964
- console.warn(`Unknown stop ID: ${line.stop_id}`);
15965
- continue;
15966
- }
15967
- stops.push(stopData.id);
15968
- const departure = (_d = line.departure_time) !== null && _d !== void 0 ? _d : line.arrival_time;
15969
- const arrival = (_e = line.arrival_time) !== null && _e !== void 0 ? _e : line.departure_time;
15970
- if (!arrival || !departure) {
15971
- console.warn(`Missing time data for ${line.trip_id} at stop ${line.stop_id}`);
15972
- continue;
15973
- }
15974
- arrivalTimes.push(toTime(arrival).toMinutes());
15975
- departureTimes.push(toTime(departure).toMinutes());
15976
- pickUpTypes.push(parsePickupDropOffType(line.pickup_type));
15977
- dropOffTypes.push(parsePickupDropOffType(line.drop_off_type));
15978
- previousSeq = line.stop_sequence;
16382
+ for await (const rawLine of parseCsv(stopTimesStream, ['stop_sequence'])) {
16383
+ const line = rawLine;
16384
+ if (line.trip_id === currentTripId && line.stop_sequence <= previousSeq) {
16385
+ console.warn(`Stop sequences not increasing for trip ${line.trip_id}: ${line.stop_sequence} > ${previousSeq}.`);
16386
+ continue;
15979
16387
  }
15980
- }
15981
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
15982
- finally {
15983
- try {
15984
- if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
16388
+ if (!line.arrival_time && !line.departure_time) {
16389
+ console.warn(`Missing arrival or departure time for ${line.trip_id} at stop ${line.stop_id}.`);
16390
+ continue;
16391
+ }
16392
+ if (line.pickup_type === '1' && line.drop_off_type === '1') {
16393
+ // Warning: could potentially lead to issues if there is an in-seat transfer
16394
+ // at this stop - it can be not boardable nor alightable but still useful for an in-seat transfer.
16395
+ // This doesn't seem to happen in practice for now so keeping this condition to save memory.
16396
+ continue;
16397
+ }
16398
+ if (currentTripId && line.trip_id !== currentTripId && stops.length > 0) {
16399
+ addTrip(currentTripId);
16400
+ }
16401
+ currentTripId = line.trip_id;
16402
+ const stopData = stopsMap.get(line.stop_id);
16403
+ if (!stopData) {
16404
+ console.warn(`Unknown stop ID: ${line.stop_id}`);
16405
+ continue;
16406
+ }
16407
+ stops.push(stopData.id);
16408
+ const departure = line.departure_time ?? line.arrival_time;
16409
+ const arrival = line.arrival_time ?? line.departure_time;
16410
+ if (!arrival || !departure) {
16411
+ console.warn(`Missing time data for ${line.trip_id} at stop ${line.stop_id}`);
16412
+ continue;
15985
16413
  }
15986
- finally { if (e_2) throw e_2.error; }
16414
+ arrivalTimes.push(toTime(arrival).toMinutes());
16415
+ departureTimes.push(toTime(departure).toMinutes());
16416
+ pickUpTypes.push(parsePickupDropOffType(line.pickup_type));
16417
+ dropOffTypes.push(parsePickupDropOffType(line.drop_off_type));
16418
+ previousSeq = line.stop_sequence;
15987
16419
  }
15988
16420
  if (currentTripId) {
15989
16421
  addTrip(currentTripId);
15990
16422
  }
15991
16423
  const routesAdjacency = [];
16424
+ const tripsMapping = new Map();
15992
16425
  for (const [, routeBuilder] of routeBuilders) {
15993
- const routeData = finalizeRouteFromBuilder(routeBuilder);
15994
- routesAdjacency.push(new Route(routeData.stopTimes, routeData.pickUpDropOffTypes, routeData.stops, routeData.serviceRouteId));
16426
+ const [routeData, gtfsTripIds] = finalizeRouteFromBuilder(routeBuilder);
16427
+ const routeId = routesAdjacency.length;
16428
+ routesAdjacency.push(new Route(routeId, routeData.stopTimes, routeData.pickUpDropOffTypes, routeData.stops, routeData.serviceRouteId));
16429
+ gtfsTripIds.forEach((tripId, index) => {
16430
+ tripsMapping.set(tripId, {
16431
+ routeId,
16432
+ tripRouteIndex: index,
16433
+ });
16434
+ });
15995
16435
  }
15996
- return { routes: routesAdjacency, serviceRoutesMap };
15997
- });
16436
+ return { routes: routesAdjacency, serviceRoutesMap, tripsMapping };
16437
+ };
15998
16438
  const parsePickupDropOffType = (gtfsType) => {
15999
16439
  switch (gtfsType) {
16000
16440
  default:
@@ -16018,6 +16458,8 @@ const STOP_TIMES_FILE = 'stop_times.txt';
16018
16458
  const STOPS_FILE = 'stops.txt';
16019
16459
  const TRANSFERS_FILE = 'transfers.txt';
16020
16460
  class GtfsParser {
16461
+ path;
16462
+ profile;
16021
16463
  constructor(path, profile = standardProfile) {
16022
16464
  // TODO: support input from multiple sources
16023
16465
  this.path = path;
@@ -16029,74 +16471,81 @@ class GtfsParser {
16029
16471
  * @param date The active date.
16030
16472
  * @returns The parsed timetable.
16031
16473
  */
16032
- parseTimetable(date) {
16033
- return __awaiter(this, void 0, void 0, function* () {
16034
- log.setLevel('INFO');
16035
- const zip = new StreamZip.async({ file: this.path });
16036
- const entries = yield zip.entries();
16037
- const datetime = DateTime.fromJSDate(date);
16038
- const activeServiceIds = new Set();
16039
- const activeStopIds = new Set();
16040
- log.info(`Parsing ${STOPS_FILE}`);
16041
- const stopsStart = performance.now();
16042
- const stopsStream = yield zip.stream(STOPS_FILE);
16043
- const parsedStops = yield parseStops(stopsStream);
16044
- const stopsEnd = performance.now();
16045
- log.info(`${parsedStops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
16046
- if (entries[CALENDAR_FILE]) {
16047
- log.info(`Parsing ${CALENDAR_FILE}`);
16048
- const calendarStart = performance.now();
16049
- const calendarStream = yield zip.stream(CALENDAR_FILE);
16050
- yield parseCalendar(calendarStream, activeServiceIds, datetime);
16051
- const calendarEnd = performance.now();
16052
- log.info(`${activeServiceIds.size} valid services. (${(calendarEnd - calendarStart).toFixed(2)}ms)`);
16053
- }
16054
- if (entries[CALENDAR_DATES_FILE]) {
16055
- log.info(`Parsing ${CALENDAR_DATES_FILE}`);
16056
- const calendarDatesStart = performance.now();
16057
- const calendarDatesStream = yield zip.stream(CALENDAR_DATES_FILE);
16058
- yield parseCalendarDates(calendarDatesStream, activeServiceIds, datetime);
16059
- const calendarDatesEnd = performance.now();
16060
- log.info(`${activeServiceIds.size} valid services. (${(calendarDatesEnd - calendarDatesStart).toFixed(2)}ms)`);
16061
- }
16062
- log.info(`Parsing ${ROUTES_FILE}`);
16063
- const routesStart = performance.now();
16064
- const routesStream = yield zip.stream(ROUTES_FILE);
16065
- const validGtfsRoutes = yield parseRoutes(routesStream, this.profile);
16066
- const routesEnd = performance.now();
16067
- log.info(`${validGtfsRoutes.size} valid GTFS routes. (${(routesEnd - routesStart).toFixed(2)}ms)`);
16068
- log.info(`Parsing ${TRIPS_FILE}`);
16069
- const tripsStart = performance.now();
16070
- const tripsStream = yield zip.stream(TRIPS_FILE);
16071
- const trips = yield parseTrips(tripsStream, activeServiceIds, validGtfsRoutes);
16072
- const tripsEnd = performance.now();
16073
- log.info(`${trips.size} valid trips. (${(tripsEnd - tripsStart).toFixed(2)}ms)`);
16074
- let transfers = new Map();
16075
- if (entries[TRANSFERS_FILE]) {
16076
- log.info(`Parsing ${TRANSFERS_FILE}`);
16077
- const transfersStart = performance.now();
16078
- const transfersStream = yield zip.stream(TRANSFERS_FILE);
16079
- transfers = yield parseTransfers(transfersStream, parsedStops);
16080
- const transfersEnd = performance.now();
16081
- log.info(`${transfers.size} valid transfers. (${(transfersEnd - transfersStart).toFixed(2)}ms)`);
16082
- }
16083
- log.info(`Parsing ${STOP_TIMES_FILE}`);
16084
- const stopTimesStart = performance.now();
16085
- const stopTimesStream = yield zip.stream(STOP_TIMES_FILE);
16086
- const { routes, serviceRoutesMap } = yield parseStopTimes(stopTimesStream, parsedStops, trips, activeStopIds);
16087
- const serviceRoutes = indexRoutes(validGtfsRoutes, serviceRoutesMap);
16088
- const stopTimesEnd = performance.now();
16089
- log.info(`${routes.length} valid unique routes. (${(stopTimesEnd - stopTimesStart).toFixed(2)}ms)`);
16090
- log.info('Building stops adjacency structure');
16091
- const stopsAdjacencyStart = performance.now();
16092
- const stopsAdjacency = buildStopsAdjacencyStructure(serviceRoutes, routes, transfers, parsedStops.size, activeStopIds);
16093
- const stopsAdjacencyEnd = performance.now();
16094
- log.info(`${stopsAdjacency.length} valid stops in the structure. (${(stopsAdjacencyEnd - stopsAdjacencyStart).toFixed(2)}ms)`);
16095
- yield zip.close();
16096
- const timetable = new Timetable(stopsAdjacency, routes, serviceRoutes);
16097
- log.info('Parsing complete.');
16098
- return timetable;
16099
- });
16474
+ async parseTimetable(date) {
16475
+ log.setLevel('INFO');
16476
+ const zip = new StreamZip.async({ file: this.path });
16477
+ const entries = await zip.entries();
16478
+ const datetime = DateTime.fromJSDate(date);
16479
+ const activeServiceIds = new Set();
16480
+ const activeStopIds = new Set();
16481
+ log.info(`Parsing ${STOPS_FILE}`);
16482
+ const stopsStart = performance.now();
16483
+ const stopsStream = await zip.stream(STOPS_FILE);
16484
+ const parsedStops = await parseStops(stopsStream);
16485
+ const stopsEnd = performance.now();
16486
+ log.info(`${parsedStops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
16487
+ if (entries[CALENDAR_FILE]) {
16488
+ log.info(`Parsing ${CALENDAR_FILE}`);
16489
+ const calendarStart = performance.now();
16490
+ const calendarStream = await zip.stream(CALENDAR_FILE);
16491
+ await parseCalendar(calendarStream, activeServiceIds, datetime);
16492
+ const calendarEnd = performance.now();
16493
+ log.info(`${activeServiceIds.size} valid services. (${(calendarEnd - calendarStart).toFixed(2)}ms)`);
16494
+ }
16495
+ if (entries[CALENDAR_DATES_FILE]) {
16496
+ log.info(`Parsing ${CALENDAR_DATES_FILE}`);
16497
+ const calendarDatesStart = performance.now();
16498
+ const calendarDatesStream = await zip.stream(CALENDAR_DATES_FILE);
16499
+ await parseCalendarDates(calendarDatesStream, activeServiceIds, datetime);
16500
+ const calendarDatesEnd = performance.now();
16501
+ log.info(`${activeServiceIds.size} valid services. (${(calendarDatesEnd - calendarDatesStart).toFixed(2)}ms)`);
16502
+ }
16503
+ log.info(`Parsing ${ROUTES_FILE}`);
16504
+ const routesStart = performance.now();
16505
+ const routesStream = await zip.stream(ROUTES_FILE);
16506
+ const validGtfsRoutes = await parseRoutes(routesStream, this.profile);
16507
+ const routesEnd = performance.now();
16508
+ log.info(`${validGtfsRoutes.size} valid GTFS routes. (${(routesEnd - routesStart).toFixed(2)}ms)`);
16509
+ log.info(`Parsing ${TRIPS_FILE}`);
16510
+ const tripsStart = performance.now();
16511
+ const tripsStream = await zip.stream(TRIPS_FILE);
16512
+ const trips = await parseTrips(tripsStream, activeServiceIds, validGtfsRoutes);
16513
+ const tripsEnd = performance.now();
16514
+ log.info(`${trips.size} valid trips. (${(tripsEnd - tripsStart).toFixed(2)}ms)`);
16515
+ let transfers = new Map();
16516
+ let tripContinuationsMap = [];
16517
+ if (entries[TRANSFERS_FILE]) {
16518
+ log.info(`Parsing ${TRANSFERS_FILE}`);
16519
+ const transfersStart = performance.now();
16520
+ const transfersStream = await zip.stream(TRANSFERS_FILE);
16521
+ const { transfers: parsedTransfers, tripContinuations: parsedTripContinuations, } = await parseTransfers(transfersStream, parsedStops);
16522
+ transfers = parsedTransfers;
16523
+ tripContinuationsMap = parsedTripContinuations;
16524
+ const transfersEnd = performance.now();
16525
+ log.info(`${transfers.size} valid transfers and ${tripContinuationsMap.length} trip continuations. (${(transfersEnd - transfersStart).toFixed(2)}ms)`);
16526
+ }
16527
+ log.info(`Parsing ${STOP_TIMES_FILE}`);
16528
+ const stopTimesStart = performance.now();
16529
+ const stopTimesStream = await zip.stream(STOP_TIMES_FILE);
16530
+ const { routes, serviceRoutesMap, tripsMapping } = await parseStopTimes(stopTimesStream, parsedStops, trips, activeStopIds);
16531
+ const serviceRoutes = indexRoutes(validGtfsRoutes, serviceRoutesMap);
16532
+ const stopTimesEnd = performance.now();
16533
+ log.info(`${routes.length} valid unique routes. (${(stopTimesEnd - stopTimesStart).toFixed(2)}ms)`);
16534
+ log.info('Building stops adjacency structure');
16535
+ const stopsAdjacencyStart = performance.now();
16536
+ const stopsAdjacency = buildStopsAdjacencyStructure(serviceRoutes, routes, transfers, parsedStops.size, activeStopIds);
16537
+ const stopsAdjacencyEnd = performance.now();
16538
+ log.info(`${stopsAdjacency.length} valid stops in the structure. (${(stopsAdjacencyEnd - stopsAdjacencyStart).toFixed(2)}ms)`);
16539
+ await zip.close();
16540
+ // temporary timetable for building continuations
16541
+ const timetable = new Timetable(stopsAdjacency, routes, serviceRoutes);
16542
+ log.info('Building in-seat trip continuations');
16543
+ const tripContinuationsStart = performance.now();
16544
+ const tripContinuations = buildTripContinuations(tripsMapping, tripContinuationsMap, timetable, activeStopIds);
16545
+ const tripContinuationsEnd = performance.now();
16546
+ log.info(`${tripContinuations.size} in-seat trip continuations origins created. (${(tripContinuationsEnd - tripContinuationsStart).toFixed(2)}ms)`);
16547
+ log.info('Parsing complete.');
16548
+ return new Timetable(stopsAdjacency, routes, serviceRoutes, tripContinuations);
16100
16549
  }
16101
16550
  /**
16102
16551
  * Parses a GTFS feed to extract all stops.
@@ -16104,18 +16553,16 @@ class GtfsParser {
16104
16553
  * @param activeStops The set of active stop IDs to include in the index.
16105
16554
  * @returns An index of stops.
16106
16555
  */
16107
- parseStops() {
16108
- return __awaiter(this, void 0, void 0, function* () {
16109
- const zip = new StreamZip.async({ file: this.path });
16110
- log.info(`Parsing ${STOPS_FILE}`);
16111
- const stopsStart = performance.now();
16112
- const stopsStream = yield zip.stream(STOPS_FILE);
16113
- const stops = yield parseStops(stopsStream);
16114
- const stopsEnd = performance.now();
16115
- log.info(`${stops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
16116
- yield zip.close();
16117
- return new StopsIndex(Array.from(stops.values()));
16118
- });
16556
+ async parseStops() {
16557
+ const zip = new StreamZip.async({ file: this.path });
16558
+ log.info(`Parsing ${STOPS_FILE}`);
16559
+ const stopsStart = performance.now();
16560
+ const stopsStream = await zip.stream(STOPS_FILE);
16561
+ const stops = await parseStops(stopsStream);
16562
+ const stopsEnd = performance.now();
16563
+ log.info(`${stops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
16564
+ await zip.close();
16565
+ return new StopsIndex(Array.from(stops.values()));
16119
16566
  }
16120
16567
  }
16121
16568