Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Построение графика

В этом уроке мы рассмотрим пример построения графика по заданному множеству значений. Размер графика и размер подписи будет зависеть от размера изображения. (см. рисунки).

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

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

r[x]=(v[x]+v[x-1]+v[x-2]+v[x+1]+v[x+2])/5

Для сглаживания точек в краях графика нам придется добавить по два дополнительных значения:

v[-1]=v[0];
v[-2]=v[0];
v[count+1]=v[count];
v[count+2]=v[count];

где, count - количество точек на графике

Пример 32: Построение графика.

<?
// Задаем входные данные ############################################

// Входные данные - три ряда, содержащие случайные данные.
// Деление на 2 и 3 взято для того чтобы передние ряды не 
// пересекались

// Массив $DATA["x"] содержит подписи по оси "X"

$DATA=Array();
for ($i=0;$i<20;$i++) {
    $DATA[0][]=rand(0,100);
    $DATA[1][]=rand(0,100)/2;
    $DATA[2][]=rand(0,100)/3;
    $DATA["x"][]=$i;
    }

// Задаем изменяемые значения #######################################

// Размер изображения

$W=16384;
$H=8192;

// Отступы
$MB=20;  // Нижний
$ML=8;   // Левый 
$M=5;    // Верхний и правый отступы.
         // Они меньше, так как там нет текста

// Ширина одного символа
$LW=imagefontwidth(2);

// Подсчитаем количество элементов (точек) на графике
$count=count($DATA[0]);
if (count($DATA[1])>$count) $count=count($DATA[1]);
if (count($DATA[2])>$count) $count=count($DATA[2]);

if ($count==0) $count=1;

// Сглаживаем графики ###############################################
if ($_GET["smooth"]==1) {

    // Добавим по две точки справа и слева от графиков. Значения в
    // этих точках примем равными крайним. Например, точка если
    // y[0]=16 и y[n]=17, то y[1]=16 и y[-2]=16 и y[n+1]=17 и y[n+2]=17

    // Такое добавление точек необходимо для сглаживания точек
    // в краях графика

    for ($j=0;$j<3;$j++) {
        $DATA[$j][-1]=$DATA[$j][-2]=$DATA[$j][0];
        $DATA[$j][$count]=$DATA[$j][$count+1]=$DATA[$j][$count-1];
        }

    // Сглаживание графики методом усреднения соседних значений

    for ($i=0;$i<$count;$i++) {
        for ($j=0;$j<3;$j++) {
            $DATA[$j][$i]=($DATA[$j][$i-1]+$DATA[$j][$i-2]+
                           $DATA[$j][$i]+$DATA[$j][$i+1]+
                           $DATA[$j][$i+2])/5;
            }
        }
    }


// Подсчитаем максимальное значение
$max=0;

for ($i=0;$i<$count;$i++) {
    $max=$max<$DATA[0][$i]?$DATA[0][$i]:$max;
    $max=$max<$DATA[1][$i]?$DATA[1][$i]:$max;
    $max=$max<$DATA[2][$i]?$DATA[2][$i]:$max;
    }

// Увеличим максимальное значение на 10% (для того, чтобы столбик
// соответствующий максимальному значение не упирался в в границу
// графика
$max=intval($max+($max/10));

// Количество подписей и горизонтальных линий
// сетки по оси Y.
$county=10;

// Работа с изображением ############################################

// Создадим изображение
$im=imagecreate($W,$H);

// Цвет фона (белый)
$bg[0]=imagecolorallocate($im,255,255,255);

// Цвет задней грани графика (светло-серый)
$bg[1]=imagecolorallocate($im,231,231,231);

// Цвет левой грани графика (серый)
$bg[2]=imagecolorallocate($im,212,212,212);

// Цвет сетки (серый, темнее)
$c=imagecolorallocate($im,184,184,184);

// Цвет текста (темно-серый)
$text=imagecolorallocate($im,136,136,136);

