Стилизация draw элементов

Стилизация draw-объектов в MapLibre GL JS строится вокруг стандартного механизма слоёв стиля и геообъектов GeoJSON, которые динамически добавляются и обновляются библиотекой рисования (обычно через совместимый пакет MapLibre Draw, основанный на Mapbox GL Draw). Визуальное представление геометрий определяется не самим инструментом рисования, а набором слоёв, фильтров и выражений стиля, которые привязываются к источнику данных draw.

Основная идея заключается в том, что все создаваемые пользователем объекты попадают в единый GeoJSON source, после чего отображаются через несколько специализированных слоёв: линии, полигоны и точки. Каждый слой отвечает за свой тип геометрии и может иметь отдельную стилизацию для состояний объекта (обычный, выделенный, редактируемый, активный вершиной).


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

  • единый GeoJSON source (например, mapbox-gl-draw-cold)

  • набор слоёв:

    • слой заливки (fill) для полигонов
    • слой обводки (line) для контуров
    • слой точек (circle) для вершин и маркеров
    • дополнительные слои для активных состояний

Каждый слой использует фильтрацию по свойствам feature, которые инструмент добавляет автоматически:

  • active: активен ли объект
  • mode: режим редактирования
  • meta: тип служебной геометрии (vertex, midPoint и т.д.)
  • id: идентификатор объекта

Пример базовой структуры слоя:

map.addLayer({
  id: 'gl-draw-polygon-fill',
  type: 'fill',
  source: 'mapbox-gl-draw-cold',
  filter: ['all', ['==', '$type', 'Polygon']],
  paint: {
    'fill-color': '#3bb2d0',
    'fill-opacity': 0.4
  }
});

Разделение стилей по типам геометрий

Полигональные объекты

Полигональные draw-объекты обычно визуализируются через два слоя:

  • fill — заливка
  • line — контур

Стилизация fill слоя:

'fill-color': [
  'case',
  ['boolean', ['feature-state', 'active'], false],
  '#ff6b6b',
  '#3bb2d0'
],
'fill-opacity': 0.4

Контур:

'line-color': '#1f6feb',
'line-width': 2

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


Линейные объекты

Линии используют тип слоя line и часто стилизуются через ширину и цвет:

map.addLayer({
  id: 'gl-draw-line',
  type: 'line',
  source: 'mapbox-gl-draw-cold',
  filter: ['all', ['==', '$type', 'LineString']],
  paint: {
    'line-color': [
      'case',
      ['boolean', ['feature-state', 'active'], false],
      '#ff9800',
      '#4caf50'
    ],
    'line-width': 3
  }
});

Дополнительно можно использовать:

  • line-dasharray для пунктирных линий
  • выражения по zoom для изменения толщины

Точечные элементы и вершины

Точки используются для:

  • отображения вершин полигона
  • промежуточных точек
  • отдельных point-объектов
map.addLayer({
  id: 'gl-draw-point',
  type: 'circle',
  source: 'mapbox-gl-draw-cold',
  filter: ['all', ['==', '$type', 'Point']],
  paint: {
    'circle-radius': 5,
    'circle-color': [
      'case',
      ['boolean', ['feature-state', 'active'], false],
      '#e91e63',
      '#2196f3'
    ],
    'circle-stroke-width': 1,
    'circle-stroke-color': '#ffffff'
  }
});

Использование feature-state для интерактивной стилизации

Механизм feature-state является ключевым инструментом динамической стилизации draw-объектов. Он позволяет изменять внешний вид без изменения GeoJSON.

Основные состояния:

  • active — объект выбран
  • hover — наведён курсор
  • selectedVertex — активная вершина

Пример установки состояния:

map.setFeatureState(
  { source: 'mapbox-gl-draw-cold', id: featureId },
  { active: true }
);

Стили на основе состояния:

'fill-opacity': [
  'case',
  ['boolean', ['feature-state', 'active'], false],
  0.7,
  0.3
]

