Тайлификация данных

Тайлификация данных (tiling) — процесс разбиения пространственной информации на множество небольших фрагментов, называемых тайлами (tiles), которые загружаются и отображаются по мере необходимости. В основе большинства современных веб-карт, включая решения на базе Mapbox GL JS, лежит именно тайловая архитектура.

При работе с крупными наборами геоданных отображение всего массива объектов одновременно приводит к значительным затратам памяти, увеличению времени загрузки и снижению производительности браузера. Тайлификация позволяет передавать только ту часть данных, которая соответствует текущей области просмотра карты.

Вместо единого файла с миллионами объектов карта получает десятки или сотни небольших блоков данных, каждый из которых отвечает за конкретный участок территории и уровень масштабирования.


Зачем нужна тайлификация

Основные задачи тайлификации:

  • уменьшение объёма передаваемых данных;
  • ускорение отображения карты;
  • снижение нагрузки на клиентское устройство;
  • возможность работы с очень большими наборами пространственной информации;
  • поддержка плавного масштабирования и перемещения карты.

Например, если карта содержит информацию обо всех дорогах мира, нет необходимости загружать данные Африки при просмотре территории Японии. Благодаря тайлам клиент получает только нужный набор данных.


Тайловая сетка

В Mapbox GL JS используется система координат Web Mercator, в которой поверхность Земли проецируется на квадратную плоскость.

Эта плоскость последовательно делится на квадраты.

На нулевом уровне масштабирования существует только один тайл:

z=0

+---------+
|    0    |
+---------+

На первом уровне масштабирования карта делится на четыре части:

z=1

+----+----+
|0,0 |1,0 |
+----+----+
|0,1 |1,1 |
+----+----+

На втором уровне масштабирования количество тайлов увеличивается до шестнадцати:

z=2

4 × 4 тайла

Общее количество тайлов вычисляется по формуле:

2^z ^z = 4^z

где:

  • z — уровень масштабирования;
  • количество строк равно 2^z;
  • количество столбцов равно 2^z.

Например:

Zoom Количество тайлов
0 1
1 4
2 16
3 64
10 1 048 576

По мере увеличения масштаба количество тайлов растёт экспоненциально.


Координаты тайла

Каждый тайл имеет три координаты:

z/x/y

где:

  • z — уровень масштабирования;
  • x — положение по горизонтали;
  • y — положение по вертикали.

Пример:

12/2154/1348

Это означает:

  • масштаб 12;
  • столбец 2154;
  • строка 1348.

Такая схема применяется практически во всех картографических сервисах.


Растровые и векторные тайлы

Mapbox GL JS способен работать с двумя основными типами тайлов.

Растровые тайлы

Растровый тайл представляет собой готовое изображение.

Обычно используются форматы:

  • PNG;
  • JPEG;
  • WebP.

Подобный тайл уже содержит отрисованную карту.

map.addSource('satellite', {
    type: 'raster',
    tiles: [
        'https://server.com/{z}/{x}/{y}.png'
    ],
    tileSize: 256
});

Преимущества:

  • простота генерации;
  • совместимость с большинством GIS-систем.

Недостатки:

  • невозможность изменить стиль на клиенте;
  • больший объём данных;
  • потеря качества при масштабировании.

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

Векторный тайл содержит не изображение, а геометрические объекты:

  • точки;
  • линии;
  • полигоны;
  • атрибуты объектов.

Отрисовка выполняется непосредственно в браузере средствами WebGL.

map.addSource('roads', {
    type: 'vector',
    url: 'mapbox://examples.roads'
});

Преимущества:

  • компактность;
  • гибкая стилизация;
  • высокая скорость отображения;
  • возможность интерактивного взаимодействия с объектами.

Недостатки:

  • более сложная генерация;
  • повышенные требования к обработке данных.

Формат Mapbox Vector Tile

Стандарт Mapbox Vector Tile (MVT) является основой большинства проектов на Mapbox GL JS.

Внутри одного тайла могут находиться:

Tile
 ├─ roads
 ├─ buildings
 ├─ water
 ├─ landuse
 └─ places

Каждый слой содержит набор геометрий и атрибутов.

Пример структуры:

{
  "id": 1,
  "type": "Feature",
  "properties": {
    "name": "Main Street",
    "class": "primary"
  }
}

При загрузке тайла браузер получает бинарный protobuf-файл и преобразует его в графические примитивы.


Размер тайла

Существует два наиболее распространённых размера:

256 × 256 пикселей
512 × 512 пикселей

Mapbox по умолчанию использует:

512 × 512

Пример настройки:

map.addSource('raster-source', {
    type: 'raster',
    tiles: [
        'https://server.com/{z}/{x}/{y}.png'
    ],
    tileSize: 512
});

Увеличенный размер тайла уменьшает количество запросов к серверу, однако каждый отдельный файл становится тяжелее.


Буферизация тайлов

Геометрия объектов часто пересекает границы тайлов.

Например:

+-----+-----+
|     |     |
|=====|=====|
|     |     |
+-----+-----+

