commit d4b3b803e04c7dca348e451f071deee18a976795 Author: sito Date: Sat Aug 8 20:29:27 2026 +0200 RAVE SCOUT: análisis de aislamiento de España y visor de localizaciones Barrido de los 498.528 km² de la península y Baleares sobre malla de 100 m (EPSG:3035) para localizar sitios alejados de todo con acceso rodado. - 12,5 M de edificios y 1,4 M de km de viales de OSM, incluidos los países vecinos para no inflar el aislamiento en la franja fronteriza - Espacios protegidos oficiales (Red Natura 2000 + designación nacional) vía la Agencia Europea de Medio Ambiente: cubren el 29 % del país frente al 15 % que tenía OSM - Modelo acústico con propagación real; es el filtro que de verdad corta, y deja 505 km² en toda España - 355 localizaciones valoradas de 1 a 5 estrellas en seis criterios - Visor autónomo con relieve, ortofoto PNOA por localización, relieve 3D y mapa deslizante con satélite, catastro y espacios protegidos diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..111ad14 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Datos de partida y productos intermedios: 22 GB, todo reconstruible con +# `make datos`. Nunca al repositorio. +data/ + +# Entorno de Python +.venv/ +__pycache__/ +*.pyc + +# Salidas pesadas que se regeneran y no hacen falta para publicar el visor +out/score_rave.tif +out/relieve.png +out/protegidos.png +out/aislamiento_ov.png + +# Restos de trabajo +*.log +.DS_Store + +# sondeos temporales de las APIs +*.json +!out/stats.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..aca5a01 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,21 @@ +# Publica el visor en GitLab Pages. +# +# No hay build: el visor y el informe ya son HTML estático. El trabajo consiste +# en copiar out/ a public/, que es la carpeta que GitLab Pages sirve, y poner +# un index que lleve a los dos. +# +# La URL queda en https://.gitlab.io// + +pages: + stage: deploy + image: alpine:latest + script: + - mkdir -p public + - cp -r out/. public/ + - cp web/index.html public/index.html + - ls -la public | head -20 + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..14fac91 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f85980e --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +# Pipeline completo. Cada paso deja su resultado en data/interim o en out/, +# así que se puede reejecutar solo la parte que interese. +PY := ./.venv/bin/python + +.PHONY: all datos rasters modelo salidas revisar limpio + +all: datos rasters modelo salidas revisar + +## --- descargas (~2,5 GB, una vez) --- +datos: + bash src/dl_dem.sh + $(PY) src/dl_protected.py + @echo "OSM: descarga los .pbf de Geofabrik en data/raw/ (ver README)" + +## --- extracción y rásters --- +rasters: + $(PY) src/extract_osm.py + $(PY) src/extract_areas.py + $(PY) src/extract_extra.py + $(PY) src/dem_terrain.py + $(PY) src/build_rasters.py + $(PY) src/build_protected.py + $(PY) src/build_rasters2.py + +## --- modelo de estrellas --- +modelo: + $(PY) src/stars.py + +## --- visor, informe y mapas --- +salidas: + $(PY) src/render_relief.py + $(PY) src/render_map.py + $(PY) -c "from PIL import Image; \ + [Image.open(f'out/{n}.png').save(f'out/{n}.webp','WEBP',quality=q,method=6) \ + for n,q in (('relieve',88),('protegidos',80),('aislamiento_ov',80))]" + $(PY) src/dl_orthos.py + rm -rf out/ortho && cp -r data/interim/ortho out/ortho + $(PY) src/build_viewer.py + $(PY) src/report.py + +## --- comprobación de coherencia del resultado --- +revisar: + $(PY) src/revisar.py + +limpio: + rm -rf out/*.html out/*.png out/*.webp out/ortho diff --git a/README.md b/README.md new file mode 100644 index 0000000..e36b989 --- /dev/null +++ b/README.md @@ -0,0 +1,115 @@ +# RAVE SCOUT + +Barrido de los 498.528 km² de la España peninsular y balear, celda a celda sobre +una malla de 100 m, buscando los puntos más alejados de cualquier rastro de gente +a los que **además** se pueda llegar con un coche cargado. + +Salen **355 localizaciones**, valoradas de 1 a 5 estrellas, con un visor para +explorarlas sobre relieve, ortofoto y catastro. + +![vista del visor](docs/visor.png) + +## 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: + +```bash +xdg-open out/visor.html # Linux +start out\visor.html # Windows +open out/visor.html # macOS +``` + +No hace falta servidor ni conexión. Ojo: `visor.html` necesita la carpeta +`out/ortho/` al lado, que es donde están las fotos aéreas. + +## Qué hay dentro + +| | | +|---|---| +| `out/visor.html` | El visor: mapa de España con relieve, filtros, ficha por localización con foto aérea y relieve 3D, y mapa deslizante con satélite, topográfico, catastro y espacios protegidos | +| `out/informe.html` | El método explicado, los mapas y la tabla de las mejores | +| `out/candidatos_estrellas.csv` | Las 355 con todos los campos, para abrir en LibreOffice o QGIS | +| `out/ortho/` | Ortofoto PNOA de cada localización, 640 m de lado | +| `out/stats.json` | Cifras del modelo, que leen el informe y el visor | +| `src/` | El pipeline entero, reproducible | + +## Cómo se calcula + +Sobre una malla de 100 m en proyección equiárea (EPSG:3035) se vuelcan 12,5 +millones de edificios y 1,4 millones de kilómetros de viales de OpenStreetMap, y +para cada celda se calcula por transformada de distancia a qué distancia real +está lo más cercano de cada tipo. + +**Primero se descarta.** Una celda queda fuera si está en espacio protegido +oficial o a menos de 300 m de su borde, en zona militar, en agua, a menos de +2 km de un edificio, a menos de 400 m de una carretera, a más de 800 m de un +vial por el que meter un coche, o con más de 10° de pendiente. + +**Después manda el sonido.** Se estima el nivel que llega a la casa más cercana +con propagación real —divergencia esférica, absorción atmosférica en frecuencias +bajas, apantallamiento del relieve y absorción del arbolado, desde 130 dB a 1 m— y +se exige que no pase de 45 dB, el límite nocturno típico en suelo rural. Es el +filtro que de verdad corta: de 47.519 km² que pasan lo anterior, solo **505 km²** +bajan de 45 dB. + +**Lo que sobrevive se puntúa** en seis criterios: sonido (30 %), soledad (22 %), +acceso (18 %), agua (12 %), arbolado (10 %) y clima (8 %). Las estrellas salen de +la media ponderada penalizada por el eslabón más débil, para que un sitio +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 + +Hacen falta ~25 GB de disco y un par de horas. Los datos de partida no están en +el repositorio: se descargan solos. + +```bash +python3 -m venv .venv +./.venv/bin/pip install numpy scipy rasterio osmium shapely pyproj requests pillow +make all +``` + +O paso a paso, ver el `Makefile`. + +## Lo que este análisis no sabe + +- **No ve vallas ni propiedad.** Casi todo el suroeste que domina la lista es + dehesa privada, en fincas cerradas y cotos de caza. Ve la pista que llega; no + ve la cadena y el candado. Por eso el visor lleva la capa de catastro. +- **Los protegidos son de diciembre de 2024.** Son los límites oficiales, pero + las figuras autonómicas cambian y hay ordenanzas municipales que no están en + ningún mapa nacional. +- **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 de lo que dice el número. +- **Ignora la estación.** Buena parte de estos sitios son monte mediterráneo en + riesgo extremo de incendio de junio a septiembre. +- **Distancia en línea recta, no tiempo de coche.** + +## 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 espacios de designación nacional | Agencia Europea de Medio Ambiente / MITECO | reutilización con atribución | +| Ortofotos PNOA | [Instituto Geográfico Nacional](https://www.ign.es) | CC BY 4.0 | +| Cartografía catastral | [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 de este repositorio, bajo licencia MIT (ver `LICENSE`). Los datos +mantienen la licencia de su fuente. + +## 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/abrir.sh b/abrir.sh new file mode 100755 index 0000000..ecba6b2 --- /dev/null +++ b/abrir.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Abre el visor de localizaciones en el navegador por defecto. +# Es un fichero local: no necesita servidor, ni conexión, ni nada de Claude. +# +# ./abrir.sh -> visor interactivo (mapa + 3D + filtros) +# ./abrir.sh informe -> informe con el método y la tabla +set -eu +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +case "${1:-visor}" in + informe|report) F="$DIR/out/informe.html" ;; + *) F="$DIR/out/visor.html" ;; +esac + +[ -f "$F" ] || { echo "No encuentro $F"; exit 1; } + +# Primero un navegador de verdad, y xdg-open solo como último recurso: en este +# equipo text/html está asociado al editor de texto, y xdg-open respeta esa +# asociación MIME en vez del navegador por defecto, así que abría el editor. +for b in firefox-esr firefox chromium chromium-browser google-chrome; do + if command -v "$b" >/dev/null; then exec "$b" "file://$F"; fi +done +if command -v xdg-open >/dev/null && xdg-open "file://$F" 2>/dev/null; then + exit 0 +fi +echo "No he encontrado navegador. Abre a mano: file://$F" +exit 1 diff --git a/docs/visor.png b/docs/visor.png new file mode 100644 index 0000000..62189c9 Binary files /dev/null and b/docs/visor.png differ diff --git a/out/aislamiento_ov.webp b/out/aislamiento_ov.webp new file mode 100644 index 0000000..985944d Binary files /dev/null and b/out/aislamiento_ov.webp differ diff --git a/out/candidatos.csv b/out/candidatos.csv new file mode 100644 index 0000000..e92d51c --- /dev/null +++ b/out/candidatos.csv @@ -0,0 +1,61 @@ +rank,lat,lon,municipio,comunidad,score,d_edificio_m,d_carretera_m,d_acceso_m,d_nucleo_m,d_protegido_m,d_militar_m,cota_m,pendiente_deg,tpi_m,edif_5km,km_madrid +1,38.06287,-5.1757,Villanueva del Rey,Andalucía,0.9962,6576,5423,412,14268,9141,26148,395,0.4,-34.2,0,287.8 +2,38.34667,-3.28549,Santisteban del Puerto,Andalucía,0.9954,5336,5069,100,10580,7357,22934,447,0.5,-109.9,0,232.4 +3,38.55127,-4.52142,Almodóvar del Campo,Castilla-La Mancha,0.9946,5115,5223,447,8213,7269,44969,709,0.5,-50.7,0,217.0 +4,38.43556,-3.68651,Viso del Marqués,Castilla-La Mancha,0.9929,5560,6082,100,11364,8884,15945,963,0.7,-76.1,0,219.2 +5,38.44065,-2.92249,Chiclana de Segura,Andalucía,0.989,5941,6040,100,8050,11200,21982,707,1.1,-67.7,0,229.8 +6,39.24702,-4.99906,Herrera del Duque,Extremadura,0.9872,5333,4925,500,8993,13600,61175,491,1.0,-33.4,0,169.2 +7,39.22167,-6.90758,Alburquerque,Extremadura,0.9826,7278,4738,500,8041,20341,19383,278,0.7,-34.1,0,303.0 +8,39.10688,-6.21413,Montánchez,Extremadura,0.9823,5108,4900,100,8205,2475,17836,302,1.4,-32.9,0,257.8 +9,40.07779,-6.88696,Cilleros,Extremadura,0.976,5240,5936,412,8547,13384,80146,355,0.5,-24.3,0,273.4 +10,37.87092,-7.14462,Rosal de la Frontera,Andalucía,0.9755,8300,5131,565,9140,728,84791,266,2.5,-50.8,0,406.3 +11,38.76912,-5.96255,Don Benito,Extremadura,0.9714,5314,5536,0,7083,26444,40383,431,1.0,-30.3,0,264.3 +12,38.90002,-5.29879,Monterrubio de la Serena,Extremadura,0.9706,6600,6363,282,8354,7843,55276,389,0.3,-21.9,0,215.0 +13,38.23908,-3.77675,Baños de la Encina,Andalucía,0.9687,5536,4785,100,7566,6447,10186,368,1.4,-62.2,0,240.8 +14,37.69508,-7.20742,Paymogo,Andalucía,0.9673,7333,5103,100,7623,17844,69148,137,0.8,-24.9,0,423.8 +15,41.4182,0.22977,Fraga,Aragón,0.9664,5366,5434,583,7518,8309,93148,174,2.4,-71.9,0,349.0 +16,39.05018,-4.89497,Fuenlabrada de los Montes,Extremadura,0.966,6281,4360,0,7840,24298,72713,569,0.5,-32.9,0,181.2 +17,40.59572,-0.89423,Orrios,Aragón,0.965,5162,5872,0,6705,5162,67727,1376,0.9,-38.0,0,239.4 +18,38.20084,-6.14295,Montemolín,Extremadura,0.9648,6381,4924,200,7877,16611,44949,566,2.8,-29.5,0,320.5 +19,39.42412,-4.34442,Retuerta del Bullaque,Castilla-La Mancha,0.9646,5500,4850,0,6789,4964,51145,692,0.5,-33.8,0,122.0 +20,39.46578,-3.74467,Consuegra,Castilla-La Mancha,0.9639,5728,4967,100,6649,22173,24207,728,0.5,-29.2,0,105.3 +21,37.53579,-7.01779,Alosno,Andalucía,0.9628,5385,7892,500,8876,28205,45848,109,1.2,-22.4,0,425.4 +22,38.30851,-6.6121,Jerez de los Caballeros,Extremadura,0.9627,5950,5192,565,6926,9130,65547,290,0.1,-25.6,0,339.7 +23,41.36079,-0.17822,Sástago,Aragón,0.9624,6313,4780,100,11763,12727,66636,292,0.4,-22.6,0,314.5 +24,39.16856,-4.62131,Arroba de los Montes,Castilla-La Mancha,0.9623,4997,5140,0,6795,16277,79107,557,1.4,-54.2,0,157.9 +25,39.35078,-4.18342,Retuerta del Bullaque,Castilla-La Mancha,0.9622,4920,4802,0,7816,6082,49880,825,2.1,-77.7,0,124.3 +26,41.60134,-0.05886,Villanueva de Sigena,Aragón,0.9609,4940,7206,0,7752,39926,66038,391,1.5,-25.5,0,332.7 +27,39.17435,-5.28458,Talarrubias,Extremadura,0.9565,6264,4472,223,8500,4579,38327,482,2.2,-36.2,0,191.6 +28,40.30502,-6.57399,Villasrubias,Castilla y León,0.9529,4808,5189,400,6720,1264,93209,910,0.9,-53.7,0,244.5 +29,38.41832,-5.89163,Valencia de las Torres,Extremadura,0.9522,7023,5440,100,5640,16199,54423,440,0.1,-33.7,0,288.3 +30,39.4711,-4.0508,Los Yébenes,Castilla-La Mancha,0.9482,4981,5675,0,5909,5903,32491,1108,0.9,-30.9,0,108.4 +31,38.42337,-4.17661,Fuencaliente,Castilla-La Mancha,0.9482,4972,5800,583,10057,3935,51072,768,0.6,-16.9,0,223.6 +32,38.90876,-4.74684,Valdemanco del Esteras,Castilla-La Mancha,0.9474,7262,4143,600,7963,39906,60421,581,1.8,-37.6,0,188.1 +33,39.05771,-4.40184,Piedrabuena,Castilla-La Mancha,0.9461,4891,4101,0,12650,25205,57346,694,1.1,-41.2,0,161.0 +34,38.69587,-4.5906,Almodóvar del Campo,Castilla-La Mancha,0.9449,4992,5269,223,8340,20631,48304,605,5.5,-37.2,0,204.0 +35,38.47308,-3.21349,Villamanrique,Castilla-La Mancha,0.9442,4272,7962,0,9213,13333,32224,674,0.6,-61.8,0,219.8 +36,39.27436,-3.84608,Los Yébenes,Castilla-La Mancha,0.9401,7218,4669,282,6441,11067,36293,756,1.6,-48.6,0,126.8 +37,38.73027,-3.60681,Moral de Calatrava,Castilla-La Mancha,0.9391,5780,4295,565,10625,21888,25091,665,0.4,-21.4,0,186.9 +38,38.01888,-6.15112,Monesterio,Extremadura,0.9377,5166,4860,400,6140,5200,35867,699,2.0,-42.0,0,336.5 +39,37.55272,-5.54487,Carmona,Andalucía,0.9369,5813,4801,565,7937,12924,10259,86,0.8,-16.4,0,352.2 +40,40.00496,-3.2808,Villarrubia de Santiago,Castilla-La Mancha,0.9361,6280,4981,100,5470,13336,12778,598,1.3,-43.3,0,58.6 +41,38.06582,-5.51439,Hornachuelos,Andalucía,0.9356,4729,4785,360,12949,500,17101,319,3.7,-46.1,0,301.2 +42,36.41683,-5.82344,Medina Sidonia,Andalucía,0.9333,5126,5188,0,5126,4000,21005,38,0.9,-35.8,0,476.1 +43,37.1289,-5.34582,Morón de la Frontera,Andalucía,0.9249,6307,3590,0,7746,27549,10568,265,1.4,-53.0,0,388.2 +44,39.61766,-1.49845,Mira,Castilla-La Mancha,0.9247,6053,4365,424,5813,8237,62721,862,0.6,-32.0,0,209.8 +45,38.5549,-3.85046,Calzada de Calatrava,Castilla-La Mancha,0.9242,4539,4816,282,6413,15851,35609,530,0.5,-43.8,0,206.2 +46,39.57463,-6.90807,Alcántara,Extremadura,0.9201,4992,5536,360,11795,538,47327,264,1.2,-9.8,0,288.2 +47,39.60971,-6.00399,Trujillo,Extremadura,0.9191,3981,5532,100,9617,17426,34753,320,1.2,-31.5,0,214.8 +48,39.0247,-6.60132,Montijo,Extremadura,0.9187,7632,5215,200,9192,24481,20934,242,0.5,-7.1,0,290.5 +49,41.40568,-0.3686,Velilla de Ebro,Aragón,0.9169,5020,4252,0,6802,18577,50195,249,1.1,-24.5,0,301.0 +50,41.40387,-1.90091,Villalengua,Aragón,0.9157,5028,5797,0,6113,26121,23361,939,1.3,-19.9,0,186.2 +51,39.7348,-3.07369,Villanueva de Alcardete,Castilla-La Mancha,0.9154,5208,5124,0,8683,20208,25941,731,0.4,-5.7,0,93.5 +52,38.61326,-6.02991,Puebla de la Reina,Extremadura,0.9139,5246,3560,412,7184,29044,48662,393,1.2,-32.1,0,280.4 +53,40.25601,-2.91121,Comunidad de Albalate de Zorita e Illana,Castilla-La Mancha,0.9136,4904,4522,100,6741,40231,28313,765,1.7,-24.4,0,70.1 +54,42.68931,-4.73128,Congosto de Valdavia,Castilla y León,0.9134,4838,4827,100,4967,16007,69579,1049,0.8,-40.5,0,267.4 +55,37.44485,-4.96863,Écija,Andalucía,0.9118,7705,4738,0,7002,9481,15469,156,0.6,-14.5,0,344.6 +56,38.19032,-4.62323,Adamuz,Andalucía,0.9117,4883,4110,565,14651,18703,16773,595,1.9,-22.3,0,257.5 +57,41.67708,-2.96155,Talveila,Castilla y León,0.9102,4833,4726,0,5154,8402,12756,1014,0.8,-29.2,0,152.2 +58,39.94034,-3.08974,Fuente de Pedro Naharro,Castilla-La Mancha,0.9096,5124,5099,0,6977,30910,18767,774,0.2,-9.5,0,75.0 +59,39.94011,-6.74119,Moraleja,Extremadura,0.909,6685,3466,424,6800,15065,60360,227,0.6,-47.4,0,263.7 +60,39.09732,-4.22891,Piedrabuena,Castilla-La Mancha,0.9076,7002,3482,565,7202,22830,44201,566,0.8,-27.8,0,152.1 diff --git a/out/candidatos_estrellas.csv b/out/candidatos_estrellas.csv new file mode 100644 index 0000000..b425e84 --- /dev/null +++ b/out/candidatos_estrellas.csv @@ -0,0 +1,356 @@ +id,lat,lon,estrellas,score,municipio,comunidad,c_sonido,c_soledad,c_agua,c_arbolado,c_acceso,c_clima,db_en_casa,d_edificio,d_carretera,d_pista,d_agua,d_protegido,protegido_cerca,km_protegido,arbolado_pct,roca_pct,pendiente,tpi,cota,tmax,tmin,km_madrid,row,col +1,39.36483,-5.24824,5,79.5,Alía,Extremadura,63,100,83,100,95,83,36.6,6449,6543,0,100,3080,Puerto Peña - los Golondrinos (Natura 2000),3.1,33,0,3.0,-48.5,432,32.8,2.6,174.9,5753,4543 +2,39.28557,-4.82193,5,78.6,Helechosa de los Montes,Extremadura,75,76,63,99,91,88,34.6,6500,1769,100,2059,4579,Estena (Natura 2000),4.6,65,0,2.1,-123.7,539,32.0,2.2,156.5,5912,4889 +3,39.02884,-4.54425,5,76.3,Puebla de Don Rodrigo,Castilla-La Mancha,66,79,88,100,86,84,36.1,5846,1204,0,1280,1063,Ríos de la cuenca media del Guadiana y laderas vertientes (Natura 2000),1.1,61,0,6.4,-47.7,654,32.6,1.3,168.6,6238,5076 +4,38.69868,-4.38489,5,75.5,Brazatortas,Castilla-La Mancha,69,69,71,100,94,80,35.6,6815,1664,0,1788,1700,"Ríos Quejigal, Valdeazogues y Alcudia (Natura 2000)",1.7,59,0,3.6,-43.1,744,33.4,0.9,198.0,6624,5150 +5,39.4601,-5.5653,5,74.4,Berzocana,Extremadura,72,71,100,100,64,88,35.1,6931,400,500,400,3894, (Designación nacional),3.9,51,0,1.9,-46.2,527,32.0,2.4,190.0,5594,4294 +6,39.35756,-4.12491,5,74.1,Retuerta del Bullaque,Castilla-La Mancha,76,84,41,47,84,96,34.3,6040,2469,0,2729,360,Montes de Toledo (Natura 2000),0.4,83,0,7.1,-49.2,974,30.7,0.0,122.1,5948,5495 +7,39.18565,-4.88608,5,74.1,Fuenlabrada de los Montes,Extremadura,82,95,0,76,75,90,33.2,6963,4172,282,4561,3206,Puerto Peña - los Golondrinos (Natura 2000),3.2,74,0,3.3,-48.5,688,31.7,1.5,168.6,6010,4815 +8,38.79136,-5.82379,5,73.6,Quintana de la Serena,Extremadura,67,100,0,92,100,81,35.9,6087,4459,0,4140,500, (Designación nacional),0.5,68,0,1.6,-45.0,500,33.1,2.5,254.0,6275,3937 +9,37.12819,-5.34452,5,72.9,Morón de la Frontera,Andalucía,57,94,83,87,83,84,37.7,6412,3667,0,100,4716, (Designación nacional),4.7,17,0,7.3,-47.7,273,32.7,4.9,388.2,8168,4017 +10,39.31406,-4.51529,5,72.4,Alcoba,Castilla-La Mancha,58,90,90,100,68,86,37.6,6612,3383,200,1216,447,Montes de Toledo (Natura 2000),0.4,31,0,7.6,-43.5,660,32.3,1.5,139.5,5932,5155 +11,40.06814,-2.3802,5,72.1,Abia de la Obispalía,Castilla-La Mancha,64,64,70,85,93,98,36.5,5385,1726,0,1824,12920,,12.9,70,0,4.0,-66.0,1031,30.3,-0.5,119.9,5433,7098 +12,41.63594,-1.77527,4,70.8,Aranda de Moncayo,Aragón,61,80,70,78,79,95,37.0,5069,3701,0,1824,3026, (Designación nacional),3.1,73,0,8.5,-58.8,1075,28.2,-1.9,209.9,3797,7857 +13,39.94037,-6.75562,4,69.2,Moraleja,Extremadura,64,85,73,0,94,92,36.5,7789,2302,0,1746,316,Canchos de Ramiro y Ladronera (Natura 2000),0.3,0,0,3.5,-47.1,303,31.3,4.7,264.9,4855,3396 +14,40.44825,-5.5637,4,69.1,El Tejado,Castilla y León,54,57,83,100,96,100,38.3,4800,1910,0,1442,3080,Riberas del Río Tormes y afluentes (Natura 2000),3.1,62,0,2.7,-50.2,1016,28.3,-0.4,158.3,4519,4499 +15,39.1442,-4.30836,4,68.8,Piedrabuena,Castilla-La Mancha,65,66,51,100,71,84,36.2,6040,1100,100,2404,3312,Ríos de la cuenca media del Guadiana y laderas vertientes (Natura 2000),3.3,52,0,8.9,-58.6,670,32.7,1.3,149.3,6151,5299 +16,37.67526,-3.03813,4,68.6,Huesa,Andalucía,52,81,83,100,83,71,38.6,5456,3228,141,100,1486, (Designación nacional),1.5,32,0,4.0,-48.0,662,34.8,1.7,309.5,7952,6135 +17,39.41756,-4.69774,4,68.1,Helechosa de los Montes,Extremadura,60,64,60,87,89,86,37.2,6135,1442,141,2140,707,Estena (Natura 2000),0.8,69,0,2.0,-39.1,592,32.4,1.7,138.5,5789,5020 +18,40.29354,-2.68815,4,67.5,Huete,Castilla-La Mancha,59,69,83,100,60,84,37.4,6412,400,583,100,1746,Estepas yesosas de La Alcarria conquense (Natura 2000),1.8,35,0,0.4,-44.6,707,32.7,0.5,87.8,5143,6876 +19,42.69107,-4.73172,4,67.4,Congosto de Valdavia,Castilla y León,54,81,83,33,93,99,38.3,5028,4701,100,100,2668,Riberas del Río Pisuerga y afluentes (Natura 2000),2.7,88,0,1.5,-39.4,1052,25.2,-1.2,267.6,2213,5639 +20,39.15173,-4.46609,4,67.1,Piedrabuena,Castilla-La Mancha,60,81,45,85,71,88,37.2,5099,3264,424,2617,3420,Bonales de la comarca de Los Montes del Guadiana (Natura 2000),3.4,70,0,0.5,-47.5,738,32.0,1.2,153.6,6117,5166 +21,38.98983,-4.75109,4,67.0,Agudo,Castilla-La Mancha,55,89,44,62,93,81,38.0,6352,2469,100,2640,5968,Ríos de la cuenca media del Guadiana y laderas vertientes (Natura 2000),6.0,12,0,1.0,-48.9,610,33.1,1.7,180.5,6246,4892 +22,40.06355,-6.87354,4,66.9,Cilleros,Extremadura,59,96,100,99,40,97,37.3,6768,5360,1077,670,1500, (Designación nacional),1.5,20,0,3.1,-45.9,326,30.5,4.5,272.5,4699,3325 +23,39.75725,-1.39878,4,66.6,Garaballa,Castilla-La Mancha,62,66,95,0,90,96,36.9,4455,2334,0,1063,989,"Hoces del Cabriel, Guadazaón y ojos de Moya (Natura 2000)",1.1,100,0,5.0,-47.6,963,30.6,-1.0,211.3,5905,7881 +24,41.74254,-1.60532,4,66.6,Ambel,Aragón,54,68,28,100,100,100,38.2,4788,2102,0,3140,4501,Sierra de Nava Alta - Puerto de la Chabola (Natura 2000),4.5,63,0,1.4,-49.4,680,29.5,0.2,228.3,3702,8014 +25,39.17383,-5.2821,4,66.5,Talarrubias,Extremadura,59,99,0,100,70,88,37.4,6395,4294,424,4418,4036, (Designación nacional),4.1,45,0,1.7,-42.3,478,32.0,2.5,191.5,5955,4476 +26,38.84744,-4.48759,4,66.2,Abenójar,Castilla-La Mancha,47,87,72,100,78,82,39.5,8780,4031,316,1772,3600,Sierra de Los Canalizos (Natura 2000),3.6,30,0,1.4,-21.9,672,33.0,1.2,185.2,6445,5090 +27,39.63124,-4.83664,4,66.2,Torrecilla de la Jara,Castilla-La Mancha,57,59,83,83,72,99,37.7,4884,1500,200,100,806,Montes de Toledo (Natura 2000),0.9,71,0,6.4,-47.2,811,30.2,1.1,129.2,5533,4944 +28,38.2372,-6.17273,4,66.0,Fuente de Cantos,Extremadura,38,84,97,100,100,74,41.2,7158,2408,0,1000,2941, (Designación nacional),3.0,64,0,0.9,-17.0,557,34.4,2.3,319.1,6813,3523 +29,39.82924,-1.94614,4,65.9,Monteagudo de las Salinas,Castilla-La Mancha,59,69,54,43,93,96,37.3,4609,2900,100,2325,8100,"Hoces del Cabriel, Guadazaón y ojos de Moya (Natura 2000)",8.1,85,0,1.5,-49.4,992,30.7,-1.0,164.7,5754,7427 +30,39.26777,-4.9603,4,65.2,Herrera del Duque,Extremadura,74,83,0,24,72,88,34.6,6080,2607,360,5714,894,Puerto Peña - los Golondrinos (Natura 2000),0.9,92,0,2.6,-44.5,606,32.0,1.9,165.4,5908,4768 +31,41.25428,-1.32647,4,65.0,Codos,Aragón,61,55,100,57,70,98,37.1,4838,447,100,500,1118,Sierra Vicort (Natura 2000),1.2,80,0,9.4,-58.5,943,29.4,-1.3,220.3,4273,8169 +32,38.58789,-5.74676,4,64.3,Zalamea de la Serena,Extremadura,41,71,100,99,88,77,40.6,3800,3522,0,200,4272,Río Guadamez (Natura 2000),4.3,65,0,5.5,-51.9,474,33.9,2.6,266.0,6510,3961 +33,38.17857,-6.04108,4,64.2,Trasierra,Extremadura,61,67,19,90,84,75,37.1,5467,1749,100,3400,6500, (Designación nacional),6.5,69,0,4.6,-45.2,496,34.2,2.8,316.8,6901,3624 +34,39.21502,-6.90347,4,64.0,Alburquerque,Extremadura,43,98,23,100,98,80,40.3,7684,4360,0,3301,1000, (Designación nacional),1.0,36,0,2.1,-23.9,265,33.3,4.5,303.0,5614,3111 +35,42.77336,-5.32078,4,64.0,Vegaquemada,Castilla y León,60,50,91,28,100,100,37.2,4875,1019,0,1166,13679,,13.7,90,0,1.6,-44.6,938,25.2,-0.5,295.9,2027,5183 +36,37.8542,-7.07419,4,63.8,Aroche,Andalucía,61,83,99,0,56,96,37.0,7340,1513,500,921,583,Sierra Pelada y Rivera del Aserrador (Natura 2000),0.6,0,0,4.7,-59.0,472,30.6,4.2,403.2,7056,2665 +37,37.15841,-5.13089,4,63.7,Osuna,Andalucía,69,68,55,0,76,78,35.6,8246,400,300,2302,6977, (Designación nacional),7.0,0,0,2.6,-48.1,346,33.6,4.5,378.9,8173,4210 +38,41.31303,0.23323,4,63.5,Mequinenza / Mequinensa,Aragón,57,77,0,78,93,87,37.7,5124,1900,100,5948,1897,Matarraña - Aiguabarreix (Natura 2000),1.9,73,0,1.6,-44.8,328,32.2,1.8,346.1,4391,9475 +39,39.523,-4.89553,4,63.2,Sevilleja de la Jara,Castilla-La Mancha,59,61,100,50,57,90,37.4,6841,412,632,806,2137,Ríos de la cuenca media del Guadiana y laderas vertientes (Natura 2000),2.2,10,0,1.3,-47.5,594,31.6,1.9,141.0,5641,4873 +40,38.04833,-6.15128,4,63.2,Monesterio,Extremadura,45,86,100,100,48,76,39.9,5913,2954,640,500,6315, (Designación nacional),6.3,27,0,4.3,-40.4,502,34.0,3.1,333.9,7022,3502 +41,39.33611,-3.9768,4,63.0,Los Cortijos,Castilla-La Mancha,45,62,83,100,82,87,39.9,4738,1252,100,100,761,Montes de Toledo (Natura 2000),0.8,47,0,5.1,-45.5,847,32.2,0.5,121.5,5995,5617 +42,40.22638,-6.99076,4,62.9,Valverde del Fresno,Extremadura,47,67,83,38,96,100,39.5,3733,5032,0,100,1000,Serra da Malcata (Natura 2000),1.0,87,0,2.8,-66.9,568,27.7,4.0,280.4,4500,3264 +43,40.46339,-0.98534,4,62.9,Peralejos,Aragón,48,69,51,100,84,85,39.3,4640,4404,141,2408,500,Castelfrío - Mas de Tarín (Natura 2000),0.5,51,0,3.8,-51.3,1156,30.2,-3.5,231.4,5183,8336 +44,37.42111,-2.46481,4,62.8,Lúcar,Andalucía,40,64,100,100,88,85,40.9,3785,3269,100,223,2716, (Designación nacional),2.7,61,0,3.4,-51.7,1080,32.4,-0.3,350.0,8314,6596 +45,39.10465,-3.99852,4,62.8,Miguelturra,Castilla-La Mancha,50,80,0,100,99,82,39.0,5200,3176,0,4539,2529,Sierra de Picón (Natura 2000),2.6,35,0,1.7,-56.4,711,33.0,1.1,147.0,6244,5556 +46,39.99736,-1.71303,4,62.5,Cañete,Castilla-La Mancha,49,61,61,73,93,94,39.2,4455,3047,100,2102,1216,Sabinares de Campillos - Sierra y Valdemorillo de la Sierra (Natura 2000),1.3,74,0,0.7,-44.5,1122,30.2,-1.8,176.9,5601,7651 +47,41.68438,-2.95459,4,62.3,Talveila,Castilla y León,37,81,54,97,100,95,41.3,5024,4176,0,2319,728,Sabinares Sierra de Cabrejas (Natura 2000),0.8,66,0,1.1,-32.7,1027,27.4,-1.8,153.2,3583,6894 +48,42.84939,-4.94376,3,61.8,Valderrueda,Castilla y León,60,46,100,40,85,100,37.2,4617,707,0,721,2886,Picos de Europa en Castilla y León (Natura 2000),2.9,86,0,6.7,-75.5,1104,24.2,-1.0,290.3,2006,5501 +49,39.72496,-1.74565,3,61.5,Paracuellos,Castilla-La Mancha,45,59,100,54,88,91,39.9,3705,2900,100,200,640,"Hoces del Cabriel, Guadazaón y ojos de Moya (Natura 2000)",0.6,81,0,3.3,-59.9,855,31.4,-0.2,185.3,5895,7581 +50,38.12205,-6.77491,3,61.3,Higuera la Real,Extremadura,43,85,96,0,97,82,40.2,5632,3453,0,1019,1004, (Designación nacional),1.0,0,0,2.4,-48.1,402,33.1,3.8,364.1,6824,2981 +51,42.72977,-6.01695,3,61.2,Valdesamario,Castilla y León,49,43,100,100,76,96,39.2,4527,400,100,538,4838,Omaña (Natura 2000),4.9,56,0,7.3,-49.2,1174,24.5,-1.8,323.5,1956,4616 +52,42.74203,-5.57202,3,60.9,Garrafe de Torío,Castilla y León,46,57,83,38,100,100,39.8,3640,2912,0,100,4924,Riberas del Río Esla y afluentes (Natura 2000),4.9,87,0,1.5,-47.9,988,26.0,-0.8,303.4,2019,4975 +53,37.64697,-2.87042,3,60.8,Cuevas del Campo,Andalucía,53,67,100,0,84,70,38.4,6530,1700,100,223,11011,Río Guadiana Menor - Tramo Superior (Natura 2000),11.0,0,0,4.6,-94.5,688,35.0,1.7,315.9,8008,6277 +54,42.47124,-7.01416,3,60.7,Vilamartín de Valdeorras,Galicia,36,63,83,100,88,100,41.4,3640,3612,0,100,6010,Ancares - Courel (Natura 2000),6.0,59,0,5.4,-163.9,731,26.5,1.6,361.5,2058,3759 +55,41.18585,-6.49738,3,60.7,La Peña,Castilla y León,43,41,99,97,86,100,40.2,4031,538,100,921,538, (Designación nacional),0.6,66,0,3.9,-46.9,621,28.8,1.6,252.5,3550,3886 +56,38.42684,-5.92866,3,60.6,Valencia de las Torres,Extremadura,42,86,96,0,99,73,40.4,6648,3111,0,1019,921,Río Matachel (Natura 2000),1.0,0,0,1.8,-39.2,443,34.5,2.7,289.6,6652,3772 +57,38.30008,-5.12412,3,60.6,Belmez,Andalucía,56,73,48,0,97,76,38.0,6791,500,0,2500,6824, (Designación nacional),6.8,0,0,2.4,-53.0,611,33.9,1.8,262.5,6933,4438 +58,41.09222,-2.88523,3,60.4,La Bodera,Castilla-La Mancha,37,63,100,99,74,100,41.3,3544,3613,223,316,5308,Valle y salinas del Salado (Natura 2000),5.3,65,0,5.0,-72.9,911,30.0,-0.8,101.2,4241,6848 +59,38.14952,-1.69269,3,60.3,Cehegín,Región de Murcia,59,58,80,0,81,77,37.4,4604,1788,282,1526,1170,"Sierra del Molino, Embalse del Quípar y Llanos del Cagitán (Natura 2000)",1.2,100,0,0.9,-44.3,402,33.8,2.3,307.5,7626,7385 +60,36.53231,-5.7408,3,60.1,Alcalá de los Gazules,Andalucía,53,80,32,0,98,95,38.5,6488,1802,0,3000,1838, (Designación nacional),1.9,0,0,2.2,-51.9,146,30.8,7.2,461.7,8743,3551 +61,39.94756,-2.4797,3,60.0,Huerta de la Obispalía,Castilla-La Mancha,49,60,48,100,70,91,39.1,4401,2580,300,2523,13462,,13.5,64,0,4.7,-59.2,927,31.6,-0.3,117.5,5551,6994 +62,40.80261,-2.56026,3,59.2,Canredondo,Castilla-La Mancha,68,59,0,0,100,97,35.8,4981,2022,0,5035,5434,Valle del Tajuña en Torrecuadrada (Natura 2000),5.5,100,0,0.6,-47.2,1092,29.3,-1.6,105.5,4604,7069 +63,42.45989,-1.45359,3,59.1,Ujué / Uxue,Navarra / Nafarroa,34,65,99,97,75,100,41.9,3721,2640,0,943,300,Tramo medio del río Aragón (Natura 2000),0.3,66,0,10.0,-43.6,469,28.6,1.7,292.7,2934,8251 +64,41.35554,-0.17615,3,59.1,Sástago,Aragón,37,100,0,100,93,82,41.3,6888,4237,100,6640,316,Monegros (Natura 2000),0.4,30,0,0.8,-26.7,287,33.0,1.9,314.5,4299,9140 +65,39.47766,-5.68911,3,58.9,Madroñera,Extremadura,56,67,0,100,65,88,38.0,5522,400,400,4410,2012,Riberos del Almonte (Natura 2000),2.0,49,0,4.1,-45.9,656,32.1,2.1,197.8,5553,4193 +66,42.16156,-2.04695,3,58.9,Quel,La Rioja,37,57,73,90,93,100,41.3,3498,2823,0,1746,2906, (Designación nacional),2.9,69,0,3.9,-65.2,715,27.9,0.2,236.6,3185,7719 +67,39.10281,-6.20014,3,58.9,Arroyomolinos,Extremadura,23,84,97,100,97,78,43.9,4134,4410,0,1000,900, (Designación nacional),0.9,36,0,2.4,-38.5,323,33.7,3.6,257.0,5868,3682 +68,39.70814,-4.47545,3,58.8,San Martín de Montalbán,Castilla-La Mancha,48,77,83,0,80,83,39.4,6640,1170,141,100,509,Complejo lagunar de La Jara (Natura 2000),0.5,0,0,4.9,-43.5,626,32.8,1.7,101.7,5509,5264 +69,40.34497,-2.54502,3,58.6,Tinajas,Castilla-La Mancha,29,59,100,100,97,92,42.8,3443,3244,0,141,5044,Estepas yesosas de La Alcarria conquense (Natura 2000),5.1,45,0,2.7,-63.0,934,31.3,-0.2,99.1,5107,7005 +70,39.28368,-3.79407,3,58.4,Fuente el Fresno,Castilla-La Mancha,41,55,52,100,93,84,40.6,3920,1000,0,2376,894,Montes de Toledo (Natura 2000),0.9,60,0,3.8,-87.2,849,32.7,0.5,125.5,6081,5763 +71,38.89781,-4.87076,3,58.2,Baterno,Extremadura,41,88,100,25,66,78,40.7,5597,4036,300,509,316,Corredores de Siruela (Natura 2000),0.3,5,0,6.1,-45.0,465,33.6,2.4,194.3,6326,4772 +72,42.20076,-6.29969,3,58.1,Castrocontrigo,Castilla y León,46,72,0,100,82,97,39.7,4178,3981,0,4327,538,Sierra de la Cabrera (Natura 2000),0.6,63,0,7.6,-105.3,1139,25.5,-1.6,296.5,2482,4272 +73,40.58787,-1.3963,3,58.1,Alba,Aragón,33,44,99,100,100,87,42.0,3720,538,0,943,1811,Parameras de Pozondón (Natura 2000),1.8,45,0,1.3,-57.0,1107,30.7,-2.6,196.8,4995,8009 +74,37.69298,-5.8572,3,58.0,Castilblanco de los Arroyos,Andalucía,37,87,100,0,95,69,41.3,8105,2236,0,141,2927, (Designación nacional),2.9,0,0,3.3,-25.9,49,35.2,5.3,351.6,7462,3683 +75,38.43314,-6.71594,3,57.7,Jerez de los Caballeros,Extremadura,58,65,48,97,45,84,37.6,5197,707,1236,2505,6826, (Designación nacional),6.8,66,0,0.8,-46.1,423,32.7,3.7,337.2,6498,3099 +76,39.67008,-5.26994,3,57.5,Villar del Pedroso,Extremadura,40,73,92,100,40,91,40.9,4465,3306,640,1140,300,Sierra de las Villuercas y Valle del Guadarranque (Natura 2000),0.3,40,0,6.9,-45.9,612,31.5,2.0,156.3,5417,4586 +77,39.40419,-5.07456,3,57.5,Alía,Extremadura,57,70,83,66,40,86,37.8,4617,2600,2051,1442,943,Puerto Peña - los Golondrinos (Natura 2000),1.0,77,0,3.0,-62.5,487,32.4,2.3,161.0,5740,4698 +78,40.6307,-5.08669,3,57.4,San Juan del Olmo,Castilla y León,27,60,87,100,99,96,43.1,3716,3006,0,1300,4879,Dehesa de los Ríos Gamo y Margañán (Natura 2000),4.9,28,0,1.9,-52.4,1352,26.7,-1.6,120.2,4402,4933 +79,41.20571,-1.57255,3,57.3,Fuentes de Jiloca,Aragón,32,48,83,100,100,94,42.2,3394,1769,0,100,4036,Muelas del Jiloca: El Campo y La Torreta (Natura 2000),4.1,58,0,1.4,-101.0,694,31.0,-0.0,199.3,4295,7957 +80,38.17414,-5.19288,3,57.2,Villanueva del Rey,Andalucía,31,49,100,95,99,79,42.3,4178,600,0,424,608, (Designación nacional),0.6,19,0,1.9,-58.9,623,33.5,1.9,277.5,7058,4354 +81,38.91376,-4.26025,3,56.8,Cabezarados,Castilla-La Mancha,37,72,100,100,45,79,41.3,4429,3101,3324,707,316,Ríos de la cuenca media del Guadiana y laderas vertientes (Natura 2000),0.3,46,0,1.6,-43.1,663,33.5,1.2,172.3,6410,5297 +82,41.40778,-1.88355,3,56.7,Moros,Aragón,22,75,80,100,98,100,44.1,4410,4438,0,1526,4455,Cihuela-Deza (Natura 2000),4.5,28,0,2.2,-36.6,869,30.1,-1.0,187.7,4033,7731 +83,40.68148,-5.72469,3,56.6,Pedrosillo de los Aires,Castilla y León,26,62,100,100,85,100,43.3,5448,2109,200,447,6586,Río Alagón (Natura 2000),6.6,23,0,1.9,-31.4,893,29.2,0.1,174.6,4237,4414 +84,38.31871,-6.60649,3,56.4,Burguillos del Cerro,Extremadura,30,91,100,0,99,77,42.5,5869,4393,0,800,800,Embalse de Valuengo (Natura 2000),0.8,0,0,2.0,-36.6,354,33.9,3.3,338.6,6643,3168 +85,42.01428,-2.04308,3,56.4,Cervera del Río Alhama,La Rioja,45,59,31,100,68,100,40.0,4101,3206,282,3036,3383,Sierra de Alcarama y Valle del Alhama (Natura 2000),3.4,62,0,5.8,-75.0,865,27.9,-0.8,223.9,3347,7698 +86,39.7273,-5.43561,3,56.3,Peraleda de San Román,Extremadura,34,69,91,100,58,90,41.9,4143,3448,500,1166,300,Sierra de las Villuercas y Valle del Guadarranque (Natura 2000),0.3,27,0,4.0,-50.6,430,31.7,3.0,165.5,5326,4458 +87,37.65192,-2.43735,3,56.2,Orce,Andalucía,48,95,0,0,95,79,39.3,6082,5110,0,7003,13505,,40.0,0,0,3.3,-54.5,1267,32.7,-1.8,326.6,8066,6657 +88,40.86515,-0.32267,3,56.1,Alcorisa,Aragón,42,59,58,100,58,98,40.5,3945,1769,583,2202,4825,Río Guadalope - Maestrazgo (Natura 2000),4.9,60,0,2.0,-49.3,735,30.3,-0.9,290.5,4821,8950 +89,39.96777,-1.44806,3,55.9,Casas de Garcimolina,Castilla-La Mancha,24,58,100,100,98,88,43.7,3862,2441,0,141,1612,Rentos de Orchova y Páramos de Moya (Natura 2000),1.6,47,0,2.1,-39.2,1137,30.7,-2.4,199.7,5668,7871 +90,41.77534,-5.96613,3,55.9,Ferreruela,Castilla y León,51,52,21,61,78,100,38.8,4833,1300,282,3352,316,Sierra de la Culebra (Natura 2000),0.3,79,0,2.4,-41.6,770,28.6,0.2,244.7,3004,4449 +91,38.1588,-3.38307,3,55.9,Úbeda,Andalucía,33,61,100,100,72,67,42.1,4110,2692,141,141,3606,Río Guadalimar (Natura 2000),3.6,28,0,7.6,-47.6,524,35.5,2.3,251.7,7372,5917 +92,41.18859,0.44934,3,55.8,la Fatarella,Catalunya,43,45,80,43,90,93,40.3,3488,1200,0,1523,721, (Designación nacional),0.7,85,0,5.1,-50.7,356,31.1,1.9,360.7,4551,9639 +93,42.66869,-6.19122,3,55.8,Villagatón,Castilla y León,44,62,100,33,56,93,40.1,3500,3413,583,824,8732,Omaña (Natura 2000),8.8,88,0,2.7,-49.8,1167,24.7,-2.2,327.4,1992,4463 +94,39.718,-5.62459,3,55.5,Higuera de Albalat,Extremadura,34,52,83,100,76,90,41.8,3716,1843,0,100,1315,Monfragüe (Natura 2000),1.3,49,0,9.7,-89.0,437,31.6,3.2,180.4,5303,4297 +95,38.73811,-1.07989,3,55.5,Caudete,Castilla-La Mancha,31,49,87,100,90,88,42.4,3492,1726,0,1300,4177,Estepas de Yecla (Natura 2000),4.2,50,0,5.0,-48.4,860,32.0,-0.7,294.9,7062,8004 +96,40.83213,-0.1145,3,55.5,La Cerollera / la Sorollera,Aragón,49,65,35,0,91,99,39.1,3584,3820,0,2927,4346,Río Bergantes (Natura 2000),4.4,100,0,4.6,-56.8,629,30.2,-0.4,307.5,4881,9120 +97,39.52586,-2.04245,3,55.5,Alarcón,Castilla-La Mancha,36,64,83,92,63,83,41.5,3748,3535,500,100,500,Hoces de Alarcón (Natura 2000),0.6,68,0,2.2,-44.1,734,32.9,0.5,174.4,6073,7297 +98,38.9585,-1.01367,3,55.5,Ayora,Comunitat Valenciana,37,55,74,52,96,91,41.4,3401,2692,0,1700,3423,Meca-Mugrón-San Benito (Natura 2000),3.5,82,0,3.0,-45.6,906,31.5,-1.0,284.2,6829,8093 +99,38.5456,-3.84504,3,55.5,Calzada de Calatrava,Castilla-La Mancha,35,73,100,100,46,68,41.7,4036,3828,728,728,3324,Túneles del Ojailén (Natura 2000),3.3,60,0,2.9,-42.2,557,35.4,1.9,207.2,6878,5586 +100,37.75582,-2.32947,3,55.4,Orce,Andalucía,56,67,0,0,98,74,37.9,7892,500,0,8839,7300, (Designación nacional),7.3,0,0,2.2,-40.8,1021,33.9,-1.5,319.3,7968,6768 +101,41.14139,-6.06154,3,55.4,Villaseco de los Reyes,Castilla y León,25,70,83,100,81,100,43.5,5640,2617,100,100,8045,Riberas del Río Tormes y afluentes (Natura 2000),8.0,47,0,5.5,-23.2,731,29.7,0.7,216.2,3677,4234 +102,38.80055,-2.93595,3,55.3,Villahermosa,Castilla-La Mancha,34,47,83,100,91,68,41.9,4070,583,0,100,3700,Áreas esteparias del Campo de Montiel (Natura 2000),3.7,53,0,4.7,-43.0,860,35.4,-0.5,191.7,6739,6413 +103,39.89342,-2.15749,3,55.3,Valdetórtola,Castilla-La Mancha,25,51,100,100,97,94,43.6,3059,2607,0,400,2154,Cueva de Los Morciguillos (Natura 2000),2.2,58,0,2.4,-46.8,966,31.1,-0.5,145.1,5655,7258 +104,38.7167,-6.08801,3,55.1,Oliva de Mérida,Extremadura,28,59,83,100,94,73,42.9,4648,2549,0,100,1063, (Designación nacional),1.1,45,0,3.7,-35.0,327,34.6,3.3,276.1,6308,3696 +105,42.58219,-5.94047,3,55.0,Benavides,Castilla y León,29,47,100,100,83,98,42.9,5024,412,0,282,8345,Riberas del Río Órbigo y afluentes (Natura 2000),8.4,47,0,7.1,-31.1,916,27.6,-1.3,306.6,2130,4645 +106,40.36,-6.27091,3,54.8,Caminomorisco,Extremadura,42,45,76,57,78,100,40.5,3522,1772,141,1649,1000, (Designación nacional),1.0,80,0,5.6,-199.5,676,27.9,3.0,218.5,4489,3892 +107,39.60032,-5.19407,3,54.5,Mohedas de la Jara,Castilla-La Mancha,32,69,89,100,50,97,42.3,3921,3921,412,1252,1029,Sierra de las Villuercas y Valle del Guadarranque (Natura 2000),1.1,31,0,8.9,-76.0,824,30.6,1.1,155.1,5506,4636 +108,43.11821,-3.96913,3,54.5,Molledo,Cantabria,37,58,83,52,75,100,41.4,3189,3224,100,100,1711,Sierra del Escudo (Natura 2000),1.8,82,0,7.5,-131.9,532,24.6,3.1,300.7,1865,6335 +109,38.72872,-5.14546,3,54.4,Zarza-Capilla,Extremadura,29,77,45,100,88,79,42.8,3956,4244,141,2600,316,Sierras de Peñalsordo y Capilla (Natura 2000),0.4,21,0,2.3,-57.1,467,33.5,2.5,222.5,6463,4504 +110,40.54291,-2.39008,3,54.3,Castilforte,Castilla-La Mancha,42,44,84,9,98,94,40.5,3206,1345,0,1389,1526,Serranía de Cuenca (Natura 2000),1.6,97,0,2.2,-58.0,859,31.0,0.3,112.4,4912,7168 +111,42.30375,-6.69337,3,54.2,Benuza,Castilla y León,42,47,87,90,46,94,40.4,3780,1204,583,1300,4204,Sierra de la Cabrera (Natura 2000),4.3,69,0,6.2,-123.6,1278,24.8,-2.1,329.0,2299,3978 +112,41.82797,-6.5245,3,54.1,Figueruela de Arriba,Castilla y León,45,49,100,33,53,100,39.9,3569,1581,500,300,316,Riberas del Río Manzanas y afluentes (Natura 2000),0.3,88,0,5.9,-51.8,643,28.6,0.4,286.3,2847,4007 +113,40.73986,-0.76907,3,54.0,Jarque de la Val,Aragón,25,64,100,100,74,83,43.4,3125,3921,141,316,6841,Desfiladeros del río Martín (Natura 2000),6.9,57,0,7.1,-46.7,1260,29.1,-4.0,251.3,4906,8558 +114,38.71084,-3.60164,3,53.9,Moral de Calatrava,Castilla-La Mancha,36,87,56,0,100,70,41.6,7694,2247,0,2267,10735,Lagunas volcánicas del Campo de Calatrava (Natura 2000),10.7,0,0,1.3,-27.3,687,35.1,0.7,189.1,6736,5825 +115,41.56572,-2.50577,3,53.9,Almazán,Castilla y León,41,48,88,0,94,96,40.6,3811,1204,0,1272,1204,Riberas del Río Duero y afluentes (Natura 2000),1.2,100,0,3.6,-39.6,1003,27.5,-1.8,161.4,3776,7243 +116,39.36507,-3.67979,3,53.7,Urda,Castilla-La Mancha,37,74,27,100,66,85,41.3,5860,1523,316,3176,300,Montes de Toledo (Natura 2000),0.3,30,0,5.7,-34.6,865,32.4,0.3,116.5,6010,5875 +117,41.57159,-6.01022,3,53.7,Muelas del Pan,Castilla y León,32,39,58,100,99,100,42.3,3753,700,0,2202,2778,Riberas del Río Aliste y afluentes (Natura 2000),2.8,61,0,1.9,-42.8,728,28.6,0.6,234.5,3218,4369 +118,38.39284,-1.30565,3,53.6,Jumilla,Región de Murcia,31,45,73,100,92,82,42.5,3931,1315,0,1746,6711, (Designación nacional),6.7,28,0,4.1,-49.8,584,33.0,1.4,307.4,7411,7758 +119,37.76619,-2.67329,3,53.5,Castilléjar,Andalucía,33,58,83,83,77,72,42.1,3176,2745,141,100,6200,Sierra de La Sagra (Natura 2000),6.2,71,0,6.1,-48.7,828,34.6,0.2,307.8,7907,6469 +120,42.37794,-0.16812,3,53.5,Sabiñánigo,Aragón,41,51,81,64,58,97,40.7,3511,2418,282,1503,1315,Sierra y Cañones de Guara (Natura 2000),1.4,78,0,9.1,-66.6,1097,26.8,-1.5,365.3,3176,9288 +121,40.27051,-1.23015,3,53.5,Villel,Aragón,27,54,83,100,83,90,43.1,3162,2912,0,100,3190,Cuenca del Ebrón (Natura 2000),3.2,55,0,7.1,-71.8,1046,30.5,-2.2,211.7,5364,8101 +122,36.87875,-4.90141,3,53.3,Ardales,Andalucía,35,70,100,0,81,92,41.8,4887,2545,100,632,3400,Sierras de Alcaparaín y Aguas (Natura 2000),3.4,0,0,5.7,-55.6,549,31.4,3.9,402.9,8517,4358 +123,39.19665,-4.12157,3,53.3,Porzuna,Castilla-La Mancha,41,52,100,0,85,81,40.7,5381,500,141,538,6082, (Designación nacional),6.1,0,0,3.2,-50.5,736,33.1,1.0,139.1,6124,5468 +124,37.69528,-7.20631,3,53.3,Paymogo,Andalucía,27,96,83,0,90,80,43.1,7324,5047,141,100,4964,Andévalo Occidental (Natura 2000),5.0,0,0,1.0,-23.7,138,33.3,5.6,423.7,7202,2516 +125,38.14109,-6.42019,3,53.0,Segura de León,Extremadura,33,69,100,0,92,77,42.1,4743,2109,100,141,1500, (Designación nacional),1.5,0,0,1.7,-49.3,506,33.8,3.0,341.2,6871,3290 +126,42.15499,-6.52126,3,52.9,Rosinos de la Requejada,Castilla y León,43,49,83,0,82,97,40.3,3201,2256,0,1414,640,Sierra de la Cabrera - ZEPA (Natura 2000),0.7,100,0,7.5,-74.9,1192,25.1,-1.6,307.4,2492,4083 +127,42.31157,-6.91949,3,52.8,A Veiga,Galicia,41,62,0,100,74,95,40.7,4248,2459,223,4589,4695,Pena Trevinca (Natura 2000),4.7,44,0,5.1,-55.4,1312,23.5,-1.9,344.3,2249,3798 +128,38.18492,-3.14387,3,52.7,Castellar,Andalucía,35,67,87,0,97,65,41.6,5640,721,0,1300,1264,Río Guadalimar (Natura 2000),1.3,0,0,2.4,-42.1,509,35.8,2.1,252.3,7380,6129 +129,40.25094,-2.24972,3,52.7,Villar de Domingo García,Castilla-La Mancha,34,58,40,64,96,98,42.0,3101,3324,0,2765,10323,Serranía de Cuenca (Natura 2000),10.3,78,0,2.8,-49.5,1066,30.4,-0.8,125.6,5251,7238 +130,41.60098,-1.52145,3,52.6,Mesones de Isuela,Aragón,19,75,63,100,92,97,44.6,4080,4741,0,2061,1264,Sierra de Nava Alta - Puerto de la Chabola (Natura 2000),1.3,33,0,4.2,-36.5,656,30.6,0.4,224.6,3868,8061 +131,37.84765,-3.55892,3,52.6,Jimena,Andalucía,51,72,0,0,93,66,38.8,6332,1529,100,5096,4172, (Designación nacional),4.2,0,0,0.5,-48.7,504,35.7,2.8,284.6,7684,5710 +132,42.64117,-4.92675,3,52.6,Villazanzo de Valderaduey,Castilla y León,34,71,22,45,100,100,41.9,3482,5679,0,3328,1581,Rebollares del Cea (Natura 2000),1.6,84,0,1.2,-41.8,1015,25.3,-1.0,268.4,2236,5472 +133,40.20637,-0.70558,3,52.5,Rubielos de Mora,Aragón,32,45,94,43,93,92,42.2,2900,1315,0,1081,5646,Maestrazgo y Sierra de Gúdar (Natura 2000),5.6,85,0,3.8,-48.3,976,30.2,-2.1,257.1,5499,8535 +134,38.03342,-6.31823,3,52.3,Calera de León,Extremadura,27,57,63,97,87,89,43.1,2954,2308,100,2051,3512, (Designación nacional),3.6,66,0,3.6,-48.6,720,31.8,2.3,344.3,7007,3355 +135,42.29756,-0.80698,3,52.2,Agüero,Aragón,35,41,78,33,100,100,41.7,2973,1140,0,1581,6350,Sierras de Santo Domingo y Caballera y río Onsella (Natura 2000),6.4,88,0,1.6,-66.4,616,28.5,2.0,318.0,3191,8754 +136,42.23198,-6.11365,3,52.1,Quintana y Congosto,Castilla y León,38,45,55,38,95,100,41.1,3721,1216,0,2282,509,Montes Aquilanos (Natura 2000),0.6,87,0,3.3,-41.5,908,27.2,-0.9,287.5,2481,4429 +137,38.02987,-2.18558,3,52.1,Caravaca de la Cruz,Región de Murcia,30,48,50,100,98,76,42.5,3471,2118,0,2459,4964,Sierra de Mojantes (Natura 2000),5.0,49,0,2.2,-63.2,1194,33.0,-2.0,296.8,7689,6937 +138,42.81983,-5.46036,2,51.8,Matallana de Torío,Castilla y León,37,63,10,100,70,97,41.3,3765,3492,316,3700,6800,Hoces de Vegacervera (Natura 2000),6.8,55,0,4.3,-54.1,1171,23.8,-1.6,306.1,1953,5081 +139,40.75283,-2.74782,2,51.6,Brihuega,Castilla-La Mancha,31,44,76,92,74,99,42.4,4004,1077,316,1640,2012,Quejigares de Barriopedro y Brihuega (Natura 2000),2.0,68,0,2.9,-37.9,990,29.7,-1.2,88.8,4632,6904 +140,41.9379,-1.0161,2,51.5,Tauste,Aragón,21,66,44,100,99,98,44.2,3101,4509,0,2624,447,"Montes de Zuera, Castejón de Valdejasa y El Castellar (Natura 2000)",0.4,61,0,1.9,-42.9,530,30.4,1.4,280.2,3561,8529 +141,38.93288,-2.4346,2,51.4,El Bonillo,Castilla-La Mancha,24,55,80,90,93,75,43.7,5869,500,100,1523,1140,Zona esteparia de El Bonillo (Natura 2000),1.2,69,0,1.3,-15.7,1017,33.9,-1.3,198.6,6667,6866 +142,38.7461,-3.87899,2,51.3,Aldea del Rey,Castilla-La Mancha,37,49,24,85,92,77,41.3,3443,1843,0,3269,412, (Designación nacional),0.5,70,0,4.3,-52.2,750,33.9,0.6,185.2,6654,5593 +143,43.43588,-6.33288,2,51.2,Salas,Asturias / Asturies,34,33,90,97,64,100,41.9,3324,412,300,1208,5474,Turbera de la Molina (Natura 2000),5.5,66,0,6.7,-149.3,291,24.0,4.1,402.5,1132,4522 +144,41.03139,-3.05554,2,51.1,Zarzuela de Jadraque,Castilla-La Mancha,42,47,83,0,73,100,40.4,3471,1252,223,100,761, (Designación nacional),0.8,100,0,5.6,-44.3,939,29.2,-0.8,86.8,4283,6696 +145,38.51807,-5.64024,2,51.1,Peraleda del Zaucejo,Extremadura,23,56,83,100,79,83,43.8,3000,1649,282,100,4060,Campiña sur - Embalse de Arroyo Conejos (Natura 2000),4.1,53,0,2.1,-49.1,662,32.8,1.9,266.1,6605,4038 +146,37.71108,-5.67235,2,51.1,Constantina,Andalucía,48,71,77,0,45,71,39.3,6087,707,4365,1612,3453, (Designación nacional),3.5,0,0,1.0,-48.0,140,34.8,4.8,341.9,7476,3847 +147,42.72898,-1.80559,2,51.1,Puente la Reina / Gares,Navarra / Nafarroa,18,44,100,97,96,100,44.8,2517,2961,0,447,7416,Peña de Etxauri (Natura 2000),7.5,66,0,2.8,-70.1,567,26.6,1.3,299.6,2594,8008 +148,37.22861,-2.38232,2,51.0,Velefique,Andalucía,20,50,97,100,85,89,44.3,3195,2024,100,1004,2690, (Designación nacional),2.7,33,0,4.2,-76.3,1598,30.1,-2.9,372.5,8536,6638 +149,42.55585,-1.08485,2,50.8,Urriés,Aragón,42,44,98,9,59,100,40.4,3228,1252,500,948,1208,Sierras de San Juan de la Peña y Peña Oroel (Natura 2000),1.3,97,0,3.6,-73.2,606,28.0,1.8,320.8,2874,8566 +150,41.47494,-3.30066,2,50.8,San Esteban de Gormaz,Castilla y León,27,47,83,58,91,100,43.1,4114,1555,0,100,11562,Riberas del Río Duero y afluentes (Natura 2000),11.6,12,0,4.6,-47.4,926,29.4,-1.0,121.6,3762,6572 +151,37.44809,-6.96691,2,50.8,Gibraleón,Andalucía,30,79,83,0,84,81,42.7,5630,2088,141,100,12120,,12.1,0,0,3.5,-37.9,11,33.2,6.4,429.8,7517,2669 +152,38.80873,-4.63904,2,50.8,Almadenejos,Castilla-La Mancha,30,89,0,95,72,79,42.5,5903,3805,400,4716,400,Sierra de Los Canalizos (Natura 2000),0.4,19,0,1.7,-31.8,558,33.5,1.9,194.1,6462,4953 +153,42.25247,-2.21479,2,50.7,Arnedillo,La Rioja,32,57,100,7,79,95,42.2,2701,3624,0,223,2469, (Designación nacional),2.5,98,0,8.6,-80.0,1106,25.3,-1.9,237.3,3063,7597 +154,41.55917,-2.70095,2,50.7,Fuentepinilla,Castilla y León,33,69,32,26,97,95,42.0,5314,2690,0,3023,6888,Riberas del Río Duero y afluentes (Natura 2000),6.9,91,0,2.5,-21.4,981,28.3,-1.8,151.2,3756,7081 +155,39.13758,-6.77799,2,50.6,Villar del Rey,Extremadura,36,71,100,0,63,77,41.6,5414,1612,223,670,3606, (Designación nacional),3.6,0,0,8.9,-44.1,257,33.8,4.2,297.4,5722,3200 +156,40.70124,-6.07151,2,50.6,La Sagrada,Castilla y León,20,54,83,100,83,100,44.4,4802,1697,223,100,1100,"Riberas de los Ríos Huebra, Yeltes, Uces y afluentes (Natura 2000)",1.1,57,0,2.0,-24.6,816,28.8,0.8,204.0,4154,4131 +157,42.07272,-6.94922,2,50.3,Lubián,Castilla y León,22,51,99,100,70,93,44.1,3067,2846,100,921,447, (Designación nacional),0.5,45,0,9.4,-48.0,1387,23.3,-2.2,331.0,2503,3719 +158,41.06377,-1.12609,2,50.3,Loscos,Aragón,21,45,100,100,79,92,44.1,3026,2400,0,200,4992,Sierra de Fonfría (Natura 2000),5.0,45,0,8.6,-63.2,1106,29.6,-2.4,228.9,4507,8307 +159,42.52521,-4.61337,2,50.3,Saldaña,Castilla y León,17,52,83,100,93,100,44.9,3769,2236,100,100,4661,Riberas del Río Pisuerga y afluentes (Natura 2000),4.6,48,0,0.8,-35.2,909,27.0,-0.7,246.9,2413,5701 +160,42.85356,-3.97176,2,50.2,Valderredible,Cantabria,33,43,100,7,86,100,42.0,2780,2163,0,360,1772, (Designación nacional),1.8,98,0,6.1,-47.6,984,24.3,-0.3,271.4,2154,6282 +161,40.13861,-2.7544,2,50.1,Huete,Castilla-La Mancha,33,60,65,0,99,86,42.1,4801,1140,0,1972,4825,Sierra de Altomira (Natura 2000),4.8,0,0,1.7,-47.6,833,32.3,-0.1,87.1,5303,6794 +162,39.63137,-1.34369,2,49.8,Utiel,Comunitat Valenciana,32,63,0,100,85,92,42.3,4410,2334,141,5536,5381, (Designación nacional),5.4,40,0,3.2,-40.8,945,31.3,-1.1,221.2,6050,7909 +163,42.42774,-1.09902,2,49.8,Uncastillo,Aragón,22,57,48,100,89,100,44.0,2969,3417,0,2523,5059,Sierras de Santo Domingo y Caballera (Natura 2000),5.1,64,0,5.2,-44.9,868,26.9,0.1,309.9,3013,8535 +164,38.23538,-2.82173,2,49.8,Beas de Segura,Andalucía,25,52,65,83,96,74,43.5,3800,2088,0,1992,447, (Designación nacional),0.5,17,0,3.0,-117.0,784,34.3,1.0,254.2,7373,6417 +165,37.80955,-6.50789,2,49.7,Aracena,Andalucía,25,65,100,0,100,80,43.6,4143,2952,0,600,4785, (Designación nacional),4.8,0,0,1.4,-70.2,363,33.3,4.2,373.8,7214,3144 +166,40.94144,-0.02476,2,49.7,Valjunquera / Valljunquera,Aragón,19,43,79,100,99,95,44.6,3008,1100,0,1555,5594,Río Mezquín y Oscuros (Natura 2000),5.6,59,0,1.9,-43.2,576,30.8,0.2,316.6,4771,9210 +167,40.98916,-1.38828,2,49.5,Castejón de Tornos,Aragón,29,51,67,28,99,92,42.7,2668,3200,0,1910,1897,Montes de la Cuenca de Gallocanta (Natura 2000),1.9,90,0,1.7,-50.4,945,30.6,-1.8,205.4,4556,8077 +168,39.77734,-4.77569,2,49.5,Alcaudete de la Jara,Castilla-La Mancha,22,38,100,100,83,84,44.0,3046,424,200,141,360,Rios de la margen izquierda del Tajo y berrocales del Tajo (Natura 2000),0.4,46,0,2.6,-49.9,494,32.6,2.3,114.9,5384,5024 +169,42.51633,-0.93015,2,49.5,Bagüés,Aragón,22,51,88,57,93,100,44.1,2469,3275,0,1272,3580,Sierras de Santo Domingo y Caballera y río Onsella (Natura 2000),3.6,80,0,3.9,-66.5,937,26.5,0.2,326.6,2936,8686 +170,37.84286,-6.90196,2,49.4,Almonaster la Real,Andalucía,34,78,74,0,62,92,42.0,4809,4272,500,1702,500,Sierra Pelada y Rivera del Aserrador (Natura 2000),0.5,0,0,2.9,-49.0,477,31.4,4.0,393.7,7102,2811 +171,38.14089,-2.04714,2,49.3,Moratalla,Región de Murcia,30,33,64,97,86,82,42.6,3130,707,100,2024,3224,Sierra de Moratalla (Natura 2000),3.2,66,0,4.1,-54.6,1093,32.7,-1.3,291.7,7587,7075 +172,40.19528,-1.16243,2,49.2,Villel,Aragón,25,52,67,100,75,88,43.6,3361,2080,360,1920,2080,"Altos de Marimezquita, Los Pinarejos y Muela de Cascante (Natura 2000)",2.1,37,0,1.7,-73.3,1041,30.6,-2.4,218.5,5455,8147 +173,41.88815,-2.85549,2,49.0,Molinos de Duero,Castilla y León,32,47,90,0,91,93,42.2,2657,1992,0,1216,2561,Riberas del Río Duero y afluentes (Natura 2000),2.6,100,0,4.5,-49.4,1209,24.9,-2.3,177.0,3374,7011 +174,42.88865,-5.25611,2,49.0,Boñar,Castilla y León,27,44,93,40,89,93,43.1,2624,2435,0,1118,721, (Designación nacional),0.7,86,0,5.1,-113.8,1366,23.4,-2.2,304.9,1912,5259 +175,42.23115,0.43823,2,49.0,Graus,Aragón,33,41,45,76,81,100,42.1,3140,1562,100,2600,2600,Río Isábena (Natura 2000),2.6,74,0,5.7,-54.5,673,30.1,0.1,399.4,3403,9765 +176,40.27249,-2.87652,2,48.9,Albalate de Zorita,Castilla-La Mancha,28,60,18,100,84,89,43.0,3352,3006,0,3448,300,Sierra de Altomira (Natura 2000),0.3,47,0,7.0,-50.0,797,31.9,0.2,72.5,5139,6714 +177,37.77639,-3.27295,2,48.9,Jódar,Andalucía,44,66,46,2,65,68,40.1,3634,2844,500,2570,5295, (Designación nacional),5.3,99,0,1.8,-43.7,616,35.3,2.2,295.0,7806,5947 +178,43.16945,-4.12036,2,48.9,Arenas de Iguña,Cantabria,22,39,100,100,71,100,44.1,3138,1334,223,316,316, (Designación nacional),0.4,40,0,6.3,-50.7,426,24.6,3.6,307.7,1786,6224 +179,38.28969,-2.20055,2,48.8,Letur,Castilla-La Mancha,30,53,29,85,90,79,42.6,3041,3041,100,3100,1526,Sierra de Alcaraz y Segura y cañones del Segura y del Mundo (Natura 2000),1.6,70,0,2.6,-48.3,1019,33.2,-1.3,270.6,7403,6965 +180,36.93407,-5.11239,2,48.8,Alcalá del Valle,Andalucía,29,62,74,0,95,89,42.8,4438,1500,0,1702,1640, (Designación nacional),1.7,0,0,3.1,-60.7,643,31.8,3.2,402.0,8420,4183 +181,38.58595,-4.71414,2,48.7,Almodóvar del Campo,Castilla-La Mancha,45,60,81,0,45,73,40.0,5728,412,1000,1500,400, (Designación nacional),0.4,0,0,1.1,-61.9,473,34.5,2.4,219.1,6692,4846 +182,40.91586,-0.60823,2,48.5,Estercuel,Aragón,33,47,100,0,76,95,42.0,2745,2505,100,141,1802,Desfiladeros del río Martín (Natura 2000),1.9,100,0,7.4,-47.5,674,30.9,-0.5,267.6,4732,8718 +183,40.2409,-6.65797,2,48.1,Gata,Extremadura,33,45,97,0,77,100,42.1,2683,2140,282,989,948, (Designación nacional),1.0,100,0,2.7,-64.3,556,27.5,3.7,252.1,4547,3544 +184,40.69567,-1.7429,2,48.1,Tordellego,Castilla-La Mancha,24,38,67,76,98,86,43.6,2701,1000,0,1910,412,Montes de Picaza (Natura 2000),0.5,74,0,2.0,-56.2,1313,29.0,-3.4,169.0,4832,7735 +185,39.15221,-5.13468,2,48.1,Talarrubias,Extremadura,31,44,0,100,100,87,42.5,3800,509,0,4304,1360,Puerto Peña - los Golondrinos (Natura 2000),1.3,36,0,0.8,-47.7,384,32.2,3.0,184.7,6004,4597 +186,41.98922,-2.95656,2,48.1,Regumiel de la Sierra,Castilla y León,37,44,83,2,76,87,41.3,2912,1860,0,100,300,Sierra de la Demanda - ZEPA (Natura 2000),0.3,99,0,9.5,-65.9,1446,23.6,-3.3,184.4,3249,6946 +187,38.19917,-6.87014,2,48.1,Jerez de los Caballeros,Extremadura,48,51,68,26,41,83,39.3,3720,500,824,1902,1131, (Designación nacional),1.2,91,0,2.9,-48.7,366,32.8,4.0,364.2,6722,2916 +188,41.90808,-4.56806,2,47.9,Dueñas,Castilla y León,17,46,83,87,90,100,44.9,3420,1280,141,100,1627,Montes Torozos y Páramos de Torquemada-Astudillo (Natura 2000),1.6,17,0,1.5,-46.6,761,29.8,-0.1,181.6,3094,5615 +189,42.02716,0.59679,2,47.8,Viacamp y Litera/Viacamp i Lliterà,Aragón,20,43,89,100,72,100,44.4,2683,2024,360,1252,4100,Lagunas de Estaña (Natura 2000),4.1,61,0,2.4,-70.6,669,29.7,0.0,401.2,3644,9869 +190,41.55563,-5.44175,2,47.7,Pozoantiguo,Castilla y León,19,48,100,35,97,100,44.6,3744,800,0,200,5032,Riberas del Río Duero y afluentes (Natura 2000),5.1,88,0,2.5,-27.3,683,29.2,0.5,194.9,3334,4830 +191,40.5905,-2.63949,2,47.6,Alique,Castilla-La Mancha,18,27,100,100,92,98,44.8,2563,500,0,565,4729,Alto Tajo (Natura 2000),4.8,62,0,4.1,-73.3,939,30.3,-0.5,92.2,4825,6967 +192,38.08321,-2.96404,2,47.6,Villacarrillo,Andalucía,18,53,100,100,71,69,44.7,2996,2580,200,300,565, (Designación nacional),0.6,37,0,6.6,-132.4,569,35.2,2.4,266.9,7518,6268 +193,42.4202,-0.03706,2,47.6,Fiscal,Aragón,24,41,88,97,62,99,43.7,2789,1746,200,1264,921,Sierra y Cañones de Guara (Natura 2000),0.9,66,0,9.8,-73.7,1093,26.5,-1.2,376.8,3144,9401 +194,42.72781,-6.34766,2,47.5,Igüeña,Castilla y León,33,45,81,0,83,99,42.1,2692,2507,200,1500,7299,Alto Sil (Natura 2000),7.3,100,0,2.7,-128.5,1149,24.5,-1.2,340.9,1900,4351 +195,40.07481,-0.78257,2,47.4,Albentosa,Aragón,18,33,100,100,84,90,44.7,2692,1118,100,141,3590,Sierra de Javalambre II (Natura 2000),3.6,56,0,4.6,-61.3,928,30.8,-1.8,252.8,5634,8451 +196,37.57454,-3.89681,2,47.3,Castillo de Locubín,Andalucía,31,53,93,0,81,74,42.4,4700,1264,100,1118,12015,,12.0,0,0,5.6,-47.4,798,34.4,1.8,314.4,7928,5367 +197,41.98716,0.46228,2,47.3,Baells,Aragón,42,42,13,31,81,99,40.4,3383,1529,141,3584,6324,Lagunas de Estaña (Natura 2000),6.4,89,0,4.7,-50.5,762,30.1,-0.7,389.2,3674,9753 +198,38.02794,-6.00604,2,47.2,Monesterio,Extremadura,17,52,100,100,72,75,45.0,3640,1702,200,707,1431,Valdecigüeñas (Natura 2000),1.4,23,0,6.4,-42.4,392,34.2,3.3,328.1,7071,3623 +199,38.25903,-5.31672,2,47.2,Fuente Obejuna,Andalucía,24,45,40,100,91,79,43.6,3512,1603,0,2765,4785,Alto Guadiato (Natura 2000),4.8,30,0,4.5,-47.7,619,33.4,1.7,274.3,6944,4264 +200,41.2749,-3.84434,2,47.2,Sebúlcor,Castilla y León,19,27,83,100,94,100,44.6,2640,509,0,100,500,Hoces del Río Duratón (Natura 2000),0.5,61,0,3.6,-52.8,891,29.1,-0.6,96.0,3899,6087 +201,40.43643,-5.92619,2,47.1,Horcajo de Montemayor,Castilla y León,34,42,100,40,45,100,41.9,2968,1555,1910,424,707,Río Alagón (Natura 2000),0.7,86,0,1.4,-53.6,714,28.7,1.5,189.1,4468,4195 +202,38.87405,-3.11572,2,47.0,Alhambra,Castilla-La Mancha,26,54,47,100,72,73,43.3,3106,2729,300,2531,7355,Áreas esteparias del Campo de Montiel (Natura 2000),7.4,55,0,4.0,-50.7,896,34.6,-0.7,178.8,6632,6271 +203,39.567,-3.75449,2,47.0,Consuegra,Castilla-La Mancha,31,89,15,0,86,81,42.5,4887,4326,200,3535,943,Área esteparia de La Mancha norte (Natura 2000),1.0,100,0,1.7,-21.2,747,33.2,0.7,94.1,5778,5848 +204,38.67537,-2.2251,2,47.0,Peñascosa,Castilla-La Mancha,32,46,0,92,91,80,42.2,3214,1697,0,5048,1627,Sierra de Alcaraz y Segura y cañones del Segura y del Mundo (Natura 2000),1.6,68,0,4.6,-64.5,1120,32.7,-1.7,232.7,6978,7005 +205,39.55252,-4.51594,2,47.0,Navas de Estena,Castilla-La Mancha,39,62,80,0,42,94,41.0,5215,424,707,1529,447,Montes de Toledo (Natura 2000),0.5,0,0,4.7,-53.9,764,31.1,1.4,117.4,5672,5200 +206,38.18636,-4.76126,2,46.9,Pozoblanco,Andalucía,29,57,100,0,77,70,42.7,4491,447,0,565,538, (Designación nacional),0.6,0,0,9.3,-52.1,413,35.1,2.8,261.6,7119,4729 +207,43.38953,-7.52633,2,46.8,Abadín,Galicia,21,43,46,85,98,100,44.2,2607,2385,0,2580,1166,Serra do Xistral (Natura 2000),1.2,70,0,2.3,-64.6,688,22.7,2.4,461.3,965,3570 +208,42.78065,-2.05448,2,46.8,Abárzuza / Abartzuza,Navarra / Nafarroa,28,46,90,9,80,100,42.9,2523,2700,100,1216,360,Urbasa y Andia (Natura 2000),0.4,97,0,5.8,-76.9,935,24.2,-0.4,294.4,2505,7815 +209,42.22322,0.1495,2,46.7,Naval,Aragón,17,39,61,100,97,100,44.9,2561,1664,0,2102,3577,Sierra y Cañones de Guara (Natura 2000),3.6,60,0,2.5,-63.6,739,29.5,0.8,378.2,3381,9527 +210,37.71918,-6.0826,2,46.7,Castilblanco de los Arroyos,Andalucía,28,63,83,0,83,72,42.9,5614,1565,200,100,6592,Sierra Morena de Sevilla (Natura 2000),6.6,0,0,2.7,-37.1,233,34.7,4.5,359.7,7392,3493 +211,37.58537,-3.44943,2,46.7,Huelma,Andalucía,34,65,75,0,65,75,42.0,4804,1860,509,1664,7702,Estribaciones de Sierra Mágina (Natura 2000),7.7,0,0,1.2,-60.2,1014,34.2,-0.0,314.2,7987,5760 +212,37.71277,-4.18901,2,46.7,Santiago de Calatrava,Andalucía,31,68,47,0,93,65,42.4,4606,2716,100,2531,11175, (Designación nacional),11.2,0,0,1.4,-53.7,406,35.9,3.2,301.2,7730,5137 +213,42.33729,-3.3657,2,46.6,Arlanzón,Castilla y León,30,42,84,0,88,98,42.6,2549,2402,141,1389,3328,Riberas del Río Arlanzón y afluentes (Natura 2000),3.4,100,0,2.1,-54.6,1107,25.4,-1.4,214.4,2809,6675 +214,38.65638,-1.76441,2,46.6,Tobarra,Castilla-La Mancha,31,74,18,100,42,80,42.4,4830,5693,761,3448,3640,Sierra de Abenuj (Natura 2000),3.7,31,0,3.5,-38.8,754,33.4,0.1,258.7,7062,7400 +215,41.60399,-1.89026,2,46.4,Malanquilla,Aragón,42,43,13,78,50,96,40.5,3687,400,583,3601,7864,Altos Campos de Gómara (Natura 2000),7.9,73,0,4.7,-47.8,1040,28.7,-1.7,200.3,3817,7757 +216,39.43574,-4.48726,2,46.2,Retuerta del Bullaque,Castilla-La Mancha,24,60,100,35,56,95,43.7,3125,2418,447,141,412,Montes de Toledo (Natura 2000),0.4,88,0,6.0,-38.3,834,30.9,0.7,126.7,5804,5202 +217,38.46406,-1.88501,2,46.2,Liétor,Castilla-La Mancha,26,49,100,0,90,74,43.3,4252,1562,0,509,1252,Sierra de Alcaraz y Segura y cañones del Segura y del Mundo (Natura 2000),1.2,0,0,4.8,-48.9,610,34.3,1.2,269.1,7256,7266 +218,42.50521,-3.5444,2,46.1,Valle de las Navas,Castilla y León,17,39,90,74,87,100,44.9,3352,1019,100,1200,10492,Riberas del Río Oca y afluentes (Natura 2000),10.5,15,0,3.5,-48.6,863,27.5,-0.2,231.7,2599,6561 +219,37.9892,-3.27229,2,46.0,Torreperogil,Andalucía,23,70,83,0,91,64,43.9,4100,3342,100,100,4356, (Designación nacional),4.4,0,0,2.3,-47.2,466,36.0,2.6,271.7,7574,5984 +220,39.612,-1.58426,2,45.9,La Pesquera,Castilla-La Mancha,23,60,64,9,97,90,43.8,3701,2683,0,2012,316,"Hoces del Cabriel, Guadazaón y ojos de Moya (Natura 2000)",0.4,97,0,2.5,-28.8,740,31.6,0.5,203.4,6040,7701 +221,42.91992,-6.81494,2,45.8,Ibias,Asturias / Asturies,19,41,83,100,69,100,44.6,2823,1612,316,1431,538,"Fuentes del Narcea, Degaña e Ibias (Natura 2000)",0.6,49,0,4.8,-141.5,1159,22.9,-0.7,382.9,1607,4022 +222,38.67208,-3.05238,2,45.8,Cózar,Castilla-La Mancha,22,58,50,50,100,66,44.0,7481,632,0,2459,2657,Áreas esteparias del Campo de Montiel (Natura 2000),2.7,10,0,1.1,-16.6,828,35.6,-0.4,201.9,6862,6291 +223,37.67254,-5.19488,2,45.7,Palma del Río,Andalucía,22,62,97,0,90,70,44.1,6315,848,100,984,6640, (Designación nacional),6.7,0,0,2.4,-27.1,78,35.0,4.5,327.8,7603,4254 +224,37.44275,-4.99911,2,45.6,Écija,Andalucía,19,85,55,0,100,71,44.6,7924,2022,0,2308,3008, (Designación nacional),3.1,0,0,1.1,-13.7,138,34.8,4.3,345.7,7887,4380 +225,42.39245,-0.39079,2,45.6,Sabiñánigo,Aragón,21,27,100,73,78,100,44.2,2529,707,100,707,1345,La Guarguera (Natura 2000),1.4,74,0,6.5,-78.6,804,29.0,0.6,351.4,3135,9108 +226,42.51774,-6.31152,2,45.4,Torre del Bierzo,Castilla y León,31,48,100,0,59,97,42.4,2601,2860,282,632,15430,,15.5,100,0,8.9,-62.0,1158,24.8,-1.6,321.7,2135,4333 +227,41.84869,-2.15981,2,45.4,Suellacabras,Castilla y León,23,32,83,59,87,93,43.9,2817,781,100,100,412,Quejigares y encinares de Sierra del Madero (Natura 2000),0.5,79,0,3.5,-43.6,1135,26.9,-2.3,203.7,3513,7575 +228,41.44051,-5.50278,2,45.3,Toro,Castilla y León,28,59,13,47,85,100,42.9,3962,2343,223,3612,6001,Llanuras del Guareña (Natura 2000),6.0,83,0,0.3,-32.7,686,29.6,0.7,191.0,3449,4756 +229,40.41069,-2.85472,2,45.3,Pastrana,Castilla-La Mancha,26,53,19,100,70,89,43.3,3622,2121,360,3420,2121,Sierra de Altomira (Natura 2000),2.1,29,0,3.2,-52.8,759,31.8,0.4,72.3,4991,6756 +230,40.57478,-2.95528,2,45.1,Romanones,Castilla-La Mancha,17,35,100,100,67,93,44.9,2545,1077,200,500,3041,Laderas yesosas de Tendilla (Natura 2000),3.0,62,0,8.0,-55.8,827,31.2,-0.3,65.7,4797,6700 +231,37.58457,-6.29141,2,45.1,Aznalcóllar,Andalucía,22,62,100,0,81,75,44.1,4800,640,100,424,424,Corredor Ecológico del Río Guadiamar (Natura 2000),0.4,0,0,5.7,-39.6,169,34.1,5.1,382.1,7499,3284 +232,37.91854,-2.24683,2,45.0,Puebla de Don Fadrique,Andalucía,33,75,0,0,91,74,42.0,4802,3780,0,5608,5326,Sierra del Oso (Natura 2000),5.3,0,0,4.4,-53.5,1234,33.1,-2.4,305.6,7802,6866 +233,42.84452,-0.99218,2,44.9,Vidángoz / Bidankoze,Navarra / Nafarroa,33,59,0,17,89,100,42.0,2801,3911,0,0,1414,Ríos Eska y Biniés (Natura 2000),1.4,94,0,5.1,-181.0,1041,24.6,-0.1,349.4,2568,8685 +234,42.89411,-3.29362,2,44.9,Valle de Losa,Castilla y León,27,38,100,0,79,100,43.1,2418,1897,100,141,1170,Valderejo-Sobrón-Árcenako mendilerroa / Valderejo-Sobrón-Sierra de Árcena (Natura 2000),1.2,100,0,6.2,-65.9,756,25.3,0.9,276.3,2210,6835 +235,40.84869,-1.76812,2,44.8,Molina de Aragón,Castilla-La Mancha,18,50,74,64,83,89,44.7,3026,2500,200,1702,1200, (Designación nacional),1.2,78,0,2.6,-38.0,1230,29.3,-3.0,170.4,4661,7738 +236,43.62404,-7.47,2,44.8,Xove,Galicia,24,33,100,0,96,100,43.7,2282,1726,0,200,2507, (Designación nacional),2.5,100,0,2.9,-50.6,239,23.1,5.6,477.0,721,3671 +237,42.22363,0.67692,2,44.8,Arén / Areny de Noguera,Aragón,26,46,55,80,58,100,43.3,2807,2927,583,2282,3114,Vall Alta de Serradell-Serra de Sant Gervàs (Natura 2000),3.1,72,0,2.3,-130.4,757,29.0,-0.5,416.5,3436,9960 +238,37.72295,-6.66932,2,44.7,El Campillo,Andalucía,25,45,100,0,85,78,43.5,4219,670,0,200,7963, (Designación nacional),8.0,0,0,6.4,-47.7,215,33.6,5.0,390.0,7277,2986 +239,38.18328,-4.63905,2,44.6,Villanueva de Córdoba,Andalucía,25,85,55,0,71,71,43.5,4167,3982,412,2308,670,Guadalmellato (Natura 2000),0.7,0,0,1.6,-49.7,559,34.8,2.1,258.6,7143,4834 +240,40.0327,-6.48669,2,44.6,Guijo de Coria,Extremadura,18,54,100,0,92,92,44.7,4301,984,100,854,3001,Arroyos Patana y Regueros (Natura 2000),3.0,0,0,1.8,-41.8,276,31.3,4.3,240.5,4805,3641 +241,42.72957,-0.92765,2,44.5,Salvatierra de Esca,Aragón,18,57,100,0,86,100,44.8,2118,4220,0,761,700,Sierra de San Miguel (Natura 2000),0.7,100,0,6.1,-46.4,869,25.9,0.8,343.5,2702,8720 +242,40.15377,-6.75728,2,44.2,Villamiel,Extremadura,25,33,100,66,52,100,43.5,2668,800,565,141,1772,Sierra de Gata (Natura 2000),1.8,77,0,4.6,-48.1,576,28.5,3.5,261.4,4623,3442 +243,38.17253,-3.57034,2,44.2,Carboneros,Andalucía,27,51,100,0,74,62,43.1,4341,800,223,200,3769, (Designación nacional),3.8,0,0,5.0,-61.5,385,36.3,3.1,248.7,7328,5757 +244,40.59572,-0.89423,2,44.1,Orrios,Aragón,24,87,0,0,100,79,43.7,5162,5872,0,5147,1562,Parameras de Alfambra (Natura 2000),1.6,0,0,0.9,-38.0,1376,29.4,-4.6,239.4,5049,8432 +245,43.48809,-6.97067,2,44.0,Castropol,Asturias / Asturies,26,30,70,80,64,100,43.4,2789,608,316,1838,4763,Ría del Eo (Natura 2000),4.8,72,0,6.3,-69.8,223,23.7,5.1,438.7,961,4031 +246,42.03619,-6.78524,2,44.0,Requejo,Castilla y León,21,31,100,73,60,100,44.1,2549,412,300,412,316,Riberas del Río Tera y afluentes (Natura 2000),0.4,74,0,8.2,-50.6,1102,25.2,-0.5,317.4,2573,3843 +247,38.78188,-4.96015,2,44.0,Chillón,Castilla-La Mancha,24,66,3,100,67,81,43.7,4244,2236,200,3900,316,Sierra de Moraleja y Piedra Santa (Natura 2000),0.4,40,0,8.1,-37.1,521,33.2,2.1,209.2,6437,4673 +248,40.122,-2.54476,2,43.9,Huete,Castilla-La Mancha,19,34,72,83,83,92,44.6,2473,1389,0,1769,4816,Estepas yesosas de La Alcarria conquense (Natura 2000),4.8,71,0,7.3,-49.5,951,31.3,-0.4,104.6,5351,6968 +249,37.58352,-5.56459,2,43.9,Carmona,Andalucía,19,77,100,0,65,73,44.6,7596,2126,509,900,15861,,,0,0,0.8,-16.0,59,34.5,4.9,349.9,7634,3915 +250,42.24373,-6.55722,2,43.9,Encinedo,Castilla y León,29,39,67,0,87,100,42.9,2483,2121,0,1910,2473,Sierra de la Cabrera (Natura 2000),2.5,100,0,5.8,-150.1,1097,25.8,-0.9,315.9,2389,4074 +251,37.25094,-4.64756,2,43.8,Lucena,Andalucía,27,52,83,0,79,70,43.1,4393,761,100,100,2701,Embalse de Malpasillo (Natura 2000),2.7,0,0,6.4,-47.4,255,35.0,4.3,358.1,8156,4651 +252,40.67353,-1.45958,2,43.7,Peracense,Aragón,18,42,100,100,48,88,44.8,2954,1400,700,447,6126,Parameras de Pozondón (Natura 2000),6.2,45,0,2.8,-46.6,1101,30.5,-2.5,192.4,4893,7969 +253,37.80094,-5.34485,2,43.7,Hornachuelos,Andalucía,24,65,100,0,59,74,43.7,4143,2952,316,223,400,Barrancos del Río Retortillo (Natura 2000),0.4,0,0,8.1,-47.7,176,34.3,4.5,320.1,7437,4149 +254,40.911,0.12453,2,43.5,Valderrobres / Vall de Roures,Aragón,22,39,74,26,91,95,44.1,2325,2247,100,1711,2865,Río Matarranya (Natura 2000),2.9,91,0,2.3,-48.9,502,30.8,0.8,328.6,4821,9331 +255,39.64593,-6.07368,2,43.4,Monroy,Extremadura,18,79,81,0,72,79,44.7,5059,2147,412,1486,1360,Río Almonte (Natura 2000),1.4,0,0,1.4,-34.6,321,33.5,3.8,218.7,5301,3904 +256,41.87672,-1.84399,2,43.4,Ágreda,Castilla y León,17,43,83,100,55,100,45.0,3195,860,316,100,300,Sierra de Moncayo - Los Fayos - Sierra de Armas (Natura 2000),0.3,22,0,9.5,-93.0,659,29.4,0.6,223.5,3524,7839 +257,38.2574,-6.38723,2,43.3,Fuente de Cantos,Extremadura,22,86,83,0,51,71,44.0,5846,2817,707,100,6685, (Designación nacional),6.7,0,0,1.0,-30.9,445,34.8,2.9,329.9,6751,3343 +258,43.378,-6.97977,2,43.3,Vegadeo,Asturias / Asturies,21,30,70,100,66,100,44.3,2720,700,141,1838,7021, (Designación nacional),7.0,61,0,9.9,-125.1,592,23.4,2.8,429.8,1079,3998 +259,37.47013,-3.38907,2,43.2,Píñar,Andalucía,23,53,77,0,90,76,43.9,4031,1200,100,1600,4802, (Designación nacional),4.8,0,0,2.6,-48.3,1002,34.1,0.2,327.4,8122,5793 +260,38.79949,-1.90815,2,43.2,Albacete,Castilla-La Mancha,22,69,0,40,100,78,44.0,4272,3220,0,6044,23958,,26.2,86,0,1.5,-24.5,824,33.6,-0.4,238.4,6886,7298 +261,38.63591,-4.16053,2,43.2,Puertollano,Castilla-La Mancha,32,37,64,2,84,76,42.3,2758,1552,0,2002,4382, (Designación nacional),4.4,99,0,7.1,-46.4,772,33.9,0.5,200.3,6729,5331 +262,37.25217,-2.54999,2,43.1,Bacares,Andalucía,39,55,40,5,49,87,41.0,3006,3224,509,2773,583, (Designación nacional),0.6,98,0,7.0,-138.6,1503,31.0,-2.2,365.6,8486,6494 +263,42.07211,-2.26486,2,43.1,San Pedro Manrique,Castilla y León,22,46,81,43,69,90,44.1,2402,3000,316,1486,5571,Riberas del Río Cidacos y afluentes (Natura 2000),5.6,85,0,4.6,-103.7,1314,25.2,-2.8,218.3,3254,7526 +264,41.11352,-1.54207,2,43.1,Atea,Aragón,30,39,7,71,78,96,42.5,3001,565,223,3780,400,Sierra de Santa Cruz - Puerto de Used (Natura 2000),0.4,75,0,3.8,-47.7,988,29.7,-1.7,197.6,4400,7968 +265,41.54118,-3.90976,2,43.0,Cuevas de Provanco,Castilla y León,27,51,100,0,51,100,43.2,4295,894,500,200,10121,Sierra de Pradales (Natura 2000),10.1,0,0,6.6,-52.7,873,29.3,-0.8,126.1,3598,6083 +266,37.73492,-6.4075,2,43.0,El Castillo de las Guardas,Andalucía,20,55,83,0,88,81,44.3,5316,943,100,1442,360,Corredor Ecológico del Río Guadiamar (Natura 2000),0.4,0,0,3.1,-34.1,423,33.2,3.8,374.7,7314,3215 +267,41.64654,-3.42467,2,42.9,Peñaranda de Duero,Castilla y León,21,59,6,50,96,100,44.3,3500,3130,0,3820,3920,Riberas del Río Duero y afluentes (Natura 2000),3.9,83,0,2.8,-32.6,919,28.8,-1.0,137.9,3556,6501 +268,36.46691,-5.3447,2,42.9,Gaucín,Andalucía,25,66,83,0,50,98,43.5,4177,2765,500,100,1341,Valle del Río del Genal (Natura 2000),1.4,0,0,6.9,-66.0,106,30.4,8.0,456.9,8886,3888 +269,40.74189,-6.78063,2,42.8,Villar de Ciervo,Castilla y León,18,49,0,100,93,100,44.7,3244,2102,100,0,4080,Douro Internacional (Natura 2000),4.1,52,0,1.3,-41.2,618,28.0,2.5,264.2,3980,3554 +270,38.67325,-2.82749,2,42.8,Montiel,Castilla-La Mancha,27,54,29,87,58,69,43.2,3894,1700,565,3087,6087,Áreas esteparias del Campo de Montiel (Natura 2000),6.1,69,0,2.6,-35.6,946,35.2,-0.9,208.3,6894,6485 +271,42.17033,-3.29517,2,42.7,San Millán de Lara,Castilla y León,32,47,100,43,27,86,42.2,2906,2700,2137,854,640,Sierra de la Demanda (Natura 2000),0.7,85,0,7.7,-65.9,1493,24.0,-3.5,196.9,3002,6702 +272,38.15676,-5.38016,2,42.7,Fuente Obejuna,Andalucía,28,54,28,0,98,78,42.9,4492,1749,0,3124,509,Guadiato-Bembézar (Natura 2000),0.6,0,0,2.3,-47.2,568,33.7,2.3,286.8,7044,4189 +273,42.57592,-7.32609,2,42.6,A Pobra do Brollón,Galicia,23,28,56,78,76,100,43.9,2641,500,100,2262,1403,Ancares - Courel (Natura 2000),1.5,73,0,7.4,-56.8,643,25.7,1.9,389.1,1886,3534 +274,41.5908,-4.40746,2,42.6,Sardón de Duero,Castilla y León,18,34,65,45,99,100,44.8,2247,1414,0,1972,2213,Riberas del Río Duero y afluentes (Natura 2000),2.2,84,0,1.9,-48.2,785,29.3,-0.4,143.8,3466,5684 +275,42.69461,-6.59972,2,42.4,Cubillos del Sil,Castilla y León,26,38,74,2,82,100,43.3,2376,2024,0,1711,7877,Sierra de los Ancares (Natura 2000),7.9,99,0,7.7,-53.0,870,25.9,-0.4,352.5,1891,4142 +276,42.12651,1.16947,2,42.2,Coll de Nargó,Catalunya,24,42,100,2,64,100,43.6,2308,2817,282,141,1000,"Serres del Montsec, Sant Mamet i Mitjana (Natura 2000)",1.0,99,0,7.1,-64.9,949,26.5,-0.8,449.0,3592,10353 +277,43.21812,-6.70883,2,42.1,Allande,Asturias / Asturies,27,47,38,87,41,100,43.1,2906,2758,700,2831,1000,Sierra de los Lagos (Natura 2000),1.0,69,0,5.3,-201.9,885,23.4,0.9,401.8,1302,4175 +278,40.31986,-2.10745,2,42.1,Arcos de la Sierra,Castilla-La Mancha,18,31,72,57,86,100,44.7,2416,700,0,1772,1897,Serranía de Cuenca (Natura 2000),1.9,80,0,6.4,-46.5,1061,29.9,-0.9,136.6,5195,7369 +279,42.08446,-6.08848,2,42.0,San Pedro de Ceque,Castilla y León,21,63,39,0,90,100,44.2,4527,2154,141,2801,6260,Lagunas de Tera y Vidriales (Natura 2000),6.3,0,0,1.5,-41.7,801,28.2,-0.6,274.7,2646,4417 +280,40.58612,-0.41227,1,41.8,Mirambel,Aragón,17,39,96,100,45,85,45.0,3201,500,1004,1029,2692,Muelas y Estrechos del río Guadalope (Natura 2000),2.7,22,0,1.2,-98.3,1197,28.9,-3.6,280.3,5117,8836 +281,42.54719,-8.13576,1,41.6,O Irixo,Galicia,18,29,84,50,82,100,44.8,2267,1118,223,1389,2941, (Designación nacional),3.0,83,0,2.3,-54.5,659,24.8,1.9,442.8,1761,2881 +282,39.88463,-4.84814,1,41.6,Las Herencias,Castilla-La Mancha,22,40,80,80,45,86,44.0,3087,509,1118,1513,2059,Barrancas de Talavera (Natura 2000),2.1,72,0,1.4,-41.5,423,32.3,2.3,113.3,5255,4984 +283,39.38059,-5.70917,1,41.6,Herguijuela,Extremadura,18,38,30,100,85,86,44.8,3001,608,200,3059,4531, (Designación nacional),4.5,36,0,2.1,-72.6,562,32.4,2.7,205.1,5655,4156 +284,42.69447,-6.87004,1,41.5,Trabadelo,Castilla y León,28,41,72,0,67,98,42.9,2475,2267,282,1780,894,Sierra de los Ancares (Natura 2000),0.9,100,0,6.3,-54.0,1157,23.8,-1.3,368.6,1842,3926 +285,39.9755,-3.26873,1,41.3,Villarrubia de Santiago,Castilla-La Mancha,20,82,0,0,100,78,44.4,6040,2262,0,6306,412,Yesares del valle del Tajo (Natura 2000),0.5,0,0,1.4,-27.9,674,33.6,0.5,61.8,5406,6332 +286,40.01256,-1.88119,1,41.3,Cañada del Hoyo,Castilla-La Mancha,24,43,92,0,66,95,43.7,3014,1746,300,1140,1118,Serranía de Cuenca (Natura 2000),1.1,100,0,6.1,-37.0,1099,30.4,-1.5,162.6,5562,7511 +287,40.48811,-2.28871,1,41.1,Priego,Castilla-La Mancha,24,46,53,50,60,95,43.7,2531,2088,400,2360,412,Serranía de Cuenca (Natura 2000),0.4,83,0,5.8,-53.5,893,30.8,0.3,120.5,4986,7244 +288,41.17258,-3.52024,1,40.9,Santo Tomé del Puerto,Castilla y León,39,59,83,17,22,90,41.0,3087,3716,806,100,806, (Designación nacional),0.8,94,0,9.5,-161.0,1534,24.7,-2.8,84.9,4060,6336 +289,38.37211,-2.14987,1,40.9,Letur,Castilla-La Mancha,22,32,100,0,88,71,44.0,2202,1581,0,316,316,Sierra de Alcaraz y Segura y cañones del Segura y del Mundo (Natura 2000),0.4,100,0,5.4,-53.0,659,34.8,0.7,264.9,7320,7022 +290,42.33391,2.76509,1,40.9,Sant Llorenç de la Muga,Catalunya,20,32,98,0,83,100,44.4,2126,1972,0,948,700,Alta Garrotxa-Massís de les Salines (Natura 2000),0.7,100,0,7.3,-55.5,324,26.4,2.4,580.4,3504,11689 +291,42.68066,-1.60972,1,40.8,Unzué / Untzue,Navarra / Nafarroa,17,30,83,64,70,100,44.9,2308,1345,100,100,2701, (Designación nacional),2.7,78,0,9.5,-114.8,798,26.2,0.0,304.1,2672,8159 +292,43.07217,-4.12918,1,40.8,Campoo de Enmedio,Cantabria,24,49,0,100,57,100,43.7,2900,2801,509,0,583,Valles altos del Nansa y Saja y Alto Campoo (Natura 2000),0.6,60,0,4.1,-124.9,926,23.4,0.8,297.1,1891,6198 +293,42.44665,-3.39528,1,40.6,Villaescusa la Sombría,Castilla y León,25,41,67,33,57,100,43.6,2601,2500,632,1910,4186,Riberas del Río Oca y afluentes (Natura 2000),4.2,88,0,1.3,-46.1,894,27.1,-0.5,226.1,2685,6671 +294,37.62171,-4.42565,1,40.6,Baena,Andalucía,32,55,0,0,92,66,42.2,4686,424,100,5119,9701,Sierras Subbéticas (Natura 2000),9.8,0,0,1.8,-54.1,317,35.7,3.8,314.3,7790,4914 +295,40.82682,-1.06109,1,40.6,Alpeñés,Aragón,23,31,100,83,30,86,43.9,2778,400,1118,282,4964,Parameras de Campo Visiedo (Natura 2000),5.0,71,0,6.5,-46.4,1182,29.6,-3.4,228.2,4775,8326 +296,36.95148,-5.63786,1,40.2,Villamartín,Andalucía,18,81,28,0,79,84,44.7,5693,2954,300,3140,948, (Designación nacional),1.0,0,0,0.8,-29.2,117,32.7,6.1,415.7,8307,3725 +297,42.80146,-6.46478,1,40.0,Páramo del Sil,Castilla y León,23,45,0,94,66,97,43.9,2720,2555,223,0,2983,Alto Sil (Natura 2000),3.0,67,0,7.7,-122.2,1155,24.0,-1.6,353.5,1799,4274 +298,37.6416,-6.4402,1,40.0,El Madroño,Andalucía,20,39,68,0,98,79,44.4,3858,670,0,1897,1029, (Designación nacional),1.1,0,0,2.0,-47.7,317,33.5,4.5,384.6,7409,3167 +299,40.0033,-6.17041,1,39.9,Plasencia,Extremadura,20,44,57,94,40,91,44.4,2596,2088,781,2247,4220,Ríos Alagon y Jerte (Natura 2000),4.3,67,0,3.6,-49.7,394,31.5,3.4,214.7,4895,3899 +300,37.171,-4.87458,1,39.8,Pedrera,Andalucía,21,62,19,0,98,82,44.3,5100,1700,0,3413,6000,Laguna del Gosque (Natura 2000),6.0,0,0,2.2,-36.0,541,32.9,3.0,371.3,8204,4437 +301,39.63544,-5.74206,1,39.6,Deleitosa,Extremadura,18,79,100,0,32,82,44.8,4317,3687,781,640,565,Riberos del Almonte (Natura 2000),0.6,0,0,6.5,-41.2,406,33.0,3.2,193.4,5372,4181 +302,37.51085,-4.09655,1,39.6,Fuente-Tójar,Andalucía,21,38,83,0,86,65,44.1,3935,500,200,100,8984,Laguna Honda (Natura 2000),9.0,0,0,1.6,-53.3,448,35.9,3.4,322.5,7965,5181 +303,41.99971,-7.48603,1,39.5,Monterrei,Galicia,17,29,52,73,76,100,44.9,2360,761,100,2376,2376,Río Támega (Natura 2000),2.4,74,0,7.2,-65.5,621,26.0,2.1,365.5,2481,3269 +304,38.65357,-2.10181,1,39.4,Alcadozo,Castilla-La Mancha,30,40,0,21,90,81,42.6,2662,1835,100,6016,9047,Sierra de Alcaraz y Segura y cañones del Segura y del Mundo (Natura 2000),9.1,93,0,2.4,-51.4,1078,32.8,-1.5,240.8,7019,7108 +305,40.39524,-5.10181,1,39.1,Navarredonda de Gredos,Castilla y León,24,61,60,0,51,91,43.7,4101,2912,632,2147,4753,Riberas del Río Alberche y afluentes (Natura 2000),4.8,0,0,3.4,-50.5,1583,24.8,-2.5,119.0,4656,4873 +306,40.53219,-6.28095,1,39.1,Serradilla del Arroyo,Castilla y León,26,36,55,57,42,100,43.3,2901,700,2701,2308,670,Las Batuecas-Sierra de Francia -ZEPA (Natura 2000),0.7,80,0,2.5,-44.6,1061,24.9,0.7,219.6,4300,3921 +307,42.02831,-7.28108,1,38.8,O Riós,Galicia,29,37,87,7,36,100,42.9,2517,1697,2302,1303,7178, (Designación nacional),7.2,98,0,4.5,-56.2,961,24.6,0.1,352.1,2489,3441 +308,43.00351,-1.25972,1,38.6,Orbaizeta,Navarra / Nafarroa,19,38,83,0,71,100,44.5,2102,2416,141,100,860,Roncesvalles-Selva de Irati (Natura 2000),0.9,100,0,7.9,-61.6,944,23.9,0.1,349.7,2361,8493 +309,43.5274,-7.36262,1,38.4,Alfoz do Castrodouro,Galicia,18,21,87,0,91,100,44.7,2059,424,0,1303,2343, (Designación nacional),2.4,100,0,4.4,-54.5,101,23.8,5.9,463.1,846,3732 +310,37.55449,-7.03174,1,38.3,Alosno,Andalucía,18,96,12,0,58,81,44.7,7144,5700,608,3613,4965, (Designación nacional),5.0,0,0,1.3,-18.4,151,33.1,5.5,424.6,7389,2636 +311,42.12545,-2.43117,1,37.9,Villar del Río,Castilla y León,24,42,47,2,74,89,43.7,2280,2657,141,2555,1140, (Designación nacional),1.2,99,0,7.1,-130.4,1381,24.0,-2.8,216.1,3173,7399 +312,40.40225,-3.09018,1,37.9,Escariche,Castilla-La Mancha,19,34,100,43,43,88,44.6,2801,670,2100,608,4178,"Vegas, cuestas y páramos del sureste de Madrid (Natura 2000)",4.2,85,0,2.1,-39.7,751,32.0,-0.0,52.3,4966,6557 +313,40.92521,-2.35699,1,37.6,Anguita,Castilla-La Mancha,32,32,70,9,33,95,42.2,2816,412,1200,1843,721,Alto Tajo (Natura 2000),0.8,97,0,5.5,-46.2,1094,29.6,-1.9,126.6,4498,7259 +314,37.84734,-5.50547,1,37.6,Constantina,Andalucía,25,58,63,0,45,79,43.6,6926,761,3255,2061,761,Venta de Las Navas (Natura 2000),0.8,0,0,1.1,-24.6,448,33.4,3.1,321.8,7358,4019 +315,41.40059,-2.63412,1,37.4,Almazán,Castilla y León,20,44,8,35,87,95,44.4,2501,2596,100,3748,1486,Altos de Barahona (Natura 2000),1.5,88,0,3.7,-44.3,1047,28.5,-1.9,140.6,3939,7109 +316,36.72471,-5.62758,1,37.1,Arcos de la Frontera,Andalucía,18,60,0,0,99,91,44.8,3701,2200,0,5053,5008,Los Alcornocales (Natura 2000),5.1,0,0,1.9,-49.1,207,31.5,6.3,438.4,8555,3689 +317,37.2741,-3.39975,1,37.0,Quéntar,Andalucía,19,50,0,64,70,92,44.6,2500,2789,282,0,509,Sierra de Huétor (Natura 2000),0.5,78,0,4.9,-46.1,1375,30.9,-1.4,348.9,8334,5750 +318,38.59289,-1.30614,1,36.8,Jumilla,Región de Murcia,17,46,0,40,97,86,45.0,3448,500,0,4854,8792,Sierra del Buey (Natura 2000),8.8,86,0,2.6,-29.6,840,32.4,-0.5,291.2,7192,7787 +319,37.45642,-6.62209,1,36.7,Niebla,Andalucía,18,46,34,0,93,80,44.7,3733,761,100,2954,2863, (Designación nacional),2.9,0,0,1.1,-48.6,80,33.3,5.8,410.4,7575,2970 +320,42.91111,-2.7733,1,36.5,Vitoria-Gasteiz,Euskadi,22,35,100,0,37,100,44.0,2220,2555,1216,721,5590, (Designación nacional),5.6,100,0,4.1,-51.6,655,24.3,0.6,286.2,2265,7257 +321,42.9357,-2.12229,1,36.4,Bakaiku,Navarra / Nafarroa,19,41,99,0,41,100,44.5,2102,2039,500,921,509,Aralar (Natura 2000),0.6,100,0,10.0,-49.0,760,22.8,0.7,307.3,2326,7786 +322,43.23326,-6.39257,1,36.3,Cangas del Narcea,Asturias / Asturies,22,35,0,100,47,100,44.0,3182,943,412,0,1208,Peña Manteca-Genestaza (Natura 2000),1.2,40,0,9.8,-80.5,829,23.2,0.7,387.0,1342,4429 +323,42.81873,-1.26757,1,36.0,Arce / Artzi,Navarra / Nafarroa,19,35,99,12,41,100,44.6,2126,2080,509,921,640,"Sierra de Artxuga, Zarikieta y Montes de Areta (Natura 2000)",0.6,96,0,9.6,-68.9,841,25.4,0.4,333.0,2563,8458 +324,42.98434,-5.35635,1,36.0,Valdelugueros,Castilla y León,27,40,8,0,74,93,43.1,2435,2280,200,3764,806,Picos de Europa en Castilla y León (Natura 2000),0.8,100,0,5.6,-162.2,1419,22.9,-2.2,318.2,1791,5199 +325,40.46167,-5.21749,1,35.9,Navaescurial,Castilla y León,17,57,39,0,71,87,45.0,3640,2720,100,2780,2765,Riberas del Río Tormes y afluentes (Natura 2000),2.8,0,0,9.0,-70.9,1705,23.6,-3.3,128.9,4564,4790 +326,42.09755,-7.3646,1,35.8,Laza,Galicia,20,41,0,100,44,100,44.3,2729,1811,948,0,1414, (Designación nacional),1.4,60,0,1.9,-119.6,983,24.7,0.4,362.0,2398,3390 +327,39.75977,-5.19642,1,35.5,Villar del Pedroso,Extremadura,19,48,0,100,45,81,44.5,4143,500,921,0,3280,Embalse de Valdecañas (Natura 2000),3.3,35,0,1.1,-35.9,395,33.1,2.6,145.9,5332,4666 +328,39.92833,-1.24657,1,35.5,Santa Cruz de Moya,Castilla-La Mancha,23,42,0,52,59,96,43.9,2500,2209,400,0,707,Rentos de Orchova y Páramos de Moya (Natura 2000),0.7,82,0,6.0,-50.4,922,30.6,-0.9,217.6,5737,8036 +329,39.78426,-6.56638,1,35.5,Acehúche,Extremadura,18,76,26,0,52,84,44.8,4545,3395,700,3190,1676, (Designación nacional),1.7,0,0,1.5,-39.3,278,32.7,4.6,253.3,5060,3520 +330,37.55904,-3.72091,1,35.4,Valdepeñas de Jaén,Andalucía,26,62,24,0,43,78,43.3,4272,2209,1600,3269,3900, (Designación nacional),3.9,0,0,2.1,-141.3,1022,33.6,0.8,315.9,7973,5518 +331,42.26146,-7.97137,1,35.4,Cartelle,Galicia,18,27,51,0,86,100,44.7,2061,1077,200,2416,18335,,18.4,100,0,1.2,-53.7,451,26.9,2.4,415.0,2103,2942 +332,37.92338,-2.36457,1,35.2,Puebla de Don Fadrique,Andalucía,17,79,3,0,71,74,45.0,5060,2942,424,3894,1702,Sierra de La Sagra (Natura 2000),1.7,0,0,1.4,-33.6,1059,33.8,-1.6,300.9,7780,6764 +333,42.4132,1.38713,1,34.9,les Valls de Valira,Catalunya,24,46,0,7,79,76,43.6,2334,3264,0,0,300, (Designación nacional),0.3,98,0,8.6,-51.2,1636,21.4,-5.3,478.3,3297,10566 +334,36.88438,-4.66863,1,34.9,Álora,Andalucía,20,44,83,0,42,84,44.4,3832,424,583,100,4200, (Designación nacional),4.2,0,0,7.6,-68.0,245,32.7,5.4,397.9,8551,4564 +335,38.06603,-5.6859,1,34.8,Alanís,Andalucía,17,51,59,0,61,79,44.9,3661,1676,360,2184,360,Sierra de Alanís (Natura 2000),0.4,0,0,6.5,-50.6,578,33.4,2.5,309.0,7088,3907 +336,42.62696,-1.41815,1,34.8,Leache / Leatxe,Navarra / Nafarroa,18,37,0,33,86,100,44.7,2213,2158,0,0,5703,Peña Izaga (Natura 2000),5.7,88,0,6.4,-59.5,779,26.3,0.2,308.8,2755,8306 +337,41.09537,-1.91714,1,34.8,Milmarcos,Castilla-La Mancha,32,44,0,31,32,93,42.2,3138,1403,721,4338,424,"Parameras de Maranchón, hoz del Mesa y Aragoncillo (Natura 2000)",0.5,89,0,7.6,-43.9,1118,29.1,-2.2,168.0,4371,7653 +338,41.31361,-2.88332,1,34.8,Romanillos de Atienza,Castilla-La Mancha,17,52,67,0,48,94,45.0,2000,3889,670,1910,854,Altos de Barahona (Natura 2000),0.9,100,0,3.4,-53.4,1135,28.8,-2.0,120.3,3999,6888 +339,41.41404,-3.12013,1,34.2,Montejo de Tiermes,Castilla y León,19,37,0,100,41,97,44.5,2765,1664,781,0,2846,Encinares de Tiermes (Natura 2000),2.9,55,0,3.3,-53.8,1030,28.9,-1.4,120.3,3855,6710 +340,42.37823,-3.11133,1,34.1,Belorado,Castilla y León,23,33,83,0,30,100,43.8,2247,1600,700,100,989, (Designación nacional),1.0,100,0,8.8,-59.7,980,25.5,-0.7,222.3,2801,6889 +341,36.83702,-2.71879,1,33.9,Vícar,Andalucía,20,38,0,0,92,92,44.3,2137,2334,0,0,316,Sierras de Gádor y Enix (Natura 2000),0.4,100,0,4.2,-76.6,641,31.4,3.0,406.3,8914,6278 +342,41.16069,-2.6174,1,33.8,Sigüenza,Castilla-La Mancha,17,39,0,100,46,93,45.0,2529,1910,700,4883,6859,Valle y salinas del Salado (Natura 2000),6.9,62,0,3.4,-50.6,1128,28.9,-2.2,122.6,4204,7082 +343,37.90094,-3.83054,1,33.2,Jaén,Andalucía,17,44,29,0,88,58,44.9,4110,1044,141,3100,9007, (Designación nacional),9.1,0,0,2.4,-42.9,339,36.9,3.5,278.2,7583,5483 +344,40.25139,-5.04689,1,33.1,Mombeltrán,Castilla y León,17,25,87,9,48,100,45.0,2039,412,412,1300,583,Sierra de Gredos (Natura 2000),0.6,97,0,9.5,-63.6,737,28.5,1.6,115.6,4822,4890 +345,39.51226,-3.4214,1,33.1,Madridejos,Castilla-La Mancha,20,79,0,0,45,73,44.3,8532,3001,1303,8234,3001,Área esteparia de La Mancha norte (Natura 2000),3.1,0,0,1.1,-11.1,669,34.5,0.6,103.4,5889,6121 +346,38.24839,-5.55693,1,32.9,Granja de Torrehermosa,Extremadura,17,42,83,0,44,71,44.9,4204,670,1077,100,3224,Río Bembezar (Natura 2000),3.3,0,0,1.8,-42.2,497,34.8,2.4,286.1,6913,4055 +347,39.21361,-6.00123,1,32.5,Zarza de Montánchez,Extremadura,27,43,25,0,42,79,43.2,2404,1664,1220,3231,3059, (Designación nacional),3.1,100,0,2.6,-51.1,399,33.4,3.4,236.1,5784,3874 +348,40.10928,-0.50875,1,30.9,Montanejos,Comunitat Valenciana,17,24,0,0,96,97,45.0,2002,905,0,0,3046,Serra d'Espadà (ZEPA) (Natura 2000),3.1,100,0,2.8,-130.0,592,30.5,0.7,275.3,5629,8688 +349,40.9696,0.25614,1,30.6,Arens de Lledó / Arenys de Lledó,Aragón,17,26,0,33,76,94,45.0,2154,1627,0,0,2202, (Designación nacional),2.2,88,0,9.8,-48.1,431,31.0,1.4,340.5,4771,9449 +350,41.78822,2.65405,1,30.3,Massanes,Catalunya,18,24,94,0,23,100,44.7,2061,700,1170,1100,1720, (Designación nacional),1.8,100,0,8.9,-48.3,118,28.5,2.2,555.0,4097,11543 +351,42.47891,-0.72826,1,29.9,Bailo,Aragón,17,50,51,0,23,100,44.9,2009,4441,1063,2433,1389, (Designación nacional),1.4,100,0,8.9,-59.3,800,27.6,1.1,335.9,3001,8845 +352,38.41045,-2.82071,1,29.7,La Puerta de Segura,Andalucía,17,36,90,0,25,68,44.9,3667,412,1720,1208,412, (Designación nacional),0.5,0,0,8.2,-49.4,714,35.4,0.6,235.8,7182,6447 +353,43.05167,-3.79465,1,29.1,Merindad de Valdeporres,Castilla y León,20,27,0,0,68,100,44.4,2118,905,400,0,5000,Río Pas (Natura 2000),5.0,100,0,3.0,-114.4,788,24.3,1.4,292.4,1964,6462 +354,43.17315,-4.52698,1,27.6,Cillorigo de Liébana,Cantabria,20,37,0,14,35,100,44.3,2202,2163,608,0,500,Liébana (Natura 2000),0.5,95,0,9.5,-180.5,1084,22.3,-0.1,314.2,1719,5900 +355,42.56084,-0.02154,1,26.1,Fanlo,Aragón,20,36,0,0,37,91,44.3,2140,1979,707,0,670,Cuenca del río Yesa (Natura 2000),0.7,100,0,6.4,-154.4,1363,24.2,-2.6,386.8,2991,9433 diff --git a/out/informe.html b/out/informe.html new file mode 100644 index 0000000..30a044d --- /dev/null +++ b/out/informe.html @@ -0,0 +1,1078 @@ +Dónde no hay nadie · aislamiento en España + + +
+ +
+
Análisis geoespacial · malla de 100 m · península y Baleares
+

Dónde no
hay nadie

+

Un barrido de los 498.528 km² de la España peninsular y balear, + celda a celda, buscando sitios lejos de todo a los que además se pueda llegar — con + los límites oficiales de espacios protegidos, no los aproximados.

+ +
+ +
+
+

Corrección sobre la primera versión. Los espacios protegidos se + tomaron primero de OpenStreetMap, que resultó cubrir solo el 15,3 % de España. Los + datos oficiales —Red Natura 2000 y espacios de designación nacional, descargados de + los servicios de la Agencia Europea de Medio Ambiente— cubren el 29,0 %. + Eran 87.480 km² protegidos que no se estaban viendo.

+

Con el límite bueno, 33 de los 60 candidatos de la primera versión estaban + dentro de espacio protegido, incluidos los seis primeros. Ese ranking queda + anulado y sustituido por el de aquí abajo.

+
+
+ +
+
+
Lo que sale
+

Cinco cifras

+
+
+
29,0 %De España está protegido: 144.439 km² + entre Red Natura 2000 y figuras nacionales.
+
47.519 km²Pasan las eliminatorias de acceso, terreno + y legalidad: el 9,5 % del país.
+
505 km²Y de eso, lo que además baja de 45 dB + en la casa más cercana. El sonido es el cuello de botella real.
+
9,8 kmLa mayor distancia a un edificio que se + alcanza en España. Menos de lo que casi nadie supone.
+
355Localizaciones finales, valoradas de 1 a 5 estrellas + y separadas 10 km entre sí.
+
+
+ +
+
+
El mapa
+

El vacío tiene forma de red

+
+
+
+ Mapa de aislamiento de España. Las carreteras dibujan una malla clara sobre
+        el país; las zonas más aisladas aparecen en azul denso en Sierra Morena, Montes de
+        Toledo, La Serena, los Monegros, Doñana y el Pirineo. + Mapa de aislamiento de España en modo oscuro. Las zonas más aisladas
+        aparecen claras sobre fondo oscuro. +
+
+ pobladovacío + candidatos de 3★ o más +
+
Cada píxel es la media geométrica de tres distancias: al edificio más + cercano, a la carretera más cercana y al núcleo de población más cercano. La red de + carreteras se dibuja sola en negativo. Lo que queda entre las mallas es lo que + buscamos. Galicia y la costa mediterránea, pese a la fama de una y el vacío aparente + de la otra, casi no tienen huecos: el poblamiento disperso llega a todas partes.
+
+
+ +
+
+
Método
+

Seis criterios y una eliminatoria

+
+
+

Primero se descarta. Una celda queda fuera, por buena que sea, si está dentro de un + espacio protegido oficial o a menos de 300 m de su borde, en zona militar, en + agua, a menos de 2.000 m de un edificio, a menos de 400 m de una carretera, + a más de 800 m de un vial por el que meter un coche, o con más de 10° de + pendiente. Sobreviven 47.519 km², el 9,5 % del país.

+

Encima de eso hay un umbral acústico: si el nivel estimado en la casa más cercana + supera los 45 dB —el límite nocturno típico en suelo rural— el punto se descarta + aunque puntúe bien en todo lo demás. Ese filtro es el que de verdad corta: deja + 505 km² en toda España. El sonido, y no la soledad, es el cuello de botella.

+

Lo que sobrevive se puntúa en seis criterios independientes, y 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 un sitio de cuatro estrellas.

+
+
+

Sonido 30 %

+ No es una nota abstracta: es propagación real. Divergencia esférica, absorción + atmosférica en frecuencias bajas, apantallamiento del relieve y absorción del + arbolado, partiendo de 130 dB a 1 m. El resultado es cuántos dB llegan a la casa más + cercana. El mejor punto del país se queda en 33,2 dB.
+

Soledad 22 %

+ Lejanía de edificios, de carreteras y de núcleos, y cuánta construcción hay en + 5 km a la redonda.
+

Acceso 18 %

+ Pista en condiciones cerca. OSM distingue firme y grado, así que una pista + compactada no cuenta igual que una grade5 embarrada. Más llano y sin pedregal.
+

Agua 12 %

+ Río, arroyo o fuente entre 120 y 900 m. Cerca, pero no encima.
+

Arbolado 10 %

+ Entre el 20 y el 65 % de cobertura alrededor: sombra y pantalla visual sin que + sea selva por la que no se pasa.
+

Clima 8 %

+ Máxima del mes más cálido y mínima del más frío, de WorldClim, corregidas por + altitud con el DEM de 100 m.
+
+
+

Los candidatos no se eligen cogiendo los mejores del país, porque saldrían todos del + mismo rincón. Se divide España en bloques de 20 km y se coge el mejor punto elegible de + cada bloque; después se exige un mínimo de 10 km entre los elegidos, porque dos + bloques vecinos pueden escoger cada uno una celda pegada a su frontera común y quedar a + cien metros. Salen 355 opciones repartidas por todas partes y con un abanico natural de + estrellas.

+
+
+
★★★★★11
+
★★★★36
+
★★★90
+
★★142
+
76
+
+
+ +
+
+
Resultados
+

Las 47 mejores

+
+
+

Todas las de cuatro y cinco estrellas. dB es el nivel estimado en la casa más + cercana; por debajo de 35 es ruido de fondo del campo de noche. casa y + agua en kilómetros, pista en metros, árbol en porcentaje de + cobertura, prot la distancia al espacio protegido más cercano.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#valorcoordenadasmunicipiocomunidaddBcasa kmpista magua kmárbol %prot kmMadrid kmver
1★★★★★39.36483, -5.24824AlíaExtremadura36.66,400,1333,1174.9sat +sv
2★★★★★39.28557, -4.82193Helechosa de los MontesExtremadura34.66,51002,1654,6156.5sat +sv
3★★★★★39.02884, -4.54425Puebla de Don RodrigoCastilla-La Mancha36.15,801,3611,1168.6sat +sv
4★★★★★38.69868, -4.38489BrazatortasCastilla-La Mancha35.66,801,8591,7198.0sat +sv
5★★★★★39.4601, -5.5653BerzocanaExtremadura35.16,95000,4513,9190.0sat +sv
6★★★★★39.35756, -4.12491Retuerta del BullaqueCastilla-La Mancha34.36,002,7830,4122.1sat +sv
7★★★★★39.18565, -4.88608Fuenlabrada de los MontesExtremadura33.27,02824,6743,2168.6sat +sv
8★★★★★38.79136, -5.82379Quintana de la SerenaExtremadura35.96,104,1680,5254.0sat +sv
9★★★★★37.12819, -5.34452Morón de la FronteraAndalucía37.76,400,1174,7388.2sat +sv
10★★★★★39.31406, -4.51529AlcobaCastilla-La Mancha37.66,62001,2310,4139.5sat +sv
11★★★★★40.06814, -2.3802Abia de la ObispalíaCastilla-La Mancha36.55,401,87012,9119.9sat +sv
12★★★★41.63594, -1.77527Aranda de MoncayoAragón37.05,101,8733,0209.9sat +sv
13★★★★39.94037, -6.75562MoralejaExtremadura36.57,801,700,3264.9sat +sv
14★★★★40.44825, -5.5637El TejadoCastilla y León38.34,801,4623,1158.3sat +sv
15★★★★39.1442, -4.30836PiedrabuenaCastilla-La Mancha36.26,01002,4523,3149.3sat +sv
16★★★★37.67526, -3.03813HuesaAndalucía38.65,51410,1321,5309.5sat +sv
17★★★★39.41756, -4.69774Helechosa de los MontesExtremadura37.26,11412,1690,7138.5sat +sv
18★★★★40.29354, -2.68815HueteCastilla-La Mancha37.46,45830,1351,787.8sat +sv
19★★★★42.69107, -4.73172Congosto de ValdaviaCastilla y León38.35,01000,1882,7267.6sat +sv
20★★★★39.15173, -4.46609PiedrabuenaCastilla-La Mancha37.25,14242,6703,4153.6sat +sv
21★★★★38.98983, -4.75109AgudoCastilla-La Mancha38.06,41002,6126,0180.5sat +sv
22★★★★40.06355, -6.87354CillerosExtremadura37.36,810770,7201,5272.5sat +sv
23★★★★39.75725, -1.39878GaraballaCastilla-La Mancha36.94,501,11001,0211.3sat +sv
24★★★★41.74254, -1.60532AmbelAragón38.24,803,1634,5228.3sat +sv
25★★★★39.17383, -5.2821TalarrubiasExtremadura37.46,44244,4454,0191.5sat +sv
26★★★★38.84744, -4.48759AbenójarCastilla-La Mancha39.58,83161,8303,6185.2sat +sv
27★★★★39.63124, -4.83664Torrecilla de la JaraCastilla-La Mancha37.74,92000,1710,8129.2sat +sv
28★★★★38.2372, -6.17273Fuente de CantosExtremadura41.27,201,0642,9319.1sat +sv
29★★★★39.82924, -1.94614Monteagudo de las SalinasCastilla-La Mancha37.34,61002,3858,1164.7sat +sv
30★★★★39.26777, -4.9603Herrera del DuqueExtremadura34.66,13605,7920,9165.4sat +sv
31★★★★41.25428, -1.32647CodosAragón37.14,81000,5801,1220.3sat +sv
32★★★★38.58789, -5.74676Zalamea de la SerenaExtremadura40.63,800,2654,3266.0sat +sv
33★★★★38.17857, -6.04108TrasierraExtremadura37.15,51003,4696,5316.8sat +sv
34★★★★39.21502, -6.90347AlburquerqueExtremadura40.37,703,3361,0303.0sat +sv
35★★★★42.77336, -5.32078VegaquemadaCastilla y León37.24,901,29013,7295.9sat +sv
36★★★★37.8542, -7.07419ArocheAndalucía37.07,35000,900,6403.2sat +sv
37★★★★37.15841, -5.13089OsunaAndalucía35.68,23002,307,0378.9sat +sv
38★★★★41.31303, 0.23323Mequinenza / MequinensaAragón37.75,11005,9731,9346.1sat +sv
39★★★★39.523, -4.89553Sevilleja de la JaraCastilla-La Mancha37.46,86320,8102,1141.0sat +sv
40★★★★38.04833, -6.15128MonesterioExtremadura39.95,96400,5276,3333.9sat +sv
41★★★★39.33611, -3.9768Los CortijosCastilla-La Mancha39.94,71000,1470,8121.5sat +sv
42★★★★40.22638, -6.99076Valverde del FresnoExtremadura39.53,700,1871,0280.4sat +sv
43★★★★40.46339, -0.98534PeralejosAragón39.34,61412,4510,5231.4sat +sv
44★★★★37.42111, -2.46481LúcarAndalucía40.93,81000,2612,7350.0sat +sv
45★★★★39.10465, -3.99852MiguelturraCastilla-La Mancha39.05,204,5352,5147.0sat +sv
46★★★★39.99736, -1.71303CañeteCastilla-La Mancha39.24,51002,1741,2176.9sat +sv
47★★★★41.68438, -2.95459TalveilaCastilla y León41.35,002,3660,7153.2sat +sv
+
+
+ +
+
+
Límites
+

Lo que este análisis no sabe

+
+
+
    +
  • No ve vallas ni propiedad

    Casi todo el suroeste que domina la lista es + dehesa privada, en fincas cerradas y cotos de caza. El modelo ve una pista que llega; + no ve la cadena y el candado a la entrada. Sigue siendo la mayor diferencia entre el + ranking y la realidad de campo.
  • +
  • Los protegidos son de diciembre de 2024

    Son los límites oficiales, pero + las figuras autonómicas cambian y hay ordenanzas municipales que no están en ningún + mapa nacional. El margen de 300 m ayuda, no exime de comprobarlo.
  • +
  • El modelo acústico es de manual

    Divergencia, absorción y apantallamiento + son buenas aproximaciones, pero una inversión térmica nocturna o el viento a favor + pueden llevar el bajo mucho más lejos de lo que dice el número.
  • +
  • Ignora la estación

    Buena parte de estos sitios son monte mediterráneo en + 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.
  • +
+
+
+ +
+

Datos: OpenStreetMap (ODbL) para edificios, viales, agua, arbolado y roca; + Red Natura 2000 y espacios de designación nacional vía Agencia Europea de Medio Ambiente; + Copernicus DEM GLO-90 para el relieve; WorldClim 2.1 para el clima. + Malla EPSG:3035 a 100 m. Canarias, Ceuta y Melilla quedan fuera.

+

Visor local en out/visor.html, tabla completa en + out/candidatos_estrellas.csv, pipeline en + src/.

+
+ +
diff --git a/out/mapa_dark.png b/out/mapa_dark.png new file mode 100644 index 0000000..7bef0db Binary files /dev/null and b/out/mapa_dark.png differ diff --git a/out/mapa_light.png b/out/mapa_light.png new file mode 100644 index 0000000..71c8d31 Binary files /dev/null and b/out/mapa_light.png differ diff --git a/out/ortho/1.jpg b/out/ortho/1.jpg new file mode 100644 index 0000000..bd02854 Binary files /dev/null and b/out/ortho/1.jpg differ diff --git a/out/ortho/10.jpg b/out/ortho/10.jpg new file mode 100644 index 0000000..8a79d3a Binary files /dev/null and b/out/ortho/10.jpg differ diff --git a/out/ortho/100.jpg b/out/ortho/100.jpg new file mode 100644 index 0000000..53634a9 Binary files /dev/null and b/out/ortho/100.jpg differ diff --git a/out/ortho/101.jpg b/out/ortho/101.jpg new file mode 100644 index 0000000..6362f7b Binary files /dev/null and b/out/ortho/101.jpg differ diff --git a/out/ortho/102.jpg b/out/ortho/102.jpg new file mode 100644 index 0000000..4e2885b Binary files /dev/null and b/out/ortho/102.jpg differ diff --git a/out/ortho/103.jpg b/out/ortho/103.jpg new file mode 100644 index 0000000..a01db1b Binary files /dev/null and b/out/ortho/103.jpg differ diff --git a/out/ortho/104.jpg b/out/ortho/104.jpg new file mode 100644 index 0000000..f679843 Binary files /dev/null and b/out/ortho/104.jpg differ diff --git a/out/ortho/105.jpg b/out/ortho/105.jpg new file mode 100644 index 0000000..fa735e5 Binary files /dev/null and b/out/ortho/105.jpg differ diff --git a/out/ortho/106.jpg b/out/ortho/106.jpg new file mode 100644 index 0000000..a5f4007 Binary files /dev/null and b/out/ortho/106.jpg differ diff --git a/out/ortho/107.jpg b/out/ortho/107.jpg new file mode 100644 index 0000000..5c6b6fe Binary files /dev/null and b/out/ortho/107.jpg differ diff --git a/out/ortho/108.jpg b/out/ortho/108.jpg new file mode 100644 index 0000000..188c888 Binary files /dev/null and b/out/ortho/108.jpg differ diff --git a/out/ortho/109.jpg b/out/ortho/109.jpg new file mode 100644 index 0000000..2f8ff0b Binary files /dev/null and b/out/ortho/109.jpg differ diff --git a/out/ortho/11.jpg b/out/ortho/11.jpg new file mode 100644 index 0000000..dcafbe5 Binary files /dev/null and b/out/ortho/11.jpg differ diff --git a/out/ortho/110.jpg b/out/ortho/110.jpg new file mode 100644 index 0000000..1dd8bbd Binary files /dev/null and b/out/ortho/110.jpg differ diff --git a/out/ortho/111.jpg b/out/ortho/111.jpg new file mode 100644 index 0000000..0c7e35c Binary files /dev/null and b/out/ortho/111.jpg differ diff --git a/out/ortho/112.jpg b/out/ortho/112.jpg new file mode 100644 index 0000000..00e054b Binary files /dev/null and b/out/ortho/112.jpg differ diff --git a/out/ortho/113.jpg b/out/ortho/113.jpg new file mode 100644 index 0000000..1acc46a Binary files /dev/null and b/out/ortho/113.jpg differ diff --git a/out/ortho/114.jpg b/out/ortho/114.jpg new file mode 100644 index 0000000..effbd77 Binary files /dev/null and b/out/ortho/114.jpg differ diff --git a/out/ortho/115.jpg b/out/ortho/115.jpg new file mode 100644 index 0000000..b981c86 Binary files /dev/null and b/out/ortho/115.jpg differ diff --git a/out/ortho/116.jpg b/out/ortho/116.jpg new file mode 100644 index 0000000..5518492 Binary files /dev/null and b/out/ortho/116.jpg differ diff --git a/out/ortho/117.jpg b/out/ortho/117.jpg new file mode 100644 index 0000000..15c6eb4 Binary files /dev/null and b/out/ortho/117.jpg differ diff --git a/out/ortho/118.jpg b/out/ortho/118.jpg new file mode 100644 index 0000000..2421adf Binary files /dev/null and b/out/ortho/118.jpg differ diff --git a/out/ortho/119.jpg b/out/ortho/119.jpg new file mode 100644 index 0000000..2dbb5b3 Binary files /dev/null and b/out/ortho/119.jpg differ diff --git a/out/ortho/12.jpg b/out/ortho/12.jpg new file mode 100644 index 0000000..ba1623c Binary files /dev/null and b/out/ortho/12.jpg differ diff --git a/out/ortho/120.jpg b/out/ortho/120.jpg new file mode 100644 index 0000000..7ff00af Binary files /dev/null and b/out/ortho/120.jpg differ diff --git a/out/ortho/121.jpg b/out/ortho/121.jpg new file mode 100644 index 0000000..ab97185 Binary files /dev/null and b/out/ortho/121.jpg differ diff --git a/out/ortho/122.jpg b/out/ortho/122.jpg new file mode 100644 index 0000000..0b91cf8 Binary files /dev/null and b/out/ortho/122.jpg differ diff --git a/out/ortho/123.jpg b/out/ortho/123.jpg new file mode 100644 index 0000000..ae7fb17 Binary files /dev/null and b/out/ortho/123.jpg differ diff --git a/out/ortho/124.jpg b/out/ortho/124.jpg new file mode 100644 index 0000000..c255eb1 Binary files /dev/null and b/out/ortho/124.jpg differ diff --git a/out/ortho/125.jpg b/out/ortho/125.jpg new file mode 100644 index 0000000..4e33091 Binary files /dev/null and b/out/ortho/125.jpg differ diff --git a/out/ortho/126.jpg b/out/ortho/126.jpg new file mode 100644 index 0000000..e30f951 Binary files /dev/null and b/out/ortho/126.jpg differ diff --git a/out/ortho/127.jpg b/out/ortho/127.jpg new file mode 100644 index 0000000..28a2752 Binary files /dev/null and b/out/ortho/127.jpg differ diff --git a/out/ortho/128.jpg b/out/ortho/128.jpg new file mode 100644 index 0000000..21f0697 Binary files /dev/null and b/out/ortho/128.jpg differ diff --git a/out/ortho/129.jpg b/out/ortho/129.jpg new file mode 100644 index 0000000..d445b5a Binary files /dev/null and b/out/ortho/129.jpg differ diff --git a/out/ortho/13.jpg b/out/ortho/13.jpg new file mode 100644 index 0000000..b5c89bc Binary files /dev/null and b/out/ortho/13.jpg differ diff --git a/out/ortho/130.jpg b/out/ortho/130.jpg new file mode 100644 index 0000000..76502be Binary files /dev/null and b/out/ortho/130.jpg differ diff --git a/out/ortho/131.jpg b/out/ortho/131.jpg new file mode 100644 index 0000000..0d0d725 Binary files /dev/null and b/out/ortho/131.jpg differ diff --git a/out/ortho/132.jpg b/out/ortho/132.jpg new file mode 100644 index 0000000..5e51b8a Binary files /dev/null and b/out/ortho/132.jpg differ diff --git a/out/ortho/133.jpg b/out/ortho/133.jpg new file mode 100644 index 0000000..b898a4e Binary files /dev/null and b/out/ortho/133.jpg differ diff --git a/out/ortho/134.jpg b/out/ortho/134.jpg new file mode 100644 index 0000000..b89b3cb Binary files /dev/null and b/out/ortho/134.jpg differ diff --git a/out/ortho/135.jpg b/out/ortho/135.jpg new file mode 100644 index 0000000..2e86760 Binary files /dev/null and b/out/ortho/135.jpg differ diff --git a/out/ortho/136.jpg b/out/ortho/136.jpg new file mode 100644 index 0000000..869827f Binary files /dev/null and b/out/ortho/136.jpg differ diff --git a/out/ortho/137.jpg b/out/ortho/137.jpg new file mode 100644 index 0000000..afa5309 Binary files /dev/null and b/out/ortho/137.jpg differ diff --git a/out/ortho/138.jpg b/out/ortho/138.jpg new file mode 100644 index 0000000..78539ed Binary files /dev/null and b/out/ortho/138.jpg differ diff --git a/out/ortho/139.jpg b/out/ortho/139.jpg new file mode 100644 index 0000000..c0c74e2 Binary files /dev/null and b/out/ortho/139.jpg differ diff --git a/out/ortho/14.jpg b/out/ortho/14.jpg new file mode 100644 index 0000000..337eb4f Binary files /dev/null and b/out/ortho/14.jpg differ diff --git a/out/ortho/140.jpg b/out/ortho/140.jpg new file mode 100644 index 0000000..c868a32 Binary files /dev/null and b/out/ortho/140.jpg differ diff --git a/out/ortho/141.jpg b/out/ortho/141.jpg new file mode 100644 index 0000000..5465094 Binary files /dev/null and b/out/ortho/141.jpg differ diff --git a/out/ortho/142.jpg b/out/ortho/142.jpg new file mode 100644 index 0000000..df12320 Binary files /dev/null and b/out/ortho/142.jpg differ diff --git a/out/ortho/143.jpg b/out/ortho/143.jpg new file mode 100644 index 0000000..b3fe270 Binary files /dev/null and b/out/ortho/143.jpg differ diff --git a/out/ortho/144.jpg b/out/ortho/144.jpg new file mode 100644 index 0000000..5182758 Binary files /dev/null and b/out/ortho/144.jpg differ diff --git a/out/ortho/145.jpg b/out/ortho/145.jpg new file mode 100644 index 0000000..4fc14ec Binary files /dev/null and b/out/ortho/145.jpg differ diff --git a/out/ortho/146.jpg b/out/ortho/146.jpg new file mode 100644 index 0000000..127d13f Binary files /dev/null and b/out/ortho/146.jpg differ diff --git a/out/ortho/147.jpg b/out/ortho/147.jpg new file mode 100644 index 0000000..5d85575 Binary files /dev/null and b/out/ortho/147.jpg differ diff --git a/out/ortho/148.jpg b/out/ortho/148.jpg new file mode 100644 index 0000000..0447653 Binary files /dev/null and b/out/ortho/148.jpg differ diff --git a/out/ortho/149.jpg b/out/ortho/149.jpg new file mode 100644 index 0000000..2ca3d36 Binary files /dev/null and b/out/ortho/149.jpg differ diff --git a/out/ortho/15.jpg b/out/ortho/15.jpg new file mode 100644 index 0000000..1823837 Binary files /dev/null and b/out/ortho/15.jpg differ diff --git a/out/ortho/150.jpg b/out/ortho/150.jpg new file mode 100644 index 0000000..25e0461 Binary files /dev/null and b/out/ortho/150.jpg differ diff --git a/out/ortho/151.jpg b/out/ortho/151.jpg new file mode 100644 index 0000000..009cd77 Binary files /dev/null and b/out/ortho/151.jpg differ diff --git a/out/ortho/152.jpg b/out/ortho/152.jpg new file mode 100644 index 0000000..ce44ebb Binary files /dev/null and b/out/ortho/152.jpg differ diff --git a/out/ortho/153.jpg b/out/ortho/153.jpg new file mode 100644 index 0000000..8f76376 Binary files /dev/null and b/out/ortho/153.jpg differ diff --git a/out/ortho/154.jpg b/out/ortho/154.jpg new file mode 100644 index 0000000..c8d748c Binary files /dev/null and b/out/ortho/154.jpg differ diff --git a/out/ortho/155.jpg b/out/ortho/155.jpg new file mode 100644 index 0000000..cd29547 Binary files /dev/null and b/out/ortho/155.jpg differ diff --git a/out/ortho/156.jpg b/out/ortho/156.jpg new file mode 100644 index 0000000..3e78a5c Binary files /dev/null and b/out/ortho/156.jpg differ diff --git a/out/ortho/157.jpg b/out/ortho/157.jpg new file mode 100644 index 0000000..111f262 Binary files /dev/null and b/out/ortho/157.jpg differ diff --git a/out/ortho/158.jpg b/out/ortho/158.jpg new file mode 100644 index 0000000..559e978 Binary files /dev/null and b/out/ortho/158.jpg differ diff --git a/out/ortho/159.jpg b/out/ortho/159.jpg new file mode 100644 index 0000000..3f1a9a6 Binary files /dev/null and b/out/ortho/159.jpg differ diff --git a/out/ortho/16.jpg b/out/ortho/16.jpg new file mode 100644 index 0000000..1e92dec Binary files /dev/null and b/out/ortho/16.jpg differ diff --git a/out/ortho/160.jpg b/out/ortho/160.jpg new file mode 100644 index 0000000..5f4b956 Binary files /dev/null and b/out/ortho/160.jpg differ diff --git a/out/ortho/161.jpg b/out/ortho/161.jpg new file mode 100644 index 0000000..38c3630 Binary files /dev/null and b/out/ortho/161.jpg differ diff --git a/out/ortho/162.jpg b/out/ortho/162.jpg new file mode 100644 index 0000000..eda49f2 Binary files /dev/null and b/out/ortho/162.jpg differ diff --git a/out/ortho/163.jpg b/out/ortho/163.jpg new file mode 100644 index 0000000..be1c8b5 Binary files /dev/null and b/out/ortho/163.jpg differ diff --git a/out/ortho/164.jpg b/out/ortho/164.jpg new file mode 100644 index 0000000..e97a3b0 Binary files /dev/null and b/out/ortho/164.jpg differ diff --git a/out/ortho/165.jpg b/out/ortho/165.jpg new file mode 100644 index 0000000..e91ae47 Binary files /dev/null and b/out/ortho/165.jpg differ diff --git a/out/ortho/166.jpg b/out/ortho/166.jpg new file mode 100644 index 0000000..3c65f61 Binary files /dev/null and b/out/ortho/166.jpg differ diff --git a/out/ortho/167.jpg b/out/ortho/167.jpg new file mode 100644 index 0000000..c4f43f4 Binary files /dev/null and b/out/ortho/167.jpg differ diff --git a/out/ortho/168.jpg b/out/ortho/168.jpg new file mode 100644 index 0000000..78d57a0 Binary files /dev/null and b/out/ortho/168.jpg differ diff --git a/out/ortho/169.jpg b/out/ortho/169.jpg new file mode 100644 index 0000000..18ab27d Binary files /dev/null and b/out/ortho/169.jpg differ diff --git a/out/ortho/17.jpg b/out/ortho/17.jpg new file mode 100644 index 0000000..b022e56 Binary files /dev/null and b/out/ortho/17.jpg differ diff --git a/out/ortho/170.jpg b/out/ortho/170.jpg new file mode 100644 index 0000000..6a61ed5 Binary files /dev/null and b/out/ortho/170.jpg differ diff --git a/out/ortho/171.jpg b/out/ortho/171.jpg new file mode 100644 index 0000000..701331f Binary files /dev/null and b/out/ortho/171.jpg differ diff --git a/out/ortho/172.jpg b/out/ortho/172.jpg new file mode 100644 index 0000000..1254542 Binary files /dev/null and b/out/ortho/172.jpg differ diff --git a/out/ortho/173.jpg b/out/ortho/173.jpg new file mode 100644 index 0000000..5e51694 Binary files /dev/null and b/out/ortho/173.jpg differ diff --git a/out/ortho/174.jpg b/out/ortho/174.jpg new file mode 100644 index 0000000..1dc93cd Binary files /dev/null and b/out/ortho/174.jpg differ diff --git a/out/ortho/175.jpg b/out/ortho/175.jpg new file mode 100644 index 0000000..106ce98 Binary files /dev/null and b/out/ortho/175.jpg differ diff --git a/out/ortho/176.jpg b/out/ortho/176.jpg new file mode 100644 index 0000000..2b0f6ab Binary files /dev/null and b/out/ortho/176.jpg differ diff --git a/out/ortho/177.jpg b/out/ortho/177.jpg new file mode 100644 index 0000000..934fe27 Binary files /dev/null and b/out/ortho/177.jpg differ diff --git a/out/ortho/178.jpg b/out/ortho/178.jpg new file mode 100644 index 0000000..49a4660 Binary files /dev/null and b/out/ortho/178.jpg differ diff --git a/out/ortho/179.jpg b/out/ortho/179.jpg new file mode 100644 index 0000000..6c5359e Binary files /dev/null and b/out/ortho/179.jpg differ diff --git a/out/ortho/18.jpg b/out/ortho/18.jpg new file mode 100644 index 0000000..69ac83d Binary files /dev/null and b/out/ortho/18.jpg differ diff --git a/out/ortho/180.jpg b/out/ortho/180.jpg new file mode 100644 index 0000000..f68d91d Binary files /dev/null and b/out/ortho/180.jpg differ diff --git a/out/ortho/181.jpg b/out/ortho/181.jpg new file mode 100644 index 0000000..e44eea7 Binary files /dev/null and b/out/ortho/181.jpg differ diff --git a/out/ortho/182.jpg b/out/ortho/182.jpg new file mode 100644 index 0000000..17ea622 Binary files /dev/null and b/out/ortho/182.jpg differ diff --git a/out/ortho/183.jpg b/out/ortho/183.jpg new file mode 100644 index 0000000..f424251 Binary files /dev/null and b/out/ortho/183.jpg differ diff --git a/out/ortho/184.jpg b/out/ortho/184.jpg new file mode 100644 index 0000000..2cdfcad Binary files /dev/null and b/out/ortho/184.jpg differ diff --git a/out/ortho/185.jpg b/out/ortho/185.jpg new file mode 100644 index 0000000..64fa787 Binary files /dev/null and b/out/ortho/185.jpg differ diff --git a/out/ortho/186.jpg b/out/ortho/186.jpg new file mode 100644 index 0000000..cc8bbe1 Binary files /dev/null and b/out/ortho/186.jpg differ diff --git a/out/ortho/187.jpg b/out/ortho/187.jpg new file mode 100644 index 0000000..55f4b80 Binary files /dev/null and b/out/ortho/187.jpg differ diff --git a/out/ortho/188.jpg b/out/ortho/188.jpg new file mode 100644 index 0000000..3b5eb06 Binary files /dev/null and b/out/ortho/188.jpg differ diff --git a/out/ortho/189.jpg b/out/ortho/189.jpg new file mode 100644 index 0000000..5051a20 Binary files /dev/null and b/out/ortho/189.jpg differ diff --git a/out/ortho/19.jpg b/out/ortho/19.jpg new file mode 100644 index 0000000..2db7e5f Binary files /dev/null and b/out/ortho/19.jpg differ diff --git a/out/ortho/190.jpg b/out/ortho/190.jpg new file mode 100644 index 0000000..32e3716 Binary files /dev/null and b/out/ortho/190.jpg differ diff --git a/out/ortho/191.jpg b/out/ortho/191.jpg new file mode 100644 index 0000000..f806844 Binary files /dev/null and b/out/ortho/191.jpg differ diff --git a/out/ortho/192.jpg b/out/ortho/192.jpg new file mode 100644 index 0000000..85be819 Binary files /dev/null and b/out/ortho/192.jpg differ diff --git a/out/ortho/193.jpg b/out/ortho/193.jpg new file mode 100644 index 0000000..53511de Binary files /dev/null and b/out/ortho/193.jpg differ diff --git a/out/ortho/194.jpg b/out/ortho/194.jpg new file mode 100644 index 0000000..9969e42 Binary files /dev/null and b/out/ortho/194.jpg differ diff --git a/out/ortho/195.jpg b/out/ortho/195.jpg new file mode 100644 index 0000000..4f5dbbe Binary files /dev/null and b/out/ortho/195.jpg differ diff --git a/out/ortho/196.jpg b/out/ortho/196.jpg new file mode 100644 index 0000000..0d37775 Binary files /dev/null and b/out/ortho/196.jpg differ diff --git a/out/ortho/197.jpg b/out/ortho/197.jpg new file mode 100644 index 0000000..284febd Binary files /dev/null and b/out/ortho/197.jpg differ diff --git a/out/ortho/198.jpg b/out/ortho/198.jpg new file mode 100644 index 0000000..47f312b Binary files /dev/null and b/out/ortho/198.jpg differ diff --git a/out/ortho/199.jpg b/out/ortho/199.jpg new file mode 100644 index 0000000..38ff414 Binary files /dev/null and b/out/ortho/199.jpg differ diff --git a/out/ortho/2.jpg b/out/ortho/2.jpg new file mode 100644 index 0000000..833f2ba Binary files /dev/null and b/out/ortho/2.jpg differ diff --git a/out/ortho/20.jpg b/out/ortho/20.jpg new file mode 100644 index 0000000..3f1d6f5 Binary files /dev/null and b/out/ortho/20.jpg differ diff --git a/out/ortho/200.jpg b/out/ortho/200.jpg new file mode 100644 index 0000000..c0d5f0b Binary files /dev/null and b/out/ortho/200.jpg differ diff --git a/out/ortho/201.jpg b/out/ortho/201.jpg new file mode 100644 index 0000000..0b3de68 Binary files /dev/null and b/out/ortho/201.jpg differ diff --git a/out/ortho/202.jpg b/out/ortho/202.jpg new file mode 100644 index 0000000..1a8b625 Binary files /dev/null and b/out/ortho/202.jpg differ diff --git a/out/ortho/203.jpg b/out/ortho/203.jpg new file mode 100644 index 0000000..503161c Binary files /dev/null and b/out/ortho/203.jpg differ diff --git a/out/ortho/204.jpg b/out/ortho/204.jpg new file mode 100644 index 0000000..4fce908 Binary files /dev/null and b/out/ortho/204.jpg differ diff --git a/out/ortho/205.jpg b/out/ortho/205.jpg new file mode 100644 index 0000000..4f56352 Binary files /dev/null and b/out/ortho/205.jpg differ diff --git a/out/ortho/206.jpg b/out/ortho/206.jpg new file mode 100644 index 0000000..b0ad349 Binary files /dev/null and b/out/ortho/206.jpg differ diff --git a/out/ortho/207.jpg b/out/ortho/207.jpg new file mode 100644 index 0000000..17967c3 Binary files /dev/null and b/out/ortho/207.jpg differ diff --git a/out/ortho/208.jpg b/out/ortho/208.jpg new file mode 100644 index 0000000..213be74 Binary files /dev/null and b/out/ortho/208.jpg differ diff --git a/out/ortho/209.jpg b/out/ortho/209.jpg new file mode 100644 index 0000000..1cea29b Binary files /dev/null and b/out/ortho/209.jpg differ diff --git a/out/ortho/21.jpg b/out/ortho/21.jpg new file mode 100644 index 0000000..b1c815f Binary files /dev/null and b/out/ortho/21.jpg differ diff --git a/out/ortho/210.jpg b/out/ortho/210.jpg new file mode 100644 index 0000000..5012bb2 Binary files /dev/null and b/out/ortho/210.jpg differ diff --git a/out/ortho/211.jpg b/out/ortho/211.jpg new file mode 100644 index 0000000..e34fc7b Binary files /dev/null and b/out/ortho/211.jpg differ diff --git a/out/ortho/212.jpg b/out/ortho/212.jpg new file mode 100644 index 0000000..e843f2a Binary files /dev/null and b/out/ortho/212.jpg differ diff --git a/out/ortho/213.jpg b/out/ortho/213.jpg new file mode 100644 index 0000000..c2d409a Binary files /dev/null and b/out/ortho/213.jpg differ diff --git a/out/ortho/214.jpg b/out/ortho/214.jpg new file mode 100644 index 0000000..99db416 Binary files /dev/null and b/out/ortho/214.jpg differ diff --git a/out/ortho/215.jpg b/out/ortho/215.jpg new file mode 100644 index 0000000..f6c0b05 Binary files /dev/null and b/out/ortho/215.jpg differ diff --git a/out/ortho/216.jpg b/out/ortho/216.jpg new file mode 100644 index 0000000..004efed Binary files /dev/null and b/out/ortho/216.jpg differ diff --git a/out/ortho/217.jpg b/out/ortho/217.jpg new file mode 100644 index 0000000..f825c8e Binary files /dev/null and b/out/ortho/217.jpg differ diff --git a/out/ortho/218.jpg b/out/ortho/218.jpg new file mode 100644 index 0000000..9327d5e Binary files /dev/null and b/out/ortho/218.jpg differ diff --git a/out/ortho/219.jpg b/out/ortho/219.jpg new file mode 100644 index 0000000..b06fa1c Binary files /dev/null and b/out/ortho/219.jpg differ diff --git a/out/ortho/22.jpg b/out/ortho/22.jpg new file mode 100644 index 0000000..358547c Binary files /dev/null and b/out/ortho/22.jpg differ diff --git a/out/ortho/220.jpg b/out/ortho/220.jpg new file mode 100644 index 0000000..3aa83b4 Binary files /dev/null and b/out/ortho/220.jpg differ diff --git a/out/ortho/221.jpg b/out/ortho/221.jpg new file mode 100644 index 0000000..5252a45 Binary files /dev/null and b/out/ortho/221.jpg differ diff --git a/out/ortho/222.jpg b/out/ortho/222.jpg new file mode 100644 index 0000000..a0a3a20 Binary files /dev/null and b/out/ortho/222.jpg differ diff --git a/out/ortho/223.jpg b/out/ortho/223.jpg new file mode 100644 index 0000000..65bf198 Binary files /dev/null and b/out/ortho/223.jpg differ diff --git a/out/ortho/224.jpg b/out/ortho/224.jpg new file mode 100644 index 0000000..5e1d297 Binary files /dev/null and b/out/ortho/224.jpg differ diff --git a/out/ortho/225.jpg b/out/ortho/225.jpg new file mode 100644 index 0000000..f33829f Binary files /dev/null and b/out/ortho/225.jpg differ diff --git a/out/ortho/226.jpg b/out/ortho/226.jpg new file mode 100644 index 0000000..29495c1 Binary files /dev/null and b/out/ortho/226.jpg differ diff --git a/out/ortho/227.jpg b/out/ortho/227.jpg new file mode 100644 index 0000000..0abe4f1 Binary files /dev/null and b/out/ortho/227.jpg differ diff --git a/out/ortho/228.jpg b/out/ortho/228.jpg new file mode 100644 index 0000000..50cc44a Binary files /dev/null and b/out/ortho/228.jpg differ diff --git a/out/ortho/229.jpg b/out/ortho/229.jpg new file mode 100644 index 0000000..cb0795f Binary files /dev/null and b/out/ortho/229.jpg differ diff --git a/out/ortho/23.jpg b/out/ortho/23.jpg new file mode 100644 index 0000000..1a425df Binary files /dev/null and b/out/ortho/23.jpg differ diff --git a/out/ortho/230.jpg b/out/ortho/230.jpg new file mode 100644 index 0000000..c9a1aa7 Binary files /dev/null and b/out/ortho/230.jpg differ diff --git a/out/ortho/231.jpg b/out/ortho/231.jpg new file mode 100644 index 0000000..3f61b2e Binary files /dev/null and b/out/ortho/231.jpg differ diff --git a/out/ortho/232.jpg b/out/ortho/232.jpg new file mode 100644 index 0000000..928e577 Binary files /dev/null and b/out/ortho/232.jpg differ diff --git a/out/ortho/233.jpg b/out/ortho/233.jpg new file mode 100644 index 0000000..69ccbc8 Binary files /dev/null and b/out/ortho/233.jpg differ diff --git a/out/ortho/234.jpg b/out/ortho/234.jpg new file mode 100644 index 0000000..346fb7c Binary files /dev/null and b/out/ortho/234.jpg differ diff --git a/out/ortho/235.jpg b/out/ortho/235.jpg new file mode 100644 index 0000000..83a280f Binary files /dev/null and b/out/ortho/235.jpg differ diff --git a/out/ortho/236.jpg b/out/ortho/236.jpg new file mode 100644 index 0000000..0a88e34 Binary files /dev/null and b/out/ortho/236.jpg differ diff --git a/out/ortho/237.jpg b/out/ortho/237.jpg new file mode 100644 index 0000000..38db711 Binary files /dev/null and b/out/ortho/237.jpg differ diff --git a/out/ortho/238.jpg b/out/ortho/238.jpg new file mode 100644 index 0000000..70250a6 Binary files /dev/null and b/out/ortho/238.jpg differ diff --git a/out/ortho/239.jpg b/out/ortho/239.jpg new file mode 100644 index 0000000..5f6481e Binary files /dev/null and b/out/ortho/239.jpg differ diff --git a/out/ortho/24.jpg b/out/ortho/24.jpg new file mode 100644 index 0000000..e14aacb Binary files /dev/null and b/out/ortho/24.jpg differ diff --git a/out/ortho/240.jpg b/out/ortho/240.jpg new file mode 100644 index 0000000..8407f55 Binary files /dev/null and b/out/ortho/240.jpg differ diff --git a/out/ortho/241.jpg b/out/ortho/241.jpg new file mode 100644 index 0000000..8aeee09 Binary files /dev/null and b/out/ortho/241.jpg differ diff --git a/out/ortho/242.jpg b/out/ortho/242.jpg new file mode 100644 index 0000000..8024a28 Binary files /dev/null and b/out/ortho/242.jpg differ diff --git a/out/ortho/243.jpg b/out/ortho/243.jpg new file mode 100644 index 0000000..93562a1 Binary files /dev/null and b/out/ortho/243.jpg differ diff --git a/out/ortho/244.jpg b/out/ortho/244.jpg new file mode 100644 index 0000000..9fafd80 Binary files /dev/null and b/out/ortho/244.jpg differ diff --git a/out/ortho/245.jpg b/out/ortho/245.jpg new file mode 100644 index 0000000..b58bd60 Binary files /dev/null and b/out/ortho/245.jpg differ diff --git a/out/ortho/246.jpg b/out/ortho/246.jpg new file mode 100644 index 0000000..3ba369f Binary files /dev/null and b/out/ortho/246.jpg differ diff --git a/out/ortho/247.jpg b/out/ortho/247.jpg new file mode 100644 index 0000000..8b1cd51 Binary files /dev/null and b/out/ortho/247.jpg differ diff --git a/out/ortho/248.jpg b/out/ortho/248.jpg new file mode 100644 index 0000000..b8986d4 Binary files /dev/null and b/out/ortho/248.jpg differ diff --git a/out/ortho/249.jpg b/out/ortho/249.jpg new file mode 100644 index 0000000..8035e1c Binary files /dev/null and b/out/ortho/249.jpg differ diff --git a/out/ortho/25.jpg b/out/ortho/25.jpg new file mode 100644 index 0000000..7d334ea Binary files /dev/null and b/out/ortho/25.jpg differ diff --git a/out/ortho/250.jpg b/out/ortho/250.jpg new file mode 100644 index 0000000..2f98412 Binary files /dev/null and b/out/ortho/250.jpg differ diff --git a/out/ortho/251.jpg b/out/ortho/251.jpg new file mode 100644 index 0000000..65b2666 Binary files /dev/null and b/out/ortho/251.jpg differ diff --git a/out/ortho/252.jpg b/out/ortho/252.jpg new file mode 100644 index 0000000..e8f0b26 Binary files /dev/null and b/out/ortho/252.jpg differ diff --git a/out/ortho/253.jpg b/out/ortho/253.jpg new file mode 100644 index 0000000..4f7cdc1 Binary files /dev/null and b/out/ortho/253.jpg differ diff --git a/out/ortho/254.jpg b/out/ortho/254.jpg new file mode 100644 index 0000000..11dc8c3 Binary files /dev/null and b/out/ortho/254.jpg differ diff --git a/out/ortho/255.jpg b/out/ortho/255.jpg new file mode 100644 index 0000000..d4665c7 Binary files /dev/null and b/out/ortho/255.jpg differ diff --git a/out/ortho/256.jpg b/out/ortho/256.jpg new file mode 100644 index 0000000..92eda75 Binary files /dev/null and b/out/ortho/256.jpg differ diff --git a/out/ortho/257.jpg b/out/ortho/257.jpg new file mode 100644 index 0000000..e9f3408 Binary files /dev/null and b/out/ortho/257.jpg differ diff --git a/out/ortho/258.jpg b/out/ortho/258.jpg new file mode 100644 index 0000000..1d21903 Binary files /dev/null and b/out/ortho/258.jpg differ diff --git a/out/ortho/259.jpg b/out/ortho/259.jpg new file mode 100644 index 0000000..137bcb8 Binary files /dev/null and b/out/ortho/259.jpg differ diff --git a/out/ortho/26.jpg b/out/ortho/26.jpg new file mode 100644 index 0000000..f1d618e Binary files /dev/null and b/out/ortho/26.jpg differ diff --git a/out/ortho/260.jpg b/out/ortho/260.jpg new file mode 100644 index 0000000..6993c76 Binary files /dev/null and b/out/ortho/260.jpg differ diff --git a/out/ortho/261.jpg b/out/ortho/261.jpg new file mode 100644 index 0000000..49c6b5b Binary files /dev/null and b/out/ortho/261.jpg differ diff --git a/out/ortho/262.jpg b/out/ortho/262.jpg new file mode 100644 index 0000000..691ee4d Binary files /dev/null and b/out/ortho/262.jpg differ diff --git a/out/ortho/263.jpg b/out/ortho/263.jpg new file mode 100644 index 0000000..609418e Binary files /dev/null and b/out/ortho/263.jpg differ diff --git a/out/ortho/264.jpg b/out/ortho/264.jpg new file mode 100644 index 0000000..566c78d Binary files /dev/null and b/out/ortho/264.jpg differ diff --git a/out/ortho/265.jpg b/out/ortho/265.jpg new file mode 100644 index 0000000..e059bbf Binary files /dev/null and b/out/ortho/265.jpg differ diff --git a/out/ortho/266.jpg b/out/ortho/266.jpg new file mode 100644 index 0000000..6fc341f Binary files /dev/null and b/out/ortho/266.jpg differ diff --git a/out/ortho/267.jpg b/out/ortho/267.jpg new file mode 100644 index 0000000..28f2ac5 Binary files /dev/null and b/out/ortho/267.jpg differ diff --git a/out/ortho/268.jpg b/out/ortho/268.jpg new file mode 100644 index 0000000..822c553 Binary files /dev/null and b/out/ortho/268.jpg differ diff --git a/out/ortho/269.jpg b/out/ortho/269.jpg new file mode 100644 index 0000000..f5053c8 Binary files /dev/null and b/out/ortho/269.jpg differ diff --git a/out/ortho/27.jpg b/out/ortho/27.jpg new file mode 100644 index 0000000..755ddd8 Binary files /dev/null and b/out/ortho/27.jpg differ diff --git a/out/ortho/270.jpg b/out/ortho/270.jpg new file mode 100644 index 0000000..49608aa Binary files /dev/null and b/out/ortho/270.jpg differ diff --git a/out/ortho/271.jpg b/out/ortho/271.jpg new file mode 100644 index 0000000..08faa7a Binary files /dev/null and b/out/ortho/271.jpg differ diff --git a/out/ortho/272.jpg b/out/ortho/272.jpg new file mode 100644 index 0000000..8ae5701 Binary files /dev/null and b/out/ortho/272.jpg differ diff --git a/out/ortho/273.jpg b/out/ortho/273.jpg new file mode 100644 index 0000000..b3eadfd Binary files /dev/null and b/out/ortho/273.jpg differ diff --git a/out/ortho/274.jpg b/out/ortho/274.jpg new file mode 100644 index 0000000..f886776 Binary files /dev/null and b/out/ortho/274.jpg differ diff --git a/out/ortho/275.jpg b/out/ortho/275.jpg new file mode 100644 index 0000000..7ebb27b Binary files /dev/null and b/out/ortho/275.jpg differ diff --git a/out/ortho/276.jpg b/out/ortho/276.jpg new file mode 100644 index 0000000..7e2eafe Binary files /dev/null and b/out/ortho/276.jpg differ diff --git a/out/ortho/277.jpg b/out/ortho/277.jpg new file mode 100644 index 0000000..fd7f317 Binary files /dev/null and b/out/ortho/277.jpg differ diff --git a/out/ortho/278.jpg b/out/ortho/278.jpg new file mode 100644 index 0000000..07f8ddb Binary files /dev/null and b/out/ortho/278.jpg differ diff --git a/out/ortho/279.jpg b/out/ortho/279.jpg new file mode 100644 index 0000000..ef5b831 Binary files /dev/null and b/out/ortho/279.jpg differ diff --git a/out/ortho/28.jpg b/out/ortho/28.jpg new file mode 100644 index 0000000..2083aeb Binary files /dev/null and b/out/ortho/28.jpg differ diff --git a/out/ortho/280.jpg b/out/ortho/280.jpg new file mode 100644 index 0000000..638ecfe Binary files /dev/null and b/out/ortho/280.jpg differ diff --git a/out/ortho/281.jpg b/out/ortho/281.jpg new file mode 100644 index 0000000..d61aa3e Binary files /dev/null and b/out/ortho/281.jpg differ diff --git a/out/ortho/282.jpg b/out/ortho/282.jpg new file mode 100644 index 0000000..c27ca8f Binary files /dev/null and b/out/ortho/282.jpg differ diff --git a/out/ortho/283.jpg b/out/ortho/283.jpg new file mode 100644 index 0000000..760b212 Binary files /dev/null and b/out/ortho/283.jpg differ diff --git a/out/ortho/284.jpg b/out/ortho/284.jpg new file mode 100644 index 0000000..3cbaef4 Binary files /dev/null and b/out/ortho/284.jpg differ diff --git a/out/ortho/285.jpg b/out/ortho/285.jpg new file mode 100644 index 0000000..f8fd5ee Binary files /dev/null and b/out/ortho/285.jpg differ diff --git a/out/ortho/286.jpg b/out/ortho/286.jpg new file mode 100644 index 0000000..eeefa5c Binary files /dev/null and b/out/ortho/286.jpg differ diff --git a/out/ortho/287.jpg b/out/ortho/287.jpg new file mode 100644 index 0000000..340ec48 Binary files /dev/null and b/out/ortho/287.jpg differ diff --git a/out/ortho/288.jpg b/out/ortho/288.jpg new file mode 100644 index 0000000..3d2869b Binary files /dev/null and b/out/ortho/288.jpg differ diff --git a/out/ortho/289.jpg b/out/ortho/289.jpg new file mode 100644 index 0000000..ce0b218 Binary files /dev/null and b/out/ortho/289.jpg differ diff --git a/out/ortho/29.jpg b/out/ortho/29.jpg new file mode 100644 index 0000000..7f3d112 Binary files /dev/null and b/out/ortho/29.jpg differ diff --git a/out/ortho/290.jpg b/out/ortho/290.jpg new file mode 100644 index 0000000..bd3ec34 Binary files /dev/null and b/out/ortho/290.jpg differ diff --git a/out/ortho/291.jpg b/out/ortho/291.jpg new file mode 100644 index 0000000..904638c Binary files /dev/null and b/out/ortho/291.jpg differ diff --git a/out/ortho/292.jpg b/out/ortho/292.jpg new file mode 100644 index 0000000..7830254 Binary files /dev/null and b/out/ortho/292.jpg differ diff --git a/out/ortho/293.jpg b/out/ortho/293.jpg new file mode 100644 index 0000000..03e45b1 Binary files /dev/null and b/out/ortho/293.jpg differ diff --git a/out/ortho/294.jpg b/out/ortho/294.jpg new file mode 100644 index 0000000..5e6267d Binary files /dev/null and b/out/ortho/294.jpg differ diff --git a/out/ortho/295.jpg b/out/ortho/295.jpg new file mode 100644 index 0000000..d7f0f6c Binary files /dev/null and b/out/ortho/295.jpg differ diff --git a/out/ortho/296.jpg b/out/ortho/296.jpg new file mode 100644 index 0000000..c0e5666 Binary files /dev/null and b/out/ortho/296.jpg differ diff --git a/out/ortho/297.jpg b/out/ortho/297.jpg new file mode 100644 index 0000000..a355e22 Binary files /dev/null and b/out/ortho/297.jpg differ diff --git a/out/ortho/298.jpg b/out/ortho/298.jpg new file mode 100644 index 0000000..6a007c2 Binary files /dev/null and b/out/ortho/298.jpg differ diff --git a/out/ortho/299.jpg b/out/ortho/299.jpg new file mode 100644 index 0000000..40c68be Binary files /dev/null and b/out/ortho/299.jpg differ diff --git a/out/ortho/3.jpg b/out/ortho/3.jpg new file mode 100644 index 0000000..4cb0ad6 Binary files /dev/null and b/out/ortho/3.jpg differ diff --git a/out/ortho/30.jpg b/out/ortho/30.jpg new file mode 100644 index 0000000..0fd11a3 Binary files /dev/null and b/out/ortho/30.jpg differ diff --git a/out/ortho/300.jpg b/out/ortho/300.jpg new file mode 100644 index 0000000..c2dd927 Binary files /dev/null and b/out/ortho/300.jpg differ diff --git a/out/ortho/301.jpg b/out/ortho/301.jpg new file mode 100644 index 0000000..4fd4678 Binary files /dev/null and b/out/ortho/301.jpg differ diff --git a/out/ortho/302.jpg b/out/ortho/302.jpg new file mode 100644 index 0000000..039cc4d Binary files /dev/null and b/out/ortho/302.jpg differ diff --git a/out/ortho/303.jpg b/out/ortho/303.jpg new file mode 100644 index 0000000..e9b43ab Binary files /dev/null and b/out/ortho/303.jpg differ diff --git a/out/ortho/304.jpg b/out/ortho/304.jpg new file mode 100644 index 0000000..5174eac Binary files /dev/null and b/out/ortho/304.jpg differ diff --git a/out/ortho/305.jpg b/out/ortho/305.jpg new file mode 100644 index 0000000..be63ad2 Binary files /dev/null and b/out/ortho/305.jpg differ diff --git a/out/ortho/306.jpg b/out/ortho/306.jpg new file mode 100644 index 0000000..7c2eb6f Binary files /dev/null and b/out/ortho/306.jpg differ diff --git a/out/ortho/307.jpg b/out/ortho/307.jpg new file mode 100644 index 0000000..1166df2 Binary files /dev/null and b/out/ortho/307.jpg differ diff --git a/out/ortho/308.jpg b/out/ortho/308.jpg new file mode 100644 index 0000000..92d9df0 Binary files /dev/null and b/out/ortho/308.jpg differ diff --git a/out/ortho/309.jpg b/out/ortho/309.jpg new file mode 100644 index 0000000..8f111eb Binary files /dev/null and b/out/ortho/309.jpg differ diff --git a/out/ortho/31.jpg b/out/ortho/31.jpg new file mode 100644 index 0000000..b094807 Binary files /dev/null and b/out/ortho/31.jpg differ diff --git a/out/ortho/310.jpg b/out/ortho/310.jpg new file mode 100644 index 0000000..207e7e1 Binary files /dev/null and b/out/ortho/310.jpg differ diff --git a/out/ortho/311.jpg b/out/ortho/311.jpg new file mode 100644 index 0000000..2df3bbc Binary files /dev/null and b/out/ortho/311.jpg differ diff --git a/out/ortho/312.jpg b/out/ortho/312.jpg new file mode 100644 index 0000000..e4fb605 Binary files /dev/null and b/out/ortho/312.jpg differ diff --git a/out/ortho/313.jpg b/out/ortho/313.jpg new file mode 100644 index 0000000..344baac Binary files /dev/null and b/out/ortho/313.jpg differ diff --git a/out/ortho/314.jpg b/out/ortho/314.jpg new file mode 100644 index 0000000..b40c6e9 Binary files /dev/null and b/out/ortho/314.jpg differ diff --git a/out/ortho/315.jpg b/out/ortho/315.jpg new file mode 100644 index 0000000..58aa331 Binary files /dev/null and b/out/ortho/315.jpg differ diff --git a/out/ortho/316.jpg b/out/ortho/316.jpg new file mode 100644 index 0000000..078166c Binary files /dev/null and b/out/ortho/316.jpg differ diff --git a/out/ortho/317.jpg b/out/ortho/317.jpg new file mode 100644 index 0000000..ef49682 Binary files /dev/null and b/out/ortho/317.jpg differ diff --git a/out/ortho/318.jpg b/out/ortho/318.jpg new file mode 100644 index 0000000..bb8579c Binary files /dev/null and b/out/ortho/318.jpg differ diff --git a/out/ortho/319.jpg b/out/ortho/319.jpg new file mode 100644 index 0000000..14ab9c7 Binary files /dev/null and b/out/ortho/319.jpg differ diff --git a/out/ortho/32.jpg b/out/ortho/32.jpg new file mode 100644 index 0000000..8320537 Binary files /dev/null and b/out/ortho/32.jpg differ diff --git a/out/ortho/320.jpg b/out/ortho/320.jpg new file mode 100644 index 0000000..a067c15 Binary files /dev/null and b/out/ortho/320.jpg differ diff --git a/out/ortho/321.jpg b/out/ortho/321.jpg new file mode 100644 index 0000000..39e28dc Binary files /dev/null and b/out/ortho/321.jpg differ diff --git a/out/ortho/322.jpg b/out/ortho/322.jpg new file mode 100644 index 0000000..0914c95 Binary files /dev/null and b/out/ortho/322.jpg differ diff --git a/out/ortho/323.jpg b/out/ortho/323.jpg new file mode 100644 index 0000000..1fd03b3 Binary files /dev/null and b/out/ortho/323.jpg differ diff --git a/out/ortho/324.jpg b/out/ortho/324.jpg new file mode 100644 index 0000000..4d5bb26 Binary files /dev/null and b/out/ortho/324.jpg differ diff --git a/out/ortho/325.jpg b/out/ortho/325.jpg new file mode 100644 index 0000000..aefb9f8 Binary files /dev/null and b/out/ortho/325.jpg differ diff --git a/out/ortho/326.jpg b/out/ortho/326.jpg new file mode 100644 index 0000000..1369f4c Binary files /dev/null and b/out/ortho/326.jpg differ diff --git a/out/ortho/327.jpg b/out/ortho/327.jpg new file mode 100644 index 0000000..f1d36e0 Binary files /dev/null and b/out/ortho/327.jpg differ diff --git a/out/ortho/328.jpg b/out/ortho/328.jpg new file mode 100644 index 0000000..af2e717 Binary files /dev/null and b/out/ortho/328.jpg differ diff --git a/out/ortho/329.jpg b/out/ortho/329.jpg new file mode 100644 index 0000000..e74e3c5 Binary files /dev/null and b/out/ortho/329.jpg differ diff --git a/out/ortho/33.jpg b/out/ortho/33.jpg new file mode 100644 index 0000000..8738f24 Binary files /dev/null and b/out/ortho/33.jpg differ diff --git a/out/ortho/330.jpg b/out/ortho/330.jpg new file mode 100644 index 0000000..fc4039d Binary files /dev/null and b/out/ortho/330.jpg differ diff --git a/out/ortho/331.jpg b/out/ortho/331.jpg new file mode 100644 index 0000000..6139fb6 Binary files /dev/null and b/out/ortho/331.jpg differ diff --git a/out/ortho/332.jpg b/out/ortho/332.jpg new file mode 100644 index 0000000..95025b7 Binary files /dev/null and b/out/ortho/332.jpg differ diff --git a/out/ortho/333.jpg b/out/ortho/333.jpg new file mode 100644 index 0000000..b84eff6 Binary files /dev/null and b/out/ortho/333.jpg differ diff --git a/out/ortho/334.jpg b/out/ortho/334.jpg new file mode 100644 index 0000000..8d3a771 Binary files /dev/null and b/out/ortho/334.jpg differ diff --git a/out/ortho/335.jpg b/out/ortho/335.jpg new file mode 100644 index 0000000..0825088 Binary files /dev/null and b/out/ortho/335.jpg differ diff --git a/out/ortho/336.jpg b/out/ortho/336.jpg new file mode 100644 index 0000000..cc0956e Binary files /dev/null and b/out/ortho/336.jpg differ diff --git a/out/ortho/337.jpg b/out/ortho/337.jpg new file mode 100644 index 0000000..8902f2a Binary files /dev/null and b/out/ortho/337.jpg differ diff --git a/out/ortho/338.jpg b/out/ortho/338.jpg new file mode 100644 index 0000000..98190a9 Binary files /dev/null and b/out/ortho/338.jpg differ diff --git a/out/ortho/339.jpg b/out/ortho/339.jpg new file mode 100644 index 0000000..4378cff Binary files /dev/null and b/out/ortho/339.jpg differ diff --git a/out/ortho/34.jpg b/out/ortho/34.jpg new file mode 100644 index 0000000..551983d Binary files /dev/null and b/out/ortho/34.jpg differ diff --git a/out/ortho/340.jpg b/out/ortho/340.jpg new file mode 100644 index 0000000..c37d30e Binary files /dev/null and b/out/ortho/340.jpg differ diff --git a/out/ortho/341.jpg b/out/ortho/341.jpg new file mode 100644 index 0000000..d922bea Binary files /dev/null and b/out/ortho/341.jpg differ diff --git a/out/ortho/342.jpg b/out/ortho/342.jpg new file mode 100644 index 0000000..58182dc Binary files /dev/null and b/out/ortho/342.jpg differ diff --git a/out/ortho/343.jpg b/out/ortho/343.jpg new file mode 100644 index 0000000..71d2cc4 Binary files /dev/null and b/out/ortho/343.jpg differ diff --git a/out/ortho/344.jpg b/out/ortho/344.jpg new file mode 100644 index 0000000..89a0240 Binary files /dev/null and b/out/ortho/344.jpg differ diff --git a/out/ortho/345.jpg b/out/ortho/345.jpg new file mode 100644 index 0000000..8436d53 Binary files /dev/null and b/out/ortho/345.jpg differ diff --git a/out/ortho/346.jpg b/out/ortho/346.jpg new file mode 100644 index 0000000..82c49f8 Binary files /dev/null and b/out/ortho/346.jpg differ diff --git a/out/ortho/347.jpg b/out/ortho/347.jpg new file mode 100644 index 0000000..c3a396a Binary files /dev/null and b/out/ortho/347.jpg differ diff --git a/out/ortho/348.jpg b/out/ortho/348.jpg new file mode 100644 index 0000000..6ce0fb7 Binary files /dev/null and b/out/ortho/348.jpg differ diff --git a/out/ortho/349.jpg b/out/ortho/349.jpg new file mode 100644 index 0000000..aef9795 Binary files /dev/null and b/out/ortho/349.jpg differ diff --git a/out/ortho/35.jpg b/out/ortho/35.jpg new file mode 100644 index 0000000..1174125 Binary files /dev/null and b/out/ortho/35.jpg differ diff --git a/out/ortho/350.jpg b/out/ortho/350.jpg new file mode 100644 index 0000000..8a6ca8f Binary files /dev/null and b/out/ortho/350.jpg differ diff --git a/out/ortho/351.jpg b/out/ortho/351.jpg new file mode 100644 index 0000000..6b4c70a Binary files /dev/null and b/out/ortho/351.jpg differ diff --git a/out/ortho/352.jpg b/out/ortho/352.jpg new file mode 100644 index 0000000..2a2653b Binary files /dev/null and b/out/ortho/352.jpg differ diff --git a/out/ortho/353.jpg b/out/ortho/353.jpg new file mode 100644 index 0000000..de7c892 Binary files /dev/null and b/out/ortho/353.jpg differ diff --git a/out/ortho/354.jpg b/out/ortho/354.jpg new file mode 100644 index 0000000..d15ff00 Binary files /dev/null and b/out/ortho/354.jpg differ diff --git a/out/ortho/355.jpg b/out/ortho/355.jpg new file mode 100644 index 0000000..a469c27 Binary files /dev/null and b/out/ortho/355.jpg differ diff --git a/out/ortho/36.jpg b/out/ortho/36.jpg new file mode 100644 index 0000000..c707fef Binary files /dev/null and b/out/ortho/36.jpg differ diff --git a/out/ortho/37.jpg b/out/ortho/37.jpg new file mode 100644 index 0000000..9969791 Binary files /dev/null and b/out/ortho/37.jpg differ diff --git a/out/ortho/38.jpg b/out/ortho/38.jpg new file mode 100644 index 0000000..d41aa00 Binary files /dev/null and b/out/ortho/38.jpg differ diff --git a/out/ortho/39.jpg b/out/ortho/39.jpg new file mode 100644 index 0000000..6eb1067 Binary files /dev/null and b/out/ortho/39.jpg differ diff --git a/out/ortho/4.jpg b/out/ortho/4.jpg new file mode 100644 index 0000000..a572729 Binary files /dev/null and b/out/ortho/4.jpg differ diff --git a/out/ortho/40.jpg b/out/ortho/40.jpg new file mode 100644 index 0000000..291bc7e Binary files /dev/null and b/out/ortho/40.jpg differ diff --git a/out/ortho/41.jpg b/out/ortho/41.jpg new file mode 100644 index 0000000..474d9b3 Binary files /dev/null and b/out/ortho/41.jpg differ diff --git a/out/ortho/42.jpg b/out/ortho/42.jpg new file mode 100644 index 0000000..b5272e4 Binary files /dev/null and b/out/ortho/42.jpg differ diff --git a/out/ortho/43.jpg b/out/ortho/43.jpg new file mode 100644 index 0000000..63ae952 Binary files /dev/null and b/out/ortho/43.jpg differ diff --git a/out/ortho/44.jpg b/out/ortho/44.jpg new file mode 100644 index 0000000..6cd5a39 Binary files /dev/null and b/out/ortho/44.jpg differ diff --git a/out/ortho/45.jpg b/out/ortho/45.jpg new file mode 100644 index 0000000..e3912cf Binary files /dev/null and b/out/ortho/45.jpg differ diff --git a/out/ortho/46.jpg b/out/ortho/46.jpg new file mode 100644 index 0000000..4f722ee Binary files /dev/null and b/out/ortho/46.jpg differ diff --git a/out/ortho/47.jpg b/out/ortho/47.jpg new file mode 100644 index 0000000..6e4b764 Binary files /dev/null and b/out/ortho/47.jpg differ diff --git a/out/ortho/48.jpg b/out/ortho/48.jpg new file mode 100644 index 0000000..f789ba0 Binary files /dev/null and b/out/ortho/48.jpg differ diff --git a/out/ortho/49.jpg b/out/ortho/49.jpg new file mode 100644 index 0000000..44e8d0e Binary files /dev/null and b/out/ortho/49.jpg differ diff --git a/out/ortho/5.jpg b/out/ortho/5.jpg new file mode 100644 index 0000000..a66bd02 Binary files /dev/null and b/out/ortho/5.jpg differ diff --git a/out/ortho/50.jpg b/out/ortho/50.jpg new file mode 100644 index 0000000..e8e9bf7 Binary files /dev/null and b/out/ortho/50.jpg differ diff --git a/out/ortho/51.jpg b/out/ortho/51.jpg new file mode 100644 index 0000000..dc78c36 Binary files /dev/null and b/out/ortho/51.jpg differ diff --git a/out/ortho/52.jpg b/out/ortho/52.jpg new file mode 100644 index 0000000..ce54b24 Binary files /dev/null and b/out/ortho/52.jpg differ diff --git a/out/ortho/53.jpg b/out/ortho/53.jpg new file mode 100644 index 0000000..69e2468 Binary files /dev/null and b/out/ortho/53.jpg differ diff --git a/out/ortho/54.jpg b/out/ortho/54.jpg new file mode 100644 index 0000000..fc68e29 Binary files /dev/null and b/out/ortho/54.jpg differ diff --git a/out/ortho/55.jpg b/out/ortho/55.jpg new file mode 100644 index 0000000..dff4b93 Binary files /dev/null and b/out/ortho/55.jpg differ diff --git a/out/ortho/56.jpg b/out/ortho/56.jpg new file mode 100644 index 0000000..01929b1 Binary files /dev/null and b/out/ortho/56.jpg differ diff --git a/out/ortho/57.jpg b/out/ortho/57.jpg new file mode 100644 index 0000000..81898ec Binary files /dev/null and b/out/ortho/57.jpg differ diff --git a/out/ortho/58.jpg b/out/ortho/58.jpg new file mode 100644 index 0000000..3f64b5b Binary files /dev/null and b/out/ortho/58.jpg differ diff --git a/out/ortho/59.jpg b/out/ortho/59.jpg new file mode 100644 index 0000000..30e050a Binary files /dev/null and b/out/ortho/59.jpg differ diff --git a/out/ortho/6.jpg b/out/ortho/6.jpg new file mode 100644 index 0000000..4ff91c6 Binary files /dev/null and b/out/ortho/6.jpg differ diff --git a/out/ortho/60.jpg b/out/ortho/60.jpg new file mode 100644 index 0000000..717452d Binary files /dev/null and b/out/ortho/60.jpg differ diff --git a/out/ortho/61.jpg b/out/ortho/61.jpg new file mode 100644 index 0000000..254e7a4 Binary files /dev/null and b/out/ortho/61.jpg differ diff --git a/out/ortho/62.jpg b/out/ortho/62.jpg new file mode 100644 index 0000000..0092a0d Binary files /dev/null and b/out/ortho/62.jpg differ diff --git a/out/ortho/63.jpg b/out/ortho/63.jpg new file mode 100644 index 0000000..4a90408 Binary files /dev/null and b/out/ortho/63.jpg differ diff --git a/out/ortho/64.jpg b/out/ortho/64.jpg new file mode 100644 index 0000000..afa011a Binary files /dev/null and b/out/ortho/64.jpg differ diff --git a/out/ortho/65.jpg b/out/ortho/65.jpg new file mode 100644 index 0000000..c6d3e5f Binary files /dev/null and b/out/ortho/65.jpg differ diff --git a/out/ortho/66.jpg b/out/ortho/66.jpg new file mode 100644 index 0000000..c5cde1f Binary files /dev/null and b/out/ortho/66.jpg differ diff --git a/out/ortho/67.jpg b/out/ortho/67.jpg new file mode 100644 index 0000000..114b973 Binary files /dev/null and b/out/ortho/67.jpg differ diff --git a/out/ortho/68.jpg b/out/ortho/68.jpg new file mode 100644 index 0000000..5732824 Binary files /dev/null and b/out/ortho/68.jpg differ diff --git a/out/ortho/69.jpg b/out/ortho/69.jpg new file mode 100644 index 0000000..ee335c5 Binary files /dev/null and b/out/ortho/69.jpg differ diff --git a/out/ortho/7.jpg b/out/ortho/7.jpg new file mode 100644 index 0000000..19c82c9 Binary files /dev/null and b/out/ortho/7.jpg differ diff --git a/out/ortho/70.jpg b/out/ortho/70.jpg new file mode 100644 index 0000000..a3166f3 Binary files /dev/null and b/out/ortho/70.jpg differ diff --git a/out/ortho/71.jpg b/out/ortho/71.jpg new file mode 100644 index 0000000..52de328 Binary files /dev/null and b/out/ortho/71.jpg differ diff --git a/out/ortho/72.jpg b/out/ortho/72.jpg new file mode 100644 index 0000000..8807239 Binary files /dev/null and b/out/ortho/72.jpg differ diff --git a/out/ortho/73.jpg b/out/ortho/73.jpg new file mode 100644 index 0000000..da3f558 Binary files /dev/null and b/out/ortho/73.jpg differ diff --git a/out/ortho/74.jpg b/out/ortho/74.jpg new file mode 100644 index 0000000..8279114 Binary files /dev/null and b/out/ortho/74.jpg differ diff --git a/out/ortho/75.jpg b/out/ortho/75.jpg new file mode 100644 index 0000000..f18f8fa Binary files /dev/null and b/out/ortho/75.jpg differ diff --git a/out/ortho/76.jpg b/out/ortho/76.jpg new file mode 100644 index 0000000..b461c12 Binary files /dev/null and b/out/ortho/76.jpg differ diff --git a/out/ortho/77.jpg b/out/ortho/77.jpg new file mode 100644 index 0000000..1587d98 Binary files /dev/null and b/out/ortho/77.jpg differ diff --git a/out/ortho/78.jpg b/out/ortho/78.jpg new file mode 100644 index 0000000..31d1aef Binary files /dev/null and b/out/ortho/78.jpg differ diff --git a/out/ortho/79.jpg b/out/ortho/79.jpg new file mode 100644 index 0000000..9063453 Binary files /dev/null and b/out/ortho/79.jpg differ diff --git a/out/ortho/8.jpg b/out/ortho/8.jpg new file mode 100644 index 0000000..d819360 Binary files /dev/null and b/out/ortho/8.jpg differ diff --git a/out/ortho/80.jpg b/out/ortho/80.jpg new file mode 100644 index 0000000..f73e622 Binary files /dev/null and b/out/ortho/80.jpg differ diff --git a/out/ortho/81.jpg b/out/ortho/81.jpg new file mode 100644 index 0000000..ce0d730 Binary files /dev/null and b/out/ortho/81.jpg differ diff --git a/out/ortho/82.jpg b/out/ortho/82.jpg new file mode 100644 index 0000000..ff4ad5b Binary files /dev/null and b/out/ortho/82.jpg differ diff --git a/out/ortho/83.jpg b/out/ortho/83.jpg new file mode 100644 index 0000000..0753bef Binary files /dev/null and b/out/ortho/83.jpg differ diff --git a/out/ortho/84.jpg b/out/ortho/84.jpg new file mode 100644 index 0000000..01faee8 Binary files /dev/null and b/out/ortho/84.jpg differ diff --git a/out/ortho/85.jpg b/out/ortho/85.jpg new file mode 100644 index 0000000..5d7744e Binary files /dev/null and b/out/ortho/85.jpg differ diff --git a/out/ortho/86.jpg b/out/ortho/86.jpg new file mode 100644 index 0000000..25ea3c1 Binary files /dev/null and b/out/ortho/86.jpg differ diff --git a/out/ortho/87.jpg b/out/ortho/87.jpg new file mode 100644 index 0000000..6cecc7b Binary files /dev/null and b/out/ortho/87.jpg differ diff --git a/out/ortho/88.jpg b/out/ortho/88.jpg new file mode 100644 index 0000000..78d582d Binary files /dev/null and b/out/ortho/88.jpg differ diff --git a/out/ortho/89.jpg b/out/ortho/89.jpg new file mode 100644 index 0000000..8d98e9b Binary files /dev/null and b/out/ortho/89.jpg differ diff --git a/out/ortho/9.jpg b/out/ortho/9.jpg new file mode 100644 index 0000000..57c3810 Binary files /dev/null and b/out/ortho/9.jpg differ diff --git a/out/ortho/90.jpg b/out/ortho/90.jpg new file mode 100644 index 0000000..e409ceb Binary files /dev/null and b/out/ortho/90.jpg differ diff --git a/out/ortho/91.jpg b/out/ortho/91.jpg new file mode 100644 index 0000000..6f5bf24 Binary files /dev/null and b/out/ortho/91.jpg differ diff --git a/out/ortho/92.jpg b/out/ortho/92.jpg new file mode 100644 index 0000000..d6a2a8e Binary files /dev/null and b/out/ortho/92.jpg differ diff --git a/out/ortho/93.jpg b/out/ortho/93.jpg new file mode 100644 index 0000000..7e461fe Binary files /dev/null and b/out/ortho/93.jpg differ diff --git a/out/ortho/94.jpg b/out/ortho/94.jpg new file mode 100644 index 0000000..6cd5b9d Binary files /dev/null and b/out/ortho/94.jpg differ diff --git a/out/ortho/95.jpg b/out/ortho/95.jpg new file mode 100644 index 0000000..f0df6d9 Binary files /dev/null and b/out/ortho/95.jpg differ diff --git a/out/ortho/96.jpg b/out/ortho/96.jpg new file mode 100644 index 0000000..d090c7d Binary files /dev/null and b/out/ortho/96.jpg differ diff --git a/out/ortho/97.jpg b/out/ortho/97.jpg new file mode 100644 index 0000000..f2552ce Binary files /dev/null and b/out/ortho/97.jpg differ diff --git a/out/ortho/98.jpg b/out/ortho/98.jpg new file mode 100644 index 0000000..c3b8fbb Binary files /dev/null and b/out/ortho/98.jpg differ diff --git a/out/ortho/99.jpg b/out/ortho/99.jpg new file mode 100644 index 0000000..349276c Binary files /dev/null and b/out/ortho/99.jpg differ diff --git a/out/protegidos.webp b/out/protegidos.webp new file mode 100644 index 0000000..671095a Binary files /dev/null and b/out/protegidos.webp differ diff --git a/out/relieve.webp b/out/relieve.webp new file mode 100644 index 0000000..7f46587 Binary files /dev/null and b/out/relieve.webp differ diff --git a/out/stats.json b/out/stats.json new file mode 100644 index 0000000..9f776c6 --- /dev/null +++ b/out/stats.json @@ -0,0 +1,24 @@ +{ + "n": 355, + "elegibles_km2": 47519, + "elegibles_pct": 9.5, + "acustico_km2": 505, + "estrellas": { + "1": 76, + "2": 142, + "3": 90, + "4": 36, + "5": 11 + }, + "mejor_db": 33.2, + "max_d_build_km": 9.8, + "prot_pct": 29.0, + "prot_km2": 144439, + "min_d_build": 2000, + "min_d_road": 400, + "max_spl": 45.0, + "min_sep_km": 10, + "max_d_access": 800, + "max_slope": 10.0, + "min_d_prot": 300 +} \ No newline at end of file diff --git a/out/visor.html b/out/visor.html new file mode 100644 index 0000000..4504944 --- /dev/null +++ b/out/visor.html @@ -0,0 +1,1395 @@ + + + + + +Visor de localizaciones · España + + + + + + + +
+ +
+
Elige una localización en la lista
para verla de cerca
+ +
+ + + + + + + +
+ +
+
+ Fondo + + + +
+
+ Encima + + + + +
+
+
+
arrastra para mover · rueda para zoom · clic en un punto
+ + +
+ + + + + diff --git a/src/build_protected.py b/src/build_protected.py new file mode 100644 index 0000000..d57319d --- /dev/null +++ b/src/build_protected.py @@ -0,0 +1,92 @@ +"""Máscara oficial de espacios protegidos, en sustitución de la de OSM. + +Une Red Natura 2000 (ZEC/LIC + ZEPA) y los espacios de designación nacional +(parques nacionales y naturales, reservas, monumentos naturales, paisajes +protegidos). Un punto vale solo si está fuera de las dos. + +Guarda además los nombres y la geometría reproyectada para poder decir, en cada +candidato, cuál es el espacio protegido más cercano y a qué distancia. +""" +import json +import pickle +import sys +import time + +import numpy as np +import rasterio.features +from shapely.geometry import mapping, shape +from shapely.ops import transform as shp_transform +from shapely.validation import make_valid + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C +from scipy.ndimage import distance_transform_edt + +NAME_KEYS = ("SITENAME", "SITE_NAME", "NAME", "CDDA_NAME", "name") +CODE_KEYS = ("SITECODE", "SITE_CODE", "CDDA_ID", "cdda_id") + + +def load(path, kind): + with open(path) as fh: + gj = json.load(fh) + recs = [] + bad = 0 + for f in gj.get("features", []): + g = f.get("geometry") + if not g: + continue + try: + geom = shape(g) + if not geom.is_valid: + geom = make_valid(geom) + geom = shp_transform(lambda xx, yy: C.to_grid(xx, yy), geom) + except Exception: + bad += 1 + continue + if geom.is_empty: + continue + p = f.get("properties", {}) + recs.append({ + "geom": geom, + "name": next((str(p[k]) for k in NAME_KEYS if p.get(k)), ""), + "code": next((str(p[k]) for k in CODE_KEYS if p.get(k)), ""), + "kind": kind, + }) + print(f" {path.name:22s} {len(recs):>6,} espacios ({bad} descartados)", flush=True) + return recs + + +def main(): + t0 = time.time() + recs = (load(C.RAW / "prot" / "natura2000.geojson", "Natura 2000") + + load(C.RAW / "prot" / "natda.geojson", "Designación nacional")) + with open(C.INTERIM / "prot_official.pkl", "wb") as fh: + pickle.dump(recs, fh, protocol=4) + + print("rasterizando…", flush=True) + mask = rasterio.features.rasterize( + [(mapping(r["geom"]), 1) for r in recs], + out_shape=(C.HEIGHT, C.WIDTH), transform=C.TRANSFORM, + fill=0, dtype=np.uint8, all_touched=True) # all_touched: conservador + np.save(C.INTERIM / "mask_protected_official.npy", mask) + + spain = np.load(C.INTERIM / "mask_spain.npy").astype(bool) + old = np.load(C.INTERIM / "mask_protected.npy").astype(bool) + new = mask.astype(bool) + print(f"\ncobertura sobre España:") + print(f" OSM {(old & spain).sum()/spain.sum()*100:5.1f}% " + f"({(old & spain).sum()*0.01:>8,.0f} km2)") + print(f" oficial {(new & spain).sum()/spain.sum()*100:5.1f}% " + f"({(new & spain).sum()*0.01:>8,.0f} km2)") + gained = (new & ~old & spain).sum() + print(f" protegido que OSM no veía: {gained*0.01:,.0f} km2 " + f"({gained/spain.sum()*100:.1f}% del país)") + + print("\ndistancia al protegido más cercano…", flush=True) + d = distance_transform_edt(~new, sampling=C.RES).astype(np.float32) + np.save(C.INTERIM / "d_protected_official.npy", d) + print(f" listo en {time.time()-t0:.0f}s") + + +if __name__ == "__main__": + main() diff --git a/src/build_rasters.py b/src/build_rasters.py new file mode 100644 index 0000000..0092423 --- /dev/null +++ b/src/build_rasters.py @@ -0,0 +1,168 @@ +"""Rasteriza las capas de OSM y calcula las transformadas de distancia. + +Cada capa de distancia responde a una pregunta concreta sobre un punto del mapa: + d_build ¿a qué distancia está el edificio más cercano? -> quién te oye + d_major ¿y la carretera con tráfico? -> quién te ve + d_access ¿y el vial por el que puedo meter un coche? -> si puedes llegar +Las tres a la vez son el problema: aislamiento y acceso tiran en sentidos +opuestos, y el sitio bueno es el que maximiza una con la otra acotada. + +Los puntos de Portugal, Andorra y el Pirineo francés se mezclan con los +españoles antes de calcular distancias. Si no, toda la franja fronteriza daría +aislamiento falso por ausencia de datos al otro lado. +""" +import pickle +import sys +import time + +import numpy as np +import rasterio.features +from scipy.ndimage import distance_transform_edt, uniform_filter +from shapely.geometry import mapping + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +SUFFIXES = ["", "_pt", "_ad", "_fr1", "_fr2", "_fr3"] + + +def load_points(base): + """Junta la capa española con las de los vecinos que existan.""" + xs, ys = [], [] + for s in SUFFIXES: + p = C.INTERIM / f"{base}{s}.npy" + if p.exists(): + a = np.load(p) + if a.shape[1]: + xs.append(a[0]) + ys.append(a[1]) + if not xs: + return None + return np.concatenate(xs), np.concatenate(ys) + + +def mask_from_points(base): + pts = load_points(base) + m = np.zeros((C.HEIGHT, C.WIDTH), dtype=bool) + if pts is None: + print(f" [aviso] sin datos para {base}", flush=True) + return m, 0 + r, c = C.xy_to_rowcol(*pts) + ok = C.inside(r, c) + m[r[ok], c[ok]] = True + return m, int(ok.sum()) + + +def edt(mask, name): + """Distancia en metros a la celda ocupada más cercana.""" + t0 = time.time() + d = distance_transform_edt(~mask, sampling=C.RES).astype(np.float32) + np.save(C.INTERIM / f"{name}.npy", d) + print(f" {name:12s} p50={np.median(d):8.0f} m max={d.max():9.0f} m " + f"({time.time()-t0:.0f}s)", flush=True) + return d + + +def rasterize_areas(cat, names_out=None): + with open(C.INTERIM / f"area_{cat}.pkl", "rb") as fh: + recs = pickle.load(fh) + shapes = [(mapping(r["geom"]), 1) for r in recs if not r["geom"].is_empty] + if not shapes: + return np.zeros((C.HEIGHT, C.WIDTH), dtype=np.uint8) + out = rasterio.features.rasterize( + shapes, out_shape=(C.HEIGHT, C.WIDTH), transform=C.TRANSFORM, + fill=0, dtype=np.uint8, all_touched=False) + print(f" area_{cat:14s} {len(shapes):>7,} polígonos " + f"{out.mean()*100:5.2f}% de la malla", flush=True) + return out + + +def main(): + print("=== máscara de España (unión de comunidades) ===", flush=True) + with open(C.INTERIM / "area_admin_region.pkl", "rb") as fh: + regions = [r for r in pickle.load(fh) if r["geom"].area > 1e6] + print(f" {len(regions)} comunidades, " + f"{sum(r['geom'].area for r in regions)/1e6:,.0f} km2", flush=True) + spain = rasterio.features.rasterize( + [(mapping(r["geom"]), 1) for r in regions], + out_shape=(C.HEIGHT, C.WIDTH), transform=C.TRANSFORM, + fill=0, dtype=np.uint8, all_touched=False) + + # La máscara tiene que usar EXACTAMENTE la misma ventana lat/lon con la que + # se filtraron los puntos. Si no, un territorio que entra en la malla pero + # cuyos edificios se descartaron (Melilla: la rejilla en 3035 no está + # alineada con lat/lon y baja más al sur que la ventana) aparece como el + # punto más aislado del país por ausencia de datos. + window = rasterio.features.rasterize( + [(mapping(C.window_polygon()), 1)], + out_shape=(C.HEIGHT, C.WIDTH), transform=C.TRANSFORM, + fill=0, dtype=np.uint8, all_touched=False) + dropped = int((spain & ~window.astype(bool)).sum()) + if dropped: + print(f" [ventana] {dropped*0.01:,.0f} km2 fuera de la ventana de " + f"análisis descartados (territorios sin datos de puntos)") + spain = (spain.astype(bool) & window.astype(bool)).astype(np.uint8) + np.save(C.INTERIM / "mask_spain.npy", spain) + print(f" {spain.sum()*0.01:,.0f} km2 rasterizados " + f"({spain.mean()*100:.1f}% de la malla)", flush=True) + + print("=== exclusiones ===", flush=True) + for cat in ("protected", "military", "water", "urban"): + np.save(C.INTERIM / f"mask_{cat}.npy", rasterize_areas(cat)) + + # Distancia al espacio protegido más cercano. Estar fuera del polígono no + # basta: los parques se delimitaron rodeando justo lo más despoblado, así + # que el óptimo del modelo tiende a pegarse al borde, que es donde hay + # zona periférica de protección y vigilancia. + print("=== distancia a exclusiones ===", flush=True) + for cat in ("protected", "military"): + m = np.load(C.INTERIM / f"mask_{cat}.npy").astype(bool) + edt(m, f"d_{cat}") + + print("=== rasterizando puntos y calculando distancias ===", flush=True) + layers = { + "d_build": "buildings", + "d_major": "line_major", + "d_minor": "line_minor", + "d_track": "line_track", + "d_path": "line_path", + "d_rail": "line_rail", + } + build_mask = None + for out_name, base in layers.items(): + m, n = mask_from_points(base) + print(f" {base:12s} {n:>12,} pts -> {m.sum():>11,} celdas", flush=True) + if out_name == "d_build": + build_mask = m.copy() + edt(m, out_name) + del m + + # Núcleos de población, agrupados por tamaño. + big = np.zeros((C.HEIGHT, C.WIDTH), dtype=bool) + any_p = np.zeros((C.HEIGHT, C.WIDTH), dtype=bool) + for kind in ("city", "town", "village", "hamlet", "isolated_dwelling", + "farm", "suburb", "borough", "quarter", "neighbourhood"): + m, n = mask_from_points(f"place_{kind}") + if n == 0: + continue + any_p |= m + if kind in ("city", "town"): + big |= m + edt(big, "d_place_big") + edt(any_p, "d_place_any") + del big, any_p + + # Densidad de edificios en 5 km: aproxima el resplandor lumínico y el + # "cuánta gente vive alrededor" mejor que la distancia al más cercano, + # que se deja engañar por una nave aislada en medio de la nada. + print("=== densidad de edificios 5 km ===", flush=True) + k = int(round(5000 / C.RES)) | 1 + dens = uniform_filter(build_mask.astype(np.float32), size=k, mode="nearest") + dens *= k * k # nº de celdas con edificio en la ventana + np.save(C.INTERIM / "dens_build_5km.npy", dens.astype(np.float32)) + print(f" p50={np.median(dens):.0f} p99={np.percentile(dens,99):.0f} " + "celdas con edificio en la ventana", flush=True) + + +if __name__ == "__main__": + main() diff --git a/src/build_rasters2.py b/src/build_rasters2.py new file mode 100644 index 0000000..00466dc --- /dev/null +++ b/src/build_rasters2.py @@ -0,0 +1,119 @@ +"""Segunda tanda de rásters: agua, arbolado, roca, calidad de pista y clima. + +Estas capas no miden aislamiento sino habitabilidad del sitio. La distinción +importa: el modelo anterior contestaba "¿está lejos de todo?", y estas capas +contestan "¿y se puede estar ahí?". + +Del clima se sacan dos números por celda: la máxima del mes más cálido y la +mínima del mes más frío. WorldClim viene a 2,5' (~4,6 km), demasiado grueso para +un país con tanto desnivel, así que se baja a 100 m corrigiendo por altitud con +el gradiente térmico estándar de 6,5 °C/km sobre la diferencia entre el DEM fino +y la altitud implícita de la celda gruesa. +""" +import glob +import pickle +import sys +import time + +import numpy as np +import rasterio +import rasterio.features +from rasterio.warp import Resampling, reproject +from scipy.ndimage import distance_transform_edt, uniform_filter +from shapely.geometry import mapping + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +LAPSE = 6.5 / 1000.0 # °C por metro + + +def edt_from_points(base, out): + a = np.load(C.INTERIM / f"{base}.npy") + m = np.zeros((C.HEIGHT, C.WIDTH), dtype=bool) + r, c = C.xy_to_rowcol(a[0], a[1]) + ok = C.inside(r, c) + m[r[ok], c[ok]] = True + d = distance_transform_edt(~m, sampling=C.RES).astype(np.float32) + np.save(C.INTERIM / f"{out}.npy", d) + print(f" {out:16s} desde {int(ok.sum()):>12,} pts " + f"p50={np.median(d)/1000:5.1f} km", flush=True) + del m, d + + +def frac_from_areas(cat, out, radius_m): + with open(C.INTERIM / f"area_{cat}.pkl", "rb") as fh: + geoms = pickle.load(fh) + shapes = [(mapping(g), 1) for g in geoms if not g.is_empty] + m = rasterio.features.rasterize( + shapes, out_shape=(C.HEIGHT, C.WIDTH), transform=C.TRANSFORM, + fill=0, dtype=np.uint8, all_touched=False) + k = int(round(radius_m * 2 / C.RES)) | 1 + frac = uniform_filter(m.astype(np.float32), size=k, mode="nearest") + np.save(C.INTERIM / f"{out}.npy", frac.astype(np.float32)) + print(f" {out:16s} {len(shapes):>8,} polígonos " + f"cobertura media {m.mean()*100:4.1f}%", flush=True) + del m, frac + + +def climate(): + """bio5 = máxima del mes más cálido; bio6 = mínima del mes más frío.""" + files = {} + for p in glob.glob(str(C.RAW / "clima" / "*.tif")): + n = p.rsplit("/", 1)[-1] + if n.endswith("_bio_5.tif") or n.endswith("bio_5.tif"): + files["tmax"] = p + elif n.endswith("_bio_6.tif") or n.endswith("bio_6.tif"): + files["tmin"] = p + elif "elev" in n: + files["elev"] = p + missing = {"tmax", "tmin", "elev"} - set(files) + if missing: + print(f" [aviso] faltan capas de clima: {missing}; se omite", flush=True) + return + + dem = np.load(C.INTERIM / "dem.npy") + out = {} + for key in ("tmax", "tmin", "elev"): + dst = np.full((C.HEIGHT, C.WIDTH), np.nan, dtype=np.float32) + with rasterio.open(files[key]) as src: + reproject(source=rasterio.band(src, 1), destination=dst, + src_transform=src.transform, src_crs=src.crs, + dst_transform=C.TRANSFORM, dst_crs=C.CRS_GRID, + src_nodata=src.nodata, dst_nodata=np.nan, + resampling=Resampling.bilinear) + out[key] = dst + + # Corrección altitudinal: la celda gruesa lleva su propia altitud media; + # la diferencia con el DEM fino explica buena parte del error térmico. + dz = np.where(np.isfinite(dem) & np.isfinite(out["elev"]), + dem - out["elev"], 0.0).astype(np.float32) + for key in ("tmax", "tmin"): + adj = (out[key] - LAPSE * dz).astype(np.float32) + np.save(C.INTERIM / f"{key}.npy", adj) + ok = np.isfinite(adj) + print(f" {key:16s} p5={np.nanpercentile(adj[ok],5):5.1f} " + f"p50={np.nanpercentile(adj[ok],50):5.1f} " + f"p95={np.nanpercentile(adj[ok],95):5.1f} °C", flush=True) + + +def main(): + t0 = time.time() + print("=== distancias a agua y pistas ===", flush=True) + for base, out in (("line_water", "d_water"), ("springs", "d_spring"), + ("line_track_good", "d_track_good"), + ("line_track_bad", "d_track_bad")): + edt_from_points(base, out) + + print("=== fracciones de cobertura del suelo ===", flush=True) + frac_from_areas("forest", "frac_forest", 500) + frac_from_areas("rock", "frac_rock", 300) + frac_from_areas("farmland", "frac_farmland", 300) + + print("=== clima ===", flush=True) + climate() + print(f"total {time.time()-t0:.0f}s") + + +if __name__ == "__main__": + main() diff --git a/src/build_viewer.py b/src/build_viewer.py new file mode 100644 index 0000000..d4f6dbf --- /dev/null +++ b/src/build_viewer.py @@ -0,0 +1,829 @@ +"""Genera el visor local: un único HTML autónomo, sin dependencias externas. + +Todo va incrustado (relieve, capas, candidatos y un recorte del terreno por +candidato) para que funcione abriendo el fichero con doble clic, sin servidor y +sin conexión. Por eso las imágenes son WebP y el terreno se guarda como uint8 +normalizado por tesela: en PNG y float el fichero se iba a 25 MB. + +El 3D se dibuja con canvas 2D y algoritmo del pintor en lugar de WebGL o una +librería externa: son 48x48 celdas, va sobrado a 60 fps, y evita depender de un +CDN que en local no cargaría. +""" +import base64 +import json +import pickle +import sys + +import numpy as np + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +F = 5 # factor del mapa base (500 m/px) +N = 48 # muestras por lado del recorte 3D +S = 2 # paso en celdas -> 48*2*100 m = 9,6 km de lado + + +def b64(path): + return base64.b64encode(path.read_bytes()).decode("ascii") + + +def terrain_tiles(cands, dem): + """Recorte de relieve alrededor de cada candidato, uint8 por tesela.""" + buf = np.zeros((len(cands), N * N), dtype=np.uint8) + meta = [] + half = N * S // 2 + for i, cd in enumerate(cands): + r, c = cd["row"], cd["col"] + r0, c0 = r - half, c - half + rr = np.clip(np.arange(r0, r0 + N * S, S), 0, C.HEIGHT - 1) + cc = np.clip(np.arange(c0, c0 + N * S, S), 0, C.WIDTH - 1) + tile = dem[np.ix_(rr, cc)] + tile = np.where(np.isfinite(tile), tile, 0.0) + lo, hi = float(tile.min()), float(tile.max()) + rng = max(hi - lo, 1.0) + buf[i] = ((tile - lo) / rng * 255).astype(np.uint8).ravel() + meta.append([round(lo, 1), round(rng, 1)]) + return buf, meta + + +def main(): + with open(C.INTERIM / "cands.pkl", "rb") as fh: + cands = pickle.load(fh) + dem = np.load(C.INTERIM / "dem.npy", mmap_mode="r") + + print(f"recortando terreno de {len(cands)} candidatos…", flush=True) + tiles, tmeta = terrain_tiles(cands, dem) + + keys = ["id", "lat", "lon", "estrellas", "score", "municipio", "comunidad", + "c_sonido", "c_soledad", "c_agua", "c_arbolado", "c_acceso", "c_clima", + "db_en_casa", "d_edificio", "d_carretera", "d_pista", "d_agua", + "d_protegido", "protegido_cerca", "km_protegido", "arbolado_pct", + "roca_pct", "pendiente", "tpi", "cota", "tmax", "tmin", "km_madrid"] + rows = [] + for i, cd in enumerate(cands): + rec = [cd[k] for k in keys] + rec.append(round(cd["col"] / F, 1)) # px en la imagen base + rec.append(round(cd["row"] / F, 1)) + rec.append(tmeta[i]) + rows.append(rec) + + payload = { + "keys": keys + ["px", "py", "tmeta"], + "rows": rows, + "img_w": (C.WIDTH // F), "img_h": (C.HEIGHT // F), + } + + vendor = C.ROOT / "vendor" + html = TEMPLATE + for token, val in ( + ("__LEAFLET_CSS__", (vendor / "leaflet.css").read_text(encoding="utf-8")), + ("__LEAFLET_JS__", (vendor / "leaflet.js").read_text(encoding="utf-8")), + ("__RELIEF__", b64(C.OUT / "relieve.webp")), + ("__PROT__", b64(C.OUT / "protegidos.webp")), + ("__ISO__", b64(C.OUT / "aislamiento_ov.webp")), + ("__TERRAIN__", base64.b64encode(tiles.tobytes()).decode("ascii")), + ("__DATA__", json.dumps(payload, ensure_ascii=False, separators=(",", ":"))), + ("__N_CAND__", f"{len(cands):,}".replace(",", ".")), + ("__N__", str(N)), + ): + html = html.replace(token, val) + + out = C.OUT / "visor.html" + out.write_text(html, encoding="utf-8") + print(f"{out} {out.stat().st_size/1e6:.2f} MB") + + +TEMPLATE = r""" + + + + +Visor de localizaciones · España + + + + + + + +
+ +
+
Elige una localización en la lista
para verla de cerca
+ +
+ + + + + + + +
+ +
+
+ Fondo + + + +
+
+ Encima + + + + +
+
+
+
arrastra para mover · rueda para zoom · clic en un punto
+ + +
+ + + + + +""" + +if __name__ == "__main__": + main() diff --git a/src/config.py b/src/config.py new file mode 100644 index 0000000..1548f10 --- /dev/null +++ b/src/config.py @@ -0,0 +1,95 @@ +"""Configuración compartida del pipeline de aislamiento. + +Malla de trabajo: EPSG:3035 (ETRS89-LAEA Europa). Es equiárea y está en metros, +así que una distancia euclídea en la malla es una distancia real en el terreno, +que es justo lo que necesitamos para "a cuántos metros está la casa más cercana". + +Cubrimos península + Baleares. Canarias queda fuera: en 3035 se deforma mucho y +además no es un destino al que se llegue conduciendo. +""" +from pathlib import Path + +import numpy as np +from pyproj import CRS, Transformer + +ROOT = Path(__file__).resolve().parent.parent +RAW = ROOT / "data" / "raw" +INTERIM = ROOT / "data" / "interim" +OUT = ROOT / "out" +for _d in (RAW, INTERIM, OUT): + _d.mkdir(parents=True, exist_ok=True) + +PBF = RAW / "spain-latest.osm.pbf" + +# Ventana geográfica: península + Baleares, con un pequeño margen. +LON_MIN, LON_MAX = -9.60, 4.40 +LAT_MIN, LAT_MAX = 35.85, 43.90 + +CRS_WGS84 = CRS.from_epsg(4326) +CRS_GRID = CRS.from_epsg(3035) + +to_grid = Transformer.from_crs(CRS_WGS84, CRS_GRID, always_xy=True).transform +to_wgs = Transformer.from_crs(CRS_GRID, CRS_WGS84, always_xy=True).transform + +RES = 100.0 # metros por celda + + +def _bounds(): + """Bbox en coordenadas de malla, muestreando el borde (la proyección curva).""" + lons = np.linspace(LON_MIN, LON_MAX, 200) + lats = np.linspace(LAT_MIN, LAT_MAX, 200) + edge_lon = np.concatenate([lons, lons, np.full(200, LON_MIN), np.full(200, LON_MAX)]) + edge_lat = np.concatenate([np.full(200, LAT_MIN), np.full(200, LAT_MAX), lats, lats]) + x, y = to_grid(edge_lon, edge_lat) + return x.min(), y.min(), x.max(), y.max() + + +_x0, _y0, _x1, _y1 = _bounds() +X_MIN = np.floor(_x0 / RES) * RES +Y_MIN = np.floor(_y0 / RES) * RES +X_MAX = np.ceil(_x1 / RES) * RES +Y_MAX = np.ceil(_y1 / RES) * RES + +WIDTH = int(round((X_MAX - X_MIN) / RES)) +HEIGHT = int(round((Y_MAX - Y_MIN) / RES)) + +# Transform estilo rasterio: origen arriba-izquierda, y decreciente. +from rasterio.transform import from_origin # noqa: E402 + +TRANSFORM = from_origin(X_MIN, Y_MAX, RES, RES) + + +def window_polygon(): + """La ventana de análisis lat/lon como polígono en coordenadas de malla. + + Es la misma ventana con la que se filtran los puntos en extract_osm, y por + eso hay que recortar la máscara del país con ella: en 3035 los bordes son + curvos, así que la rejilla rectangular abarca terreno fuera de la ventana. + """ + from shapely.geometry import Polygon + + n = 400 + lons = np.linspace(LON_MIN, LON_MAX, n) + lats = np.linspace(LAT_MIN, LAT_MAX, n) + ring_lon = np.concatenate([lons, np.full(n, LON_MAX), lons[::-1], np.full(n, LON_MIN)]) + ring_lat = np.concatenate([np.full(n, LAT_MIN), lats, np.full(n, LAT_MAX), lats[::-1]]) + x, y = to_grid(ring_lon, ring_lat) + return Polygon(zip(x, y)) + + +def xy_to_rowcol(x, y): + """Coordenadas de malla -> (fila, columna) en int32. Sin comprobar límites.""" + col = ((x - X_MIN) / RES).astype(np.int32) + row = ((Y_MAX - y) / RES).astype(np.int32) + return row, col + + +def inside(row, col): + return (row >= 0) & (row < HEIGHT) & (col >= 0) & (col < WIDTH) + + +if __name__ == "__main__": + print(f"malla EPSG:3035 {WIDTH} x {HEIGHT} celdas de {RES:.0f} m") + print(f" x: {X_MIN:,.0f} .. {X_MAX:,.0f}") + print(f" y: {Y_MIN:,.0f} .. {Y_MAX:,.0f}") + print(f" celdas: {WIDTH * HEIGHT / 1e6:,.1f} M ({WIDTH * HEIGHT * 4 / 1e9:.2f} GB por capa float32)") diff --git a/src/dem_terrain.py b/src/dem_terrain.py new file mode 100644 index 0000000..e0fb5ac --- /dev/null +++ b/src/dem_terrain.py @@ -0,0 +1,98 @@ +"""Mosaico del DEM sobre la malla de trabajo + derivadas del terreno. + +Reproyecta tesela a tesela directamente sobre la malla destino en vez de +construir un mosaico intermedio en 4326: evita un array gigante y es más rápido. + +Derivadas: + slope_deg pendiente, para descartar laderas donde no se puede montar nada. + tpi_2km posición topográfica: cota menos la cota media en 2 km. Negativo = + hondonada. Es la métrica que de verdad importa para una rave: en un + hueco el sonido queda encerrado y las luces no se ven desde fuera. +""" +import glob +import sys +import time + +import numpy as np +import rasterio +from rasterio.warp import Resampling, reproject +from rasterio.transform import from_origin +from scipy.ndimage import uniform_filter + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + + +def build_mosaic(): + dst = np.full((C.HEIGHT, C.WIDTH), np.nan, dtype=np.float32) + files = sorted(glob.glob(str(C.RAW / "dem" / "*.tif"))) + t0 = time.time() + for i, f in enumerate(files): + with rasterio.open(f) as src: + b = src.bounds + # Esquinas + bordes de la tesela a la malla, para acotar la ventana + # destino (la proyección curva los lados, no basta con 4 puntos). + lo = np.concatenate([np.linspace(b.left, b.right, 50)] * 2 + + [np.full(50, b.left), np.full(50, b.right)]) + la = np.concatenate([np.full(50, b.bottom), np.full(50, b.top)] + + [np.linspace(b.bottom, b.top, 50)] * 2) + x, y = C.to_grid(lo, la) + c0 = int(np.floor((x.min() - C.X_MIN) / C.RES)) - 2 + c1 = int(np.ceil((x.max() - C.X_MIN) / C.RES)) + 2 + r0 = int(np.floor((C.Y_MAX - y.max()) / C.RES)) - 2 + r1 = int(np.ceil((C.Y_MAX - y.min()) / C.RES)) + 2 + c0, r0 = max(c0, 0), max(r0, 0) + c1, r1 = min(c1, C.WIDTH), min(r1, C.HEIGHT) + if c1 <= c0 or r1 <= r0: + continue + + win = np.full((r1 - r0, c1 - c0), np.nan, dtype=np.float32) + win_tr = from_origin(C.X_MIN + c0 * C.RES, C.Y_MAX - r0 * C.RES, + C.RES, C.RES) + reproject( + source=rasterio.band(src, 1), + destination=win, + src_transform=src.transform, src_crs=src.crs, + dst_transform=win_tr, dst_crs=C.CRS_GRID, + dst_nodata=np.nan, + resampling=Resampling.average, + ) + sub = dst[r0:r1, c0:c1] + m = ~np.isnan(win) + sub[m] = win[m] + if (i + 1) % 25 == 0: + print(f" {i+1}/{len(files)} teselas {time.time()-t0:.0f}s", flush=True) + return dst + + +def main(): + print("mosaico del DEM…", flush=True) + dem = build_mosaic() + cover = np.isfinite(dem).mean() + print(f" cobertura {cover*100:.1f}% de la malla", flush=True) + np.save(C.INTERIM / "dem.npy", dem) + + filled = np.where(np.isfinite(dem), dem, 0).astype(np.float32) + + print("pendiente…", flush=True) + gy, gx = np.gradient(filled, C.RES) + slope = np.degrees(np.arctan(np.hypot(gx, gy))).astype(np.float32) + slope[~np.isfinite(dem)] = np.nan + np.save(C.INTERIM / "slope_deg.npy", slope) + del gy, gx + + print("TPI 2 km…", flush=True) + k = int(round(2000 / C.RES)) | 1 # ventana impar de ~2 km + tpi = (filled - uniform_filter(filled, size=k, mode="nearest")).astype(np.float32) + tpi[~np.isfinite(dem)] = np.nan + np.save(C.INTERIM / "tpi_2km.npy", tpi) + + ok = np.isfinite(dem) + print(f"cota min {np.nanmin(dem):.0f} max {np.nanmax(dem):.0f} m") + print(f"pendiente mediana {np.nanmedian(slope[ok]):.1f}°") + print(f"TPI p5/p95 {np.nanpercentile(tpi[ok],5):.1f} / " + f"{np.nanpercentile(tpi[ok],95):.1f} m") + + +if __name__ == "__main__": + main() diff --git a/src/dl_dem.sh b/src/dl_dem.sh new file mode 100755 index 0000000..51a1913 --- /dev/null +++ b/src/dl_dem.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Descarga las teselas del DEM Copernicus GLO-90 que cubren península + Baleares. +# El bucket es público (open data en AWS), no hace falta autenticación. +# Las teselas que caen enteras en el mar no existen y devuelven 404: se ignoran. +set -u +DEST=/home/sito/RAVE_SCOUT/data/raw/dem +mkdir -p "$DEST" +BASE=https://copernicus-dem-90m.s3.amazonaws.com + +gen_urls() { + for lat in $(seq 35 43); do + for lon in $(seq 1 10); do + printf '%s/Copernicus_DSM_COG_30_N%02d_00_W%03d_00_DEM/Copernicus_DSM_COG_30_N%02d_00_W%03d_00_DEM.tif\n' \ + "$BASE" "$lat" "$lon" "$lat" "$lon" + done + for lon in $(seq 0 4); do + printf '%s/Copernicus_DSM_COG_30_N%02d_00_E%03d_00_DEM/Copernicus_DSM_COG_30_N%02d_00_E%03d_00_DEM.tif\n' \ + "$BASE" "$lat" "$lon" "$lat" "$lon" + done + done +} + +gen_urls | xargs -P 8 -I{} sh -c ' + f=$(basename "{}") + curl -sfL --retry 3 -o "'"$DEST"'/$f.part" "{}" && mv "'"$DEST"'/$f.part" "'"$DEST"'/$f" || rm -f "'"$DEST"'/$f.part" +' +echo "teselas descargadas: $(ls -1 "$DEST"/*.tif 2>/dev/null | wc -l)" +du -sh "$DEST" diff --git a/src/dl_orthos.py b/src/dl_orthos.py new file mode 100644 index 0000000..0d9e5db --- /dev/null +++ b/src/dl_orthos.py @@ -0,0 +1,89 @@ +"""Descarga la ortofoto del PNOA de cada candidato para incrustarla en el visor. + +El PNOA es del Instituto Geográfico Nacional, gratuito y reutilizable citando la +fuente. Se pide por WMS un recorte de 640 m de lado centrado en cada punto, se +reescala a 320 px y se recomprime: así el visor enseña la foto aérea real al +seleccionar una localización, al instante y sin conexión. + +Se piden a 384 px y se bajan a 320: reescalar desde algo más grande da bastante +mejor resultado que pedir directamente el tamaño final. +""" +import io +import pickle +import sys +import time +from concurrent.futures import ThreadPoolExecutor + +import requests +from PIL import Image +from pyproj import Transformer + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +WMS = "https://www.ign.es/wms-inspire/pnoa-ma" +FETCH_PX = 384 +OUT_PX = 320 +MPP = 2.0 # metros por píxel del recorte final +QUALITY = 72 +WORKERS = 4 # suave con los servidores del IGN + +to3857 = Transformer.from_crs(4326, 3857, always_xy=True).transform +DEST = C.INTERIM / "ortho" + + +def fetch(cd): + path = DEST / f"{cd['id']}.jpg" + if path.exists() and path.stat().st_size > 2000: + return "cache" + x, y = to3857(cd["lon"], cd["lat"]) + half = OUT_PX * MPP / 2 + params = { + "SERVICE": "WMS", "VERSION": "1.3.0", "REQUEST": "GetMap", + "LAYERS": "OI.OrthoimageCoverage", "STYLES": "", "CRS": "EPSG:3857", + "BBOX": f"{x-half},{y-half},{x+half},{y+half}", + "WIDTH": FETCH_PX, "HEIGHT": FETCH_PX, "FORMAT": "image/jpeg", + } + for attempt in range(4): + try: + r = requests.get(WMS, params=params, timeout=120) + r.raise_for_status() + if not r.headers.get("content-type", "").startswith("image"): + raise ValueError("respuesta no es imagen") + im = Image.open(io.BytesIO(r.content)).convert("RGB") + im = im.resize((OUT_PX, OUT_PX), Image.LANCZOS) + im.save(path, "JPEG", quality=QUALITY, optimize=True, progressive=True) + return "ok" + except Exception as e: + if attempt == 3: + return f"fallo: {type(e).__name__}" + time.sleep(2 * (attempt + 1)) + + +def main(): + DEST.mkdir(parents=True, exist_ok=True) + with open(C.INTERIM / "cands.pkl", "rb") as fh: + cands = pickle.load(fh) + print(f"{len(cands)} ortofotos ({OUT_PX}px, {OUT_PX*MPP:.0f} m de lado)", flush=True) + + t0 = time.time() + done = {"ok": 0, "cache": 0} + fails = [] + with ThreadPoolExecutor(WORKERS) as ex: + for i, res in enumerate(ex.map(fetch, cands), 1): + if res in done: + done[res] += 1 + else: + fails.append(res) + if i % 50 == 0: + print(f" {i}/{len(cands)} {time.time()-t0:.0f}s", flush=True) + + total = sum(f.stat().st_size for f in DEST.glob("*.jpg")) + print(f"nuevas={done['ok']} ya estaban={done['cache']} fallos={len(fails)}") + if fails: + print(" " + "; ".join(sorted(set(fails))[:3])) + print(f"total en disco: {total/1e6:.1f} MB en {time.time()-t0:.0f}s") + + +if __name__ == "__main__": + main() diff --git a/src/dl_protected.py b/src/dl_protected.py new file mode 100644 index 0000000..79228b9 --- /dev/null +++ b/src/dl_protected.py @@ -0,0 +1,117 @@ +"""Descarga los espacios protegidos oficiales desde los servicios de la EEA. + +Dos conjuntos, porque no son el mismo y hacen falta los dos: + Natura 2000 ZEC/LIC + ZEPA, la red europea. Es lo que España notifica a + Bruselas, la misma cartografía que publica el MITECO. + NatDA espacios de designación nacional: parques naturales, reservas, + monumentos naturales, paisajes protegidos. Muchos NO están en + Natura 2000, y son justo los que preocupan. + +Se descarga por teselas y con las geometrías generalizadas a ~50 m, que sobra +para una malla de 100 m y evita traerse cientos de MB de vértices. Si una tesela +llega truncada por el límite del servidor, se parte en cuatro y se reintenta. + +Nota sobre TLS: los servidores del ministerio mandan la cadena incompleta; para +ellos se construye un bundle con el intermedio oficial de la FNMT. Los de la EEA +verifican con el almacén normal. +""" +import json +import sys +import time +from pathlib import Path + +import requests + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +BASE = "https://bio.discomap.eea.europa.eu/arcgis/rest/services/ProtectedSites" +SETS = { + "natura2000": f"{BASE}/Natura2000Sites/MapServer/2/query", + "natda": f"{BASE}/NatDAv23_Dyna_WM/MapServer/4/query", +} +OFFSET = 0.0005 # ~50 m de generalización +DEST = C.RAW / "prot" + + +def fetch(url, bbox, depth=0): + """Devuelve la lista de features del bbox, subdividiendo si viene truncado.""" + params = { + "where": "1=1", + "geometry": ",".join(f"{v:.4f}" for v in bbox), + "geometryType": "esriGeometryEnvelope", + "inSR": "4326", "outSR": "4326", + "spatialRel": "esriSpatialRelIntersects", + "outFields": "*", + "returnGeometry": "true", + "maxAllowableOffset": str(OFFSET), + "f": "geojson", + } + for attempt in range(4): + try: + r = requests.get(url, params=params, timeout=300) + r.raise_for_status() + d = r.json() + break + except Exception as e: + if attempt == 3: + print(f" [fallo] {bbox} {type(e).__name__}", flush=True) + return [] + time.sleep(3 * (attempt + 1)) + feats = d.get("features") or [] + truncated = d.get("properties", {}).get("exceededTransferLimit") or \ + d.get("exceededTransferLimit") + if truncated and depth < 3: + x0, y0, x1, y1 = bbox + mx, my = (x0 + x1) / 2, (y0 + y1) / 2 + out = [] + for sub in ((x0, y0, mx, my), (mx, y0, x1, my), + (x0, my, mx, y1), (mx, my, x1, y1)): + out += fetch(url, sub, depth + 1) + return out + return feats + + +def main(): + DEST.mkdir(parents=True, exist_ok=True) + step = 2.0 + tiles = [] + y = C.LAT_MIN + while y < C.LAT_MAX: + x = C.LON_MIN + while x < C.LON_MAX: + tiles.append((x, y, min(x + step, C.LON_MAX), min(y + step, C.LAT_MAX))) + x += step + y += step + + for name, url in SETS.items(): + out_path = DEST / f"{name}.geojson" + if out_path.exists() and out_path.stat().st_size > 1000: + print(f"{name}: ya descargado ({out_path.stat().st_size/1e6:.1f} MB)") + continue + print(f"=== {name}: {len(tiles)} teselas ===", flush=True) + seen, feats = set(), [] + t0 = time.time() + for i, b in enumerate(tiles, 1): + got = fetch(url, b) + new = 0 + for f in got: + # las teselas solapan en los bordes: deduplicamos por id de sitio + p = f.get("properties", {}) + key = (p.get("SITECODE") or p.get("CDDA_ID") or + p.get("SITE_CODE") or p.get("OBJECTID") or json.dumps(p)[:80]) + if key in seen: + continue + seen.add(key) + feats.append(f) + new += 1 + print(f" [{i:>2}/{len(tiles)}] {b[0]:6.1f},{b[1]:5.1f} " + f"+{new:<5} total={len(feats):,} {time.time()-t0:5.0f}s", flush=True) + with open(out_path, "w") as fh: + json.dump({"type": "FeatureCollection", "features": feats}, fh) + print(f" -> {out_path.name} {len(feats):,} espacios " + f"{out_path.stat().st_size/1e6:.1f} MB", flush=True) + + +if __name__ == "__main__": + main() diff --git a/src/extract_areas.py b/src/extract_areas.py new file mode 100644 index 0000000..609116d --- /dev/null +++ b/src/extract_areas.py @@ -0,0 +1,93 @@ +"""Pasada 2 sobre el PBF: polígonos (exclusiones + límites administrativos). + +Se usa with_areas() porque muchos parques nacionales y todos los límites +administrativos están en OSM como relaciones multipolígono, no como ways +cerrados: sin ensamblar relaciones la máscara se dejaría fuera justo los +espacios más grandes. + +El límite de España (admin_level=2) no es opcional. El extracto de Geofabrik +está recortado al país, así que al otro lado de la frontera no hay edificios +en los datos aunque sí los haya en el terreno. Sin recortar, todo el borde +portugués y pirenaico saldría como "aislamiento perfecto" por un artefacto. +Los niveles 4 y 8 (comunidad y municipio) sirven para etiquetar candidatos. +""" +import pickle +import sys +import time + +import osmium +import osmium.geom +from shapely import wkb as shapely_wkb +from shapely.ops import transform as shp_transform + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +WKB = osmium.geom.WKBFactory() +ADMIN_LEVELS = {"2": "country", "4": "region", "8": "municipality"} + + +def classify(tags): + if tags.get("boundary") == "administrative": + lvl = ADMIN_LEVELS.get(tags.get("admin_level", "")) + return ("admin_" + lvl) if lvl else None + if tags.get("landuse") == "military" or "military" in tags: + return "military" + if (tags.get("boundary") in ("protected_area", "national_park") + or tags.get("leisure") == "nature_reserve" + or "protect_class" in tags): + return "protected" + if tags.get("natural") == "water" or tags.get("landuse") == "reservoir": + return "water" + if tags.get("landuse") in ("residential", "industrial", "commercial", "retail"): + return "urban" + return None + + +def main(): + keys = osmium.filter.KeyFilter( + "landuse", "military", "boundary", "leisure", "protect_class", "natural") + fp = osmium.FileProcessor(str(C.PBF)).with_areas().with_filter(keys) + + cats = ["admin_country", "admin_region", "admin_municipality", + "military", "protected", "water", "urban"] + out = {c: [] for c in cats} + t0 = time.time() + n = nbad = 0 + for obj in fp: + if not obj.is_area(): + continue + cat = classify(obj.tags) + if cat is None: + continue + try: + geom = shapely_wkb.loads(WKB.create_multipolygon(obj), hex=True) + except Exception: + nbad += 1 + continue + if geom.is_empty: + continue + n += 1 + if n % 20000 == 0: + print(f" {n:,} áreas {time.time()-t0:.0f}s", flush=True) + out[cat].append({"geom": geom, "name": obj.tags.get("name", "")}) + + print(f"total {n:,} áreas ({nbad:,} descartadas) en {time.time()-t0:.0f}s", flush=True) + + for cat, items in out.items(): + # Reproyectamos a la malla antes de guardar para que el rasterizado + # posterior sea directo. + recs = [] + for it in items: + try: + g = shp_transform(lambda xx, yy: C.to_grid(xx, yy), it["geom"]) + except Exception: + continue + recs.append({"geom": g, "name": it["name"]}) + with open(C.INTERIM / f"area_{cat}.pkl", "wb") as fh: + pickle.dump(recs, fh, protocol=4) + print(f" area_{cat:20s} {len(recs):>8,} polígonos", flush=True) + + +if __name__ == "__main__": + main() diff --git a/src/extract_extra.py b/src/extract_extra.py new file mode 100644 index 0000000..ac1c67a --- /dev/null +++ b/src/extract_extra.py @@ -0,0 +1,140 @@ +"""Capas adicionales para el modelo de estrellas. + +Pasada A (líneas y nodos): cursos de agua y pistas clasificadas por calidad. +Pasada B (áreas): arbolado, roca desnuda, matorral y cultivo. + +La calidad de la pista importa tanto como su existencia: una grade1 compactada +admite una furgoneta cargada, una grade5 embarrada en un repecho no. OSM lo +etiqueta con tracktype y surface, así que se separan en dos capas en vez de +tratar todas las pistas por igual como hacía el primer modelo. +""" +import pickle +import sys +import time +from array import array + +import numpy as np +import osmium +import osmium.geom +from shapely import wkb as shapely_wkb +from shapely.ops import transform as shp_transform + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C +from extract_osm import Lines, Pts # reutilizamos los acumuladores + +WKB = osmium.geom.WKBFactory() + +WATER_LINE = {"river", "stream", "canal"} +GOOD_TRACK = {"grade1", "grade2"} +GOOD_SURF = {"paved", "asphalt", "concrete", "compacted", "gravel", + "fine_gravel", "pebblestone"} +BAD_SURF = {"mud", "sand", "grass", "ground", "dirt", "earth"} + + +def area_class(tags): + n, lu = tags.get("natural"), tags.get("landuse") + if n == "wood" or lu == "forest": + return "forest" + if n in ("bare_rock", "scree", "shingle", "cliff"): + return "rock" + if n in ("scrub", "heath", "grassland"): + return "scrub" + if lu in ("farmland", "orchard", "vineyard"): + return "farmland" + return None + + +def pass_lines(pbf, suffix): + water = Lines() + track_good, track_bad = Lines(), Lines() + springs = Pts() + + keys = osmium.filter.KeyFilter("waterway", "highway", "natural") + fp = (osmium.FileProcessor(pbf, osmium.osm.NODE | osmium.osm.WAY) + .with_locations("flex_mem").with_filter(keys)) + + t0 = time.time() + for obj in fp: + tags = obj.tags + if obj.is_node(): + if tags.get("natural") == "spring" and obj.location.valid(): + springs.add(obj.location.lon, obj.location.lat) + continue + + ww, hw = tags.get("waterway"), tags.get("highway") + if ww is None and hw != "track": + continue + try: + lons = [nd.lon for nd in obj.nodes] + lats = [nd.lat for nd in obj.nodes] + except osmium.InvalidLocationError: + continue + if len(lons) < 2: + continue + + if ww in WATER_LINE: + water.add(lons, lats) + elif hw == "track": + tt, sf = tags.get("tracktype"), tags.get("surface") + good = (tt in GOOD_TRACK) or (sf in GOOD_SURF) + bad = (sf in BAD_SURF) or (tt in ("grade4", "grade5")) + (track_bad if (bad and not good) else track_good).add(lons, lats) + + print(f" pasada de líneas {time.time()-t0:.0f}s", flush=True) + + def save(name, lon, lat): + lon, lat = np.asarray(lon), np.asarray(lat) + m = ((lon >= C.LON_MIN) & (lon <= C.LON_MAX) & + (lat >= C.LAT_MIN) & (lat <= C.LAT_MAX)) + x, y = C.to_grid(lon[m], lat[m]) + np.save(C.INTERIM / f"{name}{suffix}.npy", + np.stack([x, y]).astype(np.float32)) + print(f" {name+suffix:20s} {int(m.sum()):>12,} pts", flush=True) + + save("line_water", *water.densify()) + save("line_track_good", *track_good.densify()) + save("line_track_bad", *track_bad.densify()) + save("springs", *springs.arrays()) + + +def pass_areas(pbf, suffix): + keys = osmium.filter.KeyFilter("natural", "landuse") + fp = osmium.FileProcessor(pbf).with_areas().with_filter(keys) + out = {"forest": [], "rock": [], "scrub": [], "farmland": []} + t0 = time.time() + n = 0 + for obj in fp: + if not obj.is_area(): + continue + cat = area_class(obj.tags) + if cat is None: + continue + try: + g = shapely_wkb.loads(WKB.create_multipolygon(obj), hex=True) + except Exception: + continue + if g.is_empty: + continue + n += 1 + out[cat].append(g) + print(f" pasada de áreas: {n:,} en {time.time()-t0:.0f}s", flush=True) + + for cat, geoms in out.items(): + recs = [] + for g in geoms: + try: + recs.append(shp_transform(lambda xx, yy: C.to_grid(xx, yy), g)) + except Exception: + continue + with open(C.INTERIM / f"area_{cat}{suffix}.pkl", "wb") as fh: + pickle.dump(recs, fh, protocol=4) + print(f" area_{cat+suffix:18s} {len(recs):>10,}", flush=True) + + +if __name__ == "__main__": + pbf = sys.argv[1] if len(sys.argv) > 1 else str(C.PBF) + suf = sys.argv[2] if len(sys.argv) > 2 else "" + print(f"== {pbf} ==", flush=True) + pass_lines(pbf, suf) + pass_areas(pbf, suf) diff --git a/src/extract_osm.py b/src/extract_osm.py new file mode 100644 index 0000000..52f64b4 --- /dev/null +++ b/src/extract_osm.py @@ -0,0 +1,207 @@ +"""Pasada 1 sobre el PBF: extrae puntos y líneas de interés. + +Salida: .npy con coordenadas ya proyectadas a EPSG:3035 en data/interim/. + +Las líneas (carreteras, pistas) se densifican a <=60 m para que al rasterizarlas +en la malla de 100 m no queden huecos entre vértices: OSM guarda tramos rectos +largos con solo dos nodos, y sin densificar una autovía aparecería como una +ristra de puntos sueltos en vez de como una barrera continua. +""" +import sys +import time +from array import array + +import numpy as np +import osmium + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +STEP_M = 60.0 + +# Clasificación de viales. La distinción que importa para una rave no es +# "carretera vs no", sino: por dónde pasa gente (malo) y por dónde puedo meter +# un coche cargado (imprescindible). +MAJOR = {"motorway", "trunk", "primary", "secondary", + "motorway_link", "trunk_link", "primary_link", "secondary_link"} +MINOR = {"tertiary", "unclassified", "residential", "living_street", + "tertiary_link", "road"} +TRACK = {"track", "service"} +PATH = {"path", "footway", "bridleway", "cycleway", "steps"} + +PLACE_KEEP = {"city", "town", "village", "hamlet", "isolated_dwelling", + "farm", "suburb", "borough", "quarter", "neighbourhood"} + + +class Lines: + """Acumula vértices de varias polilíneas en arrays planos.""" + + def __init__(self): + self.lon = array("d") + self.lat = array("d") + self.counts = array("i") + + def add(self, lons, lats): + if len(lons) < 2: + return + self.lon.extend(lons) + self.lat.extend(lats) + self.counts.append(len(lons)) + + def densify(self): + """Devuelve puntos a <=STEP_M de separación, vectorizado.""" + if not self.counts: + return np.zeros(0), np.zeros(0) + lon = np.frombuffer(self.lon, dtype=np.float64) + lat = np.frombuffer(self.lat, dtype=np.float64) + counts = np.frombuffer(self.counts, dtype=np.int32).astype(np.int64) + + ends = np.cumsum(counts) - 1 # último vértice de cada línea + keep = np.ones(lon.size, dtype=bool) + keep[ends] = False # no arranca segmento + i0 = np.flatnonzero(keep) + i1 = i0 + 1 + + lon0, lat0 = lon[i0], lat[i0] + lon1, lat1 = lon[i1], lat[i1] + + # Longitud métrica aproximada (suficiente para decidir cuántos pasos). + mlat = 111320.0 + mlon = mlat * np.cos(np.radians(0.5 * (lat0 + lat1))) + seg = np.hypot((lon1 - lon0) * mlon, (lat1 - lat0) * mlat) + + nstep = np.maximum(1, np.ceil(seg / STEP_M).astype(np.int64)) + total = int(nstep.sum()) + + # t = 0, 1/n, 2/n ... por segmento, generado sin bucles de Python. + rep = np.repeat(np.arange(nstep.size), nstep) + offs = np.arange(total) - np.repeat(np.cumsum(nstep) - nstep, nstep) + t = offs / nstep[rep] + + out_lon = lon0[rep] + (lon1[rep] - lon0[rep]) * t + out_lat = lat0[rep] + (lat1[rep] - lat0[rep]) * t + # Añadimos los vértices finales para no perder los extremos. + return (np.concatenate([out_lon, lon[ends]]), + np.concatenate([out_lat, lat[ends]])) + + +class Pts: + def __init__(self): + self.lon = array("d") + self.lat = array("d") + + def add(self, lo, la): + self.lon.append(lo) + self.lat.append(la) + + def arrays(self): + return (np.frombuffer(self.lon, dtype=np.float64), + np.frombuffer(self.lat, dtype=np.float64)) + + +def main(pbf=None, suffix=""): + pbf = pbf or str(C.PBF) + buildings = Pts() + lamps = Pts() + places = {k: Pts() for k in PLACE_KEEP} + lines = {"major": Lines(), "minor": Lines(), "track": Lines(), + "path": Lines(), "rail": Lines()} + + # El filtro por clave se aplica en C++, así que los ~200 M de nodos sin + # etiquetas ni siquiera llegan a Python. Las localizaciones se indexan + # antes del filtro, así que las geometrías de los ways siguen resolviendo. + keys = osmium.filter.KeyFilter( + "building", "highway", "place", "railway", "building:part") + + fp = (osmium.FileProcessor(pbf, osmium.osm.NODE | osmium.osm.WAY) + .with_locations("flex_mem") + .with_filter(keys)) + + t0 = time.time() + n = 0 + n_bad_geom = 0 + for obj in fp: + n += 1 + if n % 5_000_000 == 0: + print(f" {n/1e6:5.1f} M objetos {time.time()-t0:6.0f}s " + f"edificios={len(buildings.lon):,}", flush=True) + tags = obj.tags + + if obj.is_node(): + loc = obj.location + if not loc.valid(): + continue + if tags.get("highway") == "street_lamp": + lamps.add(loc.lon, loc.lat) + p = tags.get("place") + if p in PLACE_KEEP: + places[p].add(loc.lon, loc.lat) + continue + + # --- ways --- + b = tags.get("building") + hw = tags.get("highway") + rw = tags.get("railway") + if b is None and hw is None and rw is None: + continue + + try: + lons = [nd.lon for nd in obj.nodes] + lats = [nd.lat for nd in obj.nodes] + except osmium.InvalidLocationError: + n_bad_geom += 1 + continue + if not lons: + continue + + if b is not None and b != "no": + # Centroide del contorno: para "a qué distancia está la casa más + # cercana" el centro de la planta es precisión de sobra. + buildings.add(sum(lons) / len(lons), sum(lats) / len(lats)) + continue + + if hw is not None: + if hw in MAJOR: + lines["major"].add(lons, lats) + elif hw in MINOR: + lines["minor"].add(lons, lats) + elif hw in TRACK: + lines["track"].add(lons, lats) + elif hw in PATH: + lines["path"].add(lons, lats) + elif rw in ("rail", "light_rail", "narrow_gauge"): + lines["rail"].add(lons, lats) + + print(f"pasada completa: {n:,} objetos en {time.time()-t0:.0f}s " + f"({n_bad_geom:,} ways sin geometría)", flush=True) + + def save(name, lon, lat): + lon = np.asarray(lon) + lat = np.asarray(lat) + m = ((lon >= C.LON_MIN) & (lon <= C.LON_MAX) & + (lat >= C.LAT_MIN) & (lat <= C.LAT_MAX)) + x, y = C.to_grid(lon[m], lat[m]) + arr = np.stack([x, y]).astype(np.float32) + np.save(C.INTERIM / f"{name}{suffix}.npy", arr) + print(f" {name+suffix:20s} {arr.shape[1]:>12,} pts", flush=True) + + print("proyectando y guardando…", flush=True) + save("buildings", *buildings.arrays()) + save("lamps", *lamps.arrays()) + for k, v in places.items(): + lo, la = v.arrays() + if len(lo): + save(f"place_{k}", lo, la) + for k, v in lines.items(): + lo, la = v.densify() + save(f"line_{k}", lo, la) + + +if __name__ == "__main__": + # Sin argumentos: España. Con argumentos: , para los + # países vecinos, cuyos edificios hacen falta para no inflar el aislamiento + # a este lado de la frontera. + if len(sys.argv) > 1: + main(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else "") + else: + main() diff --git a/src/render_map.py b/src/render_map.py new file mode 100644 index 0000000..3eefcd6 --- /dev/null +++ b/src/render_map.py @@ -0,0 +1,133 @@ +"""Renderiza el mapa de aislamiento a PNG (versión clara y oscura). + +Se dibuja solo España porque el análisis solo es válido dentro de España: los +datos de los vecinos entran para calcular distancias correctas en la frontera, +pero sus celdas no se puntúan ni se muestran. + +Dos versiones de color porque un PNG no se adapta al tema de quien lo mira, y +el informe sí. +""" +import csv +import sys + +import numpy as np +from PIL import Image, ImageDraw, ImageFont + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +F = 8 # factor de reducción -> ~1645 px de ancho + +# Rampa secuencial azul (100 -> 700), magnitud continua, un solo tono. +RAMP = ["#cde2fb", "#b7d3f6", "#9ec5f4", "#86b6ef", "#6da7ec", "#5598e7", + "#3987e5", "#2a78d6", "#256abf", "#1c5cab", "#184f95", "#104281", + "#0d366b"] + +# El fondo de cada versión es el mismo que el de la página, para que el mapa no +# lleve marco y se funda con el papel. +# +# En claro la rampa va de claro a oscuro: más aislado = azul más denso, que +# destaca sobre papel claro. En oscuro se invierte. No es un volteo automático +# del gradiente sino la misma rampa recorrida al revés, porque lo que tiene que +# conservarse es el significado: lo aislado siempre es lo que más resalta. Con +# la rampa sin invertir, los vacíos —justo lo que buscamos— se disolverían en +# el fondo negro. +THEMES = { + "light": dict(surface="#e9edf2", ring="#ffffff", label="#0f1620", + halo="#ffffff", reverse=False), + "dark": dict(surface="#0b0f14", ring="#0b0f14", label="#e8edf3", + halo="#0b0f14", reverse=True), +} +MARKER = {"light": "#d4541f", "dark": "#f08a5a"} + + +def hex2rgb(h): + return tuple(int(h[i:i + 2], 16) for i in (1, 3, 5)) + + +def ramp_lut(reverse=False): + cols = np.array([hex2rgb(h) for h in (RAMP[::-1] if reverse else RAMP)], + dtype=np.float32) + xs = np.linspace(0, 255, len(cols)) + lut = np.zeros((256, 3), dtype=np.uint8) + for ch in range(3): + lut[:, ch] = np.interp(np.arange(256), xs, cols[:, ch]).astype(np.uint8) + return lut + + +def block_reduce(a, f, how="mean"): + h = (a.shape[0] // f) * f + w = (a.shape[1] // f) * f + a = a[:h, :w].reshape(h // f, f, w // f, f) + return a.mean(axis=(1, 3)) if how == "mean" else a.max(axis=(1, 3)) + + +def get_font(size): + for p in ("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", + "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"): + try: + return ImageFont.truetype(p, size) + except OSError: + continue + return ImageFont.load_default() + + +def main(): + iso = np.load(C.INTERIM / "iso_pure.npy") + spain = np.load(C.INTERIM / "mask_spain.npy").astype(bool) + + vals = iso[spain & np.isfinite(iso)] + lo, hi = np.percentile(vals, 2), np.percentile(vals, 99.5) + print(f"escala del mapa: {lo:.0f} .. {hi:.0f} m") + + filled = np.where(np.isfinite(iso), iso, 0.0) + small = block_reduce(filled, F, "mean") + land = block_reduce(spain.astype(np.float32), F, "mean") > 0.35 + + norm = np.clip((small - lo) / (hi - lo), 0, 1) + idx = (norm * 255).astype(np.uint8) + + with open(C.OUT / "candidatos_estrellas.csv") as fh: + cands = [c for c in csv.DictReader(fh) if int(c["estrellas"]) >= 3] + cands.sort(key=lambda c: -float(c["score"])) + for i, c in enumerate(cands, 1): + c["rank"] = i + + for theme, col in THEMES.items(): + rgb = ramp_lut(col["reverse"])[idx] + img_arr = np.empty(rgb.shape, dtype=np.uint8) + img_arr[:] = hex2rgb(col["surface"]) + img_arr[land] = rgb[land] + img = Image.fromarray(img_arr, "RGB") + d = ImageDraw.Draw(img) + + f_lbl = get_font(15) + for cd in cands: + x, y = C.to_grid(float(cd["lon"]), float(cd["lat"])) + px = (x - C.X_MIN) / C.RES / F + py = (C.Y_MAX - y) / C.RES / F + rank = int(cd["rank"]) + r = 6 if rank <= 10 else 4 + # anillo del color de la superficie: separa el punto del fondo + d.ellipse([px - r - 2, py - r - 2, px + r + 2, py + r + 2], + fill=hex2rgb(col["ring"])) + d.ellipse([px - r, py - r, px + r, py + r], fill=hex2rgb(MARKER[theme])) + if rank <= 10: + t = str(rank) + tx, ty = px + r + 5, py - 9 + for ox, oy in ((-1, 0), (1, 0), (0, -1), (0, 1)): + d.text((tx + ox, ty + oy), t, font=f_lbl, + fill=hex2rgb(col["halo"])) + d.text((tx, ty), t, font=f_lbl, fill=hex2rgb(col["label"])) + + out = C.OUT / f"mapa_{theme}.png" + # Paleta de 200 colores: la rampa es continua pero el ojo no distingue + # más pasos, y el PNG baja de ~1,5 MB a ~400 KB, que importa porque va + # incrustado en el informe. + img.convert("P", palette=Image.ADAPTIVE, colors=200).save(out, optimize=True) + print(f" {out.name} {img.size[0]}x{img.size[1]} " + f"{out.stat().st_size/1024:.0f} KB") + + +if __name__ == "__main__": + main() diff --git a/src/render_relief.py b/src/render_relief.py new file mode 100644 index 0000000..bd39d09 --- /dev/null +++ b/src/render_relief.py @@ -0,0 +1,113 @@ +"""Mapa base de relieve de España: tinta hipsométrica + sombreado del terreno. + +Se genera desde el DEM de Copernicus ya remuestreado a la malla, así que el +relieve coincide exactamente con las capas del modelo y con las coordenadas que +usa el visor para colocar los marcadores. + +La paleta va deliberadamente desaturada: el mapa es el fondo, y lo que tiene que +destacar encima son los candidatos. +""" +import sys + +import numpy as np +from PIL import Image +from scipy.ndimage import uniform_filter + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +F = 5 # factor de reducción -> ~2631 x 2152 px + +# Tinta hipsométrica: cota (m) -> color +HYPSO = [ + (0, (0xd7, 0xdf, 0xcd)), (150, (0xc6, 0xd0, 0xb4)), + (400, (0xd5, 0xcd, 0xa5)), (800, (0xcd, 0xb4, 0x88)), + (1200, (0xbc, 0x9a, 0x72)), (1700, (0xa5, 0x84, 0x6c)), + (2200, (0x99, 0x86, 0x80)), (2800, (0xc2, 0xbc, 0xba)), + (3500, (0xee, 0xed, 0xec)), +] + + +def block_mean(a, f): + h, w = (a.shape[0] // f) * f, (a.shape[1] // f) * f + return a[:h, :w].reshape(h // f, f, w // f, f).mean(axis=(1, 3)) + + +def hypso_lut(): + lut = np.zeros((3501, 3), dtype=np.float32) + elevs = [e for e, _ in HYPSO] + for ch in range(3): + lut[:, ch] = np.interp(np.arange(3501), elevs, + [c[ch] for _, c in HYPSO]) + return lut + + +def hillshade(z, res, az=315.0, alt=45.0): + """Sombreado estándar. z ya viene suavizado para que no granule.""" + gy, gx = np.gradient(z, res) + slope = np.arctan(np.hypot(gx, gy)) + aspect = np.arctan2(-gx, gy) + a, zn = np.radians(alt), np.radians(az) + hs = (np.sin(a) * np.cos(slope) + + np.cos(a) * np.sin(slope) * np.cos(zn - aspect)) + return np.clip(hs, 0, 1) + + +def main(): + dem = np.load(C.INTERIM / "dem.npy") + spain = np.load(C.INTERIM / "mask_spain.npy").astype(bool) + + z = block_mean(np.where(np.isfinite(dem), dem, 0.0), F) + land = block_mean(spain.astype(np.float32), F) > 0.35 + res = C.RES * F + + # Exageración vertical: sin ella la meseta sale plana y no se lee nada. + hs = hillshade(uniform_filter(z, 3, mode="nearest") * 2.2, res) + hs = 0.55 + 0.45 * hs # no aplastar a negro los umbríos + + lut = hypso_lut() + zi = np.clip(z, 0, 3500).astype(np.int32) + rgb = lut[zi] * hs[..., None] + + # RGBA con el mar transparente: así el visor puede ponerle el fondo que + # quiera sin que aparezca un rectángulo de color alrededor del país. + rgba = np.zeros((*z.shape, 4), dtype=np.uint8) + rgba[..., :3][land] = np.clip(rgb[land], 0, 255).astype(np.uint8) + rgba[..., 3][land] = 255 + + out = C.OUT / "relieve.png" + Image.fromarray(rgba, "RGBA").save(out, optimize=True) + print(f" {out.name} {z.shape[1]}x{z.shape[0]} " + f"{out.stat().st_size/1024:.0f} KB") + + # --- capa de espacios protegidos --- + prot = block_mean( + np.load(C.INTERIM / "mask_protected_official.npy").astype(np.float32), F) + ov = np.zeros((*z.shape, 4), dtype=np.uint8) + m = land & (prot > 0.25) + ov[..., 0][m], ov[..., 1][m], ov[..., 2][m] = 0xc4, 0x3f, 0x2f + ov[..., 3][m] = (np.clip(prot[m], 0, 1) * 150).astype(np.uint8) + p_out = C.OUT / "protegidos.png" + Image.fromarray(ov, "RGBA").save(p_out, optimize=True) + print(f" {p_out.name} {p_out.stat().st_size/1024:.0f} KB " + f"({m.mean()*100:.1f}% de la imagen)") + + # --- capa de aislamiento --- + iso = block_mean(np.nan_to_num(np.load(C.INTERIM / "iso_pure.npy")), F) + lo, hi = np.percentile(iso[land], 2), np.percentile(iso[land], 99) + t = np.clip((iso - lo) / (hi - lo), 0, 1) + io = np.zeros((*z.shape, 4), dtype=np.uint8) + io[..., 0][land] = (0x14 + t[land] * 0x20).astype(np.uint8) + io[..., 1][land] = (0x3a + t[land] * 0x60).astype(np.uint8) + io[..., 2][land] = (0x7a + t[land] * 0x70).astype(np.uint8) + io[..., 3][land] = (t[land] * 205).astype(np.uint8) + i_out = C.OUT / "aislamiento_ov.png" + Image.fromarray(io, "RGBA").save(i_out, optimize=True) + print(f" {i_out.name} {i_out.stat().st_size/1024:.0f} KB") + + # Geometría que necesita el visor para situar los marcadores. + print(f" origen x={C.X_MIN} y={C.Y_MAX} escala={res} m/px") + + +if __name__ == "__main__": + main() diff --git a/src/report.py b/src/report.py new file mode 100644 index 0000000..2398ad3 --- /dev/null +++ b/src/report.py @@ -0,0 +1,417 @@ +"""Genera el informe HTML a partir del CSV de estrellas y los mapas.""" +import base64 +import csv +import html +import sys + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + + +def b64(path): + return base64.b64encode(path.read_bytes()).decode("ascii") + + +def esc(s): + return html.escape(str(s)) + + +def km(m): + return f"{int(m)/1000:.1f}".replace(".", ",") + + +def main(): + import json + ST = json.load(open(C.OUT / "stats.json")) + rows = list(csv.DictReader(open(C.OUT / "candidatos_estrellas.csv"))) + rows.sort(key=lambda r: -float(r["score"])) + dist = {s: sum(1 for r in rows if int(r["estrellas"]) == s) for s in range(1, 6)} + top = [r for r in rows if int(r["estrellas"]) >= 4] + + trs = [] + for i, r in enumerate(top, 1): + lat, lon = r["lat"], r["lon"] + s = int(r["estrellas"]) + gm = f"https://www.google.com/maps/@{lat},{lon},1500m/data=!3m1!1e3" + sv = f"https://www.google.com/maps/@?api=1&map_action=pano&viewpoint={lat},{lon}" + trs.append(f""" +{i} +{'★'*s} +{esc(lat)}, {esc(lon)} +{esc(r['municipio']) or '—'} +{esc(r['comunidad'])} +{esc(r['db_en_casa'])} +{km(r['d_edificio'])} +{esc(r['d_pista'])} +{km(r['d_agua'])} +{esc(r['arbolado_pct'])} +{km(r['d_protegido'])} +{esc(r['km_madrid'])} +sat +sv +""") + + mil = lambda v: f"{v:,}".replace(",", ".") + doc = TEMPLATE.format( + map_light=b64(C.OUT / "mapa_light.png"), + map_dark=b64(C.OUT / "mapa_dark.png"), + rows="\n".join(trs), + n=len(rows), ntop=len(top), + d5=dist[5], d4=dist[4], d3=dist[3], d2=dist[2], d1=dist[1], + prot_pct=str(ST["prot_pct"]).replace(".", ","), + prot_km2=mil(ST["prot_km2"]), + eleg_pct=str(ST["elegibles_pct"]).replace(".", ","), + eleg_km2=mil(ST["elegibles_km2"]), + acus_km2=mil(ST["acustico_km2"]), + mejor_db=str(ST["mejor_db"]).replace(".", ","), + max_build=str(ST["max_d_build_km"]).replace(".", ","), + min_build=mil(ST["min_d_build"]), min_road=ST["min_d_road"], + max_spl=int(ST["max_spl"]), min_sep=ST["min_sep_km"], + max_acc=ST["max_d_access"], max_slope=int(ST["max_slope"]), + min_prot=ST["min_d_prot"], + ) + out = C.OUT / "informe.html" + out.write_text(doc, encoding="utf-8") + print(f"{out} {out.stat().st_size/1e6:.2f} MB ({len(rows)} candidatos, {len(top)} en tabla)") + + +TEMPLATE = """Dónde no hay nadie · aislamiento en España + + +
+ +
+
Análisis geoespacial · malla de 100 m · península y Baleares
+

Dónde no
hay nadie

+

Un barrido de los 498.528 km² de la España peninsular y balear, + celda a celda, buscando sitios lejos de todo a los que además se pueda llegar — con + los límites oficiales de espacios protegidos, no los aproximados.

+ +
+ +
+
+

Corrección sobre la primera versión. Los espacios protegidos se + tomaron primero de OpenStreetMap, que resultó cubrir solo el 15,3 % de España. Los + datos oficiales —Red Natura 2000 y espacios de designación nacional, descargados de + los servicios de la Agencia Europea de Medio Ambiente— cubren el 29,0 %. + Eran 87.480 km² protegidos que no se estaban viendo.

+

Con el límite bueno, 33 de los 60 candidatos de la primera versión estaban + dentro de espacio protegido, incluidos los seis primeros. Ese ranking queda + anulado y sustituido por el de aquí abajo.

+
+
+ +
+
+
Lo que sale
+

Cinco cifras

+
+
+
{prot_pct} %De España está protegido: {prot_km2} km² + entre Red Natura 2000 y figuras nacionales.
+
{eleg_km2} km²Pasan las eliminatorias de acceso, terreno + y legalidad: el {eleg_pct} % del país.
+
{acus_km2} km²Y de eso, lo que además baja de {max_spl} dB + en la casa más cercana. El sonido es el cuello de botella real.
+
{max_build} kmLa mayor distancia a un edificio que se + alcanza en España. Menos de lo que casi nadie supone.
+
{n}Localizaciones finales, valoradas de 1 a 5 estrellas + y separadas {min_sep} km entre sí.
+
+
+ +
+
+
El mapa
+

El vacío tiene forma de red

+
+
+
+ Mapa de aislamiento de España. Las carreteras dibujan una malla clara sobre
+        el país; las zonas más aisladas aparecen en azul denso en Sierra Morena, Montes de
+        Toledo, La Serena, los Monegros, Doñana y el Pirineo. + Mapa de aislamiento de España en modo oscuro. Las zonas más aisladas
+        aparecen claras sobre fondo oscuro. +
+
+ pobladovacío + candidatos de 3★ o más +
+
Cada píxel es la media geométrica de tres distancias: al edificio más + cercano, a la carretera más cercana y al núcleo de población más cercano. La red de + carreteras se dibuja sola en negativo. Lo que queda entre las mallas es lo que + buscamos. Galicia y la costa mediterránea, pese a la fama de una y el vacío aparente + de la otra, casi no tienen huecos: el poblamiento disperso llega a todas partes.
+
+
+ +
+
+
Método
+

Seis criterios y una eliminatoria

+
+
+

Primero se descarta. Una celda queda fuera, por buena que sea, si está dentro de un + espacio protegido oficial o a menos de {min_prot} m de su borde, en zona militar, en + agua, a menos de {min_build} m de un edificio, a menos de {min_road} m de una carretera, + a más de {max_acc} m de un vial por el que meter un coche, o con más de {max_slope}° de + pendiente. Sobreviven {eleg_km2} km², el {eleg_pct} % del país.

+

Encima de eso hay un umbral acústico: si el nivel estimado en la casa más cercana + supera los {max_spl} dB —el límite nocturno típico en suelo rural— el punto se descarta + aunque puntúe bien en todo lo demás. Ese filtro es el que de verdad corta: deja + {acus_km2} km² en toda España. El sonido, y no la soledad, es el cuello de botella.

+

Lo que sobrevive se puntúa en seis criterios independientes, y 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 un sitio de cuatro estrellas.

+
+
+

Sonido 30 %

+ No es una nota abstracta: es propagación real. Divergencia esférica, absorción + atmosférica en frecuencias bajas, apantallamiento del relieve y absorción del + arbolado, partiendo de 130 dB a 1 m. El resultado es cuántos dB llegan a la casa más + cercana. El mejor punto del país se queda en {mejor_db} dB.
+

Soledad 22 %

+ Lejanía de edificios, de carreteras y de núcleos, y cuánta construcción hay en + 5 km a la redonda.
+

Acceso 18 %

+ Pista en condiciones cerca. OSM distingue firme y grado, así que una pista + compactada no cuenta igual que una grade5 embarrada. Más llano y sin pedregal.
+

Agua 12 %

+ Río, arroyo o fuente entre 120 y 900 m. Cerca, pero no encima.
+

Arbolado 10 %

+ Entre el 20 y el 65 % de cobertura alrededor: sombra y pantalla visual sin que + sea selva por la que no se pasa.
+

Clima 8 %

+ Máxima del mes más cálido y mínima del más frío, de WorldClim, corregidas por + altitud con el DEM de 100 m.
+
+
+

Los candidatos no se eligen cogiendo los mejores del país, porque saldrían todos del + mismo rincón. Se divide España en bloques de 20 km y se coge el mejor punto elegible de + cada bloque; después se exige un mínimo de {min_sep} km entre los elegidos, porque dos + bloques vecinos pueden escoger cada uno una celda pegada a su frontera común y quedar a + cien metros. Salen {n} opciones repartidas por todas partes y con un abanico natural de + estrellas.

+
+
+
★★★★★{d5}
+
★★★★{d4}
+
★★★{d3}
+
★★{d2}
+
{d1}
+
+
+ +
+
+
Resultados
+

Las {ntop} mejores

+
+
+

Todas las de cuatro y cinco estrellas. dB es el nivel estimado en la casa más + cercana; por debajo de 35 es ruido de fondo del campo de noche. casa y + agua en kilómetros, pista en metros, árbol en porcentaje de + cobertura, prot la distancia al espacio protegido más cercano.

+
+
+ + + + + + + +{rows} + +
#valorcoordenadasmunicipiocomunidaddBcasa kmpista magua kmárbol %prot kmMadrid kmver
+
+
+ +
+
+
Límites
+

Lo que este análisis no sabe

+
+
+
    +
  • No ve vallas ni propiedad

    Casi todo el suroeste que domina la lista es + dehesa privada, en fincas cerradas y cotos de caza. El modelo ve una pista que llega; + no ve la cadena y el candado a la entrada. Sigue siendo la mayor diferencia entre el + ranking y la realidad de campo.
  • +
  • Los protegidos son de diciembre de 2024

    Son los límites oficiales, pero + las figuras autonómicas cambian y hay ordenanzas municipales que no están en ningún + mapa nacional. El margen de 300 m ayuda, no exime de comprobarlo.
  • +
  • El modelo acústico es de manual

    Divergencia, absorción y apantallamiento + son buenas aproximaciones, pero una inversión térmica nocturna o el viento a favor + pueden llevar el bajo mucho más lejos de lo que dice el número.
  • +
  • Ignora la estación

    Buena parte de estos sitios son monte mediterráneo en + 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.
  • +
+
+
+ +
+

Datos: OpenStreetMap (ODbL) para edificios, viales, agua, arbolado y roca; + Red Natura 2000 y espacios de designación nacional vía Agencia Europea de Medio Ambiente; + Copernicus DEM GLO-90 para el relieve; WorldClim 2.1 para el clima. + Malla EPSG:3035 a 100 m. Canarias, Ceuta y Melilla quedan fuera.

+

Visor local en out/visor.html, tabla completa en + out/candidatos_estrellas.csv, pipeline en + src/.

+
+ +
+""" + + +if __name__ == "__main__": + # anchura de las barras del reparto de estrellas + import re + _orig = TEMPLATE + rows = list(csv.DictReader(open(C.OUT / "candidatos_estrellas.csv"))) + dist = {s: sum(1 for r in rows if int(r["estrellas"]) == s) for s in range(1, 6)} + mx = max(dist.values()) + for s in range(1, 6): + TEMPLATE = TEMPLATE.replace("{d%dw}" % s, f"{dist[s]/mx*100:.1f}") + main() diff --git a/src/revisar.py b/src/revisar.py new file mode 100644 index 0000000..0bcd5be --- /dev/null +++ b/src/revisar.py @@ -0,0 +1,170 @@ +"""Revisión de coherencia de todo el resultado. + +Comprueba invariantes sobre los datos finales, no sobre el código: que ningún +candidato incumpla una eliminatoria, que las cifras del CSV coincidan con los +rásters de los que salieron, y que el visor y el informe cuenten lo mismo. +""" +import csv +import json +import re +import sys +from pathlib import Path + +import numpy as np + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C +import stars as S + +FAIL = [] +WARN = [] + + +def check(cond, msg, warn=False): + if cond: + print(f" OK {msg}") + else: + (WARN if warn else FAIL).append(msg) + print(f" {'AVISO' if warn else 'FALLO'} {msg}") + + +def main(): + rows = list(csv.DictReader(open(C.OUT / "candidatos_estrellas.csv"))) + n = len(rows) + print(f"=== {n} candidatos ===\n") + + lat = np.array([float(r["lat"]) for r in rows]) + lon = np.array([float(r["lon"]) for r in rows]) + rr = np.array([int(r["row"]) for r in rows]) + cc = np.array([int(r["col"]) for r in rows]) + + print("-- eliminatorias --") + spain = np.load(C.INTERIM / "mask_spain.npy").astype(bool) + prot = np.load(C.INTERIM / "mask_protected_official.npy").astype(bool) + mil = np.load(C.INTERIM / "mask_military.npy").astype(bool) + wat = np.load(C.INTERIM / "mask_water.npy").astype(bool) + check(spain[rr, cc].all(), "todos dentro de España") + check(not prot[rr, cc].any(), "ninguno dentro de espacio protegido oficial") + check(not mil[rr, cc].any(), "ninguno en zona militar") + check(not wat[rr, cc].any(), "ninguno sobre lámina de agua") + + d_prot = np.load(C.INTERIM / "d_protected_official.npy") + check(d_prot[rr, cc].min() >= S.MIN_D_PROT, + f"margen al protegido >= {S.MIN_D_PROT} m (mín real " + f"{d_prot[rr,cc].min():.0f} m)") + + d_build = np.load(C.INTERIM / "d_build.npy") + check(d_build[rr, cc].min() >= S.MIN_D_BUILD, + f"distancia a edificio >= {S.MIN_D_BUILD} m (mín real " + f"{d_build[rr,cc].min():.0f} m)") + + d_major = np.load(C.INTERIM / "d_major.npy") + d_minor = np.load(C.INTERIM / "d_minor.npy") + d_road = np.minimum(d_major, d_minor) + check(d_road[rr, cc].min() >= S.MIN_D_ROAD, + f"distancia a carretera >= {S.MIN_D_ROAD} m (mín real " + f"{d_road[rr,cc].min():.0f} m)") + + d_tg = np.load(C.INTERIM / "d_track_good.npy") + d_tb = np.load(C.INTERIM / "d_track_bad.npy") + d_acc = np.minimum(np.minimum(d_major, d_minor), np.minimum(d_tg, d_tb)) + check(d_acc[rr, cc].max() <= S.MAX_D_ACCESS, + f"acceso <= {S.MAX_D_ACCESS} m (máx real {d_acc[rr,cc].max():.0f} m)") + + slope = np.load(C.INTERIM / "slope_deg.npy") + check(slope[rr, cc].max() <= S.MAX_SLOPE, + f"pendiente <= {S.MAX_SLOPE}° (máx real {slope[rr,cc].max():.1f}°)") + + print("\n-- coherencia CSV vs rásters --") + csv_db = np.array([float(r["d_edificio"]) for r in rows]) + check(np.abs(csv_db - d_build[rr, cc]).max() < 1.5, + "columna d_edificio coincide con el ráster") + csv_prot = np.array([float(r["d_protegido"]) for r in rows]) + check(np.abs(csv_prot - d_prot[rr, cc]).max() < 1.5, + "columna d_protegido coincide con el ráster") + + # el nivel sonoro debe reproducirse con la fórmula declarada + tpi = np.load(C.INTERIM / "tpi_2km.npy") + ff = np.load(C.INTERIM / "frac_forest.npy") + d = np.maximum(d_build[rr, cc], 1.0) + spl = (S.L0 - 20 * np.log10(d) - S.ALPHA_KM * d / 1000.0 + - ff[rr, cc] * S.MAX_FOREST_ATT + - np.clip(-tpi[rr, cc] / 4.0, 0, S.MAX_TERRAIN_ATT)) + csv_spl = np.array([float(r["db_en_casa"]) for r in rows]) + check(np.abs(csv_spl - spl).max() < 0.15, + "columna db_en_casa reproduce el modelo acústico") + + print("\n-- coordenadas y etiquetas --") + check(np.isfinite(lat).all() and np.isfinite(lon).all(), "sin coordenadas NaN") + check((lat > 35.9).all() and (lat < 44).all() and + (lon > -9.6).all() and (lon < 4.4).all(), "coordenadas dentro de la ventana") + sin_mun = sum(1 for r in rows if not r["municipio"]) + check(sin_mun == 0, f"todos con municipio ({sin_mun} sin asignar)", + warn=sin_mun < n * 0.02) + sin_com = sum(1 for r in rows if not r["comunidad"]) + check(sin_com == 0, f"todos con comunidad ({sin_com} sin asignar)", + warn=sin_com < n * 0.02) + + print("\n-- valores fuera de rango --") + for col, lo, hi in (("c_sonido", 0, 100), ("c_soledad", 0, 100), + ("c_agua", 0, 100), ("c_arbolado", 0, 100), + ("c_acceso", 0, 100), ("c_clima", 0, 100), + ("estrellas", 1, 5), ("score", 0, 100), + ("arbolado_pct", 0, 100), ("roca_pct", 0, 100)): + v = np.array([float(r[col]) for r in rows]) + check(bool((v >= lo).all() and (v <= hi).all()), + f"{col} dentro de [{lo},{hi}] (real {v.min():.0f}..{v.max():.0f})") + + print("\n-- separación entre candidatos --") + x, y = C.to_grid(lon, lat) + from scipy.spatial import cKDTree + t = cKDTree(np.c_[x, y]) + dd, _ = t.query(np.c_[x, y], k=2) + check(dd[:, 1].min() > 1000, + f"sin duplicados pegados (separación mínima {dd[:,1].min()/1000:.1f} km)") + + print("\n-- estrellas --") + st = np.array([int(r["estrellas"]) for r in rows]) + sc = np.array([float(r["score"]) for r in rows]) + ok_mono = all(sc[st == a].min() >= sc[st == b].max() - 1e-6 + for a, b in ((5, 4), (4, 3), (3, 2), (2, 1)) + if (st == a).any() and (st == b).any()) + check(ok_mono, "las estrellas son monótonas respecto al score") + for s in (5, 4, 3, 2, 1): + print(f" {'★'*s:<5} {(st==s).sum():>5}") + + print("\n-- ficheros de salida --") + v = (C.OUT / "visor.html") + h = v.read_text(encoding="utf-8") + m = re.search(r'const DATA = (\{.*?\});\nconst NT', h, re.S) + D = json.loads(m.group(1)) + check(len(D["rows"]) == n, f"el visor lleva los mismos {n} candidatos") + check("Leaflet 1.9.4" in h, "Leaflet incrustado (sin CDN)") + check(not re.search(r']+src=|]+href="http', h), + "el visor no carga recursos externos") + ter = re.search(r'atob\("([A-Za-z0-9+/=]+)"\)', h).group(1) + import base64 + check(len(base64.b64decode(ter)) == n * 48 * 48, "recortes de relieve completos") + inf = (C.OUT / "informe.html").read_text(encoding="utf-8") + check(f"{n}" in inf.replace(".", ""), "el informe cita el mismo total", + warn=True) + for f in ("visor.html", "informe.html", "candidatos_estrellas.csv", + "score_rave.tif", "relieve.png"): + check((C.OUT / f).exists(), f"existe out/{f}") + + print("\n" + "=" * 58) + if FAIL: + print(f"{len(FAIL)} FALLOS:") + for m_ in FAIL: + print(" -", m_) + else: + print("Sin fallos.") + if WARN: + print(f"{len(WARN)} avisos:") + for m_ in WARN: + print(" -", m_) + return 1 if FAIL else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/score.py b/src/score.py new file mode 100644 index 0000000..a246c64 --- /dev/null +++ b/src/score.py @@ -0,0 +1,201 @@ +"""Combina las capas en dos rankings distintos y extrae candidatos. + +1) AISLAMIENTO PURO: ¿qué punto de España está más lejos de cualquier rastro de + civilización? Es la pregunta interesante, pero su respuesta suele ser una + cara norte a 2.400 m sin forma de llegar. + +2) APTITUD PARA RAVE: aislamiento acotado por lo que la fiesta necesita de + verdad. Acceso rodado, suelo llano, fuera de espacio protegido y militar, y + a ser posible en una hondonada, que encierra el sonido y tapa las luces. + +Las dos restricciones tiran en sentidos opuestos: cuanto más aislado, peor +acceso. El sitio bueno no es el máximo de ninguna de las dos, sino el máximo de +una con la otra acotada, que es justo lo que hace el modelo. +""" +import pickle +import sys + +import numpy as np +import rasterio +from shapely.geometry import Point +from shapely.strtree import STRtree + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +# --- parámetros del modelo (todo en metros) --- +MIN_D_BUILD = 1500 # umbral duro: por debajo, te oyen desde casa +MAX_D_ACCESS = 600 # más lejos de un vial, no metes el equipo +MIN_D_PROT = 500 # margen al borde del espacio protegido (zona periférica) +MAX_SLOPE = 8.0 # grados; por encima no se monta ni se aparca +SAT_BUILD = 5000 # a partir de aquí el aislamiento ya no suma más +SAT_PLACE = 8000 +SAT_ROAD = 5000 + +N_CAND = 60 +MIN_SEP = 15000 # separación mínima entre candidatos, para no devolver + # 60 celdas del mismo valle + +MADRID = C.to_grid(-3.7038, 40.4168) + + +def load(name): + return np.load(C.INTERIM / f"{name}.npy") + + +def norm(a, sat): + return np.clip(a / sat, 0, 1) + + +def main(): + print("cargando capas…", flush=True) + d_build = load("d_build") + d_major = load("d_major") + d_minor = load("d_minor") + d_track = load("d_track") + d_place = load("d_place_any") + dens = load("dens_build_5km") + slope = load("slope_deg") + tpi = load("tpi_2km") + spain = load("mask_spain").astype(bool) + protected = load("mask_protected").astype(bool) + military = load("mask_military").astype(bool) + water = load("mask_water").astype(bool) + + d_access = np.minimum(np.minimum(d_major, d_minor), d_track) + + # ---------- 1) aislamiento puro ---------- + # Media geométrica de las tres distancias que definen "lejos de todo". + # Geométrica y no aritmética: así un sitio a 8 km de una casa pero a 200 m + # de una nacional no puntúa; hay que estar lejos de las tres cosas a la vez. + iso = np.cbrt(np.maximum(d_build, 1) * + np.maximum(np.minimum(d_major, d_minor), 1) * + np.maximum(d_place, 1)).astype(np.float32) + iso[~spain] = np.nan + np.save(C.INTERIM / "iso_pure.npy", iso) + + valid_iso = spain & np.isfinite(iso) + print(f"\n=== AISLAMIENTO PURO ===") + print(f" celdas válidas: {valid_iso.sum():,}") + for q in (50, 90, 99, 99.9): + print(f" p{q:<5} {np.nanpercentile(iso[valid_iso], q):7.0f} m") + print(f" máximo {np.nanmax(iso):7.0f} m") + print(f" distancia máxima a un edificio en España: " + f"{np.nanmax(np.where(spain, d_build, np.nan))/1000:.1f} km") + print(f" distancia máxima a una carretera: " + f"{np.nanmax(np.where(spain, np.minimum(d_major, d_minor), np.nan))/1000:.1f} km") + + # ---------- 2) aptitud para rave ---------- + d_prot = load("d_protected") + d_mil = load("d_military") + ok = (spain & ~protected & ~military & ~water & + (d_build >= MIN_D_BUILD) & + (d_access <= MAX_D_ACCESS) & + (slope <= MAX_SLOPE) & + (d_prot >= MIN_D_PROT) & + np.isfinite(slope)) + print(f"\n=== APTITUD PARA RAVE ===") + print(f" celdas que pasan todas las restricciones: {ok.sum():,} " + f"({ok.sum()/spain.sum()*100:.2f}% del país, " + f"{ok.sum()*0.01:,.0f} km2)") + + score = (0.34 * norm(d_build, SAT_BUILD) + + 0.16 * norm(d_place, SAT_PLACE) + + 0.20 * norm(np.minimum(d_major, d_minor), SAT_ROAD) + + 0.12 * (1 - np.clip(dens / 200.0, 0, 1)) + + 0.10 * np.clip(-tpi / 30.0, 0, 1) + # hondonada + 0.08 * (1 - np.clip(slope / MAX_SLOPE, 0, 1))) + score = score.astype(np.float32) + score[~ok] = np.nan + np.save(C.INTERIM / "score_rave.npy", score) + + with rasterio.open( + C.OUT / "score_rave.tif", "w", driver="GTiff", + height=C.HEIGHT, width=C.WIDTH, count=1, dtype="float32", + crs=C.CRS_GRID, transform=C.TRANSFORM, nodata=np.nan, + compress="deflate", tiled=True) as dst: + dst.write(score, 1) + print(f" ráster guardado en out/score_rave.tif") + + # ---------- extracción de candidatos ---------- + print(f"\nextrayendo {N_CAND} candidatos con {MIN_SEP/1000:.0f} km de separación…", + flush=True) + with open(C.INTERIM / "area_admin_municipality.pkl", "rb") as fh: + munis = pickle.load(fh) + with open(C.INTERIM / "area_admin_region.pkl", "rb") as fh: + regs = [r for r in pickle.load(fh) if r["geom"].area > 1e6] + muni_tree = STRtree([m["geom"] for m in munis]) + reg_tree = STRtree([r["geom"] for r in regs]) + + work = np.where(np.isfinite(score), score, -1.0).astype(np.float32) + rad = int(MIN_SEP / C.RES) + rows = [] + for i in range(N_CAND): + idx = int(np.argmax(work)) + r, c = divmod(idx, C.WIDTH) + if work[r, c] <= 0: + break + x = C.X_MIN + (c + 0.5) * C.RES + y = C.Y_MAX - (r + 0.5) * C.RES + lon, lat = C.to_wgs(x, y) + pt = Point(x, y) + + def lookup(tree, recs): + for j in tree.query(pt): + if recs[j]["geom"].contains(pt): + return recs[j]["name"] + return "" + + rows.append({ + "rank": i + 1, + "lat": round(float(lat), 5), + "lon": round(float(lon), 5), + "municipio": lookup(muni_tree, munis), + "comunidad": lookup(reg_tree, regs), + "score": round(float(score[r, c]), 4), + "d_edificio_m": int(d_build[r, c]), + "d_carretera_m": int(min(d_major[r, c], d_minor[r, c])), + "d_acceso_m": int(d_access[r, c]), + "d_nucleo_m": int(d_place[r, c]), + "d_protegido_m": int(d_prot[r, c]), + "d_militar_m": int(d_mil[r, c]), + "cota_m": int(np.nan_to_num(load_dem_at(r, c))), + "pendiente_deg": round(float(slope[r, c]), 1), + "tpi_m": round(float(tpi[r, c]), 1), + "edif_5km": int(dens[r, c]), + "km_madrid": round(float(np.hypot(x - MADRID[0], y - MADRID[1]) / 1000), 1), + }) + + # suprimimos un disco alrededor para el siguiente candidato + r0, r1 = max(0, r - rad), min(C.HEIGHT, r + rad + 1) + c0, c1 = max(0, c - rad), min(C.WIDTH, c + rad + 1) + yy, xx = np.ogrid[r0:r1, c0:c1] + work[r0:r1, c0:c1][((yy - r) ** 2 + (xx - c) ** 2) <= rad * rad] = -1.0 + + import csv + cols = list(rows[0].keys()) + with open(C.OUT / "candidatos.csv", "w", newline="") as fh: + w = csv.DictWriter(fh, fieldnames=cols) + w.writeheader() + w.writerows(rows) + print(f" {len(rows)} candidatos -> out/candidatos.csv") + for r_ in rows[:15]: + print(f" #{r_['rank']:<3} {r_['lat']:8.4f},{r_['lon']:9.4f} " + f"{r_['municipio'][:26]:26s} {r_['comunidad'][:18]:18s} " + f"casa={r_['d_edificio_m']:>6}m ctra={r_['d_carretera_m']:>5}m " + f"acc={r_['d_acceso_m']:>4}m") + + +_dem = None + + +def load_dem_at(r, c): + global _dem + if _dem is None: + _dem = np.load(C.INTERIM / "dem.npy", mmap_mode="r") + v = _dem[r, c] + return 0.0 if not np.isfinite(v) else float(v) + + +if __name__ == "__main__": + main() diff --git a/src/stars.py b/src/stars.py new file mode 100644 index 0000000..2323d83 --- /dev/null +++ b/src/stars.py @@ -0,0 +1,307 @@ +"""Modelo multicriterio y generación de candidatos con valoración 1-5 estrellas. + +Seis criterios independientes, cada uno 0-100: + + sonido nivel estimado que llega a la casa más cercana, en dB. No es una + puntuación abstracta: es propagación real (divergencia esférica + + absorción atmosférica + apantallamiento del relieve + absorción del + arbolado) partiendo de un equipo de 130 dB a 1 m. Es el criterio que + de verdad decide si te dan el aviso. + soledad lejanía de edificios, núcleos y densidad de construcción alrededor. + agua río, arroyo o fuente cerca, pero no encima. + arbolado cobertura arbórea alrededor: sombra y pantalla visual sin que sea + selva impenetrable. + acceso pista en condiciones cerca, llano y sin pedregal. + clima máxima del mes más cálido y mínima del más frío. + +Las estrellas salen de la media ponderada penalizada por el eslabón más débil: +un sitio perfecto salvo que no se puede llegar no es un sitio de cuatro +estrellas. Los criterios legales no puntúan, eliminan. + +Los candidatos se eligen por bloques de 20 km cogiendo el mejor punto elegible +de cada bloque. Así salen cientos de opciones repartidas por todo el país y con +un abanico natural de estrellas, en vez de sesenta variantes del mismo barranco. +""" +import csv +import pickle +import sys + +import numpy as np +from shapely.geometry import Point +from shapely.strtree import STRtree + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +# --- eliminatorias --- +MIN_D_BUILD = 2000 +MIN_D_ROAD = 400 # a menos de esto te ve cada coche que pasa +MAX_D_ACCESS = 800 +MAX_SLOPE = 10.0 +MIN_D_PROT = 300 # margen al borde del espacio protegido oficial +MAX_SPL = 45.0 # dB estimados en la casa más cercana. 45 es el límite + # nocturno típico en suelo rural: por encima, el sitio + # no es viable por mucho que puntúe en lo demás. +MIN_SEP = 10000 # separación mínima entre candidatos + +# --- modelo acústico --- +L0 = 130.0 # dB a 1 m de un equipo grande +ALPHA_KM = 0.5 # dB/km de absorción atmosférica en frecuencias bajas +MAX_FOREST_ATT = 6.0 +MAX_TERRAIN_ATT = 12.0 + +BLOCK = 200 # celdas = 20 km +MADRID = C.to_grid(-3.7038, 40.4168) + + +def load(n): + return np.load(C.INTERIM / f"{n}.npy") + + +def ramp(x, lo, hi): + """0 en lo, 100 en hi (o al revés si hi= MIN_D_BUILD) & + (d_road >= MIN_D_ROAD) & + (d_access <= MAX_D_ACCESS) & + (slope <= MAX_SLOPE) & + (d_prot >= MIN_D_PROT) & + np.isfinite(slope) & np.isfinite(tmax)) + # El umbral acústico se aplica después de calcular el nivel, más abajo. + print(f"celdas elegibles: {ok.sum():,} ({ok.sum()*0.01:,.0f} km2, " + f"{ok.sum()/spain.sum()*100:.1f}% del país)", flush=True) + + # ---------- criterio sonido ---------- + d = np.maximum(d_build, 1.0) + att_forest = frac_forest * MAX_FOREST_ATT + att_terrain = np.clip(-tpi / 4.0, 0, MAX_TERRAIN_ATT) + spl = (L0 - 20.0 * np.log10(d) - ALPHA_KM * d / 1000.0 + - att_forest - att_terrain).astype(np.float32) + # 45 dB es el límite nocturno típico en suelo rural; 30 dB ya es ruido de + # fondo del campo de noche, o sea inaudible en la práctica. + c_sonido = ramp(-spl, -48, -30) + ok_before_spl = int(ok.sum()) + ok &= (spl <= MAX_SPL) + print(f" tras el umbral acústico de {MAX_SPL:.0f} dB: {ok.sum():,} celdas " + f"({ok.sum()*0.01:,.0f} km2)", flush=True) + + # ---------- resto de criterios ---------- + # La distancia a carretera pesa aquí de forma explícita: sin ella el modelo + # colocaba puntos de cinco estrellas sobre el propio asfalto, porque miraba + # solo lo lejos que estaba la casa más cercana. Da igual que no te oigan si + # te ve cada coche que pasa. + c_soledad = (0.40 * ramp(d_build, 1200, 6000) + + 0.25 * ramp(d_road, MIN_D_ROAD, 4000) + + 0.20 * ramp(d_place, 1500, 9000) + + 0.15 * (100 - np.clip(dens / 150.0, 0, 1) * 100)) + d_any_water = np.minimum(d_water, d_spring) + c_agua = band(d_any_water, 0, 120, 900, 4000) + c_arbolado = band(frac_forest * 100, 0, 20, 65, 100) + c_acceso = (0.55 * (100 - ramp(d_tg, 0, MAX_D_ACCESS)) + + 0.25 * (100 - ramp(slope, 1.5, MAX_SLOPE)) + + 0.20 * (100 - np.clip(frac_rock * 400, 0, 100))) + c_clima = (0.6 * (100 - ramp(tmax, 30, 40)) + + 0.4 * (100 - ramp(-tmin, 1, 8))) + + crit = {"sonido": c_sonido, "soledad": c_soledad, "agua": c_agua, + "arbolado": c_arbolado, "acceso": c_acceso, "clima": c_clima} + W = {"sonido": .30, "soledad": .22, "acceso": .18, + "agua": .12, "arbolado": .10, "clima": .08} + + total = sum(W[k] * crit[k] for k in W).astype(np.float32) + # El eslabón más débil: sin esto salen sitios de 5 estrellas a los que no se + # puede llegar o donde te oyen desde el pueblo. + weakest = np.minimum.reduce([crit["sonido"], crit["acceso"], crit["soledad"]]) + total = (0.75 * total + 0.25 * weakest).astype(np.float32) + total[~ok] = -1 + + # ---------- muestreo por bloques ---------- + print(f"muestreando el mejor punto de cada bloque de {BLOCK*C.RES/1000:.0f} km…", + flush=True) + H = (C.HEIGHT // BLOCK) * BLOCK + Wd = (C.WIDTH // BLOCK) * BLOCK + view = total[:H, :Wd].reshape(H // BLOCK, BLOCK, Wd // BLOCK, BLOCK) + view = view.transpose(0, 2, 1, 3).reshape(-1, BLOCK * BLOCK) + best = view.argmax(axis=1) + bestval = view.max(axis=1) + keep = np.flatnonzero(bestval > 0) + nb_c = Wd // BLOCK + rows_out = [] + for bi in keep: + br, bc = divmod(int(bi), nb_c) + lr, lc = divmod(int(best[bi]), BLOCK) + r, c = br * BLOCK + lr, bc * BLOCK + lc + rows_out.append((r, c, float(bestval[bi]))) + rows_out.sort(key=lambda t: -t[2]) + print(f" {len(rows_out)} tras el muestreo por bloques", flush=True) + + # Un punto por bloque no garantiza separación: dos bloques vecinos pueden + # elegir cada uno una celda pegada a su frontera común y quedar a 100 m. + # Filtro voraz por score: nos quedamos con el mejor y descartamos todo lo + # que caiga a menos de MIN_SEP. + from scipy.spatial import cKDTree + xs = np.array([C.X_MIN + (c + 0.5) * C.RES for _, c, _ in rows_out]) + ys = np.array([C.Y_MAX - (r + 0.5) * C.RES for r, _, _ in rows_out]) + tree = cKDTree(np.c_[xs, ys]) + dead = np.zeros(len(rows_out), dtype=bool) + kept = [] + for i in range(len(rows_out)): # ya vienen ordenados por score + if dead[i]: + continue + kept.append(i) + for j in tree.query_ball_point([xs[i], ys[i]], MIN_SEP): + if j != i: + dead[j] = True + rows_out = [rows_out[i] for i in kept] + print(f" {len(rows_out)} tras exigir {MIN_SEP/1000:.0f} km de separación", + flush=True) + + # ---------- etiquetado ---------- + with open(C.INTERIM / "area_admin_municipality.pkl", "rb") as fh: + munis = pickle.load(fh) + with open(C.INTERIM / "area_admin_region.pkl", "rb") as fh: + regs = [x for x in pickle.load(fh) if x["geom"].area > 1e6] + with open(C.INTERIM / "prot_official.pkl", "rb") as fh: + prots = pickle.load(fh) + t_mu, t_rg = STRtree([m["geom"] for m in munis]), STRtree([x["geom"] for x in regs]) + t_pr = STRtree([p["geom"] for p in prots]) + + def stars_of(v): + return 5 if v >= 72 else 4 if v >= 62 else 3 if v >= 52 else 2 if v >= 42 else 1 + + out = [] + for i, (r, c, val) in enumerate(rows_out, 1): + x = C.X_MIN + (c + 0.5) * C.RES + y = C.Y_MAX - (r + 0.5) * C.RES + lon, lat = C.to_wgs(x, y) + p = Point(x, y) + + def look(tree, recs): + for j in tree.query(p): + if recs[j]["geom"].contains(p): + return recs[j]["name"] + # Los límites municipales de OSM dejan alguna costura sin cubrir; + # en ese caso nos quedamos con el municipio más cercano. + near, nd_ = "", 1e9 + for j in tree.query(p.buffer(4000)): + dd_ = recs[j]["geom"].distance(p) + if dd_ < nd_: + nd_, near = dd_, recs[j]["name"] + return near + + near, nd = "", 1e9 + for j in t_pr.query(p.buffer(12000)): + dd = prots[j]["geom"].distance(p) + if dd < nd: + nd, near = dd, f'{prots[j]["name"]} ({prots[j]["kind"]})' + + out.append({ + "id": i, "lat": round(float(lat), 5), "lon": round(float(lon), 5), + "estrellas": stars_of(val), "score": round(val, 1), + "municipio": look(t_mu, munis), "comunidad": look(t_rg, regs), + "c_sonido": round(float(c_sonido[r, c])), + "c_soledad": round(float(c_soledad[r, c])), + "c_agua": round(float(c_agua[r, c])), + "c_arbolado": round(float(c_arbolado[r, c])), + "c_acceso": round(float(c_acceso[r, c])), + "c_clima": round(float(c_clima[r, c])), + "db_en_casa": round(float(spl[r, c]), 1), + "d_edificio": int(d_build[r, c]), + "d_carretera": int(min(d_major[r, c], d_minor[r, c])), + "d_pista": int(d_tg[r, c]), + "d_agua": int(d_any_water[r, c]), + "d_protegido": int(d_prot[r, c]), + "protegido_cerca": near if nd < 12000 else "", + "km_protegido": round(nd / 1000, 1) if nd < 1e8 else None, + "arbolado_pct": round(float(frac_forest[r, c]) * 100), + "roca_pct": round(float(frac_rock[r, c]) * 100), + "pendiente": round(float(slope[r, c]), 1), + "tpi": round(float(tpi[r, c]), 1), + "cota": int(dem[r, c]) if np.isfinite(dem[r, c]) else 0, + "tmax": round(float(tmax[r, c]), 1), + "tmin": round(float(tmin[r, c]), 1), + "km_madrid": round(float(np.hypot(x - MADRID[0], y - MADRID[1]) / 1000), 1), + "row": r, "col": c, + }) + + # Las cifras del informe y del visor se leen de aquí, para que no se queden + # obsoletas al cambiar un umbral del modelo. + import json + stats = { + "n": len(out), + "elegibles_km2": round(float(ok_before_spl * 0.01)), + "elegibles_pct": round(float(ok_before_spl / spain.sum() * 100), 1), + "acustico_km2": round(float(ok.sum() * 0.01)), + "estrellas": {str(s): sum(1 for o in out if o["estrellas"] == s) + for s in range(1, 6)}, + "mejor_db": round(min(o["db_en_casa"] for o in out), 1), + "max_d_build_km": round(float(np.nanmax(np.where(spain, d_build, np.nan))) / 1000, 1), + "prot_pct": round(float((prot & spain).sum() / spain.sum() * 100), 1), + "prot_km2": round(float((prot & spain).sum() * 0.01)), + "min_d_build": MIN_D_BUILD, "min_d_road": MIN_D_ROAD, + "max_spl": MAX_SPL, "min_sep_km": MIN_SEP // 1000, + "max_d_access": MAX_D_ACCESS, "max_slope": MAX_SLOPE, + "min_d_prot": MIN_D_PROT, + } + with open(C.OUT / "stats.json", "w") as fh: + json.dump(stats, fh, ensure_ascii=False, indent=1) + + with open(C.OUT / "candidatos_estrellas.csv", "w", newline="") as fh: + w = csv.DictWriter(fh, fieldnames=list(out[0].keys())) + w.writeheader() + w.writerows(out) + with open(C.INTERIM / "cands.pkl", "wb") as fh: + pickle.dump(out, fh) + + dist = {s: sum(1 for o in out if o["estrellas"] == s) for s in (5, 4, 3, 2, 1)} + print("\nreparto de estrellas:") + for s in (5, 4, 3, 2, 1): + print(f" {'★'*s:<5} {dist[s]:>4}") + print(f"\ntop 12:") + for o in out[:12]: + print(f" {'★'*o['estrellas']:<5} {o['score']:5.1f} {o['lat']:8.4f},{o['lon']:9.4f} " + f"{o['municipio'][:22]:22s} {o['db_en_casa']:5.1f}dB " + f"pista={o['d_pista']:>4}m agua={o['d_agua']:>5}m") + + +if __name__ == "__main__": + main() diff --git a/src/validate_coverage.py b/src/validate_coverage.py new file mode 100644 index 0000000..9875f39 --- /dev/null +++ b/src/validate_coverage.py @@ -0,0 +1,74 @@ +"""Control de sesgo: ¿está OSM igual de mapeado en toda España? + +Importa porque el modelo mide aislamiento como "distancia al edificio más +cercano". Si una comunidad tiene los edificios a medio mapear, sus celdas +saldrán aisladas por un hueco en los datos y no por estar lejos de nada. +El import del Catastro cubre casi toda España pero no Navarra ni Euskadi, que +tienen catastro propio, así que el sesgo es esperable y hay que cuantificarlo. + +Como no usamos datos externos de población, el contraste es interno: viales por +km2 (que en España están mapeados de forma homogénea) frente a edificios por +km2. Si la proporción se desploma en una región, es un agujero de datos. +""" +import pickle +import sys + +import numpy as np +import rasterio.features +from shapely.geometry import mapping + +sys.path.insert(0, str(__file__.rsplit("/", 1)[0])) +import config as C + +STEP_M = 60.0 # espaciado de densificación usado en extract_osm + + +def main(): + with open(C.INTERIM / "area_admin_region.pkl", "rb") as fh: + regs = [r for r in pickle.load(fh) if r["geom"].area > 1e6] + regs.sort(key=lambda r: -r["geom"].area) + + rid = rasterio.features.rasterize( + [(mapping(r["geom"]), i + 1) for i, r in enumerate(regs)], + out_shape=(C.HEIGHT, C.WIDTH), transform=C.TRANSFORM, + fill=0, dtype=np.uint8, all_touched=False) + + def counts_for(base): + a = np.load(C.INTERIM / f"{base}.npy") + r, c = C.xy_to_rowcol(a[0], a[1]) + ok = C.inside(r, c) + ids = rid[r[ok], c[ok]] + return np.bincount(ids, minlength=len(regs) + 1) + + b = counts_for("buildings") + v = counts_for("line_minor") + counts_for("line_track") + cells = np.bincount(rid.ravel(), minlength=len(regs) + 1) + + print(f"{'comunidad':24s} {'km2':>8s} {'edif':>10s} {'edif/km2':>9s} " + f"{'vial km':>9s} {'edif/vial-km':>13s}") + print("-" * 78) + ratios = [] + for i, r in enumerate(regs, start=1): + km2 = cells[i] * 0.01 + if km2 < 100: + continue + vial_km = v[i] * STEP_M / 1000.0 + ratio = b[i] / vial_km if vial_km else 0 + ratios.append((ratio, r["name"])) + print(f"{r['name'][:24]:24s} {km2:8,.0f} {b[i]:10,} {b[i]/km2:9.1f} " + f"{vial_km:9,.0f} {ratio:13.2f}") + + med = np.median([x for x, _ in ratios]) + print("-" * 78) + print(f"mediana edif/vial-km: {med:.2f}") + print("\nregiones con menos de la mitad de la mediana " + "(sospecha de infra-mapeo de edificios):") + flagged = [n for x, n in ratios if x < 0.5 * med] + for n in flagged: + print(f" - {n}") + if not flagged: + print(" ninguna") + + +if __name__ == "__main__": + main() diff --git a/vendor/leaflet.css b/vendor/leaflet.css new file mode 100644 index 0000000..2961b76 --- /dev/null +++ b/vendor/leaflet.css @@ -0,0 +1,661 @@ +/* required styles */ + +.leaflet-pane, +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-tile-container, +.leaflet-pane > svg, +.leaflet-pane > canvas, +.leaflet-zoom-box, +.leaflet-image-layer, +.leaflet-layer { + position: absolute; + left: 0; + top: 0; + } +.leaflet-container { + overflow: hidden; + } +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-user-drag: none; + } +/* Prevents IE11 from highlighting tiles in blue */ +.leaflet-tile::selection { + background: transparent; +} +/* Safari renders non-retina tile on retina better with this, but Chrome is worse */ +.leaflet-safari .leaflet-tile { + image-rendering: -webkit-optimize-contrast; + } +/* hack that prevents hw layers "stretching" when loading new tiles */ +.leaflet-safari .leaflet-tile-container { + width: 1600px; + height: 1600px; + -webkit-transform-origin: 0 0; + } +.leaflet-marker-icon, +.leaflet-marker-shadow { + display: block; + } +/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ +/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ +.leaflet-container .leaflet-overlay-pane svg { + max-width: none !important; + max-height: none !important; + } +.leaflet-container .leaflet-marker-pane img, +.leaflet-container .leaflet-shadow-pane img, +.leaflet-container .leaflet-tile-pane img, +.leaflet-container img.leaflet-image-layer, +.leaflet-container .leaflet-tile { + max-width: none !important; + max-height: none !important; + width: auto; + padding: 0; + } + +.leaflet-container img.leaflet-tile { + /* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */ + mix-blend-mode: plus-lighter; +} + +.leaflet-container.leaflet-touch-zoom { + -ms-touch-action: pan-x pan-y; + touch-action: pan-x pan-y; + } +.leaflet-container.leaflet-touch-drag { + -ms-touch-action: pinch-zoom; + /* Fallback for FF which doesn't support pinch-zoom */ + touch-action: none; + touch-action: pinch-zoom; +} +.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { + -ms-touch-action: none; + touch-action: none; +} +.leaflet-container { + -webkit-tap-highlight-color: transparent; +} +.leaflet-container a { + -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); +} +.leaflet-tile { + filter: inherit; + visibility: hidden; + } +.leaflet-tile-loaded { + visibility: inherit; + } +.leaflet-zoom-box { + width: 0; + height: 0; + -moz-box-sizing: border-box; + box-sizing: border-box; + z-index: 800; + } +/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ +.leaflet-overlay-pane svg { + -moz-user-select: none; + } + +.leaflet-pane { z-index: 400; } + +.leaflet-tile-pane { z-index: 200; } +.leaflet-overlay-pane { z-index: 400; } +.leaflet-shadow-pane { z-index: 500; } +.leaflet-marker-pane { z-index: 600; } +.leaflet-tooltip-pane { z-index: 650; } +.leaflet-popup-pane { z-index: 700; } + +.leaflet-map-pane canvas { z-index: 100; } +.leaflet-map-pane svg { z-index: 200; } + +.leaflet-vml-shape { + width: 1px; + height: 1px; + } +.lvml { + behavior: url(#default#VML); + display: inline-block; + position: absolute; + } + + +/* control positioning */ + +.leaflet-control { + position: relative; + z-index: 800; + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } +.leaflet-top, +.leaflet-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; + } +.leaflet-top { + top: 0; + } +.leaflet-right { + right: 0; + } +.leaflet-bottom { + bottom: 0; + } +.leaflet-left { + left: 0; + } +.leaflet-control { + float: left; + clear: both; + } +.leaflet-right .leaflet-control { + float: right; + } +.leaflet-top .leaflet-control { + margin-top: 10px; + } +.leaflet-bottom .leaflet-control { + margin-bottom: 10px; + } +.leaflet-left .leaflet-control { + margin-left: 10px; + } +.leaflet-right .leaflet-control { + margin-right: 10px; + } + + +/* zoom and fade animations */ + +.leaflet-fade-anim .leaflet-popup { + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + } +.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { + opacity: 1; + } +.leaflet-zoom-animated { + -webkit-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; + } +svg.leaflet-zoom-animated { + will-change: transform; +} + +.leaflet-zoom-anim .leaflet-zoom-animated { + -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); + -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); + transition: transform 0.25s cubic-bezier(0,0,0.25,1); + } +.leaflet-zoom-anim .leaflet-tile, +.leaflet-pan-anim .leaflet-tile { + -webkit-transition: none; + -moz-transition: none; + transition: none; + } + +.leaflet-zoom-anim .leaflet-zoom-hide { + visibility: hidden; + } + + +/* cursors */ + +.leaflet-interactive { + cursor: pointer; + } +.leaflet-grab { + cursor: -webkit-grab; + cursor: -moz-grab; + cursor: grab; + } +.leaflet-crosshair, +.leaflet-crosshair .leaflet-interactive { + cursor: crosshair; + } +.leaflet-popup-pane, +.leaflet-control { + cursor: auto; + } +.leaflet-dragging .leaflet-grab, +.leaflet-dragging .leaflet-grab .leaflet-interactive, +.leaflet-dragging .leaflet-marker-draggable { + cursor: move; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + cursor: grabbing; + } + +/* marker & overlays interactivity */ +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-image-layer, +.leaflet-pane > svg path, +.leaflet-tile-container { + pointer-events: none; + } + +.leaflet-marker-icon.leaflet-interactive, +.leaflet-image-layer.leaflet-interactive, +.leaflet-pane > svg path.leaflet-interactive, +svg.leaflet-image-layer.leaflet-interactive path { + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } + +/* visual tweaks */ + +.leaflet-container { + background: #ddd; + outline-offset: 1px; + } +.leaflet-container a { + color: #0078A8; + } +.leaflet-zoom-box { + border: 2px dotted #38f; + background: rgba(255,255,255,0.5); + } + + +/* general typography */ +.leaflet-container { + font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; + font-size: 12px; + font-size: 0.75rem; + line-height: 1.5; + } + + +/* general toolbar styles */ + +.leaflet-bar { + box-shadow: 0 1px 5px rgba(0,0,0,0.65); + border-radius: 4px; + } +.leaflet-bar a { + background-color: #fff; + border-bottom: 1px solid #ccc; + width: 26px; + height: 26px; + line-height: 26px; + display: block; + text-align: center; + text-decoration: none; + color: black; + } +.leaflet-bar a, +.leaflet-control-layers-toggle { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; + } +.leaflet-bar a:hover, +.leaflet-bar a:focus { + background-color: #f4f4f4; + } +.leaflet-bar a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.leaflet-bar a:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; + } +.leaflet-bar a.leaflet-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; + } + +.leaflet-touch .leaflet-bar a { + width: 30px; + height: 30px; + line-height: 30px; + } +.leaflet-touch .leaflet-bar a:first-child { + border-top-left-radius: 2px; + border-top-right-radius: 2px; + } +.leaflet-touch .leaflet-bar a:last-child { + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + } + +/* zoom control */ + +.leaflet-control-zoom-in, +.leaflet-control-zoom-out { + font: bold 18px 'Lucida Console', Monaco, monospace; + text-indent: 1px; + } + +.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { + font-size: 22px; + } + + +/* layers control */ + +.leaflet-control-layers { + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + background: #fff; + border-radius: 5px; + } +.leaflet-control-layers-toggle { + background-image: url(images/layers.png); + width: 36px; + height: 36px; + } +.leaflet-retina .leaflet-control-layers-toggle { + background-image: url(images/layers-2x.png); + background-size: 26px 26px; + } +.leaflet-touch .leaflet-control-layers-toggle { + width: 44px; + height: 44px; + } +.leaflet-control-layers .leaflet-control-layers-list, +.leaflet-control-layers-expanded .leaflet-control-layers-toggle { + display: none; + } +.leaflet-control-layers-expanded .leaflet-control-layers-list { + display: block; + position: relative; + } +.leaflet-control-layers-expanded { + padding: 6px 10px 6px 6px; + color: #333; + background: #fff; + } +.leaflet-control-layers-scrollbar { + overflow-y: scroll; + overflow-x: hidden; + padding-right: 5px; + } +.leaflet-control-layers-selector { + margin-top: 2px; + position: relative; + top: 1px; + } +.leaflet-control-layers label { + display: block; + font-size: 13px; + font-size: 1.08333em; + } +.leaflet-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; + } + +/* Default icon URLs */ +.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */ + background-image: url(images/marker-icon.png); + } + + +/* attribution and scale controls */ + +.leaflet-container .leaflet-control-attribution { + background: #fff; + background: rgba(255, 255, 255, 0.8); + margin: 0; + } +.leaflet-control-attribution, +.leaflet-control-scale-line { + padding: 0 5px; + color: #333; + line-height: 1.4; + } +.leaflet-control-attribution a { + text-decoration: none; + } +.leaflet-control-attribution a:hover, +.leaflet-control-attribution a:focus { + text-decoration: underline; + } +.leaflet-attribution-flag { + display: inline !important; + vertical-align: baseline !important; + width: 1em; + height: 0.6669em; + } +.leaflet-left .leaflet-control-scale { + margin-left: 5px; + } +.leaflet-bottom .leaflet-control-scale { + margin-bottom: 5px; + } +.leaflet-control-scale-line { + border: 2px solid #777; + border-top: none; + line-height: 1.1; + padding: 2px 5px 1px; + white-space: nowrap; + -moz-box-sizing: border-box; + box-sizing: border-box; + background: rgba(255, 255, 255, 0.8); + text-shadow: 1px 1px #fff; + } +.leaflet-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + border-bottom: none; + margin-top: -2px; + } +.leaflet-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; + } + +.leaflet-touch .leaflet-control-attribution, +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + box-shadow: none; + } +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + border: 2px solid rgba(0,0,0,0.2); + background-clip: padding-box; + } + + +/* popup */ + +.leaflet-popup { + position: absolute; + text-align: center; + margin-bottom: 20px; + } +.leaflet-popup-content-wrapper { + padding: 1px; + text-align: left; + border-radius: 12px; + } +.leaflet-popup-content { + margin: 13px 24px 13px 20px; + line-height: 1.3; + font-size: 13px; + font-size: 1.08333em; + min-height: 1px; + } +.leaflet-popup-content p { + margin: 17px 0; + margin: 1.3em 0; + } +.leaflet-popup-tip-container { + width: 40px; + height: 20px; + position: absolute; + left: 50%; + margin-top: -1px; + margin-left: -20px; + overflow: hidden; + pointer-events: none; + } +.leaflet-popup-tip { + width: 17px; + height: 17px; + padding: 1px; + + margin: -10px auto 0; + pointer-events: auto; + + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + } +.leaflet-popup-content-wrapper, +.leaflet-popup-tip { + background: white; + color: #333; + box-shadow: 0 3px 14px rgba(0,0,0,0.4); + } +.leaflet-container a.leaflet-popup-close-button { + position: absolute; + top: 0; + right: 0; + border: none; + text-align: center; + width: 24px; + height: 24px; + font: 16px/24px Tahoma, Verdana, sans-serif; + color: #757575; + text-decoration: none; + background: transparent; + } +.leaflet-container a.leaflet-popup-close-button:hover, +.leaflet-container a.leaflet-popup-close-button:focus { + color: #585858; + } +.leaflet-popup-scrolled { + overflow: auto; + } + +.leaflet-oldie .leaflet-popup-content-wrapper { + -ms-zoom: 1; + } +.leaflet-oldie .leaflet-popup-tip { + width: 24px; + margin: 0 auto; + + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); + } + +.leaflet-oldie .leaflet-control-zoom, +.leaflet-oldie .leaflet-control-layers, +.leaflet-oldie .leaflet-popup-content-wrapper, +.leaflet-oldie .leaflet-popup-tip { + border: 1px solid #999; + } + + +/* div icon */ + +.leaflet-div-icon { + background: #fff; + border: 1px solid #666; + } + + +/* Tooltip */ +/* Base styles for the element that has a tooltip */ +.leaflet-tooltip { + position: absolute; + padding: 6px; + background-color: #fff; + border: 1px solid #fff; + border-radius: 3px; + color: #222; + white-space: nowrap; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + pointer-events: none; + box-shadow: 0 1px 3px rgba(0,0,0,0.4); + } +.leaflet-tooltip.leaflet-interactive { + cursor: pointer; + pointer-events: auto; + } +.leaflet-tooltip-top:before, +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + position: absolute; + pointer-events: none; + border: 6px solid transparent; + background: transparent; + content: ""; + } + +/* Directions */ + +.leaflet-tooltip-bottom { + margin-top: 6px; +} +.leaflet-tooltip-top { + margin-top: -6px; +} +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-top:before { + left: 50%; + margin-left: -6px; + } +.leaflet-tooltip-top:before { + bottom: 0; + margin-bottom: -12px; + border-top-color: #fff; + } +.leaflet-tooltip-bottom:before { + top: 0; + margin-top: -12px; + margin-left: -6px; + border-bottom-color: #fff; + } +.leaflet-tooltip-left { + margin-left: -6px; +} +.leaflet-tooltip-right { + margin-left: 6px; +} +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + top: 50%; + margin-top: -6px; + } +.leaflet-tooltip-left:before { + right: 0; + margin-right: -12px; + border-left-color: #fff; + } +.leaflet-tooltip-right:before { + left: 0; + margin-left: -12px; + border-right-color: #fff; + } + +/* Printing */ + +@media print { + /* Prevent printers from removing background-images of controls. */ + .leaflet-control { + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + } + } diff --git a/vendor/leaflet.js b/vendor/leaflet.js new file mode 100644 index 0000000..a3bf693 --- /dev/null +++ b/vendor/leaflet.js @@ -0,0 +1,6 @@ +/* @preserve + * Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com + * (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).leaflet={})}(this,function(t){"use strict";function l(t){for(var e,i,n=1,o=arguments.length;n=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=_(t);var e=this.min,i=this.max,n=t.min,t=t.max,o=t.x>=e.x&&n.x<=i.x,t=t.y>=e.y&&n.y<=i.y;return o&&t},overlaps:function(t){t=_(t);var e=this.min,i=this.max,n=t.min,t=t.max,o=t.x>e.x&&n.xe.y&&n.y=n.lat&&i.lat<=o.lat&&e.lng>=n.lng&&i.lng<=o.lng},intersects:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),t=t.getNorthEast(),o=t.lat>=e.lat&&n.lat<=i.lat,t=t.lng>=e.lng&&n.lng<=i.lng;return o&&t},overlaps:function(t){t=g(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),t=t.getNorthEast(),o=t.lat>e.lat&&n.late.lng&&n.lng","http://www.w3.org/2000/svg"===(Wt.firstChild&&Wt.firstChild.namespaceURI));function y(t){return 0<=navigator.userAgent.toLowerCase().indexOf(t)}var b={ie:pt,ielt9:mt,edge:n,webkit:ft,android:gt,android23:vt,androidStock:yt,opera:xt,chrome:wt,gecko:bt,safari:Pt,phantom:Lt,opera12:o,win:Tt,ie3d:Mt,webkit3d:zt,gecko3d:_t,any3d:Ct,mobile:Zt,mobileWebkit:St,mobileWebkit3d:Et,msPointer:kt,pointer:Ot,touch:Bt,touchNative:At,mobileOpera:It,mobileGecko:Rt,retina:Nt,passiveEvents:Dt,canvas:jt,svg:Ht,vml:!Ht&&function(){try{var t=document.createElement("div"),e=(t.innerHTML='',t.firstChild);return e.style.behavior="url(#default#VML)",e&&"object"==typeof e.adj}catch(t){return!1}}(),inlineSvg:Wt,mac:0===navigator.platform.indexOf("Mac"),linux:0===navigator.platform.indexOf("Linux")},Ft=b.msPointer?"MSPointerDown":"pointerdown",Ut=b.msPointer?"MSPointerMove":"pointermove",Vt=b.msPointer?"MSPointerUp":"pointerup",qt=b.msPointer?"MSPointerCancel":"pointercancel",Gt={touchstart:Ft,touchmove:Ut,touchend:Vt,touchcancel:qt},Kt={touchstart:function(t,e){e.MSPOINTER_TYPE_TOUCH&&e.pointerType===e.MSPOINTER_TYPE_TOUCH&&O(e);ee(t,e)},touchmove:ee,touchend:ee,touchcancel:ee},Yt={},Xt=!1;function Jt(t,e,i){return"touchstart"!==e||Xt||(document.addEventListener(Ft,$t,!0),document.addEventListener(Ut,Qt,!0),document.addEventListener(Vt,te,!0),document.addEventListener(qt,te,!0),Xt=!0),Kt[e]?(i=Kt[e].bind(this,i),t.addEventListener(Gt[e],i,!1),i):(console.warn("wrong event specified:",e),u)}function $t(t){Yt[t.pointerId]=t}function Qt(t){Yt[t.pointerId]&&(Yt[t.pointerId]=t)}function te(t){delete Yt[t.pointerId]}function ee(t,e){if(e.pointerType!==(e.MSPOINTER_TYPE_MOUSE||"mouse")){for(var i in e.touches=[],Yt)e.touches.push(Yt[i]);e.changedTouches=[e],t(e)}}var ie=200;function ne(t,i){t.addEventListener("dblclick",i);var n,o=0;function e(t){var e;1!==t.detail?n=t.detail:"mouse"===t.pointerType||t.sourceCapabilities&&!t.sourceCapabilities.firesTouchEvents||((e=Ne(t)).some(function(t){return t instanceof HTMLLabelElement&&t.attributes.for})&&!e.some(function(t){return t instanceof HTMLInputElement||t instanceof HTMLSelectElement})||((e=Date.now())-o<=ie?2===++n&&i(function(t){var e,i,n={};for(i in t)e=t[i],n[i]=e&&e.bind?e.bind(t):e;return(t=n).type="dblclick",n.detail=2,n.isTrusted=!1,n._simulated=!0,n}(t)):n=1,o=e))}return t.addEventListener("click",e),{dblclick:i,simDblclick:e}}var oe,se,re,ae,he,le,ue=we(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),ce=we(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),de="webkitTransition"===ce||"OTransition"===ce?ce+"End":"transitionend";function _e(t){return"string"==typeof t?document.getElementById(t):t}function pe(t,e){var i=t.style[e]||t.currentStyle&&t.currentStyle[e];return"auto"===(i=i&&"auto"!==i||!document.defaultView?i:(t=document.defaultView.getComputedStyle(t,null))?t[e]:null)?null:i}function P(t,e,i){t=document.createElement(t);return t.className=e||"",i&&i.appendChild(t),t}function T(t){var e=t.parentNode;e&&e.removeChild(t)}function me(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function fe(t){var e=t.parentNode;e&&e.lastChild!==t&&e.appendChild(t)}function ge(t){var e=t.parentNode;e&&e.firstChild!==t&&e.insertBefore(t,e.firstChild)}function ve(t,e){return void 0!==t.classList?t.classList.contains(e):0<(t=xe(t)).length&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(t)}function M(t,e){var i;if(void 0!==t.classList)for(var n=F(e),o=0,s=n.length;othis.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,e){this._enforcingBounds=!0;var i=this.getCenter(),t=this._limitCenter(i,this._zoom,g(t));return i.equals(t)||this.panTo(t,e),this._enforcingBounds=!1,this},panInside:function(t,e){var i=m((e=e||{}).paddingTopLeft||e.padding||[0,0]),n=m(e.paddingBottomRight||e.padding||[0,0]),o=this.project(this.getCenter()),t=this.project(t),s=this.getPixelBounds(),i=_([s.min.add(i),s.max.subtract(n)]),s=i.getSize();return i.contains(t)||(this._enforcingBounds=!0,n=t.subtract(i.getCenter()),i=i.extend(t).getSize().subtract(s),o.x+=n.x<0?-i.x:i.x,o.y+=n.y<0?-i.y:i.y,this.panTo(this.unproject(o),e),this._enforcingBounds=!1),this},invalidateSize:function(t){if(!this._loaded)return this;t=l({animate:!1,pan:!0},!0===t?{animate:!0}:t);var e=this.getSize(),i=(this._sizeChanged=!0,this._lastCenter=null,this.getSize()),n=e.divideBy(2).round(),o=i.divideBy(2).round(),n=n.subtract(o);return n.x||n.y?(t.animate&&t.pan?this.panBy(n):(t.pan&&this._rawPanBy(n),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(a(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:e,newSize:i})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){var e,i;return t=this._locateOptions=l({timeout:1e4,watch:!1},t),"geolocation"in navigator?(e=a(this._handleGeolocationResponse,this),i=a(this._handleGeolocationError,this),t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t)):this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e;this._container._leaflet_id&&(e=t.code,t=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout"),this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+t+"."}))},_handleGeolocationResponse:function(t){if(this._container._leaflet_id){var e,i,n=new v(t.coords.latitude,t.coords.longitude),o=n.toBounds(2*t.coords.accuracy),s=this._locateOptions,r=(s.setView&&(e=this.getBoundsZoom(o),this.setView(n,s.maxZoom?Math.min(e,s.maxZoom):e)),{latlng:n,bounds:o,timestamp:t.timestamp});for(i in t.coords)"number"==typeof t.coords[i]&&(r[i]=t.coords[i]);this.fire("locationfound",r)}},addHandler:function(t,e){return e&&(e=this[t]=new e(this),this._handlers.push(e),this.options[t]&&e.enable()),this},remove:function(){if(this._initEvents(!0),this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}for(var t in void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),T(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(r(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload"),this._layers)this._layers[t].remove();for(t in this._panes)T(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,e){e=P("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),e||this._mapPane);return t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter.clone():this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new s(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,e,i){t=g(t),i=m(i||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),t=t.getSouthEast(),i=this.getSize().subtract(i),t=_(this.project(t,n),this.project(r,n)).getSize(),r=b.any3d?this.options.zoomSnap:1,a=i.x/t.x,i=i.y/t.y,t=e?Math.max(a,i):Math.min(a,i),n=this.getScaleZoom(t,n);return r&&(n=Math.round(n/(r/100))*(r/100),n=e?Math.ceil(n/r)*r:Math.floor(n/r)*r),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new p(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,e){t=this._getTopLeftPoint(t,e);return new f(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,e){var i=this.options.crs;return e=void 0===e?this._zoom:e,i.scale(t)/i.scale(e)},getScaleZoom:function(t,e){var i=this.options.crs,t=(e=void 0===e?this._zoom:e,i.zoom(t*i.scale(e)));return isNaN(t)?1/0:t},project:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.latLngToPoint(w(t),e)},unproject:function(t,e){return e=void 0===e?this._zoom:e,this.options.crs.pointToLatLng(m(t),e)},layerPointToLatLng:function(t){t=m(t).add(this.getPixelOrigin());return this.unproject(t)},latLngToLayerPoint:function(t){return this.project(w(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(w(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(g(t))},distance:function(t,e){return this.options.crs.distance(w(t),w(e))},containerPointToLayerPoint:function(t){return m(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return m(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){t=this.containerPointToLayerPoint(m(t));return this.layerPointToLatLng(t)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(w(t)))},mouseEventToContainerPoint:function(t){return De(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){t=this._container=_e(t);if(!t)throw new Error("Map container not found.");if(t._leaflet_id)throw new Error("Map container is already initialized.");S(t,"scroll",this._onScroll,this),this._containerId=h(t)},_initLayout:function(){var t=this._container,e=(this._fadeAnimated=this.options.fadeAnimation&&b.any3d,M(t,"leaflet-container"+(b.touch?" leaflet-touch":"")+(b.retina?" leaflet-retina":"")+(b.ielt9?" leaflet-oldie":"")+(b.safari?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":"")),pe(t,"position"));"absolute"!==e&&"relative"!==e&&"fixed"!==e&&"sticky"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Z(this._mapPane,new p(0,0)),this.createPane("tilePane"),this.createPane("overlayPane"),this.createPane("shadowPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(M(t.markerPane,"leaflet-zoom-hide"),M(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,e,i){Z(this._mapPane,new p(0,0));var n=!this._loaded,o=(this._loaded=!0,e=this._limitZoom(e),this.fire("viewprereset"),this._zoom!==e);this._moveStart(o,i)._move(t,e)._moveEnd(o),this.fire("viewreset"),n&&this.fire("load")},_moveStart:function(t,e){return t&&this.fire("zoomstart"),e||this.fire("movestart"),this},_move:function(t,e,i,n){void 0===e&&(e=this._zoom);var o=this._zoom!==e;return this._zoom=e,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),n?i&&i.pinch&&this.fire("zoom",i):((o||i&&i.pinch)&&this.fire("zoom",i),this.fire("move",i)),this},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return r(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Z(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={};var e=t?k:S;e((this._targets[h(this._container)]=this)._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress keydown keyup",this._handleDOMEvent,this),this.options.trackResize&&e(window,"resize",this._onResize,this),b.any3d&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){r(this._resizeRequest),this._resizeRequest=x(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,e){for(var i,n=[],o="mouseout"===e||"mouseover"===e,s=t.target||t.srcElement,r=!1;s;){if((i=this._targets[h(s)])&&("click"===e||"preclick"===e)&&this._draggableMoved(i)){r=!0;break}if(i&&i.listens(e,!0)){if(o&&!We(s,t))break;if(n.push(i),o)break}if(s===this._container)break;s=s.parentNode}return n=n.length||r||o||!this.listens(e,!0)?n:[this]},_isClickDisabled:function(t){for(;t&&t!==this._container;){if(t._leaflet_disable_click)return!0;t=t.parentNode}},_handleDOMEvent:function(t){var e,i=t.target||t.srcElement;!this._loaded||i._leaflet_disable_events||"click"===t.type&&this._isClickDisabled(i)||("mousedown"===(e=t.type)&&Me(i),this._fireDOMEvent(t,e))},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,i){"click"===t.type&&((a=l({},t)).type="preclick",this._fireDOMEvent(a,a.type,i));var n=this._findEventTargets(t,e);if(i){for(var o=[],s=0;sthis.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),n=this._getCenterOffset(t)._divideBy(1-1/n);if(!0!==i.animate&&!this.getSize().contains(n))return!1;x(function(){this._moveStart(!0,i.noMoveStart||!1)._animateZoom(t,e,!0)},this)}return!0},_animateZoom:function(t,e,i,n){this._mapPane&&(i&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=e,M(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:e,noUpdate:n}),this._tempFireZoomEvent||(this._tempFireZoomEvent=this._zoom!==this._animateToZoom),this._move(this._animateToCenter,this._animateToZoom,void 0,!0),setTimeout(a(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&z(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom,void 0,!0),this._tempFireZoomEvent&&this.fire("zoom"),delete this._tempFireZoomEvent,this.fire("move"),this._moveEnd(!0))}});function Ue(t){return new B(t)}var B=et.extend({options:{position:"topright"},initialize:function(t){c(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),t=t._controlCorners[i];return M(e,"leaflet-control"),-1!==i.indexOf("bottom")?t.insertBefore(e,t.firstChild):t.appendChild(e),this._map.on("unload",this.remove,this),this},remove:function(){return this._map&&(T(this._container),this.onRemove&&this.onRemove(this._map),this._map.off("unload",this.remove,this),this._map=null),this},_refocusOnMap:function(t){this._map&&t&&0",e=document.createElement("div");return e.innerHTML=t,e.firstChild},_addItem:function(t){var e,i=document.createElement("label"),n=this._map.hasLayer(t.layer),n=(t.overlay?((e=document.createElement("input")).type="checkbox",e.className="leaflet-control-layers-selector",e.defaultChecked=n):e=this._createRadioElement("leaflet-base-layers_"+h(this),n),this._layerControlInputs.push(e),e.layerId=h(t.layer),S(e,"click",this._onInputClick,this),document.createElement("span")),o=(n.innerHTML=" "+t.name,document.createElement("span"));return i.appendChild(o),o.appendChild(e),o.appendChild(n),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(i),this._checkDisabledLayers(),i},_onInputClick:function(){if(!this._preventClick){var t,e,i=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=i.length-1;0<=s;s--)t=i[s],e=this._getLayer(t.layerId).layer,t.checked?n.push(e):t.checked||o.push(e);for(s=0;se.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expandSafely:function(){var t=this._section,e=(this._preventClick=!0,S(t,"click",O),this.expand(),this);setTimeout(function(){k(t,"click",O),e._preventClick=!1})}})),qe=B.extend({options:{position:"topleft",zoomInText:'',zoomInTitle:"Zoom in",zoomOutText:'',zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=P("div",e+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,e+"-in",i,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,e+"-out",i,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,e,i,n,o){i=P("a",i,n);return i.innerHTML=t,i.href="#",i.title=e,i.setAttribute("role","button"),i.setAttribute("aria-label",e),Ie(i),S(i,"click",Re),S(i,"click",o,this),S(i,"click",this._refocusOnMap,this),i},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";z(this._zoomInButton,e),z(this._zoomOutButton,e),this._zoomInButton.setAttribute("aria-disabled","false"),this._zoomOutButton.setAttribute("aria-disabled","false"),!this._disabled&&t._zoom!==t.getMinZoom()||(M(this._zoomOutButton,e),this._zoomOutButton.setAttribute("aria-disabled","true")),!this._disabled&&t._zoom!==t.getMaxZoom()||(M(this._zoomInButton,e),this._zoomInButton.setAttribute("aria-disabled","true"))}}),Ge=(A.mergeOptions({zoomControl:!0}),A.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new qe,this.addControl(this.zoomControl))}),B.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var e="leaflet-control-scale",i=P("div",e),n=this.options;return this._addScales(n,e+"-line",i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=P("div",e,i)),t.imperial&&(this._iScale=P("div",e,i))},_update:function(){var t=this._map,e=t.getSize().y/2,t=t.distance(t.containerPointToLatLng([0,e]),t.containerPointToLatLng([this.options.maxWidth,e]));this._updateScales(t)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var e=this._getRoundNum(t);this._updateScale(this._mScale,e<1e3?e+" m":e/1e3+" km",e/t)},_updateImperial:function(t){var e,i,t=3.2808399*t;5280'+(b.inlineSvg?' ':"")+"Leaflet"},initialize:function(t){c(this,t),this._attributions={}},onAdd:function(t){for(var e in(t.attributionControl=this)._container=P("div","leaflet-control-attribution"),Ie(this._container),t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return this._update(),t.on("layeradd",this._addAttribution,this),this._container},onRemove:function(t){t.off("layeradd",this._addAttribution,this)},_addAttribution:function(t){t.layer.getAttribution&&(this.addAttribution(t.layer.getAttribution()),t.layer.once("remove",function(){this.removeAttribution(t.layer.getAttribution())},this))},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t&&(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update()),this},removeAttribution:function(t){return t&&this._attributions[t]&&(this._attributions[t]--,this._update()),this},_update:function(){if(this._map){var t,e=[];for(t in this._attributions)this._attributions[t]&&e.push(t);var i=[];this.options.prefix&&i.push(this.options.prefix),e.length&&i.push(e.join(", ")),this._container.innerHTML=i.join(' ')}}}),n=(A.mergeOptions({attributionControl:!0}),A.addInitHook(function(){this.options.attributionControl&&(new Ke).addTo(this)}),B.Layers=Ve,B.Zoom=qe,B.Scale=Ge,B.Attribution=Ke,Ue.layers=function(t,e,i){return new Ve(t,e,i)},Ue.zoom=function(t){return new qe(t)},Ue.scale=function(t){return new Ge(t)},Ue.attribution=function(t){return new Ke(t)},et.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled||(this._enabled=!0,this.addHooks()),this},disable:function(){return this._enabled&&(this._enabled=!1,this.removeHooks()),this},enabled:function(){return!!this._enabled}})),ft=(n.addTo=function(t,e){return t.addHandler(e,this),this},{Events:e}),Ye=b.touch?"touchstart mousedown":"mousedown",Xe=it.extend({options:{clickTolerance:3},initialize:function(t,e,i,n){c(this,n),this._element=t,this._dragStartTarget=e||t,this._preventOutline=i},enable:function(){this._enabled||(S(this._dragStartTarget,Ye,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Xe._dragging===this&&this.finishDrag(!0),k(this._dragStartTarget,Ye,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){var e,i;this._enabled&&(this._moved=!1,ve(this._element,"leaflet-zoom-anim")||(t.touches&&1!==t.touches.length?Xe._dragging===this&&this.finishDrag():Xe._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||((Xe._dragging=this)._preventOutline&&Me(this._element),Le(),re(),this._moving||(this.fire("down"),i=t.touches?t.touches[0]:t,e=Ce(this._element),this._startPoint=new p(i.clientX,i.clientY),this._startPos=Pe(this._element),this._parentScale=Ze(e),i="mousedown"===t.type,S(document,i?"mousemove":"touchmove",this._onMove,this),S(document,i?"mouseup":"touchend touchcancel",this._onUp,this)))))},_onMove:function(t){var e;this._enabled&&(t.touches&&1e&&(i.push(t[n]),o=n);oe.max.x&&(i|=2),t.ye.max.y&&(i|=8),i}function ri(t,e,i,n){var o=e.x,e=e.y,s=i.x-o,r=i.y-e,a=s*s+r*r;return 0this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(l=!l);return l||yi.prototype._containsPoint.call(this,t,!0)}});var wi=ci.extend({initialize:function(t,e){c(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,o=d(t)?t:t.features;if(o){for(e=0,i=o.length;es.x&&(r=i.x+a-s.x+o.x),i.x-r-n.x<(a=0)&&(r=i.x-n.x),i.y+e+o.y>s.y&&(a=i.y+e-s.y+o.y),i.y-a-n.y<0&&(a=i.y-n.y),(r||a)&&(this.options.keepInView&&(this._autopanning=!0),t.fire("autopanstart").panBy([r,a]))))},_getAnchor:function(){return m(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}})),Ii=(A.mergeOptions({closePopupOnClick:!0}),A.include({openPopup:function(t,e,i){return this._initOverlay(Bi,t,e,i).openOn(this),this},closePopup:function(t){return(t=arguments.length?t:this._popup)&&t.close(),this}}),o.include({bindPopup:function(t,e){return this._popup=this._initOverlay(Bi,this._popup,t,e),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t){return this._popup&&(this instanceof ci||(this._popup._source=this),this._popup._prepareOpen(t||this._latlng)&&this._popup.openOn(this._map)),this},closePopup:function(){return this._popup&&this._popup.close(),this},togglePopup:function(){return this._popup&&this._popup.toggle(this),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var e;this._popup&&this._map&&(Re(t),e=t.layer||t.target,this._popup._source!==e||e instanceof fi?(this._popup._source=e,this.openPopup(t.latlng)):this._map.hasLayer(this._popup)?this.closePopup():this.openPopup(t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}}),Ai.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,opacity:.9},onAdd:function(t){Ai.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&(this.addEventParent(this._source),this._source.fire("tooltipopen",{tooltip:this},!0))},onRemove:function(t){Ai.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&(this.removeEventParent(this._source),this._source.fire("tooltipclose",{tooltip:this},!0))},getEvents:function(){var t=Ai.prototype.getEvents.call(this);return this.options.permanent||(t.preclick=this.close),t},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=P("div",t),this._container.setAttribute("role","tooltip"),this._container.setAttribute("id","leaflet-tooltip-"+h(this))},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var e,i=this._map,n=this._container,o=i.latLngToContainerPoint(i.getCenter()),i=i.layerPointToContainerPoint(t),s=this.options.direction,r=n.offsetWidth,a=n.offsetHeight,h=m(this.options.offset),l=this._getAnchor(),i="top"===s?(e=r/2,a):"bottom"===s?(e=r/2,0):(e="center"===s?r/2:"right"===s?0:"left"===s?r:i.xthis.options.maxZoom||nthis.options.maxZoom||void 0!==this.options.minZoom&&oi.max.x)||!e.wrapLat&&(t.yi.max.y))return!1}return!this.options.bounds||(e=this._tileCoordsToBounds(t),g(this.options.bounds).overlaps(e))},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var e=this._map,i=this.getTileSize(),n=t.scaleBy(i),i=n.add(i);return[e.unproject(n,t.z),e.unproject(i,t.z)]},_tileCoordsToBounds:function(t){t=this._tileCoordsToNwSe(t),t=new s(t[0],t[1]);return t=this.options.noWrap?t:this._map.wrapLatLngBounds(t)},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var t=t.split(":"),e=new p(+t[0],+t[1]);return e.z=+t[2],e},_removeTile:function(t){var e=this._tiles[t];e&&(T(e.el),delete this._tiles[t],this.fire("tileunload",{tile:e.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){M(t,"leaflet-tile");var e=this.getTileSize();t.style.width=e.x+"px",t.style.height=e.y+"px",t.onselectstart=u,t.onmousemove=u,b.ielt9&&this.options.opacity<1&&C(t,this.options.opacity)},_addTile:function(t,e){var i=this._getTilePos(t),n=this._tileCoordsToKey(t),o=this.createTile(this._wrapCoords(t),a(this._tileReady,this,t));this._initTile(o),this.createTile.length<2&&x(a(this._tileReady,this,t,null,o)),Z(o,i),this._tiles[n]={el:o,coords:t,current:!0},e.appendChild(o),this.fire("tileloadstart",{tile:o,coords:t})},_tileReady:function(t,e,i){e&&this.fire("tileerror",{error:e,tile:i,coords:t});var n=this._tileCoordsToKey(t);(i=this._tiles[n])&&(i.loaded=+new Date,this._map._fadeAnimated?(C(i.el,0),r(this._fadeFrame),this._fadeFrame=x(this._updateOpacity,this)):(i.active=!0,this._pruneTiles()),e||(M(i.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:i.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),b.ielt9||!this._map._fadeAnimated?x(this._pruneTiles,this):setTimeout(a(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var e=new p(this._wrapX?H(t.x,this._wrapX):t.x,this._wrapY?H(t.y,this._wrapY):t.y);return e.z=t.z,e},_pxBoundsToTileRange:function(t){var e=this.getTileSize();return new f(t.min.unscaleBy(e).floor(),t.max.unscaleBy(e).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}});var Di=Ni.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1,referrerPolicy:!1},initialize:function(t,e){this._url=t,(e=c(this,e)).detectRetina&&b.retina&&0')}}catch(t){}return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}(),zt={_initContainer:function(){this._container=P("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(Wi.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var e=t._container=Vi("shape");M(e,"leaflet-vml-shape "+(this.options.className||"")),e.coordsize="1 1",t._path=Vi("path"),e.appendChild(t._path),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){var e=t._container;this._container.appendChild(e),t.options.interactive&&t.addInteractiveTarget(e)},_removePath:function(t){var e=t._container;T(e),t.removeInteractiveTarget(e),delete this._layers[h(t)]},_updateStyle:function(t){var e=t._stroke,i=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(e=e||(t._stroke=Vi("stroke")),o.appendChild(e),e.weight=n.weight+"px",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=d(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):e.dashStyle="",e.endcap=n.lineCap.replace("butt","flat"),e.joinstyle=n.lineJoin):e&&(o.removeChild(e),t._stroke=null),n.fill?(i=i||(t._fill=Vi("fill")),o.appendChild(i),i.color=n.fillColor||n.color,i.opacity=n.fillOpacity):i&&(o.removeChild(i),t._fill=null)},_updateCircle:function(t){var e=t._point.round(),i=Math.round(t._radius),n=Math.round(t._radiusY||i);this._setPath(t,t._empty()?"M0 0":"AL "+e.x+","+e.y+" "+i+","+n+" 0,23592600")},_setPath:function(t,e){t._path.v=e},_bringToFront:function(t){fe(t._container)},_bringToBack:function(t){ge(t._container)}},qi=b.vml?Vi:ct,Gi=Wi.extend({_initContainer:function(){this._container=qi("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=qi("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){T(this._container),k(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_update:function(){var t,e,i;this._map._animatingZoom&&this._bounds||(Wi.prototype._update.call(this),e=(t=this._bounds).getSize(),i=this._container,this._svgSize&&this._svgSize.equals(e)||(this._svgSize=e,i.setAttribute("width",e.x),i.setAttribute("height",e.y)),Z(i,t.min),i.setAttribute("viewBox",[t.min.x,t.min.y,e.x,e.y].join(" ")),this.fire("update"))},_initPath:function(t){var e=t._path=qi("path");t.options.className&&M(e,t.options.className),t.options.interactive&&M(e,"leaflet-interactive"),this._updateStyle(t),this._layers[h(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){T(t._path),t.removeInteractiveTarget(t._path),delete this._layers[h(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var e=t._path,t=t.options;e&&(t.stroke?(e.setAttribute("stroke",t.color),e.setAttribute("stroke-opacity",t.opacity),e.setAttribute("stroke-width",t.weight),e.setAttribute("stroke-linecap",t.lineCap),e.setAttribute("stroke-linejoin",t.lineJoin),t.dashArray?e.setAttribute("stroke-dasharray",t.dashArray):e.removeAttribute("stroke-dasharray"),t.dashOffset?e.setAttribute("stroke-dashoffset",t.dashOffset):e.removeAttribute("stroke-dashoffset")):e.setAttribute("stroke","none"),t.fill?(e.setAttribute("fill",t.fillColor||t.color),e.setAttribute("fill-opacity",t.fillOpacity),e.setAttribute("fill-rule",t.fillRule||"evenodd")):e.setAttribute("fill","none"))},_updatePoly:function(t,e){this._setPath(t,dt(t._parts,e))},_updateCircle:function(t){var e=t._point,i=Math.max(Math.round(t._radius),1),n="a"+i+","+(Math.max(Math.round(t._radiusY),1)||i)+" 0 1,0 ",e=t._empty()?"M0 0":"M"+(e.x-i)+","+e.y+n+2*i+",0 "+n+2*-i+",0 ";this._setPath(t,e)},_setPath:function(t,e){t._path.setAttribute("d",e)},_bringToFront:function(t){fe(t._path)},_bringToBack:function(t){ge(t._path)}});function Ki(t){return b.svg||b.vml?new Gi(t):null}b.vml&&Gi.include(zt),A.include({getRenderer:function(t){t=(t=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer)||(this._renderer=this._createRenderer());return this.hasLayer(t)||this.addLayer(t),t},_getPaneRenderer:function(t){var e;return"overlayPane"!==t&&void 0!==t&&(void 0===(e=this._paneRenderers[t])&&(e=this._createRenderer({pane:t}),this._paneRenderers[t]=e),e)},_createRenderer:function(t){return this.options.preferCanvas&&Ui(t)||Ki(t)}});var Yi=xi.extend({initialize:function(t,e){xi.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return[(t=g(t)).getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});Gi.create=qi,Gi.pointsToPath=dt,wi.geometryToLayer=bi,wi.coordsToLatLng=Li,wi.coordsToLatLngs=Ti,wi.latLngToCoords=Mi,wi.latLngsToCoords=zi,wi.getFeature=Ci,wi.asFeature=Zi,A.mergeOptions({boxZoom:!0});var _t=n.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){S(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){k(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){T(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),re(),Le(),this._startPoint=this._map.mouseEventToContainerPoint(t),S(document,{contextmenu:Re,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=P("div","leaflet-zoom-box",this._container),M(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var t=new f(this._point,this._startPoint),e=t.getSize();Z(this._box,t.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(T(this._box),z(this._container,"leaflet-crosshair")),ae(),Te(),k(document,{contextmenu:Re,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){1!==t.which&&1!==t.button||(this._finish(),this._moved&&(this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(a(this._resetState,this),0),t=new s(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point)),this._map.fitBounds(t).fire("boxzoomend",{boxZoomBounds:t})))},_onKeyDown:function(t){27===t.keyCode&&(this._finish(),this._clearDeferredResetState(),this._resetState())}}),Ct=(A.addInitHook("addHandler","boxZoom",_t),A.mergeOptions({doubleClickZoom:!0}),n.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom(),n=e.options.zoomDelta,i=t.originalEvent.shiftKey?i-n:i+n;"center"===e.options.doubleClickZoom?e.setZoom(i):e.setZoomAround(t.containerPoint,i)}})),Zt=(A.addInitHook("addHandler","doubleClickZoom",Ct),A.mergeOptions({dragging:!0,inertia:!0,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0}),n.extend({addHooks:function(){var t;this._draggable||(t=this._map,this._draggable=new Xe(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))),M(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){z(this._map._container,"leaflet-grab"),z(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t,e=this._map;e._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity?(t=g(this._map.options.maxBounds),this._offsetLimit=_(this._map.latLngToContainerPoint(t.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(t.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))):this._offsetLimit=null,e.fire("movestart").fire("dragstart"),e.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){var e,i;this._map.options.inertia&&(e=this._lastTime=+new Date,i=this._lastPos=this._draggable._absPos||this._draggable._newPos,this._positions.push(i),this._times.push(e),this._prunePositions(e)),this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;1e.max.x&&(t.x=this._viscousLimit(t.x,e.max.x)),t.y>e.max.y&&(t.y=this._viscousLimit(t.y,e.max.y)),this._draggable._newPos=this._draggable._startPos.add(t))},_onPreDragWrap:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,n=(n+e+i)%t-e-i,t=Math.abs(o+i)e.getMaxZoom()&&1 + + + + +RAVE SCOUT · localizaciones aisladas en España + + + +
+

RAVE SCOUT

+

Los 498.528 km² de la España peninsular y balear barridos celda a + celda, buscando sitios lejos de todo a los que además se pueda llegar.

+ + + +
+ 355 localizaciones + 12,5 M edificios + 4.139 espacios protegidos + 100 m de malla +
+ + +
+ +