Пространственные запросы

Работа с пространственными запросами в Mapbox GL JS опирается на взаимодействие с уже отрисованными векторными данными, геометрией на карте и внутренним пространственным индексом рендерера. Основная цель таких запросов — извлечение объектов карты по координатам, области экрана или географическому охвату без обращения к серверу.

Основные принципы пространственных запросов

Mapbox GL JS хранит отрисованные данные в виде тайлового векторного слоя, где каждый слой разбит на геометрические фрагменты. При рендере создаётся пространственный индекс, позволяющий выполнять быстрые запросы по экранным координатам.

Ключевые особенности:

  • запросы выполняются по уже загруженным и отрисованным данным;
  • используется экранная система координат (pixel space);
  • результаты зависят от текущего масштаба и наклона карты;
  • учитываются слои, видимость и порядок отрисовки.

Пространственные операции в библиотеке реализуются через методы объекта карты, прежде всего через queryRenderedFeatures и querySourceFeatures.


Запрос объектов в точке

queryRenderedFeatures

Метод позволяет получить объекты, находящиеся в конкретной точке или области экрана.

Сигнатура:

map.queryRenderedFeatures(point, options);

Параметры:

  • point — координаты в пикселях {x, y};
  • options — фильтрация по слоям и атрибутам.

Пример получения объектов под курсором:

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

Каждый элемент результата содержит:

  • geometry — геометрия объекта;
  • properties — атрибуты;
  • layer — слой, из которого получен объект;
  • source — источник данных.

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

При работе с большими наборами данных важно ограничивать область поиска:

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

Фильтрация снижает нагрузку и ускоряет выполнение запроса за счёт сокращения области поиска в рендер-дереве.


Пространственные запросы по области

Прямоугольная область (bounding box)

Запрос объектов внутри прямоугольника экрана используется для выделения областей, перетаскивания и анализа сцены.

const bbox = [
    [100, 100],
    [300, 300]
];

const features = map.queryRenderedFeatures(bbox);

Bounding box интерпретируется как экранный прямоугольник, что делает операцию независимой от географических координат.

Использование в интерактивных инструментах

Типичные сценарии:

  • выделение объектов рамкой;
  • массовое редактирование;
  • анализ плотности объектов;
  • построение пользовательских фильтров.

Запрос объектов из источника данных

querySourceFeatures

В отличие от queryRenderedFeatures, этот метод работает с исходными данными слоя до рендеринга.

const features = map.querySourceFeatures('my-source', {
    sourceLayer: 'buildings'
});

Особенности:

  • не зависит от текущего масштаба;
  • не учитывает видимость слоёв;
  • возвращает сырые данные из векторных тайлов.

Ограничения

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

Географические пространственные запросы через преобразование координат

Mapbox GL JS работает в экранной системе координат, поэтому для географических операций требуется преобразование:

const point = map.project([lng, lat]);

Обратное преобразование:

const lngLat = map.unproject([x, y]);

Эти методы используются для:

  • построения пользовательских геометрических запросов;
  • конвертации bounding box между системами координат;
  • реализации географических выделений.

Алгоритмы попадания (hit detection)

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

Для различных типов геометрий:

  • Point — точное совпадение или радиус пиксельной чувствительности;
  • LineString — проверка расстояния до линии;
  • Polygon — проверка принадлежности точки внутренней области.

Для линий применяется буферизация, зависящая от line-width и line-gap-width, а также текущего масштаба.


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

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

Принцип:

  • верхние слои карты имеют приоритет;
  • объекты из верхних слоёв идут первыми в массиве результата;
  • порядок соответствует стилю слоя (style layers order).

Это критично для задач выбора объектов, так как первый результат часто интерпретируется как «верхний» объект под курсором.


Фильтрация результатов

Mapbox GL JS поддерживает фильтрацию результатов через параметры запроса и постобработку.

Фильтр по свойствам

const features = map.queryRenderedFeatures(point, {
    filter: ['==', ['get', 'type'], 'park']
});

Используется expression syntax Mapbox Style Specification.

Постфильтрация

const features = map.queryRenderedFeatures(point)
    .filter(f => f.properties.population > 10000);

Постфильтрация применяется, когда логика слишком сложна для выражений стиля.


Пространственные запросы в событиях карты

События карты автоматически передают координаты, что позволяет сразу выполнять запросы.

mousemove

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

click

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

Типичный сценарий — определение объекта под курсором и изменение интерфейса.


Работа с множественными объектами в одной точке

В случае наложения нескольких объектов возвращается массив:

  • порядок зависит от слоёв;
  • каждый объект содержит полные атрибуты;
  • возможны дубликаты при сложной композиции тайлов.

Стратегии обработки:

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

Ограничения пространственных запросов

Ограничения рендера

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

Ограничения производительности

  • большие bounding box замедляют запрос;
  • частые вызовы queryRenderedFeatures в mousemove требуют оптимизации;
  • сложные фильтры увеличивают стоимость вычислений.

Оптимизация пространственных операций

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

map.queryRenderedFeatures(point, {
    layers: ['important-layer']
});

Дебаунс событий

let timeout;

map.on('mousemove', (e) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
        map.queryRenderedFeatures(e.point);
    }, 50);
});

Минимизация области запроса

Использование точек вместо больших bounding box снижает нагрузку на spatial index.


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

Mapbox GL JS позволяет комбинировать пространственные запросы с динамическим состоянием объектов:

map.setFeatureState(
    { source: 'places', id: 1 },
    { hover: true }
);

После этого пространственные запросы могут учитывать состояние через фильтры или стили.


Практика комбинирования запросов

Типичный сценарий сложной логики:

  1. получение объектов под курсором;
  2. фильтрация по слоям;
  3. анализ свойств;
  4. выбор приоритетного объекта.
const features = map.queryRenderedFeatures(e.point, {
    layers: ['roads', 'buildings']
});

const selected = features.find(f => f.properties.important === true);

Пространственные запросы и векторные тайлы

Векторные тайлы влияют на точность результатов:

  • геометрия может быть упрощена;
  • мелкие объекты могут отсутствовать;
  • границы объектов могут быть смещены из-за генерализации.

Это особенно важно при работе с полигонами и тонкими линиями.


Координатные системы в пространственных запросах

Mapbox GL JS использует:

  • долготу/широту (WGS84) для источников;
  • пиксельные координаты для запросов;
  • внутреннюю проекцию Mercator для рендера.

Переход между системами происходит автоматически при вызовах project и unproject, а также внутри queryRenderedFeatures.


Сложные сценарии пространственного анализа

Поиск ближайшего объекта

const features = map.queryRenderedFeatures(e.point, {
    layers: ['pois']
});

const nearest = features.sort((a, b) => {
    return distance(a, e.point) - distance(b, e.point);
})[0];

Группировка объектов

const grouped = features.reduce((acc, f) => {
    const type = f.properties.type;
    acc[type] = acc[type] || [];
    acc[type].push(f);
    return acc;
}, {});

Пространственные запросы и интерактивные визуализации

Использование запросов позволяет строить:

  • heatmap-интерактивность;
  • hover-эффекты;
  • выделение кластеров;
  • динамические подписи.

Каждый из этих сценариев основан на постоянном выполнении queryRenderedFeatures в зависимости от положения курсора или состояния камеры.