Chapter 6
@data = ( [ qw(1995 1996 1997 1998) ], # timespan of data
[ 5, 8, 24, 32], # thousands of people in Peoria
[ 6.5, 12.8, 31.7, 3] ); # thousands of people in Santa Fe
use GIFgraph::lines;
$somegraph = new GIFgraph::lines( );
$somegraph->set(
title => "America's love affair with cheese",
x_label => 'Time',
y_label => 'People (thousands)',
y_max_value => 50,
y_tick_number => 5
);
$somegraph->plot_to_gif( "cheesegraph.gif", \@data );
#!/usr/local/bin/perl
#
# biopage.cgi
# Generate the html 'wrapper' page for biorhythm graphing example.
#
use GD;
use CGI;
# First get the parameters passed into the script
# Use the CGI module to parse the CGI form submission.
#
my $query = new CGI;
my $dob = $query->param('dob');
my $start = $query->param('start');
my $end = $query->param('end');
# Print the header and beginning html for the result page.
#
print $query->header(-type => 'text/html');
print $query->start_html(-title => "Biorhythm!");
if ($dob && $start && $end) {
# In this case we have data passed in from the form.
# Add error checking here...
# Output the html for the image tag. This will pass the data on to
# the bio.cgi script to generate the image data.
#
print "<H1>Your biorhythm from $start to $end</H1>";
print "<IMG WIDTH=500 HEIGHT=300 ";
print "SRC=\"bio.cgi?dob=$dob&";
print "start=$start&end=$end\"> <BR>";
} else {
# In this case, no data has been passed to the script; this may
# be because this is the first invocation of the script or
# blank fields were submitted. We can just print an appropriate
# headline...
#
print "<H1>Get your biorhythms here!</H1>"
}
# Now print the html form at the bottom of the page.
# You could let the CGI module do this, but this uses a 'here' document
#
print "Enter all dates in the form mm/dd/yyyy. <BR>";
print "<FORM METHOD=POST ACTION=\"biopage.cgi?";
print "dob=$dob&start=$start&end=$end\">";
print <<EOF;
Date of Birth:<INPUT TYPE=text NAME=dob SIZE=10 VALUE=$dob><br>
Calculate biorhythms from
<INPUT TYPE=text NAME=start SIZE=10 VALUE=$start>
to
<INPUT TYPE=text NAME=end SIZE=10 VALUE=$end><br>
<!-- Note that by using a size of 10, we have a Year 10,000 problem :)>
<INPUT TYPE="submit" VALUE="Give me my biorhythm!"><br>
</FORM>
</BODY>
</HTML>
EOF
#!/usr/local/bin/perl
#
# bio.cgi
# Dynamically generate a biorhythm bar graph from data passed in from
# a form from the web page generated by biopage.cgi. The data passed to
# this script is the date of birth, the start date and the end date.
#
use GIFgraph::bars;
use Date::DateCalc;
use CGI;
# We will need a decent value of pi in the calculate subroutine;
# We can do a quick approximation using the atan2 function which returns
# the arctangent of y/x for -pi to pi. Since the atan of 1 is 45
# degrees (or pi / 4 radians), we can use:
#
my $pi = atan2(1,1) * 4;
# Get the date of birth, start date and end date parameters
# that were passed from the form
#
my $query = new CGI;
my $dob = $query->param('dob');
my $start = $query->param('start');
my $end = $query->param('end');
# Calculate the number of days between the start and end date
# using our own 'wrapper' datediff function...
#
my $days = datediff(parsedate($start),
parsedate($end));
# Calculate the number of days between date of birth and the start date
#
my $dobdiff = datediff(parsedate($dob),
parsedate($start));
# The xvalues list contains all of the numbers of the days within the
# start to end range. Use the daterange funtion described below to
# return all the days between two dates. This handles situations where
# the range spans a month or year border.
#
my @xvalues = daterange(parsedate($start));
# Now we can calculate the biorythmic graphs for the given range.
# The first parameter to calculate is the period of the sine wave,
# the second is the starting offset value for x, and the third is the
# number of days in the range of dates.
# pvalues = the 'physical' data set
# evalues = the 'emotional' data set
# ivalues = the 'intellectual' data set
#
my @pvalues = calculate(23, $dobdiff % 23, $days);
my @evalues = calculate(28, $dobdiff % 28, $days);
my @ivalues = calculate(33, $dobdiff % 33, $days);
# Organize the data in a form that we can pass to a plotting routine...
#
my @data = (\@xvalues,
\@pvalues,
\@evalues,
\@ivalues,
);
# The 'legend' is the set of labels for the various data sets
#
my @legend = ('Physical', 'Emotional', 'Intellectual');
# Create a new bar graph
#
my $graph = new GIFgraph::bars(500,300);
# Set the attributes for the graph
#
$graph->set(x_label => '', # No labels
y_label => '',
title => 'Your Biorhythm',
# The y values are representing non-quantitative 'good' and 'bad'
# values so we won't plot numerical values on the y axis
#
y_plot_values => 0,
y_max_value => 1, # sine range is -1 to 1
y_min_value => -1,
y_tick_number => 8,
long_ticks => 0, # use short ticks on axes
x_label_skip => 3, # print every third x label
# Since we are using a bar graph, we want the x axis labels along the
# bottom edge of the graph so it doesn't get messy...
#
zero_axis => 0,
zero_axis_only => 0,
);
# Add the legend to the graph
#
$graph->set_legend(@legend);
# Draw the graph and write it to STDOUT
#
print STDOUT $query->header(-type => 'image/gif');
binmode STDOUT; # switch to binary mode, if you have to
print STDOUT $graph->plot(\@data );
sub parsedate {
# Parse the month, day and year from a given date string.
# In this case, the string is represented as a forward slash-
# delimited string of numbers. This routine could be replaced
# with a more robust parsing routine, if that's something
# that you're interested in...
#
$_[0] =~ /(.+)\/(.+)\/(.+)/;
return ($1, $2, $3);
}
sub datediff {
# This routine will calculate the difference between two dates.
# It is in a separate routine so that it can be easily changed
# with whatever date modules are installed locally.
# In this case, we're using the Date::DateCalc module.
# The dates_difference function takes arguments in the form
# yy, mm, dd.
# We are passing the two date parameters as mm1,dd1,yy1,mm2,dd2,yy2.
#
return Date::DateCalc::dates_difference($_[2],$_[0],$_[1],
$_[5],$_[3],$_[4]);
}
sub daterange {
# This routine takes a starting date in the form mm, yy, dd
# and an offset number of days and returns a list
# of all the days between date and the date that is offset days
# away. In this case we use the DateCalc module's calc_new_date
# function, using only the day field returned by the function.
#
my (@returnlist, $yy, $mm, $dd);
foreach my $day (0..$days) {
# calc_new_date takes a yy, mm, dd date and an offset.
# It returns the new date as a yy, mm, dd list
#
($yy, $mm, $dd) = Date::DateCalc::calc_new_date($_[2],
$_[0],
$_[1],
$day);
push @returnlist, "$dd";
}
return @returnlist;
}
#!/usr/local/bin/perl
# proxygraph.pl
# Create a graph illustrating the load on a proxy server by analyzing its log.
#
use strict;
use Data::Xtab;
use GIFgraph::bars; # a bar graph is appropriate for this type of data
my @data = parse_log('proxy-log');
# The outputcols parameter lists the labels and the order for the x-axis values
#
my @outputcols = (0..23);
my $xtab = new Data::Xtab(\@data, \@outputcols);
my @graph_data = $xtab->graph_data;
pop @graph_data; # Remove the row of totals
my $proxygraph = new GIFgraph::bars(500,200);
$proxygraph->set( 'x_label' => 'Hour of Day',
'y_label' => 'Kb requested',
'title' => 'Total Load by Hour',
'y_max_value' => 8000,
'y_min_value' => 0,
'y_tick_number' => 5,
'y_label_skip' => 2 );
print $proxygraph->plot_to_gif('proxyload.gif', \@graph_data );
sub parse_log {
# Parse a log file given a filename;
# returned table is in the form of Table 6-3.
#
my $filename = shift @_;
my @returnvalue;
my ($host, $hour, $bytes);
open INFILE, $filename || die "Couldn't open $filename!";
while (<INFILE>) {
# The format of log file is same as httpd-cern format.
# This regular expression will extract the hostname,
# the hour, and the number of bytes from one entry...
#
$_ = /^(.+?)\s.+?:(..):.+?\s(\d+?)$/;
# Coerce the hour and bytes into number scalars.
# Also convert the bytes into kilobytes.
#
($host, $hour, $bytes) = ($1, $2+0, $3/1000);
# There may be cases where certain fields don't exist; ignore them
#
if ($host && $hour && $bytes) {
push @returnvalue, [ $host, $hour, $bytes ];
}
}
return @returnvalue;
}
#!/usr/local/bin/perl
# bio.cgi (modified)
# This script demonstrates how to pass a graph drawn with GIFgraph to an image
# created with the GD module.
#
use strict;
use GD;
use GIFgraph::bars;
use Date::DateCalc;
use CGI;
my $query = new CGI;
# Retrieve the parameters passed in from the form
#
my ($dob, $start, $end) =( $query->param('dob'),
$query->param('start'),
$query->param('end')
);
# The following section is the same as the bio.cgi script from the
# beginning of the chapter (without the comments)
#
my $pi = atan2(1,1) * 4;
my $days = datediff(parsedate($start),
parsedate($end));
my $dobdiff = datediff(parsedate($dob),
parsedate($start));
my @xvalues = daterange(parsedate($start));
my @pvalues = calculate(23, $dobdiff % 23, $days);
my @evalues = calculate(28, $dobdiff % 28, $days);
my @ivalues = calculate(33, $dobdiff % 33, $days);
my @data = (
\@xvalues,
\@pvalues,
\@evalues,
\@ivalues,
);
my @legend = ('Physical', 'Emotional', 'Intellectual');
my $graph = new GIFgraph::bars(370, 190);
$graph->set(
x_label => '',
y_label => '',
title => '',
y_plot_values => 0,
y_max_value => 1,
y_min_value => -1,
y_tick_number => 8,
long_ticks => 0,
y_label_skip => 2,
x_label_skip => 3,
zero_axis => 0,
zero_axis_only => 0,
);
$graph->set_legend(@legend);
open GRAPH,"+>temp.gif"; # Open a r/w filehandle
print GRAPH $graph->plot(\@data ); # Plot the graph to the filehandle
seek GRAPH, 0, 0; # Rewind the filehandle to beginning
my $gdgraph = newFromGif GD::Image(\*GRAPH); # Read into GD object
close GRAPH;
# Create a new image object for our final image,
# from the bfile background.gif, which is a previously created image
# (500x300) that will make a nice border for the graph.
#
open BKGRND, "background.gif";
my $image = newFromGif GD::Image(\*BKGRND);
close BKGRND;
# Allocate white as the first (background) color in the final image
#
$image->colorAllocate(255,255,255);
# Copy the graph into the center of the background
#
$image->copy($gdgraph,65,65,0,0,370,190);
# Send the image data to the web browser
#
print STDOUT $query->header(-type=>'image/gif',
-expires=>'+1s' );
binmode STDOUT;
print STDOUT $image->gif();