File Coverage

blib/lib/Bio/DOOP/Graphics/Feature.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Bio::DOOP::Graphics::Feature;
2              
3 1     1   7 use strict;
  1         3  
  1         44  
4 1     1   5 use warnings;
  1         2  
  1         34  
5 1     1   545 use GD;
  0            
  0            
6              
7             =head1 NAME
8              
9             Bio::DOOP::Graphics::Feature - Graphical representation of the features
10              
11             =head1 VERSION
12              
13             Version 0.18
14              
15             =cut
16              
17             our $VERSION = '0.18';
18              
19             =head1 SYNOPSIS
20              
21             =head1 DESCRIPTION
22              
23             This object represents a picture that contains all the sequences and sequence features of a subset.
24             The module is fast enough to use it in your CGI scripts. You can also use it to visualize
25             the subset.
26              
27             =head1 AUTHOR
28              
29             Tibor Nagy, Godollo, Hungary
30              
31             =head1 METHODS
32              
33             =head2 create
34              
35             Creates a new picture. Later you can add your own graphical elements to it.
36              
37             Arguments: Bio::DOOP::DBSQL object and subset primary id.
38              
39             Return type: Bio::DOOP::Graphics::Feature object
40              
41             $picture = Bio::DOOP::Graphics::Feature->create($db,"1234");
42              
43             =cut
44              
45             sub create {
46              
47             my $self = {};
48             my $dummy = shift;
49             my $db = shift;
50             my $subset = shift;
51              
52             my @seqs = @{$subset->get_all_seqs};
53             my $height = ($#seqs+1) * 90 + 40;
54              
55             my $width = $subset->get_cluster->get_promo_type + 20;
56             my $image = new GD::Image($width,$height); # Create the image
57              
58             $self->{IMAGE} = $image;
59             $self->{DB} = $db;
60             $self->{SEQS} = \@seqs;
61             $self->{WIDTH} = $width;
62             $self->{HEIGHT} = $height;
63             $self->{POS} = 0;
64             $self->{SUBSET_ID} = $subset->get_id;
65              
66             # This is the map of the image. It is useful for HTML image maps.
67             # TODO : Add more types to this hash.
68             $self->{MAP} = {
69             motif => [],
70             dbtss => [],
71             utr => []
72             };
73             # The colormap of the object.
74             $self->{COLOR} = {
75             background => [200,200,200],
76             label => [0,0,0],
77             strip => [220,220,220],
78             utr => [100,100,255],
79             motif => [0,100,0],
80             tss => [0,0,0],
81             frame => [255,0,0],
82             fuzzres => [0,0,255]
83             };
84              
85             bless $self;
86             return($self);
87             }
88              
89             =head2 add_color
90              
91             Add an RGB color to the specified element.
92              
93             The available elements are the following : background, label, strip, utr, motif, tss, frame, fuzzres.
94              
95             $image->add_color("background",200,200,200);
96             $image->set_colors;
97              
98             =cut
99              
100             sub add_color {
101             my $self = shift;
102             my $code = shift;
103             my $r = shift;
104             my $g = shift;
105             my $b = shift;
106             my @color;
107             @color = ($r,$g,$b);
108             $self->{COLOR}->{"$code"} = \@color;
109             }
110              
111             =head2 set_colors
112              
113             Sets all colors. Allocate colors previously with add_color. Use this method only ONCE after you set
114             all the colors. If you use it more than once, the results will be strange.
115              
116             =cut
117              
118             sub set_colors {
119             my $self = shift;
120              
121             my $r;
122             my $g;
123             my $b;
124             ($r,$g,$b) = @{$self->{COLOR}->{background}};
125             $self->{IMAGE}->colorAllocate($r,$g,$b); # Set the background color.
126             ($r,$g,$b) = @{$self->{COLOR}->{label}};
127             $self->{LABEL} = $self->{IMAGE}->colorAllocate($r,$g,$b); # Set the label color.
128             ($r,$g,$b) = @{$self->{COLOR}->{utr}};
129             $self->{UTR} = $self->{IMAGE}->colorAllocate($r,$g,$b); # Set the UTR color.
130             ($r,$g,$b) = @{$self->{COLOR}->{motif}};
131             $self->{MOTIFCOLOR} = $self->{IMAGE}->colorAllocate($r,$g,$b); # Set the motif color.
132             ($r,$g,$b) = @{$self->{COLOR}->{tss}};
133             $self->{TSSCOLOR} = $self->{IMAGE}->colorAllocate($r,$g,$b); # Set the tss color.
134             ($r,$g,$b) = @{$self->{COLOR}->{strip}};
135             $self->{STRIP} = $self->{IMAGE}->colorAllocate($r,$g,$b); # Set the strip color.
136             ($r,$g,$b) = @{$self->{COLOR}->{frame}};
137             $self->{FRAME} = $self->{IMAGE}->colorAllocate($r,$g,$b); # Set the frame color.
138             ($r,$g,$b) = @{$self->{COLOR}->{fuzzres}};
139             $self->{FUZZRES} = $self->{IMAGE}->colorAllocate($r,$g,$b); # Set the fuzznuc result color.
140             }
141              
142             =head2 add_scale
143              
144             Draws the scale on the picture.
145              
146             =cut
147              
148             sub add_scale {
149             my $self = shift;
150              
151             my $color = $self->{LABEL};
152              
153             # Draw the main axis.
154             $self->{IMAGE}->line(10,5,$self->{WIDTH}-10,5,$color);
155              
156             # Draw the scales.
157             my $i;
158             for ($i = 20; $i < $self->{WIDTH}-10; $i += 10){
159             if( ($i / 100) == int($i / 100) ) {
160             $self->{IMAGE}->line($i+10,0,$i+10,10,$color); # Large scale.
161             my $str = ($self->{WIDTH} - 20 - $i) * -1; # The scale label.
162             my $posx = $i - (length($str)/2)*6 + 10; # Nice label positioning.
163             $self->{IMAGE}->string(gdSmallFont,$posx,10,$str,$color);
164             }
165             else {
166             $self->{IMAGE}->line($i+10,3,$i+10,7,$color); # Small scale.
167             }
168             }
169              
170             # Draw the arrow.
171             my $arrow = new GD::Polygon;
172             $arrow->addPt(9,5);
173             $arrow->addPt(15,2);
174             $arrow->addPt(15,8);
175             $self->{IMAGE}->filledPolygon($arrow,$color);
176             }
177              
178             =head2 add_bck_lines
179              
180             Draws scale lines through the whole image background.
181              
182             =cut
183              
184             sub add_bck_lines {
185             my $self = shift;
186             my $color = $self->{STRIP};
187              
188             my $i;
189             for ($i = 20; $i < $self->{WIDTH}-10; $i += 10){
190             $self->{IMAGE}->line($i,0,$i,$self->{HEIGHT},$color);
191             }
192              
193             }
194              
195             =head2 add_seq
196              
197             Draws a specified sequence on the picture. This is internal code, do not use it directly.
198              
199             =cut
200              
201             sub add_seq {
202             my $self = shift;
203             my $index = shift;
204              
205             my $seq = $self->{SEQS}->[$index];
206             my $len = $seq->get_length;
207             my $x1 = $self->{WIDTH} - 10;
208             my $x2 = $x1-$len;
209              
210             # Draw the seq line.
211             $self->{IMAGE}->line($x2, $index*90+40, $x1, $index*90+40, $self->{LABEL});
212              
213             # Draw UTR.
214             my $utrlen = $seq->get_utr_length;
215             if ($utrlen){
216             my $utrlen2 = $x1 - $utrlen;
217             if ($utrlen2 < 10){$utrlen2 = 10}
218             $self->{IMAGE}->filledRectangle($utrlen2, $index*90+35, $x1, $index*90+45, $self->{UTR});
219             $self->{IMAGE}->string(gdTinyFont, $utrlen2, $index*90+36, "UTR ".$utrlen." bp", $self->{LABEL});
220             }
221              
222             # Print the sequence name and length.
223             my $text = $seq->get_taxon_name . " " . $len . " bp";
224             $self->{IMAGE}->string(gdSmallFont, $x2, $index*90+22, $text, $self->{LABEL});
225              
226             # Draw features.
227             my $features = $seq->get_all_seq_features;
228             if ($features == -1){ return }
229             my $motif_Y = $index*90 + 60;
230             my $shift_factor = 0;
231             my $motif_count;
232              
233             my $min_motif_id;
234             for my $feat (@$features){
235             if( ($feat->get_type eq "con") && ($feat->get_subsetid eq $self->{SUBSET_ID})){
236             $min_motif_id = $feat->get_motifid;
237             last;
238             }
239             }
240             for my $feat (@$features){
241             # Draw motifs.
242             if( ($feat->get_type eq "con") && ($feat->get_subsetid eq $self->{SUBSET_ID})){
243             $motif_count = $feat->get_motifid - $min_motif_id + 1;
244             # This code helps to make three rows for the motifs.
245             my $label_length = (length($motif_count) + 1) * 6; # Label width with gdSmallFont
246             my %motif_element = ($feat->get_motifid => [ $x1 - $len + $feat->get_start,
247             $motif_Y + $shift_factor,
248             $x1 - $len + $feat->get_end,
249             $motif_Y + $shift_factor + 5 ]);
250             $self->{IMAGE}->filledRectangle($x1 - $len + $feat->get_start,
251             $motif_Y + $shift_factor,
252             $x1 - $len + $feat->get_end,
253             $motif_Y + $shift_factor + 5,
254             $self->{MOTIFCOLOR});
255             $self->{IMAGE}->string(gdSmallFont, $x1 - $len + $feat->get_start, $motif_Y+$shift_factor+6, "m$motif_count", $self->{LABEL});
256             push @{$self->{MAP}->{"motif"}},\%motif_element;
257             if ($feat->length > $label_length){
258             $shift_factor = 0;
259             }
260             elsif( ($feat->length < $label_length) && ($shift_factor < 36)){
261             $shift_factor += 18;
262             }
263             else {
264             $shift_factor = 0;
265             }
266             }
267              
268             # Draw tss.
269             if( ($feat->get_type eq "tss")){
270             my $motif_Y = $index*90 + 40;
271             my $tssfeat = new GD::Polygon;
272             $tssfeat->addPt($x1-$len+$feat->get_start,$motif_Y);
273             $tssfeat->addPt($x1-$len+$feat->get_start-5,$motif_Y+10);
274             $tssfeat->addPt($x1-$len+$feat->get_start+5,$motif_Y+10);
275             $self->{IMAGE}->filledPolygon($tssfeat,$self->{TSSCOLOR});
276             }
277              
278             }
279              
280             }
281              
282             =head2 add_all_seq
283              
284             Draws all sequences of the subset. The first one is the reference species.
285              
286             =cut
287              
288             sub add_all_seq {
289             my $self = shift;
290             my @seqs = @{$self->{SEQS}};
291             my $i;
292             for($i = 0; $i < $#seqs+1; $i++){
293             $self->add_seq($i);
294             }
295             }
296              
297             =head2 get_png
298              
299             Returns the png image. Use this when you finish the work and would like to see the result.
300              
301             open IMAGE,">picture.png";
302             binmode IMAGE;
303             print IMAGE $image->get_png;
304             close IMAGE;
305              
306             =cut
307              
308             sub get_png {
309             my $self = shift;
310             return($self->{IMAGE}->png);
311             }
312              
313              
314             =head2 get_image
315              
316             Returns the drawn image pointer. Useful for adding your own GD methods for unique picture manipulation.
317              
318             =cut
319              
320             sub get_image {
321             my $self = shift;
322             return($self->{IMAGE});
323             }
324              
325             =head2 get_map
326              
327             Returns a hash of arrays of hash of arrays reference that contains the image map information.
328             Here is a real world example of how to handle this method :
329              
330             use Bio::DOOP::DOOP;
331              
332             $db = Bio::DOOP::DBSQL->connect($user,$passwd,"doop-plant-1_5","localhost");
333             $cluster = Bio::DOOP::Cluster->new($db,'81001110','500');
334             $image = Bio::DOOP::Graphics::Feature->create($db,$cluster);
335              
336             for $motif (@{$image->get_map->{motif}}){
337             for $motif_id (keys %{$motif}){
338             @coords = @{$$motif{$motif_id}};
339             # Print out the motif primary id and the four coordinates in the picture
340             # id x1 y1 x2 y2
341             print "$motif_id $coords[0] $coords[1] $coords[2] $coords[3]\n";
342             }
343             }
344            
345             It is somewhat difficult, but if you are familiar with references and nested data structures, you
346             will understand it.
347              
348             =cut
349              
350             sub get_map {
351             my $self = shift;
352             return($self->{MAP});
353             }
354              
355             =head2 get_motif_map
356              
357             Returns only the arrayref of motif hashes.
358              
359             =cut
360              
361             sub get_motif_map {
362             my $self = shift;
363             return($self->{MAP}->{motif});
364             }
365              
366             =head2 get_motif_id_by_coord
367              
368             With this, you can get a motif id, if you specify the coordinates of a pixel.
369              
370             $motif_id = $image->get_motif_id_by_coord(100,200);
371              
372             =cut
373              
374             sub get_motif_id_by_coord {
375             my $self = shift;
376             my $x = shift;
377             my $y = shift;
378              
379             for my $motif (@{$self->get_motif_map}){
380             for my $motif_id (keys %{$motif}){
381             my @coords = @{$$motif{$motif_id}};
382             if(($x > $coords[0]) && ($x < $coords[2]) &&
383             ($y > $coords[1]) && ($y < $coords[3])) {
384             return($motif_id);
385             }
386             }
387             }
388             return(0);
389             }
390              
391             =head2 draw_motif_frame
392              
393             This method draws a frame around a given motif.
394              
395             Arguments: motif primary id
396              
397             Return type: 0 if success, -1 if the given motif id is not in the picture.
398              
399             $image->draw_motif_frame($motifid);
400              
401             =cut
402              
403             sub draw_motif_frame {
404             my $self = shift;
405             my $motifid = shift;
406             my $actualid;
407             my $have = 0;
408              
409             for my $motif (@{$self->{MAP}->{motif}}){
410             ($actualid) = keys %{$motif};
411             if ($actualid == $motifid){
412             my @choords = @{$$motif{$actualid}};
413             $have = 1;
414              
415             # Draw the frame
416             $self->{IMAGE}->rectangle($choords[0]-3,$choords[1]-3,$choords[2]+3,$choords[3]+3,$self->{FRAME});
417             $self->{IMAGE}->rectangle($choords[0]-2,$choords[1]-2,$choords[2]+2,$choords[3]+2,$self->{FRAME});
418             }
419             }
420              
421             if ($have == 0){
422             return(-1)
423             }
424             else{
425             return(0)
426             }
427             }
428              
429             =head2 draw_fuzz_result
430              
431             You can draw the fuzznuc result on the picture with this method.
432              
433             Arguments : sequence primary id, start position, end position
434              
435             To set drawing color, you can use the setcolor("fuzzres",$r,$g,$b) method.
436             The method shows the orientation. The arrow always points to the start position.
437              
438             Return value : 0 if success, -1 if the given sequence id can't be found.
439              
440             $image->draw_fuzz_result(357,20,70);
441              
442             =cut
443              
444             sub draw_fuzz_result {
445             my $self = shift;
446             my $seqid = shift;
447             my $start = shift;
448             my $end = shift;
449             my $index = 0;
450             my $ori;
451              
452             for my $i (@{$self->{SEQS}}){
453             if ($i->get_id eq $seqid){
454             my $y = $index*90+50;
455             my $len = $self->{WIDTH} - 10 - $i->get_length;
456             my $x1 = $len + $start;
457             my $x2 = $len + $end;
458             my $poly = new GD::Polygon;
459             if(($end - $start) > 0){ $ori = -1 }else{ $ori = 1 }
460              
461             $poly->addPt($start, $y);
462             $poly->addPt($start - 5*$ori, $y - 5);
463             $poly->addPt($start - 5*$ori, $y - 2);
464             $poly->addPt($end, $y - 2);
465             $poly->addPt($end, $y + 3);
466             $poly->addPt($start - 5*$ori, $y + 3);
467             $poly->addPt($start - 5*$ori, $y + 5);
468              
469             $self->{IMAGE}->filledPolygon($poly,$self->{FUZZRES});
470             return(0);
471             }
472             $index++;
473             }
474             return(-1);
475             }
476              
477             1;