Рисование линий

Векторное отображение линий в MapLibre GL JS основано на концепции источников данных (sources) и слоёв (layers), где линия представляется типом геометрии LineString в формате GeoJSON. Вся логика рендеринга строится вокруг декларативного описания: данные отделены от визуального представления, а стиль управляется через набор свойств слоя.

Линии применяются для отображения маршрутов, границ, треков движения, инженерных сетей и любых линейных объектов, где важна непрерывность геометрии.


GeoJSON как основа линейной геометрии

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

{
  type: "Feature",
  geometry: {
    type: "LineString",
    coordinates: [
      [71.4304, 51.1282],
      [71.4501, 51.1320],
      [71.4700, 51.1405]
    ]
  },
  properties: {
    name: "Маршрут 1"
  }
}

Ключевое правило: координаты задаются в формате [долгота, широта], что критично для корректного позиционирования на карте.


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

Перед отображением линии необходимо зарегистрировать источник:

map.addSource("route-source", {
  type: "geojson",
  data: {
    type: "Feature",
    geometry: {
      type: "LineString",
      coordinates: [
        [71.4304, 51.1282],
        [71.4501, 51.1320]
      ]
    }
  }
});

Источник может быть как статическим, так и динамически обновляемым через setData, что позволяет изменять геометрию без пересоздания слоя.


Создание линейного слоя

Отрисовка линии осуществляется через слой типа line:

map.addLayer({
  id: "route-layer",
  type: "line",
  source: "route-source",
  layout: {},
  paint: {
    "line-color": "#2E86DE",
    "line-width": 4
  }
});

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


Ключевые свойства визуализации линий

Толщина линии

"line-width": 6

Толщина может быть фиксированной или зависеть от масштаба карты:

"line-width": [
  "interpolate",
  ["linear"],
  ["zoom"],
  5, 2,
  15, 8
]

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


Цвет линии

"line-color": "#FF5733"

Допускается использование выражений:

"line-color": [
  "match",
  ["get", "type"],
  "highway", "#e74c3c",
  "street", "#3498db",
  "#95a5a6"
]

Это пример data-driven styling, где внешний вид зависит от атрибутов данных.


Тип соединений сегментов

"line-join": "round"

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

  • round — скруглённые углы
  • bevel — срезанные углы
  • miter — острые соединения

Скругления особенно важны при визуализации маршрутов и дорог.


Тип окончания линии

"line-cap": "round"

Варианты:

  • butt — обрезанный конец
  • round — закругление
  • square — квадратное завершение

Пунктирные и штриховые линии

Штриховые линии задаются через line-dasharray:

"line-dasharray": [2, 4]

Где:

  • первое значение — длина штриха
  • второе — длина пробела

Более сложные паттерны:

"line-dasharray": [1, 2, 3, 2]

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


Множественные линии в одном источнике

Один источник может содержать несколько линий через MultiLineString:

{
  type: "Feature",
  geometry: {
    type: "MultiLineString",
    coordinates: [
      [
        [71.1, 51.1],
        [71.2, 51.2]
      ],
      [
        [71.3, 51.3],
        [71.4, 51.4]
      ]
    ]
  }
}

Это удобно для сложных сетевых структур, например транспортных маршрутов.


Динамическое обновление линии

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

const source = map.getSource("route-source");

source.setData({
  type: "Feature",
  geometry: {
    type: "LineString",
    coordinates: [
      [71.4304, 51.1282],
      [71.4800, 51.1500]
    ]
  }
});

Это основной механизм анимации маршрутов и трекинга объектов.


Анимация линии через dashoffset

Эффект движения линии достигается изменением line-dasharray и line-dashoffset:

let offset = 0;

function animate() {
  offset = (offset + 1) % 100;

  map.setPaintProperty("route-layer", "line-dasharray", [2, 4]);
  map.setPaintProperty("route-layer", "line-dashoffset", offset);

  requestAnimationFrame(animate);
}

animate();

Такой приём используется для имитации движения вдоль маршрута.


Градиенты вдоль линии

Цвет линии может изменяться по длине с помощью интерполяции:

"line-gradient": [
  "interpolate",
  ["linear"],
  ["line-progress"],
  0, "#2ecc71",
  1, "#e74c3c"
]

Для корректной работы требуется включение:

"lineMetrics": true

в источнике данных.


Обработка событий на линии

Линии поддерживают стандартные события карты:

map.on("click", "route-layer", (e) => {
  console.log(e.features[0]);
});

Можно реализовать:

  • выбор маршрута
  • подсветку сегмента
  • отображение информации

Фильтрация линий

Если слой содержит несколько объектов, применяется фильтр:

filter: ["==", ["get", "type"], "primary"]

Также возможны логические операции:

filter: [
  "all",
  ["==", ["get", "active"], true],
  [">", ["get", "length"], 1000]
]

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

При большом количестве линий важно учитывать:

  • использование simplify в данных GeoJSON
  • минимизацию количества вершин
  • объединение линий в MultiLineString
  • ограничение перерисовок setData

Векторный рендеринг в MapLibre оптимизирован под GPU, но избыточная геометрия приводит к падению FPS.


Контекстная стилизация и масштабирование

Линии могут менять внешний вид в зависимости от масштаба:

"line-opacity": [
  "interpolate",
  ["linear"],
  ["zoom"],
  10, 0.2,
  14, 1
]

Это позволяет скрывать второстепенные детали на малых масштабах и усиливать визуализацию при приближении.


Комбинирование нескольких слоёв линий

Часто используется стек слоёв:

  • нижний слой — широкая подложка (обводка)
  • верхний слой — основная линия
map.addLayer({
  id: "route-outline",
  type: "line",
  source: "route-source",
  paint: {
    "line-color": "#000",
    "line-width": 8
  }
});

map.addLayer({
  id: "route-main",
  type: "line",
  source: "route-source",
  paint: {
    "line-color": "#2E86DE",
    "line-width": 4
  }
});

Такой подход создаёт эффект обводки и улучшает читаемость на сложных подложках.