Abr@X@bra.ru
Визуализация данных на JS
Визуализация данных на JS

Визуализация данных на JS

21.02.2017
490

D3 (Data-Driven Documents — Управление данными в документах [досл.]). Она позволяет «оживить» ваш проект, используя произвольные данные, совместить их с HTML, CSS и SVG (Scalable Vector Graphic — Векторная графика), динамически манипулируя элементами DOM (Document Object Model — Объектная модель документа) и на выходе получить, к примеру, линейную диаграмму или любой другой объект/картинку. Из реального использования D3 можно отметить такие важные применения, как построение графиков, создание карты, с использованием координат и другое.

Для успешного изучения данной библиотеки потребуется знания в области HTML, CSS, JS и SVG (хорошее знакомство с SVG можете посмотреть здесь). Данная статья дает лишь краткий обзор широких возможностей, которые вы сможете изучить уже в дальнейшем самостоятельно.

Что же подразумевается под данными? Данные — это любая информация, потенциально содержащая некий смысл, причем как явного, так и неявного характера и применяя визуализацию к данным в такой постановке вопроса, можно этот смысл представить в довольно наглядном виде. В самом простом виде мы можем представить себе данные, как массив чисел, описывающих какой то смысл, к примеру то же самое голосование в виде линейного графика, сколько проголосовало за «хорошо», а сколько за «плохо». На входе поступают данные (пользователь выбрал вариант, этот вариант обработался), а на выходе мы видим отображение для проделанных действий, — голос зачтен, суммировался с предыдущими голосами и общая картинка представилась в виде графика.

Что нам предоставляет D3? Библиотека дает нам все необходимые инструменты для обработки данных и манипулирования DOM (наподобие jQuery, в частности построение цепочек методов). Данные библиотеки имеют очень большое сходство друг с другом, так как имеют встроенные процедуры манипулирования DOM, распознают CSS-селекторы (выборку элементов) и основываются на веб-стандартах, но все же имеют отличие друг от друга по назначению. jQuery — обертка над стандартным JS, имеющимся в любом браузере, библиотека общего назначения, предназначенная для облегчения множества операции, проделываемых на обычном JS, в то время как D3 предназначается для манипуляции данными (их визуализации, фильтрации, трансформацией, генерированием и т.д.), поддерживает работу с векторной графикой и содержит различные механизмы привязки данных к html-документу.

Давайте немного потренируемся в рисовании примитивов векторной графики, заодно вспомним, (или узнаем, для кого как) как вообще описать средствами html разметки такие геометрические фигуры, как линии и окружности. Нарисуем, к примеру вот такой вымышленный график, с осями координат и линией под 45 градусов, наложим на этот график окружность так чтобы линия проходила через нее и пересекала пополам:


<svg width="250" height="250"> <line x1="0" y1="250" x2="0" y2="0" stroke-width="1" stroke="#000000"></line> <line x1="0" y1="250" x2="250" y2="250" stroke-width="1" stroke="#000000"></line> <line x1="250" y1="0" x2="0" y2="250" stroke-width="1" stroke="#9b321e"></line> <circle r="50" cx="125" cy="125" fill="#c8c8c8"></circle> </svg>

Прежде всего нужно отметить, что весь код пишется в тегах svg, в контейнере для векторной графики с шириной и высотой в 250 пикселей (на самом деле вы можете сами определять размеры, просто я сделал 250px для примера). На листинге отрисованы всего навсего 4 геометрических фигуры: три линии и одна окружность, по-изучайте немного данный код, здесь все просто, а статья про знакомство с SVG, о которой я писал выше развеет все вопросы.

Мы нарисовали данные фигуры и обозначили их параметры статично, но вся прелесть в том что D3 позволяет нам в зависимости от данных делать это все в динамике, — еще раз повторюсь, будь то рисование простых графиков, или же просто захватывающих дух, примеры визуализации.

Теперь попробуем сделать тот же «причудливый» график но уже в динамике, методами D3. Для начала подключим D3, например так:


<script src="https://d3js.org/d3.v3.min.js"></script>

Затем создайте элемент:


<svg id="area" width="250" height="250"></svg>

И пропишите следующий код:


var svg = d3.select("#area"), // выбираем контейнер svg line_1 = svg.append("line"), // вставляем линию line_2 = svg.append("line"), line_3 = svg.append("line"), circle = svg.append("circle"); // вставляем окружность /* присваиваем атрибуты вновь добавленным элементам в контейнер svg */ line_1.attr("x1", "0") .attr("y1", "250") .attr("x2", "0") .attr("y2", "0") .attr("stroke-width", "1") .attr("stroke", "#000000"); line_2.attr("x1", "0") .attr("y1", "250") .attr("x2", "250") .attr("y2", "250") .attr("stroke-width", "1") .attr("stroke", "#000000"); line_3.attr("x1", "250") .attr("y1", "0") .attr("x2", "0") .attr("y2", "250") .attr("stroke-width", "1") .attr("stroke", "#000000"); circle.attr("r", "50") .attr("cx", "125") .attr("cy", "125") .attr("fill", "#c8c8c8");

Проанализировав этот листинг, можно убедиться в том что D3 работает почти также как и jQuery. Сначала идет выборка элементов, мы выбираем ту область куда будем добавлять наши новые элементы, вставляем их и присваиваем им атрибуты, даже методы одноименные, что очень радует. А присваивая данные в процессе добавления элементов мы привносим динамику! Вот такой простейший пример уже должен показать вам как все обстоит.

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

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

Концепция D3

