comparison variant_effect_predictor/Bio/Graphics/Glyph/xyplot.pm @ 0:21066c0abaf5 draft

Uploaded
author willmclaren
date Fri, 03 Aug 2012 10:04:48 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:21066c0abaf5
1 package Bio::Graphics::Glyph::xyplot;
2
3 use strict;
4 use Bio::Graphics::Glyph::segments;
5 use vars '@ISA';
6 use GD 'gdTinyFont';
7
8 @ISA = 'Bio::Graphics::Glyph::segments';
9
10 use constant DEFAULT_POINT_RADIUS=>1;
11
12 my %SYMBOLS = (
13 triangle => \&draw_triangle,
14 square => \&draw_square,
15 disc => \&draw_disc,
16 point => \&draw_point,
17 );
18
19 sub point_radius {
20 shift->option('point_radius') || DEFAULT_POINT_RADIUS;
21 }
22
23 sub pad_top { 0 }
24
25 sub draw {
26 my $self = shift;
27 my ($gd,$dx,$dy) = @_;
28 my ($left,$top,$right,$bottom) = $self->calculate_boundaries($dx,$dy);
29
30 my @parts = $self->parts;
31 return $self->SUPER::draw(@_) unless @parts > 0;
32
33 # figure out the scale and such like
34 my $max_score = $self->option('max_score');
35 my $min_score = $self->option('min_score');
36
37 unless (defined $max_score && defined $min_score) {
38 my $first = $parts[0];
39 $max_score = $min_score = eval { $first->feature->score} || 0;
40 for my $part (@parts) {
41 my $s = eval { $part->feature->score };
42 next unless defined $s;
43 $max_score = $s if $s > $max_score;
44 $min_score = $s if $s < $min_score;
45 }
46 }
47
48 # if a scale is called for, then we adjust the max and min to be even
49 # multiples of a power of 10.
50 if ($self->option('scale')) {
51 $max_score = max10($max_score);
52 $min_score = min10($min_score);
53 }
54
55 my $height = $self->option('height');
56 my $scale = $max_score > $min_score ? $height/($max_score-$min_score)
57 : 1;
58 my $x = $dx;
59 my $y = $dy + $self->top + $self->pad_top;
60
61 # now seed all the parts with the information they need to draw their positions
62 foreach (@parts) {
63 my $s = eval {$_->feature->score};
64 next unless defined $s;
65 my $position = ($s-$min_score) * $scale;
66 $_->{_y_position} = $bottom - $position;
67 }
68
69 my $type = $self->option('graph_type');
70 $self->_draw_histogram($gd,$x,$y) if $type eq 'histogram';
71 $self->_draw_boxes($gd,$x,$y) if $type eq 'boxes';
72 $self->_draw_line ($gd,$x,$y) if $type eq 'line'
73 or $type eq 'linepoints';
74 $self->_draw_points($gd,$x,$y) if $type eq 'points'
75 or $type eq 'linepoints';
76
77 $self->_draw_scale($gd,$scale,$min_score,$max_score,$dx,$dy) if $self->option('scale');
78 }
79
80 sub log10 { log(shift)/log(10) }
81 sub max10 {
82 my $a = shift;
83 $a = 1 if $a <= 0;
84 my $l=int(log10($a));
85 $l = 10**$l;
86 my $r = $a/$l;
87 return $r*$l if int($r) == $r;
88 return $l*int(($a+$l)/$l);
89 }
90 sub min10 {
91 my $a = shift;
92 $a = 1 if $a <= 0;
93 my $l=int(log10($a));
94 $l = 10**$l;
95 my $r = $a/$l;
96 return $r*$l if int($r) == $r;
97 return $l*int($a/$l);
98 }
99
100 sub _draw_histogram {
101 my $self = shift;
102 my ($gd,$left,$top) = @_;
103
104 my @parts = $self->parts;
105 my $fgcolor = $self->fgcolor;
106
107 # draw each of the component lines of the histogram surface
108 for (my $i = 0; $i < @parts; $i++) {
109 my $part = $parts[$i];
110 my $next = $parts[$i+1];
111 my ($x1,$y1,$x2,$y2) = $part->calculate_boundaries($left,$top);
112 $gd->line($x1,$part->{_y_position},$x2,$part->{_y_position},$fgcolor);
113 next unless $next;
114 my ($x3,$y3,$x4,$y4) = $next->calculate_boundaries($left,$top);
115 if ($x2 == $x3) {# connect vertically to next level
116 $gd->line($x2,$part->{_y_position},$x2,$next->{_y_position},$fgcolor);
117 } else {
118 $gd->line($x2,$part->{_y_position},$x2,$y2,$fgcolor); # to bottom
119 $gd->line($x2,$y2,$x3,$y2,$fgcolor); # to right
120 $gd->line($x3,$y4,$x3,$next->{_y_position},$fgcolor); # up
121 }
122 }
123
124 # end points: from bottom to first
125 my ($x1,$y1,$x2,$y2) = $parts[0]->calculate_boundaries($left,$top);
126 $gd->line($x1,$y2,$x1,$parts[0]->{_y_position},$fgcolor);
127 # from last to bottom
128 my ($x3,$y3,$x4,$y4) = $parts[-1]->calculate_boundaries($left,$top);
129 $gd->line($x4,$parts[-1]->{_y_position},$x4,$y4,$fgcolor);
130
131 # from left to right -- don't like this
132 # $gd->line($x1,$y2,$x4,$y4,$fgcolor);
133
134 # That's it. Not too hard.
135 }
136
137 sub _draw_boxes {
138 my $self = shift;
139 my ($gd,$left,$top) = @_;
140
141 my @parts = $self->parts;
142 my $fgcolor = $self->fgcolor;
143 my $bgcolor = $self->bgcolor;
144 my $height = $self->height;
145
146 # draw each of the component lines of the histogram surface
147 for (my $i = 0; $i < @parts; $i++) {
148 my $part = $parts[$i];
149 my $next = $parts[$i+1];
150 my ($x1,$y1,$x2,$y2) = $part->calculate_boundaries($left,$top);
151 $self->filled_box($gd,$x1,$part->{_y_position},$x2,$y2,$bgcolor,$fgcolor);
152 next unless $next;
153 my ($x3,$y3,$x4,$y4) = $next->calculate_boundaries($left,$top);
154 $gd->line($x2,$y2,$x3,$y4,$fgcolor) if $x2 < $x3;
155 }
156
157 # That's it.
158 }
159
160 sub _draw_line {
161 my $self = shift;
162 my ($gd,$left,$top) = @_;
163
164 my @parts = $self->parts;
165 my $fgcolor = $self->fgcolor;
166 my $bgcolor = $self->bgcolor;
167
168 # connect to center positions of each interval
169 my $first_part = shift @parts;
170 my ($x1,$y1,$x2,$y2) = $first_part->calculate_boundaries($left,$top);
171 my $current_x = ($x1+$x2)/2;
172 my $current_y = $first_part->{_y_position};
173
174 for my $part (@parts) {
175 my ($x1,$y1,$x2,$y2) = $part->calculate_boundaries($left,$top);
176 my $next_x = ($x1+$x2)/2;
177 my $next_y = $part->{_y_position};
178 $gd->line($current_x,$current_y,$next_x,$next_y,$fgcolor);
179 ($current_x,$current_y) = ($next_x,$next_y);
180 }
181
182 }
183
184 sub _draw_points {
185 my $self = shift;
186 my ($gd,$left,$top) = @_;
187 my $symbol_name = $self->option('point_symbol') || 'point';
188 my $symbol_ref = $SYMBOLS{$symbol_name};
189
190 my @parts = $self->parts;
191 my $bgcolor = $self->bgcolor;
192 my $pr = $self->point_radius;
193
194 for my $part (@parts) {
195 my ($x1,$y1,$x2,$y2) = $part->calculate_boundaries($left,$top);
196 my $x = ($x1+$x2)/2;
197 my $y = $part->{_y_position};
198 $symbol_ref->($gd,$x,$y,$pr,$bgcolor);
199 }
200 }
201
202 sub _draw_scale {
203 my $self = shift;
204 my ($gd,$scale,$min,$max,$dx,$dy) = @_;
205 my ($x1,$y1,$x2,$y2) = $self->calculate_boundaries($dx,$dy);
206
207 my $side = $self->option('scale');
208 return if $side eq 'none';
209 $side ||= 'both';
210
211 my $fg = $self->fgcolor;
212 my $half = ($y1+$y2)/2;
213
214 $gd->line($x1+1,$y1,$x1+1,$y2,$fg) if $side eq 'left' || $side eq 'both';
215 $gd->line($x2-2,$y1,$x2-2,$y2,$fg) if $side eq 'right' || $side eq 'both';
216
217 for ([$y1,$max],[$half,int(($max-$min)/2+0.5)]) {
218 $gd->line($x1,$_->[0],$x1+3,$_->[0],$fg) if $side eq 'left' || $side eq 'both';
219 $gd->line($x2-4,$_->[0],$x2,$_->[0],$fg) if $side eq 'right' || $side eq 'both';
220 if ($side eq 'left' or $side eq 'both') {
221 $gd->string(gdTinyFont,
222 $x1 + 5,$_->[0]-(gdTinyFont->height/3),
223 $_->[1],
224 $fg);
225 }
226 if ($side eq 'right' or $side eq 'both') {
227 $gd->string(gdTinyFont,
228 $x2-5 - (length($_->[1])*gdTinyFont->width),$_->[0]-(gdTinyFont->height/3),
229 $_->[1],
230 $fg);
231 }
232 }
233 }
234
235 # we are unbumpable!
236 sub bump {
237 return 0;
238 }
239
240 sub connector {
241 my $self = shift;
242 my $type = $self->option('graph_type');
243 return 1 if $type eq 'line' or $type eq 'linepoints';
244 }
245
246 sub height {
247 my $self = shift;
248 return $self->option('graph_height') || $self->SUPER::height;
249 }
250
251 sub draw_triangle {
252 my ($gd,$x,$y,$pr,$color) = @_;
253 my ($vx1,$vy1) = ($x-$pr,$y+$pr);
254 my ($vx2,$vy2) = ($x, $y-$pr);
255 my ($vx3,$vy3) = ($x+$pr,$y+$pr);
256 $gd->line($vx1,$vy1,$vx2,$vy2,$color);
257 $gd->line($vx2,$vy2,$vx3,$vy3,$color);
258 $gd->line($vx3,$vy3,$vx1,$vy1,$color);
259 }
260 sub draw_square {
261 my ($gd,$x,$y,$pr,$color) = @_;
262 $gd->line($x-$pr,$y-$pr,$x+$pr,$y-$pr,$color);
263 $gd->line($x+$pr,$y-$pr,$x+$pr,$y+$pr,$color);
264 $gd->line($x+$pr,$y+$pr,$x-$pr,$y+$pr,$color);
265 $gd->line($x-$pr,$y+$pr,$x-$pr,$y-$pr,$color);
266 }
267 sub draw_disc {
268 my ($gd,$x,$y,$pr,$color) = @_;
269 $gd->arc($x,$y,$pr,$pr,0,360,$color);
270 }
271 sub draw_point {
272 my ($gd,$x,$y,$pr,$color) = @_;
273 $gd->setPixel($x,$y,$color);
274 }
275
276 sub _subseq {
277 my $class = shift;
278 my $feature = shift;
279 return $feature->segments if $feature->can('segments');
280 my @split = eval { my $id = $feature->location->seq_id;
281 my @subs = $feature->location->sub_Location;
282 grep {$id eq $_->seq_id} @subs};
283 return @split if @split;
284 return $feature->sub_SeqFeature if $feature->can('sub_SeqFeature');
285 return;
286 }
287
288 sub keyglyph {
289 my $self = shift;
290
291 my $scale = 1/$self->scale; # base pairs/pixel
292
293 my $feature =
294 Bio::Graphics::Feature->new(
295 -segments=>[ [ 0*$scale,9*$scale],
296 [ 10*$scale,19*$scale],
297 [ 20*$scale, 29*$scale]
298 ],
299 -name => 'foo bar',
300 -strand => '+1');
301 ($feature->segments)[0]->score(10);
302 ($feature->segments)[1]->score(50);
303 ($feature->segments)[2]->score(25);
304 my $factory = $self->factory->clone;
305 $factory->set_option(label => 1);
306 $factory->set_option(bump => 0);
307 $factory->set_option(connector => 'solid');
308 my $glyph = $factory->make_glyph(0,$feature);
309 return $glyph;
310 }
311
312
313 1;
314
315 __END__
316
317 =head1 NAME
318
319 Bio::Graphics::Glyph::xyplot - The xyplot glyph
320
321 =head1 SYNOPSIS
322
323 See L<Bio::Graphics::Panel> and L<Bio::Graphics::Glyph>.
324
325 =head1 DESCRIPTION
326
327 This glyph is used for drawing features that have a position on the
328 genome and a numeric value. It can be used to represent gene
329 prediction scores, motif-calling scores, percent similarity,
330 microarray intensities, or other features that require a line plot.
331
332 The X axis represents the position on the genome, as per all other
333 glyphs. The Y axis represents the score. Options allow you to set
334 the height of the glyph, the maximum and minimum scores, the color of
335 the line and axis, and the symbol to draw.
336
337 The plot is designed to work on a single feature group that contains
338 subfeatures. It is the subfeatures that carry the score
339 information. The best way to arrange for this is to create an
340 aggregator for the feature. We'll take as an example a histogram of
341 repeat density in which interval are spaced every megabase and the
342 score indicates the number of repeats in the interval; we'll assume
343 that the database has been loaded in in such a way that each interval
344 is a distinct feature with the method name "density" and the source
345 name "repeat". Furthermore, all the repeat features are grouped
346 together into a single group (the name of the group is irrelevant).
347 If you are using Bio::DB::GFF and Bio::Graphics directly, the sequence
348 of events would look like this:
349
350 my $agg = Bio::DB::GFF::Aggregator->new(-method => 'repeat_density',
351 -sub_parts => 'density:repeat');
352 my $db = Bio::DB::GFF->new(-dsn=>'my_database',
353 -aggregators => $agg);
354 my $segment = $db->segment('Chr1');
355 my @features = $segment->features('repeat_density');
356
357 my $panel = Bio::Graphics::Panel->new;
358 $panel->add_track(\@features,
359 -glyph => 'xyplot');
360
361 If you are using Generic Genome Browser, you will add this to the
362 configuration file:
363
364 aggregators = repeat_density{density:repeat}
365 clone alignment etc
366
367 =head2 OPTIONS
368
369 The following options are standard among all Glyphs. See
370 L<Bio::Graphics::Glyph> for a full explanation.
371
372 Option Description Default
373 ------ ----------- -------
374
375 -fgcolor Foreground color black
376
377 -outlinecolor Synonym for -fgcolor
378
379 -bgcolor Background color turquoise
380
381 -fillcolor Synonym for -bgcolor
382
383 -linewidth Line width 1
384
385 -height Height of glyph 10
386
387 -font Glyph font gdSmallFont
388
389 -label Whether to draw a label 0 (false)
390
391 -description Whether to draw a description 0 (false)
392
393 In addition, the alignment glyph recognizes the following
394 glyph-specific options:
395
396 Option Description Default
397 ------ ----------- -------
398
399 -max_score Maximum value of the Calculated
400 feature's "score" attribute
401
402 -min_score Minimum value of the Calculated
403 feature's "score" attribute
404
405 -graph_type Type of graph to generate. Histogram
406 Options are: "histogram",
407 "boxes", "line", "points",
408 or "linepoints".
409
410 -point_symbol Symbol to use. Options are none
411 "triangle", "square", "disc",
412 "point", and "none".
413
414 -point_radius Radius of the symbol, in 1
415 pixels
416
417 -scale Position where the Y axis none
418 scale is drawn if any.
419 It should be one of
420 "left", "right" or "none"
421
422 -graph_height Specify height of the graph Same as the
423 "height" option.
424
425 =head1 BUGS
426
427 Please report them.
428
429 =head1 SEE ALSO
430
431 L<Bio::Graphics::Panel>,
432 L<Bio::Graphics::Track>,
433 L<Bio::Graphics::Glyph::transcript2>,
434 L<Bio::Graphics::Glyph::anchored_arrow>,
435 L<Bio::Graphics::Glyph::arrow>,
436 L<Bio::Graphics::Glyph::box>,
437 L<Bio::Graphics::Glyph::primers>,
438 L<Bio::Graphics::Glyph::segments>,
439 L<Bio::Graphics::Glyph::toomany>,
440 L<Bio::Graphics::Glyph::transcript>,
441
442 =head1 AUTHOR
443
444 Lincoln Stein E<lt>lstein@cshl.orgE<gt>
445
446 Copyright (c) 2001 Cold Spring Harbor Laboratory
447
448 This library is free software; you can redistribute it and/or modify
449 it under the same terms as Perl itself. See DISCLAIMER.txt for
450 disclaimers of warranty.
451
452 =cut
453