Мир визуализации: Путеводитель по D3.js и React

Мир визуализации: Путеводитель по D3.js и React

Наш выпускник Алим расскажет вам о возможностях, которые предоставляет сочетание D3.js и React, а также покажет на практике, как использовать эту комбинацию для создания впечатляющих визуализаций данных.

Давайте для начала познакомимся с нашим выпускником:

— Привет Алим, расскажи пару слов о себе?

— Меня зовут Алим Шогенов, я учился в Эльбрусе на направлении веб-разработки в 2020 году, сейчас работаю ведущим веб-разработчиком в Иннотехе. Отвечаю за полный цикл разработки информационных ресурсов, включая анализ требований, проектирование, разработку, модификацию, локализацию, кастомизацию и доработку приложений. Моя задача — создавать высококачественные продукты, соответствующие потребностям пользователей, и адаптировать их под новые требования или изменения в бизнес-процессах.

— Ты предложил рассказать будущим студентам о библиотеке D3.js, почему?

— D3.js предоставляет обширный набор инструментов для создания различных типов графиков, диаграмм, карт и других визуализаций. Её удобство заключается в том, что она позволяет полностью настраивать внешний вид и поведение визуализации в соответствии с требованиями проекта. Это дает возможность создавать красочные и интерактивные визуализации данных, которые эффективно передают информацию и привлекают внимание пользователей. Также мне понравилась гибкость D3.js в работе с данными. Она позволяет легко связывать данные с элементами DOM, что делает их динамическими и управляемыми. Это особенно полезно, когда требуется визуализировать большие объемы данных или обновлять визуализации в реальном времени.

— Где ты ее применял, она потребовалась тебе больше одного раза?

— Я применял библиотеку D3.js как в личных проектах, так и в коммерческих. Она оказалась полезной и эффективной в создании различных визуализаций данных, поэтому я ее использую достаточно часто.

Что такое D3.js?

D3.js (Data-Driven Documents) — это популярная библиотека JavaScript для создания динамических интерактивных визуализаций данных с использованием HTML, CSS и SVG.

Входит в топ-100 самых популярных репозиториев на GitHub. Такие компании, как Accenture, Coinbase и Coursera, используют ее как часть своего стека. Сочетание D3.js с React позволяет разработчикам создавать красивые и функциональные веб-приложения, обогащенные визуализациями данных.

Почему использование D3.js в React-приложениях имеет множество преимуществ? ↓

  • Управление данными: D3.js позволяет легко связывать данные с элементами DOM, делая их управляемыми и динамическими
  • Интерактивность: благодаря этой библиотеке можно создавать интерактивные визуализации, которые позволяют пользователям взаимодействовать с данными
  • Гибкость и мощь: библиотека предоставляет широкий спектр функций и методов для создания разнообразных типов визуализаций данных. Мы можем контролировать каждый аспект визуализации, настраивая расположение элементов, анимации и переходы между состояниями. Это позволяет создавать уникальные и впечатляющие визуализации, которые точно привлекут внимание аудитории
  • Поддержка: D3.js активно поддерживается сообществом разработчиков и постоянно обновляется с новыми функциями и улучшениями.

Примеры использования D3.js в React

Давайте рассмотрим некоторые примеры использования D3.js в React-приложениях для создания интерактивных и динамических визуализаций данных. ↓

  • Графики и диаграммы: D3.js позволяет создавать различные типы графиков и диаграмм, такие как линейные графики, столбчатые диаграммы, круговые диаграммы и многое другое
  • Карты и географические визуализации: С помощью данной библиотеки можно создавать интерактивные карты и географические визуализации, которые отображают данные по странам, регионам или географическим объектам.
  • Инфографики и визуализации данных: библиотека также используется для создания различных видов информационных графиков и визуализаций данных, которые помогают визуально представить и анализировать данные.

Начало работы с D3.js в React

Для установки данной библиотеки в наше React-приложение выполним команду:

npm install d3


или если вы используете yarn, запустим:

yarn add d3

Также создаем файл формата .jsx, в котором мы далее будем работать.

Формат .jsx используется в языке программирования JavaScript для написания компонентов интерфейса в фреймворке React. JSX позволяет встраивать код JavaScript непосредственно в разметку интерфейса, что делает код более читаемым и удобным для разработчиков. Компоненты, написанные в формате .jsx, компилируются в обычный JavaScript для выполнения в браузере.

