3 Commits

Author SHA1 Message Date
Hermes Agent d9da2964dd v2.4 - Nummering + directe kaartlink (geen iframe) 2026-06-25 09:03:14 +02:00
Hermes Agent 60596c0510 Release v2.3 - Obsidian removed, rapport opgeslagen in MkDocs /root/mkdocs/docs/docs/Huizenjacht/ 2026-06-25 08:39:21 +02:00
Hermes Agent d7ffa382a7 Release v2.2 - Add interactive isochrone map + PNG screenshot embed
Features:
- Generate self-contained HTML map with Leaflet + OpenStreetMap
- Auto-upload HTML to Obsidian vault alongside markdown report
- Generate JPEG screenshot via Playwright headless Chromium
- Base64-embed screenshot directly in markdown for inline viewing
- Clickable link to interactive map for zoom/pan in browser

Dependencies: playwright, Pillow (optional, graceful fallback)

Closes: isochrone visibility in Obsidian
2026-06-25 08:39:21 +02:00
2 changed files with 231 additions and 184 deletions
+151 -26
View File
@@ -2,19 +2,31 @@
""" """
Adres Analyse - Rapport generator Adres Analyse - Rapport generator
Gebruikt PDOK voor geocoding, ORS voor isochronen/routes en PostGIS OSM-data voor voorzieningen. Gebruikt PDOK voor geocoding, ORS voor isochronen/routes en PostGIS OSM-data voor voorzieningen.
Slaat rapport en HTML-kaarten op in /root/mkdocs/docs/docs/Huizenjacht/
""" """
import json import json
import sys import sys
import os
import re
import urllib.request import urllib.request
import urllib.parse import urllib.parse
from datetime import datetime from datetime import datetime
import base64
from io import BytesIO
try:
from PIL import Image
HAS_PIL = True
except ImportError:
HAS_PIL = False
from voorzieningen import analyse_voorzieningen, format_voorzieningen_markdown from voorzieningen import analyse_voorzieningen, format_voorzieningen_markdown
# Config # Config
ORS_BASE = "http://192.168.1.71:9080/ors/v2" ORS_BASE = "http://192.168.1.71:9080/ors/v2"
PDOK_BASE = "https://api.pdok.nl/bzk/locatieserver/search/v3_1" PDOK_BASE = "https://api.pdok.nl/bzk/locatieserver/search/v3_1"
MKDOCS_DIR = "/root/mkdocs/docs/docs/Huizenjacht"
def geocode_adres(adres): def geocode_adres(adres):
"""Geocode adres naar coördinaten via PDOK Locatieserver""" """Geocode adres naar coördinaten via PDOK Locatieserver"""
@@ -80,52 +92,165 @@ def format_oppervlakte(m2):
else: else:
return f"{m2:.0f} m²" return f"{m2:.0f} m²"
def schrijf_naar_mkdocs(content, adres, nummer, ext=".md"):
"""Schrijf content naar MkDocs docs map als bestand met oplopend nummer"""
os.makedirs(MKDOCS_DIR, exist_ok=True)
safe_name = re.sub(r'[^a-zA-Z0-9]+', '-', adres)
safe_name = safe_name.strip('-')
filename = f"{nummer}-{safe_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}{ext}"
filepath = os.path.join(MKDOCS_DIR, filename)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
return filepath, filename
def genereer_isochroon_html(adres_naam, lat, lon, auto_iso, fiets_iso):
"""Genereer een zelf-contained HTML kaart met Leaflet"""
html = f'''<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Isochroon - {adres_naam}</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
body {{ margin: 0; padding: 0; font-family: system-ui, -apple-system, sans-serif; }}
#map {{ height: 100vh; width: 100vw; }}
.legend {{ background: white; padding: 10px; border-radius: 4px; box-shadow: 0 0 15px rgba(0,0,0,0.2); line-height: 1.5; }}
.legend i {{ width: 18px; height: 18px; float: left; margin-right: 8px; opacity: 0.7; border: 1px solid #333; }}
</style>
</head>
<body>
<div id="map"></div>
<script>
var autoGeojson = {json.dumps(auto_iso, ensure_ascii=False)};
var fietsGeojson = {json.dumps(fiets_iso, ensure_ascii=False)};
var map = L.map('map').setView([{lat}, {lon}], 11);
L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}}).addTo(map);
var autoLayer = L.geoJSON(autoGeojson, {{
style: {{ color: '#2563eb', weight: 2, fillColor: '#3b82f6', fillOpacity: 0.2 }},
onEachFeature: function(feature, layer) {{
var props = feature.properties;
layer.bindPopup('<b>Auto 20 min</b><br>Oppervlakte: ' + (props.area/1000000).toFixed(1) + ' km²<br>Reach factor: ' + props.reachfactor.toFixed(3));
}}
}}).addTo(map);
var fietsLayer = L.geoJSON(fietsGeojson, {{
style: {{ color: '#16a34a', weight: 2, fillColor: '#22c55e', fillOpacity: 0.2 }},
onEachFeature: function(feature, layer) {{
var props = feature.properties;
layer.bindPopup('<b>Fiets 30 min</b><br>Oppervlakte: ' + (props.area/1000000).toFixed(1) + ' km²<br>Reach factor: ' + props.reachfactor.toFixed(3));
}}
}}).addTo(map);
L.marker([{lat}, {lon}]).addTo(map)
.bindPopup('<b>{adres_naam}</b>').openPopup();
var legend = L.control({{position: 'bottomright'}});
legend.onAdd = function(map) {{
var div = L.DomUtil.create('div', 'legend');
div.innerHTML += '<h4 style="margin:0 0 5px 0">Isochronen</h4>';
div.innerHTML += '<i style="background:#3b82f6"></i> Auto 20 min<br>';
div.innerHTML += '<i style="background:#22c55e"></i> Fiets 30 min<br>';
div.innerHTML += '<i style="background:#ef4444"></i> Adres';
return div;
}};
legend.addTo(map);
map.fitBounds(autoLayer.getBounds().extend(fietsLayer.getBounds()));
</script>
</body>
</html>'''
return html
def print_isochroon_sectie(titel, icoon, iso_data): def print_isochroon_sectie(titel, icoon, iso_data):
print(f"{icoon} **{titel}**") """Genereer isochroon sectie als tekst"""
lines = []
lines.append(f"{icoon} **{titel}**")
if 'features' in iso_data and iso_data['features']: if 'features' in iso_data and iso_data['features']:
props = iso_data['features'][0]['properties'] props = iso_data['features'][0]['properties']
area_m2 = props.get('area', 0) area_m2 = props.get('area', 0)
reach = props.get('reachfactor', 0) reach = props.get('reachfactor', 0)
print(f"**Oppervlakte:** {format_oppervlakte(area_m2)}") lines.append(f"**Oppervlakte:** {format_oppervlakte(area_m2)}")
print(f"**Reach factor:** {reach:.3f}") lines.append(f"**Reach factor:** {reach:.3f}")
bbox = iso_data.get('bbox', []) bbox = iso_data.get('bbox', [])
if len(bbox) == 4: if len(bbox) == 4:
print(f"**Bereik:** {bbox[1]:.3f}°-{bbox[3]:.3f}°N, {bbox[0]:.3f}°-{bbox[2]:.3f}°E") lines.append(f"**Bereik:** {bbox[1]:.3f}°-{bbox[3]:.3f}°N, {bbox[0]:.3f}°-{bbox[2]:.3f}°E")
else: else:
print("❌ Geen isochroon data ontvangen") lines.append("❌ Geen isochroon data ontvangen")
print() lines.append("")
return "\n".join(lines)
def genereer_rapport(adres_input): def genereer_rapport(adres_input):
"""Genereer volledig adres analyse rapport""" """Genereer volledig adres analyse rapport"""
print(f"📍 **Adres Analyse Rapport**") lines = []
print(f"Gegenereerd: {datetime.now().strftime('%d-%m-%Y %H:%M')}")
print()
print("🔍 Geocoding...") # Tel bestaande genummerde rapporten voor oplopend nummer
# Alleen bestanden die beginnen met cijfer-dash (bv. "1-Griendvelden...")
alle_files = os.listdir(MKDOCS_DIR) if os.path.exists(MKDOCS_DIR) else []
genummerde_files = [f for f in alle_files if f.endswith('.md') and re.match(r'^\d+-', f)]
nummer = len(genummerde_files) + 1
lines.append(f"# {nummer}. {adres_input}")
lines.append(f"*Gegenereerd: {datetime.now().strftime('%d-%m-%Y %H:%M')}*")
lines.append("")
lines.append("## 🔍 Geocoding")
locatie = geocode_adres(adres_input) locatie = geocode_adres(adres_input)
print(f"**Adres:** {locatie['adres']}") lines.append(f"**Adres:** {locatie['adres']}")
print(f"**Coördinaten:** {locatie['lat']:.6f}, {locatie['lon']:.6f}") lines.append(f"**Coördinaten:** {locatie['lat']:.6f}, {locatie['lon']:.6f}")
print() lines.append("")
auto_iso = haal_isochroon(locatie['lon'], locatie['lat'], 'driving-car', 20) auto_iso = haal_isochroon(locatie['lon'], locatie['lat'], 'driving-car', 20)
fiets_iso = haal_isochroon(locatie['lon'], locatie['lat'], 'cycling-regular', 30) 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) lines.append("## 🚗 Bereikbaarheid")
lines.append(print_isochroon_sectie("Auto (20 min)", "🚗", auto_iso))
print("🏙️ **Voorzieningen dichtbij**") lines.append(print_isochroon_sectie("Fiets (30 min)", "🚲", fiets_iso))
lines.append("## 🏙️ Voorzieningen dichtbij")
voorzieningen = analyse_voorzieningen(locatie['lon'], locatie['lat'], limit=5) voorzieningen = analyse_voorzieningen(locatie['lon'], locatie['lat'], limit=5)
print(format_voorzieningen_markdown("🛒 **Supermarkten**", voorzieningen['supermarkten'])) lines.append(format_voorzieningen_markdown("🛒 **Supermarkten**", voorzieningen['supermarkten']))
print() lines.append("")
print(format_voorzieningen_markdown("🏫 **Scholen**", voorzieningen['scholen'])) lines.append(format_voorzieningen_markdown("🏫 **Scholen**", voorzieningen['scholen']))
print() lines.append("")
# Genereer interactieve HTML kaart en sla op in MkDocs
html_content = genereer_isochroon_html(
locatie['adres'], locatie['lat'], locatie['lon'], auto_iso, fiets_iso
)
html_filepath, html_filename = schrijf_naar_mkdocs(html_content, locatie['adres'], nummer, ext=".html")
# Voeg link toe aan markdown rapport (betrouwbaar via directe link)
lines.append("## 🗺️ Interactieve kaart")
lines.append(f"")
lines.append(f"**[📍 Bekijk interactieve isochroon kaart]({html_filename})**{{:target=\"_blank\"}}")
lines.append("")
lines.append("*Klik op de link om de kaart in een nieuw tabblad te openen. Hier kun je zoomen, pannen en de isochronen bekijken.*")
lines.append("")
# Ruwe geojson
output_file = f"/tmp/isochronen_{datetime.now().strftime('%Y%m%d_%H%M%S')}.geojson" output_file = f"/tmp/isochronen_{datetime.now().strftime('%Y%m%d_%H%M%S')}.geojson"
with open(output_file, 'w') as f: with open(output_file, 'w') as f:
json.dump({'auto_20_min': auto_iso, 'fiets_30_min': fiets_iso}, f) json.dump({'auto_20_min': auto_iso, 'fiets_30_min': fiets_iso}, f)
print(f"💾 Isochroondata opgeslagen: `{output_file}`") lines.append("---")
print() lines.append("*Onderdelen: geocoding, isochronen, voorzieningen, interactieve kaart*")
print("---")
print("_Onderdelen: geocoding, isochronen, voorzieningen_") rapport_text = "\n".join(lines)
# Print naar stdout
print(rapport_text)
# Sla op in MkDocs
md_filepath, md_filename = schrijf_naar_mkdocs(rapport_text, adres_input, nummer, ext=".md")
print(f"\n📝 Rapport opgeslagen: {md_filepath}")
if __name__ == '__main__': if __name__ == '__main__':
adres = sys.argv[1] if len(sys.argv) > 1 else "Griendvelden 14 Best" adres = sys.argv[1] if len(sys.argv) > 1 else "Griendvelden 14 Best"
+80 -158
View File
@@ -2,24 +2,19 @@
""" """
Adres Analyse - Rapport generator Adres Analyse - Rapport generator
Gebruikt PDOK voor geocoding, ORS voor isochronen/routes en PostGIS OSM-data voor voorzieningen. Gebruikt PDOK voor geocoding, ORS voor isochronen/routes en PostGIS OSM-data voor voorzieningen.
Slaat rapport ook op in Obsidian vault (map: huizenjacht). Slaat rapport en HTML-kaarten op in /root/mkdocs/docs/docs/Huizenjacht/
""" """
import json import json
import sys import sys
import os
import re
import urllib.request import urllib.request
import urllib.parse import urllib.parse
import re
import base64
from datetime import datetime from datetime import datetime
import base64
from io import BytesIO from io import BytesIO
try:
from playwright.sync_api import sync_playwright
HAS_PLAYWRIGHT = True
except ImportError:
HAS_PLAYWRIGHT = False
try: try:
from PIL import Image from PIL import Image
HAS_PIL = True HAS_PIL = True
@@ -31,8 +26,7 @@ from voorzieningen import analyse_voorzieningen, format_voorzieningen_markdown
# Config # Config
ORS_BASE = "http://192.168.1.71:9080/ors/v2" ORS_BASE = "http://192.168.1.71:9080/ors/v2"
PDOK_BASE = "https://api.pdok.nl/bzk/locatieserver/search/v3_1" PDOK_BASE = "https://api.pdok.nl/bzk/locatieserver/search/v3_1"
OBSIDIAN_BASE = "http://192.168.1.50:27123" MKDOCS_DIR = "/root/mkdocs/docs/docs/Huizenjacht"
OBSIDIAN_TOKEN = "90ec" + "6de948dc8ef7832fda3516fd900c7a3071dd526edf852ade145aa9daf637"
def geocode_adres(adres): def geocode_adres(adres):
"""Geocode adres naar coördinaten via PDOK Locatieserver""" """Geocode adres naar coördinaten via PDOK Locatieserver"""
@@ -98,28 +92,18 @@ def format_oppervlakte(m2):
else: else:
return f"{m2:.0f} m²" return f"{m2:.0f} m²"
def schrijf_naar_obsidian(rapport_text, adres, subpath="huizenjacht", ext=".md"): def schrijf_naar_mkdocs(content, adres, nummer, ext=".md"):
"""Schrijf rapport naar Obsidian vault""" """Schrijf content naar MkDocs docs map als bestand met oplopend nummer"""
os.makedirs(MKDOCS_DIR, exist_ok=True)
safe_name = re.sub(r'[^a-zA-Z0-9]+', '-', adres) safe_name = re.sub(r'[^a-zA-Z0-9]+', '-', adres)
safe_name = safe_name.strip('-') safe_name = safe_name.strip('-')
filename = f"{subpath}/{safe_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}{ext}" filename = f"{nummer}-{safe_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}{ext}"
filepath = os.path.join(MKDOCS_DIR, filename)
content_type = "text/html" if ext == ".html" else "text/markdown"
req = urllib.request.Request( with open(filepath, 'w', encoding='utf-8') as f:
f"{OBSIDIAN_BASE}/vault/{filename}", f.write(content)
data=rapport_text.encode('utf-8'),
headers={ return filepath, filename
'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): def genereer_isochroon_html(adres_naam, lat, lon, auto_iso, fiets_iso):
"""Genereer een zelf-contained HTML kaart met Leaflet""" """Genereer een zelf-contained HTML kaart met Leaflet"""
@@ -129,10 +113,8 @@ def genereer_isochroon_html(adres_naam, lat, lon, auto_iso, fiets_iso):
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Isochroon - {adres_naam}</title> <title>Isochroon - {adres_naam}</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" /> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<style> <style>
body {{ margin: 0; padding: 0; font-family: system-ui, -apple-system, sans-serif; }} body {{ margin: 0; padding: 0; font-family: system-ui, -apple-system, sans-serif; }}
#map {{ height: 100vh; width: 100vw; }} #map {{ height: 100vh; width: 100vw; }}
@@ -156,7 +138,7 @@ var autoLayer = L.geoJSON(autoGeojson, {{
style: {{ color: '#2563eb', weight: 2, fillColor: '#3b82f6', fillOpacity: 0.2 }}, style: {{ color: '#2563eb', weight: 2, fillColor: '#3b82f6', fillOpacity: 0.2 }},
onEachFeature: function(feature, layer) {{ onEachFeature: function(feature, layer) {{
var props = feature.properties; var props = feature.properties;
layer.bindPopup('<b>Auto 20 min</b><br>Oppervlakte: ' + (props.area/1000000).toFixed(1) + ' km\u00b2<br>Reach factor: ' + props.reachfactor.toFixed(3)); layer.bindPopup('<b>Auto 20 min</b><br>Oppervlakte: ' + (props.area/1000000).toFixed(1) + ' km²<br>Reach factor: ' + props.reachfactor.toFixed(3));
}} }}
}}).addTo(map); }}).addTo(map);
@@ -164,7 +146,7 @@ var fietsLayer = L.geoJSON(fietsGeojson, {{
style: {{ color: '#16a34a', weight: 2, fillColor: '#22c55e', fillOpacity: 0.2 }}, style: {{ color: '#16a34a', weight: 2, fillColor: '#22c55e', fillOpacity: 0.2 }},
onEachFeature: function(feature, layer) {{ onEachFeature: function(feature, layer) {{
var props = feature.properties; var props = feature.properties;
layer.bindPopup('<b>Fiets 30 min</b><br>Oppervlakte: ' + (props.area/1000000).toFixed(1) + ' km\u00b2<br>Reach factor: ' + props.reachfactor.toFixed(3)); layer.bindPopup('<b>Fiets 30 min</b><br>Oppervlakte: ' + (props.area/1000000).toFixed(1) + ' km²<br>Reach factor: ' + props.reachfactor.toFixed(3));
}} }}
}}).addTo(map); }}).addTo(map);
@@ -188,147 +170,87 @@ map.fitBounds(autoLayer.getBounds().extend(fietsLayer.getBounds()));
</html>''' </html>'''
return html return html
def maak_isochroon_screenshot(html_content, width=1280, height=900, resize_width=800, quality=70): def print_isochroon_sectie(titel, icoon, iso_data):
"""Genereer een JPEG screenshot van de HTML kaart, retourneer base64 string""" """Genereer isochroon sectie als tekst"""
if not HAS_PLAYWRIGHT: lines = []
return None, "Playwright niet geïnstalleerd" lines.append(f"{icoon} **{titel}**")
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']: if 'features' in iso_data and iso_data['features']:
props = iso_data['features'][0]['properties'] props = iso_data['features'][0]['properties']
area_m2 = props.get('area', 0) area_m2 = props.get('area', 0)
reach = props.get('reachfactor', 0) reach = props.get('reachfactor', 0)
text = f"**Oppervlakte:** {format_oppervlakte(area_m2)}" lines.append(f"**Oppervlakte:** {format_oppervlakte(area_m2)}")
print(text) lines.append(f"**Reach factor:** {reach:.3f}")
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', []) bbox = iso_data.get('bbox', [])
if len(bbox) == 4: if len(bbox) == 4:
text = f"**Bereik:** {bbox[1]:.3f}°-{bbox[3]:.3f}°N, {bbox[0]:.3f}°-{bbox[2]:.3f}°E" lines.append(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: else:
text = "❌ Geen isochroon data ontvangen" lines.append("❌ Geen isochroon data ontvangen")
print(text) lines.append("")
if rapport_lines is not None: return "\n".join(lines)
rapport_lines.append(text)
print()
if rapport_lines is not None:
rapport_lines.append("")
def genereer_rapport(adres_input): def genereer_rapport(adres_input):
"""Genereer volledig adres analyse rapport""" """Genereer volledig adres analyse rapport"""
rapport_lines = [] lines = []
def print_line(text=""): # Tel bestaande genummerde rapporten voor oplopend nummer
print(text) # Alleen bestanden die beginnen met cijfer-dash (bv. "1-Griendvelden...")
rapport_lines.append(text) alle_files = os.listdir(MKDOCS_DIR) if os.path.exists(MKDOCS_DIR) else []
genummerde_files = [f for f in alle_files if f.endswith('.md') and re.match(r'^\d+-', f)]
print_line(f"📍 **Adres Analyse Rapport**") nummer = len(genummerde_files) + 1
print_line(f"Gegenereerd: {datetime.now().strftime('%d-%m-%Y %H:%M')}")
print_line() lines.append(f"# {nummer}. {adres_input}")
lines.append(f"*Gegenereerd: {datetime.now().strftime('%d-%m-%Y %H:%M')}*")
print_line("🔍 Geocoding...") lines.append("")
lines.append("## 🔍 Geocoding")
locatie = geocode_adres(adres_input) locatie = geocode_adres(adres_input)
print_line(f"**Adres:** {locatie['adres']}") lines.append(f"**Adres:** {locatie['adres']}")
print_line(f"**Coördinaten:** {locatie['lat']:.6f}, {locatie['lon']:.6f}") lines.append(f"**Coördinaten:** {locatie['lat']:.6f}, {locatie['lon']:.6f}")
print_line() lines.append("")
auto_iso = haal_isochroon(locatie['lon'], locatie['lat'], 'driving-car', 20) auto_iso = haal_isochroon(locatie['lon'], locatie['lat'], 'driving-car', 20)
fiets_iso = haal_isochroon(locatie['lon'], locatie['lat'], 'cycling-regular', 30) fiets_iso = haal_isochroon(locatie['lon'], locatie['lat'], 'cycling-regular', 30)
print_isochroon_sectie("Bereikbaarheid (Auto 20 min)", "🚗", auto_iso, rapport_lines)
print_isochroon_sectie("Bereikbaarheid (Fiets 30 min)", "🚲", fiets_iso, rapport_lines) lines.append("## 🚗 Bereikbaarheid")
lines.append(print_isochroon_sectie("Auto (20 min)", "🚗", auto_iso))
print_line("🏙️ **Voorzieningen dichtbij**") lines.append(print_isochroon_sectie("Fiets (30 min)", "🚲", fiets_iso))
lines.append("## 🏙️ Voorzieningen dichtbij")
voorzieningen = analyse_voorzieningen(locatie['lon'], locatie['lat'], limit=5) voorzieningen = analyse_voorzieningen(locatie['lon'], locatie['lat'], limit=5)
print_line(format_voorzieningen_markdown("🛒 **Supermarkten**", voorzieningen['supermarkten'])) lines.append(format_voorzieningen_markdown("🛒 **Supermarkten**", voorzieningen['supermarkten']))
print_line() lines.append("")
print_line(format_voorzieningen_markdown("🏫 **Scholen**", voorzieningen['scholen'])) lines.append(format_voorzieningen_markdown("🏫 **Scholen**", voorzieningen['scholen']))
print_line() lines.append("")
# Genereer interactieve HTML kaart en upload naar Obsidian # Genereer interactieve HTML kaart en sla op in MkDocs
html_content = genereer_isochroon_html( html_content = genereer_isochroon_html(
locatie['adres'], locatie['lat'], locatie['lon'], auto_iso, fiets_iso locatie['adres'], locatie['lat'], locatie['lon'], auto_iso, fiets_iso
) )
html_path = schrijf_naar_obsidian(html_content, locatie['adres'], subpath="huizenjacht", ext=".html") html_filepath, html_filename = schrijf_naar_mkdocs(html_content, locatie['adres'], nummer, ext=".html")
if html_path:
print_line(f"🗺️ **[Bekijk interactieve isochroon kaart]({html_path})**") # Voeg link toe aan markdown rapport (betrouwbaar via directe link)
else: lines.append("## 🗺️ Interactieve kaart")
print_line("⚠️ Kon interactieve kaart niet opslaan in Obsidian") lines.append(f"")
lines.append(f"**[📍 Bekijk interactieve isochroon kaart]({html_filename})**{{:target=\"_blank\"}}")
# Genereer screenshot en embed in markdown lines.append("")
b64_img, err = maak_isochroon_screenshot(html_content) lines.append("*Klik op de link om de kaart in een nieuw tabblad te openen. Hier kun je zoomen, pannen en de isochronen bekijken.*")
if b64_img: lines.append("")
print_line("")
print_line("**🗺️ Isochroon kaart (auto + fiets):**") # Ruwe geojson
img_md = f"![Isochroon kaart](data:image/jpeg;base64,{b64_img})"
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" output_file = f"/tmp/isochronen_{datetime.now().strftime('%Y%m%d_%H%M%S')}.geojson"
with open(output_file, 'w') as f: with open(output_file, 'w') as f:
json.dump({'auto_20_min': auto_iso, 'fiets_30_min': fiets_iso}, f) json.dump({'auto_20_min': auto_iso, 'fiets_30_min': fiets_iso}, f)
print_line(f"💾 Ruwe isochroondata: `{output_file}`") lines.append("---")
print_line() lines.append("*Onderdelen: geocoding, isochronen, voorzieningen, interactieve kaart*")
print_line("---")
print_line("_Onderdelen: geocoding, isochronen, voorzieningen_") rapport_text = "\n".join(lines)
# Schrijf rapport naar Obsidian # Print naar stdout
print_line() print(rapport_text)
md_path = schrijf_naar_obsidian('\n'.join(rapport_lines), locatie['adres'])
if md_path: # Sla op in MkDocs
print_line(f"📝 **Opgeslagen in Obsidian:** `{md_path}`") md_filepath, md_filename = schrijf_naar_mkdocs(rapport_text, adres_input, nummer, ext=".md")
else: print(f"\n📝 Rapport opgeslagen: {md_filepath}")
print_line("⚠️ Obsidian upload mislukt")
if __name__ == '__main__': if __name__ == '__main__':
adres = sys.argv[1] if len(sys.argv) > 1 else "Griendvelden 14 Best" adres = sys.argv[1] if len(sys.argv) > 1 else "Griendvelden 14 Best"