I've fallen in love with PEAR's Image_Graph package. More than once I've come up against a requirement, and then found that the developers have implemented a solution in quite an elegant way. They'd be the first to admit there's still lots to do - the package is after all still an alpha release. But it's achieved a lot, so I can't resist taking it on a second date. After last month's introduction, this time I look a little deeper into the available functionality.

Using a background image

As developers, we should all know how clients are usually impressed by glitz, and not substance. A great-looking site with shoddy backend code is much more likely to impress than a poor-looking site with stunning code. It's the same with graphs. Perhaps a background image is just the touch of class needed to make the figures on that ever-so-important graph stand out from the crowd. Here's how you do it. Taking last week's image_graph4.php as a basis, add the bolded lines. You can download and reuse the helicopter image (under a Creative Commons licence) here.
2image_graph1.php

<?php
include 'Image/Graph.php';
$Graph =& Image_Graph::factory('graph', array(600, 300));
$Plotarea =& $Graph->addNew('plotarea');
$Dataset =& Image_Graph::factory('dataset');
$Dataset->addPoint('Jan', 5, 'J');
$Dataset->addPoint('Feb', 13, 'F');
$Dataset->addPoint('March', 10, 'M');
$Plot =& $Plotarea->addNew('bar', &$Dataset);

$Plot->setLineColor('green');
$Plot->setBackgroundColor('green@0.1');

$fill =& Image_Graph::factory('Image_Graph_Fill_Array');
$fill->addColor('red', 'J');
$fill->addColor('blue', 'F');
$fill->addColor('yellow', 'M');
$Plot->setFillStyle($fill);

//set Background
$bgfill =& Image_Graph::factory('Image_Graph_Fill_Image', 'images/helicopter.jpg');
$Plotarea->setFillStyle($bgfill);

$Font =& $Graph->addNew('ttf_font', 'Bitstream-Vera-Sans-Mono');
$Font->setSize(12);
$Graph->setFont($Font);

$YAxis =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_Y);
$YAxis->setTitle('Rainy Days', 'vertical');
$XAxis =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_X);
$XAxis->setTitle('Month');

$Graph->done();
?>
Unfortunately your beautiful image is obscured by the harsh colours of the graph. We can't have that. Let's add some transparency to the three bars. By adding @0.x immediately after the colour, x being a positive number, the colour is made transparent. The closer to zero x is, the more transparent the bar. Change the parameters passed to the addColor method, as follows:
$fill->addColor('red@0.8', 'J');
$fill->addColor('blue@0.5', 'F');
$fill->addColor('yellow@0.2', 'M');
Now we can see the helicopter in (almost) all of its glory. For best results, many graphs of this type use a transparency of around 0.2, but it of course depends on your background and your visual intent. Our example gives you a range to show you the effects, but you'll probably agree that the red in particular at 0.8 is still too strong.

A stacked area chart

Let's look at a slightly different sort of example - a stacked area chart. Fundamentally, very little is different, as you'd expect in an abstract class like this. When calling the addNew method on our PlotArea object, simply specify stacked instead of bar. For our stacked area chart, we're going to create an array of datasets to stack on top of each other. They will appear in order of bottom to top.
<?php
2image_graph2.php

include 'Image/Graph.php';

// create a graph 600 pixels wide by 300 high
$Graph = & Image_Graph::factory('graph', array(600, 300));

$Font =& $Graph->addNew('ttf_font', 'Bitstream-Vera-Sans-Mono');
$Font->setSize(12);

$Graph->setFont($Font);

$Graph->add(
  Image_Graph::vertical(
    Image_Graph::factory('title', array('My way or the highway', 12)),
    Image_Graph::vertical(
      $Plotarea = Image_Graph::factory('plotarea'),
      $Legend = Image_Graph::factory('legend'),
      90
    ),
    5
  )
);
$Legend->setPlotarea($Plotarea);


// Create and populate 3 datasets

$Dataset1 =& Image_Graph::factory('dataset');
$Dataset1->addPoint('Jan', 1);
$Dataset1->addPoint('Feb', 2);
$Dataset1->addPoint('Mar', 2);
$Dataset1->addPoint('Apr', 4);