Создание компонента для отрисовки карты

Давайте создадим компонент, который будет отрисовывать карту мира с плотностью населения стран. Для этого импортируем необходимые библиотеки и создадим компонент WorldMap:

import * as d3 from 'd3';
import {
    useEffect,
    useRef
}
from 'react';
export WorldMap() {
    const svgRef = useRef();
    useEffect(() => {}, []);
    return <svg ref = {svgRef} width = "1300" height = "1000" / >;
}

В этом компоненте мы используем useRef, чтобы создать ссылку на SVG-элемент, который будет использоваться для отрисовки карты. А также используем useEffect, чтобы выполнить код D3.js после рендеринга компонента.

Svg элемент и его размеры

Внутри useEffect вызываем метод select из библиотеки D3.js, чтобы выбрать SVG-элемент, на который мы создали ссылку с помощью useRef. Затем, с помощью методов attr('width') и attr('height'), мы получаем значения атрибутов width и height SVG-элемента: ширину и высоту SVG-контейнера соответственно. Эти значения преобразуются из строкового типа в числовой, используя унарный оператор +.

useEffect(() => {
    const svg = d3.select(svgRef.current);
    const width = +svg.attr('width');
    const height = +svg.attr('height');
}, []);


Проекция и ее свойства

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

  • d3.geoPath() — создает экземпляр генератора пути, который преобразует географические данные в строку SVG path
  • d3.geoMercator() — создает географическую проекцию, используемую для отображения геометрии географических объектов на плоскости. Мы определяем параметры проекции, такие как масштаб, центральные координаты и смещение, с помощью методов scale(), center() и translate()
  • d3.scaleThreshold() — определяет цветовую шкалу, которая преобразует значения популяции в цвета заполнения объектов на карте. Диапазон значений популяции и соответствующие им цвета определяются с помощью методов domain() и range().
const path = d3.geoPath();
const projection = d3
  .geoMercator()
  .scale(200)
  .center([0, 20])
  .translate([width / 2, height / 2]);
const colorScale = d3
  .scaleThreshold()
  .domain([100000, 1000000, 10000000, 30000000, 100000000, 500000000])
  .range(d3.schemeBlues[7]);

Получение данных для отображения

Для того чтобы отобразить карту, необходимо загрузить данные о географических объектах и их популяции. Давайте разберемся, каким образом мы получаем эти данные и используем их:

  • Мы используем Promise.all() для загрузки двух файлов данных: географических данных в формате GeoJSON и данных о популяции стран в формате CSV.
Promise.all([
  d3.json(
    'https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson',
  ),
  d3.csv(
    'https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world_population.csv',
  ),
]).then(([topology, population]) => {
  const data = new Map();
  population.forEach(d => data.set(d.code, +d.pop));
  svg
    .selectAll('path')
    .data(topology.features)
    .join('path')
    .attr('d', path.projection(projection))
    .attr('fill', d => {
      const total = data.get(d.id) || 0;
      return colorScale(total);
    });
});
  • После успешной загрузки данных вызывается коллбэк-функция, которая получает массив данных. topology содержит географические данные (GeoJSON), а population содержит данные о популяции стран.
  • Для удобства хранения данных о популяции стран мы создаем объект Map. Затем мы проходимся по каждой записи в массиве population (каждой строке в CSV файле) и добавляем данные о популяции в объект Map, связывая код страны с популяцией.
  • Затем мы используем данные topology.features, чтобы отрисовать географические объекты (например, страны) на карте. Мы используем методы selectAll(), data() и join() для связывания данных с элементами <path> на карте.
  • Метод attr('d', ...) устанавливает атрибут d каждого элемента <path>, представляющего геометрию географического объекта. Мы используем метод path.projection(projection) для преобразования географических данных в координаты SVG, используя проекцию projection.
  • Метод attr('fill', ...) устанавливает цвет заполнения каждого элемента <path>. Коллбэк-функция определяет цвет заполнения в зависимости от данных объекта, используя объект Map для определения значения популяции для каждой страны и применения его к цветовой шкале colorScale.

После всех этих шагов мы создали компонент WorldMap, который отвечает за отображение карты с плотностью населения стран мира. Давайте рассмотрим, как этот компонент выглядит в итоге ↓

