Re: Help with GD::Graph
by samtregar (Abbot) on May 11, 2006 at 07:10 UTC
|
Showing ("00" .. "24") will be easy but I doubt it'll look very good. It also won't work as soon as you have more than 24 hours to display, or when you don't have enough space for all the numbers. I had a similar problem a few months back, here's how I solved it:
# format a set of dates for display on the X axis
sub dates_to_header {
my ($self, $dates) = @_;
my @header = ("") x scalar @$dates;
# choose a format based on the length of the range
my $delta = $dates->[-1]->subtract_datetime($dates->[0]);
my $months = $delta->delta_months;
my $days = $delta->delta_days + ($months * 30); # rough guess is
+ ok here
my ($format, $number);
if ($months >= 5 and $months <= 11) {
$format = "%b"; # month abrev
$number = $months + 1;
} elsif ($days <= 1) {
$format = "%l %p"; # HH AM/PM
$number = 5;
} elsif ($days <= 6) {
$format = "%a"; # weekdays, mark them all
$number = @$dates;
} elsif ($dates->[0]->year eq $dates->[-1]->year) {
$format = "%m/%d"; # MM/DD
$number = 5;
} else {
$format = "%D"; # MM/DD/YYx
$number = 5;
}
if ($number == @$dates) {
# marking all bars
return [map { $_->strftime($format) } @$dates];
}
# find the optimal spacing between labels
my $spacing = @$dates / ("$number.0" - 1);
# place a label at the edges
my @spots = (0, $#$dates);
# if there are odd numbers of dates to place, put one in the cente
+r
my $center = int(@$dates / 2);
push(@spots, $center) if $number % 2;
# place the remaining dates around the center, following the spaci
+ng
for (1 .. (($number - @spots) / 2)) {
push(@spots, $center + int($spacing * $_));
push(@spots, $center - int($spacing * $_));
}
# render the date at each chosen spot
foreach my $spot (@spots) {
$header[$spot] = $dates->[$spot]->strftime($format);
}
return \@header;
}
That subroutine takes a reference to an array of DateTime objects representing the data-points on the graph. It then applies a bunch of heuristics to come up with some reasonable labels for the X axis, leaving enough space for them to render successfully.
Hope that helps.
-sam
| [reply] [d/l] |
Re: Help with GD::Graph
by zentara (Cardinal) on May 11, 2006 at 12:09 UTC
|
Maybe you are looking for the X_labels_vertical option?
#!/usr/bin/perl
use strict;
use warnings;
use GD::Graph::lines;
my $data_ref = [
['2004-01-08','2004-01-09','2004-01-10'], # X-values
[ 15.9, 10.7, 15.8 ], # Y-value 1
[ 18,18,18 ], #Y-value 2 reference line
[ 12,12,12 ],
];
my $graph = GD::Graph::lines->new(400, 300);
$graph->set(
transparent => '0',
bgclr => 'lgray',
boxclr => 'white',
fgclr => 'white',
x_label => 'Time',
x_labels_vertical => 1,
x_label_skip => 50,
y_label => 'Price',
title => 'Some simple graph',
y_max_value => 20,
y_tick_number => 1,
y_label_skip => 2,
long_ticks => 0, # make a grid of all the ticks
) or die $graph->error;
my $gd_image = $graph->plot($data_ref) or die $graph->error;
open(OUTPUT, ">$0.png") or die "Can't open $0.png: $!\n";
print OUTPUT $gd_image->png();
close(OUTPUT);
I'm not really a human, but I play one on earth.
flash japh
| [reply] [d/l] |
|
|
Thanks for the replies!
Hmmm, not sure what I'm doing wrong now.. I tried that but the graph is kinda screwed.
#!/usr/bin/perl
use GD::Graph::lines;
use strict;
my $i = "";
open(DATA, "newdata.txt")
or die "Cannot open for reading: $!\n";
my @newdata = <DATA>;
close(DATA);
my @graph_data = [ ['May 12', 'May 13'], [], [], [] ];
for($i = 0; $i < scalar($#newdata + 1); $i++) {
my ($celcius, $radiation, $time) = split(/ /, $newdata[$i]);
$graph_data[1][$i] = $celcius;
$graph_data[2][$i] = $radiation;
$graph_data[3][$i] = $time;
}
my $mygraph = GD::Graph::lines->new(600, 300);
$mygraph->set(
x_label => 'Time',
y_label => 'Value',
title => 'Temperature and Radiation',
x_labels_vertical => 1,
x_label_skip => 50,
line_types => [1, 1],
line_width => 2,
dclrs => ['blue', 'green'],
) or warn $mygraph->error;
$mygraph->set_legend_font(GD::gdMediumBoldFont);
$mygraph->set_legend('Temperature', 'Radiation');
my $myimage = $mygraph->plot(\@graph_data) or die $mygraph->error;
open(GRAPH, ">graph.png");
print GRAPH $myimage->png;
close(GRAPH);
newdata.txt:
36 85 13:37
36 86 13:38
Man I suck at Perl :/
| [reply] [d/l] [select] |
|
|
You are on the right track, just making some dereferencing mistakes, and the big x_label_skip value. I'll post a better example later, if you don't figure it out.
I'm not really a human, but I play one on earth.
flash japh
| [reply] |
|
|
Try this. I will say, that you are going to run into a problem of having too many x values for the width of the graph, then you need to adjust this x_label_skip value to condense it. A Tk canvas would be a nice way to do this. You could plot every value on a scrolled canvas, and scroll to the current time(or time of interest). Of course if it's for the web, Tk won't help.
#!/usr/bin/perl
use warnings;
use GD::Graph::lines;
use strict;
my @time;
my @temp;
my @rad;
while(my $line = <DATA>){
chomp $line;
my ($celcius, $radiation, $time) = split(/ /, $line);
push @temp, $celcius;
push @rad, $radiation;
push @time, $time;
}
my $graph_data = [
[@time], # X-values
[@rad], # Y-value 1
[@temp], #Y-value 2 reference line
];
my $mygraph = GD::Graph::lines->new(600, 300);
$mygraph->set(
transparent => '0',
bgclr => 'white',
boxclr => 'lgray',
fgclr => 'white',
x_label => 'Time',
y_label => 'Value',
title => 'Temperature and Radiation',
x_labels_vertical => 1,
# x_label_skip => 1,
# line_types => [1, 1],
line_width => 2,
dclrs => ['blue', 'green'],
) or warn $mygraph->error;
$mygraph->set_legend_font(GD::gdLargeFont);
$mygraph->set_legend('Radiation','Temperature');
my $myimage = $mygraph->plot($graph_data) or die $mygraph->error;
open(GRAPH, ">$0.png");
print GRAPH $myimage->png;
close(GRAPH);
__END__
36 85 13:37
36 86 13:38
37 85 13:39
37 86 13:40
38 85 13:41
38 86 13:42
38 85 13:43
39 86 13:44
39 85 13:45
39 88 13:46
39 88 13:47
39 88 13:48
39 88 13:49
39 88 13:50
39 88 13:51
38 87 13:52
38 85 13:53
38 86 13:54
38 85 13:55
38 86 13:56
38 85 13:57
38 86 13:58
37 85 13:59
37 86 14:00
37 85 14:01
37 86 14:02
37 85 14:03
37 86 14:04
37 85 14:05
36 86 14:06
36 85 14:07
36 86 14:08
I'm not really a human, but I play one on earth.
flash japh
| [reply] [d/l] |
Re: Help with GD::Graph
by odha57 (Monk) on May 12, 2006 at 20:24 UTC
|
The last post has pretty much laid out how I use GD::Graph.
Here is an example to look at from something I use. The data I collect is on 5 minute intervals, but the graph can be from a day to several days wide. On a 600x480 graph, if I get more than 24 time values it looks very cluttered. You will want to use x_label_skip for this and x_labels_vertical to stand them on end. I hope it helps. One other thing to consider is using RRDTool. If your graphing app is going 7x24 it can collect your data, age off your data and then produce a graph for you. I use it all the time for just this type of thing. It should install fine on the machine you are using GD::Graph on. It uses all the same stuff.
If you would like to see a couple of examples of putting in data and generating a graph I'd be happy to pass on a couple.
# here is where we put a varying amount of timestamps across the graph
+ depending on how much time is involved.
# if this isn't done, you can either see a graph with 2 timestamps or
+with so many they are not legible.
if ($elapsed_time > 604800){
$x_label_skip = 216;
} elsif ($elapsed_time > 172800){
$x_label_skip = 36;
} elsif ($elapsed_time > 86400){
$x_label_skip = 24;
} elsif ($elapsed_time > 43200){
$x_label_skip = 12;
} elsif ($elapsed_time > 3600){
$x_label_skip = 6;
} else {
$x_label_skip = 1;
}
foreach $entry (@timeline){
+ # build an array of timestamps of date year
$date = &SYSTIME($entry);
push @dateline, $date;
+ # the epoch time is converted to a string and saved in a list
}
$poop = 'CDR data from '.$startdate.' - '.$enddate;
+ # a fancy title for our graph
@data = (\@dateline, \@callAgent_A, \@callAgent_B);
+ # this is an array of arrays. The first one is the X axis
+ # which is the time, the others are the Y axis
my $query = new CGI;
+ # start a new cgi object
print $query->header(-type => 'image/png');
+ # output a content type header for a png file
my $data = GD::Graph::Data->new() or die GD::Graph::Data->error;
$data->copy_from(\@data);
+ # this tells the graph to get the data from the data array
my $my_graph = new GD::Graph::area(600,480);
+ # and this says what type (area) and it's size
$my_graph->set(
+ # and set up all the particulars...
r_margin => 1,
x_label => 'Time',
y_label => 'Hourly Attempts',
title => $poop,
y_min_value => 0,
y_tick_number => 8,
y_label_skip => 1,
x_ticks => 1,
x_label_skip => $x_label_skip,
x_labels_vertical => 1,
x_label_position => 1/2,
cumulate => 1,
transparent => 1,
)or warn $my_graph->error;
open (IMG, ">-") or die;
+ # open up an output file to STDOUT
binmode IMG;
$my_graph->set( dclrs => [ qw(lgreen lblue) ] );
+ # here is the colors in the graph
$my_graph->set_legend("Call Agent A","Call Agent B" );
+ # and the legend text
$my_graph->set_title_font(GD::Font->Giant);
print IMG $my_graph->plot($data)->png();
+ # and this plots the image out to a png format
close IMG;
| [reply] [d/l] |