Files
adres-analyse/scripts/generate-map.py
T

138 lines
4.5 KiB
Python

#!/usr/bin/env python3
"""
Genereer interactieve kaart voor adres analyse.
Toont in één figuur:
- blauw: 20 minuten autorijden (driving-car)
- groen: 30 minuten fietsen (cycling-regular)
"""
import json
import sys
import urllib.parse
import urllib.request
import folium
ORS_BASE = "http://192.168.1.71:9080/ors/v2"
PDOK_BASE = "https://api.pdok.nl/bzk/locatieserver/search/v3_1"
def geocode_adres(adres):
params = urllib.parse.urlencode({
"q": adres,
"rows": 5,
"fl": "centroide_ll,weergavenaam,postcode,woonplaatsnaam,type,huisnummer",
})
url = f"{PDOK_BASE}/free?{params}"
with urllib.request.urlopen(url, timeout=10) as resp:
data = json.loads(resp.read())
docs = data.get("response", {}).get("docs", [])
if not docs:
raise ValueError(f"Adres niet gevonden: {adres}")
doc = next((d for d in docs if d.get("type") == "adres"), docs[0])
point = doc["centroide_ll"].replace("POINT(", "").replace(")", "").split()
lon, lat = float(point[0]), float(point[1])
return {
"adres": doc.get("weergavenaam", adres),
"postcode": doc.get("postcode", ""),
"woonplaats": doc.get("woonplaatsnaam", ""),
"lat": lat,
"lon": lon,
}
def haal_isochroon(lon, lat, profiel, minuten):
payload = json.dumps({
"locations": [[lon, lat]],
"range": [minuten * 60],
"attributes": ["area", "reachfactor", "total_pop"],
}).encode()
req = urllib.request.Request(
f"{ORS_BASE}/isochrones/{profiel}",
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req, timeout=60) as resp:
return json.loads(resp.read())
def polygon_bounds(feature_collection):
bbox = feature_collection.get("bbox", [])
if len(bbox) == 4:
return [[bbox[1], bbox[0]], [bbox[3], bbox[2]]]
bounds = []
for feature in feature_collection.get("features", []):
coords = feature.get("geometry", {}).get("coordinates", [[]])[0]
bounds.extend([[lat, lon] for lon, lat in coords])
return bounds
def voeg_isochroon_toe(m, iso, label, kleur, tooltip):
for feature in iso.get("features", []):
coords = feature["geometry"]["coordinates"][0]
props = feature.get("properties", {})
area_km2 = props.get("area", 0) / 1_000_000
reach = props.get("reachfactor", 0)
folium.Polygon(
locations=[[lat, lon] for lon, lat in coords],
color=kleur,
weight=3,
fill=True,
fill_color=kleur,
fill_opacity=0.18,
popup=(
f"<b>{label}</b><br>"
f"Oppervlakte: {area_km2:.1f} km²<br>"
f"Reach factor: {reach:.3f}"
),
tooltip=tooltip,
).add_to(m)
def genereer_kaart(adres, output_file="/root/adres-analyse/kaart.html"):
locatie = geocode_adres(adres)
lat, lon = locatie["lat"], locatie["lon"]
auto_iso = haal_isochroon(lon, lat, "driving-car", 20)
fiets_iso = haal_isochroon(lon, lat, "cycling-regular", 30)
m = folium.Map(location=[lat, lon], zoom_start=11, tiles="OpenStreetMap")
folium.Marker(
[lat, lon],
popup=locatie["adres"],
tooltip=locatie["adres"],
icon=folium.Icon(color="red", icon="home"),
).add_to(m)
voeg_isochroon_toe(m, auto_iso, "Auto 20 min", "#2196F3", "Bereikbaar in 20 min (auto)")
voeg_isochroon_toe(m, fiets_iso, "Fiets 30 min", "#2E7D32", "Bereikbaar in 30 min (fiets)")
all_bounds = polygon_bounds(auto_iso) + polygon_bounds(fiets_iso)
if all_bounds:
m.fit_bounds(all_bounds)
title_html = f'''
<div style="position:fixed;top:10px;left:60px;z-index:1000;background:white;padding:10px 15px;border-radius:8px;box-shadow:0 2px 6px rgba(0,0,0,0.3);font-family:sans-serif;font-size:14px;line-height:1.35;">
<b>📍 Adres Analyse</b><br>
{locatie["adres"]}<br>
<span style="color:#2196F3">■</span> 20 min autorijden<br>
<span style="color:#2E7D32">■</span> 30 min fietsen
</div>
'''
m.get_root().html.add_child(folium.Element(title_html))
m.save(output_file)
print(f"Kaart gegenereerd: {output_file}")
print(f"Adres: {locatie['adres']}")
print(f"Coördinaten: {lat}, {lon}")
if __name__ == "__main__":
adres = sys.argv[1] if len(sys.argv) > 1 else "Griendvelden 14 Best"
output = sys.argv[2] if len(sys.argv) > 2 else "/root/adres-analyse/kaart.html"
genereer_kaart(adres, output)