В приложениях, построенных на основе Deck.gl, карта редко существует изолированно. Её положение, масштаб, наклон и ориентация часто должны быть согласованы с другими компонентами интерфейса:
Синхронизация состояния карты представляет собой процесс централизованного управления параметрами отображения, чтобы различные части приложения реагировали на изменения согласованным образом.
Основным объектом состояния выступает view state — набор параметров камеры, определяющий текущий вид карты.
Типичный объект состояния выглядит следующим образом:
const viewState = {
longitude: 37.6176,
latitude: 55.7558,
zoom: 10,
pitch: 45,
bearing: 0
};
Каждое изменение положения карты приводит к обновлению этих значений.
Deck.gl поддерживает два подхода к управлению состоянием карты.
В этом случае библиотека самостоятельно изменяет положение камеры.
new Deck({
initialViewState: {
longitude: -122.4,
latitude: 37.74,
zoom: 11
},
controller: true,
layers: []
});
Свойство initialViewState используется только во время
инициализации.
После запуска приложения Deck.gl самостоятельно хранит текущее состояние.
Преимущества:
Недостатки:
В крупных приложениях обычно используется внешний источник состояния.
new Deck({
viewState,
controller: true,
layers: []
});
Теперь библиотека получает данные извне и отображает карту в соответствии с переданным состоянием.
Изменение координат должно выполняться программно.
Такой подход обеспечивает:
Для получения нового состояния используется обработчик
onViewStateChange.
new Deck({
viewState,
controller: true,
onViewStateChange: ({viewState}) => {
updateViewState(viewState);
}
});
Во время перемещения карты Deck.gl передаёт актуальные параметры камеры.
Структура аргумента:
{
viewState,
interactionState,
oldViewState
}
Где:
viewState — новое состояние;oldViewState — предыдущее состояние;interactionState — информация о действиях
пользователя.Пример:
onViewStateChange: ({viewState, interactionState}) => {
console.log(interactionState.isDragging);
console.log(interactionState.isZooming);
updateViewState(viewState);
}
Наиболее распространённый сценарий — хранение состояния в React.
import {useState} from "react";
import DeckGL from "@deck.gl/react";
function Map() {
const [viewState, setViewState] = useState({
longitude: -74,
latitude: 40.7,
zoom: 12
});
return (
<DeckGL
controller={true}
viewState={viewState}
onViewStateCha nge={({viewState}) =>
setViewState(viewState)
}
/>
);
}
Схема работы:
onViewStateChange.Такой цикл делает React единственным источником правды.
При наличии сложной архитектуры состояние карты часто хранится в Redux.
Создание действия:
export const setViewState = payload => ({
type: "SET_VIEW_STATE",
payload
});
Редьюсер:
const initialState = {
longitude: 0,
latitude: 0,
zoom: 2
};
export default function reducer(
state = initialState,
action
) {
switch (action.type) {
case "SET_VIEW_STATE":
return {
...state,
...action.payload
};
default:
return state;
}
}
Подключение Deck.gl:
<DeckGL
viewState={viewState}
controller={true}
onViewStateCha nge={({viewState}) =>
dispatch(setViewState(viewState))
}
/>
Преимущества централизованного хранения:
Часто требуется отображать несколько карт одновременно.
Например:
Все представления должны перемещаться синхронно.
const [viewState, setViewState] = useState({
longitude: -122.4,
latitude: 37.7,
zoom: 10
});
Первая карта:
<DeckGL
viewState={viewState}
controller={true}
onViewStateCha nge={({viewState}) =>
setViewState(viewState)
}
/>
Вторая карта:
<DeckGL
viewState={viewState}
controller={false}
/>
Теперь любое перемещение первой карты автоматически изменяет вторую.
Если обе карты должны реагировать на действия пользователя:
const sync = ({viewState}) => {
setViewState(viewState);
};
<DeckGL
viewState={viewState}
controller={true}
onViewStateCha nge={sync}
/>
<DeckGL
viewState={viewState}
controller={true}
onViewStateCha nge={sync}
/>
Обе карты используют единый источник состояния.
Иногда необходимо запретить выход за определённые координаты.
Для этого состояние модифицируется перед сохранением.
onViewStateChange: ({viewState}) => {
viewState.latitude = Math.min(
85,
Math.max(-85, viewState.latitude)
);
setViewState(viewState);
}
Можно ограничить:
Пример ограничения масштаба:
onViewStateChange: ({viewState}) => {
viewState.zoom = Math.min(
15,
Math.max(5, viewState.zoom)
);
setViewState(viewState);
}
Многие геоинформационные системы сохраняют положение карты в адресной строке.
Преимущества:
Обновление URL:
onViewStateChange: ({viewState}) => {
const params = new URLSearchParams({
lng: viewState.longitude,
lat: viewState.latitude,
zoom: viewState.zoom
});
history.replaceState(
null,
"",
`?${params}`
);
}
Восстановление состояния:
const params = new URLSearchParams(
window.location.search
);
const viewState = {
longitude: Number(params.get("lng")),
latitude: Number(params.get("lat")),
zoom: Number(params.get("zoom"))
};
Подобный подход широко применяется в аналитических и картографических системах.
Некоторые приложения сохраняют текущее положение карты на сервере.
Например:
Отправка данных:
onViewStateChange: ({viewState}) => {
saveViewState(viewState);
}
Запрос:
async function saveViewState(viewState) {
await fetch("/api/viewstate", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(viewState)
});
}
При следующем входе пользователя состояние может быть восстановлено автоматически.
Положение карты часто связано с другими визуальными компонентами.
<input
type="range"
min="1"
max="20"
value={viewState.zoom}
onCha nge={e =>
setViewState({
...viewState,
zoom: Number(e.target.value)
})
}
/>
Изменение ползунка приводит к изменению масштаба карты.
После выбора объекта карта может автоматически перемещаться к нему.
setViewState({
longitude: place.lng,
latitude: place.lat,
zoom: 15
});
При выборе строки:
function selectRow(item) {
setViewState({
longitude: item.longitude,
latitude: item.latitude,
zoom: 14
});
}
Таким образом достигается тесная связь между данными и картой.
Резкое изменение координат часто выглядит неестественно.
Deck.gl поддерживает плавные переходы.
import {
FlyToInterpolator
} from "@deck.gl/core";
Создание нового состояния:
setViewState({
longitude: -73.985,
latitude: 40.748,
zoom: 14,
transitionInterpolator:
new FlyToInterpolator(),
transitionDuration: 3000
});
В результате камера плавно перелетает к новой точке.
Подобные переходы особенно полезны при:
Одним из наиболее популярных сценариев является использование Deck.gl поверх Mapbox.
В этом случае обе библиотеки должны использовать одинаковое состояние камеры.
Пример общего состояния:
const [viewState, setViewState] = useState({
longitude: -122.4,
latitude: 37.8,
zoom: 11,
pitch: 45,
bearing: 0
});
Передача состояния в Mapbox:
<Map
{...viewState}
onM ove={event =>
setViewState(event.viewState)
}
/>
Передача состояния в Deck.gl:
<DeckGL
viewState={viewState}
controller={true}
onViewStateCha nge={({viewState}) =>
setViewState(viewState)
}
/>
Обе системы получают данные из одного источника и остаются полностью синхронизированными.
Во время перетаскивания карты события могут возникать десятки раз в секунду.
Неоптимизированная синхронизация приводит к:
Часто применяется ограничение частоты обновлений.
Пример с использованием requestAnimationFrame:
let frame = null;
function update(viewState) {
if (frame) return;
frame = requestAnimationFrame(() => {
setViewState(viewState);
frame = null;
});
}
Использование:
onViewStateChange: ({viewState}) => {
update(viewState);
}
Подобная техника особенно полезна при работе с большими наборами геоданных.
Объект interactionState позволяет определить текущее
действие пользователя.
Пример:
onViewStateChange: ({
viewState,
interactionState
}) => {
if (interactionState.isDragging) {
console.log("Перемещение карты");
}
if (interactionState.isZooming) {
console.log("Изменение масштаба");
}
setViewState(viewState);
}
Доступные признаки обычно включают:
interactionState.isDragging
interactionState.isZooming
interactionState.isRotating
interactionState.inTransition
Эти данные полезны для управления интерфейсом во время навигации.
Подходит для небольших приложений.
Deck.gl
↕
React State
Подходит для средних и крупных проектов.
Deck.gl
↕
Redux / Zustand
↕
Остальные компоненты
Подходит для аналитических панелей.
Deck.gl
↕
URL
↕
История браузера
Используется в многопользовательских системах.
Deck.gl
↕
Store
↕
Backend API
↕
База данных
Управляемый режим (viewState)
предпочтителен для большинства профессиональных приложений.
Единый источник состояния существенно упрощает синхронизацию карт, таблиц и панелей управления.
Сохранение состояния в URL повышает удобство навигации и обмена ссылками.
Ограничение частоты обновлений помогает сохранить высокую производительность интерфейса.
Синхронизация через глобальный Store облегчает поддержку сложных геоинформационных систем.
Плавные переходы камеры делают взаимодействие с картой более естественным и улучшают восприятие пространственных данных.