diff --git a/tasks/enduro-trails/BRD_PHASE3.md b/tasks/enduro-trails/BRD_PHASE3.md new file mode 100644 index 0000000..6d4b2b3 --- /dev/null +++ b/tasks/enduro-trails/BRD_PHASE3.md @@ -0,0 +1,399 @@ +# BRD: Enduro Trails — Фаза 3 «Умный маршрут» + +**Версия:** 1.0 +**Дата:** 2026-05-04 +**Автор:** Стрим 🌊 +**Статус:** На согласовании со Славой + +--- + +## 1. Контекст и цели + +### Что уже есть (Фаза 1–2) +- Карта грунтовок ЦФО+Чувашия (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. Нажимает «Дикий путь». Появляются 3–5 маршрутов разными цветами. Карточки вариантов показывают дистанцию, % грунта, время. Слава выбирает вариант с максимальным % грунта. Остальные исчезают, выбранный остаётся. + +**UC-01.2 — Сравнение вариантов** +> Слава видит вариант 1 (120 км, 85% грунт) и вариант 3 (95 км, 70% грунт). Хочет посмотреть оба на карте. Наводит мышь на карточку — соответствующий маршрут подсвечивается жирнее. Решает взять вариант 1 — больше грунта, пусть и длиннее. + +**UC-01.3 — Нет альтернатив** +> Точки A и B близко, только один разумный путь. Система показывает 1–2 варианта и поясняет: «Альтернативных маршрутов не найдено». + +#### Требования +- Минимум 1 маршрут, максимум 5 +- Варианты визуально различимы: разные цвета (палитра: синий, зелёный, фиолетовый, оранжевый, серый) +- Активный (выбранный) маршрут — жирнее и ярче +- Hover на карточке → подсветка маршрута на карте +- Клик на карточке → выбор маршрута +- Клик на маршруте на карте → выбор маршрута +- Кнопка «Сбросить» убирает все маршруты и маркеры + +#### Технические заметки +OSRM поддерживает `alternatives=true` (до 3 вариантов) и `alternatives=N` (до 5). Параметр передаётся в запросе к `/route/v1/driving/`. Дополнительно можно варьировать `radiuses` и `snapping` для получения более разнообразных вариантов. + +--- + +### 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. + +**UC-05.2 — GPX с промежуточными точками** +> Маршрут с 3 промежуточными точками. GPX содержит все waypoints + трек. В OsmAnd точки отображаются как промежуточные остановки. + +**UC-05.3 — Имя файла** +> Файл называется `enduro-YYYYMMDD-HHMMSS.gpx` — уникальное имя, не перезаписывает предыдущие. + +#### Требования +- GPX 1.1 формат +- Содержит: `` с геометрией маршрута + `` для всех точек (A, промежуточные, B) +- Метаданные: name, desc, time +- Генерация на фронте (из геометрии OSRM ответа) — не требует бэкенда +- Кнопка «Скачать 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) метки включаются как `` с именами. + +#### Требования +- Хранение: 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. Открытые вопросы для Славы + +1. **Количество альтернатив:** сколько вариантов хочешь видеть максимум? OSRM даёт до 5, но 3–4 обычно достаточно. +2. **Статистика — точность vs скорость:** расчёт по сегментам даёт ~5% погрешность, но быстро. Нужна точнее — будет медленнее. Устраивает приближение? +3. **Промежуточные точки — перетаскивание на карте:** нужно ли, или достаточно удалить + добавить заново? +4. **Флажки — синхронизация:** метки только в браузере (localStorage) или хочешь чтобы сохранялись на сервере (между устройствами)? +5. **GPX — треки или маршрут:** GPX может содержать трек (все точки геометрии) или маршрут (только waypoints для навигатора). Что нужно? Или оба варианта? + +--- + +*Документ готов к передаче Dev-агенту после согласования со Славой.*