ArcLayer

ArcLayer — один из базовых слоёв библиотеки Deck.gl, предназначенный для визуализации связей между двумя географическими точками. Каждая связь отображается в виде дуги, соединяющей исходную и конечную координаты. Такой способ представления данных широко используется для отображения транспортных маршрутов, миграционных потоков, логистических цепочек, финансовых транзакций, телекоммуникационных соединений и любых других объектов, имеющих направление перемещения между двумя местами.

Главная особенность слоя заключается в том, что линии отображаются не как обычные прямые сегменты, а как визуально выразительные дуги, поднимающиеся над поверхностью карты. Благодаря этому большие наборы связей становятся более наглядными и легче воспринимаются.


Подключение ArcLayer

ArcLayer входит в пакет @deck.gl/layers.

npm install @deck.gl/core
npm install @deck.gl/layers

Импорт слоя:

import {ArcLayer} from '@deck.gl/layers';

Создание простейшего слоя:

const arcLayer = new ArcLayer({
  id: 'arc-layer',
  data: [
    {
      from: [37.618423, 55.751244],
      to: [30.335099, 59.934280]
    }
  ],

  getSourcePosition: d => d.from,
  getTargetPosition: d => d.to,

  getSourceColor: [0, 128, 255],
  getTargetColor: [255, 0, 0],

  getWidth: 3
});

В данном примере отображается одна дуга между Москвой и Санкт-Петербургом.


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

Каждый объект данных должен содержать информацию о начальной и конечной точке.

Простейшая структура:

[
  {
    source: [37.618423, 55.751244],
    target: [30.335099, 59.934280]
  }
]

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

