GD is a wonderful tool for drawing on the Web. With it, you can create all sorts of marvelous things. Most of the web graphics I want to create are charts and graphs based on various types of data. I could use GD to create such graphs, but that would involve too much work.
Luckily, as is often the case with Perl, someone else had this problem and decided to solve it. Martien Verbruggen wrote and distributed the GIFgraph module, which allows us to create different types of charts based on a list of data points. GIFgraph uses GD, but provides us with an object-oriented interface to the new graph. This allows us to think in terms of graphs, styles and shapes—as opposed to GD, which would force us to think in terms of pixels and lines.
GIFgraph is actually a set of modules collected under the single “GIFgraph” name. One module handles bar graphs, another pie charts and so on, for nearly ten different types of graphs.
In Listing 2, for example, we create a simple bar graph, with labels “a”, “b” and “c”, with respective values of 1, 2 and 3. We do this by creating an array, traditionally called @data. Each element of @data is an array reference, with the first element corresponding to the labels. Our program displays results from a single set of data:
my @data = (["a", "b", "c"], [1, 2, 3]);
We could easily compare two sets of data:
my @data = (["a","b","c"], [1,2,3], [4,5,6]);GIFgraph is smart enough to use different colors for different sets of data. So given the above data, it will draw six bars—three each of two colors, with the values 1 and 4 associated with the “a” label, the values 2 and 5 associated with the “b” label, and the values 3 and 6 associated with the “c” label.
Before we can send the output to the user's browser, we must send a MIME type. Because it relies on GD, GIFgraph can produce output in GIF format. We tell the browser what to expect with the following command:
print $query->header(-type => "image/gif");
Now we will create our graph object and send its GIF output to the user's browser:
my $graph = new GIFgraph::bars; print $graph->plot(\@data);Notice how we pass an array reference to @data by prefacing it with a backslash (\@data). Passing @data as a reference ensures it will be handed to the plot method as intended.
In this example, we created a bar chart. What if we want a different kind of chart? We can do that by importing a different Perl module (e.g., GIFgraph::lines instead of GIFgraph::bars) and making $graph an instance of the new type.
Note that calling $graph->plot creates a graph based on @data but does not send it to the user's browser. This method returns the resulting GIF to its caller, allowing us to save it to disk, send it to the user's browser or manipulate the resulting GIF in Perl or external tools. Since the CGI standard mandates all output to STDOUT be sent to the user's browser, we can display the chart on the user's computer by printing the result from this call.
Now that we have seen a simple program that produces a chart, let us look at a slightly more complicated example, one which mirrors some real-world situations. Assume we want to create a graph based on a text file. For example, assume we are implementing part of the reporting function for a web-based voting system. The results of a given election will be placed in a text file, called votes.txt:
Tom 123456 Dick 100000 Harry 20000
The election data is stored in the above file, with the candidate's name and the number of votes he received separated with one or more tab characters. This allows the candidates' names to contain space characters, such as between first and last names.
We could use a bar chart with this data, but it would not be nearly as useful as a pie chart, in which each candidate is given a proportional part of the pie. As you can see in Listing 3, our program vote.pl is not very difficult to create and produces results relatively quickly.
It does this by iterating through each line of votes.txt, using Perl's built-in “split” function to turn a scalar value (the line from votes.txt) into a list value. In this case, we split that line across tabs, putting everything before the tab in $name and everything after the tab in $votes. We then use the “push” function to add these values to @names and @votes, respectively, which are built up with every iteration through votes.txt. If there are four candidates in votes.txt, this loop is executed four times, and @names and @votes each has four elements.
When we exit from the loop, we create @data by inserting references to @names and @votes. As always, the first element of @data is an array reference containing the names. Subsequent elements of @data contain values; in this case, we have only one value, @votes. We create the graph by creating an instance of GIFgraph::pie and then plotting it to the user's browser.