Получившийся компонент

Вот как выглядит итоговый код компонента WorldMap:

import * as d3 from 'd3';
import { useEffect, useRef } from 'react';

export function WorldMap() {
	const svgRef = useRef();

	useEffect(() => {
		const svg = d3.select(svgRef.current);
		const width = +svg.attr('width');
		const height = +svg.attr('height');

		const path = d3.geoPath();
		const projection = d3
			.geoMercator()
			.scale(200)
			.center([0, 20])
			.translate([width / 2, height / 2]);

		const colorScale = d3
			.scaleThreshold()
			.domain([100000, 1000000, 10000000, 30000000, 100000000, 500000000])
			.range(d3.schemeBlues[7]);

		Promise.all([
			d3.json(
				'https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson',
			),
			d3.csv(
				'https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world_population.csv',
			),
		]).then(([topology, population]) => {
			const data = new Map();
			population.forEach((d) => data.set(d.code, +d.pop));

			svg.selectAll('path')
				.data(topology.features)
				.join('path')
				.attr('d', path.projection(projection))
				.attr('fill', (d) => {
					const total = data.get(d.id) || 0;
					return colorScale(total);
				});
		});
	}, []);

	return <svg ref={svgRef} width="1300" height="1000" />;
}


А сам отрисованный компонент превращается в такую карту:

Дополнительные возможности D3.js: Создание интерактивного глобуса

Помимо создания статичных визуализаций, библиотека D3.js также предоставляет инструменты для создания интерактивных визуализаций, таких как вращающийся глобус. Давайте заменим функцию d3.geoMercator() на d3.geoOrthographic() для создания проекции глобуса. Эта проекция позволяет визуализировать земной шар с помощью трехмерной ортографической проекции. Для добавления интерактивности внедрим обработчики событий мыши, позволяющие вращать глобус. Кроме того, изменим визуальный эффект, заменив цветовую палитру на оттенки зеленого с помощью d3.schemeGreens[7].

import * as d3 from 'd3';
import { useEffect, useRef } from 'react';

export function WorldMap() {
	const svgRef = useRef();

	const projectionRef = useRef(
		d3.geoOrthographic().scale(400).center([0, -220]),
	);

	useEffect(() => {
		const svg = d3.select(svgRef.current);

		const path = d3.geoPath().projection(projectionRef.current);

		const colorScale = d3
			.scaleThreshold()
			.domain([100000, 1000000, 10000000, 30000000, 100000000, 500000000])
			.range(d3.schemeGreens[8]);

		Promise.all([
			d3.json(
				'https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson',
			),
			d3.csv(
				'https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world_population.csv',
			),
		]).then(([topology, population]) => {
			const data = new Map();
			population.forEach((d) => data.set(d.code, +d.pop));

			svg.selectAll('path')
				.data(topology.features)
				.join('path')
				.attr('d', path)
				.attr('fill', (d) => {
					const total = data.get(d.id) || 0;
					return colorScale(total);
				});
		});

		// Добавляем обработчики событий мыши для изменения центра проекции глобуса
		svg.call(
			d3.drag().on('drag', function (event) {
				const rotate = projectionRef.current.rotate();
				projectionRef.current.rotate([
					rotate[0] + event.dx / 2,
					rotate[1] - event.dy / 2,
				]);
				svg.selectAll('path').attr('d', path);
			}),
		);
	}, []);

	return <svg ref={svgRef} width="1300" height="1000" />;
}

И, наконец, посмотрим, что у нас получилось:

В этом примере мы используем проекцию d3.geoOrthographic(), обрабатываем события мыши для вращения глобуса и добавляем возможность интерактивного управления. Таким образом, мы можем создать увлекательные и понятные визуализации, которые позволяют пользователям исследовать данные на глобусе с помощью простых и интуитивно понятных действий.

И в заключение небольшое напутствие: Использование D3.js открывает мир возможностей в области визуализации данных. Проявляйте свою фантазию и экспериментируйте с различными функциями и подходами. Пусть погружение в мир D3.js принесет много радости и творческих достижений!.

Если у вас остались вопросы или вы захотите больше узнать о моем опыте обучения в буткемпе — пишите мне в телеграм @AlimS63.

Софья Пирогова

Софья Пирогова

Главный редактор / Автор статей