Выделение объектов

Модель взаимодействия выбора

Выделение объектов в OpenLayers строится вокруг концепции интерактивных слоёв и векторных объектов, где ключевым элементом выступает механизм определения попадания курсора в геометрию. Базовая единица — объект Feature, размещённый в VectorSource и визуализируемый через VectorLayer.

Процесс выбора опирается на две основные стратегии:

  • Пиксельное определение попадания (hit detection) — анализ координат курсора относительно рендера карты
  • Фильтрация объектов по условиям — логика отбора через функции filter, condition, layers

Interaction Sel ect как основной механизм

Центральным инструментом выделения является Select из модуля взаимодействий:

import Select fr om 'ol/interaction/Sel ect.js';
import {click} fr om 'ol/events/condition.js';

Конфигурация базового выделения:

const sel ect = new Select({
  condition: click
});

map.addInteraction(select);

Событие выбора формируется при попадании клика в геометрию Feature. Внутри взаимодействия поддерживается коллекция выбранных объектов, доступная через:

select.getFeatures()

Эта коллекция является реактивной и может использоваться для синхронизации состояния интерфейса.


Условия выбора (conditions)

Механизм condition определяет событие, которое инициирует выбор:

  • click — выбор по клику
  • pointerMove — выбор при наведении
  • singleClick — одиночный клик без dblclick
  • пользовательские функции

Пример выбора при наведении:

import {pointerMove} fr om 'ol/events/condition.js';

const sel ect = new Select({
  condition: pointerMove
});

Условия можно комбинировать через логические функции, формируя сложные сценарии взаимодействия.


Множественное выделение и управление состоянием

Для работы с несколькими объектами используются параметры:

const select = new Select({
  multi: true
});

Дополнительные механизмы:

  • toggleCondition — переключение состояния объекта
  • addCondition — добавление в выбор
  • removeCondition — удаление из выбора

Пример с клавишей Shift:

import {shiftKeyOnly} fr om 'ol/events/condition.js';

const sel ect = new Select({
  condition: click,
  toggleCondition: shiftKeyOnly
});

Фильтрация объектов при выборе

Выбор может быть ограничен логикой фильтрации через filter:

const select = new Select({
  filter: function(feature, layer) {
    return feature.get('selectable') === true;
  }
});

Фильтрация используется для:

  • исключения служебных объектов
  • ограничения выбора по типу геометрии
  • разграничения слоёв

Ограничение выбора по слоям

Параметр layers позволяет задать список допустимых слоёв:

const select = new Select({
  layers: function(layer) {
    return layer.get('interactive') === true;
  }
});

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


Стилизация выбранных объектов

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

const select = new Select({
  style: function(feature) {
    return new Style({
      stroke: new Stroke({
        color: 'red',
        width: 2
      }),
      fill: new Fill({
        color: 'rgba(255,0,0,0.2)'
      })
    });
  }
});

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


Управление состоянием выделения

Выбранные объекты хранятся в коллекции:

const features = select.getFeatures();

Основные операции:

  • добавление: features.push(feature)
  • удаление: features.remove(feature)
  • очистка: features.clear()

События коллекции позволяют отслеживать изменения:

features.on('add', function(e) {});
features.on('remove', function(e) {});

Программное выделение объектов

Выделение может выполняться без пользовательского взаимодействия:

const feature = vectorSource.getFeatureById(1);
select.getFeatures().push(feature);

Очистка выделения:

select.getFeatures().clear();

Определение объекта по координате

Альтернативный способ — прямой hit-test через карту:

map.forEachFeatureAtPixel(pixel, function(feature, layer) {
  console.log(feature);
});

Либо более точный вариант:

map.getFeaturesAtPixel(pixel);

Этот подход используется для кастомных механизмов выбора и сложных UI-сценариев.


Точность попадания (hitTolerance)

Параметр hitTolerance расширяет область клика:

const select = new Select({
  hitTolerance: 10
});

Применяется при:

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

Выделение через DragBox

Дополнительный инструмент выделения — прямоугольная рамка:

import DragBox fr om 'ol/interaction/DragBox.js';

Пример логики:

const dragBox = new DragBox();

dragBox.on('boxend', function() {
  const extent = dragBox.getGeometry().getExtent();

  vectorSource.forEachFeatureIntersectingExtent(extent, function(feature) {
    select.getFeatures().push(feature);
  });
});

Этот механизм применяется для массового выбора.


Интеграция с кластерами

При использовании кластеризации (Cluster source) выбор требует работы с внутренними элементами:

feature.get('features')

Кластер представляет собой агрегированный объект, внутри которого находится массив исходных Feature.

Логика выбора:

  • клик по кластеру
  • раскрытие списка объектов
  • выбор отдельных элементов

События выбора

Select взаимодействие генерирует события:

  • select — изменение состояния выбора
  • change — модификация коллекции

Пример:

select.on('select', function(e) {
  console.log(e.selected);
  console.log(e.deselected);
});

Структура события включает:

  • выбранные объекты
  • снятые с выбора объекты
  • состояние предыдущего выделения

Хранение и восстановление выделения

Состояние выбора может сериализоваться через идентификаторы:

const ids = select.getFeatures().getArray().map(f => f.getId());

Восстановление:

ids.forEach(id => {
  const feature = source.getFeatureById(id);
  select.getFeatures().push(feature);
});

Производительность при большом количестве объектов

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

  • использование SpatialIndex внутри источника
  • ограничение forEachFeatureAtPixel
  • минимизация кастомных стилей
  • применение кластеризации

Оптимизация hit-test часто важнее оптимизации рендера.


Кастомная логика выбора

Сложные сценарии реализуются через комбинирование:

  • Select
  • DragBox
  • forEachFeatureAtPixel
  • пользовательские состояния приложения

Пример гибридного подхода:

const select = new Select({
  condition: click,
  filter: feature => feature.getGeometry().getType() === 'Point'
});

Отмена и переключение выбора

Контроль состояния осуществляется вручную:

if (select.getFeatures().getLength() > 0) {
  select.getFeatures().clear();
}

Либо через toggle-логику:

if (features.getArray().includes(feature)) {
  features.remove(feature);
} else {
  features.push(feature);
}