Snap для привязки к объектам

Векторное редактирование в OpenLayers требует точного контроля координат при создании и модификации геометрий. Одной из ключевых механик, обеспечивающих аккуратность редактирования, выступает Snap — взаимодействие, позволяющее «прилипать» вершинам и сегментам к существующим объектам слоя.

Snap-интеракция работает поверх источника данных (source) и анализирует близость курсора к геометрическим элементам уже существующих features. При достижении заданного порога расстояния координата автоматически корректируется, обеспечивая топологическую согласованность данных.


Архитектура Snap-интеракции

Snap реализован как взаимодействие (interaction), которое подключается к карте и слушает события указателя. Основной принцип работы:

  • отслеживание движения курсора;
  • поиск ближайших вершин и сегментов в целевом источнике;
  • вычисление расстояния до геометрических элементов;
  • подмена координаты на «привязанную» точку при попадании в радиус захвата.

Snap не изменяет геометрию напрямую, а влияет на поток координат, поступающих от других интеракций (Draw, Modify).


Подключение Snap к карте

Snap используется как дополнительная интеракция, чаще всего совместно с Draw и Modify.

import Map from 'ol/Map.js';
import View from 'ol/View.js';

import VectorLayer from 'ol/layer/Vector.js';
import VectorSource from 'ol/source/Vector.js';

import Draw from 'ol/interaction/Draw.js';
import Snap from 'ol/interaction/Snap.js';

const source = new VectorSource();

const vectorLayer = new VectorLayer({
  source: source
});

const map = new Map({
  target: 'map',
  layers: [vectorLayer],
  view: new View({
    center: [0, 0],
    zoom: 2
  })
});

const draw = new Draw({
  source: source,
  type: 'LineString'
});

const snap = new Snap({
  source: source
});

map.addInteraction(draw);
map.addInteraction(snap);

В этом сценарии Snap использует тот же источник, что и Draw, что позволяет мгновенно привязываться к уже созданным объектам.


Принцип работы с источниками данных

Snap может работать с одним или несколькими источниками:

  • один VectorSource;
  • массив источников;
  • комбинация источников и слоёв.

При использовании нескольких источников Snap объединяет их геометрии в единое пространство поиска.

import Snap from 'ol/interaction/Snap.js';

const snap = new Snap({
  sources: [source1, source2]
});

Такой подход применяется в задачах, где объекты распределены по разным слоям: например, дороги, здания и административные границы.


Порог срабатывания (pixelTolerance)

Ключевой параметр Snap — pixelTolerance. Он определяет радиус захвата в пикселях экрана, внутри которого происходит привязка.

const snap = new Snap({
  source: source,
  pixelTolerance: 12
});

Поведение:

  • маленькие значения (1–5) — высокая точность, сложнее «поймать» вершину;
  • средние (8–15) — баланс между удобством и точностью;
  • большие (20+) — агрессивное прилипание, используется в картографической разметке.

Snap переводит пиксельную дистанцию в координатную систему карты с учётом текущего масштаба.


Snap с Draw-интеракцией

Наиболее распространённый сценарий — совместное использование Snap и Draw. При рисовании линий или полигонов координаты автоматически корректируются при приближении к существующим вершинам.

const draw = new Draw({
  source: source,
  type: 'Polygon'
});

const snap = new Snap({
  source: source,
  pixelTolerance: 10
});

map.addInteraction(draw);
map.addInteraction(snap);

При таком подключении:

  • вершины полигона «замыкаются» на существующие точки;
  • линии стыкуются без зазоров;
  • обеспечивается топологическая целостность.

Snap с Modify-интеракцией

Modify отвечает за редактирование уже существующих объектов. Snap здесь играет роль стабилизатора геометрии.

import Modify from 'ol/interaction/Modify.js';

const modify = new Modify({
  source: source
});

const snap = new Snap({
  source: source
});

map.addInteraction(modify);
map.addInteraction(snap);

В этом режиме:

  • перемещение вершин происходит с привязкой;
  • сегменты не «разрываются» при случайном смещении;
  • сохраняется связность линий.

Snap к вершинам и сегментам

Snap поддерживает два основных режима привязки:

  • привязка к вершинам;
  • привязка к сегментам (ребрам геометрий).

Внутренне алгоритм анализирует ближайшие элементы и вычисляет оптимальную точку на геометрии.

При работе с сегментами происходит проекция точки курсора на отрезок.


Фильтрация объектов через feature filter

Snap может ограничивать набор объектов, к которым происходит привязка. Это реализуется через функцию фильтрации features.

const snap = new Snap({
  source: source,
  filter: function (feature) {
    return feature.get('type') === 'road';
  }
});

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

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

Учет геометрических типов

Snap работает с различными типами геометрий:

  • Point — точечная привязка;
  • LineString — привязка к вершинам и сегментам;
  • Polygon — привязка к границам и узлам;
  • MultiGeometries — объединённая обработка.

При этом алгоритм унифицирует обработку через базовую модель координат.


Работа в режиме высокой плотности объектов

При большом количестве features производительность Snap становится критическим фактором. Основные оптимизации:

  • пространственные индексы в VectorSource;
  • ограничение pixelTolerance;
  • фильтрация по атрибутам;
  • использование разделённых источников.

Snap использует оптимизированный поиск ближайших геометрий, но сложные сцены всё равно требуют контроля нагрузки.


Snap и стилевые слои

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

  • стили не влияют на привязку;
  • скрытые слои могут участвовать в snapping;
  • прозрачные объекты остаются активными в логике привязки.

Приоритет интеракций

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

Обычно применяется следующая схема:

  • Draw или Modify;
  • Snap как вспомогательная интеракция.

Snap перехватывает координаты до их финализации основной интеракцией, корректируя положение точки.


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

В сложных проектах используется несколько VectorSource, объединённых в Snap:

const snap = new Snap({
  sources: [roadsSource, buildingsSource, bordersSource],
  pixelTolerance: 8
});

Поведение:

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

Геометрическая точность и масштаб

Snap тесно связан с текущим масштабом карты:

  • на малых масштабах захват охватывает большие области в координатах;
  • на крупных масштабах точность возрастает;
  • pixelTolerance остаётся фиксированным в экранных пикселях.

Это обеспечивает одинаковое поведение независимо от zoom-level.


Использование Snap в топологических системах

Snap применяется в системах, где важна топология:

  • кадастровые карты;
  • GIS-системы;
  • инженерные сети;
  • маршрутизация.

Основная задача — исключить:

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

Особенности поведения при пересечениях

При пересечении нескольких геометрий Snap выбирает ближайший элемент, основываясь на:

  • расстоянии до вершины;
  • расстоянии до сегмента;
  • порядке обработки источников.

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


Ограничения Snap-интеракции

Несмотря на универсальность, Snap имеет ограничения:

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

Snap работает исключительно как механизм корректировки координат в момент ввода.


Комбинирование Snap с пользовательскими взаимодействиями

Snap может использоваться совместно с кастомными интеракциями, перехватывающими pointermove и click события.

В таких сценариях:

  • пользовательская логика формирует координату;
  • Snap корректирует её перед записью;
  • результат передается в feature.

Это позволяет реализовывать сложные редакторы геометрий с контролем точности на уровне UX.