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
This commit is contained in:
commit
d4b3b803e0
394 changed files with 7393 additions and 0 deletions
140
src/extract_extra.py
Normal file
140
src/extract_extra.py
Normal file
|
|
@ -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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue