--- name: valhalla_isochrones_async kind: function lang: py domain: geo version: "1.0.0" purity: impure signature: "async def valhalla_isochrones_async(requests: list[dict], base_url: str = 'http://localhost:8002', costing: str = 'auto', concurrency: int = 6, timeout_s: float = 120.0, denoise: float = 0.6, generalize_m: int = 50) -> list[dict | None]" description: "Calcula isócronas para múltiples puntos en paralelo usando httpx.AsyncClient y asyncio.Semaphore. Order-preserving: la lista retornada es paralela a la de entrada." tags: [valhalla, isochrone, geo, http, async, asyncio, geojson, batch] uses_functions: [] uses_types: [] returns: [] returns_optional: true error_type: "error_go_core" imports: [httpx, asyncio] params: - name: requests desc: "Lista de dicts con 'lat' (float), 'lon' (float), 'minutes' (int) y opcionalmente 'id' (str). Un elemento por punto." - name: base_url desc: "URL base del servidor Valhalla. Por defecto http://localhost:8002." - name: costing desc: "Modelo de coste: 'auto', 'bicycle', 'pedestrian', etc." - name: concurrency desc: "Número máximo de requests HTTP simultáneas (Semaphore). Por defecto 6." - name: timeout_s desc: "Timeout en segundos por request individual." - name: denoise desc: "Factor de suavizado del contorno (0-1)." - name: generalize_m desc: "Tolerancia de generalización de la geometría en metros." output: "Lista paralela a 'requests' con GeoJSON dict (campo 'features') o None por cada punto. Preserva el orden de entrada. Nunca lanza excepcion — fallos individuales se mapean a None." tested: true tests: ["3 puntos Madrid retornan lista de 3"] test_file_path: "python/functions/geo/tests/test_valhalla_isochrones_async.py" file_path: "python/functions/geo/valhalla_isochrones_async.py" source_repo: "internal:footprint_aurgi" source_license: "internal-aurgi" source_file: "ponderacion_isochronas/src/generar_isochronas_aurgi.py" --- ## Ejemplo ```python import asyncio pts = [ {"lat": 40.4168, "lon": -3.7038, "minutes": 10, "id": "madrid_centro"}, {"lat": 40.4530, "lon": -3.6883, "minutes": 10, "id": "retiro"}, ] results = asyncio.run(valhalla_isochrones_async(pts)) for req, gj in zip(pts, results): status = "ok" if gj else "error" print(f"{req['id']}: {status}") ``` ## Notas Adaptada de `_run_isochrones` y `_fetch_isochrone` en `generar_isochronas_aurgi.py`. La versión original acoplaba pandas DataFrames y escritura a disco — esta versión es pandas-free y retorna datos en memoria. Usa asyncio.gather para preservar el orden de resultados.