Разделение cold и hot источников

Draw-инструменты часто используют два источника:

  • ...-cold — стабильные объекты
  • ...-hot — редактируемые состояния

Cold слой отвечает за сохранённые геометрии, hot — за текущие изменения.

Пример фильтрации:

filter: ['all', ['!=', 'mode', 'static']]

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


Стилизация вершин и midpoints

Особое внимание уделяется вершинам полигонов:

  • вершины (vertex)
  • середины рёбер (midpoint)

Фильтрация:

filter: ['all', ['==', 'meta', 'vertex']]

Стилизация вершин:

'circle-radius': 4,
'circle-color': '#ffffff',
'circle-stroke-color': '#000000',
'circle-stroke-width': 2

Midpoints часто визуализируются более прозрачно:

'circle-opacity': 0.5

Кастомизация стиля через массив styles (Draw API)

При инициализации draw-инструмента можно полностью переопределить стили:

const draw = new MapboxDraw({
  styles: [
    {
      id: 'gl-draw-polygon-fill',
      type: 'fill',
      filter: ['all', ['==', '$type', 'Polygon']],
      paint: {
        'fill-color': '#2ecc71',
        'fill-opacity': 0.5
      }
    },
    {
      id: 'gl-draw-line',
      type: 'line',
      filter: ['all', ['==', '$type', 'LineString']],
      paint: {
        'line-color': '#27ae60',
        'line-width': 3
      }
    }
  ]
});

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


Стилизация по zoom-уровням

Zoom-dependent стили важны для масштабируемости отображения:

'circle-radius': [
  'interpolate',
  ['linear'],
  ['zoom'],
  5, 2,
  10, 4,
  15, 8
]

Контуры также могут усиливаться при увеличении масштаба:

'line-width': [
  'interpolate',
  ['linear'],
  ['zoom'],
  10, 1,
  16, 4
]

Сложные выражения для визуальных состояний

MapLibre GL JS поддерживает expression-based стили, позволяющие комбинировать условия:

'fill-color': [
  'case',
  ['all',
    ['boolean', ['feature-state', 'active'], false],
    ['==', ['get', 'type'], 'restricted']
  ],
  '#e74c3c',
  '#3498db'
]

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

  • различие типов объектов
  • комбинированные состояния
  • контекстную визуализацию

Работа с кастомными свойствами GeoJSON

Draw-объекты могут содержать пользовательские свойства:

draw.add({
  type: 'Feature',
  geometry: {
    type: 'Polygon',
    coordinates: [...]
  },
  properties: {
    category: 'zone',
    priority: 3
  }
});

Стилизация по свойствам:

'fill-color': [
  'match',
  ['get', 'category'],
  'zone', '#8e44ad',
  'restricted', '#c0392b',
  '#2980b9'
]

Приоритет слоёв и порядок отрисовки

Важно учитывать порядок слоёв:

  1. fill-слои (фон)
  2. line-слои (контуры)
  3. circle-слои (вершины)
  4. overlay-индикаторы (hover/active)

Неправильная последовательность приводит к перекрытию интерактивных элементов.


Оптимизация отрисовки draw-объектов

Для сложных сцен применяются:

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

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


Интерактивные состояния и визуальная обратная связь

Стилизация draw-элементов напрямую связана с UX взаимодействия:

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

Пример hover:

'line-color': [
  'case',
  ['boolean', ['feature-state', 'hover'], false],
  '#f1c40f',
  '#2c3e50'
]

Комбинирование draw-стилей с внешними слоями карты

Draw-слои могут визуально интегрироваться с основным стилем карты:

  • использование тех же цветовых токенов
  • соблюдение прозрачности для базового тайла
  • адаптация под light/dark темы

Пример адаптации:

'fill-color': [
  'interpolate',
  ['linear'],
  ['zoom'],
  0, '#5dade2',
  10, '#2e86c1',
  15, '#1b4f72'
]