Files
wiki/tasks/enduro-trails/PROJECT.md
2026-05-06 18:40:01 +03:00

263 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Enduro Trails 🏍️
> OSM-карта с фокусом на грунтовые дороги для построения красивых эндуро-маршрутов
**Статус:** active
**Старт:** 2026-05-02
**Автор:** Слава
---
## Концепция
Обычные карты оптимизированы под автомобили — асфальт яркий, грунтовки не видны. Enduro Trails переворачивает эту логику: **грунтовки/тропы — главный слой**, асфальт — тусклый фон. Плюс фичи для поиска и построения красивых маршрутов (минимум асфальта, максимум красоты).
---
## Регион
1. **ЦФО + Чувашия** (первый регион, прототип)
2. Расширение на новые ФО по запросу
---
## Архитектура
### Стек
- Pyrosm/Osmium → парсинг PBF
- Spatialite → хранение (прототип), PostGIS → продакшен
- OSRM (кастомный профиль `enduro.lua`) → роутинг
- FastAPI + uvicorn (4 workers) → бэкенд
- MapLibre GL JS → фронт (веб + PWA)
### Инфраструктура
- **Сервер:** `slin@82.22.50.71`, sudo `motoZ@yaz2010`
- **Контейнер приложения:** `prototype-enduro-trails-1`, порт `5558`
- **URL:** `https://openclaw.mva154.duckdns.org/enduro/`
- **Код на сервере:** `/home/slin/enduro-trails/prototype/`
- **Workspace:** `/home/node/.openclaw/workspace/tasks/enduro-trails/prototype/`
- **БД:** `/home/slin/enduro-trails/data/centralfederal.sqlite` (431 MB, 1.1M треков, 14K POI)
- **OSRM контейнер:** `osrm-osrm-routed-1`, порт `5559`
- **OSRM данные:** `/home/slin/enduro-trails/data/enduro.osrm.*` (~5.2 GB)
- **OSRM профиль:** `/home/slin/enduro-trails/osrm/enduro.lua`
- **Swap:** `/home/slin/swapfile3` (4 GB), итого 6 GB swap
- **Деплой:** Node.js ssh2 (SSH бинарник в контейнере не работает — glibc 2.36 vs 2.38)
### Деплой
```bash
# ВАЖНО: docker cp нужно делать ПОСЛЕ рестарта контейнера!
# Образ перезаписывает статику при рестарте.
# 1. Загрузить файлы на сервер через SFTP (deploy_static.js)
# 2. docker restart prototype-enduro-trails-1
# 3. Подождать 8 сек
# 4. docker cp /home/slin/enduro-trails/prototype/static/app.js prototype-enduro-trails-1:/app/static/app.js
# 5. docker cp /home/slin/enduro-trails/prototype/static/app.css prototype-enduro-trails-1:/app/static/app.css
# 6. docker cp /home/slin/enduro-trails/prototype/static/index.html prototype-enduro-trails-1:/app/static/index.html
# Для бэкенда (app.py):
docker cp /home/slin/enduro-trails/prototype/app.py prototype-enduro-trails-1:/app/app.py
docker restart prototype-enduro-trails-1
# deploy_app2.js — только для app.py!
# deploy_static.js — SFTP статики, но cp делать вручную после рестарта
```
---
## Схема БД
```sql
-- trails: id, osm_id, highway_type, track_type, surface, name, length_m,
-- mtb_scale, visibility, smoothness, access, tags, geom BLOB,
-- min_lon, max_lon, min_lat, max_lat
-- poi: id, osm_id, poi_type, name, geom BLOB, lon, lat
```
---
## Реестр фич
| ID | Фича | Описание | Статус | Фаза |
|----|------|----------|--------|------|
| F-01 | Альтернативные маршруты | До 5 вариантов с разным балансом грунт/асфальт | ✅ Готово | 3 |
| F-02 | Статистика маршрута | Карточки с % покрытия, полоска, развёрнутый вид | ✅ Готово | 3 |
| F-03 | Человекочитаемое время | "2 ч 35 мин" вместо "155 мин" | ✅ Готово | 3 |
| F-04 | Промежуточные точки | Добавление, удаление, drag, debounce 300ms | ✅ Готово | 3 |
| F-05 | GPX экспорт | Трек + waypoints + флажки, имя enduro-YYYYMMDDHHMMSS.gpx | ✅ Готово | 3 |
| F-06 | Флажки/метки | localStorage, попап → A / → B / удалить, иконки | ✅ Готово | 3 |
| F-07 | Исключить шлагбаумы | Баррьеры → inaccessible в OSRM | 📋 BRD готов | 3.1 |
| F-08 | Исключить тротуары | footway/pedestrian/steps убрать из графа | 📋 BRD готов | 3.1 |
| F-09 | Больше альтернатив | Penalized re-query + дедупликация | 📋 BRD готов | 3.1 |
| F-10 | Слой препятствий | Шлагбаумы, броды, блокпосты на карте | 📋 BRD готов | 3.1 |
| F-11 | "Красивый маршрут" | Замкнутый круг через живописные места, scenic_score, варианты | ⏳ Бэклог | 4 |
| F-12 | "Горка" | Макс набор высоты, мин дистанция (SRTM) | ⏳ Бэклог | 6 |
| F-13 | "Связка" | Соединить два трека грунтовками | ⏳ Бэклог | 4 |
| F-14 | "Разведка" | Грунтовки вокруг точки, статистика по типам, POI | ⏳ Бэклог | 4 |
| F-15 | "Народные треки" | OSM Traces, Wikiloc, Komoot, 4x4travel | ⏳ Бэклог | 8 |
| F-16 | Тёмная тема + редизайн | Две темы (авто/светлая/тёмная), SunCalc, мобильный UI, drag-and-drop точек, расстояние по маршруту | ✅ Готово | 5 |
| F-22 | Линейка UX | Расстояние сегмента под маркером, крестик удаления, зелёный Старт, панель fit-content, toast, toggle скрыть/показать, deleteRuler | ✅ Готово | 5.2 |
| F-23 | Метки UX | Починен баг удаления через попап (popup.remove() перед marker.remove()) | ✅ Готово | 5.2 |
| F-24 | Поиск точек маршрута | Inline Nominatim поиск в каждом wl-item, убран верхний search bar | ✅ Готово | 5.2 |
| F-25 | Route onboarding mini-bar | При активации режима маршрута — мини-бар с пином S/F, подсказкой и поиском Nominatim. Большой лист не открывается автоматически | ✅ Готово | 5.3 |
| F-26 | Route sheet UX | Крестик → шеврон сворачивания; «Добавить точку» показывает onboarding мини-бар с подсказкой и поиском | ✅ Готово | 5.3 |
| F-27 | Route корзина → мини-бар | Корзина в листе маршрута закрывает лист и показывает onboarding мини-бар для старта (как первый тап на кнопку маршрута) | ✅ Готово | 5.3 |
| F-28 | Линейка: центрирование панели | #ruler-info центрирован по горизонтали (left:50% + translateX(-50%)), не перекрывает кнопки масштаба | ✅ Готово | 5.3 |
| F-17 | PWA + офлайн | Service Worker, MBTiles, GPS-трекинг | ⏳ Бэклог | 7 |
| F-18 | Светлая карта | Создать `style-light.json` — светлый стиль карты для светлой темы. Сейчас при светлой теме карта остаётся тёмной (`style-light.json` отсутствует) | ⏳ Бэклог | 5.1 |
| F-19 | Иконка колеса + спиннер | SVG мотокросс-колесо (32 спицы, 36 зубцов кноблинга, 8 болтов ступицы, currentColor). Вращается при построении маршрута | ✅ Готово | 5.3 |
| F-20 | Линейка: перетаскивание точек | Возможность перетащить уже поставленную точку линейки на новое место | ⏳ Бэклог | 5.x |
| F-21 | Линейка: сохранение между сессиями | Сохранять точки линейки в localStorage, восстанавливать при перезагрузке | ⏳ Бэклог | 5.x |
---
## Выполненные фазы
### ✅ Фаза 1 — MVP (02.05.2026)
- PBF парсинг (ЦФО + Чувашия)
- Spatialite БД (1.1M треков, 14K POI)
- FastAPI self-hosted MVT тайлы
- MapLibre GL JS карта с кастомным стилем
- Попапы с информацией о дороге
- Контролы слоёв
### ✅ Фаза 2 — Роутинг + UI (03.05.2026)
- OSRM + кастомный профиль `enduro.lua` (порт 5559)
- Роутинг «Дикий путь» — кнопка 🗺️, маркеры A/B
- Поиск (Nominatim, debounce 400ms)
- Линейка 📏 (haversine)
- Геолокация 📍, компас 🧭
### ✅ Фаза 3 — Умный маршрут (04.05.2026)
- **F-01:** Альтернативные маршруты (до 5, цвета: синий/зелёный/фиолетовый/оранжевый/серый)
- **F-02:** Статистика покрытия (карточки: компактные + развёрнутые, полоска покрытия, %)
- **F-03:** `formatDuration()` — "2 ч 35 мин" / "1 дн 2 ч 50 мин"
- **F-04:** Промежуточные точки (до 8, draggable, debounce 300ms)
- **F-05:** GPX 1.1 экспорт (трек + waypoints + флажки)
- **F-06:** Флажки 🚩 (localStorage, 50 лимит, попап → A / → B / удалить)
**Баги исправлены (04.05):**
- formatDuration(86400) → "1 дн" (не "1 дн 0 ч")
- OSRM TooBig → retry 5→3→1
- Панель маршрута перекрывала кнопки → CSS right: 56px
- Длинные маршруты не строились → radiuses=5000 + NoSegment retry с 10km + timeout 60s
---
## Бэклог по фазам
### 📋 Фаза 3.1 — Улучшение роутинга (BRD готов, ожидает согласования)
**BRD:** `BRD_PHASE3.1.md`
| Фича | Описание | Сложность | Требует |
|------|----------|-----------|---------|
| F-07 | Шлагбаумы → inaccessible в OSRM | Низкая (lua) | Пересборка графа ~40 мин |
| F-08 | Убрать footway/pedestrian/steps из графа | Низкая (lua) | Пересборка графа ~40 мин |
| F-09 | Penalized re-query для разных альтернатив | Высокая | — |
| F-10 | Слой препятствий на карте (🚧🌊⛔) | Средняя | Перепарсинг PBF ~30 мин |
**Порядок:** F-07+F-08 вместе (одна пересборка) → F-09 → F-10
### ✅ Фаза 4 — Продвинутый роутинг (04.05.2026)
- **F-11** «Красивый маршрут» — замкнутый круг через живописные места, scenic_score, карточки с вариантами
- **F-13** «Связка» — соединить два трека грунтовками, альтернативы
- **F-14** «Разведка» — грунтовки в радиусе 20/50/100 км, статистика по типам, POI
### ⏳ Фаза 4 — Продвинутый роутинг (в разработке)
- **F-11** «Красивый маршрут» — замкнутый круг, scenic_score, варианты по дистанции (50/100/150/200 км)
- **F-13** «Связка» — соединить два трека грунтовками, альтернативы
- **F-14** «Разведка» — грунтовки в радиусе 20/50/100 км, статистика по типам, POI
- Мини-бар маршрута (`#mini-route-bar`) — компактный HUD поверх карты
### ✅ Фаза 5.3 — Route UX + Линейка (06.05.2026)
- **F-25** Route onboarding mini-bar — при активации режима маршрута показывается мини-бар с пином S/F, подсказкой и поиском Nominatim
- **F-26** Route sheet UX — крестик заменён на шеврон сворачивания; «Добавить точку» показывает onboarding мини-бар
- **F-27** Корзина в листе маршрута — закрывает лист и открывает onboarding мини-бар для старта
- **F-28** Линейка — панель `#ruler-info` центрирована по горизонтали, не перекрывает кнопки масштаба
### ✅ Фаза 5 — Редизайн (05.05.2026)
**BRD:** `BRD_PHASE5.md`
- F-16 Тёмная + светлая тема, авто-режим по SunCalc (восход/закат)
- Bottom sheets, toolbar, SVG Lucide-иконки, skeleton loading
- Свайп вниз для закрытия sheet
- Drag-and-drop точек маршрута (touch + mouse)
- Кнопка «Скачать GPX» (share dialog убран)
- Фикс: «Добавить точку» из мини-бара
- Десктоп-адаптив (toolbar слева, sheet max-width 400px)
- Расстояние по маршруту между точками (по геометрии OSRM, масштабирование к route.distance_m)
- Обновление расстояний при смене варианта маршрута
**Баги исправлены (05.05):**
- «Добавить точку» не работала — `addWaypointMode()` не устанавливала `routeMode = true`
- Drag-and-drop не работал на десктопе — добавлены mouse-события
- Расстояние в списке точек показывало haversine по прямой вместо маршрута
- При смене варианта маршрута расстояния не обновлялись (`selectRoute`/`selectMiniRoute` не вызывали `renderWaypointsList`)
- `renderWaypointsList` вызывался до построения маршрута → добавлен вызов после `drawRouteResults`
- Деплой статики через `deploy_app2.js` не работал (копировал только `app.py`) → нужен `deploy_static.js` + `docker cp` **после** рестарта контейнера (образ перезаписывает статику при рестарте)
**Технические решения:**
- `getRouteSegmentDistances()` — snap waypoints к геометрии маршрута, суммирование haversine по точкам, масштабирование к `route.distance_m`
- `snapIdx[0] = 0`, `snapIdx[last] = n-1` — принудительный snap первой/последней точки
- Деплой статики: SFTP → сервер → `docker restart``docker cp` (порядок важен!)
- `streaming.mode: "off"` в Telegram канале — убраны дублированные сообщения
- `send_voice.sh` — убрана отправка через `openclaw message send`, только генерация OGG + `MEDIA:` директива
### ⏳ Фаза 6 — SRTM рельеф
- F-12 «Горка» — макс набор высоты, мин дистанция
- Профиль высот на маршруте
- SRTM DEM 30м данные (Public Domain)
### ⏳ Фаза 7 — PWA + офлайн
- F-17 Service Worker, офлайн MBTiles, GPS-трекинг в реальном времени
- Интеграция с OsmAnd/Locus (экспорт)
### ⏳ Фаза 8 — Народные треки
- F-15 Источники: OSM Traces, Wikiloc, Komoot, 4x4travel.ru, Enduroad.ru
- Отдельный слой `community_tracks`, фильтрация по типу активности
---
## Ключевые решения
| Решение | Причина |
|---------|---------|
| 4 uvicorn workers | Устранение узкого места однопоточности |
| Фильтр по length_m на низких зумах | Производительность, читаемость |
| Относительные пути в app.js | Работает через nginx /enduro/ и по прямому IP |
| OSRM `weight_name='routability'` | `duration` → OSRM выбирал асфальт |
| `forward_rate = penalty` | Penalty/метр — прямой вес пути |
| `radiuses=5000` в OSRM запросах | Без этого длинные маршруты не снэппятся |
| Retry TooBig: 5→3→1 | OSRM не даёт 5 альтернатив на длинных маршрутах |
| Retry NoSegment: radiuses=10000 | Точки далеко от дорог — нужен широкий snap |
| Timeout 60s для retry | Длинные маршруты строятся >30с |
| `docker cp` после рестарта (не до) | Образ перезаписывает статику при рестарте — cp нужен после того как контейнер поднялся |
| `deploy_app2.js` только для app.py | Скрипт не копирует статику — для фронтенда использовать `deploy_static.js` + ручной cp после рестарта |
| Масштабирование сегментов к `route.distance_m` | OSRM геометрия упрощена — haversine по точкам даёт ~0.2% погрешность; масштабирование даёт точное совпадение |
| `renderWaypointsList()` после `drawRouteResults()` | Список рендерится до построения маршрута — нужен повторный вызов когда `routeResults` заполнен |
| `streaming.mode: "off"` для Telegram | `partial` и `progress` шлют промежуточные сообщения — `off` даёт одно финальное |
| Сэмплирование каждые ~500м для stats | Без этого расчёт >30с на длинных маршрутах |
| Grid cache для calc_route_stats | Убирает повторные SQL запросы для близких точек |
---
## Документы
| Документ | Описание |
|----------|----------|
| `PROJECT.md` | Этот файл — общее состояние проекта |
| `CONCEPT.md` | Концепция и архитектура |
| `TECHNICAL_SPEC.md` | ТЗ прототипа v0.1 |
| `DEV_TASK.md` | Стабилизация v0.1 (архив) |
| `BRD_PHASE3.md` | Бизнес-требования Фазы 3 (согласовано) |
| `BRD_PHASE3.1.md` | Бизнес-требования Фазы 3.1 (на согласовании) |
| `BRD_PHASE5.md` | Бизнес-требования Фазы 5 (✅ реализовано) |
| `TEST_CASES_PHASE3.md` | 56 тест-кейсов |
| `DEV_TASK_PHASE3.md` | ТЗ для Dev-агента Фаза 3 |
| `DEV_TASK_PHASE5.md` | ТЗ для Dev-агента Фаза 5 |
| `reports/` | Отчёты о тестировании |
---
*Ссылка на онтологию: `proj_enduro_trails`*