Query rendered features

Базовое назначение механизма

Механизм запроса отрисованных объектов в MapLibre GL JS позволяет получать геометрии и свойства тех слоёв карты, которые уже прошли этап рендеринга и находятся в текущем видимом состоянии. Это ключевой инструмент для интерактивности: определение объектов под курсором, обработка кликов, построение всплывающих карточек, выделение элементов и реализация логики выбора.

В отличие от запросов к исходным данным источника (source), данный подход работает исключительно с тем, что уже попало в текущий кадр карты, включая учёт стилей, фильтров слоёв, видимости и масштаба.

Основная точка входа — метод:

map.queryRenderedFeatures(...)

Сигнатура метода и режимы использования

Метод поддерживает несколько вариантов вызова в зависимости от задачи:

map.queryRenderedFeatures(point?, options?)
map.queryRenderedFeatures(bbox?, options?)

Запрос по точке

Используется для получения объектов под конкретным пикселем:

const features = map.queryRenderedFeatures(
  [x, y],
  { layers: ['cities', 'roads'] }
);

Запрос по области

Используется для получения всех объектов в прямоугольной зоне:

const features = map.queryRenderedFeatures(
  [[x1, y1], [x2, y2]],
  { layers: ['buildings'] }
);

Геометрия запроса

Пиксельные координаты

Координаты всегда задаются в экранных пикселях относительно контейнера карты. Это важно: используются не географические координаты, а координаты viewport.

map.on('click', (e) => {
  const features = map.queryRenderedFeatures(e.point);
});

Значение e.point уже нормализовано и готово для использования.


Bounding box (bbox)

Прямоугольная область задаётся двумя точками:

  • левый верхний угол
  • правый нижний угол
const bbox = [
  [100, 100],
  [300, 300]
];

const features = map.queryRenderedFeatures(bbox);

Этот режим применяется для выделения множества объектов, например при drag-selection.


Параметры options

Фильтрация по слоям

Наиболее важный параметр — ограничение поиска слоями:

map.queryRenderedFeatures(point, {
  layers: ['water', 'landuse']
});

Если параметр layers не указан, будут возвращены объекты со всех видимых слоёв, что может сильно повлиять на производительность.


Фильтрация по условиям слоя

Можно дополнительно уточнять выборку через filter:

map.queryRenderedFeatures(point, {
  layers: ['cities'],
  filter: ['==', ['get', 'type'], 'capital']
});

Фильтр использует выражения MapLibre Style Specification и выполняется уже на уровне рендеринга.


Что именно возвращается

Метод возвращает массив объектов Feature, каждый из которых содержит:

  • geometry — геометрия (Point, LineString, Polygon)
  • properties — свойства из источника данных
  • layer — информация о стиле слоя
  • source — идентификатор источника данных
  • sourceLayer — имя слоя внутри vector tile (если применимо)
  • state — состояние feature-state (если используется)

Пример структуры:

{
  type: 'Feature',
  geometry: { type: 'Point', coordinates: [...] },
  properties: { name: 'City A' },
  layer: { id: 'cities-layer', type: 'circle' },
  source: 'places'
}

Приоритет слоёв и порядок результата

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

Это поведение критично для реализации:

  • выбора «верхнего» объекта при клике
  • обработки перекрывающихся слоёв
  • UI логики hover-состояний

Влияние видимости и стиля

Запрос учитывает:

  • visibility: none (слой исключается)
  • текущий zoom
  • условия minzoom / maxzoom
  • фильтры слоя
  • layout и paint свойства, влияющие на рендеринг

Таким образом, объект может существовать в источнике, но не попадать в результат, если он не отрисован.


Отличие от querySourceFeatures

Существует принципиальное различие между двумя методами:

queryRenderedFeatures

  • работает только с отрисованным кадром
  • учитывает стиль и zoom
  • используется для интерактивности

querySourceFeatures

  • работает с данными источника
  • не учитывает стиль
  • может возвращать объекты вне видимой области

Использование в обработке событий

Наиболее частый сценарий — события мыши:

map.on('mousemove', (e) => {
  const features = map.queryRenderedFeatures(e.point, {
    layers: ['roads']
  });

  map.getCanvas().style.cursor = features.length ? 'pointer' : '';
});

Применение:

  • hover-подсветка
  • изменение курсора
  • динамическое отображение tooltip

Работа с несколькими слоями

При наличии сложной композиции слоёв важно ограничивать запрос:

const features = map.queryRenderedFeatures(point, {
  layers: ['buildings-fill', 'buildings-outline']
});

Без ограничения возможны:

  • дублирование объектов
  • неожиданный порядок
  • снижение производительности

Производительность

Запрос отрисованных объектов может быть дорогим при:

  • большом количестве слоёв
  • сложных фильтрах
  • частых вызовах (mousemove)

Рекомендации:

  • всегда ограничивать layers
  • избегать вызова без необходимости
  • кэшировать результаты при drag-сценариях
  • использовать debounce/throttle для move событий

Область применения в интерактивных системах

Выбор объектов

map.on('click', (e) => {
  const feature = map.queryRenderedFeatures(e.point, {
    layers: ['pois']
  })[0];
});

Используется для получения одного «верхнего» объекта.


Drag selection

const features = map.queryRenderedFeatures(selectionBox, {
  layers: ['buildings']
});

Позволяет реализовать выделение множества объектов.


Hover-подсветка

map.on('mousemove', (e) => {
  const features = map.queryRenderedFeatures(e.point, {
    layers: ['roads']
  });

  if (features.length) {
    map.setFeatureState(features[0], { hover: true });
  }
});

Особенности работы с векторными тайлами

При использовании vector tiles следует учитывать:

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

Это влияет на стабильность идентификации объектов без id.


Ограничения метода

  • работает только с отрисованными данными
  • не возвращает скрытые или не загруженные объекты
  • не гарантирует уникальность feature без идентификаторов
  • зависит от текущего состояния карты (zoom, pitch, bearing)

Типичные ошибки при использовании

Отсутствие ограничения слоёв

Приводит к неожиданным результатам и падению производительности.

Использование без учёта zoom

Объекты могут исчезать из результата при смене масштаба.

Предположение о стабильности порядка

Порядок зависит от стиля, а не от источника данных.


Взаимодействие с feature-state

При использовании:

map.setFeatureState({ source, id }, { selected: true });

queryRenderedFeatures может возвращать объект, но состояние нужно проверять отдельно через feature-state, а не через properties.


Практическая модель использования

Комбинация событий и запроса:

  1. получение координаты события
  2. вызов queryRenderedFeatures
  3. фильтрация по слоям
  4. выбор приоритетного объекта
  5. применение UI-логики

Этот цикл является основой большинства интерактивных картографических интерфейсов в MapLibre GL JS.