Seitenkarten auf MapLibre GL (Facade) — Runde 1: Giftköder + Verlorene

- map-gl-mini.js: Leaflet-kompatible MapLibre-Facade (createMap/svgMarker/circleMarker/
  featureGroup-Wrapper mit setView/fitBounds/invalidateSize/addTo/bindPopup/openPopup/on/remove)
- ui.js: UI.map.create/svgMarker/leafletMarker branchen auf GL (by_map_gl, Staging-Default),
  + UI.map.circleMarker/featureGroup, loadMapLibreUI
- poison.js/lost.js: window.L-Guards entfernt, L.circleMarker→UI.map.circleMarker
This commit is contained in:
rene 2026-06-05 12:33:01 +02:00
parent 9c4b999331
commit 5844f1ef51
9 changed files with 239 additions and 25 deletions

View file

@ -446,6 +446,20 @@ const UI = (() => {
attributionControl = false,
darkFilter = false,
} = options;
// MapLibre-GL-Seitenkarte (gleicher Style wie die Hauptkarte) — hinter by_map_gl-Flag.
if (_uiUseGL()) {
try {
await loadMapLibreUI();
_uiGL = true;
const isDark = document.documentElement.dataset.theme === 'dark';
return MapGLMini.createMap(containerId, { center, zoom, zoomControl, dark: isDark });
} catch (e) {
console.warn('GL-Seitenkarte nicht verfügbar — Fallback Leaflet:', e);
}
}
_uiGL = false;
await loadLeaflet();
const m = L.map(containerId, { zoomControl, attributionControl }).setView(center, zoom);
@ -483,6 +497,7 @@ const UI = (() => {
// SVG-Marker mit eigenem HTML (z.B. mit Pulse-Animation, Rotation, etc.)
svgMarker(lat, lon, html, { size = 32, anchorY = null, className = '' } = {}) {
if (_uiGL && window.MapGLMini) return MapGLMini.svgMarker(lat, lon, html, { size, anchorY });
const icon = L.divIcon({
className,
html,
@ -492,6 +507,18 @@ const UI = (() => {
return L.marker([lat, lon], { icon });
},
// Engine-neutral: Kreis-Marker (Leaflet L.circleMarker bzw. GL-HTML-Punkt).
circleMarker(lat, lon, opts = {}) {
if (_uiGL && window.MapGLMini) return MapGLMini.circleMarker(lat, lon, opts);
return L.circleMarker([lat, lon], opts);
},
// Engine-neutral: FeatureGroup (nur als Bounds-Container für fitBounds genutzt).
featureGroup(markers = []) {
if (_uiGL && window.MapGLMini) return MapGLMini.featureGroup(markers);
return L.featureGroup(markers);
},
// Feature-Flag-Status der Vektor-Basemap (für Karten, die ihren Basemap-Layer
// selbst verwalten, z.B. pages/map.js).
vectorEnabled() { return _vectorMapEnabled(); },
@ -846,7 +873,47 @@ const UI = (() => {
}
// ----------------------------------------------------------
// VEKTOR-BASEMAP (protomaps-leaflet + eigene PMTiles) — lazy laden
// MapLibre-GL für Seitenkarten (UI.map) — lazy laden + Facade
// ----------------------------------------------------------
let _uiGL = false; // ist die aktuell erstellte UI-Karte GL?
let _maplibreUIPromise = null;
// Gleiche Logik wie pages/map.js _useGL: Staging-Default AN, Prod AUS, by_map_gl überschreibt.
function _uiUseGL() {
try {
const flag = localStorage.getItem('by_map_gl');
if (flag === '1') return true;
if (flag === '0') return false;
return /(^|\.)staging\.banyaro\.app$/.test(location.hostname);
} catch (e) { return false; }
}
function loadMapLibreUI() {
if (_maplibreUIPromise) return _maplibreUIPromise;
const v = '?v=' + (window.APP_VER || '');
if (!document.querySelector('link[href*="maplibre-gl.css"]')) {
const l = document.createElement('link');
l.rel = 'stylesheet'; l.href = '/js/vendor/maplibre-gl.css';
document.head.appendChild(l);
}
const seq = (srcs) => srcs.reduce((p, src) => p.then(() => new Promise((res, rej) => {
if ((src.includes('maplibre-gl.js') && window.maplibregl) ||
(src.includes('pmtiles.js') && window.pmtiles) ||
(src.includes('map-gl-style') && window.MapGLStyle) ||
(src.includes('map-gl-mini') && window.MapGLMini)) return res();
const s = document.createElement('script');
s.src = src + v; s.onload = res; s.onerror = rej;
document.head.appendChild(s);
})), Promise.resolve());
_maplibreUIPromise = seq(['/js/vendor/maplibre-gl.js', '/js/vendor/pmtiles.js', '/js/map-gl-style.js', '/js/map-gl-mini.js']).then(() => {
if (!(window.maplibregl && window.pmtiles && window.MapGLStyle && window.MapGLMini)) throw new Error('MapLibre (UI) nicht geladen');
try { const proto = new pmtiles.Protocol(); maplibregl.addProtocol('pmtiles', proto.tile); } catch (e) { /* evtl. schon registriert */ }
});
return _maplibreUIPromise;
}
// ----------------------------------------------------------
// VEKTOR-BASEMAP (protomaps-leaflet + eigene PMTiles) — lazy laden [DEAKTIVIERT]
// ----------------------------------------------------------
let _protomapsPromise = null;
function loadProtomaps() {
@ -905,9 +972,10 @@ const UI = (() => {
// ----------------------------------------------------------
function leafletMarker({ lat, lon, color = 'var(--c-primary)', icon = '', size = 32, label = '' } = {}) {
const inner = label || icon;
const html = `<div style="background:${color};color:#fff;font-size:${Math.round(size * 0.45)}px;font-weight:700;width:${size}px;height:${size}px;border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 5px rgba(0,0,0,0.3);border:2px solid rgba(255,255,255,0.8)">${inner}</div>`;
if (_uiGL && window.MapGLMini) return MapGLMini.svgMarker(lat, lon, html, { size, anchorY: size / 2 });
const divIcon = L.divIcon({
className: '',
html: `<div style="background:${color};color:#fff;font-size:${Math.round(size * 0.45)}px;font-weight:700;width:${size}px;height:${size}px;border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 5px rgba(0,0,0,0.3);border:2px solid rgba(255,255,255,0.8)">${inner}</div>`,
className: '', html,
iconSize: [size, size],
iconAnchor: [size / 2, size / 2],
});