Режим snap

Режим привязки (snap) в экосистеме Mapbox GL JS используется для повышения точности редактирования геометрии на карте за счёт автоматического «прилипания» создаваемых вершин к существующим объектам. В контексте интерактивного редактирования он чаще всего реализуется через Mapbox GL Draw, расширяющий базовые возможности редактирования слоёв.

Назначение режима snap

Основная задача режима snap заключается в снижении погрешностей при ручном построении геометрии:

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

В системах, где карта используется как редактор (GIS-инструменты, CAD-подобные веб-приложения), отсутствие snap приводит к накоплению ошибок: пересекающиеся границы, разрывы контуров, неконсистентные полигоны.

Принцип работы

Snap-алгоритм в Mapbox GL JS обычно строится вокруг поиска ближайших геометрических примитивов в радиусе курсора.

При добавлении новой точки выполняются следующие шаги:

  1. Получение текущей позиции курсора в экранных координатах
  2. Поиск ближайших вершин существующих объектов
  3. Поиск ближайших сегментов линий (edge snapping)
  4. Вычисление расстояния до кандидатов
  5. Выбор ближайшего допустимого объекта
  6. Подмена координаты вводимой вершины на координату «прилипшей» точки

Ключевой параметр — радиус snap-зоны, который задаётся в пикселях экрана и определяет чувствительность привязки.

Типы привязки

В рамках Mapbox GL Draw и аналогичных реализаций выделяются два основных типа snap-логики:

Vertex snapping

  • привязка к существующим вершинам геометрии
  • используется при точечном редактировании
  • обеспечивает строгую топологическую согласованность

Edge snapping

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

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

Геометрическая модель

Внутренне Mapbox GL JS работает с GeoJSON-структурами. Для snap-режима это означает обработку следующих типов данных:

  • Point
  • LineString
  • Polygon
  • MultiPolygon

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

Для ускорения поиска используется пространственная индексация (обычно R-tree или его аналог), позволяющая сократить сложность поиска с линейной до логарифмической.

Алгоритм поиска ближайшей точки

Базовый алгоритм можно описать следующим образом:

  • преобразование координат из географических в экранные (projected space)
  • фильтрация кандидатов по bounding box текущего viewport
  • вычисление евклидова расстояния до вершин
  • вычисление расстояния до сегментов линий через проекцию точки на отрезок
  • выбор минимального значения

Для сегмента AB и точки P вычисляется параметр:

t = ((P - A) · (B - A)) / |B - A|²

Если t находится в диапазоне [0, 1], вычисляется ближайшая точка на отрезке.

Конфигурация snap-режима

В типичной интеграции через Mapbox GL Draw поведение snap настраивается через параметры режима редактирования:

  • radius — радиус привязки в пикселях
  • enableVertexSnap — включение привязки к вершинам
  • enableMidpointSnap — включение привязки к серединам сегментов
  • snapLayers — слои, участвующие в привязке

Пример логики конфигурации:

  • увеличение radius повышает «липкость», но снижает точность свободного рисования
  • включение midpoint snap улучшает геометрическую равномерность линий
  • ограничение snapLayers позволяет изолировать редактируемые объекты от вспомогательных данных

Взаимодействие с событиями Mapbox GL JS

Режим snap тесно связан с событиями карты:

  • mousemove — обновление позиции курсора
  • mousedown — фиксация точки с учётом привязки
  • touchmove — аналог для сенсорных устройств
  • draw.update — обновление геометрии после изменения вершины

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

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

При большом количестве геометрии snap-режим становится вычислительно затратным. Основные методы оптимизации:

Пространственная индексация

  • использование R-tree для хранения вершин
  • сокращение числа кандидатов

Кэширование viewport

  • пересчёт индекса только при изменении масштаба или перемещении карты

Декларативное ограничение слоёв

  • исключение неактивных слоёв из snap-обработки

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

  • ограничение частоты пересчётов snap-точек

Проблемы точности

Поскольку Mapbox GL JS использует Web Mercator проекцию, возникают особенности:

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

Поэтому snap-алгоритмы обычно выполняются в экранном пространстве, а не в географических координатах.

Кастомизация поведения

Snap-логика может быть расширена через пользовательские обработчики:

  • фильтрация допустимых snap-точек по атрибутам GeoJSON
  • приоритет отдельных слоёв
  • ограничение snap только на определённых стадиях рисования
  • динамическое изменение радиуса в зависимости от масштаба

В продвинутых сценариях snap используется не только для геометрии, но и для бизнес-логики: привязка к объектам инфраструктуры, маршрутам, зонам обслуживания.

Взаимодействие с режимами редактирования

В Mapbox GL Draw режим snap обычно комбинируется с другими режимами:

  • simple_select — выбор объектов с учётом привязки
  • draw_line_string — построение линий с автоматической фиксацией узлов
  • draw_polygon — замыкание контуров при совпадении с существующими вершинами

Переход между режимами требует сохранения состояния snap-контекста, чтобы не терять текущую вычисленную точку привязки.

Обработка конфликтов

При одновременном наличии нескольких подходящих snap-целей используется система приоритетов:

  1. ближайшая вершина
  2. ближайший сегмент
  3. midpoint
  4. вспомогательные слои

Если расстояния совпадают, применяется стабильный детерминированный выбор на основе порядка слоёв или индексов объектов.

Масштабирование поведения snap

Поведение snap часто зависит от zoom-level:

  • на малом масштабе snap отключается или ослабляется
  • на среднем масштабе активируется привязка к сегментам
  • на большом масштабе включается точечная привязка к вершинам

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