Deck.gl - это платформа визуализации данных с открытым исходным кодом, разработанная Uber. С Deck.gl вы можете создавать потрясающие и интерактивные 3D-визуализации, подобные той, которую вы видите ниже на рисунке 1. Несмотря на то, что этот фреймворк был выпущен 3 года назад, кажется, что не так много руководств по созданию собственной колоды. Визуализация .gl отдельно от документации на сайте. Итак, сегодня мы создадим трехмерную визуализацию данных с нуля, чтобы помочь вам лучше понять, как использовать Deck.gl в ваших личных проектах.

Настройка нашей среды разработки

В этом руководстве мы собираемся использовать React для создания проекта. Самый простой способ создать проект React - через Create React App. Если вы еще не установили приложение create response, проверьте их документацию, чтобы инструменты были готовы к работе. Мы создадим и запустим проект с помощью следующих команд:

npx create-react-app deckgl-dataviz
cd deckgl-dataviz
npm start

Имя папки действительно может быть любым, но это то, что я выбрал для этого урока. при выполнении npm start должна отображаться текущая страница с вращающимся логотипом реакции. Поздравляю, теперь у вас есть рабочий проект React, который мы можем использовать для наших данных, а именно. Deck.gl работает в сочетании с response-map-gl, который использует Mapbox для отображения карты. Мы также будем использовать Axios для извлечения данных из API и использовать масштаб d3 для масштабирования данных, чтобы столбцы на карте не увеличивались до больших размеров. добавьте следующие зависимости в свой package.json и выполните npm install.

 “axios”: “0.19.2”,
 “deck.gl”: “8.1.6”,
 “@deck.gl/core”: “8.0.15”,
 “@deck.gl/geo-layers”: “8.0.15”,
 “@deck.gl/layers”: “8.0.15”,
 “mapbox-gl”: “1.7.0”,
 “d3-scale”: “3.0.0”,
 “react-map-gl”: “5.2.3”,

Отображение карты

После завершения установки мы, наконец, можем приступить к созданию приложения. Откройте app.js и замените весь код следующим:

import React from "react";
import DeckGL from "deck.gl";
import { StaticMap } from 'react-map-gl';
const MAPBOX_ACCESS_TOKEN = "pk.eyJ1IjoidWd1cjIyMiIsImEiOiJjazZvOXVibW8wMHR3M21xZnE0cjZhbHI0In0.aCGjvePsRwkvQyNBjUEkaw";
const mapStyle = "mapbox://styles/ugur222/ckab0shlf1zlt1hqhqqelaf90";
const INITIAL_VIEW_STATE = {
  longitude: 12.8333,
  latitude: 42.8333,
  zoom: 4,
  maxZoom: 16,
  minZoom: 4,
  pitch: 60,
  bearing: 5
};
export default class App extends React.Component {
  constructor(props) {
    super();
  }
  componentDidMount() {
    // will be used to fetch data from the api later
  }
  render() {
    return (
      <div>
        }
        <DeckGL initialViewState={INITIAL_VIEW_STATE} controller={true} >
          <StaticMap mapStyle={mapStyle} mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN} />
        </DeckGL>
      </div >
    );
  }
}

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

const MAPBOX_ACCESS_TOKEN = "pk.eyJ1IjoidWd1cjIyMiIsImEiOiJjazZvOXVibW8wMHR3M21xZnE0cjZhbHI0In0.aCGjvePsRwkvQyNBjUEkaw";
const mapStyle ="mapbox://styles/ugur222/ckab0shlf1zlt1hqhqqelaf90";

После этого мы инициализируем настройки карты с помощью INITIAL_VIEW_STATE. Масштабирование, долгота и широта очевидны, но если вы никогда не работали с трехмерной картой, прежде чем пеленг и угол наклона, потребуются дополнительные пояснения. С помощью свойства pitch вы можете установить угол наклона карты. Если вы измените его на 0, вы увидите карту сверху вниз. В этом примере мы используем 60, чтобы получить четкое представление о трехмерных столбцах, которые мы вскоре реализуем. С помощью свойства пеленга вы можете решить, в каком направлении вы смотрите на карту. Итак, север - это 0, восток - это 90, юг - это 180, а запад - это 270.

const INITIAL_VIEW_STATE = {
  longitude: 12.8333,
  latitude: 42.8333,
  zoom: 4,
  maxZoom: 16,
  minZoom: 4,
  pitch: 60,
  bearing: 5
};

