CodeNet - все для программиста!CodeNet - 10 лет - мегавстреча!
Все для программиста!
  Статьи   Форум   Исходники   Каталог   Хостинг   IRC  
 Справочник функций

Ваш аккаунт

Логин:
Пароль:

Забыли пароль?
Регистрация

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



Подписчиков: 6553
(Из них RSS: 0)

Последний выпуск: 22.06.2010

Вывод круговой диаграммы

Автор: mike, www.codenet.ru
20 апреля 2005 года

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

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

Круговая диаграмма строится с помощью функции imagefilledarc. В качестве 6 и 7 параметра ей передаются начальный и конечный углы сектора. Наша основная задача - рассчитать углы секторов в зависимости от передаваемых данных. Сделать это можно с помощью следующей формулы:

angle=(val/total)*360;

где,

  • angle - угол поворота грани сектора;
  • val - входное значение ;
  • total - сумма всех входных значений.

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

В конце статьи приведен исходный текст программы с минимальными количеством комментариев. Возможно по нему вам будет проще разбираться.

Рассмотрим исходный текст программы:

<?php
function Diagramm($im,$VALUES,$LEGEND) {
    GLOBAL $COLORS,$SHADOWS;

    $black=ImageColorAllocate($im,0,0,0);
  • $im - идентификатор изображения;
  • $VALUES - массив со значениями;
  • $LEGEND - массив с подписями.

Получим размеры изображения:

    $W=ImageSX($im);
    $H=ImageSY($im);

Вывод легенды

Посчитаем количество пунктов, от этого зависит высота легенды

    $legend_count=count($LEGEND);

Посчитаем максимальную длину пункта, от этого зависит ширина легенды

    $max_length=0;
    foreach($LEGEND as $v) if ($max_length<strlen($v)) $max_length=strlen($v);

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

    $FONT=2;
    $font_w=ImageFontWidth($FONT);
    $font_h=ImageFontHeight($FONT);

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

Ширина =

  • (ширина текста (ширина символа * максимальное количество символов)) +
  • (место для квадратика с цветом (размер квадратика равен высоте шрифта)) +
  • (отступ от левого края легенды до квадратика с цветом) +
  • (отступ от квадратика с цветом до текста) +
  • (отступ от текста для правого края легенды);
    $l_width=($font_w*$max_length)+$font_h+10+5+10;

Высота =

  • (высота текста) +
  • (отступ между текстом и верхним краем) +
  • (отступ между текстом и нижним краем)
    $l_height=$font_h*$legend_count+10+10;

Получим координаты верхнего левого угла прямоугольника - границы легенды

    $l_x1=$W-10-$l_width;
    $l_y1=($H-$l_height)/2;

Вывод прямоугольника - границы легенды

    ImageRectangle($im, $l_x1, $l_y1, $l_x1+$l_width, $l_y1+$l_height, $black);

Вывод текст легенды и цветных квадратиков

    $text_x=$l_x1+10+5+$font_h;
    $square_x=$l_x1+10;
    $y=$l_y1+10;

    $i=0;
    foreach($LEGEND as $v) {
        $dy=$y+($i*$font_h);
        ImageString($im, $FONT, $text_x, $dy, $v, $black);
        ImageFilledRectangle($im,
                             $square_x+1,$dy+1,$square_x+$font_h-1,$dy+$font_h-1,
                             $COLORS[$i]);
        ImageRectangle($im,
                       $square_x+1,$dy+1,$square_x+$font_h-1,$dy+$font_h-1,
                       $black);
        $i++;
        }

Вывод круговой диаграммы

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

    $total=array_sum($VALUES);
    $anglesum=$angle=Array(0);
    $i=1;

    // Расчет углов
    while ($i<count($VALUES)) {
        $part=$VALUES[$i-1]/$total;
        $angle[$i]=floor($part*360);
        $anglesum[$i]=array_sum($angle);
        $i++;
        }
    $anglesum[]=$anglesum[0];

Расчет диаметра

    $diametr=$l_x1-10-10;

Расчет координат центра эллипса

    $circle_x=($diametr/2)+10;
    $circle_y=$H/2-10;

Поправка диаметра, если эллипс не помещается по высоте

    if ($diametr>($H*2)-10-10) $diametr=($H*2)-20-20-40;

Вывод тени

    for ($j=20;$j>0;$j--)
        for ($i=0;$i<count($anglesum)-1;$i++)
            ImageFilledArc($im,$circle_x,$circle_y+$j,
                               $diametr,$diametr/2,
                               $anglesum[$i],$anglesum[$i+1],
                               $SHADOWS[$i],IMG_ARC_PIE);

Вывод круговой диаграммы

    for ($i=0;$i<count($anglesum)-1;$i++)
        ImageFilledArc($im,$circle_x,$circle_y,
                           $diametr,$diametr/2,
                           $anglesum[$i],$anglesum[$i+1],
                           $COLORS[$i],IMG_ARC_PIE);

    } /* Конец функции вывода круговой диаграммы */

