Skip to content

ETA Estimation

Estimates arrival times for buses and walking legs using fixed speed constants and Haversine distance. Applied across the passenger app's route detail and trip suggestion features.


Where It Is Used

File Application
apps/api-server/src/trips/trips.service.ts Bus ETA in active trip suggestions
apps/passengers-app/src/app/pages/route-detail/route-detail.page.ts Walking leg and bus ride time display
apps/passengers-app/src/app/pages/gtfs-route-detail/gtfs-route-detail.page.ts GTFS bus ride ETA

Speed Constants

Mode Speed Rationale
Bus (in service) 200 m/min (12 km/h) Typical Bolivian urban congestion: frequent stops, narrow streets, traffic
Bus (road distance model) 333 m/min (20 km/h) Used when computing ride time from road distance instead of straight-line
Pedestrian 80 m/min (4.8 km/h) Average walking pace including crossings and kerbs
Road factor 1.3× Haversine × 1.3 ≈ actual road distance in Bolivian urban grids

Bus ETA to Boarding Stop

Used in Active Trip Suggestions to rank trips by how soon the bus arrives.

const busDistToBoard = haversineMeters(busLat, busLng, boardStop.lat, boardStop.lng);
const etaMinutes = Math.ceil(busDistToBoard / 200);  // 200 m/min

Haversine is used here (not road distance) because the bus ETA only needs to be a relative ranking signal, not a precise prediction.


Walking Leg ETA

const walkToBoard  = haversineMeters(origin.lat, origin.lng, boardStop.lat, boardStop.lng);
const walkFromAlight = haversineMeters(alightStop.lat, alightStop.lng, dest.lat, dest.lng);

const walkToMinutes   = Math.ceil(walkToBoard   / 80);
const walkFromMinutes = Math.ceil(walkFromAlight / 80);

Walk distances are calculated on the trimmed legs after Walking Path Snapping has clipped the OSRM path. When the walking path is available, its actual length is used instead of Haversine.


Bus Ride Time

When along-route distance is available (from Along-Route Distance Accumulation):

const rideMinutes = Math.max(5, Math.ceil(alongRouteMeters / 333));
// minimum 5 minutes — avoids displaying 0 min for very short segments

When only Haversine straight-line distance is available:

const roadEstimate = haversineMeters(boardStop, alightStop) * 1.3;  // road factor
const rideMinutes  = Math.ceil(roadEstimate / 200);

Total Journey Time

total = walkToMinutes + busEtaMinutes + rideMinutes + walkFromMinutes

For transfer journeys:

total = walkToMinutes + busEtaA + rideA + walkTransfer + busEtaB + rideB + walkFromMinutes

Walk transfer distance uses the 350 m transfer radius from Active Trip Suggestions, estimated at 350 / 80 ≈ 5 min.


Design Notes

  • All ETAs use ceiling (Math.ceil) rather than rounding — it is better to slightly overestimate and have the passenger arrive early than the reverse.
  • Speed constants are tuned for Bolivian urban conditions and differ from European or North American defaults.
  • ETAs are presented as relative estimates, not timestamps, to avoid confusion when the bus is far away or delayed.