Close Search Box
Search Box

Search: From:

Close
Newsletter

9Tutorials to your Inbox



Creating a Graph/Chart Library with PHP

Creating a Graph/Chart Library with PHP
Author lv1 (3200/5000)
2,951 views
1 Star2 Star3Star4 Star5 Star (3 votes, average: 4.67 out of 5)

If any data, such as temperature versus day or stock market values, is to be presented in a meaningful fashion, it really needs to be presented visually. This usually takes the form of charts. Various forms of charts exist, such as line graphs, bar graphs, pie charts, and so on. PHP’s Graphics library can of course handle the creation of charts by the building up of graphics primitivesa line placed here, a rectangle there, and some text over in the corner.

Many different aspects need to be handled, however, and to accomplish this task in a general fashion is a monumental undertaking. This is why various third-party PHP chart software exists, each trying to handle chart creation to various degrees. When you get deeply into it, so many variations exist for a generic solution: colors, grids, graph types, labels, keys, and much more.

Note: One example of these third-party libraries is the PEAR Image_Graph library (currently only in alpha at the time this book was written). It can be found at: http://pear.php.net/package/Image_Graph.

Fortunately, most basic graphs can be created with some basic algorithms. For an example of what needs to be undertaken, The example below develops a class, MultiGraph, which creates three different forms of charts. We define a few basic types of plot: points, lines, and bar graphs. To use this class, an object is instantiated that provides the graphics object that will be drawn to. Various other parameters, such as colors, fonts, and labels, are public members that can then be set. Afterward a call to one of the methods of this class creates the chart for you. You can then change a few aspects of the configuration and generate another chart. This class should serve as an excellent starting place to develop more specialized charts for your specific needs.

[source:php]

<?php
// A class for drawing charts that allows its configuration to be pushed
// into it via public properties
class MultiGraph {
// Public class members, all of these need filled in before drawing.
public $border_color; // Color of the border/text
public $grid_color; // Color of the grid lines
public $data_color; // Color of any data blocks
public $g; // Graphics object
public $data; // Data to turn into a graph
public $draw_border = true; // Should the border be drawn?
public $horizontal_grid_segments = 5; // How many horizontal grid lines?
public $vertical_grid_segments = 0; // How about vertical ones?
public $x = 0; // X value of the upper left corner of CHART area
public $y = 0; // Y value
public $w = 200; // Width of actual chart area
public $h = 100; // Height of actual chart area
public $point_size = 5; // Size of data points when doing a Point Chart
public $y_axis_autolabel = true; // Generate labels for Y axis?
public $x_axis_autolabel = true; // Generate labels for X axis?
public $font = ‘arial’; // Font to use
public $fontsize = 8; // Size of font.

// Constructor method. Requires the graphics object
public function __construct($graphics) {
// Store the graphics object
$this->g = $graphics;

// Go ahead and turn on antialiasing
imageantialias($this->g, true);
}

// Method to make it easy to set dimensions
public function setDimensions($x, $y, $w, $h) {
// Just do it
$this->x = $x;
$this->y = $y;
$this->h = $h;
$this->w = $w;
}

// The method that will draw a line graph to the screen!
public function drawLineGraph() {
// We are asked to draw a line graph. First of all draw the borders
$this->_drawBorder();

// Calculate the conversion factors to scale data to space given
$yscale = $this->_calcYScale();
$xscale = $this->_calcXScale();

// Draw a grid if asked
$this->_drawHorizontal();
$this->_drawVertical();

// Now we are ready for the data (finally)
// We are doing a line graph, assume the key is the X value & sort.
// Since we are sorting, make a copy to not mess up the real data
$tmpdata = $this->data;
ksort($tmpdata);

// Now step through it, drawing the lines as appropriate:
list($ldx, $ldy) = each($tmpdata);
while (list($dx, $dy) = each($tmpdata)) {
// Draw the line from the last x,y pair, to the current one
// Draw the line from the last x,y pair, to the current one
imageline($this->g,
$this->x + $ldx * $xscale,
$this->y + $this->h - $ldy * $yscale,
$this->x + $dx * $xscale,
$this->y + $this->h - $dy * $yscale,
$this->data_color);

// Reset data for next loop
$ldx = $dx;
$ldy = $dy;
}
}

// The method that will draw a point plot to the screen.
public function drawPointGraph() {
// We are asked to draw a point graph. First draw the borders
$this->_drawBorder();

// Calculate the conversion factors to scale data to space given
$yscale = $this->_calcYScale();
$xscale = $this->_calcXScale();

// Draw a grid if asked
$this->_drawHorizontal();
$this->_drawVertical();

// Now we are ready for the data - Now step through it
foreach ($this->data as $dx => $dy) {
// Draw the point for this data:
imagefilledellipse($this->g, $this->x + $dx * $xscale,
$this->y + $this->h - $dy * $yscale,
$this->point_size, $this->point_size, $this->data_color);
}
}

// The method that will draw a bar graph
// NOTE: Bar graphs assume that the keys of the array are
// labels, not ‘x’ values.
public function drawBarGraph() {
// We’ve been asked to draw a Bar graph. First draw the borders
$this->_drawBorder();

// Calculate the conversion factors to scale data to space given
$yscale = $this->_calcYScale();

// Draw a grid if asked
$this->_drawHorizontal();

// Precalculate a few items to be efficient:
// The width of each ’section’:
$section = (float) $this->w / count($this->data);
// The width of a bar, we want it 70% of section:
$bar = (int) ($section * 0.7);

// Loop once per data item that we have - assume they are in order
$count = 0;
foreach ($this->data as $label => $val) {
$count++;

// Draw the bar to the appropriate height & width:
// First as a colored filled rectangle:
imagefilledrectangle($this->g,
$this->x + ($section * $count) - $bar,
$this->y + $this->h,
$this->x + ($section * $count),
$this->y + $this->h - $val * $yscale,
$this->data_color);

// Then as an outline:
imagerectangle($this->g, $this->x + ($section * $count) - $bar,
$this->y + $this->h, $this->x + ($section * $count),
$this->y + $this->h - $val * $yscale, $this->border_color);

// Now, since vertical grids don’t really exist for bar graphs
// We need to create the labels ourselves
if ($this->x_axis_autolabel) {
// Calculate the width of the box needed by the bounding box
$box = imagettfbbox($this->fontsize, 270,
$this->font, $label);
$texwidth = abs($box[4] - $box[0]);

// Draw it going vertical
// Draw it going vertical
imagettftext($this->g,
$this->fontsize, 270,
($this->x + ($section * $count)) -
($bar / 2) - ($texwidth / 2),
$this->y + $this->h + 4,
$this->border_color,
$this->font,
$label);
}
}
}

// Support function to draw the border:
private function _drawBorder() {
if ($this->draw_border) {
// Vertical one
imageline($this->g, $this->x, $this->y, $this->x,
$this->h + $this->y, $this->border_color);
// Horizontal one
imageline($this->g, $this->x, $this->h+$this->y,
$this->w + $this->x, $this->h + $this->y,
$this->border_color);
}
}

// Support function for calculating the Y scale
private function _calcYScale() {
// Add a fudge for visual purposes so it doesn’t ever hit the top.
return (float) $this->h / $this->_calcYMax();
}

// Support function for calculating the X scale
private function _calcXScale() {
return (float) $this->w / $this->_calcXMax();
}

// Support function for calculating the maximum Y value, for scaling/etc
private function _calcYMax() {
// Add a fudge for visual purposes so it doesn’t ever hit the top.
// Also force round it to only 2 significant digits
// as this will make it look ‘clean’
$max = (float) max($this->data) * 1.05;
// Get the ‘length’ of this:
$len = strlen((int)$max);
// Find a 2 digit ceiling, if less than 2 digits, just return it:
if ($len < 2) {
return $max;
} else {
// Keep the first two digits and pad with zeros:
return intval(substr($max, 0, 2) . str_repeat(’0′, $len - 2));
}
}

// Support function for calculating the maximum X value, for scaling/etc
private function _calcXMax() {
return max(array_keys($this->data));
}

// Support function for drawing the Horizontal Grid
private function _drawHorizontal() {
if ($this->horizontal_grid_segments) {
// Figure out their placement
foreach(range(1, $this->horizontal_grid_segments) as $hg) {
// Y height for this is:
$yheight = (int) $this->y + $this->h -
($hg * ($this->h / $this->horizontal_grid_segments));
imageline($this->g, $this->x + 1, $yheight,
$this->w + $this->x, $yheight, $this->grid_color);

// Now, IF they wanted automatic labels, give ‘em
if ($this->y_axis_autolabel) {
// Calculate the value for here, and display it
$ax_step = (int)(($this->_calcYMax() /
$this->horizontal_grid_segments) * $hg);

// Calculate the width of the box needed to display
// this: Use the lower left X (0) and the upper right
// X (4) to calculate width:
$box = imagettfbbox($this->fontsize, 0,
$this->font, $ax_step);
$texwidth = abs($box[4] - $box[0]);
$texheight = abs($box[5] - $box[1]);

// Draw it right justified:
imagettftext($this->g, $this->fontsize, 0,
$this->x - 3 - $texwidth, $yheight + $texheight / 2,
$this->border_color, $this->font, $ax_step);
}
}
}
}

// Support function for drawing the Vertical Grid
private function _drawVertical() {
if ($this->vertical_grid_segments) {
// Figure out their placement
foreach(range(1, $this->vertical_grid_segments) as $vg) {
// X location for this is:
$xloc = (int) ($this->x +
($vg * ($this->w / $this->vertical_grid_segments)));
imageline($this->g, $xloc, $this->y,
$xloc, $this->y + $this->h - 1, $this->grid_color);

// Now, IF they wanted automatic labels, give ‘em
if ($this->x_axis_autolabel) {
// Calculate the value for here, and display it
$ax_step = (int)(($this->_calcXMax() /
$this->vertical_grid_segments) * $vg);

// Calculate the width of the box needed to display
// this: Use the lower left X (0) and the upper
// right X (4) to calculate width:
$box = imagettfbbox($this->fontsize, 270,
$this->font, $ax_step);
$texwidth = abs($box[4] - $box[0]);

// Draw it going vertical
imagettftext($this->g, $this->fontsize, 270,
$xloc - $texwidth / 2, $this->y + $this->h + 3,
$this->border_color, $this->font, $ax_step);
}
}
}
}
}

