minotor 6.0.0 → 7.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 +3 -3
- package/dist/cli.mjs +94 -77
- package/dist/cli.mjs.map +1 -1
- package/dist/parser.cjs.js +75 -70
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +75 -70
- 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/timetable/route.d.ts +2 -11
- package/dist/timetable/timetable.d.ts +1 -1
- package/package.json +1 -1
- package/src/__e2e__/timetable/timetable.bin +1 -1
- package/src/gtfs/trips.ts +10 -4
- package/src/routing/router.ts +19 -7
- package/src/stops/stopsIndex.ts +24 -22
- package/src/timetable/__tests__/route.test.ts +0 -19
- package/src/timetable/io.ts +39 -19
- package/src/timetable/proto/timetable.proto +2 -2
- package/src/timetable/proto/timetable.ts +6 -6
- package/src/timetable/route.ts +2 -32
- package/src/timetable/timetable.ts +9 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# [
|
|
1
|
+
# [7.0.0](https://github.com/aubryio/minotor/compare/v6.0.0...v7.0.0) (2025-09-19)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Performance Improvements
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* optimize loops on the critical path ([#26](https://github.com/aubryio/minotor/issues/26)) ([8504930](https://github.com/aubryio/minotor/commit/85049308f7bbcf4d88f3dcfb47edd2511e9da80d))
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
### BREAKING CHANGES
|
|
10
10
|
|
|
11
|
-
*
|
|
11
|
+
* Timetable binary format was updated and is not compatible with the old one.
|
package/dist/cli.mjs
CHANGED
|
@@ -16079,20 +16079,15 @@ const serializeLocationType = (locationType) => {
|
|
|
16079
16079
|
*/
|
|
16080
16080
|
class StopsIndex {
|
|
16081
16081
|
constructor(stops) {
|
|
16082
|
+
var _a;
|
|
16082
16083
|
this.stops = stops;
|
|
16083
16084
|
this.sourceStopsMap = new Map();
|
|
16084
|
-
stops.forEach((stop, id) => {
|
|
16085
|
-
this.sourceStopsMap.set(stop.sourceStopId, id);
|
|
16086
|
-
});
|
|
16087
|
-
this.textIndex = lt({
|
|
16088
|
-
fields: ['name'],
|
|
16089
|
-
storeFields: ['id'],
|
|
16090
|
-
searchOptions: { prefix: true, fuzzy: 0.2 },
|
|
16091
|
-
processTerm: generateAccentVariants,
|
|
16092
|
-
});
|
|
16093
16085
|
const stopsSet = new Map();
|
|
16094
|
-
|
|
16095
|
-
|
|
16086
|
+
this.stopPoints = [];
|
|
16087
|
+
for (let id = 0; id < stops.length; id++) {
|
|
16088
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16089
|
+
const stop = stops[id];
|
|
16090
|
+
this.sourceStopsMap.set(stop.sourceStopId, id);
|
|
16096
16091
|
const effectiveStopId = (_a = stop.parent) !== null && _a !== void 0 ? _a : id;
|
|
16097
16092
|
if (!stopsSet.has(effectiveStopId)) {
|
|
16098
16093
|
stopsSet.set(effectiveStopId, {
|
|
@@ -16101,22 +16096,26 @@ class StopsIndex {
|
|
|
16101
16096
|
name: stop.parent ? this.stops[stop.parent].name : stop.name,
|
|
16102
16097
|
});
|
|
16103
16098
|
}
|
|
16099
|
+
if (stop.lat && stop.lon) {
|
|
16100
|
+
this.stopPoints.push({
|
|
16101
|
+
id: id,
|
|
16102
|
+
lat: stop.lat,
|
|
16103
|
+
lon: stop.lon,
|
|
16104
|
+
});
|
|
16105
|
+
}
|
|
16106
|
+
}
|
|
16107
|
+
this.textIndex = lt({
|
|
16108
|
+
fields: ['name'],
|
|
16109
|
+
storeFields: ['id'],
|
|
16110
|
+
searchOptions: { prefix: true, fuzzy: 0.2 },
|
|
16111
|
+
processTerm: generateAccentVariants,
|
|
16104
16112
|
});
|
|
16105
16113
|
const stopsArray = Array.from(stopsSet.values());
|
|
16106
16114
|
q(this.textIndex, stopsArray);
|
|
16107
|
-
this.stopPoints = this.stops
|
|
16108
|
-
.filter((stop) => {
|
|
16109
|
-
if (stop.lat && stop.lon)
|
|
16110
|
-
return true;
|
|
16111
|
-
return false;
|
|
16112
|
-
})
|
|
16113
|
-
.map((stop, id) => ({
|
|
16114
|
-
id: id,
|
|
16115
|
-
lat: stop.lat,
|
|
16116
|
-
lon: stop.lon,
|
|
16117
|
-
}));
|
|
16118
16115
|
this.geoIndex = new KDBush(this.stopPoints.length);
|
|
16119
|
-
for (
|
|
16116
|
+
for (let i = 0; i < this.stopPoints.length; i++) {
|
|
16117
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16118
|
+
const { lat, lon } = this.stopPoints[i];
|
|
16120
16119
|
this.geoIndex.add(lon, lat);
|
|
16121
16120
|
}
|
|
16122
16121
|
this.geoIndex.finish();
|
|
@@ -16629,7 +16628,7 @@ const StopAdjacency = {
|
|
|
16629
16628
|
}
|
|
16630
16629
|
writer.uint32(18).fork();
|
|
16631
16630
|
for (const v of message.routes) {
|
|
16632
|
-
writer.
|
|
16631
|
+
writer.uint32(v);
|
|
16633
16632
|
}
|
|
16634
16633
|
writer.join();
|
|
16635
16634
|
return writer;
|
|
@@ -16650,13 +16649,13 @@ const StopAdjacency = {
|
|
|
16650
16649
|
}
|
|
16651
16650
|
case 2: {
|
|
16652
16651
|
if (tag === 16) {
|
|
16653
|
-
message.routes.push(reader.
|
|
16652
|
+
message.routes.push(reader.uint32());
|
|
16654
16653
|
continue;
|
|
16655
16654
|
}
|
|
16656
16655
|
if (tag === 18) {
|
|
16657
16656
|
const end2 = reader.uint32() + reader.pos;
|
|
16658
16657
|
while (reader.pos < end2) {
|
|
16659
|
-
message.routes.push(reader.
|
|
16658
|
+
message.routes.push(reader.uint32());
|
|
16660
16659
|
}
|
|
16661
16660
|
continue;
|
|
16662
16661
|
}
|
|
@@ -16713,7 +16712,7 @@ const ServiceRoute = {
|
|
|
16713
16712
|
}
|
|
16714
16713
|
writer.uint32(26).fork();
|
|
16715
16714
|
for (const v of message.routes) {
|
|
16716
|
-
writer.
|
|
16715
|
+
writer.uint32(v);
|
|
16717
16716
|
}
|
|
16718
16717
|
writer.join();
|
|
16719
16718
|
return writer;
|
|
@@ -16741,13 +16740,13 @@ const ServiceRoute = {
|
|
|
16741
16740
|
}
|
|
16742
16741
|
case 3: {
|
|
16743
16742
|
if (tag === 24) {
|
|
16744
|
-
message.routes.push(reader.
|
|
16743
|
+
message.routes.push(reader.uint32());
|
|
16745
16744
|
continue;
|
|
16746
16745
|
}
|
|
16747
16746
|
if (tag === 26) {
|
|
16748
16747
|
const end2 = reader.uint32() + reader.pos;
|
|
16749
16748
|
while (reader.pos < end2) {
|
|
16750
|
-
message.routes.push(reader.
|
|
16749
|
+
message.routes.push(reader.uint32());
|
|
16751
16750
|
}
|
|
16752
16751
|
continue;
|
|
16753
16752
|
}
|
|
@@ -17302,27 +17301,6 @@ let Route$1 = class Route {
|
|
|
17302
17301
|
: byte & 0x03; // Lower 2 bits for first pair
|
|
17303
17302
|
return toPickupDropOffType(dropOffValue);
|
|
17304
17303
|
}
|
|
17305
|
-
/**
|
|
17306
|
-
* Iterates over the stops in the route, starting from an optional specified stop.
|
|
17307
|
-
* If no start stop is provided, the iteration begins from the first stop in the route.
|
|
17308
|
-
*
|
|
17309
|
-
* @param [startStopId] - (Optional) The StopId of the stop to start the iteration from.
|
|
17310
|
-
* @returns An IterableIterator of StopIds, starting from the specified stop or the first stop.
|
|
17311
|
-
* @throws An error if the specified start stop is not found in the route.
|
|
17312
|
-
*/
|
|
17313
|
-
stopsIterator(startStopId) {
|
|
17314
|
-
const startIndex = startStopId !== undefined ? this.stopIndices.get(startStopId) : 0;
|
|
17315
|
-
if (startIndex === undefined) {
|
|
17316
|
-
throw new Error(`Start stop ${startStopId} not found in route ${this.serviceRouteId}`);
|
|
17317
|
-
}
|
|
17318
|
-
function* generator(stops, startIndex) {
|
|
17319
|
-
for (let i = startIndex; i < stops.length; i++) {
|
|
17320
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17321
|
-
yield stops[i];
|
|
17322
|
-
}
|
|
17323
|
-
}
|
|
17324
|
-
return generator(this.stops, startIndex);
|
|
17325
|
-
}
|
|
17326
17304
|
/**
|
|
17327
17305
|
* Finds the earliest trip that can be taken from a specific stop on a given route,
|
|
17328
17306
|
* optionally constrained by a latest trip index and a time before which the trip
|
|
@@ -17472,31 +17450,48 @@ const serializeServiceRoutesMap = (serviceRoutes) => {
|
|
|
17472
17450
|
});
|
|
17473
17451
|
};
|
|
17474
17452
|
const deserializeStopsAdjacency = (protoStopsAdjacency) => {
|
|
17475
|
-
|
|
17476
|
-
|
|
17477
|
-
|
|
17453
|
+
const result = [];
|
|
17454
|
+
for (let i = 0; i < protoStopsAdjacency.length; i++) {
|
|
17455
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17456
|
+
const value = protoStopsAdjacency[i];
|
|
17457
|
+
const transfers = [];
|
|
17458
|
+
for (let j = 0; j < value.transfers.length; j++) {
|
|
17459
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17460
|
+
const transfer = value.transfers[j];
|
|
17461
|
+
const newTransfer = Object.assign({ destination: transfer.destination, type: parseTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
|
|
17478
17462
|
minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
|
|
17479
|
-
}))
|
|
17463
|
+
}));
|
|
17464
|
+
transfers.push(newTransfer);
|
|
17465
|
+
}
|
|
17466
|
+
result.push({
|
|
17467
|
+
transfers: transfers,
|
|
17480
17468
|
routes: value.routes,
|
|
17481
|
-
};
|
|
17482
|
-
}
|
|
17469
|
+
});
|
|
17470
|
+
}
|
|
17471
|
+
return result;
|
|
17483
17472
|
};
|
|
17484
17473
|
const deserializeRoutesAdjacency = (protoRoutesAdjacency) => {
|
|
17485
17474
|
const routesAdjacency = [];
|
|
17486
|
-
protoRoutesAdjacency.
|
|
17475
|
+
for (let i = 0; i < protoRoutesAdjacency.length; i++) {
|
|
17476
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17477
|
+
const value = protoRoutesAdjacency[i];
|
|
17487
17478
|
const stops = bytesToUint32Array(value.stops);
|
|
17488
17479
|
routesAdjacency.push(new Route$1(bytesToUint16Array(value.stopTimes), value.pickUpDropOffTypes, stops, value.serviceRouteId));
|
|
17489
|
-
}
|
|
17480
|
+
}
|
|
17490
17481
|
return routesAdjacency;
|
|
17491
17482
|
};
|
|
17492
17483
|
const deserializeServiceRoutesMap = (protoServiceRoutes) => {
|
|
17493
|
-
|
|
17494
|
-
|
|
17484
|
+
const result = [];
|
|
17485
|
+
for (let i = 0; i < protoServiceRoutes.length; i++) {
|
|
17486
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17487
|
+
const value = protoServiceRoutes[i];
|
|
17488
|
+
result.push({
|
|
17495
17489
|
type: parseRouteType(value.type),
|
|
17496
17490
|
name: value.name,
|
|
17497
17491
|
routes: value.routes,
|
|
17498
|
-
};
|
|
17499
|
-
}
|
|
17492
|
+
});
|
|
17493
|
+
}
|
|
17494
|
+
return result;
|
|
17500
17495
|
};
|
|
17501
17496
|
const parseTransferType = (type) => {
|
|
17502
17497
|
switch (type) {
|
|
@@ -17588,7 +17583,7 @@ const ALL_TRANSPORT_MODES = new Set([
|
|
|
17588
17583
|
'TROLLEYBUS',
|
|
17589
17584
|
'MONORAIL',
|
|
17590
17585
|
]);
|
|
17591
|
-
const CURRENT_VERSION = '0.0.
|
|
17586
|
+
const CURRENT_VERSION = '0.0.7';
|
|
17592
17587
|
/**
|
|
17593
17588
|
* The internal transit timetable format.
|
|
17594
17589
|
*/
|
|
@@ -17696,7 +17691,8 @@ class Timetable {
|
|
|
17696
17691
|
return [];
|
|
17697
17692
|
}
|
|
17698
17693
|
const routes = [];
|
|
17699
|
-
for (
|
|
17694
|
+
for (let i = 0; i < stopData.routes.length; i++) {
|
|
17695
|
+
const routeId = stopData.routes[i];
|
|
17700
17696
|
const route = this.routesAdjacency[routeId];
|
|
17701
17697
|
if (route) {
|
|
17702
17698
|
routes.push(route);
|
|
@@ -17715,12 +17711,15 @@ class Timetable {
|
|
|
17715
17711
|
*/
|
|
17716
17712
|
findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
|
|
17717
17713
|
const reachableRoutes = new Map();
|
|
17718
|
-
|
|
17714
|
+
const fromStopsArray = Array.from(fromStops);
|
|
17715
|
+
for (let i = 0; i < fromStopsArray.length; i++) {
|
|
17716
|
+
const originStop = fromStopsArray[i];
|
|
17719
17717
|
const validRoutes = this.routesPassingThrough(originStop).filter((route) => {
|
|
17720
17718
|
const serviceRoute = this.getServiceRouteInfo(route);
|
|
17721
17719
|
return transportModes.has(serviceRoute.type);
|
|
17722
17720
|
});
|
|
17723
|
-
for (
|
|
17721
|
+
for (let j = 0; j < validRoutes.length; j++) {
|
|
17722
|
+
const route = validRoutes[j];
|
|
17724
17723
|
const hopOnStop = reachableRoutes.get(route);
|
|
17725
17724
|
if (hopOnStop) {
|
|
17726
17725
|
if (route.isBefore(originStop, hopOnStop)) {
|
|
@@ -20135,8 +20134,12 @@ const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbSto
|
|
|
20135
20134
|
for (let i = 0; i < nbStops; i++) {
|
|
20136
20135
|
stopsAdjacency[i] = { routes: [], transfers: [] };
|
|
20137
20136
|
}
|
|
20138
|
-
routes.
|
|
20139
|
-
|
|
20137
|
+
for (let index = 0; index < routes.length; index++) {
|
|
20138
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20139
|
+
const route = routes[index];
|
|
20140
|
+
for (let j = 0; j < route.getNbStops(); j++) {
|
|
20141
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20142
|
+
const stop = route.stops[j];
|
|
20140
20143
|
if (activeStops.has(stop)) {
|
|
20141
20144
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20142
20145
|
stopsAdjacency[stop].routes.push(index);
|
|
@@ -20147,9 +20150,11 @@ const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbSto
|
|
|
20147
20150
|
throw new Error(`Service route ${route.serviceRoute()} not found for route ${index}.`);
|
|
20148
20151
|
}
|
|
20149
20152
|
serviceRoute.routes.push(index);
|
|
20150
|
-
}
|
|
20153
|
+
}
|
|
20151
20154
|
for (const [stop, transfers] of transfersMap) {
|
|
20152
|
-
for (
|
|
20155
|
+
for (let i = 0; i < transfers.length; i++) {
|
|
20156
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20157
|
+
const transfer = transfers[i];
|
|
20153
20158
|
if (activeStops.has(stop) || activeStops.has(transfer.destination)) {
|
|
20154
20159
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20155
20160
|
stopsAdjacency[stop].transfers.push(transfer);
|
|
@@ -20815,7 +20820,9 @@ class Router {
|
|
|
20815
20820
|
var _a, _b;
|
|
20816
20821
|
const { options } = query;
|
|
20817
20822
|
const newlyMarkedStops = new Set();
|
|
20818
|
-
|
|
20823
|
+
const markedStopsArray = Array.from(markedStops);
|
|
20824
|
+
for (let i = 0; i < markedStopsArray.length; i++) {
|
|
20825
|
+
const stop = markedStopsArray[i];
|
|
20819
20826
|
const currentArrival = arrivalsAtCurrentRound.get(stop);
|
|
20820
20827
|
if (!currentArrival)
|
|
20821
20828
|
continue;
|
|
@@ -20824,7 +20831,9 @@ class Router {
|
|
|
20824
20831
|
if (previousLeg && !('route' in previousLeg)) {
|
|
20825
20832
|
continue;
|
|
20826
20833
|
}
|
|
20827
|
-
|
|
20834
|
+
const transfers = this.timetable.getTransfers(stop);
|
|
20835
|
+
for (let j = 0; j < transfers.length; j++) {
|
|
20836
|
+
const transfer = transfers[j];
|
|
20828
20837
|
let transferTime;
|
|
20829
20838
|
if (transfer.minTransferTime) {
|
|
20830
20839
|
transferTime = transfer.minTransferTime;
|
|
@@ -20859,7 +20868,9 @@ class Router {
|
|
|
20859
20868
|
}
|
|
20860
20869
|
}
|
|
20861
20870
|
}
|
|
20862
|
-
|
|
20871
|
+
const newlyMarkedStopsArray = Array.from(newlyMarkedStops);
|
|
20872
|
+
for (let i = 0; i < newlyMarkedStopsArray.length; i++) {
|
|
20873
|
+
const newStop = newlyMarkedStopsArray[i];
|
|
20863
20874
|
markedStops.add(newStop);
|
|
20864
20875
|
}
|
|
20865
20876
|
}
|
|
@@ -20873,7 +20884,8 @@ class Router {
|
|
|
20873
20884
|
earliestArrivalAtAnyStop(earliestArrivals, destinations) {
|
|
20874
20885
|
var _a, _b;
|
|
20875
20886
|
let earliestArrivalAtAnyDestination = UNREACHED;
|
|
20876
|
-
for (
|
|
20887
|
+
for (let i = 0; i < destinations.length; i++) {
|
|
20888
|
+
const destination = destinations[i];
|
|
20877
20889
|
const arrival = (_b = (_a = earliestArrivals.get(destination.id)) === null || _a === void 0 ? void 0 : _a.arrival) !== null && _b !== void 0 ? _b : UNREACHED;
|
|
20878
20890
|
earliestArrivalAtAnyDestination = Time.min(earliestArrivalAtAnyDestination, arrival);
|
|
20879
20891
|
}
|
|
@@ -20897,7 +20909,8 @@ class Router {
|
|
|
20897
20909
|
const earliestArrivalsPerRound = [earliestArrivalsWithoutAnyLeg];
|
|
20898
20910
|
// Stops that have been improved at round k-1
|
|
20899
20911
|
const markedStops = new Set();
|
|
20900
|
-
for (
|
|
20912
|
+
for (let i = 0; i < origins.length; i++) {
|
|
20913
|
+
const originStop = origins[i];
|
|
20901
20914
|
markedStops.add(originStop.id);
|
|
20902
20915
|
earliestArrivals.set(originStop.id, {
|
|
20903
20916
|
arrival: departureTime,
|
|
@@ -20921,9 +20934,13 @@ class Router {
|
|
|
20921
20934
|
const reachableRoutes = this.timetable.findReachableRoutes(markedStops, options.transportModes);
|
|
20922
20935
|
markedStops.clear();
|
|
20923
20936
|
// for each route that can be reached with at least round - 1 trips
|
|
20924
|
-
|
|
20937
|
+
const reachableRoutesArray = Array.from(reachableRoutes.entries());
|
|
20938
|
+
for (let i = 0; i < reachableRoutesArray.length; i++) {
|
|
20939
|
+
const [route, hopOnStop] = reachableRoutesArray[i];
|
|
20925
20940
|
let currentTrip = undefined;
|
|
20926
|
-
|
|
20941
|
+
const startIndex = route.stopIndex(hopOnStop);
|
|
20942
|
+
for (let j = startIndex; j < route.getNbStops(); j++) {
|
|
20943
|
+
const currentStop = route.stops[j];
|
|
20927
20944
|
// If we're currently on a trip,
|
|
20928
20945
|
// check if arrival at the stop improves the earliest arrival time
|
|
20929
20946
|
if (currentTrip !== undefined) {
|