$Dataset2 =& Image_Graph::factory('dataset');
$Dataset2->addPoint('Jan', 3);
$Dataset2->addPoint('Feb', 4);
$Dataset2->addPoint('Mar', 5);
$Dataset2->addPoint('Apr', 2);

$Dataset3 =& Image_Graph::factory('dataset');
$Dataset3->addPoint('Jan', 10);
$Dataset3->addPoint('Feb', 9);
$Dataset3->addPoint('Mar', 12);
$Dataset3->addPoint('Apr', 34);


$Dataset1->setName('The highway');
$Dataset2->setName('Their way');
$Dataset3->setName('My way');

$Datasets = array($Dataset1,$Dataset2,$Dataset3);

$Plot =& $Plotarea->addNew('Image_Graph_Plot_Area', array($Datasets,'stacked'));

$Plot->setLineColor('gray');

$FillArray =& Image_Graph::factory('Image_Graph_Fill_Array');
$FillArray->addColor('slateblue@0.2');
$FillArray->addColor('fuchsia@0.2');
$FillArray->addColor('lime@0.2');

// set a standard fill style
$Plot->setFillStyle($FillArray);

$XAxis =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_X);
$XAxis->setTitle('Month');

$YAxis =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_Y);
$YAxis->setTitle('The bigger the better', 'vertical');

// output the Graph
$Graph->done();
?>
Note that when defining our datasets, all the points use the same labels (the month). If we changed the labels for any of the points, the graph would adjust accordingly. For example, replacing Jan with Dec in the first dataset effectively creates a fifth point.
$Dataset1->addPoint('Dec', 1);
$Dataset1->addPoint('Feb', 2);
$Dataset1->addPoint('Mar', 2);
$Dataset1->addPoint('Apr', 4);
Adding another new point, in dataset2:
$Dataset1 =& Image_Graph::factory('dataset');
$Dataset1->addPoint('Dec', 1);
$Dataset1->addPoint('Feb', 2);
$Dataset1->addPoint('Mar', 2);
$Dataset1->addPoint('Apr', 4);

$Dataset2 =& Image_Graph::factory('dataset');
$Dataset2->addPoint('Jan', 3);
$Dataset2->addPoint('Feb', 4);
$Dataset2->addPoint('Apr', 5);
$Dataset2->addPoint('May', 2);

Two graphs with a different axis

It's possible to want to display two graphs on the same plot area, but with very different measurement scales. For this, you'll need different axes. The left axis is used for one graph, and the right axis for the other. Here's how it's possible. Taking the bar graph we created in last month's article, image_graph4.php, as a basis, let's first create a second dataset. We'll add a line graph on top of the original bar chart. There's no need to use unique identifiers, as we're not worrying about fill styles with a line graph.
2image_graph5.php

<?php
include 'Image/Graph.php';
$Graph =& Image_Graph::factory('graph', array(600, 300));
$Plotarea =& $Graph->addNew('plotarea');
$Dataset =& Image_Graph::factory('dataset');
$Dataset->addPoint('Jan', 5, 'J');
$Dataset->addPoint('Feb', 13, 'F');
$Dataset->addPoint('March', 10, 'M');
$Plot =& $Plotarea->addNew('bar', &$Dataset);

$Plot->setLineColor('green');
$Plot->setBackgroundColor('green@0.1');

$Dataset2 =& Image_Graph::factory('dataset');
$Dataset2->addPoint('Jan', 9);
$Dataset2->addPoint('Feb', 4);
$Dataset2->addPoint('March', 12);
$Plot2 =& $Plotarea->addNew('line', &$Dataset2);

$Plot2->setLineColor('firebrick'); // yes, that's a colour
$Plot2->setBackgroundColor('green@0.1');

$fill =& Image_Graph::factory('Image_Graph_Fill_Array');
$fill->addColor('red@0.2', 'J');
$fill->addColor('blue@0.2', 'F');
$fill->addColor('yellow@0.2', 'M');
$Plot->setFillStyle($fill);

$Font =& $Graph->addNew('ttf_font', 'Bitstream-Vera-Sans-Mono');
$Font->setSize(12);
$Graph->setFont($Font);

$YAxis =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_Y);
$YAxis->setTitle('Rainy Days', 'vertical');
$XAxis =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_X);
$XAxis->setTitle('Month');

$Graph->done();
?>

This adds a second dataset. We can add as many datasets as we wish, using as many different graph types as we wish. Of course, there're only viably two different axes available, that to the left and to the right of the graph. The above graph isn't very friendly, as the bars aren't distinct due to them having to share scaling with the line part of the graph. Let's add that second axis now. To do so is easy:
<?php
include 'Image/Graph.php';
$Graph =& Image_Graph::factory('graph', array(600, 300));
$Plotarea =& $Graph->addNew('plotarea');
$Dataset =& Image_Graph::factory('dataset');
$Dataset->addPoint('Jan', 5, 'J');
$Dataset->addPoint('Feb', 13, 'F');
$Dataset->addPoint('March', 10, 'M');
$Plot =& $Plotarea->addNew('bar', &$Dataset);

$Plot->setLineColor('green');
$Plot->setBackgroundColor('green@0.1');

$Dataset2 =& Image_Graph::factory('dataset');
$Dataset2->addPoint('Jan', 90);
$Dataset2->addPoint('Feb', 40);
$Dataset2->addPoint('March', 120);
$Plot2 =& $Plotarea->addNew('line', &$Dataset2, IMAGE_GRAPH_AXIS_Y_SECONDARY);

$Plot2->setLineColor('firebrick'); // yes, that's a colour
$Plot2->setBackgroundColor('green@0.1');

$fill =& Image_Graph::factory('Image_Graph_Fill_Array');
$fill->addColor('red@0.2', 'J');
$fill->addColor('blue@0.2', 'F');
$fill->addColor('yellow@0.2', 'M');
$Plot->setFillStyle($fill);

$Font =& $Graph->addNew('ttf_font', 'Bitstream-Vera-Sans-Mono');
$Font->setSize(12);
$Graph->setFont($Font);

$YAxis =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_Y);
$YAxis->setTitle('Rainy Days', 'vertical');
$XAxis =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_X);
$XAxis->setTitle('Month');

$YAxisSecondary =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_Y_SECONDARY);
$YAxisSecondary->setTitle('Butterfly sightings','vertical');

$Graph->done();
?>
Note that when defining $Plot2 while calling the addNew method on the $Plotarea object, we pass a third parameter, IMAGE_GRAPH_AXIS_Y_SECONDARY. This is then used when detailing the second axis, the second and third-last lines of the code.

Different axis direction

But what if you want the axis to go the other way, from top to bottom? This could be desirable because the values are negative, or that it just makes more sense to display that way. Again using image_graph4 as a basis, let's look at an example with an axis going the other way. We're going to create two plotareas, as we did in the final example last month, with the graph on one having an axis counting up from top to bottom. The grid we create has space for a heading (5% of the height), and the two graphs beneath that both take up 50% of the width.
2image_graph6.php

<?php
include 'Image/Graph.php';
$Graph =& Image_Graph::factory('graph', array(600, 300));

$Graph->add(
  Image_Graph::vertical(
      Image_Graph::factory('title', array('Top to bottom axis', 12)),
      Image_Graph::horizontal(
         $Plotarea1 = Image_Graph::factory('plotarea'),
         $Plotarea2 = Image_Graph::factory('plotarea'),
       50
      ),
      5
   )
);

$Dataset1 =& Image_Graph::factory('dataset');
$Dataset1->addPoint('Jan', 5, 'J');
$Dataset1->addPoint('Feb', 13, 'F');
$Dataset1->addPoint('March', 10, 'M');

$Dataset2 =& Image_Graph::factory('dataset');
$Dataset2->addPoint('Jan', -6, 'J');
$Dataset2->addPoint('Feb', -13, 'F');
$Dataset2->addPoint('March', -10, 'M');

$Plot1 =& $Plotarea1->addNew('bar', &$Dataset1);
$Plot2 =& $Plotarea2->addNew('bar', &$Dataset2);

$Plot1->setLineColor('green');
$Plot1->setBackgroundColor('green@0.1');

$fill =& Image_Graph::factory('Image_Graph_Fill_Array');
$fill->addColor('red@0.2', 'J');
$fill->addColor('blue@0.2', 'F');
$fill->addColor('yellow@0.2', 'M');
$Plot1->setFillStyle($fill);
$Plot2->setFillStyle($fill);

$Font =& $Graph->addNew('ttf_font', 'Bitstream-Vera-Sans-Mono');
$Font->setSize(12);
$Graph->setFont($Font);

$YAxis1 =& $Plotarea1->getAxis(IMAGE_GRAPH_AXIS_Y);
$YAxis1->setTitle('Rainy Days', 'vertical');
$XAxis1 =& $Plotarea1->getAxis(IMAGE_GRAPH_AXIS_X);
$XAxis1->setTitle('Month');

$YAxis2 =& $Plotarea2->getAxis(IMAGE_GRAPH_AXIS_Y);
$YAxis2->setTitle('Minimum Temperature', 'vertical');
$XAxis2 =& $Plotarea2->getAxis(IMAGE_GRAPH_AXIS_X);

$Graph->done();
?>
Note that the x-axis label on the right graph appears below the line, in the body of the graph. I haven't been able to find a way to overcome this. It won't have as much consequence in other cases, such as when using a line graph, but it's still an annoyance. If I find out how, I'll update the article.
The axis is reversed by default if using negative values, as one would expect, but there's a way to reverse the axis even if using positive values. Simply use the setInverted method. You can use this method on both the X and Y axis (although the same problem applies of the labelling appearing to the left of the line, in the body of the graph. Here's an example:
2image_graph7.php

<?php
include 'Image/Graph.php';
$Graph =& Image_Graph::factory('graph', array(600, 300));

$Graph->add(
  Image_Graph::vertical(
      Image_Graph::factory('title', array('Top to bottom axis', 12)),
      Image_Graph::horizontal(
         $Plotarea1 = Image_Graph::factory('plotarea'),
         $Plotarea2 = Image_Graph::factory('plotarea'),
       50
      ),
      5
   )
);

$Dataset1 =& Image_Graph::factory('dataset');
$Dataset1->addPoint('Jan', 5, 'J');
$Dataset1->addPoint('Feb', 13, 'F');
$Dataset1->addPoint('March', 10, 'M');

$Dataset2 =& Image_Graph::factory('dataset');
$Dataset2->addPoint('Jan', 6, 'J');
$Dataset2->addPoint('Feb', 13, 'F');
$Dataset2->addPoint('March', 10, 'M');

$Plot1 =& $Plotarea1->addNew('bar', &$Dataset1);
$Plot2 =& $Plotarea2->addNew('bar', &$Dataset2);

$Plot1->setLineColor('green');
$Plot1->setBackgroundColor('green@0.1');

$fill =& Image_Graph::factory('Image_Graph_Fill_Array');
$fill->addColor('red@0.2', 'J');
$fill->addColor('blue@0.2', 'F');
$fill->addColor('yellow@0.2', 'M');
$Plot1->setFillStyle($fill);
$Plot2->setFillStyle($fill);

$Font =& $Graph->addNew('ttf_font', 'Bitstream-Vera-Sans-Mono');
$Font->setSize(12);
$Graph->setFont($Font);

$YAxis1 =& $Plotarea1->getAxis(IMAGE_GRAPH_AXIS_Y);
$YAxis1->setTitle('Rainy Days', 'vertical');
$XAxis1 =& $Plotarea1->getAxis(IMAGE_GRAPH_AXIS_X);
$XAxis1->setTitle('Month');

$YAxis2 =& $Plotarea2->getAxis(IMAGE_GRAPH_AXIS_Y);
$YAxis2->setTitle('Minimum Temperature', 'vertical');
$XAxis2 =& $Plotarea2->getAxis(IMAGE_GRAPH_AXIS_X);
$YAxis2->setInverted(true);
$XAxis2->setInverted(true);

$Graph->done();
?>

Conclusion

Hopefully you'll enjoy using Image_Graph as much as I have, and, with the help of these last two articles, find the initiation a little easier than I did. It's an impressive package for one still in alpha status, and with an active community, including a forum, it's already in better shape than some of the stable PEAR packages, and is sure to develop rather quickly.

Other Links