diff --git a/README.md b/README.md index e36b989..98ae4c0 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,11 @@ explorarlas sobre relieve, ortofoto y catastro. ![vista del visor](docs/visor.png) +Documentación completa en la [wiki](../../wiki) y en `docs/`. + ## Cómo usarlo -**En el navegador, sin instalar nada** — si está publicado en GitLab Pages: -`https://.gitlab.io//`. Funciona igual en Windows, Android y -Linux. - -**En local**, tras clonar: +**Sin instalar nada**, tras clonar: ```bash xdg-open out/visor.html # Linux @@ -64,18 +62,32 @@ perfecto al que no se puede llegar no salga con cuatro estrellas. Los candidatos se eligen cogiendo el mejor punto de cada bloque de 20 km y exigiendo después 10 km de separación entre los elegidos. -## Reproducirlo +## Otros países -Hacen falta ~25 GB de disco y un par de horas. Los datos de partida no están en -el repositorio: se descargan solos. +El país es un parámetro. Hay 27 declarados entre Europa y el norte de África, +pero **solo España está calculada**: el resto hay que procesarlo. ```bash -python3 -m venv .venv -./.venv/bin/pip install numpy scipy rasterio osmium shapely pyproj requests pillow -make all +./.venv/bin/python src/regions.py # ver el catálogo +REGION=pt make all # procesar Portugal ``` -O paso a paso, ver el `Makefile`. +Una malla por país, no una continental: Europa entera a 100 m son mil millones +de celdas, 73 GB solo en capas ráster. Ver [Añadir un país](docs/Anadir-un-pais.md). + +Ojo con lo que hay disponible en cada sitio: en el norte de África **no hay +fuente libre de espacios protegidos**, así que el filtro legal no se aplica y +hay que conseguir la cartografía del país aparte. + +## Reproducirlo + +Hacen falta ~25 GB de disco por país y un par de horas. + +```bash +./install.sh # Linux y macOS +.\install.ps1 # Windows (PowerShell) +make all +``` ## Lo que este análisis no sabe diff --git a/abrir.ps1 b/abrir.ps1 new file mode 100644 index 0000000..f32b72d --- /dev/null +++ b/abrir.ps1 @@ -0,0 +1,6 @@ +# Abre el visor en el navegador por defecto (Windows). +$ErrorActionPreference = "Stop" +Set-Location -Path $PSScriptRoot +$f = if ($args[0] -eq "informe") { "out\informe.html" } else { "out\visor.html" } +if (-not (Test-Path $f)) { Write-Host "No encuentro $f" -ForegroundColor Red; exit 1 } +Start-Process (Resolve-Path $f) diff --git a/docs/Anadir-un-pais.md b/docs/Anadir-un-pais.md new file mode 100644 index 0000000..7a791b8 --- /dev/null +++ b/docs/Anadir-un-pais.md @@ -0,0 +1,56 @@ +# Añadir un país + +El país es un parámetro. Se elige con la variable `REGION`. + +```bash +./.venv/bin/python src/regions.py # ver el catálogo +REGION=pt make all # procesar Portugal entero +``` + +Los resultados van a `out/pt/` y los intermedios a `data/interim/pt/`. España, +que es la región por defecto, publica en `out/` para no romper rutas. + +## Por qué una malla por país y no un mapa continental + +Europa entera a 100 m son mil millones de celdas: **4 GB por capa** y unos +**73 GB** para las dieciocho capas del modelo, más de lo que cabe en memoria de +una vez. El norte de África añade otros 61 GB. Por país el problema es tratable +y además se puede paralelizar. + +## Lo que hay que mirar antes + +`src/regions.py` declara, por país, qué datos existen de verdad: + +| Campo | Qué significa | +|---|---| +| `protected='eea'` | Hay Red Natura 2000 y designaciones nacionales de la Agencia Europea de Medio Ambiente. El filtro legal funciona | +| `protected=None` | **No hay fuente libre equivalente.** El filtro legal no se aplica. Pasa en todo el norte de África: hay que conseguir la cartografía del país antes de fiarse de un resultado | +| `ortho='pnoa'` | Ortofoto nacional descargable. Solo España | +| `ortho=None` | Sin foto aérea incrustada. En el visor queda el mapa deslizante, que sí funciona en todas partes | + +## Proyección + +Europa usa EPSG:3035 (equiárea europea). Para el norte de África se construye +una proyección equiárea centrada en el propio país, porque 3035 deforma fuera de +Europa. Está en `regions.crs_for()`. + +## Añadir un país que no esté + +En `src/regions.py`: + +```python +"si": dict(name="Eslovenia", bbox=(13.3, 45.4, 16.6, 46.9), + osm=["europe/slovenia"], + neighbours=["europe/croatia", "europe/austria", "europe/italy"], + protected="eea", ortho=None), +``` + +- `bbox` en lon/lat con algo de margen. +- `osm` y `neighbours`, rutas de [Geofabrik](https://download.geofabrik.de). +- **Los vecinos importan.** Sin ellos, la franja fronteriza sale falsamente + vacía: el extracto está recortado por la frontera y al otro lado no hay + edificios en los datos aunque sí los haya en el terreno. + +## Coste aproximado + +Por país mediano: 2-6 GB de descarga, ~25 GB de disco temporal y un par de horas. diff --git a/docs/Fuentes-y-licencias.md b/docs/Fuentes-y-licencias.md new file mode 100644 index 0000000..34e2a91 --- /dev/null +++ b/docs/Fuentes-y-licencias.md @@ -0,0 +1,28 @@ +# Fuentes y licencias + +| Dato | Fuente | Licencia | +|---|---|---| +| Edificios, viales, agua, arbolado, roca, límites administrativos | [OpenStreetMap](https://www.openstreetmap.org/copyright) vía Geofabrik | ODbL | +| Red Natura 2000 y designaciones nacionales | Agencia Europea de Medio Ambiente / MITECO | Reutilización con atribución | +| Ortofotos PNOA (solo España) | [Instituto Geográfico Nacional](https://www.ign.es) | CC BY 4.0 | +| Cartografía catastral (solo España) | [Dirección General del Catastro](https://www.sedecatastro.gob.es) | Reutilización con atribución | +| Modelo de elevaciones | Copernicus DEM GLO-90 | Copernicus | +| Clima | [WorldClim 2.1](https://www.worldclim.org) | CC BY-SA 4.0 | +| Mapa deslizante | [Leaflet](https://leafletjs.com) 1.9.4 | BSD-2 | + +El código, MIT. Los datos mantienen la licencia de su fuente: si redistribuyes +resultados, **arrastra las atribuciones**. + +## Servicios que consulta el visor en vivo + +Solo en los modos Mapa y Ver de cerca, y solo si los activas: + +- `www.ign.es` — ortofoto PNOA y mapa topográfico +- `ovc.catastro.meh.es` — parcelas catastrales +- `geoserver.iepnb.es` — Red Natura 2000 y espacios protegidos +- `tile.openstreetmap.org` — mapa base de OSM + +Son servicios públicos con condiciones de uso razonable. El visor pide teselas +solo de lo que estás mirando; no descarga en bloque. + +El modo Relieve y todos los datos del modelo funcionan **sin conexión**. diff --git a/docs/Home.md b/docs/Home.md new file mode 100644 index 0000000..c52667b --- /dev/null +++ b/docs/Home.md @@ -0,0 +1,34 @@ +# EXPLORER + +Localiza los puntos de un país más alejados de cualquier rastro de gente **a los +que además se puede llegar** con un vehículo, y los valora de 1 a 5 estrellas. + +Barre el territorio celda a celda sobre una malla de 100 m usando datos abiertos, +descarta lo que no es viable (espacios protegidos, zonas militares, sin acceso, +demasiada pendiente) y puntúa lo que queda en seis criterios. + +## Empezar + +| Quiero… | Ir a | +|---|---| +| Solo mirar los resultados | [Uso del visor](Uso-del-visor) | +| Instalarlo en mi máquina | [Instalación](Instalacion) | +| Procesar otro país | [Añadir un país](Anadir-un-pais) | +| Entender cómo se calcula | [Metodología](Metodologia) | +| Saber qué datos usa | [Fuentes y licencias](Fuentes-y-licencias) | +| Publicarlo para más gente | [Publicar](Publicar) | + +## Estado + +Ahora mismo hay **un solo país procesado, España**, con 355 localizaciones. +El resto del catálogo (27 países entre Europa y el norte de África) está +declarado y el código los soporta, pero **sus datos no están calculados**: hay +que ejecutar el pipeline para cada uno. Ver [Añadir un país](Anadir-un-pais). + +## Aviso + +Esto localiza sitios despoblados; no es permiso para nada. Casi todo el terreno +es privado, muchas zonas tienen restricciones de acceso por incendios, y celebrar +una concentración sin autorización tiene consecuencias administrativas y, si +prende algo, penales. Comprueba propiedad, figura de protección y época del año +antes de moverte. diff --git a/docs/Instalacion.md b/docs/Instalacion.md new file mode 100644 index 0000000..31fad7a --- /dev/null +++ b/docs/Instalacion.md @@ -0,0 +1,60 @@ +# Instalación + +## Para solo mirar los resultados: nada + +El visor es un HTML. Clona el repositorio y ábrelo: + +| Sistema | Orden | +|---|---| +| Linux | `xdg-open out/visor.html` o `./abrir.sh` | +| Windows | doble clic en `out\visor.html`, o `.\abrir.ps1` | +| macOS | `open out/visor.html` | +| Android / iPhone | ver [Publicar](Publicar) | + +No hace falta servidor, ni Python, ni conexión. + +**Ojo:** `visor.html` necesita la carpeta `out/ortho/` a su lado, que es donde +están las fotos aéreas. Si mueves el HTML solo, se quedan sin cargar. + +### Si al abrirlo se abre un editor de texto + +Le pasa a quien tiene los `.html` asociados a un editor, algo habitual si +programas. `xdg-open` respeta esa asociación, no el navegador por defecto. +Usa `./abrir.sh`, que llama al navegador directamente, o arrástralo a una +ventana del navegador. + +## Para reconstruir los datos + +Hacen falta Python 3.9+, unos 25 GB de disco por país y un par de horas. + +### Linux / macOS + +```bash +./install.sh +``` + +Si falta el módulo `venv`: `sudo apt install python3-venv` en Debian/Ubuntu. + +### Windows + +```powershell +Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass +.\install.ps1 +``` + +Si no tienes Python: `winget install Python.Python.3.12`, marcando +*Add Python to PATH*. + +### Qué instala + +Solo paquetes de Python, dentro de `.venv/`, sin tocar el sistema: +`numpy`, `scipy`, `rasterio`, `osmium`, `shapely`, `pyproj`, `requests`, +`pillow`. Todos traen binarios precompilados para Linux, Windows y macOS, así +que **no hace falta compilar GDAL ni nada parecido**. + +## Comprobar que funciona + +```bash +./.venv/bin/python src/regions.py # lista los países disponibles +./.venv/bin/python src/revisar.py # comprueba la coherencia del resultado +``` diff --git a/docs/Metodologia.md b/docs/Metodologia.md new file mode 100644 index 0000000..7a3e97d --- /dev/null +++ b/docs/Metodologia.md @@ -0,0 +1,80 @@ +# Metodología + +Malla de 100 m en proyección equiárea, así que una distancia euclídea en la +malla es una distancia real en el terreno. + +## 1. Se descarta + +Una celda queda fuera, por buena que sea, si cumple alguna: + +| Condición | Por qué | +|---|---| +| En espacio protegido oficial, o a menos de 300 m del borde | Los parques se trazaron rodeando lo más vacío, y el modelo tendía a pegarse al límite, donde hay zona periférica y vigilancia | +| En zona militar o sobre agua | Obvio | +| A menos de 2 km de un edificio | Por debajo te oyen desde casa | +| A menos de 400 m de una carretera | Te ve cada coche que pasa | +| A más de 800 m de un vial | No metes un equipo más lejos | +| Más de 10° de pendiente | Ni se monta ni se aparca | + +En España sobreviven 47.519 km², el 9,5 % del país. + +## 2. Manda el sonido + +Se estima el nivel que llega a la casa más cercana con propagación real: + +``` +SPL = 130 − 20·log₁₀(d) − 0,5·d/1000 − atenuación_arbolado − apantallamiento +``` + +Divergencia esférica desde 130 dB a 1 m (equipo grande), absorción atmosférica +en frecuencias bajas (0,5 dB/km, que es lo que viaja), hasta 6 dB de absorción +por arbolado y hasta 12 dB de apantallamiento si estás en una hondonada. + +Se exige **≤ 45 dB**, el límite nocturno típico en suelo rural. Es el filtro que +de verdad corta: de 47.519 km² quedan **505 km²** en toda España. El cuello de +botella no es la soledad, es el sonido. + +## 3. Se puntúa + +| Criterio | Peso | Qué mide | +|---|---|---| +| Sonido | 30 % | Los dB del apartado anterior | +| Soledad | 22 % | Lejanía de edificios, carreteras y núcleos; densidad construida en 5 km | +| Acceso | 18 % | Pista en condiciones cerca (OSM distingue firme y grado), llano, sin pedregal | +| Agua | 12 % | Río, arroyo o fuente entre 120 y 900 m | +| Arbolado | 10 % | Entre 20 y 65 % de cobertura: sombra y pantalla sin ser impenetrable | +| Clima | 8 % | Máxima del mes más cálido y mínima del más frío, corregidas por altitud | + +Las estrellas salen de la media ponderada **penalizada por el eslabón más +débil**: un sitio perfecto al que no se puede llegar no es de cuatro estrellas. + +## 4. Se reparten + +Se divide el país en bloques de 20 km, se coge el mejor punto elegible de cada +bloque y después se exige 10 km de separación entre los elegidos. Sin ese +segundo paso salen pares casi duplicados: dos bloques vecinos pueden elegir cada +uno una celda pegada a su frontera común. + +## Trampas que costaron sangre + +- **La frontera.** El extracto de un país está recortado por su frontera, así + que al otro lado no hay edificios en los datos aunque sí en el terreno. Sin + añadir los países vecinos, toda la raya sale falsamente vacía. +- **La rejilla no está alineada con la latitud.** En EPSG:3035 baja más al sur + que la ventana de análisis, y colaba Melilla, cuyos edificios sí se habían + filtrado. Salía como el punto más aislado del país con 74,6 km. El máximo real + son 9,8 km. +- **Los protegidos de OpenStreetMap cubren el 15 %; los oficiales, el 29 %.** + Con los buenos, 33 de los 60 candidatos de la primera versión estaban dentro + de espacio protegido. +- **Sin distancia a carretera en la puntuación**, el modelo colocaba puntos de + cinco estrellas sobre el propio asfalto. + +## Lo que no sabe + +- **No ve vallas ni propiedad.** Es la mayor diferencia con la realidad de campo. + Por eso el visor lleva la capa de catastro. +- **El modelo acústico es de manual.** Una inversión térmica nocturna o el viento + a favor llevan el bajo mucho más lejos. +- **Ignora la estación.** Riesgo de incendio de junio a septiembre. +- **Distancia en línea recta, no tiempo de coche.** diff --git a/docs/Publicar.md b/docs/Publicar.md new file mode 100644 index 0000000..9dc677f --- /dev/null +++ b/docs/Publicar.md @@ -0,0 +1,48 @@ +# Publicar + +## Este repositorio está en Gitea + +**Gitea no tiene Pages integrado**, a diferencia de GitLab. Opciones: + +### Clonar y abrir (lo más simple) + +```bash +git clone https://gitea.laenre.net/hacklab/EXPLORER.git +cd EXPLORER +xdg-open out/visor.html +``` + +Funciona en Linux, Windows y macOS sin instalar nada. + +### Servirlo con cualquier servidor estático + +```bash +cd out && python3 -m http.server 8000 +``` + +Y abrir `http://localhost:8000/visor.html`. Así también funciona desde el móvil +si estás en la misma red: `http://IP-DEL-ORDENADOR:8000/visor.html`. + +Para dejarlo en un dominio, cualquier nginx/Apache sirviendo `out/` vale. No hay +backend. + +### GitLab Pages, si lo espejas + +El repositorio trae un `.gitlab-ci.yml` listo. Si creas un espejo en gitlab.com, +publica solo con hacer push y queda en +`https://USUARIO.gitlab.io/PROYECTO/`. Ese es el camino más cómodo para que lo +use gente desde el móvil sin descargar nada. + +## Móvil + +El visor se adapta a pantalla estrecha: el mapa arriba, los controles debajo y la +ficha a pantalla completa. Probado a 412 px de ancho. + +Lo que **no** está probado en un móvil real es el gesto de pellizcar para hacer +zoom en el modo Relieve, que usa eventos de puntero propios. En los modos Mapa y +Ver de cerca el zoom lo gestiona Leaflet y no hay problema. + +## Tamaño + +El repositorio son unos 18 MB: `visor.html` 3,9 MB, `informe.html` 1,8 MB y +8,4 MB de fotos aéreas. Los 22 GB de datos de partida no se versionan. diff --git a/docs/Seguridad.md b/docs/Seguridad.md new file mode 100644 index 0000000..1ba3919 --- /dev/null +++ b/docs/Seguridad.md @@ -0,0 +1,40 @@ +# Seguridad y privacidad + +## Qué NO hace el visor + +- No pide permisos del navegador. No usa geolocalización. +- No manda datos a ningún sitio. No tiene analítica, ni cookies, ni rastreo. +- Lo único que guarda es el idioma elegido, en `localStorage`. +- No carga código de ningún CDN: Leaflet va incrustado en el fichero. + +## Qué SÍ sale de tu máquina + +En los modos Mapa y Ver de cerca, el navegador pide teselas a los servidores de +la lista de [Fuentes](Fuentes-y-licencias). Esos servidores ven tu IP y **la zona +que estás mirando**, como con cualquier mapa web. Si eso te importa, quédate en +el modo Relieve, que es completamente local. + +Los enlaces de la barra inferior abren servicios de terceros (Google, Mapillary, +Catastro) con las coordenadas en la URL. Solo se abren si pulsas. + +## Escapado de contenido + +Los nombres de municipio y de espacio protegido vienen de OpenStreetMap y de +ficheros oficiales, o sea de terceros: **cualquiera puede editar el nombre de un +municipio en OSM**. Todo lo que sale de los datos y acaba en el HTML pasa por +escapado antes, incluidos los tooltips del mapa. Sin eso, alguien podría inyectar +código editando un topónimo. + +## Al reconstruir los datos + +- Todas las descargas van por HTTPS con verificación de certificado. +- Los servidores del ministerio español sirven la cadena TLS incompleta. El + proyecto **no desactiva la verificación**: descarga el certificado intermedio + oficial de la FNMT y valida la cadena entera contra la raíz del sistema. +- El repositorio no contiene credenciales. `data/` está en `.gitignore`. + +## Si lo publicas + +El visor es HTML estático, sin backend, así que la superficie de ataque es la de +cualquier página estática. Sírvelo por HTTPS y añade cabeceras +`Content-Security-Policy` si tu servidor lo permite. diff --git a/docs/Uso-del-visor.md b/docs/Uso-del-visor.md new file mode 100644 index 0000000..38ce78c --- /dev/null +++ b/docs/Uso-del-visor.md @@ -0,0 +1,56 @@ +# Uso del visor + +## Los tres modos + +Arriba a la izquierda: + +- **Relieve** — el país entero sobre relieve sombreado, generado a partir del + modelo de elevaciones. Funciona sin conexión. Encima puedes activar la capa de + aislamiento y la de espacios protegidos. +- **Mapa** — el país entero sobre mapa real: satélite, topográfico u + OpenStreetMap. Necesita conexión. +- **Ver de cerca** — lo mismo, centrado en la localización elegida. + +## Capas + +En los modos Mapa y Ver de cerca: + +| Fondo | Qué aporta | +|---|---| +| Satélite | Ortofoto. Ves la explanada, los árboles y por dónde entra la pista | +| Topográfico | Curvas de nivel, cotas, arroyos, topónimos | +| OpenStreetMap | Caminos y nombres | + +| Encima | Qué aporta | +|---|---| +| Parcelas del catastro | De quién es la finca y dónde acaba. **Lo más útil**: el modelo ve la pista, no el candado | +| Red Natura 2000 | Límites oficiales en vivo | +| Espacios protegidos | Figuras nacionales | +| Otras localizaciones | El resto de puntos, para ver alternativas cerca | + +## Filtros + +Estrellas mínimas, región, y mínimos por criterio (sonido, agua, arbolado, +acceso). La lista y el mapa se actualizan a la vez. + +## La ficha + +Al pulsar una localización: + +- **Vista aérea** — foto real del sitio, 640 m de lado. Púlsala para ir al mapa. +- **Relieve 3D** — el terreno 9,6 km alrededor. Arrastra para girarlo y ver si + estás en una hondonada. +- **Los seis criterios** con su puntuación. +- **Los datos**: dB que llegan a la casa más cercana, distancias, arbolado, + pendiente, temperaturas. +- **Aviso** del espacio protegido más cercano. + +## Los enlaces de abajo + +Bajo el mapa, para la localización elegida: Satélite, Street View, Mapillary, +OSM, Catastro y Cómo llegar. + +## Idioma + +Selector arriba a la izquierda: castellano, inglés, francés y portugués. Se +recuerda entre visitas. Los nombres de municipio no se traducen, vienen del país. diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 0000000..0810a4e --- /dev/null +++ b/install.ps1 @@ -0,0 +1,57 @@ +# Instalador para Windows (PowerShell). +# +# Solo hace falta para RECONSTRUIR los datos. Para USAR el visor no se instala +# nada: se abre out\visor.html haciendo doble clic. +# +# Si Windows bloquea el script: +# Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass +# .\install.ps1 + +$ErrorActionPreference = "Stop" +Set-Location -Path $PSScriptRoot + +Write-Host "== RAVE SCOUT / EXPLORER ==" -ForegroundColor Cyan + +# --- python --- +$py = $null +foreach ($c in @("py -3", "python", "python3")) { + try { + $exe, $arg = $c.Split(" ", 2) + $v = & $exe $arg -c "import sys;print(sys.version_info>=(3,9))" 2>$null + if ($v -eq "True") { $py = $c; break } + } catch { } +} +if (-not $py) { + Write-Host "Falta Python 3.9 o superior." -ForegroundColor Red + Write-Host " Descarga: https://www.python.org/downloads/" + Write-Host " O bien: winget install Python.Python.3.12" + Write-Host " Marca 'Add Python to PATH' durante la instalacion." + exit 1 +} +$exe, $arg = $py.Split(" ", 2) +Write-Host ("Python: " + (& $exe $arg -V)) + +# --- entorno --- +if (-not (Test-Path ".venv")) { & $exe $arg -m venv .venv } +& ".\.venv\Scripts\python.exe" -m pip -q install --upgrade pip +Write-Host "Instalando dependencias..." +& ".\.venv\Scripts\python.exe" -m pip -q install -r requirements.txt + +# --- comprobacion --- +$chk = @" +import importlib, sys +faltan = [m for m in ('numpy','scipy','rasterio','osmium','shapely','pyproj', + 'requests','PIL') if not importlib.util.find_spec(m)] +if faltan: + print('FALTAN: ' + ', '.join(faltan)); sys.exit(1) +print('Dependencias correctas.') +"@ +$chk | & ".\.venv\Scripts\python.exe" - +if ($LASTEXITCODE -ne 0) { exit 1 } + +Write-Host "" +Write-Host "Listo." -ForegroundColor Green +Write-Host " Abrir el visor: .\abrir.ps1" +Write-Host " Ver paises: .\.venv\Scripts\python.exe src\regions.py" +Write-Host " Reconstruir Espana: .\.venv\Scripts\python.exe src\stars.py" +Write-Host " Otro pais: `$env:REGION='pt'; .\.venv\Scripts\python.exe src\stars.py" diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..a741fa4 --- /dev/null +++ b/install.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Instalador para Linux y macOS. +# +# Solo hace falta para RECONSTRUIR los datos. Para USAR el visor no se instala +# nada: se abre out/visor.html en cualquier navegador. +set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")" + +echo "== RAVE SCOUT / EXPLORER ==" + +# --- python --- +PY="" +for c in python3.12 python3.11 python3.10 python3; do + if command -v "$c" >/dev/null 2>&1; then + v=$("$c" -c 'import sys;print(sys.version_info>=(3,9))' 2>/dev/null || echo False) + [ "$v" = "True" ] && { PY="$c"; break; } + fi +done +[ -n "$PY" ] || { echo "Falta Python 3.9 o superior."; echo \ + " Debian/Ubuntu: sudo apt install python3 python3-venv"; \ + echo " Fedora: sudo dnf install python3"; \ + echo " macOS: brew install python"; exit 1; } +echo "Python: $($PY -V)" + +if ! "$PY" -c "import venv" >/dev/null 2>&1; then + echo "Falta el módulo venv. En Debian/Ubuntu: sudo apt install python3-venv"; exit 1 +fi + +# --- entorno --- +[ -d .venv ] || "$PY" -m venv .venv +./.venv/bin/pip -q install --upgrade pip +echo "Instalando dependencias…" +./.venv/bin/pip -q install -r requirements.txt + +# --- comprobación --- +./.venv/bin/python - <<'EOF' +import importlib, sys +faltan = [m for m in ("numpy","scipy","rasterio","osmium","shapely","pyproj", + "requests","PIL") if not importlib.util.find_spec(m)] +if faltan: + print("FALTAN:", ", ".join(faltan)); sys.exit(1) +print("Dependencias correctas.") +EOF + +echo +echo "Listo." +echo " Abrir el visor: ./abrir.sh" +echo " Ver países: ./.venv/bin/python src/regions.py" +echo " Reconstruir España: make all" +echo " Otro país: REGION=pt make all" diff --git a/out/informe.html b/out/informe.html index 30a044d..4966bea 100644 --- a/out/informe.html +++ b/out/informe.html @@ -278,7 +278,7 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp #valorcoordenadasmunicipiocomunidad dBcasa kmpista magua kmárbol % - prot kmMadrid kmver + prot kmver @@ -293,7 +293,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,1 33 3,1 -174.9 sat sv @@ -309,7 +308,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,1 65 4,6 -156.5 sat sv @@ -325,7 +323,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,3 61 1,1 -168.6 sat sv @@ -341,7 +338,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,8 59 1,7 -198.0 sat sv @@ -357,7 +353,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,4 51 3,9 -190.0 sat sv @@ -373,7 +368,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,7 83 0,4 -122.1 sat sv @@ -389,7 +383,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 4,6 74 3,2 -168.6 sat sv @@ -405,7 +398,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 4,1 68 0,5 -254.0 sat sv @@ -421,7 +413,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,1 17 4,7 -388.2 sat sv @@ -437,7 +428,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,2 31 0,4 -139.5 sat sv @@ -453,7 +443,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,8 70 12,9 -119.9 sat sv @@ -469,7 +458,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,8 73 3,0 -209.9 sat sv @@ -485,7 +473,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,7 0 0,3 -264.9 sat sv @@ -501,7 +488,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,4 62 3,1 -158.3 sat sv @@ -517,7 +503,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,4 52 3,3 -149.3 sat sv @@ -533,7 +518,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,1 32 1,5 -309.5 sat sv @@ -549,7 +533,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,1 69 0,7 -138.5 sat sv @@ -565,7 +548,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,1 35 1,7 -87.8 sat sv @@ -581,7 +563,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,1 88 2,7 -267.6 sat sv @@ -597,7 +578,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,6 70 3,4 -153.6 sat sv @@ -613,7 +593,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,6 12 6,0 -180.5 sat sv @@ -629,7 +608,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,7 20 1,5 -272.5 sat sv @@ -645,7 +623,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,1 100 1,0 -211.3 sat sv @@ -661,7 +638,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 3,1 63 4,5 -228.3 sat sv @@ -677,7 +653,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 4,4 45 4,0 -191.5 sat sv @@ -693,7 +668,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,8 30 3,6 -185.2 sat sv @@ -709,7 +683,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,1 71 0,8 -129.2 sat sv @@ -725,7 +698,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,0 64 2,9 -319.1 sat sv @@ -741,7 +713,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,3 85 8,1 -164.7 sat sv @@ -757,7 +728,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 5,7 92 0,9 -165.4 sat sv @@ -773,7 +743,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,5 80 1,1 -220.3 sat sv @@ -789,7 +758,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,2 65 4,3 -266.0 sat sv @@ -805,7 +773,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 3,4 69 6,5 -316.8 sat sv @@ -821,7 +788,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 3,3 36 1,0 -303.0 sat sv @@ -837,7 +803,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 1,2 90 13,7 -295.9 sat sv @@ -853,7 +818,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,9 0 0,6 -403.2 sat sv @@ -869,7 +833,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,3 0 7,0 -378.9 sat sv @@ -885,7 +848,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 5,9 73 1,9 -346.1 sat sv @@ -901,7 +863,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,8 10 2,1 -141.0 sat sv @@ -917,7 +878,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,5 27 6,3 -333.9 sat sv @@ -933,7 +893,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,1 47 0,8 -121.5 sat sv @@ -949,7 +908,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,1 87 1,0 -280.4 sat sv @@ -965,7 +923,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,4 51 0,5 -231.4 sat sv @@ -981,7 +938,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 0,2 61 2,7 -350.0 sat sv @@ -997,7 +953,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 4,5 35 2,5 -147.0 sat sv @@ -1013,7 +968,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,1 74 1,2 -176.9 sat sv @@ -1029,7 +983,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp 2,3 66 0,7 -153.2 sat sv @@ -1059,8 +1012,6 @@ code.inline { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monosp riesgo extremo de incendio de junio a septiembre, con restricciones de acceso y responsabilidad penal si algo prende. El mismo punto no es el mismo sitio en marzo que en agosto. -
  • Distancia en línea recta, no tiempo de coche

    La columna de Madrid es - euclídea. Por carretera y luego pista, el tiempo real puede ser el doble.
  • diff --git a/out/visor.html b/out/visor.html index 4504944..188dc2f 100644 --- a/out/visor.html +++ b/out/visor.html @@ -686,7 +686,10 @@ button,input,select{font:inherit;color:inherit} border-right:1px solid var(--line);display:flex;flex-direction:column;height:100vh} #side header{padding:16px 18px 12px;border-bottom:1px solid var(--line)} #side h1{margin:0;font-size:15px;letter-spacing:.02em} -#side .sub{color:var(--muted);font-size:11.5px;margin-top:3px} +#side .sub{color:var(--muted);font-size:11.5px;margin-top:3px;display:flex; + justify-content:space-between;align-items:center;gap:8px} +#lang{width:auto;padding:2px 5px;font-size:11px;background:var(--panel2); + border:1px solid var(--line);border-radius:4px;color:var(--ink2);cursor:pointer} .filters{padding:14px 18px;display:flex;flex-direction:column;gap:13px; border-bottom:1px solid var(--line)} .f-row{display:flex;flex-direction:column;gap:6px} @@ -726,12 +729,30 @@ select{width:100%;background:var(--panel2);border:1px solid var(--line); border-radius:5px;padding:6px 11px;font-size:12px;cursor:pointer;color:var(--ink2)} .toolbar button[aria-pressed=true]{background:var(--accent);border-color:var(--accent); color:#08131f;font-weight:600} -.legend{position:absolute;bottom:12px;left:12px;background:rgba(21,26,33,.93); - border:1px solid var(--line);border-radius:5px;padding:9px 12px;font-size:11px; - display:flex;gap:13px;align-items:center;z-index:5} +/* Barra inferior: leyenda a la izquierda y, a su derecha, los enlaces a mapas + externos de la localización elegida. Antes había que abrir la ficha y bajar + hasta el final para encontrarlos. */ +.bottombar{position:absolute;bottom:12px;left:12px;right:12px;z-index:5; + display:flex;gap:10px;align-items:center;justify-content:space-between; + flex-wrap:wrap;pointer-events:none} +.bottombar>*{pointer-events:auto} +.legend{background:rgba(21,26,33,.93);border:1px solid var(--line); + border-radius:5px;padding:9px 12px;font-size:11px;display:flex;gap:13px; + align-items:center} .legend i{width:9px;height:9px;border-radius:50%;display:inline-block;margin-right:4px} -.hint{position:absolute;bottom:12px;right:12px;color:var(--muted);font-size:11px; - background:rgba(21,26,33,.9);padding:5px 9px;border-radius:4px;z-index:5} +.maplinks{background:rgba(21,26,33,.93);border:1px solid var(--line); + border-radius:5px;padding:5px 6px;display:flex;gap:4px;align-items:center; + flex-wrap:wrap;max-width:100%} +.maplinks a{display:flex;flex-direction:column;gap:0;padding:5px 9px; + border-radius:4px;text-decoration:none;line-height:1.2;white-space:nowrap} +.maplinks a b{color:var(--accent);font-size:11.5px;font-weight:600} +.maplinks a span{color:var(--muted);font-size:9.5px} +.maplinks a:hover{background:#1d2836} +.ml-sel{padding:5px 10px 5px 6px;border-right:1px solid var(--line); + margin-right:3px;font-size:11px;color:var(--ink2);white-space:nowrap; + display:flex;flex-direction:column;line-height:1.25} +.ml-sel b{font-size:11.5px;color:var(--ink)} +.ml-hint{color:var(--muted);font-size:11px;padding:6px 9px} /* ---------- mapa de cerca (Leaflet) ---------- */ #leaf{position:absolute;top:0;left:0;width:100%;height:100%;display:none; @@ -739,6 +760,8 @@ select{width:100%;background:var(--panel2);border:1px solid var(--line); body.cerca #map{display:none} body.cerca #leaf{display:block} body.cerca .legend{display:none} +/* los conmutadores del relieve solo tienen sentido en su propio modo */ +body.cerca .only-esp{display:none} #layers{position:absolute;top:52px;left:12px;z-index:6;width:186px; background:rgba(21,26,33,.95);border:1px solid var(--line);border-radius:6px; padding:9px 11px;display:none;flex-direction:column;gap:8px;font-size:12px} @@ -776,6 +799,7 @@ body.cerca.sinsel #nosel{display:block} .d-head h2{margin:0 26px 0 0;font-size:16px} .d-head .loc{color:var(--muted);font-size:12px;margin-top:2px} .d-head .stars{font-size:16px;margin-top:7px;letter-spacing:1px} +.d-head .btn-cerca{margin-top:11px} #close{position:absolute;top:13px;right:14px;background:none;border:0; color:var(--muted);font-size:20px;cursor:pointer;line-height:1;padding:2px 5px} #close:hover{color:var(--ink)} @@ -854,13 +878,16 @@ body.cerca.sinsel #nosel{display:block}