scriptygoddess

23 Feb, 2008

How to make a progress/goal (thermometer-like) bar graph with PHP

Posted by: Jennifer In: PHP

On one of my projects recently, they needed a dynamic bar graph that would show the progress towards a goal of donations. I've never done something like that before, and it turns out it's actually pretty simple to do. I'll explain how the code works and then include everything at the end.

What we'll be doing is calling a php script into the src of an img tag in HTML. The php processes how to draw that image. We pass values to the PHP script to tell it what to draw. I'm going to show this with those values hardcoded in that src path – but in practice these values would probably be pulled from a database that totals up those numbers into a "total donation" value.

So first things first – since we'll be passing values in the src path – we need to check for them and then pull them into our script using $_GET: (also we'll say we're making a image with header)

if (isset($_GET['cur']) && isset($_GET['goal']) && isset($_GET['height']) && isset($_GET['width']) ) {
header("Content-type: image/png");
//setting the values we'll use in our script pulled in from GET
$height = trim($_GET['height']);
$width = trim($_GET['width']);
$goal = trim($_GET['goal']);
$current = trim($_GET['cur']);

Next – for the text to be displayed on the bar graph – we'll want to say what percent towards the goal we are. (Or if the goal is reached – just say 100%)

if ($current > $goal) {
//if we've reached the goal - just max out current value and say 100% achieved
//if you don't mind text saying over 100% then you can remove the next two lines
$current = $goal;
$percent= "100%!";
} else if ($current == "" || $current == "0") {
//if no donations - just say 0%
$percent = "0%";
} else {
//otherwise calculate the percent to goal text
$percent = round(($current/$goal)*100) . "%";
}

Now lets start building our bar graph. First we create an image and assign a variable name so we can apply stuff to it. Then, to create colors we can use on our image, we use the ImageColorAllocate function in PHP – so that it understands it's a color.

$im = @imagecreate($width,$height)
or die("Cannot Initialize new GD image stream");
$bg = imagecolorallocate($im,200,200,200); // grey
$fg = imagecolorallocate($im,255,0,0); //red
$text_color = imagecolorallocate($im, 0, 0, 0); //black

(Side note: The first color that is called with imagecolorallocate… this is the color which is used as the default background of the image even though it isn't SPECIFICALLY "applied" to the image as such in any particular function.)

Now we need to calculate the "fill" box. The box I'm creating in this example grows UP. This part for some reason I had the toughest time trying to understand what the values were and how to calculate it. What "x" and "y" were in relation to my graphic. So now that I have a basic understanding of how it works, I'll try to explain it in the way that I understand it.

We're going to use the imagefilledrectangle function to create the box in our graphic that shows how many donations we have. PHP.net shows the function like this:

imagefilledrectangle ($image, $x1, $y1, $x2, $y2, $color)

x1 and y1 are coordinates on our image. It starts at 0 at the top and then works across our width for our x value, and down our height for y value. x1,y1 are coordinates of the left point of our rectangle, and x2,y2 are the coordinates of the right point which is on the opposite/diagonal side of our rectangle. If we wanted to fill in our image completely and our image was 20pixels WIDE and 100pixels HIGH: the top left of the image x,y coordinate value is 0,0. Then we tell it the second x,y coordinate – again we go across the full width of the image for the x value (20pixels) and go down the height of the image to get the y value (100pixels). And if that didn't confuse you, I made a diagram that should make all that clear as mud:

X Y coordinate diagram

In our thermometer/goal graph – that box is dynamic – so we need to calcuate that first x,y coordinate based on our donations. Specifically – the y coordinate – because that tells us how filled up our thermometer is. Here is how I calculate it. (Because you actually are still here even after my convoluted coordinate explanation I'll spare you the explanation of how the calculation works and you can take my word for it that it will figure out the correct height based on the goal and current achieved donation values.)

$fill = $height-(($current/$goal)*$height);

And now we make our rectangle:

imagefilledrectangle($im, 0, $fill, $width, $height, $fg);

Now lets write on our picture what the percentage is. imagstring has a few parameters that get passed – first is of course the image variable, then a "font" number (default fonts are 1-5), then another x , y coordinate. This will be the top left corner of where the text will show up. For simplicity sake I just have it a little off the corner at the top., then our text (we defined this earlier), and then our text color – (also defined earlier)

imagestring($im, 3, 2, 2, $percent, $text_color);

Then we output our image to the browser:

imagepng($im);

Clean up our mess and close our "if" statement:

imagedestroy($im);
}

Here's a demo.

Here's the whole thing in one file (rename it with a .php extension).

Call it in your html like this:

<img src="goalgraph.php?cur=1650&goal=3000&width=30&height=100" alt="Help us get to our goal" />

Bonus Tip
Now position your bar graph over another image with css.
(I removed the text in that last example because it was too thin…)

10 Responses to "How to make a progress/goal (thermometer-like) bar graph with PHP"

1 | Aiko

March 3rd, 2008 at 2:47 pm

Avatar

Great script ! Just what I was looking for. I'm using it on my sidebar and use PHP to specify the current value so it's fully automated now.

Thanks for sharing!

2 | Valerie

May 6th, 2008 at 9:55 am

Avatar

hey, great tutorial. :)

was wondering how easy it would be to change it to be a horizontal bar graph, a bit like a ticker?

3 | Jennifer

May 6th, 2008 at 10:19 am

Avatar

@Valerie
I believe you need to change the script with this line:
$fill = $height-(($current/$goal)*$height);
Instead of having the fill be calculated on the HEIGHT – you want it calculated to the WIDTH – so changing $height to $width where it appears in that line should do the trick… ?

4 | Nicholas Klick

May 20th, 2008 at 8:47 pm

Avatar

Jennifer,

This is a nice method that I had not seen before. I like the use of straight php code for the bar graph.

We recently created a goal graph but used a horizontally tiled image (using nested div tags), overlaid on a BG-image. The width of the interior div inherits the percentage towards the goal.

You can view it here: http://www.fightwithfact.com/impact.php

We loop thru the goal data from our database and then print it out:

for ($i = 0; $i records); $i++)
{

$title = $goalListObj->records[$i]->title;
$descrip = $goalListObj->records[$i]->description;
$actual = $goalListObj->records[$i]->actualNumber;
$target = $goalListObj->records[$i]->targetNumber;
$percentage = round(($actual / $target) * 100) ;

echo"$title ";
echo"$percentage%0
$target";
echo"$descrip";

5 | Nicholas Klick

May 20th, 2008 at 8:51 pm

Avatar

I had to replace my tags with parens I guess

echo"(div class=\"impactTitle\")$title (/div)(br/)(br/)";
echo"(div class=\"graph\")(div class=\"bar\" style=\"width: $percentage%\")$percentage%(/div)(/div)(div class=\"zero\")0(/div)(div class=\"graphNum\")$target(/div)(br/)(br/)";
echo"(div class=\"descrip\")$descrip(/div)(br/)(br/)(br/)(br/)";

6 | Music Critic

June 3rd, 2008 at 2:18 pm

Avatar

That is very cool. I can think of a number of things to use this bar graph for. Thanks.

8 | Martman

July 22nd, 2008 at 8:53 am

Avatar

Hi, first of all great site, has helped me out on several occasions, just wondering if anyone could help me with a change to this php code.

instead of letting the php fill the completed (red) box, can the php be told to fill it up with a hosted image.

something along the lines of below is what i'm on about

$fg = ImageLink($im,"graph_fill.png");

any help would be appreiciated

9 | Brian

September 29th, 2008 at 2:46 pm

Avatar

Here's the real Horizontal fix (the vertical bar is left in but commented out for safe switching)

$goal) {
$current = 100;
$percent= "100%!";
} else if ($current == "" || $current == "0") {
$percent = "0%";
} else {
$percent = round(($current/$goal)*100) . "%";
}

