Использование графики в PHP. Классы.

Программирование на PERL

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

Здесь я хотел бы показать как с помощью объектно-ориентированного подхода можно создать удобный в применении и дальнейшем усовершенствовании скрипт для работы с графикой.

Необходимые требования: установленный модуль PHP для поддержки графической библиотеки GD.

Итак, нам надо создать универсальный класс для работы с графикой. Назовем наш корневой класс Graph. Его задача - создать картинку определенного формата, напечатать HTML-код с заголовком и телом картинки, возвратить ошибку в случае неудачи. Картинки используют различные цвета для отображения графики, поэтому создадим класс Color. Больше никаких корневых классов не требуется. Теперь для каждого типа графики создадим свой класс-наследник от Graph, т.е. конкретизируем, что за картинку мы хотим создать. В нашей ситуации мы хотим создать графики столбцовой, круговой и линейной диаграмм. Для этого создадим классы Barchart, Piechart и Linechart соответственно. В дальнейшем для создания новых типов графиков следует просто создать соответсвующий новый класс.

Далее представлен листинг класса Graph:

 

<?php
class Graph {
	var $format='gif';
	var $error = '';
	var $image = '';
	var $filename = '';

  // Возвращает изображение в формате GD-графики или выводит его в файл
	function image($filename='') {
		$this->error = '';
		if (empty($this->image)) { $this->image = $this->createImage(); }
		if (!empty($filename)) {
			switch ($this->format) {
				case 'gif':
					imageGif($this->image,$filename);
					break;
				case 'jpeg':
					imageJpeg($this->image,$filename);
					break;
				case 'png':
					imagePng($this->image,$filename);
					break;
			}
		}
		return $this->image;
	}

  // Устанавливает формат вывода изображения
	function setFormat($format='gif') {
		$this->error = '';
		if (preg_match('/gif|jpeg|png/',$format)) {
			$this->format = $format;
		}
		else {
			$this->error = 'Invalid image format. Use gif,jpeg or png instead.';
		}
	}

  // Печатает изображение с заголовком на стандартный вывод
	function printHTML() {
		$this->error = '';
		if (empty($this->image)) { $this->image = $this->createImage(); }
		Header('Content-type: image/'.$this->format);
		switch ($this->format) {
			case 'gif':
				imageGif($this->image);
				break;
			case 'jpeg':
				imageJpeg($this->image);
				break;
			case 'png':
				imagePng($this->image);
				break;
		}
	}

  // Создание изображения
	function createImage() {
		$im = '';
		return $im;
	}

  // Возвращает последнюю ошибку
	function error() {
		return $this->error;
	}
}
?>

Класс Color:

 

<?php
class Color {
	var $R=255;
	var $G=255;
	var $B=255;

	function Color($R=255,$G=255,$B=255) {
		$this->R = $R;
		$this->G = $G;
		$this->B = $B;		
	}

}
?>

Круговая диаграмма (Piechart) работает только с одним набором чисел, по которым в дальнейшем вычисляется соотношение каждого числа к общей сумме. Для ввода чисел создадим метод addSegment(value:double,color:Color). Созданием и отображением класса занимаются унаследованные от класса Graph методы createImage() и printHTML().

Далее представлен листинг класса Piechart:

 

<?php
// Наследуем класс Piechart от класса Graph
class Piechart extends Graph {
	var $segments = array();
	var $img_w = 100;
	var $img_h = 100;
	var $radius = 50;
	var $bgcolor;

  // Конструктор класса
	// На вход подаются радиус окружности и цвет фона
	function Piechart($rad,$bgcolor='') {
		$this->img_w = $rad*2;
		$this->img_h = $rad*2;
		$this->radius = $rad;
		if (!empty($bgcolor)) {
			$this->bgcolor = $bgcolor;
		}
		else {
			$this->bgcolor = new Color(0,0,255);
		}
	}

  // Добавляет сегмент диаграммы и цвет его отрисовки
	function addSegment($val=0,$color) {
		$this->segments[$val]=$color;
	}

  // Переопределенный метод создания изображения
	function createImage() {
		$total = 0;
		reset($this->segments);
		while(list($key,$val)=each($this->segments)) {
			$total += $key;
		}
		$cx = floor($this->img_w/2);
		$cy = floor($this->img_h/2);

		// Создаем изображение с помощью библиотеки GD
		$im = ImageCreate($this->img_w,$this->img_h);

		$blue = ImageColorAllocate($im,0,0,255);
		$white = ImageColorAllocate($im,255,255,255);
		$bgc = ImageColorAllocate($im,$this->bgcolor->R,$this->bgcolor->G,$this->bgcolor->B);

		ImageColorTransparent($im,$bgc);
		ImageFill($im,$cx,$cy,$bgc);
		ImageArc($im,$cx,$cy,$this->img_w,$this->img_h,0,360,$blue);
		ImageLine($im,$cx,$cy,$cx+$this->img_w,$cy,$blue);

		// Отрисовываем саму круговую диаграмму
		reset($this->segments);
		$prev = 0;
		$sm = 0;
		while(list($key,$val)=each($this->segments)) {
			// процентное соотношение
			$percent = $key/$total*100;
			// угол
			$angle = (360/100)*$percent;
			$sm += $angle;
			// угол в радианах
			$rad = deg2rad($sm);
			$dx = ($this->radius) * cos($rad);
			$dy = ($this->radius) * sin($rad);
			ImageLine($im,$cx,$cy,$cx+$dx,$cy-$dy,$blue);
			$gamma = $rad - $prev;
			$delta = $prev + $gamma/2;
			$dxp = ($this->radius/2) * cos($delta);
			$dyp = ($this->radius/2) * sin($delta);
			$color = ImageColorAllocate($im,$val->R,$val->G,$val->B);
 			ImageFill($im,$cx+$dxp,$cy-$dyp,$color);
			$prev = $rad;
		}
		$this->image = $im;
		return $im;
	}
}
?>

