minotor 8.0.0 → 9.0.1
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/.github/workflows/minotor.yml +1 -1
- package/CHANGELOG.md +3 -8
- package/README.md +1 -1
- package/dist/cli.mjs +352 -256
- 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 +296 -188
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +296 -188
- 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/dist/timetable/tripId.d.ts +0 -15
- package/src/timetable/__tests__/tripId.test.ts +0 -27
- package/src/timetable/tripId.ts +0 -29
- /package/dist/timetable/__tests__/{tripId.test.d.ts → tripBoardingId.test.d.ts} +0 -0
package/dist/parser.cjs.js
CHANGED
|
@@ -12297,12 +12297,12 @@ const Transfer = {
|
|
|
12297
12297
|
},
|
|
12298
12298
|
};
|
|
12299
12299
|
function createBaseTripBoarding() {
|
|
12300
|
-
return {
|
|
12300
|
+
return { hopOnStopIndex: 0, routeId: 0, tripIndex: 0 };
|
|
12301
12301
|
}
|
|
12302
12302
|
const TripBoarding = {
|
|
12303
12303
|
encode(message, writer = new BinaryWriter()) {
|
|
12304
|
-
if (message.
|
|
12305
|
-
writer.uint32(8).uint32(message.
|
|
12304
|
+
if (message.hopOnStopIndex !== 0) {
|
|
12305
|
+
writer.uint32(8).uint32(message.hopOnStopIndex);
|
|
12306
12306
|
}
|
|
12307
12307
|
if (message.routeId !== 0) {
|
|
12308
12308
|
writer.uint32(16).uint32(message.routeId);
|
|
@@ -12323,7 +12323,7 @@ const TripBoarding = {
|
|
|
12323
12323
|
if (tag !== 8) {
|
|
12324
12324
|
break;
|
|
12325
12325
|
}
|
|
12326
|
-
message.
|
|
12326
|
+
message.hopOnStopIndex = reader.uint32();
|
|
12327
12327
|
continue;
|
|
12328
12328
|
}
|
|
12329
12329
|
case 2: {
|
|
@@ -12350,15 +12350,15 @@ const TripBoarding = {
|
|
|
12350
12350
|
},
|
|
12351
12351
|
fromJSON(object) {
|
|
12352
12352
|
return {
|
|
12353
|
-
|
|
12353
|
+
hopOnStopIndex: isSet(object.hopOnStopIndex) ? globalThis.Number(object.hopOnStopIndex) : 0,
|
|
12354
12354
|
routeId: isSet(object.routeId) ? globalThis.Number(object.routeId) : 0,
|
|
12355
12355
|
tripIndex: isSet(object.tripIndex) ? globalThis.Number(object.tripIndex) : 0,
|
|
12356
12356
|
};
|
|
12357
12357
|
},
|
|
12358
12358
|
toJSON(message) {
|
|
12359
12359
|
const obj = {};
|
|
12360
|
-
if (message.
|
|
12361
|
-
obj.
|
|
12360
|
+
if (message.hopOnStopIndex !== 0) {
|
|
12361
|
+
obj.hopOnStopIndex = Math.round(message.hopOnStopIndex);
|
|
12362
12362
|
}
|
|
12363
12363
|
if (message.routeId !== 0) {
|
|
12364
12364
|
obj.routeId = Math.round(message.routeId);
|
|
@@ -12374,22 +12374,28 @@ const TripBoarding = {
|
|
|
12374
12374
|
fromPartial(object) {
|
|
12375
12375
|
var _a, _b, _c;
|
|
12376
12376
|
const message = createBaseTripBoarding();
|
|
12377
|
-
message.
|
|
12377
|
+
message.hopOnStopIndex = (_a = object.hopOnStopIndex) !== null && _a !== void 0 ? _a : 0;
|
|
12378
12378
|
message.routeId = (_b = object.routeId) !== null && _b !== void 0 ? _b : 0;
|
|
12379
12379
|
message.tripIndex = (_c = object.tripIndex) !== null && _c !== void 0 ? _c : 0;
|
|
12380
12380
|
return message;
|
|
12381
12381
|
},
|
|
12382
12382
|
};
|
|
12383
12383
|
function createBaseTripContinuationEntry() {
|
|
12384
|
-
return {
|
|
12384
|
+
return { originStopIndex: 0, originRouteId: 0, originTripIndex: 0, continuations: [] };
|
|
12385
12385
|
}
|
|
12386
12386
|
const TripContinuationEntry = {
|
|
12387
12387
|
encode(message, writer = new BinaryWriter()) {
|
|
12388
|
-
if (message.
|
|
12389
|
-
writer.uint32(8).uint32(message.
|
|
12388
|
+
if (message.originStopIndex !== 0) {
|
|
12389
|
+
writer.uint32(8).uint32(message.originStopIndex);
|
|
12390
12390
|
}
|
|
12391
|
-
|
|
12392
|
-
|
|
12391
|
+
if (message.originRouteId !== 0) {
|
|
12392
|
+
writer.uint32(16).uint32(message.originRouteId);
|
|
12393
|
+
}
|
|
12394
|
+
if (message.originTripIndex !== 0) {
|
|
12395
|
+
writer.uint32(24).uint32(message.originTripIndex);
|
|
12396
|
+
}
|
|
12397
|
+
for (const v of message.continuations) {
|
|
12398
|
+
TripBoarding.encode(v, writer.uint32(34).fork()).join();
|
|
12393
12399
|
}
|
|
12394
12400
|
return writer;
|
|
12395
12401
|
},
|
|
@@ -12404,14 +12410,28 @@ const TripContinuationEntry = {
|
|
|
12404
12410
|
if (tag !== 8) {
|
|
12405
12411
|
break;
|
|
12406
12412
|
}
|
|
12407
|
-
message.
|
|
12413
|
+
message.originStopIndex = reader.uint32();
|
|
12408
12414
|
continue;
|
|
12409
12415
|
}
|
|
12410
12416
|
case 2: {
|
|
12411
|
-
if (tag !==
|
|
12417
|
+
if (tag !== 16) {
|
|
12412
12418
|
break;
|
|
12413
12419
|
}
|
|
12414
|
-
message.
|
|
12420
|
+
message.originRouteId = reader.uint32();
|
|
12421
|
+
continue;
|
|
12422
|
+
}
|
|
12423
|
+
case 3: {
|
|
12424
|
+
if (tag !== 24) {
|
|
12425
|
+
break;
|
|
12426
|
+
}
|
|
12427
|
+
message.originTripIndex = reader.uint32();
|
|
12428
|
+
continue;
|
|
12429
|
+
}
|
|
12430
|
+
case 4: {
|
|
12431
|
+
if (tag !== 34) {
|
|
12432
|
+
break;
|
|
12433
|
+
}
|
|
12434
|
+
message.continuations.push(TripBoarding.decode(reader, reader.uint32()));
|
|
12415
12435
|
continue;
|
|
12416
12436
|
}
|
|
12417
12437
|
}
|
|
@@ -12424,18 +12444,28 @@ const TripContinuationEntry = {
|
|
|
12424
12444
|
},
|
|
12425
12445
|
fromJSON(object) {
|
|
12426
12446
|
return {
|
|
12427
|
-
|
|
12428
|
-
|
|
12447
|
+
originStopIndex: isSet(object.originStopIndex) ? globalThis.Number(object.originStopIndex) : 0,
|
|
12448
|
+
originRouteId: isSet(object.originRouteId) ? globalThis.Number(object.originRouteId) : 0,
|
|
12449
|
+
originTripIndex: isSet(object.originTripIndex) ? globalThis.Number(object.originTripIndex) : 0,
|
|
12450
|
+
continuations: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.continuations)
|
|
12451
|
+
? object.continuations.map((e) => TripBoarding.fromJSON(e))
|
|
12452
|
+
: [],
|
|
12429
12453
|
};
|
|
12430
12454
|
},
|
|
12431
12455
|
toJSON(message) {
|
|
12432
12456
|
var _a;
|
|
12433
12457
|
const obj = {};
|
|
12434
|
-
if (message.
|
|
12435
|
-
obj.
|
|
12458
|
+
if (message.originStopIndex !== 0) {
|
|
12459
|
+
obj.originStopIndex = Math.round(message.originStopIndex);
|
|
12436
12460
|
}
|
|
12437
|
-
if (
|
|
12438
|
-
obj.
|
|
12461
|
+
if (message.originRouteId !== 0) {
|
|
12462
|
+
obj.originRouteId = Math.round(message.originRouteId);
|
|
12463
|
+
}
|
|
12464
|
+
if (message.originTripIndex !== 0) {
|
|
12465
|
+
obj.originTripIndex = Math.round(message.originTripIndex);
|
|
12466
|
+
}
|
|
12467
|
+
if ((_a = message.continuations) === null || _a === void 0 ? void 0 : _a.length) {
|
|
12468
|
+
obj.continuations = message.continuations.map((e) => TripBoarding.toJSON(e));
|
|
12439
12469
|
}
|
|
12440
12470
|
return obj;
|
|
12441
12471
|
},
|
|
@@ -12443,15 +12473,17 @@ const TripContinuationEntry = {
|
|
|
12443
12473
|
return TripContinuationEntry.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
12444
12474
|
},
|
|
12445
12475
|
fromPartial(object) {
|
|
12446
|
-
var _a, _b;
|
|
12476
|
+
var _a, _b, _c, _d;
|
|
12447
12477
|
const message = createBaseTripContinuationEntry();
|
|
12448
|
-
message.
|
|
12449
|
-
message.
|
|
12478
|
+
message.originStopIndex = (_a = object.originStopIndex) !== null && _a !== void 0 ? _a : 0;
|
|
12479
|
+
message.originRouteId = (_b = object.originRouteId) !== null && _b !== void 0 ? _b : 0;
|
|
12480
|
+
message.originTripIndex = (_c = object.originTripIndex) !== null && _c !== void 0 ? _c : 0;
|
|
12481
|
+
message.continuations = ((_d = object.continuations) === null || _d === void 0 ? void 0 : _d.map((e) => TripBoarding.fromPartial(e))) || [];
|
|
12450
12482
|
return message;
|
|
12451
12483
|
},
|
|
12452
12484
|
};
|
|
12453
12485
|
function createBaseStopAdjacency() {
|
|
12454
|
-
return { routes: [], transfers: []
|
|
12486
|
+
return { routes: [], transfers: [] };
|
|
12455
12487
|
}
|
|
12456
12488
|
const StopAdjacency = {
|
|
12457
12489
|
encode(message, writer = new BinaryWriter()) {
|
|
@@ -12463,9 +12495,6 @@ const StopAdjacency = {
|
|
|
12463
12495
|
for (const v of message.transfers) {
|
|
12464
12496
|
Transfer.encode(v, writer.uint32(18).fork()).join();
|
|
12465
12497
|
}
|
|
12466
|
-
for (const v of message.tripContinuations) {
|
|
12467
|
-
TripContinuationEntry.encode(v, writer.uint32(26).fork()).join();
|
|
12468
|
-
}
|
|
12469
12498
|
return writer;
|
|
12470
12499
|
},
|
|
12471
12500
|
decode(input, length) {
|
|
@@ -12496,13 +12525,6 @@ const StopAdjacency = {
|
|
|
12496
12525
|
message.transfers.push(Transfer.decode(reader, reader.uint32()));
|
|
12497
12526
|
continue;
|
|
12498
12527
|
}
|
|
12499
|
-
case 3: {
|
|
12500
|
-
if (tag !== 26) {
|
|
12501
|
-
break;
|
|
12502
|
-
}
|
|
12503
|
-
message.tripContinuations.push(TripContinuationEntry.decode(reader, reader.uint32()));
|
|
12504
|
-
continue;
|
|
12505
|
-
}
|
|
12506
12528
|
}
|
|
12507
12529
|
if ((tag & 7) === 4 || tag === 0) {
|
|
12508
12530
|
break;
|
|
@@ -12517,13 +12539,10 @@ const StopAdjacency = {
|
|
|
12517
12539
|
transfers: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.transfers)
|
|
12518
12540
|
? object.transfers.map((e) => Transfer.fromJSON(e))
|
|
12519
12541
|
: [],
|
|
12520
|
-
tripContinuations: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.tripContinuations)
|
|
12521
|
-
? object.tripContinuations.map((e) => TripContinuationEntry.fromJSON(e))
|
|
12522
|
-
: [],
|
|
12523
12542
|
};
|
|
12524
12543
|
},
|
|
12525
12544
|
toJSON(message) {
|
|
12526
|
-
var _a, _b
|
|
12545
|
+
var _a, _b;
|
|
12527
12546
|
const obj = {};
|
|
12528
12547
|
if ((_a = message.routes) === null || _a === void 0 ? void 0 : _a.length) {
|
|
12529
12548
|
obj.routes = message.routes.map((e) => Math.round(e));
|
|
@@ -12531,20 +12550,16 @@ const StopAdjacency = {
|
|
|
12531
12550
|
if ((_b = message.transfers) === null || _b === void 0 ? void 0 : _b.length) {
|
|
12532
12551
|
obj.transfers = message.transfers.map((e) => Transfer.toJSON(e));
|
|
12533
12552
|
}
|
|
12534
|
-
if ((_c = message.tripContinuations) === null || _c === void 0 ? void 0 : _c.length) {
|
|
12535
|
-
obj.tripContinuations = message.tripContinuations.map((e) => TripContinuationEntry.toJSON(e));
|
|
12536
|
-
}
|
|
12537
12553
|
return obj;
|
|
12538
12554
|
},
|
|
12539
12555
|
create(base) {
|
|
12540
12556
|
return StopAdjacency.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
12541
12557
|
},
|
|
12542
12558
|
fromPartial(object) {
|
|
12543
|
-
var _a, _b
|
|
12559
|
+
var _a, _b;
|
|
12544
12560
|
const message = createBaseStopAdjacency();
|
|
12545
12561
|
message.routes = ((_a = object.routes) === null || _a === void 0 ? void 0 : _a.map((e) => e)) || [];
|
|
12546
12562
|
message.transfers = ((_b = object.transfers) === null || _b === void 0 ? void 0 : _b.map((e) => Transfer.fromPartial(e))) || [];
|
|
12547
|
-
message.tripContinuations = ((_c = object.tripContinuations) === null || _c === void 0 ? void 0 : _c.map((e) => TripContinuationEntry.fromPartial(e))) || [];
|
|
12548
12563
|
return message;
|
|
12549
12564
|
},
|
|
12550
12565
|
};
|
|
@@ -12643,7 +12658,7 @@ const ServiceRoute = {
|
|
|
12643
12658
|
},
|
|
12644
12659
|
};
|
|
12645
12660
|
function createBaseTimetable() {
|
|
12646
|
-
return { version: "", stopsAdjacency: [], routesAdjacency: [], serviceRoutes: [] };
|
|
12661
|
+
return { version: "", stopsAdjacency: [], routesAdjacency: [], serviceRoutes: [], tripContinuations: [] };
|
|
12647
12662
|
}
|
|
12648
12663
|
const Timetable$1 = {
|
|
12649
12664
|
encode(message, writer = new BinaryWriter()) {
|
|
@@ -12659,6 +12674,9 @@ const Timetable$1 = {
|
|
|
12659
12674
|
for (const v of message.serviceRoutes) {
|
|
12660
12675
|
ServiceRoute.encode(v, writer.uint32(34).fork()).join();
|
|
12661
12676
|
}
|
|
12677
|
+
for (const v of message.tripContinuations) {
|
|
12678
|
+
TripContinuationEntry.encode(v, writer.uint32(42).fork()).join();
|
|
12679
|
+
}
|
|
12662
12680
|
return writer;
|
|
12663
12681
|
},
|
|
12664
12682
|
decode(input, length) {
|
|
@@ -12696,6 +12714,13 @@ const Timetable$1 = {
|
|
|
12696
12714
|
message.serviceRoutes.push(ServiceRoute.decode(reader, reader.uint32()));
|
|
12697
12715
|
continue;
|
|
12698
12716
|
}
|
|
12717
|
+
case 5: {
|
|
12718
|
+
if (tag !== 42) {
|
|
12719
|
+
break;
|
|
12720
|
+
}
|
|
12721
|
+
message.tripContinuations.push(TripContinuationEntry.decode(reader, reader.uint32()));
|
|
12722
|
+
continue;
|
|
12723
|
+
}
|
|
12699
12724
|
}
|
|
12700
12725
|
if ((tag & 7) === 4 || tag === 0) {
|
|
12701
12726
|
break;
|
|
@@ -12716,10 +12741,13 @@ const Timetable$1 = {
|
|
|
12716
12741
|
serviceRoutes: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.serviceRoutes)
|
|
12717
12742
|
? object.serviceRoutes.map((e) => ServiceRoute.fromJSON(e))
|
|
12718
12743
|
: [],
|
|
12744
|
+
tripContinuations: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.tripContinuations)
|
|
12745
|
+
? object.tripContinuations.map((e) => TripContinuationEntry.fromJSON(e))
|
|
12746
|
+
: [],
|
|
12719
12747
|
};
|
|
12720
12748
|
},
|
|
12721
12749
|
toJSON(message) {
|
|
12722
|
-
var _a, _b, _c;
|
|
12750
|
+
var _a, _b, _c, _d;
|
|
12723
12751
|
const obj = {};
|
|
12724
12752
|
if (message.version !== "") {
|
|
12725
12753
|
obj.version = message.version;
|
|
@@ -12733,18 +12761,22 @@ const Timetable$1 = {
|
|
|
12733
12761
|
if ((_c = message.serviceRoutes) === null || _c === void 0 ? void 0 : _c.length) {
|
|
12734
12762
|
obj.serviceRoutes = message.serviceRoutes.map((e) => ServiceRoute.toJSON(e));
|
|
12735
12763
|
}
|
|
12764
|
+
if ((_d = message.tripContinuations) === null || _d === void 0 ? void 0 : _d.length) {
|
|
12765
|
+
obj.tripContinuations = message.tripContinuations.map((e) => TripContinuationEntry.toJSON(e));
|
|
12766
|
+
}
|
|
12736
12767
|
return obj;
|
|
12737
12768
|
},
|
|
12738
12769
|
create(base) {
|
|
12739
12770
|
return Timetable$1.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
12740
12771
|
},
|
|
12741
12772
|
fromPartial(object) {
|
|
12742
|
-
var _a, _b, _c, _d;
|
|
12773
|
+
var _a, _b, _c, _d, _e;
|
|
12743
12774
|
const message = createBaseTimetable();
|
|
12744
12775
|
message.version = (_a = object.version) !== null && _a !== void 0 ? _a : "";
|
|
12745
12776
|
message.stopsAdjacency = ((_b = object.stopsAdjacency) === null || _b === void 0 ? void 0 : _b.map((e) => StopAdjacency.fromPartial(e))) || [];
|
|
12746
12777
|
message.routesAdjacency = ((_c = object.routesAdjacency) === null || _c === void 0 ? void 0 : _c.map((e) => Route$1.fromPartial(e))) || [];
|
|
12747
12778
|
message.serviceRoutes = ((_d = object.serviceRoutes) === null || _d === void 0 ? void 0 : _d.map((e) => ServiceRoute.fromPartial(e))) || [];
|
|
12779
|
+
message.tripContinuations = ((_e = object.tripContinuations) === null || _e === void 0 ? void 0 : _e.map((e) => TripContinuationEntry.fromPartial(e))) || [];
|
|
12748
12780
|
return message;
|
|
12749
12781
|
},
|
|
12750
12782
|
};
|
|
@@ -13044,7 +13076,14 @@ class Route {
|
|
|
13044
13076
|
this.stopIndices = new Map();
|
|
13045
13077
|
for (let i = 0; i < stops.length; i++) {
|
|
13046
13078
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
13047
|
-
|
|
13079
|
+
const stopId = stops[i];
|
|
13080
|
+
const existingIndices = this.stopIndices.get(stopId);
|
|
13081
|
+
if (existingIndices) {
|
|
13082
|
+
existingIndices.push(i);
|
|
13083
|
+
}
|
|
13084
|
+
else {
|
|
13085
|
+
this.stopIndices.set(stopId, [i]);
|
|
13086
|
+
}
|
|
13048
13087
|
}
|
|
13049
13088
|
}
|
|
13050
13089
|
/**
|
|
@@ -13132,24 +13171,6 @@ class Route {
|
|
|
13132
13171
|
serviceRouteId: this.serviceRouteId,
|
|
13133
13172
|
};
|
|
13134
13173
|
}
|
|
13135
|
-
/**
|
|
13136
|
-
* Checks if stop A is before stop B in the route.
|
|
13137
|
-
*
|
|
13138
|
-
* @param stopA - The StopId of the first stop.
|
|
13139
|
-
* @param stopB - The StopId of the second stop.
|
|
13140
|
-
* @returns True if stop A is before stop B, false otherwise.
|
|
13141
|
-
*/
|
|
13142
|
-
isBefore(stopA, stopB) {
|
|
13143
|
-
const stopAIndex = this.stopIndices.get(stopA);
|
|
13144
|
-
if (stopAIndex === undefined) {
|
|
13145
|
-
throw new Error(`Stop index not found for ${stopA} in route ${this.serviceRouteId}`);
|
|
13146
|
-
}
|
|
13147
|
-
const stopBIndex = this.stopIndices.get(stopB);
|
|
13148
|
-
if (stopBIndex === undefined) {
|
|
13149
|
-
throw new Error(`Stop index not found for ${stopB} in route ${this.serviceRouteId}`);
|
|
13150
|
-
}
|
|
13151
|
-
return stopAIndex < stopBIndex;
|
|
13152
|
-
}
|
|
13153
13174
|
/**
|
|
13154
13175
|
* Retrieves the number of stops in the route.
|
|
13155
13176
|
*
|
|
@@ -13178,47 +13199,47 @@ class Route {
|
|
|
13178
13199
|
/**
|
|
13179
13200
|
* Retrieves the arrival time at a specific stop for a given trip.
|
|
13180
13201
|
*
|
|
13181
|
-
* @param
|
|
13202
|
+
* @param stopIndex - The index of the stop in the route.
|
|
13182
13203
|
* @param tripIndex - The index of the trip.
|
|
13183
13204
|
* @returns The arrival time at the specified stop and trip as a Time object.
|
|
13184
13205
|
*/
|
|
13185
|
-
arrivalAt(
|
|
13186
|
-
const arrivalIndex = (tripIndex * this.stops.length +
|
|
13206
|
+
arrivalAt(stopIndex, tripIndex) {
|
|
13207
|
+
const arrivalIndex = (tripIndex * this.stops.length + stopIndex) * 2;
|
|
13187
13208
|
const arrival = this.stopTimes[arrivalIndex];
|
|
13188
13209
|
if (arrival === undefined) {
|
|
13189
|
-
throw new Error(`Arrival time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13210
|
+
throw new Error(`Arrival time not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13190
13211
|
}
|
|
13191
13212
|
return Time.fromMinutes(arrival);
|
|
13192
13213
|
}
|
|
13193
13214
|
/**
|
|
13194
13215
|
* Retrieves the departure time at a specific stop for a given trip.
|
|
13195
13216
|
*
|
|
13196
|
-
* @param
|
|
13217
|
+
* @param stopIndex - The index of the stop in the route.
|
|
13197
13218
|
* @param tripIndex - The index of the trip.
|
|
13198
13219
|
* @returns The departure time at the specified stop and trip as a Time object.
|
|
13199
13220
|
*/
|
|
13200
|
-
departureFrom(
|
|
13201
|
-
const departureIndex = (tripIndex * this.stops.length +
|
|
13221
|
+
departureFrom(stopIndex, tripIndex) {
|
|
13222
|
+
const departureIndex = (tripIndex * this.stops.length + stopIndex) * 2 + 1;
|
|
13202
13223
|
const departure = this.stopTimes[departureIndex];
|
|
13203
13224
|
if (departure === undefined) {
|
|
13204
|
-
throw new Error(`Departure time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13225
|
+
throw new Error(`Departure time not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13205
13226
|
}
|
|
13206
13227
|
return Time.fromMinutes(departure);
|
|
13207
13228
|
}
|
|
13208
13229
|
/**
|
|
13209
13230
|
* Retrieves the pick-up type for a specific stop and trip.
|
|
13210
13231
|
*
|
|
13211
|
-
* @param
|
|
13232
|
+
* @param stopIndex - The index of the stop in the route.
|
|
13212
13233
|
* @param tripIndex - The index of the trip.
|
|
13213
13234
|
* @returns The pick-up type at the specified stop and trip.
|
|
13214
13235
|
*/
|
|
13215
|
-
pickUpTypeFrom(
|
|
13216
|
-
const globalIndex = tripIndex * this.stops.length +
|
|
13236
|
+
pickUpTypeFrom(stopIndex, tripIndex) {
|
|
13237
|
+
const globalIndex = tripIndex * this.stops.length + stopIndex;
|
|
13217
13238
|
const byteIndex = Math.floor(globalIndex / 2);
|
|
13218
13239
|
const isSecondPair = globalIndex % 2 === 1;
|
|
13219
13240
|
const byte = this.pickUpDropOffTypes[byteIndex];
|
|
13220
13241
|
if (byte === undefined) {
|
|
13221
|
-
throw new Error(`Pick up type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13242
|
+
throw new Error(`Pick up type not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13222
13243
|
}
|
|
13223
13244
|
const pickUpValue = isSecondPair
|
|
13224
13245
|
? (byte >> 6) & 0x03 // Upper 2 bits for second pair
|
|
@@ -13228,17 +13249,17 @@ class Route {
|
|
|
13228
13249
|
/**
|
|
13229
13250
|
* Retrieves the drop-off type for a specific stop and trip.
|
|
13230
13251
|
*
|
|
13231
|
-
* @param
|
|
13252
|
+
* @param stopIndex - The index of the stop in the route.
|
|
13232
13253
|
* @param tripIndex - The index of the trip.
|
|
13233
13254
|
* @returns The drop-off type at the specified stop and trip.
|
|
13234
13255
|
*/
|
|
13235
|
-
dropOffTypeAt(
|
|
13236
|
-
const globalIndex = tripIndex * this.stops.length +
|
|
13256
|
+
dropOffTypeAt(stopIndex, tripIndex) {
|
|
13257
|
+
const globalIndex = tripIndex * this.stops.length + stopIndex;
|
|
13237
13258
|
const byteIndex = Math.floor(globalIndex / 2);
|
|
13238
13259
|
const isSecondPair = globalIndex % 2 === 1;
|
|
13239
13260
|
const byte = this.pickUpDropOffTypes[byteIndex];
|
|
13240
13261
|
if (byte === undefined) {
|
|
13241
|
-
throw new Error(`Drop off type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13262
|
+
throw new Error(`Drop off type not found for stop ${this.stopId(stopIndex)} (${stopIndex}) at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
13242
13263
|
}
|
|
13243
13264
|
const dropOffValue = isSecondPair
|
|
13244
13265
|
? (byte >> 4) & 0x03 // Bits 4-5 for second pair
|
|
@@ -13250,14 +13271,14 @@ class Route {
|
|
|
13250
13271
|
* optionally constrained by a latest trip index and a time before which the trip
|
|
13251
13272
|
* should not depart.
|
|
13252
13273
|
* *
|
|
13253
|
-
* @param
|
|
13274
|
+
* @param stopIndex - The route index of the stop where the trip should be found.
|
|
13254
13275
|
* @param [after=Time.origin()] - The earliest time after which the trip should depart.
|
|
13255
13276
|
* If not provided, searches all available trips.
|
|
13256
13277
|
* @param [beforeTrip] - (Optional) The index of the trip before which the search should be constrained.
|
|
13257
13278
|
* If not provided, searches all available trips.
|
|
13258
13279
|
* @returns The index of the earliest trip meeting the criteria, or undefined if no such trip is found.
|
|
13259
13280
|
*/
|
|
13260
|
-
findEarliestTrip(
|
|
13281
|
+
findEarliestTrip(stopIndex, after = Time.origin(), beforeTrip) {
|
|
13261
13282
|
if (this.nbTrips <= 0)
|
|
13262
13283
|
return undefined;
|
|
13263
13284
|
let hi = this.nbTrips - 1;
|
|
@@ -13269,7 +13290,7 @@ class Route {
|
|
|
13269
13290
|
let lb = -1;
|
|
13270
13291
|
while (lo <= hi) {
|
|
13271
13292
|
const mid = (lo + hi) >>> 1;
|
|
13272
|
-
const depMid = this.departureFrom(
|
|
13293
|
+
const depMid = this.departureFrom(stopIndex, mid);
|
|
13273
13294
|
if (depMid.isBefore(after)) {
|
|
13274
13295
|
lo = mid + 1;
|
|
13275
13296
|
}
|
|
@@ -13281,7 +13302,7 @@ class Route {
|
|
|
13281
13302
|
if (lb === -1)
|
|
13282
13303
|
return undefined;
|
|
13283
13304
|
for (let t = lb; t < (beforeTrip !== null && beforeTrip !== void 0 ? beforeTrip : this.nbTrips); t++) {
|
|
13284
|
-
const pickup = this.pickUpTypeFrom(
|
|
13305
|
+
const pickup = this.pickUpTypeFrom(stopIndex, t);
|
|
13285
13306
|
if (pickup !== 'NOT_AVAILABLE') {
|
|
13286
13307
|
return t;
|
|
13287
13308
|
}
|
|
@@ -13289,14 +13310,14 @@ class Route {
|
|
|
13289
13310
|
return undefined;
|
|
13290
13311
|
}
|
|
13291
13312
|
/**
|
|
13292
|
-
* Retrieves the
|
|
13313
|
+
* Retrieves the indices of a stop within the route.
|
|
13293
13314
|
* @param stopId The StopId of the stop to locate in the route.
|
|
13294
|
-
* @returns
|
|
13315
|
+
* @returns An array of indices where the stop appears in the route, or an empty array if the stop is not found.
|
|
13295
13316
|
*/
|
|
13296
|
-
|
|
13317
|
+
stopRouteIndices(stopId) {
|
|
13297
13318
|
const stopIndex = this.stopIndices.get(stopId);
|
|
13298
13319
|
if (stopIndex === undefined) {
|
|
13299
|
-
|
|
13320
|
+
return [];
|
|
13300
13321
|
}
|
|
13301
13322
|
return stopIndex;
|
|
13302
13323
|
}
|
|
@@ -13314,6 +13335,51 @@ class Route {
|
|
|
13314
13335
|
}
|
|
13315
13336
|
}
|
|
13316
13337
|
|
|
13338
|
+
// Each value uses 20 bits, allowing values from 0 to 1,048,575 (2^20 - 1)
|
|
13339
|
+
const VALUE_MASK = (BigInt(1) << BigInt(20)) - BigInt(1); // 0xFFFFF
|
|
13340
|
+
const MAX_VALUE = 1048575; // 2^20 - 1
|
|
13341
|
+
// Bit positions for each value in the 60-bit bigint
|
|
13342
|
+
const TRIP_INDEX_SHIFT = BigInt(0);
|
|
13343
|
+
const ROUTE_ID_SHIFT = BigInt(20);
|
|
13344
|
+
const STOP_INDEX_SHIFT = BigInt(40);
|
|
13345
|
+
/**
|
|
13346
|
+
* Validates that a value fits within 20 bits (0 to 1,048,575)
|
|
13347
|
+
* @param value - The value to validate
|
|
13348
|
+
* @param name - The name of the value for error reporting
|
|
13349
|
+
* @throws Error if the value is out of range
|
|
13350
|
+
*/
|
|
13351
|
+
const validateValue = (value, name) => {
|
|
13352
|
+
if (value < 0 || value > MAX_VALUE) {
|
|
13353
|
+
throw new Error(`${name} must be between 0 and ${MAX_VALUE}, got ${value}`);
|
|
13354
|
+
}
|
|
13355
|
+
};
|
|
13356
|
+
/**
|
|
13357
|
+
* Encodes a stop index, route ID, and trip index into a single trip boarding ID.
|
|
13358
|
+
* @param stopIndex - The index of the stop within the route (0 to 1,048,575)
|
|
13359
|
+
* @param routeId - The route identifier (0 to 1,048,575)
|
|
13360
|
+
* @param tripIndex - The index of the trip within the route (0 to 1,048,575)
|
|
13361
|
+
* @returns The encoded trip ID as a bigint
|
|
13362
|
+
*/
|
|
13363
|
+
const encode = (stopIndex, routeId, tripIndex) => {
|
|
13364
|
+
validateValue(stopIndex, 'stopIndex');
|
|
13365
|
+
validateValue(routeId, 'routeId');
|
|
13366
|
+
validateValue(tripIndex, 'tripIndex');
|
|
13367
|
+
return ((BigInt(stopIndex) << STOP_INDEX_SHIFT) |
|
|
13368
|
+
(BigInt(routeId) << ROUTE_ID_SHIFT) |
|
|
13369
|
+
(BigInt(tripIndex) << TRIP_INDEX_SHIFT));
|
|
13370
|
+
};
|
|
13371
|
+
/**
|
|
13372
|
+
* Decodes a trip boarding ID back into its constituent stop index, route ID, and trip index.
|
|
13373
|
+
* @param tripBoardingId - The encoded trip ID
|
|
13374
|
+
* @returns A tuple containing [stopIndex, routeId, tripIndex]
|
|
13375
|
+
*/
|
|
13376
|
+
const decode = (tripBoardingId) => {
|
|
13377
|
+
const stopIndex = Number((tripBoardingId >> STOP_INDEX_SHIFT) & VALUE_MASK);
|
|
13378
|
+
const routeId = Number((tripBoardingId >> ROUTE_ID_SHIFT) & VALUE_MASK);
|
|
13379
|
+
const tripIndex = Number((tripBoardingId >> TRIP_INDEX_SHIFT) & VALUE_MASK);
|
|
13380
|
+
return [stopIndex, routeId, tripIndex];
|
|
13381
|
+
};
|
|
13382
|
+
|
|
13317
13383
|
const isLittleEndian = (() => {
|
|
13318
13384
|
const buffer = new ArrayBuffer(4);
|
|
13319
13385
|
const view = new DataView(buffer);
|
|
@@ -13388,9 +13454,6 @@ const serializeStopsAdjacency = (stopsAdjacency) => {
|
|
|
13388
13454
|
}))))
|
|
13389
13455
|
: [],
|
|
13390
13456
|
routes: value.routes,
|
|
13391
|
-
tripContinuations: value.tripContinuations
|
|
13392
|
-
? serializeTripContinuations(value.tripContinuations)
|
|
13393
|
-
: [],
|
|
13394
13457
|
};
|
|
13395
13458
|
});
|
|
13396
13459
|
};
|
|
@@ -13436,10 +13499,6 @@ const deserializeStopsAdjacency = (protoStopsAdjacency) => {
|
|
|
13436
13499
|
if (transfers.length > 0) {
|
|
13437
13500
|
stopAdjacency.transfers = transfers;
|
|
13438
13501
|
}
|
|
13439
|
-
const deserializedTripContinuations = deserializeTripContinuations(value.tripContinuations);
|
|
13440
|
-
if (deserializedTripContinuations.size > 0) {
|
|
13441
|
-
stopAdjacency.tripContinuations = deserializedTripContinuations;
|
|
13442
|
-
}
|
|
13443
13502
|
result.push(stopAdjacency);
|
|
13444
13503
|
}
|
|
13445
13504
|
return result;
|
|
@@ -13546,11 +13605,14 @@ const serializeRouteType = (type) => {
|
|
|
13546
13605
|
};
|
|
13547
13606
|
const serializeTripContinuations = (tripContinuations) => {
|
|
13548
13607
|
const result = [];
|
|
13549
|
-
for (const [
|
|
13608
|
+
for (const [tripBoardingId, boardings] of tripContinuations.entries()) {
|
|
13609
|
+
const [originStopIndex, originRouteId, originTripIndex] = decode(tripBoardingId);
|
|
13550
13610
|
result.push({
|
|
13551
|
-
|
|
13552
|
-
|
|
13553
|
-
|
|
13611
|
+
originStopIndex,
|
|
13612
|
+
originRouteId,
|
|
13613
|
+
originTripIndex,
|
|
13614
|
+
continuations: boardings.map((tripBoarding) => ({
|
|
13615
|
+
hopOnStopIndex: tripBoarding.hopOnStopIndex,
|
|
13554
13616
|
routeId: tripBoarding.routeId,
|
|
13555
13617
|
tripIndex: tripBoarding.tripIndex,
|
|
13556
13618
|
})),
|
|
@@ -13563,28 +13625,17 @@ const deserializeTripContinuations = (protoTripContinuations) => {
|
|
|
13563
13625
|
for (let i = 0; i < protoTripContinuations.length; i++) {
|
|
13564
13626
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
13565
13627
|
const entry = protoTripContinuations[i];
|
|
13566
|
-
const
|
|
13567
|
-
|
|
13628
|
+
const tripBoardingId = encode(entry.originStopIndex, entry.originRouteId, entry.originTripIndex);
|
|
13629
|
+
const tripBoardings = entry.continuations.map((protoTripBoarding) => ({
|
|
13630
|
+
hopOnStopIndex: protoTripBoarding.hopOnStopIndex,
|
|
13568
13631
|
routeId: protoTripBoarding.routeId,
|
|
13569
13632
|
tripIndex: protoTripBoarding.tripIndex,
|
|
13570
13633
|
}));
|
|
13571
|
-
result.set(
|
|
13634
|
+
result.set(tripBoardingId, tripBoardings);
|
|
13572
13635
|
}
|
|
13573
13636
|
return result;
|
|
13574
13637
|
};
|
|
13575
13638
|
|
|
13576
|
-
// const ROUTE_ID_BITS = 17;
|
|
13577
|
-
const TRIP_INDEX_BITS = 15;
|
|
13578
|
-
/**
|
|
13579
|
-
* Encodes a route ID and trip index into a single trip ID.
|
|
13580
|
-
* @param routeId - The route identifier, needs to fit on 17 bits
|
|
13581
|
-
* @param tripIndex - The index of the trip within the route, needs to fit on 15 bits
|
|
13582
|
-
* @returns The encoded trip ID
|
|
13583
|
-
*/
|
|
13584
|
-
const encode = (routeId, tripIndex) => {
|
|
13585
|
-
return (routeId << TRIP_INDEX_BITS) | tripIndex;
|
|
13586
|
-
};
|
|
13587
|
-
|
|
13588
13639
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
13589
13640
|
const ALL_TRANSPORT_MODES = new Set([
|
|
13590
13641
|
'TRAM',
|
|
@@ -13599,21 +13650,21 @@ const ALL_TRANSPORT_MODES = new Set([
|
|
|
13599
13650
|
'MONORAIL',
|
|
13600
13651
|
]);
|
|
13601
13652
|
const EMPTY_TRIP_CONTINUATIONS = [];
|
|
13602
|
-
const CURRENT_VERSION = '0.0.
|
|
13653
|
+
const CURRENT_VERSION = '0.0.9';
|
|
13603
13654
|
/**
|
|
13604
13655
|
* The internal transit timetable format.
|
|
13605
13656
|
*/
|
|
13606
13657
|
class Timetable {
|
|
13607
|
-
constructor(stopsAdjacency, routesAdjacency, routes) {
|
|
13658
|
+
constructor(stopsAdjacency, routesAdjacency, routes, tripContinuations) {
|
|
13608
13659
|
this.stopsAdjacency = stopsAdjacency;
|
|
13609
13660
|
this.routesAdjacency = routesAdjacency;
|
|
13610
13661
|
this.serviceRoutes = routes;
|
|
13662
|
+
this.tripContinuations = tripContinuations;
|
|
13611
13663
|
this.activeStops = new Set();
|
|
13612
13664
|
for (let i = 0; i < stopsAdjacency.length; i++) {
|
|
13613
13665
|
const stop = stopsAdjacency[i];
|
|
13614
13666
|
if (stop.routes.length > 0 ||
|
|
13615
|
-
(stop.transfers && stop.transfers.length > 0)
|
|
13616
|
-
(stop.tripContinuations && stop.tripContinuations.size > 0)) {
|
|
13667
|
+
(stop.transfers && stop.transfers.length > 0)) {
|
|
13617
13668
|
this.activeStops.add(i);
|
|
13618
13669
|
}
|
|
13619
13670
|
}
|
|
@@ -13629,6 +13680,7 @@ class Timetable {
|
|
|
13629
13680
|
stopsAdjacency: serializeStopsAdjacency(this.stopsAdjacency),
|
|
13630
13681
|
routesAdjacency: serializeRoutesAdjacency(this.routesAdjacency),
|
|
13631
13682
|
serviceRoutes: serializeServiceRoutesMap(this.serviceRoutes),
|
|
13683
|
+
tripContinuations: serializeTripContinuations(this.tripContinuations || new Map()),
|
|
13632
13684
|
};
|
|
13633
13685
|
const writer = new BinaryWriter();
|
|
13634
13686
|
Timetable$1.encode(protoTimetable, writer);
|
|
@@ -13646,7 +13698,7 @@ class Timetable {
|
|
|
13646
13698
|
if (protoTimetable.version !== CURRENT_VERSION) {
|
|
13647
13699
|
throw new Error(`Unsupported timetable version ${protoTimetable.version}`);
|
|
13648
13700
|
}
|
|
13649
|
-
return new Timetable(deserializeStopsAdjacency(protoTimetable.stopsAdjacency), deserializeRoutesAdjacency(protoTimetable.routesAdjacency), deserializeServiceRoutesMap(protoTimetable.serviceRoutes));
|
|
13701
|
+
return new Timetable(deserializeStopsAdjacency(protoTimetable.stopsAdjacency), deserializeRoutesAdjacency(protoTimetable.routesAdjacency), deserializeServiceRoutesMap(protoTimetable.serviceRoutes), deserializeTripContinuations(protoTimetable.tripContinuations));
|
|
13650
13702
|
}
|
|
13651
13703
|
/**
|
|
13652
13704
|
* Checks if the given stop is active on the timetable.
|
|
@@ -13685,18 +13737,18 @@ class Timetable {
|
|
|
13685
13737
|
/**
|
|
13686
13738
|
* Retrieves all trip continuation options available at the specified stop for a given trip.
|
|
13687
13739
|
*
|
|
13688
|
-
* @param
|
|
13740
|
+
* @param stopIndex - The index in the route of the stop to get trip continuations for.
|
|
13741
|
+
* @param routeId - The ID of the route to get continuations for.
|
|
13689
13742
|
* @param tripIndex - The index of the trip to get continuations for.
|
|
13690
13743
|
* @returns An array of trip continuation options available at the stop for the specified trip.
|
|
13691
13744
|
*/
|
|
13692
|
-
getContinuousTrips(
|
|
13745
|
+
getContinuousTrips(stopIndex, routeId, tripIndex) {
|
|
13693
13746
|
var _a;
|
|
13694
|
-
const
|
|
13695
|
-
if (!
|
|
13696
|
-
|
|
13747
|
+
const tripContinuations = (_a = this.tripContinuations) === null || _a === void 0 ? void 0 : _a.get(encode(stopIndex, routeId, tripIndex));
|
|
13748
|
+
if (!tripContinuations) {
|
|
13749
|
+
return EMPTY_TRIP_CONTINUATIONS;
|
|
13697
13750
|
}
|
|
13698
|
-
return
|
|
13699
|
-
EMPTY_TRIP_CONTINUATIONS);
|
|
13751
|
+
return tripContinuations;
|
|
13700
13752
|
}
|
|
13701
13753
|
/**
|
|
13702
13754
|
* Retrieves the service route associated with the given route.
|
|
@@ -13736,12 +13788,12 @@ class Timetable {
|
|
|
13736
13788
|
}
|
|
13737
13789
|
/**
|
|
13738
13790
|
* Finds routes that are reachable from a set of stop IDs.
|
|
13739
|
-
* Also identifies the first stop available to hop on each route among
|
|
13791
|
+
* Also identifies the first stop index available to hop on each route among
|
|
13740
13792
|
* the input stops.
|
|
13741
13793
|
*
|
|
13742
13794
|
* @param fromStops - The set of stop IDs to find reachable routes from.
|
|
13743
13795
|
* @param transportModes - The set of transport modes to consider for reachable routes.
|
|
13744
|
-
* @returns A map of reachable routes to the first stop available to hop on each route.
|
|
13796
|
+
* @returns A map of reachable routes to the first stop index available to hop on each route.
|
|
13745
13797
|
*/
|
|
13746
13798
|
findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
|
|
13747
13799
|
const reachableRoutes = new Map();
|
|
@@ -13754,15 +13806,17 @@ class Timetable {
|
|
|
13754
13806
|
});
|
|
13755
13807
|
for (let j = 0; j < validRoutes.length; j++) {
|
|
13756
13808
|
const route = validRoutes[j];
|
|
13757
|
-
const
|
|
13758
|
-
|
|
13759
|
-
|
|
13809
|
+
const originStopIndices = route.stopRouteIndices(originStop);
|
|
13810
|
+
const originStopIndex = originStopIndices[0];
|
|
13811
|
+
const existingHopOnStopIndex = reachableRoutes.get(route);
|
|
13812
|
+
if (existingHopOnStopIndex !== undefined) {
|
|
13813
|
+
if (originStopIndex < existingHopOnStopIndex) {
|
|
13760
13814
|
// if the current stop is before the existing hop on stop, replace it
|
|
13761
|
-
reachableRoutes.set(route,
|
|
13815
|
+
reachableRoutes.set(route, originStopIndex);
|
|
13762
13816
|
}
|
|
13763
13817
|
}
|
|
13764
13818
|
else {
|
|
13765
|
-
reachableRoutes.set(route,
|
|
13819
|
+
reachableRoutes.set(route, originStopIndex);
|
|
13766
13820
|
}
|
|
13767
13821
|
}
|
|
13768
13822
|
}
|
|
@@ -15982,7 +16036,7 @@ const parseGtfsLocationType = (gtfsLocationType) => {
|
|
|
15982
16036
|
const parseTransfers = (transfersStream, stopsMap) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15983
16037
|
var _a, e_1, _b, _c;
|
|
15984
16038
|
const transfers = new Map();
|
|
15985
|
-
const tripContinuations =
|
|
16039
|
+
const tripContinuations = [];
|
|
15986
16040
|
try {
|
|
15987
16041
|
for (var _d = true, _e = __asyncValues(parseCsv(transfersStream, [
|
|
15988
16042
|
'transfer_type',
|
|
@@ -16000,10 +16054,12 @@ const parseTransfers = (transfersStream, stopsMap) => __awaiter(void 0, void 0,
|
|
|
16000
16054
|
console.warn(`Missing transfer origin or destination stop.`);
|
|
16001
16055
|
continue;
|
|
16002
16056
|
}
|
|
16003
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16004
16057
|
const fromStop = stopsMap.get(transferEntry.from_stop_id);
|
|
16005
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16006
16058
|
const toStop = stopsMap.get(transferEntry.to_stop_id);
|
|
16059
|
+
if (!fromStop || !toStop) {
|
|
16060
|
+
console.warn(`Transfer references non-existent stop(s): from_stop_id=${transferEntry.from_stop_id}, to_stop_id=${transferEntry.to_stop_id}`);
|
|
16061
|
+
continue;
|
|
16062
|
+
}
|
|
16007
16063
|
if (transferEntry.transfer_type === 4) {
|
|
16008
16064
|
if (transferEntry.from_trip_id === undefined ||
|
|
16009
16065
|
transferEntry.from_trip_id === '' ||
|
|
@@ -16013,17 +16069,12 @@ const parseTransfers = (transfersStream, stopsMap) => __awaiter(void 0, void 0,
|
|
|
16013
16069
|
continue;
|
|
16014
16070
|
}
|
|
16015
16071
|
const tripBoardingEntry = {
|
|
16072
|
+
fromStop: fromStop.id,
|
|
16016
16073
|
fromTrip: transferEntry.from_trip_id,
|
|
16074
|
+
toStop: toStop.id,
|
|
16017
16075
|
toTrip: transferEntry.to_trip_id,
|
|
16018
|
-
hopOnStop: toStop.id,
|
|
16019
16076
|
};
|
|
16020
|
-
|
|
16021
|
-
if (existingBoardings) {
|
|
16022
|
-
existingBoardings.push(tripBoardingEntry);
|
|
16023
|
-
}
|
|
16024
|
-
else {
|
|
16025
|
-
tripContinuations.set(fromStop.id, [tripBoardingEntry]);
|
|
16026
|
-
}
|
|
16077
|
+
tripContinuations.push(tripBoardingEntry);
|
|
16027
16078
|
continue;
|
|
16028
16079
|
}
|
|
16029
16080
|
if (transferEntry.from_trip_id && transferEntry.to_trip_id) {
|
|
@@ -16038,7 +16089,7 @@ const parseTransfers = (transfersStream, stopsMap) => __awaiter(void 0, void 0,
|
|
|
16038
16089
|
transferEntry.min_transfer_time === undefined) {
|
|
16039
16090
|
console.info(`Missing minimum transfer time between ${transferEntry.from_stop_id} and ${transferEntry.to_stop_id}.`);
|
|
16040
16091
|
}
|
|
16041
|
-
const transfer = Object.assign({ destination: toStop.id, type: parseGtfsTransferType(transferEntry.transfer_type) }, (transferEntry.min_transfer_time && {
|
|
16092
|
+
const transfer = Object.assign({ destination: toStop.id, type: parseGtfsTransferType(transferEntry.transfer_type) }, (transferEntry.min_transfer_time !== undefined && {
|
|
16042
16093
|
minTransferTime: Duration.fromSeconds(transferEntry.min_transfer_time),
|
|
16043
16094
|
}));
|
|
16044
16095
|
const fromStopTransfers = transfers.get(fromStop.id) || [];
|
|
@@ -16058,6 +16109,91 @@ const parseTransfers = (transfersStream, stopsMap) => __awaiter(void 0, void 0,
|
|
|
16058
16109
|
tripContinuations,
|
|
16059
16110
|
};
|
|
16060
16111
|
});
|
|
16112
|
+
/**
|
|
16113
|
+
* Disambiguates stops involved in a transfer.
|
|
16114
|
+
*
|
|
16115
|
+
* The GTFS specification only refers to a stopId in the trip-to-trip transfers and not the
|
|
16116
|
+
* specific stop index in the route. For routes that have multiple stops with the same stopId,
|
|
16117
|
+
* we need to determine which are the from / to stop indices in respective routes.
|
|
16118
|
+
* We do so by picking the stop indices leading to the most coherent transfer.
|
|
16119
|
+
* (we pick the closest from stop index happening after the to stop index).
|
|
16120
|
+
*/
|
|
16121
|
+
const disambiguateTransferStopsIndices = (fromStop, fromRoute, fromTripIndex, toStop, toRoute, toTripIndex) => {
|
|
16122
|
+
const fromStopIndices = fromRoute.stopRouteIndices(fromStop);
|
|
16123
|
+
const toStopIndices = toRoute.stopRouteIndices(toStop);
|
|
16124
|
+
let bestFromStopIndex;
|
|
16125
|
+
let bestToStopIndex;
|
|
16126
|
+
let bestTimeDifference = Infinity;
|
|
16127
|
+
for (const originStopIndex of fromStopIndices) {
|
|
16128
|
+
const fromArrivalTime = fromRoute.arrivalAt(originStopIndex, fromTripIndex);
|
|
16129
|
+
for (const toStopIndex of toStopIndices) {
|
|
16130
|
+
const toDepartureTime = toRoute.departureFrom(toStopIndex, toTripIndex);
|
|
16131
|
+
if (toDepartureTime.isAfter(fromArrivalTime)) {
|
|
16132
|
+
const timeDifference = toDepartureTime.toMinutes() - fromArrivalTime.toMinutes();
|
|
16133
|
+
if (timeDifference < bestTimeDifference) {
|
|
16134
|
+
bestTimeDifference = timeDifference;
|
|
16135
|
+
bestFromStopIndex = originStopIndex;
|
|
16136
|
+
bestToStopIndex = toStopIndex;
|
|
16137
|
+
}
|
|
16138
|
+
}
|
|
16139
|
+
}
|
|
16140
|
+
}
|
|
16141
|
+
if (bestFromStopIndex !== undefined && bestToStopIndex !== undefined) {
|
|
16142
|
+
return {
|
|
16143
|
+
fromStopIndex: bestFromStopIndex,
|
|
16144
|
+
toStopIndex: bestToStopIndex,
|
|
16145
|
+
};
|
|
16146
|
+
}
|
|
16147
|
+
return undefined;
|
|
16148
|
+
};
|
|
16149
|
+
/**
|
|
16150
|
+
* Builds trip continuations map from GTFS trip continuation data.
|
|
16151
|
+
*
|
|
16152
|
+
* This function processes GTFS in-seat transfer data and creates a mapping
|
|
16153
|
+
* from trip boarding IDs to continuation boarding information. It disambiguates
|
|
16154
|
+
* stop indices when routes have multiple stops with the same ID by finding
|
|
16155
|
+
* the most coherent transfer timing.
|
|
16156
|
+
*
|
|
16157
|
+
* @param tripsMapping Mapping from GTFS trip IDs to internal trip representations
|
|
16158
|
+
* @param tripContinuations Array of GTFS trip continuation data from transfers.txt
|
|
16159
|
+
* @param timetable The timetable containing route and timing information
|
|
16160
|
+
* @param activeStopIds Set of stop IDs that are active/enabled in the system
|
|
16161
|
+
* @returns A map from trip boarding IDs to arrays of continuation boarding options
|
|
16162
|
+
*/
|
|
16163
|
+
const buildTripContinuations = (tripsMapping, tripContinuations, timetable, activeStopIds) => {
|
|
16164
|
+
const continuations = new Map();
|
|
16165
|
+
for (const gtfsContinuation of tripContinuations) {
|
|
16166
|
+
if (!activeStopIds.has(gtfsContinuation.fromStop) ||
|
|
16167
|
+
!activeStopIds.has(gtfsContinuation.toStop)) {
|
|
16168
|
+
continue;
|
|
16169
|
+
}
|
|
16170
|
+
const fromTripMapping = tripsMapping.get(gtfsContinuation.fromTrip);
|
|
16171
|
+
const toTripMapping = tripsMapping.get(gtfsContinuation.toTrip);
|
|
16172
|
+
if (!fromTripMapping || !toTripMapping) {
|
|
16173
|
+
continue;
|
|
16174
|
+
}
|
|
16175
|
+
const fromRoute = timetable.getRoute(fromTripMapping.routeId);
|
|
16176
|
+
const toRoute = timetable.getRoute(toTripMapping.routeId);
|
|
16177
|
+
if (!fromRoute || !toRoute) {
|
|
16178
|
+
continue;
|
|
16179
|
+
}
|
|
16180
|
+
const bestStopIndices = disambiguateTransferStopsIndices(gtfsContinuation.fromStop, fromRoute, fromTripMapping.tripRouteIndex, gtfsContinuation.toStop, toRoute, toTripMapping.tripRouteIndex);
|
|
16181
|
+
if (!bestStopIndices) {
|
|
16182
|
+
// No valid continuation found
|
|
16183
|
+
continue;
|
|
16184
|
+
}
|
|
16185
|
+
const tripBoardingId = encode(bestStopIndices.fromStopIndex, fromTripMapping.routeId, fromTripMapping.tripRouteIndex);
|
|
16186
|
+
const continuationBoarding = {
|
|
16187
|
+
hopOnStopIndex: bestStopIndices.toStopIndex,
|
|
16188
|
+
routeId: toTripMapping.routeId,
|
|
16189
|
+
tripIndex: toTripMapping.tripRouteIndex,
|
|
16190
|
+
};
|
|
16191
|
+
const existingContinuations = continuations.get(tripBoardingId) || [];
|
|
16192
|
+
existingContinuations.push(continuationBoarding);
|
|
16193
|
+
continuations.set(tripBoardingId, existingContinuations);
|
|
16194
|
+
}
|
|
16195
|
+
return continuations;
|
|
16196
|
+
};
|
|
16061
16197
|
const parseGtfsTransferType = (gtfsTransferType) => {
|
|
16062
16198
|
switch (gtfsTransferType) {
|
|
16063
16199
|
case 0:
|
|
@@ -16194,7 +16330,7 @@ const parseTrips = (tripsStream, serviceIds, validGtfsRoutes) => __awaiter(void
|
|
|
16194
16330
|
}
|
|
16195
16331
|
return trips;
|
|
16196
16332
|
});
|
|
16197
|
-
const buildStopsAdjacencyStructure = (
|
|
16333
|
+
const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbStops, activeStops) => {
|
|
16198
16334
|
const stopsAdjacency = new Array(nbStops);
|
|
16199
16335
|
for (let i = 0; i < nbStops; i++) {
|
|
16200
16336
|
stopsAdjacency[i] = {
|
|
@@ -16234,40 +16370,6 @@ const buildStopsAdjacencyStructure = (tripsMapping, serviceRoutes, routes, trans
|
|
|
16234
16370
|
}
|
|
16235
16371
|
}
|
|
16236
16372
|
}
|
|
16237
|
-
for (const [stop, tripContinuations] of tripContinuationsMap) {
|
|
16238
|
-
for (let i = 0; i < tripContinuations.length; i++) {
|
|
16239
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16240
|
-
const tripContinuation = tripContinuations[i];
|
|
16241
|
-
if (activeStops.has(stop) ||
|
|
16242
|
-
activeStops.has(tripContinuation.hopOnStop)) {
|
|
16243
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16244
|
-
const stopAdj = stopsAdjacency[stop];
|
|
16245
|
-
if (!stopAdj.tripContinuations) {
|
|
16246
|
-
stopAdj.tripContinuations = new Map();
|
|
16247
|
-
}
|
|
16248
|
-
const originTrip = tripsMapping.get(tripContinuation.fromTrip);
|
|
16249
|
-
const destinationTrip = tripsMapping.get(tripContinuation.toTrip);
|
|
16250
|
-
if (destinationTrip === undefined || originTrip === undefined) {
|
|
16251
|
-
continue;
|
|
16252
|
-
}
|
|
16253
|
-
const tripBoarding = {
|
|
16254
|
-
hopOnStop: tripContinuation.hopOnStop,
|
|
16255
|
-
routeId: destinationTrip.routeId,
|
|
16256
|
-
tripIndex: destinationTrip.tripRouteIndex,
|
|
16257
|
-
};
|
|
16258
|
-
const tripId = encode(originTrip.routeId, originTrip.tripRouteIndex);
|
|
16259
|
-
const existingContinuations = stopAdj.tripContinuations.get(tripId);
|
|
16260
|
-
if (existingContinuations) {
|
|
16261
|
-
existingContinuations.push(tripBoarding);
|
|
16262
|
-
}
|
|
16263
|
-
else {
|
|
16264
|
-
stopAdj.tripContinuations.set(tripId, [tripBoarding]);
|
|
16265
|
-
}
|
|
16266
|
-
activeStops.add(tripContinuation.hopOnStop);
|
|
16267
|
-
activeStops.add(stop);
|
|
16268
|
-
}
|
|
16269
|
-
}
|
|
16270
|
-
}
|
|
16271
16373
|
return stopsAdjacency;
|
|
16272
16374
|
};
|
|
16273
16375
|
/**
|
|
@@ -16494,16 +16596,16 @@ class GtfsParser {
|
|
|
16494
16596
|
const tripsEnd = performance.now();
|
|
16495
16597
|
log.info(`${trips.size} valid trips. (${(tripsEnd - tripsStart).toFixed(2)}ms)`);
|
|
16496
16598
|
let transfers = new Map();
|
|
16497
|
-
let
|
|
16599
|
+
let tripContinuationsMap = [];
|
|
16498
16600
|
if (entries[TRANSFERS_FILE]) {
|
|
16499
16601
|
log.info(`Parsing ${TRANSFERS_FILE}`);
|
|
16500
16602
|
const transfersStart = performance.now();
|
|
16501
16603
|
const transfersStream = yield zip.stream(TRANSFERS_FILE);
|
|
16502
16604
|
const { transfers: parsedTransfers, tripContinuations: parsedTripContinuations, } = yield parseTransfers(transfersStream, parsedStops);
|
|
16503
16605
|
transfers = parsedTransfers;
|
|
16504
|
-
|
|
16606
|
+
tripContinuationsMap = parsedTripContinuations;
|
|
16505
16607
|
const transfersEnd = performance.now();
|
|
16506
|
-
log.info(`${transfers.size} valid transfers and ${
|
|
16608
|
+
log.info(`${transfers.size} valid transfers and ${tripContinuationsMap.length} trip continuations. (${(transfersEnd - transfersStart).toFixed(2)}ms)`);
|
|
16507
16609
|
}
|
|
16508
16610
|
log.info(`Parsing ${STOP_TIMES_FILE}`);
|
|
16509
16611
|
const stopTimesStart = performance.now();
|
|
@@ -16514,13 +16616,19 @@ class GtfsParser {
|
|
|
16514
16616
|
log.info(`${routes.length} valid unique routes. (${(stopTimesEnd - stopTimesStart).toFixed(2)}ms)`);
|
|
16515
16617
|
log.info('Building stops adjacency structure');
|
|
16516
16618
|
const stopsAdjacencyStart = performance.now();
|
|
16517
|
-
const stopsAdjacency = buildStopsAdjacencyStructure(
|
|
16619
|
+
const stopsAdjacency = buildStopsAdjacencyStructure(serviceRoutes, routes, transfers, parsedStops.size, activeStopIds);
|
|
16518
16620
|
const stopsAdjacencyEnd = performance.now();
|
|
16519
16621
|
log.info(`${stopsAdjacency.length} valid stops in the structure. (${(stopsAdjacencyEnd - stopsAdjacencyStart).toFixed(2)}ms)`);
|
|
16520
16622
|
yield zip.close();
|
|
16623
|
+
// temporary timetable for building continuations
|
|
16521
16624
|
const timetable = new Timetable(stopsAdjacency, routes, serviceRoutes);
|
|
16625
|
+
log.info('Building in-seat trip continuations');
|
|
16626
|
+
const tripContinuationsStart = performance.now();
|
|
16627
|
+
const tripContinuations = buildTripContinuations(tripsMapping, tripContinuationsMap, timetable, activeStopIds);
|
|
16628
|
+
const tripContinuationsEnd = performance.now();
|
|
16629
|
+
log.info(`${tripContinuations.size} in-seat trip continuations origins created. (${(tripContinuationsEnd - tripContinuationsStart).toFixed(2)}ms)`);
|
|
16522
16630
|
log.info('Parsing complete.');
|
|
16523
|
-
return
|
|
16631
|
+
return new Timetable(stopsAdjacency, routes, serviceRoutes, tripContinuations);
|
|
16524
16632
|
});
|
|
16525
16633
|
}
|
|
16526
16634
|
/**
|