Problems? Solutions...

Share/Save/Bookmark

Jul 11th, 2009

Draw a simple bar graph - with configurable width, height, outside padding, and optional X-Axis values - simply and effectively using the GD Library in PHP. Example:

<?php



/*
COULD BE ANY DATA SET. THE ONLY THING REQUIRED BY THE
GRAPH IS A SET OF Y-VALUES

THIS RETURNS TRAFFIC, HITS, DATE
FOR A GIVEN DATE RANGE.

THE TRAFFIC & HITS WOULD BE Y-AXIS VALUES FOR TWO DIFFERENT GRAPHS
THE DATES WOULD BE X-AXIS VALUES FOR EITHER GRAPH
*/
$set = $Bandwidth->getDailyTotalsByDateRange( $start, $end );

/*
THE WAY IT IS HERE YOU STILL HAVE TO GET THE DATA INTO ARRAYS AND FORMAT YOUR VALUES
*/
if ( count( $set ) ) {
$vals = array();
$vals2 = array();
$points = array();
foreach ( $set as $v ) {
$vals[] = number_format( $v["Traffic"]/1024/1024/1024, 2 );
$vals2[] = round( $v["Hits"]/1000 );
$points[] = date( "M jS", strtotime( $v["LDate"] ) );
}

/*
BUT ONCE YOU’VE DONE THAT, DRAWING THE GRAPHS IS REALLY SIMPLE
IF YOU HAVE A URL SET UP TO UNPACK YOUR VALUES, SET THE PARAMETERS
AND CALL THE GRAPH. YOU CAN USE THAT ONE URL TO DO ALL THE
GRAPHS IN A SET, RATHER THAN HAVING TO HAVE A DIFFERENT ONE
FOR EACH GRAPH. PACK ALL YOUR VALUES AND PARAMETERS AND
SET AN IMAGE WITH A SOURCE OF THE ACTUAL GRAPH URL.
*/

$data = $G->packSimple( $vals, $points, 600, 350, 50 );
echo "
<p><strong>Traffic Useage Overall in Gigabytes: $start - $end</strong></p>
<img src=’simpleGraph.php?data=$data’ style=’border: 1px solid #EEF;’ />";

/*
AND THEN YOU CAN DRAW ANOTHER ONE WITH THE SECOND SET OF Y-AXIS VALUES
WITHOUT A WHOLE BUNCH OF HYSTERICS, IN THE SAME PAGE, WITH DIFFERENT
DIMENSIONS IF YOU LIKE, AND YOU’VE ONLY MADE 1 DATABASE CALL FOR BOTH
GRAPHS.
*/
$data = $G->packSimple( $vals2, $points, 600, 350, 55 );
echo "
<p><strong>Resource Hits Overall in Thousands: $start - $end</strong></p>
<img src=’simpleGraph.php?data=$data’ style=’border: 1px solid #EEF;’ />";
}



?>

The page that draws out the graphs:

<?php



include_once( "graph.class.php" );

extract( $G->unpackSimple( $_GET["data"] ) );
if ( isset( $height ) ) $G->height = $height;
if ( isset( $width ) ) $G->width = $width;
if ( isset( $overallPadding ) ) $G->overallPadding = $overallPadding;

$G->drawSimple( $values, $points );



?>
<?php



