feat(proj): init

This commit is contained in:
vl.arkhangelskii
2026-09-21 03:53:15 +03:00
commit 3e34f391b3
258 changed files with 20968 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
# 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: ориентир `TileServer:JvmMaxHeap` ≈ 0.5× размер PBF (по умолчанию `8g`)
Тайлы собираются через 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`.