←

Haversine y stop detection sin dependencias externas

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.