Location: PHPKode > scripts > PHPGraphLib > phpgraphlib_pie.php
<?php
///////////////////////////////////////////////////////////
//PHPGraphLib -  PHP Graphing Library v2.30 - Pie Chart Extension
//Author: Elliott Brueggeman
//PHP v4.04 + compatible
//Please visit www.ebrueggeman.com for usage policy
//and documentation + examples
///////////////////////////////////////////////////////////
class PHPGraphLibPie extends PHPGraphLib {
	//user changeable defaults
	var $pie_precision = 0; //number of significant digits in label %
	var $bool_legend = true;
	var $bool_data_labels = true;
	var $pie_3D_height_percent = 4; //is % of total width 
	var $pie_legend_text_width = 6; //in px...
	var $pie_legend_text_height = 12;
	var $pie_label_text_width = 6;
	var $pie_label_text_height = 12;
	var $pie_legend_padding = 5; //padding inside legend box
	//default colors, in order of display on graph - you can change order if needed
	var $pie_avail_colors = array('pastel_orange_1','pastel_orange_2','pastel_blue_1','pastel_green_1','clay',
		'pastel_blue_2','pastel_yellow','silver','pastel_green_2','brown','gray','pastel_purple', 'olive', 
		'aqua','yellow','teal','lime');
	//internal defaults - change not recommended
	var $pie_center_y_offset = 50; //in %
	var $pie_center_x_offset = 50; //in %
	var $pie_center_legend_scale = 55; //offset in % of existing coords when legend
	var $pie_width_percent = 75; //default width % of total width
	var $pie_height_percent = 28; 
	var $pie_label_scale = 90; //in % scale width/height if data labels
	var $pie_legend_scale = 64;//in % scale width/height if legend
	//internals - do not change
	var $pie_width;
	var $pie_height;
	var $pie_center_x;
	var $pie_center_y;
	var $pie_legend_width;
	var $pie_legend_height;
	var $pie_legend_x;
	var $pie_legend_y;
	var $pie_data_max_length = 0;
	var $pie_color_pointer = 0;
	var $pie_data_array_percents;
	var $pie_data_label_space;
	var $pie_3D_height;
	function PHPGraphLibPie($width = '', $height = '') {
		PHPGraphLib::PHPGraphLib($width, $height);	
	}
	function initialize() {
		PHPGraphLib::initialize();
		$this->pie_data_array_percents = array();
	}
	function calcCoords() {
		//CALC COORDS OF PIE CENTER AND WIDTH/HEIGHT
		$this->pie_width = $this->width * ($this->pie_width_percent / 100);
		$this->pie_height = $this->width * ($this->pie_height_percent / 100);
		$this->pie_center_y = $this->height * ($this->pie_center_y_offset / 100);
		$this->pie_center_x = $this->width * ($this->pie_center_x_offset / 100);
		//SET DATA LABEL SPACING 
		if ($this->bool_data_labels) {
			//SET TO NUMBER OF PIXELS THAT ARE EQUAL TO TEXT WIDTH
			//7 IS A BASE SPACER THAT ALL LABELS GET
			$this->pie_data_label_space = 7 + $this->width / 30;
			$this->pie_width *= $this->pie_label_scale / 100;
			$this->pie_height *= $this->pie_label_scale / 100;
		}
		if ($this->bool_legend) {
			//COMPENSATE FOR LEGEND WITH LESSER PRESET PERCENT
			$this->pie_width *= $this->pie_legend_scale / 100;
			$this->pie_height *= $this->pie_legend_scale / 100;
			$this->pie_center_x *= $this->pie_center_legend_scale / 100;
		}
		$this->pie_3D_height = $this->pie_3D_height_percent * ($this->pie_width / 100);	
	}
	function setupData() {
		//IN THE PIE EXTENSION, THIS WILL CALCULATE THE TOTAL SUM AND THE CORRESPONDING PERCENTAGES
		if ($this->data_set_count==1) {
			$sum = array_sum($this->data_array[0]);
			if ($sum > 0) {
				foreach ($this->data_array[0] as $dataText => $dataValue) {
					$this->pie_data_array_percents[] = $dataValue / $sum;
					//FIND DATA TEXT LENGTH
					$len = strlen($dataText);
					if ($len > $this->pie_data_max_length){ $this->pie_data_max_length = $len; }
				}
				$this->bool_bars_generate = true;
			}
			else {
				$this->bool_bars_generate = false;
				$this->error[] = "Sum of data must be greater than 0.";
			}
		}
		else
		{
			$this->error[]="Multiple datasets not allowed with pie charts";
		}
	}
	function generateGrid() {}
	function generateLegend() {
		$maxChars = NULL;
		//CALC HEIGHT / WIDTH BASED ON # OF VALUES
		$this->pie_legend_height = ($this->pie_legend_text_height * $this->data_count) + (2 * $this->pie_legend_padding);	
		$this->pie_legend_width = ($this->pie_data_max_length * $this->pie_legend_text_width) + (6 * $this->pie_legend_padding);
		//ALLOTTED SPACE DOES NOT INCLUDE PADDING AROUND LEGEND (SMALLER)
		$allottedSpace = $this->width - $this->pie_center_x - ($this->pie_width / 2) - (2 * $this->pie_legend_padding);
		if ($this->bool_data_labels) {
			//ALSO COMPENSATE FOR DISPLAYED TEXT DATA % ON GRAPH
			$allottedSpace -= ((4 + $this->pie_precision) * $this->pie_label_text_width) + $this->pie_data_label_space;
		}
		//CHECK TO MAKE SURE WE ARE NOT > ALLOTTED SPACE
		if ($this->pie_legend_width > $allottedSpace) {
			//IF WE ARE, ADJUST WIDTH AND MAX LENGTH FOR DATA VALUES
			//4 = Padding | Swatch(Padding Width) | Padding | ...text... |Padding
			$swatchAndPaddingWidth = 4 * $this->pie_legend_padding;
			//MAX CHARS = ALOTTED SPACE - ENOUGH ROOM FOR SWATCHES / TEXT WIDTH
			$maxChars = floor(($allottedSpace - $swatchAndPaddingWidth) / $this->pie_legend_text_width);
			$this->pie_legend_width = ($maxChars * $this->pie_legend_text_width) + $swatchAndPaddingWidth;
		}
		else {
			//WE DIDNT GO OVER ALLOTTED SPACE, SO WE SHOULD ADJUST THE CENTER OF THE PIE CHART NOW
			$equalSpacing = ($this->width - ($this->pie_width + $this->pie_legend_width)) / 3;
			//SO NOW REPOSITION CENTER AT SPACING + 1/2 PIE WIDTH
			$this->pie_center_x = ($this->pie_width / 2) + $equalSpacing;
		}
		//AUTO ADJUSTING FORMULA FOR POSITION OF pie_legend_x BASED ON PIE CHART SIZE
		$a = ($this->pie_center_x + $this->pie_width / 2);
		$b = $this->width - $a;
		$c = ($b - $this->pie_legend_width) / 2;
		//SET PIE X & Y ARGS
		$this->pie_legend_x = $a + $c;
		$this->pie_legend_y = ($this->height - $this->pie_legend_height) / 2;		
		//BACKGROUND
		imagefilledrectangle($this->image, $this->pie_legend_x, $this->pie_legend_y, $this->pie_legend_x + $this->pie_legend_width, 
			$this->pie_legend_y + $this->pie_legend_height, $this->legend_color);
		//BORDER
		imagerectangle($this->image, $this->pie_legend_x, $this->pie_legend_y, $this->pie_legend_x + $this->pie_legend_width, 
			$this->pie_legend_y + $this->pie_legend_height, $this->legend_outline_color);
		$xValue = $this->pie_legend_x + ($this->pie_legend_padding);
		$count = 0;
		$this->resetColorPointer();
		$swatchToTextOffset = ($this->pie_legend_text_height - 6) / 2;
		$swatchSize = $this->pie_legend_text_height - (2 * $swatchToTextOffset);
		foreach ($this->data_array[0] as $dataText => $dataValue) {
			$yValue = $this->pie_legend_y + $this->pie_legend_text_height * $count + $this->pie_legend_padding;
			//DRAW COLOR BOXES
			$color = $this->generateNextColor();
			imagefilledrectangle($this->image, $xValue, $yValue + $swatchToTextOffset, $xValue + $swatchSize, $yValue + $swatchToTextOffset + $swatchSize, $color);
			imagerectangle($this->image, $xValue, $yValue + $swatchToTextOffset, $xValue + $swatchSize, $yValue + $swatchToTextOffset + $swatchSize, $this->legend_swatch_outline_color);	
			//IF LONGER THAN OUR MAX, TRIM TEXT
			if($maxChars){ $dataText = substr($dataText,0, $maxChars); }
			imagestring($this->image, 2, $xValue + (2 * $this->pie_legend_padding), $yValue, $dataText, $this->legend_text_color);
			$count++;
		}
	}
	function generateBars()
	{
		$this->resetColorPointer();
		//LOOP THROUGH AND CREATE SHADAING
		for($i = $this->pie_center_y + $this->pie_3D_height; $i > $this->pie_center_y; $i--) {
			$arcStart = 0;
			foreach ($this->pie_data_array_percents as $key => $value) {
				$color = $this->generateNextColor(true); //GENERATE A DARKER VERSION OF THE INDEXED COLOR
				imagefilledarc($this->image, $this->pie_center_x, $i, $this->pie_width, $this->pie_height, $arcStart, (360 * $value) + $arcStart, $color, IMG_ARC_PIE);
				$arcStart += 360*$value;
			}
			$this->resetColorPointer();
		}
		$arcStart = 0;	
		foreach ($this->pie_data_array_percents as $key => $value) {
			$color = $this->generateNextColor();
			imagefilledarc($this->image, $this->pie_center_x, $this->pie_center_y, $this->pie_width, $this->pie_height, $arcStart, (360*$value)+$arcStart, $color, IMG_ARC_PIE);
			if ($this->bool_data_labels) { $this->generateDataLabel($value, $arcStart); }
			$arcStart += 360 * $value;
		}
	}
	function generateDataLabel($value, $arcStart) {
		//MIDWAY IF THE MID ARC ANGLE OF THE WEDGE WE JUST DREW
		$midway = ($arcStart + (360 * $value) + $arcStart) / 2;
		//ADJUST FOR ELLIPSE HEIGHT/WIDTH RATIO
		$skew = $this->pie_height_percent / $this->pie_width_percent;
		$pi = atan(1.0) * 4.0;
		$theta = ($midway / 180) * $pi;
		$valueX = $this->pie_center_x + ($this->pie_width / 2 + $this->pie_data_label_space) * cos($theta);
		$valueY = $this->pie_center_y + ($this->pie_width / 2 + $this->pie_data_label_space) * sin($theta) * $skew;
		$displayValue = $this->formatPercent($value);
		$valueArray = $this->dataLabelHandicap($valueX, $valueY, $displayValue, $midway);
		$valueX = $valueArray[0];
		$valueY = $valueArray[1];	
		imagestring($this->image, 2, $valueX, $valueY, $displayValue, $this->label_text_color);
	}
	function formatPercent($input) {	
		return number_format($input * 100, $this->pie_precision) . '%';
	}
	function dataLabelHandicap($x, $y, $value, $midway) {
		//MOVES DATA LABEL X/Y BASED ON QUADRANT AND LENGTH OF DISPLAYED DATA
		//AND HOW TEXT IS DISPLAYED (UPPER LEFT CORNER X/Y)
		//EXTRA 1 FOR % SIGN
		$lengthOffset = (strlen($value) * ($this->pie_label_text_width)) / 2;
		$vertOffset = $this->pie_label_text_height / 2;
		if ($midway <= 30) {
			$newX = $x - (1.5 * $lengthOffset);
			$newY = $y - $vertOffset;
		}
		else if ($midway > 30 && $midway <= 135) {
			$newX = $x - $lengthOffset;
			$newY = $y - $vertOffset + $this->pie_3D_height;
		}
		else if ($midway > 135 && $midway <= 165) {
			$newX = $x - $lengthOffset;
			$newY = $y - $vertOffset;
		}
		else if ($midway > 165 && $midway <= 200) {
			//VALUE AT RISK FOR BEING OUT OF BOUNDS ON SMALLER GRAPHS
			$newX = $x - (1/3 * $lengthOffset);
			$newY = $y - $vertOffset;
		}
		else if ($midway > 200 && $midway <= 330) {
			$newX = $x - $lengthOffset;
			$newY = $y - $vertOffset;
		}
		else if ($midway > 330) {
			//VALUE AT RISK FOR OVERLAPPING THE LEGEND ON SMALLER GRAPHS
			$newX = $x - (1.5 * $lengthOffset);
			$newY = $y - $vertOffset;
		}
		else {
			$newX = $x - $lengthOffset;
			$newY = $y - $vertOffset;
		}
		return array($newX, $newY);
	}
	function generateNextColor($dark = false) {
		$array = $this->returnColorArray($this->pie_avail_colors[$this->pie_color_pointer]);
		if ($dark) {
			//WE ARE TRYING TO GENERATE A DARKER VERSION OF THE EXISTING COLOR
			$array[0] *= .8;
			$array[1] *= .8;
			$array[2] *= .8;
		}
		$color = imagecolorallocate($this->image, $array[0], $array[1], $array[2]);
		$this->pie_color_pointer++;
		if ($this->pie_color_pointer >= count($this->pie_avail_colors)) {
			$this->pie_color_pointer = 0;
		}
		return $color;
	}
	function resetColorPointer() {
		$this->pie_color_pointer = 0;
	}
	function returnColorArray($color) {
		//THIS FUNCTION FIRST CHECKS EXISITNG COLORS IN PHPGraphLib
		//THEN IF NOT FOUND CHECKS ITS OWN LIST
		//COMES WITH VARIOUS PRESET LIGHTER PIE CHART FRIENDLY COLORS
		if ($resultColor = PHPGraphLib::returnColorArray($color)) {
			return $resultColor;
		}
		else {
			//REMOVE LAST ERROR GENERATED (PHPGraphLib::returnColorArray) SETS ONLY ONE ERROR IF FALSE)
			array_pop($this->error);
			//CHECK TO SEE IF NUMERIC COLOR PASSED THROUGH IN FORM '128,128,128'
			if (strpos($color,',') !== false) {
				return explode(',', $color);
			}
			switch(strtolower($color)) {
				//NAMED COLORS BASED ON W3C's RECOMMENDED HTML COLORS
				case 'pastel_orange_1': return array(238,197,145); break;
				case 'pastel_orange_2': return array(238,180,34); break;
				case 'pastel_blue_1':   return array(122,197,205); break;
				case 'pastel_green_1':  return array(102,205,0); break;
				case 'pastel_blue_2':   return array(125,167,217); break;
				case 'pastel_green_2':  return array(196,223,155); break;
				case 'clay':            return array(246,142,85); break;
				case 'pastel_yellow':   return array(255,247,153); break;
				case 'pastel_purple':   return array(135,129,189); break;
				case 'brown':           return array(166,124,81); break;	
			}
			$this->error[] = "Color name \"$color\" not recogized.";
			return false;
		}
	}
	function generateTitle() {
		//DRAWS TITLE B/T TOP OF GRAPH AND EDGE OF CANVAS
		$pieTop = $this->pie_center_y - ($this->pie_height / 2);
		if ($this->bool_legend) {
			$topElement = ($pieTop < $this->pie_legend_y) ? $pieTop : $this->pie_legend_y;
		}
		else {
			$topElement = $pieTop;
		}
		if ($topElement < 0) {
			$this->error[] = "Not enough room for a title. Increase graph height, or eliminate data values.";
		}
		else {
			$title_y = ($topElement / 2) - ($this->title_char_height / 2);
			$title_x = ($this->width / 2) - ((strlen($this->title_text) * $this->title_char_width) / 2);
			imagestring($this->image, 2, $title_x , $title_y , $this->title_text,  $this->title_color);
		}
	}
	//"PUBLIC" CUSTOMIZATION FUNCTIONS
	function setLabelTextColor($color) {
		$this->setGenericColor($color, '$this->label_text_color', "Label text color not specified properly.");
	}
	function setPrecision($digits) {
		if (is_int($digits)) { $this->pie_precision = $digits;}
		else { $this->error[] = "Integer arg for setPrecision() not specified properly."; }
	}
	function setDataLabels($bool) {
		if (is_bool($bool)) { $this->bool_data_labels = $bool;}
		else { $this->error[] = "Boolean arg for setDataLabels() not specified properly."; }
	}
	//UNUSED PHPGRAPHLIB FUNCTIONS OVERWRITTEN
	function generateXAxis(){}
	function generateYAxis(){}
	function setupXAxis(){}
	function setupYAxis(){}
	function calcTopMargin(){}
	function calcRightMargin(){}
	function generateDataPoints(){}
}
?>
Return current item: PHPGraphLib