Механизм feature state представляет собой слой динамического состояния, привязанного к конкретным геометрическим объектам (features) на карте. Он позволяет изменять визуальное представление объектов без модификации исходных данных GeoJSON или векторных тайлов, обеспечивая высокую производительность и реактивность интерфейса.
Ключевая идея заключается в разделении:
Каждый объект идентифицируется парой:
source — источник данных слоя,feature id — уникальный идентификатор внутри
источника.Идентификатор задаётся явно через promoteId (для
GeoJSON) или через поле id в векторных тайлах.
Пример структуры идентификации:
{
source: "cities",
sourceLayer: "urban_areas",
id: 12345
}
Основной метод управления состоянием:
map.setFeatureState(
{
source: "cities",
id: 12345
},
{
hover: true,
selected: false
}
);
feature-state.Позволяет извлечь текущее состояние объекта:
const state = map.getFeatureState({
source: "cities",
id: 12345
});
console.log(state);
Результат может включать любые пользовательские ключи:
{
hover: true,
selected: false,
loading: true
}
Сброс состояния выполняется точечно или полностью:
map.removeFeatureState({
source: "cities",
id: 12345
}, "hover");
Удаление всех состояний:
map.removeFeatureState({
source: "cities",
id: 12345
});
Главная ценность механизма проявляется в стилях слоёв через выражение
["feature-state", ...].
"paint": {
"circle-radius": 6,
"circle-color": [
"case",
["boolean", ["feature-state", "hover"], false],
"#ff0000",
"#3388ff"
]
}
Feature state поддерживает множественные флаги одновременно, что позволяет строить сложную логику отображения.
Пример приоритетов:
"circle-color": [
"case",
["boolean", ["feature-state", "selected"], false],
"#00ff00",
["boolean", ["feature-state", "hover"], false],
"#ff9900",
"#3388ff"
]
Порядок проверок критически важен, так как выражение
case работает по принципу первого совпадения.
Типичный сценарий — подсветка объекта при наведении курсора:
map.on("mousemove", "cities-layer", (e) => {
if (e.features.length > 0) {
const id = e.features[0].id;
map.setFeatureState(
{ source: "cities", id },
{ hover: true }
);
}
});
map.setFeatureState(
{ source: "cities", id: selectedId },
{ selected: true }
);
При смене выбора предыдущее состояние необходимо очищать:
map.removeFeatureState(
{ source: "cities", id: previousId },
"selected"
);
Feature state часто используется для индикации загрузки:
map.setFeatureState(
{ source: "cities", id },
{ loading: true }
);
// после загрузки
map.setFeatureState(
{ source: "cities", id },
{ loading: false }
);
Feature state оптимизирован для частых обновлений и работает быстрее, чем:
setData),Причина в том, что состояние хранится в отдельной структуре данных и применяется на этапе рендеринга через WebGL-шейдеры.
Однако при большом количестве объектов следует учитывать:
Несмотря на гибкость, feature state имеет ряд ограничений:
Без уникального id невозможно корректно привязать
состояние.
После setData для GeoJSON состояния могут быть
потеряны.
Состояние хранит только JSON-подобные значения (boolean, number, string).
Feature state работает только в рамках слоёв, которые поддерживают
feature-state выражения.
Feature state не заменяет фильтры слоёв. Эти механизмы решают разные задачи:
Пример сочетания:
"filter": ["==", "type", "city"]
и одновременно:
["feature-state", "selected"]
При интерактивных картах с тысячами объектов рекомендуется:
Без id состояние не будет привязано корректно, даже если
вызов не вызывает ошибки.
При удалении источника без очистки feature state данные остаются в памяти движка.
Перезапись состояния без предварительного чтения может приводить к потере ранее установленных флагов.
При изменении масштаба или перемещении карты состояние сохраняется, так как оно не связано с геометрией напрямую. Это делает feature state особенно полезным для UI-логики, завязанной на интерактивность, а не на данные.
Feature state часто становится основой для:
Типичный цикл работы строится вокруг событий:
mousemove — hover состояние,mouseleave — сброс hover,click — selection,Без feature state аналогичная логика обычно реализуется через:
paint свойств,Такие подходы значительно менее эффективны, особенно при частых обновлениях, поскольку приводят к перерасчёту рендера всей сцены.
Один и тот же feature state может существовать независимо в разных источниках. Это важно учитывать при объединении данных из нескольких слоёв, где одинаковые ID могут пересекаться, но логически представляют разные сущности.