Журнал LinuxFormat - перейти на главную

LXF116:JpGraph

Материал из Linuxformat
Перейти к: навигация, поиск
JpGraph Добавьте графики в свои PHP-сценарии!

Содержание

Графики и диаграммы

ЧАСТЬ 2 Не нашли ничего по душе в Сравнении этого месяца? Не расстраивайтесь – Никита Шультайс покажет, как создавать диаграммы Ганта из PHP, и вы мигом нарисуете себе нужную функциональность.

В прошлый раз мы познакомились с возможностями PHP-библиотеки JpGraph по части создания линейных графиков и гистограмм на примере посещаемости воображаемого web-сайта. Излишне говорить, что этим возможности JpGraph не исчерпываются. Наш сегодняшний урок мы начнем с круговых диаграмм. Мы уже предоставили посетителям сведения о годовой и суточной посещаемости, но будущим рекламодателям может быть мало и этого: их интересуют демографические данные, например, возраст и пол пользователей. Ну что же, не будем их разочаровывать и добавим следующий код:

  1 include ( "./jpgraph/src/jpgraph.php");
  2 include ("./jpgraph/src/jpgraph_pie.php");
  3
  4 $data = array(2.2, 7.5, 26.5, 31, 15.4, 11.8, 7.6);
  5
  6 $graph = new PieGraph(600,400);
  7 $graph->SetShadow();
  8
  9 $graph->title->SetFont(FF_VERDANA,FS_NORMAL);
  10 $graph->title->Set('Демография сайта (возраст посетителей)');
  11 $graph->legend->SetFont(FF_VERDANA,FS_NORMAL);
  12
  13 $plot = new PiePlot($data);
  14 $legends = array('младше 12','12-18','19-24','25-30','31-35','36-40','старше 40');
  15 $plot->SetLegends($legends);
  16 $plot->SetCenter(0.35);
  17
  18 $graph->Add($plot);
  19 $graph->Stroke();


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

  • по умолчанию, данные на диаграмме высчитываются в процентах;
  • цвета для каждой из частей диаграммы подбираются автоматически;
  • первая секция отсчитывается от нулевого градуса (3 часа на циферблате).

Мы всегда можем изменить эти установки. Например, метод:

$plot->SetStartAngle(90);

повернет нашу диаграмму на 90 градусов против часовой стрелки, и первая секция будет отсчитываться от 12 часов. Нумерация секций также идет против часовой стрелки. Будьте аккуратны с этим методом, так как он может неверно рассчитать смещение числовых подписей к секциям, и диаграмма станет непонятной. Да и потом будут возникать проблемы в отображении при применении других функций.

Вызов $plot->SetColor('red'); покрасит линии вокруг секций в красный цвет (по умолчанию установлен черный). Чтобы добиться оптимального размера диаграммы, вы можете поиграть с SetSize(): здесь мы используем значение 0.35:

$plot->SetSize(0.35);

Вы также можете вручную задавать цвет каждой из секций – для этого добавьте в код следующие строки:

$colors = array('black','white','red','green','blue','yellow','pink');
$plot->SetSliceColors($colors);

JpGraph позволяет отображать и круговые 3D-диаграммы. Для этого нужно подключить модуль JpGraph_pie3d.php:

include ("./jpgraph/src/jpgraph_pie3d.php");

а затем заменить строку 13 на

$plot = new PiePlot3D($data);

У 3D-диаграмм есть дополнительный метод:

$plot->SetAngle(50);

принимающий значения от 5 до 85 и отвечающий за управление перспективой. Не путайте его с методом SetStartAngle(), который мы разбирали выше.

Часто бывает нужно выделить из диаграммы определенный сегмент, например, отвечающий за посетителей в возрасте от 19 до 24 лет. Для этого можно применить метод

ExplodeSlice()
$plot->ExplodeSlice(2);

принимающий в качестве аргумента номер элемента массива (не забывайте, что PHP отсчитывает их от нуля). Если вызвать $plot->ExplodeAll(); то все сегменты диаграммы будут отделены друг от друга, а если вы хотите выделить несколько сегментов, да еще и задать величину отступа, то используйте метод Explode(), например:

$plot->Explode(array(0,0,20,0,20,0,0));

Последним методом, который мы удостоим своим вниманием в этом разделе, будет SetLabelPos(), который, по возможности, старается разместить данные внутри секций:

$plot->SetLabelPos(0.6);

Карты изображений

Помимо построения диаграмм, JpGraph может генерировать карты изображений, позволяющие сделать части картинки ссылками на разные ресурсы. Чтобы назначить такую карту, нужно сначала определить адреса для каждой из секций:

 $url1 = ‘http://mysite.ru/1.html’;
 $url2 = ‘http://mysite.ru/2.html’;
 ...
 $url7 = ‘http://mysite.ru/7.html’;
 $targetarray = array($url1, $url2, $url3, $url4, $url5, $url6, $url7);

