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();