Шрифты и текстовые метки

Символьные слои в MapLibre GL JS основаны на концепции объединения иконок и текста в одном визуальном слое. Текстовые метки здесь не являются «просто подписями» — это полноценные стилизуемые объекты, тесно связанные с системой тайлов, шрифтами и векторным рендерингом. Их поведение определяется декларативным стилем слоя symbol, где каждая характеристика текста управляется выражениями и свойствами рендера.


Текст в MapLibre GL JS не рисуется браузером напрямую. Вместо этого используется пайплайн:

  1. Строка из свойства text-field
  2. Разбиение на символы
  3. Преобразование символов в glyphs (глифы)
  4. Загрузка SDF-глифов с сервера
  5. Кэширование и упаковка в texture atlas
  6. Рендеринг через WebGL

Ключевой компонент — сервер глифов, обычно доступный по шаблону:

/fonts/{fontstack}/{range}.pbf

Каждый диапазон (range) — это блок Unicode-символов. Такой подход минимизирует сетевой трафик и позволяет подгружать шрифты по мере необходимости, а не целиком.


text-field: источник текста

text-field определяет содержимое метки. Он поддерживает как простые строки, так и выражения.

Пример:

{
  type: "symbol",
  layout: {
    "text-field": "{name}"
  }
}

Использование выражений:

"text-field": [
  "concat",
  ["get", "name"],
  " - ",
  ["get", "type"]
]

Особенность системы заключается в том, что текст становится частью стиля данных, а не фиксированным UI-элементом.


text-font и fontstack

Параметр text-font задаёт список гарнитур:

"text-font": ["Noto Sans Regular", "Arial Unicode MS Regular"]

Это не локальные шрифты браузера. Каждая гарнитура соответствует заранее подготовленному набору glyphs на сервере.

Fontstack работает как fallback-механизм:

  • если glyph отсутствует в первом шрифте — берётся следующий
  • если отсутствует во всех — символ пропускается или заменяется tofu-глифом

glyph atlas и формат PBF

Глифы передаются в бинарном формате Mapbox Vector Tile (PBF). Они содержат:

  • контуры символов
  • метрики (advance, bearing)
  • SDF представление

SDF (Signed Distance Field) позволяет:

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

Рендеринг текста фактически происходит в виде растеризации signed-distance функций в fragment shader.


text-size и масштабирование

"text-size": 16

Размер текста задаётся в пикселях, но фактически интерпретируется в координатах мира с учётом zoom-level.

Возможна динамическая зависимость:

"text-size": [
  "interpolate",
  ["linear"],
  ["zoom"],
  5, 10,
  10, 16,
  15, 24
]

Это позволяет реализовывать:

  • мелкие подписи на низком зуме
  • крупные подписи на высоком
  • визуальную иерархию слоёв

text-anchor и позиционирование

text-anchor определяет точку привязки текста относительно геометрии:

  • center
  • top
  • bottom
  • left
  • right
  • комбинированные варианты (top-left, bottom-right)

Пример:

"text-anchor": "top"

Внутри рендеринга это влияет на baseline и alignment box, который вычисляется на основе метрик шрифта.


text-offset и смещение

"text-offset": [0, 0.5]

Оффсет задаётся в единицах em и позволяет:

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

Важно: смещение применяется после вычисления anchor, но до collision detection.


collision detection и размещение

MapLibre использует систему предотвращения наложений:

  • каждая метка получает bounding box
  • выполняется проверка пересечений
  • менее приоритетные метки скрываются

Ключевые параметры:

"text-allow-overlap": false
"text-ignore-placement": false
"text-padding": 2

text-padding расширяет bbox для создания «воздушного пространства» между элементами.


text-justify и выравнивание строк

"text-justify": "auto"

Поддерживаемые режимы:

  • auto
  • left
  • center
  • right

При multi-line тексте каждая строка выравнивается отдельно, а общий bounding box пересчитывается как объединение всех строк.


text-transform и локализация

"text-transform": "uppercase"

Возможные значения:

  • none
  • uppercase
  • lowercase

