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
140 lines
4.7 KiB
Python
140 lines
4.7 KiB
Python
"""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)
|