SDF иконки

В рендеринге карт Mapbox GL JS используется подход Signed Distance Field (SDF) для представления иконок как масштабируемых, легко перекрашиваемых объектов. Вместо хранения пиксельного изображения каждый пиксель хранит информацию о расстоянии до ближайшего края фигуры. Это позволяет динамически изменять визуальные свойства иконок без потери качества при масштабировании.

Ключевая особенность SDF-иконок заключается в том, что они перестают быть «картинками» в классическом смысле и становятся математически интерпретируемыми масками, на которые можно накладывать цвет, прозрачность и эффекты через стиль.


Принцип Signed Distance Field

Signed Distance Field — это функция, которая для каждой точки пространства возвращает расстояние до ближайшей границы формы:

  • отрицательные значения — внутри фигуры
  • ноль — на границе
  • положительные значения — снаружи

В растровом представлении это кодируется в градациях серого. Ближе к краю значение стремится к среднему уровню, а внутри и снаружи плавно изменяется.

Благодаря этому иконка становится не фиксированным изображением, а «шаблоном», который можно интерпретировать шейдером.


Причины использования SDF в Mapbox GL JS

Использование SDF в системе стилей карт даёт несколько фундаментальных преимуществ:

  • Динамическое изменение цвета без пересоздания изображения
  • Масштабирование без артефактов
  • Одинаковая визуальная чёткость на разных zoom-уровнях
  • Унифицированный рендеринг через GPU
  • Возможность выражения состояний через стиль (hover, active, selected)

Векторный рендеринг и GPU-шейдеры делают SDF естественным форматом для иконок в Mapbox GL JS.


Подключение SDF-иконки в стиль

Иконка может быть загружена в стиль через addImage с параметром sdf: true.

map.loadImage('marker.png', (error, image) => {
    if (error) throw error;

    map.addImage('custom-marker', image, {
        sdf: true
    });
});

После этого изображение перестаёт восприниматься как обычная текстура и становится SDF-слоем.


Использование иконок в слоях

SDF-иконки применяются через symbol layer.

map.addLayer({
    id: 'points',
    type: 'symbol',
    source: 'points-data',
    layout: {
        'icon-image': 'custom-marker',
        'icon-size': 1.2
    },
    paint: {
        'icon-color': '#ff0000',
        'icon-opacity': 0.9
    }
});

Важный момент: свойство icon-color начинает работать только при использовании SDF-иконки.


Управление цветом через SDF

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

Основные параметры:

  • icon-color — перекрашивает форму
  • icon-halo-color — добавляет обводку
  • icon-halo-width — ширина обводки
  • icon-opacity — прозрачность

Пример динамического изменения:

'icon-color': [
    'match',
    ['get', 'type'],
    'restaurant', '#e74c3c',
    'hotel', '#3498db',
    '#2ecc71'
]

Масштабирование и качество отображения

SDF-иконки сохраняют чёткость при любых значениях icon-size.

Особенность поведения:

  • при увеличении увеличивается не пиксельная структура, а интерпретируемая маска
  • края остаются гладкими благодаря интерполяции расстояний
  • отсутствуют «лесенки» при масштабировании

Это критично для карт, где zoom-уровни могут варьироваться от 0 до 22.


Взаимодействие с атласом и sprite

Mapbox использует sprite-атласы для оптимизации загрузки иконок.

SDF-иконки также могут быть частью sprite, однако с дополнительной мета-информацией о типе рендеринга.

При сборке спрайта:

  • каждая иконка конвертируется в grayscale SDF
  • добавляется padding для предотвращения bleeding
  • сохраняется единая текстура

Data-driven стилизация

Одно из ключевых преимуществ SDF — интеграция с выражениями стиля.

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

'icon-size': [
    'interpolate',
    ['linear'],
    ['zoom'],
    5, 0.5,
    10, 1,
    15, 1.8
]

Пример комбинирования цвета и данных:

'icon-color': [
    'case',
    ['==', ['get', 'priority'], 'high'],
    '#ff0000',
    ['==', ['get', 'priority'], 'medium'],
    '#ffa500',
    '#2ecc71'
]

Состояния интерфейса

SDF-иконки удобно использовать для UI-подобного поведения на карте.

Поддерживаемые состояния:

  • hover
  • selected
  • filtered
  • clustered / unclustered

Пример визуального выделения:

'icon-opacity': [
    'case',
    ['boolean', ['feature-state', 'selected'], false],
    1,
    0.5
]

Ограничения SDF-подхода

Несмотря на универсальность, SDF имеет особенности:

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

Также важно учитывать, что чрезмерно сложные формы увеличивают шум в distance field.


Подготовка изображений для SDF

Для корректного отображения иконок используются следующие принципы:

  • высокая контрастность формы
  • отсутствие мелких деталей
  • чистый фон
  • достаточный padding

Типичный pipeline:

  1. создание SVG или PNG с чёткой формой
  2. генерация distance field через инструмент сборки
  3. оптимизация и добавление в sprite
  4. регистрация через addImage

Производительность рендеринга

SDF-иконки обрабатываются на GPU, что даёт:

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

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


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

SDF-иконки часто комбинируются с текстом:

layout: {
    'icon-image': 'marker',
    'text-field': ['get', 'name'],
    'text-offset': [0, 1.2]
}

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


Практика сложной стилизации

SDF позволяет реализовать визуальные эффекты, которые обычно требуют шейдеров:

  • имитация свечения через halo
  • состояние активности через изменение насыщенности
  • визуальные приоритеты через размер и цвет

Пример «пульсации» через zoom:

'icon-opacity': [
    'interpolate',
    ['linear'],
    ['zoom'],
    10, 0.5,
    14, 1
]

Типичные ошибки при использовании

  • загрузка не-SDF изображения и попытка изменить icon-color
  • отсутствие padding в sprite
  • использование сложных изображений (фото)
  • неправильная интерпретация grayscale как RGB-иконки

Корректная работа требует строгого соответствия формату distance field и стилевым ограничениям рендерера.