443 lines
23 KiB
Markdown
443 lines
23 KiB
Markdown
---
|
||
type: trz
|
||
work_item_id: ET-012
|
||
title: "ТЗ: Показывать пользовательские треки с зума z5"
|
||
version: 1
|
||
status: draft
|
||
created_at: 2026-06-04
|
||
updated_at: 2026-06-04
|
||
authors:
|
||
- "agent:analyst"
|
||
related:
|
||
- "ET-008"
|
||
- "ET-009"
|
||
---
|
||
|
||
# ТЗ — ET-012: Показывать пользовательские треки с зума z5
|
||
|
||
## 1. Терминология
|
||
|
||
- **MVT-слой** — `gps-tracks-layer-mvt`, отрисовка треков из
|
||
vector-source `gps-tracks-tiles` (тайлы `/api/gps-tracks/tiles/{z}/{x}/{y}.mvt`).
|
||
Активен при `GPS_TRACKS_MIN_ZOOM ≤ zoom < GPS_TRACKS_ZOOM_CUTOFF`.
|
||
- **GeoJSON-слой** — `gps-tracks-layer-geo`, отрисовка треков из
|
||
GeoJSON-source (запрос `/api/gps-tracks?bbox=…`). Активен при
|
||
`zoom ≥ GPS_TRACKS_ZOOM_CUTOFF = 12`. **ET-012 не трогает этот слой.**
|
||
- **Halo** — белый ореол на спутниковой подложке
|
||
(`gps-tracks-halo-mvt-satellite`, `gps-tracks-halo-geo-satellite`).
|
||
- **Zoom-tier** — диапазон зумов (например, `z ≤ 5`, `6 ≤ z ≤ 7`),
|
||
для которого `build_gps_mvt` применяет общий набор лимитов
|
||
(`min_length_m`, `limit`) и порог упрощения (`tolerance`).
|
||
- **Douglas-Peucker tolerance** — параметр `shapely.LineString.simplify`,
|
||
в градусах WGS84. На широте 55°: 1° долготы ≈ 64 км, 1° широты ≈ 111 км.
|
||
- **Zoom-hint** — UI-надпись «Зум 8+» (`#public-tracks-zoom-hint`),
|
||
подсказывающая, что нужно увеличить зум, чтобы увидеть слой.
|
||
|
||
## 2. Архитектурные опоры
|
||
|
||
ET-012 не строит новых модулей. Используем существующее:
|
||
|
||
- `src/web/gps_tracks.js` — клиентский слой ET-008/ET-009/ET-011.
|
||
Константы: `GPS_TRACKS_ZOOM_CUTOFF = 12`, `GPS_TRACKS_MIN_ZOOM = 8`.
|
||
- `src/api/gps_tracks/mvt.py:build_gps_mvt` — серверная сборка MVT
|
||
с tier-логикой `min_length_m` / `limit` и `_simplify_coords`.
|
||
- `src/api/gps_tracks/endpoint.py:get_gps_tile` — обработчик
|
||
`/api/gps-tracks/tiles/{z}/{x}/{y}.mvt`. Валидация `0 ≤ z ≤ 22`
|
||
уже есть. LRU-кэш `_gps_tile_cache` размер 1024 — не меняем.
|
||
- `src/api/gps_tracks/db.py:get_tracks_in_bbox` — bbox-запрос
|
||
по индексам min_lon/max_lon/min_lat/max_lat. Не меняем.
|
||
|
||
ET-012 = **значения констант + одна функция-tier + одна функция-simplify + три CSS/MapLibre-выражения + один hint**.
|
||
|
||
## 3. Требования
|
||
|
||
### REQ-F-01 — Клиентская константа `GPS_TRACKS_MIN_ZOOM`
|
||
|
||
Файл `src/web/gps_tracks.js`, строка
|
||
```js
|
||
const GPS_TRACKS_MIN_ZOOM = 8; // ниже — слой скрыт
|
||
```
|
||
заменить на
|
||
```js
|
||
const GPS_TRACKS_MIN_ZOOM = 5; // ниже — слой скрыт
|
||
```
|
||
|
||
**Acceptance check.**
|
||
```bash
|
||
grep -n "GPS_TRACKS_MIN_ZOOM" src/web/gps_tracks.js
|
||
```
|
||
Первое вхождение содержит `= 5`. Никаких других мест объявления этой
|
||
константы в `src/web/` нет (`grep -R "GPS_TRACKS_MIN_ZOOM" src/web/`).
|
||
|
||
### REQ-F-02 — Vector-source minzoom использует ту же константу
|
||
|
||
В `_ensureGpsSources` (gps_tracks.js, около строки 178) запись
|
||
```js
|
||
minzoom: GPS_TRACKS_MIN_ZOOM,
|
||
```
|
||
**не меняется** — она автоматически примет новое значение 5.
|
||
|
||
**Acceptance check.** Через DevTools на test-среде:
|
||
```js
|
||
window._map.getSource('gps-tracks-tiles').minzoom === 5
|
||
```
|
||
|
||
### REQ-F-03 — Backend: zoom-tier для z=5 и z=6 в `build_gps_mvt`
|
||
|
||
Файл `src/api/gps_tracks/mvt.py`, функция `build_gps_mvt`,
|
||
блок «Min-length фильтр по зуму» (строки ~104-116) заменить на:
|
||
|
||
```python
|
||
# Min-length фильтр и cap на число фич по зуму
|
||
if z <= 5:
|
||
min_length_m = 10000 # 10 км — только «магистральные» треки
|
||
limit = 1500
|
||
elif z == 6:
|
||
min_length_m = 5000 # 5 км
|
||
limit = 2000
|
||
elif z == 7:
|
||
min_length_m = 2000 # как было для z<=7
|
||
limit = 3000
|
||
elif z <= 9:
|
||
min_length_m = 0
|
||
limit = 8000
|
||
elif z <= 11:
|
||
min_length_m = 0
|
||
limit = 15000
|
||
else:
|
||
min_length_m = 0
|
||
limit = 25000
|
||
```
|
||
|
||
Цифры подобраны под цели:
|
||
- z5: лимит 1500 фич × ~200 байт после генерализации ≈ 300 KB MVT
|
||
до gzip — близко к гейту M-8 (200 KB). Если на реальных данных
|
||
получится > 200 KB — снизить `limit` до 1000 в дев-итерации.
|
||
- min_length 10 км отсекает короткие тестовые трассы — они
|
||
визуально не различимы на z5.
|
||
|
||
### REQ-F-04 — Backend: tier для tolerance в `_simplify_coords`
|
||
|
||
Файл `src/api/gps_tracks/mvt.py`, функция `_simplify_coords`,
|
||
заменить блок выбора tolerance на:
|
||
|
||
```python
|
||
if z >= 12:
|
||
return coords
|
||
elif z >= 10:
|
||
tolerance = 0.0005 # ~50 м
|
||
elif z >= 8:
|
||
tolerance = 0.002 # ~200 м
|
||
elif z == 7:
|
||
tolerance = 0.008 # ~800 м (как сейчас для z<=7)
|
||
elif z == 6:
|
||
tolerance = 0.018 # ~2 км
|
||
else:
|
||
tolerance = 0.04 # ~4 км (z5 и ниже)
|
||
```
|
||
|
||
Замечание. `tolerance` — в градусах долготы; на 55° с.ш. её
|
||
эквивалент по расстоянию = `tolerance * 64 км`. Для z5 на пиксель
|
||
карты приходится ≈ 5 км по долготе на 55° с.ш., так что 4 км
|
||
tolerance даёт «1 точка на пиксель» — оптимум.
|
||
|
||
### REQ-F-05 — Frontend: line-width для основного MVT-слоя на z5
|
||
|
||
Файл `src/web/gps_tracks.js`, функция `_gpsLayerDef`, выражение
|
||
`line-width`:
|
||
```js
|
||
'line-width': ['interpolate', ['linear'], ['zoom'], 8, 1.0, 12, 2.0, 16, 3.0],
|
||
```
|
||
заменить на
|
||
```js
|
||
'line-width': ['interpolate', ['linear'], ['zoom'],
|
||
5, 0.8,
|
||
8, 1.0,
|
||
12, 2.0,
|
||
16, 3.0],
|
||
```
|
||
|
||
Stop на z5 = 0.8 px подобран так, чтобы на 1× и 2×-DPR дисплеях
|
||
линия гарантированно занимала ≥ 1 физический пиксель (с округлением
|
||
GPU). На retina (3×) — 2.4 пикселя, видимо.
|
||
|
||
### REQ-F-06 — Frontend: line-width для halo на z5
|
||
|
||
Файл `src/web/gps_tracks.js`, функция `_gpsHaloDef`, выражение
|
||
`line-width`:
|
||
```js
|
||
'line-width': ['interpolate', ['linear'], ['zoom'], 8, 2.5, 12, 4.0, 16, 6.0],
|
||
```
|
||
заменить на
|
||
```js
|
||
'line-width': ['interpolate', ['linear'], ['zoom'],
|
||
5, 1.8,
|
||
8, 2.5,
|
||
12, 4.0,
|
||
16, 6.0],
|
||
```
|
||
|
||
Halo на z5 = 1.8 px — белый ореол не должен «съедать» линию
|
||
толщиной 0.8 px. Соотношение ~2.25× оставляет халобакс по 0.5 px с каждой стороны.
|
||
|
||
### REQ-F-07 — Frontend: zoom-hint «Зум 5+»
|
||
|
||
Файл `src/web/index.html`, строка
|
||
```html
|
||
<span class="terrain-hint" id="public-tracks-zoom-hint" style="display:none">Зум 8+</span>
|
||
```
|
||
заменить на
|
||
```html
|
||
<span class="terrain-hint" id="public-tracks-zoom-hint" style="display:none">Зум 5+</span>
|
||
```
|
||
|
||
В `_syncGpsLayersVisibility` (gps_tracks.js, строка ~358-362) логика
|
||
```js
|
||
hint.style.display = (enabled && zoom < GPS_TRACKS_MIN_ZOOM) ? 'inline' : 'none';
|
||
```
|
||
**не меняется** — она автоматически подхватит новый порог.
|
||
|
||
**Замечание.** При z < 5 (фактически только z=0..4) hint всё ещё
|
||
появится, что и желательно: у пользователя есть подсказка, в каких
|
||
случаях линий нет «по дизайну».
|
||
|
||
### REQ-F-08 — Endpoint без изменений
|
||
|
||
`src/api/gps_tracks/endpoint.py:get_gps_tile` остаётся прежним:
|
||
|
||
- Валидация `0 ≤ z ≤ 22` уже корректно пропускает z=5..7.
|
||
- Buffer 10 % bbox остаётся (для z≤6 это формально излишне,
|
||
но не вредит — соседние тайлы кэшируются независимо).
|
||
- LRU-кэш `_gps_tile_cache` размером 1024 остаётся.
|
||
|
||
Никаких новых query-параметров не вводится. Никаких изменений
|
||
в `/api/gps-tracks?bbox=…` (GeoJSON endpoint) не делаем —
|
||
z12+ не затрагивается.
|
||
|
||
### REQ-F-09 — Unit-тесты zoom-tier в `build_gps_mvt`
|
||
|
||
Файл `tests/unit/test_gps_mvt_zoom_tiers.py` (новый или расширение
|
||
существующего `test_gps_mvt.py`):
|
||
|
||
- **UT-Z5-01.** При z=5 и 10 треках, из которых 3 короче 10 км, в
|
||
итоговом MVT — ≤ 7 features.
|
||
- **UT-Z5-02.** При z=5 и 2000 треках длиннее 10 км — в MVT не
|
||
больше `limit=1500` features.
|
||
- **UT-Z6-01.** При z=6 и треках 3 км и 6 км — в MVT попадает
|
||
только трек 6 км.
|
||
- **UT-Z6-02.** При z=6 и 2500 треках длиной ≥ 5 км — в MVT
|
||
не больше 2000 features.
|
||
- **UT-Z7-01.** При z=7 поведение совпадает с прежним
|
||
(min_length=2000, limit=3000). Регрессия.
|
||
- **UT-Z8-01.** При z=8 поведение совпадает с прежним
|
||
(min_length=0, limit=8000). Регрессия.
|
||
- **UT-Z12-01.** При z=12 поведение совпадает с прежним
|
||
(limit=25000). Регрессия.
|
||
|
||
### REQ-F-10 — Unit-тесты `_simplify_coords` для новых тиров
|
||
|
||
Файл `tests/unit/test_gps_mvt_simplify.py` (новый или расширение):
|
||
|
||
- **UT-SIMP-Z5-01.** Прямой трек 100 точек, диапазон ≈ 0.1° по широте/долготе:
|
||
при z=5 — возвращает ≤ 5 точек (DP с большим tolerance
|
||
схлопывает почти прямую).
|
||
- **UT-SIMP-Z5-02.** Зигзаг 100 точек, амплитуда зигзагов 0.01°
|
||
(≈ 1 км): при z=5 (tolerance ~4 км) — возвращает 2 точки
|
||
(зигзаги меньше tolerance, остаются только концы).
|
||
- **UT-SIMP-Z6-01.** Тот же зигзаг 100 точек, амплитуда 0.05° (~5 км):
|
||
при z=6 (tolerance ~2 км) — возвращает > 5 точек (видны
|
||
крупные зигзаги).
|
||
- **UT-SIMP-Z7-01.** Регрессия: при z=7 tolerance = 0.008,
|
||
поведение прежнее.
|
||
- **UT-SIMP-Z10-01.** Регрессия: при z=10 tolerance = 0.0005,
|
||
поведение прежнее.
|
||
- **UT-SIMP-Z12-01.** Регрессия: при z=12 функция возвращает
|
||
оригинальный coords без изменений.
|
||
|
||
### REQ-F-11 — Integration-тесты endpoint z5-z7
|
||
|
||
Файл `tests/integration/test_gps_tile_z5_z7.py` (новый):
|
||
|
||
- **IT-Z5-01.** На тестовой БД с 50 треками ≥ 10 км по ЦФО
|
||
запрос `GET /api/gps-tracks/tiles/5/19/9.mvt` (тайл, накрывающий
|
||
Москву): возвращает 200, Content-Type `application/x-protobuf`,
|
||
тело длиной > 0 и < 200 KB (M-8).
|
||
- **IT-Z5-02.** Размер MVT для того же тайла на БД из 200 треков
|
||
≥ 10 км — ≤ 200 KB.
|
||
- **IT-Z5-03.** Тайл z=5 за пределами региона (например, центр
|
||
Тихого океана `tiles/5/4/12.mvt`): тело пустое, ответ 200.
|
||
- **IT-Z6-01.** Тайл z=6 над Москвой: размер < 200 KB,
|
||
features > IT-Z5-01.
|
||
- **IT-Z7-01.** Тайл z=7 над Москвой: features > IT-Z6-01 (более
|
||
мелкие треки попадают в фильтр), но всё ещё < `limit=3000`.
|
||
- **IT-CACHE-01.** Два подряд запроса одного тайла z=5: второй
|
||
возвращает заголовок `X-Cache: HIT`.
|
||
|
||
### REQ-F-12 — Регрессионный тест: контракт endpoint не сломался
|
||
|
||
- **IT-REGRESS-Z8-01.** Endpoint `/api/gps-tracks/tiles/8/x/y.mvt`
|
||
возвращает тот же набор треков, что и до ET-012 (sanity-check
|
||
через сравнение `mapbox_vector_tile.decode(body)['gps_tracks']['features']`
|
||
до и после; допустимо различие только в порядке).
|
||
- **IT-REGRESS-Z10-01.** Аналогично для z=10.
|
||
|
||
### REQ-F-13 — Производительность: бенчмарк MVT z5
|
||
|
||
Файл `tests/performance/test_gps_mvt_z5_perf.py` (новый,
|
||
помечается маркером `@pytest.mark.perf`):
|
||
|
||
- **PERF-Z5-01.** При тестовой БД из 500 треков по ЦФО и
|
||
10 повторных вызовах `build_gps_mvt(rows, 5, 19, 9)`:
|
||
- среднее время выполнения ≤ 200 мс на CI-runner.
|
||
- 95-й перцентиль ≤ 500 мс (метрика M-6).
|
||
|
||
Запуск отдельный (`pytest -m perf`), не в основной CI-gate.
|
||
Цель — раз-в-релиз проверять, что мы не уплыли.
|
||
|
||
### REQ-F-14 — UI-тесты (Playwright)
|
||
|
||
См. `04b-ui-test-cases.md`. Ключевые проверки:
|
||
|
||
- TC-UI-01-Z5: при `zoom = 5` слой виден.
|
||
- TC-UI-02-Z6: при `zoom = 6` слой виден.
|
||
- TC-UI-03-Z7: при `zoom = 7` слой виден.
|
||
- TC-UI-04-HINT-OFF: hint «Зум 5+» **не** показывается при `zoom ≥ 5`.
|
||
- TC-UI-05-HINT-ON: hint показывается при `zoom < 5`.
|
||
- TC-UI-06-FILTER-Z6: фильтр источников работает на z6 (регрессия).
|
||
- TC-UI-07-POPUP-Z6: клик по треку на z6 открывает popup.
|
||
- TC-UI-08-Z11-REGRESS: на z11 слой по-прежнему виден (регрессия).
|
||
- TC-UI-09-Z12-CUTOFF: на z12 MVT-слой скрыт, GeoJSON-слой виден.
|
||
- TC-UI-10-Z5-MOBILE: на мобильном при z5 слой виден.
|
||
- TC-UI-11-Z5-SAT: на z5 со спутниковой подложкой halo не «глушит» подложку.
|
||
- TC-UI-12-Z5-Q: качественная проверка читаемости на z5.
|
||
|
||
### REQ-F-15 — Не менять контракт `/api/gps-tracks*`
|
||
|
||
Никаких новых query-параметров, заголовков, кодов ответа,
|
||
полей в JSON. `/health` endpoint не меняется.
|
||
|
||
### REQ-F-16 — Не менять конфиги
|
||
|
||
`config/gps_sources.yaml`, `config/gps_regions.yaml`,
|
||
миграции БД — без изменений.
|
||
|
||
### REQ-F-17 — Не менять стили карты
|
||
|
||
`src/web/style.json` и `src/web/style-dark.json` — без изменений.
|
||
Color-by-source / color-by-activity match-expressions внутри
|
||
`_buildColorExpression` в коде клиента — без изменений (треки
|
||
на z5-z7 будут окрашены теми же цветами).
|
||
|
||
### REQ-F-18 — localStorage без миграции
|
||
|
||
Текущий слой использует ключи `gps-tracks-enabled`,
|
||
`gps-tracks-activities`, `gps-tracks-sources`, `gps-tracks-color-mode`.
|
||
ET-012 не вводит новых ключей и не меняет существующие. Существующие
|
||
пользователи увидят треки на z5-z7 при следующей загрузке без потери
|
||
выбранных фильтров.
|
||
|
||
### REQ-F-19 — Деплой и валидация
|
||
|
||
После merge в `main` и деплоя в test-среду:
|
||
|
||
1. Открыть `https://openclaw.mva154.duckdns.org/enduro/`,
|
||
включить «Публичные треки», установить `zoom = 5`
|
||
(через DevTools `window._map.setZoom(5)`), убедиться, что
|
||
линии видны.
|
||
2. Снять профайл DevTools Network: размер запроса
|
||
`/api/gps-tracks/tiles/5/19/9.mvt` ≤ 200 KB.
|
||
3. Проверить три тайла z=5 над разными регионами (Москва, Урал,
|
||
Сибирь) — все ≤ 200 KB и тело > 0 для регионов с треками.
|
||
4. Зафиксировать результаты в `14-deploy-log.md`.
|
||
|
||
### REQ-F-20 — Документация
|
||
|
||
В `docs/work-items/ET-012/` после Анализа существуют:
|
||
- `00-business-request.md` (есть)
|
||
- `01-brd.md`
|
||
- `02-trz.md` (этот файл)
|
||
- `03-acceptance-criteria.md`
|
||
- `04-test-plan.yaml`
|
||
- `04b-ui-test-cases.md`
|
||
|
||
После реализации добавляются: `10-tech-risks.md` (опционально),
|
||
`12-review.md`, `13-test-report.md`, `14-deploy-log.md`.
|
||
|
||
## 4. Не-функциональные требования
|
||
|
||
### NFR-01 — Производительность сервера
|
||
|
||
- p95 `build_gps_mvt` на z=5 при БД 500 треков ≤ 500 мс на CI-runner
|
||
(метрика M-6).
|
||
- p95 endpoint `/api/gps-tracks/tiles/{5..7}/x/y.mvt` cold ≤ 700 мс,
|
||
hit ≤ 50 мс (M-7).
|
||
- Не более 10 SQLite-запросов на тайл (в идеале — 2: COUNT + SELECT).
|
||
|
||
### NFR-02 — Производительность клиента
|
||
|
||
- На z5 рендер слоя не дольше +30 мс по сравнению с состоянием
|
||
слой-выключен (замер через MapLibre `map.on('render')` интервал).
|
||
- Не вызывает frame-drop ниже 30 FPS на средне-мобильном устройстве
|
||
(iPhone 12 / Pixel 5 эквивалент).
|
||
|
||
### NFR-03 — Сетевой трафик
|
||
|
||
- Размер одного MVT-тайла z=5 ≤ 200 KB до gzip (метрика M-8).
|
||
- gzip-compression на nginx даёт обычно ×3-4 по тайлам — финальный
|
||
трафик 50-70 KB на тайл.
|
||
|
||
### NFR-04 — Кэширование
|
||
|
||
- LRU размер `_GPS_TILE_CACHE_MAX = 1024` — не меняем.
|
||
Опциональное увеличение до 2048 — на усмотрение разработчика,
|
||
если в `PERF-Z5-01` обнаружится частая инвалидация.
|
||
|
||
### NFR-05 — Безопасность
|
||
|
||
Никаких изменений в auth / CSP / валидации входных данных
|
||
ET-012 не вносит.
|
||
|
||
### NFR-06 — Совместимость
|
||
|
||
- API контракт `/api/gps-tracks*` не меняется → старые клиенты
|
||
работают без обновления.
|
||
- Существующие browser-tabs с открытой картой при следующей загрузке
|
||
получат новые лимиты автоматически (никакой миграции
|
||
localStorage не нужно).
|
||
|
||
### NFR-07 — Логирование
|
||
|
||
Никаких новых лог-сообщений. Существующее логирование
|
||
endpoint `gps_tile` (через `uvicorn.access`) показывает зум, x, y, размер ответа — это достаточно.
|
||
|
||
## 5. План работ (для разработчика)
|
||
|
||
1. **Backend: расширить `build_gps_mvt` tier-таблицу** (REQ-F-03).
|
||
2. **Backend: расширить `_simplify_coords` tier-таблицу** (REQ-F-04).
|
||
3. **Unit-тесты zoom-tier и simplify** (REQ-F-09, F-10).
|
||
4. **Integration-тесты endpoint z5-z7** (REQ-F-11, F-12).
|
||
5. **Performance-тест PERF-Z5-01** (REQ-F-13). Если не проходит —
|
||
ужесточить `limit` в REQ-F-03.
|
||
6. **Frontend: понизить `GPS_TRACKS_MIN_ZOOM` до 5** (REQ-F-01).
|
||
7. **Frontend: line-width stops для z5** в основном слое и halo
|
||
(REQ-F-05, F-06).
|
||
8. **Frontend: текст hint** (REQ-F-07).
|
||
9. **Прогон `make lint`, `make test`.**
|
||
10. **Code review → merge → deploy в test.**
|
||
11. **Ручная проверка REQ-F-19.**
|
||
12. **Прогон UI-тестов** по `04b-ui-test-cases.md`.
|
||
13. **Запись результатов** в `13-test-report.md` и `14-deploy-log.md`.
|
||
|
||
## 6. Открытые вопросы и решения по умолчанию
|
||
|
||
| Вопрос | Решение по умолчанию |
|
||
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| Опускать ли порог ещё ниже (z3-z4)? | **Нет.** На z3-z4 даже 10-км треки превращаются в точку — нужна heat-map. Это отдельный work item. |
|
||
| Увеличить ли `_GPS_TILE_CACHE_MAX`? | **Нет в MVP.** Текущие 1024 покрывают z5..z11. Только если PERF-Z5-01 покажет деградацию. |
|
||
| Уменьшать ли buffer endpoint'а до 5 % для z≤6? | **Нет в MVP.** 10 % буфер на z5-тайле в большинстве регионов не критичен (≈ 100 км запас в bbox-запросе вместо 1250). Можно вернуться, если PERF-Z5-01 не пройдёт. |
|
||
| Делать ли разные tier для color-by-source vs color-by-activity на z5? | **Нет.** Геометрия одна, цвет — runtime-выражение MapLibre, не зависит от tier. |
|
||
| Что показывать пользователю на z3-z4? | Hint «Зум 5+» (REQ-F-07) даёт явное объяснение. Heat-map — отдельный work item. |
|
||
| Сохранять ли поведение «слой пуст, но включён» через localStorage на z<5? | **Да** — чекбокс остаётся checked, hint объясняет, что нужно зумить. Логика уже есть в `_syncGpsLayersVisibility`. |
|
||
| Сразу прогружать MVT z5 при включении слоя, если карта на z2? | **Нет.** Source.minzoom=5 защищает: тайлы не запрашиваются до z≥5. Не меняем. |
|
||
| Менять ли LRU FIFO на настоящий LRU? | **Нет в MVP.** При работе с 10-20 тайлами в кадре FIFO эквивалентен LRU; разница только при больших кэшах. |
|