Разберем ключевые моменты при работе с библиотекой на простом примере. Построим линейную вертикальную диаграмму из 5 столбцов, каждый из которых описывается своей случайно сгенерированной переменной из массива. Напишем функцию:

/* простая функция генерации массива из 5 случайных положительных целых чисел в диапазоне 0..100 */ function numRand(max) { if (!max || max < 0 || max > 100) return false; var arr = []; for (var i = 0; i < 5; i++) arr[i] = Math.round(Math.random() * max); return

Теперь создадим новый HTML5 документ и обозначим там некоторые важные теги, в которых в дальнейшем будем писать скрипты:


<!doctype html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>Работа с D3</title> <script src="https://d3js.org/d3.v3.min.js"></script> <script> // ... функция генерации массива ... </script> </head> <body> <style> /* инлайн стили */ </style> <!-- пустой DIV #diagramm --> <div id="diagramm"></div> </body> <script> // ... скрипт D3 ... </script> </html>

Этот пустой документ, — заготовка для того, чтобы построить динамическую диаграмму случайных чисел, но в то же время на этом простом примере мы исследуем одну из трех возможных важных ситуации при работе с D3: реализация входного потока Enter на странице.

Ситуация входного потока Enter возникает тогда, когда элементов DOM меньше чем количество данных, или элементов DOM нет совсем, а данные есть. В таком случае из всего множества элементов выделяется подмножество элементов, которые необходимо добавить на страницу специальным для этого методом .append( )

Вставим скрипт генерации массива в наш документ вместо // ... функция генерации массива ... выше закрывающего тега head и сразу после функции сформируем данные: var randData = numRand(50);, случайные числа в диапазоне 0..50. В данный момент у нас есть пустой div id="diagramm", то есть в нем еще нет абсолютно никаких элементов DOM, но уже есть данные, переменная randData содержит массив случайных чисел!

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


.bar { display: inline-block; width: 20px; margin: 0 2px; padding: 3px; color: #fff }


Класс .bar задаст одинаковую ширину для столбцов, отступы и цвет шрифта внутри. Теперь, где фраза // ... скрипт D3 ... вставим скрипт, который осуществляет привязку данных массива и создает элементы DOM внутри div id="diagramm" методом .append( ):


var diagramm = d3.select("#diagramm"); // выбираем пустой div#diagramm diagramm .selectAll("span") // выбираем все дочерние элементы span; данных элементов нет-поэтому выборка является пустой .data(randData) // привязываем массив данных (случайные 5 чисел) с предыдущей выборкой .enter() // метод выделяет множество элементов, которые нужно добавить и которым соответствуют данные из массива - т.е данные, соответствующие всем эл-м массива .append("span") // добавляем <span></span> внутри div#diagramm .classed("bar", true) // присваиваем класс bar каждому элементу span .style("height", function (d, i) { return 3 * d + "px" }) // формируем высоту каждого span соответственно числовому значению из массива данных .style("background-color", function (d, i) { return "hsl(200,80%," + (50 - d / 2) + "%)"; }) // формируем цвет каждого span соответственно числовому значению из массива данных .text(String); // отображаем числовые значения внутри span

Метод .data() связывает данные из массива с выборкой, которую вернул метод .selectAll(). Здесь мы устанавливаем что вновь создаваемыми элементами будут span, но так как данных элементов в текущий момент времени нет, то мы резервируем место исходя из того, что каждому элементу из массива данных соответствует элемент DOM span. Метод .enter( ) тем временем выделяет подмножество добавляемых элементов (получится столько же элементов span, сколько элементов в массиве randData). Метод .append( ) непосредственно добавляет элементы в DOM.

Для того чтобы высота столбцов соответствовала числовым значениям массива, для каждого вновь добавленного элемента динамически формируется высота и цвет с помощью callback-функции function(d, i){...}, куда передается значение d и i, — это текущее значение элемента массива и его порядковый номер, а в теле функции мы возвращаем результат, например для высоты: { return 3*d+"px" }, я возвращаю утроенное значение текущего элемента массива, из за чего можно утверждать что максимальная высота столбца может быть 150px и минимальная 0. Аналогично можно обрабатывать цвет столбцов!

Множества Exit и Update

Выше мы рассмотрели ситуацию когда формируется множество входного потока Enter и реализовали простейшую вертикальную диаграмму основанную на добавлении элементов DOM, для которых есть данные в массиве. Но есть и противоположная ситуация: когда данных меньше чем элементов на странице, которым будут соответствовать эти данные, в таком случае такие элементы автоматически попадают в подмножество Exit и впоследствии удаляются методом .remove( ).

Ситуация выходного потока Exit возникает тогда, когда элементов DOM больше чем количество данных. В таком случае из всего множества элементов выделяется подмножество элементов, которым нет соответствия данным и которые необходимо удалить со страницы специальным для этого методом .remove( )

Множеству Update соответствуют элементы, для которых есть соответствующие элементы массива и есть элементы DOM, но значения которых изменились. Это ситуация находится между Enter и Exit.





Источник: zharikov

JS, D3
Читайте также:
Flexbox и как делать адаптивные сайты

Flexbox и как делать адаптивные сайты

Современный front-end разработчик активно должен уметь применять на практике различные инструменты, позволяющи...
Читать
Chrome, функции о которых вы не знали.

Chrome, функции о которых вы не знали.

Мы часто воспринимаем адресную строку браузера исключительно как место, куда мы вводим URL страницы, на которую мы хотим...
Читать
GetList вывод и фильтры инфоблоков

GetList вывод и фильтры инфоблоков

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