Зададим значение и подписи

$VALUES=Array(100,200,300,400,500,400,300);
$LEGEND=Array("John","Bob","Alex","Mike","Andrew","Greg");

Создадим изображение

header("Content-Type: image/png");
$im=ImageCreate(500,500);

Зададим цвет фона. Немного желтоватый, для того, чтобы было видно границы изображения на белом фоне.

$bgcolor=ImageColorAllocate($im,255,255,200);

Зададим цвета секторов

$COLORS[0] = imagecolorallocate($im, 255, 203, 3);
$COLORS[1] = imagecolorallocate($im, 220, 101, 29);
$COLORS[2] = imagecolorallocate($im, 189, 24, 51);
$COLORS[3] = imagecolorallocate($im, 214, 0, 127);
$COLORS[4] = imagecolorallocate($im, 98, 1, 96);
$COLORS[5] = imagecolorallocate($im, 0, 62, 136);
$COLORS[6] = imagecolorallocate($im, 0, 102, 179);
$COLORS[7] = imagecolorallocate($im, 0, 145, 195);
$COLORS[8] = imagecolorallocate($im, 0, 115, 106);
$COLORS[9] = imagecolorallocate($im, 178, 210, 52);
$COLORS[10] = imagecolorallocate($im, 137, 91, 74);
$COLORS[11] = imagecolorallocate($im, 82, 56, 47);

Зададим цвета теней секторов

$SHADOWS[0] = imagecolorallocate($im, 205, 153, 0);
$SHADOWS[1] = imagecolorallocate($im, 170, 51, 0);
$SHADOWS[2] = imagecolorallocate($im, 139, 0, 1);
$SHADOWS[3] = imagecolorallocate($im, 164, 0, 77);
$SHADOWS[4] = imagecolorallocate($im, 48, 0, 46);
$SHADOWS[5] = imagecolorallocate($im, 0, 12, 86);
$SHADOWS[6] = imagecolorallocate($im, 0, 52, 129);
$SHADOWS[7] = imagecolorallocate($im, 0, 95, 145);
$SHADOWS[8] = imagecolorallocate($im, 0, 65, 56);
$SHADOWS[9] = imagecolorallocate($im, 128, 160, 2);
$SHADOWS[10] = imagecolorallocate($im, 87, 41, 24);
$SHADOWS[11] = imagecolorallocate($im, 32, 6, 0);

Вызов функции рисования диаграммы

Diagramm($im,$VALUES,$LEGEND);

Генерация изображения

ImagePNG($im)

?>

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


500x300


500x100


150x500


120x120


300x300

Результат работы программы для изображения 4096x2048 можно посмотреть здесь.

Пример 30: Вывод круговой диаграммы. Исходный текст с сокращенным количеством комментариев:

<?php

