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 CHANGED
@@ -1,6 +1,11 @@
1
- ## [3.0.2](https://github.com/aubryio/minotor/compare/v3.0.1...v3.0.2) (2025-09-05)
1
+ # [4.0.0](https://github.com/aubryio/minotor/compare/v3.0.2...v4.0.0) (2025-09-13)
2
2
 
3
3
 
4
- ### Bug Fixes
4
+ ### Performance Improvements
5
5
 
6
- * make csv-parser cast sequence numbers as numbers ([#21](https://github.com/aubryio/minotor/issues/21)) ([8bf70c2](https://github.com/aubryio/minotor/commit/8bf70c26a4ccec6e32b849b608bd399fe53382db))
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 pickUpIndex = (tripIndex * this.stops.length + this.stopIndex(stopId)) * 2;
17758
- const pickUpValue = this.pickUpDropOffTypes[pickUpIndex];
17759
- if (pickUpValue === undefined) {
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 dropOffIndex = (tripIndex * this.stops.length + this.stopIndex(stopId)) * 2 + 1;
17773
- const dropOffValue = this.pickUpDropOffTypes[dropOffIndex];
17774
- if (dropOffValue === undefined) {
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.3';
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
- * Inserts a trip at the right place in the routes adjacency structure.
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
- let route = routes.get(routeId);
20600
- if (!route) {
20601
- const stopsCount = stops.length;
20602
- const stopsArray = new Uint32Array(stops);
20603
- const stopTimesArray = new Uint16Array(stopsCount * 2);
20604
- for (let i = 0; i < stopsCount; i++) {
20605
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20606
- stopTimesArray[i * 2] = arrivalTimes[i];
20607
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20608
- stopTimesArray[i * 2 + 1] = departureTimes[i];
20609
- }
20610
- const pickUpDropOffTypesArray = new Uint8Array(stopsCount * 2);
20611
- for (let i = 0; i < stopsCount; i++) {
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: stopsArray,
20620
- stopTimes: stopTimesArray,
20621
- pickUpDropOffTypes: pickUpDropOffTypesArray,
20703
+ stops: [...stops],
20704
+ trips: [],
20622
20705
  };
20623
- routes.set(routeId, route);
20706
+ routeBuilders.set(routeId, routeBuilder);
20624
20707
  for (const stop of stops) {
20625
20708
  validStopIds.add(stop);
20626
20709
  }
20627
20710
  }
20628
- else {
20629
- const tripFirstStopDeparture = departureTimes[0];
20630
- if (tripFirstStopDeparture === undefined) {
20631
- throw new Error(`Empty trip ${currentTripId}`);
20632
- }
20633
- // Find the correct position to insert the new trip
20634
- const stopsCount = stops.length;
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 routes = new Map();
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
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20705
- stops.push(stopsMap.get(line.stop_id).id);
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
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
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, routeData] of routes) {
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
- log.info(`${parsedStops.size} parsed stops.`);
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
- log.info(`${validServiceIds.size} valid services.`);
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
- log.info(`${validServiceIds.size} valid services.`);
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
- log.info(`${validGtfsRoutes.size} valid GTFS routes.`);
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
- log.info(`${trips.size} valid trips.`);
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
- log.info(`${transfers.size} valid transfers.`);
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
- log.info(`${routesAdjacency.size} valid unique routes.`);
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
- log.info(`${stops.size} used stop stops, ${parsedStops.size - stops.size} unused.`);
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
- log.info(`${stops.size} parsed stops.`);
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');