auto-sync: 2026-05-04 22:50:01
This commit is contained in:
295
tasks/enduro-trails/BRD_PHASE4.md
Normal file
295
tasks/enduro-trails/BRD_PHASE4.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# BRD: Enduro Trails — Фаза 4 «Продвинутый роутинг»
|
||||
|
||||
**Версия:** 1.0
|
||||
**Дата:** 2026-05-04
|
||||
**Автор:** Стрим 🌊
|
||||
**Статус:** На согласовании
|
||||
|
||||
---
|
||||
|
||||
## 1. Контекст и цели
|
||||
|
||||
### Что уже есть
|
||||
- Роутинг A→B «Дикий путь» (OSRM, до 5 альтернатив)
|
||||
- Статистика покрытия (% грунт/асфальт/тропа)
|
||||
- Промежуточные точки, GPX экспорт
|
||||
- POI в БД: water, peak, viewpoint, ruins, ford, cave_entrance (14K штук)
|
||||
|
||||
### Проблема
|
||||
«Дикий путь» даёт маршрут от A до B с максимумом грунтовок. Но эндуристу часто нужно другое:
|
||||
- **«Покататься красиво»** — замкнутый круг от дома, через живописные места, и обратно
|
||||
- **«Найти грунтовки рядом»** — не знаешь куда ехать, покажи что есть вокруг
|
||||
- **«Соединить два трека»** — есть два хороших участка, как их связать грунтовками
|
||||
|
||||
### Цель
|
||||
Дать эндуристу 3 новых режима роутинга, которые закрывают типичные сценарии: покататься, разведать, связать.
|
||||
|
||||
---
|
||||
|
||||
## 2. Фичи
|
||||
|
||||
---
|
||||
|
||||
### F-11: «Красивый маршрут» (Scenic Route)
|
||||
|
||||
#### Описание
|
||||
Пользователь указывает **точку старта** и **желаемую дистанцию**. Система строит **замкнутый кольцевой маршрут**, который проходит через максимально живописные места: озёра, видовые точки, заброшки, горы.
|
||||
|
||||
#### Зачем
|
||||
Самый частый сценарий эндуриста: «Хочу покататься км на 150 от дома, но не по асфальту, а красиво». Сейчас нужно вручную ставить точки и строить маршрут. «Красивый маршрут» делает это автоматически.
|
||||
|
||||
#### Use Cases
|
||||
|
||||
**UC-11.1 — Покататься от дома**
|
||||
> Слава нажимает 🎨 «Красивый маршрут». Кликает на карте — точка старта (дом). Вводит дистанцию: 150 км. Система строит кольцевой маршрут: дом → озеро Сенеж → видовая точка → заброшка → грунтовки → дом. 142 км, 89% грунт.
|
||||
|
||||
**UC-11.2 — Короткая поездка**
|
||||
> Слава хочет покататься на 50 км. Маршрут: дом → лесной массив → грунтовки → дом. 48 км, 94% грунт.
|
||||
|
||||
**UC-11.3 — Несколько вариантов**
|
||||
> Система предлагает 2–3 кольцевых маршрута: северный (через озёра), восточный (через леса), южный (через поля). Слава выбирает северный — через озёра красивее.
|
||||
|
||||
**UC-11.4 — Нет красивых мест рядом**
|
||||
> Слава в степи, рядом нет озёр и видов. Система строит максимально грунтовый кольцевой маршрут, но без бонусных точек. Подпись: «Живописных объектов поблизости не найдено, маршрут по грунтовкам».
|
||||
|
||||
#### Алгоритм
|
||||
|
||||
**Шаг 1: Найти привлекательные POI в радиусе**
|
||||
- Радиус = желаемая дистанция × 0.6 (примерное кольцо)
|
||||
- Выбрать POI с оценкой аттрактивности:
|
||||
|
||||
| POI тип | Баллы | Вес |
|
||||
|---------|-------|-----|
|
||||
| `natural=water` (озёра, реки) | 10 | Высокий |
|
||||
| `tourism=viewpoint` (видовые точки) | 15 | Очень высокий |
|
||||
| `historic=ruins` (заброшки) | 10 | Высокий |
|
||||
| `natural=peak` (вершины) | 12 | Высокий |
|
||||
| `natural=cave_entrance` (пещеры) | 8 | Средний |
|
||||
| `ford=yes` (броды) | 5 | Низкий |
|
||||
|
||||
**Шаг 2: Выбрать 3–5 POI для маршрута**
|
||||
- Жадный алгоритм: начать со старта, на каждом шаге выбирать ближайший POI с максимальными баллами
|
||||
- Суммарная дистанция до POI ≤ желаемая дистанция × 0.8 (оставить запас на возврат)
|
||||
- После последнего POI — маршрут обратно к старту
|
||||
|
||||
**Шаг 3: Построить маршрут через POI**
|
||||
- Использовать OSRM: старт → POI1 → POI2 → ... → старт
|
||||
- Каждый сегмент — запрос к OSRM с `alternatives=false`
|
||||
|
||||
**Шаг 4: Проверить дистанцию**
|
||||
- Если итоговая дистанция > желаемая × 1.3 — уменьшить кол-во POI, перестроить
|
||||
- Если < желаемая × 0.5 — добавить POI, перестроить
|
||||
|
||||
**Шаг 5: Альтернативы**
|
||||
- Повторить с другим набором POI (другое направление от старта)
|
||||
- Дать 2–3 варианта
|
||||
|
||||
#### UI
|
||||
|
||||
**Кнопка:** 🎨 «Красивый маршрут» (рядом с 🗺️ «Дикий путь»)
|
||||
|
||||
**Панель:**
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 🎨 Красивый маршрут │
|
||||
│ │
|
||||
│ 📍 Точка старта: кликни на карте│
|
||||
│ 📏 Дистанция: [150] км │
|
||||
│ │
|
||||
│ [Построить маршрут] │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Карточка маршрута (после построения):**
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 🎨 Северный маршрут 142 км │
|
||||
│ ████████████░░░ 4 ч 20 мин │
|
||||
│ 89% грунт · 11% асфальт │
|
||||
│ │
|
||||
│ 💊 Живописные объекты: │
|
||||
│ 💧 Озеро Сенеж │
|
||||
│ 👁 Смотровая «Высокая» │
|
||||
│ 🏚 Заброшенная ферма │
|
||||
│ │
|
||||
│ [📥 GPX] [Выбрать маршрут] │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Требования
|
||||
- Время построения ≤ 15 секунд (несколько OSRM запросов)
|
||||
- Дистанция: ввод от 20 до 500 км
|
||||
- Кольцевой маршрут = старт и финиш на одной точке
|
||||
- POI на маршруте показаны в карточке
|
||||
- Спиннер на время построения
|
||||
- До 3 вариантов маршрутов
|
||||
|
||||
---
|
||||
|
||||
### F-14: «Разведка» (Recon Mode)
|
||||
|
||||
#### Описание
|
||||
Пользователь кликает на карту → система показывает **все грунтовые дороги в радиусе X км** с краткой статистикой: сколько дорог, какая длина, какие типы.
|
||||
|
||||
#### Зачем
|
||||
«Приехал на новое место — что тут есть?». Не нужно строить маршрут — просто посмотреть какие грунтовки рядом, оценить стоит ли ехать сюда.
|
||||
|
||||
#### Use Cases
|
||||
|
||||
**UC-14.1 — Разведка в новом месте**
|
||||
> Слава приехал на дачу в незнакомый район. Нажимает 📍 «Разведка», кликает на карту. Появляется круг радиусом 20 км с выделенными грунтовками. Статистика: «124 грунтовки, 380 км грунтовых дорог, 85% grade3-5, 12 бродов».
|
||||
|
||||
**UC-14.2 — Оценка перед поездкой**
|
||||
> Слава планирует поездку на выходные. Кликает на место на карте — видит что там всего 3 короткие грунтовки. Решает ехать в другое место.
|
||||
|
||||
**UC-14.3 — Настройка радиуса**
|
||||
> По умолчанию радиус 20 км. Слава меняет на 50 км — видит больше грунтовок.
|
||||
|
||||
#### Алгоритм
|
||||
|
||||
1. Получить координаты клика (lat, lon)
|
||||
2. Запрос к БД:
|
||||
```sql
|
||||
SELECT highway_type, track_type, surface, length_m, name
|
||||
FROM trails
|
||||
WHERE min_lon <= ? AND max_lon >= ? AND min_lat <= ? AND max_lat >= ?
|
||||
AND length_m >= 100
|
||||
```
|
||||
где bbox = центр ± радиус (0.18° для 20 км, 0.45° для 50 км)
|
||||
3. Агрегировать: количество, суммарная длина, распределение по типам
|
||||
4. Отрисовать круг на карте + подсветить грунтовки внутри
|
||||
|
||||
#### UI
|
||||
|
||||
**Кнопка:** 📍 «Разведка» (рядом с 🗺️ и 🎨)
|
||||
|
||||
**Попап при клике:**
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 📍 Разведка — 20 км радиус │
|
||||
│ │
|
||||
│ 🛤 124 грунтовки · 380 км │
|
||||
│ │
|
||||
│ 🟡 Lev1-2: 45 шт · 120 км │
|
||||
│ 🔴 Lev3-5: 68 шт · 210 км │
|
||||
│ 🔴 Тропы: 11 шт · 50 км │
|
||||
│ │
|
||||
│ 💧 Озёра: 3 · 👁 Виды: 2 │
|
||||
│ 🌊 Броды: 12 · 🏚 Руины: 1 │
|
||||
│ │
|
||||
│ Радиус: [20] [50] [100] км │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
**На карте:**
|
||||
- Круг (Circle) с полупрозрачной заливкой
|
||||
- Грунтовки внутри круга подсвечены (увеличена толщина / opacity)
|
||||
|
||||
#### Требования
|
||||
- Радиус по умолчанию: 20 км, опции: 20 / 50 / 100
|
||||
- Запрос к БД ≤ 2 секунды
|
||||
- Круг визуально не мешает карте
|
||||
- Статистика в попапе обновляется при изменении радиуса
|
||||
|
||||
---
|
||||
|
||||
### F-13: «Связка» (Link Route)
|
||||
|
||||
#### Описание
|
||||
Пользователь указывает **два трека / точки на карте** → система строит **маршрут между ними по грунтовкам**, минуя асфальт.
|
||||
|
||||
#### Зачем
|
||||
«Ехал по отличной грунтовке, потом по другой — а между ними 10 км асфальта. Можно ли как-то по грунтовкам перейти?»
|
||||
|
||||
#### Use Cases
|
||||
|
||||
**UC-13.1 — Связать два участка**
|
||||
> Слава проехал грунтовку А и грунтовку Б. Между ними 8 км асфальта. Нажимает 🔗 «Связка», ставит конечную точку А и начальную точку Б. Система находит обход по грунтовкам: 12 км вместо 8 км асфальта, но 100% грунт.
|
||||
|
||||
**UC-13.2 — Нет грунтовой связки**
|
||||
> Между двумя точками нет никаких грунтовок — только асфальт. Система: «Грунтовая связка не найдена, кратчайший путь — 5 км по асфальту».
|
||||
|
||||
#### Алгоритм
|
||||
Это просто роутинг A→B через «Дикий путь» — уже реализовано. Отличие только в UI:
|
||||
- Вместо кнопки «Дикий путь» — кнопка 🔗 «Связка»
|
||||
- Маркеры: не A/B, а «Трек 1 (конец)» и «Трек 2 (начало)»
|
||||
- Карточка показывает только % грунта (асфальт — нежелательный элемент)
|
||||
|
||||
#### UI
|
||||
|
||||
**Кнопка:** 🔗 «Связка»
|
||||
|
||||
**Панель:**
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 🔗 Связка │
|
||||
│ │
|
||||
│ 1️⃣ Конец трека: кликни на карте│
|
||||
│ 2️⃣ Начало трека: кликни │
|
||||
│ │
|
||||
│ [Найти грунтовую связку] │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Карточка:**
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 🔗 Связка 12 км │
|
||||
│ ██████████████████░ 24 мин │
|
||||
│ 92% грунт · 8% асфальт │
|
||||
│ │
|
||||
│ [📥 GPX] [Выбрать] │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Требования
|
||||
- Использует тот же OSRM «Дикий путь» роутинг
|
||||
- Отличие от «Дикого пути» — только UI (другие маркеры, акцент на минимизацию асфальта)
|
||||
- До 3 альтернативных связок
|
||||
|
||||
---
|
||||
|
||||
## 3. Нефункциональные требования
|
||||
|
||||
### Производительность
|
||||
- «Красивый маршрут» — ≤ 15 сек (3–5 OSRM запросов)
|
||||
- «Разведка» — ≤ 2 сек (один SQL запрос)
|
||||
- «Связка» — ≤ 5 сек (один OSRM запрос)
|
||||
|
||||
### UI
|
||||
- 3 новые кнопки в панели управления: 🎨 🔗 📍
|
||||
- Режимы взаимоисключающие: активный режим деактивирует остальные
|
||||
- При переключении режима — сброс предыдущего состояния
|
||||
|
||||
---
|
||||
|
||||
## 4. Приоритет реализации
|
||||
|
||||
| # | Фича | Приоритет | Сложность | Ценность |
|
||||
|---|------|-----------|-----------|----------|
|
||||
| F-14 | «Разведка» | 🔴 Высокий | Низкая | Высокая |
|
||||
| F-13 | «Связка» | 🟡 Средний | Низкая | Средняя |
|
||||
| F-11 | «Красивый маршрут» | 🔴 Высокий | Высокая | Очень высокая |
|
||||
|
||||
**Рекомендуемый порядок:**
|
||||
1. **F-14 «Разведка»** — простая SQL-агрегация, быстрый результат, высокая ценность
|
||||
2. **F-13 «Связка»** — почти готово (переиспользуем OSRM), минимум нового кода
|
||||
3. **F-11 «Красивый маршрут»** — самый сложный (алгоритм выбора POI, несколько OSRM запросов), но самый ценный
|
||||
|
||||
---
|
||||
|
||||
## 5. Открытые вопросы для Славы
|
||||
|
||||
1. **«Красивый маршрут» — дистанция:** вводишь вручную или слайдер? Предлагаю: текстовое поле + пресеты (50 / 100 / 150 / 200 км).
|
||||
|
||||
2. **«Красивый маршрут» — направление:** хочешь выбрать направление (север/юг/запад/восток) или система сама подбирает? Направление полезно — «хочу на север, к озёрам».
|
||||
|
||||
3. **«Разведка» — радиус:** 20/50/100 км — ок, или хочешь другие значения?
|
||||
|
||||
4. **«Связка» — как указать точки:** просто клик на карте (как «Дикий путь») или клик на конкретную грунтовку?
|
||||
|
||||
5. **Нужно ли «Красивому маршруту» учитывать сезонность?** Например, броды весной непроходимы. Пока не учитываем — это отдельная фича.
|
||||
|
||||
---
|
||||
|
||||
*Документ готов к согласованию. После ответов — обновлю и передам Dev-агенту.*
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> OSM-карта с фокусом на грунтовые дороги для построения красивых эндуро-маршрутов
|
||||
|
||||
**Статус:** active (прототип задеплоен)
|
||||
**Статус:** active
|
||||
**Старт:** 2026-05-02
|
||||
**Автор:** Слава
|
||||
|
||||
@@ -12,147 +12,187 @@
|
||||
|
||||
Обычные карты оптимизированы под автомобили — асфальт яркий, грунтовки не видны. Enduro Trails переворачивает эту логику: **грунтовки/тропы — главный слой**, асфальт — тусклый фон. Плюс фичи для поиска и построения красивых маршрутов (минимум асфальта, максимум красоты).
|
||||
|
||||
## Ключевые фичи
|
||||
---
|
||||
|
||||
| Фича | Описание |
|
||||
|------|----------|
|
||||
| 🛤️ **"Дикий путь"** | Роутинг А→Б с максимизацией грунтовок (OSRM) |
|
||||
| 🔍 **"Поиск"** | Поиск населённых пунктов и адресов (Nominatim) |
|
||||
| 📏 **"Линейка"** | Измерение расстояния между точками на карте |
|
||||
| 🚩 **"Флажки/метки"** | Расстановка именованных меток на карте |
|
||||
| 🗺️ **"Умный маршрут"** | Промежуточные точки, % асфальт/грунт/тропа, GPX экспорт |
|
||||
| 🎨 **"Красивый маршрут"** | Замкнутый круг через водоёмы, виды, заброшки |
|
||||
| 🏔️ **"Горка"** | Макс набор высоты, мин дистанция (SRTM) |
|
||||
| 🔗 **"Связка"** | Соединить два трека грунтовками |
|
||||
| 📍 **"Разведка"** | Грунтовки вокруг точки |
|
||||
| 🚧 **"Препятствия"** | Броды, шлагбаумы, болота, ЛЭП |
|
||||
| 🌐 **"Народные треки"** | Сбор и отображение треков с внешних сервисов |
|
||||
| 🌙 **"День/ночь"** | Переключатель темы — светлая/тёмная карта |
|
||||
| 🎨 **"Эндуро-дизайн"** | Современный агрессивный UI в духе эндуро/оффроад |
|
||||
|
||||
## Регионы
|
||||
## Регион
|
||||
|
||||
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`
|
||||
### Инфраструктура
|
||||
- **Сервер:** `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)
|
||||
|
||||
### OSRM
|
||||
|
||||
- **Данные:** `/home/slin/enduro-trails/data/enduro.osrm.*`
|
||||
- **PBF:** `/home/slin/enduro-trails/data/enduro.osm.pbf`
|
||||
- **Профиль:** `/home/slin/enduro-trails/osrm/enduro.lua`
|
||||
- **Docker compose:** `/home/slin/enduro-trails/osrm/docker-compose.yml`
|
||||
- **Контейнер:** `osrm-osrm-routed-1`, порт `5559`
|
||||
- **OSRM_URL в app.py:** `http://172.22.0.1:5559`
|
||||
- **БД:** `/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)
|
||||
|
||||
## Текущее состояние (2026-05-03)
|
||||
### Деплой
|
||||
```bash
|
||||
# SFTP через ssh2: /tmp/deploy_app.js
|
||||
# docker cp + restart (файлы запечены в образ)
|
||||
docker cp /home/slin/enduro-trails/prototype/app.py prototype-enduro-trails-1:/app/app.py
|
||||
docker restart prototype-enduro-trails-1
|
||||
```
|
||||
|
||||
### ✅ Готово
|
||||
|
||||
**OSRM роутинг:**
|
||||
- ✅ «Дикий путь» — OSRM с кастомным профилем `enduro.lua`
|
||||
- ✅ `weight_name = 'routability'` (не `duration` — не оптимизирует по времени)
|
||||
- ✅ `forward_speed = 30` для всех типов дорог (duration не влияет на выбор)
|
||||
- ✅ `forward_rate` определяет предпочтительность: track=100, bridleway=90, path=85, motorway=0.1
|
||||
- ✅ `tracktype` мультипликатор: grade1×1.3, grade3×1.0, grade5×0.8
|
||||
- ✅ U-turn penalty 20s, нет односторонних ограничений
|
||||
- ✅ Граф: `enduro.osrm.*` (~5.2 GB), собран из `enduro.osm.pbf` (ЦФО + Чувашия)
|
||||
- ✅ Контейнер `osrm-osrm-routed-1`, порт 5559, OSRM_URL=`http://172.22.0.1:5559`
|
||||
|
||||
**Инфраструктура:**
|
||||
- Прототип задеплоен: `https://openclaw.mva154.duckdns.org/enduro/`
|
||||
- БД: 1 141 926 треков, 14 882 POI (Spatialite)
|
||||
- Векторные тайлы (MVT) через FastAPI, 4 uvicorn workers
|
||||
- FIFO-кэш тайлов (512 тайлов в памяти) [реализация — FIFO, не LRU]
|
||||
- Упрощение геометрии по зуму (Shapely simplify)
|
||||
- Фильтр треков по длине на низких зумах (z8: ≥500м, z9: ≥200м)
|
||||
- Dockerfile — быстрый старт без apt/pip при рестарте
|
||||
- Nginx `/enduro/` с HTTPS
|
||||
|
||||
**Карта и UI:**
|
||||
- MapLibre GL JS, легенда (Lev1-2 / Lev3-5 / Тропа)
|
||||
- Раскраска: Lev1-2 жёлтый (#FFD700), Lev3-5 красный (#FF4400)
|
||||
- Тропы (path/footway/bridleway) — красный пунктир (#cc0000)
|
||||
- Асфальт скрыт (visibility: none)
|
||||
- Подложка: saturation -0.4, contrast 0.25, brightness-max 0.9
|
||||
- Кнопка 🧭 компас (север/свободный режим)
|
||||
- Кнопка 📍 геолокация с пульсирующим маркером
|
||||
- Попапы: name, surface, tracktype, length_m, mtb_scale
|
||||
|
||||
**Фичи:**
|
||||
- ✅ Роутинг "Дикий путь" — кнопка 🗺️, маркеры A/B, карточка с дистанцией и временем
|
||||
- ✅ Поиск (Nominatim) — строка в хедере, debounce 400ms, flyTo
|
||||
- ✅ Линейка 📏 — кружки точно на координатах, плашки над ними, крестик удаления, haversine расстояние
|
||||
|
||||
### ⏳ Бэклог
|
||||
|
||||
**Фаза 3 — Умный маршрут:**
|
||||
- Промежуточные точки (перетаскиваемые)
|
||||
- Статистика: % асфальт / lev1-2 / lev3-5 / тропа
|
||||
- Скачать GPX
|
||||
|
||||
**Фаза 4 — Флажки/метки:**
|
||||
- Расстановка именованных меток на карте
|
||||
- Сохранение в localStorage
|
||||
|
||||
**Фаза 5 — Редизайн:**
|
||||
- Тёмная тема, эндуро-стиль, адаптив под мобилку
|
||||
|
||||
**Фаза 6 — SRTM рельеф:**
|
||||
- "Горка" — макс набор высоты
|
||||
- Профиль высот на маршруте
|
||||
|
||||
**Фаза 7 — PWA + офлайн:**
|
||||
- Service Worker, офлайн MBTiles, GPS-трекинг
|
||||
|
||||
**Фаза 8 — Народные треки:**
|
||||
- Источники: OSM Traces, Wikiloc, Komoot, 4x4travel.ru, Enduroad.ru
|
||||
- Отдельный слой `community_tracks`, фильтрация по типу активности
|
||||
---
|
||||
|
||||
## Схема БД
|
||||
|
||||
```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
|
||||
-- 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
|
||||
-- Примечание: poi НЕ имеет поля tags
|
||||
-- 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 | "Красивый маршрут" | Замкнутый круг через водоёмы, виды, заброшки | ⏳ Бэклог | 4 |
|
||||
| F-12 | "Горка" | Макс набор высоты, мин дистанция (SRTM) | ⏳ Бэклог | 6 |
|
||||
| F-13 | "Связка" | Соединить два трека грунтовками | ⏳ Бэклог | 4 |
|
||||
| F-14 | "Разведка" | Грунтовки вокруг точки | ⏳ Бэклог | 4 |
|
||||
| F-15 | "Народные треки" | OSM Traces, Wikiloc, Komoot, 4x4travel | ⏳ Бэклог | 8 |
|
||||
| F-16 | Тёмная тема | День/ночь + эндуро-дизайн | ⏳ Бэклог | 5 |
|
||||
| F-17 | PWA + офлайн | Service Worker, MBTiles, GPS-трекинг | ⏳ Бэклог | 7 |
|
||||
|
||||
---
|
||||
|
||||
## Выполненные фазы
|
||||
|
||||
### ✅ Фаза 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 — Продвинутый роутинг
|
||||
- F-11 «Красивый маршрут» — замкнутый круг через живописные места (озёра, виды, заброшки)
|
||||
- Оценка аттрактивности: близость к воде +10, перепад высот +15, viewpoints +20
|
||||
- F-13 «Связка» — соединить два трека грунтовками
|
||||
- F-14 «Разведка» — все грунтовки в радиусе X км от точки
|
||||
|
||||
### ⏳ Фаза 5 — Редизайн
|
||||
- F-16 Тёмная тема + эндуро-стиль + адаптив под мобилку
|
||||
|
||||
### ⏳ Фаза 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 |
|
||||
| Dockerfile вместо inline apt+pip | Устранил 452 рестарта контейнера |
|
||||
| OSRM `weight_name='routability'` | `duration` → OSRM выбирал асфальт как быстрый |
|
||||
| `forward_rate = penalty` (не /speed) | Penalty/метр — прямой вес пути |
|
||||
| Два маркера на точку линейки | Кружок anchor:center точно на координатах, плашка offset вверх |
|
||||
| stopPropagation на крестике линейки | Клик не проваливался на карту и не ставил новую точку |
|
||||
| Фильтр по 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с |
|
||||
| Node.js ssh2 для деплоя | SSH бинарник в контейнере требует glibc 2.38 |
|
||||
| docker cp вместо compose build | Файлы запечены в образ, cp в running container быстрее |
|
||||
| Сэмплирование каждые ~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 (на согласовании) |
|
||||
| `TEST_CASES_PHASE3.md` | 56 тест-кейсов |
|
||||
| `DEV_TASK_PHASE3.md` | ТЗ для Dev-агента Фаза 3 |
|
||||
| `reports/` | Отчёты о тестировании |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user