а затем добавить их на диаграмму:

 $plot->SetCSIMTargets($targetarray);

и заменить строку

 $graph->Stroke();

строкой

 $graph->StrokeCSIM();

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

Диаграммы Ганта

Если по-простому, диаграммы Ганта – это графическое представление задач и ресурсов какого-либо проекта во времени. Мы всегда можем узнать, какие задачи выполняются сейчас, какие уже выполнены и за какие предстоит взяться в будущем. С помощью диаграмм Ганта менеджер может контролировать ход выполнения проекта и вносить коррективы, если заметно отставание от плана, а также грамотно распределять ресурсы для решения разных задач. Как вы уже знаете (LXF115), JpGraph содержит модуль для построения диаграмм Ганта. Он позволяет:

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

...и так далее.

По традиции, начнем сразу с примера:

  1 <?php
  2 include ( "./jpgraph/src/jpgraph.php");
  3 include ("./jpgraph/src/jpgraph_gantt.php");
  4
  5 $graph = new GanttGraph(-1,-1);
  ...
  11 $graph->scale->SetDateLocale("ru_RU.UTF-8");
  12
  13 $graph->ShowHeaders( GANTT_HDAY | GANTT_HWEEK | GANTT_HMONTH);
  14
  15 $graph->scale->month->SetFont(FF_VERDANA,FS_NORMAL,10);
  16 $graph->scale->month->SetStyle(MONTHSTYLE_SHORTNAMEYEAR2);
  17 $graph->scale->month->SetBackgroundColor("blue");
  18
  19 $graph->scale->week->SetStyle(WEEKSTYLE_FIRSTDAY);
  20 $graph->scale->week->SetFont(FF_VERDANA,FS_NORMAL,10);
  21
  22 $graph->scale->day->SetStyle(DAYSTYLE_SHORTDATE4);
  23 $graph->scale->day->SetFont(FF_VERDANA,FS_NORMAL,10);
  24
  25 $task1 = new GanttBar(0,"Проектирование", "11.01.2009", "25.01.2009");
  26 $task1->SetPattern(BAND_RDIAG, "yellow");
  27 $task1->SetHeight(12);
  28 $task1->SetFillColor("red");
  29 $task1->title->SetFont(FF_VERDANA,FS_NORMAL,10);
  30 $task1->title->SetColor("red");
  31
  32 $task2 = new GanttBar(1,"Программирование", "25.01.2009", "03.02.2009");
  ...
  35 $task3 = new GanttBar(2,"Тестирование", "03.02.2009", "06.02.2009");
  …
  38 $graph->Add($task1);
  39 $graph->Add($task2);
  40 $graph->Add($task3);
  43 ?>

Многие части программы вам уже знакомы, поэтому здесь мы обратим внимание на новинки – полный текст сценария находится на LXFDVD. Первое, что бросается в глаза – значения ширины и высоты, переданные в конструктор GanttGraph(), равны -1, что позволяет вычислять их автоматически. В строке 11 задается локаль нашей системы, чтобы дата и время выводились по-русски. Это очень ответственное место, ведь если вы неправильно укажете название вашей локали, то в лучшем случае получите «крякозябры», а в худшем – график просто не будет создан. Чтобы не ошибиться, наберите в консоли locale и скопируйте полученное значение в исходник. В строке 13 мы перечисляем, какие заголовки должны отображаться в диаграмме: дни, недели и месяцы, то есть при просмотре мы всегда сможем определить время выполнения с точностью до дней и увидеть, на сколько недель или месяцев растянулась та или иная задача.

В строке 16 задается стиль масштабирования для месяцев. Здесь доступно несколько вариантов: MONTHSTYLE_SHORTNAME (только краткое название), MONTHSTYLE_LONGNAME (полное название), MONTHSTYLE_FIRSTLETTER (первая буква) и несколько комбинированных форматов: MONTHSTYLE_SHORTNAMEYEAR4, MONTHSTYLE_LONGNAMEYEAR2, MONTHSTYLE_LONGNAMEYEAR4. Аналогично, в строке 19 указывается стиль масштабирования для недель. Значение WEEKSTYLE_FIRSTDAY выводит дату первого дня недели в формате ДД/ММ, где ДД – день, а ММ – месяц. На выбор представлено еще несколько параметров: WEEKSTYLE_FIRSTDAY2 – дата первого дня недели в формате «01 Янв», WEEKSTYLE_WNBR – номер недели с начала года, и два комбинированных формата, с которыми вы без труда разберетесь: WEEKSTYLE_FIRSTDAYWNBR и WEEKSTYLE_FIRSTDAY2WNBR.

