LineLayer

LineLayer — один из базовых слоёв библиотеки Deck.gl, предназначенный для визуализации линейных объектов на карте или в трёхмерной сцене. Слой отображает набор независимых отрезков, каждый из которых задаётся начальной и конечной координатой.

Наиболее распространённые сценарии использования:

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

В отличие от PathLayer, который строит непрерывные маршруты из множества точек, LineLayer работает именно с отдельными сегментами. Каждый объект данных соответствует одной линии.


Подключение слоя

import {Deck} from '@deck.gl/core';
import {LineLayer} from '@deck.gl/layers';

Создание слоя:

const layer = new LineLayer({
  id: 'line-layer',
  data: [
    {
      source: [-74.006, 40.7128],
      target: [-118.2437, 34.0522]
    }
  ],

  getSourcePosition: d => d.source,
  getTargetPosition: d => d.target
});

Структура данных

Минимальный набор данных состоит из двух координат:

[
  {
    source: [37.6176, 55.7558],
    target: [30.3351, 59.9343]
  }
]

Здесь:

  • source — начало линии;
  • target — конец линии.

Каждый элемент массива представляет отдельный линейный сегмент.


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

id

Уникальный идентификатор слоя.

id: 'roads-layer'

Deck.gl использует идентификатор для управления жизненным циклом слоя.


data

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

data: roads

В качестве значения могут выступать:

data: array
data: promise
data: url
data: iterable

Пример загрузки JSON:

data: '/data/routes.json'

getSourcePosition

Возвращает координаты начала линии.

getSourcePosition: d => d.source

Если структура данных отличается:

getSourcePosition: d => [
  d.startLng,
  d.startLat
]

Также допускается работа с высотой:

getSourcePosition: d => [
  d.lng,
  d.lat,
  d.altitude
]

getTargetPosition

Возвращает координаты конца линии.

getTargetPosition: d => d.target

Пример:

getTargetPosition: d => [
  d.endLng,
  d.endLat
]

Настройка цвета

getColor

Определяет цвет линии.

Формат:

[r, g, b]

или

[r, g, b, a]

Пример:

getColor: [255, 0, 0]

Красная линия.

С использованием альфа-канала:

getColor: [255, 0, 0, 128]

Полупрозрачная линия.


Динамический цвет

Цвет может вычисляться на основе данных.

getColor: d => {
  if (d.type === 'highway') {
    return [255, 100, 0];
  }

  return [100, 100, 255];
}

По интенсивности потока:

getColor: d => [
  Math.min(d.volume * 5, 255),
  50,
  200
]

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

getWidth

Определяет ширину линии.

getWidth: 5

Или:

getWidth: d => d.width

Пример:

getWidth: d => d.passengers / 1000

Чем больше пассажиропоток, тем толще линия.


widthUnits

Единицы измерения толщины.

По умолчанию:

widthUnits: 'pixels'

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

'pixels'
'meters'
'common'

Pixels

Толщина не зависит от масштаба карты.

widthUnits: 'pixels',
getWidth: 3

Линия всегда будет отображаться шириной 3 пикселя.


Meters

Толщина рассчитывается в метрах.

widthUnits: 'meters',
getWidth: 100

Полезно для географических объектов.


widthScale

Масштабирует ширину всех линий.

widthScale: 10

Если:

getWidth: 2

то фактическая ширина станет:

2 × 10 = 20

widthMinPixels

Минимальная ширина на экране.

widthMinPixels: 1

Пример:

widthMinPixels: 3

Даже при сильном уменьшении карты линия останется различимой.


widthMaxPixels

Максимальная ширина линии.

widthMaxPixels: 20

Это ограничивает чрезмерное увеличение толстых сегментов.


Работа с прозрачностью

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

getColor: [0, 128, 255, 80]

или

getColor: d => [
  0,
  150,
  255,
  d.opacity
]

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

LineLayer поддерживает трёхмерные координаты.

Пример:

{
  source: [37.6176, 55.7558, 0],
  target: [30.3351, 59.9343, 10000]
}

Настройка:

getSourcePosition: d => d.source,
getTargetPosition: d => d.target

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


Визуализация авиамаршрутов

