124 lines
6.1 KiB
Markdown
124 lines
6.1 KiB
Markdown
# Tile Server
|
||
|
||
ASP.NET Core сервер векторных тайлов для клиентов **MapLibre GL**.
|
||
|
||
Ежедневно (и при старте) воркер:
|
||
|
||
1. Скачивает все настроенные OSM-extract’ы с Geofabrik (и любых других HTTP-ссылок на `.osm.pbf`).
|
||
2. Собирает из них OpenMapTiles MBTiles через [planetiler-openmaptiles](https://github.com/openmaptiles/planetiler-openmaptiles).
|
||
3. Отдаёт тайлы, TileJSON и стили карты.
|
||
|
||
## Что нужно
|
||
|
||
- .NET 8 SDK
|
||
- **Java 21+** в `PATH` (или путь в `TileServer:JavaPath`) — без Java тайлы не соберутся, уже готовые MBTiles сервер всё равно отдаст
|
||
- Диск: 5–10× размер PBF. ЦФО до **z15** — ориентир несколько ГБ готового MBTiles плюс столько же на staging во время сборки
|
||
- RAM: контейнер ~12g. `TileServer:JvmMaxHeap` — только Java heap, держите **~⅓ лимита** (по умолчанию `4g`). Остальное — mmap/файлы Planetiler и Kestrel. Код **137** = OOM killer, не переполнение `-Xmx`.
|
||
|
||
Тайлы собираются через OpenMapTiles. Схема рассчитана на z0–z14 (дефолт MapTiler/Planetiler). Жёсткий потолок **planetiler-openmaptiles 3.16 — z15**; z16+ этот JAR не умеет. Ближе z15 MapLibre overzoom’ит те же тайлы до z18.
|
||
|
||
Первая сборка ЦФО занимает часы. Для быстрой проверки временно поставьте Monaco:
|
||
|
||
```json
|
||
"Url": "https://download.geofabrik.de/europe/monaco-latest.osm.pbf"
|
||
```
|
||
|
||
## Запуск
|
||
|
||
```bash
|
||
dotnet run --project src/TileServer.Api
|
||
```
|
||
|
||
Откройте http://localhost:5088 — тестовая карта, выбор источника и стиля.
|
||
|
||
```bash
|
||
docker compose up -d --build
|
||
```
|
||
|
||
Сервис слушает **51300** (внутри контейнера 8080). Данные (PBF, MBTiles, стили, ключи) — Docker volume `tile-server-data`, деплой кода его не затирает. Не вызывайте `docker compose down -v`.
|
||
|
||
Если на сервере уже есть `./data`, один раз перенесите:
|
||
|
||
```bash
|
||
docker run --rm -v "$(pwd)/data:/from:ro" -v tile-server-data:/to alpine sh -c "cp -a /from/. /to/"
|
||
docker compose up -d --build
|
||
```
|
||
|
||
## API
|
||
|
||
| Метод | URL | Назначение |
|
||
| --- | --- | --- |
|
||
| `GET` | `/api/v1/tiles/{source}/{z}/{x}/{y}.pbf` | векторный тайл (MapLibre XYZ) |
|
||
| `GET` | `/api/v1/tiles/{source}.json` | TileJSON |
|
||
| `GET` | `/api/v1/styles` | список стилей |
|
||
| `GET` | `/api/v1/styles/{name}?source={source}` | MapLibre style JSON |
|
||
| `PUT` | `/api/v1/styles/{name}` | добавить/обновить стиль |
|
||
| `GET` | `/api/v1/sources` | extract’ы и статус воркера |
|
||
| `PUT` | `/api/v1/sources/{id}` | добавить регион (ещё одна ссылка на PBF) |
|
||
| `POST` | `/api/v1/sync` | прогнать все extract’ы сейчас |
|
||
| `POST` | `/api/v1/sync/{id}` | прогнать один extract |
|
||
| `GET` | `/` | демо MapLibre |
|
||
|
||
Алиасы без `/api/v1`: `/tiles/{source}/{z}/{x}/{y}.pbf`.
|
||
|
||
## Несколько регионов
|
||
|
||
В `appsettings.json` → `TileServer:Extracts` можно перечислить сколько угодно выгрузок. Воркер проходит **все** `Enabled: true` по очереди (чтобы не упереться в RAM).
|
||
|
||
```json
|
||
"Extracts": [
|
||
{
|
||
"Id": "central-fed-district",
|
||
"Name": "Центральный федеральный округ",
|
||
"Url": "https://download.geofabrik.de/russia/central-fed-district-latest.osm.pbf",
|
||
"Center": [37.6173, 55.7558]
|
||
},
|
||
{
|
||
"Id": "northwestern-fed-district",
|
||
"Name": "Северо-Западный федеральный округ",
|
||
"Url": "https://download.geofabrik.de/russia/northwestern-fed-district-latest.osm.pbf",
|
||
"Center": [30.3141, 59.9386]
|
||
}
|
||
]
|
||
```
|
||
|
||
То же самое в runtime: `PUT /api/v1/sources/{id}` (пишет `data/extracts.json`, переживает рестарт).
|
||
|
||
Расписание: cron `TileServer:Sync:Cron`, по умолчанию `0 3 * * *` UTC. Если PBF не изменился (HTTP 304) и MBTiles уже есть — пересборка пропускается, **кроме случая когда в конфиге MaxZoom больше, чем в текущем тайсете** (после поднятия z14→z15 сборка пойдёт сама).
|
||
|
||
Клиенты в TileJSON/стиле видят maxzoom собранной пирамиды (14/15): MapLibre сам растягивает эти тайлы ближе. Если кто-то всё же запросит z16–18, сервер нарежет их из родителя. Вне ЦФО отдаётся 204, не 404.
|
||
|
||
## Стили
|
||
|
||
Кладёте `data/styles/{name}.json` (MapLibre Style Spec v8, схема слоёв OpenMapTiles) или делаете `PUT /api/v1/styles/{name}`.
|
||
|
||
Клиент запрашивает стиль по имени:
|
||
|
||
```
|
||
GET /api/v1/styles/osm-bright?source=central-fed-district
|
||
```
|
||
|
||
Сервер подставляет TileJSON выбранного источника. Из коробки: `osm-bright`, `dark`, `positron`.
|
||
|
||
Пример источника в MapLibre:
|
||
|
||
```js
|
||
const map = new maplibregl.Map({
|
||
container: "map",
|
||
style: "http://localhost:5088/api/v1/styles/osm-bright?source=central-fed-district",
|
||
center: [37.6173, 55.7558],
|
||
zoom: 6
|
||
});
|
||
```
|
||
|
||
## Архитектура
|
||
|
||
```
|
||
Api контроллеры, демо, ProblemDetails
|
||
Application sync, tiles, styles, extracts
|
||
Domain extract, tile coordinate, ошибки
|
||
Infrastructure загрузка PBF, Planetiler, MBTiles/SQLite, файловые стили, hosted worker
|
||
```
|
||
|
||
Данные по умолчанию: `data/` в корне репозитория (`../../data` относительно проекта API). В проде задайте `TileServer__DataDirectory`.
|