diff --git a/tasks/enduro-trails/prototype/static/index.html b/tasks/enduro-trails/prototype/static/index.html index e9937a0..a9d0c49 100644 --- a/tasks/enduro-trails/prototype/static/index.html +++ b/tasks/enduro-trails/prototype/static/index.html @@ -339,39 +339,116 @@ function toggleLayer(group) { } // ─── Map init ───────────────────────────────────────────────────────────────── -const map = new maplibregl.Map({ - container: 'map', - style: '/style.json', - center: [40.5, 55.5], - zoom: 7, - minZoom: 4, - maxZoom: 18, - hash: true, -}); -window._map = map; +async function initMap() { + const tileBase = window.location.origin; + const style = await fetch('/style.json').then(r => r.json()); + style.sources['trails-tiles'].tiles = [`${tileBase}/api/tiles/{z}/{x}/{y}.mvt`]; -map.addControl(new maplibregl.NavigationControl(), 'top-left'); -map.addControl(new maplibregl.ScaleControl({ unit: 'metric' }), 'bottom-right'); -map.addControl(new maplibregl.FullscreenControl(), 'top-left'); + const map = new maplibregl.Map({ + container: 'map', + style: style, + center: [40.5, 55.5], + zoom: 7, + minZoom: 4, + maxZoom: 18, + hash: true, + }); + window._map = map; -// ─── Loading state ──────────────────────────────────────────────────────────── -map.on('load', () => { - document.getElementById('loading').classList.remove('visible'); - checkDataAvailability(); -}); + map.addControl(new maplibregl.NavigationControl(), 'top-left'); + map.addControl(new maplibregl.ScaleControl({ unit: 'metric' }), 'bottom-right'); + map.addControl(new maplibregl.FullscreenControl(), 'top-left'); -map.on('error', (e) => { - console.error('Map error:', e.error?.message || e); - // Снимаем loading даже при ошибке чтобы не висело - document.getElementById('loading').classList.remove('visible'); -}); + // ─── Loading state ───────────────────────────────────────────────────────────────── + map.on('load', () => { + document.getElementById('loading').classList.remove('visible'); + checkDataAvailability(); + }); -// Таймаут — если load не сработал за 15 сек, снимаем loading -setTimeout(() => { - document.getElementById('loading').classList.remove('visible'); -}, 15000); + map.on('error', (e) => { + console.error('Map error:', e.error?.message || e); + document.getElementById('loading').classList.remove('visible'); + }); + + setTimeout(() => { + document.getElementById('loading').classList.remove('visible'); + }, 15000); + + // ─── Stats bar ───────────────────────────────────────────────────────────────── + map.on('zoom', () => { + document.getElementById('zoom-val').textContent = map.getZoom().toFixed(1); + }); + + map.on('mousemove', (e) => { + const { lng, lat } = e.lngLat; + document.getElementById('coords-val').textContent = + `${lat.toFixed(4)}, ${lng.toFixed(4)}`; + }); + + // ─── Popups ───────────────────────────────────────────────────────────────── + const popup = new maplibregl.Popup({ + closeButton: true, + closeOnClick: false, + maxWidth: '300px', + }); + + function formatLength(m) { + if (!m) return '—'; + if (m >= 1000) return (m / 1000).toFixed(1) + ' км'; + return Math.round(m) + ' м'; + } + + function poiTypeLabel(t) { + const labels = { + 'natural=peak': '⛰ Вершина', + 'natural=water': '💧 Вода', + 'tourism=viewpoint': '👁 Смотровая', + 'historic=ruins': '🏙 Руины', + 'natural=cave_entrance': '🕳 Пещера', + 'ford=yes': '🌊 Брод', + }; + return labels[t] || t; + } + + // Клик по грунтовкам + ['trails-track', 'trails-path-bridleway', 'trails-asphalt'].forEach(layerId => { + map.on('click', layerId, (e) => { + const props = e.features[0].properties; + const html = ` +