Files
enduro-trails/docs/work-items/ET-013/08-data-requirements.md
claude-bot 6b88bcee28
Some checks failed
CI / lint (push) Successful in 5s
CI / test (push) Failing after 5s
CI / build (push) Has been skipped
architect(ET): auto-commit from architect run_id=79
2026-06-04 09:40:50 +00:00

290 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: data-requirements
work_item_id: ET-013
title: "Требования к данным — ET-013: Zoom-aware paint для terrain-слоёв на z9-z11"
version: 1
status: approved
created_at: 2026-06-04
authors:
- "agent:architect"
---
# Требования к данным — ET-013
## 1. Резюме
ET-013 — **pure client render change**. Никаких изменений схемы БД,
никаких новых таблиц/индексов/миграций, никаких изменений тайлов на
диске, никаких новых ключей `localStorage`, никаких изменений
конфигов источников.
Меняется **только то, как уже существующие PNG-тайлы рельефа
отрисовываются MapLibre на клиенте**:
- `raster-opacity` становится `interpolate`-выражением по `['zoom']`
(вместо константы).
- Для hillshade добавляется `raster-contrast` (тоже `interpolate`).
- `raster-resampling` для обоих terrain-слоёв переключается с
`'linear'` на `'nearest'`.
**Меняется:**
- набор `raster paint properties` у двух MapLibre-слоёв
(`terrain-hillshade`, `terrain-tri`);
- визуальная читаемость рельефа на z9-z11 (целевое улучшение).
**Не меняется:**
- содержимое и формат PNG-тайлов в `data/terrain/{hillshade,tri,hypso}/`
(PH-6 наследие);
- schema БД `centralfederal.sqlite` и `gps_tracks.sqlite`;
- контракт API `/terrain/{layer}/{z}/{x}/{y}.png` (REQ-F-18);
- содержимое тайлов hypso (в UI не подключён, OOS);
- параметры генератора hillshade на сервере (azimuth, altitude,
z-factor — PH-6, OOS);
- параметры классификации TRI (5-уровневая палитра — PH-6, OOS);
- ключи `localStorage` (`terrain-hillshade`, `terrain-tri` — REQ-F-17);
- содержимое `config/*.yaml`;
- стили `style.json`, `style-dark.json` (растровые terrain-слои в
них не описаны — добавляются динамически из JS).
## 2. Архитектурные границы данных
| Слой данных | Тип | Расположение | Изменения в ET-013 |
|-----------------------------------|----------------|----------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| OSM-vector (`trails`) | существующий | `/app/data/centralfederal.sqlite` | **нет** |
| Личные GPX треки (ET-006) | существующий | браузер (memory) | **нет** |
| Публичные GPS треки (ET-008) | существующий | `/app/data/gps_tracks.sqlite` | **нет** |
| OSRM-граф | существующий | `/app/data/enduro.osrm.*` | **нет** |
| Terrain hillshade PNG | существующий | `data/terrain/hillshade/{z}/{x}/{y}.png` (z=8..14) | **read-only**: добавляется новая комбинация `(z=9, x, y)`, которая клиент раньше не запрашивал. Тайлы на диске уже есть (PH-6 нарезка) |
| Terrain TRI PNG | существующий | `data/terrain/tri/{z}/{x}/{y}.png` (z=8..14) | **read-only**: те же тайлы, что и раньше; меняется только paint |
| Terrain hypso PNG | существующий | `data/terrain/hypso/{z}/{x}/{y}.png` | **не используется** в ET-013 (OOS) |
| User UI state | существующий | `localStorage` | **нет** новых ключей, нет миграции |
| MapLibre client tile cache | существующий | браузер (LRU MapLibre, ~100 MB) | **расширяется ключевым пространством**: теперь могут лежать тайлы hillshade с `z = 9` (раньше не запрашивались) |
| Серверный кэш `/terrain/*` | не предусмотрен | n/a (FileResponse + Cache-Control immutable) | **нет** |
## 3. Серверные данные
### 3.1 Структура `data/terrain/`
**Без изменений vs PH-6.** Структура каталога:
```
data/terrain/
├── hillshade/
│ ├── 8/{x}/{y}.png # baseline
│ ├── 9/{x}/{y}.png # используется ET-013 впервые на клиенте
│ ├── 10/{x}/{y}.png # baseline (10+ уже использовался)
│ ├── 11/{x}/{y}.png
│ ├── 12/{x}/{y}.png
│ ├── 13/{x}/{y}.png
│ └── 14/{x}/{y}.png
├── tri/ # та же структура, z=8..14
└── hypso/ # та же структура, в UI не подключён
```
Никаких ALTER/CREATE/INSERT/UPDATE/DELETE на стороне данных. Никакой
догенерации тайлов. Никакого преобразования формата (PNG остаётся
PNG 256×256).
### 3.2 Объёмы данных
| Метрика | Текущее (PH-6) | После ET-013 | Гейт |
|------------------------------------------|---------------------|-------------------------------|------------------------------------------------------|
| Объём PNG hillshade на диске | ~ X MB (PH-6 baseline) | без изменений | n/a |
| Объём PNG TRI на диске | ~ Y MB | без изменений | n/a |
| Запросы hillshade за сессию | N (только z≥10) | ~ 1.25-1.35 × N (добавился z=9) | BRD M-10: ≤ +35% |
| Запросы TRI за сессию | M (z=5..14) | без изменений | n/a |
### 3.3 Pre-deploy validation тайлов z9-z11
**Обязательная проверка перед merge** (BRD R-11, AC-19):
```bash
curl -sI 'https://openclaw.mva154.duckdns.org/enduro/terrain/hillshade/9/308/158.png' | head -1
curl -sI 'https://openclaw.mva154.duckdns.org/enduro/terrain/hillshade/10/617/317.png' | head -1
curl -sI 'https://openclaw.mva154.duckdns.org/enduro/terrain/hillshade/11/1234/635.png' | head -1
```
Ожидается `HTTP/1.1 200 OK` на все три. Если 404 — задача
останавливается, открывается PH-6 follow-up «hillshade-z9-z14
backfill». См. `07-infra-requirements.md` §6.2 шаг 1.
### 3.4 API endpoint `terrain_tile`
**Без изменений** (`src/api/main.py:1240`):
- URL: `GET /terrain/{layer}/{z}/{x}/{y}.png`, `layer ∈ {hillshade, tri, hypso}`.
- Возвращает: PNG из файловой системы (sendfile через `FileResponse`).
- Заголовки: `Cache-Control: public, max-age=31536000, immutable`
без изменений. Браузерный кэш и nginx-кэш агрессивно поглощают
повторы.
- Контракт OpenAPI — без изменений (REQ-F-18, NFR-04).
## 4. Клиентские данные
### 4.1 localStorage
**Без изменений vs PH-6 / ET-007.** Используются ключи:
| Ключ | Назначение | Изменения в ET-013 |
|----------------------------|---------------------------------------------|--------------------|
| `terrain-hillshade` | `'1' | '0'` — чекбокс «Тени рельефа» | **нет** |
| `terrain-tri` | `'1' | '0'` — чекбокс «Перепады» | **нет** |
REQ-F-17 в TRZ §3: «никакой миграции localStorage не нужно».
Существующие сессии при следующей загрузке автоматически получают
новый UI-порог 9 (вместо 10) и новые `HILLSHADE_PAINT` / `TRI_PAINT`
константы. Если у пользователя `terrain-hillshade === '1'` и текущий
zoom ≥ 9 — hillshade покажется автоматически (раньше показался бы
только на z ≥ 10).
### 4.2 MapLibre LRU (browser-side)
Браузерный MapLibre кэширует растровые тайлы в собственном LRU
(~100 MB по умолчанию). После ET-013:
- Ключевое пространство кэша: `(source_id, z, x, y)` — расширяется
для `terrain-hillshade-source` на `z = 9` (раньше source имел
`minzoom: 10` → запросов z=9 не было).
- Объём — управляется MapLibre, ~100 MB. Дельта мизерная (тайл
hillshade ≈ 8-30 KB).
- Никакой синхронизации/инвалидации не нужно (тайлы на сервере
не меняются; `Cache-Control: immutable` гарантирует консистентность).
### 4.3 In-memory paint constants
Новые константы в `src/web/app.js` после `TERRAIN_BASE_URL`:
```js
const HILLSHADE_PAINT = {
'raster-opacity': ['interpolate', ['linear'], ['zoom'],
9, 0.65, 10, 0.60, 11, 0.55, 12, 0.50, 14, 0.40],
'raster-contrast': ['interpolate', ['linear'], ['zoom'],
9, 0.40, 10, 0.35, 11, 0.30, 12, 0.15, 14, 0.00],
'raster-resampling': 'nearest'
};
const TRI_PAINT = {
'raster-opacity': ['interpolate', ['linear'], ['zoom'],
5, 0.55, 7, 0.65, 8, 0.70,
9, 0.80, 10, 0.85, 11, 0.85,
12, 0.75, 15, 0.70],
'raster-resampling': 'nearest'
};
```
- Это **компилируемые MapLibre `interpolate`-выражения**, не «данные»
в архитектурном смысле. Живут в коде, изменяются коммитом
(BRD §6 q&a «Делать ли paint-таблицы переменными окружения /
config'ом? Нет — преждевременная абстракция»).
- Память: < 1 KB суммарно. Производительность: MapLibre кэширует
скомпилированные выражения (NFR-01).
## 5. Контракты API
### 5.1 `GET /terrain/{layer}/{z}/{x}/{y}.png`
| Аспект | До ET-013 | После ET-013 |
|-----------------------|--------------------------------------------------------|-------------------------------------------------------------------------------------|
| Поддерживаемые `layer`| `hillshade`, `tri`, `hypso` | без изменений |
| Path-параметр `z` | принимается любой валидный z, тайлы на диске z=8..14 | без изменений |
| Response 200 | для существующих `(z, x, y)` PNG | без изменений |
| Response 404 | для несуществующих `(z, x, y)` | без изменений |
| Response Content-Type | `image/png` | без изменений |
| Cache-Control | `public, max-age=31536000, immutable` | без изменений |
**Старые клиенты** (старый `app.js` со старым `minzoom = 10` для
hillshade) — продолжают работать. Никакого breaking change в
контракте нет (NFR-04).
### 5.2 Прочие endpoint'ы
ET-013 не трогает: `/api/gps-tracks/*`, `/api/trails/*`, `/api/route/*`,
`/api/health`. Их контракты — без изменений.
## 6. Миграции
**Нет.** Никаких миграций БД, миграций localStorage, миграций
конфигов, миграций тайлов.
При деплое в test:
- `data/terrain/*` — без изменений (read-only для `app`).
- БД `centralfederal.sqlite`, `gps_tracks.sqlite` — без изменений.
- Серверный кэш — отсутствует у `/terrain/*` (статическая раздача
с `Cache-Control: immutable`).
- Клиентский MapLibre LRU — самоочищается при reload браузера;
явной миграции не нужно.
- localStorage — старые ключи интерпретируются как раньше;
включённый ранее hillshade автоматически появится на z9 (REQ-F-17,
AC-14).
## 7. Тестовые данные
### 7.1 Для unit-тестов
`tests/unit/test_terrain_paint.py` (новый, REQ-F-13 / REQ-F-14):
- Python-парсер исходного `src/web/app.js` через `re`.
- Никаких внешних зависимостей.
- Никаких фикстур данных.
- Проверяет наличие `HILLSHADE_PAINT` / `TRI_PAINT`, наличие
ключевых stops (`9, 0.65`, `11, 0.55`, `14, 0.40`, `8, 0.70`,
`10, 0.85`), наличие `'raster-resampling': 'nearest'`, порог
`zoom < 9` в `updateHillshadeAvailability`.
### 7.2 Для integration-тестов
`tests/integration/test_terrain_z9_tiles.py` (новый, REQ-F-15):
- Использует FastAPI `TestClient` для `src/api/main.py:app`.
- Опирается на наличие файла `data/terrain/hillshade/9/<x>/<y>.png`
если каталога нет, тест `skipped` с reason (CI без данных).
- На test-среде mva154 (где данные есть) — выполняется как
smoke-проверка endpoint'а.
- Дополнительно: `test_hillshade_invalid_zoom_404` — sanity на
невалидном zoom.
### 7.3 Для UI-тестов (Playwright)
`04b-ui-test-cases.md` — список тест-кейсов TC-UI-01..TC-UI-10:
- Запускается на test-среде `https://openclaw.mva154.duckdns.org/enduro/`.
- Данные — реальные PNG-тайлы рельефа на mva154 (PH-6 нарезка).
- Скриншот-эталоны для AC-06..AC-12 (визуальная читаемость) — в
`tests/e2e/screenshots/et013/`.
- Скриншоты сравниваются оператором (качественная приёмка), не
пиксельный diff (BRD M-9, R-1..R-3).
## 8. Резервные копии и DR
Без изменений vs PH-6.
- БД `centralfederal.sqlite`, `gps_tracks.sqlite` — бэкап тем же
crontab-скриптом, что и раньше; ET-013 не трогает.
- PNG-тайлы `data/terrain/*` — регенерируются из SRTM при необходимости
(PH-6 pipeline). RPO для тайлов = время регенерации (часы),
но они read-only и не теряются при деплое ET-013.
RPO для ET-013: 0 (никаких данных не пишется/не теряется).
## 9. Privacy / Compliance
| Аспект | Требование |
|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| PII | **Нет.** PNG-тайлы рельефа — derivative из SRTM 30 м (NASA, public domain). Никаких персональных данных нигде в data-flow ET-013 |
| Licensing | **Без изменений** (PH-6 наследие: SRTM 30 m — public domain; derivative PNG распространяется свободно). ET-013 не меняет источник данных |
| Attribution | MapLibre attribution control отображает атрибуцию активных источников (OSM, Esri). Атрибуция SRTM/NASA не выводится в UI (PH-6 решение); ET-013 это не меняет |
| GDPR / 152-ФЗ | Не применимо (нет PII) |
## 10. Связанные документы
- `01-brd.md` §2.1 (текущая реализация), §3 (F-01..F-14), §6 (Зависимости.Данные)
- `02-trz.md` §3 REQ-F-04..REQ-F-09 (paint constants), REQ-F-13..REQ-F-15 (тесты), REQ-F-17 (localStorage), REQ-F-18 (API), REQ-F-19 (configs/styles)
- `06-adr/ADR-017-zoom-aware-terrain-paint.md` §«Решение», §«Последствия»
- `07-infra-requirements.md` §3 (network), §6 (deploy procedure), §3.1 (ingress estimate)
- `10-tech-risks.md` (этот пакет)
- `docs/work-items/ET-012/08-data-requirements.md` — образец «read-pattern change» документа (наследие)
- `docs/phases/PH-6.terrain/` — наследие нарезки тайлов