Files
wiki/tasks/enduro-trails/BRD_PHASE3.md
2026-05-04 10:20:01 +03:00

404 lines
24 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.
# BRD: Enduro Trails — Фаза 3 «Умный маршрут»
**Версия:** 1.1
**Дата:** 2026-05-04
**Автор:** Стрим 🌊
**Статус:** Согласовано ✅
---
## 1. Контекст и цели
### Что уже есть (Фаза 12)
- Карта грунтовок ЦФО+Чувашия (1.1М треков)
- Роутинг «Дикий путь» A→B (OSRM, кастомный профиль)
- Поиск населённых пунктов (Nominatim)
- Линейка расстояний
### Цель Фазы 3
Превратить «Дикий путь» из простого роутинга в **инструмент планирования маршрута**: несколько вариантов на выбор, детальная статистика по каждому, удобный экспорт. Пользователь должен уметь сравнить маршруты и выбрать тот, который ему нравится — не вслепую, а опираясь на данные.
### Приоритеты
1. **Удобство** — минимум кликов, всё под рукой
2. **Наглядность** — статистика читается с первого взгляда
3. **Качество** — данные точные, UI не ломается на крайних случаях
---
## 2. Фичи
---
### F-01: Альтернативные маршруты
#### Описание
При построении маршрута A→B система генерирует **до 5 вариантов** с разным балансом грунтовок/асфальта/дистанции. Все варианты отображаются на карте одновременно. Пользователь выбирает понравившийся.
#### Зачем
Один маршрут — это лотерея. Иногда OSRM строит «технически правильный» путь, но он скучный или неудобный. Пользователь хочет видеть спектр вариантов и выбрать осознанно.
#### Use Cases
**UC-01.1 — Базовый выбор маршрута**
> Слава ставит точки A и B. Нажимает «Дикий путь». Появляются 35 маршрутов разными цветами. Карточки вариантов показывают дистанцию, % грунта, время. Слава выбирает вариант с максимальным % грунта. Остальные исчезают, выбранный остаётся.
**UC-01.2 — Сравнение вариантов**
> Слава видит вариант 1 (120 км, 85% грунт) и вариант 3 (95 км, 70% грунт). Хочет посмотреть оба на карте. Наводит мышь на карточку — соответствующий маршрут подсвечивается жирнее. Решает взять вариант 1 — больше грунта, пусть и длиннее.
**UC-01.3 — Нет альтернатив**
> Точки A и B близко, только один разумный путь. Система показывает 12 варианта и поясняет: «Альтернативных маршрутов не найдено».
#### Требования
- Минимум 1 маршрут, максимум **5** (подтверждено Славой)
- Варианты визуально различимы: разные цвета (палитра: синий, зелёный, фиолетовый, оранжевый, серый)
- Активный (выбранный) маршрут — жирнее и ярче
- Hover на карточке → подсветка маршрута на карте
- Клик на карточке → выбор маршрута
- Клик на маршруте на карте → выбор маршрута
- Кнопка «Сбросить» убирает все маршруты и маркеры
#### Технические заметки
OSRM поддерживает `alternatives=true` (до 3 вариантов) и `alternatives=N` (до 5). Параметр передаётся в запросе к `/route/v1/driving/`. Дополнительно можно варьировать `radiuses` и `snapping` для получения более разнообразных вариантов. Запрашивать `alternatives=5` всегда — показывать сколько вернёт OSRM (от 1 до 5).
---
### F-02: Статистика маршрута
#### Описание
Каждый маршрут сопровождается **карточкой со статистикой**: дистанция, время, разбивка покрытия по типам дорог. Карточки компактные, читаются за 2 секунды.
#### Зачем
Без статистики маршрут — просто линия на карте. Пользователь не может оценить «насколько это грунтовый маршрут» без цифр и визуализации.
#### Use Cases
**UC-02.1 — Быстрая оценка**
> Слава видит 4 карточки маршрутов. На каждой — дистанция, время и цветная полоска покрытия. За 5 секунд понимает: вариант 2 самый «грунтовый» (80% жёлтого в полоске), вариант 4 самый короткий. Выбирает вариант 2.
**UC-02.2 — Детальная статистика**
> Слава выбрал маршрут. Карточка разворачивается: показывает детальную разбивку по км и %, количество треков каждого типа, расчётное время. Слава видит что 15 км из 120 — асфальт (неизбежный участок через город).
**UC-02.3 — Сравнение двух маршрутов**
> Слава не может выбрать между вариантом 1 и 3. Нажимает «Сравнить» — карточки встают рядом, колонки выровнены. Видно что вариант 1 длиннее на 25 км, но даёт +15% грунта.
#### Состав статистики
**Компактная карточка (всегда видна):**
- Номер варианта + цветовой маркер
- Дистанция: `142 км`
- Время: `4 ч 35 мин` (см. F-03)
- Полоска покрытия (цветная, пропорциональная):
- 🟡 Lev1-2 (грунт твёрдый)
- 🔴 Lev3-5 (грунт мягкий)
- 🔴 Тропа (path/bridleway)
- ⬜ Асфальт
**Развёрнутая карточка (по клику «Подробнее»):**
- Разбивка по км и %:
```
Lev1-2 (твёрдый грунт): 68 км (48%)
Lev3-5 (мягкий грунт): 42 км (30%)
Тропы: 12 км (8%)
Асфальт: 20 км (14%)
```
- Итого грунт: `122 км (86%)`
- Итого асфальт: `20 км (14%)`
#### Требования
- Полоска покрытия — пропорциональная, минимальная ширина сегмента 3px (чтобы не терялся)
- Цвета полоски совпадают с цветами слоёв на карте
- Статистика считается на бэкенде по геометрии маршрута + данным БД
- Карточки не перекрывают карту (панель сбоку или снизу)
---
### F-03: Человекочитаемое время
#### Описание
Время маршрута отображается в формате `X ч Y мин` или `X дн Y ч Z мин`. Никаких «135 минут».
#### Зачем
«135 минут» — это неудобно. Мозг не воспринимает большие числа минут интуитивно. «2 ч 15 мин» — сразу понятно.
#### Use Cases
**UC-03.1 — Короткий маршрут**
> Маршрут 45 минут → отображается `45 мин`
**UC-03.2 — Стандартный маршрут**
> Маршрут 155 минут → отображается `2 ч 35 мин`
**UC-03.3 — Длинный маршрут**
> Маршрут 1560 минут → отображается `1 дн 2 ч`
**UC-03.4 — Очень длинный маршрут**
> Маршрут 2750 минут → отображается `1 дн 21 ч 50 мин`
#### Правила форматирования
```
< 60 мин → "X мин"
60 мин 23:59 → "X ч Y мин" (если Y=0 → "X ч")
≥ 24 ч → "X дн Y ч Z мин" (если Z=0 → "X дн Y ч")
```
#### Требования
- Реализовать как утилитарную функцию `formatDuration(seconds)` в `app.js`
- Применить везде где отображается время: карточки маршрутов, развёрнутая статистика
- Время считается от OSRM (секунды) — конвертировать на фронте
---
### F-04: Промежуточные точки маршрута
#### Описание
Пользователь может добавить **промежуточные точки** (waypoints) между A и B. Маршрут строится через все точки по порядку. Точки можно перетаскивать.
#### Зачем
Часто нужно «зайти» в конкретное место — деревню, лесной массив, смотровую точку. Без промежуточных точек маршрут строится напрямую и может обойти интересное место стороной.
#### Use Cases
**UC-04.1 — Добавление точки**
> Слава построил маршрут A→B. Видит что маршрут обходит интересный лесной массив. Нажимает «+ Точка» и кликает на карте в нужном месте. Маршрут перестраивается через новую точку C: A→C→B.
**UC-04.2 — Несколько точек**
> Слава добавляет 3 промежуточные точки. Маршрут строится A→C1→C2→C3→B. В панели точек видны все 5 точек с возможностью удалить любую.
**UC-04.3 — Перетаскивание точки** ✅ (подтверждено)
> Слава хочет немного сдвинуть промежуточную точку. Перетаскивает маркер на карте. Маршрут перестраивается в реальном времени (debounce 300ms).
**UC-04.4 — Удаление точки**
> В панели точек Слава нажимает ✕ рядом с промежуточной точкой. Точка удаляется, маршрут перестраивается.
**UC-04.5 — Изменение порядка**
> Слава хочет поменять порядок точек. Перетаскивает строку в панели точек (drag-and-drop). Маршрут перестраивается.
#### Требования
- Максимум 8 промежуточных точек (итого 10 точек включая A и B)
- Маркеры промежуточных точек визуально отличаются от A/B (меньше, другой цвет — белый с цветной обводкой)
- Перестройка маршрута при перетаскивании — debounce 300ms (**обязательно**, подтверждено Славой)
- Панель точек: список с иконками A, 1, 2, ..., B и кнопками удаления
- При добавлении точки режим «добавить точку» активируется кнопкой, деактивируется после клика
---
### F-05: Экспорт GPX
#### Описание
Выбранный маршрут можно скачать в формате GPX для загрузки в навигатор (OsmAnd, Garmin, Locus Map и др.).
#### Зачем
Карта в браузере — для планирования. В поле нужен навигатор. GPX — универсальный формат, поддерживается всеми мото/вело навигаторами.
#### Use Cases
**UC-05.1 — Скачать маршрут**
> Слава выбрал маршрут. Нажимает «Скачать GPX». Браузер скачивает файл `enduro-route-2026-05-04.gpx`. Слава загружает его в OsmAnd. Файл содержит и трек (полная геометрия), и waypoints (метки + точки маршрута).
**UC-05.2 — GPX с промежуточными точками**
> Маршрут с 3 промежуточными точками. GPX содержит все waypoints + трек. В OsmAnd точки отображаются как промежуточные остановки.
**UC-05.3 — Имя файла**
> Файл называется `enduro-YYYYMMDD-HHMMSS.gpx` — уникальное имя, не перезаписывает предыдущие.
#### Требования
- GPX 1.1 формат
- Содержит **оба элемента** (подтверждено Славой):
- `<trk>` + `<trkseg>` — полная геометрия маршрута (все точки от OSRM)
- `<wpt>` — точки A, промежуточные, B + все флажки/метки с карты
- Метаданные: name (`Enduro route YYYY-MM-DD`), desc (дистанция + % грунта), time
- Генерация на фронте (из геометрии OSRM ответа + localStorage меток) — не требует бэкенда
- Кнопка «Скачать GPX» активна только когда маршрут выбран
---
### F-06: Флажки / именованные метки
#### Описание
Пользователь может расставить на карте **именованные метки** (флажки) — интересные места, точки старта, лагерь, заправка и т.д. Метки сохраняются в localStorage.
#### Зачем
При планировании маршрута нужно отмечать «хочу заехать сюда», «здесь заправка», «здесь ночёвка». Без меток всё держится в голове или в отдельных заметках.
#### Use Cases
**UC-06.1 — Поставить метку**
> Слава нажимает кнопку 🚩. Курсор меняется. Кликает на карте. Появляется диалог: поле для названия (placeholder «Метка 1»), выбор иконки (🚩⛺🔧⛽💧). Нажимает «Сохранить». Метка появляется на карте.
**UC-06.2 — Метка без названия**
> Слава кликает и сразу нажимает Enter (пустое название). Метка сохраняется с автоименем «Метка N».
**UC-06.3 — Клик по метке**
> Слава кликает по существующей метке. Попап: название, координаты, кнопки «Редактировать» и «Удалить».
**UC-06.4 — Метки сохраняются**
> Слава закрывает браузер, открывает снова. Все метки на месте (localStorage).
**UC-06.5 — Использовать метку как точку маршрута**
> В попапе метки есть кнопки «Сделать точкой A» и «Сделать точкой B». Слава нажимает «Сделать точкой A» — метка становится стартом маршрута.
**UC-06.6 — Экспорт меток в GPX**
> При скачивании GPX (F-05) метки включаются как `<wpt>` с именами.
#### Требования
- Хранение: localStorage, ключ `enduro_markers` (**только браузер**, подтверждено Славой — синхронизация между устройствами не нужна)
- Максимум 50 меток (предупреждение при достижении лимита)
- Иконки: 🚩 (по умолчанию), ⛺, 🔧, ⛽, 💧, 📍
- Метки отображаются поверх всех слоёв
- Кнопка «Очистить все метки» с подтверждением
---
## 3. Нефункциональные требования
### Производительность
- Построение маршрута (включая статистику) — не более 5 секунд
- При ожидании — спиннер на кнопке, карта не блокируется
- Перестройка при перетаскивании точки — debounce 300ms, не тормозит UI
### Мобильность
- Панель маршрутов не перекрывает карту на экранах < 768px
- Карточки маршрутов скроллируются горизонтально на мобиле
- Touch-события для перетаскивания точек
### Надёжность
- Если OSRM не вернул альтернативы — показать 1 маршрут без ошибки
- Если статистика не посчиталась — показать маршрут без статистики, не ломать UI
- Если localStorage недоступен — метки работают только в сессии, без ошибки
---
## 4. Изменения в бэкенде
### Новый endpoint: `/api/route` (расширение существующего)
**Запрос:**
```json
{
"waypoints": [
{"lon": 37.6, "lat": 55.7},
{"lon": 38.1, "lat": 55.9},
{"lon": 39.5, "lat": 56.2}
],
"alternatives": 5
}
```
**Ответ:**
```json
{
"routes": [
{
"index": 0,
"geometry": { "type": "LineString", "coordinates": [...] },
"distance_m": 142000,
"duration_s": 16500,
"stats": {
"track_lev12_m": 68000,
"track_lev345_m": 42000,
"path_m": 12000,
"asphalt_m": 20000,
"track_lev12_pct": 48,
"track_lev345_pct": 30,
"path_pct": 8,
"asphalt_pct": 14,
"dirt_total_pct": 86
}
}
]
}
```
### Логика расчёта статистики
1. Получить геометрию маршрута от OSRM (LineString)
2. Разбить на сегменты по 50м
3. Для каждого сегмента — найти ближайший трек в БД (spatial query по bbox)
4. Суммировать длины по типам: `highway_type` + `track_type`
5. Вернуть в ответе вместе с геометрией
> ⚠️ Расчёт статистики — приближённый (snap to nearest road). Точность ±5% — **согласовано со Славой**, достаточно для принятия решения.
---
## 5. UI/UX требования
### Панель маршрутов
- Появляется справа (десктоп) или снизу (мобиле) после построения маршрутов
- Карточки маршрутов: компактные, скроллируемые
- Активный маршрут выделен рамкой/фоном
- Кнопка «Сбросить всё» — сбрасывает маршруты и точки
### Карточка маршрута (компактная)
```
┌─────────────────────────────────┐
│ ● Вариант 1 142 км │
│ ████████████░░░ 4 ч 35 мин │
│ 86% грунт · 14% асфальт │
│ [Подробнее ▼] │
└─────────────────────────────────┘
```
- Цветная точка = цвет маршрута на карте
- Полоска = визуализация покрытия
- Клик на карточку = выбор маршрута
### Карточка маршрута (развёрнутая)
```
┌─────────────────────────────────┐
│ ● Вариант 1 142 км │
│ ████████████░░░ 4 ч 35 мин │
│ │
│ 🟡 Lev1-2 68 км 48% │
│ 🔴 Lev3-5 42 км 30% │
│ 🔴 Тропы 12 км 8% │
│ ⬜ Асфальт 20 км 14% │
│ │
│ [📥 GPX] [Выбрать маршрут] │
└─────────────────────────────────┘
```
### Панель точек
```
┌─────────────────────────────────┐
│ 📍 Точки маршрута [+ Точка] │
│ A Москва, ул. Ленина [✕] │
│ 1 55.812, 37.441 [✕] │
│ B Тверь, центр [✕] │
└─────────────────────────────────┘
```
---
## 6. Приоритет реализации
| # | Фича | Приоритет | Сложность | Ценность |
|---|------|-----------|-----------|----------|
| F-03 | Человекочитаемое время | 🔴 Высокий | Низкая | Высокая |
| F-01 | Альтернативные маршруты | 🔴 Высокий | Средняя | Очень высокая |
| F-02 | Статистика маршрута | 🔴 Высокий | Средняя | Очень высокая |
| F-05 | Экспорт GPX | 🟡 Средний | Низкая | Высокая |
| F-04 | Промежуточные точки | 🟡 Средний | Высокая | Высокая |
| F-06 | Флажки/метки | 🟢 Низкий | Средняя | Средняя |
**Рекомендуемый порядок реализации:**
1. F-03 (время) — быстро, сразу улучшает UX
2. F-01 + F-02 (альтернативы + статистика) — ядро фазы, делать вместе
3. F-05 (GPX) — логичное продолжение после выбора маршрута
4. F-04 (промежуточные точки) — отдельный итерация
5. F-06 (флажки) — последними, наименее критично
---
## 7. Решения (согласовано 2026-05-04)
| # | Вопрос | Решение |
|---|--------|---------|
| 1 | Количество альтернатив | **5** — запрашивать максимум, показывать сколько вернёт OSRM |
| 2 | Точность статистики | **±5% ок** — приближённый расчёт по сегментам |
| 3 | Перетаскивание точек | **Нужно** — drag на карте с debounce 300ms |
| 4 | Синхронизация меток | **Только localStorage** — между устройствами не нужно |
| 5 | Формат GPX | **Трек + метки** — `<trk>` с геометрией и `<wpt>` для всех точек и флажков |
---
*Документ согласован. Готов к передаче Dev-агенту.*