Спрайт (sprite) представляет собой единый графический атлас, содержащий набор небольших изображений, используемых картой в качестве иконок, маркеров, пиктограмм, обозначений объектов и других графических элементов. Вместо загрузки множества отдельных файлов браузер получает один общий набор изображений и данные о расположении каждого изображения внутри этого набора.
Использование спрайтов обеспечивает несколько важных преимуществ:
В MapLibre GL JS спрайты чаще всего используются слоями типа
symbol, где изображения отображаются через свойство
icon-image.
Спрайт состоит из двух файлов:
Пример структуры:
sprite.png
sprite.json
Содержимое JSON может выглядеть следующим образом:
{
"restaurant": {
"x": 0,
"y": 0,
"width": 32,
"height": 32,
"pixelRatio": 1
},
"hotel": {
"x": 32,
"y": 0,
"width": 32,
"height": 32,
"pixelRatio": 1
}
}
Каждый объект описывает отдельную иконку:
x, y — координаты внутри атласа;width, height — размеры изображения;pixelRatio — коэффициент плотности пикселей.Спрайт задаётся через свойство sprite внутри объекта
стиля.
Пример:
const map = new maplibregl.Map({
container: 'map',
style: {
version: 8,
sprite: 'https://example.com/sprites/sprite',
sources: {},
layers: []
}
});
Важно понимать, что расширение файла не указывается.
Если задан путь:
sprite: 'https://example.com/sprites/sprite'
MapLibre автоматически попытается загрузить:
https://example.com/sprites/sprite.json
https://example.com/sprites/sprite.png
Для экранов высокой плотности дополнительно могут использоваться файлы:
sprite@2x.json
sprite@2x.png
После подключения спрайта любая иконка может быть использована через имя, указанное в JSON.
Пример слоя:
map.addLayer({
id: 'restaurants',
type: 'symbol',
source: 'places',
layout: {
'icon-image': 'restaurant',
'icon-size': 1
}
});
Значение:
'restaurant'
должно соответствовать ключу в файле спрайта:
{
"restaurant": {
...
}
}
Если иконка отсутствует, объект на карте не будет отображён.
Свойство icon-image поддерживает выражения.
Это позволяет выбирать различные иконки в зависимости от данных объекта.
Пример GeoJSON:
{
"type": "Feature",
"properties": {
"category": "hotel"
},
"geometry": {
"type": "Point",
"coordinates": [30, 50]
}
}
Настройка слоя:
map.addLayer({
id: 'places',
type: 'symbol',
source: 'places',
layout: {
'icon-image': ['get', 'category']
}
});
В результате значение свойства:
"hotel"
автоматически приведёт к использованию иконки:
{
"hotel": { ... }
}
из спрайта.
Такой подход позволяет обслуживать тысячи объектов одним слоем.
Самый простой способ создания спрайта заключается в объединении нескольких PNG-файлов в единый атлас и формировании JSON-описания.
Допустим, имеются изображения:
restaurant.png
hotel.png
airport.png
hospital.png
После упаковки получается:
sprite.png
sprite.json
JSON:
{
"restaurant": {
"x": 0,
"y": 0,
"width": 32,
"height": 32,
"pixelRatio": 1
},
"hotel": {
"x": 32,
"y": 0,
"width": 32,
"height": 32,
"pixelRatio": 1
},
"airport": {
"x": 64,
"y": 0,
"width": 32,
"height": 32,
"pixelRatio": 1
},
"hospital": {
"x": 96,
"y": 0,
"width": 32,
"height": 32,
"pixelRatio": 1
}
}
После этого набор становится готовым для использования в MapLibre GL JS.
На практике спрайты редко создаются вручную. Обычно применяется утилита Spritezero.
Установка:
npm install -g @mapbox/spritezero-cli
Исходная структура:
icons/
├── restaurant.svg
├── hotel.svg
├── airport.svg
└── hospital.svg
Генерация:
spritezero sprite icons
Результат:
sprite.png
sprite.json
Для Retina-дисплеев:
spritezero --retina sprite icons
Будут созданы:
sprite@2x.png
sprite@2x.json
MapLibre сможет автоматически использовать соответствующий вариант.
SVG является наиболее распространённым форматом для подготовки спрайтов.
Преимущества SVG:
Пример SVG:
<svg width="32" height="32"
xmlns="http://www.w3.org/2000/svg">
<circle
cx="16"
cy="16"
r="12"
fill="#ff0000" />
</svg>
Набор SVG-файлов удобно использовать как исходный материал для автоматической генерации спрайта.
Современные устройства часто имеют коэффициент масштабирования 2x или выше.
Если использовать обычный спрайт:
sprite.png
иконки могут выглядеть размытыми.
Для решения создаются файлы:
sprite@2x.png
sprite@2x.json
Пример записи:
{
"restaurant": {
"x": 0,
"y": 0,
"width": 64,
"height": 64,
"pixelRatio": 2
}
}
MapLibre определяет плотность экрана автоматически и выбирает подходящий вариант.
Помимо классических спрайтов библиотека поддерживает динамическое добавление изображений.
Пример:
map.loadImage(
'/images/restaurant.png',
(error, image) => {
if (error) {
throw error;
}
map.addImage('restaurant', image);
map.addLayer({
id: 'places',
type: 'symbol',
source: 'places',
layout: {
'icon-image': 'restaurant'
}
});
}
);
В этом случае файл не входит в спрайт и загружается отдельно.
Подход подходит для:
MapLibre позволяет создавать изображения программно.
Создание холста:
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#0080ff';
ctx.beginPath();
ctx.arc(32, 32, 20, 0, Math.PI * 2);
ctx.fill();
Добавление изображения:
map.addImage(
'generated-marker',
{
width: 64,
height: 64,
data: ctx.getImageData(
0,
0,
64,
64
).data
}
);
Использование:
layout: {
'icon-image': 'generated-marker'
}
Такой механизм полезен для генерации графики в реальном времени.
Иногда данные содержат ссылки на иконки, которых ещё нет в карте.
MapLibre генерирует событие:
styleimagemissing
Пример обработки:
map.on(
'styleimagemissing',
(event) => {
const id = event.id;
const canvas =
document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx =
canvas.getContext('2d');
ctx.fillStyle = '#ff0000';
ctx.fillRect(
0,
0,
64,
64
);
const imageData =
ctx.getImageData(
0,
0,
64,
64
);
map.addImage(id, {
width: 64,
height: 64,
data: imageData.data
});
}
);
Теперь отсутствующая иконка будет автоматически создана в момент обращения к ней.
Для изменяемых изображений используется метод:
map.updateImage()
Пример:
map.updateImage(
'vehicle',
newImage
);
Это позволяет обновлять внешний вид объектов без пересоздания слоя.
Типичные сценарии:
MapLibre поддерживает изображения с пользовательской логикой обновления.
Пример объекта изображения:
const animatedImage = {
width: 64,
height: 64,
data: new Uint8Array(64 * 64 * 4),
onAdd() {
this.canvas =
document.createElement('canvas');
this.canvas.width = 64;
this.canvas.height = 64;
this.context =
this.canvas.getContext('2d');
},
render() {
const time = Date.now();
const radius =
10 + Math.sin(time / 300) * 5;
this.context.clearRect(
0,
0,
64,
64
);
this.context.beginPath();
this.context.arc(
32,
32,
radius,
0,
Math.PI * 2
);
this.context.fill();
this.data =
this.context.getImageData(
0,
0,
64,
64
).data;
return true;
}
};
Регистрация:
map.addImage(
'pulse',
animatedImage
);
После этого иконка будет непрерывно перерисовываться.
При работе с большими картографическими проектами количество иконок может исчисляться сотнями.
Распространённая структура:
icons/
├── transport/
├── tourism/
├── services/
├── emergency/
├── shopping/
└── administration/
Имена рекомендуется делать уникальными:
transport-bus
transport-train
transport-airport
tourism-hotel
tourism-museum
tourism-camping
Это исключает конфликты при расширении проекта.
При работе со спрайтами необходимо учитывать несколько факторов:
Для большинства проектов эффективной считается стратегия
использования единого базового спрайта и динамической загрузки редких
изображений через addImage().
Для статических картографических стилей
Для интерактивных приложений
addImage();styleimagemissing;Для высоконагруженных карт
icon-image вместо множества
отдельных слоёв.Грамотная организация спрайтов является одним из ключевых факторов производительности MapLibre GL JS, поскольку именно через спрайтовый механизм отображается большая часть пользовательских пиктограмм, маркеров, символов и тематических обозначений на карте.