---
name: valhalla_matrix_1_to_n
kind: function
lang: py
domain: geo
version: "1.0.0"
purity: impure
signature: "def valhalla_matrix_1_to_n(origins: list[tuple[float, float]], destinations: list[tuple[float, float]], pairs: list[tuple[int, int]], base_url: str = 'http://localhost:8002', costing: str = 'auto', max_targets_per_request: int = 400, max_distance_m: float = 30000.0, timeout_s: float = 120.0, concurrency: int = 12, search_radius_m: float = 0.0) -> list[dict]"
description: "Calcula distancias (metros) y tiempos (segundos) para pares origen-destino usando Valhalla sources_to_targets. Agrupa pares por origen para minimizar requests. Paraleliza con ThreadPoolExecutor. Pandas-free."
tags: [valhalla, matrix, geo, http, routing, distance, batch, threading]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [json, math, collections, concurrent.futures, threading, urllib.request]
params:
  - name: origins
    desc: "Lista de (lat, lon) en grados decimales WGS84. Son los posibles puntos de origen."
  - name: destinations
    desc: "Lista de (lat, lon) en grados decimales WGS84. Son los posibles destinos."
  - name: pairs
    desc: "Lista de (origin_idx, dest_idx) indicando qué pares calcular. Los índices referencian las listas origins y destinations."
  - name: base_url
    desc: "URL base del servidor Valhalla."
  - name: costing
    desc: "Modelo de coste: 'auto', 'bicycle', 'pedestrian', etc."
  - name: max_targets_per_request
    desc: "Máximo de destinos por request POST a Valhalla. Chunks más grandes son más eficientes pero pueden saturar el servidor."
  - name: max_distance_m
    desc: "Distancia en metros usada como fallback cuando un par falla (error=1)."
  - name: timeout_s
    desc: "Timeout en segundos por request HTTP."
  - name: concurrency
    desc: "Número de threads paralelos para las requests."
  - name: search_radius_m
    desc: "Radio en metros para snapping de coordenadas a la red viaria (0 = default de Valhalla)."
output: "Lista paralela a 'pairs' con dicts {\"meters\": float, \"seconds\": float, \"error\": int}. error=0 si OK, error=1 si Valhalla falló. En error: meters=max_distance_m, seconds=NaN."
tested: true
tests: ["matrix 1 origen 2 destinos retorna 2 dicts con meters > 0"]
test_file_path: "python/functions/geo/tests/test_valhalla_matrix_1_to_n.py"
file_path: "python/functions/geo/valhalla_matrix_1_to_n.py"
source_repo: "internal:footprint_aurgi"
source_license: "internal-aurgi"
source_file: "ponderacion_isochronas/add_distancias_valhalla.py"
---

## Ejemplo

```python
origins = [(40.4168, -3.7038)]       # Madrid
destinations = [
    (41.3874, 2.1686),               # Barcelona
    (37.3886, -5.9823),              # Sevilla
]
pairs = [(0, 0), (0, 1)]            # Madrid->Barcelona, Madrid->Sevilla

results = valhalla_matrix_1_to_n(origins, destinations, pairs)
for (oi, di), r in zip(pairs, results):
    print(f"pair ({oi},{di}): {r['meters']/1000:.1f} km, {r['seconds']/3600:.2f} h")
```

## Notas

Adaptada de `add_valhalla_meters_seconds_1_to_n` en `add_distancias_valhalla.py`. El original acoplaba pandas DataFrames, columnas por nombre, resume/checkpoint y verbose logging — esta versión es pandas-free con firma genérica basada en índices. Usa urllib.request (stdlib) para evitar dependencia de httpx en threads (httpx.Client no es thread-safe sin precauciones). Valhalla retorna distancia en km — se convierte a metros multiplicando por 1000.
