Mercurial > repos > willmclaren > ensembl_vep
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 |