class Graph {

/*
PLEASE EXCUSE THE MESS, THIS IS A ROUGH
WORKING PROTOTYPE AND I HADN’T CLEANED IT UP YET,
JUST WANTED TO GET SOME GRAPHING GOING
*/

var $width = 600;
var $height = 350;
var $padding = 3;
var $overallPadding = 45;
var $fontDir = "/path/to/fonts/";
var $font = "Arial-12.gdf"; // this could be changed in code below to just be an int if you don’t want to go find the gd font


function packSimple( $values, $points=false, $w=false, $h=false, $op=false ) {
$toPack = array();
$toPack[] = "values=".implode( ",", $values );
if ( $points ) $toPack[] = "points=".implode( ",", $points );
if ( $w ) $toPack[] = "width=$w";
if ( $h ) $toPack[] = "height=$h";
if ( $op ) $toPack[] = "overallPadding=$op";

return urlencode( base64_encode( gzdeflate( implode( "&", $toPack ) ) ) );
}

function unpackSimple( $str ) {
$data = gzinflate( base64_decode( $str ) );
$pairs = split( "&", $data );
$r = array();
foreach ( $pairs as $pair ) {
$pair = split( "=", $pair );
$r[$pair[0]] = $pair[1];
}
return $r;
}

function drawSimple( $values, $points=false ) {

$values = split( ",", $values );

$columns = count($values);
// $width = $this->width;
// $height = $this->height;
$padding = $this->padding;
$column_width = ( $this->width-$this->overallPadding*2 ) / $columns ;

// Generate the image variables

$im = imagecreatetruecolor($this->width,$this->height);
$font = imageloadfont( $this->fontDir.$this->font );
$pearl = imagecolorallocate ($im,0xfc,0xfc,0xfa);
$gray = imagecolorallocate ($im,0xcc,0xcc,0xcc);
$blue_gray = imagecolorallocate ($im,0xcc,0xcc,0xff);
$gray_lite = imagecolorallocate ($im,0xee,0xee,0xee);
$gray_dark = imagecolorallocate ($im,0x7f,0x7f,0x7f);
$white = imagecolorallocate ($im,0xff,0xff,0xff);

// Fill in the background of the image

imagefilledrectangle($im,0,0,$this->width,$this->height,$pearl);
// imagestring($im,$font,4,4,$columns." values",$gray_dark);
// imagestring($im,$font,4,14,$values[0]." -> ".$values[count( $values )-1],$gray_dark);


$maxv = max( $values );


//////////////////////////////////////
// DO SIDEBARS
$op = $this->overallPadding-5;
imageline( $im, $op,$op,$op,$this->height-$op,$gray_dark );
imageline( $im, $op,$this->height-$op,$this->width-$op,$this->height-$op,$gray_dark );

// top int
imageline( $im, $op-5,$op,$this->width-$op,$op, $gray_dark );
imagestring( $im,$font, 5,$op-5, number_format( $maxv, 2 ), $gray_dark );
// 3/4
$tq = (($this->height-$this->overallPadding*2)*.25)+$this->overallPadding;
imageline( $im, $op,$tq,$this->width-$op,$tq, $gray_dark );
// mid int
imageline( $im, $op-5,$this->height/2,$this->width-$op,$this->height/2, $gray_dark );
imagestring( $im,$font, 5,$this->height/2-5, number_format( $maxv/2, 2 ), $gray_dark );
// 1/4
$tq = (($this->height-$this->overallPadding*2)*.75)+$this->overallPadding;
imageline( $im, $op,$tq,$this->width-$op,$tq, $gray_dark );
// 0
imageline( $im, $op-5,$this->height-$op,$op,$this->height-$op, $gray_dark );
imagestring( $im,$font, 5,$this->height-$op-5, 0, $gray_dark );

///////////////////////////////////
// Now plot each column

for($i=0;$i<$columns;$i++)
{
// $column_height = ($this->height / 100) * (( $values[$i] / $maxv) *100);

$column_height = round(($values[$i]/$maxv)*($this->height-($this->overallPadding*2)));

$x1 = $i*$column_width+$this->overallPadding;
$y1 = $this->height-$column_height-$this->overallPadding;
$x2 = (($i+1)*$column_width+$this->overallPadding)-$padding;
$y2 = $this->height-$this->overallPadding;

imagefilledrectangle($im,$x1,$y1,$x2,$y2,$blue_gray);
imagestringup($im,$font,$x1+1,$y1+strlen( number_format( $values[$i], 2 ) )*6,number_format( $values[$i], 2 ),$gray_dark);
// de-bug string per column:

// This part is just for 3D effect
imageline($im,$x1,$y1,$x1,$y2,$gray_lite);
imageline($im,$x1,$y2,$x2,$y2,$gray_lite);
imageline($im,$x2,$y1,$x2,$y2-1,$gray_dark);

}

if ( $points ) {
$points = split( ",", $points );
$py = $this->height-$op+5;
// X-Axis Points, selected
imagestring( $im, $font, $op, $py, $points[0], $gray_dark );
imagestring( $im, $font, $this->width-$op-strlen( $points[count( $points )-1] )*6,$py, $points[count( $points )-1], $gray_dark );

$onethird = round( count( $points )/3 );
imagestring( $im, $font, $this->width*.30,$py, $points[$onethird], $gray_dark );
$twothird = round( count( $points )*.6 );
imagestring( $im, $font, $this->width*.60,$py, $points[$twothird], $gray_dark );
}

// Send the PNG header information. Replace for JPEG or GIF or whatever
header ("Content-type: image/png");
imagepng($im);
imagedestroy($im);
}


}

$G = new Graph();



?>

Very simple, first working prototype. Most of the effort was making it a class, getting the values ( h, w, padding ) dynamicized.

Share/Save/Bookmark

My LinkedIn || My Resume