Feature state

Механизм feature state представляет собой слой динамического состояния, привязанного к конкретным геометрическим объектам (features) на карте. Он позволяет изменять визуальное представление объектов без модификации исходных данных GeoJSON или векторных тайлов, обеспечивая высокую производительность и реактивность интерфейса.

Ключевая идея заключается в разделении:

  • статических данных (геометрия и свойства feature),
  • динамического состояния (hover, selected, active, loading и любые пользовательские флаги).

Архитектура feature state

Каждый объект идентифицируется парой:

  • source — источник данных слоя,
  • feature id — уникальный идентификатор внутри источника.

Идентификатор задаётся явно через promoteId (для GeoJSON) или через поле id в векторных тайлах.

Пример структуры идентификации:

{
  source: "cities",
  sourceLayer: "urban_areas",
  id: 12345
}

Установка состояния: setFeatureState

Основной метод управления состоянием:

map.setFeatureState(
  {
    source: "cities",
    id: 12345
  },
  {
    hover: true,
    selected: false
  }
);

Особенности работы

  • состояние хранится отдельно от данных источника;
  • обновление происходит без перерисовки всей карты;
  • изменения применяются только к стилю слоя, использующего feature-state.

Получение состояния: getFeatureState

Позволяет извлечь текущее состояние объекта:

const state = map.getFeatureState({
  source: "cities",
  id: 12345
});

console.log(state);

Результат может включать любые пользовательские ключи:

{
  hover: true,
  selected: false,
  loading: true
}

Удаление состояния: removeFeatureState

Сброс состояния выполняется точечно или полностью:

map.removeFeatureState({
  source: "cities",
  id: 12345
}, "hover");

Удаление всех состояний:

map.removeFeatureState({
  source: "cities",
  id: 12345
});

Использование feature-state в стилях

Главная ценность механизма проявляется в стилях слоёв через выражение ["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 работает по принципу первого совпадения.


Практические сценарии применения

Hover-эффекты

Типичный сценарий — подсветка объекта при наведении курсора:

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),
  • пересоздание слоя,
  • изменение GeoJSON свойств.

Причина в том, что состояние хранится в отдельной структуре данных и применяется на этапе рендеринга через WebGL-шейдеры.

Однако при большом количестве объектов следует учитывать:

  • рост памяти на хранение состояния,
  • необходимость чистки удалённых feature,
  • стоимость обновления стилей при массовых изменениях.

Ограничения механизма

Несмотря на гибкость, feature state имеет ряд ограничений:

1. Требуется стабильный ID

Без уникального id невозможно корректно привязать состояние.

2. Не сохраняется в источнике данных

После setData для GeoJSON состояния могут быть потеряны.

3. Ограниченная типизация

Состояние хранит только JSON-подобные значения (boolean, number, string).

4. Зависимость от слоя

Feature state работает только в рамках слоёв, которые поддерживают feature-state выражения.


Связь с фильтрацией и стилями

Feature state не заменяет фильтры слоёв. Эти механизмы решают разные задачи:

  • filter — исключает объекты из рендера,
  • feature-state — изменяет внешний вид без исключения.

Пример сочетания:

"filter": ["==", "type", "city"]

и одновременно:

["feature-state", "selected"]

Сценарии высокой нагрузки

При интерактивных картах с тысячами объектов рекомендуется:

  • использовать минимальное количество ключей состояния,
  • избегать частых глобальных обновлений,
  • очищать состояние при удалении объектов,
  • группировать изменения в батчи логики приложения.

Типовые ошибки при работе

Отсутствие id у feature

Без id состояние не будет привязано корректно, даже если вызов не вызывает ошибки.

Утечки состояния

При удалении источника без очистки feature state данные остаются в памяти движка.

Конфликт состояний

Перезапись состояния без предварительного чтения может приводить к потере ранее установленных флагов.


Поведение при перерисовке карты

При изменении масштаба или перемещении карты состояние сохраняется, так как оно не связано с геометрией напрямую. Это делает feature state особенно полезным для UI-логики, завязанной на интерактивность, а не на данные.


Использование в сложных интерфейсах

Feature state часто становится основой для:

  • интерактивных тепловых карт,
  • динамических кластеров,
  • аналитических дашбордов,
  • выделения маршрутов,
  • многоуровневых систем фильтрации объектов.

Взаимодействие с событиями карты

Типичный цикл работы строится вокруг событий:

  • mousemove — hover состояние,
  • mouseleave — сброс hover,
  • click — selection,
  • внешние API — загрузка/обновление состояния.

Сравнение с альтернативными подходами

Без feature state аналогичная логика обычно реализуется через:

  • пересборку GeoJSON,
  • динамическое изменение paint свойств,
  • пересоздание слоёв.

Такие подходы значительно менее эффективны, особенно при частых обновлениях, поскольку приводят к перерасчёту рендера всей сцены.


Поведение при множественных источниках

Один и тот же feature state может существовать независимо в разных источниках. Это важно учитывать при объединении данных из нескольких слоёв, где одинаковые ID могут пересекаться, но логически представляют разные сущности.