Линейная и столбцовая диаграммы, в отличие от круговой, работают с массивами данных, поэтому на вход должны принимать массив чисел и цвет для отображения. Для этого создадим метод addArray(values:array of double,color:Color). Так же как и в круговой диаграмме методы для создания и отображения наследуются из класса Graph.

Далее представлен листинг класса Linechart:

 

<?php
class Linechart extends Graph {
	var $arrays = array();
	var $colors = array();
	var $img_w = 100;
	var $img_h = 100;
	var $bgcolor='';
	var $arr_counter = 0;

	// Конструктор класса
	// на вход подаются размеры изображения и цвет фона
	function Linechart($img_w=100,$img_h=100,$bgcolor='') {
		$this->img_w = $img_w;
		$this->img_h = $img_h;
		if (!empty($bgcolor)) {
			$this->bgcolor = $bgcolor;
		}
		else {
			$this->bgcolor = new Color(0,0,255);
		}
	}

	// Добавляет массив данных и цвет его отрисовки
	function addArray($val=array(),$color) {
		$this->arr_counter++;
		$this->arrays[$this->arr_counter] = array();
		$this->arrays[$this->arr_counter] = $val;
		$this->colors[$this->arr_counter] = $color;
	}

	// Переопределяет создание изображения
	function createImage() {
		reset($this->arrays);
		error_reporting(63);
		$cnmax = 0;
		$ar = $this->arrays[$this->arr_counter];
		$min = $ar[0];
		$max = $ar[0];
		while(list($key,$val)=each($this->arrays)) {
			$tt = count($val);
			if ($tt > $cnmax) { $cnmax = $tt; }
			$armax = $val[0];
			$armin = $val[0];
			while (list($key1,$val1)=each($val)) {
				if ($val1 > $armax) { $armax = $val1; }
				if ($val1 < $armin) { $armin = $val1; }
			}
			if ($armax > $max) { $max = $armax; }
			if ($armin < $min) { $min = $armin; }
		}

		$kx = $this->img_w/$cnmax;
		$ky = $this->img_h/(abs($max)+abs($min));

		$cx = floor($this->img_w/2);
		$cy = floor($this->img_h/2);
		$im = ImageCreate($this->img_w,$this->img_h);

		$blue = ImageColorAllocate($im,0,0,255);
		$white = ImageColorAllocate($im,255,255,255);
		$bgc = ImageColorAllocate($im,$this->bgcolor->R,$this->bgcolor->G,$this->bgcolor->B);

		ImageColorTransparent($im,$bgc);
		ImageFill($im,$cx,$cy,$bgc);
		ImageRectangle($im,0,0,$this->img_w-1,$this->img_h-1,$blue);

		$ay = abs($max);
		// ось x
		ImageLine($im,0,$ay*$ky,$this->img_w-1,$ay*$ky,$blue);

		//рисуем точки
		reset($this->arrays);
		while(list($key,$val)=each($this->arrays)) {
			$i = 0;
			reset($val);
			$prx = 0;
			$pry = $ay*$ky;
			$val_cl = $this->colors[$key];
			while (list($key1,$val1)=each($val)) {
				$i++;
				$px = floor($i*$kx);
				$py = floor($ky*($ay-$val1));
				$color = ImageColorAllocate($im,$val_cl->R,$val_cl->G,$val_cl->B);
				ImageSetPixel($im,$px,$py,$blue);
				ImageLine($im,$prx,$pry,$px,$py,$color);
				$prx = $px;
				$pry = $py;
			}
		}		

		$this->image = $im;
		return $im;
	}
}
?>

Далее представлен листинг класса Barchart:

 

<?php
class Barchart extends Graph {
	var $arrays = array();
	var $colors = array();
	var $img_w = 100;
	var $img_h = 100;
	var $bgcolor = '';
	var $arr_counter = 0;

	// Конструктор класса
	// на вход подаются размеры изображения и цвет фона
	function Barchart($img_w=100,$img_h=100,$bgcolor='') {
		$this->img_w = $img_w;
		$this->img_h = $img_h;
		if (!empty($bgcolor)) {
			$this->bgcolor = $bgcolor;
		}
		else {
			$this->bgcolor = new Color(0,0,255);
		}
	}


