Metro en el Arco Ártabro: Explorando un Sistema de Transporte Metropolitano
Las comarcas de A Coruña, Ferrol, Betanzos y Eume concentran más de 450.000 habitantes en un área de unos 1.600 km². Sin embargo, la conexión entre sus núcleos urbanos sigue dependiendo casi exclusivamente de la red viaria (AP-9, AC-12, N-651) y de un servicio ferroviario de cercanías con frecuencias limitadas. Este artículo explora la viabilidad técnica e ingenieril de un sistema de metro ligero o tren-tram que vertebre el Arco Ártabro, usando datos abiertos y análisis geoespacial.
Estado actual: tercera iteración. El modelo de coste tiene 6 capas con gradiente de distancia corregido (la capa de infraestructura ahora decae desde 0 en la vía hasta ~1 a varios km, en vez del valor binario anterior). Las coordenadas de estaciones sobre la línea C-1 (Miño, Pontedeume, Ferrol) se han ajustado a los apeaderos reales de OSM. El escenario «Seguir ferroviaria» usa waypoints intermedios (Perbes, Barallobre) que anclan el trazado al corredor costero de la ría de Betanzos: el tramo Betanzos→Pontedeume ahora tiene una distancia media de 99m al ferrocarril real.
| Variable | Fuente | Parámetro | Peso (por defecto) |
|---|---|---|---|
| Pendiente | MDT 5m → numpy.gradient | lineal hasta 6% | 0.30 |
| Coste suelo | OSM landuse → clasificación | rural=0.3 … urbano=0.8 | 0.25 |
| Población | OSM buildings → KDE σ=800m | beneficio (negativo) | -0.15 |
| POIs | Hospitales, universidades, industrial | exp(-dist/σ) σ=1000m | -0.10 |
| Ferrocarril | OSM railways → gradiente corredor | σ=1.500m (atractivo) | 0.05 |
| Carreteras | OSM highways → penalización cruce | σ=200m (obstáculo) | 0.03 |
| Puentes | OSM bridges → pasillo de cruce | σ=300m + bridge_clear ~40m | 0.02 |
| Geología | Proxy: elevación + pendiente | α=0.5 elev + 0.5 slope | 0.10 |
| Barrera agua | MDT ≤ 0m + excepción puentes | binaria | ∞ |
| Barreras OSM | Protected areas, military, cemetery | binaria | ∞ |
Fórmula de coste: coste(i,j) = Σ(wk × capak(i,j))
Las 6 capas continuas se normalizan a rango [0, 1] independientemente antes de combinarlas. Cada píxel del ráster final contiene un escalar que representa el coste estimado de construir el metro en esa celda. Las capas de beneficio (población y POIs) reciben pesos negativos: «atraen» el trazado hacia zonas pobladas en lugar de penalizarlas. El ferrocarril, aunque codificado con peso positivo, también actúa como atractivo: la capa usa la fórmula 1 – exp(–dist/σ) donde cerca de la vía = valor bajo (0) y lejos = valor alto (1), por lo que un peso positivo penaliza alejarse del corredor ferroviario. Las barreras (agua, espacios protegidos, cementerios) asignan coste infinito a sus celdas, forzando al algoritmo MCP (minimum cost path) a rodearlas. Los puentes generan una excepción en la barrera de agua mediante una máscara de corredor (bridge_clear, dilatada ~40m) que permite al trazado cruzar rías por puntos viables. Cada escenario aplica pesos distintos sobre las mismas 6 capas precomputadas (archivos .npy), sin necesidad de recalcular los gradientes ni volver a consultar OSM.
Infraestructura desagregada: la capa única de infraestructura se compone de tres sub-componentes con radios de influencia (sigma) distintos, reflejados en la tabla superior como filas independientes. El ferrocarril (σ=1.500m) genera un corredor amplio de atracción — la ruta tiende a seguir la traza ferroviaria existente donde es viable. Las carreteras principales (σ=200m) penalizan los cruces con una banda estrecha: atravesar una autopista requiere paso elevado o subterráneo. Los puentes (σ=300m) son puntos de paso habilitados sobre el agua; su máscara dilatada (bridge_clear) perfora la barrera de agua para que el trazado pueda cruzar rías por los mismos puntos donde ya existe infraestructura.
| Escenario | Longitud (km) | Coste acumulado | Rutas troncales | Rutas alimentación |
|---|---|---|---|---|
| Por defecto | — | — | — | — |
| Priorizar cobertura | — | — | — | — |
| Minimizar coste | — | — | — | — |
| Seguir ferroviaria | — | — | — | — |
Índice
- Visión general: ¿por qué un metro en el Arco Ártabro?
- Situación actual del transporte público
- Datos geoespaciales disponibles
- Metodología: algoritmo de coste mínimo para trazado
- Stack tecnológico para la implementación web
- Variables consideradas en el modelo de coste
- Próximos pasos
Visión general: ¿por qué un metro en el Arco Ártabro?
El Arco Ártabro —la fachada litoral entre las rías de A Coruña y Ferrol— presenta una de las densidades de población más altas de Galicia fuera del eje atlántico Vigo-Pontevedra. Los 15 municipios costeros del arco suman más de 350.000 habitantes, con fuertes flujos diarios de movilidad laboral entre A Coruña y su área metropolitana (Oleiros, Culleredo, Cambre, Arteixo, Sada, Bergondo).
Un sistema de metro ligero o tren-tram:
- Reduciría la congestión en la AP-9 (uno de los tramos de autopista más tensionados de Galicia) y en la AC-12 (acceso norte a A Coruña por A Pasaxe).
- Conectaría nodos clave: A Coruña ↔ Culleredo ↔ Cambre ↔ Oleiros ↔ Sada ↔ Betanzos ↔ Pontedeume ↔ Ferrol ↔ Narón.
- Daría servicio a equipamientos metropolitanos: Universidade da Coruña (Elviña y Zapateira), CHUAC (Hospital de A Coruña), polígonos industriales (A Grela, Pocomaco, Sabón), Alfonso Molina (hub de transporte), estaciones de tren y autobús.
Situación actual del transporte público en el Arco Ártabro
Ferrocarril de cercanías (Renfe Cercanías AM — antigua FEVE)
La línea Ferrol ↔ Ortigueira es la única conexión ferroviaria de cercanías en la zona. No existe un servicio de cercanías entre A Coruña y Ferrol por ferrocarril. La conexión ferroviaria A Coruña-Ferrol es de Media Distancia (MD) con paso por Betanzos-Infesta, con frecuencias limitadas (3-4 trenes diarios por sentido) y material rodante diésel en vía única sin electrificar.
Red de autobuses metropolitanos
- Transporte Metropolitano de Galicia (TMG): Gestionado por la Xunta, cubre las líneas interurbanas del área de A Coruña. Opera concesiones a empresas privadas (Arriva, Cal-Pita, etc.).
- Compañía de Tranvías de A Coruña: Opera las líneas urbanas de la ciudad y algunas conexiones metropolitanas (línea a Santa Cruz-Oleiros, línea a Cambre).
- Principales carencias: Frecuencias insuficientes en horas valle, nula integración tarifaria entre operadores, ausencia de carril bus en los accesos a la ciudad, tiempos de viaje poco competitivos frente al vehículo privado.
Infraestructura ciclista y peatonal
El vial del puerto entre A Coruña y O Burgo (vía Ártabra) es un carril bici de referencia, pero no está conectado a una red metropolitana más amplia. La mayoría de los desplazamientos intermunicipales carecen de infraestructura ciclista segregada.
Datos geoespaciales disponibles
Todos los datos mencionados a continuación son gratuitos y de libre descarga (salvo indicación contraria), con licencia CC-BY 4.0 o equivalente. La mayoría están disponibles a través del Centro de Descargas del CNIG (IGN) o de servicios WMS/WFS públicos.
Modelo Digital del Terreno (elevación)
| Producto | Resolución | Formato | Fuente | Coste |
|---|---|---|---|---|
| MDT02 (2ª Cobertura LiDAR) | 2 m | COG (Cloud Optimized GeoTIFF) | CNIG/IGN | Gratuito |
| MDT05 (1ª Cobertura) | 5 m | COG / ASCII | CNIG/IGN | Gratuito |
| MDT25 | 25 m | COG / ASCII | CNIG/IGN | Gratuito |
| LiDAR 2ª Cobertura (nube de puntos) | 0.5–4 pts/m² | LAZ | CNIG/IGN | Gratuito |
Información catastral y valor del suelo
| Producto | Contenido | Formato | Fuente |
|---|---|---|---|
| Catastro WFS INSPIRE (BU) | Edificios: huella, altura, uso, año construcción | WFS 2.0 / GML | D.G. Catastro |
| Catastro WFS INSPIRE (CP) | Parcelas catastrales: geometría, uso del suelo | WFS 2.0 / GML | D.G. Catastro |
| Cartografía Catastral WMS | Mapa base catastral (parcelario + edificios) | WMS | D.G. Catastro |
| Mapas de valores urbanos | Valor de referencia del suelo por zona | PDF/visor | Sede Catastro |
| Sede Catastro (datos no protegidos) | Superficie, uso, año construcción por parcela | REST/SOAP | Sede Catastro |
Población, demografía y movilidad
| Producto | Resolución | Fuente |
|---|---|---|
| Secciones censales INE | ~1.000–2.500 hab por sección | INE GeoServer (WFS) |
| INE Atlas de Renta (ADRH) | Renta media por sección censal | ineAtlas.data (GitHub) |
| Datos de movilidad (INE 2021) | Flujos residencia-trabajo intermunicipales | INE Censos 2021 |
Infraestructuras y red viaria
| Producto | Contenido | Fuente |
|---|---|---|
| Red de Transporte IGN (IGR-RT) | Red viaria, ferroviaria, portuaria | CNIG/IGN |
| CartoCiudad | Callejero completo, direcciones, portales | CartoCiudad (CNIG) |
| OpenStreetMap | Red viaria, POIs, edificios, usos del suelo | OpenStreetMap |
Planeamiento urbanístico
| Producto | Fuente |
|---|---|
| PGOM A Coruña (capas WMS) | IDE Coruña |
| Planes Generales municipales | Sedes electrónicas municipales / SIOTUGA (Xunta) |
| SIOSE (Sistema de Información de Ocupación del Suelo) | CNIG/IGN |
Metodología: algoritmo de coste mínimo para trazado
El núcleo técnico del proyecto es encontrar trazados óptimos entre estaciones minimizando una función de coste multivariable. El enfoque consiste en separar la combinación de variables del algoritmo de pathfinding: primero se construye una superficie de coste escalar combinando todas las variables, y luego se ejecuta el algoritmo de ruta mínima sobre esa superficie.
¿Por qué funciona con librerías estándar?
Tanto skimage.graph.route_through_array (Python) como PathFinding.js (JavaScript) operan sobre un único array de costes por celda. No necesitan saber que el coste proviene de 8 variables distintas. Esa combinación es una etapa previa independiente del pathfinding:
coste_total[i][j] = w₁ × pendiente_norm[i][j] + w₂ × suelo_norm[i][j] + w₃ × demanda_norm[i][j] + …
# Y luego, una sola llamada:
path, cost = route_through_array(coste_total, start, end)
Esto es exactamente el flujo estándar en GIS para least-cost path: álgebra de mapas para combinar capas → algoritmo de ruta mínima sobre el resultado. Las librerías de pathfinding son perfectamente válidas porque la complejidad multivariable se resuelve antes de llamarlas.
Lo que SÍ requiere trabajo propio
- Normalización: cada capa (pendiente, coste suelo, demanda) tiene unidades y rangos distintos. Hay que normalizarlas a una escala común (0-1) antes de combinarlas.
- Barreras absolutas: celdas infranqueables (agua, espacios protegidos) deben marcarse con coste infinito o NaN para que el algoritmo las evite. Tanto
route_through_arraycomo PathFinding.js manejan celdas no transitables (coste infinito o peso 0 respectivamente). - Georreferenciación: el algoritmo devuelve índices de matriz [fila, columna]; hay que convertirlos a coordenadas WGS84 con
rasterio.transform.xy()para el GeoJSON final. - Suavizado post-trazado: el resultado es una polilínea a resolución de píxel (2m) con zigzag. Necesita simplificación (Chaikin o Douglas-Peucker) para respetar radios de curvatura ferroviarios mínimos.
- Recálculo con nuevos pesos: si se quieren probar combinaciones de pesos distintas, hay que regenerar la matriz
coste_totaly re-ejecutar el pathfinding. En los scripts offline esto es trivial (bucle sobre escenarios enconfig.yaml). En el front con JS sería viable solo a resoluciones reducidas.
Superficie de coste
Se genera una malla ráster donde cada píxel contiene un valor de «coste de construcción» por metro lineal. Este coste es la suma ponderada de varias capas normalizadas:
- Pendiente del terreno (elevación): Calculada a partir del MDT02 (2 m). Las pendientes superiores al 4-6% disparan los costes de túnel o viaducto. Se puede derivar un modelo de «coste de excavación/túnel» a partir de la diferencia entre la cota del terreno y una rasante de referencia.
- Coste del suelo (expropiación): Aproximado a partir de:
- Valores de referencia del Catastro (mapas de valores urbanos/rústicos)
- Clasificación urbanística del suelo (urbano consolidado, urbanizable, rústico, protegido)
- Usos del suelo de SIOSE / Catastro
- Proximidad a demanda (población): Las celdas cercanas a centros de población (secciones censales con alta densidad) reciben una «bonificación» que atrae el trazado hacia ellas. Se modela como un coste negativo (o beneficio).
- Proximidad a POIs: Estaciones de tren, hospitales (CHUAC, Arquitecto Marcide), universidades (UDC Elviña, UDC Zapateira), polígonos industriales (A Grela, Sabón, Río do Pozo), centros comerciales, intercambiadores.
- Restricciones absolutas: Barreras infranqueables (masas de agua — rías, embalses de Cecebre), espacios naturales protegidos (Reserva de la Biosfera Mariñas Coruñesas), suelo militar, cementerios.
- Geología y tipo de terreno: Afecta al coste de excavación. Roca granítica (más caro) vs. sedimentos aluviales (más barato). Datos disponibles en el IGME (Instituto Geológico y Minero).
- Longitud del trazado: Penalización por distancia euclidiana. El algoritmo minimiza naturalmente la longitud, pero se puede modular el peso relativo frente a otras variables.
- Interferencia con infraestructura existente: Cruce con autopistas, vías férreas, ríos. No es una barrera absoluta pero incrementa el coste (necesidad de pasos elevados o túneles).
Algoritmo de trazado
Una vez generada la superficie de coste escalar (combinación ponderada de todas las variables), se aplica MCP (Minimum Cost Path) sobre la matriz para encontrar la ruta de mínimo coste entre pares de estaciones. Esto lo resuelve route_through_array sin necesidad de implementar el algoritmo. Los pasos:
- Definir estaciones potenciales en
config.yaml: coordenadas de inicio/fin y paradas intermedias candidatas (núcleos de población, equipamientos). El script las convierte a índices de matriz conrasterio.index(). - Generar superficie de coste: álgebra de mapas con NumPy — cada variable normalizada se multiplica por su peso y se suman. Las barreras (agua, protegido) reciben
np.inf. Una línea de código. - Ejecutar MCP:
route_through_array(coste_total, start, end, fully_connected=True, geometric=True). Devuelve la lista de índices de la ruta óptima y el coste acumulado. - Postprocesar: convertir índices a coordenadas WGS84, simplificar la polilínea para respetar radios de curvatura, exportar a GeoJSON.
El script itera sobre los escenarios definidos en config.yaml (distintas combinaciones de pesos) y genera un GeoJSON de trazado por escenario. El front solo elige cuál mostrar.
Consideraciones de ingeniería ferroviaria
- Radio de curvatura mínimo: 150-300 m para metro ligero, ~50 m para tranvía urbano. Obliga a suavizar los trazados generados por el algoritmo ráster.
- Pendiente máxima: 3-5% para tren-tram, hasta 6-8% para metro ligero con tracción eléctrica moderna.
- Sección tipo: Túnel (~30-80 M€/km en España), viaducto (~20-40 M€/km), en superficie (~10-20 M€/km). El algoritmo debe reflejar estas diferencias de orden de magnitud.
- Estaciones: Coste fijo de 5-15 M€ por estación según tipología (subterránea, en superficie, intercambiador).
Stack tecnológico para la implementación web
Principio clave: scripts offline a resolución nativa (2m), resultados estáticos, front ligero. El MDT02 a 2m sobre el Arco Ártabro (~1.600 km²) son del orden de 400 millones de celdas. Eso no cabe en un navegador ni se puede rutear en JS con tiempos razonables. Pero en Python con NumPy y un A* eficiente sobre una matriz en memoria tarda 30-60 segundos por trazado — perfectamente asumible para un script que se ejecuta una vez.
1. Scripts offline (scripts/) — se ejecutan una vez, a 2m
Un directorio scripts/ con Python + GDAL + scikit-image que procesa los datos a resolución nativa y exporta resultados estáticos. Se ejecuta en local, no en el servidor.
Para el pathfinding se usa skimage.graph.route_through_array, que implementa MCP (Minimum Cost Path) sobre un array NumPy n-dimensional con soporte para 8-vecinos y coste geométrico. Es código C bajo el capó — no hay que implementar A* a mano. Una llamada devuelve la ruta óptima y su coste acumulado:
from skimage.graph import route_through_array
path, cost = route_through_array(cost_surface, start_idx, end_idx, fully_connected=True, geometric=True)
Tareas del script:
- Descargar las hojas del MDT02 que cubren el Arco Ártabro desde el CNIG (±8 hojas MTN25).
- Fusionar y recortar al área de estudio con GDAL (
gdal_merge.py+gdalwarp). - Calcular pendientes derivadas a 2m (
gdaldem slope). - Integrar capas de coste (Catastro, SIOSE, INE) mediante álgebra de mapas con rasterio + NumPy.
- Ejecutar MCP sobre la malla de 2m con
skimage.graph.route_through_array. Para ~400M celdas, la implementación en C de scikit-image tarda 30-60s por trazado. - Convertir los índices de ruta a coordenadas geográficas con
rasterio.transform.xy()y exportar como GeoJSON. - Exportar la superficie de coste como tiles XYZ de mapa de calor con
gdal2tiles. - Generar tiles XYZ de hillshade a 2m con
gdaldem hillshade+gdal2tiles.
Alternativas de pathfinding en JavaScript
Si en el futuro se quisiera pathfinding interactivo en el front (requiere reducir resolución porque ~400M celdas no caben en JS), hay librerías probadas que evitan implementar A* desde cero:
- PathFinding.js (9K ★, MIT) — la más madura. Acepta una matriz de pesos con
new PF.Grid(matrix), 10 algoritmos (A*, Dijkstra, Bi-A*, Jump Point Search). API:finder.findPath(x1, y1, x2, y2, grid). Funciona en navegador y Node. - astar-typescript (MIT) — A* con TypeScript, soporta tiles con peso (
setWalkable({row, col, weight})), heurísticas Manhattan y Diagonal. - EasyStar.js (MIT) — A* asíncrono. Ideal para no bloquear el UI: ejecuta en iteraciones con
setTimeout. Soporta peso por celda y diagonales.
Para el escenario actual (sin reducir resolución), estas librerías no aplican en front; el pathfinding se hace 100% en los scripts offline con route_through_array.
scripts/
├── preprocess.py # Orquestador: descarga MDT, fusiona, recorta, pendientes y coste
├── config.yaml # Pesos de variables, bbox, estaciones, escenarios
2. Servidor estático — el blog PHP/Slim sin cambios
Los resultados de los scripts se copian a /content/coruna/data/metro/. El blog los sirve como ficheros estáticos igual que ya hace con los GeoJSON de municipios y barrios. Cero lógica de servidor nueva.
data/metro/
├── trazado_escenario_a.geojson # Ruta óptima (escenario 1: pesos por defecto)
├── trazado_escenario_b.geojson # Ruta óptima (escenario 2: priorizar cobertura)
├── trazado_escenario_c.geojson # Ruta óptima (escenario 3: minimizar coste)
├── estaciones.geojson # Ubicación de estaciones propuestas
├── estaciones_buffer.geojson # Área de influencia 800m de cada estación
├── secciones_poblacion.geojson # Secciones censales con densidad de población
├── pois.geojson # Hospitales, universidades, polígonos industriales
├── barreras.geojson # Rías, embalses, espacios protegidos
├── coste_tiles/{z}/{x}/{y}.png # Tiles del mapa de calor de coste (2m → imagen)
└── hillshade/{z}/{x}/{y}.png # Tiles de relieve (2m → imagen)
3. Cliente (navegador) — Leaflet + Turf.js, sin pathfinding
El front solo visualiza y deja comparar. Con los trazados precomputados para varios escenarios y pesos, el usuario dispone de toda la información relevante sin necesidad de ejecutar A* en el navegador.
| Capa | Tecnología | Rol |
|---|---|---|
| Mapa base + capas | Leaflet | Carga los GeoJSON estáticos (trazados, estaciones, secciones censales, POIs, barreras) y los tiles de hillshade + coste. Selector para alternar entre escenarios precomputados. Igual que ya funciona en /coruna/mapa-barrios. |
| Geometría interactiva | Turf.js | Operaciones ligeras: medir longitud de cada trazado, distancia punto-línea (qué sección censal está más cerca de qué estación), buffers para área de cobertura, intersecciones para calcular población servida por cada escenario, suavizado de líneas. |
| Relieve y coste | Leaflet + tiles XYZ | Capa de hillshade generada offline con gdaldem hillshade + gdal2tiles a resolución nativa. Capa de calor del coste generada con el mismo pipeline. El front solo carga los tiles. |
| Comparativa | JS + Turf.js | Tabla con métricas por escenario: longitud total, coste acumulado, población servida, estaciones, % en túnel/superficie/viaducto. Selector de escenario que cambia el trazado visible en el mapa. |
¿Se puede hacer interactivo sin reducir resolución?
Un A* sobre 400M de celdas en JS no es viable (~30-60s en Python contra varios minutos o un crash en JS). Pero hay alternativas que no implican reducir la resolución del dato original:
- Precomputar escenarios: ejecutar el script offline con 5-10 combinaciones de pesos distintas y servirlas todas como GeoJSON. El usuario alterna entre ellas con un selector. Cubre el 90% de los casos de uso.
- Modelo simplificado en front: construir una malla derivada independiente del MDT original (ej. 200m de lado) exclusivamente para el recálculo interactivo. Las variables que alimentan esta malla se agregan desde los datos originales (pendiente media, coste modal, densidad media). No es "reducir resolución del MDT" sino generar un modelo ligero específico para UI. El usuario ajusta pesos en este modelo y ve cambios aproximados en tiempo real; el resultado «definitivo» siempre sale del script offline a 2m.
- Mapa de calor de coste: exportar la superficie de coste a 2m como tiles de imagen. El usuario ve exactamente qué zonas son caras o baratas a resolución nativa, y los trazados superpuestos encima. Sin A* en front, pero con toda la información de coste visible.
Resumen del flujo
- Scripts (1 vez):
python scripts/preprocess.py→ procesa MDT02 a 2m, ejecuta A* para N escenarios, exporta GeoJSON + tiles adata/metro/. - Despliegue: Copiar
data/metro/a/content/coruna/data/metro/en el blog. Sin cambios en PHP. - Front: Leaflet carga los GeoJSON con
fetch()y los tiles conL.tileLayer(). Selector de escenarios, tabla comparativa de métricas, hover sobre estaciones para ver población servida (Turf.js). Cero pathfinding en navegador.
Variables consideradas en el modelo de coste
Resumen de todas las variables, su fuente, peso sugerido y modo de normalización:
| Variable | Fuente | Tipo | Peso sugerido | Normalización |
|---|---|---|---|---|
| Pendiente del terreno | MDT02 (2m) → GDAL slope | Continua (0-90°) | 0.30 | Lineal hasta 6% (óptimo), cuadrática >6% |
| Coste del suelo | Catastro + SIOSE | Categórica/continua | 0.25 | Escala 1-10 según clase urbana y valor ref. |
| Proximidad a población | INE secciones censales | Continua (densidad) | -0.15 | Kernel density con radio 800m |
| Proximidad a POIs | OSM + IDE Coruña | Distancia euclidiana | -0.10 | Decaimiento exponencial, radio 1km |
| Longitud de trazado | Ráster de coste | Implícita | 1.00 | Coste por metro lineal en el grafo |
| Barreras absolutas | OSM (agua) + SIOSE (protegido) | Binaria | ∞ | Coste infinito = infranqueable |
| Geología (tipo terreno) | IGME Mapa Geológico 1:50k | Categórica | 0.10 | Factor de excavación: granito 1.5x, aluvial 1.0x |
| Infraestructura existente | IGR-RT / OSM | Binaria | 0.10 | Penalización fija por cruce (coste de paso elevado) |
Próximos pasos
Fase 1: Preparación de datos ✅ Completado (MDT 5m vía WCS)
- ✅ MDT descargado vía WCS IGN (5m, 6 tiles, sin login). Fusionado en
mdt_artabro_full.tif(7770×9324 px, EPSG:4326). - ✅ Pendientes calculadas con numpy (
pendiente_artabro.tif). - ⬜ Secciones censales INE con población para los 35 municipios de Ártabria.
- ⬜ Red viaria y ferroviaria de IGR-RT u OpenStreetMap.
- ⬜ Usos del suelo de SIOSE o Catastro para clasificar áreas urbanas/rústicas/protegidas.
- ⬜ POIs clave (hospitales, universidades, polígonos industriales).
Fase 2: Generación de superficie de coste ✅ Completado (6 capas)
- ✅ Pendientes calculadas a partir del MDT (normalizadas [0, 1]).
- ✅ Infraestructura (OSM): ferrocarriles, carreteras primarias+ y puentes → gradiente de proximidad con decaimiento exponencial. Sigma: 500m rail, 200m carretera, 300m puentes.
- ✅ Población (OSM): densidad de edificios con filtro gaussiano (sigma=800m) → normalizado [0, 1] invertido (alta densidad = bajo coste).
- ✅ POIs (OSM): hospitales, universidades, estaciones, polígonos industriales y retail → gradiente de proximidad (sigma=1000m).
- ✅ Uso del suelo (OSM): clasificación por tipo (residencial=0.8, industrial=0.7, rural=0.3, etc.) → rasterizado por categoría.
- ✅ Barreras: agua (MDT ≤ 0m: 21M celdas) + protección (OSM: protected areas, military, cemetery: 0.6M celdas) = 21.6M celdas bloqueadas.
Fase 3: Trazado de rutas ✅ Primera iteración
- ✅ 17 estaciones definidas en
config.yaml(A Coruña, Culleredo, Cambre, Oleiros, Sada, Bergondo, Betanzos, Miño, Pontedeume, Cabanas, Ares, Mugardos, Fene, Ferrol, Narón). - ✅ 12 rutas calculadas con
skimage.graph.route_through_array(MCP): 5 troncales + 7 de alimentación. - ✅ Trazados suavizados con Douglas-Peucker (ε=0.0002° ≈ 22m).
- ✅ 3 escenarios exportados como GeoJSON (default, priorizar_cobertura, minimizar_coste).
Fase 4: Scripts de preprocesado y front estático ✅ Completo (2 scripts + 3 escenarios divergentes)
- ✅ build_layers.py: descarga OSM (infraestructura, población, POIs, uso del suelo, barreras) y rasteriza a 7770×9324 celdas (5m).
- ✅ preprocess.py: pipeline completo — descarga WCS → merge → pendiente → superficie de coste multivariable → MCP pathfinding → GeoJSON por escenario.
- ✅ Mapa Leaflet interactivo cargando trazados precomputados con selector de 3 escenarios divergentes.
- ⬜ Capa de tiles de coste para visualizar zonas caras/baratas con opacidad ajustable.
- ⬜ Tabla comparativa de escenarios con métricas (longitud, población servida, coste estimado).
- ⬜ Población cubierta por cada estación (buffer 800m intersectado con secciones censales).
Referencias y enlaces
- Centro de Descargas CNIG (IGN) — MDT02, LiDAR, IGR-RT, SIOSE
- CartoCiudad (CNIG) — Callejero, portales, códigos postales
- Sede Electrónica del Catastro — Datos catastrales, valores de referencia
- INE GeoServer — Secciones censales WFS
- IDE A Coruña — Planeamiento urbanístico, divisiones administrativas
- qgis-js (GitHub) — QGIS en WebAssembly (beta)
- Turf.js — Análisis geoespacial en JavaScript
- GDAL — Librería de procesamiento ráster y vectorial
- Ártabria — Transparencia Municipal (este proyecto)