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