## Gnuplot.pm is a sub-module of Graph.pm. It has all the subroutines ## needed for the gnuplot part of the package. ## ## $Id: Gnuplot.pm,v 1.48 2006/06/07 21:09:33 emile Exp $ $Name: $ ## ## This software product is developed by Michael Young and David Moore, ## and copyrighted(C) 1998 by the University of California, San Diego ## (UCSD), with all rights reserved. UCSD administers the CAIDA grant, ## NCR-9711092, under which part of this code was developed. ## ## There is no charge for this software. You can redistribute it and/or ## modify it under the terms of the GNU General Public License, v. 2 dated ## June 1991 which is incorporated by reference herein. This software is ## distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF MERCHANTABILITY ## OR FITNESS FOR A PARTICULAR PURPOSE or that the use of it will not ## infringe on any third party's intellectual property rights. ## ## You should have received a copy of the GNU GPL along with this program. ## ## ## IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY ## PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL ## DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS ## SOFTWARE, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF ## THE POSSIBILITY OF SUCH DAMAGE. ## ## THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND THE ## UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, ## SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. THE UNIVERSITY ## OF CALIFORNIA MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES ## OF ANY KIND, EITHER IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED ## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A ## PARTICULAR PURPOSE, OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE ## ANY PATENT, TRADEMARK OR OTHER RIGHTS. ## ## ## Contact: graph-dev@caida.org ## ## package Chart::Graph::Gnuplot; use Exporter (); @ISA = qw(Exporter); @EXPORT = qw(); @EXPORT_OK = qw(&gnuplot); use Carp; # for carp() and croak() use Chart::Graph::Utils qw(:UTILS); # get global subs and variable use POSIX 'strftime'; use FileHandle; $cvs_Id = '$Id: Gnuplot.pm,v 1.48 2006/06/07 21:09:33 emile Exp $'; $cvs_Author = '$Author: emile $'; $cvs_Name = '$Name: $'; $cvs_Revision = '$Revision: 1.48 $'; $VERSION = 3.2; use strict; use vars qw($show_year $show_seconds); # these variables hold default options for gnuplot my %def_gnu_global_opts = ( "title" => "untitled", "output type" => "png", "output file" => "untitled-gnuplot.png", "x-axis label" => "x-axis", "y-axis label" => "y-axis", "x2-axis label" => undef, "y2-axis label" => undef, "logscale x" => "0", "logscale y" => "0", "logscale x2" => "0", "logscale y2" => "0", "xtics" => undef, "ytics" => undef, "x2tics" => undef, "y2tics" => undef, "xdata" => undef, "ydata" => undef, "x2data" => undef, "y2data" => undef, "timefmt" => undef, "format" => undef, "xrange" => undef, "yrange" => undef, "extra_opts" => undef, "uts" => undef, "uts_normalize" => undef, "size"=> undef, ); my %def_gnu_data_opts = ( "title" => "untitled data", "style" => "points", # points, lines... "axes" => "x1y1", "type" => undef, "using" => "1:2", ); # # # Subroutine: gnuplot() # # Description: this is the main function you will be calling from # our scripts. please see # www.caida.org/Tools/Graph/ for a full description # and how-to of this subroutine # sub gnuplot { my ($user_global_opts_ref, @data_sets) = @_; my (%data_opts, %global_opts,); my ($plottype, $output_file, $plot_file, $output_type, $data_set_ref); # create a new filehandle to be used throughout package my $handle = new FileHandle; # create tmpdir _make_tmpdir("_Gnuplot_"); # set paths for external programs if (not _set_gnupaths()) { _cleanup_tmpdir(); return 0; } # check first arg for hash if (ref($user_global_opts_ref) ne "HASH") { carp "Global options must be a hash"; _cleanup_tmpdir(); return 0; } # check for data sets if (not @data_sets) { carp "no data sets"; $handle->close; _cleanup_tmpdir(); return 0; } # call to combine user options with default options %global_opts = _mesh_opts($user_global_opts_ref, \%def_gnu_global_opts); my $command_file = _make_tmpfile("command"); #remember to close the file if we return if (not $handle->open(">$command_file")) { carp "could not open file: $command_file"; _cleanup_tmpdir(); return 0; } # check if uts option is chosen and process first if (my $value = $global_opts{uts}) { if (defined($value) and ref($value) eq "ARRAY") { if (@{$value} < 2 || @{$value} > 4) { carp "out of range for 'uts': [start, end, , ]\n"; _cleanup_tmpdir(); return 0; } # set x tics to human readable time stamps _gnuplot_date_utc($value->[0], $value->[1], $value->[2], $value->[3], \%global_opts); } else { carp "Invalid value for 'uts', give [start, end, , ]\n"; } } # uts_normalize will be removed in a future version, so don't use it if (my $value = $global_opts{uts_normalize}) { if (defined($value) and ref($value) eq "ARRAY") { if (@{$value} < 2 || @{$value} > 3) { carp "out of range for 'uts_normalize': [start, end, ]\n"; _cleanup_tmpdir(); return 0; } # set x tics to human readable time stamps _gnuplot_date_utc_normalize($value->[0], $value->[1], $value->[2], \%global_opts); } else { carp "Invalid value for 'uts_normalize', give [start, end]"; } } # Check if we have options for reading data as date/time formats # these must be in command file before any others. foreach my $time_set ("xdata", "ydata", "x2data", "y2data") { if (defined($global_opts{$time_set})) { print $handle "set $time_set time\n"; } } # Set the format for reading data/time data. Only one format for all axes. if (my $value = $global_opts{timefmt}) { if (defined($value)) { print $handle "set timefmt \"$value\"\n"; } } # Now write remain global options to command file while (my ($key, $value) = each %global_opts) { ## Generic pass-thru, stuff random Gnuplot commands in this key if ($key eq "extra_opts") { if (defined $value) { if (ref($value) eq 'ARRAY') { # arrayref print $handle join("\n", @$value); print $handle "\n"; } else { # assume it's a string and user provided \n's print $handle "$value\n"; } } } if ($key eq "title") { print $handle "set title \"$value\"\n"; } if ($key eq "x-axis label") { print $handle "set xlabel \"$value\"\n"; } if ($key eq "y-axis label") { print $handle "set ylabel \"$value\"\n"; } if ($key eq "x2-axis label") { if (defined($value)) { print $handle "set x2label \"$value\"\n"; } } if ($key eq "y2-axis label") { if (defined($value)) { print $handle "set y2label \"$value\"\n"; } } if ($key eq "logscale x") { if ($value == 1) { print $handle "set logscale x\n"; } } if ($key eq "logscale y") { if ($value == 1) { print $handle "set logscale y\n"; } } if ($key eq "logscale x2") { if ($value == 1) { print $handle "set logscale x2\n"; } } if ($key eq "logscale y2") { if ($value == 1) { print $handle "set logscale y2\n"; } } # tics are not required so we can fall through if we want if ($key eq "xtics") { if (defined($value) and ref($value) eq "ARRAY") { _print_tics($handle, $key, @{$value}); } } if ($key eq "ytics") { if (defined($value) and ref($value) eq "ARRAY") { _print_tics($handle, $key, @{$value}); } } if ($key eq "x2tics") { if (defined($value)) { if (ref($value) eq "ARRAY") { _print_tics($handle, $key, @{$value}); } if ($value eq "on") { print $handle "set $key\n"; } } } if ($key eq "y2tics") { if (defined($value)) { if (ref($value) eq "ARRAY") { _print_tics($handle, $key, @{$value}); } if ($value eq "on") { print $handle "set $key\n"; } } } if ($key eq 'xrange' || $key eq 'yrange' ) { if (defined($value)) { if (ref($value) eq 'ARRAY') { # arrayref print $handle "set $key [$value->[0] : $value->[1]]\n"; } else { # assume string print $handle "set $key $value\n"; } } } # The only time related Gnuplot code that doesn't need to be # output first. if ($key eq "format") { if (defined($value)) { if(ref($value) eq "ARRAY") { # Print value supplying quotes for time format. print $handle "set $key ", $$value[0]," \"", $$value[1], "\" \n"; } else { carp "Invalid setting for format option"; } } } # Date/Time and UTS keys which are processed first. if ($key eq "timefmt") { # already processed } if ($key eq "xdata") { # already processed } if ($key eq "ydata") { # already processed } if ($key eq "x2data") { # already processed } if ($key eq "y2data") { # already processed } if ($key eq "uts") { # already been processed } if ($key eq "uts_normalize") { # already been processed } if ($key eq "size" && defined $value) { if (ref($value) eq 'ARRAY' && @{$value} == 2) { print $handle "set $key ". @{$value}[0] . "," . @{$value}[1] . "\n"; } else { print STDERR "option `size' must be given a two element array\n"; } } if ($key eq "output file") { $output_file = $value; } if ($key eq "output type") { if (!($value =~ /^(pbm|gif|tgif|png|svg|eps(:? .*)?)$/)) { carp "invalid output type: $value"; $handle->close(); _cleanup_tmpdir(); return 0; } $output_type = $value; } } # create the data file if ($output_type =~ /^eps( .*)?$/) { my $options = $1 || ""; if (defined $output_file) { $plot_file = _make_tmpfile("plot", "eps"); print $handle "set output \"$plot_file\"\n"; } #print $handle "set terminal postscript eps color \"Arial\" 18\n"; print $handle "set terminal postscript eps $options\n"; } elsif ($output_type eq "pbm" ) { if (defined $output_file) { $plot_file = _make_tmpfile("plot", "pbm"); print $handle "set output \"$plot_file\"\n"; } print $handle "set terminal pbm small color\n"; } elsif ($output_type eq "gif") { # always needs the tempfile because of conversion later on $plot_file = _make_tmpfile("plot", "pbm"); print $handle "set output \"$plot_file\"\n"; print $handle "set terminal pbm small color\n"; } elsif ($output_type eq "png") { if (defined $output_file) { $plot_file = _make_tmpfile("plot", "png"); print $handle "set output \"$plot_file\"\n"; } print $handle "set terminal png small\n"; } elsif ($output_type eq "tgif") { if (defined $output_file) { $plot_file = _make_tmpfile("plot", "obj"); print $handle "set output \"$plot_file\"\n"; } print $handle "set terminal tgif\n"; } elsif ($output_type eq 'svg') { if (defined $output_file) { $plot_file = _make_tmpfile("plot", "svg"); print $handle "set output \"$plot_file\"\n"; } print $handle "set terminal svg\n"; } # process data sets print $handle "plot "; while (@data_sets) { $data_set_ref = shift @data_sets; if (ref($data_set_ref) ne "ARRAY") { carp "Data set must be an array"; $handle->close(); _cleanup_tmpdir(); return 0; } if (not _gnuplot_data_set($handle, @{$data_set_ref})) { ## already printed error message $handle->close(); _cleanup_tmpdir(); return 0; } if (@data_sets) { print $handle ", "; } } $handle->close(); # gnuplot and convert pbm file to gif if (not _exec_gnuplot($command_file)) { _cleanup_tmpdir(); return 0; } if ($output_type eq "gif") { if(not _exec_pbmtogif($plot_file, $output_file)) { _cleanup_tmpdir(); return 0; } } elsif (defined $output_file && $output_type =~ /^(pbm|eps(?: .*)?|png|tgif)$/) { #try to get rid of the ugly warnings when moving a file on freebsd if ($^O eq 'freebsd') { eval { #this is opportunistic, if it fails it doesn't really matter my $gid = `/usr/bin/id -g`; chomp($gid); system('/usr/bin/chgrp',$gid,$plot_file); }; } my $status = system("mv", "$plot_file", "$output_file"); if (not _chk_status($status)) { if ($Chart::Graph::debug) { print STDERR "Couldn't mv $plot_file to $output_file: $!\n"; } _cleanup_tmpdir(); return 0; } } _cleanup_tmpdir(); return 1; } # # # Subroutine: gnuplot_data_set() # # Description: this functions processes the X number # of data sets that a user gives as # arguments to gnuplot(). Again, please # see http://www.caida.org/Tools/Graph/ # for the format of the dataset. # sub _gnuplot_data_set { my ($handle, $user_data_opts_ref, @data) = @_; my (%data_opts); # set these values with empty string because we print them out later # we don't want perl to complain of uninitialized value. my ($title, $style, $axes, $ranges, $type,) = ("", "", "", "", ""); my ($using) = (""); my $result; my $filename = _make_tmpfile("data"); ## check first arg for hash if (ref($user_data_opts_ref) ne "HASH") { carp "Data options must be a hash."; return 0; } # call to combine user options with default options %data_opts = _mesh_opts($user_data_opts_ref, \%def_gnu_data_opts); # write data options to command file while (my ($key, $value) = each %data_opts) { if ($key eq "using") { $using = "using $value"; } if ($key eq "title") { $title = "title \"$value\""; } if ($key eq "style") { $style = "with $value" } if ($key eq "axes") { $axes = "axes $value"; } if ($key eq "type") { $type = $value; } } if ($type eq "function") { #$ranges = "[t=:]"; # XXX ? print $handle "$ranges " . $data[0] . " $axes $title $style"; return 1; } else { print $handle "$ranges \"$filename\" $using $axes $title $style"; # we give the user 3 formats for supplying the data set # 1) matrix # 2) column # 3) file # please see the online docs for a description of these # formats if ($type eq "matrix") { $result = _matrix_to_file($filename, @data); } elsif ($type eq "columns") { $result = _columns_to_file($filename, @data); } elsif ($type eq "file") { $result = _file_to_file($filename, @data); } elsif ($type eq "") { carp "Need to specify data set type"; return 0; } else { carp "Illegal data set type: $type"; return 0; } } return $result; } # # Subroutine: set_gnupaths() # # Description: set paths for external programs required by gnuplot() # if they are not defined already # sub _set_gnupaths { if (not defined($gnuplot)) { if (not $gnuplot = _get_path("gnuplot")) { return 0; } } if (not defined($ppmtogif)) { if (not $ppmtogif = _get_path("ppmtogif")) { return 0; } } return 1; } # # # Subroutine: print_tics() # Description: this subroutine takes an array # of graph tic labels and prints # them to the gnuplot command file. # This subroutine is called by gnuplot(). # # Arguments: $tic_type: which axis to print the tics on # @tics: the array of tics to print to the file # sub _print_tics { my ($handle, $tic_type, @tics) = @_; my (@tic_array, $tics_formatted, $tic_label, $tic_index); # no tic set found, user entered empty tic array if (not @tics) { carp "Warning: empty tic set found"; return 1; } foreach my $tic (@tics) { #tics can come in two formats #this one is [["label1", 10], ["label2", 20],...] if (ref($tic) eq "ARRAY") { if ($#{$tic} != 1) { carp "invalid tic format"; return 0; } $tic_label = $tic->[0]; $tic_index = $tic->[1]; push (@tic_array, "\"$tic_label\" $tic_index"); # this one is [10, 20,...] } else { push (@tic_array, "$tic"); } } $tics_formatted = join(",", @tic_array); print $handle "set $tic_type ($tics_formatted)\n"; return 1; } # # # Subroutine: matrix_to_file() # # Description: converts the matrix data input into a the gnuplot # data file format. See www for the specific on the # matrix format # # sub _matrix_to_file { my ($file, $matrix_ref) = @_; my $entry_ref; my $matrix_len; if (ref($matrix_ref) ne "ARRAY") { carp "Matrix data must be a reference to an array"; return 0; } open (DATA, ">$file"); $matrix_len = @{$matrix_ref}; for (my $i = 0; $i < $matrix_len; $i++) { $entry_ref = $matrix_ref->[$i]; if (ref($entry_ref) ne "ARRAY") { carp "Matrix entry must be a reference to an array"; close DATA; return 0; } # prints blank lines for blank entries, this allows # the user to tell gnuplot to not connect lines between # all points when displaying data with lines. if (@{$entry_ref} == 0) { print DATA "\n"; } else { if (0) { # check that each entry ONLY has two entries if (@{$entry_ref} != 2) { carp "Each entry must be an array of size 2"; return 0; } print DATA $entry_ref->[0], "\t", $entry_ref->[1], "\n"; } # XXX print DATA join("\t", @{$entry_ref}), "\n"; } } close DATA; return 1; } # # # Subroutine: columns_to_file() # # Description: converts the column data input into a the gnuplot # data file format. please see www page for specifics # on this format. # sub _columns_to_file { my ($file, @columns) = @_; foreach my $dataset ( @columns ) { if (!(ref($dataset) eq "ARRAY")) { carp "Column data must be a reference to an array"; return 0; } if ($#{$dataset} != $#{$columns[$[]}) { carp "All columns must be of same length"; return 0; } } if ($#{$columns[$[]} == 0) { carp "Warning: Columns have no data!"; } open (DATA, ">$file"); for (my $i = 0; $i <= $#{$columns[$[]}; $i++) { foreach my $dataset ( @columns ) { print DATA "$dataset->[$i]\t"; } print DATA "\n"; } close DATA; return 1; } # # Subroutine: file_to_file() # # Description: If a gnuplot data set was given in # file format, we simply copy the data # and read it into # sub _file_to_file { my ($file_out, $file_in) = @_; if (not $file_in) { carp "Data set file missing"; return 0; } if (not -f $file_in) { carp "Data set file, '$file_in', does not exist."; return 0; } my $status = system("cp", "$file_in", "$file_out"); if (not _chk_status($status)) { return 0; } return 1; } # # Subroutine: exec_gnuplot() # # Description: this executes gnuplot on the command file # and data sets that we have generated. # sub _exec_gnuplot { my ($command_file) = @_; my $status = system("$gnuplot", "$command_file"); if (not _chk_status($status)) { return 0; } return 1; } # # Subroutine: exec_pbmtogif() # # Description: convert pbm file that gnuplot makes into # a gif. usually used for web pages # sub _exec_pbmtogif { my ($pbm_file, $gif_file) = @_; my $status; my $cmd = "$ppmtogif $pbm_file "; if ($gif_file) { $cmd .= "> $gif_file "; } unless ($Chart::Graph::debug) { $cmd .= "2> /dev/null "; } $status = system($cmd); if (not _chk_status($status)) { return 0; } return 1; } # # Subroutine: gnuplot_date_utc() # # Description: wrapper function that handles UNIX # time stamps as x values nicely # # Author: Ryan Koga - rkoga@caida.org # sub _gnuplot_date_utc { my ($start, $end, $samp_scale, $use_local_tz, $global_options) = @_; my $min_len = 60; my $hour_len = $min_len*60; my $day_len = $hour_len*24; my $interval = $end - $start; my $min_samp; my @tics; if (!defined($samp_scale)) { $samp_scale = 1; } if ($interval < 10) { $min_samp = 1; } elsif ($interval < 30) { $min_samp = 4; } elsif ($interval < $min_len) { $min_samp = 10; } elsif ($interval < 3*$min_len) { $min_samp = 30; } elsif ($interval < 10*$min_len) { $min_samp = $min_len; } elsif ($interval < $hour_len) { $min_samp = 5*$min_len; } elsif ($interval < 2*$hour_len) { $min_samp = 10*$min_len; } elsif ($interval < 3*$hour_len) { $min_samp = 15*$min_len; } elsif ($interval < 4*$hour_len) { $min_samp = 20*$min_len; } elsif ($interval < 5*$hour_len) { $min_samp = 30*$min_len; } elsif ($interval < 12*$hour_len) { $min_samp = $hour_len; } elsif ($interval < $day_len) { $min_samp = 2*$hour_len; } elsif ($interval < 2*$day_len) { $min_samp = 4*$hour_len; } elsif ($interval < 5*$day_len) { $min_samp = 12*$hour_len; } elsif ($interval < 7*$day_len) { $min_samp = $day_len; } elsif ($interval < 15*$day_len) { $min_samp = 2*$day_len; } elsif ($interval < 30*$day_len) { $min_samp = 7*$day_len; } elsif ($interval < 365*$day_len) { $min_samp = 30*$day_len; } elsif ($interval < 2*365*$day_len) { $min_samp = 60*$day_len; } else { $min_samp = 120*$day_len; } $min_samp /= $samp_scale; my $start_min = int($start/$min_samp); my $end_min = int($end/$min_samp); for (my $curr_min = $start_min; $curr_min <= $end_min; $curr_min++) { my $bucket = $curr_min*$min_samp; my ($bucket_str,@time_data); if ( $use_local_tz ) { @time_data = localtime($bucket); } else { @time_data = gmtime($bucket); } $time_data[$#time_data] = -1; # unset dst data, broken strftime # keep compatibility with the undocumented 'utc_seconds' global var $Chart::Graph::Gnuplot::show_seconds = $Chart::Graph::Gnuplot::utc_seconds; if ($min_samp >= $min_len && !$Chart::Graph::Gnuplot::show_seconds) { $bucket_str = strftime("%H:%M", @time_data); } else { $bucket_str = strftime("%H:%M:%S", @time_data); } if ($bucket_str =~ /^00:00(:00)?$/ || $curr_min == $start_min + 1) { $bucket_str .= strftime('\n%m/%d', @time_data); if ($Chart::Graph::Gnuplot::show_year) { $bucket_str .= strftime('\n%Y', @time_data); } } push @tics, [ $bucket_str, $bucket ]; } # must check to see if xtics were previously set in the globals # if they are, we'll append them to the time stamp tics # note: collisions are handled by the user if (defined ($global_options->{"xtics"})) { push @tics, @{$global_options->{"xtics"}}; } $global_options->{"xtics"} = \@tics; return 1; } sub _gnuplot_date_utc_normalize { my ($start, $end, $samp_scale, $global_options) = @_; ### this code used to be used as a workaround for an old gnuplot bug ### newer versions don't need it carp "'uts_normalize' is going to be depreciated in a future release\n"; my $min_len = 60; my $hour_len = $min_len*60; my $day_len = $hour_len*24; my $interval = $end - $start; my $min_samp; if (!defined($samp_scale)) { $samp_scale = 1; } if ($interval < 10) { $min_samp = 1; } elsif ($interval < 30) { $min_samp = 4; } elsif ($interval < $min_len) { $min_samp = 10; } elsif ($interval < 3*$min_len) { $min_samp = 30; } elsif ($interval < 10*$min_len) { $min_samp = $min_len; } elsif ($interval < $hour_len) { $min_samp = 5*$min_len; } elsif ($interval < 2*$hour_len) { $min_samp = 10*$min_len; } elsif ($interval < 3*$hour_len) { $min_samp = 15*$min_len; } elsif ($interval < 4*$hour_len) { $min_samp = 20*$min_len; } elsif ($interval < 5*$hour_len) { $min_samp = 30*$min_len; } elsif ($interval < 12*$hour_len) { $min_samp = $hour_len; } elsif ($interval < $day_len) { $min_samp = 2*$hour_len; } elsif ($interval < 2*$day_len) { $min_samp = 4*$hour_len; } elsif ($interval < 5*$day_len) { $min_samp = 12*$hour_len; } elsif ($interval < 7*$day_len) { $min_samp = $day_len; } elsif ($interval < 15*$day_len) { $min_samp = 2*$day_len; } elsif ($interval < 30*$day_len) { $min_samp = 3*$day_len; } else { $min_samp = 30*$day_len; } $min_samp /= $samp_scale; my $start_min = int($start/$min_samp); my $end_min = int($end/$min_samp); my @tics; my $first_date_shown = 0; for (my $curr_min = $start_min; $curr_min <= $end_min; $curr_min++) { my $bucket = $curr_min*$min_samp; my $bucket_str; my @time_data = gmtime($bucket); $time_data[$#time_data] = -1; # unset dst data, broken strftime if ($min_samp >= $min_len) { $bucket_str = strftime('%H:%M', @time_data); } else { $bucket_str = strftime('%H:%M:%S', @time_data); } my $show_date = 0; if ($bucket_str =~ /^00:00(:00)?$/) { $show_date = 1; $first_date_shown = 1; } if ($curr_min == $start_min + 1 && !$first_date_shown) { $show_date = 1; } if ($show_date) { $bucket_str .= strftime('\n%m/%d', @time_data); } push @tics, [ $bucket_str, ($bucket - $start) / $end ]; } # must check to see if xtics were previously set in the globals # if they are, we'll append them to the time stamp tics # note: collisions are handled by the user if (defined ($global_options->{"xtics"})) { push @tics, @{$global_options->{"xtics"}}; } $global_options->{"xtics"} = \@tics; return 1; } 1; __END__ =head1 NAME Chart::Graph::Gnuplot =head1 SYNOPSIS use Chart::Graph::Gnuplot qw(&gnuplot); gnuplot(\%global_options, [\%data_set_options, \@matrix], [\%data_set_options, \@x_column, \@y_column], [\%data_set_options, < filename >], ... ); =head1 DESCRIPTION I is a function in module Chart::Graph that lets you generate graphs on the fly in perl. It was written as a front-end application to gnuplot for hassle-free generation of graphs. I can be supplied with many of the same options and arguments that can be given to gnuplot. For more information on I see the end of this section. =head1 OPTIONS I has a very large number of options corresponding to options available with the gnuplot application itself. This Perl wrapper provides a large subset of the functionality of the application. +----------------------------------------------------------------------------+ | GLOBAL OPTIONS: | +----------------+-----------------------------+-----------------------------+ | NAME | OPTIONS | DEFAULT | +----------------+-----------------------------+-----------------------------+ |'title' | set your own title | 'untitled' | |'output type' | 'pbm','gif','tgif','png', | 'png' | | | 'svg' or "eps $epsoptions"| | |'output file' | set your own output file, | 'untitled-gnuplot.png' | | | undef to output to STDOUT | | |'x-axis label' | set your own label | 'x-axis' | |'y-axis label' | set your own label | 'y-axis' | |'x2-axis label' | set your own label | none | |'y2-axis label' | set your own label | none | |'logscale x' | 0 or 1 | 0 | |'logscale y' | 0 or 1 | 0 | |'logscale x2' | 0 or 1 | 0 | |'logscale y2' | 0 or 1 | 0 | | 'xtics' | set your own tics on x-axis | none | | | (see example below) | | | 'x2tics' | set your own tics on x2-axis| none | | | (see example below) | | | 'ytics' | set your own tics on y-axis | none | | | (see example below) | | | 'y2tics' | set your own tics on y2-axis| none | | | (see example below) | | | 'xrange' | set xrange, accepts both | none | | | string '[$xmin:$xmax]' | | | | or arrayref [$xmin,$xmax] | | | 'yrange' | set yrange, see xrange | none | | | | | | 'uts' | set your own range in unix | none | | | timestamps, array ref: | | | | [start_ts,end_ts,, | | | | ] | | | | see UNIX TIMESTAMPS example| | | 'xdata' | 'time' to indicate that | none | | | x-axis is date/time data | | | 'ydata' | 'time' to indicate that | none | | | y-axis is date/time data | | | 'x2data' | 'time' to indicate that | none | | | x2-axis is date/time data | | | 'y2data' | 'time' to indicate that | none | | | y2-axis is date/time data | | | 'timefmt' | "Input date/time string" | none | | | see Gnuplot manual for info| | | 'format' | array ref: First element is | | | | axis: 'x', 'y', 'x2', 'y2'.| | | | Second element is | | | | 'output date/time string" | | | | see Gnuplot manual for info| | | 'extra_opts' | set your own Gnuplot | none | | | options, either an arrayref| | | | or string ("\n"-separated) | | | 'size' | scale the display size of | none | | | the plot, arrayref [$x, $y]| | +----------------+-----------------------------+-----------------------------+ +----------------------------------------------------------------------------+ | Data Set Options: | +----------------+-----------------------------+-----------------------------+ | Name | Options | Default | +----------------+-----------------------------+-----------------------------+ | 'type' | 'matrix', 'columns', 'file',| none | | | 'function', see examples | | | | below | | | 'title' | set your own title | 'untitled data' | | 'style' | 'points','lines','impulses' | 'points' | | | 'errorbars', etc... | | | | see ERRORBARS example | | | 'axes' | 'x1y1', 'x2y2', 'x1y2', etc.| 'x1y1' | | 'using' | map data to what will be | '1:2' | | | plotted, see ERRORBARS | | | | example | | +----------------+-----------------------------+-----------------------------+ Data can be presented to I in one of 3 formats for the convenience of the user: \@matrix: an array reference of [x,y] pairs of data Alternatively: \@x_column, \@y_column: two array references of data of equal length. \@x_column is the x-axis data. \@y_column is the y-axis data. Finally, data can be stored in a file. =head2 USING GNUPLOT TO READ AND PLOT DATE/TIME DATA DIRECTLY I now has the capability to read date/time data and to create graphs which display date/time on any axis. Unfortunately, mechanism for reading data is less sophisticated than the mechanism for writing data. I implements date/time data in the same way as I itself is presently implemented for consistency with the application. Any axis can be set to read date/time data instead of numerical data. This is done by setting the options C, C, C, or C to the value C