eac686ee1a
- Leaflet 1.9.4 (CSS + JS + 5 Marker-Images) lokal unter App/assets/vendor/leaflet/ statt via unpkg.com/Cloudflare - Neuer PHP-Endpunkt App/php/tile-proxy.php mit Disk-Cache, liefert OSM- und CARTO-Kacheln serverseitig aus; Browser kontaktiert keinen Drittanbieter mehr - Whitelist (osm, carto), Zoom 3-18, 30 Tage Browser-Cache, ETag + If-Modified-Since, sauberer User-Agent fuer OSM-Policy - Cache-Pfad: APP_ROOT/data/tiles lokal, via .env TILE_CACHE_DIR auf Produktions-Volume /var/www/html/geograsim/data/tiles - Heli (heli.php + heli-game.php), Logistik (logistik.php), Routes- Dev-Tool und admin-levels.html alle auf Proxy umgestellt - Datenschutzerklaerung: alter Google-AdSense-Passus (falsch, nicht im Einsatz) entfernt, neuer Abschnitt zur serverseitigen Tile-Auslieferung mit Rechtsgrundlage Art. 6 Abs. 1 lit. f DSGVO - .gitignore: App/data/tiles ausgeschlossen - .env.example: TILE_CACHE_DIR + OpenAI/ElevenLabs dokumentiert Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
122 lines
6.0 KiB
PHP
122 lines
6.0 KiB
PHP
<?php
|
|
/**
|
|
* Heli-Übersichtskarte — Waypoints aus der DB
|
|
*/
|
|
require_once __DIR__ . '/../php/config/app.php';
|
|
require_once __DIR__ . '/../php/config/db.php';
|
|
|
|
$db = getDB();
|
|
$waypoints = $db->query("SELECT wp_key, name, lat, lon, region, wp_type, info FROM geo_waypoints ORDER BY region, name")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Als JSON für JavaScript
|
|
$wpJson = json_encode($waypoints, JSON_UNESCAPED_UNICODE);
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>GeoGraSim — Helikopter-Übersichtskarte</title>
|
|
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96">
|
|
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
|
<link rel="stylesheet" href="<?= BASE_PATH ?>/assets/vendor/leaflet/leaflet.css">
|
|
<link rel="stylesheet" href="assets/fonts/inter.css">
|
|
<style>
|
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
body{font-family:'Inter',system-ui,sans-serif;overflow:hidden;height:100vh;background:#e8e4d8}
|
|
#map{width:100%;height:100vh}
|
|
.marker-circle{width:26px;height:26px;background:#fff;border:2.5px solid #1f4e5a;border-radius:50%;box-shadow:0 2px 6px rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;font-size:13px;line-height:1}
|
|
.marker-circle.capital{border-color:#e8833a;width:30px;height:30px;font-size:15px}
|
|
.marker-circle.peak{border-color:#5a8a5e;width:24px;height:24px;font-size:12px}
|
|
.marker-circle.base{border-color:#d4a017;width:30px;height:30px;font-size:16px}
|
|
.marker-circle.base-inactive{border-color:#888;width:22px;height:22px;font-size:11px;opacity:.7}
|
|
.wp-pill{font-family:'Inter',sans-serif;font-weight:700;font-size:11px;white-space:nowrap;padding:3px 9px;border-radius:5px;box-shadow:0 1px 4px rgba(0,0,0,.12);display:flex;align-items:center;height:20px}
|
|
.wp-pill.city{background:rgba(218,232,236,.94);color:#1f4e5a;border-bottom:2.5px solid #4a7c8a}
|
|
.wp-pill.capital{background:rgba(232,200,170,.92);color:#e8833a;border-bottom:2.5px solid #e8833a}
|
|
.wp-pill.peak{background:rgba(220,234,221,.94);color:#3a6b3e;border-bottom:2.5px solid #5a8a5e}
|
|
.wp-pill.base{background:rgba(255,255,255,.92);color:#9a7800;border-left:3px solid #d4a017;font-size:10px}
|
|
.wp-pill .pop{font-size:8px;font-weight:500;color:#8a8a8a;margin-left:4px}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="map"></div>
|
|
<script src="<?= BASE_PATH ?>/assets/vendor/leaflet/leaflet.js"></script>
|
|
<script>
|
|
// Waypoints direkt aus der DB (PHP-injiziert)
|
|
var DB_WAYPOINTS = <?= $wpJson ?>;
|
|
|
|
var map = L.map('map', {zoomControl:false, maxBoundsViscosity:1.0, minZoom:7, maxZoom:15})
|
|
.setView([47.4, 13.2], 8);
|
|
L.tileLayer('<?= BASE_PATH ?>/php/tile-proxy.php?z={z}&x={x}&y={y}&p=osm', {
|
|
attribution:'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>', maxZoom:18
|
|
}).addTo(map);
|
|
L.control.zoom({position:'topright'}).addTo(map);
|
|
L.control.scale({position:'bottomleft', metric:true, imperial:false, maxWidth:200}).addTo(map);
|
|
|
|
// Render alle Waypoints nach Typ
|
|
DB_WAYPOINTS.forEach(function(w) {
|
|
var lat = parseFloat(w.lat), lon = parseFloat(w.lon);
|
|
var icon, pillClass, pillHtml, popupHtml, size;
|
|
|
|
if (w.wp_type === 'base') {
|
|
size = 30;
|
|
icon = '<div class="marker-circle base">🚁</div>';
|
|
pillClass = 'wp-pill base';
|
|
pillHtml = w.name;
|
|
popupHtml = '<div style="font-family:Inter,sans-serif;text-align:center;padding:4px">'
|
|
+'<div style="font-size:2rem">🚁</div>'
|
|
+'<div style="font-size:14px;font-weight:800;color:#1f4e5a">'+w.name+'</div>'
|
|
+(w.info?'<div style="font-size:11px;color:#8a8a8a">'+w.info+'</div>':'')
|
|
+'</div>';
|
|
} else if (w.wp_type === 'city') {
|
|
size = 26;
|
|
icon = '<div class="marker-circle">🏙️</div>';
|
|
pillClass = 'wp-pill city';
|
|
pillHtml = w.name;
|
|
popupHtml = '<div style="font-family:Inter,sans-serif;text-align:center;padding:4px">'
|
|
+'<div style="font-size:16px;font-weight:900;color:#1f4e5a">'+w.name+'</div>'
|
|
+'<div style="font-size:10px;color:#8a8a8a">'+w.region+'</div>'
|
|
+(w.info?'<div style="font-size:11px;color:#4a4a4a;margin-top:4px">'+w.info+'</div>':'')
|
|
+'</div>';
|
|
} else if (w.wp_type === 'peak') {
|
|
size = 24;
|
|
icon = '<div class="marker-circle peak">🏔️</div>';
|
|
pillClass = 'wp-pill peak';
|
|
pillHtml = w.name + (w.info?' <span class="pop">'+w.info+'</span>':'');
|
|
popupHtml = '<div style="font-family:Inter,sans-serif;text-align:center">'
|
|
+'<div style="font-size:2rem">🏔️</div>'
|
|
+'<div style="font-size:15px;font-weight:900;color:#3a6b3e">'+w.name+'</div>'
|
|
+(w.info?'<div style="font-size:13px;color:#1f4e5a;font-weight:800">'+w.info+'</div>':'')
|
|
+'</div>';
|
|
} else if (w.wp_type === 'village') {
|
|
size = 20;
|
|
icon = '<div style="width:16px;height:16px;background:#fff;border:2px solid #8a8a8a;border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.2)"></div>';
|
|
pillClass = null; // Kein Pill für kleine Dörfer
|
|
popupHtml = '<div style="font-family:Inter,sans-serif"><strong>'+w.name+'</strong>'
|
|
+(w.info?'<br><span style="font-size:11px;color:#8a8a8a">'+w.info+'</span>':'')+'</div>';
|
|
} else {
|
|
size = 18;
|
|
icon = '<div style="width:14px;height:14px;background:#c85c4a;border:2px solid #fff;border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.2)"></div>';
|
|
pillClass = null;
|
|
popupHtml = '<div style="font-family:Inter,sans-serif"><strong>'+w.name+'</strong>'
|
|
+(w.info?'<br><span style="font-size:11px;color:#8a8a8a">'+w.info+'</span>':'')+'</div>';
|
|
}
|
|
|
|
// Marker
|
|
L.marker([lat, lon], {
|
|
icon: L.divIcon({className:'', html:icon, iconSize:[size,size], iconAnchor:[size/2,size/2]}),
|
|
zIndexOffset: w.wp_type==='base'?1000:w.wp_type==='city'?500:w.wp_type==='peak'?300:100
|
|
}).addTo(map).bindPopup(popupHtml, {maxWidth:260});
|
|
|
|
// Pill (nur für Basen, Städte, Gipfel)
|
|
if (pillClass) {
|
|
L.marker([lat, lon], {
|
|
icon: L.divIcon({className:pillClass, html:pillHtml, iconSize:[140,20], iconAnchor:[-14,10]}),
|
|
interactive:false, zIndexOffset: w.wp_type==='base'?900:w.wp_type==='city'?400:200
|
|
}).addTo(map);
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|