new ArcLayer({
  data,

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

Более реалистичный вариант:

[
  {
    cityFrom: "Москва",
    cityTo: "Берлин",

    source: [37.618423, 55.751244],
    target: [13.4050, 52.5200],

    passengers: 12000,
    distance: 1600
  }
]

Такой формат позволяет использовать дополнительные свойства для настройки толщины, цвета и других параметров визуализации.


Источник и назначение дуги

getSourcePosition

Определяет координаты начала дуги.

getSourcePosition: d => d.source

Возвращаемое значение:

[longitude, latitude]

или

[longitude, latitude, altitude]

Пример:

getSourcePosition: d => [
  d.lon,
  d.lat
]

getTargetPosition

Определяет координаты конечной точки.

getTargetPosition: d => d.target

Пример:

getTargetPosition: d => [
  d.destinationLon,
  d.destinationLat
]

Для каждой записи ArcLayer строит дугу между результатами этих двух функций.


Цвет начала и конца дуги

Одной из самых полезных возможностей ArcLayer является раздельное оформление начальной и конечной стороны.

getSourceColor

Цвет начала линии.

getSourceColor: [0, 128, 255]

Либо динамически:

getSourceColor: d =>
  d.type === "domestic"
    ? [0, 200, 0]
    : [255, 120, 0]

getTargetColor

Цвет конца линии.

getTargetColor: [255, 0, 0]

Пример создания цветового градиента:

new ArcLayer({
  data,

  getSourceColor: [0, 255, 255],
  getTargetColor: [255, 0, 255]
});

Визуально дуга будет плавно менять цвет по направлению движения.


Толщина дуги

getWidth

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

Фиксированное значение:

getWidth: 2

Зависимость от данных:

getWidth: d => d.passengers / 1000

Например:

new ArcLayer({
  data,

  getWidth: d => Math.sqrt(d.volume)
});

Использование квадратного корня позволяет избежать слишком большого разброса толщин.


Масштабирование ширины

widthScale

Глобальный множитель толщины.

widthScale: 5

Итоговая ширина:

getWidth × widthScale

Пример:

new ArcLayer({
  getWidth: d => d.flow,
  widthScale: 0.1
});

widthMinPixels

Минимальная ширина в пикселях.

widthMinPixels: 2

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


widthMaxPixels

Максимальная толщина.

widthMaxPixels: 20

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


Высота дуги

ArcLayer автоматически формирует кривую между двумя точками.

Высота зависит от:

  • расстояния между точками;
  • используемой проекции;
  • параметров слоя.

Чем дальше расположены объекты, тем выше обычно отображается дуга.

Например:

{
  source: [37.6, 55.7],
  target: [139.6, 35.6]
}

Дуга между Москвой и Токио будет значительно выше, чем между двумя соседними городами.


Наклон и перспектива

Наиболее эффектно ArcLayer выглядит при использовании карты с наклоном.

Пример настроек камеры:

const INITIAL_VIEW_STATE = {
  longitude: 30,
  latitude: 50,
  zoom: 3,
  pitch: 45,
  bearing: 0
};

Параметр:

pitch: 45

создаёт трёхмерный эффект подъёма дуг над поверхностью карты.


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

Типичный сценарий — отображение потоков между регионами.

Исходные данные:

[
  {
    source: [2.3522, 48.8566],
    target: [13.4050, 52.5200],
    flow: 25000
  },
  {
    source: [-0.1276, 51.5072],
    target: [13.4050, 52.5200],
    flow: 18000
  }
]

Настройка:

new ArcLayer({
  data,

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

  getWidth: d => d.flow,
  widthScale: 0.0002
});

Чем больше поток, тем толще отображается дуга.


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

RGBA-массивы позволяют задавать альфа-канал.

Пример:

getSourceColor: [0, 128, 255, 100],
getTargetColor: [255, 0, 0, 100]

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


Интерактивность

ArcLayer поддерживает все стандартные механизмы взаимодействия Deck.gl.

Включение выбора объектов:

pickable: true

Обработка наведения:

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

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

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

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

new ArcLayer({
  data,
  pickable: true,

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

Всплывающие подсказки

Часто ArcLayer используется совместно с tooltip.

Настройка Deck:

const deck = new Deck({
  layers: [arcLayer],

  getTooltip: ({object}) =>
    object && {
      text: `
Маршрут: ${object.cityFrom} → ${object.cityTo}
Пассажиры: ${object.passengers}
`
    }
});

При наведении отображается информация о конкретной связи.


Анимация изменений данных

Deck.gl автоматически отслеживает изменение атрибутов.

Пример плавного изменения толщины:

new ArcLayer({
  data,

  getWidth: d => d.value,

  transitions: {
    getWidth: 1000
  }
});

Продолжительность:

1000

означает одну секунду.


Анимация цветов

Изменение цвета также может быть анимировано.

transitions: {
  getSourceColor: 500,
  getTargetColor: 500
}

Применение:

new ArcLayer({
  data,

  getSourceColor: d => d.sourceColor,
  getTargetColor: d => d.targetColor,

  transitions: {
    getSourceColor: 800,
    getTargetColor: 800
  }
});

Работа с большими объёмами данных

ArcLayer оптимизирован для GPU-рендеринга и способен отображать десятки тысяч связей одновременно.

Практические рекомендации:

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

Плохо:

getWidth: d => {
  return complicatedCalculation(d);
}

Лучше:

const preparedData = data.map(item => ({
  ...item,
  width: complicatedCalculation(item)
}));

getWidth: d => d.width

Комбинирование с другими слоями

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

Наиболее распространённые комбинации:

ArcLayer + ScatterplotLayer

Точки обозначают узлы сети, дуги показывают связи.

layers: [
  scatterplotLayer,
  arcLayer
]

ArcLayer + TextLayer

Названия городов и соединяющие маршруты.

layers: [
  textLayer,
  arcLayer
]

ArcLayer + GeoJsonLayer

Отображение стран или регионов вместе с потоками между ними.

layers: [
  geoJsonLayer,
  arcLayer
]

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


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

Возможность ArcLayer LineLayer
Прямая линия Нет Да
Дуга Да Нет
Раздельные цвета начала и конца Да Ограниченно
Выраженный 3D-эффект Да Нет
Визуализация потоков Отлично Удовлетворительно
Производительность Высокая Высокая

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


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

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

const data = [
  {
    source: [37.618423, 55.751244],
    target: [13.4050, 52.5200],
    passengers: 12000
  },
  {
    source: [37.618423, 55.751244],
    target: [2.3522, 48.8566],
    passengers: 18000
  }
];

const arcLayer = new ArcLayer({
  id: 'routes',

  data,

  pickable: true,

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

  getSourceColor: [0, 128, 255],
  getTargetColor: [255, 80, 80],

  getWidth: d => d.passengers,
  widthScale: 0.0002,

  widthMinPixels: 2,

  transitions: {
    getWidth: 1000
  }
});

new Deck({
  initialViewState: {
    longitude: 20,
    latitude: 52,
    zoom: 3,
    pitch: 45
  },

  controller: true,

  layers: [arcLayer],

  getTooltip: ({object}) =>
    object && {
      text: `Пассажиры: ${object.passengers}`
    }
});

Данный пример демонстрирует большинство ключевых возможностей ArcLayer: построение дуг между городами, настройку цветов, управление толщиной линий, интерактивность, всплывающие подсказки и анимированное изменение визуальных параметров.