В контексте Mapbox GL JS состояние карты не является единым объектом, а распределено между несколькими слоями управления: визуальное состояние стиля, состояние источников данных, состояние представления камеры и динамическое состояние отдельных объектов. Такая архитектура позволяет разделять ответственность между рендерингом, данными и пользовательским взаимодействием.
Ключевые категории состояния:
Разделение этих уровней позволяет строить масштабируемые интерфейсы, где карта становится лишь одним из представлений общего состояния приложения.
Камера определяет, какую часть мира видит пользователь. В Mapbox GL JS это набор параметров, которые постоянно изменяются при взаимодействии.
Основные параметры:
center — координаты центра картыzoom — уровень приближенияbearing — угол поворотаpitch — наклон перспективыИзменение состояния камеры:
map.setCenter([30.5, 50.45]);
map.setZoom(10);
map.setBearing(45);
map.setPitch(30);
Комплексные изменения обычно выполняются через единый вызов:
map.jumpTo({
center: [30.5, 50.45],
zoom: 10,
bearing: 20,
pitch: 0
});
Анимационные переходы:
map.flyTo({
center: [30.5, 50.45],
zoom: 12,
speed: 1.2,
curve: 1.4
});
Состояние камеры часто синхронизируется с внешним состоянием приложения, URL или store.
Стиль определяет, как данные визуализируются. Он включает источники
(sources) и слои (layers), каждый из которых
имеет собственные свойства.
Изменение стиля:
map.setStyle('mapbox://styles/mapbox/streets-v11');
После смены стиля все динамически добавленные источники и слои удаляются, что делает управление стилем критическим элементом состояния.
Добавление слоя:
map.addLayer({
id: 'cities',
type: 'circle',
source: 'cities-data',
paint: {
'circle-radius': 6,
'circle-color': '#ff5200'
}
});
Изменение свойств слоя:
map.setPaintProperty('cities', 'circle-radius', 10);
map.setLayoutProperty('cities', 'visibility', 'none');
Состояние стиля часто используется как декларативный слой над данными: изменения происходят через API, а не через прямое манипулирование DOM.
Источники данных определяют, какие данные доступны для рендеринга. Основные типы источников:
Пример GeoJSON источника:
map.addSource('points', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: []
}
});
Обновление данных источника:
const source = map.getSource('points');
source.setData({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [30.5, 50.45]
},
properties: {}
}
]
});
Состояние источника напрямую влияет на перерисовку слоёв, использующих его.
Feature State — один из наиболее мощных механизмов управления состоянием в Mapbox GL JS. Он позволяет хранить временные данные, связанные с конкретными географическими объектами, без изменения исходного GeoJSON.
Назначение:
Установка состояния:
map.setFeatureState(
{
source: 'points',
id: 1
},
{
hover: true
}
);
Получение состояния:
map.getFeatureState({
source: 'points',
id: 1
});
Удаление состояния:
map.removeFeatureState({
source: 'points',
id: 1
});
Использование в стилях:
'circle-color': [
'case',
['boolean', ['feature-state', 'hover'], false],
'#ff0000',
'#3388ff'
]
Feature State отделяет визуальное состояние от данных, что снижает необходимость пересоздания источников при каждом изменении UI.
Mapbox GL JS предоставляет событийную модель, которая позволяет синхронизировать внутреннее состояние карты с внешними системами.
Основные события:
movezoomrotatepitchloadrenderПример синхронизации:
map.on('move', () => {
const center = map.getCenter();
const zoom = map.getZoom();
externalStore.update({
center,
zoom
});
});
Обратная синхронизация:
store.subscribe((state) => {
map.jumpTo({
center: state.center,
zoom: state.zoom
});
});
Такая модель часто используется в архитектуре Flux/Redux.
Часто состояние карты сохраняется в URL, что позволяет воспроизводить конкретный вид карты.
Пример синхронизации:
function updateUrl() {
const center = map.getCenter();
const zoom = map.getZoom();
const hash = `#${zoom}/${center.lat}/${center.lng}`;
history.replaceState(null, '', hash);
}
Загрузка состояния из URL:
const hash = window.location.hash.replace('#', '');
const [zoom, lat, lng] = hash.split('/');
map.setView({
center: [parseFloat(lng), parseFloat(lat)],
zoom: parseFloat(zoom)
});
URL становится внешним хранилищем состояния, что полезно для шаринга и восстановления сессий.
Несмотря на императивный API, управление состоянием в Mapbox GL JS часто строится по принципу частичных обновлений. Вместо полной перерисовки карты изменяются только конкретные части состояния.
Примеры точечных изменений:
setLayoutProperty вместо пересоздания слояsetFeatureState вместо изменения GeoJSONsetData вместо пересборки картыТакой подход снижает нагрузку на рендеринг и повышает предсказуемость поведения.
В приложениях с React или другими UI-фреймворками карта рассматривается как побочный эффект состояния.
Типичная схема:
Пример логики:
useEffect(() => {
map.setCenter(store.center);
map.setZoom(store.zoom);
}, [store.center, store.zoom]);
И обратный поток:
map.on('move', () => {
dispatch(updateView({
center: map.getCenter(),
zoom: map.getZoom()
}));
});
Ключевая задача — избежать бесконечных циклов синхронизации, разделяя инициаторов изменений.
При работе с несколькими источниками состояния возникают конфликты:
Типовые стратегии:
Пример debounce:
let timeout;
map.on('move', () => {
clearTimeout(timeout);
timeout = setTimeout(() => {
syncState();
}, 100);
});
Одним из ключевых требований к сложным картографическим приложениям является возможность восстановления состояния 1:1.
Для этого сохраняются:
Пример сериализации:
const state = {
center: map.getCenter(),
zoom: map.getZoom(),
bearing: map.getBearing(),
pitch: map.getPitch()
};
Такое состояние может быть сохранено в localStorage или серверном профиле пользователя.
При интенсивных изменениях состояния важна минимизация перерисовок.
Подходы:
jumpTo вместо отдельных set*)requestAnimationFramesetDataПример:
map.on('mousemove', (e) => {
requestAnimationFrame(() => {
map.setFeatureState(
{ source: 'points', id: hoveredId },
{ hover: true }
);
});
});
Состояние Mapbox GL JS можно представить как многослойную систему:
Каждый слой имеет свою частоту обновления и свою ответственность. Согласованность достигается через события и ограниченные точки синхронизации, а не через единый источник истины.