базовый курс

ГРУППА КУРСА

Рисование графиков

Построение графиков на PHP возможно двумя способами. Можно нарисовать самостоятельно или использовать какую-нибудь библиотеку. Я предлагаю научиться создавать графики самим. Во-первых, потому что хорошие библиотеки являются платными. Во-вторых, используя любую библиотеку, вы ограничены её функционалом. Лучше рисовать графики самостоятельно.

Для начала рассмотрим самый простой вариант. Есть массив, содержащий количество пользователей, которые зарегистрировались на сайте за 5 дней.

3
$mas = array(2, 7, 20, 9, 14);

Выведем этот массив на страницу в виде графика. Сначала нарисуем оси координат.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
header('Content-type: image/png');
$image = imageCreateTrueColor(600, 280);
$white = imageColorAllocate($image, 255, 255, 255);
imagefilledrectangle($image, 0, 0, 599, 279, $white);
$blue = imageColorAllocate($image, 0, 0, 255);
imageLine($image, 50, 250, 570, 250, $blue);
imageLine($image, 50, 50, 50, 250, $blue);
imageLine($image, 150, 246, 150, 249, $blue);
imageLine($image, 250, 246, 250, 249, $blue);
imageLine($image, 350, 246, 350, 249, $blue);
imageLine($image, 450, 246, 450, 249, $blue);
imageLine($image, 550, 246, 550, 249, $blue);
imageString($image, 5, 145, 255, '1', $blue);
imageString($image, 5, 245, 255, '2', $blue);
imageString($image, 5, 345, 255, '3', $blue);
imageString($image, 5, 445, 255, '4', $blue);
imageString($image, 5, 545, 255, '5', $blue);
imageLine($image, 51, 160, 54, 160, $blue);
imageLine($image, 51, 60, 54, 60, $blue);
imageString($image, 5, 30, 155, '10', $blue);
imageString($image, 5, 30, 55, '20', $blue);
imagepng($image);
imagedestroy($image);
?>

Комментарии:

3 - создаём изображение

5 - фон

7, 8 - оси x и y

9-13 - засечки на оси x

14-18 - цифры на оси x

19, 20 - засечки на оси y

21, 22 - цифры на оси y

Рисунок должен выглядеть так:

Оси графика PHP

Чтобы нарисовать линию на графике, нужно найти на нём точки, по которым линия будет строиться. На практике можно сразу находить точку и рисовать по ней линию. Но чтобы пример был более понятным, мы разделим задачу на две части. Сначала найдём все точки, потом нарисуем по ним график. Для этого создадим двухмерный массив, в котором будут координаты x и y каждой точки. Для поиска координат нам нужна точка отсчёта. Это та точка, в которой пересекаются оси координат. В нашем рисунке она имеет координаты x: 50, y: 250 пикселей. Для определения положения каждой точки нужно использовать масштаб графика. В примере мы задали масштаб сами. По оси x для каждого дня создано 100 пикселей. Первый день находится на расстоянии 100 пикселей от начала отсчёта, второй на расстоянии 200 пикселей и так далее. Значит координата x для первого дня равна 50 + 100 = 150. Для второго 50 + 200 = 250.

По оси y для каждых 10 пользователей выделено 100 пикселей. Значит для одного пользователя - 10 пикселей. Нужно учитывать, что у изображения ось y направлена вниз, значит нужно не прибавлять значение к точке отсчёта, а вычитать. В первый день на сайте зарегистрировались 2 посетителя. Координата для этого значения равна 250 - (2 * 10) = 230. Для второго дня 250 - (7 * 10) = 180. Таким образом, массив с координатами точек получается такой:

23
24
25
26
27
28
29
30
$mas = array(2, 7, 20, 9, 14);
$points = array(
  array ('x' => 150, 'y' => 230),
  array ('x' => 250, 'y' => 180),
  array ('x' => 350, 'y' => 50),
  array ('x' => 450, 'y' => 160),
  array ('x' => 550, 'y' => 90)
  );

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

32
33
34
35
36
37
$green = imageColorAllocate($image, 50, 237, 35); //цвет графика
imageSetThickness($image, 2); //толщина линий
$num_points = count($points);
for ($i=0; $i<=$num_points-2; $i++)
  {
  imageLine($image, $points[$i]['x'], $points[$i]['y'], $points[$i+1]['x'], $points[$i+1]['y'], $green);
  }

График выглядит так:

График