Важно: трансформация применяется после формирования glyph sequence, но до layout, что влияет на ширину строки и коллизии.

Для мультиязычных карт это критично, так как изменение регистра может влиять на CJK и кириллицу иначе, чем на латиницу.


CJK и сложные письменности

Поддержка китайского, японского и корейского требует:

  • расширенных fontstack
  • больших glyph ranges
  • fallback на специализированные шрифты

Особенности:

  • символы занимают фиксированную квадратную сетку
  • метрики ширины менее вариативны
  • высокая плотность информации на пиксель

Это делает важным корректный выбор text-font, иначе появляются пропуски или tofu-символы.


text-rotation-alignment и углы

"text-rotation-alignment": "map"

Опции:

  • map — текст вращается вместе с картой
  • viewport — фиксирован относительно экрана
  • auto — адаптивное поведение

Используется в сочетании с:

"text-rotate": 45

Для линий (например дорог) чаще применяется map, чтобы подписи следовали геометрии.


icon-text-fit и комбинированные метки

Когда текст используется вместе с иконкой:

"icon-text-fit": "both"

Возможные режимы:

  • none
  • width
  • height
  • both

Это позволяет:

  • растягивать фон под текст
  • создавать бейджи
  • объединять пиктограммы и подписи

text-variable-anchor и адаптивное размещение

"text-variable-anchor": ["top", "bottom", "left", "right"]

MapLibre может автоматически выбирать лучшее положение метки, минимизируя коллизии.

Алгоритм:

  1. генерируются кандидаты позиций
  2. оценивается пересечение bbox
  3. выбирается минимально конфликтная позиция

text-writing-mode и вертикальные тексты

"text-writing-mode": ["vertical"]

Используется для:

  • японских вертикальных надписей
  • дизайнерских карт
  • специализированных слоёв

Поддержка зависит от glyph availability и ориентации шрифта.


производительность текстового слоя

Основные узкие места:

  • загрузка glyph ranges
  • количество уникальных символов
  • размер font atlas
  • количество символов на экране

Оптимизации:

  • кэширование glyph tiles
  • ограничение max-text-field-length
  • использование symbol-placement: "line" только при необходимости
  • упрощение font stack

В WebGL рендерере текст превращается в батчи, минимизирующие draw calls.


динамические выражения и стилизация

Текстовые свойства поддерживают expressions:

"text-opacity": [
  "interpolate",
  ["linear"],
  ["zoom"],
  5, 0,
  10, 1
]

Также:

"text-halo-color": "#ffffff",
"text-halo-width": 2

Halo (обводка) реализуется через SDF и шейдерные смещения, а не через отдельные геометрии.


типичные архитектурные паттерны использования

Подписи объектов

  • города
  • POI
  • административные зоны

Линейные метки

  • дороги
  • реки
  • границы

Динамические данные

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

ограничения системы текста

  • зависимость от серверных glyph tiles
  • ограниченная типографическая гибкость
  • отсутствие полноценного text layout engine (как в браузере)
  • сложности с редкими Unicode символами
  • необходимость ручного контроля font stack

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

В symbol слое текст и иконка делят одно пространство:

  • один anchor
  • единый collision box
  • общий z-index внутри слоя

Это позволяет строить сложные комбинированные маркеры, но требует точной настройки padding и alignment, иначе возникает визуальная перегруженность или скрытие элементов.


особенности отрисовки в WebGL

Текст в MapLibre GL JS фактически является набором quads, где каждая буква:

  • имеет UV-координаты в atlas
  • рендерится через shader
  • использует distance field для антиалиасинга

Shader выполняет:

  • вычисление distance
  • сглаживание краёв
  • применение halo и opacity

взаимодействие с zoom и pitch

При изменении:

  • zoom — пересчитывается масштаб glyph placement
  • pitch — изменяется перспектива и возможна деформация текста
  • bearing — влияет на ориентацию в пространстве карты

Особенно важно при text-rotation-alignment: map, где текст становится частью геометрии сцены, а не интерфейса.