Определение формата вывода дня недели находится в строке 22. Помимо DAYSTYLE_SHORTDATE4 (номер дня в месяце), можно использовать:

  • DAYSTYLE_ONELETTER За день недели отвечает только одна буква, например, «П» для понедельника (иногда работает некорректно).
  • DAYSTYLE_LONG Полное название дня недели, например, «Понедельник».
  • DAYSTYLE_LONGDAYDATE1 День и дата: «Понедельник 23 Июня».
  • DAYSTYLE_SHORT Краткое название дня, например, «Пн.», а также некоторые другие.

Кроме определения параметров масштабирования, отображение дней можно настроить с помощью дополнительных методов:

 $graph->scale->day->SetWeekendColor('green'); // устанавливаем цвет столбцов для выходных
 $graph->scale->day->SetSundayFontColor('blue'); // устанавливаем цвет шрифта для формата вывода воскресенья

а с помощью

 $graph->scale->UseWeekendBackground(false);

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


В строке 25 мы создаем первую задачу. Первый параметр (0), переданный в конструктор, отвечает за ее порядковый номер. Как и массивы, задачи нумеруются с нуля, и чем больше номер, тем ниже расположена задача на диаграмме. Следующий параметр отвечает за название задачи, затем идут даты начала и окончания. Параметры отображения диаграммы приведены в строках 26–30. В строках 32 и 35 мы создаем еще две задачи, присваивая им соответственно порядковые номера 1 и 2 (имеются в виду номера, которые передаются в конструктор, а не приписываются к названиям переменных). В строках 38–40 задачи помещаются на график.

Посмотреть, что у нас получилось, можно на рис. 2. Взгляните также на экранные снимки в Сравнении этого месяца – по-моему, вышло не хуже.

Расширяем диаграммы

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

 $vline = new GanttVLine("28.01.2009", "Сегодня", "darkred", 4, "solid");
 $vline->title->SetFont(FF_VERDANA,FS_NORMAL,10);
 $graph->Add($vline);

В конструктор GanttVLine() передаются: дата, сноска, цвет и толщина линии, а также стиль отображения (solid – непрерывная линия, dashed – пунктир, dotted – точки). Вертикальные линии могут использоваться не только для отметки текущего момента, но и для разметки различных фаз, которые могут охватывать сразу несколько задач.

Помимо отображения задач, выполняющихся во времени, на диаграмму можно наносить «одноразовые» действия: например, промежуточные отчеты перед начальством или утверждения дизайна заказчиком. Это делает следующий кусок кода:

 $milestone = new MileStone(3,"Подготовка сервера","2009-01-31","(2009-01-31)" );
 $milestone->mark->SetType(MARK_DTRIANGLE);
 $milestone->title->SetFont(FF_VERDANA,FS_NORMAL,10);
 $milestone->title->SetColor("black");
 $milestone->caption->SetFont(FF_VERDANA,FS_NORMAL,10);
 $milestone->caption->SetCo"lor("black");
 $graph->Add($milestone);

Обратите внимание, что в качестве первого параметра в конструктор передается 3 – порядковый номер задачи на диаграмме. Остальные опции просты и не требуют дополнительных разъяснений.

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

 $task1->progress->Set(1.0);
 $task1->progress->SetPattern( BAND_RDIAG, "blue");
 $task2->progress->Set(0.2);
 $task2->progress->SetPattern( BAND_RDIAG, "blue");

В метод Set() передается число от 0 (задача еще не начинала выполняться) до 1 (задача выполнена на 100%). На нашей диаграмме мы отстаем от плана на задаче «программирование».

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

а) Определить интервал времени, на который будет проецироваться диаграмма:

 $graph->SetDateRange("22.01.2009", "03.02.2009");

б) Дать каждой задаче описание, чтобы всегда видеть, когда она началась, а когда – заканчивается. Для этого нужно добавить дополнительные поля

 $graph->scale->actinfo->SetColTitles(array('Задача' , 'Начало','Конец'),array( 30,100));

и заменить в конструкторе GanttBar название задачи на массив, в котором каждый элемент соответствует определенным выше полям:

 $task1 = new GanttBar(0, array("Проектирование",'11.01.2009','25.01.2009'), "11.01.2009", "25.01.2009");

Осталось лишь придать дополнительным полям приятный вид:

 $graph->scale->actinfo->SetFont(FF_VERDANA,FS_NORMAL,10);
 $graph->scale->actinfo->SetBackgroundColor('#E0DBFF');
 $graph->scale-> actinfo->vgrid->SetStyle ('solid');
 $graph->scale-> actinfo->vgrid->SetColor ('gray');