// Цвета для линий графиков
$bar[2]=imagecolorallocate($im,191,65,170);
$bar[0]=imagecolorallocate($im,161,155,0);
$bar[1]=imagecolorallocate($im,65,170,191);

$text_width=0;
// Вывод подписей по оси Y
for ($i=1;$i<=$county;$i++) {
    $strl=strlen(($max/$county)*$i)*$LW;
    if ($strl>$text_width) $text_width=$strl;
    }

// Подравняем левую границу с учетом ширины подписей по оси Y
$ML+=$text_width;

// Посчитаем реальные размеры графика (за вычетом подписей и
// отступов)
$RW=$W-$ML-$M;
$RH=$H-$MB-$M;

// Посчитаем координаты нуля
$X0=$ML;
$Y0=$H-$MB;

$step=$RH/$county;

// Вывод главной рамки графика
imagefilledrectangle($im, $X0, $Y0-$RH, $X0+$RW, $Y0, $bg[1]);
imagerectangle($im, $X0, $Y0, $X0+$RW, $Y0-$RH, $c);

// Вывод сетки по оси Y
for ($i=1;$i<=$county;$i++) {
    $y=$Y0-$step*$i;
    imageline($im,$X0,$y,$X0+$RW,$y,$c);
    imageline($im,$X0,$y,$X0-($ML-$text_width)/4,$y,$text);
    }

// Вывод сетки по оси X
// Вывод изменяемой сетки
for ($i=0;$i<$count;$i++) {
    imageline($im,$X0+$i*($RW/$count),$Y0,$X0+$i*($RW/$count),$Y0,$c);
    imageline($im,$X0+$i*($RW/$count),$Y0,$X0+$i*($RW/$count),$Y0-$RH,$c);
    }

// Вывод линий графика
$dx=($RW/$count)/2;

$pi=$Y0-($RH/$max*$DATA[0][0]);
$po=$Y0-($RH/$max*$DATA[1][0]);
$pu=$Y0-($RH/$max*$DATA[2][0]);
$px=intval($X0+$dx);

for ($i=1;$i<$count;$i++) {
    $x=intval($X0+$i*($RW/$count)+$dx);

    $y=$Y0-($RH/$max*$DATA[0][$i]);
    imageline($im,$px,$pi,$x,$y,$bar[0]);
    $pi=$y;

    $y=$Y0-($RH/$max*$DATA[1][$i]);
    imageline($im,$px,$po,$x,$y,$bar[1]);
    $po=$y;

    $y=$Y0-($RH/$max*$DATA[2][$i]);
    imageline($im,$px,$pu,$x,$y,$bar[2]);
    $pu=$y;
    $px=$x;
    }

// Уменьшение и пересчет координат
$ML-=$text_width;

// Вывод подписей по оси Y
for ($i=1;$i<=$county;$i++) {
    $str=($max/$county)*$i;
    imagestring($im,2, $X0-strlen($str)*$LW-$ML/4-2,$Y0-$step*$i-
                       imagefontheight(2)/2,$str,$text);
    }

// Вывод подписей по оси X
$prev=100000;
$twidth=$LW*strlen($DATA["x"][0])+6;
$i=$X0+$RW;

while ($i>$X0) {
    if ($prev-$twidth>$i) {
        $drawx=$i-($RW/$count)/2;
        if ($drawx>$X0) {
            $str=$DATA["x"][round(($i-$X0)/($RW/$count))-1];
            imageline($im,$drawx,$Y0,$i-($RW/$count)/2,$Y0+5,$text);
            imagestring($im,2, $drawx-(strlen($str)*$LW)/2, $Y0+7,$str,$text);
            }
        $prev=$i;
        }
    $i-=$RW/$count;
    }

header("Content-Type: image/png");

// Генерация изображения
ImagePNG($im);

imagedestroy($im);
?>

