В экосистеме Mapbox GL JS выражения (Expressions) позволяют динамически изменять внешний вид карты на основе данных, масштаба, состояния объектов и множества других факторов. Среди наиболее важных инструментов стилизации особое место занимают выражения step и различные визуальные элементы, реализуемые в виде баннеров (banners), информационных панелей и подписей, отображаемых поверх карты.
Выражение step используется для дискретного изменения
значений при достижении определённых порогов. Такой подход особенно
полезен для тематического картографирования, когда необходимо разбить
данные на категории.
Баннеры представляют собой интерфейсный слой приложения и обычно реализуются средствами HTML, CSS и JavaScript поверх контейнера карты. Они отображают дополнительную информацию, легенды, уведомления, подсказки или результаты взаимодействия с объектами карты.
Выражение step позволяет возвращать различные значения в
зависимости от диапазона входного параметра.
Общий синтаксис:
[
"step",
input,
defaultValue,
stop1, value1,
stop2, value2,
stop3, value3
]
Где:
input — анализируемое значение;defaultValue — результат до достижения первого
порога;stop — контрольная точка;value — значение после достижения соответствующего
порога.Логика работы:
input < stop1 → defaultValue
input ≥ stop1 → value1
input ≥ stop2 → value2
input ≥ stop3 → value3
Часто требуется менять стиль в зависимости от уровня приближения.
Пример изменения размера текста:
map.addLayer({
id: 'cities',
type: 'symbol',
source: 'cities',
layout: {
'text-field': ['get', 'name'],
'text-size': [
'step',
['zoom'],
10,
6, 12,
10, 16,
14, 24
]
}
});
Результат:
| Zoom | Размер текста |
|---|---|
| < 6 | 10 |
| 6–9 | 12 |
| 10–13 | 16 |
| ≥ 14 | 24 |
Подобный подход позволяет избегать перегруженности карты на малых масштабах.
Наиболее распространённый сценарий — тематическое отображение данных.
Предположим, имеется набор городов с населением:
{
"name": "Karaganda",
"population": 497000
}
Изменение радиуса кругов:
'circle-radius': [
'step',
['get', 'population'],
5,
100000, 8,
500000, 12,
1000000, 18
]
Результат:
| Население | Радиус |
|---|---|
| < 100000 | 5 |
| 100000–499999 | 8 |
| 500000–999999 | 12 |
| ≥ 1000000 | 18 |
Цветовое кодирование является одним из основных методов тематической визуализации.
Пример окрашивания регионов по плотности населения:
'fill-color': [
'step',
['get', 'density'],
'#edf8fb',
50, '#b2e2e2',
100, '#66c2a4',
500, '#2ca25f',
1000, '#006d2c'
]
Получается пять классов данных.
Иногда необходимо скрывать малозначимые объекты.
'circle-opacity': [
'step',
['get', 'score'],
0.2,
50, 0.4,
75, 0.7,
90, 1
]
Объекты с высоким рейтингом становятся визуально более заметными.
Пример стилизации автомобильных дорог:
'line-width': [
'step',
['get', 'traffic'],
1,
1000, 2,
5000, 4,
10000, 8
]
Интенсивность движения напрямую влияет на толщину отображаемой линии.
Выражение часто используется совместно с zoom.
Пример изменения прозрачности:
'fill-opacity': [
'step',
['zoom'],
0.2,
8, 0.5,
12, 0.8
]
На удалённом масштабе слой выглядит менее навязчиво.
Mapbox GL JS предоставляет два похожих механизма:
[
'step',
['get', 'value'],
5,
100, 10,
500, 20
]
Дискретный переход:
5 → 10 → 20
[
'interpolate',
['linear'],
['get', 'value'],
0, 5,
100, 10,
500, 20
]
Плавный переход:
5 → 6 → 7 → 8 → ...
Использование:
| Задача | Метод |
|---|---|
| Классы населения | Step |
| Плавное изменение размеров | Interpolate |
| Категории данных | Step |
| Анимации | Interpolate |
Mapbox отвечает за отображение карты, а баннеры обычно создаются через DOM-элементы.
Простейший пример:
<div id="banner">
Система мониторинга транспорта
</div>
<div id="map"></div>
CSS:
#banner {
position: absolute;
top: 20px;
left: 20px;
padding: 12px 20px;
background: white;
border-radius: 6px;
z-index: 1000;
font-family: Arial;
}
Баннер располагается поверх карты благодаря абсолютному позиционированию.
Часто требуется отображать статистику.
HTML:
<div id="info-banner">
Загрузка данных...
</div>
Jav * aScript:
map.on('load', () => {
const count =
map.querySourceFeatures('cities').length;
document.getElementById('info-banner')
.innerText =
`Количество городов: ${count}`;
});
После загрузки карты баннер автоматически обновляется.
При выборе объекта можно показывать сведения о нём.
map.on('click', 'cities-layer', (e) => {
const feature = e.features[0];
document.getElementById('banner')
.innerHTML = `
<h3>${feature.properties.name}</h3>
<p>
Население:
${feature.properties.population}
</p>
`;
});
Такой подход часто используется в аналитических панелях.
Отображение текущего масштаба:
const banner =
document.getElementById('zoom-banner');
map.on('zoom', () => {
banner.textContent =
`Zoom: ${map.getZoom().toFixed(2)}`;
});
Информация обновляется в режиме реального времени.
При работе с удалёнными источниками полезно показывать индикатор загрузки.
HTML:
<div id="loading-banner">
Загрузка...
</div>
CSS:
#loading-banner {
display: none;
}
Jav * aScript:
map.on('dataloading', () => {
document
.getElementById('loading-banner')
.style.display = 'block';
});
map.on('idle', () => {
document
.getElementById('loading-banner')
.style.display = 'none';
});
Для отображения ошибок:
map.on('error', (e) => {
const banner =
document.getElementById('error-banner');
banner.style.display = 'block';
banner.textContent =
e.error.message;
});
Подобный механизм значительно упрощает диагностику проблем.
При использовании step легенда должна соответствовать
диапазонам данных.
Пример:
<div id="legend">
<div>
<span class="color low"></span>
Менее 50
</div>
<div>
<span class="color medium"></span>
50–100
</div>
<div>
<span class="color high"></span>
Более 100
</div>
</div>
Если стили слоя основаны на выражении step, легенда
должна использовать те же пороговые значения.
Пороговые значения удобно хранить отдельно.
const ranges = [
{
label: '< 50',
color: '#edf8fb'
},
{
label: '50 - 100',
color: '#b2e2e2'
},
{
label: '100 - 500',
color: '#66c2a4'
}
];
Создание элементов:
const legend =
document.getElementById('legend');
ranges.forEach(item => {
const div =
document.createElement('div');
div.innerHTML = `
<span
style="
background:${item.color};
width:15px;
height:15px;
display:inline-block;
">
</span>
${item.label}
`;
legend.appendChild(div);
});
На мобильных устройствах баннеры должны занимать минимум пространства.
Пример:
@media (max-width: 768px) {
#banner {
top: auto;
bottom: 10px;
left: 10px;
right: 10px;
width: auto;
}
}
Такой подход позволяет сохранить удобство взаимодействия с картой.
Наиболее распространённая архитектура включает:
step.Пример слоя:
map.addLayer({
id: 'population-layer',
type: 'fill',
source: 'regions',
paint: {
'fill-color': [
'step',
['get', 'population'],
'#fee5d9',
100000,
'#fcae91',
500000,
'#fb6a4a',
1000000,
'#cb181d'
]
}
});
Соответствующая легенда:
[
'< 100000',
'100000 - 500000',
'500000 - 1000000',
'> 1000000'
]
В результате карта получает наглядное тематическое оформление, а баннеры обеспечивают пользователя дополнительной контекстной информацией без перегрузки основного картографического слоя.