From c0c9441930aacc01d02c80562e44e48cd125f1cc Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Fri, 19 Jun 2026 18:53:39 +0200 Subject: [PATCH] Initial release v1.0: Adres analyse met isochroon bereikbaarheid - Geocoding via PDOK Locatieserver - Isochroon (auto 20 min) via OpenRouteService - Rapport generatie in markdown formaat - Voorbeeld adres: Griendvelden 14, Best --- .gitignore | 6 +++ README.md | 73 ++++++++++++++++++++++++++++++ rapport.py | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 rapport.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..883f8d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.json +*.html +*.png +__pycache__/ +*.pyc +.pytest_cache/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..1d09710 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# Adres Analyse + +Genereert modulaire rapporten voor Nederlandse adressen met bereikbaarheid, voorzieningen en locatie kenmerken. + +## Installatie + +```bash +git clone https://git.wouterverduin.nl/wouser/adres-analyse.git +cd adres-analyse +pip install folium shapely +``` + +## Gebruik + +```bash +python3 rapport.py "Griendvelden 14 Best" +``` + +Output is markdown formaat naar stdout. + +## Features + +### Huidig (v1.0) +- **Geocoding** via PDOK Locatieserver (gratis, geen API key) +- **Isochroon bereikbaarheid** (auto, 20 min) via OpenRouteService +- Oppervlakte bereikbaar gebied (km²) +- Reach factor en geografisch bereik + +### Gepland +- Cycling isochroon (fiets) +- OV bereikbaarheid +- Voorzieningen in de buurt (OSM) +- Luchtkwaliteit (RIVM) +- Geluidsniveaus +- Overstromingsrisico +- Demografie (CBS) +- Woningwaarde (Kadaster/WOZ) + +## Vereisten + +- **OpenRouteService**: Draaiend op `192.168.1.71:9080` met `driving-car` profiel +- **PDOK Locatieserver**: Openbaar, geen setup nodig + +## Configuratie + +Pas aan in `rapport.py`: +```python +ORS_BASE = "http://192.168.1.71:9080/ors/v2" +PDOK_BASE = "https://api.pdok.nl/bzk/locatieserver/search/v3_1" +``` + +## Voorbeeld output + +``` +📍 **Adres Analyse Rapport** +Gegenereerd: 19-06-2026 18:49 + +🔍 Geocoding... +**Adres:** Griendvelden 14, 5685JL Best +**Coördinaten:** 51.505547, 5.370553 + +🚗 **Bereikbaarheid (Auto 20 min)** +**Oppervlakte:** 316.8 km² +**Reach factor:** 0.091 +**Bereik:** 51.378°-51.679°N, 5.128°-5.530°E + +--- +_Onderdeel 1/?: Isochroon bereikbaarheid_ +``` + +## Licentie + +MIT diff --git a/rapport.py b/rapport.py new file mode 100644 index 0000000..2748671 --- /dev/null +++ b/rapport.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +""" +Adres Analyse - Rapport generator +Gebruikt PDOK voor geocoding en ORS voor isochronen +""" + +import json +import sys +import urllib.request +import urllib.parse +from datetime import datetime + +# Config +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): + """Geocode adres naar coördinaten via PDOK Locatieserver""" + # Gebruik suggest endpoint voor betere adres matching + 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()) + + if not data['response']['docs']: + raise ValueError(f"Adres niet gevonden: {adres}") + + # Zoek naar exact adres match (type=adres) + doc = None + for d in data['response']['docs']: + if d.get('type') == 'adres': + doc = d + break + + if not doc: + doc = data['response']['docs'][0] # Fallback naar eerste resultaat + + # Parse POINT(lon lat) format + point = doc['centroide_ll'] + coords = point.replace('POINT(', '').replace(')', '').split() + lon, lat = float(coords[0]), float(coords[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='driving-car', minuten=20): + """Haal isochroon op van ORS""" + url = f"{ORS_BASE}/isochrones/{profiel}" + payload = { + "locations": [[lon, lat]], + "range": [minuten * 60], # seconden + "attributes": ["area", "reachfactor", "total_pop"] + } + + req = urllib.request.Request( + url, + data=json.dumps(payload).encode(), + headers={'Content-Type': 'application/json'}, + method='POST' + ) + + with urllib.request.urlopen(req, timeout=30) as resp: + return json.loads(resp.read()) + +def format_oppervlakte(m2): + """Converteer m² naar leesbaar formaat""" + if m2 >= 1_000_000: + return f"{m2/1_000_000:.1f} km²" + elif m2 >= 10_000: + return f"{m2/10_000:.1f} ha" + else: + return f"{m2:.0f} m²" + +def genereer_rapport(adres_input): + """Genereer volledig adres analyse rapport""" + print(f"📍 **Adres Analyse Rapport**") + print(f"Gegenereerd: {datetime.now().strftime('%d-%m-%Y %H:%M')}") + print() + + # Geocoding + print("🔍 Geocoding...") + locatie = geocode_adres(adres_input) + print(f"**Adres:** {locatie['adres']}") + print(f"**Coördinaten:** {locatie['lat']:.6f}, {locatie['lon']:.6f}") + print() + + # Isochroon + print("🚗 **Bereikbaarheid (Auto 20 min)**") + iso_data = haal_isochroon(locatie['lon'], locatie['lat'], 'driving-car', 20) + + if 'features' in iso_data and iso_data['features']: + props = iso_data['features'][0]['properties'] + area_m2 = props.get('area', 0) + reach = props.get('reachfactor', 0) + + print(f"**Oppervlakte:** {format_oppervlakte(area_m2)}") + print(f"**Reach factor:** {reach:.3f}") + print() + + # Bereken bbox + bbox = iso_data.get('bbox', []) + if len(bbox) == 4: + print(f"**Bereik:** {bbox[1]:.3f}°-{bbox[3]:.3f}°N, {bbox[0]:.3f}°-{bbox[2]:.3f}°E") + print() + + # Save GeoJSON voor kaart generatie + output_file = f"/tmp/isochroon_{datetime.now().strftime('%Y%m%d_%H%M%S')}.geojson" + with open(output_file, 'w') as f: + json.dump(iso_data, f) + print(f"💾 Data opgeslagen: `{output_file}`") + else: + print("❌ Geen isochroon data ontvangen") + + print() + print("---") + print("_Onderdeel 1/?: Isochroon bereikbaarheid_") + +if __name__ == '__main__': + adres = sys.argv[1] if len(sys.argv) > 1 else "Griendvelden 14 Best" + genereer_rapport(adres)