// $im - идентификатор изображения
// $VALUES - массив со значениями
// $LEGEND - массив с подписями
function Diagramm($im,$VALUES,$LEGEND) {
	GLOBAL $COLORS,$SHADOWS;

	$black=ImageColorAllocate($im,0,0,0);

	// Получим размеры изображения
	$W=ImageSX($im);                 
	$H=ImageSY($im);

	// Вывод легенды #####################################

	// Посчитаем количество пунктов, от этого зависит высота легенды
	$legend_count=count($LEGEND);

	// Посчитаем максимальную длину пункта, от этого зависит ширина легенды
	$max_length=0;
	foreach($LEGEND as $v) if ($max_length<strlen($v)) $max_length=strlen($v);

	// Номер шрифта, котором мы будем выводить легенду
	$FONT=2;
	$font_w=ImageFontWidth($FONT);
	$font_h=ImageFontHeight($FONT);

	// Вывод прямоугольника - границы легенды ----------------------------

	$l_width=($font_w*$max_length)+$font_h+10+5+10;
	$l_height=$font_h*$legend_count+10+10;

	
	// Получим координаты верхнего левого угла прямоугольника - границы легенды
	$l_x1=$W-10-$l_width;
	$l_y1=($H-$l_height)/2;

	// Выводя прямоугольника - границы легенды
	ImageRectangle($im, $l_x1, $l_y1, $l_x1+$l_width, $l_y1+$l_height, $black);

	// Вывод текст легенды и цветных квадратиков
	$text_x=$l_x1+10+5+$font_h;
	$square_x=$l_x1+10;
	$y=$l_y1+10;

	$i=0;
	foreach($LEGEND as $v) {
		$dy=$y+($i*$font_h);
		ImageString($im, $FONT, $text_x, $dy, $v, $black);
		ImageFilledRectangle($im,
                             $square_x+1,$dy+1,$square_x+$font_h-1,$dy+$font_h-1,
                             $COLORS[$i]);
		ImageRectangle($im,
                       $square_x+1,$dy+1,$square_x+$font_h-1,$dy+$font_h-1,
                       $black);
		$i++;
		}

	// Вывод круговой диаграммы ----------------------------------------

	$total=array_sum($VALUES);
	$anglesum=$angle=Array(0);
	$i=1;

	// Расчет углов
	while ($i<count($VALUES)) {
		$part=$VALUES[$i-1]/$total;
		$angle[$i]=floor($part*360);
		$anglesum[$i]=array_sum($angle);
		$i++;
		}
	$anglesum[]=$anglesum[0];

	// Расчет диаметра
	$diametr=$l_x1-10-10;

	// Расчет координат центра эллипса
	$circle_x=($diametr/2)+10;
	$circle_y=$H/2-10;

	// Поправка диаметра, если эллипс не помещается по высоте
	if ($diametr>($H*2)-10-10) $diametr=($H*2)-20-20-40;

	// Вывод тени
	for ($j=20;$j>0;$j--)
		for ($i=0;$i<count($anglesum)-1;$i++)
			ImageFilledArc($im,$circle_x,$circle_y+$j,
                               $diametr,$diametr/2,
                               $anglesum[$i],$anglesum[$i+1],
                               $SHADOWS[$i],IMG_ARC_PIE);

	// Вывод круговой диаграммы
	for ($i=0;$i<count($anglesum)-1;$i++)
		ImageFilledArc($im,$circle_x,$circle_y,
                           $diametr,$diametr/2,
                           $anglesum[$i],$anglesum[$i+1],
                           $COLORS[$i],IMG_ARC_PIE);
	}

// Зададим значение и подписи
$VALUES=Array(100,200,300,400,500,400,300);
$LEGEND=Array("John","Bob","Alex","Mike","Andrew","Greg");

// Создадим изображения
header("Content-Type: image/png");
$im=ImageCreate(500,500);

// Зададим цвет фона. Немного желтоватый, для того, чтобы было
// видно границы изображения на белом фоне.
$bgcolor=ImageColorAllocate($im,255,255,200);

// Зададим цвета элементов
$COLORS[0] = imagecolorallocate($im, 255, 203, 3);
$COLORS[1] = imagecolorallocate($im, 220, 101, 29);
$COLORS[2] = imagecolorallocate($im, 189, 24, 51);
$COLORS[3] = imagecolorallocate($im, 214, 0, 127);
$COLORS[4] = imagecolorallocate($im, 98, 1, 96);
$COLORS[5] = imagecolorallocate($im, 0, 62, 136);
$COLORS[6] = imagecolorallocate($im, 0, 102, 179);
$COLORS[7] = imagecolorallocate($im, 0, 145, 195);
$COLORS[8] = imagecolorallocate($im, 0, 115, 106);
$COLORS[9] = imagecolorallocate($im, 178, 210, 52);
$COLORS[10] = imagecolorallocate($im, 137, 91, 74);
$COLORS[11] = imagecolorallocate($im, 82, 56, 47);

// Зададим цвета теней элементов
$SHADOWS[0] = imagecolorallocate($im, 205, 153, 0);
$SHADOWS[1] = imagecolorallocate($im, 170, 51, 0);
$SHADOWS[2] = imagecolorallocate($im, 139, 0, 1);
$SHADOWS[3] = imagecolorallocate($im, 164, 0, 77);
$SHADOWS[4] = imagecolorallocate($im, 48, 0, 46);
$SHADOWS[5] = imagecolorallocate($im, 0, 12, 86);
$SHADOWS[6] = imagecolorallocate($im, 0, 52, 129);
$SHADOWS[7] = imagecolorallocate($im, 0, 95, 145);
$SHADOWS[8] = imagecolorallocate($im, 0, 65, 56);
$SHADOWS[9] = imagecolorallocate($im, 128, 160, 2);
$SHADOWS[10] = imagecolorallocate($im, 87, 41, 24);
$SHADOWS[11] = imagecolorallocate($im, 32, 6, 0);

// Вызов функции рисования диаграммы
Diagramm($im,$VALUES,$LEGEND);

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

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


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

Комментарии пользователей (всего 5)

7upers / 20 марта 2009, 01:03:33

еще один моментик если сумма всех элементов массива $VALUES очень большая по сравнению с отдельными элементами то она составляет угол близкий к нулю от всего пирога а так как мы округляем до целых, то может возникнуть ситуация когда угол элемента =0 и тогда рисуется полный круг затирая предидущие дольки! Решение следующее: если угол =0 то дольку просто не рисуем , для этого просто добавляем условие
// Вывод тени
for ($j=60;$j>0;$j--)
for ($i=0;$i<count($anglesum)-1;$i++)
<b>if ($anglesum[$i]!=$anglesum[$i+1])</b> ImageFilledArc($im,$circle_x,$circle_y+$j,
$diametr,$diametr/2,
$anglesum[$i],$anglesum[$i+1],
$SHADOWS[$i],IMG_ARC_PIE);

// Вывод круговой диаграммы
for ($i=0;$i<count($anglesum)-1;$i++)
<b>if ($anglesum[$i]!=$anglesum[$i+1])</b> ImageFilledArc($im,$circle_x,$circle_y,
$diametr,$diametr/2,
$anglesum[$i],$anglesum[$i+1],
$COLORS[$i],IMG_ARC_PIE);
}

dbs / 16 ноября 2005, 16:16:17

статья отличная, пригодилось но нашёл ошибочку маленькую в процессе пользования , в куске // Расчет углов
while ($i<count($VALUES)) {
$part=$VALUES[$i-1]/$total;
$angle[$i]=floor($part*360);
$anglesum[$i]=array_sum($angle);
$i++;
}
нужно do
{
$part=$VALUES[$i-1]/$total;
$angle[$i]=floor($part*360);
$anglesum[$i]=array_sum($angle);
$i++;
}
while ($i<count($VALUES));
потому как если count($VALUES)=1 т.е позиция одна и занимает 100%; ничего не нарисует, хотя автор сам наверное всё исправил

pps / 25 октября 2005, 22:54:00

Спасибо за урок! Автору респект. Все просто и гениально

mike / 23 апреля 2005, 16:12:10

Нужно просто потерпеть до урока №13 :)

Terri / 23 апреля 2005, 00:12:55

статья действительно отличная, ещё хотелось бы узнать как можно сделать края более сглажеными?

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

Оставлять комментарии могут только зарегистрированные пользователи.

Если вы не являетесь зарегистрированным пользователем, то вам необходимо зарегистрироваться. Регистрация бесплатна. Если вы уже зарегистрированы на CodeNet, то вам необходимо ввести логин и пароль в верхней (Alt-U) части страницы.

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог