Files
geograsim/App/heli.html.bak-pre-christo-bulk
T
Adminator 1e51ef7def Nachtrag: alle bisher untracked Ordner + hängende Änderungen mit-committen
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/
- 12 code-workspace-Files
- STATUS-*.md
- viele M/D/R-Änderungen an bereits getrackten Files
- .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-08 02:27:02 +02:00

455 lines
34 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Helikopter-Navigation</title>
<link rel="icon" type="image/png" href="favicon-96x96.png">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.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}
/* Aktive Basen: groß, farbig */
.heli-active{font-size:32px;text-align:center;line-height:1;filter:drop-shadow(0 2px 4px rgba(0,0,0,.4))}
.heli-active.oeamtc{filter:drop-shadow(0 2px 4px rgba(0,0,0,.3)) brightness(1.3) saturate(3) hue-rotate(-30deg) sepia(.3)}
.heli-active.ara{filter:drop-shadow(0 2px 4px rgba(0,0,0,.3)) hue-rotate(320deg) saturate(2)}
.heli-active.police{filter:drop-shadow(0 2px 4px rgba(0,0,0,.3)) hue-rotate(180deg) saturate(1.5) brightness(1.1)}
.heli-active.bh{filter:drop-shadow(0 2px 4px rgba(0,0,0,.3)) hue-rotate(90deg) saturate(2)}
/* Inaktive Basen: klein, deutlich sichtbar aber entsättigt */
.heli-inactive{font-size:18px;text-align:center;line-height:1;filter:drop-shadow(0 1px 3px rgba(0,0,0,.3)) grayscale(.7) brightness(.7) contrast(1.2)}
/* Labels/Pills */
.heli-pill{font-family:'Inter',system-ui,sans-serif;font-weight:600;font-size:11px;white-space:nowrap;padding:3px 8px;border-radius:5px;background:rgba(255,255,255,.92);box-shadow:0 1px 4px rgba(0,0,0,.15);line-height:1.3;max-width:180px}
.heli-pill .pill-name{font-weight:800;display:block}
.heli-pill .pill-org{font-size:8px;opacity:.7;display:block}
.heli-pill.oeamtc{border-left:4px solid #d4a017;color:#7a5f00}
.heli-pill.ara{border-left:4px solid #cc3333;color:#882222}
.heli-pill.police{border-left:4px solid #2255aa;color:#1a3a77}
.heli-pill.bh{border-left:4px solid #228833;color:#145522}
.heli-pill-sm{font-family:'Inter',system-ui,sans-serif;font-weight:700;font-size:10px;white-space:nowrap;padding:2px 6px;border-radius:3px;background:rgba(255,255,255,.88);box-shadow:0 1px 3px rgba(0,0,0,.15);color:#555;border-left:3px solid #888}
/* Marker: weiße Kreise mit Rahmen */
.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.heli-m{border-color:#d4a017;width:30px;height:30px;font-size:16px}
.marker-circle.heli-m.inactive{width:22px;height:22px;font-size:11px;border-color:#888;opacity:.7}
/* Stadt-Pills */
.city-pill{font-family:'Inter',system-ui,sans-serif;font-weight:700;font-size:11px;white-space:nowrap;padding:4px 10px;border-radius:5px;background:rgba(218,232,236,.94);box-shadow:0 1px 4px rgba(0,0,0,.15);color:#1f4e5a;border-bottom:2.5px solid #4a7c8a;display:flex;align-items:center;height:22px}
.city-pill.capital{color:#e8833a;background:rgba(232,200,170,.92);border-bottom-color:#e8833a}
.city-pill .city-pop{font-size:8px;font-weight:500;color:#8a8a8a;margin-left:4px}
/* Berg-Pills */
.peak-pill{font-family:'Inter',system-ui,sans-serif;font-weight:700;font-size:10px;white-space:nowrap;padding:4px 9px;border-radius:5px;background:rgba(220,234,221,.94);box-shadow:0 1px 4px rgba(0,0,0,.12);color:#3a6b3e;border-bottom:2.5px solid #5a8a5e;display:flex;align-items:center;height:20px}
.peak-pill .peak-ele{font-size:8px;font-weight:500;color:#5a8a5e;margin-left:4px}
/* Quartett-Popup */
.heli-popup .leaflet-popup-content{margin:0;padding:0}
.heli-popup .leaflet-popup-content-wrapper{border-radius:12px;padding:0;overflow:hidden;box-shadow:0 4px 20px rgba(0,0,0,.15)}
.quartett{width:260px;font-family:'Inter',system-ui,sans-serif}
.quartett-img{height:140px;overflow:hidden;background:#f7f6f3;display:flex;align-items:center;justify-content:center}
.quartett-img img{width:100%;height:100%;object-fit:cover}
.quartett-body{padding:10px 12px 12px}
.quartett-name{font-size:13px;font-weight:800;color:#1f4e5a;margin-bottom:2px}
.quartett-org{font-size:10px;color:#8a8a8a;margin-bottom:8px}
.quartett-stats{display:grid;grid-template-columns:1fr 1fr;gap:4px}
.quartett-stat{background:#f7f6f3;border-radius:5px;padding:4px 6px}
.quartett-stat .qs-label{font-size:8px;color:#8a8a8a;font-weight:500}
.quartett-stat .qs-val{font-size:12px;font-weight:800;color:#1f4e5a}
.quartett-fact{margin-top:8px;font-size:10px;color:#4a4a4a;font-style:italic;line-height:1.4;border-top:1px solid #eee;padding-top:6px}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
var bounds = L.latLngBounds(L.latLng(46.3, 9.4), L.latLng(49.1, 17.2));
var map = L.map('map', {
zoomControl: false,
maxBounds: bounds.pad(0.05),
maxBoundsViscosity: 1.0,
minZoom: 7,
maxZoom: 15
}).setView([47.4, 13.2], 8);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
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);
// === HUBSCHRAUBER-TYPEN (Quartett-Daten) ===
var HELI_TYPES = {
'EC135': {name:'Eurocopter EC135', img:'sims/heli/assets/heli-ec135.png', engines:2, crew:'1 Pilot + 1 Notarzt + 1 Sanitäter', maxSpeed:254, range:635, ceiling:6096, weight:2910, seats:6, role:'Rettung & Notfall', fact:'Der EC135 ist der meistgenutzte Rettungshubschrauber in Europa.', wiki:'https://de.wikipedia.org/wiki/Eurocopter_EC_135'},
'H145': {name:'Airbus H145', img:'sims/heli/assets/heli-h145.png', engines:2, crew:'12 Piloten + Rettungsteam', maxSpeed:268, range:680, ceiling:5485, weight:3700, seats:8, role:'Rettung & Intensivtransport', fact:'Der H145 kann auch bei Nacht und schlechtem Wetter fliegen (IFR).', wiki:'https://de.wikipedia.org/wiki/Airbus_Helicopters_H145'},
'AW169': {name:'Leonardo AW169', img:'sims/heli/assets/heli-aw169.png', engines:2, crew:'12 Piloten + Besatzung', maxSpeed:280, range:820, ceiling:4572, weight:4600, seats:10, role:'Polizei & Überwachung', fact:'Die Flugpolizei nennt ihre Hubschrauber „Libelle".', wiki:'https://de.wikipedia.org/wiki/Leonardo_AW169'},
'AB212': {name:'Agusta-Bell AB212', img:'sims/heli/assets/heli-ab212.png', engines:2, crew:'2 Piloten + Besatzung', maxSpeed:240, range:450, ceiling:4630, weight:5070, seats:14, role:'Militär & Katastrophenhilfe', fact:'Das Bundesheer setzt den AB212 seit den 1980er-Jahren ein.', wiki:'https://de.wikipedia.org/wiki/Agusta-Bell_AB_212'},
};
// === ALLE HUBSCHRAUBER-BASEN ÖSTERREICH ===
var bases = [
// ÖAMTC Christophorus
{name:'Christophorus 1', code:'C1', org:'ÖAMTC', orgClass:'oeamtc', lat:47.2600, lon:11.3439, active:true, heli:'EC135', region:'Innsbruck / Nordtirol'},
{name:'Christophorus 2', code:'C2', org:'ÖAMTC', orgClass:'oeamtc', lat:48.4306, lon:15.6100, active:false, heli:'EC135', region:'Gneixendorf / Waldviertel'},
{name:'Christophorus 3', code:'C3', org:'ÖAMTC', orgClass:'oeamtc', lat:47.8419, lon:16.2331, active:false, heli:'EC135', region:'Wiener Neustadt'},
{name:'Christophorus 4', code:'C4', org:'ÖAMTC', orgClass:'oeamtc', lat:47.3801, lon:12.3580, active:true, heli:'H145', region:'Reith bei Kitzbühel'},
{name:'Christophorus 5', code:'C5', org:'ÖAMTC', orgClass:'oeamtc', lat:47.1597, lon:10.5933, active:true, heli:'H145', region:'Zams / Oberinntal'},
{name:'Christophorus 6', code:'C6', org:'ÖAMTC', orgClass:'oeamtc', lat:47.7950, lon:13.0044, active:false, heli:'H145', region:'Salzburg'},
{name:'Christophorus 7', code:'C7', org:'ÖAMTC', orgClass:'oeamtc', lat:46.8297, lon:12.7647, active:true, heli:'EC135', region:'Lienz / Osttirol'},
{name:'Christophorus 8', code:'C8', org:'ÖAMTC', orgClass:'oeamtc', lat:47.1828, lon:9.7072, active:true, heli:'H145', region:'Nenzing / Vorarlberg'},
{name:'Christophorus 9', code:'C9', org:'ÖAMTC', orgClass:'oeamtc', lat:48.1172, lon:16.5025, active:false, heli:'EC135', region:'Wien'},
{name:'Christophorus 10', code:'C10', org:'ÖAMTC', orgClass:'oeamtc', lat:48.2331, lon:14.1875, active:false, heli:'EC135', region:'Linz'},
{name:'Christophorus 11', code:'C11', org:'ÖAMTC', orgClass:'oeamtc', lat:46.6503, lon:14.3372, active:false, heli:'EC135', region:'Klagenfurt'},
{name:'Christophorus 12', code:'C12', org:'ÖAMTC', orgClass:'oeamtc', lat:47.0094, lon:15.4106, active:false, heli:'EC135', region:'Graz'},
{name:'Christophorus 14', code:'C14', org:'ÖAMTC', orgClass:'oeamtc', lat:47.4756, lon:14.0042, active:false, heli:'H145', region:'Niederöblarn'},
{name:'Christophorus 15', code:'C15', org:'ÖAMTC', orgClass:'oeamtc', lat:47.9483, lon:14.8894, active:false, heli:'EC135', region:'Ybbsitz'},
{name:'Christophorus 16', code:'C16', org:'ÖAMTC', orgClass:'oeamtc', lat:47.2886, lon:16.2069, active:false, heli:'EC135', region:'Oberwart'},
{name:'Christophorus 17', code:'C17', org:'ÖAMTC', orgClass:'oeamtc', lat:47.3500, lon:11.7000, active:false, heli:'EC135', region:'Vomp / Inntal'},
// ARA Flugrettung
{name:'Alpin 1 Reutte', code:'RK1', org:'ARA Flugrettung', orgClass:'ara', lat:47.4858, lon:10.7181, active:true, heli:'H145', region:'Reutte / Außerfern'},
{name:'Alpin 2 Fresach', code:'RK2', org:'ARA Flugrettung', orgClass:'ara', lat:46.7250, lon:13.6825, active:false, heli:'H145', region:'Fresach / Kärnten'},
{name:'Alpin 3 Ybbsitz', code:'RK3', org:'ARA Flugrettung', orgClass:'ara', lat:47.9500, lon:14.8900, active:false, heli:'EC135', region:'Ybbsitz / Mostviertel'},
{name:'Alpin 5 Salzburg', code:'RK5', org:'ARA Flugrettung', orgClass:'ara', lat:47.7933, lon:13.0100, active:false, heli:'H145', region:'Salzburg'},
{name:'Alpin 6 Linz', code:'RK6', org:'ARA Flugrettung', orgClass:'ara', lat:48.2333, lon:14.2000, active:false, heli:'EC135', region:'Linz'},
// Polizei/BMI Flugpolizei
{name:'Libelle Innsbruck', code:'LIB-I', org:'Flugpolizei BMI', orgClass:'police', lat:47.2583, lon:11.3556, active:true, heli:'AW169', region:'Innsbruck'},
{name:'Hohenems (Flugplatz)', code:'LIB-H', org:'Flugpolizei BMI', orgClass:'police', lat:47.38154, lon:9.69851, active:true, heli:'AW169', region:'Hohenems / Rheintal'},
{name:'Libelle Wien', code:'LIB-W', org:'Flugpolizei BMI', orgClass:'police', lat:48.1103, lon:16.5697, active:false, heli:'AW169', region:'Wien'},
{name:'Libelle Salzburg', code:'LIB-S', org:'Flugpolizei BMI', orgClass:'police', lat:47.7933, lon:13.0033, active:false, heli:'AW169', region:'Salzburg'},
{name:'Libelle Klagenfurt', code:'LIB-K', org:'Flugpolizei BMI', orgClass:'police', lat:46.6428, lon:14.3375, active:false, heli:'AW169', region:'Klagenfurt'},
{name:'Libelle Linz', code:'LIB-L', org:'Flugpolizei BMI', orgClass:'police', lat:48.2400, lon:14.1867, active:false, heli:'AW169', region:'Linz'},
{name:'Libelle Graz', code:'LIB-G', org:'Flugpolizei BMI', orgClass:'police', lat:47.0094, lon:15.4400, active:false, heli:'AW169', region:'Graz'},
// Bundesheer
{name:'Aigen im Ennstal', code:'BH-A', org:'Bundesheer', orgClass:'bh', lat:47.5236, lon:14.1350, active:false, heli:'AB212', region:'Aigen / Ennstal'},
{name:'Höttinger Au', code:'BH-I', org:'Bundesheer', orgClass:'bh', lat:47.2722, lon:11.3753, active:true, heli:'AB212', region:'Innsbruck'},
{name:'Langenlebarn', code:'BH-L', org:'Bundesheer', orgClass:'bh', lat:48.3239, lon:15.9836, active:false, heli:'AB212', region:'Langenlebarn / Tullnerfeld'},
// Krankenhaus-Landeplätze
{name:'LKH Innsbruck', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.2636, lon:11.3833, active:false},
{name:'LKH Feldkirch', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.2400, lon:9.5956, active:false},
{name:'AKH Wien', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:48.2206, lon:16.3453, active:false},
{name:'LKH Salzburg', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.7989, lon:13.0333, active:false},
{name:'LKH Graz', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.0808, lon:15.4678, active:false},
{name:'KH Kufstein', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.5836, lon:12.1647, active:false},
{name:'KH St. Johann', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.3528, lon:12.4247, active:false},
{name:'KH Zams', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.1575, lon:10.5917, active:false},
{name:'BKH Lienz', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:46.8300, lon:12.7700, active:false},
{name:'LKH Bregenz', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.4997, lon:9.7431, active:false},
];
function makeHeliPopup(b) {
var t = HELI_TYPES[b.heli];
if (!t) return '<strong>' + b.name + '</strong><br>' + b.org;
var bp = location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1);
return '<div class="quartett">'
+ '<div class="quartett-img"><img src="' + bp + t.img + '" alt="' + t.name + '"></div>'
+ '<div class="quartett-body">'
+ '<div class="quartett-name">' + b.code + ' — ' + b.name + '</div>'
+ '<div class="quartett-org">' + b.org + ' · ' + (b.region||'') + '</div>'
+ '<div class="quartett-stats">'
+ '<div class="quartett-stat"><div class="qs-label">Typ</div><div class="qs-val">' + t.name + '</div></div>'
+ '<div class="quartett-stat"><div class="qs-label">Max. Speed</div><div class="qs-val">' + t.maxSpeed + ' km/h</div></div>'
+ '<div class="quartett-stat"><div class="qs-label">Reichweite</div><div class="qs-val">' + t.range + ' km</div></div>'
+ '<div class="quartett-stat"><div class="qs-label">Gipfelhöhe</div><div class="qs-val">' + t.ceiling + ' m</div></div>'
+ '<div class="quartett-stat"><div class="qs-label">Triebwerke</div><div class="qs-val">' + t.engines + '</div></div>'
+ '<div class="quartett-stat"><div class="qs-label">Besatzung</div><div class="qs-val">' + t.crew + '</div></div>'
+ '</div>'
+ '<div class="quartett-fact">💡 ' + t.fact + '</div>'
+ (t.wiki ? '<div style="text-align:center;margin-top:6px"><a href="'+t.wiki+'" target="_blank" style="font-size:10px;color:#4a7c8a;text-decoration:none;font-weight:600">📖 Wikipedia →</a></div>' : '')
+ '</div></div>';
}
bases.forEach(function(b) {
var size = b.active ? 30 : 22;
var circleClass = b.active ? 'marker-circle heli-m' : 'marker-circle heli-m inactive';
L.marker([b.lat, b.lon], {
icon: L.divIcon({
className: '',
html: '<div class="' + circleClass + '">🚁</div>',
iconSize: [size, size],
iconAnchor: [size/2, size/2]
}),
title: b.name + ' (' + b.org + ')',
zIndexOffset: b.active ? 1000 : 0
}).addTo(map).bindPopup(makeHeliPopup(b), {maxWidth:280, className:'heli-popup'});
// Pill-Label
if (b.active) {
L.marker([b.lat, b.lon], {
icon: L.divIcon({
className: 'heli-pill ' + b.orgClass,
html: '<span class="pill-name">' + b.code + ' ' + b.name + '</span><span class="pill-org">' + b.org + '</span>',
iconSize: [160, 28],
iconAnchor: [-20, 14]
}),
interactive: false,
zIndexOffset: 900
}).addTo(map);
} else {
L.marker([b.lat, b.lon], {
icon: L.divIcon({
className: 'heli-pill-sm',
html: b.code,
iconSize: [40, 14],
iconAnchor: [-10, 7]
}),
interactive: false
}).addTo(map);
}
});
// === STÄDTE >30.000 EW + Bregenz ===
var cities = [
{name:'Wien', lat:48.2082, lon:16.3738, pop:1982097, capital:true, land:'Wien', fun:'Hat mehr Einwohner*innen als alle anderen Landeshauptstädte zusammen!', fact:'Wurde 5× zur lebenswertesten Stadt der Welt gewählt.', wiki:'https://de.wikipedia.org/wiki/Wien', img:'sims/heli/assets/cities/wien.png'},
{name:'Graz', lat:47.0707, lon:15.4395, pop:295424, capital:true, land:'Steiermark', fun:'Die Altstadt ist UNESCO-Weltkulturerbe.', fact:'Zweitgrößte Stadt Österreichs, Universitätsstadt mit über 60.000 Studierenden.', wiki:'https://de.wikipedia.org/wiki/Graz', img:'sims/heli/assets/cities/graz.png'},
{name:'Linz', lat:48.3069, lon:14.2858, pop:210118, capital:true, land:'Oberösterreich', fun:'War 2009 Europäische Kulturhauptstadt.', fact:'Wichtigster Industriestandort Österreichs (Stahl, Chemie).', wiki:'https://de.wikipedia.org/wiki/Linz', img:'sims/heli/assets/cities/linz.png'},
{name:'Salzburg', lat:47.8095, lon:13.0550, pop:157745, capital:true, land:'Salzburg', fun:'Geburtsstadt von Wolfgang Amadeus Mozart (1756).', fact:'Die Altstadt gehört zum UNESCO-Weltkulturerbe.', wiki:'https://de.wikipedia.org/wiki/Salzburg', img:'sims/heli/assets/cities/salzburg.png'},
{name:'Innsbruck', lat:47.2654, lon:11.3928, pop:130585, capital:true, land:'Tirol', fun:'Hat schon 3× Olympische Winterspiele ausgerichtet!', fact:'Liegt auf 574 m im Inntal, umgeben von Bergen bis 2.300 m.', wiki:'https://de.wikipedia.org/wiki/Innsbruck', img:'sims/heli/assets/cities/innsbruck.png'},
{name:'Klagenfurt', lat:46.6247, lon:14.3050, pop:103671, capital:true, land:'Kärnten', fun:'Am Wörthersee — einem der wärmsten Badeseen der Alpen.', fact:'Südlichste Landeshauptstadt Österreichs.', wiki:'https://de.wikipedia.org/wiki/Klagenfurt_am_Wörthersee', img:'sims/heli/assets/cities/klagenfurt.png'},
{name:'Villach', lat:46.6111, lon:13.8558, pop:65127, capital:false, land:'Kärnten', fun:'Hat einen eigenen Fasching — den Villacher Fasching!', fact:'Zweitgrößte Stadt Kärntens, wichtiger Verkehrsknotenpunkt Richtung Italien/Slowenien.', wiki:'https://de.wikipedia.org/wiki/Villach', img:'sims/heli/assets/cities/villach.png'},
{name:'Wels', lat:48.1575, lon:14.0289, pop:63539, capital:false, land:'Oberösterreich', fun:'Hier findet die größte Agrarmesse Österreichs statt.', fact:'Zweitgrößte Stadt Oberösterreichs.', wiki:'https://de.wikipedia.org/wiki/Wels_(Stadt)', img:'sims/heli/assets/cities/wels.png'},
{name:'St. Pölten', lat:48.2047, lon:15.6256, pop:57169, capital:true, land:'Niederösterreich', fun:'Jüngste Landeshauptstadt — erst seit 1986!', fact:'Vorher war Wien auch Hauptstadt von Niederösterreich.', wiki:'https://de.wikipedia.org/wiki/St._Pölten', img:'sims/heli/assets/cities/stpoelten.png'},
{name:'Dornbirn', lat:47.4136, lon:9.7424, pop:51222, capital:false, land:'Vorarlberg', fun:'Größte Stadt Vorarlbergs — nicht Bregenz!', fact:'Wichtiger Messestandort und Wirtschaftszentrum im Rheintal.', wiki:'https://de.wikipedia.org/wiki/Dornbirn', img:'sims/heli/assets/cities/dornbirn.png'},
{name:'Wiener Neustadt', lat:47.8133, lon:16.2467, pop:48519, capital:false, land:'Niederösterreich', fun:'Hier wurde die erste Militärakademie der Welt gegründet (1752).', fact:'Wichtiger Standort des Bundesheers.', wiki:'https://de.wikipedia.org/wiki/Wiener_Neustadt', img:'sims/heli/assets/cities/wiener_neustadt.png'},
{name:'Steyr', lat:48.0386, lon:14.4214, pop:38572, capital:false, land:'Oberösterreich', fun:'Die Altstadt sieht aus wie ein Märchen — mit Flüssen und mittelalterlichen Häusern.', fact:'Berühmt für Stahl und Waffen seit dem Mittelalter.', wiki:'https://de.wikipedia.org/wiki/Steyr', img:'sims/heli/assets/cities/steyr.png'},
{name:'Feldkirch', lat:47.2376, lon:9.5982, pop:35793, capital:false, land:'Vorarlberg', fun:'Hat eine echte mittelalterliche Burg — die Schattenburg.', fact:'Grenzstadt zur Schweiz und Liechtenstein.', wiki:'https://de.wikipedia.org/wiki/Feldkirch', img:'sims/heli/assets/cities/feldkirch.png'},
{name:'Bregenz', lat:47.5026, lon:9.7473, pop:29620, capital:true, land:'Vorarlberg', fun:'Die Seebühne auf dem Bodensee ist die größte Freilichtbühne der Welt!', fact:'Kleinste Landeshauptstadt Österreichs.', wiki:'https://de.wikipedia.org/wiki/Bregenz', img:'sims/heli/assets/cities/bregenz.png'},
{name:'Leoben', lat:47.3765, lon:15.0910, pop:32768, capital:false, land:'Steiermark', fun:'Hier wird an der Montanuniversität Bergbau studiert.', fact:'War jahrhundertelang das Zentrum des steirischen Erzbergbaus.', wiki:'https://de.wikipedia.org/wiki/Leoben', img:'sims/heli/assets/cities/leoben.png'},
{name:'Eisenstadt', lat:47.8456, lon:16.5189, pop:15073, capital:true, land:'Burgenland', fun:'Hier lebte der Komponist Joseph Haydn über 30 Jahre.', fact:'Kleinste Landeshauptstadt nach Einwohnern. Schloss Esterházy ist das Wahrzeichen.', wiki:'https://de.wikipedia.org/wiki/Eisenstadt', img:'sims/heli/assets/cities/eisenstadt.png'},
];
cities.forEach(function(c) {
var dotSize = c.capital ? 30 : 26;
var cls = '';
// Punkt
var capitalBadge = c.capital ? '<div style="display:inline-block;background:#e8833a;color:#fff;font-size:9px;font-weight:700;padding:2px 8px;border-radius:99px;margin-bottom:6px">★ Landeshauptstadt' + (c.land ? ' von ' + c.land : '') + '</div>' : (c.land ? '<div style="font-size:10px;color:#8a8a8a;margin-bottom:4px">' + c.land + '</div>' : '');
L.marker([c.lat, c.lon], {
icon: L.divIcon({
className: cls,
html: '<div class="marker-circle' + (c.capital ? ' capital' : '') + '">🏙️</div>',
iconSize: [dotSize, dotSize],
iconAnchor: [dotSize/2, dotSize/2]
}),
zIndexOffset: c.capital ? 500 : 100
}).addTo(map).bindPopup(
'<div style="font-family:Inter,sans-serif;width:260px;padding:0">'
+ (c.img ? '<div style="height:130px;overflow:hidden;border-radius:8px 8px 0 0;margin:-12px -12px 8px"><img src="'+c.img+'" style="width:100%;height:100%;object-fit:cover"></div>' : '')
+ '<div style="text-align:center">'
+ capitalBadge
+ '<div style="font-size:16px;font-weight:900;color:#1f4e5a;margin-bottom:2px">' + c.name + '</div>'
+ '<div style="font-size:22px;font-weight:900;color:#4a7c8a">' + c.pop.toLocaleString() + '</div>'
+ '<div style="font-size:9px;color:#8a8a8a;margin-bottom:8px">Einwohner*innen</div>'
+ '</div>'
+ (c.fun ? '<div style="font-size:11px;color:#2d5434;background:rgba(90,138,94,.08);border-radius:6px;padding:6px 8px;margin-bottom:6px">🎯 ' + c.fun + '</div>' : '')
+ (c.fact ? '<div style="font-size:11px;color:#4a4a4a;line-height:1.4;margin-bottom:6px">📚 ' + c.fact + '</div>' : '')
+ (c.wiki ? '<div style="text-align:center"><a href="'+c.wiki+'" target="_blank" style="font-size:10px;color:#4a7c8a;text-decoration:none;font-weight:600">📖 Wikipedia →</a></div>' : '')
+ '</div>'
, {maxWidth:260});
// Pill
var popStr = c.pop >= 1000000 ? (c.pop/1000000).toFixed(1) + ' Mio' : Math.round(c.pop/1000) + 'k';
var star = c.capital ? ' ★' : '';
L.marker([c.lat, c.lon], {
icon: L.divIcon({
className: 'city-pill' + (c.capital ? ' capital' : ''),
html: c.name + star + '<span class="city-pop">' + popStr + '</span>',
iconSize: [140, 22],
iconAnchor: [-16, 11]
}),
interactive: false,
zIndexOffset: c.capital ? 400 : 50
}).addTo(map);
});
// === BERGGIPFEL ===
var peaks = [
{name:'Großglockner', lat:47.0742, lon:12.6947, ele:3798, fact:'Höchster Berg Österreichs! Die berühmte Großglockner Hochalpenstraße führt bis auf 2.504 m.', wiki:'https://de.wikipedia.org/wiki/Großglockner'},
{name:'Wildspitze', lat:46.8853, lon:10.8672, ele:3768, fact:'Höchster Berg Tirols und zweithöchster Österreichs. Hat den größten Gletscher der Ostalpen.', wiki:'https://de.wikipedia.org/wiki/Wildspitze'},
{name:'Weißkugel', lat:46.8439, lon:10.7833, ele:3738, fact:'Dritthöchster Berg Österreichs, direkt an der Grenze zu Italien.', wiki:'https://de.wikipedia.org/wiki/Weißkugel'},
{name:'Großvenediger', lat:47.1092, lon:12.3464, ele:3657, fact:'Wird „Weltalte Majestät" genannt. Einer der schönsten Gletscherberge der Alpen.', wiki:'https://de.wikipedia.org/wiki/Großvenediger'},
{name:'Similaun', lat:46.7578, lon:10.8853, ele:3599, fact:'Hier wurde 1991 die Gletschermumie „Ötzi" gefunden — 5.300 Jahre alt!', wiki:'https://de.wikipedia.org/wiki/Similaun'},
{name:'Zuckerhütl', lat:46.9833, lon:11.1167, ele:3505, fact:'Höchster Berg der Stubaier Alpen. Beliebtes Ziel für Skitouren.', wiki:'https://de.wikipedia.org/wiki/Zuckerhütl'},
{name:'Piz Buin', lat:46.8444, lon:10.1186, ele:3312, fact:'Höchster Berg Vorarlbergs. Die Sonnencreme-Marke wurde nach ihm benannt!', wiki:'https://de.wikipedia.org/wiki/Piz_Buin'},
{name:'Zugspitze', lat:47.4211, lon:10.9856, ele:2962, fact:'Höchster Berg Deutschlands, direkt an der Grenze zu Österreich. Seilbahn fährt bis zum Gipfel.', wiki:'https://de.wikipedia.org/wiki/Zugspitze'},
{name:'Dachstein', lat:47.4753, lon:13.6061, ele:2995, fact:'Höchster Berg der Steiermark. Hat eine Eishöhle und den berühmten Skywalk.', wiki:'https://de.wikipedia.org/wiki/Hoher_Dachstein'},
{name:'Valluga', lat:47.1567, lon:10.2117, ele:2809, fact:'Hausberg von St. Anton am Arlberg, eines der berühmtesten Skigebiete der Welt.', wiki:'https://de.wikipedia.org/wiki/Valluga'},
{name:'Parseierspitze', lat:47.0825, lon:10.5086, ele:3036, fact:'Höchster Berg der Lechtaler Alpen. Nur für erfahrene Bergsteiger*innen!', wiki:'https://de.wikipedia.org/wiki/Parseierspitze'},
{name:'Brenner', lat:47.0422, lon:11.5067, ele:1370, fact:'Der wichtigste Alpenpass zwischen Österreich und Italien. Schon die Römer nutzten ihn.', wiki:'https://de.wikipedia.org/wiki/Brennerpass'},
{name:'Patscherkofel', lat:47.2083, lon:11.4611, ele:2246, fact:'Hausberg von Innsbruck. Hier fanden Olympische Skirennen statt.', wiki:'https://de.wikipedia.org/wiki/Patscherkofel'},
{name:'Arlberg', lat:47.1297, lon:10.2119, ele:1793, fact:'Grenze zwischen Tirol und Vorarlberg. Wiege des alpinen Skilaufs.', wiki:'https://de.wikipedia.org/wiki/Arlberg'},
];
peaks.forEach(function(p) {
L.marker([p.lat, p.lon], {
icon: L.divIcon({
className: '',
html: '<div class="marker-circle peak">🏔️</div>',
iconSize: [24, 24],
iconAnchor: [12, 12]
})
}).addTo(map).bindPopup(
'<div style="font-family:Inter,sans-serif;width:220px;padding:2px">'
+ '<div style="text-align:center">'
+ '<div style="font-size:2.5rem">🏔️</div>'
+ '<div style="font-size:15px;font-weight:900;color:#3a6b3e">' + p.name + '</div>'
+ '<div style="font-size:24px;font-weight:900;color:#1f4e5a">' + p.ele.toLocaleString() + ' m</div>'
+ '</div>'
+ (p.fact ? '<div style="font-size:11px;color:#4a4a4a;line-height:1.4;margin:8px 0 4px;background:rgba(90,138,94,.06);border-radius:6px;padding:6px 8px">🌿 ' + p.fact + '</div>' : '')
+ (p.wiki ? '<div style="text-align:center"><a href="'+p.wiki+'" target="_blank" style="font-size:10px;color:#4a7c8a;text-decoration:none;font-weight:600">📖 Wikipedia →</a></div>' : '')
+ '</div>'
, {maxWidth:240});
L.marker([p.lat, p.lon], {
icon: L.divIcon({
className: 'peak-pill',
html: p.name + '<span class="peak-ele">' + p.ele.toLocaleString() + ' m</span>',
iconSize: [140, 20],
iconAnchor: [-14, 10]
}),
interactive: false
}).addTo(map);
});
// === DB-SYNC: Koordinaten aus der Datenbank laden und Marker aktualisieren ===
(async function() {
try {
var bp = location.pathname.replace(/\/[^\/]*$/, '');
var resp = await fetch(bp + '/api/waypoints');
var dbWaypoints = await resp.json();
if (!Array.isArray(dbWaypoints)) return;
// Index nach wp_key
var dbMap = {};
dbWaypoints.forEach(function(w) { dbMap[w.wp_key] = {lat: parseFloat(w.lat), lon: parseFloat(w.lon), name: w.name}; });
// Alle Leaflet-Marker durchgehen und Positionen updaten
map.eachLayer(function(layer) {
if (layer.getLatLng && layer.options && layer.options.title) {
// Versuche den Marker anhand des Titels zu identifizieren
// (Heli-Basen haben title gesetzt)
}
});
// Einfacher: Seite neu rendern wenn DB-Daten signifikant abweichen
// Für jetzt: Konsolenwarnung bei Abweichungen
var fixes = 0;
bases.forEach(function(b) {
var key = b.name.toLowerCase().replace(/[^a-z0-9]/g,'');
// Versuche verschiedene Key-Matches
for (var dbKey in dbMap) {
if (dbMap[dbKey].name === b.name || dbMap[dbKey].name.indexOf(b.code) !== -1) {
var dLat = Math.abs(b.lat - dbMap[dbKey].lat);
var dLon = Math.abs(b.lon - dbMap[dbKey].lon);
if (dLat > 0.001 || dLon > 0.001) {
console.warn('DB-Korrektur: ' + b.name + ' (' + b.lat + ',' + b.lon + ') → (' + dbMap[dbKey].lat + ',' + dbMap[dbKey].lon + ')');
fixes++;
}
break;
}
}
});
// Marker-Positionen korrigieren: alle Layer entfernen und neu setzen
if (Object.keys(dbMap).length > 0) {
// Alle bestehenden Marker entfernen
var layersToRemove = [];
map.eachLayer(function(layer) {
if (layer.getLatLng && !(layer instanceof L.TileLayer)) {
layersToRemove.push(layer);
}
});
layersToRemove.forEach(function(l) { map.removeLayer(l); });
// Basen neu setzen mit DB-Koordinaten
bases.forEach(function(b) {
// Finde passenden DB-Eintrag
for (var dbKey in dbMap) {
if (dbMap[dbKey].name.indexOf(b.code) !== -1 || dbMap[dbKey].name === b.name) {
b.lat = dbMap[dbKey].lat;
b.lon = dbMap[dbKey].lon;
break;
}
}
var size = b.active ? 30 : 22;
var circleClass = b.active ? 'marker-circle heli-m' : 'marker-circle heli-m inactive';
L.marker([b.lat, b.lon], {
icon: L.divIcon({className:'', html:'<div class="'+circleClass+'">🚁</div>', iconSize:[size,size], iconAnchor:[size/2,size/2]}),
title: b.name+' ('+b.org+')', zIndexOffset: b.active ? 1000 : 0
}).addTo(map).bindPopup(makeHeliPopup(b), {maxWidth:280, className:'heli-popup'});
if (b.active) {
L.marker([b.lat, b.lon], {
icon: L.divIcon({className:'heli-pill '+b.orgClass, html:'<span class="pill-name">'+b.code+' '+b.name+'</span><span class="pill-org">'+b.org+'</span>', iconSize:[160,28], iconAnchor:[-20,14]}),
interactive:false, zIndexOffset:900
}).addTo(map);
}
});
// Städte neu setzen
cities.forEach(function(c) {
var dbKey = c.name.toLowerCase().replace(/[^a-zäöüß]/g,'');
for (var k in dbMap) {
if (dbMap[k].name.toLowerCase().replace(/[^a-zäöüß]/g,'') === dbKey) {
c.lat = dbMap[k].lat; c.lon = dbMap[k].lon; break;
}
}
var dotSize = c.capital ? 30 : 26;
var capitalBadge = c.capital ? '<div style="display:inline-block;background:#e8833a;color:#fff;font-size:9px;font-weight:700;padding:2px 8px;border-radius:99px;margin-bottom:6px">★ Landeshauptstadt'+(c.land?' von '+c.land:'')+'</div>' : (c.land?'<div style="font-size:10px;color:#8a8a8a;margin-bottom:4px">'+c.land+'</div>':'');
L.marker([c.lat, c.lon], {
icon: L.divIcon({className:'', html:'<div class="marker-circle'+(c.capital?' capital':'')+'">🏙️</div>', iconSize:[dotSize,dotSize], iconAnchor:[dotSize/2,dotSize/2]}),
zIndexOffset: c.capital ? 500 : 100
}).addTo(map).bindPopup(
'<div style="font-family:Inter,sans-serif;width:260px;padding:0">'
+(c.img?'<div style="height:130px;overflow:hidden;border-radius:8px 8px 0 0;margin:-12px -12px 8px"><img src="'+c.img+'" style="width:100%;height:100%;object-fit:cover"></div>':'')
+'<div style="text-align:center">'+capitalBadge
+'<div style="font-size:16px;font-weight:900;color:#1f4e5a;margin-bottom:2px">'+c.name+'</div>'
+'<div style="font-size:22px;font-weight:900;color:#4a7c8a">'+c.pop.toLocaleString()+'</div>'
+'<div style="font-size:9px;color:#8a8a8a;margin-bottom:8px">Einwohner*innen</div></div>'
+(c.fun?'<div style="font-size:11px;color:#2d5434;background:rgba(90,138,94,.08);border-radius:6px;padding:6px 8px;margin-bottom:6px">🎯 '+c.fun+'</div>':'')
+(c.fact?'<div style="font-size:11px;color:#4a4a4a;line-height:1.4;margin-bottom:6px">📚 '+c.fact+'</div>':'')
+(c.wiki?'<div style="text-align:center"><a href="'+c.wiki+'" target="_blank" style="font-size:10px;color:#4a7c8a;text-decoration:none;font-weight:600">📖 Wikipedia →</a></div>':'')
+'</div>', {maxWidth:260});
var star = c.capital ? ' ★' : '';
var popStr = c.pop >= 1000000 ? (c.pop/1000000).toFixed(1)+' Mio' : Math.round(c.pop/1000)+'k';
L.marker([c.lat, c.lon], {
icon: L.divIcon({className:'city-pill'+(c.capital?' capital':''), html:c.name+star+'<span class="city-pop">'+popStr+'</span>', iconSize:[140,22], iconAnchor:[-16,11]}),
interactive:false, zIndexOffset: c.capital ? 400 : 50
}).addTo(map);
});
// Berge neu setzen
peaks.forEach(function(p) {
L.marker([p.lat, p.lon], {
icon: L.divIcon({className:'', html:'<div class="marker-circle peak">🏔️</div>', iconSize:[24,24], iconAnchor:[12,12]})
}).addTo(map).bindPopup(
'<div style="font-family:Inter,sans-serif;width:220px;padding:2px"><div style="text-align:center">'
+'<div style="font-size:2.5rem">🏔️</div><div style="font-size:15px;font-weight:900;color:#3a6b3e">'+p.name+'</div>'
+'<div style="font-size:24px;font-weight:900;color:#1f4e5a">'+p.ele.toLocaleString()+' m</div></div>'
+(p.fact?'<div style="font-size:11px;color:#4a4a4a;line-height:1.4;margin:8px 0 4px;background:rgba(90,138,94,.06);border-radius:6px;padding:6px 8px">🌿 '+p.fact+'</div>':'')
+(p.wiki?'<div style="text-align:center"><a href="'+p.wiki+'" target="_blank" style="font-size:10px;color:#4a7c8a;text-decoration:none;font-weight:600">📖 Wikipedia →</a></div>':'')
+'</div>', {maxWidth:240});
L.marker([p.lat, p.lon], {
icon: L.divIcon({className:'peak-pill', html:p.name+'<span class="peak-ele">'+p.ele.toLocaleString()+' m</span>', iconSize:[140,20], iconAnchor:[-14,10]}),
interactive:false
}).addTo(map);
});
console.log('Karte mit DB-Koordinaten aktualisiert (' + Object.keys(dbMap).length + ' Waypoints)');
}
} catch(e) { console.warn('DB-Sync fehlgeschlagen:', e); }
})();
</script>
</body>
</html>