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