<?php 
///////////////////////////////////////////////////////////
//PHPGraphLib -  PHP Graphing Library v2.02
//Author: Elliott Brueggeman
//PHP v4.04 + compatible
//Please visit www.ebrueggeman.com for usage policy
//and full documentation + examples
///////////////////////////////////////////////////////////
class PHPGraphLib
{
	//---------------USER CHANGEABLE DEFAULTS----------------/
	var $height=300;
	var $width=400;
	var $data_max_allowable=9999999999999999;
	var $data_min_allowable=-9999999999999999;
	var $data_set_count=0;
	//SET TO ACTUAL FONT HEIGHTS AND WIDTHS USED
	var $title_char_width=6;
	var $title_char_height=12;
	var $text_width=6;
	var $text_height=12;
	var $data_value_text_width=6;
	var $data_value_text_height=12;
	//PADDING BETWEEN AXIS AND VALUE DISPLAYED
	var $axis_value_padding=5;
	//SPACE B/T TOP OF BAR OR CENTER OF POINT AND DATA VALUE
	var $data_value_padding=5; 
	//DEFAULT MARGIN % OF WIDTH / HEIGHT
	var $x_axis_default_percent=12; 
	var $y_axis_default_percent=8;
	//DATA POINT DIAMETER IN PX
	var $data_point_width=6;
	//USER CHANGEABLE DEFAULT BOOLEANS (SHOW ELEMENT BY DEFAULT?)
	var $bool_bar_outline=true;
	var $bool_x_axis=true;
	var $bool_y_axis=true;
	var $bool_x_axis_values=true;
	var $bool_y_axis_values=true;
	var $bool_grid=true;
	var $bool_line=false;
	var $bool_data_values=false;
	var $bool_x_axis_values_vert=true;
	var $bool_data_points=false;
	var $bool_title_left=false;
	var $bool_title_right=false;
	var $bool_title_center=true;
	//----------INTERNAL VARIABLES (DO NOT CHANGE)------------/
	var $image;
	var $error;
	var $bool_x_axis_setup=false;
	var $bool_y_axis_setup=false;
	var $bool_data=false;
	var $bool_bars_generate=true;
	var $bool_bars=true;
	var $bool_background=false;
	var $bool_title=false;
	var $bool_all_negative=false;
	var $bool_all_positive=false;
	var $bool_gradient=false;
	var $bool_user_data_range=false;
	var $bool_gradient_colors_found;//INIT AS ARRAY
	//COLOR VARS
	var $background_color;
	var $grid_color;
	var $bar_color;
	var $outline_color;
	var $x_axis_text_color;
	var $y_axis_text_color;
	var $title_color;
	var $x_axis_color;
	var $y_axis_color;
	var $data_point_color;
	var $data_value_color;
	var $line_color;
	var $line_color_default;
	var $goal_line_color;
	//GRADIENT COLORS STORED AS ARRAYS, NOT ALLOCATED COLOR
	var $gradient_color_1;
	var $gradient_color_2;
	var $gradient_color_array;
	var $gradient_max=200;
	var $gradient_handicap;
	//DATA VARS
	var $data_array;
	var $data_count;
	var $data_min;
	var $data_max;
	//BAR VARS / SCALE
	var $bar_spaces;
	var $bar_width;
	var $space_width;
	var $unit_scale;
	var $goal_line_array;
	//TEXT / FONT
	var $title_x;
	var $title_y;
	//AXIS POINTS
	var $x_axis_x1;
	var $x_axis_y1;
	var $x_axis_x2;
	var $x_axis_y2;
	var $y_axis_x1;
	var $y_axis_y1;
	var $y_axis_x2;
	var $y_axis_y2;
	var $x_axis_margin; //AKA BOTTOM MARGIN
	var $y_axis_margin; //AKA LEFT MARGIN
	var $data_range_max;
	var $data_range_min;
	var $top_margin=0;
	var $right_margin=0;
	var $range_divisor_factor=25; //CONTROLS AUTO-ADJUSTING GRID INTERVAL
	var $data_point_array;
	//MULTIPLE DATASET VARIABLES
	var $bool_multi_offset=true;
	var $multi_offset_two=24; //PERCENT OF BAR WIDTH
	var $multi_offset_three=15; //PERCENT OF BAR WIDTH
	var $multi_gradient_colors_1;
	var $multi_gradient_colors_2;
	var $multi_bar_colors;
	var $color_darken_factor=30	;//PERCENT DECREASE
	//LEGEND VARIABLES
	var $bool_legend=false;
	var $legend_text_width=6; //IN PX...
	var $legend_max_chars=15; 
	var $legend_total_chars; 
	var $legend_text_height=12;
	var $legend_padding=4; //PADDING INSIDE LEGEND BOX
	var $legend_width;
	var $legend_height;
	var $legend_x;
	var $legend_y;
	var $legend_text_color;
	var $legend_outline_color;
	var $legend_swatch_outline_color;
	var $legend_color;
	var $legend_titles=array();
	//--------------------"PUBLIC" CONSTRUCTOR----------------------//
	function PHPGraphLib($width='', $height='')
	{
		if(!empty($width)&&!empty($height))
		{
			$this->width=$width;
			$this->height=$height;
		}
		$this->initialize();
		$this->allocateColors(); //SETS DEFAULT COLORS	
	}
	//----------------"PRIVATE" MAIN PROGRAM FUNCTIONS ----------------//
	function initialize()
	{
		//HEADER MUST BE SENT BEFORE ANY HTML OR BLANK SPACE OUTPUT
		header("Content-type: image/png");
		$this->image = @imagecreate($this->width, $this->height)
			or die("Cannot Initialize new GD image stream - Check your PHP setup");
		$this->data_point_array=array();
		$this->goal_line_array=array();
		$this->multi_bar_colors=array();
		$this->multi_gradient_colors_1=array();
		$this->multi_gradient_colors_2=array();
		//THESE VALUES ARE NOW ARRAYS
		$this->gradient_handicap=array();
		$this->bool_gradient_colors_found=array();
		$this->legend_total_chars=array();
		$this->line_color=array();
	}
	function createGraph() //MAIN CLASS METHOD - CALLED LAST
	{
		//SETUP AXIS IF NOT ALREADY SETUP BY USER
		if($this->bool_data){
			if(!$this->bool_x_axis_setup){ $this->setupXAxis(); }
			if(!$this->bool_y_axis_setup){ $this->setupYAxis(); }
			//CALCULATIONS
			$this->calcTopMargin();
			$this->calcRightMargin();
			$this->calcCoords();
			$this->setupData();
			//START CREATING ACTUAL IMAGE ELEMENTS
			if($this->bool_background){ $this->generateBackgound(); }
			//ALWAYS GEN GRID VALUES, EVEN IF NOT DISPLAYED
			$this->generateGrid();
			if($this->bool_bars_generate){$this->generateBars(); }
			if($this->bool_data_points){$this->generateDataPoints(); }
			if($this->bool_legend){ $this->generateLegend(); }
			if($this->bool_title) { $this->generateTitle(); }
			if($this->bool_x_axis){ $this->generateXAxis(); }
			if($this->bool_y_axis){ $this->generateYAxis(); }
		}
		else
		{
			$this->error[]="No valid data added to graph. Add data with the addData() function.";
		}
		//DISPLAY ERRORS
		$this->displayErrors();
		//OUTPUT TO BROWSER
		imagepng($this->image);
		imagedestroy($this->image);
	}
	function setupData()
	{
		$this->bar_spaces=($this->data_count*2)+1;
		$unit_width=($this->width-$this->y_axis_margin-$this->right_margin)/(($this->data_count*2)+($this->data_count));
		if($unit_width<1)
		{	
			//ERROR UNITS TOO SMALL, TOO MANY DATA POINTS OR NOT LARGE ENOUGH GRAPH
			$this->bool_bars_generate=false;
			$this->error[]="Graph too small or too many data points.";
		}
		else
		{
			//DEFAULT SPACE BETWEEN BARS IS 1/2 THE WIDTH OF THE BAR
			//FIND BAR AND SPACE WIDTHS. BAR = 2 UNITS, SPACE = 1 UNIT
			$this->bar_width=2*$unit_width;
			$this->space_width=$unit_width;		
			//NOW CALCULATE HEIGHT (SCALE) UNITS
			$availVertSpace=$this->height-$this->x_axis_margin-$this->top_margin;	
			if($availVertSpace<1)
			{
				$this->bool_bars_generate=false;
				$this->error[]="Graph height not tall enough.";
				//ERROR SCALE UNITS TOO SMALL, X AXIS MARGIN TOO BIG OR GRAPH HEIGHT NOT TALL ENOUGH
			}
			else
			{
				if($this->bool_user_data_range)
				{
					if(($this->data_range_max<$this->data_max)||($this->data_range_min>$this->data_min))
					{
						$this->error[]="Data not usable with setRange() function. All data must be within range!";
					}
					if($this->data_min<0)
					{
						$this->error[]="Data not usable with setRange() function. All data must be >= 0.";
					}
					else
					{
						$graphTopScale=$this->data_range_max;
						$graphBottomScale=$this->data_range_min;	
						$graphScaleRange=$graphTopScale-$graphBottomScale;
						$this->unit_scale=$availVertSpace/$graphScaleRange;			
						$this->data_max=$this->data_range_max;
						$this->data_min=$this->data_range_min;
					}	
				}
				else
				{
					//START AT Y VALUE 0 OR DATA MIN, WHICHEVER IS LESS
					$graphBottomScale=($this->data_min<0) ? $this->data_min : 0;
					$graphTopScale=($this->data_max<0) ? 0 : $this->data_max;
					$graphScaleRange=$graphTopScale-$graphBottomScale;
					$this->unit_scale=$availVertSpace/$graphScaleRange;			
					//NOW ADJUST X AXIS IN Y VALUE IF NEGATIVE VALUES
					if($this->data_min<0)
					{
						$this->x_axis_y1-=(int)($this->unit_scale*abs($this->data_min));
						$this->x_axis_y2-=(int)($this->unit_scale*abs($this->data_min));
					}
				}
				$this->bool_bars_generate=true;	
			}
		}
	}
	function generateBars()
	{
		$this->finalizeColors();
		$barCount=0;
		$adjustment=0;
		if($this->bool_user_data_range)
		{
			$adjustment=$this->data_min*$this->unit_scale;
		}
		//REVERSE ARRAY TO ORDER DATA SETS IN ORDER OF PRIORITY
		$this->data_array=array_reverse($this->data_array);
		$dataset_offset=0;
		if($this->bool_multi_offset)
		{
			//SET DIFFERENT OFFSETS BASED ON NUMBER OF DATA SETS
			$dataset_offset=($this->data_set_count>2) ? 
				$this->bar_width*($this->multi_offset_three/100) : $this->bar_width*($this->multi_offset_two/100);
		}
		foreach($this->data_array as $data_set_num => $data_set)
		{
			$lineX2=NULL;
			$xStart=$this->y_axis_x1+$this->space_width/2;
			foreach($data_set as $key => $item)
			{		
				$x1=(int)$xStart+($dataset_offset*$data_set_num);
				$x2=(int)($xStart+$this->bar_width)+($dataset_offset*$data_set_num);
				$y1=(int)($this->x_axis_y1-($item*$this->unit_scale)+$adjustment);
				$y2=(int)$this->x_axis_y1;
				
				//DRAW BAR 
				if($this->bool_bars)
				{
					if($this->bool_gradient)
					{
						//DRAW GRADIENT IF DESIRED
						$this->drawGradientBar($x1, $y1, $x2, $y2, $this->multi_gradient_colors_1[$data_set_num], $this->multi_gradient_colors_2[$data_set_num], $data_set_num);
					}
					else
					{
						//IF/ELSE NECESSARY B/C OF BUG IN ARG ORDER OF imagefilledrectangle() FUNCTION
						if($y1<$y2){ imagefilledrectangle($this->image, $x1, $y1,$x2, $y2,  $this->multi_bar_colors[$data_set_num]); }
						else{ imagefilledrectangle($this->image, $x1, $y2,$x2, $y1,  $this->multi_bar_colors[$data_set_num]); }
					}
					//DRAW BAR OUTLINE	
					if($this->bool_bar_outline)
					{ 
						imagerectangle($this->image,  $x1, $y2, $x2, $y1, $this->outline_color); 
					}
				}
				// DRAW LINE
				if($this->bool_line)
				{
					$lineX1=$x1+($this->bar_width)/2;
					$lineY1=$y1;
					if(isset($lineX2))
					{
						imageline($this->image, $lineX2, $lineY2, $lineX1, $lineY1, $this->line_color[$data_set_num]);
						$lineX2=$lineX1;
						$lineY2=$lineY1;
					}
					else
					{
						$lineX2=$lineX1;
						$lineY2=$lineY1;
					}	
				}
				// DISPLAY DATA POINTS
				if($this->bool_data_points)
				{
					//DONT DRAW DATAPOINTS HERE OR WILL OVERLAP POORLY WITH LINE
					//INSTEAD COLLECT COORDINATES
					$pointX=$x1+($this->bar_width)/2;
					$this->data_point_array[]=array($pointX,  $y1);
				}
				// DISPLAY DATA VALUES
				if($this->bool_data_values)
				{
					$dataX=($x1+($this->bar_width)/2)-((strlen($item)*$this->data_value_text_width)/2);
					$dataY=($item>=0) ? $y1-$this->data_value_padding-$this->data_value_text_height : $y1+$this->data_value_padding;
					imagestring($this->image, 2, $dataX, $dataY, $item,  $this->data_value_color);
				}
				
				//WRITE X AXIS VALUE 
				if($this->bool_x_axis_values)
				{
					if($data_set_num==$this->data_set_count-1)
					{
						if($this->bool_x_axis_values_vert)
						{
							if($this->bool_all_negative)
							{
								//WE MUST PUT VALUES ABOVE 0 LINE
								$textVertPos=(int)($this->y_axis_y2-$this->axis_value_padding);
							}
							else
							{
								//MIX OF BOTH POS AND NEG NUMBERS
								//WRITE VALUE Y AXIS BOTTOM VALUE (WILL BE UNDER BOTTOM OF GRID EVEN IF X AXIS IS FLOATING DUE TO
								$textVertPos=(int)($this->y_axis_y1+strlen($key)*$this->text_width+$this->axis_value_padding);
							}
							$textHorizPos=(int)($xStart+($this->bar_width/2)-($this->text_height/2));
							imagestringup($this->image, 2, $textHorizPos, $textVertPos, $key,  $this->x_axis_text_color);
						}
						else
						{
							if($this->bool_all_negative)
							{
								//WE MUST PUT VALUES ABOVE 0 LINE
								$textVertPos=(int)($this->y_axis_y2-$this->text_height-$this->axis_value_padding);
							}
							else
							{
								//MIX OF BOTH POS AND NEG NUMBERS
								//WRITE VALUE Y AXIS BOTTOM VALUE (WILL BE UNDER BOTTOM OF GRID EVEN IF X AXIS IS FLOATING DUE TO
								$textVertPos=(int)($this->y_axis_y1+($this->text_height*2/3)-$this->axis_value_padding);
							}
							//HORIZONTAL DATA KEYS
							$textHorizPos=(int)($xStart+($this->bar_width/2)-((strlen($key)*$this->text_width)/2));
							imagestring($this->image, 2, $textHorizPos, $textVertPos, $key,  $this->x_axis_text_color);
						}
					}
				}
				$xStart+=$this->bar_width+$this->space_width;
			}
		}
	}
	function finalizeColors()
	{
		if($this->bool_gradient)
		{
			$num_set=count($this->multi_gradient_colors_1);
			//LOOP THROUGH SET COLORS AND ADD BACKING COLORS IF NECESSARY
			if($num_set!=$this->data_set_count)
			{
				$color_darken_decimal=(100-$this->color_darken_factor)/100;
				while($num_set<$this->data_set_count)
				{
					$color_ref_1=$this->multi_gradient_colors_1[$num_set-1];
					$color_ref_2=$this->multi_gradient_colors_2[$num_set-1];
					$this->multi_gradient_colors_1[]=array( 
						(int)$color_ref_1[0]*$color_darken_decimal, 
						(int)$color_ref_1[1]*$color_darken_decimal, 
						(int)$color_ref_1[2]*$color_darken_decimal);
					$this->multi_gradient_colors_2[]=array(
						(int)$color_ref_2[0]*$color_darken_decimal, 
						(int)$color_ref_2[1]*$color_darken_decimal, 
						(int)$color_ref_2[2]*$color_darken_decimal);
					$num_set++;
				}	
			}
			while(count($this->multi_gradient_colors_1)>$this->data_set_count)
			{
				$temp=array_pop($this->multi_gradient_colors_1);
			}
			while(count($this->multi_gradient_colors_2)>$this->data_set_count)
			{
				$temp=array_pop($this->multi_gradient_colors_2);
			}
			$this->multi_gradient_colors_1=array_reverse($this->multi_gradient_colors_1);
			$this->multi_gradient_colors_2=array_reverse($this->multi_gradient_colors_2);
		}
		else if(!$this->bool_gradient)
		{
			$num_set=count($this->multi_bar_colors);
			if($num_set==0)
			{
				$this->multi_bar_colors[0]=$this->bar_color;
				$num_set=1;
			}
			//LOOP THROUGH SET COLORS AND ADD BACKING COLORS IF NECESSARY
			while($num_set<$this->data_set_count)
			{
				$color_ref=$this->multi_bar_colors[$num_set-1];
				$color_parts=imagecolorsforindex($this->image, $color_ref);
				$color_darken_decimal=(100-$this->color_darken_factor)/100;
				$this->multi_bar_colors[$num_set]=imagecolorallocate($this->image, 
					(int)$color_parts['red']*$color_darken_decimal, 
					(int)$color_parts['green']*$color_darken_decimal, 
					(int)$color_parts['blue']*$color_darken_decimal);
				$num_set++;
			}
			while(count($this->multi_bar_colors)>$this->data_set_count)
			{
				$temp=array_pop($this->multi_bar_colors);
			}
			$this->multi_bar_colors=array_reverse($this->multi_bar_colors);
		}
		if($this->bool_line)
		{
			if(!$this->bool_bars)
			{
				$num_set=count($this->line_color);
				if($num_set==0)
				{
					$this->line_color[0]=$this->line_color_default;
					$num_set=1;
				}
				//ONLY DARKEN EACH DATA SET'S LINES WHEN NO BARS PRESENT
				while($num_set<$this->data_set_count)
				{
					$color_ref=$this->line_color[$num_set-1];
					$color_parts=imagecolorsforindex($this->image, $color_ref);
					$color_darken_decimal=(100-$this->color_darken_factor)/100;
					$this->line_color[$num_set]=imagecolorallocate($this->image, 
						(int)$color_parts['red']*$color_darken_decimal, 
						(int)$color_parts['green']*$color_darken_decimal, 
						(int)$color_parts['blue']*$color_darken_decimal);
					$num_set++;
				}
			}
			else
			{
				$num_set=count($this->line_color);
				while($num_set<$this->data_set_count)
				{
					$this->line_color[$num_set]=$this->line_color_default;
					$num_set++;
				}
			}
			while(count($this->line_color)>$this->data_set_count)
			{
				$temp=array_pop($this->line_color);
			}
			$this->line_color=array_reverse($this->line_color);
		}	
	}
	function drawGradientBar($x1, $y1, $x2, $y2, $colorArr1, $colorArr2, $data_set_num)
	{
		if(!isset($this->bool_gradient_colors_found[$data_set_num])||$this->bool_gradient_colors_found[$data_set_num]==false)
		{
			$this->gradient_handicap[$data_set_num]=0;
			$numLines=abs($x1-$x2)+1;
			while($numLines*$this->data_set_count>$this->gradient_max)
			{
				//WE HAVE MORE LINES THAN ALLOWABLE COLORS
				//USE HANDICAP TO RECORD THIS
				$numLines/=2;
				$this->gradient_handicap[$data_set_num]++;
			}
			$color1R=$colorArr1[0];
			$color1G=$colorArr1[1];
			$color1B=$colorArr1[2];
			$color2R=$colorArr2[0];
			$color2G=$colorArr2[1];
			$color2B=$colorArr2[2];
			$rScale=($color1R-$color2R)/$numLines;
			$gScale=($color1G-$color2G)/$numLines;
			$bScale=($color1B-$color2B)/$numLines;
			$this->allocateGradientColors($color1R, $color1G, $color1B, $rScale, $gScale, $bScale, $numLines, $data_set_num);
		}
		$numLines=abs($x1-$x2)+1;
		if($this->gradient_handicap[$data_set_num]>0)
		{
			//IF HANDICAP IS USED, IT WILL ALLOW US TO MOVE THROUGH THE ARRAY MORE SLOWLY, DEPENDING ON THE SET VALUE
			$interval=$this->gradient_handicap[$data_set_num];
			for($i=0;$i<$numLines;$i++)
			{
				$adjusted_index=ceil($i/pow(2,$interval))-1;
				if($adjusted_index<0)
				{
					$adjusted_index=0;
				}
				imageline($this->image, $x1+$i, $y1, $x1+$i, $y2, $this->gradient_color_array[$data_set_num][$adjusted_index]);		
			}
		}
		else
		{
			//NORMAL GRADIENTS WITH COLORS < $this->gradient_max
			for($i=0;$i<$numLines;$i++)
			{
				imageline($this->image, $x1+$i, $y1, $x1+$i, $y2, $this->gradient_color_array[$data_set_num][$i]);		
			}
		}
	}
	function generateGrid()
	{
		//DETERMINE HORIZONTAL GRID LINES
		$horizGridArray=array();
		if($this->bool_user_data_range)
		{
			$min=$this->data_min;
		}
		else
		{
			$min=0;
		}
		$horizGridArray[]=$min;
		//USE OUR FUNCTION TO DETERMINE IDEAL Y AXIS SCALE INTERVAL
		$intervalFromZero=$this->determineAxisMarkerScale($this->data_max, $this->data_min);
		//IF WE HAVE POSITIVE VALUES, ADD GRID VALUES TO ARRAY 
		//UNTIL WE REACH THE MAX NEEDED (WE WILL GO 1 OVER)
		$cur=$min;
		while($cur<$this->data_max)
		{
			$cur+=$intervalFromZero;
			$horizGridArray[]=$cur;
		}
		//IF WE HAVE NEGATIVE VALUES, ADD GRID VALUES TO ARRAY 
		//UNTIL WE REACH THE MIN NEEDED (WE WILL GO 1 OVER)
		$cur=$min;
		while($cur>$this->data_min)
		{
			$cur-=$intervalFromZero;
			$horizGridArray[]=$cur;
		}
		//SORT NEEDED B/C WE WILL USE LAST VALUE LATER (MAX)
		sort($horizGridArray);
		//DETERMINE VERTICAL GRID LINES
		$vertGridArray=array();
		$vertGrids=$this->data_count+1;
		$interval=$this->bar_width+$this->space_width;
		//ASSEMBLE VERT GRIDLINE ARRAY
		for($i=1;$i<$vertGrids;$i++)
		{
			$vertGridArray[]=$this->y_axis_x1+($interval*$i);
		}
		//LOOP THROUGH EACH HORIZONTAL LINE
		if($this->bool_user_data_range)
		{
			$adjustment=$this->data_min*$this->unit_scale;
		}
		else
		{
			$adjustment=0;
		}
		
		foreach($horizGridArray as $value)
		{
			$yValue=(int)($this->x_axis_y1-($value*$this->unit_scale)+$adjustment);
			if($this->bool_grid)
			{
				imageline($this->image, $this->y_axis_x1, $yValue, $this->x_axis_x2 , $yValue, $this->grid_color);
			}
			//DISPLAY VALUE ON Y AXIS IF DESIRED USING CALC'D GRID VALUES
			if($this->bool_y_axis_values)
			{
				$adjustedYValue=$yValue-($this->text_height/2);
				$adjustedXValue=$this->y_axis_x1-(strlen($value)*$this->text_width)-$this->axis_value_padding;
				imagestring($this->image, 2, $adjustedXValue, $adjustedYValue, $value, $this->y_axis_text_color);
			}
		}
		if(!$this->bool_all_positive&&!$this->bool_user_data_range)
		{
			//RESET WITH BETTER VALUE BASED ON GRID MIN VALUE CALCULATIONS, NOT DATA MIN
			$this->y_axis_y1=$this->x_axis_y1-($horizGridArray[0]*$this->unit_scale);
		}
		//RESET WITH BETTER VALUE BASED ON GRID VALUE CALCULATIONS, NOT DATA MIN
		$this->y_axis_y2=$yValue;
		//LOOP THROUGH EACH VERTICAL LINE
		if($this->bool_grid)
		{
			foreach($vertGridArray as $value)
			{
				$xValue=$this->y_axis_y1;
				imageline($this->image, $value, $this->y_axis_y2, $value, $xValue , $this->grid_color);
			}
		}
		//DRAW GOAL LINES IF PRESENT (AFTER GRID) - DOESN'T GET EXECUTED IF ARRAY EMPTY
		foreach($this->goal_line_array as $yLocation)
		{
			$yLocation=(int)($this->x_axis_y1-($yLocation*$this->unit_scale)+$adjustment);
			imageline($this->image, $this->y_axis_x1, $yLocation, $this->x_axis_x2 , $yLocation, $this->goal_line_color);
		}
	}
	function generateDataPoints()
	{
		foreach($this->data_point_array as $pointArray)
		{
			imagefilledellipse($this->image, $pointArray[0], $pointArray[1], $this->data_point_width, $this->data_point_width, $this->data_point_color);
		}		
	}
	function generateXAxis()
	{
		imageline($this->image, $this->x_axis_x1, $this->x_axis_y1, $this->x_axis_x2, $this->x_axis_y2, $this->x_axis_color);
	}
	function generateYAxis()
	{
		imageline($this->image, $this->y_axis_x1, $this->y_axis_y1, $this->y_axis_x2, $this->y_axis_y2, $this->y_axis_color);
	}
	function generateBackgound()
	{
		imagefilledrectangle($this->image, 0, 0, $this->width, $this->height, $this->background_color);
	}
	function generateTitle()
	{
		//SPACING MAY HAVE CHANGED SINCE EARLIER
		//USE TOP MARGIN OR GRID TOP Y, WHICHEVER LESS
		$highestElement=($this->top_margin<$this->y_axis_y2) ? $this->top_margin : $this->y_axis_y2;
		$textVertPos=($highestElement/2)-($this->title_char_height/2); //CENTERED
		$titleLength=strlen($this->title_text);
		if($this->bool_title_center)
		{
			$this->title_x=($this->width/2)-(($titleLength*$this->title_char_width)/2);
			$this->title_y=$textVertPos;
		}
		else if($this->bool_title_left)
		{
			$this->title_x=$this->y_axis_x1;
			$this->title_y=$textVertPos;
		}
		else if($this->bool_title_right)
		{
			$this->title_x=$this->x_axis_x2-($titleLength*$this->title_char_width);
			$this->title_y=$textVertPos;
		}
		imagestring($this->image, 2, $this->title_x , $this->title_y , $this->title_text,  $this->title_color);
	}
	function calcTopMargin()
	{
		if($this->bool_title)
		{
			//INCLUDE SPACE FOR TITLE, APPROX MARGIN + 3*TITLE HEIGHT
			$this->top_margin=($this->height*($this->x_axis_default_percent/100))+$this->title_char_height;
		}
		else
		{
			//JUST USE DEFAULT SPACING
			$this->top_margin=$this->height*($this->x_axis_default_percent/100);
		}	
	}
	function calcRightMargin()
	{
		//JUST USE DEFAULT SPACING
		$this->right_margin=$this->width*($this->y_axis_default_percent/100);
	}
	function calcCoords()
	{
		//CALCULATE AXIS POINTS, ALSO USED FOR OTHER CALCULATIONS
		$this->x_axis_x1=$this->y_axis_margin;
		$this->x_axis_y1=$this->height-$this->x_axis_margin;
		$this->x_axis_x2=$this->width-$this->right_margin;
		$this->x_axis_y2=$this->height-$this->x_axis_margin;
		$this->y_axis_x1=$this->y_axis_margin;
		$this->y_axis_y1=$this->height-$this->x_axis_margin;
		$this->y_axis_x2=$this->y_axis_margin;
		$this->y_axis_y2=$this->top_margin;
	}
	function determineAxisMarkerScale($max, $min)
	{
		//FOR CALCLATION, TAKE RANGE OR MAX-0
		if($this->bool_user_data_range)
		{
			$range=abs($max-$min);
		}
		else
		{
			$range=(abs($max-$min)>abs($max-0)) ? abs($max-$min) : abs($max-0);
		}
		//MULTIPLY UP TO OVER 100, TO BETTER FIGURE INTERVAL
		$count=0;
		while(abs($range)<100)
		{
			$range*=10;
			$count++;
		}
		//DIVIDE INTO INTERVALS BASED ON HEIGHT / PRESET CONSTANT - AFTER ROUNDING WILL BE APPROX
		$divisor=round($this->height/$this->range_divisor_factor);
		$divided=round($range/$divisor);
		$result=$this->roundUpOneExtraDigit($divided);
		//IF ROUNDED UP W/ EXTRA DIGIT IS MORE THAN 200% OF DIVIDED VALUE,
		//ROUND UP TO NEXT SIG NUMBER WITH SAME NUM OF DIGITS
		if($result/$divided>=2)
		{
			$result=$this->roundUpSameDigits($divided);
		}
		//DIVIDE BACK DOWN, IF NEEDED
		for($i=0;$i<$count;$i++)
		{
			$result/=10;
		}
		return $result;	
	}
	function roundUpSameDigits($num)
	{           
		$len=strlen($num);  
		if(round($num, -1*($len-1))==$num) 
		{
			//WE ALREADY HAVE A SIG NUMBER
			return $num;
		}
		else
		{
			$firstDig=substr($num, 0,1);
			$secondDig=substr($num, 1,1);
			$rest=substr($num, 2);
			$secondDig=5;
			$altered=$firstDig.$secondDig.$rest;
			//AFTER REASSEMBLY, ROUND UP TO NEXT SIG NUMBER, SAME # OF DIGITS
			return round((int)$altered, -1*($len-1));
		}
	}
	function roundUpOneExtraDigit($num)
	{                     
		$len=strlen($num);  
		$firstDig=substr($num, 0,1);
		$rest=substr($num, 1);
		$firstDig=5;
		$altered=$firstDig.$rest;
		//AFTER REASSEMBLY, ROUND UP TO NEXT SIG NUMBER, ONE EXTRA # OF DIGITS
		return round((int)$altered, -1*($len)); 
	}
	function displayErrors()
	{
		if(count($this->error)>0)
		{
			$lineHeight=12;
			$errorColor = imagecolorallocate($this->image, 0, 0, 0);
			$errorBackColor = imagecolorallocate($this->image, 255, 204, 0);
			imagefilledrectangle($this->image, 0, 0, $this->width-1, 2*$lineHeight,  $errorBackColor);
			imagestring($this->image, 3, 2, 0, "!!----- PHPGraphLib Error -----!!",  $errorColor);
			foreach($this->error as $key => $errorText)
			{
				
				imagefilledrectangle($this->image, 0, ($key*$lineHeight)+$lineHeight, $this->width-1, ($key*$lineHeight)+2*$lineHeight,  $errorBackColor);	
				imagestring($this->image, 2, 2, ($key*$lineHeight)+$lineHeight, "[". ($key+1) . "] ". $errorText,  $errorColor);	
			}
			$errorOutlineColor = imagecolorallocate($this->image, 255, 0, 0);
			imagerectangle($this->image, 0, 0, $this->width-1,($key*$lineHeight)+2*$lineHeight,  $errorOutlineColor);		
		}
	}
	function addData($data, $data2='',$data3='')
	{
		if(is_array($data)){ $this->data_array[]=$data; }
		if(is_array($data2)){ $this->data_array[]=$data2; }
		if(is_array($data3)){ $this->data_array[]=$data3; }
		
		//ASSESS DATA
		$min=$this->data_max_allowable;
		$max=$this->data_min_allowable;
		$nonZero=false;
		$this->data_count=0;
		//GET RID OF BAD DATA, FIND MAX, MIN
		foreach($this->data_array as $data_set_num => $data_set)
		{
			foreach($data_set as $key => $item)
			{
				if(!is_numeric($item))
				{
					unset($this->data_array[$data_set_num][$key]);
				}
				else
				{
					if($item>0||$item<0){ $nonZero=true; }
					if($item<$min){ $min=$item; }
					if($item>$max){ $max=$item; }
				}
			}
			$count=count($this->data_array[$data_set_num]);
			$count>$this->data_count ? $this->data_count=$count : NULL; 
			if(!$nonZero||$this->data_count==0)
			{
				$this->error[]="Dataset " . ($data_set_num+1) . " contains invalid data.";
			}
		}
		$set_count=count($this->data_array);
		$this->data_set_count=$set_count;
		if($set_count==0)
		{
			$this->error[]="No valid datasets added in adddata() function.";
		}
		else
		{
			$this->bool_data=true;
			//MIN AND MAX FOR ALL DATASETS
			$this->data_min=$min;
			$this->data_max=$max;
			if($this->data_min>=0)
			{
				$this->bool_all_positive=true;
			}
			else if($this->data_max<=0)
			{
				$this->bool_all_negative=true;
			}
		}
	}
	function setupXAxis($percent='', $color='')
	{
		if($percent===false)
		{
			$this->bool_x_axis=false;
		}
		else
		{
			$this->bool_x_axis=true;
		}
		$this->bool_x_axis_setup=true;
		if(!empty($color)&&$arr=$this->returnColorArray($color))
		{
			$this->x_axis_color = imagecolorallocate($this->image, $arr[0], $arr[1], $arr[2]);
		}
		if(is_numeric($percent)&&$percent>0)
		{ 
			$percent=$percent/100;
			$this->x_axis_margin=(int)($this->height*$percent);
		}
		else
		{
			$percent=$this->x_axis_default_percent/100;
			$this->x_axis_margin=(int)($this->height*$percent);
		}	
	}
	function setupYAxis($percent='', $color='')
	{
		if($percent===false)
		{
			$this->bool_y_axis=false;
		}
		else
		{
			$this->bool_y_axis=true;
		}
		$this->bool_y_axis_setup=true;
		if(!empty($color)&&$arr=$this->returnColorArray($color))
		{
			$this->y_axis_color = imagecolorallocate($this->image, $arr[0], $arr[1], $arr[2]);
		}
		if(is_numeric($percent)&&$percent>0)
		{ 
			$this->y_axis_margin=(int)($this->width*($percent/100));
		}
		else
		{
			$percent=$this->y_axis_default_percent/100;
			$this->y_axis_margin=(int)($this->width*$percent);
		}
	}
	function setRange($max, $min)
	{
		if($min<0||$max<=0){ $this->error[]="Range in setRange() must be non-negative."; }
		else if(is_numeric($max)&&is_numeric($min)&&$max>$min)
		{
			$this->data_range_max=$max;
			$this->data_range_min=$min;
			$this->bool_user_data_range=true;
		}
		else{ $this->error[]="Range in setRange() not specified properly. Consult documentation."; }
	}
	function setTitle($title)
	{
		if(!empty($title))
		{
			$this->title_text=$title;
			$this->bool_title=true;
		}
		else{ $this->error[]="String arg for setTitle() not specified properly."; }	
	}
	function setTitleLocation($location)
	{
		$this->bool_title_left=false;
		$this->bool_title_right=false;
		$this->bool_title_center=false;
		switch(strtolower($location))
		{
			case 'left': $this->bool_title_left=true; break;
			case 'right': $this->bool_title_right=true; break;
			case 'center': $this->bool_title_center=true; break;
			default: $this->error[]="String arg for setTitleLocation() not specified properly.";
		}	
	}
	function setBars($bool)
	{
		if(is_bool($bool)){ $this->bool_bars=$bool;}
		else{ $this->error[]="Boolean arg for setBars() not specified properly."; }
	}
	function setGrid($bool)
	{
		if(is_bool($bool)){ $this->bool_grid=$bool;}
		else{ $this->error[]="Boolean arg for setGrid() not specified properly."; }
	}
	function setXValues($bool)
	{
		if(is_bool($bool)){ $this->bool_x_axis_values=$bool;}
		else{ $this->error[]="Boolean arg for setXValues() not specified properly."; }
	}
	function setYValues($bool)
	{
		if(is_bool($bool)){ $this->bool_y_axis_values=$bool;}
		else{ $this->error[]="Boolean arg for setYValues() not specified properly."; }
	}
	function setXValuesHorizontal($bool)
	{
		if(is_bool($bool)){ 
			($bool) ? $this->bool_x_axis_values_vert=false : $this->bool_x_axis_values_vert=true;
		}
		else{ $this->error[]="Boolean arg for setXValuesHorizontal() not specified properly."; }
	}
	function setXValuesVertical($bool)
	{
		if(is_bool($bool)){ $this->bool_x_axis_values_vert=$bool;}
		else{ $this->error[]="Boolean arg for setXValuesVertical() not specified properly."; }
	}
	function setBarOutline($bool)
	{
		if(is_bool($bool)){ $this->bool_bar_outline=$bool;}
		else{ $this->error[]="Boolean arg for setBarOutline() not specified properly."; }
	}
	function setDataPoints($bool)
	{
		if(is_bool($bool)){ $this->bool_data_points=$bool;}
		else{ $this->error[]="Boolean arg for setDataPoints() not specified properly."; }
	}
	function setDataPointSize($size)
	{
		if(is_numeric($size)){ $this->data_point_width=$size;}
		else{ $this->error[]="Data point size in setDataPointSize() not specified properly."; }
	}
	function setDataValues($bool)
	{
		if(is_bool($bool)){ $this->bool_data_values=$bool;}
		else{ $this->error[]="Boolean arg for setDataValues() not specified properly."; }
	}
	function setLine($bool)
	{
		if(is_bool($bool)){ $this->bool_line=$bool;}
		else{ $this->error[]="Boolean arg for setLine() not specified properly."; }
	}
	function setGoalLine($yValue)
	{
		if(is_numeric($yValue))
		{
			$this->goal_line_array[]=$yValue;
		}
		else
		{
			$this->error[]="Goal line Y axis value not specified properly.";
		}
	}
	//-------------"PRIVATE" COLOR HANDLING FUNCTIONS---------------//
	function allocateColors()
	{
		$this->background_color = imagecolorallocate($this->image, 255, 255, 255);
		$this->grid_color = imagecolorallocate($this->image, 220, 220, 220);
		$this->bar_color = imagecolorallocate($this->image, 200, 200, 200);
		$this->line_color_default = imagecolorallocate($this->image, 100, 100, 100);
		$this->x_axis_text_color = $this->line_color_default;
		$this->y_axis_text_color = $this->line_color_default;
		$this->data_value_color = $this->line_color_default;
		$this->title_color = imagecolorallocate($this->image, 0, 0, 0);
		$this->outline_color = $this->title_color;
		$this->data_point_color = $this->title_color;
		$this->x_axis_color = $this->title_color;
		$this->y_axis_color = $this->title_color;
		$this->goal_line_color = $this->title_color;
		//New Legend Colors
		$this->legend_outline_color=$this->grid_color;
		$this->legend_color=$this->background_color;
		$this->legend_text_color=$this->line_color_default;
		$this->legend_swatch_outline_color=$this->line_color_default;
	}
	function returnColorArray($color)
	{
		//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 'black': return array(0,0,0); break;
			case 'silver': return array(192,192,192); break;
			case 'gray': return array(128,128,128); break;
			case 'white': return array(255,255,255); break;
			case 'maroon': return array(128,0,0); break;
			case 'red': return array(255,0,0); break;
			case 'purple': return array(128,0,128); break;
			case 'fuscia': return array(255,0,255); break;
			case 'green': return array(0,128,0); break;
			case 'lime': return array(0,255,0); break;
			case 'olive': return array(128,128,0); break;
			case 'yellow': return array(255,255,0); break;
			case 'navy': return array(0,0,128); break;	
			case 'blue': return array(0,0,255); break;
			case 'teal': return array(0,128,128); break;
			case 'aqua': return array(0,255,255); break;	
		}
		$this->error[]="Color name \"$color\" not recogized.";
		return false;
	}
	function allocateGradientColors($color1R, $color1G, $color1B, $rScale, $gScale, $bScale, $num, $data_set_num)
	{
		//CALUCLATE THE COLORS USED IN OUR GRADIENT AND STORE THEM IN ARRAY
		$this->gradient_color_array[$data_set_num]=array();
		for($i=0;$i<=$num+1;$i++)
		{
			$this->gradient_color_array[$data_set_num][$i] = imagecolorallocate($this->image, $color1R-($rScale*$i), $color1G-($gScale*$i), $color1B-($bScale*$i));
		}
		$this->bool_gradient_colors_found[$data_set_num]=true;
	}
	function setGenericColor($inputColor, $var, $errorMsg)
	{
		//CAN BE USED FOR MOST COLOR SETTING OPTIONS
		if(!empty($inputColor)&&$arr=$this->returnColorArray($inputColor))
		{
			eval($var . ' = imagecolorallocate($this->image, $arr[0], $arr[1], $arr[2]);');
			return true;	
		}
		else
		{
			$this->error[]=$errorMsg;
			return false;
		}
	}
	//-------------------"PUBLIC" COLOR FUNCTIONS----------------------//
	function setBackgroundColor($color)
	{
		if($this->setGenericColor($color, '$this->background_color', "Background color not specified properly."))
		{
			$this->bool_background=true;
		}
	}
	function setTitleColor($color)
	{
		$this->setGenericColor($color, '$this->title_color', "Title color not specified properly.");
	}
	function setTextColor($color)
	{
		$this->setGenericColor($color, '$this->x_axis_text_color', "X axis text color not specified properly.");
		$this->setGenericColor($color, '$this->y_axis_text_color', "Y axis Text color not specified properly.");
	}
	function setXAxisTextColor($color)
	{
		$this->setGenericColor($color, '$this->x_axis_text_color', "X axis text color not specified properly.");
	}
	function setYAxisTextColor($color)
	{
		$this->setGenericColor($color, '$this->y_axis_text_color', "Y axis Text color not specified properly.");
	}
	function setBarColor($color,$color2='',$color3='')
	{
		$this->setGenericColor($color, '$this->multi_bar_colors[]', "Bar color not specified properly.");
		if(!empty($color2)){
			$this->setGenericColor($color2, '$this->multi_bar_colors[]', "Bar color 2 not specified properly.");
		}
		if(!empty($color3)){
			$this->setGenericColor($color3, '$this->multi_bar_colors[]', "Bar color 3 not specified properly.");
		}	
	}
	function setGridColor($color)
	{
		$this->setGenericColor($color, '$this->grid_color', "Grid color not specified properly.");
	}
	function setBarOutlineColor($color)
	{
		$this->setGenericColor($color, '$this->outline_color', "Bar outline color not specified properly.");
	}
	function setDataPointColor($color)
	{
		$this->setGenericColor($color, '$this->data_point_color', "Data point color not specified properly.");
	}
	function setDataValueColor($color)
	{
		$this->setGenericColor($color, '$this->data_value_color', "Data value color not specified properly.");	
	}
	function setLineColor($color, $color2='', $color3='')
	{
		$this->setGenericColor($color, '$this->line_color[]', "Line color not specified properly.");
		if(!empty($color2)){
			$this->setGenericColor($color2, '$this->line_color[]', "Line color 2 not specified properly.");
		}
		if(!empty($color3)){
			$this->setGenericColor($color3, '$this->line_color[]', "Line color 3 not specified properly.");	
		}
	}
	function setGoalLineColor($color)
	{
		$this->setGenericColor($color, '$this->goal_line_color', "Goal line color not specified properly.");
	}
	function setGradient($color1, $color2, $color3='', $color4='', $color5='', $color6='')
	{
		if(!empty($color1)&&!empty($color2)&&($arr1=$this->returnColorArray($color1))&&($arr2=$this->returnColorArray($color2)))
		{
			$this->bool_gradient=true;
			$this->multi_gradient_colors_1[]=$arr1;
			$this->multi_gradient_colors_2[]=$arr2;
		}
		else
		{
			$this->error[]="Gradient color(s) not specified properly.";
		}
		//GRADIENTS 3,4,5,6 OPTIONAL
		if(!empty($color3)&&!empty($color4)&&($arr1=$this->returnColorArray($color3))&&($arr2=$this->returnColorArray($color4)))
		{
			$this->bool_gradient=true;
			$this->multi_gradient_colors_1[]=$arr1;
			$this->multi_gradient_colors_2[]=$arr2;
		}
		if(!empty($color5)&&!empty($color6)&&($arr1=$this->returnColorArray($color5))&&($arr2=$this->returnColorArray($color6)))
		{
			$this->bool_gradient=true;
			$this->multi_gradient_colors_1[]=$arr1;
			$this->multi_gradient_colors_2[]=$arr2;
		}
	}
	//Legend Related Functions
	function setLegend($bool)
	{
		if(is_bool($bool)){ $this->bool_legend=$bool;}
		else{ $this->error[]="Boolean arg for setLegend() not specified properly."; }
	}
	function setLegendColor($color)
	{
		$this->setGenericColor($color, '$this->legend_color', "Legend color not specified properly.");
	}
	function setLegendTextColor($color)
	{
		$this->setGenericColor($color, '$this->legend_text_color', "Legend text color not specified properly.");
	}
	function setLegendOutlineColor($color)
	{
		$this->setGenericColor($color, '$this->legend_outline_color', "Legend outline color not specified properly.");
	}
	function setSwatchOutlineColor($color)
	{
		$this->setGenericColor($color, '$this->legend_swatch_outline_color', "Swatch outline color not specified properly.");
	}
	function setLegendTitle($title, $title2='', $title3='')
	{
		if(!empty($title)){ 		
			$len=strlen($title);
			if($len>$this->legend_max_chars){ 
				$title=substr($title, 0, $this->legend_max_chars); 
				$this->legend_total_chars[]=$this->legend_max_chars;
			}
			else{$this->legend_total_chars[]=$len;}
			$this->legend_titles[]=$title; 
		}
		else{$this->error[]="String arg 1 for setLegendTitles() not specified properly."; }
		if(!empty($title2)){ 
			$len=strlen($title2);
			if($len>$this->legend_max_chars){ 
				$title2=substr($title2, 0, $this->legend_max_chars); 
				$this->legend_total_chars[]=$this->legend_max_chars;
			}
			else{$this->legend_total_chars[]=$len;}
			$this->legend_titles[]=$title2; 
		}
		if(!empty($title3)){ 
			$len=strlen($title3);
			if($len>$this->legend_max_chars){ 
				$title3=substr($title3, 0, $this->legend_max_chars); 
				$this->legend_total_chars[]=$this->legend_max_chars;
			}
			else{$this->legend_total_chars[]=$len;}
			$this->legend_titles[]=$title3; 
		}
	}
	function generateLegend()
	{
		$swatchToTextOffset=($this->legend_text_height-6)/2;
		$swatchSize=$this->legend_text_height-2*$swatchToTextOffset;
		//CALC HEIGHT / WIDTH BASED ON # OF DATA SETS
		$this->legend_height=$this->legend_text_height+(2*$this->legend_padding);
		$totalChars=0;
		for($i=0;$i<$this->data_set_count;$i++)
		{
			//COULD HAVE MORE TITLES THAN DATA SETS - CHECK FOR THIS
			if(isset($this->legend_total_chars[$i])){ $totalChars+=$this->legend_total_chars[$i]; }
		}
		$this->legend_width=$totalChars*$this->legend_text_width+($this->legend_padding*1.5)+
			($this->data_set_count*($swatchSize+($this->legend_padding*2)));
		$this->legend_x=$this->x_axis_x2-$this->legend_width;
		$highestElement=($this->top_margin<$this->y_axis_y2) ? $this->top_margin : $this->y_axis_y2;
		$this->legend_y=($highestElement/2)-($this->legend_height/2); //CENTERED
				
		//BACKGROUND
		imagefilledrectangle($this->image, $this->legend_x, $this->legend_y, $this->legend_x+$this->legend_width, 
			$this->legend_y+$this->legend_height, $this->legend_color);
		//BORDER
		imagerectangle($this->image, $this->legend_x, $this->legend_y, $this->legend_x+$this->legend_width, 
			$this->legend_y+$this->legend_height, $this->legend_outline_color);
		
		$length_covered=0;
		for($i=0;$i<$this->data_set_count;$i++)
		{
			$data_label='';
			if(isset($this->legend_titles[$i]))
			{
				$data_label=$this->legend_titles[$i];
			}
			$yValue=$this->legend_y+$this->legend_padding;
			$xValue=$this->legend_x+$this->legend_padding+($length_covered*$this->legend_text_width)+($i*4*$this->legend_padding);
			$length_covered+=strlen($data_label);
			//DRAW COLOR BOXES
			if($this->bool_bars)
			{
				if($this->bool_gradient){
					$color=$this->gradient_color_array[$this->data_set_count-$i-1][0];
				}
				else{
					$color=$this->multi_bar_colors[$this->data_set_count-$i-1];
				}
			}
			else if($this->bool_line&&!$this->bool_bars)
			{
				$color=$this->line_color[$this->data_set_count-$i-1];
			}
			
			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);	
			imagestring($this->image, 2, $xValue+(2*$this->legend_padding+2), $yValue, $data_label, $this->legend_text_color);
		}
	}
}
?>