Работа с динамическими данными в Mapbox GL JS строится вокруг управления слоями (layers), источниками данных (sources) и объектами интерфейса карты. Архитектура библиотеки предполагает строгую зависимость: сначала добавляется источник данных, затем на его основе создаются визуальные слои. Нарушение этого порядка приводит к ошибкам рендеринга и исключениям во время выполнения.
Источник (source) в Mapbox GL JS представляет собой абстракцию данных, которые будут визуализированы на карте. Чаще всего используется формат GeoJSON, хотя поддерживаются и тайловые источники.
Добавление источника выполняется через метод
addSource:
map.on('load', () => {
map.addSource('cities-source', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [69.2401, 53.2145]
},
properties: {
name: 'City A'
}
}
]
}
});
});
Ключевой момент заключается в том, что источник становится доступным
для использования только после события load. Попытка
добавления до загрузки стиля приводит к ошибкам, так как внутренняя
модель карты ещё не инициализирована.
Слой определяет способ отображения данных из источника: точки, линии, полигоны, символы, заливки и другие типы.
Простейший пример слоя типа circle:
map.addLayer({
id: 'cities-layer',
type: 'circle',
source: 'cities-source',
paint: {
'circle-radius': 6,
'circle-color': '#ff5200'
}
});
Слой напрямую связан с источником через поле source. Без
существующего источника добавление слоя невозможно.
Слои типа symbol используются для отображения текста и
иконок:
map.addLayer({
id: 'cities-labels',
type: 'symbol',
source: 'cities-source',
layout: {
'text-field': ['get', 'name'],
'text-size': 14
},
paint: {
'text-color': '#000'
}
});
Порядок добавления слоёв определяет их визуальное наложение. Позднее добавленные слои отображаются выше предыдущих.
Контроль позиции осуществляется через параметр
beforeId:
map.addLayer({
id: 'roads-layer',
type: 'line',
source: 'roads-source',
paint: {
'line-color': '#333',
'line-width': 2
}
}, 'cities-layer');
В данном случае слой дорог будет добавлен ниже слоя
cities-layer.
Удаление слоя выполняется методом removeLayer:
if (map.getLayer('cities-layer')) {
map.removeLayer('cities-layer');
}
Перед удалением важно проверить существование слоя, так как повторный вызов удаления приводит к ошибке.
Удаление слоя не затрагивает источник данных. Это разделение позволяет переиспользовать один источник в нескольких слоях.
Удаление источника выполняется через removeSource:
if (map.getSource('cities-source')) {
map.removeSource('cities-source');
}
Критическое правило архитектуры Mapbox GL JS: источник нельзя удалить, пока он используется хотя бы одним слоем. Поэтому порядок всегда обратный:
Нарушение приводит к исключениям в консоли и нестабильному состоянию карты.
Для динамических приложений ключевым механизмом является метод
setData, доступный у GeoJSON-источников.
const source = map.getSource('cities-source');
source.setData({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [69.2401, 53.2145]
},
properties: {
name: 'Updated City'
}
}
]
});
Обновление данных автоматически вызывает перерасчёт слоёв без необходимости их пересоздания.
Маркер создаётся независимо от слоя и источников:
const marker = new mapboxgl.Marker()
.setLngLat([69.2401, 53.2145])
.addTo(map);
Удаление маркера:
marker.remove();
Маркеры не связаны с системой слоёв и управляются отдельным DOM-подобным API.
Для масштабных наборов маркеров требуется хранение ссылок:
const markers = [];
for (const coord of coordinates) {
const m = new mapboxgl.Marker()
.setLngLat(coord)
.addTo(map);
markers.push(m);
}
Удаление всех маркеров:
markers.forEach(m => m.remove());
markers.length = 0;
Контролы добавляются через addControl:
const nav = new mapboxgl.NavigationControl();
map.addControl(nav, 'top-right');
Удаление контролов осуществляется методом
removeControl:
map.removeControl(nav);
Контролы являются отдельными компонентами UI и не зависят от источников и слоёв.
При динамической смене представлений карты требуется аккуратное управление всеми сущностями:
Типичный порядок очистки:
if (map.getLayer('cities-layer')) {
map.removeLayer('cities-layer');
}
if (map.getSource('cities-source')) {
map.removeSource('cities-source');
}
Дополнительно учитывается, что слои могут зависеть друг от друга
(например, beforeId), поэтому удаление выполняется в
обратном порядке добавления.
Удаление не всегда требуется; часто достаточно скрыть слой:
map.setLayoutProperty('cities-layer', 'visibility', 'none');
Возврат отображения:
map.setLayoutProperty('cities-layer', 'visibility', 'visible');
Такой подход снижает накладные расходы на повторное создание слоёв и источников.
Прямое изменение источника у слоя невозможно. Вместо этого используется пересоздание слоя:
map.removeLayer('cities-layer');
map.addLayer({
id: 'cities-layer',
type: 'circle',
source: 'new-source',
paint: {
'circle-radius': 5,
'circle-color': '#00aaff'
}
});
Этот механизм часто применяется при переключении тематических наборов данных.
Слой можно изменять «на лету» через setPaintProperty и
setLayoutProperty:
map.setPaintProperty('cities-layer', 'circle-color', '#00ff00');
map.setPaintProperty('cities-layer', 'circle-radius', 10);
Изменение применяется мгновенно, без пересоздания слоя и источника.
Частая ошибка возникает при попытке удалить источник, который используется:
Error: Source "cities-source" cannot be removed while layer "cities-layer" is using it.
Такая модель предотвращает нарушение целостности графа рендеринга. Поэтому структура зависимостей всегда должна анализироваться перед удалением.
Один источник может быть связан с несколькими слоями одновременно:
map.addLayer({
id: 'cities-points',
type: 'circle',
source: 'cities-source'
});
map.addLayer({
id: 'cities-labels',
type: 'symbol',
source: 'cities-source'
});
Удаление одного слоя не влияет на остальные, что позволяет строить сложные визуальные композиции без дублирования данных.
Распространённый паттерн — замена данных внутри одного источника:
map.getSource('cities-source').setData(newData);
Это предпочтительнее полного удаления и пересоздания слоёв, так как сохраняет состояние карты, анимации и порядок рендеринга.
При массовом обновлении слоёв используется временная блокировка перерисовки через логическую группировку операций:
map.once('idle', () => {
// операции добавления/удаления завершены
});
Такой подход позволяет избегать промежуточных состояний отображения при каскадных изменениях структуры карты.