Результат работы этой программы выглядит следующим образом:


$W=640; $H=480;


$W=320; $H=200;


$W=140; $H=140; (минимальный размер)


$W=580; $H=140;


$W=140; $H=580;

Результат экстремального теста (для изображения 16384x8192) можно посмотреть здесь.

Ссылки по теме


Назад | Оглавление | Далее

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 

Комментарии

1.
69K
02 марта 2011 года
stranger1101
0 / / 02.03.2011
+1 / -1
Мне нравитсяМне не нравится
4 марта 2011, 09:53:22
Хех. Также код падает если разное количество точек на графиках.
В общем, как взять за основу - очень полезно. Но допиливать и допиливать. =)
2.
69K
02 марта 2011 года
stranger1101
0 / / 02.03.2011
+0 / -1
Мне нравитсяМне не нравится
2 марта 2011, 10:53:23
Хм. Конечно приятная статья, но использовать код надо с осторожностью.

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

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

P. S. Если кому-то нужен этот же код, но поправленный для произвольных значений - пинайте меня.
3.
386
05 апреля 2005 года
newcss
297 / / 05.04.2005
+1 / -1
Мне нравитсяМне не нравится
15 ноября 2007, 01:29:27
Да, статья прикольная. Но автор извращенец....
[PHP]
$W=16384;
$H=8192;
[/PHP]

ништяк дефолтовые настройки...
Да и чет не работает приведенный код... не выводит график...
4.
1.6K
02 апреля 2003 года
Jimmy
58 / / 02.04.2003
+2 / -0
Мне нравитсяМне не нравится
10 января 2007, 14:38:16
Немного недоволен статъёй!
Я, видите ли, придирчивый слишком к аккуратности.
Кто-то скажет: "ай да подумаешь, небольшая ошибочка. Суть то ясна"
А вы представьте что данная программма используется в медицине, и от неё зависит жизнь человека....
в общем к делу.
Вот строки
// Добавим по две точки справа и слева от графиков. Значения в
// этих точках примем равными крайним. Например, точка если
// y[0]=16 и y[n]=17, то y[1]=16 и y[-2]=16 и y[n+1]=17 и y[n+2]=17

for ($j=0;$j<3;$j++) {
$DATA[$j][-1]=$DATA[$j][-2]=$DATA[$j][0];
$DATA[$j][$count]=$DATA[$j][$count+1]=$DATA[$j][$count-1];
}

Ничего не замечаете????

А вот я вижу, что предложение
// y[0]=16 и y[n]=17, то y[1]=16 и y[-2]=16 и y[n+1]=17 и y[n+2]=17
коту под хвост, только вот из за этой строчки
$DATA[$j][$count]=$DATA[$j][$count+1]=$DATA[$j][$count-1];

Или у нас в программировании поменялись правила с недавних пор????
5.
Аноним
+1 / -1
Мне нравитсяМне не нравится
26 мая 2006, 10:05:53
Спасибо за информацию, вопрос такого плана, а как сделать что бы не произвольно rand а точные значения выводить? У меня есть 2 файла со значениями, первый- 10, второ1 - предположим 15, и оба они время от времени меняются, нужно что бы это отобразилось на графике! Заранее спасибо!
p.s. Я не особо силён в php, но может кто подскажет.
6.
Аноним
Мне нравитсяМне не нравится
12 мая 2006, 13:23:59
не проверял, но выглядит убедительно! хороший подход к вопросам! спасибо!
7.
Аноним
Мне нравитсяМне не нравится
21 января 2006, 06:40:45
Классная статья, она мне очень пригодилась! Спасибо!
8.
Аноним
+2 / -0
Мне нравитсяМне не нравится
14 ноября 2005, 23:48:00
Отличная статья!
9.
Аноним
+1 / -0
Мне нравитсяМне не нравится
6 июня 2005, 11:34:33
Хорошая статья
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог