Понятие слоя (Layer)

Слой (Layer) — центральная абстракция библиотеки Deck.gl, через которую описываются визуализируемые данные, логика их обработки и способ отображения на карте или в трёхмерной сцене. Практически любой объект, появляющийся на экране, является экземпляром некоторого слоя.

В отличие от традиционного подхода, где разработчик вручную управляет объектами WebGL, буферами, шейдерами и отрисовкой, Deck.gl переносит эти задачи внутрь слоя. Разработчик работает на уровне данных и параметров визуализации.

Каждый слой отвечает за:

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

Слой можно рассматривать как декларативное описание набора объектов.

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

new ScatterplotLayer({
  data: points,
  getPosition: d => d.coordinates,
  getRadius: d => d.size
})

создаётся один слой, который самостоятельно отображает все элементы массива.


Базовая структура слоя

Любой слой создаётся через конструктор соответствующего класса.

Пример:

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

const layer = new ScatterplotLayer({
  id: 'cities',
  data: cities,
  getPosition: d => d.coordinates,
  getRadius: 1000,
  getFillColor: [255, 0, 0]
});

Здесь:

Свойство Назначение
id уникальный идентификатор слоя
data источник данных
getPosition получение координат объекта
getRadius размер объекта
getFillColor цвет объекта

После создания слой передаётся в массив layers.

const deck = new Deck({
  layers: [layer]
});

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


Слой как декларативное описание данных

Одной из ключевых особенностей Deck.gl является декларативная модель.

Вместо команд вида:

drawCircle(x, y);
drawCircle(x2, y2);
drawCircle(x3, y3);

описывается набор данных:

new ScatterplotLayer({
  data: dataset
})

Библиотека сама решает:

  • сколько объектов рисовать;
  • когда выполнять обновление;
  • когда пересоздавать буферы GPU;
  • какие данные отправлять в видеопамять.

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


Идентификатор слоя

Каждый слой обязан иметь уникальный идентификатор.

id: 'airports-layer'

Deck.gl использует идентификатор для:

  • отслеживания обновлений;
  • сохранения состояния;
  • сопоставления старой и новой версии слоя;
  • оптимизации рендеринга.

Нежелательно использовать одинаковые идентификаторы.

Некорректно:

new ScatterplotLayer({
  id: 'layer'
});

new ArcLayer({
  id: 'layer'
});

Правильно:

new ScatterplotLayer({
  id: 'airports'
});

new ArcLayer({
  id: 'routes'
});

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

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

Простейший вариант:

data: [
  {name: 'A'},
  {name: 'B'},
  {name: 'C'}
]

Чаще используются массивы объектов:

data: [
  {
    city: 'London',
    coordinates: [-0.12, 51.50]
  },
  {
    city: 'Paris',
    coordinates: [2.35, 48.85]
  }
]

Также допускаются:

  • URL-адреса;
  • асинхронные загрузчики;
  • объекты Promise;
  • специализированные форматы данных.

Пример:

data: '/data/cities.json'

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


Accessor-функции

Большинство свойств слоя задаются через специальные функции-доступы (accessors).

Пример:

getPosition: d => d.position

Deck.gl вызывает такую функцию для каждого элемента данных.

Пусть массив содержит:

[
  {position: [10, 20]},
  {position: [30, 40]}
]

Для каждого объекта будет вызван:

d => d.position

Результатом станет набор координат.

Популярные accessor-функции:

getPosition
getColor
getFillColor
getLineColor
getRadius
getWidth
getElevation
getText

Пример:

new ScatterplotLayer({
  data: cities,

  getPosition: d => d.coordinates,

  getRadius: d => d.population / 1000,

  getFillColor: d => [
    d.red,
    d.green,
    d.blue
  ]
});

Категории слоёв

Deck.gl содержит большое количество готовых слоёв.

Основные категории:

Точечные слои

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

Примеры:

  • ScatterplotLayer
  • IconLayer
  • TextLayer
new ScatterplotLayer({...})

Линейные слои

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

Примеры:

  • LineLayer
  • PathLayer
  • ArcLayer
new ArcLayer({...})

Полигональные слои

Работают с областями.

Примеры:

  • PolygonLayer
  • GeoJsonLayer
  • SolidPolygonLayer
new GeoJsonLayer({...})