Последняя важная часть кода - это оболочка Deck.gl внизу. Здесь мы инициализируем состояние просмотра, которое мы объявили выше. Контроллер, как следует из названия, будет включать или отключать элементы управления картой. Затем с помощью StaticMap мы добавим стиль карты и токен доступа, объявленные выше.

<DeckGL initialViewState={INITIAL_VIEW_STATE} controller={true} ><StaticMap mapStyle={mapStyle} mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN} />
</DeckGL>

Получение данных из API NovelCOVID

Теперь мы извлечем данные из API, чтобы заполнить трехмерные столбцы, которые мы собираемся использовать позже, значениями данных. NovelCOVID API предоставляет данные о вспышке COVID-19 по каждой стране. В этом руководстве мы сделаем его простым и извлечем только активные кейсы по каждой стране. Для получения данных из API воспользуемся Axios. Теперь, когда добавлен вызов API, код выглядит следующим образом

...
import axios from "axios";
...
let data;
export default class App extends React.Component {
  state = {};
  constructor(props) {
    super();
    this.state = {
      data: [],
    };
  }
  componentDidMount() {
    this.fetchData();
  }
  fetchData() {
    axios.all([
      axios.get('https://disease.sh/v2/countries?allowNull=false'),
    ]).then(axios.spread((World) => {
      let data = World.data || [];
      data = data.map(function (location) {
        return {
          active: location.active,
          country: location.country,
          continent: location.continent,
          coordinates: [location.countryInfo.long, location.countryInfo.lat]
        };
      });
      data = data.filter(location => (location.continent === "Europe"));
      this.setState({ data: data });
    })).catch((error) => {
      console.log(error); return [];
    })
  }
  render() {
    const { data } = this.state;
    console.log(data);
    return (
      <div>
        }
        <DeckGL initialViewState={INITIAL_VIEW_STATE} controller={true} ><StaticMap mapStyle={mapStyle} mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN} />
        </DeckGL>
      </div >
    );
  }
}

Я использовал функцию array.filter, чтобы показать только все страны Европы. Если вы хотите отображать только страны с другого континента, вы можете изменить это соответствующим образом. Вы даже можете полностью оставить его, чтобы получить все страны. Откройте консоль в браузере, и вы должны увидеть вывод массива, полученного из API, как показано ниже.

Теперь наступает сложная часть, когда мы должны добавить данные из API в трехмерные столбцы слоя Deck.gl. Для этого нам сначала нужно будет создать отдельный компонент, в котором мы храним все слои. Создайте новый файл и назовите его deck.gl-layer.jsx. Это будет компонент-перехватчик, который будет использоваться только для загрузки слоев в app.jsx. На сайте Deck.gl вы можете ознакомиться со всеми различными слоями, которые они предлагают для построения данных. В этом руководстве мы будем использовать columnlayer, поскольку с ним легко работать и не требуется много или сложный код.

import { ColumnLayer } from “deck.gl”;
import { scaleLinear } from “d3-scale”;
export const RenderLayers = (props) => {
 let maxActive, minActive;
 const radiusColumns = 15000;
 const { data} = props;
 const value = data.map((a) => a.active);
 maxActive = Math.max(…value);
 minActive = Math.min(…value);
const elevation = scaleLinear([minActive, maxActive], [0, 20000]);
 return [
 new ColumnLayer({
 id: “cases”,
 data,
 pickable: true,
 extruded: true,
 getPosition: d => d.coordinates,
 diskResolution: 10,
 radius: radiusColumns,
 elevationScale: 50,
 getFillColor: [255, 165, 0],
 getElevation: d => elevation(d.active),
 }),
 ];
}

Сначала мы инициализируем свойства max и min для высоты столбцов. Затем мы создаем свойство radiusColumns, чтобы установить размер столбцов. Вы можете изменить это позже по своему усмотрению, когда данные будут загружены на карту. Затем мы получаем данные через props из app.jsx. Теперь мы можем установить минимальное и максимальное значение динамически с данными из API и добавить их в свойство высоты.

maxActive = Math.max(…value);
minActive = Math.min(…value);
const elevation = scaleLinear([minActive, maxActive], [0, 20000]);

Далее мы создаем слой. Идентификатор в этом случае может быть любым, только будьте уверены, что всякий раз, когда вы добавляете второй слой, у них разные идентификаторы. Deck.gl сообщает об этом по следующей причине:

id используется для сопоставления слоев между вызовами рендеринга. deck.gl требует, чтобы каждый слой имел уникальный id. Значение по умолчанию id назначается в зависимости от типа слоя, что означает, что если вы используете несколько слоев одного типа (например, twoScatterplotLayers), вам необходимо указать пользовательский id хотя бы для одного из них.

Свойство data - это данные, которые мы получили от API. В этом случае наше свойство данных называется data, поэтому нам не нужно на него ссылаться. Если ваш массив называется, скажем, collection, вам нужно будет записать его как data:collection. Остались только важные getPosition, getFillColor и getElevation. с положением мы добавляем координаты GPS в каждый столбец, чтобы все страны, которые выбираются, получили свой собственный столбец. С цветом заливки мы можем выбрать цвет. Важно отметить, что значение должно возвращать массив со значением RGB; он не будет работать с шестнадцатеричным кодом или строкой. С помощью увеличения масштаба мы можем установить высоту столбцов. Для этого мы будем использовать активную ценность каждой страны.

getElevation: d => elevation(d.active)

Теперь нам нужно импортировать этот слой в app.jsx, например

import { RenderLayers } from “./deck.gl-layer.jsx”;

чтобы добавить слой в Deck.gl обновил оболочку Deck.gl, добавив к ней компонент renderLayers, как показано ниже

<DeckGL layers={RenderLayers({ data: data})} initialViewState={INITIAL_VIEW_STATE} controller={true} >

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

Хорошо, что вы создали свою собственную 3D-визуализацию данных. Мы собираемся добавить еще одну вещь, прежде чем мы закончим. Давайте добавим функцию наведения, которая будет отображать активные обращения в каждой стране при наведении курсора на соответствующий столбец. Во-первых, нам нужно добавить функцию onHover в файл deck.gl-Layers.jsx, как показано ниже.

const { data, onHover } = props;

Затем добавьте функцию в Columnlayer

new ColumnLayer({
 id: “cases”,
 data,
 pickable: true,
 extruded: true,
 getPosition: d => d.coordinates,
 diskResolution: 10,
 radius: radiusColumns,
 elevationScale: 50,
 getFillColor: [255, 165, 0],
 getElevation: d => elevation(d.active),
 onHover,
 }),

Теперь вернемся к app.jsx и добавим функцию зависания в Deck.gl. давайте сначала создадим объект при наведении курсора, в котором будут храниться все данные столбца при наведении курсора.

this.state = {
      data: [],
      hover: {
        x: 0,
        y: 0,
        hoveredObject: null
      }
    };

Создайте функцию для рендеринга наведения.

renderTooltip({ x, y, object, layer }) {
    this.setState({ hover: { x, y, layer, hoveredObject: object } });
}

В функции рендеринга добавьте свойство hover к this.state, чтобы нам не приходилось каждый раз писать this.state.hover.

const { hover, data } = this.state;

Прежде чем мы получим доступ к данным из свойства hover, нам сначала нужно добавить их в оболочку Deck.gl, как показано ниже.

<DeckGL ContextProvider={MapContext.Provider} layers={RenderLayers({ data: data, onHover: hover => this.renderTooltip(hover) })}initialViewState={INITIAL_VIEW_STATE} controller={true} >

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

return (
      <div>
        {hover.hoveredObject && (
          <div style={{
            position: "absolute",
            zIndex: 1000,
            background: "#ffffff",
            pointerEvents: "none",
            borderRadius: "5px",
            left: hover.x,
            top: hover.y
          }} >
            <ul className="hoveredObjectData">
              <li><h4>{hover.hoveredObject.country}</h4></li>
              <li>active cases: <span>{hover.hoveredObject.active.toLocaleString()}</span></li>
            </ul>
          </div>
        )
        }
      </div >
    );

Я добавил некоторые встроенные стили для правильного отображения данных, но вы можете изменить их на все, что захотите. Я добавил toLocatString() к активному значению, чтобы отображать большие числа с запятой, чтобы оно выглядело более чистым.

Затем добавьте этот CSS в файл index.css, чтобы получить правильный интервал во всплывающей подсказке.

.hoveredObjectData {
  list-style-type: none;
  margin:5px;
  padding:5px;
}
h4 {
  margin-bottom: 5px;
  margin-top: 5px;
}

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

Заключение

Надеюсь, вы лучше понимаете, как использовать Deck.gl в своих проектах. Следующий шаг - посмотреть, можно ли добавить в столбцы функцию щелкнуть, чтобы отобразить наложение, дающее более подробную информацию о стране. Для этого детального просмотра вы можете использовать библиотеку 2D-графиков, такую ​​как Recharts или React-VIS. Чтобы понять, как это могло быть, взгляните на мою визуализацию covid-19.

Ресурсы, использованные в этой статье…