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.
Related
- Active Trip Suggestions — primary consumer of bus ETA
- Along-Route Distance Accumulation — provides road distance for ride time
- Walking Path Snapping — provides actual walk path length
- Haversine Distance — underlying distance function