Мы рассмотрели самый простой вариант построения графика. Я заранее знал количество дней и максимальное число пользователей. Поэтому я сам выбрал масштаб графика. Но обычно эти данные могут быть разными и масштаб нужно расчитывать. И сам график выглядит немного иначе. Нам привычно, что оси координат начинаются с нуля. Но для удобства отображения это правило иногда не соблюдается. Ось x начинается не с 0, а с 1, чтобы не слева от линии не было пустого пространства. А ось y показывается так, чтобы на график поместились все значения. Например, если минимальное значение - 60, а максимальное - 70, то ось y содержит именно такой диапазон. Рассмотрим создание такого графика.

В примере ось x имеет длину 570 пикселей. Её нужно разделить на одинаковые сегменты для каждого дня. Ось x начинаться не с 0, а с 1. Поэтому сегмента между 0 и 1 не будет и количество сегментов становится на 1 меньше. Если дней 5, то получится 4 сегмента. Формула расчёта длины сегмента такая:

длина = длина оси / (число дней - 1)

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

2
3
4
5
header('Content-type: image/png');
$regusers = array(2, 7, 20, 9, 14);
$segments_x = count($regusers); //количество сегментов
$lengthsegment_x = floor((570-50)/($segmentx_x - 1)); //длина сегмента

570 - 50 это длина оси x. Она зависит от размеров изображения.

Ось y делаем такой, чтобы поместились все значения. Нужно получить минимальное и максимальное значения. Они должны занять всю высоту графика. Зная диапазон, мы сможем рассчитать, сколько пикселей должно соответствовать одному пользователю на графике.

диапазон = максимальное - минимальное

длина = длина оси / диапазон

Полученное число также нужно округлить.

6
7
8
$max = max($regusers);
$min = min($regusers);
$lengthsegment_y = floor((250-50)/($max - $min));

Сами оси рисуем, как в предыдущем примере:

9
10
11
12
13
14
$image = imageCreateTrueColor(600, 280);
$white = imageColorAllocate($image, 255, 255, 255);
imagefilledrectangle($image, 0, 0, 599, 279, $white);
$blue = imageColorAllocate($image, 0, 0, 255);
imageLine($image, 50, 250, 570, 250, $blue);
imageLine($image, 50, 50, 50, 250, $blue);

Поставим засечки и напишем числа:

15
16
17
18
19
20
21
for ($i=1; $i<=5; $i++)
  {
  $x = 50 + $lengthsegment_x * ($i - 1);
  if ($i>1)
  imageLine($image, $x, 246, $x, 249, $blue);
  imagestring($image, 5, $x-1, 255, "$i", $blue);
  }

Комментарии:

15 - цикл для каждого дня. Нулевого значения не будет

17 - координата x для засечки. 50 - это точка отсчёта

18 - для первого дня засечки не нужно, он находится в начале оси x

19 - рисуем засечку

20 - пишем цифру

На оси y не будем ставить засечки для каждого сегмента. Поставим на максимуме и в середине:

22
23
24
25
26
27
28
29
30
31
$y = 250 - $lengthsegment_y * ($max - $min);
imageLine($image, 51, $y, 54, $y, $blue);
$num = $max;
imageString($image, 5, 30, $y-5, "$num", $blue);
$y = 250 - $lengthsegment_y * floor(($max-$min) / 2);
imageLine($image, 51, $y, 54, $y, $blue);
$num = $min + floor(($max - $min) / 2);
imageString($image, 5, 30, $y-5, "$num", $blue);
$num = $min;
imageString($image, 5, 30, 241, "$num", $blue);

Комментарии:

22 - координата y для засечки. 250 - это точка отсчёта. У изображений ось y направлена вниз, поэтому не прибавляем к точке отсчёта, а вычитаем. При расчёте координаты y всегда будем отнимать минимальное значение в массиве. Тогда график опуститься вниз, и минимальное значение на графике совпадёт с точкой отсчёта

23 - рисуем засечку

24 - число, которое нужно написать

25 - пишем число

Остальные цифры ставятся таким же способом.

Строим график. Координаты точек, по которым будем рисовать линии, определяются так:

x = точка отсчёта + номер дня * длина сегмента

y = точка отсчёта - (количество пользователей - минимальное значение) * длина сегмента

Запустим цикл, в котором соединим соседние точки:

32
33
34
35
36
37
38
39
40
41
42
43
44
$green = imageColorAllocate($image, 50, 237, 35);
imageSetThickness($image, 2);
for ($i=0; $i<=$segments_x-1; $i++)
  {
  $x1 = 50 + $i * $lengthsegment_x;
  $y1 = 250 - ($regusers[$i] - $min) * $lengthsegment_y;
  if ($i>0)
  imageLine($image, $x1, $y1, $x2, $y2, $green);
  $x2 = $x1;
  $y2 = $y1;
  }
imagepng($image);
imagedestroy($image);

Результат должен выглядеть так:

Рисование графика