diff --git a/src/web/app.js b/src/web/app.js index 6158a64..cedc0ff 100644 --- a/src/web/app.js +++ b/src/web/app.js @@ -2725,6 +2725,48 @@ function initMiniRouteInteraction() { const TERRAIN_BASE_URL = window.location.pathname.replace(/\/[^/]*$/, '') + '/terrain'; +// ET-013: zoom-aware paint для слоёв рельефа (ADR-017). +// Цель — компенсировать «потерю выразительности» перепадов на z9-z11. +// Pre-z9 — hillshade не показывается (UI-минзум). На z9-z11 — максимальный +// контраст и opacity, чтобы тени читались как на z8. К z12-z14 — возврат +// к исходным значениям (тогда у пользователя есть другие способы +// читать рельеф: подложка, грунтовки, POI). +const HILLSHADE_PAINT = { + 'raster-opacity': [ + 'interpolate', ['linear'], ['zoom'], + 9, 0.65, + 10, 0.60, + 11, 0.55, + 12, 0.50, + 14, 0.40 + ], + 'raster-contrast': [ + 'interpolate', ['linear'], ['zoom'], + 9, 0.40, + 10, 0.35, + 11, 0.30, + 12, 0.15, + 14, 0.00 + ], + 'raster-resampling': 'nearest' +}; + +// ET-013: TRI остаётся 0.70 на z8 (регрессия), пик 0.80-0.85 на z9-z11. +const TRI_PAINT = { + 'raster-opacity': [ + 'interpolate', ['linear'], ['zoom'], + 5, 0.55, + 7, 0.65, + 8, 0.70, + 9, 0.80, + 10, 0.85, + 11, 0.85, + 12, 0.75, + 15, 0.70 + ], + 'raster-resampling': 'nearest' +}; + function toggleTerrainPopup() { const popup = document.getElementById('terrain-popup'); const btn = document.getElementById('terrain-toggle'); @@ -2779,8 +2821,9 @@ function onTerrainCheckbox() { btn.classList.toggle('active', hillshadeChecked || triChecked); // Apply layers - applyTerrainLayer('terrain-hillshade', TERRAIN_BASE_URL + '/hillshade/{z}/{x}/{y}.png', hillshadeChecked, 0.40, 10, 15); - applyTerrainLayer('terrain-tri', TERRAIN_BASE_URL + '/tri/{z}/{x}/{y}.png', triChecked, 0.70, 5, 15); + // ET-013: hillshade теперь доступен с z9; paint zoom-aware (см. HILLSHADE_PAINT / TRI_PAINT). + applyTerrainLayer('terrain-hillshade', TERRAIN_BASE_URL + '/hillshade/{z}/{x}/{y}.png', hillshadeChecked, HILLSHADE_PAINT, 9, 15); + applyTerrainLayer('terrain-tri', TERRAIN_BASE_URL + '/tri/{z}/{x}/{y}.png', triChecked, TRI_PAINT, 5, 15); } @@ -3313,12 +3356,29 @@ function onUnitChange() { } // <<< ET-005 unit toggle block <<< -function applyTerrainLayer(id, tileUrl, enabled, opacity, minzoom, maxzoom) { +/** + * ET-013: обратно-совместимое расширение для поддержки zoom-aware paint. + * + * @param {string} id - id слоя. + * @param {string} tileUrl - URL-шаблон тайлов. + * @param {boolean} enabled - показывать ли слой. + * @param {number|object} opacityOrPaint - либо число (старый контракт, + * станет 'raster-opacity' + linear-resampling), либо объект paint-properties + * целиком (должен содержать как минимум 'raster-opacity'). + * @param {number} minzoom + * @param {number} maxzoom + */ +function applyTerrainLayer(id, tileUrl, enabled, opacityOrPaint, minzoom, maxzoom) { const map = window._map; if (!map) return; - + const sourceId = id + '-source'; - + + // ET-013: нормализация paint — число (старый контракт) или объект. + const paint = (typeof opacityOrPaint === 'number') + ? { 'raster-opacity': opacityOrPaint, 'raster-resampling': 'linear' } + : opacityOrPaint; + if (enabled) { // Add source if not exists if (!map.getSource(sourceId)) { @@ -3334,17 +3394,14 @@ function applyTerrainLayer(id, tileUrl, enabled, opacity, minzoom, maxzoom) { // Add layer if not exists if (!map.getLayer(id)) { // Insert before first road/trail layer for correct z-order - const firstTrailLayer = map.getStyle().layers.find(l => + const firstTrailLayer = map.getStyle().layers.find(l => l.id.startsWith('trails-') || l.id.startsWith('poi-') ); map.addLayer({ id: id, type: 'raster', source: sourceId, - paint: { - 'raster-opacity': opacity, - 'raster-resampling': 'linear' - }, + paint: paint, minzoom: minzoom, maxzoom: maxzoom }, firstTrailLayer ? firstTrailLayer.id : undefined); @@ -3365,7 +3422,7 @@ function updateHillshadeAvailability() { const hint = document.getElementById('terrain-hillshade-hint'); const label = cb ? cb.closest('.terrain-checkbox') : null; - if (zoom < 10) { + if (zoom < 9) { // ET-013: на z9 hillshade уже доступен if (cb) cb.disabled = true; if (label) label.classList.add('disabled'); if (hint) hint.style.display = 'inline'; diff --git a/src/web/index.html b/src/web/index.html index 6710435..efa5cc0 100644 --- a/src/web/index.html +++ b/src/web/index.html @@ -57,7 +57,7 @@ Тени рельефа - +