minotor 6.0.0 → 7.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/CHANGELOG.md CHANGED
@@ -1,11 +1,6 @@
1
- # [6.0.0](https://github.com/aubryio/minotor/compare/v5.0.1...v6.0.0) (2025-09-19)
1
+ ## [7.0.1](https://github.com/aubryio/minotor/compare/v7.0.0...v7.0.1) (2025-09-23)
2
2
 
3
3
 
4
- ### Performance Improvements
4
+ ### Bug Fixes
5
5
 
6
- * use arrays instead of maps for stops adjacency and service routes ([#25](https://github.com/aubryio/minotor/issues/25)) ([c803b4d](https://github.com/aubryio/minotor/commit/c803b4d60d7a421b889938d88fc6f316ae43de2f))
7
-
8
-
9
- ### BREAKING CHANGES
10
-
11
- * Stops and timetable binary format was updated and is not compatible with the old ones.
6
+ * address corner cases with boarding conditions ([#27](https://github.com/aubryio/minotor/issues/27)) ([cd5fe68](https://github.com/aubryio/minotor/commit/cd5fe682ca359872a6bac46dbdc30ec9d603c8ef))
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
- stops.forEach((stop, id) => {
16095
- var _a;
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 (const { lat, lon } of this.stopPoints) {
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.int32(v);
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.int32());
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.int32());
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.int32(v);
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.int32());
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.int32());
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
@@ -17336,29 +17314,35 @@ let Route$1 = class Route {
17336
17314
  * @returns The index of the earliest trip meeting the criteria, or undefined if no such trip is found.
17337
17315
  */
17338
17316
  findEarliestTrip(stopId, after = Time.origin(), beforeTrip) {
17339
- const maxTripIndex = beforeTrip !== undefined
17340
- ? Math.min(beforeTrip - 1, this.nbTrips - 1)
17341
- : this.nbTrips - 1;
17342
- if (maxTripIndex < 0) {
17317
+ if (this.nbTrips <= 0)
17343
17318
  return undefined;
17344
- }
17345
- let earliestTripIndex;
17346
- let lowTrip = 0;
17347
- let highTrip = maxTripIndex;
17348
- while (lowTrip <= highTrip) {
17349
- const midTrip = Math.floor((lowTrip + highTrip) / 2);
17350
- const departure = this.departureFrom(stopId, midTrip);
17351
- const pickUpType = this.pickUpTypeFrom(stopId, midTrip);
17352
- if ((departure.isAfter(after) || departure.equals(after)) &&
17353
- pickUpType !== 'NOT_AVAILABLE') {
17354
- earliestTripIndex = midTrip;
17355
- highTrip = midTrip - 1;
17319
+ let hi = this.nbTrips - 1;
17320
+ if (beforeTrip !== undefined)
17321
+ hi = Math.min(hi, beforeTrip - 1);
17322
+ if (hi < 0)
17323
+ return undefined;
17324
+ let lo = 0;
17325
+ let lb = -1;
17326
+ while (lo <= hi) {
17327
+ const mid = (lo + hi) >>> 1;
17328
+ const depMid = this.departureFrom(stopId, mid);
17329
+ if (depMid.isBefore(after)) {
17330
+ lo = mid + 1;
17356
17331
  }
17357
17332
  else {
17358
- lowTrip = midTrip + 1;
17333
+ lb = mid;
17334
+ hi = mid - 1;
17359
17335
  }
17360
17336
  }
17361
- return earliestTripIndex;
17337
+ if (lb === -1)
17338
+ return undefined;
17339
+ for (let t = lb; t < (beforeTrip !== null && beforeTrip !== void 0 ? beforeTrip : this.nbTrips); t++) {
17340
+ const pickup = this.pickUpTypeFrom(stopId, t);
17341
+ if (pickup !== 'NOT_AVAILABLE') {
17342
+ return t;
17343
+ }
17344
+ }
17345
+ return undefined;
17362
17346
  }
17363
17347
  /**
17364
17348
  * Retrieves the index of a stop within the route.
@@ -17472,31 +17456,48 @@ const serializeServiceRoutesMap = (serviceRoutes) => {
17472
17456
  });
17473
17457
  };
17474
17458
  const deserializeStopsAdjacency = (protoStopsAdjacency) => {
17475
- return protoStopsAdjacency.map((value) => {
17476
- return {
17477
- transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: parseTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
17459
+ const result = [];
17460
+ for (let i = 0; i < protoStopsAdjacency.length; i++) {
17461
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
17462
+ const value = protoStopsAdjacency[i];
17463
+ const transfers = [];
17464
+ for (let j = 0; j < value.transfers.length; j++) {
17465
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
17466
+ const transfer = value.transfers[j];
17467
+ const newTransfer = Object.assign({ destination: transfer.destination, type: parseTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
17478
17468
  minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
17479
- })))),
17469
+ }));
17470
+ transfers.push(newTransfer);
17471
+ }
17472
+ result.push({
17473
+ transfers: transfers,
17480
17474
  routes: value.routes,
17481
- };
17482
- });
17475
+ });
17476
+ }
17477
+ return result;
17483
17478
  };
17484
17479
  const deserializeRoutesAdjacency = (protoRoutesAdjacency) => {
17485
17480
  const routesAdjacency = [];
17486
- protoRoutesAdjacency.forEach((value) => {
17481
+ for (let i = 0; i < protoRoutesAdjacency.length; i++) {
17482
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
17483
+ const value = protoRoutesAdjacency[i];
17487
17484
  const stops = bytesToUint32Array(value.stops);
17488
17485
  routesAdjacency.push(new Route$1(bytesToUint16Array(value.stopTimes), value.pickUpDropOffTypes, stops, value.serviceRouteId));
17489
- });
17486
+ }
17490
17487
  return routesAdjacency;
17491
17488
  };
17492
17489
  const deserializeServiceRoutesMap = (protoServiceRoutes) => {
17493
- return protoServiceRoutes.map((value) => {
17494
- return {
17490
+ const result = [];
17491
+ for (let i = 0; i < protoServiceRoutes.length; i++) {
17492
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
17493
+ const value = protoServiceRoutes[i];
17494
+ result.push({
17495
17495
  type: parseRouteType(value.type),
17496
17496
  name: value.name,
17497
17497
  routes: value.routes,
17498
- };
17499
- });
17498
+ });
17499
+ }
17500
+ return result;
17500
17501
  };
17501
17502
  const parseTransferType = (type) => {
17502
17503
  switch (type) {
@@ -17588,7 +17589,7 @@ const ALL_TRANSPORT_MODES = new Set([
17588
17589
  'TROLLEYBUS',
17589
17590
  'MONORAIL',
17590
17591
  ]);
17591
- const CURRENT_VERSION = '0.0.6';
17592
+ const CURRENT_VERSION = '0.0.7';
17592
17593
  /**
17593
17594
  * The internal transit timetable format.
17594
17595
  */
@@ -17696,7 +17697,8 @@ class Timetable {
17696
17697
  return [];
17697
17698
  }
17698
17699
  const routes = [];
17699
- for (const routeId of stopData.routes) {
17700
+ for (let i = 0; i < stopData.routes.length; i++) {
17701
+ const routeId = stopData.routes[i];
17700
17702
  const route = this.routesAdjacency[routeId];
17701
17703
  if (route) {
17702
17704
  routes.push(route);
@@ -17715,12 +17717,15 @@ class Timetable {
17715
17717
  */
17716
17718
  findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
17717
17719
  const reachableRoutes = new Map();
17718
- for (const originStop of fromStops) {
17720
+ const fromStopsArray = Array.from(fromStops);
17721
+ for (let i = 0; i < fromStopsArray.length; i++) {
17722
+ const originStop = fromStopsArray[i];
17719
17723
  const validRoutes = this.routesPassingThrough(originStop).filter((route) => {
17720
17724
  const serviceRoute = this.getServiceRouteInfo(route);
17721
17725
  return transportModes.has(serviceRoute.type);
17722
17726
  });
17723
- for (const route of validRoutes) {
17727
+ for (let j = 0; j < validRoutes.length; j++) {
17728
+ const route = validRoutes[j];
17724
17729
  const hopOnStop = reachableRoutes.get(route);
17725
17730
  if (hopOnStop) {
17726
17731
  if (route.isBefore(originStop, hopOnStop)) {
@@ -20135,8 +20140,12 @@ const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbSto
20135
20140
  for (let i = 0; i < nbStops; i++) {
20136
20141
  stopsAdjacency[i] = { routes: [], transfers: [] };
20137
20142
  }
20138
- routes.forEach((route, index) => {
20139
- for (const stop of route.stopsIterator()) {
20143
+ for (let index = 0; index < routes.length; index++) {
20144
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20145
+ const route = routes[index];
20146
+ for (let j = 0; j < route.getNbStops(); j++) {
20147
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20148
+ const stop = route.stops[j];
20140
20149
  if (activeStops.has(stop)) {
20141
20150
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20142
20151
  stopsAdjacency[stop].routes.push(index);
@@ -20147,9 +20156,11 @@ const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbSto
20147
20156
  throw new Error(`Service route ${route.serviceRoute()} not found for route ${index}.`);
20148
20157
  }
20149
20158
  serviceRoute.routes.push(index);
20150
- });
20159
+ }
20151
20160
  for (const [stop, transfers] of transfersMap) {
20152
- for (const transfer of transfers) {
20161
+ for (let i = 0; i < transfers.length; i++) {
20162
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20163
+ const transfer = transfers[i];
20153
20164
  if (activeStops.has(stop) || activeStops.has(transfer.destination)) {
20154
20165
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20155
20166
  stopsAdjacency[stop].transfers.push(transfer);
@@ -20796,7 +20807,7 @@ class Result {
20796
20807
 
20797
20808
  const UNREACHED = Time.infinity();
20798
20809
  /**
20799
- * A public transportation network router utilizing the RAPTOR algorithm for
20810
+ * A public transportation network router implementing the RAPTOR algorithm for
20800
20811
  * efficient journey planning and routing. For more information on the RAPTOR
20801
20812
  * algorithm, refer to its detailed explanation in the research paper:
20802
20813
  * https://www.microsoft.com/en-us/research/wp-content/uploads/2012/01/raptor_alenex.pdf
@@ -20815,7 +20826,9 @@ class Router {
20815
20826
  var _a, _b;
20816
20827
  const { options } = query;
20817
20828
  const newlyMarkedStops = new Set();
20818
- for (const stop of markedStops) {
20829
+ const markedStopsArray = Array.from(markedStops);
20830
+ for (let i = 0; i < markedStopsArray.length; i++) {
20831
+ const stop = markedStopsArray[i];
20819
20832
  const currentArrival = arrivalsAtCurrentRound.get(stop);
20820
20833
  if (!currentArrival)
20821
20834
  continue;
@@ -20824,7 +20837,9 @@ class Router {
20824
20837
  if (previousLeg && !('route' in previousLeg)) {
20825
20838
  continue;
20826
20839
  }
20827
- for (const transfer of this.timetable.getTransfers(stop)) {
20840
+ const transfers = this.timetable.getTransfers(stop);
20841
+ for (let j = 0; j < transfers.length; j++) {
20842
+ const transfer = transfers[j];
20828
20843
  let transferTime;
20829
20844
  if (transfer.minTransferTime) {
20830
20845
  transferTime = transfer.minTransferTime;
@@ -20859,7 +20874,9 @@ class Router {
20859
20874
  }
20860
20875
  }
20861
20876
  }
20862
- for (const newStop of newlyMarkedStops) {
20877
+ const newlyMarkedStopsArray = Array.from(newlyMarkedStops);
20878
+ for (let i = 0; i < newlyMarkedStopsArray.length; i++) {
20879
+ const newStop = newlyMarkedStopsArray[i];
20863
20880
  markedStops.add(newStop);
20864
20881
  }
20865
20882
  }
@@ -20873,7 +20890,8 @@ class Router {
20873
20890
  earliestArrivalAtAnyStop(earliestArrivals, destinations) {
20874
20891
  var _a, _b;
20875
20892
  let earliestArrivalAtAnyDestination = UNREACHED;
20876
- for (const destination of destinations) {
20893
+ for (let i = 0; i < destinations.length; i++) {
20894
+ const destination = destinations[i];
20877
20895
  const arrival = (_b = (_a = earliestArrivals.get(destination.id)) === null || _a === void 0 ? void 0 : _a.arrival) !== null && _b !== void 0 ? _b : UNREACHED;
20878
20896
  earliestArrivalAtAnyDestination = Time.min(earliestArrivalAtAnyDestination, arrival);
20879
20897
  }
@@ -20897,7 +20915,8 @@ class Router {
20897
20915
  const earliestArrivalsPerRound = [earliestArrivalsWithoutAnyLeg];
20898
20916
  // Stops that have been improved at round k-1
20899
20917
  const markedStops = new Set();
20900
- for (const originStop of origins) {
20918
+ for (let i = 0; i < origins.length; i++) {
20919
+ const originStop = origins[i];
20901
20920
  markedStops.add(originStop.id);
20902
20921
  earliestArrivals.set(originStop.id, {
20903
20922
  arrival: departureTime,
@@ -20921,9 +20940,13 @@ class Router {
20921
20940
  const reachableRoutes = this.timetable.findReachableRoutes(markedStops, options.transportModes);
20922
20941
  markedStops.clear();
20923
20942
  // for each route that can be reached with at least round - 1 trips
20924
- for (const [route, hopOnStop] of reachableRoutes.entries()) {
20943
+ const reachableRoutesArray = Array.from(reachableRoutes.entries());
20944
+ for (let i = 0; i < reachableRoutesArray.length; i++) {
20945
+ const [route, hopOnStop] = reachableRoutesArray[i];
20925
20946
  let currentTrip = undefined;
20926
- for (const currentStop of route.stopsIterator(hopOnStop)) {
20947
+ const startIndex = route.stopIndex(hopOnStop);
20948
+ for (let j = startIndex; j < route.getNbStops(); j++) {
20949
+ const currentStop = route.stops[j];
20927
20950
  // If we're currently on a trip,
20928
20951
  // check if arrival at the stop improves the earliest arrival time
20929
20952
  if (currentTrip !== undefined) {
@@ -20959,8 +20982,8 @@ class Router {
20959
20982
  const earliestArrivalOnPreviousRound = (_c = arrivalsAtPreviousRound.get(currentStop)) === null || _c === void 0 ? void 0 : _c.arrival;
20960
20983
  if (earliestArrivalOnPreviousRound !== undefined &&
20961
20984
  (currentTrip === undefined ||
20962
- earliestArrivalOnPreviousRound.isBefore(route.arrivalAt(currentStop, currentTrip.tripIndex)) ||
20963
- earliestArrivalOnPreviousRound.equals(route.arrivalAt(currentStop, currentTrip.tripIndex)))) {
20985
+ earliestArrivalOnPreviousRound.isBefore(route.departureFrom(currentStop, currentTrip.tripIndex)) ||
20986
+ earliestArrivalOnPreviousRound.equals(route.departureFrom(currentStop, currentTrip.tripIndex)))) {
20964
20987
  const earliestTrip = route.findEarliestTrip(currentStop, earliestArrivalOnPreviousRound, currentTrip === null || currentTrip === void 0 ? void 0 : currentTrip.tripIndex);
20965
20988
  if (earliestTrip !== undefined) {
20966
20989
  currentTrip = {