Contexto
Para un sistema de fleet tracking necesitaba calcular distancias entre coordenadas GPS y detectar paradas en rutas. Librerias como geopy o shapely agregan dependencias pesadas para algo que se resuelve con trigonometria basica.
Lo que aprendi
Haversine calcula distancia entre dos puntos en la superficie de una esfera. Con math estandar:
import math
def haversine(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
"""Distancia en km entre dos coordenadas GPS."""
R = 6371.0 # Radio de la Tierra en km
dlat = math.radians(lat2 - lat1)
dlon = math.radians(lon2 - lon1)
a = (
math.sin(dlat / 2) ** 2
+ math.cos(math.radians(lat1))
* math.cos(math.radians(lat2))
* math.sin(dlon / 2) ** 2
)
return R * 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
Para detectar paradas, itero los puntos de una ruta buscando secuencias donde la velocidad es menor a 3 km/h por mas de 2 minutos:
def detect_stops(points: list[dict], speed_threshold=3.0, min_duration_min=2.0):
"""Detecta paradas: velocidad < 3 km/h por > 2 minutos."""
stops = []
stop_start = None
for point in points:
if point["speed"] < speed_threshold:
if stop_start is None:
stop_start = point
else:
if stop_start:
duration = (point["time"] - stop_start["time"]).total_seconds() / 60
if duration >= min_duration_min:
stops.append({
"lat": stop_start["lat"],
"lon": stop_start["lon"],
"start_time": stop_start["time"],
"end_time": point["time"],
"duration_min": round(duration, 1),
})
stop_start = None
return stops
El trip summary combina ambas funciones para calcular distancia total, tiempo en movimiento vs detenido, velocidad promedio (solo de segmentos en movimiento), y lista de paradas.
Por que importa
Zero dependencias, ~40 lineas, precision de 1-2% (suficiente para fleet tracking urbano). Se puede copiar tal cual a cualquier proyecto sin instalar nada.