$im = @imagecreate($width,$height)
or die("Cannot Initialize new GD image stream");

$bg = ImageColorAllocate($im,200,200,200); // grey
$fg = ImageColorAllocate($im,0,150,255); //blue
$text_color = imagecolorallocate($im, 70, 70, 70); //dark grey

$vert_fill = height – (($current/$goal)*$height);
$hor_fill = (($current/$goal)*$width);

/*vertical bar*/
//imagefilledrectangle($im, 0, $vert_fill, $width, $height, $fg);

/*horizontal bar*/
imagefilledrectangle($im, 0, 0, $hor_fill, $height, $fg);

$percent_num = round(($current/$goal)*100);
if($percent_num >= 10 && $percent_num < 100)
$middle = $width/2-8;
elseif($percent_num

10 | Brian

September 29th, 2008 at 2:47 pm

Avatar

Sorry, the last post cut it short: here's the important part:

/*vertical bar*/
//imagefilledrectangle($im, 0, $vert_fill, $width, $height, $fg);

/*horizontal bar*/
imagefilledrectangle($im, 0, 0, $hor_fill, $height, $fg);

$percent_num = round(($current/$goal)*100);
if($percent_num >= 10 && $percent_num < 100)
$middle = $width/2-8;
elseif($percent_num < 10)
$middle = $width/2-5;
elseif($percent_num == 100)
$middle = $width/2-12;

/*vertical text*/
//imagestring($im, 3, 3, 1, $percent, $text_color);

/*horizontal text*/
imagestring($im, 3, $middle, 1, $percent, $text_color);

Featured Sponsors

Genesis Framework for WordPress

Advertise Here


  • Scott: Just moved changed the site URL as WP's installed in a subfolder. Cookie clearance worked for me. Thanks!
  • Stephen Lareau: Hi great blog thanks. Just thought I would add that it helps to put target = like this:1-800-555-1212 and
  • Cord Blomquist: Jennifer, you may want to check out tp2wp.com, a new service my company just launched that converts TypePad and Movable Type export files into WordPre

About


Advertisements