Routen-Vorschau: echtes Karten-PNG (Basemap+Route) statt nackter SVG-Form
In der Routenliste fehlte der geografische Kontext — man sah nur die Routen- form auf grünem Grund, nicht WO sie liegt oder wo sie entlangführt. Lösung: UI.map.snapshot() rendert pro Track ein PNG aus EINEM geteilten Offscreen-GL-Kontext (gleicher Style wie die echte Karte: Straßen, Orte, Wald, Gewässer), zeichnet Route + Start/Ziel-Marker ein und cached das Ergebnis. So bekommt jede Karte ihren Kontext, ohne bei vielen Listen- einträgen das WebGL-Kontextlimit (iOS ~8) zu sprengen. - ui.js: Offscreen-Singleton + serielle Render-Queue + Cache (_glSnapshot) - routes.js: _buildMiniMap zeigt sofort SVG, upgradet dann aufs PNG - GL aus → null → SVG-Platzhalter bleibt (Produktion/Flag aus unverändert)
This commit is contained in:
parent
a0d16ba800
commit
1defeec537
7 changed files with 115 additions and 18 deletions
|
|
@ -543,6 +543,14 @@ const UI = (() => {
|
|||
await loadProtomaps();
|
||||
return MapVector.basemapLayer(opts);
|
||||
},
|
||||
|
||||
// Rendert für einen Track (Array {lat,lon}) ein PNG-Vorschaubild MIT Basemap
|
||||
// (gleicher GL-Style wie die echte Karte) und liefert eine data-URL.
|
||||
// EIN einziger Offscreen-GL-Kontext, serielle Verarbeitung, Cache pro key —
|
||||
// so bekommt jede Routenkarte ihren geografischen Kontext, ohne das WebGL-
|
||||
// Kontextlimit zu sprengen (Problem bei N Live-Mini-Karten auf iOS).
|
||||
// Liefert null wenn GL aus ist (Aufrufer nutzt dann seinen SVG-Fallback).
|
||||
snapshot(track, opts = {}) { return _glSnapshot(track, opts); },
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
|
@ -926,6 +934,85 @@ const UI = (() => {
|
|||
return _maplibreUIPromise;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// TRACK-VORSCHAU-SNAPSHOT — ein Offscreen-GL-Kontext rendert PNGs (Basemap+Route)
|
||||
// ----------------------------------------------------------
|
||||
let _snapMap = null, _snapReady = null, _snapChain = Promise.resolve();
|
||||
const _snapCache = new Map(); // key → data-URL
|
||||
const _EMPTY_FC = { type: 'FeatureCollection', features: [] };
|
||||
|
||||
function _ensureSnapMap() {
|
||||
if (_snapReady) return _snapReady;
|
||||
_snapReady = loadMapLibreUI().then(() => new Promise((resolve, reject) => {
|
||||
const el = document.createElement('div');
|
||||
// Aspekt wie .rk-card-preview (360×140); MapLibre rendert in devicePixelRatio → scharf.
|
||||
el.style.cssText = 'position:fixed;left:-10000px;top:0;width:360px;height:140px;pointer-events:none;visibility:hidden;';
|
||||
document.body.appendChild(el);
|
||||
const isDark = document.documentElement.dataset.theme === 'dark';
|
||||
const m = new maplibregl.Map({
|
||||
container: el, style: MapGLStyle.build({ dark: isDark }),
|
||||
center: [10.4515, 51.1657], zoom: 6,
|
||||
interactive: false, attributionControl: false,
|
||||
preserveDrawingBuffer: true, fadeDuration: 0,
|
||||
});
|
||||
m.on('error', () => {}); // einzelne Tile-Fehler nicht eskalieren
|
||||
m.once('load', () => {
|
||||
m.addSource('snap-line', { type: 'geojson', data: _EMPTY_FC });
|
||||
m.addSource('snap-pts', { type: 'geojson', data: _EMPTY_FC });
|
||||
m.addLayer({ id: 'snap-line', type: 'line', source: 'snap-line',
|
||||
layout: { 'line-cap': 'round', 'line-join': 'round' },
|
||||
paint: { 'line-color': '#C4843A', 'line-width': 4, 'line-opacity': 0.95 } });
|
||||
m.addLayer({ id: 'snap-pts', type: 'circle', source: 'snap-pts',
|
||||
paint: { 'circle-radius': 6, 'circle-color': ['get', 'color'],
|
||||
'circle-stroke-color': '#fff', 'circle-stroke-width': 2 } });
|
||||
_snapMap = m;
|
||||
resolve(m);
|
||||
});
|
||||
setTimeout(() => { if (!_snapMap) reject(new Error('snap-map load timeout')); }, 8000);
|
||||
}));
|
||||
return _snapReady;
|
||||
}
|
||||
|
||||
function _renderSnap(track, key) {
|
||||
return _ensureSnapMap().then(m => new Promise(resolve => {
|
||||
const line = track.map(p => [p.lon, p.lat]);
|
||||
m.getSource('snap-line').setData({ type: 'Feature', properties: {},
|
||||
geometry: { type: 'LineString', coordinates: line } });
|
||||
const a = track[0], b = track[track.length - 1];
|
||||
m.getSource('snap-pts').setData({ type: 'FeatureCollection', features: [
|
||||
{ type: 'Feature', properties: { color: '#22C55E' }, geometry: { type: 'Point', coordinates: [a.lon, a.lat] } },
|
||||
{ type: 'Feature', properties: { color: '#EF4444' }, geometry: { type: 'Point', coordinates: [b.lon, b.lat] } },
|
||||
] });
|
||||
const bounds = line.reduce((bb, c) => bb.extend(c), new maplibregl.LngLatBounds(line[0], line[0]));
|
||||
try { m.fitBounds(bounds, { padding: 22, duration: 0, maxZoom: 16 }); } catch (e) {}
|
||||
let done = false;
|
||||
const finish = () => {
|
||||
if (done) return; done = true;
|
||||
m.off('idle', finish);
|
||||
requestAnimationFrame(() => {
|
||||
let url = null;
|
||||
try { url = m.getCanvas().toDataURL('image/png'); } catch (e) {}
|
||||
if (url) _snapCache.set(key, url);
|
||||
resolve(url);
|
||||
});
|
||||
};
|
||||
m.on('idle', finish);
|
||||
setTimeout(finish, 4000); // Fallback falls Tiles hängen
|
||||
}));
|
||||
}
|
||||
|
||||
function _glSnapshot(track, opts = {}) {
|
||||
if (!_uiUseGL()) return Promise.resolve(null); // GL aus → SVG-Fallback beim Aufrufer
|
||||
if (!track || track.length < 2) return Promise.resolve(null);
|
||||
const key = opts.key || ('t' + track.length + ',' + track[0].lat + ',' + track[0].lon + ',' +
|
||||
track[track.length - 1].lat + ',' + track[track.length - 1].lon);
|
||||
if (_snapCache.has(key)) return Promise.resolve(_snapCache.get(key));
|
||||
// Serielle Verarbeitung am gemeinsamen Offscreen-Kontext.
|
||||
const run = _snapChain.then(() => _renderSnap(track, key)).catch(() => null);
|
||||
_snapChain = run.catch(() => {});
|
||||
return run;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// VEKTOR-BASEMAP (protomaps-leaflet + eigene PMTiles) — lazy laden [DEAKTIVIERT]
|
||||
// ----------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue