File Coverage

blib/lib/Deco/Dive/Plot.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             #######################################
2             # Module : Deco::Dive::Plot.pm
3             # Author : Jaap Voets
4             # Date : 02-06-2006
5             #######################################
6             package Deco::Dive::Plot;
7              
8 1     1   921 use strict;
  1         2  
  1         48  
9 1     1   6 use warnings;
  1         2  
  1         40  
10 1     1   7 use Carp;
  1         2  
  1         87  
11 1     1   498 use GD::Graph::lines;
  0            
  0            
12             use GD::Graph::bars;
13              
14             our $VERSION = '0.3';
15              
16             # some constants used
17             use constant DEFAULT_WIDTH => 600;
18             use constant DEFAULT_HEIGHT => 400;
19              
20             # Constructor
21             sub new {
22             my $class = shift;
23             my $dive = shift;
24              
25             croak "Please provide a Deco::Dive object for plotting" unless ref($dive) eq 'Deco::Dive';
26              
27             my $self = { dive => $dive };
28            
29             bless $self, $class;
30            
31             return $self;
32             }
33              
34             # plot the depth versus time
35             sub depth {
36             my $self = shift;
37             my %opt = @_;
38            
39             # divide the seconds by 60 to get minutes
40             my @times = map { $_ / 60 } @{ $self->{dive}->{timepoints} };
41             croak "There are no timestamps set for this dive" if ( scalar( @times ) == 0);
42              
43             # multiply the depths by -1 to get a nicer picture
44             my @depths = map { -1 * $_ } @{ $self->{dive}->{depths} };
45             croak "There are no depth points set for this dive" if ( scalar( @depths ) == 0);
46              
47             my $width = $opt{width} || DEFAULT_WIDTH;
48             my $height = $opt{height} || DEFAULT_HEIGHT;
49             my $outfile = $opt{file} || 'depth.png';
50              
51             my $graph = GD::Graph::lines->new($width, $height);
52             $graph->set(
53             x_label => 'Time (minutes)',
54             y_label => 'Depth (meter)',
55             title => 'Depth profile',
56             y_max_value => 0,
57             ) or die $graph->error;
58              
59             my @data = (\@times, \@depths);
60              
61             my $gd = $graph->plot(\@data) or die $graph->error;
62             open(IMG, ">$outfile") or die $!;
63             binmode IMG;
64             print IMG $gd->png;
65             close IMG;
66              
67             }
68              
69             # plot the percentage saturation of each tissue
70             # this will be a bar graph using the tissue nr for the x-axis
71             sub percentage {
72             my $self = shift;
73             my %opt = @_;
74            
75             # at what point of the dive do we
76             my $plot_time;
77             if (! defined $opt{time} ) {
78             # take the last one
79             $plot_time = pop @{ $self->{dive}->{timepoints} };
80             } else {
81             # find the corresponding time in seconds in the time array
82             my $time = 60 * $opt{time}; # time in minutes
83             foreach my $timestamp ( @{ $self->{dive}->{timepoints} } ) {
84             if ($timestamp <= $time ) {
85             $plot_time = $timestamp;
86             }
87             }
88             }
89            
90             # get the data
91             my @nrs;
92             my @percentages;
93             foreach my $tissue ( @{ $self->{dive}->{tissues} } ) {
94             next if ! defined $tissue;
95             # fill the X-axis with the tissue numbers
96             my $num = $tissue->nr;
97             push @nrs, $num;
98             # peek inside the Dive object to get the precalculated percentages
99             push @percentages, sprintf('%.0f', $self->{dive}->{info}->{$num}->{$plot_time}->{percentage});
100             }
101             croak "There is nothing to display for this dive" if ( scalar( @percentages ) == 0);
102            
103             my $width = $opt{width} || DEFAULT_WIDTH;
104             my $height = $opt{height} || DEFAULT_HEIGHT;
105             my $outfile = $opt{file} || 'percentage.png';
106            
107             my $graph = GD::Graph::bars->new($width, $height);
108             $graph->set(
109             x_label => 'Tissue',
110             y_label => 'Percentage',
111             title => 'Tissue saturation',
112             y_min_value => 0,
113             ) or die $graph->error;
114              
115             my @data = (\@nrs, \@percentages);
116             my $gd = $graph->plot(\@data) or die $graph->error;
117             open(IMG, ">$outfile") or die $!;
118             binmode IMG;
119             print IMG $gd->png;
120             close IMG;
121             }
122              
123             # create a graph of the internal pressures of each tissue
124             # note that you can restrict the tissues displayed by doing something like
125             # pressures( tissues => [1, 5, 7] ) to get tissues 1, 5 and 7 instead of all of them
126             sub pressures {
127             my $self = shift;
128             my %opt = @_;
129              
130             $opt{y_label} = 'Internal Pressure (bar)';
131             $self->_info( 'pressure', %opt );
132             }
133              
134             sub nodeco {
135             my $self = shift;
136             my %opt = @_;
137              
138             $opt{y_label} = 'No deco time (minutes)';
139             $self->_info( 'nodeco_time', %opt );
140             }
141              
142              
143             # plot a certain info series for all tissues
144             # after simulating a dive, there are arrays of information setup
145             # throught this routine you can get the series of each info
146             sub _info {
147             my $self = shift;
148             my $what = shift; # one of nodeco_time, safe_depth, percentage or pressure
149             my %opt = @_;
150              
151             # could be we want to restrict to one or more tissues
152             my @tissues;
153             if ( defined $opt{tissues} ) {
154             @tissues = @{ $opt{tissues} };
155             }
156             my @times = @{ $self->{dive}->{timepoints} };
157             croak "There are no timestamps set for this dive" if ( scalar( @times ) == 0);
158            
159             # divide the seconds by 60 to get minutes
160             my @minutes = map { $_ / 60 } @{ $self->{dive}->{timepoints} };
161            
162             my $width = $opt{width} || DEFAULT_WIDTH;
163             my $height = $opt{height} || DEFAULT_HEIGHT;
164             my $outfile = $opt{file} || $what . '.png';
165            
166             my $y_label = $opt{y_label} || 'Depth (meter)';
167             my $graph = GD::Graph::lines->new($width, $height);
168             $graph->set(
169             x_label => 'Time (minutes)',
170             y_label => $y_label,
171             title => "$what profile",
172             ) or die $graph->error;
173            
174             my @data;
175             push @data, \@minutes; # load the time values
176              
177             foreach my $tissue ( @{ $self->{dive}->{tissues} } ) {
178             next if ! defined $tissue; # first array element is empty
179             my $num = $tissue->nr;
180            
181             if (scalar(@tissues) > 0) {
182             # we want to restrict to one or more tissues
183             # so skip the ones that are not in our tissues list
184             next if ( ! grep ($num, @tissues) );
185             }
186             my @y = ();
187             foreach my $time (@times) {
188             push @y, $self->{dive}->{info}->{$num}->{$time}->{$what};
189             }
190            
191             # add the series to the plot data
192             push @data, \@y;
193             }
194            
195             my $gd = $graph->plot(\@data) or die $graph->error;
196             open(IMG, ">$outfile") or die $!;
197             binmode IMG;
198             print IMG $gd->png;
199             close IMG;
200              
201             }
202              
203             1;
204              
205              
206             __END__