Графики, диаграммы, графы … и все это в веб? Часть 1

Материал из DOM

Перейти к: навигация, поиск

[править] Графики, диаграммы, графы … и все это в веб? Часть 1

Хотя в своей повседневной работе я часто сталкиваюсь с необходимостью программировать всевозможные отчеты, диаграммы, графики, но до не давних пор сфера моих интересов была смещена в сторону desktop standalone приложений. В редких случаях, когда возникала потребность в разработке программок работающих в браузере, я обходился встроенным в состав flex компонентом “графики”, исходные данные задавались в формате xml, визуализация была приятная и стильная – одним словом, меня все устраивало. И вот в очередном проекте мне пришлось углубится в детальное изучение различных подходов к созданию внедренных в веб-страницы графиков, диаграмм, оценить их плюсы, минусы. Так что, пока воспоминания свежи, спешу поделиться с вами.

Как строить графики в веб? Вариант 1: в общем случае графики, диаграммы разных форм и видов - это все картинки, которые могут быть отрисованы с помощью: php, java или любого серверного языка программирования - благо универсальные библиотеки рисующие 2d изображения появились давным-давно. Вариант 2: отрисовка выполняется с помощью activex-компонентов или java-апплетов. Вариант 3: использование возможностей svg графики или тега canvas и, наконец, flash/flex. Очевидно, что самый плохой вариант - 2. Несмотря на потенциальную возможность реализовать сколь угодно сложное поведение внедренного в страницу компонента, добавить анимацию, интерактивность - все это теряется на фоне слов “не универсально”, “activex-только для windows”, “вирусы и activex – дружная семья”, “java плагин огромен по размеру и есть не у всех”. Первый подход (с генерацией статической картинки серверным скриптом) - самый универсальный и независящий от возможностей клиента. Вы не зависите от версии браузера, наличия в нем flash-плагина, настроек корпоративного proxy-сервера, который может резать часть трафика – те же flash-ролики. В минусы идет потеря интерактивности: так по нажатию на какой либо столбец в диаграмме достаточно сложно реализовать некоторый скрипт, который меняет изображение, переходит к новой странице, масштабирует картинку и т.д. Есть проблемы и с поддержкой анимации: необходимо использовать посторонние библиотеки. Например для php встроенный модуль gd2 не умеет работать с анимированными gif, но вы можете использовать gifsicle (http://www.lcdf.org/gifsicle/) или http://www.phpclasses.org/browse/package/3163.html. В минусы можно записать еще и неэкономное расходование сетевого трафика в некоторых случаях. В своей статье (Про тег html canvas - 1) я приводил пример, когда при отрисовке графика функции z=f(x,y), большая часть получившегося изображения будет залита одним цветом фона и лишь небольшая часть картинки будет отображать линии графика функции, оси, подписи. Для эксперимента я создавал картинку размером 1024*768 с нарисованной линией синусоиды. Ее размеры в разных форматах были таковы: gif - 8 кб, jpeg - 33 кб, png - зашкалил за 210 кб. В тот раз я обосновывал необходимость переноса логики построения графики на сторону клиента, а ajax поможет подгружать в страницу нужные данные. Если вы еще не сталкивались с тегом canvas - то это особый тег позволяющий выполнять рисование внутри веб-страницы: линии, дуги, кривые безье, работа с другими изображениями, операции масштабирования или вращения и т.д. на javascript. Формат изображений svg - основанный на xml и внедряется в дерево dom веб-страницы, поддерживается различные фигуры, заливки, текст, анимация - и все это доступно для управления через javascript. Увы, но в internet explorer для поддержки этих двух стандартов нужны сторонние плагины, о которых массовый пользователь ничего не знает, и устанавливать не собирается - в отличие от flash-плагина, распространенного, известного и легко обновляемого. Так значит flash - самое то? Увы, не совсем и не всегда. Что хочет сделать клиент после того как посмотрел страницу? Сохранить или распечатать. Увы, но самый замечательный браузер в мире - internet explorer что 6, что 7 не умеет сохранять flash-ролики. А это значит, что как бы хорошо не вела себя в плане сохранения flash-контента opera или firefox - решение увы не универсально. К тому же если flash-ролик не содержит внутри себя набор данных необходимых для построения графика, а загружает их динамически с сервера, то сохранить такую страницу в принципе не возможно (тут я немного лукавлю, решение есть - но технически очень сложное). Та же проблема с сохранением страницы на диске верна и для ajax-основанных сайтов. Таким образом идеального решения нет. И как вывод: если вам нужно решение работающее железно всегда и везде, страницы сохраняются и печатаются - на сервере генерируется статическая картинка. Важна возможность создания интерактивных графиков, диаграмм - flash.

Разумеется, что за я не первый кто озаботился проблемой построения красивых и функциональных графиков, на рынке есть и платные и бесплатные библиотеки позволяющие автоматизировать процесс разработки диаграмм. Сегодня я расскажу об трех интересных продуктах sparkline, libchart, graphpite - это основанные на php решения для динамической генерации изображения. Разумеется что подобных библиотек достаточно много, например по следующему адресу находится целый каталог подобных скриптов: http://www.hotscripts.com/PHP/Scripts_and_Programs/Graphs_and_Charts/. В следующий раз я закрою тему, рассказав об библиотеках для flash. Если будет достаточное количество откликов, то я также расскажу и о задаче визуализации в вебе графов. Если вы никогда не сталкивались с тем как работать в php с графикой и библиотекой gd2, то милости прошу познакомиться с http://www.php5.ru/articles/image. По правде говоря, есть еще один подход к построению диаграмм - имитация с помощью чистого html (например, столбчатая диаграмма будет представлена в виде таблицы - смотрится не так ужасно, как звучит). Если вас это заинтересует, то http://www.webguys.com/pdavis/Programs/html_graphs/, а мы переходим к sparkline.

Sparkline - написанная на php библиотека для построения маленьких, “wordlike” графиков внедряемых в текст страницы. Домашний сайт проекта http://sparkline.org/. В качестве технических требований к хостингу - только наличие модуля gd2 - есть везде и давно, даже бесплатных хостингах. В настоящее время есть несколько портов данной библиотеки на java, perl, ruby, javascript-версия (как основа используется canvas). Например, вот пример кода для использования javascript-основанной версии:

<html>
  <style type="text/css">  
      .sparkline { display: none; }   
  </style>
  <script language="javascript" src="jspark.js"></script>
  <body> 
     Всю следующую неделю будет только холодать, только в конце недели выглянет солнце: 
     <span class="sparkline" style="width: 50px;">-2,-10,-8,-7,5,7</span>
  </body>
</html>

Все что мне нужно было сделать - это загрузить с сайта http://ejohn.org/projects/jspark/ файл библиотеки jspark.js, подключить ее к файлу html. В коде самой страницы я могу создавать произвольные теги с атрибутом class равным “sparkline ”. При загрузке страницы, содержимое этих тегов (если оно есть) будет автоматически заменено на динамически сгенерированный тег canvas (вы догадались, что это не работает в ie?), график же строится на основании чисел перечисленных внутри тега через запятую. Пример работы кода показан на рис. 1.

 Изображение:Wgraph_a_1.png

Смотрится, откровенно, не очень: для восприятия не хватает осей и опорных подписей значений – но все же, как удобно применять sparkline, например, в основанных на mediawiki сайтах, подготовке документации и только из-за этого можно простить все. Версия sparkline для php получше - прежде всего есть две версии sparkline: Sparkline_Bar и Sparkline_Line - диаграммы в виде набора столбиков (bar chart) и в виде линии соответственно. Я не привожу кода с примерами работы данной библиотеки поскольку ее php-версия достаточно сыра (я нашел пару багов практически не копая в глубину), и в терпеть ее недостатки по сравнению с libchart или graphpite просто не за что – данные уже не встраиваются внутрь hml-кода, а хранятся на сервере и вносятся внутрь объекта диаграммы с помощью функций setData (x, y) – схожий подход применяют и две следующие библиотеки, но качество кода там гораздо выше.

Теперь рассмотрим вторую библиотеку libchart (http://naku.dohcrew.com/libchart/pages/introduction/). Поддерживаются четыре вида диаграмм: VerticalBarChart, HorizontalBarChart, LineChart, PieChart. Методика работы традиционна: загружаете файлы библиотеки и подключаете их к своему проекту, затем создаете объект служащий для создания одной из тех самых четырех диаграмм. В качестве параметра конструктора следует указать размер будущего изображения и остается только наполнить диаграмму информацией. Для этого нам нужен еще один объект XYDataSet, метод addPoint которого как раз и служит для добавления очередной точки. XYDataSet привязывается к объекту диаграммы с помощью метода $chart->setDataSet($dataSet); и наконец мы задаем название диаграммы и говорим Render – создать изображение, как параметр задается имя файла, в который будет сохранено сформированное изображение, если же файл не задан, но результат в формате png будет отправлен сразу клиенту. Разве что вам придется явно указать тип отправляемого документа с помощью вызова header(что_мы_возвращаем). Интерес представляет также и возможность разместить на одной диаграмме несколько серий с данными (это не умеет делать sparkline). К сожалению совместить на одной картинке столбчатую диаграмму, линейную или круговую не возможно – столбчатые со столбчатыми, линейные с линейными, а круговые – вообще ни с чем. В примере ниже создаются диаграммы различных видов на основании случайных данных.

header ('Content-Type: image/png');
// задаем тип возвращемого документа
include "libchart/classes/libchart.php";
// подключаем библиотеку libchart
// создаем какую либо диаграмму
//$chart = new HorizontalBarChart(450, 250);
//$chart = new VerticalBarChart(450, 250);
//$chart = new VerticalBarChart(450, 250);
$chart = new LineChart(450, 250);
//создаем источник данных для диаграммы в виде набора серий
$dataSet = new XYSeriesDataSet();
// привязываем серии к диаграмме
$chart->setDataSet($dataSet);
 
// заполняем случайными данным диаграмму
for ($i = 0; $i < 3; $i++){
  $serie1 = new XYDataSet();
  for ($j = 0; $j < 5; $j++)
     $serie1->addPoint(new Point("User_" . $j, $i * 20 + rand(1, 10)));
  $dataSet->addSerie("Line # " . $i, $serie1);
}
// указываем название диаграммы
$chart->setTitle("Multi Series Demo");
// рисуем
$chart->render();

Я не зря в названии диаграммы и подписях к осям использовал латиницу – русские буквы превращаются в кракозябры – придется исправлять. Пример работы скрипта показан на рис. 2.

 Изображение:wgraph_a_2.PNG

В следующем примере я создаю круговую диаграмму и заодно подключаю собственные шрифты:

header ('Content-Type: image/png');
include "libchart/classes/libchart.php";
$chart = new PieChart(500, 300);
$dataSet = new XYDataSet();
$dataSet->addPoint(new Point(iconv('windows-1251', 'utf-8', 'Дельфины'), 40));
$dataSet->addPoint(new Point(iconv('windows-1251', 'utf-8', 'Касатки'), 15));
$dataSet->addPoint(new Point(iconv('windows-1251', 'utf-8', 'Акулы'), 65));
$chart->setDataSet($dataSet);
$chart->setTitle(iconv('windows-1251', 'utf-8', 'Обитатели моря'));
$chart->render();

Результат показа на рис. 3.

   Изображение:wgraph_a_3.PNG

Код почти аналогичен прошлому примеру, разве что перед выводом текста я выполнил его преобразование из исходной кодировки windows-1251 в кодировку utf-8 с помощью функции iconv. Для отрисовки текста используются два файла шрифта размещенные в подкаталоге libchart/fonts – естественно, вы можете заменить их на другие.

Самой же интересной и сложной является библиотека graPHPite, ее домашний сайт (http://graphpite.sourceforge.net/). Кроме отрисовки уже знакомых нам столбчатых, круговых и линейных диаграмм возможно создавать комбинированные диаграммы, например: векторную диаграмму, лепестковую, логарифмическую, step, impulse, диаграммы с областями, столбчатые диаграммы с накоплением и много другое. На диаграмму можно выводить фоновое изображение, выполнять заливки градиентом, создавать линии со сглаживанием. Интересен и подход с специализированными генераторами данных привязываемых к диаграмме. Например, есть объект генератор случайных чисел по заданным параметрам, есть генератор-функция, генератор функция-вектор, генератор на основании содержимого некоторого массива. Придумана и функция фильтров позволяющих сгенерированные с помощью некоторого генератора данные перед использованием немного “подрихотовать”, например, если генератор создает значения 0,10,20… то фильтр может превратить эти значения перед выводом в 0%, 10%, 20%,… Далее я приведу пример кода создающего совмещенную диаграмму: области + столбчатую. Результат работы показан на рис. 4.

   Изображение:wgraph_a_4.PNG
include("Image/Graph.php");
 // создаем объект диаграммы с заданными размерами
 $Graph =& new Image_Graph(400, 300);
 // создается область рисования внутри диаграммы
 $PlotArea =& $Graph->add(new Image_Graph_PlotArea());
 // создается источник данных - случайны набор чисел в отрезке от 2 до 15 и числом 10
 $DataSet =& new Image_Graph_Dataset_Random(10, 2, 15, true);
 // И создаем собственно график на основании области рисования и набора данных
 $Plot =& $PlotArea->addPlot(new Image_Graph_Plot_Area($DataSet));
 // задаем цвет линии
 $Plot->setLineColor(IMAGE_GRAPH_GRAY);
 // выполняем заливку синим цветом области 
 $BLUE =& $Graph->newColor(IMAGE_GRAPH_BLUE, 100);
 $Plot->setFillStyle($BLUE);
 // Создаем источник данных для второй диаграммы - тоже случайные данные
 $DataSet2 =& new Image_Graph_Dataset_Random(8, 30, 80, false);
 // СОздаем собственно диаграмму - столбчатую
 $Plot2 =& $PlotArea->addPlot(new Image_Graph_Plot_Bar($DataSet2));
 // Задаем параметры заливки цветом
 $ORANGE =& $Graph->newColor(IMAGE_GRAPH_ORANGE, 100);
 $Plot2->setFillStyle($ORANGE);
 // Указываем - отображать подписи к осям
 $AxisX =& $PlotArea->getAxis(IMAGE_GRAPH_AXIS_X); 
 $AxisY =& $PlotArea->getAxis(IMAGE_GRAPH_AXIS_Y); 
 // и рисовать стрелочки
 $AxisX->showArrow();
 $AxisY->showArrow();
 // Теперь можно указать маркеры для значений графика
 $Marker =& $Plot->add(new Image_Graph_Marker_Value(IMAGE_GRAPH_PCT_Y_MAX));
 // фоновый цвет будет белым
 $Marker->setFillColor(IMAGE_GRAPH_RED);
 // черная граница
 $Marker->setBorderColor(IMAGE_GRAPH_BLACK);
 // задаем форму маркера
 $PointingMarker =& $Plot->add(new Image_Graph_Marker_Pointing_Angular(20, $Marker));
 // и привязываем маркер к графику # 1
 $Plot->setMarker($PointingMarker); 
 // теперь форматируем значения текста для маркера
 $Marker->setDataPreProcessor(new Image_Graph_DataPreprocessor_Formatted("%0.1f%%"));
 // возможно указать легенду диаграммы с помощью пользовательских шрифтов 
 $Arial =& $Graph->addFont(new Image_Graph_Font_TTF("arial.ttf"));
 // задаются размеры шрифта
 $Arial->setSize(11);
 // И выводим заданную надпись заданным шрифтом, незабываем также выполнить преобразование в формат utf-8
 $Graph->add(new Image_Graph_Title(iconv("windows-1251", "utf-8", "Пример диаграммы с областями и столбиками"), $Arial));
 // строим график и отправляем его клиенту
 $Graph->done();

Возможности библиотеки безграничны. Перспективы – неясны. Последняя версия 1.2.1 была выпущена еще пару лет назад и какого-то целенаправленного развития я не заметил, с другой стороны я сделал на основе этой библиотеки большой проект, багов не обнаружил и крайне доволен.

Subscribe Now!

 

ObMachine projects & articles (java, flash, flex, php, ...)  -- black-zorro.com