Векторные тайлы

> Кратко. Карта рисует сеть векторными тайлами (MVT) из трёх источников: функций PostGIS через martin (участки, узлы, здания), GeoServer (остальные слои рабочей области) и архивы PMTiles через API (здания). У встраиваемой карты — свои тайлы API. Ниже — какой тайл откуда, как он кешируется и как его запросить самому.

Читаю как

Источники

Источник Что отдаёт Адрес Вход
martin участки, узлы, здания — функции tgid_mvt_<слой>(z, x, y, query_params) в базе города <MARTIN_TILES_URL>/<префикс города><слой>/{z}/{x}/{y}?viewparams=… (за nginx — /geoserver/tiles/…) cookie tgid_gs
GeoServer слои рабочей области города (WMS-MVT, кеш GWC) …/geoserver/… cookie tgid_gs
PMTiles через API статичные слои: здания zdaniya_2, zdaniya_tu /api/v1/static-tiles/{слой}/{z}/{x}/{y}.mvt?v=<версия> токен входа
тайлы встраивания слои ключа встраивания /api/v1/embed/tiles/{слой}/{z}/{x}/{y}?key= ключ

Какие слои идут из martin, задают переменные веба MARTIN_TILES_URL, MARTIN_SOURCE_PREFIX, MARTIN_LAYERS (по умолчанию heatpipesections,uzel,zdaniya_2,zdaniya_tu), MARTIN_WORKSPACE. В собранном вебе их задают с префиксом NUXT_ (NUXT_MARTIN_TILES_URL …): nuxt.config уже вычислен при сборке. Без MARTIN_TILES_URL всё берётся из GeoServer.

martin

  • Функции генерирует scripts/martin/build_martin_functions.py из SQL-представлений GeoServer рабочей области города (featuretype.xml): тот же SQL, те же атрибуты и имя слоя в тайле, поэтому стили MapLibre одни и те же. scripts/martin/build_martin_config.py пишет config.yaml с источником <город>_<слой>.
  • Параметры — строка viewparams как у GeoServer: fragments:74;RAS:on;rev:26;. Функция разбирает её сама.
  • Ниже минимального зума стиля функция отдаёт пустой тайл. Если стиль делит объекты по полю (узлы — по code, участки — по фильтрам MBStyle), в тайл идут только объекты, которые стиль рисует на этом зуме.
  • Атрибуты, которые веб не читает (TILE_UNUSED_COLUMNS), в тайл не идут.
  • После переконвертации базы API возвращает функции сам — пункт city_schema «функции тайлов martin» из MARTIN_SQL_FILE (API одного города) или MARTIN_SQL_DIR/<город>.sql (общий API).
  • Доступ: nginx публикует martin под /geoserver/tiles/ с той же проверкой cookie, что GeoServer (/auth/check); город проверяется по префиксу источника.

Кеш и версия данных

Тайл сети зависит от данных, поэтому веб добавляет в viewparams версию rev:<N> (utils/mvtTileUrl.ts, withTileRevision). Версия — GET /api/v1/map/data-version (сумма счётчиков изменений таблиц; на копии Алматы сейчас {"version":"26"}). После расчёта или правки сети версия растёт, адреса тайлов меняются, и ни кеш GWC, ни кеш martin (cache_size_mb, по умолчанию 512 МБ в памяти; ключ — точная строка запроса), ни браузер старый тайл не отдают. Сбрасывать кеши вручную не нужно; прогреть кеш заранее нельзя — ключ меняется после каждой правки.

PMTiles (статичные слои)

Здания меняются редко, а их тайлы дороже всего строить в базе. Поэтому администратор запекает их в архив PMTiles v3 (окно Статичные слои (PMTiles)); веб подменяет адрес тайлов слоя на API (utils/staticTiles.ts).

Адрес Что Право
GET /api/v1/static-tiles архивы города: зумы, охват, число тайлов, размер, version; администратору — слои, которые можно запечь вход
POST /api/v1/static-tiles/{слой}/build {maxzoom, minzoom?} запечь (Celery; ход — GET /api/v1/task/{id}) администратор
DELETE /api/v1/static-tiles/{слой} удалить архив — слой снова из martin или GeoServer администратор
GET /api/v1/static-tiles/{слой}/{z}/{x}/{y} (.mvt, .pbf — можно) тайл; нет тайла — 204 вход
GET /api/v1/static-tiles/{слой}.pmtiles архив целиком, с Range — для клиентов PMTiles и офлайна вход

Запекаются только слои, у функции которых нет параметров (здания). Тайл отдаётся как лежит в архиве — gzip с Content-Encoding: gzip, Cache-Control: private, max-age=604800 (7 дней); версия архива в адресе (?v=) обходит кеш после пересборки. Архив — <STATIC_TILES_DIR>/<город>/<слой>.pmtiles (по умолчанию files/static_tiles в каталоге API).

Пример на копии Алматы: архив zdaniya_2 — z14–16, 3120 тайлов, 22 МБ; тайл центра города:

curl -s -D - -o tile.mvt "$API/api/v1/static-tiles/zdaniya_2/14/11693/6004.mvt?v=1793287350" -H "Authorization: Bearer $TOKEN"
# 200, content-type: application/vnd.mapbox-vector-tile, content-encoding: gzip, 73 423 байта, 0,28 с

Тайл вне охвата (…/14/11693/6062.mvt) — 204 без тела.

Тайлы встраиваемой карты

/api/v1/embed/tiles/{слой}/{z}/{x}/{y}?key= строятся из тех же запросов, что OGC API (database/ogc_features.py), но только с полями без персональных данных и с прореживанием участков на обзорных зумах (до z12 — Ду от 500, на z12 — от 300). id признака MVT — номер объекта в базе. Тайлы обзорных зумов API держит в памяти 10 минут, ответ — Cache-Control: public, max-age=300. Подробно — Встраивание.

Как проверить тайл

MVT — двоичный формат protobuf. Посмотреть содержимое можно GDAL (драйвер MVT читает и тайл в gzip; номер тайла нужен, чтобы получить координаты Web Mercator):

ogrinfo -ro -so -oo Z=14 -oo X=11693 -oo Y=6004 tile.mvt zdaniya_2
# Feature Count: 1299 / mvt_id: Integer64, objectid: Real, number_1: String, txt: String, floor: String, …

В браузере — map.querySourceFeatures(источник) в консоли страницы карты. В Node fs.readFileSync маленького файла может вернуть срез общего буфера — копируйте в new Uint8Array(buf) перед разбором.

Для разработчика

  • API: scripts/martin/ (генератор функций и конфигурации, install-martin.ps1), database/pmtiles.py (формат PMTiles без сторонних пакетов), database/static_tiles.py, routers/static_tiles.py, задача bake_static_tiles в worker.py, database/embed.py. Описания — docs/martin.md, docs/static-tiles.md.
  • Веб: server/api/geoserver-layers.ts (каталог слоёв и адреса martin), utils/mvtTileUrl.ts, utils/staticTiles.ts, stores/mapStore.ts (transformRequest добавляет токен к адресам API).
  • Тесты: API — tests/test_martin_scripts.py, tests/test_static_tiles.py, tests/test_embed.py; веб — tests/utils/staticTiles.spec.ts.
Хотите увидеть эту функцию на своих данных?Покажем ТГИД на демонстрации и ответим на вопросы инженеров.
Запросить демо