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
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
*.json
|
||||
*.html
|
||||
*.png
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.pytest_cache/
|
||||
@@ -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
|
||||
+130
@@ -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)
|
||||
Reference in New Issue
Block a user