Vector tiles

Vector tiles (векторные тайлы) — технология представления картографических данных в виде небольших фрагментов, содержащих геометрические объекты и их атрибуты, а не готовые изображения. В отличие от растровых тайлов, которые представляют собой заранее отрисованные PNG или JPEG-картинки, векторные тайлы содержат исходные геометрии: точки, линии и полигоны.

Каждый тайл обычно соответствует определённому уровню масштабирования и координатам сетки. При загрузке карта получает не изображение дороги или здания, а набор координат и связанных с ними свойств. Отрисовка выполняется уже на стороне клиента.

Основные преимущества:

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

В современных веб-картах векторные тайлы стали стандартом благодаря высокой производительности и гибкости.


Отличия растровых и векторных тайлов

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

Структура работы:

  1. Сервер генерирует изображение.
  2. Браузер загружает PNG или JPEG.
  3. Изображение отображается без возможности изменения объектов.

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

  • простота реализации;
  • широкая поддержка;
  • минимальная нагрузка на клиент.

Недостатки:

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

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

Структура работы:

  1. Сервер отдаёт геометрические данные.
  2. Браузер интерпретирует объекты.
  3. Отрисовка выполняется динамически.

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

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

Недостатки:

  • более сложная обработка;
  • повышенные требования к производительности браузера.

Формат Mapbox Vector Tile (MVT)

Наиболее распространённым стандартом является Mapbox Vector Tile (MVT).

Особенности формата:

  • бинарное представление данных;
  • высокая степень сжатия;
  • использование Protocol Buffers;
  • поддержка нескольких слоёв внутри одного тайла.

Типичный URL векторного тайла:

https://server.com/tiles/{z}/{x}/{y}.pbf

где:

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

Расширение .pbf указывает на использование формата Protocol Buffers.


Поддержка векторных тайлов в Leaflet

Базовая библиотека Leaflet ориентирована на работу с растровыми тайлами:

L.tileLayer(urlTemplate).addTo(map);

Для работы с векторными тайлами используются дополнительные плагины:

  • Leaflet.VectorGrid;
  • MapLibre GL Leaflet;
  • Tangram Leaflet;
  • Leaflet.MapboxVectorTile.

Наиболее популярным решением считается Leaflet.VectorGrid.


Подключение Leaflet.VectorGrid

Через CDN:

<script src="https://unpkg.com/leaflet.vectorgrid/dist/Leaflet.VectorGrid.bundled.js"></script>

После подключения появляется пространство имён:

L.vectorGrid

Основные компоненты:

L.vectorGrid.protobuf()
L.vectorGrid.slicer()

Загрузка MVT-тайлов

Простейший пример:

const vectorLayer = L.vectorGrid.protobuf(
    "https://server.com/tiles/{z}/{x}/{y}.pbf"
);

vectorLayer.addTo(map);

Leaflet автоматически:

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

Настройка максимального масштаба

Параметры слоя:

const vectorLayer = L.vectorGrid.protobuf(
    "https://server.com/tiles/{z}/{x}/{y}.pbf",
    {
        maxZoom: 18
    }
);

Также могут использоваться:

{
    minZoom: 0,
    maxZoom: 20
}

Стилизация слоёв

Каждый MVT-тайл может содержать несколько слоёв.

Например:

  • roads;
  • buildings;
  • water;
  • landuse.

Настройка внешнего вида:

const vectorLayer = L.vectorGrid.protobuf(
    url,
    {
        vectorTileLayerStyles: {
            roads: {
                color: "red",
                weight: 2
            }
        }
    }
);

Стилизация дорог

Пример оформления дорожной сети:

vectorTileLayerStyles: {
    roads: {
        color: "#444",
        weight: 3,
        opacity: 0.8
    }
}

Параметры линий:

Свойство Назначение
color цвет линии
weight толщина
opacity прозрачность
dashArray пунктир
lineCap форма концов
lineJoin соединение сегментов

Стилизация полигонов

Для зданий:

vectorTileLayerStyles: {
    buildings: {
        fill: true,
        fillColor: "#cfcfcf",
        fillOpacity: 0.8,
        color: "#888",
        weight: 1
    }
}

Для парков:

parks: {
    fillColor: "#8bc34a",
    fillOpacity: 0.7,
    color: "#4caf50"
}

Стилизация точечных объектов

Точки могут отображаться как окружности:

places: {
    radius: 5,
    fillColor: "#2196f3",
    color: "#ffffff",
    weight: 1
}

Для разных типов объектов допустимо использовать индивидуальные стили.


Динамическая стилизация

Вместо объекта можно использовать функцию.

Пример:

roads: function(properties) {
    return {
        color:
            properties.class === "motorway"
                ? "red"
                : "gray",
        weight:
            properties.class === "motorway"
                ? 5
                : 2
    };
}