	// Добавляет массив данных и цвет его отрисовки
	function addArray($val=array(),$color) {
		$this->arr_counter++;
		$this->arrays[$this->arr_counter] = array();
		$this->arrays[$this->arr_counter] = $val;
		$this->colors[$this->arr_counter] = $color;
	}

	// Переопределяет создание изображения
	function createImage() {
		reset($this->arrays);
		error_reporting(63);
		// коеффициент растяга
		$k = 3;
		// расстояние между столбцами
		$dbs = 4;

		$cnmax = 0;
		$ar = $this->arrays[$this->arr_counter];
		$min = $ar[0];
		$max = $ar[0];
		while(list($key,$val)=each($this->arrays)) {
			$tt = count($val);
			if ($tt > $cnmax) { $cnmax = $tt; }
			$armax = $val[0];
			$armin = $val[0];
			while (list($key1,$val1)=each($val)) {
				if ($val1 > $armax) { $armax = $val1; }
				if ($val1 < $armin) { $armin = $val1; }
			}
			if ($armax > $max) { $max = $armax; }
			if ($armin < $min) { $min = $armin; }
		}

		// ширина столбца
		$bw = ($this->img_w-$dbs*($this->arr_counter-1)*$cnmax)/($k*($cnmax+1)+$this->arr_counter*$cnmax);

		$kx = ($this->img_w-$this->arr_counter*($dbs+$k*$bw))/($cnmax);
		$ky = $this->img_h/(abs($max)+abs($min));

		$cx = floor($this->img_w/2);
		$cy = floor($this->img_h/2);
		$im = ImageCreate($this->img_w,$this->img_h);

		$blue = ImageColorAllocate($im,0,0,255);
		$white = ImageColorAllocate($im,255,255,255);
		$bgc = ImageColorAllocate($im,$this->bgcolor->R,$this->bgcolor->G,$this->bgcolor->B);

		ImageColorTransparent($im,$bgc);
		ImageFill($im,$cx,$cy,$bgc);
		ImageRectangle($im,0,0,$this->img_w-1,$this->img_h-1,$blue);

		$ay = abs($max);
		// ось x
		ImageLine($im,0,$ay*$ky,$this->img_w-1,$ay*$ky,$blue);

		//рисуем точки
		reset($this->arrays);
		$db=0;
		while(list($key,$val)=each($this->arrays)) {
			$i = 0;
			reset($val);
			$val_cl = $this->colors[$key];
			while (list($key1,$val1)=each($val)) {
				$i++;
				$px = floor($i*$kx);
				$py = floor($ky*($ay-$val1));
				$color = ImageColorAllocate($im,$val_cl->R,$val_cl->G,$val_cl->B);
				ImageRectangle($im,$px+$db,$ay*$ky,$px+$bw+$db,$py,$color);
				ImageFill($im,floor($px+$db+$bw/2),floor($ky*($ay-$val1/2)),$color);
			}
			$db += ($bw+$dbs);
		}		

		$this->image = $im;
		return $im;
	}
}
?>

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

test_pie.php

 

<?php
require "Color.php";
require "Graph.php";
require "Piechart.php";

$white = new Color(255,255,255);
$blue = new Color(0,0,255);
$red = new Color(255,0,0);
$green = new Color(0,255,0);

$pie = new Piechart(100,$white);
$pie->addSegment(23,$white);
$pie->addSegment(120,$blue);
$pie->addSegment(60,$red);
$pie->addSegment(30,$green);
$pie->setFormat('gif');
$pie->printHTML();
?>

test_line.php

 

<?php
require "Color.php";
require "Graph.php";
require "Linechart.php";

$white = new Color(255,255,255);
$blue = new Color(0,0,255);
$red = new Color(255,0,0);
$green = new Color(0,255,0);

$line = new Linechart(200,200,$white);

$ar1 = array(12,13,16,2,5,10,0,1,2,8);
$ar2 = array(20,13,-1,2,23,11,1,3,4,9);
$ar3 = array(11,15,20,12,6,-10,28,30,10,0);

$line->addArray($ar1, $white);
$line->addArray($ar2, $green);
$line->addArray($ar3, $red);
$line->printHTML();
?>

test_bar.php

 

<?php
require "Color.php";
require "Graph.php";
require "Barchart.php";

$white = new Color(255,255,255);
$blue = new Color(0,0,255);
$red = new Color(255,0,0);
$green = new Color(0,255,0);

$bar = new Barchart(500,500,$white);

$ar1 = array(12,13,16,2,5,10,0,1,2,8);
$ar2 = array(20,13,-1,2,23,11,1,3,4,9);
$ar3 = array(11,15,20,12,6,-10,28,30,10,0);

$bar->addArray($ar1, $white);
$bar->addArray($ar2, $green);
$bar->addArray($ar3, $red);
$bar->printHTML();
?>

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



Опубликовал admin
10 Сен, Среда 2003г.



Программирование для чайников.