Рисование полигонов

В MapLibre GL JS полигональные объекты почти всегда описываются через формат GeoJSON. Основная структура — объект типа Feature с геометрией Polygon или MultiPolygon.

Ключевые особенности структуры:

  • координаты задаются массивом колец
  • первое кольцо — внешний контур
  • последующие кольца — «дыры» (внутренние вырезы)
  • координаты задаются в порядке [долгота, широта]

Пример полигона с вырезом:

{
  "type": "Feature",
  "geometry": {
    "type": "Polygon",
    "coordinates": [
      [
        [37.62, 55.75],
        [37.65, 55.75],
        [37.65, 55.78],
        [37.62, 55.78],
        [37.62, 55.75]
      ],
      [
        [37.63, 55.76],
        [37.64, 55.76],
        [37.64, 55.77],
        [37.63, 55.77],
        [37.63, 55.76]
      ]
    ]
  },
  "properties": {
    "name": "test polygon"
  }
}

Добавление источника данных (source)

Полигональные данные подключаются через источник geojson. Это базовый способ работы с пользовательскими геометриями.

map.on('load', () => {
  map.addSource('areas', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: [polygonFeature]
    }
  });
});

Источник можно обновлять динамически, заменяя весь набор данных:

map.getSource('areas').setData(newGeojson);

Это ключевой механизм для интерактивных редакторов и динамических карт.


Отрисовка полигонов через слой fill

Основной тип слоя для отображения полигонов — fill.

map.addLayer({
  id: 'areas-fill',
  type: 'fill',
  source: 'areas',
  paint: {
    'fill-color': '#3b82f6',
    'fill-opacity': 0.5
  }
});

Основные свойства fill-слоя

  • fill-color — цвет заливки
  • fill-opacity — прозрачность
  • fill-outline-color — цвет обводки (ограниченно используется)
  • fill-pattern — текстура вместо цвета

Пример с условной стилизацией:

'fill-color': [
  'case',
  ['==', ['get', 'type'], 'park'],
  '#22c55e',
  '#3b82f6'
]

Контур полигона: отдельный слой line

Хотя fill-outline-color существует, на практике для точного контроля используется отдельный слой line.

map.addLayer({
  id: 'areas-outline',
  type: 'line',
  source: 'areas',
  paint: {
    'line-color': '#1f2937',
    'line-width': 2
  }
});

Причины разделения fill и line

  • независимая стилизация
  • возможность анимации обводки
  • контроль порядка наложения
  • поддержка сложных эффектов (например, dashed lines)

Многослойная структура и порядок рендеринга

MapLibre GL JS рендерит слои строго по порядку добавления. Это важно для полигонов, особенно при наложении нескольких типов визуализации:

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

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

map.addLayer(fillLayer);
map.addLayer(outlineLayer);
map.addLayer(labelLayer);

Работа с MultiPolygon и сложными формами

MultiPolygon используется для объединения нескольких независимых областей в одном объекте.

{
  "type": "MultiPolygon",
  "coordinates": [
    [
      [
        [0, 0],
        [1, 0],
        [1, 1],
        [0, 1],
        [0, 0]
      ]
    ],
    [
      [
        [2, 2],
        [3, 2],
        [3, 3],
        [2, 3],
        [2, 2]
      ]
    ]
  ]
}

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

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

Обновление данных и динамические полигоны

Одно из ключевых преимуществ GeoJSON-сорсов — возможность обновления без перерисовки всей карты.

function updatePolygon(newCoordinates) {
  const source = map.getSource('areas');

  const updated = {
    type: 'FeatureCollection',
    features: [{
      type: 'Feature',
      geometry: {
        type: 'Polygon',
        coordinates: newCoordinates
      }
    }]
  };

  source.setData(updated);
}

Типичные сценарии:

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

Интерактивность: события и выбор объектов

MapLibre GL JS позволяет работать с полигонами через события карты.

Клик по полигону

map.on('click', 'areas-fill', (e) => {
  const feature = e.features[0];
  console.log(feature.properties);
});

Наведение курсора

map.on('mouseenter', 'areas-fill', () => {
  map.getCanvas().style.cursor = 'pointer';
});

map.on('mouseleave', 'areas-fill', () => {
  map.getCanvas().style.cursor = '';
});

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

map.on('mousemove', (e) => {
  const features = map.queryRenderedFeatures(e.point, {
    layers: ['areas-fill']
  });
});

Фильтрация полигонов

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

map.setFilter('areas-fill', ['==', ['get', 'active'], true]);

Распространённые операции:

  • сравнение свойств
  • диапазоны значений
  • логические выражения all, any, none

Выражения для стилизации полигонов

MapLibre GL JS поддерживает выражения (expressions), позволяющие строить динамическую визуализацию.

Интерполяция цвета

'fill-color': [
  'interpolate',
  ['linear'],
  ['get', 'density'],
  0, '#dbeafe',
  100, '#1d4ed8'
]

Условная прозрачность

'fill-opacity': [
  'case',
  ['boolean', ['get', 'selected'], false],
  0.8,
  0.3
]

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

Полигональные данные могут быть тяжёлыми, особенно при высокой детализации.

Основные ограничения:

  • большое число вершин замедляет рендер
  • сложные MultiPolygon увеличивают нагрузку на GPU
  • частые setData вызывают перерасчёт слоя

Оптимизационные подходы:

  • упрощение геометрии (simplification)
  • разбиение на тайлы (vector tiles)
  • минимизация частоты обновлений
  • использование generateId: true для отслеживания объектов

Использование векторных тайлов вместо GeoJSON

При больших наборах данных предпочтительнее использовать vector source:

map.addSource('areas', {
  type: 'vector',
  url: 'mapbox://tileset.id'
});

Слои остаются теми же, но данные подгружаются по тайлам, а не целиком.


3D-полигоны через fill-extrusion

Плоские полигоны можно преобразовать в объёмные объекты.

map.addLayer({
  id: 'buildings-3d',
  type: 'fill-extrusion',
  source: 'areas',
  paint: {
    'fill-extrusion-color': '#888',
    'fill-extrusion-height': ['get', 'height'],
    'fill-extrusion-opacity': 0.7
  }
});

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

  • высота берётся из свойства объекта
  • поддерживается освещение сцены
  • используется WebGL extrusion

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

При работе с перекрывающимися областями важен контроль порядка и прозрачности:

  • прозрачные fill могут смешиваться визуально
  • outline помогает визуально разделять границы
  • fill-opacity влияет на читаемость нижних слоёв

Иногда применяется техника:

  • основной полигон — полупрозрачный fill
  • активный полигон — яркий fill + толстый outline
  • остальные — приглушённые цвета

Редактирование полигонов в интерактивных приложениях

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

Типовые операции:

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

Обновление происходит через:

source.setData(updatedGeojson);

При этом UI-слой обычно синхронизируется с массивом координат в памяти приложения.


Частые ошибки при работе с полигонами

  • незамкнутые кольца (первая и последняя точка должны совпадать)
  • перепутанный порядок координат (lat/lng вместо lng/lat)
  • отсутствие внешнего кольца
  • слишком детализированные контуры
  • некорректная структура MultiPolygon

Такие ошибки приводят к некорректному отображению или полному отсутствию слоя.


Стилизация через зависимости от масштаба

Полигоны часто требуют разного вида на разных zoom-уровнях:

'fill-opacity': [
  'interpolate',
  ['linear'],
  ['zoom'],
  5, 0.2,
  10, 0.6,
  15, 0.9
]

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

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