fix: switchMapStyle loads style as JSON with absolute tile URLs

Fixes trails/tracks not rendering after theme switch.
The issue was that map.setStyle(url) caused MapLibre to resolve
relative tile paths without /enduro/ prefix, resulting in 404s.
This commit is contained in:
2026-05-17 11:03:51 +03:00
parent fdc9836690
commit f561c3bd41

View File

@@ -88,6 +88,7 @@ function switchMapStyle() {
if (!map) return;
const dark = isDarkTheme();
const basePath = window.location.pathname.replace(/\/[^/]*$/, '') || '';
const tileBase = window.location.origin + basePath;
const styleUrl = dark ? basePath + '/style-dark.json' : basePath + '/style.json';
// Save current position before style change
@@ -96,19 +97,22 @@ function switchMapStyle() {
const bearing = map.getBearing();
const pitch = map.getPitch();
fetch(styleUrl, { method: 'HEAD' }).then(r => {
if (r.ok) {
map.setStyle(styleUrl);
// Restore position and overlays after style loads
map.once('idle', () => {
map.jumpTo({ center, zoom, bearing, pitch });
rebuildMapOverlays();
});
} else {
console.log('Map style not available:', styleUrl);
fetch(styleUrl).then(r => {
if (r.ok) return r.json();
throw new Error('Style not available');
}).then(style => {
// Fix tile URLs to absolute (same as initMap)
if (style.sources && style.sources['trails-tiles'] && style.sources['trails-tiles'].tiles) {
style.sources['trails-tiles'].tiles = [`${tileBase}/api/tiles/{z}/{x}/{y}.mvt`];
}
map.setStyle(style);
// Restore position and overlays after style loads
map.once('idle', () => {
map.jumpTo({ center, zoom, bearing, pitch });
rebuildMapOverlays();
});
}).catch(() => {
// Network error, don't switch
// Network error or style not available, don't switch
});
}
@@ -1436,52 +1440,9 @@ async function initMap() {
zoomEl.textContent = 'z' + zoom;
}
// ─── Scale bar & Zoom level ───────────────────────────────────────────────
function updateScaleBar() {
const zoom = map.getZoom();
const lat = map.getCenter().lat;
// Метров на пиксель при текущем зуме и широте
const metersPerPixel = 156543.03392 * Math.cos(lat * Math.PI / 180) / Math.pow(2, zoom);
// Целевая ширина линейки ~100px
const targetWidth = 100;
const meters = metersPerPixel * targetWidth;
let label, width;
if (meters >= 1000) {
const km = Math.round(meters / 1000);
label = km + ' км';
width = km * 1000 / metersPerPixel;
} else {
const m = Math.round(meters / 50) * 50 || 50;
label = m + ' м';
width = m / metersPerPixel;
}
const scaleLine = document.getElementById('scale-line');
const scaleLabel = document.getElementById('scale-label');
if (scaleLine) scaleLine.style.width = Math.round(width) + 'px';
if (scaleLabel) scaleLabel.textContent = label;
}
function updateZoomLevel() {
const el = document.getElementById('zoom-level');
if (el) el.textContent = Math.round(map.getZoom());
}
updateScaleZoom();
updateScaleBar();
updateZoomLevel();
map.on('zoom', () => {
updateScaleZoom();
updateScaleBar();
updateZoomLevel();
if (typeof updateHillshadeAvailability === 'function') updateHillshadeAvailability();
});
map.on('move', () => {
updateScaleZoom();
updateScaleBar();
});
map.on('zoom', updateScaleZoom);
map.on('move', updateScaleZoom);
map.on('load', () => {
checkDataAvailability();