Один из самых популярных сценариев применения слоя.

Структура данных:

[
  {
    from: [-0.1276, 51.5074],
    to: [2.3522, 48.8566],
    passengers: 120000
  }
]

Создание слоя:

new LineLayer({
  id: 'flights',

  data: flights,

  getSourcePosition: d => d.from,

  getTargetPosition: d => d.to,

  getWidth: d => d.passengers / 20000,

  getColor: [255, 140, 0]
})

Толщина линии показывает интенсивность авиасообщения.


Визуализация связей между объектами

Данные:

[
  {
    source: [10, 20],
    target: [30, 40],
    strength: 0.8
  }
]

Слой:

new LineLayer({
  id: 'network',

  data,

  getSourcePosition: d => d.source,

  getTargetPosition: d => d.target,

  getWidth: d => d.strength * 10,

  getColor: d => [
    255,
    0,
    0,
    d.strength * 255
  ]
})

Сильные связи отображаются более толстыми и насыщенными линиями.


Поддержка событий

pickable

Разрешает взаимодействие с объектами.

pickable: true

Без этого свойства события выбора работать не будут.


onHover

Срабатывает при наведении курсора.

onHover: info => {
  console.log(info.object);
}

Пример отображения подсказки:

onHover: ({object, x, y}) => {
  if (object) {
    showTooltip(object, x, y);
  }
}

onClick

Обработка клика.

onClick: info => {
  console.log(info.object);
}

Пример:

onClick: ({object}) => {
  openRouteDetails(object.id);
}

Использование updateTriggers

При изменении внешних параметров слой можно обновлять выборочно.

new LineLayer({
  getColor: d => colorScale(d.value),

  updateTriggers: {
    getColor: [theme]
  }
})

При изменении theme Deck.gl пересчитает только цвета линий.


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

Использование бинарных данных

Для больших объёмов данных рекомендуется применять бинарный формат.

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

Float32Array
Uint8Array

Такой подход снижает нагрузку на сборщик мусора JavaScript и ускоряет передачу данных в GPU.


Минимизация вычислений

Менее эффективно:

getColor: d => {
  return calculateComplexColor(d);
}

Более эффективно:

const colors = precomputeColors(data);

getColor: (_, {index}) => colors[index];

Использование updateTriggers

Избыточные пересчёты:

layers = [
  new LineLayer({...})
];

Оптимизированный вариант:

updateTriggers: {
  getWidth: [scale]
}

Сравнение LineLayer и PathLayer

Возможность LineLayer PathLayer
Отдельные сегменты Да Нет
Маршруты из множества точек Нет Да
Простота вычислений Высокая Средняя
Большие наборы связей Отлично Ограниченно
Графовые структуры Отлично Неудобно

Для независимых соединений между объектами предпочтителен LineLayer. Для дорог, треков GPS и сложных маршрутов обычно используется PathLayer.


Полный пример

import {Deck} from '@deck.gl/core';
import {LineLayer} from '@deck.gl/layers';

const routes = [
  {
    source: [37.6176, 55.7558],
    target: [30.3351, 59.9343],
    traffic: 50
  },
  {
    source: [37.6176, 55.7558],
    target: [27.5615, 53.9045],
    traffic: 80
  }
];

const lineLayer = new LineLayer({
  id: 'routes',

  data: routes,

  pickable: true,

  getSourcePosition: d => d.source,

  getTargetPosition: d => d.target,

  getWidth: d => d.traffic / 10,

  widthMinPixels: 2,

  getColor: d => [
    Math.min(d.traffic * 3, 255),
    100,
    255
  ],

  onClick: ({object}) => {
    console.log(object);
  }
});

new Deck({
  initialViewState: {
    longitude: 37.6176,
    latitude: 55.7558,
    zoom: 4
  },

  controller: true,

  layers: [lineLayer]
});

LineLayer представляет собой высокопроизводительный инструмент визуализации линейных связей, оптимизированный для работы с большим количеством независимых сегментов. Благодаря поддержке GPU-рендеринга, динамической стилизации, интерактивности и трёхмерных координат слой эффективно используется для построения сетей, маршрутов, потоков данных и любых геопространственных связей между объектами.