290 lines
20 KiB
Markdown
290 lines
20 KiB
Markdown
---
|
||
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/` — наследие нарезки тайлов
|