Files
enduro-trails/docs/work-items/ET-012/01-brd.md
claude-bot eb9adbc930
All checks were successful
CI / lint (push) Successful in 5s
CI / test (push) Successful in 7s
CI / build (push) Successful in 2s
analyst(ET): auto-commit from analyst run_id=72
2026-06-04 06:00:55 +00:00

21 KiB
Raw Blame History

type, work_item_id, title, version, status, created_at, updated_at, authors, related
type work_item_id title version status created_at updated_at authors related
brd ET-012 BRD: Показывать пользовательские треки с зума z5 (сейчас с z8) 1 draft 2026-06-04 2026-06-04
agent:analyst
ET-008
ET-009

BRD — ET-012: Показывать пользовательские треки с зума z5

1. Цель

Снизить нижний порог видимости слоя публичных GPS-треков (gps-tracks-layer-mvt) с z8 до z5, чтобы пользователь видел общее покрытие сети треков на средних/мелких масштабах (z5 ≈ ¼ Европы в кадре, z7 ≈ область размером с ЦФО) и мог «с высоты птичьего полёта» искать интересные треки.

На сегодня (после ET-008/ET-009) слой публичных треков физически скрыт ниже z8 двумя механизмами:

  • vector-source задаёт minzoom: 8 (тайлы не запрашиваются);
  • клиентский visibility-фильтр zoom >= GPS_TRACKS_MIN_ZOOM (8) в _syncGpsLayersVisibility и applyGpsHaloVisibility;
  • UI-hint «Зум 8+» (#public-tracks-zoom-hint) висит как обоснование «почему пусто».

ET-012 = снизить порог + сохранить читаемость и производительность на новых зумах z5-z7.

2. Контекст

2.1 Текущее поведение (после ET-009)

  • Источник gps-tracks-tiles (MVT): tiles: /api/gps-tracks/tiles/{z}/{x}/{y}.mvt, minzoom: 8, maxzoom: 11.
  • Источник gps-tracks-geo (GeoJSON, через /api/gps-tracks?bbox=…) включается при zoom >= GPS_TRACKS_ZOOM_CUTOFF = 12 — ET-012 в этом регионе ничего не меняет.
  • build_gps_mvt (src/api/gps_tracks/mvt.py) уже содержит zoom-aware упрощение и пороги:
    • _simplify_coords: tolerance 0.008° (~800м) на z≤7, 0.002° (~200м) на z8-9, 0.0005° (~50м) на z10-11, без упрощения на z≥12.
    • В build_gps_mvt: при z≤7 — min_length_m=2000, limit=3000 features на тайл; на больших зумах limit/min_length мягче.
  • Endpoint /api/gps-tracks/tiles/{z}/{x}/{y}.mvt принимает любой 0 ≤ z ≤ 22; никакой пре-нарезки тайлов нет — каждый тайл строится из БД on-demand и кэшируется в FIFO размером 1024.
  • На клиенте используется LRU-кэш MapLibre и сетевой кэш браузера.
  • Текущая БД (test-среда) содержит порядка нескольких сотен треков (ожидаемо ≤ 5000 в горизонте года), геометрия каждого трека — десятки-тысячи точек.

2.2 Почему это бизнес-важно

  • На малых масштабах (z5-z7) пользователю сейчас негде искать треки: при первом открытии карта по умолчанию показывает обзор региона; чтобы увидеть хоть что-то из публичных треков, нужно сразу зумить до z8 — это лишний шаг и плохой UX.
  • Видимость на z5-z7 = понимание «где вообще катаются» в масштабах целого региона/страны, что помогает планировать выезды и оценивать покрытие.
  • Конкуренты (Wikiloc, Komoot) показывают clustered/density слои с z3-z4; для нас достаточно начать с z5.

2.3 Открытые вопросы из бизнес-запроса — ответы по результатам анализа

Вопрос Ответ
Где задаётся minzoom слоя? Клиент: src/web/gps_tracks.js, константа GPS_TRACKS_MIN_ZOOM = 8 (используется в source.minzoom, visibility, halo, hint).
Тайлы уже нарезаны до z5 или нужно догенерить? Нарезки нет вообще — тайлы строятся on-demand из SQLite по bbox. Никакой генерации/инвалидации делать не нужно.
Нужна ли генерализация линий на малых зумах? Базовая уже есть в _simplify_coords (DP-tolerance 800м при z≤7). Для z5-z6 нужно ужесточить пороги (min_length, limit, tolerance) — F-04..F-06.

3. Scope

In scope

# Функция
F-01 Снизить клиентскую константу GPS_TRACKS_MIN_ZOOM с 8 до 5 в src/web/gps_tracks.js.
F-02 Уменьшить minzoom vector-source gps-tracks-tiles с 8 до 5 (использует ту же константу).
F-03 На бэкенде в build_gps_mvt расширить tier-структуру: добавить уровни z5, z6 с более жёсткими min_length_m и limit.
F-04 В _simplify_coords добавить tier для z5-z6: tolerance ~0.02° (~2 км) на z5, ~0.01° (~1 км) на z6.
F-05 Расширить line-width (основной слой) и line-width (halo) для z5: явные stops чтобы линия читалась.
F-06 UI-hint #public-tracks-zoom-hint: либо обновить текст до «Зум 5+», либо скрывать всегда (после снижения порога порог фактически недостижим в обычных сценариях).
F-07 Halo на спутнике активируется на z5-z11 (как и основной MVT-слой).
F-08 Производительность: p95 generation одного MVT-тайла z5 ≤ 500 мс при размере БД ≤ 5000 треков; p95 endpoint не выше +50 мс относительно baseline ET-009.
F-09 Читаемость: на z5 с включённым слоем при ≥ 200 треках по ЦФО карта остаётся «читаемой» — линии не сливаются в сплошную кашу. Критерий приёмки качественный, см. AC-08.
F-10 Halo на спутнике на z5-z7: не «глушит» подложку. Halo-width на z5 ≤ 2 px.
F-11 Регрессия: поведение на z8-z11 (MVT) и z12+ (GeoJSON) не меняется.
F-12 Регрессия: фильтры по activity / source работают на z5-z7 так же, как на z8+.
F-13 Регрессия: popup трека и кнопка «Скачать GPX» (ET-011) работают при клике на трек на z5-z7. (Замечание: на низких зумах кликнуть в линию пальцем сложнее — оставляем как есть, hit-area не расширяем.)

Out of scope

  • Clustering / heat-map на z3-z4. Идея здравая, но требует отдельной серверной агрегации (например, h3-cell counts) и нового UI-слоя. Делаем отдельным work item.
  • Пре-нарезка тайлов на диск. Не требуется при текущем размере БД; on-demand + LRU справляются.
  • Изменение поведения GeoJSON-слоя на z12+. Не трогаем.
  • Изменение фильтров по activity/source. Не трогаем.
  • Изменения popup'а трека. Не трогаем.
  • Расширение gps_tracks_minzoom в админ-конфиг. Константа остаётся хардкодом — менять через релиз. Если в будущем появится потребность в feature-flag — отдельный work item.
  • Изменения схемы БД и dedup-алгоритма. Не трогаем.
  • Изменения OSRM / routing. Не трогаем.

4. Метрики успеха

# Метрика Критерий
M-1 Видимость на z5 При включённом чекбоксе «Публичные треки» и zoom = 5 слой gps-tracks-layer-mvt имеет visibility: visible. На карте отображаются линии треков.
M-2 Видимость на z6, z7 Аналогично M-1 для z6 и z7.
M-3 Поведение на z8-z11 не изменилось Регрессия: на z8-z11 виден тот же набор треков, что и до ET-012 (с поправкой на улучшенную z5-z7 генерализацию — не считается регрессией).
M-4 Поведение на z12+ не изменилось Регрессия: GeoJSON-слой включается ровно на z=12 как раньше; MVT слой скрывается ровно на z=12.
M-5 Hint «Зум 5+» / «Зум 8+» После ET-012: при включённом слое и zoom ≥ 5 hint скрыт. (До ET-012 hint показывал «Зум 8+» при zoom < 8.)
M-6 p95 MVT tile generation на z5 ≤ 500 мс на test-среде при размере БД до 5000 треков; ≤ 1 с при размере до 20 000 треков (запас).
M-7 p95 endpoint /api/gps-tracks/tiles/5/x/y.mvt cold ≤ 700 мс, hit ≤ 50 мс (кэш).
M-8 Размер MVT тайла z5 ≤ 200 KB после генерализации и фильтра min_length (защита от мобильного трафика). Если больше — F-03/F-04 переусиливают (ужесточить limit).
M-9 Читаемость z5 На скриншоте z5 с ≥ 200 треков по ЦФО видны минимум 3 разных линии в разных частях кадра; нет «сплошной заливки» одной зоны. Качественная проверка по TC-UI-12-Z5-Q.
M-10 Регрессия фильтров Снятие галки «EnduroRussia» в #sheet-gps-filters на z=6 убирает соответствующие линии (как и на z=10).
M-11 LRU-кэш не переполняется ненужно После панорамирования по миру на z5-z6 (≈ 50 уникальных тайлов) кэш-хит на повторных тайлах ≥ 80 %.

5. Риски

# Риск Вероятность Влияние Митигация
R-1 На z5 слишком много фич в одном тайле → MVT > 1 MB, тормоза рендера на мобильном. Средняя Высокое F-03: жёсткий min_length_m и limit для z=5. Метрика M-8 (≤ 200 KB) — гейт. При нарушении — ужесточить limit/min_length.
R-2 На z5 линии после Douglas-Peucker превращаются в «обрубки» (трек из 1000 точек → 3 точки). Средняя Низкое Качественная проверка по TC-UI-12-Z5-Q. Tolerance подобрана так, чтобы трек ≤ 5 км превращался в прямую — это норма на z5.
R-3 Линия line-width: 0.5 px на z5 невидима на retina-дисплеях. Низкая Низкое F-05: явные stops interpolate linear zoom 5 0.8 8 1.0 12 2.0 16 3.0. Проверка по TC-UI-01-Z5.
R-4 Бэкенд-запрос к БД с огромным bbox (z5 тайл ~1250×1250 км) тянет ВСЕ треки региона. Средняя Среднее Запрос уже идёт через индекс по min_lon/max_lon/min_lat/max_lat в SQLite; при ≤ 5000 строк это < 100 мс. M-7 — гейт. При деградации — добавить индекс length_m.
R-5 На z5 buffer 10 % bbox в endpoint раздувает запрос до 130 % площади. Низкая Низкое На z5 это уже не имеет смысла (соседний тайл всё равно отрисует пограничные фичи). Опционально — снизить buffer до 5 % для z≤6. См. TRZ §3.10.
R-6 LRU-кэш в 1024 тайла на z5 (всего 32×32 = 1024 тайла в мире) — теоретически переполняется на «walk through world». Низкая Низкое На практике пользователь видит ~10-20 тайлов одновременно на z5; ротация работает. Опционально — увеличить _GPS_TILE_CACHE_MAX до 2048. См. TRZ §3.11.
R-7 Hint «Зум 8+» забыли удалить → пользователь видит и линии, и подсказку «увеличь зум». Средняя Низкое F-06 явно: либо hide-always при GPS_TRACKS_MIN_ZOOM = 5, либо текст «Зум 5+». См. AC-05.
R-8 Регрессия halo на спутнике: halo на z5 «закрывает» линию. Низкая Низкое F-10: halo-width ≤ 2 px на z5; проверка по TC-UI-09-Z5-SAT.
R-9 Пользователи на мобильном с медленным интернетом получают раздутые тайлы z5-z6 при первом открытии. Средняя Среднее Размер ≤ 200 KB (M-8) + gzip на nginx + браузерный кэш. Опционально — отсрочить включение слоя до первого panMove (не в scope ET-012).
R-10 Конфликт с поведением другого слоя gps-tracks-halo-mvt-satellite: оба используют те же фичи MVT — на z5 halo и линия должны быть согласованы. Низкая Низкое Используют тот же source/source-layer; видимость синхронизируется через _syncGpsLayersVisibility + applyGpsHaloVisibility. Регрессионная проверка TC-UI-09-Z5-SAT.

6. Зависимости

Backend

  • src/api/gps_tracks/mvt.py:build_gps_mvt — расширить tier-таблицу для z5, z6 (F-03).
  • src/api/gps_tracks/mvt.py:_simplify_coords — добавить tier для z5-z6 (F-04).
  • src/api/gps_tracks/endpoint.py — без изменений логики, опциональная правка buffer для z≤6 (R-5). По умолчанию не меняем.
  • Endpoint /api/gps-tracks/tiles/{z}/{x}/{y}.mvt уже принимает z 0..22 — не трогаем.

Frontend

  • src/web/gps_tracks.js:
    • константа GPS_TRACKS_MIN_ZOOM = 5 (F-01, F-02).
    • _gpsLayerDef paint.line-width — расширить interpolate-выражение для z5 (F-05).
    • _gpsHaloDef paint.line-width — то же (F-05, F-10).
  • src/web/index.html:
    • #public-tracks-zoom-hint — обновить текст или скрыть навсегда (F-06).
  • Стили style.json / style-dark.json — без изменений (минзум слоя в стилях не задаётся; он живёт в коде клиента).

Тесты

  • Новые unit-тесты tests/unit/test_gps_mvt_zoom_tiers.py (новый файл): тиры min_length и limit для z=5..z=12.
  • Новые unit-тесты tests/unit/test_gps_mvt_simplify.py или расширение существующих: tolerance для z5-z6.
  • Новые integration-тесты tests/integration/test_gps_tile_z5_z7.py: endpoint отдаёт непустой MVT для z=5/6/7 на регионе с ≥ 10 треками.
  • UI-тесты см. 04b-ui-test-cases.md.

Документация

  • 01-brd.md (этот файл).
  • 02-trz.md, 03-acceptance-criteria.md, 04-test-plan.yaml, 04b-ui-test-cases.md — этот пакет.
  • Опциональный ADR не требуется: tile-pipeline уже спроектирован под динамические тиры в ET-008/ET-009; это calibration, а не архитектурное решение. Если разработчик в реализации обнаружит нужду в смене политики (например, переход к heat-map на z5) — добавляет ADR в 06-adr/.

Инфра / Данные

  • Test-среда https://openclaw.mva154.duckdns.org/enduro/ — существующий деплой.
  • БД data/gps_tracks.sqlite — без миграций.
  • nginx gzip уже включён.

Связи с другими work items

  • ET-008 — родительский слой публичных GPS-треков.
  • ET-009 — заполнил БД треками EnduroRussia/Wikiloc; без этих данных z5-z7 будет визуально пустым в test-среде.
  • ET-011 — кнопка «Скачать GPX» в popup'е; регрессия покрывается.
  • PH-3 Smart Route — независимо.
  • Будущий work item «Heat-map / clustering на z3-z4» — отдельная задача.

7. План в одну строку

Снижаем константу GPS_TRACKS_MIN_ZOOM с 8 до 5, расширяем zoom-tier структуру в build_gps_mvt и _simplify_coords для z5-z6, добавляем явные line-width stops для z5, скрываем/обновляем hint, гарантируем читаемость и производительность тестами и скриншот-тестами.