Contexto
Un cliente quiere ver el resumen de la ruta de un vehiculo: cuantos km recorrio, cuanto tiempo estuvo en movimiento vs detenido, velocidad maxima, y donde se detuvo. Los proveedores GPS dan puntos crudos (lat, lon, speed, time) pero no calculan resumenes.
Lo que aprendi
El trip summary itera los puntos de la ruta y clasifica cada segmento entre dos puntos consecutivos como "movimiento" o "parada":
def trip_summary(points: list[dict]) -> dict:
"""Resumen de viaje a partir de puntos GPS crudos."""
if len(points) < 2:
return {"total_distance_km": 0, "stops": []}
total_distance = 0.0
moving_time = 0.0
stopped_time = 0.0
max_speed = 0.0
stops = []
stop_start_idx = None
for i in range(1, len(points)):
prev, curr = points[i - 1], points[i]
dt = (curr["time"] - prev["time"]).total_seconds()
if curr["speed"] >= 3.0: # En movimiento
total_distance += haversine(
prev["lat"], prev["lon"],
curr["lat"], curr["lon"]
)
moving_time += dt
max_speed = max(max_speed, curr["speed"])
# Si estaba en parada, cerrarla
if stop_start_idx is not None:
stop_duration = (prev["time"] - points[stop_start_idx]["time"]).total_seconds() / 60
if stop_duration >= 2.0: # Solo paradas > 2 min
stops.append({
"lat": points[stop_start_idx]["lat"],
"lon": points[stop_start_idx]["lon"],
"start_time": points[stop_start_idx]["time"],
"end_time": prev["time"],
"duration_min": round(stop_duration, 1),
})
stop_start_idx = None
else: # Detenido
stopped_time += dt
if stop_start_idx is None:
stop_start_idx = i
avg_speed = (total_distance / (moving_time / 3600)) if moving_time > 0 else 0
return {
"total_distance_km": round(total_distance, 2),
"total_time_min": round((moving_time + stopped_time) / 60, 1),
"moving_time_min": round(moving_time / 60, 1),
"stopped_time_min": round(stopped_time / 60, 1),
"max_speed": round(max_speed, 1),
"avg_speed": round(avg_speed, 1),
"stop_count": len(stops),
"stops": stops,
}
Detalles importantes:
- Velocidad promedio se calcula solo de segmentos en movimiento (excluye paradas)
- Paradas menores a 2 minutos se ignoran (semaforos, trafico)
- Si el ultimo punto es una parada, hay que cerrarla al final del loop
- Puntos sin timestamp valido se saltan
Por que importa
Con ~50 lineas de puro Python y math, generas el mismo resumen que apps de fleet tracking comerciales cobran por vehiculo. La funcion es stateless y sin dependencias, se puede reusar en cualquier proyecto.