minotor 8.0.0 → 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.
- package/CHANGELOG.md +4 -4
- package/README.md +1 -1
- package/dist/cli.mjs +835 -816
- package/dist/cli.mjs.map +1 -1
- package/dist/gtfs/transfers.d.ts +21 -6
- package/dist/gtfs/trips.d.ts +2 -2
- package/dist/parser.cjs.js +666 -642
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +666 -642
- package/dist/parser.esm.js.map +1 -1
- package/dist/router.cjs.js +1 -1
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.esm.js +1 -1
- package/dist/router.esm.js.map +1 -1
- package/dist/router.umd.js +1 -1
- package/dist/router.umd.js.map +1 -1
- package/dist/routing/router.d.ts +4 -4
- package/dist/timetable/io.d.ts +3 -3
- package/dist/timetable/proto/timetable.d.ts +6 -4
- package/dist/timetable/route.d.ts +13 -21
- package/dist/timetable/timetable.d.ts +13 -11
- package/dist/timetable/tripBoardingId.d.ts +34 -0
- package/package.json +1 -1
- package/src/__e2e__/timetable/timetable.bin +2 -2
- package/src/cli/repl.ts +53 -67
- package/src/gtfs/__tests__/parser.test.ts +19 -4
- package/src/gtfs/__tests__/transfers.test.ts +598 -318
- package/src/gtfs/__tests__/trips.test.ts +3 -44
- package/src/gtfs/parser.ts +26 -8
- package/src/gtfs/transfers.ts +151 -20
- package/src/gtfs/trips.ts +1 -39
- package/src/routing/__tests__/result.test.ts +10 -10
- package/src/routing/__tests__/router.test.ts +11 -9
- package/src/routing/result.ts +2 -2
- package/src/routing/router.ts +34 -22
- package/src/timetable/__tests__/io.test.ts +8 -7
- package/src/timetable/__tests__/route.test.ts +66 -80
- package/src/timetable/__tests__/timetable.test.ts +32 -29
- package/src/timetable/__tests__/tripBoardingId.test.ts +57 -0
- package/src/timetable/io.ts +21 -20
- package/src/timetable/proto/timetable.proto +6 -4
- package/src/timetable/proto/timetable.ts +84 -48
- package/src/timetable/route.ts +39 -56
- package/src/timetable/timetable.ts +37 -26
- package/src/timetable/tripBoardingId.ts +94 -0
- package/tsconfig.json +2 -2
- package/dist/timetable/tripId.d.ts +0 -15
- package/src/timetable/__tests__/tripId.test.ts +0 -27
- package/src/timetable/tripId.ts +0 -29
- /package/dist/timetable/__tests__/{tripId.test.d.ts → tripBoardingId.test.d.ts} +0 -0
package/dist/parser.esm.js
CHANGED
|
@@ -5,58 +5,6 @@ import require$$3 from 'events';
|
|
|
5
5
|
import require$$4 from 'zlib';
|
|
6
6
|
import require$$5, { Transform } from 'stream';
|
|
7
7
|
|
|
8
|
-
/******************************************************************************
|
|
9
|
-
Copyright (c) Microsoft Corporation.
|
|
10
|
-
|
|
11
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
12
|
-
purpose with or without fee is hereby granted.
|
|
13
|
-
|
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
15
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
16
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
17
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
18
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
19
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
20
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
21
|
-
***************************************************************************** */
|
|
22
|
-
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
26
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function __values(o) {
|
|
36
|
-
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
37
|
-
if (m) return m.call(o);
|
|
38
|
-
if (o && typeof o.length === "number") return {
|
|
39
|
-
next: function () {
|
|
40
|
-
if (o && i >= o.length) o = void 0;
|
|
41
|
-
return { value: o && o[i++], done: !o };
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function __asyncValues(o) {
|
|
48
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
49
|
-
var m = o[Symbol.asyncIterator], i;
|
|
50
|
-
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
51
|
-
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
52
|
-
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
56
|
-
var e = new Error(message);
|
|
57
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
8
|
function getDefaultExportFromCjs (x) {
|
|
61
9
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
62
10
|
}
|
|
@@ -11552,14 +11500,13 @@ const Stop = {
|
|
|
11552
11500
|
sourceStopId: isSet$1(object.sourceStopId) ? globalThis.String(object.sourceStopId) : "",
|
|
11553
11501
|
lat: isSet$1(object.lat) ? globalThis.Number(object.lat) : undefined,
|
|
11554
11502
|
lon: isSet$1(object.lon) ? globalThis.Number(object.lon) : undefined,
|
|
11555
|
-
children: globalThis.Array.isArray(object
|
|
11503
|
+
children: globalThis.Array.isArray(object?.children) ? object.children.map((e) => globalThis.Number(e)) : [],
|
|
11556
11504
|
parent: isSet$1(object.parent) ? globalThis.Number(object.parent) : undefined,
|
|
11557
11505
|
locationType: isSet$1(object.locationType) ? locationTypeFromJSON(object.locationType) : 0,
|
|
11558
11506
|
platform: isSet$1(object.platform) ? globalThis.String(object.platform) : undefined,
|
|
11559
11507
|
};
|
|
11560
11508
|
},
|
|
11561
11509
|
toJSON(message) {
|
|
11562
|
-
var _a;
|
|
11563
11510
|
const obj = {};
|
|
11564
11511
|
if (message.name !== "") {
|
|
11565
11512
|
obj.name = message.name;
|
|
@@ -11573,7 +11520,7 @@ const Stop = {
|
|
|
11573
11520
|
if (message.lon !== undefined) {
|
|
11574
11521
|
obj.lon = message.lon;
|
|
11575
11522
|
}
|
|
11576
|
-
if (
|
|
11523
|
+
if (message.children?.length) {
|
|
11577
11524
|
obj.children = message.children.map((e) => Math.round(e));
|
|
11578
11525
|
}
|
|
11579
11526
|
if (message.parent !== undefined) {
|
|
@@ -11588,19 +11535,18 @@ const Stop = {
|
|
|
11588
11535
|
return obj;
|
|
11589
11536
|
},
|
|
11590
11537
|
create(base) {
|
|
11591
|
-
return Stop.fromPartial(base
|
|
11538
|
+
return Stop.fromPartial(base ?? {});
|
|
11592
11539
|
},
|
|
11593
11540
|
fromPartial(object) {
|
|
11594
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
11595
11541
|
const message = createBaseStop();
|
|
11596
|
-
message.name =
|
|
11597
|
-
message.sourceStopId =
|
|
11598
|
-
message.lat =
|
|
11599
|
-
message.lon =
|
|
11600
|
-
message.children =
|
|
11601
|
-
message.parent =
|
|
11602
|
-
message.locationType =
|
|
11603
|
-
message.platform =
|
|
11542
|
+
message.name = object.name ?? "";
|
|
11543
|
+
message.sourceStopId = object.sourceStopId ?? "";
|
|
11544
|
+
message.lat = object.lat ?? undefined;
|
|
11545
|
+
message.lon = object.lon ?? undefined;
|
|
11546
|
+
message.children = object.children?.map((e) => e) || [];
|
|
11547
|
+
message.parent = object.parent ?? undefined;
|
|
11548
|
+
message.locationType = object.locationType ?? 0;
|
|
11549
|
+
message.platform = object.platform ?? undefined;
|
|
11604
11550
|
return message;
|
|
11605
11551
|
},
|
|
11606
11552
|
};
|
|
@@ -11649,28 +11595,26 @@ const StopsMap = {
|
|
|
11649
11595
|
fromJSON(object) {
|
|
11650
11596
|
return {
|
|
11651
11597
|
version: isSet$1(object.version) ? globalThis.String(object.version) : "",
|
|
11652
|
-
stops: globalThis.Array.isArray(object
|
|
11598
|
+
stops: globalThis.Array.isArray(object?.stops) ? object.stops.map((e) => Stop.fromJSON(e)) : [],
|
|
11653
11599
|
};
|
|
11654
11600
|
},
|
|
11655
11601
|
toJSON(message) {
|
|
11656
|
-
var _a;
|
|
11657
11602
|
const obj = {};
|
|
11658
11603
|
if (message.version !== "") {
|
|
11659
11604
|
obj.version = message.version;
|
|
11660
11605
|
}
|
|
11661
|
-
if (
|
|
11606
|
+
if (message.stops?.length) {
|
|
11662
11607
|
obj.stops = message.stops.map((e) => Stop.toJSON(e));
|
|
11663
11608
|
}
|
|
11664
11609
|
return obj;
|
|
11665
11610
|
},
|
|
11666
11611
|
create(base) {
|
|
11667
|
-
return StopsMap.fromPartial(base
|
|
11612
|
+
return StopsMap.fromPartial(base ?? {});
|
|
11668
11613
|
},
|
|
11669
11614
|
fromPartial(object) {
|
|
11670
|
-
var _a, _b;
|
|
11671
11615
|
const message = createBaseStopsMap();
|
|
11672
|
-
message.version =
|
|
11673
|
-
message.stops =
|
|
11616
|
+
message.version = object.version ?? "";
|
|
11617
|
+
message.stops = object.stops?.map((e) => Stop.fromPartial(e)) || [];
|
|
11674
11618
|
return message;
|
|
11675
11619
|
},
|
|
11676
11620
|
};
|
|
@@ -11754,8 +11698,12 @@ const serializeLocationType = (locationType) => {
|
|
|
11754
11698
|
* to efficiently find stops based on user queries.
|
|
11755
11699
|
*/
|
|
11756
11700
|
class StopsIndex {
|
|
11701
|
+
stops;
|
|
11702
|
+
sourceStopsMap;
|
|
11703
|
+
textIndex;
|
|
11704
|
+
geoIndex;
|
|
11705
|
+
stopPoints;
|
|
11757
11706
|
constructor(stops) {
|
|
11758
|
-
var _a;
|
|
11759
11707
|
this.stops = stops;
|
|
11760
11708
|
this.sourceStopsMap = new Map();
|
|
11761
11709
|
const stopsSet = new Map();
|
|
@@ -11764,7 +11712,7 @@ class StopsIndex {
|
|
|
11764
11712
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
11765
11713
|
const stop = stops[id];
|
|
11766
11714
|
this.sourceStopsMap.set(stop.sourceStopId, id);
|
|
11767
|
-
const effectiveStopId =
|
|
11715
|
+
const effectiveStopId = stop.parent ?? id;
|
|
11768
11716
|
if (!stopsSet.has(effectiveStopId)) {
|
|
11769
11717
|
stopsSet.set(effectiveStopId, {
|
|
11770
11718
|
id: effectiveStopId,
|
|
@@ -11879,7 +11827,6 @@ class StopsIndex {
|
|
|
11879
11827
|
* Find ids of all sibling stops.
|
|
11880
11828
|
*/
|
|
11881
11829
|
equivalentStops(sourceId) {
|
|
11882
|
-
var _a, _b;
|
|
11883
11830
|
const id = this.sourceStopsMap.get(sourceId);
|
|
11884
11831
|
if (id === undefined) {
|
|
11885
11832
|
return [];
|
|
@@ -11889,13 +11836,14 @@ class StopsIndex {
|
|
|
11889
11836
|
return [];
|
|
11890
11837
|
}
|
|
11891
11838
|
const equivalentStops = stop.parent
|
|
11892
|
-
? (
|
|
11839
|
+
? (this.stops[stop.parent]?.children ?? [])
|
|
11893
11840
|
: stop.children;
|
|
11894
11841
|
return Array.from(new Set([id, ...equivalentStops])).map((stopId) => this.stops[stopId]);
|
|
11895
11842
|
}
|
|
11896
11843
|
}
|
|
11897
11844
|
|
|
11898
11845
|
class Duration {
|
|
11846
|
+
totalSeconds;
|
|
11899
11847
|
constructor(totalSeconds) {
|
|
11900
11848
|
this.totalSeconds = totalSeconds;
|
|
11901
11849
|
}
|
|
@@ -12198,15 +12146,14 @@ const Route$1 = {
|
|
|
12198
12146
|
return obj;
|
|
12199
12147
|
},
|
|
12200
12148
|
create(base) {
|
|
12201
|
-
return Route$1.fromPartial(base
|
|
12149
|
+
return Route$1.fromPartial(base ?? {});
|
|
12202
12150
|
},
|
|
12203
12151
|
fromPartial(object) {
|
|
12204
|
-
var _a, _b, _c, _d;
|
|
12205
12152
|
const message = createBaseRoute();
|
|
12206
|
-
message.stopTimes =
|
|
12207
|
-
message.pickUpDropOffTypes =
|
|
12208
|
-
message.stops =
|
|
12209
|
-
message.serviceRouteId =
|
|
12153
|
+
message.stopTimes = object.stopTimes ?? new Uint8Array(0);
|
|
12154
|
+
message.pickUpDropOffTypes = object.pickUpDropOffTypes ?? new Uint8Array(0);
|
|
12155
|
+
message.stops = object.stops ?? new Uint8Array(0);
|
|
12156
|
+
message.serviceRouteId = object.serviceRouteId ?? 0;
|
|
12210
12157
|
return message;
|
|
12211
12158
|
},
|
|
12212
12159
|
};
|
|
@@ -12283,24 +12230,23 @@ const Transfer = {
|
|
|
12283
12230
|
return obj;
|
|
12284
12231
|
},
|
|
12285
12232
|
create(base) {
|
|
12286
|
-
return Transfer.fromPartial(base
|
|
12233
|
+
return Transfer.fromPartial(base ?? {});
|
|
12287
12234
|
},
|
|
12288
12235
|
fromPartial(object) {
|
|
12289
|
-
var _a, _b, _c;
|
|
12290
12236
|
const message = createBaseTransfer();
|
|
12291
|
-
message.destination =
|
|
12292
|
-
message.type =
|
|
12293
|
-
message.minTransferTime =
|
|
12237
|
+
message.destination = object.destination ?? 0;
|
|
12238
|
+
message.type = object.type ?? 0;
|
|
12239
|
+
message.minTransferTime = object.minTransferTime ?? undefined;
|
|
12294
12240
|
return message;
|
|
12295
12241
|
},
|
|
12296
12242
|
};
|
|
12297
12243
|
function createBaseTripBoarding() {
|
|
12298
|
-
return {
|
|
12244
|
+
return { hopOnStopIndex: 0, routeId: 0, tripIndex: 0 };
|
|
12299
12245
|
}
|
|
12300
12246
|
const TripBoarding = {
|
|
12301
12247
|
encode(message, writer = new BinaryWriter()) {
|
|
12302
|
-
if (message.
|
|
12303
|
-
writer.uint32(8).uint32(message.
|
|
12248
|
+
if (message.hopOnStopIndex !== 0) {
|
|
12249
|
+
writer.uint32(8).uint32(message.hopOnStopIndex);
|
|
12304
12250
|
}
|
|
12305
12251
|
if (message.routeId !== 0) {
|
|
12306
12252
|
writer.uint32(16).uint32(message.routeId);
|
|
@@ -12321,7 +12267,7 @@ const TripBoarding = {
|
|
|
12321
12267
|
if (tag !== 8) {
|
|
12322
12268
|
break;
|
|
12323
12269
|
}
|
|
12324
|
-
message.
|
|
12270
|
+
message.hopOnStopIndex = reader.uint32();
|
|
12325
12271
|
continue;
|
|
12326
12272
|
}
|
|
12327
12273
|
case 2: {
|
|
@@ -12348,15 +12294,15 @@ const TripBoarding = {
|
|
|
12348
12294
|
},
|
|
12349
12295
|
fromJSON(object) {
|
|
12350
12296
|
return {
|
|
12351
|
-
|
|
12297
|
+
hopOnStopIndex: isSet(object.hopOnStopIndex) ? globalThis.Number(object.hopOnStopIndex) : 0,
|
|
12352
12298
|
routeId: isSet(object.routeId) ? globalThis.Number(object.routeId) : 0,
|
|
12353
12299
|
tripIndex: isSet(object.tripIndex) ? globalThis.Number(object.tripIndex) : 0,
|
|
12354
12300
|
};
|
|
12355
12301
|
},
|
|
12356
12302
|
toJSON(message) {
|
|
12357
12303
|
const obj = {};
|
|
12358
|
-
if (message.
|
|
12359
|
-
obj.
|
|
12304
|
+
if (message.hopOnStopIndex !== 0) {
|
|
12305
|
+
obj.hopOnStopIndex = Math.round(message.hopOnStopIndex);
|
|
12360
12306
|
}
|
|
12361
12307
|
if (message.routeId !== 0) {
|
|
12362
12308
|
obj.routeId = Math.round(message.routeId);
|
|
@@ -12367,27 +12313,32 @@ const TripBoarding = {
|
|
|
12367
12313
|
return obj;
|
|
12368
12314
|
},
|
|
12369
12315
|
create(base) {
|
|
12370
|
-
return TripBoarding.fromPartial(base
|
|
12316
|
+
return TripBoarding.fromPartial(base ?? {});
|
|
12371
12317
|
},
|
|
12372
12318
|
fromPartial(object) {
|
|
12373
|
-
var _a, _b, _c;
|
|
12374
12319
|
const message = createBaseTripBoarding();
|
|
12375
|
-
message.
|
|
12376
|
-
message.routeId =
|
|
12377
|
-
message.tripIndex =
|
|
12320
|
+
message.hopOnStopIndex = object.hopOnStopIndex ?? 0;
|
|
12321
|
+
message.routeId = object.routeId ?? 0;
|
|
12322
|
+
message.tripIndex = object.tripIndex ?? 0;
|
|
12378
12323
|
return message;
|
|
12379
12324
|
},
|
|
12380
12325
|
};
|
|
12381
12326
|
function createBaseTripContinuationEntry() {
|
|
12382
|
-
return {
|
|
12327
|
+
return { originStopIndex: 0, originRouteId: 0, originTripIndex: 0, continuations: [] };
|
|
12383
12328
|
}
|
|
12384
12329
|
const TripContinuationEntry = {
|
|
12385
12330
|
encode(message, writer = new BinaryWriter()) {
|
|
12386
|
-
if (message.
|
|
12387
|
-
writer.uint32(8).uint32(message.
|
|
12331
|
+
if (message.originStopIndex !== 0) {
|
|
12332
|
+
writer.uint32(8).uint32(message.originStopIndex);
|
|
12333
|
+
}
|
|
12334
|
+
if (message.originRouteId !== 0) {
|
|
12335
|
+
writer.uint32(16).uint32(message.originRouteId);
|
|
12336
|
+
}
|
|
12337
|
+
if (message.originTripIndex !== 0) {
|
|
12338
|
+
writer.uint32(24).uint32(message.originTripIndex);
|
|
12388
12339
|
}
|
|
12389
|
-
for (const v of message.
|
|
12390
|
-
TripBoarding.encode(v, writer.uint32(
|
|
12340
|
+
for (const v of message.continuations) {
|
|
12341
|
+
TripBoarding.encode(v, writer.uint32(34).fork()).join();
|
|
12391
12342
|
}
|
|
12392
12343
|
return writer;
|
|
12393
12344
|
},
|
|
@@ -12402,14 +12353,28 @@ const TripContinuationEntry = {
|
|
|
12402
12353
|
if (tag !== 8) {
|
|
12403
12354
|
break;
|
|
12404
12355
|
}
|
|
12405
|
-
message.
|
|
12356
|
+
message.originStopIndex = reader.uint32();
|
|
12406
12357
|
continue;
|
|
12407
12358
|
}
|
|
12408
12359
|
case 2: {
|
|
12409
|
-
if (tag !==
|
|
12360
|
+
if (tag !== 16) {
|
|
12361
|
+
break;
|
|
12362
|
+
}
|
|
12363
|
+
message.originRouteId = reader.uint32();
|
|
12364
|
+
continue;
|
|
12365
|
+
}
|
|
12366
|
+
case 3: {
|
|
12367
|
+
if (tag !== 24) {
|
|
12410
12368
|
break;
|
|
12411
12369
|
}
|
|
12412
|
-
message.
|
|
12370
|
+
message.originTripIndex = reader.uint32();
|
|
12371
|
+
continue;
|
|
12372
|
+
}
|
|
12373
|
+
case 4: {
|
|
12374
|
+
if (tag !== 34) {
|
|
12375
|
+
break;
|
|
12376
|
+
}
|
|
12377
|
+
message.continuations.push(TripBoarding.decode(reader, reader.uint32()));
|
|
12413
12378
|
continue;
|
|
12414
12379
|
}
|
|
12415
12380
|
}
|
|
@@ -12422,34 +12387,44 @@ const TripContinuationEntry = {
|
|
|
12422
12387
|
},
|
|
12423
12388
|
fromJSON(object) {
|
|
12424
12389
|
return {
|
|
12425
|
-
|
|
12426
|
-
|
|
12390
|
+
originStopIndex: isSet(object.originStopIndex) ? globalThis.Number(object.originStopIndex) : 0,
|
|
12391
|
+
originRouteId: isSet(object.originRouteId) ? globalThis.Number(object.originRouteId) : 0,
|
|
12392
|
+
originTripIndex: isSet(object.originTripIndex) ? globalThis.Number(object.originTripIndex) : 0,
|
|
12393
|
+
continuations: globalThis.Array.isArray(object?.continuations)
|
|
12394
|
+
? object.continuations.map((e) => TripBoarding.fromJSON(e))
|
|
12395
|
+
: [],
|
|
12427
12396
|
};
|
|
12428
12397
|
},
|
|
12429
12398
|
toJSON(message) {
|
|
12430
|
-
var _a;
|
|
12431
12399
|
const obj = {};
|
|
12432
|
-
if (message.
|
|
12433
|
-
obj.
|
|
12400
|
+
if (message.originStopIndex !== 0) {
|
|
12401
|
+
obj.originStopIndex = Math.round(message.originStopIndex);
|
|
12402
|
+
}
|
|
12403
|
+
if (message.originRouteId !== 0) {
|
|
12404
|
+
obj.originRouteId = Math.round(message.originRouteId);
|
|
12434
12405
|
}
|
|
12435
|
-
if (
|
|
12436
|
-
obj.
|
|
12406
|
+
if (message.originTripIndex !== 0) {
|
|
12407
|
+
obj.originTripIndex = Math.round(message.originTripIndex);
|
|
12408
|
+
}
|
|
12409
|
+
if (message.continuations?.length) {
|
|
12410
|
+
obj.continuations = message.continuations.map((e) => TripBoarding.toJSON(e));
|
|
12437
12411
|
}
|
|
12438
12412
|
return obj;
|
|
12439
12413
|
},
|
|
12440
12414
|
create(base) {
|
|
12441
|
-
return TripContinuationEntry.fromPartial(base
|
|
12415
|
+
return TripContinuationEntry.fromPartial(base ?? {});
|
|
12442
12416
|
},
|
|
12443
12417
|
fromPartial(object) {
|
|
12444
|
-
var _a, _b;
|
|
12445
12418
|
const message = createBaseTripContinuationEntry();
|
|
12446
|
-
message.
|
|
12447
|
-
message.
|
|
12419
|
+
message.originStopIndex = object.originStopIndex ?? 0;
|
|
12420
|
+
message.originRouteId = object.originRouteId ?? 0;
|
|
12421
|
+
message.originTripIndex = object.originTripIndex ?? 0;
|
|
12422
|
+
message.continuations = object.continuations?.map((e) => TripBoarding.fromPartial(e)) || [];
|
|
12448
12423
|
return message;
|
|
12449
12424
|
},
|
|
12450
12425
|
};
|
|
12451
12426
|
function createBaseStopAdjacency() {
|
|
12452
|
-
return { routes: [], transfers: []
|
|
12427
|
+
return { routes: [], transfers: [] };
|
|
12453
12428
|
}
|
|
12454
12429
|
const StopAdjacency = {
|
|
12455
12430
|
encode(message, writer = new BinaryWriter()) {
|
|
@@ -12461,9 +12436,6 @@ const StopAdjacency = {
|
|
|
12461
12436
|
for (const v of message.transfers) {
|
|
12462
12437
|
Transfer.encode(v, writer.uint32(18).fork()).join();
|
|
12463
12438
|
}
|
|
12464
|
-
for (const v of message.tripContinuations) {
|
|
12465
|
-
TripContinuationEntry.encode(v, writer.uint32(26).fork()).join();
|
|
12466
|
-
}
|
|
12467
12439
|
return writer;
|
|
12468
12440
|
},
|
|
12469
12441
|
decode(input, length) {
|
|
@@ -12494,13 +12466,6 @@ const StopAdjacency = {
|
|
|
12494
12466
|
message.transfers.push(Transfer.decode(reader, reader.uint32()));
|
|
12495
12467
|
continue;
|
|
12496
12468
|
}
|
|
12497
|
-
case 3: {
|
|
12498
|
-
if (tag !== 26) {
|
|
12499
|
-
break;
|
|
12500
|
-
}
|
|
12501
|
-
message.tripContinuations.push(TripContinuationEntry.decode(reader, reader.uint32()));
|
|
12502
|
-
continue;
|
|
12503
|
-
}
|
|
12504
12469
|
}
|
|
12505
12470
|
if ((tag & 7) === 4 || tag === 0) {
|
|
12506
12471
|
break;
|
|
@@ -12511,38 +12476,29 @@ const StopAdjacency = {
|
|
|
12511
12476
|
},
|
|
12512
12477
|
fromJSON(object) {
|
|
12513
12478
|
return {
|
|
12514
|
-
routes: globalThis.Array.isArray(object
|
|
12515
|
-
transfers: globalThis.Array.isArray(object
|
|
12479
|
+
routes: globalThis.Array.isArray(object?.routes) ? object.routes.map((e) => globalThis.Number(e)) : [],
|
|
12480
|
+
transfers: globalThis.Array.isArray(object?.transfers)
|
|
12516
12481
|
? object.transfers.map((e) => Transfer.fromJSON(e))
|
|
12517
12482
|
: [],
|
|
12518
|
-
tripContinuations: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.tripContinuations)
|
|
12519
|
-
? object.tripContinuations.map((e) => TripContinuationEntry.fromJSON(e))
|
|
12520
|
-
: [],
|
|
12521
12483
|
};
|
|
12522
12484
|
},
|
|
12523
12485
|
toJSON(message) {
|
|
12524
|
-
var _a, _b, _c;
|
|
12525
12486
|
const obj = {};
|
|
12526
|
-
if (
|
|
12487
|
+
if (message.routes?.length) {
|
|
12527
12488
|
obj.routes = message.routes.map((e) => Math.round(e));
|
|
12528
12489
|
}
|
|
12529
|
-
if (
|
|
12490
|
+
if (message.transfers?.length) {
|
|
12530
12491
|
obj.transfers = message.transfers.map((e) => Transfer.toJSON(e));
|
|
12531
12492
|
}
|
|
12532
|
-
if ((_c = message.tripContinuations) === null || _c === void 0 ? void 0 : _c.length) {
|
|
12533
|
-
obj.tripContinuations = message.tripContinuations.map((e) => TripContinuationEntry.toJSON(e));
|
|
12534
|
-
}
|
|
12535
12493
|
return obj;
|
|
12536
12494
|
},
|
|
12537
12495
|
create(base) {
|
|
12538
|
-
return StopAdjacency.fromPartial(base
|
|
12496
|
+
return StopAdjacency.fromPartial(base ?? {});
|
|
12539
12497
|
},
|
|
12540
12498
|
fromPartial(object) {
|
|
12541
|
-
var _a, _b, _c;
|
|
12542
12499
|
const message = createBaseStopAdjacency();
|
|
12543
|
-
message.routes =
|
|
12544
|
-
message.transfers =
|
|
12545
|
-
message.tripContinuations = ((_c = object.tripContinuations) === null || _c === void 0 ? void 0 : _c.map((e) => TripContinuationEntry.fromPartial(e))) || [];
|
|
12500
|
+
message.routes = object.routes?.map((e) => e) || [];
|
|
12501
|
+
message.transfers = object.transfers?.map((e) => Transfer.fromPartial(e)) || [];
|
|
12546
12502
|
return message;
|
|
12547
12503
|
},
|
|
12548
12504
|
};
|
|
@@ -12611,11 +12567,10 @@ const ServiceRoute = {
|
|
|
12611
12567
|
return {
|
|
12612
12568
|
type: isSet(object.type) ? routeTypeFromJSON(object.type) : 0,
|
|
12613
12569
|
name: isSet(object.name) ? globalThis.String(object.name) : "",
|
|
12614
|
-
routes: globalThis.Array.isArray(object
|
|
12570
|
+
routes: globalThis.Array.isArray(object?.routes) ? object.routes.map((e) => globalThis.Number(e)) : [],
|
|
12615
12571
|
};
|
|
12616
12572
|
},
|
|
12617
12573
|
toJSON(message) {
|
|
12618
|
-
var _a;
|
|
12619
12574
|
const obj = {};
|
|
12620
12575
|
if (message.type !== 0) {
|
|
12621
12576
|
obj.type = routeTypeToJSON(message.type);
|
|
@@ -12623,25 +12578,24 @@ const ServiceRoute = {
|
|
|
12623
12578
|
if (message.name !== "") {
|
|
12624
12579
|
obj.name = message.name;
|
|
12625
12580
|
}
|
|
12626
|
-
if (
|
|
12581
|
+
if (message.routes?.length) {
|
|
12627
12582
|
obj.routes = message.routes.map((e) => Math.round(e));
|
|
12628
12583
|
}
|
|
12629
12584
|
return obj;
|
|
12630
12585
|
},
|
|
12631
12586
|
create(base) {
|
|
12632
|
-
return ServiceRoute.fromPartial(base
|
|
12587
|
+
return ServiceRoute.fromPartial(base ?? {});
|
|
12633
12588
|
},
|
|
12634
12589
|
fromPartial(object) {
|
|
12635
|
-
var _a, _b, _c;
|
|
12636
12590
|
const message = createBaseServiceRoute();
|
|
12637
|
-
message.type =
|
|
12638
|
-
message.name =
|
|
12639
|
-
message.routes =
|
|
12591
|
+
message.type = object.type ?? 0;
|
|
12592
|
+
message.name = object.name ?? "";
|
|
12593
|
+
message.routes = object.routes?.map((e) => e) || [];
|
|
12640
12594
|
return message;
|
|
12641
12595
|
},
|
|
12642
12596
|
};
|
|
12643
12597
|
function createBaseTimetable() {
|
|
12644
|
-
return { version: "", stopsAdjacency: [], routesAdjacency: [], serviceRoutes: [] };
|
|
12598
|
+
return { version: "", stopsAdjacency: [], routesAdjacency: [], serviceRoutes: [], tripContinuations: [] };
|
|
12645
12599
|
}
|
|
12646
12600
|
const Timetable$1 = {
|
|
12647
12601
|
encode(message, writer = new BinaryWriter()) {
|
|
@@ -12657,6 +12611,9 @@ const Timetable$1 = {
|
|
|
12657
12611
|
for (const v of message.serviceRoutes) {
|
|
12658
12612
|
ServiceRoute.encode(v, writer.uint32(34).fork()).join();
|
|
12659
12613
|
}
|
|
12614
|
+
for (const v of message.tripContinuations) {
|
|
12615
|
+
TripContinuationEntry.encode(v, writer.uint32(42).fork()).join();
|
|
12616
|
+
}
|
|
12660
12617
|
return writer;
|
|
12661
12618
|
},
|
|
12662
12619
|
decode(input, length) {
|
|
@@ -12694,6 +12651,13 @@ const Timetable$1 = {
|
|
|
12694
12651
|
message.serviceRoutes.push(ServiceRoute.decode(reader, reader.uint32()));
|
|
12695
12652
|
continue;
|
|
12696
12653
|
}
|
|
12654
|
+
case 5: {
|
|
12655
|
+
if (tag !== 42) {
|
|
12656
|
+
break;
|
|
12657
|
+
}
|
|
12658
|
+
message.tripContinuations.push(TripContinuationEntry.decode(reader, reader.uint32()));
|
|
12659
|
+
continue;
|
|
12660
|
+
}
|
|
12697
12661
|
}
|
|
12698
12662
|
if ((tag & 7) === 4 || tag === 0) {
|
|
12699
12663
|
break;
|
|
@@ -12705,44 +12669,49 @@ const Timetable$1 = {
|
|
|
12705
12669
|
fromJSON(object) {
|
|
12706
12670
|
return {
|
|
12707
12671
|
version: isSet(object.version) ? globalThis.String(object.version) : "",
|
|
12708
|
-
stopsAdjacency: globalThis.Array.isArray(object
|
|
12672
|
+
stopsAdjacency: globalThis.Array.isArray(object?.stopsAdjacency)
|
|
12709
12673
|
? object.stopsAdjacency.map((e) => StopAdjacency.fromJSON(e))
|
|
12710
12674
|
: [],
|
|
12711
|
-
routesAdjacency: globalThis.Array.isArray(object
|
|
12675
|
+
routesAdjacency: globalThis.Array.isArray(object?.routesAdjacency)
|
|
12712
12676
|
? object.routesAdjacency.map((e) => Route$1.fromJSON(e))
|
|
12713
12677
|
: [],
|
|
12714
|
-
serviceRoutes: globalThis.Array.isArray(object
|
|
12678
|
+
serviceRoutes: globalThis.Array.isArray(object?.serviceRoutes)
|
|
12715
12679
|
? object.serviceRoutes.map((e) => ServiceRoute.fromJSON(e))
|
|
12716
12680
|
: [],
|
|
12681
|
+
tripContinuations: globalThis.Array.isArray(object?.tripContinuations)
|
|
12682
|
+
? object.tripContinuations.map((e) => TripContinuationEntry.fromJSON(e))
|
|
12683
|
+
: [],
|
|
12717
12684
|
};
|
|
12718
12685
|
},
|
|
12719
12686
|
toJSON(message) {
|
|
12720
|
-
var _a, _b, _c;
|
|
12721
12687
|
const obj = {};
|
|
12722
12688
|
if (message.version !== "") {
|
|
12723
12689
|
obj.version = message.version;
|
|
12724
12690
|
}
|
|
12725
|
-
if (
|
|
12691
|
+
if (message.stopsAdjacency?.length) {
|
|
12726
12692
|
obj.stopsAdjacency = message.stopsAdjacency.map((e) => StopAdjacency.toJSON(e));
|
|
12727
12693
|
}
|
|
12728
|
-
if (
|
|
12694
|
+
if (message.routesAdjacency?.length) {
|
|
12729
12695
|
obj.routesAdjacency = message.routesAdjacency.map((e) => Route$1.toJSON(e));
|
|
12730
12696
|
}
|
|
12731
|
-
if (
|
|
12697
|
+
if (message.serviceRoutes?.length) {
|
|
12732
12698
|
obj.serviceRoutes = message.serviceRoutes.map((e) => ServiceRoute.toJSON(e));
|
|
12733
12699
|
}
|
|
12700
|
+
if (message.tripContinuations?.length) {
|
|
12701
|
+
obj.tripContinuations = message.tripContinuations.map((e) => TripContinuationEntry.toJSON(e));
|
|
12702
|
+
}
|
|
12734
12703
|
return obj;
|
|
12735
12704
|
},
|
|
12736
12705
|
create(base) {
|
|
12737
|
-
return Timetable$1.fromPartial(base
|
|
12706
|
+
return Timetable$1.fromPartial(base ?? {});
|
|
12738
12707
|
},
|
|
12739
12708
|
fromPartial(object) {
|
|
12740
|
-
var _a, _b, _c, _d;
|
|
12741
12709
|
const message = createBaseTimetable();
|
|
12742
|
-
message.version =
|
|
12743
|
-
message.stopsAdjacency =
|
|
12744
|
-
message.routesAdjacency =
|
|
12745
|
-
message.serviceRoutes =
|
|
12710
|
+
message.version = object.version ?? "";
|
|
12711
|
+
message.stopsAdjacency = object.stopsAdjacency?.map((e) => StopAdjacency.fromPartial(e)) || [];
|
|
12712
|
+
message.routesAdjacency = object.routesAdjacency?.map((e) => Route$1.fromPartial(e)) || [];
|
|
12713
|
+
message.serviceRoutes = object.serviceRoutes?.map((e) => ServiceRoute.fromPartial(e)) || [];
|
|
12714
|
+
message.tripContinuations = object.tripContinuations?.map((e) => TripContinuationEntry.fromPartial(e)) || [];
|
|
12746
12715
|
return message;
|
|
12747
12716
|
},
|
|
12748
12717
|
};
|
|
@@ -12779,6 +12748,11 @@ function isSet(value) {
|
|
|
12779
12748
|
* A class representing a time as minutes since midnight.
|
|
12780
12749
|
*/
|
|
12781
12750
|
class Time {
|
|
12751
|
+
/*
|
|
12752
|
+
* Number of minutes since midnight.
|
|
12753
|
+
Note that this value can go beyond 3600 to model services overlapping with the next day.
|
|
12754
|
+
*/
|
|
12755
|
+
minutesSinceMidnight;
|
|
12782
12756
|
/**
|
|
12783
12757
|
* Gets the infinity time as a Time instance.
|
|
12784
12758
|
* This represents a time that is conceptually beyond any real possible time.
|
|
@@ -13031,6 +13005,51 @@ const toPickupDropOffType = (numericalType) => {
|
|
|
13031
13005
|
* A route identifies all trips of a given service route sharing the same list of stops.
|
|
13032
13006
|
*/
|
|
13033
13007
|
class Route {
|
|
13008
|
+
id;
|
|
13009
|
+
/**
|
|
13010
|
+
* Arrivals and departures encoded as minutes from midnight.
|
|
13011
|
+
* Format: [arrival1, departure1, arrival2, departure2, etc.]
|
|
13012
|
+
*/
|
|
13013
|
+
stopTimes;
|
|
13014
|
+
/**
|
|
13015
|
+
* PickUp and DropOff types represented as a 2-bit encoded Uint8Array.
|
|
13016
|
+
* Values (2 bits each):
|
|
13017
|
+
* 0: REGULAR
|
|
13018
|
+
* 1: NOT_AVAILABLE
|
|
13019
|
+
* 2: MUST_PHONE_AGENCY
|
|
13020
|
+
* 3: MUST_COORDINATE_WITH_DRIVER
|
|
13021
|
+
*
|
|
13022
|
+
* Encoding format: Each byte contains 2 pickup/drop-off pairs (4 bits each)
|
|
13023
|
+
* Bit layout per byte: [pickup_1 (2 bits)][drop_off_1 (2 bits)][pickup_0 (2 bits)][drop_off_0 (2 bits)]
|
|
13024
|
+
* Example: For stops 0 and 1 in a trip, one byte encodes all 4 values
|
|
13025
|
+
*/
|
|
13026
|
+
pickUpDropOffTypes;
|
|
13027
|
+
/**
|
|
13028
|
+
* A binary array of stopIds in the route.
|
|
13029
|
+
* [stop1, stop2, stop3,...]
|
|
13030
|
+
*/
|
|
13031
|
+
stops;
|
|
13032
|
+
/**
|
|
13033
|
+
* A reverse mapping of each stop with their index in the route:
|
|
13034
|
+
* {
|
|
13035
|
+
* 4: 0,
|
|
13036
|
+
* 5: 1,
|
|
13037
|
+
* ...
|
|
13038
|
+
* }
|
|
13039
|
+
*/
|
|
13040
|
+
stopIndices;
|
|
13041
|
+
/**
|
|
13042
|
+
* The identifier of the route as a service shown to users.
|
|
13043
|
+
*/
|
|
13044
|
+
serviceRouteId;
|
|
13045
|
+
/**
|
|
13046
|
+
* The total number of stops in the route.
|
|
13047
|
+
*/
|
|
13048
|
+
nbStops;
|
|
13049
|
+
/**
|
|
13050
|
+
* The total number of trips in the route.
|
|
13051
|
+
*/
|
|
13052
|
+
nbTrips;
|
|
13034
13053
|
constructor(id, stopTimes, pickUpDropOffTypes, stops, serviceRouteId) {
|
|
13035
13054
|
this.id = id;
|
|
13036
13055
|
this.stopTimes = stopTimes;
|
|
@@ -13042,7 +13061,14 @@ class Route {
|
|
|
13042
13061
|
this.stopIndices = new Map();
|
|
13043
13062
|
for (let i = 0; i < stops.length; i++) {
|
|
13044
13063
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
13045
|
-
|
|
13064
|
+
const stopId = stops[i];
|
|
13065
|
+
const existingIndices = this.stopIndices.get(stopId);
|
|
13066
|
+
if (existingIndices) {
|
|
13067
|
+
existingIndices.push(i);
|
|
13068
|
+
}
|
|
13069
|
+
else {
|
|
13070
|
+
this.stopIndices.set(stopId, [i]);
|
|
13071
|
+
}
|
|
13046
13072
|
}
|
|
13047
13073
|
}
|
|
13048
13074
|
/**
|
|
@@ -13052,7 +13078,6 @@ class Route {
|
|
|
13052
13078
|
* @returns The new route.
|
|
13053
13079
|
*/
|
|
13054
13080
|
static of(params) {
|
|
13055
|
-
var _a, _b;
|
|
13056
13081
|
const { id, serviceRouteId, trips } = params;
|
|
13057
13082
|
if (trips.length === 0) {
|
|
13058
13083
|
throw new Error('At least one trip must be provided');
|
|
@@ -13099,8 +13124,8 @@ class Route {
|
|
|
13099
13124
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
13100
13125
|
const stop = trip.stops[stopIndex];
|
|
13101
13126
|
const globalIndex = tripIndex * numStops + stopIndex;
|
|
13102
|
-
const pickUp =
|
|
13103
|
-
const dropOff =
|
|
13127
|
+
const pickUp = stop.pickUpType ?? REGULAR;
|
|
13128
|
+
const dropOff = stop.dropOffType ?? REGULAR;
|
|
13104
13129
|
const byteIndex = Math.floor(globalIndex / 2);
|
|
13105
13130
|
const isSecondPair = globalIndex % 2 === 1;
|
|
13106
13131
|
if (isSecondPair) {
|
|
@@ -13130,24 +13155,6 @@ class Route {
|
|
|
13130
13155
|
serviceRouteId: this.serviceRouteId,
|
|
13131
13156
|
};
|
|
13132
13157
|
}
|
|
13133
|
-
/**
|
|
13134
|
-
* Checks if stop A is before stop B in the route.
|
|
13135
|
-
*
|
|
13136
|
-
* @param stopA - The StopId of the first stop.
|
|
13137
|
-
* @param stopB - The StopId of the second stop.
|
|
13138
|
-
* @returns True if stop A is before stop B, false otherwise.
|
|
13139
|
-
*/
|
|
13140
|
-
isBefore(stopA, stopB) {
|
|
13141
|
-
const stopAIndex = this.stopIndices.get(stopA);
|
|
13142
|
-
if (stopAIndex === undefined) {
|
|
13143
|
-
throw new Error(`Stop index not found for ${stopA} in route ${this.serviceRouteId}`);
|
|
13144
|
-
}
|
|
13145
|
-
const stopBIndex = this.stopIndices.get(stopB);
|
|
13146
|
-
if (stopBIndex === undefined) {
|
|
13147
|
-
throw new Error(`Stop index not found for ${stopB} in route ${this.serviceRouteId}`);
|
|
13148
|
-
}
|
|
13149
|
-
return stopAIndex < stopBIndex;
|
|
13150
|
-
}
|
|
13151
13158
|
/**
|
|
13152
13159
|
* Retrieves the number of stops in the route.
|
|
13153
13160
|
*
|
|
@@ -13176,47 +13183,47 @@ class Route {
|
|
|
13176
13183
|
/**
|
|
13177
13184
|
* Retrieves the arrival time at a specific stop for a given trip.
|
|
13178
13185
|
*
|
|
13179
|
-
* @param
|
|
13186
|
+
* @param stopIndex - The index of the stop in the route.
|
|
13180
13187
|
* @param tripIndex - The index of the trip.
|
|
13181
13188
|
* @returns The arrival time at the specified stop and trip as a Time object.
|
|
13182
13189
|
*/
|
|
13183
|
-
arrivalAt(
|
|
13184
|
-
const arrivalIndex = (tripIndex * this.stops.length +
|
|
13190
|
+
arrivalAt(stopIndex, tripIndex) {
|
|
13191
|
+
const arrivalIndex = (tripIndex * this.stops.length + stopIndex) * 2;
|
|
13185
13192
|
const arrival = this.stopTimes[arrivalIndex];
|
|
13186
13193
|
if (arrival === undefined) {
|
|
13187
|
-
throw new Error(`Arrival time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13194
|
+
throw new Error(`Arrival time not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13188
13195
|
}
|
|
13189
13196
|
return Time.fromMinutes(arrival);
|
|
13190
13197
|
}
|
|
13191
13198
|
/**
|
|
13192
13199
|
* Retrieves the departure time at a specific stop for a given trip.
|
|
13193
13200
|
*
|
|
13194
|
-
* @param
|
|
13201
|
+
* @param stopIndex - The index of the stop in the route.
|
|
13195
13202
|
* @param tripIndex - The index of the trip.
|
|
13196
13203
|
* @returns The departure time at the specified stop and trip as a Time object.
|
|
13197
13204
|
*/
|
|
13198
|
-
departureFrom(
|
|
13199
|
-
const departureIndex = (tripIndex * this.stops.length +
|
|
13205
|
+
departureFrom(stopIndex, tripIndex) {
|
|
13206
|
+
const departureIndex = (tripIndex * this.stops.length + stopIndex) * 2 + 1;
|
|
13200
13207
|
const departure = this.stopTimes[departureIndex];
|
|
13201
13208
|
if (departure === undefined) {
|
|
13202
|
-
throw new Error(`Departure time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13209
|
+
throw new Error(`Departure time not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13203
13210
|
}
|
|
13204
13211
|
return Time.fromMinutes(departure);
|
|
13205
13212
|
}
|
|
13206
13213
|
/**
|
|
13207
13214
|
* Retrieves the pick-up type for a specific stop and trip.
|
|
13208
13215
|
*
|
|
13209
|
-
* @param
|
|
13216
|
+
* @param stopIndex - The index of the stop in the route.
|
|
13210
13217
|
* @param tripIndex - The index of the trip.
|
|
13211
13218
|
* @returns The pick-up type at the specified stop and trip.
|
|
13212
13219
|
*/
|
|
13213
|
-
pickUpTypeFrom(
|
|
13214
|
-
const globalIndex = tripIndex * this.stops.length +
|
|
13220
|
+
pickUpTypeFrom(stopIndex, tripIndex) {
|
|
13221
|
+
const globalIndex = tripIndex * this.stops.length + stopIndex;
|
|
13215
13222
|
const byteIndex = Math.floor(globalIndex / 2);
|
|
13216
13223
|
const isSecondPair = globalIndex % 2 === 1;
|
|
13217
13224
|
const byte = this.pickUpDropOffTypes[byteIndex];
|
|
13218
13225
|
if (byte === undefined) {
|
|
13219
|
-
throw new Error(`Pick up type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13226
|
+
throw new Error(`Pick up type not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13220
13227
|
}
|
|
13221
13228
|
const pickUpValue = isSecondPair
|
|
13222
13229
|
? (byte >> 6) & 0x03 // Upper 2 bits for second pair
|
|
@@ -13226,17 +13233,17 @@ class Route {
|
|
|
13226
13233
|
/**
|
|
13227
13234
|
* Retrieves the drop-off type for a specific stop and trip.
|
|
13228
13235
|
*
|
|
13229
|
-
* @param
|
|
13236
|
+
* @param stopIndex - The index of the stop in the route.
|
|
13230
13237
|
* @param tripIndex - The index of the trip.
|
|
13231
13238
|
* @returns The drop-off type at the specified stop and trip.
|
|
13232
13239
|
*/
|
|
13233
|
-
dropOffTypeAt(
|
|
13234
|
-
const globalIndex = tripIndex * this.stops.length +
|
|
13240
|
+
dropOffTypeAt(stopIndex, tripIndex) {
|
|
13241
|
+
const globalIndex = tripIndex * this.stops.length + stopIndex;
|
|
13235
13242
|
const byteIndex = Math.floor(globalIndex / 2);
|
|
13236
13243
|
const isSecondPair = globalIndex % 2 === 1;
|
|
13237
13244
|
const byte = this.pickUpDropOffTypes[byteIndex];
|
|
13238
13245
|
if (byte === undefined) {
|
|
13239
|
-
throw new Error(`Drop off type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13246
|
+
throw new Error(`Drop off type not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13240
13247
|
}
|
|
13241
13248
|
const dropOffValue = isSecondPair
|
|
13242
13249
|
? (byte >> 4) & 0x03 // Bits 4-5 for second pair
|
|
@@ -13248,14 +13255,14 @@ class Route {
|
|
|
13248
13255
|
* optionally constrained by a latest trip index and a time before which the trip
|
|
13249
13256
|
* should not depart.
|
|
13250
13257
|
* *
|
|
13251
|
-
* @param
|
|
13258
|
+
* @param stopIndex - The route index of the stop where the trip should be found.
|
|
13252
13259
|
* @param [after=Time.origin()] - The earliest time after which the trip should depart.
|
|
13253
13260
|
* If not provided, searches all available trips.
|
|
13254
13261
|
* @param [beforeTrip] - (Optional) The index of the trip before which the search should be constrained.
|
|
13255
13262
|
* If not provided, searches all available trips.
|
|
13256
13263
|
* @returns The index of the earliest trip meeting the criteria, or undefined if no such trip is found.
|
|
13257
13264
|
*/
|
|
13258
|
-
findEarliestTrip(
|
|
13265
|
+
findEarliestTrip(stopIndex, after = Time.origin(), beforeTrip) {
|
|
13259
13266
|
if (this.nbTrips <= 0)
|
|
13260
13267
|
return undefined;
|
|
13261
13268
|
let hi = this.nbTrips - 1;
|
|
@@ -13267,7 +13274,7 @@ class Route {
|
|
|
13267
13274
|
let lb = -1;
|
|
13268
13275
|
while (lo <= hi) {
|
|
13269
13276
|
const mid = (lo + hi) >>> 1;
|
|
13270
|
-
const depMid = this.departureFrom(
|
|
13277
|
+
const depMid = this.departureFrom(stopIndex, mid);
|
|
13271
13278
|
if (depMid.isBefore(after)) {
|
|
13272
13279
|
lo = mid + 1;
|
|
13273
13280
|
}
|
|
@@ -13278,8 +13285,8 @@ class Route {
|
|
|
13278
13285
|
}
|
|
13279
13286
|
if (lb === -1)
|
|
13280
13287
|
return undefined;
|
|
13281
|
-
for (let t = lb; t < (beforeTrip
|
|
13282
|
-
const pickup = this.pickUpTypeFrom(
|
|
13288
|
+
for (let t = lb; t < (beforeTrip ?? this.nbTrips); t++) {
|
|
13289
|
+
const pickup = this.pickUpTypeFrom(stopIndex, t);
|
|
13283
13290
|
if (pickup !== 'NOT_AVAILABLE') {
|
|
13284
13291
|
return t;
|
|
13285
13292
|
}
|
|
@@ -13287,14 +13294,14 @@ class Route {
|
|
|
13287
13294
|
return undefined;
|
|
13288
13295
|
}
|
|
13289
13296
|
/**
|
|
13290
|
-
* Retrieves the
|
|
13297
|
+
* Retrieves the indices of a stop within the route.
|
|
13291
13298
|
* @param stopId The StopId of the stop to locate in the route.
|
|
13292
|
-
* @returns
|
|
13299
|
+
* @returns An array of indices where the stop appears in the route, or an empty array if the stop is not found.
|
|
13293
13300
|
*/
|
|
13294
|
-
|
|
13301
|
+
stopRouteIndices(stopId) {
|
|
13295
13302
|
const stopIndex = this.stopIndices.get(stopId);
|
|
13296
13303
|
if (stopIndex === undefined) {
|
|
13297
|
-
|
|
13304
|
+
return [];
|
|
13298
13305
|
}
|
|
13299
13306
|
return stopIndex;
|
|
13300
13307
|
}
|
|
@@ -13312,6 +13319,51 @@ class Route {
|
|
|
13312
13319
|
}
|
|
13313
13320
|
}
|
|
13314
13321
|
|
|
13322
|
+
// Each value uses 20 bits, allowing values from 0 to 1,048,575 (2^20 - 1)
|
|
13323
|
+
const VALUE_MASK = (1n << 20n) - 1n; // 0xFFFFF
|
|
13324
|
+
const MAX_VALUE = 1_048_575; // 2^20 - 1
|
|
13325
|
+
// Bit positions for each value in the 60-bit bigint
|
|
13326
|
+
const TRIP_INDEX_SHIFT = 0n;
|
|
13327
|
+
const ROUTE_ID_SHIFT = 20n;
|
|
13328
|
+
const STOP_INDEX_SHIFT = 40n;
|
|
13329
|
+
/**
|
|
13330
|
+
* Validates that a value fits within 20 bits (0 to 1,048,575)
|
|
13331
|
+
* @param value - The value to validate
|
|
13332
|
+
* @param name - The name of the value for error reporting
|
|
13333
|
+
* @throws Error if the value is out of range
|
|
13334
|
+
*/
|
|
13335
|
+
const validateValue = (value, name) => {
|
|
13336
|
+
if (value < 0 || value > MAX_VALUE) {
|
|
13337
|
+
throw new Error(`${name} must be between 0 and ${MAX_VALUE}, got ${value}`);
|
|
13338
|
+
}
|
|
13339
|
+
};
|
|
13340
|
+
/**
|
|
13341
|
+
* Encodes a stop index, route ID, and trip index into a single trip boarding ID.
|
|
13342
|
+
* @param stopIndex - The index of the stop within the route (0 to 1,048,575)
|
|
13343
|
+
* @param routeId - The route identifier (0 to 1,048,575)
|
|
13344
|
+
* @param tripIndex - The index of the trip within the route (0 to 1,048,575)
|
|
13345
|
+
* @returns The encoded trip ID as a bigint
|
|
13346
|
+
*/
|
|
13347
|
+
const encode = (stopIndex, routeId, tripIndex) => {
|
|
13348
|
+
validateValue(stopIndex, 'stopIndex');
|
|
13349
|
+
validateValue(routeId, 'routeId');
|
|
13350
|
+
validateValue(tripIndex, 'tripIndex');
|
|
13351
|
+
return ((BigInt(stopIndex) << STOP_INDEX_SHIFT) |
|
|
13352
|
+
(BigInt(routeId) << ROUTE_ID_SHIFT) |
|
|
13353
|
+
(BigInt(tripIndex) << TRIP_INDEX_SHIFT));
|
|
13354
|
+
};
|
|
13355
|
+
/**
|
|
13356
|
+
* Decodes a trip boarding ID back into its constituent stop index, route ID, and trip index.
|
|
13357
|
+
* @param tripBoardingId - The encoded trip ID
|
|
13358
|
+
* @returns A tuple containing [stopIndex, routeId, tripIndex]
|
|
13359
|
+
*/
|
|
13360
|
+
const decode = (tripBoardingId) => {
|
|
13361
|
+
const stopIndex = Number((tripBoardingId >> STOP_INDEX_SHIFT) & VALUE_MASK);
|
|
13362
|
+
const routeId = Number((tripBoardingId >> ROUTE_ID_SHIFT) & VALUE_MASK);
|
|
13363
|
+
const tripIndex = Number((tripBoardingId >> TRIP_INDEX_SHIFT) & VALUE_MASK);
|
|
13364
|
+
return [stopIndex, routeId, tripIndex];
|
|
13365
|
+
};
|
|
13366
|
+
|
|
13315
13367
|
const isLittleEndian = (() => {
|
|
13316
13368
|
const buffer = new ArrayBuffer(4);
|
|
13317
13369
|
const view = new DataView(buffer);
|
|
@@ -13381,14 +13433,15 @@ const serializeStopsAdjacency = (stopsAdjacency) => {
|
|
|
13381
13433
|
return stopsAdjacency.map((value) => {
|
|
13382
13434
|
return {
|
|
13383
13435
|
transfers: value.transfers
|
|
13384
|
-
? value.transfers.map((transfer) => (
|
|
13385
|
-
|
|
13386
|
-
|
|
13436
|
+
? value.transfers.map((transfer) => ({
|
|
13437
|
+
destination: transfer.destination,
|
|
13438
|
+
type: serializeTransferType(transfer.type),
|
|
13439
|
+
...(transfer.minTransferTime !== undefined && {
|
|
13440
|
+
minTransferTime: transfer.minTransferTime.toSeconds(),
|
|
13441
|
+
}),
|
|
13442
|
+
}))
|
|
13387
13443
|
: [],
|
|
13388
13444
|
routes: value.routes,
|
|
13389
|
-
tripContinuations: value.tripContinuations
|
|
13390
|
-
? serializeTripContinuations(value.tripContinuations)
|
|
13391
|
-
: [],
|
|
13392
13445
|
};
|
|
13393
13446
|
});
|
|
13394
13447
|
};
|
|
@@ -13423,9 +13476,13 @@ const deserializeStopsAdjacency = (protoStopsAdjacency) => {
|
|
|
13423
13476
|
for (let j = 0; j < value.transfers.length; j++) {
|
|
13424
13477
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
13425
13478
|
const transfer = value.transfers[j];
|
|
13426
|
-
const newTransfer =
|
|
13427
|
-
|
|
13428
|
-
|
|
13479
|
+
const newTransfer = {
|
|
13480
|
+
destination: transfer.destination,
|
|
13481
|
+
type: parseTransferType(transfer.type),
|
|
13482
|
+
...(transfer.minTransferTime !== undefined && {
|
|
13483
|
+
minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
|
|
13484
|
+
}),
|
|
13485
|
+
};
|
|
13429
13486
|
transfers.push(newTransfer);
|
|
13430
13487
|
}
|
|
13431
13488
|
const stopAdjacency = {
|
|
@@ -13434,10 +13491,6 @@ const deserializeStopsAdjacency = (protoStopsAdjacency) => {
|
|
|
13434
13491
|
if (transfers.length > 0) {
|
|
13435
13492
|
stopAdjacency.transfers = transfers;
|
|
13436
13493
|
}
|
|
13437
|
-
const deserializedTripContinuations = deserializeTripContinuations(value.tripContinuations);
|
|
13438
|
-
if (deserializedTripContinuations.size > 0) {
|
|
13439
|
-
stopAdjacency.tripContinuations = deserializedTripContinuations;
|
|
13440
|
-
}
|
|
13441
13494
|
result.push(stopAdjacency);
|
|
13442
13495
|
}
|
|
13443
13496
|
return result;
|
|
@@ -13544,11 +13597,14 @@ const serializeRouteType = (type) => {
|
|
|
13544
13597
|
};
|
|
13545
13598
|
const serializeTripContinuations = (tripContinuations) => {
|
|
13546
13599
|
const result = [];
|
|
13547
|
-
for (const [
|
|
13600
|
+
for (const [tripBoardingId, boardings] of tripContinuations.entries()) {
|
|
13601
|
+
const [originStopIndex, originRouteId, originTripIndex] = decode(tripBoardingId);
|
|
13548
13602
|
result.push({
|
|
13549
|
-
|
|
13550
|
-
|
|
13551
|
-
|
|
13603
|
+
originStopIndex,
|
|
13604
|
+
originRouteId,
|
|
13605
|
+
originTripIndex,
|
|
13606
|
+
continuations: boardings.map((tripBoarding) => ({
|
|
13607
|
+
hopOnStopIndex: tripBoarding.hopOnStopIndex,
|
|
13552
13608
|
routeId: tripBoarding.routeId,
|
|
13553
13609
|
tripIndex: tripBoarding.tripIndex,
|
|
13554
13610
|
})),
|
|
@@ -13561,28 +13617,17 @@ const deserializeTripContinuations = (protoTripContinuations) => {
|
|
|
13561
13617
|
for (let i = 0; i < protoTripContinuations.length; i++) {
|
|
13562
13618
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
13563
13619
|
const entry = protoTripContinuations[i];
|
|
13564
|
-
const
|
|
13565
|
-
|
|
13620
|
+
const tripBoardingId = encode(entry.originStopIndex, entry.originRouteId, entry.originTripIndex);
|
|
13621
|
+
const tripBoardings = entry.continuations.map((protoTripBoarding) => ({
|
|
13622
|
+
hopOnStopIndex: protoTripBoarding.hopOnStopIndex,
|
|
13566
13623
|
routeId: protoTripBoarding.routeId,
|
|
13567
13624
|
tripIndex: protoTripBoarding.tripIndex,
|
|
13568
13625
|
}));
|
|
13569
|
-
result.set(
|
|
13626
|
+
result.set(tripBoardingId, tripBoardings);
|
|
13570
13627
|
}
|
|
13571
13628
|
return result;
|
|
13572
13629
|
};
|
|
13573
13630
|
|
|
13574
|
-
// const ROUTE_ID_BITS = 17;
|
|
13575
|
-
const TRIP_INDEX_BITS = 15;
|
|
13576
|
-
/**
|
|
13577
|
-
* Encodes a route ID and trip index into a single trip ID.
|
|
13578
|
-
* @param routeId - The route identifier, needs to fit on 17 bits
|
|
13579
|
-
* @param tripIndex - The index of the trip within the route, needs to fit on 15 bits
|
|
13580
|
-
* @returns The encoded trip ID
|
|
13581
|
-
*/
|
|
13582
|
-
const encode = (routeId, tripIndex) => {
|
|
13583
|
-
return (routeId << TRIP_INDEX_BITS) | tripIndex;
|
|
13584
|
-
};
|
|
13585
|
-
|
|
13586
13631
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
13587
13632
|
const ALL_TRANSPORT_MODES = new Set([
|
|
13588
13633
|
'TRAM',
|
|
@@ -13597,21 +13642,26 @@ const ALL_TRANSPORT_MODES = new Set([
|
|
|
13597
13642
|
'MONORAIL',
|
|
13598
13643
|
]);
|
|
13599
13644
|
const EMPTY_TRIP_CONTINUATIONS = [];
|
|
13600
|
-
const CURRENT_VERSION = '0.0.
|
|
13645
|
+
const CURRENT_VERSION = '0.0.9';
|
|
13601
13646
|
/**
|
|
13602
13647
|
* The internal transit timetable format.
|
|
13603
13648
|
*/
|
|
13604
13649
|
class Timetable {
|
|
13605
|
-
|
|
13650
|
+
stopsAdjacency;
|
|
13651
|
+
routesAdjacency;
|
|
13652
|
+
serviceRoutes;
|
|
13653
|
+
tripContinuations;
|
|
13654
|
+
activeStops;
|
|
13655
|
+
constructor(stopsAdjacency, routesAdjacency, routes, tripContinuations) {
|
|
13606
13656
|
this.stopsAdjacency = stopsAdjacency;
|
|
13607
13657
|
this.routesAdjacency = routesAdjacency;
|
|
13608
13658
|
this.serviceRoutes = routes;
|
|
13659
|
+
this.tripContinuations = tripContinuations;
|
|
13609
13660
|
this.activeStops = new Set();
|
|
13610
13661
|
for (let i = 0; i < stopsAdjacency.length; i++) {
|
|
13611
13662
|
const stop = stopsAdjacency[i];
|
|
13612
13663
|
if (stop.routes.length > 0 ||
|
|
13613
|
-
(stop.transfers && stop.transfers.length > 0)
|
|
13614
|
-
(stop.tripContinuations && stop.tripContinuations.size > 0)) {
|
|
13664
|
+
(stop.transfers && stop.transfers.length > 0)) {
|
|
13615
13665
|
this.activeStops.add(i);
|
|
13616
13666
|
}
|
|
13617
13667
|
}
|
|
@@ -13627,6 +13677,7 @@ class Timetable {
|
|
|
13627
13677
|
stopsAdjacency: serializeStopsAdjacency(this.stopsAdjacency),
|
|
13628
13678
|
routesAdjacency: serializeRoutesAdjacency(this.routesAdjacency),
|
|
13629
13679
|
serviceRoutes: serializeServiceRoutesMap(this.serviceRoutes),
|
|
13680
|
+
tripContinuations: serializeTripContinuations(this.tripContinuations || new Map()),
|
|
13630
13681
|
};
|
|
13631
13682
|
const writer = new BinaryWriter();
|
|
13632
13683
|
Timetable$1.encode(protoTimetable, writer);
|
|
@@ -13644,7 +13695,7 @@ class Timetable {
|
|
|
13644
13695
|
if (protoTimetable.version !== CURRENT_VERSION) {
|
|
13645
13696
|
throw new Error(`Unsupported timetable version ${protoTimetable.version}`);
|
|
13646
13697
|
}
|
|
13647
|
-
return new Timetable(deserializeStopsAdjacency(protoTimetable.stopsAdjacency), deserializeRoutesAdjacency(protoTimetable.routesAdjacency), deserializeServiceRoutesMap(protoTimetable.serviceRoutes));
|
|
13698
|
+
return new Timetable(deserializeStopsAdjacency(protoTimetable.stopsAdjacency), deserializeRoutesAdjacency(protoTimetable.routesAdjacency), deserializeServiceRoutesMap(protoTimetable.serviceRoutes), deserializeTripContinuations(protoTimetable.tripContinuations));
|
|
13648
13699
|
}
|
|
13649
13700
|
/**
|
|
13650
13701
|
* Checks if the given stop is active on the timetable.
|
|
@@ -13683,18 +13734,17 @@ class Timetable {
|
|
|
13683
13734
|
/**
|
|
13684
13735
|
* Retrieves all trip continuation options available at the specified stop for a given trip.
|
|
13685
13736
|
*
|
|
13686
|
-
* @param
|
|
13737
|
+
* @param stopIndex - The index in the route of the stop to get trip continuations for.
|
|
13738
|
+
* @param routeId - The ID of the route to get continuations for.
|
|
13687
13739
|
* @param tripIndex - The index of the trip to get continuations for.
|
|
13688
13740
|
* @returns An array of trip continuation options available at the stop for the specified trip.
|
|
13689
13741
|
*/
|
|
13690
|
-
getContinuousTrips(
|
|
13691
|
-
|
|
13692
|
-
|
|
13693
|
-
|
|
13694
|
-
throw new Error(`Stop ID ${stopId} not found`);
|
|
13742
|
+
getContinuousTrips(stopIndex, routeId, tripIndex) {
|
|
13743
|
+
const tripContinuations = this.tripContinuations?.get(encode(stopIndex, routeId, tripIndex));
|
|
13744
|
+
if (!tripContinuations) {
|
|
13745
|
+
return EMPTY_TRIP_CONTINUATIONS;
|
|
13695
13746
|
}
|
|
13696
|
-
return
|
|
13697
|
-
EMPTY_TRIP_CONTINUATIONS);
|
|
13747
|
+
return tripContinuations;
|
|
13698
13748
|
}
|
|
13699
13749
|
/**
|
|
13700
13750
|
* Retrieves the service route associated with the given route.
|
|
@@ -13734,12 +13784,12 @@ class Timetable {
|
|
|
13734
13784
|
}
|
|
13735
13785
|
/**
|
|
13736
13786
|
* Finds routes that are reachable from a set of stop IDs.
|
|
13737
|
-
* Also identifies the first stop available to hop on each route among
|
|
13787
|
+
* Also identifies the first stop index available to hop on each route among
|
|
13738
13788
|
* the input stops.
|
|
13739
13789
|
*
|
|
13740
13790
|
* @param fromStops - The set of stop IDs to find reachable routes from.
|
|
13741
13791
|
* @param transportModes - The set of transport modes to consider for reachable routes.
|
|
13742
|
-
* @returns A map of reachable routes to the first stop available to hop on each route.
|
|
13792
|
+
* @returns A map of reachable routes to the first stop index available to hop on each route.
|
|
13743
13793
|
*/
|
|
13744
13794
|
findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
|
|
13745
13795
|
const reachableRoutes = new Map();
|
|
@@ -13752,15 +13802,17 @@ class Timetable {
|
|
|
13752
13802
|
});
|
|
13753
13803
|
for (let j = 0; j < validRoutes.length; j++) {
|
|
13754
13804
|
const route = validRoutes[j];
|
|
13755
|
-
const
|
|
13756
|
-
|
|
13757
|
-
|
|
13805
|
+
const originStopIndices = route.stopRouteIndices(originStop);
|
|
13806
|
+
const originStopIndex = originStopIndices[0];
|
|
13807
|
+
const existingHopOnStopIndex = reachableRoutes.get(route);
|
|
13808
|
+
if (existingHopOnStopIndex !== undefined) {
|
|
13809
|
+
if (originStopIndex < existingHopOnStopIndex) {
|
|
13758
13810
|
// if the current stop is before the existing hop on stop, replace it
|
|
13759
|
-
reachableRoutes.set(route,
|
|
13811
|
+
reachableRoutes.set(route, originStopIndex);
|
|
13760
13812
|
}
|
|
13761
13813
|
}
|
|
13762
13814
|
else {
|
|
13763
|
-
reachableRoutes.set(route,
|
|
13815
|
+
reachableRoutes.set(route, originStopIndex);
|
|
13764
13816
|
}
|
|
13765
13817
|
}
|
|
13766
13818
|
}
|
|
@@ -15736,35 +15788,22 @@ const parseCsv = (stream, numericColumns = []) => {
|
|
|
15736
15788
|
* @param profile A configuration object defining the specificities of the GTFS feed.
|
|
15737
15789
|
* @returns A map of all the valid routes.
|
|
15738
15790
|
*/
|
|
15739
|
-
const parseRoutes =
|
|
15740
|
-
var _a, e_1, _b, _c;
|
|
15791
|
+
const parseRoutes = async (routesStream, profile = standardProfile) => {
|
|
15741
15792
|
const routes = new Map();
|
|
15742
|
-
|
|
15743
|
-
|
|
15744
|
-
|
|
15745
|
-
|
|
15746
|
-
|
|
15747
|
-
|
|
15748
|
-
const routeType = profile.routeTypeParser(line.route_type);
|
|
15749
|
-
if (routeType === undefined) {
|
|
15750
|
-
log.info(`Unsupported route type ${line.route_type} for route ${line.route_id}.`);
|
|
15751
|
-
continue;
|
|
15752
|
-
}
|
|
15753
|
-
routes.set(line.route_id, {
|
|
15754
|
-
name: line.route_short_name,
|
|
15755
|
-
type: routeType,
|
|
15756
|
-
});
|
|
15757
|
-
}
|
|
15758
|
-
}
|
|
15759
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
15760
|
-
finally {
|
|
15761
|
-
try {
|
|
15762
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
15793
|
+
for await (const rawLine of parseCsv(routesStream, ['route_type'])) {
|
|
15794
|
+
const line = rawLine;
|
|
15795
|
+
const routeType = profile.routeTypeParser(line.route_type);
|
|
15796
|
+
if (routeType === undefined) {
|
|
15797
|
+
log.info(`Unsupported route type ${line.route_type} for route ${line.route_id}.`);
|
|
15798
|
+
continue;
|
|
15763
15799
|
}
|
|
15764
|
-
|
|
15800
|
+
routes.set(line.route_id, {
|
|
15801
|
+
name: line.route_short_name,
|
|
15802
|
+
type: routeType,
|
|
15803
|
+
});
|
|
15765
15804
|
}
|
|
15766
15805
|
return routes;
|
|
15767
|
-
}
|
|
15806
|
+
};
|
|
15768
15807
|
/**
|
|
15769
15808
|
* Creates an array of ServiceRoute objects by combining GTFS route data with service route mappings.
|
|
15770
15809
|
*
|
|
@@ -15827,46 +15866,33 @@ const weekdays = {
|
|
|
15827
15866
|
* @param date The active date.
|
|
15828
15867
|
* @param calendarStream A readable stream for the GTFS calendar.txt file.
|
|
15829
15868
|
*/
|
|
15830
|
-
const parseCalendar = (calendarStream, serviceIds, date) =>
|
|
15831
|
-
var _a, e_1, _b, _c;
|
|
15869
|
+
const parseCalendar = async (calendarStream, serviceIds, date) => {
|
|
15832
15870
|
const activeDate = toGtfsDate(date);
|
|
15833
15871
|
const weekday = date.weekday;
|
|
15834
15872
|
const weekdayIndex = weekdays[weekday];
|
|
15835
|
-
|
|
15836
|
-
|
|
15837
|
-
|
|
15838
|
-
|
|
15839
|
-
|
|
15840
|
-
|
|
15841
|
-
|
|
15842
|
-
|
|
15843
|
-
|
|
15844
|
-
|
|
15845
|
-
|
|
15846
|
-
|
|
15847
|
-
|
|
15848
|
-
|
|
15849
|
-
|
|
15850
|
-
const line = rawLine;
|
|
15851
|
-
if (activeDate < line.start_date || activeDate > line.end_date) {
|
|
15852
|
-
// If the service is not valid on this date
|
|
15853
|
-
continue;
|
|
15854
|
-
}
|
|
15855
|
-
if (line[weekdayIndex] !== 1) {
|
|
15856
|
-
// If the service is not valid on this week day
|
|
15857
|
-
continue;
|
|
15858
|
-
}
|
|
15859
|
-
serviceIds.add(line['service_id']);
|
|
15873
|
+
for await (const rawLine of parseCsv(calendarStream, [
|
|
15874
|
+
'monday',
|
|
15875
|
+
'tuesday',
|
|
15876
|
+
'wednesday',
|
|
15877
|
+
'thursday',
|
|
15878
|
+
'friday',
|
|
15879
|
+
'saturday',
|
|
15880
|
+
'sunday',
|
|
15881
|
+
'start_date',
|
|
15882
|
+
'end_date',
|
|
15883
|
+
])) {
|
|
15884
|
+
const line = rawLine;
|
|
15885
|
+
if (activeDate < line.start_date || activeDate > line.end_date) {
|
|
15886
|
+
// If the service is not valid on this date
|
|
15887
|
+
continue;
|
|
15860
15888
|
}
|
|
15861
|
-
|
|
15862
|
-
|
|
15863
|
-
|
|
15864
|
-
try {
|
|
15865
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
15889
|
+
if (line[weekdayIndex] !== 1) {
|
|
15890
|
+
// If the service is not valid on this week day
|
|
15891
|
+
continue;
|
|
15866
15892
|
}
|
|
15867
|
-
|
|
15893
|
+
serviceIds.add(line['service_id']);
|
|
15868
15894
|
}
|
|
15869
|
-
}
|
|
15895
|
+
};
|
|
15870
15896
|
/**
|
|
15871
15897
|
* Parses a gtfs calendar_dates.txt file and finds the service ids valid at a given date.
|
|
15872
15898
|
*
|
|
@@ -15874,39 +15900,24 @@ const parseCalendar = (calendarStream, serviceIds, date) => __awaiter(void 0, vo
|
|
|
15874
15900
|
* @param date The active date, in the format "YYYYMMDD".
|
|
15875
15901
|
* @param calendarDatesStream A readable stream for the GTFS calendar_dates.txt file.
|
|
15876
15902
|
*/
|
|
15877
|
-
const parseCalendarDates = (calendarDatesStream, serviceIds, date) =>
|
|
15878
|
-
var _a, e_2, _b, _c;
|
|
15903
|
+
const parseCalendarDates = async (calendarDatesStream, serviceIds, date) => {
|
|
15879
15904
|
const activeDate = toGtfsDate(date);
|
|
15880
|
-
|
|
15881
|
-
|
|
15882
|
-
|
|
15883
|
-
|
|
15884
|
-
|
|
15885
|
-
|
|
15886
|
-
|
|
15887
|
-
|
|
15888
|
-
|
|
15889
|
-
if (line.date !== activeDate) {
|
|
15890
|
-
// No rule on the active date
|
|
15891
|
-
}
|
|
15892
|
-
else if (line.exception_type === 2 && serviceIds.has(line.service_id)) {
|
|
15893
|
-
// Service has been removed for the specified date.
|
|
15894
|
-
serviceIds.delete(line.service_id);
|
|
15895
|
-
}
|
|
15896
|
-
else if (line.exception_type === 1) {
|
|
15897
|
-
// Service is present on the active date
|
|
15898
|
-
serviceIds.add(line.service_id);
|
|
15899
|
-
}
|
|
15905
|
+
for await (const rawLine of parseCsv(calendarDatesStream, [
|
|
15906
|
+
'date',
|
|
15907
|
+
'exception_type',
|
|
15908
|
+
])) {
|
|
15909
|
+
const line = rawLine;
|
|
15910
|
+
if (line.date !== activeDate) ;
|
|
15911
|
+
else if (line.exception_type === 2 && serviceIds.has(line.service_id)) {
|
|
15912
|
+
// Service has been removed for the specified date.
|
|
15913
|
+
serviceIds.delete(line.service_id);
|
|
15900
15914
|
}
|
|
15901
|
-
|
|
15902
|
-
|
|
15903
|
-
|
|
15904
|
-
try {
|
|
15905
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
15915
|
+
else if (line.exception_type === 1) {
|
|
15916
|
+
// Service is present on the active date
|
|
15917
|
+
serviceIds.add(line.service_id);
|
|
15906
15918
|
}
|
|
15907
|
-
finally { if (e_2) throw e_2.error; }
|
|
15908
15919
|
}
|
|
15909
|
-
}
|
|
15920
|
+
};
|
|
15910
15921
|
|
|
15911
15922
|
/**
|
|
15912
15923
|
* Parses the stops.txt file from a GTFS feed.
|
|
@@ -15914,33 +15925,30 @@ const parseCalendarDates = (calendarDatesStream, serviceIds, date) => __awaiter(
|
|
|
15914
15925
|
* @param stopsStream The readable stream containing the stops data.
|
|
15915
15926
|
* @return A mapping of stop IDs to corresponding stop details.
|
|
15916
15927
|
*/
|
|
15917
|
-
const parseStops = (stopsStream) =>
|
|
15918
|
-
var _a, e_1, _b, _c;
|
|
15928
|
+
const parseStops = async (stopsStream) => {
|
|
15919
15929
|
const parsedStops = new Map();
|
|
15920
15930
|
let i = 0;
|
|
15921
|
-
|
|
15922
|
-
|
|
15923
|
-
|
|
15924
|
-
|
|
15925
|
-
|
|
15926
|
-
|
|
15927
|
-
|
|
15928
|
-
|
|
15929
|
-
|
|
15930
|
-
|
|
15931
|
-
|
|
15932
|
-
|
|
15933
|
-
|
|
15934
|
-
|
|
15935
|
-
|
|
15936
|
-
|
|
15937
|
-
|
|
15938
|
-
|
|
15939
|
-
|
|
15940
|
-
|
|
15941
|
-
|
|
15942
|
-
}
|
|
15943
|
-
finally { if (e_1) throw e_1.error; }
|
|
15931
|
+
for await (const rawLine of parseCsv(stopsStream, [
|
|
15932
|
+
'stop_lat',
|
|
15933
|
+
'stop_lon',
|
|
15934
|
+
'location_type',
|
|
15935
|
+
])) {
|
|
15936
|
+
const line = rawLine;
|
|
15937
|
+
const stop = {
|
|
15938
|
+
id: i,
|
|
15939
|
+
sourceStopId: line.stop_id,
|
|
15940
|
+
name: line.stop_name,
|
|
15941
|
+
lat: line.stop_lat,
|
|
15942
|
+
lon: line.stop_lon,
|
|
15943
|
+
locationType: line.location_type
|
|
15944
|
+
? parseGtfsLocationType(line.location_type)
|
|
15945
|
+
: 'SIMPLE_STOP_OR_PLATFORM',
|
|
15946
|
+
...(line.platform_code && { platform: line.platform_code }),
|
|
15947
|
+
children: [],
|
|
15948
|
+
...(line.parent_station && { parentSourceId: line.parent_station }),
|
|
15949
|
+
};
|
|
15950
|
+
parsedStops.set(line.stop_id, stop);
|
|
15951
|
+
i = i + 1;
|
|
15944
15952
|
}
|
|
15945
15953
|
for (const [sourceStopId, stop] of parsedStops) {
|
|
15946
15954
|
if (stop.parentSourceId) {
|
|
@@ -15954,7 +15962,7 @@ const parseStops = (stopsStream) => __awaiter(void 0, void 0, void 0, function*
|
|
|
15954
15962
|
}
|
|
15955
15963
|
}
|
|
15956
15964
|
return parsedStops;
|
|
15957
|
-
}
|
|
15965
|
+
};
|
|
15958
15966
|
const parseGtfsLocationType = (gtfsLocationType) => {
|
|
15959
15967
|
switch (gtfsLocationType) {
|
|
15960
15968
|
case 0:
|
|
@@ -15977,85 +15985,158 @@ const parseGtfsLocationType = (gtfsLocationType) => {
|
|
|
15977
15985
|
* @param stopsStream The readable stream containing the stops data.
|
|
15978
15986
|
* @return A mapping of stop IDs to corresponding stop details.
|
|
15979
15987
|
*/
|
|
15980
|
-
const parseTransfers = (transfersStream, stopsMap) =>
|
|
15981
|
-
var _a, e_1, _b, _c;
|
|
15988
|
+
const parseTransfers = async (transfersStream, stopsMap) => {
|
|
15982
15989
|
const transfers = new Map();
|
|
15983
|
-
const tripContinuations =
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
15993
|
-
|
|
15994
|
-
|
|
15995
|
-
|
|
15996
|
-
|
|
15997
|
-
|
|
15998
|
-
|
|
15999
|
-
|
|
16000
|
-
}
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
|
|
16006
|
-
|
|
16007
|
-
|
|
16008
|
-
|
|
16009
|
-
transferEntry.to_trip_id === '') {
|
|
16010
|
-
console.warn(`Unsupported in-seat transfer, missing from_trip_id and/or to_trip_id.`);
|
|
16011
|
-
continue;
|
|
16012
|
-
}
|
|
16013
|
-
const tripBoardingEntry = {
|
|
16014
|
-
fromTrip: transferEntry.from_trip_id,
|
|
16015
|
-
toTrip: transferEntry.to_trip_id,
|
|
16016
|
-
hopOnStop: toStop.id,
|
|
16017
|
-
};
|
|
16018
|
-
const existingBoardings = tripContinuations.get(fromStop.id);
|
|
16019
|
-
if (existingBoardings) {
|
|
16020
|
-
existingBoardings.push(tripBoardingEntry);
|
|
16021
|
-
}
|
|
16022
|
-
else {
|
|
16023
|
-
tripContinuations.set(fromStop.id, [tripBoardingEntry]);
|
|
16024
|
-
}
|
|
16025
|
-
continue;
|
|
16026
|
-
}
|
|
16027
|
-
if (transferEntry.from_trip_id && transferEntry.to_trip_id) {
|
|
16028
|
-
console.warn(`Unsupported transfer of type ${transferEntry.transfer_type} between trips ${transferEntry.from_trip_id} and ${transferEntry.to_trip_id}.`);
|
|
16029
|
-
continue;
|
|
16030
|
-
}
|
|
16031
|
-
if (transferEntry.from_route_id && transferEntry.to_route_id) {
|
|
16032
|
-
console.warn(`Unsupported transfer of type ${transferEntry.transfer_type} between routes ${transferEntry.from_route_id} and ${transferEntry.to_route_id}.`);
|
|
15990
|
+
const tripContinuations = [];
|
|
15991
|
+
for await (const rawLine of parseCsv(transfersStream, [
|
|
15992
|
+
'transfer_type',
|
|
15993
|
+
'min_transfer_time',
|
|
15994
|
+
])) {
|
|
15995
|
+
const transferEntry = rawLine;
|
|
15996
|
+
if (transferEntry.transfer_type === 3 ||
|
|
15997
|
+
transferEntry.transfer_type === 5) {
|
|
15998
|
+
continue;
|
|
15999
|
+
}
|
|
16000
|
+
if (!transferEntry.from_stop_id || !transferEntry.to_stop_id) {
|
|
16001
|
+
console.warn(`Missing transfer origin or destination stop.`);
|
|
16002
|
+
continue;
|
|
16003
|
+
}
|
|
16004
|
+
const fromStop = stopsMap.get(transferEntry.from_stop_id);
|
|
16005
|
+
const toStop = stopsMap.get(transferEntry.to_stop_id);
|
|
16006
|
+
if (!fromStop || !toStop) {
|
|
16007
|
+
console.warn(`Transfer references non-existent stop(s): from_stop_id=${transferEntry.from_stop_id}, to_stop_id=${transferEntry.to_stop_id}`);
|
|
16008
|
+
continue;
|
|
16009
|
+
}
|
|
16010
|
+
if (transferEntry.transfer_type === 4) {
|
|
16011
|
+
if (transferEntry.from_trip_id === undefined ||
|
|
16012
|
+
transferEntry.from_trip_id === '' ||
|
|
16013
|
+
transferEntry.to_trip_id === undefined ||
|
|
16014
|
+
transferEntry.to_trip_id === '') {
|
|
16015
|
+
console.warn(`Unsupported in-seat transfer, missing from_trip_id and/or to_trip_id.`);
|
|
16033
16016
|
continue;
|
|
16034
16017
|
}
|
|
16035
|
-
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
|
|
16040
|
-
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
fromStopTransfers.push(transfer);
|
|
16044
|
-
transfers.set(fromStop.id, fromStopTransfers);
|
|
16018
|
+
const tripBoardingEntry = {
|
|
16019
|
+
fromStop: fromStop.id,
|
|
16020
|
+
fromTrip: transferEntry.from_trip_id,
|
|
16021
|
+
toStop: toStop.id,
|
|
16022
|
+
toTrip: transferEntry.to_trip_id,
|
|
16023
|
+
};
|
|
16024
|
+
tripContinuations.push(tripBoardingEntry);
|
|
16025
|
+
continue;
|
|
16045
16026
|
}
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16027
|
+
if (transferEntry.from_trip_id && transferEntry.to_trip_id) {
|
|
16028
|
+
console.warn(`Unsupported transfer of type ${transferEntry.transfer_type} between trips ${transferEntry.from_trip_id} and ${transferEntry.to_trip_id}.`);
|
|
16029
|
+
continue;
|
|
16030
|
+
}
|
|
16031
|
+
if (transferEntry.from_route_id && transferEntry.to_route_id) {
|
|
16032
|
+
console.warn(`Unsupported transfer of type ${transferEntry.transfer_type} between routes ${transferEntry.from_route_id} and ${transferEntry.to_route_id}.`);
|
|
16033
|
+
continue;
|
|
16051
16034
|
}
|
|
16052
|
-
|
|
16035
|
+
if (transferEntry.transfer_type === 2 &&
|
|
16036
|
+
transferEntry.min_transfer_time === undefined) {
|
|
16037
|
+
console.info(`Missing minimum transfer time between ${transferEntry.from_stop_id} and ${transferEntry.to_stop_id}.`);
|
|
16038
|
+
}
|
|
16039
|
+
const transfer = {
|
|
16040
|
+
destination: toStop.id,
|
|
16041
|
+
type: parseGtfsTransferType(transferEntry.transfer_type),
|
|
16042
|
+
...(transferEntry.min_transfer_time !== undefined && {
|
|
16043
|
+
minTransferTime: Duration.fromSeconds(transferEntry.min_transfer_time),
|
|
16044
|
+
}),
|
|
16045
|
+
};
|
|
16046
|
+
const fromStopTransfers = transfers.get(fromStop.id) || [];
|
|
16047
|
+
fromStopTransfers.push(transfer);
|
|
16048
|
+
transfers.set(fromStop.id, fromStopTransfers);
|
|
16053
16049
|
}
|
|
16054
16050
|
return {
|
|
16055
16051
|
transfers,
|
|
16056
16052
|
tripContinuations,
|
|
16057
16053
|
};
|
|
16058
|
-
}
|
|
16054
|
+
};
|
|
16055
|
+
/**
|
|
16056
|
+
* Disambiguates stops involved in a transfer.
|
|
16057
|
+
*
|
|
16058
|
+
* The GTFS specification only refers to a stopId in the trip-to-trip transfers and not the
|
|
16059
|
+
* specific stop index in the route. For routes that have multiple stops with the same stopId,
|
|
16060
|
+
* we need to determine which are the from / to stop indices in respective routes.
|
|
16061
|
+
* We do so by picking the stop indices leading to the most coherent transfer.
|
|
16062
|
+
* (we pick the closest from stop index happening after the to stop index).
|
|
16063
|
+
*/
|
|
16064
|
+
const disambiguateTransferStopsIndices = (fromStop, fromRoute, fromTripIndex, toStop, toRoute, toTripIndex) => {
|
|
16065
|
+
const fromStopIndices = fromRoute.stopRouteIndices(fromStop);
|
|
16066
|
+
const toStopIndices = toRoute.stopRouteIndices(toStop);
|
|
16067
|
+
let bestFromStopIndex;
|
|
16068
|
+
let bestToStopIndex;
|
|
16069
|
+
let bestTimeDifference = Infinity;
|
|
16070
|
+
for (const originStopIndex of fromStopIndices) {
|
|
16071
|
+
const fromArrivalTime = fromRoute.arrivalAt(originStopIndex, fromTripIndex);
|
|
16072
|
+
for (const toStopIndex of toStopIndices) {
|
|
16073
|
+
const toDepartureTime = toRoute.departureFrom(toStopIndex, toTripIndex);
|
|
16074
|
+
if (toDepartureTime.isAfter(fromArrivalTime)) {
|
|
16075
|
+
const timeDifference = toDepartureTime.toMinutes() - fromArrivalTime.toMinutes();
|
|
16076
|
+
if (timeDifference < bestTimeDifference) {
|
|
16077
|
+
bestTimeDifference = timeDifference;
|
|
16078
|
+
bestFromStopIndex = originStopIndex;
|
|
16079
|
+
bestToStopIndex = toStopIndex;
|
|
16080
|
+
}
|
|
16081
|
+
}
|
|
16082
|
+
}
|
|
16083
|
+
}
|
|
16084
|
+
if (bestFromStopIndex !== undefined && bestToStopIndex !== undefined) {
|
|
16085
|
+
return {
|
|
16086
|
+
fromStopIndex: bestFromStopIndex,
|
|
16087
|
+
toStopIndex: bestToStopIndex,
|
|
16088
|
+
};
|
|
16089
|
+
}
|
|
16090
|
+
return undefined;
|
|
16091
|
+
};
|
|
16092
|
+
/**
|
|
16093
|
+
* Builds trip continuations map from GTFS trip continuation data.
|
|
16094
|
+
*
|
|
16095
|
+
* This function processes GTFS in-seat transfer data and creates a mapping
|
|
16096
|
+
* from trip boarding IDs to continuation boarding information. It disambiguates
|
|
16097
|
+
* stop indices when routes have multiple stops with the same ID by finding
|
|
16098
|
+
* the most coherent transfer timing.
|
|
16099
|
+
*
|
|
16100
|
+
* @param tripsMapping Mapping from GTFS trip IDs to internal trip representations
|
|
16101
|
+
* @param tripContinuations Array of GTFS trip continuation data from transfers.txt
|
|
16102
|
+
* @param timetable The timetable containing route and timing information
|
|
16103
|
+
* @param activeStopIds Set of stop IDs that are active/enabled in the system
|
|
16104
|
+
* @returns A map from trip boarding IDs to arrays of continuation boarding options
|
|
16105
|
+
*/
|
|
16106
|
+
const buildTripContinuations = (tripsMapping, tripContinuations, timetable, activeStopIds) => {
|
|
16107
|
+
const continuations = new Map();
|
|
16108
|
+
for (const gtfsContinuation of tripContinuations) {
|
|
16109
|
+
if (!activeStopIds.has(gtfsContinuation.fromStop) ||
|
|
16110
|
+
!activeStopIds.has(gtfsContinuation.toStop)) {
|
|
16111
|
+
continue;
|
|
16112
|
+
}
|
|
16113
|
+
const fromTripMapping = tripsMapping.get(gtfsContinuation.fromTrip);
|
|
16114
|
+
const toTripMapping = tripsMapping.get(gtfsContinuation.toTrip);
|
|
16115
|
+
if (!fromTripMapping || !toTripMapping) {
|
|
16116
|
+
continue;
|
|
16117
|
+
}
|
|
16118
|
+
const fromRoute = timetable.getRoute(fromTripMapping.routeId);
|
|
16119
|
+
const toRoute = timetable.getRoute(toTripMapping.routeId);
|
|
16120
|
+
if (!fromRoute || !toRoute) {
|
|
16121
|
+
continue;
|
|
16122
|
+
}
|
|
16123
|
+
const bestStopIndices = disambiguateTransferStopsIndices(gtfsContinuation.fromStop, fromRoute, fromTripMapping.tripRouteIndex, gtfsContinuation.toStop, toRoute, toTripMapping.tripRouteIndex);
|
|
16124
|
+
if (!bestStopIndices) {
|
|
16125
|
+
// No valid continuation found
|
|
16126
|
+
continue;
|
|
16127
|
+
}
|
|
16128
|
+
const tripBoardingId = encode(bestStopIndices.fromStopIndex, fromTripMapping.routeId, fromTripMapping.tripRouteIndex);
|
|
16129
|
+
const continuationBoarding = {
|
|
16130
|
+
hopOnStopIndex: bestStopIndices.toStopIndex,
|
|
16131
|
+
routeId: toTripMapping.routeId,
|
|
16132
|
+
tripIndex: toTripMapping.tripRouteIndex,
|
|
16133
|
+
};
|
|
16134
|
+
const existingContinuations = continuations.get(tripBoardingId) || [];
|
|
16135
|
+
existingContinuations.push(continuationBoarding);
|
|
16136
|
+
continuations.set(tripBoardingId, existingContinuations);
|
|
16137
|
+
}
|
|
16138
|
+
return continuations;
|
|
16139
|
+
};
|
|
16059
16140
|
const parseGtfsTransferType = (gtfsTransferType) => {
|
|
16060
16141
|
switch (gtfsTransferType) {
|
|
16061
16142
|
case 0:
|
|
@@ -16163,36 +16244,23 @@ const finalizeRouteFromBuilder = (builder) => {
|
|
|
16163
16244
|
* @param serviceRoutes A mapping of route IDs to route details.
|
|
16164
16245
|
* @returns A mapping of trip IDs to corresponding route IDs.
|
|
16165
16246
|
*/
|
|
16166
|
-
const parseTrips = (tripsStream, serviceIds, validGtfsRoutes) =>
|
|
16167
|
-
var _a, e_1, _b, _c;
|
|
16247
|
+
const parseTrips = async (tripsStream, serviceIds, validGtfsRoutes) => {
|
|
16168
16248
|
const trips = new Map();
|
|
16169
|
-
|
|
16170
|
-
|
|
16171
|
-
|
|
16172
|
-
|
|
16173
|
-
|
|
16174
|
-
const line = rawLine;
|
|
16175
|
-
if (!serviceIds.has(line.service_id)) {
|
|
16176
|
-
// The trip doesn't correspond to an active service
|
|
16177
|
-
continue;
|
|
16178
|
-
}
|
|
16179
|
-
if (!validGtfsRoutes.has(line.route_id)) {
|
|
16180
|
-
// The trip doesn't correspond to a supported route
|
|
16181
|
-
continue;
|
|
16182
|
-
}
|
|
16183
|
-
trips.set(line.trip_id, line.route_id);
|
|
16249
|
+
for await (const rawLine of parseCsv(tripsStream, ['stop_sequence'])) {
|
|
16250
|
+
const line = rawLine;
|
|
16251
|
+
if (!serviceIds.has(line.service_id)) {
|
|
16252
|
+
// The trip doesn't correspond to an active service
|
|
16253
|
+
continue;
|
|
16184
16254
|
}
|
|
16185
|
-
|
|
16186
|
-
|
|
16187
|
-
|
|
16188
|
-
try {
|
|
16189
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
16255
|
+
if (!validGtfsRoutes.has(line.route_id)) {
|
|
16256
|
+
// The trip doesn't correspond to a supported route
|
|
16257
|
+
continue;
|
|
16190
16258
|
}
|
|
16191
|
-
|
|
16259
|
+
trips.set(line.trip_id, line.route_id);
|
|
16192
16260
|
}
|
|
16193
16261
|
return trips;
|
|
16194
|
-
}
|
|
16195
|
-
const buildStopsAdjacencyStructure = (
|
|
16262
|
+
};
|
|
16263
|
+
const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbStops, activeStops) => {
|
|
16196
16264
|
const stopsAdjacency = new Array(nbStops);
|
|
16197
16265
|
for (let i = 0; i < nbStops; i++) {
|
|
16198
16266
|
stopsAdjacency[i] = {
|
|
@@ -16232,40 +16300,6 @@ const buildStopsAdjacencyStructure = (tripsMapping, serviceRoutes, routes, trans
|
|
|
16232
16300
|
}
|
|
16233
16301
|
}
|
|
16234
16302
|
}
|
|
16235
|
-
for (const [stop, tripContinuations] of tripContinuationsMap) {
|
|
16236
|
-
for (let i = 0; i < tripContinuations.length; i++) {
|
|
16237
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16238
|
-
const tripContinuation = tripContinuations[i];
|
|
16239
|
-
if (activeStops.has(stop) ||
|
|
16240
|
-
activeStops.has(tripContinuation.hopOnStop)) {
|
|
16241
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16242
|
-
const stopAdj = stopsAdjacency[stop];
|
|
16243
|
-
if (!stopAdj.tripContinuations) {
|
|
16244
|
-
stopAdj.tripContinuations = new Map();
|
|
16245
|
-
}
|
|
16246
|
-
const originTrip = tripsMapping.get(tripContinuation.fromTrip);
|
|
16247
|
-
const destinationTrip = tripsMapping.get(tripContinuation.toTrip);
|
|
16248
|
-
if (destinationTrip === undefined || originTrip === undefined) {
|
|
16249
|
-
continue;
|
|
16250
|
-
}
|
|
16251
|
-
const tripBoarding = {
|
|
16252
|
-
hopOnStop: tripContinuation.hopOnStop,
|
|
16253
|
-
routeId: destinationTrip.routeId,
|
|
16254
|
-
tripIndex: destinationTrip.tripRouteIndex,
|
|
16255
|
-
};
|
|
16256
|
-
const tripId = encode(originTrip.routeId, originTrip.tripRouteIndex);
|
|
16257
|
-
const existingContinuations = stopAdj.tripContinuations.get(tripId);
|
|
16258
|
-
if (existingContinuations) {
|
|
16259
|
-
existingContinuations.push(tripBoarding);
|
|
16260
|
-
}
|
|
16261
|
-
else {
|
|
16262
|
-
stopAdj.tripContinuations.set(tripId, [tripBoarding]);
|
|
16263
|
-
}
|
|
16264
|
-
activeStops.add(tripContinuation.hopOnStop);
|
|
16265
|
-
activeStops.add(stop);
|
|
16266
|
-
}
|
|
16267
|
-
}
|
|
16268
|
-
}
|
|
16269
16303
|
return stopsAdjacency;
|
|
16270
16304
|
};
|
|
16271
16305
|
/**
|
|
@@ -16277,9 +16311,7 @@ const buildStopsAdjacencyStructure = (tripsMapping, serviceRoutes, routes, trans
|
|
|
16277
16311
|
* @param activeStopIds A set of valid stop IDs.
|
|
16278
16312
|
* @returns A mapping of route IDs to route details. The routes returned correspond to the set of trips from GTFS that share the same stop list.
|
|
16279
16313
|
*/
|
|
16280
|
-
const parseStopTimes = (stopTimesStream, stopsMap, activeTripIds, activeStopIds) =>
|
|
16281
|
-
var _a, e_2, _b, _c;
|
|
16282
|
-
var _d, _e;
|
|
16314
|
+
const parseStopTimes = async (stopTimesStream, stopsMap, activeTripIds, activeStopIds) => {
|
|
16283
16315
|
/**
|
|
16284
16316
|
* Adds a trip to the appropriate route builder
|
|
16285
16317
|
*/
|
|
@@ -16347,55 +16379,43 @@ const parseStopTimes = (stopTimesStream, stopsMap, activeTripIds, activeStopIds)
|
|
|
16347
16379
|
let pickUpTypes = [];
|
|
16348
16380
|
let dropOffTypes = [];
|
|
16349
16381
|
let currentTripId = undefined;
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
const line = rawLine;
|
|
16356
|
-
if (line.trip_id === currentTripId && line.stop_sequence <= previousSeq) {
|
|
16357
|
-
console.warn(`Stop sequences not increasing for trip ${line.trip_id}: ${line.stop_sequence} > ${previousSeq}.`);
|
|
16358
|
-
continue;
|
|
16359
|
-
}
|
|
16360
|
-
if (!line.arrival_time && !line.departure_time) {
|
|
16361
|
-
console.warn(`Missing arrival or departure time for ${line.trip_id} at stop ${line.stop_id}.`);
|
|
16362
|
-
continue;
|
|
16363
|
-
}
|
|
16364
|
-
if (line.pickup_type === '1' && line.drop_off_type === '1') {
|
|
16365
|
-
// Warning: could potentially lead to issues if there is an in-seat transfer
|
|
16366
|
-
// at this stop - it can be not boardable nor alightable but still useful for an in-seat transfer.
|
|
16367
|
-
// This doesn't seem to happen in practice for now so keeping this condition to save memory.
|
|
16368
|
-
continue;
|
|
16369
|
-
}
|
|
16370
|
-
if (currentTripId && line.trip_id !== currentTripId && stops.length > 0) {
|
|
16371
|
-
addTrip(currentTripId);
|
|
16372
|
-
}
|
|
16373
|
-
currentTripId = line.trip_id;
|
|
16374
|
-
const stopData = stopsMap.get(line.stop_id);
|
|
16375
|
-
if (!stopData) {
|
|
16376
|
-
console.warn(`Unknown stop ID: ${line.stop_id}`);
|
|
16377
|
-
continue;
|
|
16378
|
-
}
|
|
16379
|
-
stops.push(stopData.id);
|
|
16380
|
-
const departure = (_d = line.departure_time) !== null && _d !== void 0 ? _d : line.arrival_time;
|
|
16381
|
-
const arrival = (_e = line.arrival_time) !== null && _e !== void 0 ? _e : line.departure_time;
|
|
16382
|
-
if (!arrival || !departure) {
|
|
16383
|
-
console.warn(`Missing time data for ${line.trip_id} at stop ${line.stop_id}`);
|
|
16384
|
-
continue;
|
|
16385
|
-
}
|
|
16386
|
-
arrivalTimes.push(toTime(arrival).toMinutes());
|
|
16387
|
-
departureTimes.push(toTime(departure).toMinutes());
|
|
16388
|
-
pickUpTypes.push(parsePickupDropOffType(line.pickup_type));
|
|
16389
|
-
dropOffTypes.push(parsePickupDropOffType(line.drop_off_type));
|
|
16390
|
-
previousSeq = line.stop_sequence;
|
|
16382
|
+
for await (const rawLine of parseCsv(stopTimesStream, ['stop_sequence'])) {
|
|
16383
|
+
const line = rawLine;
|
|
16384
|
+
if (line.trip_id === currentTripId && line.stop_sequence <= previousSeq) {
|
|
16385
|
+
console.warn(`Stop sequences not increasing for trip ${line.trip_id}: ${line.stop_sequence} > ${previousSeq}.`);
|
|
16386
|
+
continue;
|
|
16391
16387
|
}
|
|
16392
|
-
|
|
16393
|
-
|
|
16394
|
-
|
|
16395
|
-
|
|
16396
|
-
|
|
16388
|
+
if (!line.arrival_time && !line.departure_time) {
|
|
16389
|
+
console.warn(`Missing arrival or departure time for ${line.trip_id} at stop ${line.stop_id}.`);
|
|
16390
|
+
continue;
|
|
16391
|
+
}
|
|
16392
|
+
if (line.pickup_type === '1' && line.drop_off_type === '1') {
|
|
16393
|
+
// Warning: could potentially lead to issues if there is an in-seat transfer
|
|
16394
|
+
// at this stop - it can be not boardable nor alightable but still useful for an in-seat transfer.
|
|
16395
|
+
// This doesn't seem to happen in practice for now so keeping this condition to save memory.
|
|
16396
|
+
continue;
|
|
16397
16397
|
}
|
|
16398
|
-
|
|
16398
|
+
if (currentTripId && line.trip_id !== currentTripId && stops.length > 0) {
|
|
16399
|
+
addTrip(currentTripId);
|
|
16400
|
+
}
|
|
16401
|
+
currentTripId = line.trip_id;
|
|
16402
|
+
const stopData = stopsMap.get(line.stop_id);
|
|
16403
|
+
if (!stopData) {
|
|
16404
|
+
console.warn(`Unknown stop ID: ${line.stop_id}`);
|
|
16405
|
+
continue;
|
|
16406
|
+
}
|
|
16407
|
+
stops.push(stopData.id);
|
|
16408
|
+
const departure = line.departure_time ?? line.arrival_time;
|
|
16409
|
+
const arrival = line.arrival_time ?? line.departure_time;
|
|
16410
|
+
if (!arrival || !departure) {
|
|
16411
|
+
console.warn(`Missing time data for ${line.trip_id} at stop ${line.stop_id}`);
|
|
16412
|
+
continue;
|
|
16413
|
+
}
|
|
16414
|
+
arrivalTimes.push(toTime(arrival).toMinutes());
|
|
16415
|
+
departureTimes.push(toTime(departure).toMinutes());
|
|
16416
|
+
pickUpTypes.push(parsePickupDropOffType(line.pickup_type));
|
|
16417
|
+
dropOffTypes.push(parsePickupDropOffType(line.drop_off_type));
|
|
16418
|
+
previousSeq = line.stop_sequence;
|
|
16399
16419
|
}
|
|
16400
16420
|
if (currentTripId) {
|
|
16401
16421
|
addTrip(currentTripId);
|
|
@@ -16414,7 +16434,7 @@ const parseStopTimes = (stopTimesStream, stopsMap, activeTripIds, activeStopIds)
|
|
|
16414
16434
|
});
|
|
16415
16435
|
}
|
|
16416
16436
|
return { routes: routesAdjacency, serviceRoutesMap, tripsMapping };
|
|
16417
|
-
}
|
|
16437
|
+
};
|
|
16418
16438
|
const parsePickupDropOffType = (gtfsType) => {
|
|
16419
16439
|
switch (gtfsType) {
|
|
16420
16440
|
default:
|
|
@@ -16438,6 +16458,8 @@ const STOP_TIMES_FILE = 'stop_times.txt';
|
|
|
16438
16458
|
const STOPS_FILE = 'stops.txt';
|
|
16439
16459
|
const TRANSFERS_FILE = 'transfers.txt';
|
|
16440
16460
|
class GtfsParser {
|
|
16461
|
+
path;
|
|
16462
|
+
profile;
|
|
16441
16463
|
constructor(path, profile = standardProfile) {
|
|
16442
16464
|
// TODO: support input from multiple sources
|
|
16443
16465
|
this.path = path;
|
|
@@ -16449,77 +16471,81 @@ class GtfsParser {
|
|
|
16449
16471
|
* @param date The active date.
|
|
16450
16472
|
* @returns The parsed timetable.
|
|
16451
16473
|
*/
|
|
16452
|
-
parseTimetable(date) {
|
|
16453
|
-
|
|
16454
|
-
|
|
16455
|
-
|
|
16456
|
-
|
|
16457
|
-
|
|
16458
|
-
|
|
16459
|
-
|
|
16460
|
-
|
|
16461
|
-
|
|
16462
|
-
|
|
16463
|
-
|
|
16464
|
-
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
|
|
16469
|
-
|
|
16470
|
-
|
|
16471
|
-
|
|
16472
|
-
|
|
16473
|
-
|
|
16474
|
-
|
|
16475
|
-
|
|
16476
|
-
|
|
16477
|
-
|
|
16478
|
-
|
|
16479
|
-
|
|
16480
|
-
|
|
16481
|
-
|
|
16482
|
-
|
|
16483
|
-
|
|
16484
|
-
|
|
16485
|
-
|
|
16486
|
-
|
|
16487
|
-
|
|
16488
|
-
|
|
16489
|
-
|
|
16490
|
-
|
|
16491
|
-
|
|
16492
|
-
|
|
16493
|
-
|
|
16494
|
-
|
|
16495
|
-
|
|
16496
|
-
|
|
16497
|
-
|
|
16498
|
-
|
|
16499
|
-
|
|
16500
|
-
|
|
16501
|
-
|
|
16502
|
-
|
|
16503
|
-
|
|
16504
|
-
|
|
16505
|
-
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
|
|
16509
|
-
|
|
16510
|
-
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
|
|
16515
|
-
|
|
16516
|
-
|
|
16517
|
-
|
|
16518
|
-
|
|
16519
|
-
|
|
16520
|
-
|
|
16521
|
-
|
|
16522
|
-
|
|
16474
|
+
async parseTimetable(date) {
|
|
16475
|
+
log.setLevel('INFO');
|
|
16476
|
+
const zip = new StreamZip.async({ file: this.path });
|
|
16477
|
+
const entries = await zip.entries();
|
|
16478
|
+
const datetime = DateTime.fromJSDate(date);
|
|
16479
|
+
const activeServiceIds = new Set();
|
|
16480
|
+
const activeStopIds = new Set();
|
|
16481
|
+
log.info(`Parsing ${STOPS_FILE}`);
|
|
16482
|
+
const stopsStart = performance.now();
|
|
16483
|
+
const stopsStream = await zip.stream(STOPS_FILE);
|
|
16484
|
+
const parsedStops = await parseStops(stopsStream);
|
|
16485
|
+
const stopsEnd = performance.now();
|
|
16486
|
+
log.info(`${parsedStops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
|
|
16487
|
+
if (entries[CALENDAR_FILE]) {
|
|
16488
|
+
log.info(`Parsing ${CALENDAR_FILE}`);
|
|
16489
|
+
const calendarStart = performance.now();
|
|
16490
|
+
const calendarStream = await zip.stream(CALENDAR_FILE);
|
|
16491
|
+
await parseCalendar(calendarStream, activeServiceIds, datetime);
|
|
16492
|
+
const calendarEnd = performance.now();
|
|
16493
|
+
log.info(`${activeServiceIds.size} valid services. (${(calendarEnd - calendarStart).toFixed(2)}ms)`);
|
|
16494
|
+
}
|
|
16495
|
+
if (entries[CALENDAR_DATES_FILE]) {
|
|
16496
|
+
log.info(`Parsing ${CALENDAR_DATES_FILE}`);
|
|
16497
|
+
const calendarDatesStart = performance.now();
|
|
16498
|
+
const calendarDatesStream = await zip.stream(CALENDAR_DATES_FILE);
|
|
16499
|
+
await parseCalendarDates(calendarDatesStream, activeServiceIds, datetime);
|
|
16500
|
+
const calendarDatesEnd = performance.now();
|
|
16501
|
+
log.info(`${activeServiceIds.size} valid services. (${(calendarDatesEnd - calendarDatesStart).toFixed(2)}ms)`);
|
|
16502
|
+
}
|
|
16503
|
+
log.info(`Parsing ${ROUTES_FILE}`);
|
|
16504
|
+
const routesStart = performance.now();
|
|
16505
|
+
const routesStream = await zip.stream(ROUTES_FILE);
|
|
16506
|
+
const validGtfsRoutes = await parseRoutes(routesStream, this.profile);
|
|
16507
|
+
const routesEnd = performance.now();
|
|
16508
|
+
log.info(`${validGtfsRoutes.size} valid GTFS routes. (${(routesEnd - routesStart).toFixed(2)}ms)`);
|
|
16509
|
+
log.info(`Parsing ${TRIPS_FILE}`);
|
|
16510
|
+
const tripsStart = performance.now();
|
|
16511
|
+
const tripsStream = await zip.stream(TRIPS_FILE);
|
|
16512
|
+
const trips = await parseTrips(tripsStream, activeServiceIds, validGtfsRoutes);
|
|
16513
|
+
const tripsEnd = performance.now();
|
|
16514
|
+
log.info(`${trips.size} valid trips. (${(tripsEnd - tripsStart).toFixed(2)}ms)`);
|
|
16515
|
+
let transfers = new Map();
|
|
16516
|
+
let tripContinuationsMap = [];
|
|
16517
|
+
if (entries[TRANSFERS_FILE]) {
|
|
16518
|
+
log.info(`Parsing ${TRANSFERS_FILE}`);
|
|
16519
|
+
const transfersStart = performance.now();
|
|
16520
|
+
const transfersStream = await zip.stream(TRANSFERS_FILE);
|
|
16521
|
+
const { transfers: parsedTransfers, tripContinuations: parsedTripContinuations, } = await parseTransfers(transfersStream, parsedStops);
|
|
16522
|
+
transfers = parsedTransfers;
|
|
16523
|
+
tripContinuationsMap = parsedTripContinuations;
|
|
16524
|
+
const transfersEnd = performance.now();
|
|
16525
|
+
log.info(`${transfers.size} valid transfers and ${tripContinuationsMap.length} trip continuations. (${(transfersEnd - transfersStart).toFixed(2)}ms)`);
|
|
16526
|
+
}
|
|
16527
|
+
log.info(`Parsing ${STOP_TIMES_FILE}`);
|
|
16528
|
+
const stopTimesStart = performance.now();
|
|
16529
|
+
const stopTimesStream = await zip.stream(STOP_TIMES_FILE);
|
|
16530
|
+
const { routes, serviceRoutesMap, tripsMapping } = await parseStopTimes(stopTimesStream, parsedStops, trips, activeStopIds);
|
|
16531
|
+
const serviceRoutes = indexRoutes(validGtfsRoutes, serviceRoutesMap);
|
|
16532
|
+
const stopTimesEnd = performance.now();
|
|
16533
|
+
log.info(`${routes.length} valid unique routes. (${(stopTimesEnd - stopTimesStart).toFixed(2)}ms)`);
|
|
16534
|
+
log.info('Building stops adjacency structure');
|
|
16535
|
+
const stopsAdjacencyStart = performance.now();
|
|
16536
|
+
const stopsAdjacency = buildStopsAdjacencyStructure(serviceRoutes, routes, transfers, parsedStops.size, activeStopIds);
|
|
16537
|
+
const stopsAdjacencyEnd = performance.now();
|
|
16538
|
+
log.info(`${stopsAdjacency.length} valid stops in the structure. (${(stopsAdjacencyEnd - stopsAdjacencyStart).toFixed(2)}ms)`);
|
|
16539
|
+
await zip.close();
|
|
16540
|
+
// temporary timetable for building continuations
|
|
16541
|
+
const timetable = new Timetable(stopsAdjacency, routes, serviceRoutes);
|
|
16542
|
+
log.info('Building in-seat trip continuations');
|
|
16543
|
+
const tripContinuationsStart = performance.now();
|
|
16544
|
+
const tripContinuations = buildTripContinuations(tripsMapping, tripContinuationsMap, timetable, activeStopIds);
|
|
16545
|
+
const tripContinuationsEnd = performance.now();
|
|
16546
|
+
log.info(`${tripContinuations.size} in-seat trip continuations origins created. (${(tripContinuationsEnd - tripContinuationsStart).toFixed(2)}ms)`);
|
|
16547
|
+
log.info('Parsing complete.');
|
|
16548
|
+
return new Timetable(stopsAdjacency, routes, serviceRoutes, tripContinuations);
|
|
16523
16549
|
}
|
|
16524
16550
|
/**
|
|
16525
16551
|
* Parses a GTFS feed to extract all stops.
|
|
@@ -16527,18 +16553,16 @@ class GtfsParser {
|
|
|
16527
16553
|
* @param activeStops The set of active stop IDs to include in the index.
|
|
16528
16554
|
* @returns An index of stops.
|
|
16529
16555
|
*/
|
|
16530
|
-
parseStops() {
|
|
16531
|
-
|
|
16532
|
-
|
|
16533
|
-
|
|
16534
|
-
|
|
16535
|
-
|
|
16536
|
-
|
|
16537
|
-
|
|
16538
|
-
|
|
16539
|
-
|
|
16540
|
-
return new StopsIndex(Array.from(stops.values()));
|
|
16541
|
-
});
|
|
16556
|
+
async parseStops() {
|
|
16557
|
+
const zip = new StreamZip.async({ file: this.path });
|
|
16558
|
+
log.info(`Parsing ${STOPS_FILE}`);
|
|
16559
|
+
const stopsStart = performance.now();
|
|
16560
|
+
const stopsStream = await zip.stream(STOPS_FILE);
|
|
16561
|
+
const stops = await parseStops(stopsStream);
|
|
16562
|
+
const stopsEnd = performance.now();
|
|
16563
|
+
log.info(`${stops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
|
|
16564
|
+
await zip.close();
|
|
16565
|
+
return new StopsIndex(Array.from(stops.values()));
|
|
16542
16566
|
}
|
|
16543
16567
|
}
|
|
16544
16568
|
|