Объёмные слои

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

Примеры:

  • ColumnLayer
  • HexagonLayer
  • GridLayer
new ColumnLayer({...})

Наследование от базового класса Layer

Все слои библиотеки происходят от базового класса Layer.

Упрощённая схема:

Layer
 ├─ ScatterplotLayer
 ├─ ArcLayer
 ├─ IconLayer
 ├─ TextLayer
 ├─ GeoJsonLayer
 ├─ PathLayer
 └─ ColumnLayer

Базовый класс определяет:

  • жизненный цикл;
  • систему свойств;
  • управление состоянием;
  • интеграцию с WebGL;
  • механизм обновлений.

Благодаря этому все слои обладают единым интерфейсом.


Свойства слоя (Props)

Практически всё поведение слоя задаётся через свойства (props).

Пример:

new ScatterplotLayer({
  id: 'points',
  data: dataset,
  opacity: 0.7,
  visible: true,
  pickable: true
});

Наиболее важные свойства:

visible

Управляет отображением слоя.

visible: false

Слой остаётся в памяти, но не рисуется.


opacity

Прозрачность.

opacity: 0.5

Диапазон:

0.0 — полностью прозрачный
1.0 — полностью непрозрачный

pickable

Разрешает взаимодействие мышью.

pickable: true

После включения становятся доступны события:

onHover
onClick

autoHighlight

Автоматическая подсветка объектов.

autoHighlight: true

При наведении курсора объект получает выделение.


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

Слои поддерживают интерактивность.

Пример обработки клика:

new ScatterplotLayer({
  pickable: true,

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

Объект info содержит сведения о выбранном элементе.

Основные поля:

info.object
info.index
info.coordinate
info.x
info.y

Пример:

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

Обновление слоя

Deck.gl придерживается принципов неизменяемости (immutability).

Вместо изменения существующего слоя создаётся новый экземпляр.

Неправильно:

layer.radius = 100;

Правильно:

new ScatterplotLayer({
  ...oldProps,
  getRadius: 100
});

При обновлении Deck.gl сравнивает:

  • старый слой;
  • новый слой.

Если изменились только отдельные свойства, библиотека обновляет лишь необходимые ресурсы GPU.


Жизненный цикл слоя

Каждый слой проходит несколько этапов существования.

Инициализация

Создание объекта.

new ScatterplotLayer({...})

Создание состояния

Внутри вызывается:

initializeState()

Создаются:

  • атрибуты;
  • буферы;
  • внутренние структуры данных.

Обновление

При изменении свойств выполняется:

updateState()

Происходит пересчёт ресурсов.


Отрисовка

Вызывается:

draw()

В этот момент происходит взаимодействие с WebGL.


Удаление

При уничтожении слоя:

finalizeState()

Освобождаются:

  • буферы;
  • текстуры;
  • память GPU.

Состояние слоя

Каждый слой имеет внутреннее состояние.

Доступ осуществляется через:

this.state

Пример пользовательского слоя:

initializeState() {
  this.setState({
    selectedIndex: -1
  });
}

Изменение:

this.setState({
  selectedIndex: 5
});

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


Атрибуты слоя

Одним из важнейших механизмов являются атрибуты.

Атрибуты связывают данные приложения с буферами GPU.

Например:

positions
colors
radii

Для массива:

[
  {x: 10, y: 20},
  {x: 30, y: 40}
]

создаётся массив координат:

[
  10, 20,
  30, 40
]

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

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


Композиция слоёв

Несколько слоёв могут работать одновременно.

Пример:

layers: [
  airportsLayer,
  routesLayer,
  labelsLayer
]

Здесь:

  • первый слой отображает аэропорты;
  • второй рисует маршруты;
  • третий показывает подписи.

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

Подобная композиция является основой сложных визуализаций в Deck.gl.


Пользовательские слои

При необходимости можно создавать собственные классы.

Пример минимального слоя:

import {Layer} from '@deck.gl/core';

class CustomLayer extends Layer {

  initializeState() {
  }

  draw() {
  }
}

Чаще пользовательские слои наследуются от существующих.

class MyScatterLayer extends ScatterplotLayer {

}

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


Преимущества слоя как основной единицы визуализации

Использование концепции слоя обеспечивает ряд преимуществ:

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

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