---
type: brd
work_item_id: ET-012
title: "BRD: Показывать пользовательские треки с зума z5 (сейчас с z8)"
version: 1
status: draft
created_at: 2026-06-04
updated_at: 2026-06-04
authors:
  - "agent:analyst"
related:
  - "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,
гарантируем читаемость и производительность тестами и
скриншот-тестами.
