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