Tile-Server: Vektor-Basemap in PWA integrieren (protomaps-leaflet, Feature-Flag)
- ui.js Map.create: Basemap-Swap OSM-Raster→PMTiles-Vektorlayer hinter Flag 'by_vector_map' (?vectormap=1/0). Leaflet+markercluster+Marker unverändert, sauberer Raster-Fallback bei Fehler. Attribution Pflicht eingeblendet. - map-vector.js: protomaps-leaflet paintRules/labelRules für OpenMapTiles-Schema (Light+Dark), Labels per Canvas-Text → keine Glyphs nötig. Quelle /tiles/dach.pmtiles. - protomaps-leaflet 4.0.1 vendored. - Makefile: 'make tiles' (download→merge -H+time-filter dedup→planetiler) + 'make tiles-deploy' (atomarer Swap, ENV=prod für Produktion).
This commit is contained in:
parent
a561759034
commit
2b5afcf0ae
4 changed files with 205 additions and 6 deletions
|
|
@ -439,7 +439,6 @@ const UI = (() => {
|
|||
OSM_MAX_ZOOM: 19,
|
||||
|
||||
async create(containerId, options = {}) {
|
||||
await loadLeaflet();
|
||||
const {
|
||||
center = [51.1657, 10.4515],
|
||||
zoom = 6,
|
||||
|
|
@ -447,11 +446,33 @@ const UI = (() => {
|
|||
attributionControl = false,
|
||||
darkFilter = false,
|
||||
} = options;
|
||||
await loadLeaflet();
|
||||
const m = L.map(containerId, { zoomControl, attributionControl }).setView(center, zoom);
|
||||
const tiles = L.tileLayer(this.OSM_URL, { maxZoom: this.OSM_MAX_ZOOM }).addTo(m);
|
||||
if (darkFilter) {
|
||||
const isDark = document.documentElement.dataset.theme === 'dark';
|
||||
if (isDark) tiles.getContainer().style.filter = 'brightness(0.7) invert(1) contrast(0.9) hue-rotate(200deg)';
|
||||
|
||||
// Vektor-Basemap aus eigenen PMTiles (hinter Feature-Flag). Bei Fehler
|
||||
// (Tiles/Lib nicht da) sauberer Fallback auf den OSM-Raster — Marker etc.
|
||||
// bleiben in beiden Fällen identisch (reiner Basemap-Tausch).
|
||||
let usedVector = false;
|
||||
if (_vectorMapEnabled()) {
|
||||
try {
|
||||
await loadProtomaps();
|
||||
const isDark = document.documentElement.dataset.theme === 'dark';
|
||||
MapVector.basemapLayer({ dark: isDark }).addTo(m);
|
||||
if (!attributionControl) {
|
||||
L.control.attribution({ prefix: false }).addTo(m)
|
||||
.addAttribution('© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors');
|
||||
}
|
||||
usedVector = true;
|
||||
} catch (e) {
|
||||
console.warn('Vektor-Basemap nicht verfügbar — Fallback auf Raster:', e);
|
||||
}
|
||||
}
|
||||
if (!usedVector) {
|
||||
const tiles = L.tileLayer(this.OSM_URL, { maxZoom: this.OSM_MAX_ZOOM }).addTo(m);
|
||||
if (darkFilter) {
|
||||
const isDark = document.documentElement.dataset.theme === 'dark';
|
||||
if (isDark) tiles.getContainer().style.filter = 'brightness(0.7) invert(1) contrast(0.9) hue-rotate(200deg)';
|
||||
}
|
||||
}
|
||||
// Safety-Net: Container-Größe nach Layout neu vermessen. Verhindert
|
||||
// grau bleibende Bereiche wenn die Karte vor dem finalen Layout erstellt
|
||||
|
|
@ -813,6 +834,41 @@ const UI = (() => {
|
|||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// VEKTOR-BASEMAP (protomaps-leaflet + eigene PMTiles) — lazy laden
|
||||
// ----------------------------------------------------------
|
||||
let _protomapsPromise = null;
|
||||
function loadProtomaps() {
|
||||
if (_protomapsPromise) return _protomapsPromise;
|
||||
const v = '?v=' + (window.APP_VER || '');
|
||||
const loadSeq = (srcs) => srcs.reduce((p, src) => p.then(() => new Promise((res, rej) => {
|
||||
if ((src.includes('protomaps-leaflet') && window.protomapsL) ||
|
||||
(src.includes('map-vector') && window.MapVector)) return res();
|
||||
const s = document.createElement('script');
|
||||
s.src = src + v;
|
||||
s.onload = res; s.onerror = rej;
|
||||
document.head.appendChild(s);
|
||||
})), Promise.resolve());
|
||||
// map-vector.js hängt von protomapsL ab → strikt sequenziell laden.
|
||||
_protomapsPromise = loadSeq(['/js/vendor/protomaps-leaflet.js', '/js/map-vector.js'])
|
||||
.then(() => {
|
||||
if (window.protomapsL && window.MapVector) return;
|
||||
throw new Error('protomaps-leaflet/MapVector nicht geladen');
|
||||
});
|
||||
return _protomapsPromise;
|
||||
}
|
||||
|
||||
// Feature-Flag: localStorage 'by_vector_map'==='1'. ?vectormap=1/0 setzt ihn (Testing).
|
||||
function _vectorMapEnabled() {
|
||||
try {
|
||||
const u = new URLSearchParams(location.search);
|
||||
if (u.has('vectormap')) {
|
||||
localStorage.setItem('by_vector_map', u.get('vectormap') === '0' ? '0' : '1');
|
||||
}
|
||||
return localStorage.getItem('by_vector_map') === '1';
|
||||
} catch (e) { return false; }
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// LEAFLET MARKER FACTORY — erzeugt einen L.divIcon-Marker
|
||||
// Verwendung:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue