Files
enduro-trails/docs/work-items/ET-012/08-data-requirements.md
claude-bot c7d472023f
All checks were successful
CI / lint (push) Successful in 4s
CI / test (push) Successful in 7s
CI / build (push) Successful in 2s
architect(ET): auto-commit from architect run_id=73
2026-06-04 06:19:02 +00:00

21 KiB
Raw Blame History

type, work_item_id, title, version, status, created_at, authors
type work_item_id title version status created_at authors
data-requirements ET-012 Требования к данным — ET-012: Снижение minzoom публичных треков до z5 1 approved 2026-06-04
agent:architect

Требования к данным — ET-012

1. Резюме

ET-012 — pure read pattern change. Никаких изменений схемы БД, никаких новых таблиц, индексов, миграций, файлов БД, ключей localStorage, изменений конфигов источников.

Меняется только как существующие данные читаются и сериализуются в MVT при z ∈ {5, 6}:

  • build_gps_mvt отбирает другой набор rows (фильтр по length_m) и применяет более жёсткий лимит фич;
  • _simplify_coords применяет другой tolerance Douglas-Peucker'а к существующим WKB-координатам.

Меняется:

  • набор фич, попадающих в MVT-тайл при z ∈ {5, 6};
  • размер итогового protobuf MVT (за счёт меньшего числа фич и более агрессивного упрощения).

