File Coverage

blib/lib/Chart/Plot/Annotated.pm
Criterion Covered Total %
statement 15 18 83.3
branch n/a
condition n/a
subroutine 5 6 83.3
pod n/a
total 20 24 83.3


line stmt bran cond sub pod time code
1             package Chart::Plot::Annotated;
2            
3 1     1   262725 use 5.006;
  1         5  
  1         50  
4 1     1   6 use strict;
  1         2  
  1         40  
5 1     1   6 use warnings;
  1         15  
  1         37  
6 1     1   6 use Carp;
  1         2  
  1         87  
7             our $VERSION = '0.01';
8            
9             ##################################################################
10             # define this class:
11 1     1   5 use base 'Chart::Plot';
  1         1  
  1         1820  
12             use Class::MethodMaker
13             # the extra data we'll put into the Chart::Plot object
14 0           object_list => [ Chart::Plot::Annotated::_DataPt => '_AnnoData' ],
15            
16             # formatting internals
17             get_set => [ qw [ _anno_xOffset _anno_yOffset _anno_font _anno_color ] ],
18            
19             # error-reporting
20 0     0     get_set => [ '_problem' ];
  0            
21             ##################################################################
22             # define an auxiliary class:
23             use Class::Struct Chart::Plot::Annotated::_DataPt =>
24             [ X => '$', Y => '$', anno => '$' ];
25             ##################################################################
26             # new public method:
27             sub setAnnoData {
28             my $self = shift;
29             my @annos = @{shift @_};
30            
31             my $rc = $self->setData(@_);
32             if (not $rc) {
33             return $rc;
34             }
35            
36             # store the datapoints for later
37             if (ref $_[0] eq 'ARRAY' and ref $_[1] eq 'ARRAY') {
38             # x datapts and y datapts separated
39             my (@x) = @{shift @_};
40             my (@y) = @{shift @_};
41             unless (@annos == @x) {
42             $self->_problem("different numbers of annotations and x-values");
43             return 0;
44             }
45            
46             unless (@annos == @y) {
47             $self->_problem("different numbers of annotations and y-values");
48             return 0;
49             }
50            
51             while (@annos) {
52             my $datum =
53             Chart::Plot::Annotated::_DataPt->new( X => shift @x,
54             Y => shift @y,
55             anno => shift @annos
56             );
57             $self->push__AnnoData($datum);
58             }
59             }
60             else {
61             # assume X and Y datapoints presented as one array
62             my @xy = @{shift @_};
63             unless (@xy == 2*@annos) {
64             $self->_problem("annos not synced with (x,y) values " .
65             "-- different numbers of elements");
66             return 0;
67             }
68             while (@annos) {
69             my $datum;
70             my $anno = shift @annos;
71             if (not defined $anno) {
72             $anno = '';
73             }
74             $datum =
75             Chart::Plot::Annotated::_DataPt->new( X => shift @xy,
76             Y => shift @xy,
77             anno => $anno );
78             $self->push__AnnoData($datum);
79             }
80             }
81            
82             if (defined $_[0] and ref $_[0] eq 'ARRAY') {
83             $self->_problem("too many arrayrefs to setAnnoData");
84             return 0;
85             }
86            
87             # arrive here? no problems.
88             return 1;
89             }
90             ##################################################################
91             # override base class to handle extra layer's new possible errors
92             sub error {
93             my $self = shift;
94             if (defined $self->_problem) {
95             return $self->_problem();
96             }
97             # else call base class
98             return $self->SUPER::error();
99             }
100             ##################################################################
101             # override base class to handle extra layer's extra options
102             sub setGraphOptions {
103             my $self = shift;
104             my %args = @_;
105             if (defined $args{anno_color}) {
106             if (ref $args{anno_color} ne 'ARRAY') {
107             $self->_problem("anno_color arg to setGraphOptions()" .
108             " needs arrayref value");
109             return 0;
110             }
111             $self->_setAnnoColor(@{$args{anno_color}})
112             or return 0; # problem?
113             delete $args{anno_color};
114             }
115            
116             if (defined $args{anno_xOffset}) {
117             $self->_anno_xOffset($args{anno_xOffset});
118             delete $args{anno_xOffset};
119             }
120             if (defined $args{anno_yOffset}) {
121             $self->_anno_yOffset($args{anno_yOffset});
122             delete $args{anno_yOffset};
123             }
124            
125             # send remaining args to base class, if there are any left
126             if (%args) {
127             return $self->SUPER::setGraphOptions(%args);
128             }
129             else {
130             # everything went fine!
131             return 1;
132             }
133             }
134             ##################################################################
135             use GD; # use this to directly annotate the resulting plot with the
136             # annotations.
137             ##################################################################
138             # override base class to handle extra layer's extra markup on the
139             # image object
140             sub draw {
141             my $self = shift;
142             my $gdObj = $self->SUPER::getGDobject();
143            
144             if (not defined $self->_anno_color) {
145             $self->_setAnnoColor(0,0,0); # black
146             }
147             if (not defined $self->_anno_font) {
148             $self->_anno_font( gdTinyFont );
149             }
150             if (not defined $self->_anno_xOffset) {
151             $self->_anno_xOffset( 0 );
152             }
153             if (not defined $self->_anno_yOffset) {
154             $self->_anno_yOffset( 0 );
155             }
156            
157             # set all the annotations
158             while ($self->count__AnnoData) {
159             my $datum = $self->shift__AnnoData;
160             if (defined $datum->anno and length $datum->anno) {
161             $self->_setAnno($gdObj, $datum);
162             }
163             # otherwise skip empty strings
164             }
165            
166             # done with extra markup, now call base class draw; returning
167             # whatever it does
168             return $self->SUPER::draw();
169             }
170             ##################################################################
171             # private methods
172             ##################################################################
173             sub _setAnnoColor {
174             # sets the annotation color to the appropriate color-triple. Used
175             # for handling configuration data
176             my $self = shift;
177             my ($r, $g, $b) = @_;
178            
179             if (@_ < 3) {
180             # fatal
181             $self->_problem( "need 3 args to annotation color-setting" );
182             return 0;
183             }
184             if (@_ > 3) {
185             # non-fatal, though silly
186             carp "_setAnnoColor args beyond (R,G,B) ignored";
187             }
188            
189             my $gdObj = $self->SUPER::getGDobject();
190             my $color = $gdObj->colorAllocate($r, $g, $b);
191            
192             $self->_anno_color($color);
193             return 1;
194             }
195             ##################################################################
196             sub _setAnno {
197             # writes an annotation onto the base class Chart::Plot, given a
198             # pointer to the GD Object underneath.
199             my $self = shift;
200             my $gdObj = shift;
201             my $datum = shift;
202             my ($xp, $yp) = $self->SUPER::data2pxl($datum->X, $datum->Y);
203             $gdObj->string($self->_anno_font,
204             ($xp + $self->_anno_xOffset),
205             ($yp + $self->_anno_yOffset),
206             $datum->anno,
207             $self->_anno_color);
208             }
209             ##################################################################
210             1;
211            
212             __END__