16 KiB
16 KiB
type, work_item_id, title, version, status, created_at, updated_at, authors
| type | work_item_id | title | version | status | created_at | updated_at | authors | |
|---|---|---|---|---|---|---|---|---|
| trz | ET-006 | ТЗ: Загрузка и визуализация GPX-треков | 2 | approved | 2026-05-22 | 2026-05-22 |
|
ТЗ — 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: Отрисовка трека на карте
- Каждый трек отрисовывается как
linelayer в 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.
REQ-F-13: Сохранение треков при переключении стиля карты
- При переключении стиля карты (тёмная тема, восстановление слоёв рельефа) вызывается
map.setStyle(), который удаляет все пользовательские source и layer. - После смены стиля все загруженные GPX-треки должны быть автоматически восстановлены: линии треков, source, waypoints-маркеры.
- Восстановление выполняется в функции
rebuildMapOverlays()(src/web/app.js) — по аналогии с уже реализованными там маршрутом OSRM, разведкой и scenic-маршрутами. - Данные треков (
window.gpxTracks) хранятся в памяти и приsetStyle()не теряются — пересоздаются только объекты карты (source / layer / маркеры). - Активный трек, его статистика и профиль высот должны сохраняться после переключения стиля.
- Z-order GPX-слоёв (см. REQ-F-04) корректно восстанавливается и после смены стиля.
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 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)
// Массив загруженных 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, но не деактивирует другие режимы.
- При смене стиля карты (
setStyle— тёмная тема, слои рельефа) GPX-слои восстанавливаются черезrebuildMapOverlays()— см. REQ-F-13.