229 lines
23 KiB
Markdown
229 lines
23 KiB
Markdown
---
|
||
type: brd
|
||
work_item_id: ET-008
|
||
title: "BRD: GPS-треки с публичных платформ на карте"
|
||
version: 2
|
||
status: draft
|
||
created_at: 2026-06-01
|
||
updated_at: 2026-06-01
|
||
changelog:
|
||
- "v2 (2026-06-01): полная переработка под реальный business request — серверная агрегация из ≥3 источников по региону, дедупликация, фильтры по активности и источнику, расширяемость на регионы. Предыдущая v1 трактовала задачу как URL-импорт + OSM live-поиск, что не соответствовало бизнес-цели."
|
||
authors:
|
||
- "agent:analyst"
|
||
---
|
||
|
||
# BRD — ET-008: GPS-треки с публичных платформ на карте
|
||
|
||
## 1. Цель
|
||
|
||
Показать пользователю Enduro Trails реальные GPS-треки, **заранее
|
||
собранные с публичных платформ** (Wikiloc, Offmaps.ru, Тропинки.ру,
|
||
EnduroRussia.ru, OSM Public GPS Traces, Nakarte.me, Komoot и т.п.) и
|
||
сохранённые на сервере. Цель — три практические задачи мотоэндуриста:
|
||
|
||
1. **Видеть реальные дороги/тропы, которых нет в OSM.** Vector-тайлы
|
||
`trails` показывают только OSM-данные; реальные грунтовки/тропы из
|
||
GPS-логов дают информацию, которой в OSM никогда не было.
|
||
2. **Понимать, где реально ездят.** Плотность публичных треков на
|
||
участке — прямая прокси-метрика популярности и проходимости.
|
||
3. **Выявлять «мёртвые» дороги.** OSM-грунтовка, не покрытая ни одним
|
||
публичным треком за последние N лет — кандидат на «давно никто не
|
||
ездит, может быть заросла».
|
||
|
||
ET-008 даёт **новый отдельный слой** (поверх `trails`, ниже маршрута
|
||
OSRM) с отдельными линиями (не heatmap), цветом по источнику или типу
|
||
активности, с UI-фильтрами.
|
||
|
||
## 2. Контекст
|
||
|
||
- Vector-тайлы из OSM (`/api/tiles/{z}/{x}/{y}.mvt`) уже отдают
|
||
грунтовки/тропы/POI. ET-008 их **не заменяет** — добавляет
|
||
параллельный слой публичных GPS-треков.
|
||
- ET-006 реализовал клиентский импорт GPX-файлов пользователем
|
||
(`window.gpxTracks`, `#sheet-gpx`). Это **другой сценарий**: ET-006 —
|
||
«мой трек в памяти браузера», ET-008 — «треки сообщества с сервера».
|
||
Модели данных не пересекаются.
|
||
- Стек БД: SQLite + Spatialite. Для ET-008 заводится **отдельная** БД
|
||
`data/gps_tracks.sqlite` — чтобы не смешивать данные с основной
|
||
`centralfederal.sqlite` и иметь независимый цикл обновления / бэкапа.
|
||
- Pipeline сбора — **офлайн-скрипт на cron**, не runtime. На запрос
|
||
пользователя сервер отдаёт уже собранные данные.
|
||
- Регион MVP: **ЦФО + Чувашия** (18 субъектов ЦФО + Чувашская
|
||
Республика, площадь ≈ 670 тыс. км²). Расширение на другие регионы —
|
||
через конфиг-файл.
|
||
|
||
## 3. Scope
|
||
|
||
### In scope
|
||
|
||
| # | Функция |
|
||
| ----- | ----------------------------------------------------------------------------- |
|
||
| F-01 | Pipeline сбора GPX-треков с ≥ 3 публичных источников |
|
||
| F-02 | Хранение треков в SQLite + Spatialite: геометрия + метаданные |
|
||
| F-03 | Дедупликация: один реальный трек = одна запись, даже если найден в N источниках |
|
||
| F-04 | Метаданные трека: источник, URL, тип активности, дата, длина, кол-во точек, автор (если публичен) |
|
||
| F-05 | API endpoint `GET /api/gps-tracks?bbox=…&activity=…&source=…` для отдачи треков клиенту |
|
||
| F-06 | Векторные тайлы публичных треков `GET /api/gps-tracks/tiles/{z}/{x}/{y}.mvt` для эффективной отдачи на низких зумах |
|
||
| F-07 | Визуализация **отдельными линиями** (не heatmap) на карте |
|
||
| F-08 | Цветовая дифференциация: палитра по источнику (default) с возможностью переключения на палитру по типу активности |
|
||
| F-09 | UI-чекбокс «Публичные треки» в `#terrain-popup`: включить/выключить весь слой |
|
||
| F-10 | UI-фильтр по типу активности (enduro / moto / offroad / bicycle / hike / other), multi-select |
|
||
| F-11 | UI-фильтр по источнику, multi-select |
|
||
| F-12 | Конфиг-файл регионов: bbox + название + список активных источников |
|
||
| F-13 | MVP-датасет: ЦФО + Чувашия, ≥ 5000 треков |
|
||
| F-14 | Совместимость со сменой стиля карты (через `rebuildMapOverlays()` по аналогии с ET-006 REQ-F-13 и ET-007 REQ-F-06) |
|
||
| F-15 | Совместимость со спутниковой подложкой (ET-007): треки видны на спутнике с halo для контраста |
|
||
| F-16 | Клик по треку → popup с метаданными: имя/тип активности/источник/дата/длина и ссылка на оригинал |
|
||
| F-17 | Health-эндпоинт `/api/gps-tracks/health`: дата последнего сбора, кол-во треков по источникам, ошибки последнего прогона |
|
||
|
||
### Out of scope
|
||
|
||
- **Real-time сбор**: только периодический офлайн (cron, 1–2 раза в неделю).
|
||
- **Wikiloc Premium / Komoot Premium / любые платные API**: используем
|
||
только бесплатные публичные endpoints и публичные HTML-страницы там,
|
||
где это разрешено ToS источника.
|
||
- **Strava Metro как источник линий**: это heatmap, не отдельные треки —
|
||
не соответствует бизнес-требованию «отдельные линии». Опционально в
|
||
будущем — как метрика популярности для валидации, не для MVP.
|
||
- **OAuth-интеграции** (вход пользователя в Strava/Komoot со своим
|
||
аккаунтом): отдельный work item.
|
||
- **Загрузка пользователем своих треков в общую базу**: отдельный work item.
|
||
- **Редактирование/обрезка треков на стороне сервера**.
|
||
- **Конвертация из KML/FIT/TCX**: pipeline принимает только GPX.
|
||
- **Snap-to-road** для треков (выравнивание под дороги OSM).
|
||
- **Учёт сложности (drag-level) внутри трека**: фильтр только по типу
|
||
активности; сложность — отдельная задача (требует анализа геометрии и
|
||
скорости).
|
||
|
||
## 4. Источники (с оценкой реализуемости в MVP)
|
||
|
||
Анализ каждого источника из business request с честной оценкой
|
||
доступности и юридических условий:
|
||
|
||
| # | Источник | Тип доступа | MVP | Комментарий |
|
||
| - | ------------------------- | ------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------- |
|
||
| 1 | OSM Public GPS Traces | Документированный API | **да** | `/api/0.6/trackpoints?bbox=…&page=…`. Лицензия ODbL, атрибуция OSM. Объём для ЦФО оценочно ≈ 50–100K точек, тыс. треков. |
|
||
| 2 | EnduroRussia.ru | Web (HTML + GPX-ссылки) | **да** | По регионам, есть прямые GPX-ссылки. Лицензия и условия скрейпинга — фиксируются в ADR `06-adr/source-licensing.md` до начала разработки. |
|
||
| 3 | Тропинки.ру / ttrails.ru | Web (GPX/KML) | **да** | Эндуро-категория, GPX доступен без авторизации. Условия скрейпинга — то же ADR. |
|
||
| 4 | Offmaps.ru | Web | пилот | Требует ревью формата выдачи и лицензии. Подключаем в пилот-режим если ADR разрешает. |
|
||
| 5 | Nakarte.me | Public layers + JSON | пилот | Агрегатор: содержит ссылки на треки внешних источников. Может быть «бесплатным» путём к Wikiloc/Strava-treki косвенно. Требует ревью лицензии. |
|
||
| 6 | Wikiloc | API (премиум) | нет | Бесплатный публичный API не отдаёт GPX. Без премиума — невозможно. **Откладываем.** |
|
||
| 7 | Komoot | API (партнёрский) | нет | Публичный API ограничен, нет публичной выдачи GPX по bbox. **Откладываем.** |
|
||
| 8 | Strava Metro | API (исследовательский) | нет | Heatmap, не отдельные треки → не соответствует бизнес-требованию. **Out of scope.** |
|
||
|
||
**MVP-минимум: 3 источника живут в продакшне** — обязательно OSM
|
||
(гарантированно доступен), плюс минимум 2 из (2)–(5) по результатам
|
||
ADR-ревью лицензий.
|
||
|
||
### Юридический минимум
|
||
|
||
Перед началом разработки каждого источника (2)–(5) — **обязательный
|
||
ADR** `docs/work-items/ET-008/06-adr/<source>-licensing.md`:
|
||
|
||
1. Что говорит ToS источника о скрейпинге / массовой загрузке GPX.
|
||
2. Что говорит robots.txt.
|
||
3. На каких условиях разрешена публикация чужих треков
|
||
(имя/анонимизация/атрибуция).
|
||
4. Rate-limit, который мы будем соблюдать (default: 1 req / 5 sec, с
|
||
корректным `User-Agent: enduro-trails/<v> (+contact)`).
|
||
5. Список метаданных, которые **нельзя** сохранять/публиковать (личные
|
||
адреса, имена при отсутствии явного согласия).
|
||
|
||
Источник без явного зелёного света в ADR — **не включается** в pipeline.
|
||
|
||
## 5. Метрики успеха
|
||
|
||
| Метрика | Критерий MVP |
|
||
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
|
||
| Покрытие региона | ≥ 5000 уникальных треков для ЦФО + Чувашии после первого полного прогона pipeline |
|
||
| Источники в продакшне | ≥ 3 источника, отдающих данные в БД |
|
||
| Дедупликация | < 5% дублей (один реальный трек — одна запись). Метрика: руками отсэмплировать 100 треков, посчитать дубли. |
|
||
| Производительность отдачи bbox | `GET /api/gps-tracks?bbox=…` ≤ 300 мс p95 на z ≥ 10 (≤ 500 треков в видимой области) |
|
||
| Производительность отдачи тайлов | `GET /api/gps-tracks/tiles/{z}/{x}/{y}.mvt` ≤ 200 мс p95 на z = 8–11 |
|
||
| Производительность отрисовки | При включённом слое pan/zoom без видимых фризов на десктопе и мобильных с 4 ГБ RAM |
|
||
| Расширяемость на регион | Добавить новый регион (bbox + название + список источников) — ≤ 30 строк YAML-конфига, без правки кода |
|
||
| Скорость UI-фильтров | Переключение фильтра по активности/источнику меняет видимую выборку за ≤ 200 мс (фильтрация на клиенте) |
|
||
| Сохранение слоя при `setStyle()` | Слой не теряется при переключении тёмной темы / спутника / hillshade — восстанавливается через `rebuildMapOverlays()` |
|
||
| Pipeline стабильность | Падение парсера одного источника не валит остальных; лог + алерт в `/api/gps-tracks/health` |
|
||
| Атрибуция | На карте видна атрибуция каждого активного источника; в popup трека — ссылка на оригинал |
|
||
|
||
## 6. Риски
|
||
|
||
| Риск | Вероятность | Влияние | Митигация |
|
||
| ----------------------------------------------------------------------------------- | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||
| Источник меняет HTML → парсер ломается | Высокая | Среднее | Каждый источник в отдельном модуле, изолированная ошибка. Pipeline пишет статус по источнику в health-эндпоинт. Алерт при 2 неудачных прогонах подряд. |
|
||
| ToS источника запрещает скрейпинг | Средняя | Высокое | Обязательный ADR с фиксацией лицензии до подключения источника. Источник без явного разрешения — не включается. |
|
||
| Дубли треков из разных источников (один и тот же трек выкладывают на 2 платформах) | Высокая | Среднее | Spatial+temporal hash: bbox округлённый до 0.01° + длина ± 5% + дата ± 1 день → одна запись. Алгоритм в TRZ §6. |
|
||
| Перегрузка карты на низких зумах (10K+ треков в видимой области) | Высокая | Высокое | На клиенте: на z < 10 — отдача через MVT-тайлы с упрощением геометрии (как `simplify_coords` для `trails`). На z ≥ 10 — JSON с лимитом 500 треков. |
|
||
| Размер БД растёт неконтролируемо (миллионы треков при расширении на РФ) | Низкая | Среднее | Отдельная `gps_tracks.sqlite`. Ротация: треки старше N лет (по конфигу, default 5) удаляются. Метрика размера БД в health. |
|
||
| Скрейпер банится по IP | Средняя | Среднее | Rate-limit + backoff + `User-Agent` с контактом. Сбор по cron 1–2 раза в неделю, не чаще. Per-source конфигурируемый delay. |
|
||
| Персональные данные в треках (точки «дом», имена) | Низкая | Высокое | Не сохраняем waypoint без явного публичного флага. Не сохраняем `author` если ToS требует анонимизации. Список запрещённых полей — в `08-data-requirements.md`. |
|
||
| Лицензия источника обязывает менять/удалять данные по требованию автора | Средняя | Среднее | Сохраняем `external_id` и `external_url` — можем удалить точечно по запросу. Pipeline уважает «удалённое на источнике» → удалять и у нас. |
|
||
| Pipeline ест слишком много трафика mva154 | Средняя | Низкое | Per-source лимит на прогон (например, max 1000 новых треков за прогон). Метрики в health. |
|
||
| Отдача больших MVT тайлов медленная | Средняя | Среднее | Серверный кэш тайлов (LRU 1024 записи, как уже сделано для `trails`). Упрощение геометрии по зуму. |
|
||
|
||
## 7. Зависимости
|
||
|
||
### Backend
|
||
|
||
- Новый пакет `src/api/gps_tracks/` с подмодулями:
|
||
- `models.py` — Pydantic + SQL schema
|
||
- `sources/<source>.py` — модули per-source (OSM, EnduroRussia, ttrails, …)
|
||
- `dedup.py` — алгоритм дедупликации
|
||
- `db.py` — обвязка SQLite + Spatialite
|
||
- `endpoint.py` — FastAPI routes
|
||
- `mvt.py` — генерация MVT-тайлов
|
||
- Зависимости Python: `httpx` (есть), `lxml` или `defusedxml` (новая —
|
||
для безопасного парсинга XML на сервере), `shapely` (есть).
|
||
|
||
### Pipeline
|
||
|
||
- Скрипт `scripts/gps_collect.py` — точка входа.
|
||
- Конфиг `config/gps_sources.yaml` — список источников и параметры.
|
||
- Конфиг `config/gps_regions.yaml` — список регионов (bbox + список
|
||
активных источников per-region).
|
||
- Cron на mva154: `0 3 * * 1,4 /usr/local/bin/python
|
||
/opt/enduro-trails/scripts/gps_collect.py` (Mon + Thu, 03:00 UTC).
|
||
- Логи: `/var/log/enduro-trails/gps-collect.log`.
|
||
|
||
### Frontend
|
||
|
||
- Новый модуль `src/web/gps_tracks.js` — слой, фильтры, popup, по
|
||
аналогии с `gpx.js`.
|
||
- Расширение `index.html`:
|
||
- Чекбокс «Публичные треки» и кнопка «Фильтры» в `#terrain-popup`.
|
||
- Bottom sheet `#sheet-gps-filters` с фильтрами по активности и
|
||
источнику.
|
||
- Расширение `style.json` / `style-dark.json`: layer + halo-layer для
|
||
спутника (по аналогии с `trails-track-halo-satellite` из ET-007).
|
||
- Интеграция с `rebuildMapOverlays()` в `app.js`.
|
||
|
||
### Инфра
|
||
|
||
- Файловая: `data/gps_tracks.sqlite` на mva154, права чтения для FastAPI,
|
||
права записи только для pipeline. Бэкап в общий backup-стек проекта.
|
||
- Сетевая: исходящие HTTPS к источникам с mva154 (уже разрешено).
|
||
|
||
### Документация
|
||
|
||
- `06-adr/source-licensing.md` — лицензии всех источников.
|
||
- `06-adr/dedup-algorithm.md` — обоснование выбора алгоритма
|
||
дедупликации.
|
||
- `06-adr/storage-schema.md` — обоснование отдельной БД vs единой.
|
||
- `07-infra-requirements.md` — cron, бэкапы, ротация, мониторинг.
|
||
- `08-data-requirements.md` — схема БД, поля, ограничения, политика
|
||
персональных данных.
|
||
- `10-tech-risks.md` — расширенный риск-реестр (расширяет §6 BRD).
|
||
|
||
### Связи с другими work items
|
||
|
||
- **ET-006** — модель `window.gpxTracks` живёт параллельно. ET-008 не
|
||
трогает её, использует свою модель `window.gpsTracksLayer`.
|
||
- **ET-007** — спутниковая подложка. ET-008 добавляет halo-слой для
|
||
публичных треков в режиме «Спутник» по тому же паттерну.
|
||
- **PH-3 Smart Route** — публичные треки в будущем могут стать входом
|
||
для построения «реально-езженого» маршрута. Не в scope ET-008.
|
||
- **PH-9 PWA** — слой публичных треков должен корректно работать в
|
||
офлайне (через cached MVT). Учитывается в TRZ, но реализация офлайна
|
||
— задача PH-9.
|