diff --git a/tasks/enduro-trails/prototype/static/app.css b/tasks/enduro-trails/prototype/static/app.css index 98b76ba..36581b9 100644 --- a/tasks/enduro-trails/prototype/static/app.css +++ b/tasks/enduro-trails/prototype/static/app.css @@ -480,7 +480,7 @@ body { .recon-radius-btn.active { background: #ff6600; color: #fff; border-color: #ff6600; font-weight: 600; } /* ─── Фаза 4: Красивый маршрут ─────────────────────────────────────────── */ -.scenic-dist-btn { +.scenic-km-btn { flex: 1; padding: 4px 0; border: 1px solid #ccc; @@ -490,8 +490,8 @@ body { background: #f0f0f0; transition: background 0.15s; } -.scenic-dist-btn:hover { background: #e0e0e0; } -.scenic-dist-btn.active { background: #ff6600; color: #fff; border-color: #ff6600; font-weight: 600; } +.scenic-km-btn:hover { background: #e0e0e0; } +.scenic-km-btn.active { background: #ff6600; color: #fff; border-color: #ff6600; font-weight: 600; } .scenic-poi-item { display: flex; align-items: center; gap: 6px; diff --git a/tasks/enduro-trails/prototype/static/app.js b/tasks/enduro-trails/prototype/static/app.js index 41c4e99..0a56d43 100644 --- a/tasks/enduro-trails/prototype/static/app.js +++ b/tasks/enduro-trails/prototype/static/app.js @@ -1,23 +1,12 @@ // ─── Общее: деактивация всех режимов ──────────────────────────────────────────── -function deactivateAllModes() { - if (routeMode) toggleRouteMode(); - if (rulerMode) toggleRuler(); - if (markerMode) toggleMarkerMode(); - if (typeof reconMode !== 'undefined' && reconMode) toggleReconMode(); - if (typeof linkMode !== 'undefined' && linkMode) toggleLinkMode(); - if (typeof scenicMode !== 'undefined' && scenicMode) toggleScenicMode(); -} - -// ─── Общее: деактивация всех режимов ──────────────────────────────────────────── - function deactivateAllModes() { if (routeMode) { routeMode = false; document.getElementById('btn-route').classList.remove('active'); document.getElementById('route-panel').style.display = 'none'; clearRoute(); } if (rulerMode) toggleRuler(); if (markerMode) toggleMarkerMode(); - if (reconMode) toggleReconMode(); - if (linkMode) toggleLinkMode(); - if (scenicMode) toggleScenicMode(); + if (typeof reconMode !== 'undefined' && reconMode) toggleReconMode(); + if (typeof linkMode !== 'undefined' && linkMode) toggleLinkMode(); + if (typeof scenicMode !== 'undefined' && scenicMode) toggleScenicMode(); if (window._map) window._map.getCanvas().style.cursor = ''; } @@ -948,8 +937,6 @@ function initRouteClicks(map) { if (!routeMode) return; - const { lng, lat } = e.lngLat; - // Режим добавления промежуточной точки if (addingWaypoint) { addingWaypoint = false; @@ -1252,11 +1239,9 @@ function toggleLinkMode() { linkMode = true; btn.classList.add('active'); window._map.getCanvas().style.cursor = 'crosshair'; - document.getElementById('route-panel').style.display = 'block'; - document.querySelector('#route-panel > div:first-child').textContent = '🔗 Связка'; - document.getElementById('route-status').textContent = 'Кликни первую точку'; - document.getElementById('route-info').style.display = 'none'; - document.getElementById('route-actions').style.display = 'none'; + document.getElementById('link-panel').style.display = 'block'; + document.getElementById('link-status').textContent = '1️⃣ Кликни конец первого трека'; + document.getElementById('link-cards').innerHTML = ''; } else { btn.classList.remove('active'); window._map.getCanvas().style.cursor = ''; @@ -1275,7 +1260,7 @@ function addLinkPoint(lng, lat) { linkMarkers.push(marker); if (idx === 1) { - document.getElementById('route-status').textContent = 'Кликни вторую точку'; + document.getElementById('link-status').textContent = '2️⃣ Кликни начало второго трека'; } else if (idx >= 2) { buildLinkRoute(); } @@ -1283,7 +1268,7 @@ function addLinkPoint(lng, lat) { async function buildLinkRoute() { const map = window._map; - document.getElementById('route-status').textContent = '⏳ Ищу связку...'; + document.getElementById('link-status').textContent = '⏳ Ищу связку...'; const basePath = window.location.pathname.replace(/\/[^/]*$/, '') || ''; try { const resp = await fetch(`${basePath}/api/route`, { @@ -1294,13 +1279,69 @@ async function buildLinkRoute() { if (!resp.ok) throw new Error('Не найдена'); const data = await resp.json(); if (data.routes && data.routes.length > 0) { - renderRouteCards(data.routes, 'link'); - document.getElementById('route-status').textContent = '✅ Связка найдена'; + renderLinkCards(data.routes); + document.getElementById('link-status').textContent = '✅ Связка найдена'; } else { - document.getElementById('route-status').textContent = '❌ Грунтовая связка не найдена'; + document.getElementById('link-status').textContent = '❌ Грунтовая связка не найдена'; } } catch(e) { - document.getElementById('route-status').textContent = '❌ ' + e.message; + document.getElementById('link-status').textContent = '❌ ' + e.message; + } +} + +function renderLinkCards(routes) { + const map = window._map; + const colors = ['#0066ff', '#00aa44', '#9933cc']; + const cardsEl = document.getElementById('link-cards'); + cardsEl.innerHTML = ''; + + routes.forEach((r, i) => { + const geojson = { type: 'Feature', geometry: r.geometry, properties: {} }; + const sid = `link-src-${i}`; + const lid = `link-line-${i}`; + if (map.getSource(sid)) map.removeSource(sid); + if (map.getLayer(lid)) map.removeLayer(lid); + map.addSource(sid, { type: 'geojson', data: geojson }); + map.addLayer({ + id: lid, type: 'line', source: sid, + paint: { + 'line-color': colors[i % colors.length], + 'line-width': i === 0 ? 5 : 3, + 'line-opacity': i === 0 ? 0.9 : 0.5, + }, + layout: { 'line-cap': 'round', 'line-join': 'round' } + }); + + const km = (r.distance / 1000).toFixed(0); + const time = formatDuration(r.duration); + const dirt = r.stats?.dirt_total_pct || '?'; + const col = colors[i % colors.length]; + const card = document.createElement('div'); + card.className = 'route-card' + (i === 0 ? ' active' : ''); + card.innerHTML = ` +
+ + Вариант ${i+1} + ${km} км + ${time} +
+
${dirt}% грунт
+ `; + card.onclick = () => selectLinkRoute(i); + cardsEl.appendChild(card); + }); +} + +function selectLinkRoute(idx) { + const map = window._map; + document.querySelectorAll('#link-cards .route-card').forEach((c, i) => c.classList.toggle('active', i === idx)); + // Highlight selected route + for (let i = 0; i < 3; i++) { + const lid = `link-line-${i}`; + if (map.getLayer(lid)) { + map.setPaintProperty(lid, 'line-width', i === idx ? 5 : 3); + map.setPaintProperty(lid, 'line-opacity', i === idx ? 0.9 : 0.5); + } } } @@ -1309,15 +1350,14 @@ function clearLink() { linkMarkers.forEach(m => m.remove()); linkMarkers = []; const map = window._map; - // Remove route layers for (let i = 0; i < 5; i++) { const lid = `link-line-${i}`; if (map.getLayer(lid)) map.removeLayer(lid); const sid = `link-src-${i}`; if (map.getSource(sid)) map.removeSource(sid); } - document.getElementById('route-panel').style.display = 'none'; - document.getElementById('route-cards').innerHTML = ''; + document.getElementById('link-panel').style.display = 'none'; + document.getElementById('link-cards').innerHTML = ''; } // ─── Фаза 4: Красивый маршрут ──────────────────────────────────────────────── @@ -1345,12 +1385,12 @@ function toggleScenicMode() { } } -function setScenicDist(km) { +function setScenicKm(km) { scenicTargetKm = km; - document.querySelectorAll('.scenic-dist-btn').forEach(b => { - b.classList.toggle('active', +b.textContent === km); + document.querySelectorAll('.scenic-km-btn').forEach(b => { + b.classList.toggle('active', +b.dataset.km === km); }); - const inp = document.getElementById('scenic-dist-input'); + const inp = document.getElementById('scenic-custom-km'); if (inp) inp.value = km; }