Добавление изображений в sprite

Векторные карты в Mapbox GL JS используют систему стилей, где большинство графических элементов представлено через иконки и изображения. Эти изображения объединяются в единый набор ресурсов — sprite, который обеспечивает быстрый доступ к графике при рендеринге карты. Sprite представляет собой комбинацию растрового файла (PNG/WebP) и JSON-описания координат каждого изображения внутри этого файла.

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


Структура sprite в стиле Mapbox

В стиле Mapbox GL JS sprite задаётся через свойства:

  • sprite: базовый URL без расширения

  • автоматически подгружаются файлы:

    • sprite.png — изображение-атлас
    • sprite.json — описание координат

Пример:

{
  "version": 8,
  "sprite": "mapbox://sprites/username/styleid"
}

или при локальном использовании:

{
  "version": 8,
  "sprite": "https://example.com/sprites/sprite"
}

В этом случае библиотека запрашивает:

  • https://example.com/sprites/sprite.png
  • https://example.com/sprites/sprite.json

JSON содержит метаданные:

{
  "icon-name": {
    "x": 0,
    "y": 0,
    "width": 32,
    "height": 32,
    "pixelRatio": 1
  }
}

Каждый ключ соответствует имени изображения, используемого в стиле через icon-image.


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

Sprite сам по себе не отображается на карте. Он используется через свойства слоёв, например:

{
  "id": "points",
  "type": "symbol",
  "source": "places",
  "layout": {
    "icon-image": "icon-name"
  }
}

Значение icon-image должно совпадать с ключом в sprite JSON.


Динамическое добавление изображений через API

Mapbox GL JS позволяет добавлять изображения в sprite во время выполнения с помощью метода addImage.

map.on('load', () => {
  map.loadImage('https://example.com/icon.png', (error, image) => {
    if (error) throw error;

    map.addImage('custom-icon', image);
  });
});

После добавления изображение становится доступным как часть sprite и может использоваться в стилях:

{
  "id": "markers",
  "type": "symbol",
  "source": "points",
  "layout": {
    "icon-image": "custom-icon"
  }
}

Особенности метода addImage

Метод addImage поддерживает дополнительные параметры, влияющие на отображение:

map.addImage('custom-icon', image, {
  pixelRatio: 2,
  sdf: true
});
  • pixelRatio — масштабирование для Retina-дисплеев
  • sdf — включение Signed Distance Field рендеринга, позволяющего менять цвет иконки через icon-color

SDF особенно полезен для однотонных иконок:

"paint": {
  "icon-color": "#ff0000"
}

Загрузка изображений и управление состоянием

Изображения нельзя использовать до полной загрузки стиля. Обычно используется событие:

map.on('load', () => {
  // безопасное добавление изображений
});

При работе с динамическими стилями важно учитывать, что при смене стиля (setStyle) sprite очищается, и изображения нужно добавлять заново через style.load.

map.on('style.load', () => {
  map.addImage('custom-icon', image);
});

Проверка наличия изображения

Перед добавлением можно проверить наличие изображения в sprite:

if (!map.hasImage('custom-icon')) {
  map.addImage('custom-icon', image);
}

Это предотвращает ошибки повторного добавления.


Удаление изображений из sprite

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

map.removeImage('custom-icon');

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


Обновление изображений

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

map.removeImage('custom-icon');
map.addImage('custom-icon', newImage);

Это позволяет динамически изменять иконки в зависимости от состояния данных.


Работа с внешними изображениями

Загрузка изображений выполняется через loadImage, который поддерживает CORS:

map.loadImage('https://example.com/icon.png', callback);

При использовании внешних доменов важно учитывать:

  • необходимость корректных CORS-заголовков
  • ограничения браузера на canvas-tainted images
  • возможные задержки загрузки

Sprite и производительность

Использование sprite значительно ускоряет рендеринг:

  • уменьшается количество HTTP-запросов
  • повышается кэшируемость ресурсов
  • ускоряется отрисовка символов

При большом количестве иконок предпочтительно использовать единый sprite atlas вместо множества отдельных файлов.


Формирование собственного sprite atlas

Sprite можно генерировать автоматически из набора изображений. Инструменты обычно объединяют:

  • PNG-иконки
  • JSON-метаданные с координатами

Результат должен соответствовать формату Mapbox:

{
  "icon-name": {
    "x": 64,
    "y": 32,
    "width": 32,
    "height": 32,
    "pixelRatio": 1
  }
}

Каждое изображение размещается в общем атласе без перекрытия.


Использование sprite в кастомных стилях

При создании собственного стиля через Style Specification sprite можно полностью заменить:

{
  "sprite": "https://cdn.example.com/my-sprite"
}

Это позволяет:

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

Ограничения и особенности рендеринга

  • максимальный размер одного sprite зависит от GPU и браузера
  • слишком крупные атласы могут снижать производительность
  • изображения автоматически кэшируются, но не обновляются без изменения URL
  • порядок загрузки влияет на момент появления иконок на карте

Динамические сценарии использования

Sprite в Mapbox GL JS часто используется в сценариях:

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

При этом ключевым становится баланс между количеством уникальных иконок и эффективностью их хранения в sprite.