Управление коллизиями

В Mapbox GL JS управление коллизиями определяет поведение текстовых иконок и подписей при конкуренции за ограниченное экранное пространство. Коллизия возникает, когда два или более визуальных элемента (подписи, иконки, маркеры символов) претендуют на одну и ту же область карты.

Система рендеринга использует геометрическую модель занятости пространства, в которой каждый символ слоя представляется ограничивающим прямоугольником (collision box). Эти области проверяются на пересечения в процессе размещения на каждом кадре отрисовки карты.

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

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

Symbol layer и механизм размещения

Основной системой, отвечающей за коллизии, является symbol layer. Он объединяет текст и иконки в единый слой, где применяется общий алгоритм размещения.

Каждый объект слоя проходит несколько этапов:

  1. Генерация геометрии символа (иконка/текст)
  2. Расчёт bounding box в экранных координатах
  3. Проверка пересечений с уже размещёнными объектами
  4. Принятие решения о показе или скрытии

Ключевое поведение определяется свойствами layout:

map.addLayer({
  id: 'cities',
  type: 'symbol',
  source: 'places',
  layout: {
    'text-field': ['get', 'name'],
    'text-size': 14,
    'icon-image': 'marker',
  }
});

Текстовые иконки и их взаимодействие

Текст и иконки обрабатываются как независимые, но связанные сущности. Для каждого типа существует собственная система коллизий:

  • текстовые bounding box
  • иконки bounding box
  • комбинированные зоны (text + icon)

Поведение задаётся через свойства:

  • text-allow-overlap
  • icon-allow-overlap
  • text-ignore-placement
  • icon-ignore-placement

Если allow-overlap отключён, элемент может быть скрыт при пересечении с любым другим объектом.

Пример:

layout: {
  'text-field': ['get', 'name'],
  'text-allow-overlap': false,
  'icon-allow-overlap': false
}

Геометрия коллизий и padding

Каждый символ имеет расширенную область столкновения. Она включает:

  • визуальный размер текста или иконки
  • внутренние отступы (padding)
  • дополнительные зоны при повороте карты

Параметры управления:

  • text-padding
  • icon-padding
layout: {
  'text-field': ['get', 'name'],
  'text-padding': 2,
  'icon-padding': 1
}

Padding увеличивает вероятность блокировки соседних объектов, снижая плотность подписей.


Приоритеты размещения и порядок рендеринга

Система коллизий опирается на иерархию приоритетов. При равных условиях конфликт разрешается по следующим правилам:

  1. порядок слоёв в стиле (layers array)
  2. значение symbol-sort-key
  3. масштаб отображения (zoom)
  4. плотность геометрии

Пример задания приоритета:

layout: {
  'symbol-sort-key': ['get', 'priority']
}

Объекты с более высоким значением получают преимущество при размещении.


Влияние масштаба и угла наклона

Коллизии динамически пересчитываются при изменении:

  • zoom уровня
  • pitch (наклон карты)
  • bearing (поворот)

При увеличении масштаба:

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

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


Контроль поведения через optional rendering

Свойства text-optional и icon-optional позволяют системе не блокировать более важные элементы, даже если текущий объект не может быть размещён.

layout: {
  'text-field': ['get', 'name'],
  'text-optional': true
}

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


Слои плотности и стратегия прореживания

При высокой плотности данных используется автоматическое прореживание:

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

Часто применяется совместно с:

  • фильтрацией по zoom (minzoom, maxzoom)
  • кластеризацией источника данных
  • динамическими стилями

Тонкая настройка размещения текста

Для текстовых слоёв доступны дополнительные параметры, влияющие на коллизии:

  • text-keep-upright — сохраняет ориентацию текста при наклоне карты
  • text-anchor — точка привязки (center, top, bottom, left, right)
  • text-offset — смещение относительно геометрии
  • text-radial-offset — радиальное смещение вдоль направления линии

Пример:

layout: {
  'text-field': ['get', 'name'],
  'text-anchor': 'top',
  'text-offset': [0, 1],
  'text-keep-upright': true
}

Эти параметры напрямую влияют на форму collision box.


Взаимодействие иконок и текста в одном symbol layer

При совместном использовании текста и иконок формируется единый блок размещения. Возможны три стратегии:

  • объединённая коллизия (иконка + текст блокируют как единое целое)
  • независимая коллизия (каждый элемент проверяется отдельно)
  • приоритет текста над иконкой или наоборот

Поведение зависит от комбинации:

  • icon-allow-overlap
  • text-allow-overlap
  • icon-ignore-placement
  • text-ignore-placement

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

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

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

Также важную роль играет:

  • кеширование glyph atlas
  • переиспользование collision grid между тайлами
  • ограничение частоты пересчёта при анимации

Поведение при повторном рендере

Коллизии пересчитываются при изменениях:

  • стиля слоя
  • источника данных
  • камеры (move, zoom, rotate)
  • feature-state

При этом используется инкрементальная модель: пересчитываются только затронутые тайлы и символы, что снижает нагрузку на GPU и CPU.


Типовые конфигурации управления плотностью подписей

Высокая плотность (городские карты):

layout: {
  'text-field': ['get', 'name'],
  'text-size': 12,
  'text-allow-overlap': false,
  'text-padding': 2,
  'symbol-sort-key': ['get', 'importance']
}

Средняя плотность (региональные карты):

layout: {
  'text-field': ['get', 'name'],
  'text-size': 14,
  'text-optional': true,
  'text-padding': 1
}

Низкая плотность (обзорные карты):

layout: {
  'text-field': ['get', 'name'],
  'text-allow-overlap': true,
  'icon-allow-overlap': true
}