# BRD: Рельеф (Terrain Layer) ## Цель Пользователь видит рельеф местности на карте — цветами (все зумы) и тенями (зум 10+). Это позволяет на мелком зуме искать интересные холмистые/горные зоны, а на крупном — детально планировать маршрут по оврагам и склонам. ## Пользовательский сценарий 1. Открывает карту на зуме 6 (весь ЦФО) 2. Видит зелёные равнины, жёлтые холмы, коричневые горы 3. Находит интересную гористую зону 4. Зумит туда 5. На зуме 12+ добавляются тени — овраги и склоны становятся объёмными 6. Строит маршрут по интересному рельефу ## UI ### Кнопка в toolbar - Иконка: 🏔️ (или `mountain` из Lucide) - Позиция: после кнопки слоёв, перед линейкой - Состояние: inactive (серая) / active (белая, подсвечена) ### Попап при нажатии ``` ┌─────────────┐ │ 🏔️ Рельеф │ ├─────────────┤ │ ☑️ Цветной │ ← всегда доступен │ рельеф │ │ │ │ ☐ Теневой │ ← disabled на зуме < 10 │ рельеф │ (grayed out с подписью │ │ "Доступно при приближении") └─────────────┘ ``` - Чекбоксы независимые - Состояние сохраняется в localStorage - Попап закрывается по тапу вне его или повторному тапу на кнопку ## Слои на карте (MapLibre) ### Цветной рельеф (hypsometric) - **Z-Index:** между `background` и основными слоями (дороги, POI) - **Opacity:** 0.4-0.5 (настраиваемо) - **Zoom:** 5-15 (все зумы) - **Source:** `terrain-hypso` → растровые тайлы `/terrain/hypso/{z}/{x}/{y}.png` ### Теневой рельеф (hillshade) - **Z-Index:** поверх цветного, под дорогами - **Opacity:** 0.3-0.4 - **Zoom:** 10-15 (minzoom 10) - **Source:** `terrain-hillshade` → `/terrain/hillshade/{z}/{x}/{y}.png` - **Visibility:** none при зуме < 10 (MapLibre `minzoom` + toggle) ## Данные ### Источник SRTM 1 Arc-Second Global (~30м разрешение) - Формат: `.hgt` файлы - Лицензия: Public Domain (NASA) - Покрытие: 60°N - 56°S ### Регион ЦФО + Чувашия (первый этап) - ~30-40 тайлов .hgt (1°×1°) - ~100 MB сырых данных ### Генерация тайлов #### Цветной рельеф - GDAL: `gdaldem color-relief` с кастомной ramp - Цвета: - 0-50м: #2d5016 (тёмно-зелёный) - 50-150м: #5a8a3a (зелёный) - 150-300м: #a8c66c (светло-зелёный) - 300-500м: #d4b85a (жёлтый) - 500-800м: #c49420 (оранжево-жёлтый) - 800-1200м: #8b5a2b (коричневый) - 1200-2000м: #6b4423 (тёмно-коричневый) - 2000м+: #ffffff (белый, снежные вершины) - Растеризация в тайлы: `gdal2tiles.py` или rio-tiler - Формат: PNG с прозрачностью (или без, opacity в MapLibre) - Зумы: 5-15 - Размер тайла: 256×256 - Итоговый объём: ~50-100 MB (оценочно) #### Теневой рельеф - GDAL: `gdaldem hillshade` (-az 315 -alt 45) - Grayscale PNG - Зумы: 10-15 - Итоговый объём: ~20-40 MB ### Хранение - Сервер: `/home/slin/enduro-trails/data/terrain/` - `hypso/{z}/{x}/{y}.png` - `hillshade/{z}/{x}/{y}.png` - Nginx: location `/enduro/terrain/` → alias `/home/slin/enduro-trails/data/terrain/` - Кэширование: `expires 1y` (тайлы не меняются) ## Интеграция с существующим кодом ### Backend (FastAPI) - Новый endpoint не нужен — nginx отдаёт статику - При необходимости: `/terrain/{layer}/{z}/{x}/{y}.png` → FileResponse ### Frontend (MapLibre) - Добавить `raster` sources в стиль карты - Добавить `raster` layers с `paint: { 'raster-opacity': ... }` - Toolbar: кнопка + попап компонент - localStorage ключ: `terrain_layers` ### Стили - Тёмная тема: ramp адаптирован под тёмную подложку - Светлая тема: ramp адаптирован под светлую (после F-18) ## Приоритет P1 — блокер для фазы 6 (SRTM профиль высот, фича «Горка») ## Критерии приёмки - [ ] На зуме 6 виден цветной рельеф всего ЦФО - [ ] Москва — зелёная, Смоленская возвышенность — жёлтая, Урал на востоке — коричневый - [ ] На зуме 12+ теневой рельеф показывает овраги и склоны - [ ] Тoggle работает: можно цветной без теневого, теневой без цветного, оба, ни одного - [ ] Состояние сохраняется после перезагрузки - [ ] Мобильный: попап не перекрывает кнопки, закрывается по тапу вне - [ ] Десктоп: попап позиционируется относительно кнопки ## Зависимости - SRTM данные (скачать) - GDAL в контейнере/на сервере (или отдельный скрипт генерации) - Место на диске: ~150 MB тайлов