minotor 8.0.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -4
- package/README.md +1 -1
- package/dist/cli.mjs +835 -816
- package/dist/cli.mjs.map +1 -1
- package/dist/gtfs/transfers.d.ts +21 -6
- package/dist/gtfs/trips.d.ts +2 -2
- package/dist/parser.cjs.js +666 -642
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +666 -642
- package/dist/parser.esm.js.map +1 -1
- package/dist/router.cjs.js +1 -1
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.esm.js +1 -1
- package/dist/router.esm.js.map +1 -1
- package/dist/router.umd.js +1 -1
- package/dist/router.umd.js.map +1 -1
- package/dist/routing/router.d.ts +4 -4
- package/dist/timetable/io.d.ts +3 -3
- package/dist/timetable/proto/timetable.d.ts +6 -4
- package/dist/timetable/route.d.ts +13 -21
- package/dist/timetable/timetable.d.ts +13 -11
- package/dist/timetable/tripBoardingId.d.ts +34 -0
- package/package.json +1 -1
- package/src/__e2e__/timetable/timetable.bin +2 -2
- package/src/cli/repl.ts +53 -67
- package/src/gtfs/__tests__/parser.test.ts +19 -4
- package/src/gtfs/__tests__/transfers.test.ts +598 -318
- package/src/gtfs/__tests__/trips.test.ts +3 -44
- package/src/gtfs/parser.ts +26 -8
- package/src/gtfs/transfers.ts +151 -20
- package/src/gtfs/trips.ts +1 -39
- package/src/routing/__tests__/result.test.ts +10 -10
- package/src/routing/__tests__/router.test.ts +11 -9
- package/src/routing/result.ts +2 -2
- package/src/routing/router.ts +34 -22
- package/src/timetable/__tests__/io.test.ts +8 -7
- package/src/timetable/__tests__/route.test.ts +66 -80
- package/src/timetable/__tests__/timetable.test.ts +32 -29
- package/src/timetable/__tests__/tripBoardingId.test.ts +57 -0
- package/src/timetable/io.ts +21 -20
- package/src/timetable/proto/timetable.proto +6 -4
- package/src/timetable/proto/timetable.ts +84 -48
- package/src/timetable/route.ts +39 -56
- package/src/timetable/timetable.ts +37 -26
- package/src/timetable/tripBoardingId.ts +94 -0
- package/tsconfig.json +2 -2
- package/dist/timetable/tripId.d.ts +0 -15
- package/src/timetable/__tests__/tripId.test.ts +0 -27
- package/src/timetable/tripId.ts +0 -29
- /package/dist/timetable/__tests__/{tripId.test.d.ts → tripBoardingId.test.d.ts} +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -13,58 +13,6 @@ import require$$5, { Transform } from 'stream';
|
|
|
13
13
|
import { performance as performance$1 } from 'perf_hooks';
|
|
14
14
|
import repl from 'node:repl';
|
|
15
15
|
|
|
16
|
-
/******************************************************************************
|
|
17
|
-
Copyright (c) Microsoft Corporation.
|
|
18
|
-
|
|
19
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
20
|
-
purpose with or without fee is hereby granted.
|
|
21
|
-
|
|
22
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
23
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
24
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
25
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
26
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
27
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
28
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
29
|
-
***************************************************************************** */
|
|
30
|
-
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
34
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
35
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
36
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
37
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
38
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
39
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function __values(o) {
|
|
44
|
-
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
45
|
-
if (m) return m.call(o);
|
|
46
|
-
if (o && typeof o.length === "number") return {
|
|
47
|
-
next: function () {
|
|
48
|
-
if (o && i >= o.length) o = void 0;
|
|
49
|
-
return { value: o && o[i++], done: !o };
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function __asyncValues(o) {
|
|
56
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
57
|
-
var m = o[Symbol.asyncIterator], i;
|
|
58
|
-
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);
|
|
59
|
-
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); }); }; }
|
|
60
|
-
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
64
|
-
var e = new Error(message);
|
|
65
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
16
|
function getDefaultExportFromCjs (x) {
|
|
69
17
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
70
18
|
}
|
|
@@ -15864,14 +15812,13 @@ const Stop = {
|
|
|
15864
15812
|
sourceStopId: isSet$1(object.sourceStopId) ? globalThis.String(object.sourceStopId) : "",
|
|
15865
15813
|
lat: isSet$1(object.lat) ? globalThis.Number(object.lat) : undefined,
|
|
15866
15814
|
lon: isSet$1(object.lon) ? globalThis.Number(object.lon) : undefined,
|
|
15867
|
-
children: globalThis.Array.isArray(object
|
|
15815
|
+
children: globalThis.Array.isArray(object?.children) ? object.children.map((e) => globalThis.Number(e)) : [],
|
|
15868
15816
|
parent: isSet$1(object.parent) ? globalThis.Number(object.parent) : undefined,
|
|
15869
15817
|
locationType: isSet$1(object.locationType) ? locationTypeFromJSON(object.locationType) : 0,
|
|
15870
15818
|
platform: isSet$1(object.platform) ? globalThis.String(object.platform) : undefined,
|
|
15871
15819
|
};
|
|
15872
15820
|
},
|
|
15873
15821
|
toJSON(message) {
|
|
15874
|
-
var _a;
|
|
15875
15822
|
const obj = {};
|
|
15876
15823
|
if (message.name !== "") {
|
|
15877
15824
|
obj.name = message.name;
|
|
@@ -15885,7 +15832,7 @@ const Stop = {
|
|
|
15885
15832
|
if (message.lon !== undefined) {
|
|
15886
15833
|
obj.lon = message.lon;
|
|
15887
15834
|
}
|
|
15888
|
-
if (
|
|
15835
|
+
if (message.children?.length) {
|
|
15889
15836
|
obj.children = message.children.map((e) => Math.round(e));
|
|
15890
15837
|
}
|
|
15891
15838
|
if (message.parent !== undefined) {
|
|
@@ -15900,19 +15847,18 @@ const Stop = {
|
|
|
15900
15847
|
return obj;
|
|
15901
15848
|
},
|
|
15902
15849
|
create(base) {
|
|
15903
|
-
return Stop.fromPartial(base
|
|
15850
|
+
return Stop.fromPartial(base ?? {});
|
|
15904
15851
|
},
|
|
15905
15852
|
fromPartial(object) {
|
|
15906
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
15907
15853
|
const message = createBaseStop();
|
|
15908
|
-
message.name =
|
|
15909
|
-
message.sourceStopId =
|
|
15910
|
-
message.lat =
|
|
15911
|
-
message.lon =
|
|
15912
|
-
message.children =
|
|
15913
|
-
message.parent =
|
|
15914
|
-
message.locationType =
|
|
15915
|
-
message.platform =
|
|
15854
|
+
message.name = object.name ?? "";
|
|
15855
|
+
message.sourceStopId = object.sourceStopId ?? "";
|
|
15856
|
+
message.lat = object.lat ?? undefined;
|
|
15857
|
+
message.lon = object.lon ?? undefined;
|
|
15858
|
+
message.children = object.children?.map((e) => e) || [];
|
|
15859
|
+
message.parent = object.parent ?? undefined;
|
|
15860
|
+
message.locationType = object.locationType ?? 0;
|
|
15861
|
+
message.platform = object.platform ?? undefined;
|
|
15916
15862
|
return message;
|
|
15917
15863
|
},
|
|
15918
15864
|
};
|
|
@@ -15961,28 +15907,26 @@ const StopsMap = {
|
|
|
15961
15907
|
fromJSON(object) {
|
|
15962
15908
|
return {
|
|
15963
15909
|
version: isSet$1(object.version) ? globalThis.String(object.version) : "",
|
|
15964
|
-
stops: globalThis.Array.isArray(object
|
|
15910
|
+
stops: globalThis.Array.isArray(object?.stops) ? object.stops.map((e) => Stop.fromJSON(e)) : [],
|
|
15965
15911
|
};
|
|
15966
15912
|
},
|
|
15967
15913
|
toJSON(message) {
|
|
15968
|
-
var _a;
|
|
15969
15914
|
const obj = {};
|
|
15970
15915
|
if (message.version !== "") {
|
|
15971
15916
|
obj.version = message.version;
|
|
15972
15917
|
}
|
|
15973
|
-
if (
|
|
15918
|
+
if (message.stops?.length) {
|
|
15974
15919
|
obj.stops = message.stops.map((e) => Stop.toJSON(e));
|
|
15975
15920
|
}
|
|
15976
15921
|
return obj;
|
|
15977
15922
|
},
|
|
15978
15923
|
create(base) {
|
|
15979
|
-
return StopsMap.fromPartial(base
|
|
15924
|
+
return StopsMap.fromPartial(base ?? {});
|
|
15980
15925
|
},
|
|
15981
15926
|
fromPartial(object) {
|
|
15982
|
-
var _a, _b;
|
|
15983
15927
|
const message = createBaseStopsMap();
|
|
15984
|
-
message.version =
|
|
15985
|
-
message.stops =
|
|
15928
|
+
message.version = object.version ?? "";
|
|
15929
|
+
message.stops = object.stops?.map((e) => Stop.fromPartial(e)) || [];
|
|
15986
15930
|
return message;
|
|
15987
15931
|
},
|
|
15988
15932
|
};
|
|
@@ -16066,8 +16010,12 @@ const serializeLocationType = (locationType) => {
|
|
|
16066
16010
|
* to efficiently find stops based on user queries.
|
|
16067
16011
|
*/
|
|
16068
16012
|
class StopsIndex {
|
|
16013
|
+
stops;
|
|
16014
|
+
sourceStopsMap;
|
|
16015
|
+
textIndex;
|
|
16016
|
+
geoIndex;
|
|
16017
|
+
stopPoints;
|
|
16069
16018
|
constructor(stops) {
|
|
16070
|
-
var _a;
|
|
16071
16019
|
this.stops = stops;
|
|
16072
16020
|
this.sourceStopsMap = new Map();
|
|
16073
16021
|
const stopsSet = new Map();
|
|
@@ -16076,7 +16024,7 @@ class StopsIndex {
|
|
|
16076
16024
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16077
16025
|
const stop = stops[id];
|
|
16078
16026
|
this.sourceStopsMap.set(stop.sourceStopId, id);
|
|
16079
|
-
const effectiveStopId =
|
|
16027
|
+
const effectiveStopId = stop.parent ?? id;
|
|
16080
16028
|
if (!stopsSet.has(effectiveStopId)) {
|
|
16081
16029
|
stopsSet.set(effectiveStopId, {
|
|
16082
16030
|
id: effectiveStopId,
|
|
@@ -16191,7 +16139,6 @@ class StopsIndex {
|
|
|
16191
16139
|
* Find ids of all sibling stops.
|
|
16192
16140
|
*/
|
|
16193
16141
|
equivalentStops(sourceId) {
|
|
16194
|
-
var _a, _b;
|
|
16195
16142
|
const id = this.sourceStopsMap.get(sourceId);
|
|
16196
16143
|
if (id === undefined) {
|
|
16197
16144
|
return [];
|
|
@@ -16201,13 +16148,14 @@ class StopsIndex {
|
|
|
16201
16148
|
return [];
|
|
16202
16149
|
}
|
|
16203
16150
|
const equivalentStops = stop.parent
|
|
16204
|
-
? (
|
|
16151
|
+
? (this.stops[stop.parent]?.children ?? [])
|
|
16205
16152
|
: stop.children;
|
|
16206
16153
|
return Array.from(new Set([id, ...equivalentStops])).map((stopId) => this.stops[stopId]);
|
|
16207
16154
|
}
|
|
16208
16155
|
}
|
|
16209
16156
|
|
|
16210
16157
|
class Duration {
|
|
16158
|
+
totalSeconds;
|
|
16211
16159
|
constructor(totalSeconds) {
|
|
16212
16160
|
this.totalSeconds = totalSeconds;
|
|
16213
16161
|
}
|
|
@@ -16510,15 +16458,14 @@ const Route$2 = {
|
|
|
16510
16458
|
return obj;
|
|
16511
16459
|
},
|
|
16512
16460
|
create(base) {
|
|
16513
|
-
return Route$2.fromPartial(base
|
|
16461
|
+
return Route$2.fromPartial(base ?? {});
|
|
16514
16462
|
},
|
|
16515
16463
|
fromPartial(object) {
|
|
16516
|
-
var _a, _b, _c, _d;
|
|
16517
16464
|
const message = createBaseRoute();
|
|
16518
|
-
message.stopTimes =
|
|
16519
|
-
message.pickUpDropOffTypes =
|
|
16520
|
-
message.stops =
|
|
16521
|
-
message.serviceRouteId =
|
|
16465
|
+
message.stopTimes = object.stopTimes ?? new Uint8Array(0);
|
|
16466
|
+
message.pickUpDropOffTypes = object.pickUpDropOffTypes ?? new Uint8Array(0);
|
|
16467
|
+
message.stops = object.stops ?? new Uint8Array(0);
|
|
16468
|
+
message.serviceRouteId = object.serviceRouteId ?? 0;
|
|
16522
16469
|
return message;
|
|
16523
16470
|
},
|
|
16524
16471
|
};
|
|
@@ -16595,24 +16542,23 @@ const Transfer = {
|
|
|
16595
16542
|
return obj;
|
|
16596
16543
|
},
|
|
16597
16544
|
create(base) {
|
|
16598
|
-
return Transfer.fromPartial(base
|
|
16545
|
+
return Transfer.fromPartial(base ?? {});
|
|
16599
16546
|
},
|
|
16600
16547
|
fromPartial(object) {
|
|
16601
|
-
var _a, _b, _c;
|
|
16602
16548
|
const message = createBaseTransfer();
|
|
16603
|
-
message.destination =
|
|
16604
|
-
message.type =
|
|
16605
|
-
message.minTransferTime =
|
|
16549
|
+
message.destination = object.destination ?? 0;
|
|
16550
|
+
message.type = object.type ?? 0;
|
|
16551
|
+
message.minTransferTime = object.minTransferTime ?? undefined;
|
|
16606
16552
|
return message;
|
|
16607
16553
|
},
|
|
16608
16554
|
};
|
|
16609
16555
|
function createBaseTripBoarding() {
|
|
16610
|
-
return {
|
|
16556
|
+
return { hopOnStopIndex: 0, routeId: 0, tripIndex: 0 };
|
|
16611
16557
|
}
|
|
16612
16558
|
const TripBoarding = {
|
|
16613
16559
|
encode(message, writer = new BinaryWriter()) {
|
|
16614
|
-
if (message.
|
|
16615
|
-
writer.uint32(8).uint32(message.
|
|
16560
|
+
if (message.hopOnStopIndex !== 0) {
|
|
16561
|
+
writer.uint32(8).uint32(message.hopOnStopIndex);
|
|
16616
16562
|
}
|
|
16617
16563
|
if (message.routeId !== 0) {
|
|
16618
16564
|
writer.uint32(16).uint32(message.routeId);
|
|
@@ -16633,7 +16579,7 @@ const TripBoarding = {
|
|
|
16633
16579
|
if (tag !== 8) {
|
|
16634
16580
|
break;
|
|
16635
16581
|
}
|
|
16636
|
-
message.
|
|
16582
|
+
message.hopOnStopIndex = reader.uint32();
|
|
16637
16583
|
continue;
|
|
16638
16584
|
}
|
|
16639
16585
|
case 2: {
|
|
@@ -16660,15 +16606,15 @@ const TripBoarding = {
|
|
|
16660
16606
|
},
|
|
16661
16607
|
fromJSON(object) {
|
|
16662
16608
|
return {
|
|
16663
|
-
|
|
16609
|
+
hopOnStopIndex: isSet(object.hopOnStopIndex) ? globalThis.Number(object.hopOnStopIndex) : 0,
|
|
16664
16610
|
routeId: isSet(object.routeId) ? globalThis.Number(object.routeId) : 0,
|
|
16665
16611
|
tripIndex: isSet(object.tripIndex) ? globalThis.Number(object.tripIndex) : 0,
|
|
16666
16612
|
};
|
|
16667
16613
|
},
|
|
16668
16614
|
toJSON(message) {
|
|
16669
16615
|
const obj = {};
|
|
16670
|
-
if (message.
|
|
16671
|
-
obj.
|
|
16616
|
+
if (message.hopOnStopIndex !== 0) {
|
|
16617
|
+
obj.hopOnStopIndex = Math.round(message.hopOnStopIndex);
|
|
16672
16618
|
}
|
|
16673
16619
|
if (message.routeId !== 0) {
|
|
16674
16620
|
obj.routeId = Math.round(message.routeId);
|
|
@@ -16679,27 +16625,32 @@ const TripBoarding = {
|
|
|
16679
16625
|
return obj;
|
|
16680
16626
|
},
|
|
16681
16627
|
create(base) {
|
|
16682
|
-
return TripBoarding.fromPartial(base
|
|
16628
|
+
return TripBoarding.fromPartial(base ?? {});
|
|
16683
16629
|
},
|
|
16684
16630
|
fromPartial(object) {
|
|
16685
|
-
var _a, _b, _c;
|
|
16686
16631
|
const message = createBaseTripBoarding();
|
|
16687
|
-
message.
|
|
16688
|
-
message.routeId =
|
|
16689
|
-
message.tripIndex =
|
|
16632
|
+
message.hopOnStopIndex = object.hopOnStopIndex ?? 0;
|
|
16633
|
+
message.routeId = object.routeId ?? 0;
|
|
16634
|
+
message.tripIndex = object.tripIndex ?? 0;
|
|
16690
16635
|
return message;
|
|
16691
16636
|
},
|
|
16692
16637
|
};
|
|
16693
16638
|
function createBaseTripContinuationEntry() {
|
|
16694
|
-
return {
|
|
16639
|
+
return { originStopIndex: 0, originRouteId: 0, originTripIndex: 0, continuations: [] };
|
|
16695
16640
|
}
|
|
16696
16641
|
const TripContinuationEntry = {
|
|
16697
16642
|
encode(message, writer = new BinaryWriter()) {
|
|
16698
|
-
if (message.
|
|
16699
|
-
writer.uint32(8).uint32(message.
|
|
16643
|
+
if (message.originStopIndex !== 0) {
|
|
16644
|
+
writer.uint32(8).uint32(message.originStopIndex);
|
|
16645
|
+
}
|
|
16646
|
+
if (message.originRouteId !== 0) {
|
|
16647
|
+
writer.uint32(16).uint32(message.originRouteId);
|
|
16700
16648
|
}
|
|
16701
|
-
|
|
16702
|
-
|
|
16649
|
+
if (message.originTripIndex !== 0) {
|
|
16650
|
+
writer.uint32(24).uint32(message.originTripIndex);
|
|
16651
|
+
}
|
|
16652
|
+
for (const v of message.continuations) {
|
|
16653
|
+
TripBoarding.encode(v, writer.uint32(34).fork()).join();
|
|
16703
16654
|
}
|
|
16704
16655
|
return writer;
|
|
16705
16656
|
},
|
|
@@ -16714,14 +16665,28 @@ const TripContinuationEntry = {
|
|
|
16714
16665
|
if (tag !== 8) {
|
|
16715
16666
|
break;
|
|
16716
16667
|
}
|
|
16717
|
-
message.
|
|
16668
|
+
message.originStopIndex = reader.uint32();
|
|
16718
16669
|
continue;
|
|
16719
16670
|
}
|
|
16720
16671
|
case 2: {
|
|
16721
|
-
if (tag !==
|
|
16672
|
+
if (tag !== 16) {
|
|
16673
|
+
break;
|
|
16674
|
+
}
|
|
16675
|
+
message.originRouteId = reader.uint32();
|
|
16676
|
+
continue;
|
|
16677
|
+
}
|
|
16678
|
+
case 3: {
|
|
16679
|
+
if (tag !== 24) {
|
|
16680
|
+
break;
|
|
16681
|
+
}
|
|
16682
|
+
message.originTripIndex = reader.uint32();
|
|
16683
|
+
continue;
|
|
16684
|
+
}
|
|
16685
|
+
case 4: {
|
|
16686
|
+
if (tag !== 34) {
|
|
16722
16687
|
break;
|
|
16723
16688
|
}
|
|
16724
|
-
message.
|
|
16689
|
+
message.continuations.push(TripBoarding.decode(reader, reader.uint32()));
|
|
16725
16690
|
continue;
|
|
16726
16691
|
}
|
|
16727
16692
|
}
|
|
@@ -16734,34 +16699,44 @@ const TripContinuationEntry = {
|
|
|
16734
16699
|
},
|
|
16735
16700
|
fromJSON(object) {
|
|
16736
16701
|
return {
|
|
16737
|
-
|
|
16738
|
-
|
|
16702
|
+
originStopIndex: isSet(object.originStopIndex) ? globalThis.Number(object.originStopIndex) : 0,
|
|
16703
|
+
originRouteId: isSet(object.originRouteId) ? globalThis.Number(object.originRouteId) : 0,
|
|
16704
|
+
originTripIndex: isSet(object.originTripIndex) ? globalThis.Number(object.originTripIndex) : 0,
|
|
16705
|
+
continuations: globalThis.Array.isArray(object?.continuations)
|
|
16706
|
+
? object.continuations.map((e) => TripBoarding.fromJSON(e))
|
|
16707
|
+
: [],
|
|
16739
16708
|
};
|
|
16740
16709
|
},
|
|
16741
16710
|
toJSON(message) {
|
|
16742
|
-
var _a;
|
|
16743
16711
|
const obj = {};
|
|
16744
|
-
if (message.
|
|
16745
|
-
obj.
|
|
16712
|
+
if (message.originStopIndex !== 0) {
|
|
16713
|
+
obj.originStopIndex = Math.round(message.originStopIndex);
|
|
16714
|
+
}
|
|
16715
|
+
if (message.originRouteId !== 0) {
|
|
16716
|
+
obj.originRouteId = Math.round(message.originRouteId);
|
|
16746
16717
|
}
|
|
16747
|
-
if (
|
|
16748
|
-
obj.
|
|
16718
|
+
if (message.originTripIndex !== 0) {
|
|
16719
|
+
obj.originTripIndex = Math.round(message.originTripIndex);
|
|
16720
|
+
}
|
|
16721
|
+
if (message.continuations?.length) {
|
|
16722
|
+
obj.continuations = message.continuations.map((e) => TripBoarding.toJSON(e));
|
|
16749
16723
|
}
|
|
16750
16724
|
return obj;
|
|
16751
16725
|
},
|
|
16752
16726
|
create(base) {
|
|
16753
|
-
return TripContinuationEntry.fromPartial(base
|
|
16727
|
+
return TripContinuationEntry.fromPartial(base ?? {});
|
|
16754
16728
|
},
|
|
16755
16729
|
fromPartial(object) {
|
|
16756
|
-
var _a, _b;
|
|
16757
16730
|
const message = createBaseTripContinuationEntry();
|
|
16758
|
-
message.
|
|
16759
|
-
message.
|
|
16731
|
+
message.originStopIndex = object.originStopIndex ?? 0;
|
|
16732
|
+
message.originRouteId = object.originRouteId ?? 0;
|
|
16733
|
+
message.originTripIndex = object.originTripIndex ?? 0;
|
|
16734
|
+
message.continuations = object.continuations?.map((e) => TripBoarding.fromPartial(e)) || [];
|
|
16760
16735
|
return message;
|
|
16761
16736
|
},
|
|
16762
16737
|
};
|
|
16763
16738
|
function createBaseStopAdjacency() {
|
|
16764
|
-
return { routes: [], transfers: []
|
|
16739
|
+
return { routes: [], transfers: [] };
|
|
16765
16740
|
}
|
|
16766
16741
|
const StopAdjacency = {
|
|
16767
16742
|
encode(message, writer = new BinaryWriter()) {
|
|
@@ -16773,9 +16748,6 @@ const StopAdjacency = {
|
|
|
16773
16748
|
for (const v of message.transfers) {
|
|
16774
16749
|
Transfer.encode(v, writer.uint32(18).fork()).join();
|
|
16775
16750
|
}
|
|
16776
|
-
for (const v of message.tripContinuations) {
|
|
16777
|
-
TripContinuationEntry.encode(v, writer.uint32(26).fork()).join();
|
|
16778
|
-
}
|
|
16779
16751
|
return writer;
|
|
16780
16752
|
},
|
|
16781
16753
|
decode(input, length) {
|
|
@@ -16806,13 +16778,6 @@ const StopAdjacency = {
|
|
|
16806
16778
|
message.transfers.push(Transfer.decode(reader, reader.uint32()));
|
|
16807
16779
|
continue;
|
|
16808
16780
|
}
|
|
16809
|
-
case 3: {
|
|
16810
|
-
if (tag !== 26) {
|
|
16811
|
-
break;
|
|
16812
|
-
}
|
|
16813
|
-
message.tripContinuations.push(TripContinuationEntry.decode(reader, reader.uint32()));
|
|
16814
|
-
continue;
|
|
16815
|
-
}
|
|
16816
16781
|
}
|
|
16817
16782
|
if ((tag & 7) === 4 || tag === 0) {
|
|
16818
16783
|
break;
|
|
@@ -16823,38 +16788,29 @@ const StopAdjacency = {
|
|
|
16823
16788
|
},
|
|
16824
16789
|
fromJSON(object) {
|
|
16825
16790
|
return {
|
|
16826
|
-
routes: globalThis.Array.isArray(object
|
|
16827
|
-
transfers: globalThis.Array.isArray(object
|
|
16791
|
+
routes: globalThis.Array.isArray(object?.routes) ? object.routes.map((e) => globalThis.Number(e)) : [],
|
|
16792
|
+
transfers: globalThis.Array.isArray(object?.transfers)
|
|
16828
16793
|
? object.transfers.map((e) => Transfer.fromJSON(e))
|
|
16829
16794
|
: [],
|
|
16830
|
-
tripContinuations: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.tripContinuations)
|
|
16831
|
-
? object.tripContinuations.map((e) => TripContinuationEntry.fromJSON(e))
|
|
16832
|
-
: [],
|
|
16833
16795
|
};
|
|
16834
16796
|
},
|
|
16835
16797
|
toJSON(message) {
|
|
16836
|
-
var _a, _b, _c;
|
|
16837
16798
|
const obj = {};
|
|
16838
|
-
if (
|
|
16799
|
+
if (message.routes?.length) {
|
|
16839
16800
|
obj.routes = message.routes.map((e) => Math.round(e));
|
|
16840
16801
|
}
|
|
16841
|
-
if (
|
|
16802
|
+
if (message.transfers?.length) {
|
|
16842
16803
|
obj.transfers = message.transfers.map((e) => Transfer.toJSON(e));
|
|
16843
16804
|
}
|
|
16844
|
-
if ((_c = message.tripContinuations) === null || _c === void 0 ? void 0 : _c.length) {
|
|
16845
|
-
obj.tripContinuations = message.tripContinuations.map((e) => TripContinuationEntry.toJSON(e));
|
|
16846
|
-
}
|
|
16847
16805
|
return obj;
|
|
16848
16806
|
},
|
|
16849
16807
|
create(base) {
|
|
16850
|
-
return StopAdjacency.fromPartial(base
|
|
16808
|
+
return StopAdjacency.fromPartial(base ?? {});
|
|
16851
16809
|
},
|
|
16852
16810
|
fromPartial(object) {
|
|
16853
|
-
var _a, _b, _c;
|
|
16854
16811
|
const message = createBaseStopAdjacency();
|
|
16855
|
-
message.routes =
|
|
16856
|
-
message.transfers =
|
|
16857
|
-
message.tripContinuations = ((_c = object.tripContinuations) === null || _c === void 0 ? void 0 : _c.map((e) => TripContinuationEntry.fromPartial(e))) || [];
|
|
16812
|
+
message.routes = object.routes?.map((e) => e) || [];
|
|
16813
|
+
message.transfers = object.transfers?.map((e) => Transfer.fromPartial(e)) || [];
|
|
16858
16814
|
return message;
|
|
16859
16815
|
},
|
|
16860
16816
|
};
|
|
@@ -16923,11 +16879,10 @@ const ServiceRoute = {
|
|
|
16923
16879
|
return {
|
|
16924
16880
|
type: isSet(object.type) ? routeTypeFromJSON(object.type) : 0,
|
|
16925
16881
|
name: isSet(object.name) ? globalThis.String(object.name) : "",
|
|
16926
|
-
routes: globalThis.Array.isArray(object
|
|
16882
|
+
routes: globalThis.Array.isArray(object?.routes) ? object.routes.map((e) => globalThis.Number(e)) : [],
|
|
16927
16883
|
};
|
|
16928
16884
|
},
|
|
16929
16885
|
toJSON(message) {
|
|
16930
|
-
var _a;
|
|
16931
16886
|
const obj = {};
|
|
16932
16887
|
if (message.type !== 0) {
|
|
16933
16888
|
obj.type = routeTypeToJSON(message.type);
|
|
@@ -16935,25 +16890,24 @@ const ServiceRoute = {
|
|
|
16935
16890
|
if (message.name !== "") {
|
|
16936
16891
|
obj.name = message.name;
|
|
16937
16892
|
}
|
|
16938
|
-
if (
|
|
16893
|
+
if (message.routes?.length) {
|
|
16939
16894
|
obj.routes = message.routes.map((e) => Math.round(e));
|
|
16940
16895
|
}
|
|
16941
16896
|
return obj;
|
|
16942
16897
|
},
|
|
16943
16898
|
create(base) {
|
|
16944
|
-
return ServiceRoute.fromPartial(base
|
|
16899
|
+
return ServiceRoute.fromPartial(base ?? {});
|
|
16945
16900
|
},
|
|
16946
16901
|
fromPartial(object) {
|
|
16947
|
-
var _a, _b, _c;
|
|
16948
16902
|
const message = createBaseServiceRoute();
|
|
16949
|
-
message.type =
|
|
16950
|
-
message.name =
|
|
16951
|
-
message.routes =
|
|
16903
|
+
message.type = object.type ?? 0;
|
|
16904
|
+
message.name = object.name ?? "";
|
|
16905
|
+
message.routes = object.routes?.map((e) => e) || [];
|
|
16952
16906
|
return message;
|
|
16953
16907
|
},
|
|
16954
16908
|
};
|
|
16955
16909
|
function createBaseTimetable() {
|
|
16956
|
-
return { version: "", stopsAdjacency: [], routesAdjacency: [], serviceRoutes: [] };
|
|
16910
|
+
return { version: "", stopsAdjacency: [], routesAdjacency: [], serviceRoutes: [], tripContinuations: [] };
|
|
16957
16911
|
}
|
|
16958
16912
|
const Timetable$1 = {
|
|
16959
16913
|
encode(message, writer = new BinaryWriter()) {
|
|
@@ -16969,6 +16923,9 @@ const Timetable$1 = {
|
|
|
16969
16923
|
for (const v of message.serviceRoutes) {
|
|
16970
16924
|
ServiceRoute.encode(v, writer.uint32(34).fork()).join();
|
|
16971
16925
|
}
|
|
16926
|
+
for (const v of message.tripContinuations) {
|
|
16927
|
+
TripContinuationEntry.encode(v, writer.uint32(42).fork()).join();
|
|
16928
|
+
}
|
|
16972
16929
|
return writer;
|
|
16973
16930
|
},
|
|
16974
16931
|
decode(input, length) {
|
|
@@ -17006,6 +16963,13 @@ const Timetable$1 = {
|
|
|
17006
16963
|
message.serviceRoutes.push(ServiceRoute.decode(reader, reader.uint32()));
|
|
17007
16964
|
continue;
|
|
17008
16965
|
}
|
|
16966
|
+
case 5: {
|
|
16967
|
+
if (tag !== 42) {
|
|
16968
|
+
break;
|
|
16969
|
+
}
|
|
16970
|
+
message.tripContinuations.push(TripContinuationEntry.decode(reader, reader.uint32()));
|
|
16971
|
+
continue;
|
|
16972
|
+
}
|
|
17009
16973
|
}
|
|
17010
16974
|
if ((tag & 7) === 4 || tag === 0) {
|
|
17011
16975
|
break;
|
|
@@ -17017,44 +16981,49 @@ const Timetable$1 = {
|
|
|
17017
16981
|
fromJSON(object) {
|
|
17018
16982
|
return {
|
|
17019
16983
|
version: isSet(object.version) ? globalThis.String(object.version) : "",
|
|
17020
|
-
stopsAdjacency: globalThis.Array.isArray(object
|
|
16984
|
+
stopsAdjacency: globalThis.Array.isArray(object?.stopsAdjacency)
|
|
17021
16985
|
? object.stopsAdjacency.map((e) => StopAdjacency.fromJSON(e))
|
|
17022
16986
|
: [],
|
|
17023
|
-
routesAdjacency: globalThis.Array.isArray(object
|
|
16987
|
+
routesAdjacency: globalThis.Array.isArray(object?.routesAdjacency)
|
|
17024
16988
|
? object.routesAdjacency.map((e) => Route$2.fromJSON(e))
|
|
17025
16989
|
: [],
|
|
17026
|
-
serviceRoutes: globalThis.Array.isArray(object
|
|
16990
|
+
serviceRoutes: globalThis.Array.isArray(object?.serviceRoutes)
|
|
17027
16991
|
? object.serviceRoutes.map((e) => ServiceRoute.fromJSON(e))
|
|
17028
16992
|
: [],
|
|
16993
|
+
tripContinuations: globalThis.Array.isArray(object?.tripContinuations)
|
|
16994
|
+
? object.tripContinuations.map((e) => TripContinuationEntry.fromJSON(e))
|
|
16995
|
+
: [],
|
|
17029
16996
|
};
|
|
17030
16997
|
},
|
|
17031
16998
|
toJSON(message) {
|
|
17032
|
-
var _a, _b, _c;
|
|
17033
16999
|
const obj = {};
|
|
17034
17000
|
if (message.version !== "") {
|
|
17035
17001
|
obj.version = message.version;
|
|
17036
17002
|
}
|
|
17037
|
-
if (
|
|
17003
|
+
if (message.stopsAdjacency?.length) {
|
|
17038
17004
|
obj.stopsAdjacency = message.stopsAdjacency.map((e) => StopAdjacency.toJSON(e));
|
|
17039
17005
|
}
|
|
17040
|
-
if (
|
|
17006
|
+
if (message.routesAdjacency?.length) {
|
|
17041
17007
|
obj.routesAdjacency = message.routesAdjacency.map((e) => Route$2.toJSON(e));
|
|
17042
17008
|
}
|
|
17043
|
-
if (
|
|
17009
|
+
if (message.serviceRoutes?.length) {
|
|
17044
17010
|
obj.serviceRoutes = message.serviceRoutes.map((e) => ServiceRoute.toJSON(e));
|
|
17045
17011
|
}
|
|
17012
|
+
if (message.tripContinuations?.length) {
|
|
17013
|
+
obj.tripContinuations = message.tripContinuations.map((e) => TripContinuationEntry.toJSON(e));
|
|
17014
|
+
}
|
|
17046
17015
|
return obj;
|
|
17047
17016
|
},
|
|
17048
17017
|
create(base) {
|
|
17049
|
-
return Timetable$1.fromPartial(base
|
|
17018
|
+
return Timetable$1.fromPartial(base ?? {});
|
|
17050
17019
|
},
|
|
17051
17020
|
fromPartial(object) {
|
|
17052
|
-
var _a, _b, _c, _d;
|
|
17053
17021
|
const message = createBaseTimetable();
|
|
17054
|
-
message.version =
|
|
17055
|
-
message.stopsAdjacency =
|
|
17056
|
-
message.routesAdjacency =
|
|
17057
|
-
message.serviceRoutes =
|
|
17022
|
+
message.version = object.version ?? "";
|
|
17023
|
+
message.stopsAdjacency = object.stopsAdjacency?.map((e) => StopAdjacency.fromPartial(e)) || [];
|
|
17024
|
+
message.routesAdjacency = object.routesAdjacency?.map((e) => Route$2.fromPartial(e)) || [];
|
|
17025
|
+
message.serviceRoutes = object.serviceRoutes?.map((e) => ServiceRoute.fromPartial(e)) || [];
|
|
17026
|
+
message.tripContinuations = object.tripContinuations?.map((e) => TripContinuationEntry.fromPartial(e)) || [];
|
|
17058
17027
|
return message;
|
|
17059
17028
|
},
|
|
17060
17029
|
};
|
|
@@ -17091,6 +17060,11 @@ function isSet(value) {
|
|
|
17091
17060
|
* A class representing a time as minutes since midnight.
|
|
17092
17061
|
*/
|
|
17093
17062
|
class Time {
|
|
17063
|
+
/*
|
|
17064
|
+
* Number of minutes since midnight.
|
|
17065
|
+
Note that this value can go beyond 3600 to model services overlapping with the next day.
|
|
17066
|
+
*/
|
|
17067
|
+
minutesSinceMidnight;
|
|
17094
17068
|
/**
|
|
17095
17069
|
* Gets the infinity time as a Time instance.
|
|
17096
17070
|
* This represents a time that is conceptually beyond any real possible time.
|
|
@@ -17343,6 +17317,51 @@ const toPickupDropOffType = (numericalType) => {
|
|
|
17343
17317
|
* A route identifies all trips of a given service route sharing the same list of stops.
|
|
17344
17318
|
*/
|
|
17345
17319
|
let Route$1 = class Route {
|
|
17320
|
+
id;
|
|
17321
|
+
/**
|
|
17322
|
+
* Arrivals and departures encoded as minutes from midnight.
|
|
17323
|
+
* Format: [arrival1, departure1, arrival2, departure2, etc.]
|
|
17324
|
+
*/
|
|
17325
|
+
stopTimes;
|
|
17326
|
+
/**
|
|
17327
|
+
* PickUp and DropOff types represented as a 2-bit encoded Uint8Array.
|
|
17328
|
+
* Values (2 bits each):
|
|
17329
|
+
* 0: REGULAR
|
|
17330
|
+
* 1: NOT_AVAILABLE
|
|
17331
|
+
* 2: MUST_PHONE_AGENCY
|
|
17332
|
+
* 3: MUST_COORDINATE_WITH_DRIVER
|
|
17333
|
+
*
|
|
17334
|
+
* Encoding format: Each byte contains 2 pickup/drop-off pairs (4 bits each)
|
|
17335
|
+
* Bit layout per byte: [pickup_1 (2 bits)][drop_off_1 (2 bits)][pickup_0 (2 bits)][drop_off_0 (2 bits)]
|
|
17336
|
+
* Example: For stops 0 and 1 in a trip, one byte encodes all 4 values
|
|
17337
|
+
*/
|
|
17338
|
+
pickUpDropOffTypes;
|
|
17339
|
+
/**
|
|
17340
|
+
* A binary array of stopIds in the route.
|
|
17341
|
+
* [stop1, stop2, stop3,...]
|
|
17342
|
+
*/
|
|
17343
|
+
stops;
|
|
17344
|
+
/**
|
|
17345
|
+
* A reverse mapping of each stop with their index in the route:
|
|
17346
|
+
* {
|
|
17347
|
+
* 4: 0,
|
|
17348
|
+
* 5: 1,
|
|
17349
|
+
* ...
|
|
17350
|
+
* }
|
|
17351
|
+
*/
|
|
17352
|
+
stopIndices;
|
|
17353
|
+
/**
|
|
17354
|
+
* The identifier of the route as a service shown to users.
|
|
17355
|
+
*/
|
|
17356
|
+
serviceRouteId;
|
|
17357
|
+
/**
|
|
17358
|
+
* The total number of stops in the route.
|
|
17359
|
+
*/
|
|
17360
|
+
nbStops;
|
|
17361
|
+
/**
|
|
17362
|
+
* The total number of trips in the route.
|
|
17363
|
+
*/
|
|
17364
|
+
nbTrips;
|
|
17346
17365
|
constructor(id, stopTimes, pickUpDropOffTypes, stops, serviceRouteId) {
|
|
17347
17366
|
this.id = id;
|
|
17348
17367
|
this.stopTimes = stopTimes;
|
|
@@ -17354,7 +17373,14 @@ let Route$1 = class Route {
|
|
|
17354
17373
|
this.stopIndices = new Map();
|
|
17355
17374
|
for (let i = 0; i < stops.length; i++) {
|
|
17356
17375
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17357
|
-
|
|
17376
|
+
const stopId = stops[i];
|
|
17377
|
+
const existingIndices = this.stopIndices.get(stopId);
|
|
17378
|
+
if (existingIndices) {
|
|
17379
|
+
existingIndices.push(i);
|
|
17380
|
+
}
|
|
17381
|
+
else {
|
|
17382
|
+
this.stopIndices.set(stopId, [i]);
|
|
17383
|
+
}
|
|
17358
17384
|
}
|
|
17359
17385
|
}
|
|
17360
17386
|
/**
|
|
@@ -17364,7 +17390,6 @@ let Route$1 = class Route {
|
|
|
17364
17390
|
* @returns The new route.
|
|
17365
17391
|
*/
|
|
17366
17392
|
static of(params) {
|
|
17367
|
-
var _a, _b;
|
|
17368
17393
|
const { id, serviceRouteId, trips } = params;
|
|
17369
17394
|
if (trips.length === 0) {
|
|
17370
17395
|
throw new Error('At least one trip must be provided');
|
|
@@ -17411,8 +17436,8 @@ let Route$1 = class Route {
|
|
|
17411
17436
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17412
17437
|
const stop = trip.stops[stopIndex];
|
|
17413
17438
|
const globalIndex = tripIndex * numStops + stopIndex;
|
|
17414
|
-
const pickUp =
|
|
17415
|
-
const dropOff =
|
|
17439
|
+
const pickUp = stop.pickUpType ?? REGULAR;
|
|
17440
|
+
const dropOff = stop.dropOffType ?? REGULAR;
|
|
17416
17441
|
const byteIndex = Math.floor(globalIndex / 2);
|
|
17417
17442
|
const isSecondPair = globalIndex % 2 === 1;
|
|
17418
17443
|
if (isSecondPair) {
|
|
@@ -17442,24 +17467,6 @@ let Route$1 = class Route {
|
|
|
17442
17467
|
serviceRouteId: this.serviceRouteId,
|
|
17443
17468
|
};
|
|
17444
17469
|
}
|
|
17445
|
-
/**
|
|
17446
|
-
* Checks if stop A is before stop B in the route.
|
|
17447
|
-
*
|
|
17448
|
-
* @param stopA - The StopId of the first stop.
|
|
17449
|
-
* @param stopB - The StopId of the second stop.
|
|
17450
|
-
* @returns True if stop A is before stop B, false otherwise.
|
|
17451
|
-
*/
|
|
17452
|
-
isBefore(stopA, stopB) {
|
|
17453
|
-
const stopAIndex = this.stopIndices.get(stopA);
|
|
17454
|
-
if (stopAIndex === undefined) {
|
|
17455
|
-
throw new Error(`Stop index not found for ${stopA} in route ${this.serviceRouteId}`);
|
|
17456
|
-
}
|
|
17457
|
-
const stopBIndex = this.stopIndices.get(stopB);
|
|
17458
|
-
if (stopBIndex === undefined) {
|
|
17459
|
-
throw new Error(`Stop index not found for ${stopB} in route ${this.serviceRouteId}`);
|
|
17460
|
-
}
|
|
17461
|
-
return stopAIndex < stopBIndex;
|
|
17462
|
-
}
|
|
17463
17470
|
/**
|
|
17464
17471
|
* Retrieves the number of stops in the route.
|
|
17465
17472
|
*
|
|
@@ -17488,47 +17495,47 @@ let Route$1 = class Route {
|
|
|
17488
17495
|
/**
|
|
17489
17496
|
* Retrieves the arrival time at a specific stop for a given trip.
|
|
17490
17497
|
*
|
|
17491
|
-
* @param
|
|
17498
|
+
* @param stopIndex - The index of the stop in the route.
|
|
17492
17499
|
* @param tripIndex - The index of the trip.
|
|
17493
17500
|
* @returns The arrival time at the specified stop and trip as a Time object.
|
|
17494
17501
|
*/
|
|
17495
|
-
arrivalAt(
|
|
17496
|
-
const arrivalIndex = (tripIndex * this.stops.length +
|
|
17502
|
+
arrivalAt(stopIndex, tripIndex) {
|
|
17503
|
+
const arrivalIndex = (tripIndex * this.stops.length + stopIndex) * 2;
|
|
17497
17504
|
const arrival = this.stopTimes[arrivalIndex];
|
|
17498
17505
|
if (arrival === undefined) {
|
|
17499
|
-
throw new Error(`Arrival time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17506
|
+
throw new Error(`Arrival time not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17500
17507
|
}
|
|
17501
17508
|
return Time.fromMinutes(arrival);
|
|
17502
17509
|
}
|
|
17503
17510
|
/**
|
|
17504
17511
|
* Retrieves the departure time at a specific stop for a given trip.
|
|
17505
17512
|
*
|
|
17506
|
-
* @param
|
|
17513
|
+
* @param stopIndex - The index of the stop in the route.
|
|
17507
17514
|
* @param tripIndex - The index of the trip.
|
|
17508
17515
|
* @returns The departure time at the specified stop and trip as a Time object.
|
|
17509
17516
|
*/
|
|
17510
|
-
departureFrom(
|
|
17511
|
-
const departureIndex = (tripIndex * this.stops.length +
|
|
17517
|
+
departureFrom(stopIndex, tripIndex) {
|
|
17518
|
+
const departureIndex = (tripIndex * this.stops.length + stopIndex) * 2 + 1;
|
|
17512
17519
|
const departure = this.stopTimes[departureIndex];
|
|
17513
17520
|
if (departure === undefined) {
|
|
17514
|
-
throw new Error(`Departure time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17521
|
+
throw new Error(`Departure time not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17515
17522
|
}
|
|
17516
17523
|
return Time.fromMinutes(departure);
|
|
17517
17524
|
}
|
|
17518
17525
|
/**
|
|
17519
17526
|
* Retrieves the pick-up type for a specific stop and trip.
|
|
17520
17527
|
*
|
|
17521
|
-
* @param
|
|
17528
|
+
* @param stopIndex - The index of the stop in the route.
|
|
17522
17529
|
* @param tripIndex - The index of the trip.
|
|
17523
17530
|
* @returns The pick-up type at the specified stop and trip.
|
|
17524
17531
|
*/
|
|
17525
|
-
pickUpTypeFrom(
|
|
17526
|
-
const globalIndex = tripIndex * this.stops.length +
|
|
17532
|
+
pickUpTypeFrom(stopIndex, tripIndex) {
|
|
17533
|
+
const globalIndex = tripIndex * this.stops.length + stopIndex;
|
|
17527
17534
|
const byteIndex = Math.floor(globalIndex / 2);
|
|
17528
17535
|
const isSecondPair = globalIndex % 2 === 1;
|
|
17529
17536
|
const byte = this.pickUpDropOffTypes[byteIndex];
|
|
17530
17537
|
if (byte === undefined) {
|
|
17531
|
-
throw new Error(`Pick up type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17538
|
+
throw new Error(`Pick up type not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17532
17539
|
}
|
|
17533
17540
|
const pickUpValue = isSecondPair
|
|
17534
17541
|
? (byte >> 6) & 0x03 // Upper 2 bits for second pair
|
|
@@ -17538,17 +17545,17 @@ let Route$1 = class Route {
|
|
|
17538
17545
|
/**
|
|
17539
17546
|
* Retrieves the drop-off type for a specific stop and trip.
|
|
17540
17547
|
*
|
|
17541
|
-
* @param
|
|
17548
|
+
* @param stopIndex - The index of the stop in the route.
|
|
17542
17549
|
* @param tripIndex - The index of the trip.
|
|
17543
17550
|
* @returns The drop-off type at the specified stop and trip.
|
|
17544
17551
|
*/
|
|
17545
|
-
dropOffTypeAt(
|
|
17546
|
-
const globalIndex = tripIndex * this.stops.length +
|
|
17552
|
+
dropOffTypeAt(stopIndex, tripIndex) {
|
|
17553
|
+
const globalIndex = tripIndex * this.stops.length + stopIndex;
|
|
17547
17554
|
const byteIndex = Math.floor(globalIndex / 2);
|
|
17548
17555
|
const isSecondPair = globalIndex % 2 === 1;
|
|
17549
17556
|
const byte = this.pickUpDropOffTypes[byteIndex];
|
|
17550
17557
|
if (byte === undefined) {
|
|
17551
|
-
throw new Error(`Drop off type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17558
|
+
throw new Error(`Drop off type not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17552
17559
|
}
|
|
17553
17560
|
const dropOffValue = isSecondPair
|
|
17554
17561
|
? (byte >> 4) & 0x03 // Bits 4-5 for second pair
|
|
@@ -17560,14 +17567,14 @@ let Route$1 = class Route {
|
|
|
17560
17567
|
* optionally constrained by a latest trip index and a time before which the trip
|
|
17561
17568
|
* should not depart.
|
|
17562
17569
|
* *
|
|
17563
|
-
* @param
|
|
17570
|
+
* @param stopIndex - The route index of the stop where the trip should be found.
|
|
17564
17571
|
* @param [after=Time.origin()] - The earliest time after which the trip should depart.
|
|
17565
17572
|
* If not provided, searches all available trips.
|
|
17566
17573
|
* @param [beforeTrip] - (Optional) The index of the trip before which the search should be constrained.
|
|
17567
17574
|
* If not provided, searches all available trips.
|
|
17568
17575
|
* @returns The index of the earliest trip meeting the criteria, or undefined if no such trip is found.
|
|
17569
17576
|
*/
|
|
17570
|
-
findEarliestTrip(
|
|
17577
|
+
findEarliestTrip(stopIndex, after = Time.origin(), beforeTrip) {
|
|
17571
17578
|
if (this.nbTrips <= 0)
|
|
17572
17579
|
return undefined;
|
|
17573
17580
|
let hi = this.nbTrips - 1;
|
|
@@ -17579,7 +17586,7 @@ let Route$1 = class Route {
|
|
|
17579
17586
|
let lb = -1;
|
|
17580
17587
|
while (lo <= hi) {
|
|
17581
17588
|
const mid = (lo + hi) >>> 1;
|
|
17582
|
-
const depMid = this.departureFrom(
|
|
17589
|
+
const depMid = this.departureFrom(stopIndex, mid);
|
|
17583
17590
|
if (depMid.isBefore(after)) {
|
|
17584
17591
|
lo = mid + 1;
|
|
17585
17592
|
}
|
|
@@ -17590,8 +17597,8 @@ let Route$1 = class Route {
|
|
|
17590
17597
|
}
|
|
17591
17598
|
if (lb === -1)
|
|
17592
17599
|
return undefined;
|
|
17593
|
-
for (let t = lb; t < (beforeTrip
|
|
17594
|
-
const pickup = this.pickUpTypeFrom(
|
|
17600
|
+
for (let t = lb; t < (beforeTrip ?? this.nbTrips); t++) {
|
|
17601
|
+
const pickup = this.pickUpTypeFrom(stopIndex, t);
|
|
17595
17602
|
if (pickup !== 'NOT_AVAILABLE') {
|
|
17596
17603
|
return t;
|
|
17597
17604
|
}
|
|
@@ -17599,14 +17606,14 @@ let Route$1 = class Route {
|
|
|
17599
17606
|
return undefined;
|
|
17600
17607
|
}
|
|
17601
17608
|
/**
|
|
17602
|
-
* Retrieves the
|
|
17609
|
+
* Retrieves the indices of a stop within the route.
|
|
17603
17610
|
* @param stopId The StopId of the stop to locate in the route.
|
|
17604
|
-
* @returns
|
|
17611
|
+
* @returns An array of indices where the stop appears in the route, or an empty array if the stop is not found.
|
|
17605
17612
|
*/
|
|
17606
|
-
|
|
17613
|
+
stopRouteIndices(stopId) {
|
|
17607
17614
|
const stopIndex = this.stopIndices.get(stopId);
|
|
17608
17615
|
if (stopIndex === undefined) {
|
|
17609
|
-
|
|
17616
|
+
return [];
|
|
17610
17617
|
}
|
|
17611
17618
|
return stopIndex;
|
|
17612
17619
|
}
|
|
@@ -17624,6 +17631,51 @@ let Route$1 = class Route {
|
|
|
17624
17631
|
}
|
|
17625
17632
|
};
|
|
17626
17633
|
|
|
17634
|
+
// Each value uses 20 bits, allowing values from 0 to 1,048,575 (2^20 - 1)
|
|
17635
|
+
const VALUE_MASK = (1n << 20n) - 1n; // 0xFFFFF
|
|
17636
|
+
const MAX_VALUE = 1_048_575; // 2^20 - 1
|
|
17637
|
+
// Bit positions for each value in the 60-bit bigint
|
|
17638
|
+
const TRIP_INDEX_SHIFT = 0n;
|
|
17639
|
+
const ROUTE_ID_SHIFT = 20n;
|
|
17640
|
+
const STOP_INDEX_SHIFT = 40n;
|
|
17641
|
+
/**
|
|
17642
|
+
* Validates that a value fits within 20 bits (0 to 1,048,575)
|
|
17643
|
+
* @param value - The value to validate
|
|
17644
|
+
* @param name - The name of the value for error reporting
|
|
17645
|
+
* @throws Error if the value is out of range
|
|
17646
|
+
*/
|
|
17647
|
+
const validateValue = (value, name) => {
|
|
17648
|
+
if (value < 0 || value > MAX_VALUE) {
|
|
17649
|
+
throw new Error(`${name} must be between 0 and ${MAX_VALUE}, got ${value}`);
|
|
17650
|
+
}
|
|
17651
|
+
};
|
|
17652
|
+
/**
|
|
17653
|
+
* Encodes a stop index, route ID, and trip index into a single trip boarding ID.
|
|
17654
|
+
* @param stopIndex - The index of the stop within the route (0 to 1,048,575)
|
|
17655
|
+
* @param routeId - The route identifier (0 to 1,048,575)
|
|
17656
|
+
* @param tripIndex - The index of the trip within the route (0 to 1,048,575)
|
|
17657
|
+
* @returns The encoded trip ID as a bigint
|
|
17658
|
+
*/
|
|
17659
|
+
const encode = (stopIndex, routeId, tripIndex) => {
|
|
17660
|
+
validateValue(stopIndex, 'stopIndex');
|
|
17661
|
+
validateValue(routeId, 'routeId');
|
|
17662
|
+
validateValue(tripIndex, 'tripIndex');
|
|
17663
|
+
return ((BigInt(stopIndex) << STOP_INDEX_SHIFT) |
|
|
17664
|
+
(BigInt(routeId) << ROUTE_ID_SHIFT) |
|
|
17665
|
+
(BigInt(tripIndex) << TRIP_INDEX_SHIFT));
|
|
17666
|
+
};
|
|
17667
|
+
/**
|
|
17668
|
+
* Decodes a trip boarding ID back into its constituent stop index, route ID, and trip index.
|
|
17669
|
+
* @param tripBoardingId - The encoded trip ID
|
|
17670
|
+
* @returns A tuple containing [stopIndex, routeId, tripIndex]
|
|
17671
|
+
*/
|
|
17672
|
+
const decode = (tripBoardingId) => {
|
|
17673
|
+
const stopIndex = Number((tripBoardingId >> STOP_INDEX_SHIFT) & VALUE_MASK);
|
|
17674
|
+
const routeId = Number((tripBoardingId >> ROUTE_ID_SHIFT) & VALUE_MASK);
|
|
17675
|
+
const tripIndex = Number((tripBoardingId >> TRIP_INDEX_SHIFT) & VALUE_MASK);
|
|
17676
|
+
return [stopIndex, routeId, tripIndex];
|
|
17677
|
+
};
|
|
17678
|
+
|
|
17627
17679
|
const isLittleEndian = (() => {
|
|
17628
17680
|
const buffer = new ArrayBuffer(4);
|
|
17629
17681
|
const view = new DataView(buffer);
|
|
@@ -17693,14 +17745,15 @@ const serializeStopsAdjacency = (stopsAdjacency) => {
|
|
|
17693
17745
|
return stopsAdjacency.map((value) => {
|
|
17694
17746
|
return {
|
|
17695
17747
|
transfers: value.transfers
|
|
17696
|
-
? value.transfers.map((transfer) => (
|
|
17697
|
-
|
|
17698
|
-
|
|
17748
|
+
? value.transfers.map((transfer) => ({
|
|
17749
|
+
destination: transfer.destination,
|
|
17750
|
+
type: serializeTransferType(transfer.type),
|
|
17751
|
+
...(transfer.minTransferTime !== undefined && {
|
|
17752
|
+
minTransferTime: transfer.minTransferTime.toSeconds(),
|
|
17753
|
+
}),
|
|
17754
|
+
}))
|
|
17699
17755
|
: [],
|
|
17700
17756
|
routes: value.routes,
|
|
17701
|
-
tripContinuations: value.tripContinuations
|
|
17702
|
-
? serializeTripContinuations(value.tripContinuations)
|
|
17703
|
-
: [],
|
|
17704
17757
|
};
|
|
17705
17758
|
});
|
|
17706
17759
|
};
|
|
@@ -17735,9 +17788,13 @@ const deserializeStopsAdjacency = (protoStopsAdjacency) => {
|
|
|
17735
17788
|
for (let j = 0; j < value.transfers.length; j++) {
|
|
17736
17789
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17737
17790
|
const transfer = value.transfers[j];
|
|
17738
|
-
const newTransfer =
|
|
17739
|
-
|
|
17740
|
-
|
|
17791
|
+
const newTransfer = {
|
|
17792
|
+
destination: transfer.destination,
|
|
17793
|
+
type: parseTransferType(transfer.type),
|
|
17794
|
+
...(transfer.minTransferTime !== undefined && {
|
|
17795
|
+
minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
|
|
17796
|
+
}),
|
|
17797
|
+
};
|
|
17741
17798
|
transfers.push(newTransfer);
|
|
17742
17799
|
}
|
|
17743
17800
|
const stopAdjacency = {
|
|
@@ -17746,10 +17803,6 @@ const deserializeStopsAdjacency = (protoStopsAdjacency) => {
|
|
|
17746
17803
|
if (transfers.length > 0) {
|
|
17747
17804
|
stopAdjacency.transfers = transfers;
|
|
17748
17805
|
}
|
|
17749
|
-
const deserializedTripContinuations = deserializeTripContinuations(value.tripContinuations);
|
|
17750
|
-
if (deserializedTripContinuations.size > 0) {
|
|
17751
|
-
stopAdjacency.tripContinuations = deserializedTripContinuations;
|
|
17752
|
-
}
|
|
17753
17806
|
result.push(stopAdjacency);
|
|
17754
17807
|
}
|
|
17755
17808
|
return result;
|
|
@@ -17856,11 +17909,14 @@ const serializeRouteType = (type) => {
|
|
|
17856
17909
|
};
|
|
17857
17910
|
const serializeTripContinuations = (tripContinuations) => {
|
|
17858
17911
|
const result = [];
|
|
17859
|
-
for (const [
|
|
17912
|
+
for (const [tripBoardingId, boardings] of tripContinuations.entries()) {
|
|
17913
|
+
const [originStopIndex, originRouteId, originTripIndex] = decode(tripBoardingId);
|
|
17860
17914
|
result.push({
|
|
17861
|
-
|
|
17862
|
-
|
|
17863
|
-
|
|
17915
|
+
originStopIndex,
|
|
17916
|
+
originRouteId,
|
|
17917
|
+
originTripIndex,
|
|
17918
|
+
continuations: boardings.map((tripBoarding) => ({
|
|
17919
|
+
hopOnStopIndex: tripBoarding.hopOnStopIndex,
|
|
17864
17920
|
routeId: tripBoarding.routeId,
|
|
17865
17921
|
tripIndex: tripBoarding.tripIndex,
|
|
17866
17922
|
})),
|
|
@@ -17873,28 +17929,17 @@ const deserializeTripContinuations = (protoTripContinuations) => {
|
|
|
17873
17929
|
for (let i = 0; i < protoTripContinuations.length; i++) {
|
|
17874
17930
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17875
17931
|
const entry = protoTripContinuations[i];
|
|
17876
|
-
const
|
|
17877
|
-
|
|
17932
|
+
const tripBoardingId = encode(entry.originStopIndex, entry.originRouteId, entry.originTripIndex);
|
|
17933
|
+
const tripBoardings = entry.continuations.map((protoTripBoarding) => ({
|
|
17934
|
+
hopOnStopIndex: protoTripBoarding.hopOnStopIndex,
|
|
17878
17935
|
routeId: protoTripBoarding.routeId,
|
|
17879
17936
|
tripIndex: protoTripBoarding.tripIndex,
|
|
17880
17937
|
}));
|
|
17881
|
-
result.set(
|
|
17938
|
+
result.set(tripBoardingId, tripBoardings);
|
|
17882
17939
|
}
|
|
17883
17940
|
return result;
|
|
17884
17941
|
};
|
|
17885
17942
|
|
|
17886
|
-
// const ROUTE_ID_BITS = 17;
|
|
17887
|
-
const TRIP_INDEX_BITS = 15;
|
|
17888
|
-
/**
|
|
17889
|
-
* Encodes a route ID and trip index into a single trip ID.
|
|
17890
|
-
* @param routeId - The route identifier, needs to fit on 17 bits
|
|
17891
|
-
* @param tripIndex - The index of the trip within the route, needs to fit on 15 bits
|
|
17892
|
-
* @returns The encoded trip ID
|
|
17893
|
-
*/
|
|
17894
|
-
const encode = (routeId, tripIndex) => {
|
|
17895
|
-
return (routeId << TRIP_INDEX_BITS) | tripIndex;
|
|
17896
|
-
};
|
|
17897
|
-
|
|
17898
17943
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
17899
17944
|
const ALL_TRANSPORT_MODES = new Set([
|
|
17900
17945
|
'TRAM',
|
|
@@ -17909,21 +17954,26 @@ const ALL_TRANSPORT_MODES = new Set([
|
|
|
17909
17954
|
'MONORAIL',
|
|
17910
17955
|
]);
|
|
17911
17956
|
const EMPTY_TRIP_CONTINUATIONS = [];
|
|
17912
|
-
const CURRENT_VERSION = '0.0.
|
|
17957
|
+
const CURRENT_VERSION = '0.0.9';
|
|
17913
17958
|
/**
|
|
17914
17959
|
* The internal transit timetable format.
|
|
17915
17960
|
*/
|
|
17916
17961
|
class Timetable {
|
|
17917
|
-
|
|
17962
|
+
stopsAdjacency;
|
|
17963
|
+
routesAdjacency;
|
|
17964
|
+
serviceRoutes;
|
|
17965
|
+
tripContinuations;
|
|
17966
|
+
activeStops;
|
|
17967
|
+
constructor(stopsAdjacency, routesAdjacency, routes, tripContinuations) {
|
|
17918
17968
|
this.stopsAdjacency = stopsAdjacency;
|
|
17919
17969
|
this.routesAdjacency = routesAdjacency;
|
|
17920
17970
|
this.serviceRoutes = routes;
|
|
17971
|
+
this.tripContinuations = tripContinuations;
|
|
17921
17972
|
this.activeStops = new Set();
|
|
17922
17973
|
for (let i = 0; i < stopsAdjacency.length; i++) {
|
|
17923
17974
|
const stop = stopsAdjacency[i];
|
|
17924
17975
|
if (stop.routes.length > 0 ||
|
|
17925
|
-
(stop.transfers && stop.transfers.length > 0)
|
|
17926
|
-
(stop.tripContinuations && stop.tripContinuations.size > 0)) {
|
|
17976
|
+
(stop.transfers && stop.transfers.length > 0)) {
|
|
17927
17977
|
this.activeStops.add(i);
|
|
17928
17978
|
}
|
|
17929
17979
|
}
|
|
@@ -17939,6 +17989,7 @@ class Timetable {
|
|
|
17939
17989
|
stopsAdjacency: serializeStopsAdjacency(this.stopsAdjacency),
|
|
17940
17990
|
routesAdjacency: serializeRoutesAdjacency(this.routesAdjacency),
|
|
17941
17991
|
serviceRoutes: serializeServiceRoutesMap(this.serviceRoutes),
|
|
17992
|
+
tripContinuations: serializeTripContinuations(this.tripContinuations || new Map()),
|
|
17942
17993
|
};
|
|
17943
17994
|
const writer = new BinaryWriter();
|
|
17944
17995
|
Timetable$1.encode(protoTimetable, writer);
|
|
@@ -17956,7 +18007,7 @@ class Timetable {
|
|
|
17956
18007
|
if (protoTimetable.version !== CURRENT_VERSION) {
|
|
17957
18008
|
throw new Error(`Unsupported timetable version ${protoTimetable.version}`);
|
|
17958
18009
|
}
|
|
17959
|
-
return new Timetable(deserializeStopsAdjacency(protoTimetable.stopsAdjacency), deserializeRoutesAdjacency(protoTimetable.routesAdjacency), deserializeServiceRoutesMap(protoTimetable.serviceRoutes));
|
|
18010
|
+
return new Timetable(deserializeStopsAdjacency(protoTimetable.stopsAdjacency), deserializeRoutesAdjacency(protoTimetable.routesAdjacency), deserializeServiceRoutesMap(protoTimetable.serviceRoutes), deserializeTripContinuations(protoTimetable.tripContinuations));
|
|
17960
18011
|
}
|
|
17961
18012
|
/**
|
|
17962
18013
|
* Checks if the given stop is active on the timetable.
|
|
@@ -17995,18 +18046,17 @@ class Timetable {
|
|
|
17995
18046
|
/**
|
|
17996
18047
|
* Retrieves all trip continuation options available at the specified stop for a given trip.
|
|
17997
18048
|
*
|
|
17998
|
-
* @param
|
|
18049
|
+
* @param stopIndex - The index in the route of the stop to get trip continuations for.
|
|
18050
|
+
* @param routeId - The ID of the route to get continuations for.
|
|
17999
18051
|
* @param tripIndex - The index of the trip to get continuations for.
|
|
18000
18052
|
* @returns An array of trip continuation options available at the stop for the specified trip.
|
|
18001
18053
|
*/
|
|
18002
|
-
getContinuousTrips(
|
|
18003
|
-
|
|
18004
|
-
|
|
18005
|
-
|
|
18006
|
-
throw new Error(`Stop ID ${stopId} not found`);
|
|
18054
|
+
getContinuousTrips(stopIndex, routeId, tripIndex) {
|
|
18055
|
+
const tripContinuations = this.tripContinuations?.get(encode(stopIndex, routeId, tripIndex));
|
|
18056
|
+
if (!tripContinuations) {
|
|
18057
|
+
return EMPTY_TRIP_CONTINUATIONS;
|
|
18007
18058
|
}
|
|
18008
|
-
return
|
|
18009
|
-
EMPTY_TRIP_CONTINUATIONS);
|
|
18059
|
+
return tripContinuations;
|
|
18010
18060
|
}
|
|
18011
18061
|
/**
|
|
18012
18062
|
* Retrieves the service route associated with the given route.
|
|
@@ -18046,12 +18096,12 @@ class Timetable {
|
|
|
18046
18096
|
}
|
|
18047
18097
|
/**
|
|
18048
18098
|
* Finds routes that are reachable from a set of stop IDs.
|
|
18049
|
-
* Also identifies the first stop available to hop on each route among
|
|
18099
|
+
* Also identifies the first stop index available to hop on each route among
|
|
18050
18100
|
* the input stops.
|
|
18051
18101
|
*
|
|
18052
18102
|
* @param fromStops - The set of stop IDs to find reachable routes from.
|
|
18053
18103
|
* @param transportModes - The set of transport modes to consider for reachable routes.
|
|
18054
|
-
* @returns A map of reachable routes to the first stop available to hop on each route.
|
|
18104
|
+
* @returns A map of reachable routes to the first stop index available to hop on each route.
|
|
18055
18105
|
*/
|
|
18056
18106
|
findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
|
|
18057
18107
|
const reachableRoutes = new Map();
|
|
@@ -18064,15 +18114,17 @@ class Timetable {
|
|
|
18064
18114
|
});
|
|
18065
18115
|
for (let j = 0; j < validRoutes.length; j++) {
|
|
18066
18116
|
const route = validRoutes[j];
|
|
18067
|
-
const
|
|
18068
|
-
|
|
18069
|
-
|
|
18117
|
+
const originStopIndices = route.stopRouteIndices(originStop);
|
|
18118
|
+
const originStopIndex = originStopIndices[0];
|
|
18119
|
+
const existingHopOnStopIndex = reachableRoutes.get(route);
|
|
18120
|
+
if (existingHopOnStopIndex !== undefined) {
|
|
18121
|
+
if (originStopIndex < existingHopOnStopIndex) {
|
|
18070
18122
|
// if the current stop is before the existing hop on stop, replace it
|
|
18071
|
-
reachableRoutes.set(route,
|
|
18123
|
+
reachableRoutes.set(route, originStopIndex);
|
|
18072
18124
|
}
|
|
18073
18125
|
}
|
|
18074
18126
|
else {
|
|
18075
|
-
reachableRoutes.set(route,
|
|
18127
|
+
reachableRoutes.set(route, originStopIndex);
|
|
18076
18128
|
}
|
|
18077
18129
|
}
|
|
18078
18130
|
}
|
|
@@ -20048,35 +20100,22 @@ const parseCsv = (stream, numericColumns = []) => {
|
|
|
20048
20100
|
* @param profile A configuration object defining the specificities of the GTFS feed.
|
|
20049
20101
|
* @returns A map of all the valid routes.
|
|
20050
20102
|
*/
|
|
20051
|
-
const parseRoutes =
|
|
20052
|
-
var _a, e_1, _b, _c;
|
|
20103
|
+
const parseRoutes = async (routesStream, profile = standardProfile) => {
|
|
20053
20104
|
const routes = new Map();
|
|
20054
|
-
|
|
20055
|
-
|
|
20056
|
-
|
|
20057
|
-
|
|
20058
|
-
|
|
20059
|
-
|
|
20060
|
-
const routeType = profile.routeTypeParser(line.route_type);
|
|
20061
|
-
if (routeType === undefined) {
|
|
20062
|
-
log.info(`Unsupported route type ${line.route_type} for route ${line.route_id}.`);
|
|
20063
|
-
continue;
|
|
20064
|
-
}
|
|
20065
|
-
routes.set(line.route_id, {
|
|
20066
|
-
name: line.route_short_name,
|
|
20067
|
-
type: routeType,
|
|
20068
|
-
});
|
|
20069
|
-
}
|
|
20070
|
-
}
|
|
20071
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
20072
|
-
finally {
|
|
20073
|
-
try {
|
|
20074
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
20105
|
+
for await (const rawLine of parseCsv(routesStream, ['route_type'])) {
|
|
20106
|
+
const line = rawLine;
|
|
20107
|
+
const routeType = profile.routeTypeParser(line.route_type);
|
|
20108
|
+
if (routeType === undefined) {
|
|
20109
|
+
log.info(`Unsupported route type ${line.route_type} for route ${line.route_id}.`);
|
|
20110
|
+
continue;
|
|
20075
20111
|
}
|
|
20076
|
-
|
|
20112
|
+
routes.set(line.route_id, {
|
|
20113
|
+
name: line.route_short_name,
|
|
20114
|
+
type: routeType,
|
|
20115
|
+
});
|
|
20077
20116
|
}
|
|
20078
20117
|
return routes;
|
|
20079
|
-
}
|
|
20118
|
+
};
|
|
20080
20119
|
/**
|
|
20081
20120
|
* Creates an array of ServiceRoute objects by combining GTFS route data with service route mappings.
|
|
20082
20121
|
*
|
|
@@ -20139,46 +20178,33 @@ const weekdays = {
|
|
|
20139
20178
|
* @param date The active date.
|
|
20140
20179
|
* @param calendarStream A readable stream for the GTFS calendar.txt file.
|
|
20141
20180
|
*/
|
|
20142
|
-
const parseCalendar = (calendarStream, serviceIds, date) =>
|
|
20143
|
-
var _a, e_1, _b, _c;
|
|
20181
|
+
const parseCalendar = async (calendarStream, serviceIds, date) => {
|
|
20144
20182
|
const activeDate = toGtfsDate(date);
|
|
20145
20183
|
const weekday = date.weekday;
|
|
20146
20184
|
const weekdayIndex = weekdays[weekday];
|
|
20147
|
-
|
|
20148
|
-
|
|
20149
|
-
|
|
20150
|
-
|
|
20151
|
-
|
|
20152
|
-
|
|
20153
|
-
|
|
20154
|
-
|
|
20155
|
-
|
|
20156
|
-
|
|
20157
|
-
|
|
20158
|
-
|
|
20159
|
-
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
const line = rawLine;
|
|
20163
|
-
if (activeDate < line.start_date || activeDate > line.end_date) {
|
|
20164
|
-
// If the service is not valid on this date
|
|
20165
|
-
continue;
|
|
20166
|
-
}
|
|
20167
|
-
if (line[weekdayIndex] !== 1) {
|
|
20168
|
-
// If the service is not valid on this week day
|
|
20169
|
-
continue;
|
|
20170
|
-
}
|
|
20171
|
-
serviceIds.add(line['service_id']);
|
|
20185
|
+
for await (const rawLine of parseCsv(calendarStream, [
|
|
20186
|
+
'monday',
|
|
20187
|
+
'tuesday',
|
|
20188
|
+
'wednesday',
|
|
20189
|
+
'thursday',
|
|
20190
|
+
'friday',
|
|
20191
|
+
'saturday',
|
|
20192
|
+
'sunday',
|
|
20193
|
+
'start_date',
|
|
20194
|
+
'end_date',
|
|
20195
|
+
])) {
|
|
20196
|
+
const line = rawLine;
|
|
20197
|
+
if (activeDate < line.start_date || activeDate > line.end_date) {
|
|
20198
|
+
// If the service is not valid on this date
|
|
20199
|
+
continue;
|
|
20172
20200
|
}
|
|
20173
|
-
|
|
20174
|
-
|
|
20175
|
-
|
|
20176
|
-
try {
|
|
20177
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
20201
|
+
if (line[weekdayIndex] !== 1) {
|
|
20202
|
+
// If the service is not valid on this week day
|
|
20203
|
+
continue;
|
|
20178
20204
|
}
|
|
20179
|
-
|
|
20205
|
+
serviceIds.add(line['service_id']);
|
|
20180
20206
|
}
|
|
20181
|
-
}
|
|
20207
|
+
};
|
|
20182
20208
|
/**
|
|
20183
20209
|
* Parses a gtfs calendar_dates.txt file and finds the service ids valid at a given date.
|
|
20184
20210
|
*
|
|
@@ -20186,39 +20212,24 @@ const parseCalendar = (calendarStream, serviceIds, date) => __awaiter(void 0, vo
|
|
|
20186
20212
|
* @param date The active date, in the format "YYYYMMDD".
|
|
20187
20213
|
* @param calendarDatesStream A readable stream for the GTFS calendar_dates.txt file.
|
|
20188
20214
|
*/
|
|
20189
|
-
const parseCalendarDates = (calendarDatesStream, serviceIds, date) =>
|
|
20190
|
-
var _a, e_2, _b, _c;
|
|
20215
|
+
const parseCalendarDates = async (calendarDatesStream, serviceIds, date) => {
|
|
20191
20216
|
const activeDate = toGtfsDate(date);
|
|
20192
|
-
|
|
20193
|
-
|
|
20194
|
-
|
|
20195
|
-
|
|
20196
|
-
|
|
20197
|
-
|
|
20198
|
-
|
|
20199
|
-
|
|
20200
|
-
|
|
20201
|
-
if (line.date !== activeDate) {
|
|
20202
|
-
// No rule on the active date
|
|
20203
|
-
}
|
|
20204
|
-
else if (line.exception_type === 2 && serviceIds.has(line.service_id)) {
|
|
20205
|
-
// Service has been removed for the specified date.
|
|
20206
|
-
serviceIds.delete(line.service_id);
|
|
20207
|
-
}
|
|
20208
|
-
else if (line.exception_type === 1) {
|
|
20209
|
-
// Service is present on the active date
|
|
20210
|
-
serviceIds.add(line.service_id);
|
|
20211
|
-
}
|
|
20217
|
+
for await (const rawLine of parseCsv(calendarDatesStream, [
|
|
20218
|
+
'date',
|
|
20219
|
+
'exception_type',
|
|
20220
|
+
])) {
|
|
20221
|
+
const line = rawLine;
|
|
20222
|
+
if (line.date !== activeDate) ;
|
|
20223
|
+
else if (line.exception_type === 2 && serviceIds.has(line.service_id)) {
|
|
20224
|
+
// Service has been removed for the specified date.
|
|
20225
|
+
serviceIds.delete(line.service_id);
|
|
20212
20226
|
}
|
|
20213
|
-
|
|
20214
|
-
|
|
20215
|
-
|
|
20216
|
-
try {
|
|
20217
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
20227
|
+
else if (line.exception_type === 1) {
|
|
20228
|
+
// Service is present on the active date
|
|
20229
|
+
serviceIds.add(line.service_id);
|
|
20218
20230
|
}
|
|
20219
|
-
finally { if (e_2) throw e_2.error; }
|
|
20220
20231
|
}
|
|
20221
|
-
}
|
|
20232
|
+
};
|
|
20222
20233
|
|
|
20223
20234
|
/**
|
|
20224
20235
|
* Parses the stops.txt file from a GTFS feed.
|
|
@@ -20226,33 +20237,30 @@ const parseCalendarDates = (calendarDatesStream, serviceIds, date) => __awaiter(
|
|
|
20226
20237
|
* @param stopsStream The readable stream containing the stops data.
|
|
20227
20238
|
* @return A mapping of stop IDs to corresponding stop details.
|
|
20228
20239
|
*/
|
|
20229
|
-
const parseStops = (stopsStream) =>
|
|
20230
|
-
var _a, e_1, _b, _c;
|
|
20240
|
+
const parseStops = async (stopsStream) => {
|
|
20231
20241
|
const parsedStops = new Map();
|
|
20232
20242
|
let i = 0;
|
|
20233
|
-
|
|
20234
|
-
|
|
20235
|
-
|
|
20236
|
-
|
|
20237
|
-
|
|
20238
|
-
|
|
20239
|
-
|
|
20240
|
-
|
|
20241
|
-
|
|
20242
|
-
|
|
20243
|
-
|
|
20244
|
-
|
|
20245
|
-
|
|
20246
|
-
|
|
20247
|
-
|
|
20248
|
-
|
|
20249
|
-
|
|
20250
|
-
|
|
20251
|
-
|
|
20252
|
-
|
|
20253
|
-
|
|
20254
|
-
}
|
|
20255
|
-
finally { if (e_1) throw e_1.error; }
|
|
20243
|
+
for await (const rawLine of parseCsv(stopsStream, [
|
|
20244
|
+
'stop_lat',
|
|
20245
|
+
'stop_lon',
|
|
20246
|
+
'location_type',
|
|
20247
|
+
])) {
|
|
20248
|
+
const line = rawLine;
|
|
20249
|
+
const stop = {
|
|
20250
|
+
id: i,
|
|
20251
|
+
sourceStopId: line.stop_id,
|
|
20252
|
+
name: line.stop_name,
|
|
20253
|
+
lat: line.stop_lat,
|
|
20254
|
+
lon: line.stop_lon,
|
|
20255
|
+
locationType: line.location_type
|
|
20256
|
+
? parseGtfsLocationType(line.location_type)
|
|
20257
|
+
: 'SIMPLE_STOP_OR_PLATFORM',
|
|
20258
|
+
...(line.platform_code && { platform: line.platform_code }),
|
|
20259
|
+
children: [],
|
|
20260
|
+
...(line.parent_station && { parentSourceId: line.parent_station }),
|
|
20261
|
+
};
|
|
20262
|
+
parsedStops.set(line.stop_id, stop);
|
|
20263
|
+
i = i + 1;
|
|
20256
20264
|
}
|
|
20257
20265
|
for (const [sourceStopId, stop] of parsedStops) {
|
|
20258
20266
|
if (stop.parentSourceId) {
|
|
@@ -20266,7 +20274,7 @@ const parseStops = (stopsStream) => __awaiter(void 0, void 0, void 0, function*
|
|
|
20266
20274
|
}
|
|
20267
20275
|
}
|
|
20268
20276
|
return parsedStops;
|
|
20269
|
-
}
|
|
20277
|
+
};
|
|
20270
20278
|
const parseGtfsLocationType = (gtfsLocationType) => {
|
|
20271
20279
|
switch (gtfsLocationType) {
|
|
20272
20280
|
case 0:
|
|
@@ -20289,85 +20297,158 @@ const parseGtfsLocationType = (gtfsLocationType) => {
|
|
|
20289
20297
|
* @param stopsStream The readable stream containing the stops data.
|
|
20290
20298
|
* @return A mapping of stop IDs to corresponding stop details.
|
|
20291
20299
|
*/
|
|
20292
|
-
const parseTransfers = (transfersStream, stopsMap) =>
|
|
20293
|
-
var _a, e_1, _b, _c;
|
|
20300
|
+
const parseTransfers = async (transfersStream, stopsMap) => {
|
|
20294
20301
|
const transfers = new Map();
|
|
20295
|
-
const tripContinuations =
|
|
20296
|
-
|
|
20297
|
-
|
|
20298
|
-
|
|
20299
|
-
|
|
20300
|
-
|
|
20301
|
-
|
|
20302
|
-
|
|
20303
|
-
|
|
20304
|
-
|
|
20305
|
-
|
|
20306
|
-
|
|
20307
|
-
|
|
20308
|
-
|
|
20309
|
-
|
|
20310
|
-
|
|
20311
|
-
|
|
20312
|
-
}
|
|
20313
|
-
|
|
20314
|
-
|
|
20315
|
-
|
|
20316
|
-
|
|
20317
|
-
|
|
20318
|
-
|
|
20319
|
-
|
|
20320
|
-
|
|
20321
|
-
transferEntry.to_trip_id === '') {
|
|
20322
|
-
console.warn(`Unsupported in-seat transfer, missing from_trip_id and/or to_trip_id.`);
|
|
20323
|
-
continue;
|
|
20324
|
-
}
|
|
20325
|
-
const tripBoardingEntry = {
|
|
20326
|
-
fromTrip: transferEntry.from_trip_id,
|
|
20327
|
-
toTrip: transferEntry.to_trip_id,
|
|
20328
|
-
hopOnStop: toStop.id,
|
|
20329
|
-
};
|
|
20330
|
-
const existingBoardings = tripContinuations.get(fromStop.id);
|
|
20331
|
-
if (existingBoardings) {
|
|
20332
|
-
existingBoardings.push(tripBoardingEntry);
|
|
20333
|
-
}
|
|
20334
|
-
else {
|
|
20335
|
-
tripContinuations.set(fromStop.id, [tripBoardingEntry]);
|
|
20336
|
-
}
|
|
20337
|
-
continue;
|
|
20338
|
-
}
|
|
20339
|
-
if (transferEntry.from_trip_id && transferEntry.to_trip_id) {
|
|
20340
|
-
console.warn(`Unsupported transfer of type ${transferEntry.transfer_type} between trips ${transferEntry.from_trip_id} and ${transferEntry.to_trip_id}.`);
|
|
20341
|
-
continue;
|
|
20342
|
-
}
|
|
20343
|
-
if (transferEntry.from_route_id && transferEntry.to_route_id) {
|
|
20344
|
-
console.warn(`Unsupported transfer of type ${transferEntry.transfer_type} between routes ${transferEntry.from_route_id} and ${transferEntry.to_route_id}.`);
|
|
20302
|
+
const tripContinuations = [];
|
|
20303
|
+
for await (const rawLine of parseCsv(transfersStream, [
|
|
20304
|
+
'transfer_type',
|
|
20305
|
+
'min_transfer_time',
|
|
20306
|
+
])) {
|
|
20307
|
+
const transferEntry = rawLine;
|
|
20308
|
+
if (transferEntry.transfer_type === 3 ||
|
|
20309
|
+
transferEntry.transfer_type === 5) {
|
|
20310
|
+
continue;
|
|
20311
|
+
}
|
|
20312
|
+
if (!transferEntry.from_stop_id || !transferEntry.to_stop_id) {
|
|
20313
|
+
console.warn(`Missing transfer origin or destination stop.`);
|
|
20314
|
+
continue;
|
|
20315
|
+
}
|
|
20316
|
+
const fromStop = stopsMap.get(transferEntry.from_stop_id);
|
|
20317
|
+
const toStop = stopsMap.get(transferEntry.to_stop_id);
|
|
20318
|
+
if (!fromStop || !toStop) {
|
|
20319
|
+
console.warn(`Transfer references non-existent stop(s): from_stop_id=${transferEntry.from_stop_id}, to_stop_id=${transferEntry.to_stop_id}`);
|
|
20320
|
+
continue;
|
|
20321
|
+
}
|
|
20322
|
+
if (transferEntry.transfer_type === 4) {
|
|
20323
|
+
if (transferEntry.from_trip_id === undefined ||
|
|
20324
|
+
transferEntry.from_trip_id === '' ||
|
|
20325
|
+
transferEntry.to_trip_id === undefined ||
|
|
20326
|
+
transferEntry.to_trip_id === '') {
|
|
20327
|
+
console.warn(`Unsupported in-seat transfer, missing from_trip_id and/or to_trip_id.`);
|
|
20345
20328
|
continue;
|
|
20346
20329
|
}
|
|
20347
|
-
|
|
20348
|
-
|
|
20349
|
-
|
|
20350
|
-
|
|
20351
|
-
|
|
20352
|
-
|
|
20353
|
-
|
|
20354
|
-
|
|
20355
|
-
fromStopTransfers.push(transfer);
|
|
20356
|
-
transfers.set(fromStop.id, fromStopTransfers);
|
|
20330
|
+
const tripBoardingEntry = {
|
|
20331
|
+
fromStop: fromStop.id,
|
|
20332
|
+
fromTrip: transferEntry.from_trip_id,
|
|
20333
|
+
toStop: toStop.id,
|
|
20334
|
+
toTrip: transferEntry.to_trip_id,
|
|
20335
|
+
};
|
|
20336
|
+
tripContinuations.push(tripBoardingEntry);
|
|
20337
|
+
continue;
|
|
20357
20338
|
}
|
|
20358
|
-
|
|
20359
|
-
|
|
20360
|
-
|
|
20361
|
-
try {
|
|
20362
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
20339
|
+
if (transferEntry.from_trip_id && transferEntry.to_trip_id) {
|
|
20340
|
+
console.warn(`Unsupported transfer of type ${transferEntry.transfer_type} between trips ${transferEntry.from_trip_id} and ${transferEntry.to_trip_id}.`);
|
|
20341
|
+
continue;
|
|
20363
20342
|
}
|
|
20364
|
-
|
|
20343
|
+
if (transferEntry.from_route_id && transferEntry.to_route_id) {
|
|
20344
|
+
console.warn(`Unsupported transfer of type ${transferEntry.transfer_type} between routes ${transferEntry.from_route_id} and ${transferEntry.to_route_id}.`);
|
|
20345
|
+
continue;
|
|
20346
|
+
}
|
|
20347
|
+
if (transferEntry.transfer_type === 2 &&
|
|
20348
|
+
transferEntry.min_transfer_time === undefined) {
|
|
20349
|
+
console.info(`Missing minimum transfer time between ${transferEntry.from_stop_id} and ${transferEntry.to_stop_id}.`);
|
|
20350
|
+
}
|
|
20351
|
+
const transfer = {
|
|
20352
|
+
destination: toStop.id,
|
|
20353
|
+
type: parseGtfsTransferType(transferEntry.transfer_type),
|
|
20354
|
+
...(transferEntry.min_transfer_time !== undefined && {
|
|
20355
|
+
minTransferTime: Duration.fromSeconds(transferEntry.min_transfer_time),
|
|
20356
|
+
}),
|
|
20357
|
+
};
|
|
20358
|
+
const fromStopTransfers = transfers.get(fromStop.id) || [];
|
|
20359
|
+
fromStopTransfers.push(transfer);
|
|
20360
|
+
transfers.set(fromStop.id, fromStopTransfers);
|
|
20365
20361
|
}
|
|
20366
20362
|
return {
|
|
20367
20363
|
transfers,
|
|
20368
20364
|
tripContinuations,
|
|
20369
20365
|
};
|
|
20370
|
-
}
|
|
20366
|
+
};
|
|
20367
|
+
/**
|
|
20368
|
+
* Disambiguates stops involved in a transfer.
|
|
20369
|
+
*
|
|
20370
|
+
* The GTFS specification only refers to a stopId in the trip-to-trip transfers and not the
|
|
20371
|
+
* specific stop index in the route. For routes that have multiple stops with the same stopId,
|
|
20372
|
+
* we need to determine which are the from / to stop indices in respective routes.
|
|
20373
|
+
* We do so by picking the stop indices leading to the most coherent transfer.
|
|
20374
|
+
* (we pick the closest from stop index happening after the to stop index).
|
|
20375
|
+
*/
|
|
20376
|
+
const disambiguateTransferStopsIndices = (fromStop, fromRoute, fromTripIndex, toStop, toRoute, toTripIndex) => {
|
|
20377
|
+
const fromStopIndices = fromRoute.stopRouteIndices(fromStop);
|
|
20378
|
+
const toStopIndices = toRoute.stopRouteIndices(toStop);
|
|
20379
|
+
let bestFromStopIndex;
|
|
20380
|
+
let bestToStopIndex;
|
|
20381
|
+
let bestTimeDifference = Infinity;
|
|
20382
|
+
for (const originStopIndex of fromStopIndices) {
|
|
20383
|
+
const fromArrivalTime = fromRoute.arrivalAt(originStopIndex, fromTripIndex);
|
|
20384
|
+
for (const toStopIndex of toStopIndices) {
|
|
20385
|
+
const toDepartureTime = toRoute.departureFrom(toStopIndex, toTripIndex);
|
|
20386
|
+
if (toDepartureTime.isAfter(fromArrivalTime)) {
|
|
20387
|
+
const timeDifference = toDepartureTime.toMinutes() - fromArrivalTime.toMinutes();
|
|
20388
|
+
if (timeDifference < bestTimeDifference) {
|
|
20389
|
+
bestTimeDifference = timeDifference;
|
|
20390
|
+
bestFromStopIndex = originStopIndex;
|
|
20391
|
+
bestToStopIndex = toStopIndex;
|
|
20392
|
+
}
|
|
20393
|
+
}
|
|
20394
|
+
}
|
|
20395
|
+
}
|
|
20396
|
+
if (bestFromStopIndex !== undefined && bestToStopIndex !== undefined) {
|
|
20397
|
+
return {
|
|
20398
|
+
fromStopIndex: bestFromStopIndex,
|
|
20399
|
+
toStopIndex: bestToStopIndex,
|
|
20400
|
+
};
|
|
20401
|
+
}
|
|
20402
|
+
return undefined;
|
|
20403
|
+
};
|
|
20404
|
+
/**
|
|
20405
|
+
* Builds trip continuations map from GTFS trip continuation data.
|
|
20406
|
+
*
|
|
20407
|
+
* This function processes GTFS in-seat transfer data and creates a mapping
|
|
20408
|
+
* from trip boarding IDs to continuation boarding information. It disambiguates
|
|
20409
|
+
* stop indices when routes have multiple stops with the same ID by finding
|
|
20410
|
+
* the most coherent transfer timing.
|
|
20411
|
+
*
|
|
20412
|
+
* @param tripsMapping Mapping from GTFS trip IDs to internal trip representations
|
|
20413
|
+
* @param tripContinuations Array of GTFS trip continuation data from transfers.txt
|
|
20414
|
+
* @param timetable The timetable containing route and timing information
|
|
20415
|
+
* @param activeStopIds Set of stop IDs that are active/enabled in the system
|
|
20416
|
+
* @returns A map from trip boarding IDs to arrays of continuation boarding options
|
|
20417
|
+
*/
|
|
20418
|
+
const buildTripContinuations = (tripsMapping, tripContinuations, timetable, activeStopIds) => {
|
|
20419
|
+
const continuations = new Map();
|
|
20420
|
+
for (const gtfsContinuation of tripContinuations) {
|
|
20421
|
+
if (!activeStopIds.has(gtfsContinuation.fromStop) ||
|
|
20422
|
+
!activeStopIds.has(gtfsContinuation.toStop)) {
|
|
20423
|
+
continue;
|
|
20424
|
+
}
|
|
20425
|
+
const fromTripMapping = tripsMapping.get(gtfsContinuation.fromTrip);
|
|
20426
|
+
const toTripMapping = tripsMapping.get(gtfsContinuation.toTrip);
|
|
20427
|
+
if (!fromTripMapping || !toTripMapping) {
|
|
20428
|
+
continue;
|
|
20429
|
+
}
|
|
20430
|
+
const fromRoute = timetable.getRoute(fromTripMapping.routeId);
|
|
20431
|
+
const toRoute = timetable.getRoute(toTripMapping.routeId);
|
|
20432
|
+
if (!fromRoute || !toRoute) {
|
|
20433
|
+
continue;
|
|
20434
|
+
}
|
|
20435
|
+
const bestStopIndices = disambiguateTransferStopsIndices(gtfsContinuation.fromStop, fromRoute, fromTripMapping.tripRouteIndex, gtfsContinuation.toStop, toRoute, toTripMapping.tripRouteIndex);
|
|
20436
|
+
if (!bestStopIndices) {
|
|
20437
|
+
// No valid continuation found
|
|
20438
|
+
continue;
|
|
20439
|
+
}
|
|
20440
|
+
const tripBoardingId = encode(bestStopIndices.fromStopIndex, fromTripMapping.routeId, fromTripMapping.tripRouteIndex);
|
|
20441
|
+
const continuationBoarding = {
|
|
20442
|
+
hopOnStopIndex: bestStopIndices.toStopIndex,
|
|
20443
|
+
routeId: toTripMapping.routeId,
|
|
20444
|
+
tripIndex: toTripMapping.tripRouteIndex,
|
|
20445
|
+
};
|
|
20446
|
+
const existingContinuations = continuations.get(tripBoardingId) || [];
|
|
20447
|
+
existingContinuations.push(continuationBoarding);
|
|
20448
|
+
continuations.set(tripBoardingId, existingContinuations);
|
|
20449
|
+
}
|
|
20450
|
+
return continuations;
|
|
20451
|
+
};
|
|
20371
20452
|
const parseGtfsTransferType = (gtfsTransferType) => {
|
|
20372
20453
|
switch (gtfsTransferType) {
|
|
20373
20454
|
case 0:
|
|
@@ -20475,36 +20556,23 @@ const finalizeRouteFromBuilder = (builder) => {
|
|
|
20475
20556
|
* @param serviceRoutes A mapping of route IDs to route details.
|
|
20476
20557
|
* @returns A mapping of trip IDs to corresponding route IDs.
|
|
20477
20558
|
*/
|
|
20478
|
-
const parseTrips = (tripsStream, serviceIds, validGtfsRoutes) =>
|
|
20479
|
-
var _a, e_1, _b, _c;
|
|
20559
|
+
const parseTrips = async (tripsStream, serviceIds, validGtfsRoutes) => {
|
|
20480
20560
|
const trips = new Map();
|
|
20481
|
-
|
|
20482
|
-
|
|
20483
|
-
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
const line = rawLine;
|
|
20487
|
-
if (!serviceIds.has(line.service_id)) {
|
|
20488
|
-
// The trip doesn't correspond to an active service
|
|
20489
|
-
continue;
|
|
20490
|
-
}
|
|
20491
|
-
if (!validGtfsRoutes.has(line.route_id)) {
|
|
20492
|
-
// The trip doesn't correspond to a supported route
|
|
20493
|
-
continue;
|
|
20494
|
-
}
|
|
20495
|
-
trips.set(line.trip_id, line.route_id);
|
|
20561
|
+
for await (const rawLine of parseCsv(tripsStream, ['stop_sequence'])) {
|
|
20562
|
+
const line = rawLine;
|
|
20563
|
+
if (!serviceIds.has(line.service_id)) {
|
|
20564
|
+
// The trip doesn't correspond to an active service
|
|
20565
|
+
continue;
|
|
20496
20566
|
}
|
|
20497
|
-
|
|
20498
|
-
|
|
20499
|
-
|
|
20500
|
-
try {
|
|
20501
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
20567
|
+
if (!validGtfsRoutes.has(line.route_id)) {
|
|
20568
|
+
// The trip doesn't correspond to a supported route
|
|
20569
|
+
continue;
|
|
20502
20570
|
}
|
|
20503
|
-
|
|
20571
|
+
trips.set(line.trip_id, line.route_id);
|
|
20504
20572
|
}
|
|
20505
20573
|
return trips;
|
|
20506
|
-
}
|
|
20507
|
-
const buildStopsAdjacencyStructure = (
|
|
20574
|
+
};
|
|
20575
|
+
const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbStops, activeStops) => {
|
|
20508
20576
|
const stopsAdjacency = new Array(nbStops);
|
|
20509
20577
|
for (let i = 0; i < nbStops; i++) {
|
|
20510
20578
|
stopsAdjacency[i] = {
|
|
@@ -20544,40 +20612,6 @@ const buildStopsAdjacencyStructure = (tripsMapping, serviceRoutes, routes, trans
|
|
|
20544
20612
|
}
|
|
20545
20613
|
}
|
|
20546
20614
|
}
|
|
20547
|
-
for (const [stop, tripContinuations] of tripContinuationsMap) {
|
|
20548
|
-
for (let i = 0; i < tripContinuations.length; i++) {
|
|
20549
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20550
|
-
const tripContinuation = tripContinuations[i];
|
|
20551
|
-
if (activeStops.has(stop) ||
|
|
20552
|
-
activeStops.has(tripContinuation.hopOnStop)) {
|
|
20553
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20554
|
-
const stopAdj = stopsAdjacency[stop];
|
|
20555
|
-
if (!stopAdj.tripContinuations) {
|
|
20556
|
-
stopAdj.tripContinuations = new Map();
|
|
20557
|
-
}
|
|
20558
|
-
const originTrip = tripsMapping.get(tripContinuation.fromTrip);
|
|
20559
|
-
const destinationTrip = tripsMapping.get(tripContinuation.toTrip);
|
|
20560
|
-
if (destinationTrip === undefined || originTrip === undefined) {
|
|
20561
|
-
continue;
|
|
20562
|
-
}
|
|
20563
|
-
const tripBoarding = {
|
|
20564
|
-
hopOnStop: tripContinuation.hopOnStop,
|
|
20565
|
-
routeId: destinationTrip.routeId,
|
|
20566
|
-
tripIndex: destinationTrip.tripRouteIndex,
|
|
20567
|
-
};
|
|
20568
|
-
const tripId = encode(originTrip.routeId, originTrip.tripRouteIndex);
|
|
20569
|
-
const existingContinuations = stopAdj.tripContinuations.get(tripId);
|
|
20570
|
-
if (existingContinuations) {
|
|
20571
|
-
existingContinuations.push(tripBoarding);
|
|
20572
|
-
}
|
|
20573
|
-
else {
|
|
20574
|
-
stopAdj.tripContinuations.set(tripId, [tripBoarding]);
|
|
20575
|
-
}
|
|
20576
|
-
activeStops.add(tripContinuation.hopOnStop);
|
|
20577
|
-
activeStops.add(stop);
|
|
20578
|
-
}
|
|
20579
|
-
}
|
|
20580
|
-
}
|
|
20581
20615
|
return stopsAdjacency;
|
|
20582
20616
|
};
|
|
20583
20617
|
/**
|
|
@@ -20589,9 +20623,7 @@ const buildStopsAdjacencyStructure = (tripsMapping, serviceRoutes, routes, trans
|
|
|
20589
20623
|
* @param activeStopIds A set of valid stop IDs.
|
|
20590
20624
|
* @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.
|
|
20591
20625
|
*/
|
|
20592
|
-
const parseStopTimes = (stopTimesStream, stopsMap, activeTripIds, activeStopIds) =>
|
|
20593
|
-
var _a, e_2, _b, _c;
|
|
20594
|
-
var _d, _e;
|
|
20626
|
+
const parseStopTimes = async (stopTimesStream, stopsMap, activeTripIds, activeStopIds) => {
|
|
20595
20627
|
/**
|
|
20596
20628
|
* Adds a trip to the appropriate route builder
|
|
20597
20629
|
*/
|
|
@@ -20659,55 +20691,43 @@ const parseStopTimes = (stopTimesStream, stopsMap, activeTripIds, activeStopIds)
|
|
|
20659
20691
|
let pickUpTypes = [];
|
|
20660
20692
|
let dropOffTypes = [];
|
|
20661
20693
|
let currentTripId = undefined;
|
|
20662
|
-
|
|
20663
|
-
|
|
20664
|
-
|
|
20665
|
-
|
|
20666
|
-
|
|
20667
|
-
const line = rawLine;
|
|
20668
|
-
if (line.trip_id === currentTripId && line.stop_sequence <= previousSeq) {
|
|
20669
|
-
console.warn(`Stop sequences not increasing for trip ${line.trip_id}: ${line.stop_sequence} > ${previousSeq}.`);
|
|
20670
|
-
continue;
|
|
20671
|
-
}
|
|
20672
|
-
if (!line.arrival_time && !line.departure_time) {
|
|
20673
|
-
console.warn(`Missing arrival or departure time for ${line.trip_id} at stop ${line.stop_id}.`);
|
|
20674
|
-
continue;
|
|
20675
|
-
}
|
|
20676
|
-
if (line.pickup_type === '1' && line.drop_off_type === '1') {
|
|
20677
|
-
// Warning: could potentially lead to issues if there is an in-seat transfer
|
|
20678
|
-
// at this stop - it can be not boardable nor alightable but still useful for an in-seat transfer.
|
|
20679
|
-
// This doesn't seem to happen in practice for now so keeping this condition to save memory.
|
|
20680
|
-
continue;
|
|
20681
|
-
}
|
|
20682
|
-
if (currentTripId && line.trip_id !== currentTripId && stops.length > 0) {
|
|
20683
|
-
addTrip(currentTripId);
|
|
20684
|
-
}
|
|
20685
|
-
currentTripId = line.trip_id;
|
|
20686
|
-
const stopData = stopsMap.get(line.stop_id);
|
|
20687
|
-
if (!stopData) {
|
|
20688
|
-
console.warn(`Unknown stop ID: ${line.stop_id}`);
|
|
20689
|
-
continue;
|
|
20690
|
-
}
|
|
20691
|
-
stops.push(stopData.id);
|
|
20692
|
-
const departure = (_d = line.departure_time) !== null && _d !== void 0 ? _d : line.arrival_time;
|
|
20693
|
-
const arrival = (_e = line.arrival_time) !== null && _e !== void 0 ? _e : line.departure_time;
|
|
20694
|
-
if (!arrival || !departure) {
|
|
20695
|
-
console.warn(`Missing time data for ${line.trip_id} at stop ${line.stop_id}`);
|
|
20696
|
-
continue;
|
|
20697
|
-
}
|
|
20698
|
-
arrivalTimes.push(toTime(arrival).toMinutes());
|
|
20699
|
-
departureTimes.push(toTime(departure).toMinutes());
|
|
20700
|
-
pickUpTypes.push(parsePickupDropOffType(line.pickup_type));
|
|
20701
|
-
dropOffTypes.push(parsePickupDropOffType(line.drop_off_type));
|
|
20702
|
-
previousSeq = line.stop_sequence;
|
|
20694
|
+
for await (const rawLine of parseCsv(stopTimesStream, ['stop_sequence'])) {
|
|
20695
|
+
const line = rawLine;
|
|
20696
|
+
if (line.trip_id === currentTripId && line.stop_sequence <= previousSeq) {
|
|
20697
|
+
console.warn(`Stop sequences not increasing for trip ${line.trip_id}: ${line.stop_sequence} > ${previousSeq}.`);
|
|
20698
|
+
continue;
|
|
20703
20699
|
}
|
|
20704
|
-
|
|
20705
|
-
|
|
20706
|
-
|
|
20707
|
-
|
|
20708
|
-
|
|
20700
|
+
if (!line.arrival_time && !line.departure_time) {
|
|
20701
|
+
console.warn(`Missing arrival or departure time for ${line.trip_id} at stop ${line.stop_id}.`);
|
|
20702
|
+
continue;
|
|
20703
|
+
}
|
|
20704
|
+
if (line.pickup_type === '1' && line.drop_off_type === '1') {
|
|
20705
|
+
// Warning: could potentially lead to issues if there is an in-seat transfer
|
|
20706
|
+
// at this stop - it can be not boardable nor alightable but still useful for an in-seat transfer.
|
|
20707
|
+
// This doesn't seem to happen in practice for now so keeping this condition to save memory.
|
|
20708
|
+
continue;
|
|
20709
|
+
}
|
|
20710
|
+
if (currentTripId && line.trip_id !== currentTripId && stops.length > 0) {
|
|
20711
|
+
addTrip(currentTripId);
|
|
20712
|
+
}
|
|
20713
|
+
currentTripId = line.trip_id;
|
|
20714
|
+
const stopData = stopsMap.get(line.stop_id);
|
|
20715
|
+
if (!stopData) {
|
|
20716
|
+
console.warn(`Unknown stop ID: ${line.stop_id}`);
|
|
20717
|
+
continue;
|
|
20709
20718
|
}
|
|
20710
|
-
|
|
20719
|
+
stops.push(stopData.id);
|
|
20720
|
+
const departure = line.departure_time ?? line.arrival_time;
|
|
20721
|
+
const arrival = line.arrival_time ?? line.departure_time;
|
|
20722
|
+
if (!arrival || !departure) {
|
|
20723
|
+
console.warn(`Missing time data for ${line.trip_id} at stop ${line.stop_id}`);
|
|
20724
|
+
continue;
|
|
20725
|
+
}
|
|
20726
|
+
arrivalTimes.push(toTime(arrival).toMinutes());
|
|
20727
|
+
departureTimes.push(toTime(departure).toMinutes());
|
|
20728
|
+
pickUpTypes.push(parsePickupDropOffType(line.pickup_type));
|
|
20729
|
+
dropOffTypes.push(parsePickupDropOffType(line.drop_off_type));
|
|
20730
|
+
previousSeq = line.stop_sequence;
|
|
20711
20731
|
}
|
|
20712
20732
|
if (currentTripId) {
|
|
20713
20733
|
addTrip(currentTripId);
|
|
@@ -20726,7 +20746,7 @@ const parseStopTimes = (stopTimesStream, stopsMap, activeTripIds, activeStopIds)
|
|
|
20726
20746
|
});
|
|
20727
20747
|
}
|
|
20728
20748
|
return { routes: routesAdjacency, serviceRoutesMap, tripsMapping };
|
|
20729
|
-
}
|
|
20749
|
+
};
|
|
20730
20750
|
const parsePickupDropOffType = (gtfsType) => {
|
|
20731
20751
|
switch (gtfsType) {
|
|
20732
20752
|
default:
|
|
@@ -20750,6 +20770,8 @@ const STOP_TIMES_FILE = 'stop_times.txt';
|
|
|
20750
20770
|
const STOPS_FILE = 'stops.txt';
|
|
20751
20771
|
const TRANSFERS_FILE = 'transfers.txt';
|
|
20752
20772
|
class GtfsParser {
|
|
20773
|
+
path;
|
|
20774
|
+
profile;
|
|
20753
20775
|
constructor(path, profile = standardProfile) {
|
|
20754
20776
|
// TODO: support input from multiple sources
|
|
20755
20777
|
this.path = path;
|
|
@@ -20761,77 +20783,81 @@ class GtfsParser {
|
|
|
20761
20783
|
* @param date The active date.
|
|
20762
20784
|
* @returns The parsed timetable.
|
|
20763
20785
|
*/
|
|
20764
|
-
parseTimetable(date) {
|
|
20765
|
-
|
|
20766
|
-
|
|
20767
|
-
|
|
20768
|
-
|
|
20769
|
-
|
|
20770
|
-
|
|
20771
|
-
|
|
20772
|
-
|
|
20773
|
-
|
|
20774
|
-
|
|
20775
|
-
|
|
20776
|
-
|
|
20777
|
-
|
|
20778
|
-
|
|
20779
|
-
|
|
20780
|
-
|
|
20781
|
-
|
|
20782
|
-
|
|
20783
|
-
|
|
20784
|
-
|
|
20785
|
-
|
|
20786
|
-
|
|
20787
|
-
|
|
20788
|
-
|
|
20789
|
-
|
|
20790
|
-
|
|
20791
|
-
|
|
20792
|
-
|
|
20793
|
-
|
|
20794
|
-
|
|
20795
|
-
|
|
20796
|
-
|
|
20797
|
-
|
|
20798
|
-
|
|
20799
|
-
|
|
20800
|
-
|
|
20801
|
-
|
|
20802
|
-
|
|
20803
|
-
|
|
20804
|
-
|
|
20805
|
-
|
|
20806
|
-
|
|
20807
|
-
|
|
20808
|
-
|
|
20809
|
-
|
|
20810
|
-
|
|
20811
|
-
|
|
20812
|
-
|
|
20813
|
-
|
|
20814
|
-
|
|
20815
|
-
|
|
20816
|
-
|
|
20817
|
-
|
|
20818
|
-
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
|
|
20822
|
-
|
|
20823
|
-
|
|
20824
|
-
|
|
20825
|
-
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
|
|
20830
|
-
|
|
20831
|
-
|
|
20832
|
-
|
|
20833
|
-
|
|
20834
|
-
|
|
20786
|
+
async parseTimetable(date) {
|
|
20787
|
+
log.setLevel('INFO');
|
|
20788
|
+
const zip = new StreamZip.async({ file: this.path });
|
|
20789
|
+
const entries = await zip.entries();
|
|
20790
|
+
const datetime = DateTime.fromJSDate(date);
|
|
20791
|
+
const activeServiceIds = new Set();
|
|
20792
|
+
const activeStopIds = new Set();
|
|
20793
|
+
log.info(`Parsing ${STOPS_FILE}`);
|
|
20794
|
+
const stopsStart = performance.now();
|
|
20795
|
+
const stopsStream = await zip.stream(STOPS_FILE);
|
|
20796
|
+
const parsedStops = await parseStops(stopsStream);
|
|
20797
|
+
const stopsEnd = performance.now();
|
|
20798
|
+
log.info(`${parsedStops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
|
|
20799
|
+
if (entries[CALENDAR_FILE]) {
|
|
20800
|
+
log.info(`Parsing ${CALENDAR_FILE}`);
|
|
20801
|
+
const calendarStart = performance.now();
|
|
20802
|
+
const calendarStream = await zip.stream(CALENDAR_FILE);
|
|
20803
|
+
await parseCalendar(calendarStream, activeServiceIds, datetime);
|
|
20804
|
+
const calendarEnd = performance.now();
|
|
20805
|
+
log.info(`${activeServiceIds.size} valid services. (${(calendarEnd - calendarStart).toFixed(2)}ms)`);
|
|
20806
|
+
}
|
|
20807
|
+
if (entries[CALENDAR_DATES_FILE]) {
|
|
20808
|
+
log.info(`Parsing ${CALENDAR_DATES_FILE}`);
|
|
20809
|
+
const calendarDatesStart = performance.now();
|
|
20810
|
+
const calendarDatesStream = await zip.stream(CALENDAR_DATES_FILE);
|
|
20811
|
+
await parseCalendarDates(calendarDatesStream, activeServiceIds, datetime);
|
|
20812
|
+
const calendarDatesEnd = performance.now();
|
|
20813
|
+
log.info(`${activeServiceIds.size} valid services. (${(calendarDatesEnd - calendarDatesStart).toFixed(2)}ms)`);
|
|
20814
|
+
}
|
|
20815
|
+
log.info(`Parsing ${ROUTES_FILE}`);
|
|
20816
|
+
const routesStart = performance.now();
|
|
20817
|
+
const routesStream = await zip.stream(ROUTES_FILE);
|
|
20818
|
+
const validGtfsRoutes = await parseRoutes(routesStream, this.profile);
|
|
20819
|
+
const routesEnd = performance.now();
|
|
20820
|
+
log.info(`${validGtfsRoutes.size} valid GTFS routes. (${(routesEnd - routesStart).toFixed(2)}ms)`);
|
|
20821
|
+
log.info(`Parsing ${TRIPS_FILE}`);
|
|
20822
|
+
const tripsStart = performance.now();
|
|
20823
|
+
const tripsStream = await zip.stream(TRIPS_FILE);
|
|
20824
|
+
const trips = await parseTrips(tripsStream, activeServiceIds, validGtfsRoutes);
|
|
20825
|
+
const tripsEnd = performance.now();
|
|
20826
|
+
log.info(`${trips.size} valid trips. (${(tripsEnd - tripsStart).toFixed(2)}ms)`);
|
|
20827
|
+
let transfers = new Map();
|
|
20828
|
+
let tripContinuationsMap = [];
|
|
20829
|
+
if (entries[TRANSFERS_FILE]) {
|
|
20830
|
+
log.info(`Parsing ${TRANSFERS_FILE}`);
|
|
20831
|
+
const transfersStart = performance.now();
|
|
20832
|
+
const transfersStream = await zip.stream(TRANSFERS_FILE);
|
|
20833
|
+
const { transfers: parsedTransfers, tripContinuations: parsedTripContinuations, } = await parseTransfers(transfersStream, parsedStops);
|
|
20834
|
+
transfers = parsedTransfers;
|
|
20835
|
+
tripContinuationsMap = parsedTripContinuations;
|
|
20836
|
+
const transfersEnd = performance.now();
|
|
20837
|
+
log.info(`${transfers.size} valid transfers and ${tripContinuationsMap.length} trip continuations. (${(transfersEnd - transfersStart).toFixed(2)}ms)`);
|
|
20838
|
+
}
|
|
20839
|
+
log.info(`Parsing ${STOP_TIMES_FILE}`);
|
|
20840
|
+
const stopTimesStart = performance.now();
|
|
20841
|
+
const stopTimesStream = await zip.stream(STOP_TIMES_FILE);
|
|
20842
|
+
const { routes, serviceRoutesMap, tripsMapping } = await parseStopTimes(stopTimesStream, parsedStops, trips, activeStopIds);
|
|
20843
|
+
const serviceRoutes = indexRoutes(validGtfsRoutes, serviceRoutesMap);
|
|
20844
|
+
const stopTimesEnd = performance.now();
|
|
20845
|
+
log.info(`${routes.length} valid unique routes. (${(stopTimesEnd - stopTimesStart).toFixed(2)}ms)`);
|
|
20846
|
+
log.info('Building stops adjacency structure');
|
|
20847
|
+
const stopsAdjacencyStart = performance.now();
|
|
20848
|
+
const stopsAdjacency = buildStopsAdjacencyStructure(serviceRoutes, routes, transfers, parsedStops.size, activeStopIds);
|
|
20849
|
+
const stopsAdjacencyEnd = performance.now();
|
|
20850
|
+
log.info(`${stopsAdjacency.length} valid stops in the structure. (${(stopsAdjacencyEnd - stopsAdjacencyStart).toFixed(2)}ms)`);
|
|
20851
|
+
await zip.close();
|
|
20852
|
+
// temporary timetable for building continuations
|
|
20853
|
+
const timetable = new Timetable(stopsAdjacency, routes, serviceRoutes);
|
|
20854
|
+
log.info('Building in-seat trip continuations');
|
|
20855
|
+
const tripContinuationsStart = performance.now();
|
|
20856
|
+
const tripContinuations = buildTripContinuations(tripsMapping, tripContinuationsMap, timetable, activeStopIds);
|
|
20857
|
+
const tripContinuationsEnd = performance.now();
|
|
20858
|
+
log.info(`${tripContinuations.size} in-seat trip continuations origins created. (${(tripContinuationsEnd - tripContinuationsStart).toFixed(2)}ms)`);
|
|
20859
|
+
log.info('Parsing complete.');
|
|
20860
|
+
return new Timetable(stopsAdjacency, routes, serviceRoutes, tripContinuations);
|
|
20835
20861
|
}
|
|
20836
20862
|
/**
|
|
20837
20863
|
* Parses a GTFS feed to extract all stops.
|
|
@@ -20839,18 +20865,16 @@ class GtfsParser {
|
|
|
20839
20865
|
* @param activeStops The set of active stop IDs to include in the index.
|
|
20840
20866
|
* @returns An index of stops.
|
|
20841
20867
|
*/
|
|
20842
|
-
parseStops() {
|
|
20843
|
-
|
|
20844
|
-
|
|
20845
|
-
|
|
20846
|
-
|
|
20847
|
-
|
|
20848
|
-
|
|
20849
|
-
|
|
20850
|
-
|
|
20851
|
-
|
|
20852
|
-
return new StopsIndex(Array.from(stops.values()));
|
|
20853
|
-
});
|
|
20868
|
+
async parseStops() {
|
|
20869
|
+
const zip = new StreamZip.async({ file: this.path });
|
|
20870
|
+
log.info(`Parsing ${STOPS_FILE}`);
|
|
20871
|
+
const stopsStart = performance.now();
|
|
20872
|
+
const stopsStream = await zip.stream(STOPS_FILE);
|
|
20873
|
+
const stops = await parseStops(stopsStream);
|
|
20874
|
+
const stopsEnd = performance.now();
|
|
20875
|
+
log.info(`${stops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
|
|
20876
|
+
await zip.close();
|
|
20877
|
+
return new StopsIndex(Array.from(stops.values()));
|
|
20854
20878
|
}
|
|
20855
20879
|
}
|
|
20856
20880
|
|
|
@@ -20904,27 +20928,27 @@ const chGtfsProfile = {
|
|
|
20904
20928
|
};
|
|
20905
20929
|
|
|
20906
20930
|
class Plotter {
|
|
20931
|
+
result;
|
|
20932
|
+
ROUND_COLORS = [
|
|
20933
|
+
'#60a5fa', // Round 1
|
|
20934
|
+
'#ff9800', // Round 2
|
|
20935
|
+
'#14b8a6', // Round 3
|
|
20936
|
+
'#fb7185', // Round 4
|
|
20937
|
+
'#ffdf00', // Round 5
|
|
20938
|
+
'#b600ff', // Round 6
|
|
20939
|
+
'#ee82ee', // Round 7+
|
|
20940
|
+
];
|
|
20907
20941
|
constructor(result) {
|
|
20908
|
-
this.ROUND_COLORS = [
|
|
20909
|
-
'#60a5fa', // Round 1
|
|
20910
|
-
'#ff9800', // Round 2
|
|
20911
|
-
'#14b8a6', // Round 3
|
|
20912
|
-
'#fb7185', // Round 4
|
|
20913
|
-
'#ffdf00', // Round 5
|
|
20914
|
-
'#b600ff', // Round 6
|
|
20915
|
-
'#ee82ee', // Round 7+
|
|
20916
|
-
];
|
|
20917
20942
|
this.result = result;
|
|
20918
20943
|
}
|
|
20919
20944
|
/**
|
|
20920
20945
|
* Gets the color for a round based on the specified palette.
|
|
20921
20946
|
*/
|
|
20922
20947
|
getRoundColor(round) {
|
|
20923
|
-
var _a;
|
|
20924
20948
|
if (round === 0)
|
|
20925
20949
|
return '#888888';
|
|
20926
20950
|
const colorIndex = Math.min(round - 1, this.ROUND_COLORS.length - 1);
|
|
20927
|
-
return
|
|
20951
|
+
return this.ROUND_COLORS[colorIndex] ?? '#ee82ee';
|
|
20928
20952
|
}
|
|
20929
20953
|
/**
|
|
20930
20954
|
* Escapes special characters in DOT strings to prevent syntax errors.
|
|
@@ -20941,8 +20965,7 @@ class Plotter {
|
|
|
20941
20965
|
* Determines station type (origin/destination) information.
|
|
20942
20966
|
*/
|
|
20943
20967
|
getStationInfo(stopId) {
|
|
20944
|
-
|
|
20945
|
-
const isOrigin = (_b = (_a = this.result.routingState.graph[0]) === null || _a === void 0 ? void 0 : _a.has(stopId)) !== null && _b !== void 0 ? _b : false;
|
|
20968
|
+
const isOrigin = this.result.routingState.graph[0]?.has(stopId) ?? false;
|
|
20946
20969
|
const isDestination = this.result.routingState.destinations.includes(stopId);
|
|
20947
20970
|
return { isOrigin, isDestination };
|
|
20948
20971
|
}
|
|
@@ -20989,7 +21012,6 @@ class Plotter {
|
|
|
20989
21012
|
* Creates a vehicle edge with route information oval in the middle.
|
|
20990
21013
|
*/
|
|
20991
21014
|
createVehicleEdge(edge, round) {
|
|
20992
|
-
var _a;
|
|
20993
21015
|
const fromNodeId = `s_${edge.from}`;
|
|
20994
21016
|
const toNodeId = `s_${edge.to}`;
|
|
20995
21017
|
const roundColor = this.getRoundColor(round);
|
|
@@ -20998,8 +21020,8 @@ class Plotter {
|
|
|
20998
21020
|
const serviceRouteInfo = route
|
|
20999
21021
|
? this.result.timetable.getServiceRouteInfo(route)
|
|
21000
21022
|
: null;
|
|
21001
|
-
const routeName =
|
|
21002
|
-
const routeType =
|
|
21023
|
+
const routeName = serviceRouteInfo?.name ?? `Route ${String(edge.routeId)}`;
|
|
21024
|
+
const routeType = serviceRouteInfo?.type || 'UNKNOWN';
|
|
21003
21025
|
const departureTime = route
|
|
21004
21026
|
? route.departureFrom(edge.from, edge.tripIndex).toString()
|
|
21005
21027
|
: 'N/A';
|
|
@@ -21018,12 +21040,11 @@ class Plotter {
|
|
|
21018
21040
|
* Creates a transfer edge with transfer information oval in the middle.
|
|
21019
21041
|
*/
|
|
21020
21042
|
createTransferEdge(edge, round) {
|
|
21021
|
-
var _a;
|
|
21022
21043
|
const fromNodeId = `s_${edge.from}`;
|
|
21023
21044
|
const toNodeId = `s_${edge.to}`;
|
|
21024
21045
|
const roundColor = this.getRoundColor(round);
|
|
21025
21046
|
const transferOvalId = `e_${edge.from}_${edge.to}_${round}`;
|
|
21026
|
-
const transferTime =
|
|
21047
|
+
const transferTime = edge.minTransferTime?.toString() || 'N/A';
|
|
21027
21048
|
const escapedTransferTime = this.escapeDotString(transferTime);
|
|
21028
21049
|
const ovalLabel = `Transfer\\n${escapedTransferTime}`;
|
|
21029
21050
|
return [
|
|
@@ -21036,7 +21057,6 @@ class Plotter {
|
|
|
21036
21057
|
* Creates a continuation edge to visually link trip continuations.
|
|
21037
21058
|
*/
|
|
21038
21059
|
createContinuationEdge(fromEdge, toEdge, round) {
|
|
21039
|
-
var _a, _b;
|
|
21040
21060
|
const fromStationId = `s_${fromEdge.to}`;
|
|
21041
21061
|
const toStationId = `s_${toEdge.from}`;
|
|
21042
21062
|
const roundColor = this.getRoundColor(round);
|
|
@@ -21049,10 +21069,10 @@ class Plotter {
|
|
|
21049
21069
|
const toServiceRouteInfo = toRoute
|
|
21050
21070
|
? this.result.timetable.getServiceRouteInfo(toRoute)
|
|
21051
21071
|
: null;
|
|
21052
|
-
const fromRouteName =
|
|
21053
|
-
const toRouteName =
|
|
21054
|
-
const fromRouteType =
|
|
21055
|
-
const toRouteType =
|
|
21072
|
+
const fromRouteName = fromServiceRouteInfo?.name ?? `Route ${String(fromEdge.routeId)}`;
|
|
21073
|
+
const toRouteName = toServiceRouteInfo?.name ?? `Route ${String(toEdge.routeId)}`;
|
|
21074
|
+
const fromRouteType = fromServiceRouteInfo?.type || 'UNKNOWN';
|
|
21075
|
+
const toRouteType = toServiceRouteInfo?.type || 'UNKNOWN';
|
|
21056
21076
|
const fromArrivalTime = fromEdge.arrival.toString();
|
|
21057
21077
|
const toDepartureTime = toRoute
|
|
21058
21078
|
? toRoute.departureFrom(toEdge.from, toEdge.tripIndex).toString()
|
|
@@ -21147,79 +21167,85 @@ class Plotter {
|
|
|
21147
21167
|
}
|
|
21148
21168
|
|
|
21149
21169
|
class Query {
|
|
21170
|
+
from;
|
|
21171
|
+
to;
|
|
21172
|
+
departureTime;
|
|
21173
|
+
lastDepartureTime;
|
|
21174
|
+
options;
|
|
21150
21175
|
constructor(builder) {
|
|
21151
21176
|
this.from = builder.fromValue;
|
|
21152
21177
|
this.to = builder.toValue;
|
|
21153
21178
|
this.departureTime = builder.departureTimeValue;
|
|
21154
21179
|
this.options = builder.optionsValue;
|
|
21155
21180
|
}
|
|
21156
|
-
|
|
21157
|
-
|
|
21158
|
-
|
|
21159
|
-
|
|
21181
|
+
static Builder = class {
|
|
21182
|
+
fromValue;
|
|
21183
|
+
toValue = new Set();
|
|
21184
|
+
departureTimeValue;
|
|
21160
21185
|
// lastDepartureTimeValue?: Date;
|
|
21161
21186
|
// via: StopId[] = [];
|
|
21162
|
-
|
|
21187
|
+
optionsValue = {
|
|
21163
21188
|
maxTransfers: 5,
|
|
21164
21189
|
minTransferTime: Duration.fromSeconds(120),
|
|
21165
21190
|
transportModes: ALL_TRANSPORT_MODES,
|
|
21166
21191
|
};
|
|
21167
|
-
|
|
21168
|
-
|
|
21169
|
-
|
|
21170
|
-
|
|
21171
|
-
|
|
21172
|
-
|
|
21173
|
-
|
|
21174
|
-
|
|
21175
|
-
|
|
21176
|
-
|
|
21177
|
-
|
|
21178
|
-
|
|
21179
|
-
|
|
21180
|
-
|
|
21181
|
-
|
|
21182
|
-
|
|
21183
|
-
|
|
21184
|
-
|
|
21185
|
-
|
|
21186
|
-
|
|
21187
|
-
|
|
21188
|
-
|
|
21189
|
-
|
|
21190
|
-
|
|
21191
|
-
|
|
21192
|
-
|
|
21193
|
-
|
|
21194
|
-
|
|
21195
|
-
|
|
21196
|
-
|
|
21197
|
-
|
|
21198
|
-
|
|
21199
|
-
|
|
21200
|
-
|
|
21201
|
-
|
|
21202
|
-
|
|
21203
|
-
|
|
21204
|
-
|
|
21205
|
-
|
|
21206
|
-
|
|
21207
|
-
|
|
21208
|
-
|
|
21209
|
-
|
|
21210
|
-
|
|
21211
|
-
|
|
21212
|
-
|
|
21213
|
-
|
|
21214
|
-
|
|
21215
|
-
}
|
|
21216
|
-
}
|
|
21192
|
+
/**
|
|
21193
|
+
* Sets the starting stop.
|
|
21194
|
+
*/
|
|
21195
|
+
from(from) {
|
|
21196
|
+
this.fromValue = from;
|
|
21197
|
+
return this;
|
|
21198
|
+
}
|
|
21199
|
+
/**
|
|
21200
|
+
* Sets the destination stops(s), routing will stop when all the provided stops are reached.
|
|
21201
|
+
*/
|
|
21202
|
+
to(to) {
|
|
21203
|
+
this.toValue = to instanceof Set ? to : new Set([to]);
|
|
21204
|
+
return this;
|
|
21205
|
+
}
|
|
21206
|
+
/**
|
|
21207
|
+
* Sets the departure time for the query.
|
|
21208
|
+
* Note that the router will favor routes that depart shortly after the provided departure time,
|
|
21209
|
+
* even if a later route might arrive at the same time.
|
|
21210
|
+
* Range queries will allow to specify a range of departure times in the future.
|
|
21211
|
+
*/
|
|
21212
|
+
departureTime(departureTime) {
|
|
21213
|
+
this.departureTimeValue = departureTime;
|
|
21214
|
+
return this;
|
|
21215
|
+
}
|
|
21216
|
+
/**
|
|
21217
|
+
* Sets the maximum number of transfers allowed.
|
|
21218
|
+
*/
|
|
21219
|
+
maxTransfers(maxTransfers) {
|
|
21220
|
+
this.optionsValue.maxTransfers = maxTransfers;
|
|
21221
|
+
return this;
|
|
21222
|
+
}
|
|
21223
|
+
/**
|
|
21224
|
+
* Sets the minimum transfer time to use when no transfer time is provided in the data.
|
|
21225
|
+
*/
|
|
21226
|
+
minTransferTime(minTransferTime) {
|
|
21227
|
+
this.optionsValue.minTransferTime = minTransferTime;
|
|
21228
|
+
return this;
|
|
21229
|
+
}
|
|
21230
|
+
/**
|
|
21231
|
+
* Sets the transport modes to consider.
|
|
21232
|
+
*/
|
|
21233
|
+
transportModes(transportModes) {
|
|
21234
|
+
this.optionsValue.transportModes = transportModes;
|
|
21235
|
+
return this;
|
|
21236
|
+
}
|
|
21237
|
+
build() {
|
|
21238
|
+
return new Query(this);
|
|
21239
|
+
}
|
|
21240
|
+
};
|
|
21241
|
+
}
|
|
21217
21242
|
|
|
21218
21243
|
/**
|
|
21219
21244
|
* Represents a resolved route consisting of multiple legs,
|
|
21220
21245
|
* which can be either vehicle legs or transfer legs.
|
|
21221
21246
|
*/
|
|
21222
21247
|
class Route {
|
|
21248
|
+
legs;
|
|
21223
21249
|
constructor(legs) {
|
|
21224
21250
|
this.legs = legs;
|
|
21225
21251
|
}
|
|
@@ -21289,11 +21315,10 @@ class Route {
|
|
|
21289
21315
|
toString() {
|
|
21290
21316
|
return this.legs
|
|
21291
21317
|
.map((leg, index) => {
|
|
21292
|
-
var _a;
|
|
21293
21318
|
const fromStop = `From: ${leg.from.name}${leg.from.platform ? ` (Pl. ${leg.from.platform})` : ''}`;
|
|
21294
21319
|
const toStop = `To: ${leg.to.name}${leg.to.platform ? ` (Pl. ${leg.to.platform})` : ''}`;
|
|
21295
21320
|
const transferDetails = 'minTransferTime' in leg
|
|
21296
|
-
? `Minimum Transfer Time: ${
|
|
21321
|
+
? `Minimum Transfer Time: ${leg.minTransferTime?.toString()}`
|
|
21297
21322
|
: '';
|
|
21298
21323
|
const travelDetails = 'route' in leg && 'departureTime' in leg && 'arrivalTime' in leg
|
|
21299
21324
|
? `Route: ${leg.route.type} ${leg.route.name}, Departure: ${leg.departureTime.toString()}, Arrival: ${leg.arrivalTime.toString()}`
|
|
@@ -21329,9 +21354,14 @@ class Route {
|
|
|
21329
21354
|
};
|
|
21330
21355
|
}
|
|
21331
21356
|
else {
|
|
21332
|
-
return
|
|
21333
|
-
|
|
21334
|
-
|
|
21357
|
+
return {
|
|
21358
|
+
from: leg.from.sourceStopId,
|
|
21359
|
+
to: leg.to.sourceStopId,
|
|
21360
|
+
type: leg.type,
|
|
21361
|
+
...(leg.minTransferTime !== undefined && {
|
|
21362
|
+
minTransferTime: leg.minTransferTime.toString(),
|
|
21363
|
+
}),
|
|
21364
|
+
};
|
|
21335
21365
|
}
|
|
21336
21366
|
});
|
|
21337
21367
|
return jsonLegs;
|
|
@@ -21339,6 +21369,10 @@ class Route {
|
|
|
21339
21369
|
}
|
|
21340
21370
|
|
|
21341
21371
|
class Result {
|
|
21372
|
+
query;
|
|
21373
|
+
routingState;
|
|
21374
|
+
stopsIndex;
|
|
21375
|
+
timetable;
|
|
21342
21376
|
constructor(query, routingState, stopsIndex, timetable) {
|
|
21343
21377
|
this.query = query;
|
|
21344
21378
|
this.routingState = routingState;
|
|
@@ -21353,7 +21387,6 @@ class Result {
|
|
|
21353
21387
|
* @returns a route to the destination stop if it exists.
|
|
21354
21388
|
*/
|
|
21355
21389
|
bestRoute(to) {
|
|
21356
|
-
var _a;
|
|
21357
21390
|
const destinationList = to instanceof Set
|
|
21358
21391
|
? Array.from(to)
|
|
21359
21392
|
: to
|
|
@@ -21380,7 +21413,7 @@ class Result {
|
|
|
21380
21413
|
let currentStop = fastestDestination;
|
|
21381
21414
|
let round = fastestTime.legNumber;
|
|
21382
21415
|
while (round > 0) {
|
|
21383
|
-
const edge =
|
|
21416
|
+
const edge = this.routingState.graph[round]?.get(currentStop);
|
|
21384
21417
|
if (!edge) {
|
|
21385
21418
|
throw new Error(`No edge arriving at stop ${currentStop} at round ${round}`);
|
|
21386
21419
|
}
|
|
@@ -21430,9 +21463,9 @@ class Result {
|
|
|
21430
21463
|
const lastRoute = this.timetable.getRoute(lastEdge.routeId);
|
|
21431
21464
|
return {
|
|
21432
21465
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
21433
|
-
from: this.stopsIndex.findStopById(firstEdge.from),
|
|
21466
|
+
from: this.stopsIndex.findStopById(firstRoute.stopId(firstEdge.from)),
|
|
21434
21467
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
21435
|
-
to: this.stopsIndex.findStopById(lastEdge.to),
|
|
21468
|
+
to: this.stopsIndex.findStopById(lastRoute.stopId(lastEdge.to)),
|
|
21436
21469
|
// The route info comes from the first boarded route in case on continuous trips
|
|
21437
21470
|
route: this.timetable.getServiceRouteInfo(firstRoute),
|
|
21438
21471
|
departureTime: firstRoute.departureFrom(firstEdge.from, firstEdge.tripIndex),
|
|
@@ -21465,7 +21498,6 @@ class Result {
|
|
|
21465
21498
|
* @returns The arrival time if the target stop is reachable, otherwise undefined.
|
|
21466
21499
|
*/
|
|
21467
21500
|
arrivalAt(stop, maxTransfers) {
|
|
21468
|
-
var _a;
|
|
21469
21501
|
const equivalentStops = this.stopsIndex.equivalentStops(stop);
|
|
21470
21502
|
let earliestArrival = undefined;
|
|
21471
21503
|
for (const equivalentStop of equivalentStops) {
|
|
@@ -21477,7 +21509,7 @@ class Result {
|
|
|
21477
21509
|
// We have no guarantee that the stop was visited in the last round,
|
|
21478
21510
|
// so we need to check all rounds if it's not found in the last one.
|
|
21479
21511
|
for (let i = maxTransfers + 1; i >= 0; i--) {
|
|
21480
|
-
const arrivalEdge =
|
|
21512
|
+
const arrivalEdge = this.routingState.graph[i]?.get(equivalentStop.id);
|
|
21481
21513
|
if (arrivalEdge !== undefined) {
|
|
21482
21514
|
arrivalTime = {
|
|
21483
21515
|
arrival: arrivalEdge.arrival,
|
|
@@ -21506,6 +21538,8 @@ const UNREACHED = Time.infinity();
|
|
|
21506
21538
|
* https://www.microsoft.com/en-us/research/wp-content/uploads/2012/01/raptor_alenex.pdf
|
|
21507
21539
|
*/
|
|
21508
21540
|
class Router {
|
|
21541
|
+
timetable;
|
|
21542
|
+
stopsIndex;
|
|
21509
21543
|
constructor(timetable, stopsIndex) {
|
|
21510
21544
|
this.timetable = timetable;
|
|
21511
21545
|
this.stopsIndex = stopsIndex;
|
|
@@ -21533,8 +21567,8 @@ class Router {
|
|
|
21533
21567
|
const reachableRoutes = this.timetable.findReachableRoutes(markedStops, query.options.transportModes);
|
|
21534
21568
|
markedStops.clear();
|
|
21535
21569
|
// for each route that can be reached with at least round - 1 trips
|
|
21536
|
-
for (const [route,
|
|
21537
|
-
const newlyMarkedStops = this.scanRoute(route,
|
|
21570
|
+
for (const [route, hopOnStopIndex] of reachableRoutes) {
|
|
21571
|
+
const newlyMarkedStops = this.scanRoute(route, hopOnStopIndex, round, routingState);
|
|
21538
21572
|
for (const newStop of newlyMarkedStops) {
|
|
21539
21573
|
markedStops.add(newStop);
|
|
21540
21574
|
}
|
|
@@ -21545,7 +21579,7 @@ class Router {
|
|
|
21545
21579
|
const stopsFromContinuations = new Set();
|
|
21546
21580
|
for (const continuation of continuations) {
|
|
21547
21581
|
const route = this.timetable.getRoute(continuation.routeId);
|
|
21548
|
-
const routeScanResults = this.scanRoute(route, continuation.
|
|
21582
|
+
const routeScanResults = this.scanRoute(route, continuation.hopOnStopIndex, round, routingState, continuation);
|
|
21549
21583
|
for (const newStop of routeScanResults) {
|
|
21550
21584
|
stopsFromContinuations.add(newStop);
|
|
21551
21585
|
}
|
|
@@ -21576,12 +21610,12 @@ class Router {
|
|
|
21576
21610
|
const arrival = edgesAtCurrentRound.get(stopId);
|
|
21577
21611
|
if (!arrival || !('routeId' in arrival))
|
|
21578
21612
|
continue;
|
|
21579
|
-
const continuousTrips = this.timetable.getContinuousTrips(
|
|
21613
|
+
const continuousTrips = this.timetable.getContinuousTrips(arrival.to, arrival.routeId, arrival.tripIndex);
|
|
21580
21614
|
for (let i = 0; i < continuousTrips.length; i++) {
|
|
21581
21615
|
const trip = continuousTrips[i];
|
|
21582
21616
|
continuations.push({
|
|
21583
21617
|
routeId: trip.routeId,
|
|
21584
|
-
|
|
21618
|
+
hopOnStopIndex: trip.hopOnStopIndex,
|
|
21585
21619
|
tripIndex: trip.tripIndex,
|
|
21586
21620
|
previousEdge: arrival,
|
|
21587
21621
|
});
|
|
@@ -21636,33 +21670,31 @@ class Router {
|
|
|
21636
21670
|
* are available if no given trip is provided as a parameter.
|
|
21637
21671
|
*
|
|
21638
21672
|
* @param route The route to scan for possible trips
|
|
21639
|
-
* @param
|
|
21673
|
+
* @param hopOnStopIndex The stop index where passengers can board the route
|
|
21640
21674
|
* @param round The current round number in the RAPTOR algorithm
|
|
21641
21675
|
* @param routingState The current routing state containing arrival times and marked stops
|
|
21642
21676
|
*/
|
|
21643
|
-
scanRoute(route,
|
|
21644
|
-
var _a, _b, _c;
|
|
21677
|
+
scanRoute(route, hopOnStopIndex, round, routingState, tripContinuation) {
|
|
21645
21678
|
const newlyMarkedStops = new Set();
|
|
21646
21679
|
let activeTrip = tripContinuation
|
|
21647
21680
|
? {
|
|
21648
21681
|
routeId: route.id,
|
|
21649
|
-
|
|
21682
|
+
hopOnStopIndex,
|
|
21650
21683
|
tripIndex: tripContinuation.tripIndex,
|
|
21651
21684
|
}
|
|
21652
21685
|
: undefined;
|
|
21653
21686
|
const edgesAtCurrentRound = routingState.graph[round];
|
|
21654
21687
|
const edgesAtPreviousRound = routingState.graph[round - 1];
|
|
21655
|
-
const startIndex = route.stopRouteIndex(hopOnStop);
|
|
21656
21688
|
// Compute target pruning criteria only once per route
|
|
21657
21689
|
const earliestArrivalAtAnyDestination = this.earliestArrivalAtAnyStop(routingState.earliestArrivals, routingState.destinations);
|
|
21658
|
-
for (let
|
|
21659
|
-
const currentStop = route.stops[
|
|
21690
|
+
for (let currentStopIndex = hopOnStopIndex; currentStopIndex < route.getNbStops(); currentStopIndex++) {
|
|
21691
|
+
const currentStop = route.stops[currentStopIndex];
|
|
21660
21692
|
// If we're currently on a trip,
|
|
21661
21693
|
// check if arrival at the stop improves the earliest arrival time
|
|
21662
21694
|
if (activeTrip !== undefined) {
|
|
21663
|
-
const arrivalTime = route.arrivalAt(
|
|
21664
|
-
const dropOffType = route.dropOffTypeAt(
|
|
21665
|
-
const earliestArrivalAtCurrentStop =
|
|
21695
|
+
const arrivalTime = route.arrivalAt(currentStopIndex, activeTrip.tripIndex);
|
|
21696
|
+
const dropOffType = route.dropOffTypeAt(currentStopIndex, activeTrip.tripIndex);
|
|
21697
|
+
const earliestArrivalAtCurrentStop = routingState.earliestArrivals.get(currentStop)?.arrival ?? UNREACHED;
|
|
21666
21698
|
if (dropOffType !== 'NOT_AVAILABLE' &&
|
|
21667
21699
|
arrivalTime.isBefore(earliestArrivalAtCurrentStop) &&
|
|
21668
21700
|
arrivalTime.isBefore(earliestArrivalAtAnyDestination)) {
|
|
@@ -21670,8 +21702,8 @@ class Router {
|
|
|
21670
21702
|
arrival: arrivalTime,
|
|
21671
21703
|
routeId: route.id,
|
|
21672
21704
|
tripIndex: activeTrip.tripIndex,
|
|
21673
|
-
from: activeTrip.
|
|
21674
|
-
to:
|
|
21705
|
+
from: activeTrip.hopOnStopIndex,
|
|
21706
|
+
to: currentStopIndex,
|
|
21675
21707
|
};
|
|
21676
21708
|
if (tripContinuation) {
|
|
21677
21709
|
// In case of continuous trip, we set a pointer to the previous edge
|
|
@@ -21691,21 +21723,21 @@ class Router {
|
|
|
21691
21723
|
}
|
|
21692
21724
|
// check if we can board an earlier trip at the current stop
|
|
21693
21725
|
// if there was no current trip, find the first one reachable
|
|
21694
|
-
const earliestArrivalOnPreviousRound =
|
|
21726
|
+
const earliestArrivalOnPreviousRound = edgesAtPreviousRound.get(currentStop)?.arrival;
|
|
21695
21727
|
// TODO if the last edge is not a transfer, and if there is no trip continuation of type 1 (guaranteed)
|
|
21696
21728
|
// Add the minTransferTime to make sure there's at least 2 minutes to transfer.
|
|
21697
21729
|
// If platforms are collapsed, make sure to apply the station level transfer time
|
|
21698
21730
|
// (or later at route reconstruction time)
|
|
21699
21731
|
if (earliestArrivalOnPreviousRound !== undefined &&
|
|
21700
21732
|
(activeTrip === undefined ||
|
|
21701
|
-
earliestArrivalOnPreviousRound.isBefore(route.departureFrom(
|
|
21702
|
-
earliestArrivalOnPreviousRound.equals(route.departureFrom(
|
|
21703
|
-
const earliestTrip = route.findEarliestTrip(
|
|
21733
|
+
earliestArrivalOnPreviousRound.isBefore(route.departureFrom(currentStopIndex, activeTrip.tripIndex)) ||
|
|
21734
|
+
earliestArrivalOnPreviousRound.equals(route.departureFrom(currentStopIndex, activeTrip.tripIndex)))) {
|
|
21735
|
+
const earliestTrip = route.findEarliestTrip(currentStopIndex, earliestArrivalOnPreviousRound, activeTrip?.tripIndex);
|
|
21704
21736
|
if (earliestTrip !== undefined) {
|
|
21705
21737
|
activeTrip = {
|
|
21706
21738
|
routeId: route.id,
|
|
21707
21739
|
tripIndex: earliestTrip,
|
|
21708
|
-
|
|
21740
|
+
hopOnStopIndex: currentStopIndex,
|
|
21709
21741
|
};
|
|
21710
21742
|
}
|
|
21711
21743
|
}
|
|
@@ -21723,7 +21755,6 @@ class Router {
|
|
|
21723
21755
|
* @param routingState The current routing state containing arrival times and marked stops
|
|
21724
21756
|
*/
|
|
21725
21757
|
considerTransfers(query, round, markedStops, routingState) {
|
|
21726
|
-
var _a, _b;
|
|
21727
21758
|
const { options } = query;
|
|
21728
21759
|
const arrivalsAtCurrentRound = routingState.graph[round];
|
|
21729
21760
|
const newlyMarkedStops = new Set();
|
|
@@ -21747,7 +21778,8 @@ class Router {
|
|
|
21747
21778
|
transferTime = options.minTransferTime;
|
|
21748
21779
|
}
|
|
21749
21780
|
const arrivalAfterTransfer = currentArrival.arrival.plus(transferTime);
|
|
21750
|
-
const originalArrival =
|
|
21781
|
+
const originalArrival = arrivalsAtCurrentRound.get(transfer.destination)?.arrival ??
|
|
21782
|
+
UNREACHED;
|
|
21751
21783
|
if (arrivalAfterTransfer.isBefore(originalArrival)) {
|
|
21752
21784
|
arrivalsAtCurrentRound.set(transfer.destination, {
|
|
21753
21785
|
arrival: arrivalAfterTransfer,
|
|
@@ -21774,11 +21806,10 @@ class Router {
|
|
|
21774
21806
|
* @returns The earliest arrival time among the provided destinations.
|
|
21775
21807
|
*/
|
|
21776
21808
|
earliestArrivalAtAnyStop(earliestArrivals, destinations) {
|
|
21777
|
-
var _a, _b;
|
|
21778
21809
|
let earliestArrivalAtAnyDestination = UNREACHED;
|
|
21779
21810
|
for (let i = 0; i < destinations.length; i++) {
|
|
21780
21811
|
const destination = destinations[i];
|
|
21781
|
-
const arrival =
|
|
21812
|
+
const arrival = earliestArrivals.get(destination)?.arrival ?? UNREACHED;
|
|
21782
21813
|
earliestArrivalAtAnyDestination = Time.min(earliestArrivalAtAnyDestination, arrival);
|
|
21783
21814
|
}
|
|
21784
21815
|
return earliestArrivalAtAnyDestination;
|
|
@@ -21980,7 +22011,6 @@ const startRepl = (stopsPath, timetablePath) => {
|
|
|
21980
22011
|
if (bestRoute) {
|
|
21981
22012
|
console.log(`Found route from ${fromStop.name} to ${toStop.name}:`);
|
|
21982
22013
|
console.log(bestRoute.toString());
|
|
21983
|
-
console.log(bestRoute.asJson());
|
|
21984
22014
|
}
|
|
21985
22015
|
else {
|
|
21986
22016
|
console.log('No route found');
|
|
@@ -22087,7 +22117,6 @@ const startRepl = (stopsPath, timetablePath) => {
|
|
|
22087
22117
|
return;
|
|
22088
22118
|
}
|
|
22089
22119
|
const inspectRoute = (routeIdStr) => {
|
|
22090
|
-
var _a, _b, _c;
|
|
22091
22120
|
const routeId = parseInt(routeIdStr.trim());
|
|
22092
22121
|
if (isNaN(routeId)) {
|
|
22093
22122
|
console.log('Usage: .inspect route <routeId>');
|
|
@@ -22109,29 +22138,30 @@ const startRepl = (stopsPath, timetablePath) => {
|
|
|
22109
22138
|
console.log('\n--- Stops ---');
|
|
22110
22139
|
for (let i = 0; i < route.stops.length; i++) {
|
|
22111
22140
|
const stopId = route.stopId(i);
|
|
22141
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
22112
22142
|
const stop = stopsIndex.findStopById(stopId);
|
|
22113
|
-
const platform =
|
|
22114
|
-
console.log(`${i + 1}. ${
|
|
22143
|
+
const platform = stop.platform ? ` (Pl. ${stop.platform})` : '';
|
|
22144
|
+
console.log(`${i + 1}. ${stop.name}${platform} (${stopId}, ${stop.sourceStopId})`);
|
|
22115
22145
|
}
|
|
22116
22146
|
console.log('\n--- Trips ---');
|
|
22117
22147
|
for (let tripIndex = 0; tripIndex < route.getNbTrips(); tripIndex++) {
|
|
22118
22148
|
console.log(`\nTrip ${tripIndex}:`);
|
|
22119
22149
|
for (let stopIndex = 0; stopIndex < route.stops.length; stopIndex++) {
|
|
22120
22150
|
const stopId = route.stopId(stopIndex);
|
|
22151
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
22121
22152
|
const stop = stopsIndex.findStopById(stopId);
|
|
22122
|
-
const departure = route.departureFrom(
|
|
22123
|
-
const arrival = route.arrivalAt(
|
|
22124
|
-
const pickupType = route.pickUpTypeFrom(
|
|
22125
|
-
const dropOffType = route.dropOffTypeAt(
|
|
22153
|
+
const departure = route.departureFrom(stopIndex, tripIndex);
|
|
22154
|
+
const arrival = route.arrivalAt(stopIndex, tripIndex);
|
|
22155
|
+
const pickupType = route.pickUpTypeFrom(stopIndex, tripIndex);
|
|
22156
|
+
const dropOffType = route.dropOffTypeAt(stopIndex, tripIndex);
|
|
22126
22157
|
const pickupStr = formatPickupDropoffType(pickupType);
|
|
22127
22158
|
const dropOffStr = formatPickupDropoffType(dropOffType);
|
|
22128
|
-
console.log(` ${stopIndex + 1}. ${
|
|
22159
|
+
console.log(` ${stopIndex + 1}. ${stop.name}: arr ${arrival.toString()} (${pickupStr}) → dep ${departure.toString()} (${dropOffStr})`);
|
|
22129
22160
|
}
|
|
22130
22161
|
}
|
|
22131
22162
|
console.log();
|
|
22132
22163
|
};
|
|
22133
22164
|
const inspectStop = (stopIdStr) => {
|
|
22134
|
-
var _a, _b, _c;
|
|
22135
22165
|
let stop;
|
|
22136
22166
|
const stopBySourceId = stopsIndex.findStopBySourceStopId(stopIdStr);
|
|
22137
22167
|
if (stopBySourceId !== undefined) {
|
|
@@ -22187,68 +22217,57 @@ const startRepl = (stopsPath, timetablePath) => {
|
|
|
22187
22217
|
if (transfers.length > 0) {
|
|
22188
22218
|
console.log('\n--- Transfers ---');
|
|
22189
22219
|
transfers.forEach((transfer, index) => {
|
|
22190
|
-
var _a, _b;
|
|
22191
22220
|
const destStop = stopsIndex.findStopById(transfer.destination);
|
|
22192
|
-
const platform =
|
|
22221
|
+
const platform = destStop?.platform
|
|
22193
22222
|
? ` (Pl. ${destStop.platform})`
|
|
22194
22223
|
: '';
|
|
22195
22224
|
const minTime = transfer.minTransferTime
|
|
22196
22225
|
? ` (min: ${Math.floor(transfer.minTransferTime.toSeconds() / 60)}min)`
|
|
22197
22226
|
: '';
|
|
22198
|
-
console.log(`${index + 1}. ${transfer.type} to ${
|
|
22227
|
+
console.log(`${index + 1}. ${transfer.type} to ${destStop?.name ?? 'Unknown'}${platform} (${transfer.destination}, ${destStop?.sourceStopId ?? 'N/A'})${minTime}`);
|
|
22199
22228
|
});
|
|
22200
22229
|
}
|
|
22201
22230
|
let totalContinuations = 0;
|
|
22202
|
-
|
|
22231
|
+
console.log('\n--- Trip Continuations ---');
|
|
22203
22232
|
routes.forEach((route) => {
|
|
22233
|
+
const serviceRouteInfo = timetable.getServiceRouteInfo(route);
|
|
22234
|
+
const stopIndices = route.stopRouteIndices(stop.id);
|
|
22204
22235
|
for (let tripIndex = 0; tripIndex < route.getNbTrips(); tripIndex++) {
|
|
22205
|
-
const
|
|
22206
|
-
|
|
22207
|
-
|
|
22208
|
-
|
|
22209
|
-
|
|
22210
|
-
|
|
22211
|
-
|
|
22212
|
-
|
|
22213
|
-
|
|
22236
|
+
for (const stopIndex of stopIndices) {
|
|
22237
|
+
const continuations = timetable.getContinuousTrips(stopIndex, route.id, tripIndex);
|
|
22238
|
+
for (const continuation of continuations) {
|
|
22239
|
+
totalContinuations++;
|
|
22240
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
22241
|
+
const destRoute = timetable.getRoute(continuation.routeId);
|
|
22242
|
+
const destStopId = destRoute.stopId(continuation.hopOnStopIndex);
|
|
22243
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
22244
|
+
const destStop = stopsIndex.findStopById(destStopId);
|
|
22245
|
+
const destPlatform = destStop.platform
|
|
22246
|
+
? ` (Pl. ${destStop.platform})`
|
|
22247
|
+
: '';
|
|
22248
|
+
const destServiceRouteInfo = timetable.getServiceRouteInfo(destRoute);
|
|
22249
|
+
const originTime = route.departureFrom(stopIndex, tripIndex);
|
|
22250
|
+
const continuationTime = destRoute.departureFrom(continuation.hopOnStopIndex, continuation.tripIndex);
|
|
22251
|
+
console.log(`${totalContinuations}. From Route ${route.id} (${serviceRouteInfo.name}) Trip ${tripIndex} at ${originTime.toString()} → ` +
|
|
22252
|
+
`Route ${continuation.routeId} (${destServiceRouteInfo.name}) Trip ${continuation.tripIndex} at ${continuationTime.toString()} ` +
|
|
22253
|
+
`at ${destStop.name}${destPlatform} (${destStopId}, ${destStop.sourceStopId})`);
|
|
22254
|
+
}
|
|
22214
22255
|
}
|
|
22215
22256
|
}
|
|
22216
22257
|
});
|
|
22217
|
-
|
|
22218
|
-
|
|
22219
|
-
|
|
22220
|
-
|
|
22221
|
-
|
|
22222
|
-
const { route, tripIndex, continuations } = value;
|
|
22223
|
-
const serviceRouteInfo = timetable.getServiceRouteInfo(route);
|
|
22224
|
-
for (const continuation of continuations) {
|
|
22225
|
-
const destStop = stopsIndex.findStopById(continuation.hopOnStop);
|
|
22226
|
-
const destPlatform = (destStop === null || destStop === void 0 ? void 0 : destStop.platform)
|
|
22227
|
-
? ` (Pl. ${destStop.platform})`
|
|
22228
|
-
: '';
|
|
22229
|
-
const destRoute = timetable.getRoute(continuation.routeId);
|
|
22230
|
-
const destServiceRouteInfo = destRoute
|
|
22231
|
-
? timetable.getServiceRouteInfo(destRoute)
|
|
22232
|
-
: null;
|
|
22233
|
-
const originTime = route.departureFrom(stop.id, tripIndex);
|
|
22234
|
-
const continuationTime = destRoute === null || destRoute === void 0 ? void 0 : destRoute.departureFrom(continuation.hopOnStop, continuation.tripIndex);
|
|
22235
|
-
const continuationTimeStr = continuationTime
|
|
22236
|
-
? ` at ${continuationTime.toString()}`
|
|
22237
|
-
: '';
|
|
22238
|
-
console.log(`${continuationIndex}. From Route ${route.id} (${serviceRouteInfo.name}) Trip ${tripIndex} at ${originTime.toString()} → ` +
|
|
22239
|
-
`Route ${continuation.routeId} (${(_a = destServiceRouteInfo === null || destServiceRouteInfo === void 0 ? void 0 : destServiceRouteInfo.name) !== null && _a !== void 0 ? _a : 'Unknown'}) Trip ${continuation.tripIndex}${continuationTimeStr} ` +
|
|
22240
|
-
`at ${(_b = destStop === null || destStop === void 0 ? void 0 : destStop.name) !== null && _b !== void 0 ? _b : 'Unknown'}${destPlatform} (${continuation.hopOnStop}, ${(_c = destStop === null || destStop === void 0 ? void 0 : destStop.sourceStopId) !== null && _c !== void 0 ? _c : 'N/A'})`);
|
|
22241
|
-
continuationIndex++;
|
|
22242
|
-
}
|
|
22243
|
-
}
|
|
22258
|
+
if (totalContinuations === 0) {
|
|
22259
|
+
console.log('No trip continuations found.');
|
|
22260
|
+
}
|
|
22261
|
+
else {
|
|
22262
|
+
console.log(`\nTotal trip continuations: ${totalContinuations}`);
|
|
22244
22263
|
}
|
|
22245
22264
|
console.log();
|
|
22246
22265
|
};
|
|
22247
22266
|
if (type === 'route') {
|
|
22248
|
-
inspectRoute(idStr
|
|
22267
|
+
inspectRoute(idStr ?? '');
|
|
22249
22268
|
}
|
|
22250
22269
|
else {
|
|
22251
|
-
inspectStop(idStr
|
|
22270
|
+
inspectStop(idStr ?? '');
|
|
22252
22271
|
}
|
|
22253
22272
|
this.displayPrompt();
|
|
22254
22273
|
},
|
|
@@ -22272,7 +22291,7 @@ program
|
|
|
22272
22291
|
.option('-s, --stopsOutputPath <path>', 'Path to output stops file', '/tmp/stops')
|
|
22273
22292
|
.option('-p, --profileName <name>', 'Profile name for GTFS config', 'CH')
|
|
22274
22293
|
.option('-v, --verbose', 'Verbose mode', false)
|
|
22275
|
-
.action((gtfsPath, options) =>
|
|
22294
|
+
.action(async (gtfsPath, options) => {
|
|
22276
22295
|
if (options.verbose) {
|
|
22277
22296
|
log.setDefaultLevel(log.levels.INFO);
|
|
22278
22297
|
}
|
|
@@ -22280,11 +22299,11 @@ program
|
|
|
22280
22299
|
log.setDefaultLevel(log.levels.ERROR);
|
|
22281
22300
|
}
|
|
22282
22301
|
const parser = new GtfsParser(gtfsPath, profiles[options.profileName]);
|
|
22283
|
-
const stopsIndex =
|
|
22302
|
+
const stopsIndex = await parser.parseStops();
|
|
22284
22303
|
fs.writeFileSync(options.stopsOutputPath, stopsIndex.serialize());
|
|
22285
|
-
const timetable =
|
|
22304
|
+
const timetable = await parser.parseTimetable(new Date(options.date));
|
|
22286
22305
|
fs.writeFileSync(options.timetableOutputPath, timetable.serialize());
|
|
22287
|
-
})
|
|
22306
|
+
});
|
|
22288
22307
|
program
|
|
22289
22308
|
.command('parse-stops')
|
|
22290
22309
|
.description('Parse a GTFS feed and output a stops file.')
|
|
@@ -22292,7 +22311,7 @@ program
|
|
|
22292
22311
|
.option('-s, --outputPath <path>', 'Path to output stops file', '/tmp/stops')
|
|
22293
22312
|
.option('-p, --profileName <name>', 'Profile name for GTFS config', 'CH')
|
|
22294
22313
|
.option('-v, --verbose', 'Verbose mode', false)
|
|
22295
|
-
.action((gtfsPath, options) =>
|
|
22314
|
+
.action(async (gtfsPath, options) => {
|
|
22296
22315
|
if (options.verbose) {
|
|
22297
22316
|
log.setDefaultLevel(log.levels.INFO);
|
|
22298
22317
|
}
|
|
@@ -22300,9 +22319,9 @@ program
|
|
|
22300
22319
|
log.setDefaultLevel(log.levels.ERROR);
|
|
22301
22320
|
}
|
|
22302
22321
|
const parser = new GtfsParser(gtfsPath, profiles[options.profileName]);
|
|
22303
|
-
const stopsIndex =
|
|
22322
|
+
const stopsIndex = await parser.parseStops();
|
|
22304
22323
|
fs.writeFileSync(options.stopsOutputPath, stopsIndex.serialize());
|
|
22305
|
-
})
|
|
22324
|
+
});
|
|
22306
22325
|
program
|
|
22307
22326
|
.command('repl')
|
|
22308
22327
|
.description('Find stops matching a textual query')
|