minotor 3.0.2 → 4.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 +8 -3
- package/README.md +1 -0
- package/dist/cli.mjs +175 -98
- package/dist/cli.mjs.map +1 -1
- package/dist/gtfs/trips.d.ts +6 -1
- package/dist/parser.cjs.js +172 -94
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +172 -94
- 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/proto/timetable.d.ts +2 -1
- package/dist/timetable/route.d.ts +6 -4
- package/dist/timetable/timetable.d.ts +1 -1
- package/package.json +1 -1
- package/src/__e2e__/timetable/timetable.bin +2 -2
- package/src/cli/repl.ts +0 -1
- package/src/gtfs/__tests__/trips.test.ts +15 -26
- package/src/gtfs/parser.ts +49 -9
- package/src/gtfs/trips.ts +176 -95
- package/src/timetable/__tests__/route.test.ts +25 -17
- package/src/timetable/__tests__/timetable.test.ts +7 -11
- package/src/timetable/proto/timetable.proto +2 -1
- package/src/timetable/proto/timetable.ts +2 -1
- package/src/timetable/route.ts +26 -12
- package/src/timetable/timetable.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
# [4.0.0](https://github.com/aubryio/minotor/compare/v3.0.2...v4.0.0) (2025-09-13)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
###
|
|
4
|
+
### Performance Improvements
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* encode boarding flags on 2 bits only ([b373d0c](https://github.com/aubryio/minotor/commit/b373d0c606ca6ead53e9138d5ca31d2928e2415b))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### BREAKING CHANGES
|
|
10
|
+
|
|
11
|
+
* Timetable format 0.0.4 introduced to use 2 bits instead of 8 for boarding flags.
|
package/README.md
CHANGED
|
@@ -183,6 +183,7 @@ The project is under active development. Here are some of the features that are
|
|
|
183
183
|
Contact [the author](https://aubry.io/) for feature requests.
|
|
184
184
|
|
|
185
185
|
- Route/Trip-based transfer support
|
|
186
|
+
- Favor shorter trips with less transfers in case of tie
|
|
186
187
|
- Arrive-by support
|
|
187
188
|
- Range queries
|
|
188
189
|
- Transfer preferences
|
package/dist/cli.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import require$$2$1 from 'path';
|
|
|
10
10
|
import require$$3$1 from 'events';
|
|
11
11
|
import require$$4$1 from 'zlib';
|
|
12
12
|
import require$$5, { Transform } from 'stream';
|
|
13
|
-
import { performance } from 'perf_hooks';
|
|
13
|
+
import { performance as performance$1 } from 'perf_hooks';
|
|
14
14
|
import repl from 'node:repl';
|
|
15
15
|
|
|
16
16
|
/******************************************************************************
|
|
@@ -17754,11 +17754,16 @@ let Route$1 = class Route {
|
|
|
17754
17754
|
* @returns The pick-up type at the specified stop and trip.
|
|
17755
17755
|
*/
|
|
17756
17756
|
pickUpTypeFrom(stopId, tripIndex) {
|
|
17757
|
-
const
|
|
17758
|
-
const
|
|
17759
|
-
|
|
17757
|
+
const globalIndex = tripIndex * this.stops.length + this.stopIndex(stopId);
|
|
17758
|
+
const byteIndex = Math.floor(globalIndex / 2);
|
|
17759
|
+
const isSecondPair = globalIndex % 2 === 1;
|
|
17760
|
+
const byte = this.pickUpDropOffTypes[byteIndex];
|
|
17761
|
+
if (byte === undefined) {
|
|
17760
17762
|
throw new Error(`Pick up type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17761
17763
|
}
|
|
17764
|
+
const pickUpValue = isSecondPair
|
|
17765
|
+
? (byte >> 6) & 0x03 // Upper 2 bits for second pair
|
|
17766
|
+
: (byte >> 2) & 0x03; // Bits 2-3 for first pair
|
|
17762
17767
|
return toPickupDropOffType(pickUpValue);
|
|
17763
17768
|
}
|
|
17764
17769
|
/**
|
|
@@ -17769,11 +17774,16 @@ let Route$1 = class Route {
|
|
|
17769
17774
|
* @returns The drop-off type at the specified stop and trip.
|
|
17770
17775
|
*/
|
|
17771
17776
|
dropOffTypeAt(stopId, tripIndex) {
|
|
17772
|
-
const
|
|
17773
|
-
const
|
|
17774
|
-
|
|
17777
|
+
const globalIndex = tripIndex * this.stops.length + this.stopIndex(stopId);
|
|
17778
|
+
const byteIndex = Math.floor(globalIndex / 2);
|
|
17779
|
+
const isSecondPair = globalIndex % 2 === 1;
|
|
17780
|
+
const byte = this.pickUpDropOffTypes[byteIndex];
|
|
17781
|
+
if (byte === undefined) {
|
|
17775
17782
|
throw new Error(`Drop off type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17776
17783
|
}
|
|
17784
|
+
const dropOffValue = isSecondPair
|
|
17785
|
+
? (byte >> 4) & 0x03 // Bits 4-5 for second pair
|
|
17786
|
+
: byte & 0x03; // Lower 2 bits for first pair
|
|
17777
17787
|
return toPickupDropOffType(dropOffValue);
|
|
17778
17788
|
}
|
|
17779
17789
|
/**
|
|
@@ -18075,7 +18085,7 @@ const ALL_TRANSPORT_MODES = new Set([
|
|
|
18075
18085
|
'TROLLEYBUS',
|
|
18076
18086
|
'MONORAIL',
|
|
18077
18087
|
]);
|
|
18078
|
-
const CURRENT_VERSION = '0.0.
|
|
18088
|
+
const CURRENT_VERSION = '0.0.4';
|
|
18079
18089
|
/**
|
|
18080
18090
|
* The internal transit timetable format.
|
|
18081
18091
|
*/
|
|
@@ -20506,6 +20516,86 @@ const parseGtfsTransferType = (gtfsTransferType) => {
|
|
|
20506
20516
|
}
|
|
20507
20517
|
};
|
|
20508
20518
|
|
|
20519
|
+
/**
|
|
20520
|
+
* Encodes pickup/drop-off types into a Uint8Array using 2 bits per value.
|
|
20521
|
+
* Layout per byte: [drop_off_1][pickup_1][drop_off_0][pickup_0] for stops 0 and 1
|
|
20522
|
+
*/
|
|
20523
|
+
const encodePickUpDropOffTypes = (pickUpTypes, dropOffTypes) => {
|
|
20524
|
+
const stopsCount = pickUpTypes.length;
|
|
20525
|
+
// Each byte stores 2 pickup/drop-off pairs (4 bits each)
|
|
20526
|
+
const arraySize = Math.ceil(stopsCount / 2);
|
|
20527
|
+
const encoded = new Uint8Array(arraySize);
|
|
20528
|
+
for (let i = 0; i < stopsCount; i++) {
|
|
20529
|
+
const byteIndex = Math.floor(i / 2);
|
|
20530
|
+
const isSecondPair = i % 2 === 1;
|
|
20531
|
+
const dropOffType = dropOffTypes[i];
|
|
20532
|
+
const pickUpType = pickUpTypes[i];
|
|
20533
|
+
if (dropOffType !== undefined &&
|
|
20534
|
+
pickUpType !== undefined &&
|
|
20535
|
+
byteIndex < encoded.length) {
|
|
20536
|
+
if (isSecondPair) {
|
|
20537
|
+
// Second pair: upper 4 bits
|
|
20538
|
+
const currentByte = encoded[byteIndex];
|
|
20539
|
+
if (currentByte !== undefined) {
|
|
20540
|
+
encoded[byteIndex] =
|
|
20541
|
+
currentByte | (dropOffType << 4) | (pickUpType << 6);
|
|
20542
|
+
}
|
|
20543
|
+
}
|
|
20544
|
+
else {
|
|
20545
|
+
// First pair: lower 4 bits
|
|
20546
|
+
const currentByte = encoded[byteIndex];
|
|
20547
|
+
if (currentByte !== undefined) {
|
|
20548
|
+
encoded[byteIndex] = currentByte | dropOffType | (pickUpType << 2);
|
|
20549
|
+
}
|
|
20550
|
+
}
|
|
20551
|
+
}
|
|
20552
|
+
}
|
|
20553
|
+
return encoded;
|
|
20554
|
+
};
|
|
20555
|
+
/**
|
|
20556
|
+
* Sorts trips by departure time and creates optimized typed arrays
|
|
20557
|
+
*/
|
|
20558
|
+
const finalizeRouteFromBuilder = (builder) => {
|
|
20559
|
+
builder.trips.sort((a, b) => a.firstDeparture - b.firstDeparture);
|
|
20560
|
+
const stopsCount = builder.stops.length;
|
|
20561
|
+
const tripsCount = builder.trips.length;
|
|
20562
|
+
const stopsArray = new Uint32Array(builder.stops);
|
|
20563
|
+
const stopTimesArray = new Uint16Array(stopsCount * tripsCount * 2);
|
|
20564
|
+
const allPickUpTypes = [];
|
|
20565
|
+
const allDropOffTypes = [];
|
|
20566
|
+
for (let tripIndex = 0; tripIndex < tripsCount; tripIndex++) {
|
|
20567
|
+
const trip = builder.trips[tripIndex];
|
|
20568
|
+
if (!trip) {
|
|
20569
|
+
throw new Error(`Missing trip data at index ${tripIndex}`);
|
|
20570
|
+
}
|
|
20571
|
+
const baseIndex = tripIndex * stopsCount * 2;
|
|
20572
|
+
for (let stopIndex = 0; stopIndex < stopsCount; stopIndex++) {
|
|
20573
|
+
const timeIndex = baseIndex + stopIndex * 2;
|
|
20574
|
+
const arrivalTime = trip.arrivalTimes[stopIndex];
|
|
20575
|
+
const departureTime = trip.departureTimes[stopIndex];
|
|
20576
|
+
const pickUpType = trip.pickUpTypes[stopIndex];
|
|
20577
|
+
const dropOffType = trip.dropOffTypes[stopIndex];
|
|
20578
|
+
if (arrivalTime === undefined ||
|
|
20579
|
+
departureTime === undefined ||
|
|
20580
|
+
pickUpType === undefined ||
|
|
20581
|
+
dropOffType === undefined) {
|
|
20582
|
+
throw new Error(`Missing trip data for trip ${tripIndex} at stop ${stopIndex}`);
|
|
20583
|
+
}
|
|
20584
|
+
stopTimesArray[timeIndex] = arrivalTime;
|
|
20585
|
+
stopTimesArray[timeIndex + 1] = departureTime;
|
|
20586
|
+
allDropOffTypes.push(dropOffType);
|
|
20587
|
+
allPickUpTypes.push(pickUpType);
|
|
20588
|
+
}
|
|
20589
|
+
}
|
|
20590
|
+
// Use 2-bit encoding for pickup/drop-off types
|
|
20591
|
+
const pickUpDropOffTypesArray = encodePickUpDropOffTypes(allPickUpTypes, allDropOffTypes);
|
|
20592
|
+
return {
|
|
20593
|
+
serviceRouteId: builder.serviceRouteId,
|
|
20594
|
+
stops: stopsArray,
|
|
20595
|
+
stopTimes: stopTimesArray,
|
|
20596
|
+
pickUpDropOffTypes: pickUpDropOffTypesArray,
|
|
20597
|
+
};
|
|
20598
|
+
};
|
|
20509
20599
|
/**
|
|
20510
20600
|
* Parses the trips.txt file from a GTFS feed
|
|
20511
20601
|
*
|
|
@@ -20583,11 +20673,11 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
|
|
|
20583
20673
|
var _a, e_2, _b, _c;
|
|
20584
20674
|
var _d, _e;
|
|
20585
20675
|
/**
|
|
20586
|
-
*
|
|
20676
|
+
* Adds a trip to the appropriate route builder
|
|
20587
20677
|
*/
|
|
20588
20678
|
const addTrip = (currentTripId) => {
|
|
20589
20679
|
const gtfsRouteId = validTripIds.get(currentTripId);
|
|
20590
|
-
if (!gtfsRouteId) {
|
|
20680
|
+
if (!gtfsRouteId || stops.length === 0) {
|
|
20591
20681
|
stops = [];
|
|
20592
20682
|
arrivalTimes = [];
|
|
20593
20683
|
departureTimes = [];
|
|
@@ -20596,83 +20686,42 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
|
|
|
20596
20686
|
return;
|
|
20597
20687
|
}
|
|
20598
20688
|
const routeId = `${gtfsRouteId}_${hashIds(stops)}`;
|
|
20599
|
-
|
|
20600
|
-
if (
|
|
20601
|
-
|
|
20602
|
-
|
|
20603
|
-
|
|
20604
|
-
|
|
20605
|
-
|
|
20606
|
-
|
|
20607
|
-
|
|
20608
|
-
|
|
20609
|
-
|
|
20610
|
-
|
|
20611
|
-
|
|
20612
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20613
|
-
pickUpDropOffTypesArray[i * 2] = pickUpTypes[i];
|
|
20614
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20615
|
-
pickUpDropOffTypesArray[i * 2 + 1] = dropOffTypes[i];
|
|
20616
|
-
}
|
|
20617
|
-
route = {
|
|
20689
|
+
const firstDeparture = departureTimes[0];
|
|
20690
|
+
if (firstDeparture === undefined) {
|
|
20691
|
+
console.warn(`Empty trip ${currentTripId}`);
|
|
20692
|
+
stops = [];
|
|
20693
|
+
arrivalTimes = [];
|
|
20694
|
+
departureTimes = [];
|
|
20695
|
+
pickUpTypes = [];
|
|
20696
|
+
dropOffTypes = [];
|
|
20697
|
+
return;
|
|
20698
|
+
}
|
|
20699
|
+
let routeBuilder = routeBuilders.get(routeId);
|
|
20700
|
+
if (!routeBuilder) {
|
|
20701
|
+
routeBuilder = {
|
|
20618
20702
|
serviceRouteId: gtfsRouteId,
|
|
20619
|
-
stops:
|
|
20620
|
-
|
|
20621
|
-
pickUpDropOffTypes: pickUpDropOffTypesArray,
|
|
20703
|
+
stops: [...stops],
|
|
20704
|
+
trips: [],
|
|
20622
20705
|
};
|
|
20623
|
-
|
|
20706
|
+
routeBuilders.set(routeId, routeBuilder);
|
|
20624
20707
|
for (const stop of stops) {
|
|
20625
20708
|
validStopIds.add(stop);
|
|
20626
20709
|
}
|
|
20627
20710
|
}
|
|
20628
|
-
|
|
20629
|
-
|
|
20630
|
-
|
|
20631
|
-
|
|
20632
|
-
|
|
20633
|
-
|
|
20634
|
-
|
|
20635
|
-
let insertPosition = 0;
|
|
20636
|
-
const existingTripsCount = route.stopTimes.length / (stopsCount * 2);
|
|
20637
|
-
for (let tripIndex = 0; tripIndex < existingTripsCount; tripIndex++) {
|
|
20638
|
-
const currentDeparture = route.stopTimes[tripIndex * stopsCount * 2 + 1];
|
|
20639
|
-
if (currentDeparture && tripFirstStopDeparture > currentDeparture) {
|
|
20640
|
-
insertPosition = (tripIndex + 1) * stopsCount;
|
|
20641
|
-
}
|
|
20642
|
-
else {
|
|
20643
|
-
break;
|
|
20644
|
-
}
|
|
20645
|
-
}
|
|
20646
|
-
// insert data for the new trip at the right place
|
|
20647
|
-
const newStopTimesLength = route.stopTimes.length + stopsCount * 2;
|
|
20648
|
-
const newStopTimes = new Uint16Array(newStopTimesLength);
|
|
20649
|
-
const newPickUpDropOffTypes = new Uint8Array(newStopTimesLength);
|
|
20650
|
-
newStopTimes.set(route.stopTimes.slice(0, insertPosition * 2), 0);
|
|
20651
|
-
newPickUpDropOffTypes.set(route.pickUpDropOffTypes.slice(0, insertPosition * 2), 0);
|
|
20652
|
-
for (let i = 0; i < stopsCount; i++) {
|
|
20653
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20654
|
-
newStopTimes[(insertPosition + i) * 2] = arrivalTimes[i];
|
|
20655
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20656
|
-
newStopTimes[(insertPosition + i) * 2 + 1] = departureTimes[i];
|
|
20657
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20658
|
-
newPickUpDropOffTypes[(insertPosition + i) * 2] = pickUpTypes[i];
|
|
20659
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20660
|
-
newPickUpDropOffTypes[(insertPosition + i) * 2 + 1] = dropOffTypes[i];
|
|
20661
|
-
}
|
|
20662
|
-
const afterInsertionSlice = route.stopTimes.slice(insertPosition * 2);
|
|
20663
|
-
newStopTimes.set(afterInsertionSlice, (insertPosition + stopsCount) * 2);
|
|
20664
|
-
const afterInsertionTypesSlice = route.pickUpDropOffTypes.slice(insertPosition * 2);
|
|
20665
|
-
newPickUpDropOffTypes.set(afterInsertionTypesSlice, (insertPosition + stopsCount) * 2);
|
|
20666
|
-
route.stopTimes = newStopTimes;
|
|
20667
|
-
route.pickUpDropOffTypes = newPickUpDropOffTypes;
|
|
20668
|
-
}
|
|
20711
|
+
routeBuilder.trips.push({
|
|
20712
|
+
firstDeparture,
|
|
20713
|
+
arrivalTimes: [...arrivalTimes],
|
|
20714
|
+
departureTimes: [...departureTimes],
|
|
20715
|
+
pickUpTypes: [...pickUpTypes],
|
|
20716
|
+
dropOffTypes: [...dropOffTypes],
|
|
20717
|
+
});
|
|
20669
20718
|
stops = [];
|
|
20670
20719
|
arrivalTimes = [];
|
|
20671
20720
|
departureTimes = [];
|
|
20672
20721
|
pickUpTypes = [];
|
|
20673
20722
|
dropOffTypes = [];
|
|
20674
20723
|
};
|
|
20675
|
-
const
|
|
20724
|
+
const routeBuilders = new Map();
|
|
20676
20725
|
let previousSeq = 0;
|
|
20677
20726
|
let stops = [];
|
|
20678
20727
|
let arrivalTimes = [];
|
|
@@ -20687,27 +20736,33 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
|
|
|
20687
20736
|
const rawLine = _c;
|
|
20688
20737
|
const line = rawLine;
|
|
20689
20738
|
if (line.trip_id === currentTripId && line.stop_sequence <= previousSeq) {
|
|
20690
|
-
console.warn(`Stop sequences not increasing for trip ${line.trip_id}.`);
|
|
20739
|
+
console.warn(`Stop sequences not increasing for trip ${line.trip_id}: ${line.stop_sequence} > ${previousSeq}.`);
|
|
20691
20740
|
continue;
|
|
20692
20741
|
}
|
|
20693
20742
|
if (!line.arrival_time && !line.departure_time) {
|
|
20694
20743
|
console.warn(`Missing arrival or departure time for ${line.trip_id} at stop ${line.stop_id}.`);
|
|
20695
20744
|
continue;
|
|
20696
20745
|
}
|
|
20697
|
-
if (line.pickup_type === 1 && line.drop_off_type === 1) {
|
|
20746
|
+
if (line.pickup_type === '1' && line.drop_off_type === '1') {
|
|
20698
20747
|
continue;
|
|
20699
20748
|
}
|
|
20700
20749
|
if (currentTripId && line.trip_id !== currentTripId && stops.length > 0) {
|
|
20701
20750
|
addTrip(currentTripId);
|
|
20702
20751
|
}
|
|
20703
20752
|
currentTripId = line.trip_id;
|
|
20704
|
-
|
|
20705
|
-
|
|
20753
|
+
const stopData = stopsMap.get(line.stop_id);
|
|
20754
|
+
if (!stopData) {
|
|
20755
|
+
console.warn(`Unknown stop ID: ${line.stop_id}`);
|
|
20756
|
+
continue;
|
|
20757
|
+
}
|
|
20758
|
+
stops.push(stopData.id);
|
|
20706
20759
|
const departure = (_d = line.departure_time) !== null && _d !== void 0 ? _d : line.arrival_time;
|
|
20707
20760
|
const arrival = (_e = line.arrival_time) !== null && _e !== void 0 ? _e : line.departure_time;
|
|
20708
|
-
|
|
20761
|
+
if (!arrival || !departure) {
|
|
20762
|
+
console.warn(`Missing time data for ${line.trip_id} at stop ${line.stop_id}`);
|
|
20763
|
+
continue;
|
|
20764
|
+
}
|
|
20709
20765
|
arrivalTimes.push(toTime(arrival).toMinutes());
|
|
20710
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20711
20766
|
departureTimes.push(toTime(departure).toMinutes());
|
|
20712
20767
|
pickUpTypes.push(parsePickupDropOffType(line.pickup_type));
|
|
20713
20768
|
dropOffTypes.push(parsePickupDropOffType(line.drop_off_type));
|
|
@@ -20725,7 +20780,8 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
|
|
|
20725
20780
|
addTrip(currentTripId);
|
|
20726
20781
|
}
|
|
20727
20782
|
const routesAdjacency = new Map();
|
|
20728
|
-
for (const [routeId,
|
|
20783
|
+
for (const [routeId, routeBuilder] of routeBuilders) {
|
|
20784
|
+
const routeData = finalizeRouteFromBuilder(routeBuilder);
|
|
20729
20785
|
routesAdjacency.set(routeId, new Route$1(routeData.stopTimes, routeData.pickUpDropOffTypes, routeData.stops, routeData.serviceRouteId));
|
|
20730
20786
|
}
|
|
20731
20787
|
return routesAdjacency;
|
|
@@ -20734,13 +20790,13 @@ const parsePickupDropOffType = (gtfsType) => {
|
|
|
20734
20790
|
switch (gtfsType) {
|
|
20735
20791
|
default:
|
|
20736
20792
|
return REGULAR;
|
|
20737
|
-
case 0:
|
|
20793
|
+
case '0':
|
|
20738
20794
|
return REGULAR;
|
|
20739
|
-
case 1:
|
|
20795
|
+
case '1':
|
|
20740
20796
|
return NOT_AVAILABLE;
|
|
20741
|
-
case 2:
|
|
20797
|
+
case '2':
|
|
20742
20798
|
return MUST_PHONE_AGENCY;
|
|
20743
|
-
case 3:
|
|
20799
|
+
case '3':
|
|
20744
20800
|
return MUST_COORDINATE_WITH_DRIVER;
|
|
20745
20801
|
}
|
|
20746
20802
|
};
|
|
@@ -20768,54 +20824,74 @@ class GtfsParser {
|
|
|
20768
20824
|
*/
|
|
20769
20825
|
parse(date) {
|
|
20770
20826
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20827
|
+
log.setLevel('INFO');
|
|
20771
20828
|
const zip = new StreamZip.async({ file: this.path });
|
|
20772
20829
|
const entries = yield zip.entries();
|
|
20773
20830
|
const datetime = DateTime.fromJSDate(date);
|
|
20774
20831
|
const validServiceIds = new Set();
|
|
20775
20832
|
const validStopIds = new Set();
|
|
20776
20833
|
log.info(`Parsing ${STOPS_FILE}`);
|
|
20834
|
+
const stopsStart = performance.now();
|
|
20777
20835
|
const stopsStream = yield zip.stream(STOPS_FILE);
|
|
20778
20836
|
const parsedStops = yield parseStops(stopsStream, this.profile.platformParser);
|
|
20779
|
-
|
|
20837
|
+
const stopsEnd = performance.now();
|
|
20838
|
+
log.info(`${parsedStops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
|
|
20780
20839
|
if (entries[CALENDAR_FILE]) {
|
|
20781
20840
|
log.info(`Parsing ${CALENDAR_FILE}`);
|
|
20841
|
+
const calendarStart = performance.now();
|
|
20782
20842
|
const calendarStream = yield zip.stream(CALENDAR_FILE);
|
|
20783
20843
|
yield parseCalendar(calendarStream, validServiceIds, datetime);
|
|
20784
|
-
|
|
20844
|
+
const calendarEnd = performance.now();
|
|
20845
|
+
log.info(`${validServiceIds.size} valid services. (${(calendarEnd - calendarStart).toFixed(2)}ms)`);
|
|
20785
20846
|
}
|
|
20786
20847
|
if (entries[CALENDAR_DATES_FILE]) {
|
|
20787
20848
|
log.info(`Parsing ${CALENDAR_DATES_FILE}`);
|
|
20849
|
+
const calendarDatesStart = performance.now();
|
|
20788
20850
|
const calendarDatesStream = yield zip.stream(CALENDAR_DATES_FILE);
|
|
20789
20851
|
yield parseCalendarDates(calendarDatesStream, validServiceIds, datetime);
|
|
20790
|
-
|
|
20852
|
+
const calendarDatesEnd = performance.now();
|
|
20853
|
+
log.info(`${validServiceIds.size} valid services. (${(calendarDatesEnd - calendarDatesStart).toFixed(2)}ms)`);
|
|
20791
20854
|
}
|
|
20792
20855
|
log.info(`Parsing ${ROUTES_FILE}`);
|
|
20856
|
+
const routesStart = performance.now();
|
|
20793
20857
|
const routesStream = yield zip.stream(ROUTES_FILE);
|
|
20794
20858
|
const validGtfsRoutes = yield parseRoutes(routesStream, this.profile);
|
|
20795
|
-
|
|
20859
|
+
const routesEnd = performance.now();
|
|
20860
|
+
log.info(`${validGtfsRoutes.size} valid GTFS routes. (${(routesEnd - routesStart).toFixed(2)}ms)`);
|
|
20796
20861
|
log.info(`Parsing ${TRIPS_FILE}`);
|
|
20862
|
+
const tripsStart = performance.now();
|
|
20797
20863
|
const tripsStream = yield zip.stream(TRIPS_FILE);
|
|
20798
20864
|
const trips = yield parseTrips(tripsStream, validServiceIds, validGtfsRoutes);
|
|
20799
|
-
|
|
20865
|
+
const tripsEnd = performance.now();
|
|
20866
|
+
log.info(`${trips.size} valid trips. (${(tripsEnd - tripsStart).toFixed(2)}ms)`);
|
|
20800
20867
|
let transfers = new Map();
|
|
20801
20868
|
if (entries[TRANSFERS_FILE]) {
|
|
20802
20869
|
log.info(`Parsing ${TRANSFERS_FILE}`);
|
|
20870
|
+
const transfersStart = performance.now();
|
|
20803
20871
|
const transfersStream = yield zip.stream(TRANSFERS_FILE);
|
|
20804
20872
|
transfers = yield parseTransfers(transfersStream, parsedStops);
|
|
20805
|
-
|
|
20873
|
+
const transfersEnd = performance.now();
|
|
20874
|
+
log.info(`${transfers.size} valid transfers. (${(transfersEnd - transfersStart).toFixed(2)}ms)`);
|
|
20806
20875
|
}
|
|
20807
20876
|
log.info(`Parsing ${STOP_TIMES_FILE}`);
|
|
20877
|
+
const stopTimesStart = performance.now();
|
|
20808
20878
|
const stopTimesStream = yield zip.stream(STOP_TIMES_FILE);
|
|
20809
20879
|
const routesAdjacency = yield parseStopTimes(stopTimesStream, parsedStops, trips, validStopIds);
|
|
20810
20880
|
const stopsAdjacency = buildStopsAdjacencyStructure(validStopIds, routesAdjacency, transfers);
|
|
20811
|
-
|
|
20881
|
+
const stopTimesEnd = performance.now();
|
|
20882
|
+
log.info(`${routesAdjacency.size} valid unique routes. (${(stopTimesEnd - stopTimesStart).toFixed(2)}ms)`);
|
|
20812
20883
|
log.info(`Removing unused stops.`);
|
|
20884
|
+
const indexStopsStart = performance.now();
|
|
20813
20885
|
const stops = indexStops(parsedStops, validStopIds);
|
|
20814
|
-
|
|
20886
|
+
const indexStopsEnd = performance.now();
|
|
20887
|
+
log.info(`${stops.size} used stop stops, ${parsedStops.size - stops.size} unused. (${(indexStopsEnd - indexStopsStart).toFixed(2)}ms)`);
|
|
20815
20888
|
yield zip.close();
|
|
20816
20889
|
const timetable = new Timetable(stopsAdjacency, routesAdjacency, validGtfsRoutes);
|
|
20817
20890
|
log.info(`Building stops index.`);
|
|
20891
|
+
const stopsIndexStart = performance.now();
|
|
20818
20892
|
const stopsIndex = new StopsIndex(stops);
|
|
20893
|
+
const stopsIndexEnd = performance.now();
|
|
20894
|
+
log.info(`Stops index built. (${(stopsIndexEnd - stopsIndexStart).toFixed(2)}ms)`);
|
|
20819
20895
|
log.info('Parsing complete.');
|
|
20820
20896
|
return { timetable, stopsIndex };
|
|
20821
20897
|
});
|
|
@@ -20831,9 +20907,11 @@ class GtfsParser {
|
|
|
20831
20907
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20832
20908
|
const zip = new StreamZip.async({ file: this.path });
|
|
20833
20909
|
log.info(`Parsing ${STOPS_FILE}`);
|
|
20910
|
+
const stopsStart = performance.now();
|
|
20834
20911
|
const stopsStream = yield zip.stream(STOPS_FILE);
|
|
20835
20912
|
const stops = indexStops(yield parseStops(stopsStream, this.profile.platformParser));
|
|
20836
|
-
|
|
20913
|
+
const stopsEnd = performance.now();
|
|
20914
|
+
log.info(`${stops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
|
|
20837
20915
|
yield zip.close();
|
|
20838
20916
|
return new StopsIndex(stops);
|
|
20839
20917
|
});
|
|
@@ -21456,9 +21534,9 @@ const testRouterPerformance = (router, stopsIndex, tasks, iterations) => {
|
|
|
21456
21534
|
global.gc();
|
|
21457
21535
|
}
|
|
21458
21536
|
const startMemory = process.memoryUsage().heapUsed;
|
|
21459
|
-
const startTime = performance.now();
|
|
21537
|
+
const startTime = performance$1.now();
|
|
21460
21538
|
router.route(task);
|
|
21461
|
-
const endTime = performance.now();
|
|
21539
|
+
const endTime = performance$1.now();
|
|
21462
21540
|
const endMemory = process.memoryUsage().heapUsed;
|
|
21463
21541
|
totalTime += endTime - startTime;
|
|
21464
21542
|
if (endMemory >= startMemory) {
|
|
@@ -21610,7 +21688,6 @@ const startRepl = (stopsPath, timetablePath) => {
|
|
|
21610
21688
|
if (bestRoute) {
|
|
21611
21689
|
console.log(`Found route from ${fromStop.name} to ${toStop.name}:`);
|
|
21612
21690
|
console.log(bestRoute.toString());
|
|
21613
|
-
console.log(JSON.stringify(bestRoute.asJson(), null, 2));
|
|
21614
21691
|
}
|
|
21615
21692
|
else {
|
|
21616
21693
|
console.log('No route found');
|