docs(ET-006): BRD, ТЗ, AC, Test Plan — загрузка GPX-треков
This commit is contained in:
28
docs/work-items/ET-006/00-business-request.md
Normal file
28
docs/work-items/ET-006/00-business-request.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
type: business-request
|
||||
work_item_id: ET-006
|
||||
title: "Загрузка и визуализация GPX-треков на карте"
|
||||
created_at: 2026-05-22
|
||||
source: telegram
|
||||
requester: Слава
|
||||
---
|
||||
|
||||
# Бизнес-запрос — ET-006
|
||||
|
||||
## Исходная формулировка
|
||||
|
||||
> Нужно добавить опцию загрузки GPX-треков. Я хочу нажать на кнопку в интерфейсе, выбрать файл с треком и далее увидеть его визуализацию на карте.
|
||||
|
||||
## Уточнения (из диалога)
|
||||
|
||||
1. Формат: GPX 1.1 стандартный. Возможны несколько треков в одном файле.
|
||||
2. Визуализация: линия трека + waypoints как маркеры + профиль высот + статистика (длина, набор/сброс высоты).
|
||||
3. Кнопка: отдельная в интерфейсе.
|
||||
4. После загрузки: карта центрируется на треке (fit bounds).
|
||||
5. Можно загрузить несколько треков одновременно.
|
||||
6. Можно удалить загруженный трек.
|
||||
7. Хранение: только на время сессии (в памяти браузера), без сохранения на сервер.
|
||||
8. Каждый трек — свой цвет (для различения).
|
||||
9. Лимит файла: до 50 МБ, без упрощения (simplify) точек.
|
||||
10. Waypoints из GPX: показывать как маркеры на карте с именами.
|
||||
11. UX профиля высот и статистики — на усмотрение аналитика.
|
||||
81
docs/work-items/ET-006/01-brd.md
Normal file
81
docs/work-items/ET-006/01-brd.md
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
type: brd
|
||||
work_item_id: ET-006
|
||||
title: "BRD: Загрузка и визуализация GPX-треков"
|
||||
version: 1
|
||||
status: draft
|
||||
created_at: 2026-05-22
|
||||
authors:
|
||||
- "agent:analyst"
|
||||
---
|
||||
|
||||
# BRD — ET-006: Загрузка и визуализация GPX-треков
|
||||
|
||||
## 1. Цель
|
||||
|
||||
Дать пользователю возможность загрузить GPX-файл с треком и увидеть его на карте: линию маршрута, waypoints, профиль высот и статистику. Это позволяет визуально оценить чужой или ранее записанный трек перед поездкой.
|
||||
|
||||
## 2. Контекст
|
||||
|
||||
- Приложение уже умеет строить маршруты через OSRM и экспортировать их в GPX (кнопка «Скачать GPX» в sheet-route).
|
||||
- Обратная операция — импорт GPX — отсутствует.
|
||||
- Фаза PH-3 (Smart Route) в roadmap включает работу с GPX.
|
||||
- Фронтенд: MapLibre GL JS + vanilla JS, без фреймворков.
|
||||
- Backend-изменения не требуются — парсинг GPX происходит на клиенте.
|
||||
|
||||
## 3. Scope
|
||||
|
||||
### In scope
|
||||
|
||||
| # | Функция |
|
||||
|---|---------|
|
||||
| F-01 | Кнопка загрузки GPX в тулбаре карты |
|
||||
| F-02 | Парсинг GPX 1.1 на клиенте (XML → GeoJSON) |
|
||||
| F-03 | Поддержка нескольких треков в одном файле |
|
||||
| F-04 | Отрисовка линии трека на карте (каждый трек — свой цвет) |
|
||||
| F-05 | Отображение waypoints из GPX как маркеров с именами |
|
||||
| F-06 | Fit bounds — карта центрируется на загруженном треке |
|
||||
| F-07 | Загрузка нескольких файлов (треки накапливаются) |
|
||||
| F-08 | Удаление отдельного трека |
|
||||
| F-09 | Панель управления треками (список, цвет, удаление) |
|
||||
| F-10 | Профиль высот выбранного трека |
|
||||
| F-11 | Статистика трека: длина, набор высоты, сброс высоты, мин/макс высота |
|
||||
| F-12 | Лимит размера файла: 50 МБ |
|
||||
|
||||
### Out of scope
|
||||
|
||||
- Сохранение треков на сервер / в БД
|
||||
- Редактирование трека (обрезка, склейка)
|
||||
- Конвертация из других форматов (KML, FIT, TCX)
|
||||
- Упрощение (simplify) точек трека
|
||||
- Экспорт загруженного трека обратно в GPX
|
||||
- Роутинг по загруженному треку (snap to road)
|
||||
|
||||
## 4. Метрики успеха
|
||||
|
||||
| Метрика | Критерий |
|
||||
|---------|----------|
|
||||
| Загрузка файла | Файл до 50 МБ загружается и парсится без ошибок за ≤ 3 сек (на среднем устройстве) |
|
||||
| Визуализация | Трек отображается на карте как цветная линия |
|
||||
| Waypoints | Маркеры с именами видны на карте |
|
||||
| Fit bounds | Карта автоматически подстраивает zoom/center под трек |
|
||||
| Множественные треки | 5+ треков отображаются одновременно, различимы по цвету |
|
||||
| Удаление | Удалённый трек исчезает с карты и из панели |
|
||||
| Профиль высот | Отображается корректный график высот для выбранного трека |
|
||||
| Статистика | Длина, набор/сброс высоты отображаются корректно |
|
||||
| Не ломает существующий функционал | Роутинг, рельеф, POI, линейка работают как прежде |
|
||||
|
||||
## 5. Риски
|
||||
|
||||
| Риск | Вероятность | Влияние | Митигация |
|
||||
|------|-------------|---------|-----------|
|
||||
| Большой GPX (50 МБ, 500K+ точек) тормозит рендеринг | Средняя | Среднее | Использовать GeoJSON source + line layer (MapLibre оптимизирует); при необходимости — Web Worker для парсинга |
|
||||
| GPX без данных высот → профиль пустой | Средняя | Низкое | Показать сообщение «Данные высот отсутствуют» |
|
||||
| Невалидный GPX → ошибка парсинга | Низкая | Низкое | Показать пользователю понятное сообщение об ошибке |
|
||||
| Конфликт цветов треков с цветами маршрута OSRM | Низкая | Низкое | Использовать отдельную палитру, отличную от цветов роутинга |
|
||||
|
||||
## 6. Зависимости
|
||||
|
||||
- Нет внешних зависимостей
|
||||
- Только фронтенд (vanilla JS + MapLibre GL JS)
|
||||
- Парсинг XML: нативный DOMParser браузера
|
||||
278
docs/work-items/ET-006/02-trz.md
Normal file
278
docs/work-items/ET-006/02-trz.md
Normal file
@@ -0,0 +1,278 @@
|
||||
---
|
||||
type: trz
|
||||
work_item_id: ET-006
|
||||
title: "ТЗ: Загрузка и визуализация GPX-треков"
|
||||
version: 1
|
||||
status: draft
|
||||
created_at: 2026-05-22
|
||||
authors:
|
||||
- "agent:analyst"
|
||||
---
|
||||
|
||||
# ТЗ — ET-006: Загрузка и визуализация GPX-треков
|
||||
|
||||
## 1. Функциональные требования
|
||||
|
||||
### REQ-F-01: Кнопка загрузки GPX
|
||||
|
||||
- В правой панели кнопок карты (`#map-controls-r`) добавляется кнопка «GPX» с иконкой загрузки (стрелка вверх + документ).
|
||||
- Позиция: между кнопкой «Компас» и «Моё местоположение» (верхняя часть панели).
|
||||
- По нажатию открывается системный диалог выбора файла (`<input type="file" accept=".gpx">`).
|
||||
- Допускается множественный выбор (`multiple`).
|
||||
|
||||
### REQ-F-02: Парсинг GPX
|
||||
|
||||
- Парсинг выполняется на клиенте через `DOMParser` (XML → DOM → GeoJSON).
|
||||
- Поддерживается GPX 1.1 (namespace `http://www.topografix.com/GPX/1/1`).
|
||||
- Извлекаются:
|
||||
- `<trk>` → массив треков, каждый `<trkseg>` → массив точек `[lon, lat, ele?, time?]`
|
||||
- `<wpt>` → waypoints `{lon, lat, name?, ele?}`
|
||||
- `<rte>` → route points (трактуются как трек)
|
||||
- Если файл содержит несколько `<trk>`, каждый трек — отдельная сущность.
|
||||
- При ошибке парсинга — показать toast-уведомление: «Не удалось прочитать GPX-файл».
|
||||
|
||||
### REQ-F-03: Валидация
|
||||
|
||||
- Максимальный размер файла: 50 МБ. При превышении — toast: «Файл слишком большой (макс. 50 МБ)».
|
||||
- Если файл не содержит ни одного трека и ни одного waypoint — toast: «GPX-файл не содержит данных».
|
||||
|
||||
### REQ-F-04: Отрисовка трека на карте
|
||||
|
||||
- Каждый трек отрисовывается как `line` layer в MapLibre.
|
||||
- Source: GeoJSON (`LineString` или `MultiLineString`).
|
||||
- Цвет: из палитры 8 цветов, циклически. Палитра отличается от цветов роутинга (синий/зелёный/оранжевый).
|
||||
- Предлагаемая палитра: `#e6194b`, `#3cb44b`, `#ffe119`, `#4363d8`, `#f58231`, `#911eb4`, `#42d4f4`, `#f032e6`.
|
||||
- Толщина линии: 4px.
|
||||
- Opacity: 0.85.
|
||||
- Z-index: выше базовых слоёв, ниже маршрута OSRM (если активен).
|
||||
|
||||
### REQ-F-05: Отображение waypoints
|
||||
|
||||
- Каждый `<wpt>` отображается как маркер (circle layer + symbol layer для имени).
|
||||
- Цвет маркера: совпадает с цветом трека из того же файла (или нейтральный, если waypoints без трека).
|
||||
- Имя waypoint (`<name>`) отображается как label рядом с маркером.
|
||||
- Если имя отсутствует — маркер без подписи.
|
||||
|
||||
### REQ-F-06: Fit bounds
|
||||
|
||||
- После загрузки файла карта выполняет `fitBounds` по bbox всех точек загруженного файла.
|
||||
- Padding: 50px со всех сторон.
|
||||
- Если загружено несколько файлов подряд — fit bounds только по последнему загруженному.
|
||||
|
||||
### REQ-F-07: Множественная загрузка
|
||||
|
||||
- Треки из разных файлов накапливаются в сессии.
|
||||
- Каждый файл получает следующий цвет из палитры.
|
||||
- Максимальное количество одновременных треков: не ограничено (разумный предел — производительность браузера).
|
||||
|
||||
### REQ-F-08: Удаление трека
|
||||
|
||||
- В панели управления треками (REQ-F-09) у каждого трека есть кнопка удаления (иконка ✕).
|
||||
- При удалении: убирается line layer, source, маркеры waypoints с карты.
|
||||
- Если удалён активный (выбранный) трек — панель профиля высот скрывается.
|
||||
|
||||
### REQ-F-09: Панель управления треками (GPX Sheet)
|
||||
|
||||
- Реализуется как bottom sheet (`#sheet-gpx`), аналогично существующим sheet-route, sheet-recon.
|
||||
- Открывается автоматически при загрузке первого трека.
|
||||
- Содержит:
|
||||
- Заголовок «GPX-треки» с иконкой и кнопкой свернуть.
|
||||
- Список загруженных треков: цветной кружок + имя файла (без расширения) + кнопка удаления.
|
||||
- По тапу на трек в списке — он становится «активным» (выделяется), показывается его статистика и профиль высот.
|
||||
- Кнопка в тулбаре нижнего toolbar (`#toolbar`): «GPX» — переключает видимость sheet.
|
||||
|
||||
### REQ-F-10: Профиль высот
|
||||
|
||||
- Отображается в нижней части sheet-gpx (под списком треков) для активного трека.
|
||||
- График: canvas-элемент, ширина 100% sheet, высота 120px.
|
||||
- Ось X: расстояние от начала трека (км).
|
||||
- Ось Y: высота (м).
|
||||
- Линия графика: цвет трека.
|
||||
- Заливка под линией: цвет трека с opacity 0.2.
|
||||
- Если данные высот отсутствуют (`<ele>` нет) — показать текст: «Данные высот отсутствуют».
|
||||
- При наведении/тапе на график — показать tooltip с высотой и расстоянием, и подсветить соответствующую точку на карте (маркер-курсор).
|
||||
|
||||
### REQ-F-11: Статистика трека
|
||||
|
||||
- Отображается над профилем высот в sheet-gpx для активного трека.
|
||||
- Формат: компактная сетка (аналогично recon-grid).
|
||||
- Поля:
|
||||
- Длина (км) — сумма расстояний между точками (Haversine).
|
||||
- Набор высоты (м) — сумма положительных дельт `ele`.
|
||||
- Сброс высоты (м) — сумма отрицательных дельт `ele` (абсолютное значение).
|
||||
- Мин. высота (м).
|
||||
- Макс. высота (м).
|
||||
- Если данные высот отсутствуют — показать только длину, остальные поля: «—».
|
||||
|
||||
### REQ-F-12: Интерактивность трека на карте
|
||||
|
||||
- При клике на линию трека на карте — этот трек становится активным в панели (показывается статистика + профиль).
|
||||
- Курсор при наведении на трек: pointer.
|
||||
|
||||
## 2. Нефункциональные требования
|
||||
|
||||
### REQ-NF-01: Производительность
|
||||
|
||||
- Парсинг файла 50 МБ: ≤ 5 секунд на устройстве с 4 ГБ RAM.
|
||||
- Рендеринг трека 500K точек: без видимых фризов при pan/zoom (MapLibre оптимизирует GeoJSON line layers).
|
||||
- Во время парсинга показывать индикатор загрузки (spinner или moto-wheel).
|
||||
|
||||
### REQ-NF-02: Совместимость
|
||||
|
||||
- Работает в Chrome 90+, Firefox 90+, Safari 15+.
|
||||
- Работает на мобильных (touch events для профиля высот).
|
||||
|
||||
### REQ-NF-03: UX
|
||||
|
||||
- Кнопка загрузки доступна всегда, независимо от активного режима (роутинг, разведка и т.д.).
|
||||
- GPX-треки не конфликтуют с активным маршрутом OSRM — отображаются одновременно.
|
||||
- При ошибках — toast-уведомления (не alert/confirm).
|
||||
|
||||
### REQ-NF-04: Хранение
|
||||
|
||||
- Данные треков хранятся только в памяти (JS-переменные).
|
||||
- При перезагрузке страницы — все треки теряются.
|
||||
- Не используется localStorage/sessionStorage для данных треков (слишком большие).
|
||||
|
||||
## 3. UI-спецификация
|
||||
|
||||
### 3.1 Кнопка в правой панели (#map-controls-r)
|
||||
|
||||
```
|
||||
┌──────────┐
|
||||
│ ↑ GPX │ ← новая кнопка (между Компас и Геолокация)
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
- Класс: `map-btn`
|
||||
- ID: `btn-gpx-upload`
|
||||
- Иконка: стрелка вверх из документа (upload file)
|
||||
- Title: «Загрузить GPX»
|
||||
|
||||
### 3.2 Кнопка в нижнем тулбаре (#toolbar)
|
||||
|
||||
```
|
||||
[ Маршрут | Связка | Красивый | Разведка | Линейка | Поиск | Метка | GPX ]
|
||||
```
|
||||
|
||||
- Класс: `tb-btn`
|
||||
- ID: `tb-gpx`
|
||||
- Иконка: файл с линией (track)
|
||||
- Label: «GPX»
|
||||
- Действие: `toggleGpxSheet()`
|
||||
|
||||
### 3.3 Bottom sheet (#sheet-gpx)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ ═══ (handle) │
|
||||
│ 📄 GPX-треки [свернуть]│
|
||||
├─────────────────────────────────────┤
|
||||
│ 🔴 track_morning.gpx [✕] │
|
||||
│ 🔵 weekend_ride.gpx ✓ [✕] │ ← активный (выделен)
|
||||
│ 🟢 test_route.gpx [✕] │
|
||||
├─────────────────────────────────────┤
|
||||
│ СТАТИСТИКА │
|
||||
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
|
||||
│ │47км│ │820м│ │650м│ │120м│ │980м│ │
|
||||
│ │длин│ │наб.│ │сбр.│ │мин │ │макс│ │
|
||||
│ └────┘ └────┘ └────┘ └────┘ └────┘ │
|
||||
├─────────────────────────────────────┤
|
||||
│ ПРОФИЛЬ ВЫСОТ │
|
||||
│ ┌───────────────────────────────┐ │
|
||||
│ │ ╱╲ ╱╲╱╲ │ │
|
||||
│ │╱ ╲╱╲╱ ╲╱╲ │ │
|
||||
│ └───────────────────────────────┘ │
|
||||
│ 0 km 23.5 km 47 km │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.4 Toast-уведомления
|
||||
|
||||
- Позиция: верх экрана, по центру.
|
||||
- Автоскрытие: 4 секунды.
|
||||
- Стиль: аналогично `#ruler-toast`.
|
||||
|
||||
## 4. Данные
|
||||
|
||||
### Входные данные (GPX 1.1)
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gpx version="1.1" xmlns="http://www.topografix.com/GPX/1/1">
|
||||
<trk>
|
||||
<name>Morning Ride</name>
|
||||
<trkseg>
|
||||
<trkpt lat="55.7558" lon="37.6173"><ele>150</ele><time>2026-01-01T08:00:00Z</time></trkpt>
|
||||
...
|
||||
</trkseg>
|
||||
</trk>
|
||||
<wpt lat="55.76" lon="37.62">
|
||||
<name>Кафе</name>
|
||||
<ele>155</ele>
|
||||
</wpt>
|
||||
</gpx>
|
||||
```
|
||||
|
||||
### Внутренняя модель (JS)
|
||||
|
||||
```javascript
|
||||
// Массив загруженных GPX-файлов
|
||||
window.gpxTracks = [
|
||||
{
|
||||
id: 'gpx-1716336000000', // уникальный ID (timestamp)
|
||||
filename: 'morning_ride', // имя файла без расширения
|
||||
color: '#e6194b', // цвет из палитры
|
||||
tracks: [ // массив треков из файла
|
||||
{
|
||||
name: 'Morning Ride',
|
||||
points: [[lon, lat, ele, time], ...], // массив точек
|
||||
stats: { distanceKm, elevGain, elevLoss, eleMin, eleMax }
|
||||
}
|
||||
],
|
||||
waypoints: [
|
||||
{ lon, lat, name, ele }
|
||||
],
|
||||
sourceId: 'gpx-source-1716336000000',
|
||||
layerId: 'gpx-layer-1716336000000',
|
||||
waypointLayerId: 'gpx-wpt-1716336000000'
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
## 5. Алгоритмы
|
||||
|
||||
### 5.1 Расчёт расстояния (Haversine)
|
||||
|
||||
Сумма расстояний между последовательными точками трека. Формула Haversine для каждой пары.
|
||||
|
||||
### 5.2 Расчёт набора/сброса высоты
|
||||
|
||||
```
|
||||
elevGain = Σ max(0, ele[i+1] - ele[i]) для всех i
|
||||
elevLoss = Σ max(0, ele[i] - ele[i+1]) для всех i
|
||||
```
|
||||
|
||||
Фильтрация шума: игнорировать дельты < 2 м (GPS-шум).
|
||||
|
||||
### 5.3 Палитра цветов
|
||||
|
||||
Циклический массив из 8 цветов. Индекс = `gpxTracks.length % 8` на момент добавления.
|
||||
|
||||
## 6. Файловая структура изменений
|
||||
|
||||
```
|
||||
src/web/
|
||||
├── index.html # + кнопка в #map-controls-r, + sheet-gpx, + tb-btn
|
||||
├── app.js # + gpx-модуль (парсинг, рендеринг, управление)
|
||||
├── app.css # + стили sheet-gpx, профиля высот, toast
|
||||
```
|
||||
|
||||
Альтернативно, GPX-логику можно вынести в отдельный файл `gpx.js` (аналогично `units.js`).
|
||||
|
||||
## 7. Взаимодействие с существующими режимами
|
||||
|
||||
- GPX-треки отображаются **параллельно** с любым активным режимом (роутинг, разведка, красивый маршрут).
|
||||
- Z-order: GPX-треки ниже активного маршрута OSRM, но выше базовых слоёв (trails, terrain).
|
||||
- Кнопка загрузки в `#map-controls-r` доступна всегда.
|
||||
- Кнопка «GPX» в toolbar переключает sheet, но не деактивирует другие режимы.
|
||||
234
docs/work-items/ET-006/03-acceptance-criteria.md
Normal file
234
docs/work-items/ET-006/03-acceptance-criteria.md
Normal file
@@ -0,0 +1,234 @@
|
||||
---
|
||||
type: acceptance-criteria
|
||||
work_item_id: ET-006
|
||||
title: "AC: Загрузка и визуализация GPX-треков"
|
||||
version: 1
|
||||
status: draft
|
||||
created_at: 2026-05-22
|
||||
authors:
|
||||
- "agent:analyst"
|
||||
---
|
||||
|
||||
# Acceptance Criteria — ET-006: Загрузка и визуализация GPX-треков
|
||||
|
||||
## AC-01: Загрузка файла через кнопку
|
||||
|
||||
```gherkin
|
||||
Feature: Загрузка GPX-файла
|
||||
|
||||
Scenario: Успешная загрузка одного файла
|
||||
Given пользователь находится на карте
|
||||
When пользователь нажимает кнопку «Загрузить GPX» в правой панели
|
||||
And выбирает валидный GPX-файл размером < 50 МБ
|
||||
Then файл парсится без ошибок
|
||||
And трек отображается на карте цветной линией
|
||||
And карта выполняет fit bounds по треку
|
||||
And в панели GPX-треков появляется запись с именем файла
|
||||
|
||||
Scenario: Файл превышает лимит
|
||||
Given пользователь находится на карте
|
||||
When пользователь выбирает GPX-файл размером > 50 МБ
|
||||
Then показывается toast «Файл слишком большой (макс. 50 МБ)»
|
||||
And трек не загружается
|
||||
|
||||
Scenario: Невалидный файл
|
||||
Given пользователь находится на карте
|
||||
When пользователь выбирает файл с невалидным XML
|
||||
Then показывается toast «Не удалось прочитать GPX-файл»
|
||||
And трек не загружается
|
||||
|
||||
Scenario: Пустой GPX (без треков и waypoints)
|
||||
Given пользователь находится на карте
|
||||
When пользователь выбирает GPX-файл без <trk> и без <wpt>
|
||||
Then показывается toast «GPX-файл не содержит данных»
|
||||
```
|
||||
|
||||
## AC-02: Визуализация трека
|
||||
|
||||
```gherkin
|
||||
Feature: Отрисовка трека на карте
|
||||
|
||||
Scenario: Один трек в файле
|
||||
Given загружен GPX-файл с одним треком
|
||||
Then на карте отображается линия трека
|
||||
And линия имеет цвет из палитры
|
||||
And толщина линии 4px
|
||||
|
||||
Scenario: Несколько треков в одном файле
|
||||
Given загружен GPX-файл с 3 треками
|
||||
Then на карте отображаются 3 линии
|
||||
And все линии одного цвета (цвет файла)
|
||||
And в панели — одна запись (имя файла) с 3 треками внутри
|
||||
|
||||
Scenario: Несколько файлов
|
||||
Given загружены 3 GPX-файла
|
||||
Then на карте отображаются треки из всех файлов
|
||||
And каждый файл имеет свой цвет из палитры
|
||||
And в панели — 3 записи
|
||||
```
|
||||
|
||||
## AC-03: Waypoints
|
||||
|
||||
```gherkin
|
||||
Feature: Отображение waypoints
|
||||
|
||||
Scenario: Waypoints с именами
|
||||
Given загружен GPX-файл с waypoints, у которых есть <name>
|
||||
Then на карте отображаются маркеры в позициях waypoints
|
||||
And рядом с каждым маркером отображается имя
|
||||
|
||||
Scenario: Waypoints без имён
|
||||
Given загружен GPX-файл с waypoints без <name>
|
||||
Then на карте отображаются маркеры без подписей
|
||||
|
||||
Scenario: Файл без waypoints
|
||||
Given загружен GPX-файл без <wpt>
|
||||
Then маркеры waypoints не отображаются
|
||||
And трек отображается нормально
|
||||
```
|
||||
|
||||
## AC-04: Fit bounds
|
||||
|
||||
```gherkin
|
||||
Feature: Автоцентрирование карты
|
||||
|
||||
Scenario: Карта центрируется на загруженном треке
|
||||
Given карта показывает произвольную область
|
||||
When пользователь загружает GPX-файл
|
||||
Then карта выполняет fit bounds по всем точкам файла
|
||||
And padding составляет 50px со всех сторон
|
||||
|
||||
Scenario: Загрузка второго файла
|
||||
Given на карте уже есть загруженный трек
|
||||
When пользователь загружает второй GPX-файл
|
||||
Then карта выполняет fit bounds только по второму файлу
|
||||
```
|
||||
|
||||
## AC-05: Удаление трека
|
||||
|
||||
```gherkin
|
||||
Feature: Удаление загруженного трека
|
||||
|
||||
Scenario: Удаление трека из панели
|
||||
Given загружены 2 GPX-файла
|
||||
When пользователь нажимает кнопку удаления (✕) у первого трека
|
||||
Then первый трек исчезает с карты (линия + waypoints)
|
||||
And первый трек исчезает из панели
|
||||
And второй трек остаётся на карте
|
||||
|
||||
Scenario: Удаление активного трека
|
||||
Given загружены 2 GPX-файла, второй — активный (показана статистика)
|
||||
When пользователь удаляет второй трек
|
||||
Then профиль высот и статистика скрываются
|
||||
And первый трек остаётся на карте
|
||||
And первый трек не становится автоматически активным
|
||||
|
||||
Scenario: Удаление последнего трека
|
||||
Given загружен 1 GPX-файл
|
||||
When пользователь удаляет его
|
||||
Then карта пуста (нет GPX-слоёв)
|
||||
And панель GPX показывает пустое состояние
|
||||
```
|
||||
|
||||
## AC-06: Панель управления (sheet-gpx)
|
||||
|
||||
```gherkin
|
||||
Feature: Панель GPX-треков
|
||||
|
||||
Scenario: Открытие панели при загрузке
|
||||
Given панель GPX закрыта
|
||||
When пользователь загружает первый GPX-файл
|
||||
Then панель GPX открывается автоматически
|
||||
|
||||
Scenario: Переключение через toolbar
|
||||
Given панель GPX закрыта
|
||||
When пользователь нажимает кнопку «GPX» в нижнем тулбаре
|
||||
Then панель GPX открывается
|
||||
|
||||
Scenario: Выбор активного трека
|
||||
Given загружены 3 GPX-файла
|
||||
When пользователь тапает на второй трек в списке
|
||||
Then второй трек выделяется визуально
|
||||
And показывается его статистика и профиль высот
|
||||
```
|
||||
|
||||
## AC-07: Профиль высот
|
||||
|
||||
```gherkin
|
||||
Feature: Профиль высот
|
||||
|
||||
Scenario: Трек с данными высот
|
||||
Given выбран активный трек с данными <ele>
|
||||
Then в панели отображается график профиля высот
|
||||
And ось X — расстояние (км)
|
||||
And ось Y — высота (м)
|
||||
And линия графика — цвет трека
|
||||
|
||||
Scenario: Трек без данных высот
|
||||
Given выбран активный трек без данных <ele>
|
||||
Then вместо графика отображается текст «Данные высот отсутствуют»
|
||||
|
||||
Scenario: Интерактивность профиля
|
||||
Given отображается профиль высот
|
||||
When пользователь наводит курсор (или тапает) на точку графика
|
||||
Then показывается tooltip с высотой и расстоянием
|
||||
And на карте подсвечивается соответствующая точка трека
|
||||
```
|
||||
|
||||
## AC-08: Статистика трека
|
||||
|
||||
```gherkin
|
||||
Feature: Статистика трека
|
||||
|
||||
Scenario: Полная статистика (с высотами)
|
||||
Given выбран активный трек с данными <ele>
|
||||
Then отображаются: длина (км), набор высоты (м), сброс высоты (м), мин. высота (м), макс. высота (м)
|
||||
|
||||
Scenario: Частичная статистика (без высот)
|
||||
Given выбран активный трек без данных <ele>
|
||||
Then отображается только длина (км)
|
||||
And остальные поля показывают «—»
|
||||
```
|
||||
|
||||
## AC-09: Интерактивность на карте
|
||||
|
||||
```gherkin
|
||||
Feature: Клик по треку на карте
|
||||
|
||||
Scenario: Выбор трека кликом
|
||||
Given на карте отображаются 3 трека из разных файлов
|
||||
When пользователь кликает на линию второго трека
|
||||
Then второй трек становится активным в панели
|
||||
And показывается его статистика и профиль высот
|
||||
```
|
||||
|
||||
## AC-10: Совместимость с другими режимами
|
||||
|
||||
```gherkin
|
||||
Feature: Параллельная работа с роутингом
|
||||
|
||||
Scenario: GPX + активный маршрут
|
||||
Given пользователь построил маршрут через OSRM
|
||||
And загрузил GPX-файл
|
||||
Then оба отображаются на карте одновременно
|
||||
And маршрут OSRM визуально выше GPX-трека
|
||||
And оба интерактивны
|
||||
|
||||
Scenario: GPX + режим разведки
|
||||
Given пользователь в режиме разведки
|
||||
When загружает GPX-файл
|
||||
Then трек отображается на карте
|
||||
And режим разведки продолжает работать
|
||||
```
|
||||
|
||||
## AC-11: Индикатор загрузки
|
||||
|
||||
```gherkin
|
||||
Feature: Индикатор при парсинге
|
||||
|
||||
Scenario: Большой файл
|
||||
Given пользователь выбирает GPX-файл > 10 МБ
|
||||
Then показывается индикатор загрузки (spinner)
|
||||
And после завершения парсинга индикатор скрывается
|
||||
And трек отображается на карте
|
||||
```
|
||||
248
docs/work-items/ET-006/04-test-plan.yaml
Normal file
248
docs/work-items/ET-006/04-test-plan.yaml
Normal file
@@ -0,0 +1,248 @@
|
||||
---
|
||||
type: test-plan
|
||||
work_item_id: ET-006
|
||||
title: "Test Plan: Загрузка и визуализация GPX-треков"
|
||||
version: 1
|
||||
status: draft
|
||||
created_at: 2026-05-22
|
||||
authors:
|
||||
- "agent:analyst"
|
||||
|
||||
test_suites:
|
||||
|
||||
- name: unit-gpx-parser
|
||||
type: unit
|
||||
description: "Парсинг GPX XML → внутренняя модель"
|
||||
cases:
|
||||
- id: U-01
|
||||
name: "Парсинг валидного GPX 1.1 с одним треком"
|
||||
input: "GPX-файл с 1 trk, 1 trkseg, 10 trkpt (lat, lon, ele, time)"
|
||||
expected: "Возвращает объект с 1 треком, 10 точками, корректными координатами и высотами"
|
||||
|
||||
- id: U-02
|
||||
name: "Парсинг GPX с несколькими треками"
|
||||
input: "GPX-файл с 3 trk"
|
||||
expected: "Возвращает массив из 3 треков"
|
||||
|
||||
- id: U-03
|
||||
name: "Парсинг waypoints"
|
||||
input: "GPX-файл с 5 wpt (lat, lon, name, ele)"
|
||||
expected: "Возвращает массив из 5 waypoints с именами и координатами"
|
||||
|
||||
- id: U-04
|
||||
name: "Парсинг route (rte)"
|
||||
input: "GPX-файл с 1 rte, 20 rtept"
|
||||
expected: "Возвращает как трек с 20 точками"
|
||||
|
||||
- id: U-05
|
||||
name: "GPX без данных высот"
|
||||
input: "GPX-файл с trkpt без <ele>"
|
||||
expected: "Точки имеют ele=null, stats.elevGain=null"
|
||||
|
||||
- id: U-06
|
||||
name: "Невалидный XML"
|
||||
input: "Файл с битым XML"
|
||||
expected: "Выбрасывает ошибку с сообщением"
|
||||
|
||||
- id: U-07
|
||||
name: "Пустой GPX (нет trk, wpt, rte)"
|
||||
input: "Валидный XML, но без данных"
|
||||
expected: "Выбрасывает ошибку 'no data'"
|
||||
|
||||
- id: U-08
|
||||
name: "GPX с namespace и без"
|
||||
input: "GPX без xmlns атрибута"
|
||||
expected: "Парсится корректно (fallback без namespace)"
|
||||
|
||||
- name: unit-gpx-stats
|
||||
type: unit
|
||||
description: "Расчёт статистики трека"
|
||||
cases:
|
||||
- id: U-10
|
||||
name: "Расчёт длины (Haversine)"
|
||||
input: "Трек из 3 точек: [37.6, 55.7], [37.7, 55.8], [37.8, 55.9]"
|
||||
expected: "Длина ≈ 28.3 км (±0.5 км)"
|
||||
|
||||
- id: U-11
|
||||
name: "Расчёт набора высоты"
|
||||
input: "Точки с ele: [100, 150, 120, 200, 180]"
|
||||
expected: "elevGain = 130 м (50 + 80), elevLoss = 70 м (30 + 20)"
|
||||
|
||||
- id: U-12
|
||||
name: "Фильтрация шума высот (дельта < 2м)"
|
||||
input: "Точки с ele: [100, 101, 100, 101, 150]"
|
||||
expected: "elevGain = 50 м (только 100→150), мелкие колебания игнорируются"
|
||||
|
||||
- id: U-13
|
||||
name: "Мин/макс высота"
|
||||
input: "Точки с ele: [100, 250, 80, 300, 150]"
|
||||
expected: "eleMin=80, eleMax=300"
|
||||
|
||||
- id: U-14
|
||||
name: "Статистика без данных высот"
|
||||
input: "Точки без ele"
|
||||
expected: "distanceKm рассчитан, elevGain/elevLoss/eleMin/eleMax = null"
|
||||
|
||||
- name: unit-gpx-colors
|
||||
type: unit
|
||||
description: "Назначение цветов из палитры"
|
||||
cases:
|
||||
- id: U-20
|
||||
name: "Первый файл получает первый цвет"
|
||||
input: "Загрузка первого файла"
|
||||
expected: "Цвет = #e6194b"
|
||||
|
||||
- id: U-21
|
||||
name: "Девятый файл получает первый цвет (цикл)"
|
||||
input: "Загрузка 9-го файла"
|
||||
expected: "Цвет = #e6194b (индекс 8 % 8 = 0)"
|
||||
|
||||
- name: integration-gpx-map
|
||||
type: integration
|
||||
description: "Интеграция GPX с MapLibre"
|
||||
cases:
|
||||
- id: I-01
|
||||
name: "Добавление source и layer при загрузке"
|
||||
input: "Загрузка валидного GPX"
|
||||
expected: "map.getSource(sourceId) !== null, map.getLayer(layerId) !== null"
|
||||
|
||||
- id: I-02
|
||||
name: "Удаление source и layer при удалении трека"
|
||||
input: "Удаление загруженного трека"
|
||||
expected: "map.getSource(sourceId) === null, map.getLayer(layerId) === null"
|
||||
|
||||
- id: I-03
|
||||
name: "Fit bounds после загрузки"
|
||||
input: "Загрузка GPX с bbox [37.5, 55.6, 37.9, 55.9]"
|
||||
expected: "map.getBounds() содержит указанный bbox"
|
||||
|
||||
- id: I-04
|
||||
name: "Waypoints как маркеры"
|
||||
input: "GPX с 3 waypoints"
|
||||
expected: "На карте 3 маркера с подписями"
|
||||
|
||||
- id: I-05
|
||||
name: "Клик по треку активирует его"
|
||||
input: "Клик на линию трека"
|
||||
expected: "Трек становится активным, показывается статистика"
|
||||
|
||||
- id: I-06
|
||||
name: "GPX-слои ниже маршрута OSRM"
|
||||
input: "Загружен GPX + построен маршрут"
|
||||
expected: "Layer order: gpx-layer before route-layer"
|
||||
|
||||
- name: integration-gpx-elevation
|
||||
type: integration
|
||||
description: "Профиль высот"
|
||||
cases:
|
||||
- id: I-10
|
||||
name: "Рендеринг canvas профиля"
|
||||
input: "Активный трек с 100 точками и ele"
|
||||
expected: "Canvas отрисован, ширина = ширина контейнера, высота = 120px"
|
||||
|
||||
- id: I-11
|
||||
name: "Tooltip при наведении"
|
||||
input: "Mousemove по canvas на позиции 50%"
|
||||
expected: "Tooltip показывает высоту и расстояние средней точки"
|
||||
|
||||
- id: I-12
|
||||
name: "Маркер-курсор на карте при наведении на профиль"
|
||||
input: "Mousemove по canvas"
|
||||
expected: "На карте появляется маркер в соответствующей точке трека"
|
||||
|
||||
- name: e2e-gpx-workflow
|
||||
type: e2e
|
||||
description: "Полный пользовательский сценарий"
|
||||
cases:
|
||||
- id: E-01
|
||||
name: "Загрузка → визуализация → статистика → удаление"
|
||||
steps:
|
||||
- "Открыть приложение"
|
||||
- "Нажать кнопку GPX в правой панели"
|
||||
- "Выбрать файл test-track.gpx (1 трек, 500 точек, с ele)"
|
||||
- "Убедиться: трек на карте, панель открыта, статистика показана"
|
||||
- "Проверить профиль высот"
|
||||
- "Удалить трек"
|
||||
- "Убедиться: карта пуста, панель пуста"
|
||||
|
||||
- id: E-02
|
||||
name: "Множественная загрузка и различение цветов"
|
||||
steps:
|
||||
- "Загрузить 3 GPX-файла последовательно"
|
||||
- "Убедиться: 3 трека на карте разных цветов"
|
||||
- "Убедиться: 3 записи в панели"
|
||||
- "Кликнуть на второй трек в панели"
|
||||
- "Убедиться: показана статистика второго трека"
|
||||
|
||||
- id: E-03
|
||||
name: "Большой файл (50 МБ)"
|
||||
steps:
|
||||
- "Загрузить GPX-файл ~50 МБ"
|
||||
- "Убедиться: показан индикатор загрузки"
|
||||
- "Убедиться: трек отображается после парсинга"
|
||||
- "Убедиться: pan/zoom работают без фризов"
|
||||
|
||||
- id: E-04
|
||||
name: "Файл с waypoints"
|
||||
steps:
|
||||
- "Загрузить GPX с 5 waypoints"
|
||||
- "Убедиться: 5 маркеров на карте с подписями"
|
||||
- "Удалить трек"
|
||||
- "Убедиться: маркеры исчезли"
|
||||
|
||||
- id: E-05
|
||||
name: "GPX параллельно с роутингом"
|
||||
steps:
|
||||
- "Построить маршрут через OSRM"
|
||||
- "Загрузить GPX-файл"
|
||||
- "Убедиться: оба отображаются, маршрут выше GPX"
|
||||
- "Удалить GPX"
|
||||
- "Убедиться: маршрут не затронут"
|
||||
|
||||
- id: E-06
|
||||
name: "Ошибки: невалидный файл, превышение лимита"
|
||||
steps:
|
||||
- "Попытаться загрузить .txt файл переименованный в .gpx"
|
||||
- "Убедиться: toast с ошибкой"
|
||||
- "Попытаться загрузить файл > 50 МБ"
|
||||
- "Убедиться: toast с ошибкой"
|
||||
- "Убедиться: предыдущие треки не затронуты"
|
||||
|
||||
- id: E-07
|
||||
name: "Мобильное устройство (touch)"
|
||||
steps:
|
||||
- "Открыть на мобильном (или эмуляция)"
|
||||
- "Загрузить GPX"
|
||||
- "Тапнуть на трек в панели"
|
||||
- "Тапнуть на профиль высот"
|
||||
- "Убедиться: tooltip и маркер-курсор работают"
|
||||
|
||||
- name: e2e-gpx-toolbar
|
||||
type: e2e
|
||||
description: "Кнопка GPX в toolbar"
|
||||
cases:
|
||||
- id: E-10
|
||||
name: "Переключение панели через toolbar"
|
||||
steps:
|
||||
- "Нажать кнопку GPX в нижнем тулбаре"
|
||||
- "Убедиться: панель GPX открылась"
|
||||
- "Нажать ещё раз"
|
||||
- "Убедиться: панель свернулась"
|
||||
|
||||
test_data:
|
||||
- name: "test-track-simple.gpx"
|
||||
description: "1 трек, 10 точек, с ele и time"
|
||||
- name: "test-track-multi.gpx"
|
||||
description: "3 трека в одном файле"
|
||||
- name: "test-track-waypoints.gpx"
|
||||
description: "1 трек + 5 waypoints с именами"
|
||||
- name: "test-track-no-ele.gpx"
|
||||
description: "1 трек без данных высот"
|
||||
- name: "test-track-large.gpx"
|
||||
description: "~50 МБ, 500K+ точек"
|
||||
- name: "test-track-invalid.gpx"
|
||||
description: "Битый XML"
|
||||
- name: "test-track-empty.gpx"
|
||||
description: "Валидный GPX без trk/wpt/rte"
|
||||
- name: "test-track-route.gpx"
|
||||
description: "GPX с <rte> вместо <trk>"
|
||||
Reference in New Issue
Block a user