// Leaflet-kompatible MapLibre-Facade für die SEITENKARTEN (Giftköder, Verlorene, // Events, Gassi, Routen). Liefert Wrapper, die die von den Seiten genutzte Leaflet- // API nachbilden (setView/fitBounds/invalidateSize/addTo/bindPopup/openPopup/on/remove), // sodass die Seiten fast unverändert auf demselben GL-Style (MapGLStyle) laufen. // Koordinaten nach außen [lat,lon] (Leaflet-Konvention), intern MapLibre [lng,lat]. (function () { 'use strict'; function _ll(latlon) { return [latlon[1], latlon[0]]; } // [lat,lon] → [lng,lat] // ---- Map-Wrapper ---- function _wrapMap(map) { return { _gl: map, _isGL: true, setView: function (latlon, zoom) { map.jumpTo({ center: _ll(latlon), zoom: zoom }); return this; }, flyTo: function (latlon, zoom, opts) { map.flyTo({ center: _ll(latlon), zoom: zoom, duration: opts && opts.duration ? opts.duration * 1000 : 1000 }); return this; }, panTo: function (latlon) { map.panTo(_ll(latlon)); return this; }, fitBounds: function (b, opts) { var bb = _toBounds(b); if (bb) { var pad = 40; if (opts && opts.padding) pad = Array.isArray(opts.padding) ? opts.padding[0] : opts.padding; map.fitBounds(bb, { padding: pad, maxZoom: opts && opts.maxZoom, duration: 0 }); } return this; }, invalidateSize: function () { map.resize(); return this; }, removeLayer: function (layer) { if (layer && layer.remove) layer.remove(); return this; }, addLayer: function (layer) { if (layer && layer.addTo) layer.addTo(this); return this; }, hasLayer: function () { return true; }, remove: function () { try { map.remove(); } catch (e) {} }, on: function (ev, fn) { map.on(ev, fn); return this; }, off: function (ev, fn) { map.off(ev, fn); return this; }, getZoom: function () { return map.getZoom(); }, getCenter: function () { var c = map.getCenter(); return { lat: c.lat, lng: c.lng }; }, }; } // Bounds aus: Array von [lat,lon] | featureGroup-Wrapper (_coords) | Leaflet-Bounds. function _toBounds(b) { if (!b) return null; var coords = null; if (Array.isArray(b)) coords = b; else if (b._coords) coords = b._coords; else if (typeof b.getSouthWest === 'function') { var sw = b.getSouthWest(), ne = b.getNorthEast(); return new maplibregl.LngLatBounds([sw.lng, sw.lat], [ne.lng, ne.lat]); } if (!coords || !coords.length) return null; var bb = new maplibregl.LngLatBounds(); coords.forEach(function (c) { bb.extend(_ll(c)); }); return bb; } // ---- Marker-Wrapper (HTML-Marker; svgMarker + circleMarker) ---- function _wrapMarker(lat, lon, el, anchor) { var m = new maplibregl.Marker({ element: el, anchor: anchor || 'center' }).setLngLat([lon, lat]); var wrap = { _gl: m, _el: el, addTo: function (mapWrap) { m.addTo(mapWrap && mapWrap._gl ? mapWrap._gl : mapWrap); return this; }, bindPopup: function (html, opts) { m.setPopup(new maplibregl.Popup({ maxWidth: (opts && opts.maxWidth ? opts.maxWidth + 'px' : '260px'), closeButton: true, offset: 18 }).setHTML(html)); return this; }, openPopup: function () { var p = m.getPopup(); if (p && !p.isOpen()) m.togglePopup(); return this; }, closePopup: function () { var p = m.getPopup(); if (p && p.isOpen()) m.togglePopup(); return this; }, bindTooltip: function (t) { try { el.title = typeof t === 'string' ? t.replace(/<[^>]*>/g, '') : ''; } catch (e) {} return this; }, on: function (ev, fn) { if (ev === 'click') el.addEventListener('click', function (e) { e.stopPropagation(); fn(e); }); return this; }, setLatLng: function (latlon) { m.setLngLat([latlon[1], latlon[0]]); return this; }, setOpacity: function (o) { el.style.opacity = o; return this; }, remove: function () { try { m.remove(); } catch (e) {} return this; }, }; return wrap; } // Element aus HTML-String (für svgMarker mit custom HTML). function _elFromHtml(html, size, anchorY) { var wrap = document.createElement('div'); wrap.innerHTML = html; var el = wrap.firstElementChild || wrap; el.style.cursor = 'pointer'; return el; } window.MapGLMini = { createMap: function (container, opts) { opts = opts || {}; var el = typeof container === 'string' ? document.getElementById(container) : container; var center = opts.center || [51.1657, 10.4515]; var map = new maplibregl.Map({ container: el, style: MapGLStyle.build({ dark: !!opts.dark }), center: _ll(center), zoom: opts.zoom != null ? opts.zoom : 6, attributionControl: false, dragRotate: false, pitchWithRotate: false, maxZoom: 19, }); map.touchZoomRotate.disableRotation(); map.touchPitch.disable(); try { el.style.touchAction = 'none'; } catch (e) {} if (opts.zoomControl !== false) map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-left'); map.addControl(new maplibregl.AttributionControl({ compact: true, customAttribution: '© OpenStreetMap contributors', })); return _wrapMap(map); }, // svgMarker: custom HTML-Icon. opts: { size, anchorY } svgMarker: function (lat, lon, html, opts) { opts = opts || {}; var el = _elFromHtml(html); // anchorY: Pixel von oben zum Ankerpunkt (Leaflet iconAnchor). 'bottom' wenn anchorY≈size. var anchor = 'center'; if (opts.anchorY != null && opts.size) { anchor = opts.anchorY >= opts.size * 0.8 ? 'bottom' : 'center'; } return _wrapMarker(lat, lon, el, anchor); }, circleMarker: function (lat, lon, opts) { opts = opts || {}; var r = opts.radius || 8; var el = document.createElement('div'); el.style.cssText = 'width:' + (r * 2) + 'px;height:' + (r * 2) + 'px;border-radius:50%;background:' + (opts.fillColor || opts.color || '#3B82F6') + ';border:' + (opts.weight || 2) + 'px solid ' + (opts.color || '#fff') + ';opacity:' + (opts.fillOpacity != null ? opts.fillOpacity : 1) + ';box-shadow:0 1px 4px rgba(0,0,0,.35);cursor:pointer'; return _wrapMarker(lat, lon, el, 'center'); }, // featureGroup: nur als Bounds-Container (markers = Array von Wrappern mit _gl.getLngLat()). featureGroup: function (markers) { var coords = (markers || []).map(function (m) { var ll = m && m._gl && m._gl.getLngLat ? m._gl.getLngLat() : null; return ll ? [ll.lat, ll.lng] : null; }).filter(Boolean); return { _coords: coords, getBounds: function () { return { _coords: coords }; }, addTo: function () { return this; } }; }, }; })();