What you will need
- A server with php installed and GD Library enabled
The Process
- Prepare your php document.
- Set up your data.
- Create the canvas.
- Draw the grid and axis labels.
- Draw the bars.
- Create the chart labels.
- Output the chart.
The Source
Part One: Preparing your php document.
Open up your favorite script editor and make a new file. I prefer NotePad++ but any pure text editor will suffice.
Save the file as barchart.php
Part Two: Setting up the data
A bar chart is useless without data so lets make some!
Grab the following code below and paste it into your php file.
//Setting the chart variables $graphTitle = "Favorite Reptile"; $xLabel = "Reptile"; $yLabel = "Votes"; $data['Alligator'] = 12; $data['Crocodile'] = 15; $data['Iguana'] = 17; $data['Snake'] = 3; $data['Turtle'] = 25; $data['Lizard'] = 3; $data['Barney'] = 1; //getting the maximum and minimum values for Y $newData = $data; asort($newData); //minimum $places = strlen(current($newData)); $mod = pow(10, $places-1); $min = $mod - current($newData); //maximum $places = strlen(end($newData)); $mod = pow(10, $places-1); $max = $mod + end($newData); $yAxis = array("min" => $min, "max" => $max);
The first part is pretty self-explanatory so I will skip that and explain what is going on below that.
Below I’m saving the $data array into a new array because I want to preserve the original values.
Then I sort the array from lowest to highest value.
//getting the maximum and minimum values for Y $newData = $data; asort($newData);
Ok, the next part is a little tricky. In a graph it’s always a good idea to have a little space (a buffer) between the top of the graph and the highest value so that the bar doesn’t go directly to the top. Therefore we need to find a maximum value for the Y-Axis that is greater than the highest value of the data.
The same applies for the minimum value, if the lowest value is 2 we would want the graph to start at 0. However, if the minimum value is 200, we may want to start at a higher value.
Take a look below to see my method for finding this buffer
//minimum $places = strlen(current($newData)); //string length of first element in array. so strlen(1) = 1; $mod = pow(10, $places-1); //raising that number minus 1 to the power of 10. so pow(10, 0) = 1; $min = $mod - current($newData); //subtracting that from the minimum. so 1 - 1 = 0; <-your y-axis minimum //maximum $places = strlen(end($newData)); //strlen(25) = 2; $mod = pow(10, $places-1); //pow(10, 1) = 10; $max = $mod + end($newData); //10 + 25 = 35; <-- your new y-axis maximum //storing those min and max values into an array $yAxis = array("min"=>$min, "max"=> $max);
Part Three: Preparing the Canvas.
Ok that wasn’t so bad, now copy the following code and paste it below the previous one.
//------------------------------------------------ // Preparing the Canvas //------------------------------------------------ //setting the image dimensions $canvasWidth = 500; $canvasHeight = 300; $perimeter = 50; //creating the canvas $canvas = imagecreatetruecolor($canvasWidth, $canvasHeight); //allocating the colors $white = imagecolorallocate($canvas, 255, 255, 255); $black = imagecolorallocate($canvas, 0,0,0); $yellow = imagecolorallocate($canvas, 248, 255, 190); $blue = imagecolorallocate($canvas, 3,12,94); $grey = imagecolorallocate($canvas, 102, 102, 102); $lightGrey = imagecolorallocate($canvas, 216, 216, 216); //getting the size of the fonts $fontwidth = imagefontwidth(2); $fontheight = imagefontheight(2); //filling the canvas with light grey imagefill($canvas, 0,0, $lightGrey);
Again the code above is pretty self-explanatory.
But let me explain what I mean by perimeter.
In a graph you want to have some space to write your labels, therefore it’s necessary to have a perimeter. By setting this value you’re setting how wide you want it to be.
At this point you should have something that looks like this
Part Four: Drawing the Grid and Axis Labels
I like to place a grid down on my graphs because it makes building them so much easier. Plus when it comes to testing I have a visual way to tell if the data is being represented accurately.
Copy the following code and paste it below the previous one.
//------------------------------------------------ // Preparing the grid //------------------------------------------------ //getting the size of the grid $gridWidth = $canvasWidth - ($perimeter*2); $gridHeight = $canvasHeight - ($perimeter*2); //getting the grid plane coordinates $c1 = array("x"=>$perimeter, "y"=>$perimeter); $c2 = array("x"=>$gridWidth+$perimeter, "y"=>$perimeter); $c3 = array("x"=>$gridWidth+$perimeter, "y"=>$gridHeight+$perimeter); $c4 = array("x" => $perimeter, "y" => $gridHeight+$perimeter); //------------------------------------------------ //creating the grid plane //------------------------------------------------ imagefilledrectangle($canvas, $c1['x'], $c1['y'], $c3['x'], $c3['y'], $white); //finding the size of the grid squares $sqW = $gridWidth/count($data); $sqH = $gridHeight/$yAxis['max']; //------------------------------------------------ //drawing the vertical lines and axis values //------------------------------------------------ $verticalPadding = $sqW/2; foreach($data as $assoc=>$value) { //drawing the line imageline($canvas, $verticalPadding+$c4['x']+$increment, $c4['y'], $verticalPadding+$c1['x']+$increment, $c1['y'], $black); //axis values $wordWidth = strlen($assoc)*$fontwidth; $xPos = $c4['x']+$increment+$verticalPadding-($wordWidth/2); ImageString($canvas, 2, $xPos, $c4['y'], $assoc, $black); $increment += $sqW; } //------------------------------------------------ //drawing the horizontel lines and axis labels //------------------------------------------------ //resetting the increment back to 0 $increment = 0; for($i=$yAxis['min']; $i<$yAxis['max']; $i++) { //main lines //often the y-values can be in the thousands, if this is the case then we don't want to draw every single //line so we need to make sure that a line is only drawn every 50 or 100 units. if($i%$mod==0){ //drawing the line imageline($canvas, $c4['x'], $c4['y']+$increment, $c3['x'], $c3['y']+$increment, $black); //axis values $xPos = $c1['x']-($fontwidth*strlen($i))-5; ImageString($canvas, 2, $xPos, $c4['y']+$increment-($fontheight/2), $i, $black); } //tics //these are the smaller lines between the longer, main lines. elseif(($mod/5)>1 &amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp; $i%($mod/5)==0) { imageline($canvas, $c4['x'], $c4['y']+$increment, $c4['x']+10, $c4['y']+$increment, $grey); } //because these lines begin at the bottom we want to subtract $increment-=$sqH; }
Below I’m setting the width and height of the grid, remember that we want the grid to go inside the perimeter.
//getting the size of the grid $gridWidth = $canvasWidth - ($perimeter*2); $gridHeight = $canvasHeight - ($perimeter*2);
The next part is the most important of the whole project!
I like to assign coordinates to my graph so that I don’t have to constantly remember that the first point lies 30 pixels to the left of the canvas. It just makes graph construction and manipulation much easier.
//getting the grid plane coordinates $c1 = array("x"=>$perimeter, "y"=>$perimeter); $c2 = array("x"=>$gridWidth+$perimeter, "y"=>$perimeter); $c3 = array("x"=>$gridWidth+$perimeter, "y"=>$gridHeight+$perimeter); $c4 = array("x"=>$perimeter, "y"=>$gridHeight+$perimeter)
The imagefilledrectangle() function as you might suspect draws a filled rectangle on the canvas. Take a look at the illustration below to see how it works
imagefilledrectangle($canvas, $c1['x'], $c1['y'], $c3['x'], $c3['y'], $white);
A grid as you well know is a collection of squares or rectangles. The height and width of those shapes represents a unit, therefore we have to find the unit for our graph.
//finding the size of the grid squares $sqW = $gridWidth/count($data); $sqH = $gridHeight/$yAxis['max'];
Now it’s time to draw the vertical lines and axis labels on our graph!
//------------------------------------------------ //drawing the vertical lines and axis labels //------------------------------------------------ $verticalPadding = $sqW/2; foreach($data as $assoc=>$value) { //drawing the line imageline($canvas, $verticalPadding+$c4['x']+$increment, $c4['y'], $verticalPadding+$c1['x']+$increment, $c1['y'], $black); //axis labels $wordWidth = strlen($assoc)*$fontwidth; $xPos = $c4['x']+$increment+$verticalPadding-($wordWidth/2); ImageString($canvas, 2, $xPos, $c4['y'], $assoc, $black); $increment += $sqW; }
It is good technique to have a buffer between the y-axis and the first vertical line or else it would be hard to read. Look below, notice that the one on the right looks much better and will give us nice evenly spaced bars.
$verticalPadding = $sqW/2;
I’m going to assume that you have an understanding of loops
The imageline() function looks very complex but just work it out slowly you’ll see that it makes a lot of sense.
//drawing the line imageline($canvas, $verticalPadding+$c4['x']+$increment, $c4['y'], $verticalPadding+$c1['x']+$increment, $c1['y'], $black)
Next we’re going to add the labels for the x-axis.
The labels are going to go on graph from left to right and will be below the grid plane.
//axis labels $wordWidth = strlen($assoc)*$fontwidth; //getting the width of the word $xPos = $c4['x']+$increment+$verticalPadding-($wordWidth/2); //dividing that by 2 to center the word ImageString($canvas, 2, $xPos, $c4['y'], $assoc, $black); //outputting the word on canvas
The lines for y-axis follow a similar method.
//------------------------------------------------ //drawing the horizontal lines and axis labels //------------------------------------------------ //resetting the increment back to 0 $increment = 0; for($i=$yAxis['min']; $i<$yAxis['max']; $i++) { //main lines //often the y-values can be in the thousands, if this is the case then we don't want to draw every single //line so we need to make sure that a line is only drawn every 50 or 100 units. if($i%$mod==0){ //drawing the line imageline($canvas, $c4['x'], $c4['y']+$increment, $c3['x'], $c3['y']+$increment, $black); //axis values $xPos = $c1['x']-($fontwidth*strlen($i))-5; ImageString($canvas, 2, $xPos, $c4['y']+$increment-($fontheight/2), $i, $black); } //tics //these are the smaller lines between the longer, main lines. elseif(($mod/5)>1 &amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp; $i%($mod/5)==0) { imageline($canvas, $c4['x'], $c4['y']+$increment, $c4['x']+10, $c4['y']+$increment, $grey); } //because these lines begin at the bottom we want to subtract $increment-=$sqH; }
Copy the following code and paste it below your previous code. This creates a vertical black line on the left hand side of the graph.
imageline($canvas, $c1['x'], $c1['y'], $c4['x'], $c4['y'], $black);
Part Five: Drawing the bars
Copy the following code and paste it below your previous code.
//------------------------------------------------ // Making the vertical bars //------------------------------------------------ $increment = 0; //resetting the increment value $barWidth = $sqW*.2; //setting a width size for the bars, play with this number foreach($data as $assoc=>$value) { $yPos = $c4['y']-($value*$sqH); $xPos = $c4['x']+$increment+$verticalPadding-($barWidth/2); imagefilledrectangle($canvas, $xPos, $c4['y'], $xPos+$barWidth, $yPos, $blue); $increment += $sqW; }
The part that we need to pay attention to here is $yPos, we want the bar to start at the bottom of the graph and then go up the appropriate y-value to make the right height.
Part Six: Making the Graph Labels
Copy and paste
//Graph Title ImageString($canvas, 2, ($canvasWidth/2)-(strlen($graphTitle)*$fontwidth)/2, $c1['y']-($perimeter/2), $graphTitle, $green); //X-Axis ImageString($canvas, 2, ($canvasWidth/2)-(strlen($xLabel)*$fontwidth)/2, $c4['y']+($perimeter/2), $xLabel, $green); //Y-Axis ImageStringUp($canvas, 2, $c1['x']-$fontheight*3, $canvasHeight/2+(strlen($yLabel)*$fontwidth)/2, $yLabel, $green);
Part Seven: Final Output
Copy. . you know the drill 😉
header("content-type: image/jpg"); imagejpeg($canvas); imagedestroy($canvas);
Tadaa! A graph!
Conclusion
I hope this tutorial has been helpful, if you have any questions or suggestions please feel free to contact me.
Comments
Post by Luca on July 23, 2007
Hi there,very good tutorial, thank you for sharing.Though, I have no idea why but I had to correct this line of the code:imagefilledrectangle($canvas, $xPos, $c4['y'], $xPos $barWidth, $yPos, $blue);with this one:imagefilledrectangle($canvas, $xPos, $yPos, $xPos $barWidth, $c4['y'], $blue);in order to make the whole tutorial run.Thanks alot again,Luca
Post by sean on December 12, 2007
Very good tutorial but i had to remove the following code to display this table correctly: $xPos = $c4['x'] $increment $verticalPadding-($wordWidth/2); ImageString($canvas, 2, $xPos, $c4['y'], $assoc, $black); $increment = $sqW;*/
Post by Scott Cronk on April 9, 2009
I have data sets with negative values and positive values and your tutorial code only seems to work for positive values.. negatives are drawn below and outside the canvas. I am planning to decipher your code to develop a fix ... but perhaps you have a quick and easy fix I could use???
Post by Anthony on August 12, 2010
Do you have a tutorial on how to graph data from a database?
Post by fsaravia on October 7, 2011
got some errors! solved by adding: // line 64 $green = imagecolorallocate($canvas, 255, 102, 102); // line 101 $increment = 0;
Post by akshay on April 4, 2013
is there any code available to create the same kind of graph dynamically with values from a database ?
Post by Pat on January 18, 2015
I'd also like to know if you have a tutorial on how to graph data from a database?