Files
enduro-trails/docs/work-items/ET-006/02-trz.md

16 KiB
Raw Blame History

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
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.

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.