Если дорога проходит через два соседних тайла, при отсутствии дополнительных данных на границе могут возникать разрывы.

Для решения проблемы используется буфер (buffer).

Во время генерации векторного тайла в него включаются объекты, выходящие за пределы основной области.

Tile Area
+-------------+
|  Buffer     |
| +---------+ |
| | Content | |
| +---------+ |
|             |
+-------------+

Благодаря этому линии и полигоны отображаются непрерывно.


Уровни детализации

Одни и те же данные обычно существуют в нескольких вариантах детализации.

Для мелких масштабов:

Мир
 └─ только основные объекты

Для крупных масштабов:

Город
 ├─ улицы
 ├─ дома
 ├─ дорожные знаки
 ├─ POI
 └─ адреса

Процесс упрощения геометрии называется генерализацией.

Например, сложный полигон с тысячами вершин может быть преобразован в упрощённую форму для отображения на небольших масштабах.

Это существенно уменьшает размер тайлов.


Тайлификация GeoJSON

GeoJSON представляет собой единый набор данных.

Пример:

map.addSource('cities', {
    type: 'geojson',
    data: 'cities.geojson'
});

При небольшом количестве объектов этого достаточно.

Однако крупный файл может содержать:

  • сотни тысяч точек;
  • десятки мегабайт данных;
  • сложные полигоны.

В таких случаях GeoJSON становится неэффективным.

Типичный путь оптимизации:

GeoJSON
    ↓
Tile Generator
    ↓
MVT Tiles
    ↓
Mapbox GL JS

После тайлификации карта начинает загружать только необходимые участки данных.


Клиентская тайлификация GeoJSON

Mapbox GL JS способен самостоятельно разбивать GeoJSON на внутренние тайлы.

map.addSource('earthquakes', {
    type: 'geojson',
    data: earthquakesData
});

После загрузки источник проходит несколько этапов:

  1. анализ объектов;
  2. пространственная индексация;
  3. разделение по тайлам;
  4. генерация буферов;
  5. подготовка к рендерингу.

Такой подход удобен для небольших и средних наборов данных.


Серверная тайлификация

Для больших объёмов данных чаще применяется предварительная генерация тайлов на сервере.

Схема выглядит следующим образом:

PostGIS
    ↓
Tippecanoe
    ↓
MVT
    ↓
CDN
    ↓
Mapbox GL JS

Преимущества:

  • минимальная нагрузка на браузер;
  • быстрая доставка через CDN;
  • возможность обслуживать миллионы пользователей.

Генерация тайлов с помощью Tippecanoe

Одним из самых популярных инструментов является Tippecanoe.

Пример создания набора тайлов:

tippecanoe \
-o cities.mbtiles \
-z14 \
-Z0 \
cities.geojson

Параметры:

  • -o — выходной файл;
  • -z — максимальный zoom;
  • -Z — минимальный zoom.

Результатом становится база MBTiles, содержащая набор векторных тайлов.


Использование тайлового источника в Mapbox GL JS

Подключение векторных тайлов:

map.addSource('cities', {
    type: 'vector',
    tiles: [
        'https://tiles.example.com/cities/{z}/{x}/{y}.pbf'
    ]
});

Добавление слоя:

map.addLayer({
    id: 'cities-layer',
    type: 'circle',
    source: 'cities',
    'source-layer': 'cities',
    paint: {
        'circle-radius': 4,
        'circle-color': '#ff0000'
    }
});

Здесь:

  • источник получает тайлы по URL;
  • каждый тайл загружается отдельно;
  • объекты отображаются только в пределах текущего окна карты.

Кэширование тайлов

После загрузки тайлы сохраняются в памяти браузера.

Повторный просмотр области карты обычно не требует повторной загрузки данных.

Сценарий работы:

Пользователь перемещает карту
          ↓
Тайл загружается
          ↓
Тайл сохраняется в кэше
          ↓
Повторный просмотр
          ↓
Использование локальной копии

Кэширование существенно уменьшает сетевой трафик и ускоряет навигацию.


Пирамида тайлов

Вся система тайлификации образует древовидную структуру.

z=0
  |
  +-- z=1
       |
       +-- z=2
            |
            +-- z=3

Каждый тайл верхнего уровня имеет четыре дочерних тайла следующего масштаба.

Такую структуру называют пирамидой тайлов (Tile Pyramid).

Она позволяет быстро определять, какие данные необходимо загрузить для конкретного масштаба и области просмотра.


Влияние тайлификации на производительность

Грамотно организованная тайловая структура обеспечивает:

  • быстрый старт карты;
  • минимальный объём сетевых запросов;
  • эффективное использование памяти;
  • плавное масштабирование;
  • высокую частоту кадров при рендеринге;
  • возможность работы с наборами данных размером в гигабайты.

Именно тайлификация делает возможным отображение огромных геопространственных массивов в браузере в режиме реального времени, сохраняя высокую производительность Mapbox GL JS даже при работе со сложными векторными слоями и многомиллионными коллекциями объектов.