Leaflet будет вызывать функцию для каждого объекта.


Использование атрибутов объектов

Типичная структура свойств:

{
    name: "Main Street",
    class: "primary",
    bridge: false,
    tunnel: false
}

Обращение к данным:

roads: function(props) {
    return {
        color:
            props.bridge
                ? "orange"
                : "black"
    };
}

Это позволяет строить сложные правила визуализации.


Отображение объектов разных классов

Пример классификации дорог:

roads: function(props) {

    switch(props.class) {

        case "motorway":
            return {
                color: "#ff5722",
                weight: 6
            };

        case "primary":
            return {
                color: "#ff9800",
                weight: 4
            };

        default:
            return {
                color: "#757575",
                weight: 2
            };
    }
}

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


Интерактивность объектов

Векторные тайлы позволяют взаимодействовать с отдельными объектами.

Необходимо включить:

interactive: true

Пример:

const layer = L.vectorGrid.protobuf(
    url,
    {
        interactive: true
    }
);

После этого становятся доступны события.


Обработка кликов

Событие выбора объекта:

layer.on("click", function(e) {

    console.log(e.layer.properties);

});

Содержимое:

e.layer.properties

может содержать:

{
    name: "Central Park",
    type: "park"
}

Всплывающие окна

Отображение информации:

layer.on("click", function(e) {

    L.popup()
        .setLatLng(e.latlng)
        .setContent(
            e.layer.properties.name
        )
        .openOn(map);

});

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


Подсветка при наведении

Пример:

layer.on("mouseover", function(e) {

    layer.setFeatureStyle(
        e.layer.properties.id,
        {
            color: "yellow",
            weight: 5
        }
    );

});

Возврат стиля:

layer.resetFeatureStyle(id);

Такая функциональность часто используется в кадастровых и аналитических системах.


Фильтрация объектов

Можно скрывать ненужные элементы:

roads: function(props) {

    if(props.class === "service") {
        return null;
    }

    return {
        color: "#555"
    };

}

Объекты с классом service отображаться не будут.


Использование slicer

Не всегда данные доступны в формате MVT.

Если имеется GeoJSON, можно создать виртуальные тайлы на стороне клиента.

Пример:

L.vectorGrid.slicer(geojsonData);

Полный вариант:

const layer = L.vectorGrid.slicer(
    geojsonData,
    {
        vectorTileLayerStyles: {
            sliced: {
                color: "blue"
            }
        }
    }
);

layer.addTo(map);

Когда использовать slicer

Подход удобен для:

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

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


Производительность векторных тайлов

На скорость работы влияют:

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

Основные рекомендации:

{
    maxNativeZoom: 14
}

Позволяет избежать загрузки чрезмерно детализированных данных.

Также полезно:

  • упрощать геометрию;
  • исключать лишние атрибуты;
  • разделять данные по слоям.

Сравнение Canvas и SVG

Leaflet поддерживает два механизма рендеринга.

SVG

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

  • удобная работа с DOM;
  • простая отладка;
  • хорошая интерактивность.

Недостатки:

  • снижение производительности при большом количестве объектов.

Canvas

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

  • высокая скорость;
  • меньшее потребление памяти.

Недостатки:

  • отсутствуют отдельные DOM-элементы.

Для крупных наборов векторных тайлов обычно используется Canvas-рендеринг.


Использование собственных серверов тайлов

Популярные серверные решения:

  • TileServer GL;
  • Tegola;
  • Tile38;
  • GeoServer.

Типичный процесс:

  1. Данные хранятся в PostGIS.
  2. Сервер генерирует MVT.
  3. Leaflet загружает тайлы.
  4. Клиент выполняет отрисовку.

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

Часто источником данных выступает проект OpenStreetMap.

Последовательность обработки:

  1. Экспорт данных OSM.
  2. Импорт в PostGIS.
  3. Генерация MVT-тайлов.
  4. Публикация через сервер тайлов.
  5. Отображение в Leaflet.

Такой подход позволяет создавать полностью собственные картографические сервисы.


Комбинирование растровых и векторных слоёв

На практике часто используется смешанная архитектура.

Растровая подложка:

L.tileLayer(rasterUrl).addTo(map);

Векторный слой поверх:

L.vectorGrid.protobuf(vectorUrl)
    .addTo(map);

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

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

Работа с несколькими слоями внутри MVT

Один тайл может содержать:

roads
buildings
water
parks
labels

Настройка:

vectorTileLayerStyles: {

    roads: {
        color: "#444"
    },

    buildings: {
        fillColor: "#ddd"
    },

    water: {
        fillColor: "#90caf9"
    }

}

Каждый слой стилизуется независимо.


Ограничения технологии

Несмотря на высокую эффективность, векторные тайлы имеют ряд особенностей:

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

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