// Pre/setup for the date functions:
date_default_timezone_set(’America/New_York’);

// Create a blank image to test with.
$gfx = imagecreatetruecolor(950, 650);

// Declare a few colors
$red = imagecolorallocate($gfx, 255, 0, 0);
$manilla = imagecolorallocate($gfx, 255, 255, 245);
$black = imagecolorallocate($gfx, 0, 0, 0);
$lgray = imagecolorallocate($gfx, 200, 200, 200);

// Color our background
imagefill($gfx, 0, 0, $manilla);

// Initialize a new Multigraph
$graph = new MultiGraph($gfx);
$graph->border_color = $black;
$graph->grid_color = $lgray;
$graph->data_color = $red;
$graph->vertical_grid_segments = 10;

// Create some fake data
// Generate up to 50 random points of data, with random values:
$chartdata = array();
for ($i = 0; $i < 50; $i++) {
$chartdata[rand(1,200)] = rand(1,1000);
}
$graph->data = $chartdata;

// Ok, output a line graph
$graph->setDimensions(50, 50, 300, 200);
$graph->drawLineGraph();

// Now do it as a point graph with some changed grid parameters
$graph->horizontal_grid_segments = 9;
$graph->vertical_grid_segments = 15;
$graph->setDimensions(50, 350, 300, 200);
$graph->drawPointGraph();

// Finally let’s create a bar graph, we need new data for that, with labels.
// Automatically generate some that is based on Month names.
$bardata = array();
foreach(range(1,12) as $mon) {
$bardata[date('F', mktime(0, 0, 0, $mon))] = rand(1,5000);
}

// Change some options, and display the BIG bar graph:
$cyan = imagecolorallocate($gfx, 0, 255, 255);
$graph->data_color = $cyan;
$graph->fontsize = 11;
$graph->horizontal_grid_segments = 12;
$graph->setDimensions(450, 40, 400, 500);
$graph->data = $bardata;
$graph->drawBarGraph();

// Output our sample as a PNG
header(’Content-type: image/png’);
imagepng($gfx);
?>
[/source]

Within this program, many decisions were made as to how to structure it. It was decided that making a class would be ideal due to the large amount of configuration needed to generate a chart. Had this been done in an functional manner, the parameter list for the “draw” functions would have been astronomically long, and/or an array of values would have needed to be used. Either way, it would not have been as convenient as just setting those values on an object as needed. Also, using on object provides the capability to draw multiple charts with small changes to configuration.

A design point was the use of many private support methods. Within these methods, small individual parts of drawing a graph can be easily handled. More importantly, common aspects to any type of graph can be done. For example, the _drawBorder() method takes care of drawing the border, which all three of the chart types may want to have. This method also includes the logic as to whether borders should even be drawn. Therefore the individual chart methods don’t ever have to worry about the border drawing.

As stated, our MultiGraph class is just a starting point for a fully functional generic library. There are many issues not taken into account that you might want to. For one, the x,y values provided control only the actual chart area; all labels fall outside that. This is fine for specific uses, but someone might want to completely contain a chart, including its labels, into a certain area. Other things are left out as well, such as variations on how labels are printed, keys, multiple color schemes, and much more. Listing 18.4.1 however, serves as a good example of how to create charting software for specific purposes that you may have. Nothing can beat the flexibility of creating a solution by hand that meets your specific needs.

 

del.icio.us:Creating a Graph/Chart Library with PHP digg:Creating a Graph/Chart Library with PHP spurl:Creating a Graph/Chart Library with PHP newsvine:Creating a Graph/Chart Library with PHP blinklist:Creating a Graph/Chart Library with PHP furl:Creating a Graph/Chart Library with PHP reddit:Creating a Graph/Chart Library with PHP blogmarks:Creating a Graph/Chart Library with PHP Y!:Creating a Graph/Chart Library with PHP magnolia:Creating a Graph/Chart Library with PHP segnalo:Creating a Graph/Chart Library with PHP

Post a Comment »








Safari hates me

Comment Guidelines

  • Hyperlinks are automatically generated.
  • <em>italic</em>
  • <strong>bold</strong>