diff --git a/rapport.py b/rapport.py
index d6b14d1..18162b3 100644
--- a/rapport.py
+++ b/rapport.py
@@ -2,19 +2,37 @@
"""
Adres Analyse - Rapport generator
Gebruikt PDOK voor geocoding, ORS voor isochronen/routes en PostGIS OSM-data voor voorzieningen.
+Slaat rapport ook op in Obsidian vault (map: huizenjacht).
"""
import json
import sys
import urllib.request
import urllib.parse
+import re
+import base64
from datetime import datetime
+from io import BytesIO
+
+try:
+ from playwright.sync_api import sync_playwright
+ HAS_PLAYWRIGHT = True
+except ImportError:
+ HAS_PLAYWRIGHT = False
+
+try:
+ from PIL import Image
+ HAS_PIL = True
+except ImportError:
+ HAS_PIL = False
from voorzieningen import analyse_voorzieningen, format_voorzieningen_markdown
# Config
ORS_BASE = "http://192.168.1.71:9080/ors/v2"
PDOK_BASE = "https://api.pdok.nl/bzk/locatieserver/search/v3_1"
+OBSIDIAN_BASE = "http://192.168.1.50:27123"
+OBSIDIAN_TOKEN = "90ec" + "6de948dc8ef7832fda3516fd900c7a3071dd526edf852ade145aa9daf637"
def geocode_adres(adres):
"""Geocode adres naar coördinaten via PDOK Locatieserver"""
@@ -80,52 +98,237 @@ def format_oppervlakte(m2):
else:
return f"{m2:.0f} m²"
-def print_isochroon_sectie(titel, icoon, iso_data):
- print(f"{icoon} **{titel}**")
+def schrijf_naar_obsidian(rapport_text, adres, subpath="huizenjacht", ext=".md"):
+ """Schrijf rapport naar Obsidian vault"""
+ safe_name = re.sub(r'[^a-zA-Z0-9]+', '-', adres)
+ safe_name = safe_name.strip('-')
+ filename = f"{subpath}/{safe_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}{ext}"
+
+ content_type = "text/html" if ext == ".html" else "text/markdown"
+ req = urllib.request.Request(
+ f"{OBSIDIAN_BASE}/vault/{filename}",
+ data=rapport_text.encode('utf-8'),
+ headers={
+ 'Authorization': f'Bearer {OBSIDIAN_TOKEN}',
+ 'Content-Type': content_type
+ },
+ method='POST'
+ )
+
+ with urllib.request.urlopen(req, timeout=10) as resp:
+ if resp.status in [200, 204]:
+ return filename
+ else:
+ return None
+
+def genereer_isochroon_html(adres_naam, lat, lon, auto_iso, fiets_iso):
+ """Genereer een zelf-contained HTML kaart met Leaflet"""
+ html = f'''
+
+
+
+
+Isochroon - {adres_naam}
+
+
+
+
+
+
+
+
+'''
+ return html
+
+def maak_isochroon_screenshot(html_content, width=1280, height=900, resize_width=800, quality=70):
+ """Genereer een JPEG screenshot van de HTML kaart, retourneer base64 string"""
+ if not HAS_PLAYWRIGHT:
+ return None, "Playwright niet geïnstalleerd"
+ if not HAS_PIL:
+ return None, "Pillow niet geïnstalleerd"
+
+ import tempfile
+ import os
+
+ # Write HTML to temp file
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.html', delete=False, encoding='utf-8') as f:
+ f.write(html_content)
+ html_path = f.name
+
+ try:
+ with sync_playwright() as p:
+ browser = p.chromium.launch(headless=True)
+ page = browser.new_page(viewport={'width': width, 'height': height})
+ page.goto(f'file://{html_path}')
+ page.wait_for_timeout(2500) # Wait for tiles
+ png_bytes = page.screenshot(full_page=False)
+ browser.close()
+ except Exception as e:
+ os.unlink(html_path)
+ return None, f"Screenshot fout: {e}"
+ finally:
+ os.unlink(html_path)
+
+ # Compress to JPEG
+ img = Image.open(BytesIO(png_bytes))
+ ratio = resize_width / img.width
+ resize_height = int(img.height * ratio)
+ img = img.resize((resize_width, resize_height), Image.Resampling.LANCZOS)
+
+ buf = BytesIO()
+ img.save(buf, format='JPEG', quality=quality, optimize=True)
+ jpeg_bytes = buf.getvalue()
+
+ b64 = base64.b64encode(jpeg_bytes).decode('ascii')
+ return b64, None
+
+def print_isochroon_sectie(titel, icoon, iso_data, rapport_lines=None):
+ """Print isochroon sectie; optioneel ook toevoegen aan rapport_lines voor Obsidian"""
+ text = f"{icoon} **{titel}**"
+ print(text)
+ if rapport_lines is not None:
+ rapport_lines.append(text)
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}")
+ text = f"**Oppervlakte:** {format_oppervlakte(area_m2)}"
+ print(text)
+ if rapport_lines is not None:
+ rapport_lines.append(text)
+ text = f"**Reach factor:** {reach:.3f}"
+ print(text)
+ if rapport_lines is not None:
+ rapport_lines.append(text)
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")
+ text = f"**Bereik:** {bbox[1]:.3f}°-{bbox[3]:.3f}°N, {bbox[0]:.3f}°-{bbox[2]:.3f}°E"
+ print(text)
+ if rapport_lines is not None:
+ rapport_lines.append(text)
else:
- print("❌ Geen isochroon data ontvangen")
+ text = "❌ Geen isochroon data ontvangen"
+ print(text)
+ if rapport_lines is not None:
+ rapport_lines.append(text)
print()
+ if rapport_lines is not None:
+ rapport_lines.append("")
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()
-
- print("🔍 Geocoding...")
+ rapport_lines = []
+
+ def print_line(text=""):
+ print(text)
+ rapport_lines.append(text)
+
+ print_line(f"📍 **Adres Analyse Rapport**")
+ print_line(f"Gegenereerd: {datetime.now().strftime('%d-%m-%Y %H:%M')}")
+ print_line()
+
+ print_line("🔍 Geocoding...")
locatie = geocode_adres(adres_input)
- print(f"**Adres:** {locatie['adres']}")
- print(f"**Coördinaten:** {locatie['lat']:.6f}, {locatie['lon']:.6f}")
- print()
-
+ print_line(f"**Adres:** {locatie['adres']}")
+ print_line(f"**Coördinaten:** {locatie['lat']:.6f}, {locatie['lon']:.6f}")
+ print_line()
+
auto_iso = haal_isochroon(locatie['lon'], locatie['lat'], 'driving-car', 20)
fiets_iso = haal_isochroon(locatie['lon'], locatie['lat'], 'cycling-regular', 30)
- print_isochroon_sectie("Bereikbaarheid (Auto 20 min)", "🚗", auto_iso)
- print_isochroon_sectie("Bereikbaarheid (Fiets 30 min)", "🚲", fiets_iso)
+ print_isochroon_sectie("Bereikbaarheid (Auto 20 min)", "🚗", auto_iso, rapport_lines)
+ print_isochroon_sectie("Bereikbaarheid (Fiets 30 min)", "🚲", fiets_iso, rapport_lines)
- print("🏙️ **Voorzieningen dichtbij**")
+ print_line("🏙️ **Voorzieningen dichtbij**")
voorzieningen = analyse_voorzieningen(locatie['lon'], locatie['lat'], limit=5)
- print(format_voorzieningen_markdown("🛒 **Supermarkten**", voorzieningen['supermarkten']))
- print()
- print(format_voorzieningen_markdown("🏫 **Scholen**", voorzieningen['scholen']))
- print()
+ print_line(format_voorzieningen_markdown("🛒 **Supermarkten**", voorzieningen['supermarkten']))
+ print_line()
+ print_line(format_voorzieningen_markdown("🏫 **Scholen**", voorzieningen['scholen']))
+ print_line()
+ # Genereer interactieve HTML kaart en upload naar Obsidian
+ html_content = genereer_isochroon_html(
+ locatie['adres'], locatie['lat'], locatie['lon'], auto_iso, fiets_iso
+ )
+ html_path = schrijf_naar_obsidian(html_content, locatie['adres'], subpath="huizenjacht", ext=".html")
+ if html_path:
+ print_line(f"🗺️ **[Bekijk interactieve isochroon kaart]({html_path})**")
+ else:
+ print_line("⚠️ Kon interactieve kaart niet opslaan in Obsidian")
+
+ # Genereer screenshot en embed in markdown
+ b64_img, err = maak_isochroon_screenshot(html_content)
+ if b64_img:
+ print_line("")
+ print_line("**🗺️ Isochroon kaart (auto + fiets):**")
+ img_md = f""
+ print_line(img_md)
+ else:
+ print_line(f"⚠️ Kon geen screenshot maken: {err}")
+
+ # Sla ook ruwe geojson op (lokaal, niet in Obsidian)
output_file = f"/tmp/isochronen_{datetime.now().strftime('%Y%m%d_%H%M%S')}.geojson"
with open(output_file, 'w') as f:
json.dump({'auto_20_min': auto_iso, 'fiets_30_min': fiets_iso}, f)
- print(f"💾 Isochroondata opgeslagen: `{output_file}`")
- print()
- print("---")
- print("_Onderdelen: geocoding, isochronen, voorzieningen_")
+ print_line(f"💾 Ruwe isochroondata: `{output_file}`")
+ print_line()
+ print_line("---")
+ print_line("_Onderdelen: geocoding, isochronen, voorzieningen_")
+
+ # Schrijf rapport naar Obsidian
+ print_line()
+ md_path = schrijf_naar_obsidian('\n'.join(rapport_lines), locatie['adres'])
+ if md_path:
+ print_line(f"📝 **Opgeslagen in Obsidian:** `{md_path}`")
+ else:
+ print_line("⚠️ Obsidian upload mislukt")
if __name__ == '__main__':
adres = sys.argv[1] if len(sys.argv) > 1 else "Griendvelden 14 Best"