и добавить горизонтальную «зебру»:

$graph->hgrid->Show();
$graph->hgrid->SetRowFillColor('darkblue@0.9');

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

$task1->SetConstrain(1,CONSTRAIN_ENDSTART);

Это значит, что по завершении задачи $task1 начнет выполняться задача с порядковым номером 1 (у нас это $task2). Конечно, данный пример наигран, да и вообще – многие зависимости в разработке программ очевидны, поэтому лучше тратить время не на их рисование, а на написание кода: мы ведь и так отстаем от графика.

Рис 3.

Рис. 3. Пара дополнительных строк кода — и ваша диаграмма выглядит не хуже, чем в MS Project.

Любителям физики

В прошлый раз мы посмотрели, как JpGraph может помочь математикам в построении графиков функций; а сегодня поможем физикам. Если сильно раскачать качели и оставить их на произвол судьбы, то они постепенно остановятся. Это происходит потому, что все колебания, которым мы не помогаем, рано или поздно затухают. Особую ценность представляет визуализация таких колебаний.

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

LXF116 83 1.jpg

где А – амплитуда (то, насколько сильно качели отклонятся от положения равновесия), А0 – начальная амплитуда (отклонение в тот момент, когда мы перестали раскачивать качели), e – константа Эйлера, равная 2,71828..., k – коэффициент затухания (чем он больше, тем быстрее остановятся качели), t – время, которое прошло с момента начала самостоятельных колебаний, ω – угловая частота и ϕ – начальная фаза колебаний. Теперь, когда у нас есть формула и JpGraph, можно пробовать построить график затухающих колебаний:


 1 <?php
 2 include ( "./jpgraph/src/jpgraph.php");
 3 include ("./jpgraph/src/jpgraph_scatter.php");
 4
 5 $numpoints=50;
 6 $k=0.05;
 7
 8 for($t=0; $t<$numpoints; ++$t) {
 9    $datay[$t]=exp(-$k*$t)*cos(M_PI/10*$t);
 10 }
 11
 12 $graph = new Graph(400,200);
 13 $graph->SetScale("intlin");
 14 $graph->SetShadow();
 15 $graph->SetBox();
 16
 17 $graph->title->Set("Затухающие колебания");
 18 $graph->title->SetFont(FF_VERDANA,FS_NORMAL);
 19
 20 $graph->xaxis->SetPos("min");
 21
 22 $graph->yaxis->SetLabelMargin(12);
 23 $graph->xaxis->SetLabelMargin(6);
 24 $graph->yaxis->SetTickSide(SIDE_LEFT);
 25 $graph->xaxis->SetTickSide(SIDE_DOWN);
 26
 27 $sp1 = new ScatterPlot($datay);
 28 $sp1->mark->SetType(MARK_SQUARE);
 29 $sp1->mark->SetFillColor("red");
 30 $sp1->SetImpuls();
 31 $sp1->SetColor("blue");
 32 $sp1->SetWeight(1);
 33 $sp1->mark->SetWidth(3);
 34
 35 $graph->Add($sp1);
 36
 37 $graph->Stroke();
 38 ?>

В строке 3 мы подключаем библиотеку для создания графиков разбросов точечных значений, а затем задаем число точек и коэффициент затухания k (строки 5 и 6 соответственно). Можете изменять значения переменной k, чтобы посмотреть, как ведут себя колебания. В строках 8–11 вычисляются значения по оси y или, иными словами, амплитуды в определенные моменты времени, ведь в строке 9 записана формула затухающих колебаний, только на PHP. Далее идет создание графика и вызывается новый для нас метод SetBox(), который добавляет рамку вокруг области, в которой строится график. В строке 20 мы устанавливаем ось x внизу, а не посередине, как это задано по умолчанию. В строках 22–25 определяется величина отступов и направления отметок значений на осях. И начиная с 27 строки, создается график, который помещается на изображение в строке 35.


Я уже упомянул, что мы подключаем библиотеку для создания графиков разбросов точек: обычно этот модуль применяется для определения зон, в которых какое-нибудь событие происходило наиболее часто. Если мы будем бросать мяч с одной силой, под одним углом и в одну сторону, то он будет падать примерно в одно и то же место, и только иногда значения будут отклоняться, например, из-за неожиданного порыва ветра (рис. 5).

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

На этом изучение бесплатной (и свободной!) части библиотеки JpGraph можно считать законченным. Покупку и исследование возможностей профессиональной версии в части печати штрихкодов оставляем заинтересованному читателю в качестве домашнего задания. LXF

Персональные инструменты
купить
подписаться
Яндекс.Метрика