Не меняется:

  • schema таблицы tracks (ET-008 / ADR-005);
  • schema таблицы pipeline_runs;
  • индексы idx_tracks_geom (R-tree), min_lon/max_lon/min_lat/max_lat;
  • контракт API /api/gps-tracks/* (REQ-F-15);
  • содержимое отдельных треков (geom, name, sources_json, etc.);
  • dedup-алгоритм (compute_dedup_key);
  • ACTIVITY_TYPES enum;
  • маппинги SOURCE_ATTRIBUTIONS, SOURCE_LABELS;
  • localStorage ключи и значения клиента (REQ-F-18);
  • содержимое config/gps_sources.yaml, config/gps_regions.yaml (REQ-F-16).

2. Архитектурные границы данных

Слой данных Тип Расположение Изменения в ET-012
OSM-vector (trails) существующий /app/data/centralfederal.sqlite нет
Личные GPX треки (ET-006) существующий браузер (memory) нет
Публичные GPS треки (ET-008) существующий /app/data/gps_tracks.sqlite read-only: новые комбинации параметров (z, x, y) теперь принимаются (z=5/6/7); никаких INSERT/UPDATE/DELETE
OSRM-граф существующий /app/data/enduro.osrm.* нет
User UI state существующий localStorage нет новых ключей, нет миграции
MVT-кэш в RAM app существующий _gps_tile_cache (Python dict) расширяется ключевым пространством: теперь могут лежать тайлы с z ∈ {5,6,7} в дополнение к 8..11. Ёмкость 1024 — без изменений
Серверный MVT-тайл (выход) существующий формат, новый z bytes в HTTP response формат application/x-protobuf (Mapbox Vector Tile spec), source-layer gps_tracks, properties как в ET-008 (id, activity, source, sources, length_km, name, ext_url)
Клиентский MapLibre LRU существующий браузер расширяется ключевым пространством аналогично серверу

3. Серверные данные — gps_tracks.sqlite

3.1 Schema

Без изменений vs ET-008/ET-009/ET-011. См. docs/work-items/ET-008/08-data-requirements.md §3.1, §3.5. Никаких ALTER TABLE / DROP COLUMN / CREATE INDEX.

3.2 Используемые поля в SELECT при сборке MVT z5-z7

Поле Использование
id MVT property
name MVT property
activity_type MVT property
length_m NEW USE: фильтр length_m >= min_length_m где min_length_m=10000 (z5) или 5000 (z6) или 2000 (z7). Раньше фильтр применялся только для z≤7 с порогом 2000
points_count не используется в MVT (только в /download, ET-011)
geom (WKB) парсится через _wkb_to_coords()[(lon, lat), ...] → передаётся в _simplify_coords(coords, z). NEW: для z=5 tolerance=0.04°, для z=6 tolerance=0.018°
sources_json первый элемент → MVT property source; весь список → comma-separated в property sources
external_urls_json первый URL → MVT property ext_url
dedup_key, description, tags_json, user, inserted_at, updated_at, created_at, min_lon..max_lat не используется в MVT (часть полей нужна только в /download или GeoJSON-режиме z≥12)

Запрос идентичен ET-008 (get_tracks_in_bbox):

SELECT t.* FROM tracks t WHERE t.ROWID IN (
  SELECT pkid FROM idx_tracks_geom WHERE
    xmin <= ? AND xmax >= ? AND ymin <= ? AND ymax >= ?
) ORDER BY length_m DESC

Изменения SQL: нет. Фильтр по length_m — на Python-стороне в build_gps_mvt, чтобы не вводить новые SQL-параметры (TRZ §3 REQ-F-08).

3.3 Объёмы данных

Метрика Текущее (ET-009) Прогноз через 12 мес. Гейт ET-012
Число треков в gps_tracks.sqlite ~500 (test) ~5000 M-6 (p95 build_gps_mvt z5 ≤ 500 мс на БД 5000)
Длинных треков (≥ 10 км) ~150-200 (ЦФО) ~1500-2000 M-8 (размер MVT z5 ≤ 200 KB)
Точек на трек (среднее) 2000-5000 2000-5000 (Tolerance Douglas-Peucker отсечёт лишнее)
Размер БД (на диске) ~50 MB ~500 MB Disk-impact на mva154 — пренебрежимо

При БД из 5000 треков и БД-индекс по bbox:

  • Один z=5 тайл накрывает ~1250×1250 км по экватору, ~700×1250 на 55° с.ш.
  • В bbox z=5 над ЦФО попадает ≤ 100% длинных треков ЦФО = ~1500.
  • После Python-фильтра length_m ≥ 10000 остаётся ~1500 длинных треков → ограничивается limit=1500.
  • После _simplify_coords (tolerance 0.04° → ~5-30 точек на трек) → средний размер фичи ≈ 200 байт → MVT ≈ 300 KB до gzip → ≈ 80 KB после.

3.4 Индексы

Без изменений vs ET-008. Существующий R-tree-индекс idx_tracks_geom достаточен для bbox-запросов z=5. Вторичный индекс на length_m не нуженORDER BY length_m DESC дёшев на выборках < 5000 строк (Python sort после SQL-фильтра по bbox; SQLite делает табличный SCAN после R-tree фильтра).

Watch-flag (TRZ §6, R-4): если PERF-Z5-01 покажет деградацию при росте БД > 20k треков — рассмотреть CREATE INDEX idx_tracks_length ON tracks(length_m DESC) как отдельный work-item. Не в MVP.

4. Клиентские данные

4.1 localStorage

Без изменений vs ET-008/ET-009/ET-011. Используются ключи:

Ключ Назначение Изменения в ET-012
gps-tracks-enabled bool — чекбокс «Публичные треки» нет
gps-tracks-activities JSON-array — выбранные активности нет
gps-tracks-sources JSON-array — выбранные источники нет
gps-tracks-color-mode `"source" "activity"`

REQ-F-18 в TRZ §3: «никакой миграции localStorage не нужно». Существующие сессии при следующей загрузке автоматически получают новый порог GPS_TRACKS_MIN_ZOOM = 5 и видят слой на z5-z7.

4.2 MapLibre LRU (browser-side)

Браузерный MapLibre кэширует тайлы в собственном LRU. После ET-012:

  • Ключевое пространство кэша: (source_id, z, x, y) — расширяется на z ∈ {5, 6, 7}.
  • Объём — управляется MapLibre, по умолчанию ~100 МБ; пользовательский опыт не страдает.
  • Никакой синхронизации с серверным _gps_tile_cache не нужно (independent caches; их инвалидация — через POST /api/gps-tracks/cache/clear, которая инвалидирует только серверный LRU; клиент дёрнет свежий MVT при следующем reload или после move-выхода-возврата за пределы LRU).

5. Контракты API

5.1 GET /api/gps-tracks/tiles/{z}/{x}/{y}.mvt

Аспект До ET-012 После ET-012
Path-параметр z принимается 0 ≤ z ≤ 22 принимается 0 ≤ z ≤ 22 (без изменений)
Response 200 для z=8..11 — непустой MVT; для z<8 — пустой MVT для z=5..11 — непустой MVT (новые z=5/6/7); для z<5 — пустой MVT
Response Content-Type application/x-protobuf application/x-protobuf (без изменений)
Properties фич id, activity, source, sources, length_km, name, ext_url без изменений
Cache-Control public, max-age=300 без изменений
Размер тела (z5) (раньше не использовалось клиентом, был ~0-50 KB пустой) ≤ 200 KB до gzip (M-8)

Старые клиенты (старый gps_tracks.js, который никогда не запрашивал z=5..7) — продолжают работать. Никакого breaking change в контракте нет.

5.2 GET /api/gps-tracks?bbox=...

Без изменений. Этот endpoint обслуживает GeoJSON-режим z≥12, а ET-012 не трогает z≥12.

5.3 POST /api/gps-tracks/cache/clear

Без изменений. Инвалидирует серверный _gps_tile_cache целиком (все z). Pipeline gps-collector дёргает его после успешного прогона (ADR-007 §7). После ET-012 этот вызов очищает и тайлы z=5..7 автоматически.

5.4 GET /api/gps-tracks/{id}/download

Без изменений. ET-011 endpoint, не зависит от zoom.

5.5 GET /api/gps-tracks/health

Без изменений. Возвращает tracks_total, tracks_by_source, last_pipeline_run.

6. Миграции

Нет. Никаких миграций БД, никаких миграций localStorage, никаких миграций конфигов.

При деплое в test:

  • БД data/gps_tracks.sqlite — без изменений (read-only для app).
  • data/centralfederal.sqlite — без изменений (другой слой).
  • Серверный MVT-кэш — очищается через POST /api/gps-tracks/cache/clear для подстраховки (см. 07-infra-requirements.md §6.2 шаг 4); это не миграция, а кэш-инвалидация.
  • Клиентский MapLibre LRU — самоочищается при reload браузера; явной миграции не нужно.

7. Тестовые данные

7.1 Для unit-тестов

tests/unit/test_gps_mvt_zoom_tiers.py (новый, REQ-F-09):

  • Использует in-memory SQLite (как существующие тесты в tests/unit/test_gps_mvt.py).
  • Фикстуры: треки разной длины (например, 1 км, 3 км, 6 км, 12 км, 25 км), геометрия — простые LineString из 5-10 точек.
  • Никаких внешних зависимостей.

tests/unit/test_gps_mvt_simplify.py (новый или расширение, REQ-F-10):

  • Чистые unit-тесты _simplify_coords(coords, z) — массивы coords захардкожены, БД не нужна.

7.2 Для integration-тестов

tests/integration/test_gps_tile_z5_z7.py (новый, REQ-F-11):

  • Использует existing fixture gps_tracks_test_db (фикстура из conftest.py ET-008), которая заливает 50 треков по ЦФО разной длины с реалистичными координатами.
  • При необходимости расширяется до 200 треков для IT-Z5-02.

7.3 Для performance-теста

tests/performance/test_gps_mvt_z5_perf.py (новый, REQ-F-13):

  • Fixture: 500 треков по ЦФО, каждый ≥ 10 км, реалистичная геометрия.
  • Маркер @pytest.mark.perf — не запускается в основном make test.
  • Запускается вручную или отдельным CI-джобом.

7.4 Для UI-тестов

tests/e2e/test_ui_gps_z5.spec.ts (новый, REQ-F-14 / 04b-ui-test-cases.md):

  • Запускается на test-среде https://openclaw.mva154.duckdns.org/enduro/.
  • Данные — реальная БД test-среды (после ET-009 — ~200 треков ЦФО).
  • Скриншот-эталоны для AC-08 (визуальная читаемость) — в tests/e2e/screenshots/et012/.

8. Резервные копии и DR

Без изменений vs ET-008. БД gps_tracks.sqlite бэкапится тем же crontab-скриптом, что и раньше. RPO = 0 (ET-012 не трогает данные).

9. Privacy / Compliance

Аспект Требование
PII в новых MVT Нет нового PII. На z=5..7 в MVT-фичу попадают те же поля, что и на z=8..11: id, activity, source, sources, length_km, name, ext_url. Поле user (потенциальный PII) в MVT не попадает на любых z. Поле name может содержать имя автора — но это уже было разрешено ET-008/ADR-005 для всех z ≥ 8.
Licensing Без изменений (ADR-009 OSM ODbL, ADR-010 EnduroRussia accepted, ADR-012 Wikiloc accepted с обезличиванием). Снижение minzoom не меняет, какие источники exposed клиенту — все треки в БД уже прошли licensing-guard pipeline'а
Attribution MapLibre attribution control отображает атрибуцию всех активных источников; это работает независимо от zoom — на z=5 пользователь видит те же бейджи «© OSM

10. Связанные документы

  • 01-brd.md §6 Зависимости.Backend, §6 Зависимости.Тесты
  • 02-trz.md §3 REQ-F-09..F-14 (тесты), REQ-F-16..F-18 (не меняем конфиги/стили/localStorage)
  • 06-adr/ADR-016-z5-tiling-policy.md §«Решение», §«Последствия»
  • 07-infra-requirements.md §4 (LRU, RAM), §6 (cache clear at deploy)
  • 10-tech-risks.md (этот пакет)
  • docs/work-items/ET-008/08-data-requirements.md §3 (schema, индексы) — наследие
  • docs/work-items/ET-009/08-data-requirements.md (если есть) — наследие