IconLayer — специализированный слой библиотеки Deck.gl,
предназначенный для отображения большого количества иконок на карте или
в произвольной геопространственной сцене. Слой активно применяется для
визуализации точек интереса (POI), транспортных средств, объектов
инфраструктуры, датчиков, пользователей, событий и любых других
сущностей, представленных координатами.
В отличие от обычного использования HTML-маркеров или компонентов
картографических библиотек, IconLayer выполняет рендеринг
средствами WebGL на стороне GPU, что позволяет отображать десятки и
сотни тысяч объектов с высокой производительностью.
Основные возможности слоя:
import {Deck} from '@deck.gl/core';
import {IconLayer} from '@deck.gl/layers';
Создание простейшего слоя:
const iconLayer = new IconLayer({
id: 'icons',
data: [
{
position: [37.6176, 55.7558]
}
],
getPosition: d => d.position,
getIcon: () => ({
url: '/images/marker.png',
width: 128,
height: 128,
anchorY: 128
}),
getSize: 40
});
Каждый объект из массива data преобразуется в отдельную
иконку.
Схематично процесс выглядит следующим образом:
getPosition,
getIcon, getSize и другие).Пример структуры данных:
const places = [
{
id: 1,
name: 'Airport',
coordinates: [37.61, 55.75]
},
{
id: 2,
name: 'Station',
coordinates: [37.59, 55.77]
}
];
Использование:
new IconLayer({
id: 'places',
data: places,
getPosition: d => d.coordinates
});
Для достижения максимальной производительности Deck.gl обычно использует один общий файл изображения, содержащий множество иконок.
Такой файл называется атласом.
Например:
+-----------------------+
| hotel | bus | train |
+-----------------------+
| car | taxi | plane |
+-----------------------+
Вместо загрузки множества отдельных файлов WebGL работает с одной текстурой.
Преимущества:
Указывает путь к изображению атласа.
new IconLayer({
iconAtlas: '/images/icons.png'
});
Описывает расположение каждой иконки внутри атласа.
Пример:
const iconMapping = {
airport: {
x: 0,
y: 0,
width: 128,
height: 128,
mask: true
},
train: {
x: 128,
y: 0,
width: 128,
height: 128,
mask: true
},
hotel: {
x: 256,
y: 0,
width: 128,
height: 128,
mask: true
}
};
Подключение:
new IconLayer({
iconAtlas: '/images/icons.png',
iconMapping
});
Наиболее распространённый способ выбора изображения — возврат имени
из iconMapping.
Данные:
const data = [
{
type: 'airport',
position: [37.61, 55.75]
},
{
type: 'hotel',
position: [37.62, 55.76]
}
];
Слой:
new IconLayer({
data,
iconAtlas,
iconMapping,
getIcon: d => d.type
});
В результате каждая запись автоматически использует свою область в атласе.
Начиная с современных версий Deck.gl можно возвращать описание изображения напрямую.
getIcon: d => ({
url: d.iconUrl,
width: 128,
height: 128,
anchorY: 128
})
Пример данных:
[
{
iconUrl: '/icons/car.png',
position: [37.6, 55.7]
},
{
iconUrl: '/icons/bus.png',
position: [37.7, 55.8]
}
]
Такой подход удобен при динамической загрузке данных с сервера.
Определяет координаты объекта.
getPosition: d => d.coordinates
Пример:
{
coordinates: [37.6176, 55.7558]
}
Для картографических приложений используется формат:
[longitude, latitude]
Важно соблюдать именно этот порядок.
Правильно:
[37.6176, 55.7558]
Неправильно:
[55.7558, 37.6176]
Задаёт размер отображения.
getSize: d => 40
Или:
getSize: d => d.importance * 10
Данные:
{
importance: 5
}
Результат:
50
Параметр применяется ко всем объектам слоя.
new IconLayer({
getSize: d => d.size,
sizeScale: 2
});
Фактический размер:
размер = getSize × sizeScale
Пример:
getSize: () => 20
sizeScale: 3
Результат:
60 пикселей
Минимальный размер.
sizeMinPixels: 16
Максимальный размер.
sizeMaxPixels: 100
Полный пример:
new IconLayer({
sizeScale: 5,
sizeMinPixels: 16,
sizeMaxPixels: 80
});
Даже при сильном изменении масштаба карты иконки останутся в указанных пределах.
Позволяет менять цвет программно.
getColor: [255, 0, 0]
Либо:
getColor: d => {
if (d.status === 'error') {
return [255, 0, 0];
}
return [0, 200, 0];
}
Данные:
[
{
status: 'ok'
},
{
status: 'error'
}
]
Используется RGBA.
[255, 0, 0]
Красный.
[0, 255, 0]
Зелёный.
[0, 0, 255]
Синий.
[255, 255, 255]
Белый.
[255, 255, 255, 128]
Полупрозрачный белый.
Отдельная настройка альфа-канала:
getColor: [0, 120, 255, 100]
Либо:
getColor: d => [
0,
120,
255,
d.opacity
]
Позволяет задавать угол вращения.
getAngle: d => d.heading
Данные:
{
heading: 90
}
Применяется для:
Пример:
new IconLayer({
getAngle: d => d.direction
});
Параметры задаются внутри описания иконки.
{
url: '/marker.png',
width: 128,
height: 128,
anchorX: 64,
anchorY: 128
}
Горизонтальная точка привязки.
Вертикальная точка привязки.
Для классического маркера:
anchorX: width / 2
anchorY: height
Точка карты совпадает с кончиком маркера.
Сам IconLayer не отображает текст.
Обычно используется совместно с TextLayer.
layers: [
iconLayer,
textLayer
]
Пример:
new TextLayer({
data,
getText: d => d.name,
getPosition: d => d.position
});
Включение взаимодействия.
new IconLayer({
pickable: true
});
После этого становятся доступны события.
new IconLayer({
pickable: true,
onClick: info => {
console.log(info.object);
}
});
Получение данных объекта:
onClick: ({object}) => {
if (object) {
console.log(object.name);
}
}
onHover: info => {
console.log(info.object);
}
Пример отображения всплывающей подсказки:
onHover: ({object, x, y}) => {
if (!object) {
return;
}
showTooltip({
x,
y,
text: object.name
});
}
Deck.gl способен самостоятельно подсвечивать объект под курсором.
new IconLayer({
pickable: true,
autoHighlight: true
});
highlightColor: [255, 255, 0]
Жёлтая подсветка.
Пример:
new IconLayer({
pickable: true,
autoHighlight: true,
highlightColor: [255, 255, 0, 120]
});
visible: false
Пример:
new IconLayer({
visible: isIconsVisible
});
При изменении массива данных слой автоматически пересоздаёт необходимые буферы.
const layer = new IconLayer({
data: vehicles
});
Обновление:
deck.setProps({
layers: [
new IconLayer({
id: 'vehicles',
data: updatedVehicles
})
]
});
Наиболее эффективный вариант:
iconAtlas
iconMapping
Вместо:
getIcon: d => ({
url: d.image
})
Особенно заметно при десятках тысяч объектов.
Плохо:
data.map(item => ({
...item
}))
при каждом рендере.
Лучше:
const data = useMemo(
() => vehicles,
[vehicles]
);
Позволяет обновлять только необходимые атрибуты.
new IconLayer({
getColor: d => colorMap[d.type],
updateTriggers: {
getColor: colorMap
}
});
При изменении цветов не пересчитываются остальные параметры.
Наиболее распространённые комбинации:
[
scatterLayer,
iconLayer
]
Круг показывает область интереса, иконка — конкретный объект.
[
iconLayer,
textLayer
]
Используется для подписей объектов.
[
pathLayer,
iconLayer
]
Маршрут отображается линией, а транспорт — иконкой.
[
geoJsonLayer,
iconLayer
]
Позволяет накладывать точки интереса поверх полигонов и административных границ.
Структура данных:
const vehicles = [
{
id: 1,
type: 'bus',
heading: 45,
position: [37.617, 55.755]
},
{
id: 2,
type: 'tram',
heading: 180,
position: [37.625, 55.760]
}
];
Создание слоя:
const vehicleLayer = new IconLayer({
id: 'vehicles',
data: vehicles,
pickable: true,
iconAtlas: '/images/transport.png',
iconMapping,
getPosition: d => d.position,
getIcon: d => d.type,
getSize: 32,
getAngle: d => d.heading,
autoHighlight: true,
onClick: ({object}) => {
if (!object) {
return;
}
console.log(object.id);
}
});
В результате формируется высокопроизводительный слой, способный отображать тысячи движущихся объектов с индивидуальными иконками, ориентацией, интерактивностью и поддержкой аппаратного ускорения через WebGL.