| BioPerl documentation | Contained in the BioPerl distribution. |
Bio::Align::Graphics - Graphic Rendering of Bio::Align::AlignI Objects
use Bio::Align::Graphics; #Get an AlignI object, usually by using Bio::AlignIO my $file=shift @ARGV; my $in=new Bio::AlignIO(-file=>$file, -format=>'clustalw'); my $aln=$in->next_aln(); #Create a new Graphics object my $print_align = new Bio::Align::Graphics(align => $aln); #Draw the alignment $print_align->draw();
Bio::Align::Graphics is a module designed to create image files out of Bio::Align::AlignI objects. An alignment may be manipulated with various formatting and highlighting options.
An example:
#!/usr/bin/perl -w
use Bio::AlignIO;
use Bio::Align::Graphics;
use strict;
#Get an alignment file
my $file = shift @ARGV;
#Create an AlignI object using AlignIO
my $in=new Bio::AlignIO(-file=>$file, -format=>'clustalw');
#Read the alignment
my $aln=$in->next_aln();
#Create some domains for highlighting
my @domain_start = ( 25 , 50, 80 );
my @domain_end = ( 40 , 60 , 100 );
my @domain_color = ( 'red' , 'cyan' , 'green' );
#Create Labels for the domains
my @dml = ("CARD", "Proline Rich", "Transmembrane");
my @dml_start = (25, 50, 80);
my @dml_end = (40, 60, 100);
my @dml_color = ("lightpink", "lightblue", "lightgreen");
#Create individual labels
my %labels = ( 145 => "Hep-c target");
my $print_align = new Bio::Align::Graphics( align => $aln,
pad_bottom => 5,
domain_start => \@domain_start,
domain_end => \@domain_end,
dm_color => \@domain_color,
dm_labels => \@dml,
dm_label_start => \@dml_start,
dm_label_end => \@dml_end,
dm_label_color => \@dml_color,
labels => \%labels,
out_format => "png");
$print_align->draw();
This section describes the class and object methods for Bio::Align::Graphics.
Typically you will begin by creating a Bio::Align::Graphics object, passing it an alignment object created using Bio::AlignIO. The Bio::Align::Graphics->new() method has a number of configuration variables that allow you to control the appearance of the final image.
You will then call the draw() method to output the final image.
new() is the constructor for Bio::Align::Graphics:
The new() method creates a new graphics object. The options are a set of tag/value pairs as follows:
Option Value Default ------ ----- ------- align Bio::AlignI object None, must be supplied to draw an alignment output Filename to print image to STDOUT out_format png, jpeg, gif, gd png font Size of font, ranging from 1 to 5 2 and equal to the standard GD fonts ranging from gdTinyFont to gdGiantFont x_label Draws a scale numbering alignment true bases along top of image, every x bases are numbered, where x is the block_size option y_label Draws sequence ids of alignment true along left side of image bg_color Background color of the image white font_color Color of the font used for drawing black the alignment characters x_label_color Color of the font used for drawing red the base scale characters y_label_color Color of the font used for drawing blue the sequence id characters p_color Colors protein bases according to false a coloring scheme proposed by W.R. Taylor(Protein Engineering, vol 10 no 7, 1997), only works with protein alignments pad_top Additional whitespace characters 5 between top of image and x-label pad_bottom Additional whitespace characters 5 between bottom of image and alignment pad_left Additional whitespace characters 5 between left side of image and y-label pad_right Additional whitespace characters 5 between right side of image and alignment x_label_space Additional whitespace characters 1 between x_label and alignment y_label_space Additional whitespace characters 1 between y_label and alignment reference Characters which are identical to false the reference sequence are replaced with the match character reference_id Sequence id of the sequence to use First sequence as the reference supplied in alignment match_char Character to replace identical bases . in aligned sequences block_size Number of bases to group together 10 when printing alignment, groups are separated by whitespace block_space Amount of character whitespace to 2 separate groups of bases by labels A hash containing labels to be none printed beneath the alignment, where the keys are the bases to print the values at dm_start An array containing start bases none for highlighting of segments of the alignment, paired with dm_end option dm_end An array containing end bases none for highlighting of segments of the alignment, paired with dm_start options dm_color An array containing colors for silver highlighting segments of bases denoted by the coordinates located in the dm_start and dm_end options dml_start An array containing start bases none for addition of domain labels underneath the alignment, paired with dml_end dml_end An array containing end bases none for addition of domain labels underneath the alignment, paired with dml_start dml_color An array containing colors for silver the domain labels denoted by the coordinates located in the dml_start and dml_end options dm_labels An array containing labels to be none printed underneath specified domains, each label should correspond with the base position located in the dml_start option show_nonsynonymous Boolean value to turn option false on or off. If 0 (or undef), option is off. If 1 (or non-0), option is on. Only valid for nucleotide alignments. Output images are wider with this option on.
Note that all arrays and hashes must be passed by reference.
The draw() method draws the image with the options that were specified with new().
Get the width of the image created with new(), in pixels.
Get the height of the image created with new(), in pixels.
Get the length of the alignment submitted to new().
Get the format of the alignment submitted to new().
Get the number of sequences in the alignment submitted to new().
William McCaig, <wmccaig@gmail.com>
Mikhail Bekarev, <mbekarev@hunter.cuny.edu>
Y&246;zen Hern&225;ndez, <yzhernand@gmail.com>
Weigang Qiu (Corresponding Developer), <weigang@genectr.hunter.cuny.edu>
Copyright (C) 2006-2008 by William McCaig
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available.
| BioPerl documentation | Contained in the BioPerl distribution. |
#Author: William McCaig #Date: 06/16/2006 #Purpose: To print visual images of alignments # #Requires: An alignment file # #Produces: An image file # #Revision History: #09/01/2006 - WDM - Introduction of "wrap" flag, allowing alignment to be # wrapped at a set base and stacked vertically # Addition of internal members y_num and y_size for tracking # of number of vertical panels and size of panels, # respectively # #09/06/2006 - WDM - Introduction of "p_legend" flag, for printing of an optional # colored legend when protein coloring is selected # #09/24/2008 - WDM - Test file created for the module # #03/01/2009 - YH - Introduction of "show_nonsynonymous" flag which enables # highlighting of nonsynonymous mutations in nucleotide # alignments. Addition of internal members codon_table and # missense_pos for translating codons -> amino acids and for # keeping track of missense mutation positions respectively. # #03/05/2009 - YH - Swapped names of subroutines x_label and y_label to match # both documentation and intuition. Finalized implementation # of show_nonsynonymous functionality. # docs after the code! package Bio::Align::Graphics; use vars qw( @PRINT_PARAMS %OK_FIELD); use 5.008003; use strict; use warnings; use GD; use GD::Simple; use Bio::AlignIO; use Data::Dumper; use POSIX qw(ceil floor); require Exporter; our @ISA = qw(Exporter); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use PrintAlignment ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( ); # Preloaded methods go here. our %FONT_TABLE = (1 => gdTinyFont, 2 => gdSmallFont, 3 => gdMediumBoldFont, 4 => gdLargeFont, 5 => gdGiantFont ); our %PROTEIN_COLORS = ('Q' => [255, 0, 204], 'E' => [255, 0, 102], 'D' => [255, 0, 0] , 'S' => [255, 51, 0] , 'T' => [255, 102, 0 ], 'G' => [255, 153, 0] , 'P' => [255, 204, 0] , 'C' => [255, 255, 0] , 'A' => [204, 255, 0] , 'V' => [153, 255, 0], 'I' => [102, 255, 0] , 'L' => [51 , 255, 0] , 'M' => [0, 255, 0] , 'F' => [0 , 255, 102] , 'Y' => [0 , 255, 204], 'W' => [0, 204, 255] , 'H' => [0, 102, 255] , 'R' => [0, 0, 255] , 'K' => [102, 0, 255] , 'N' => [204, 0, 255] ); ################################################################# #New sub new { my $class = shift; my %options = @_; my $self = { #####OPTIONS##### #Display Defaults font => defined($options{font}) ? $FONT_TABLE{$options{font}} : $FONT_TABLE{2}, x_label => defined($options{x_label}) ? $options{x_label} : 1, y_label => defined($options{y_label}) ? $options{y_label} : 1, #Colors bg_color => $options{bg_color} || 'white', fg_color => $options{font_color} || 'black', x_label_color => $options{x_label_color} || 'blue', y_label_color => $options{y_label_color} || 'red', p_color => $options{p_color} || undef, p_legend => $options{p_legend} || undef, p_color_table => undef, #Sequence Defaults reference => $options{reference} || undef, reference_id => $options{reference_id} || undef, match_char => $options{match_char} || ".", block_size => defined($options{block_size}) ? $options{block_size} : 10, block_space => defined ($options{block_space}) ? ($options{block_space} * ($options{font} ? $FONT_TABLE{$options{font}}->width : $FONT_TABLE{2}->width)) : ( ($options{font} ? ($FONT_TABLE{$options{font}}->width * 2 ) : ($FONT_TABLE{2}->width * 2)) ), wrap => $options{wrap} || 80, show_nonsynonymous => $options{show_nonsynonymous} || undef, # If turned on, will highlight nonsynonymous (missense) mutations. Valid only for nucleotide alignments #Padding pad_left => $options{pad_left} || 5, #space between x label and border pad_right => $options{pad_right} || 5, #space between end of sequences and border pad_top => $options{pad_top} || 5, #space between y label and border pad_bottom => $options{pad_bottom} || 5, #space between bottom of sequences and border x_label_space => $options{x_label_space} || 1, #space between x label and sequences y_label_space => $options{y_label_space} || 1, #space between y label and sequences #Labels labels => $options{labels} || undef, dm_labels => $options{dm_labels} || undef, dm_label_start => $options{dml_start} || undef, dm_label_end => $options{dml_end} || undef, dm_label_color => $options{dml_color} || undef, domain_start => $options{dm_start} || undef, domain_end => $options{dm_end} || undef, domain_color => $options{dm_color} || undef, #File Defaults align => $options{align} || undef, output => $options{output} || undef, out_format => $options{out_format} || undef, ####PRIVATE VALUES##### image => $options{image} || undef, seq_format => undef, #X and Y size of char x_char_size => ($options{font} ? $FONT_TABLE{$options{font}}->width : $FONT_TABLE{2}->width), y_char_size => ($options{font} ? $FONT_TABLE{$options{font}}->height : $FONT_TABLE{2}->height), #Image W & H width => undef, #overall width of the image height => undef, #overall height of image #Sequences sequences => undef, seq_ids => undef, ref_sequence => undef, id_length => 0, seq_length => $options{align}->length() || 0, no_sequences => $options{align}->num_sequences() || 0, seq_start_x => undef, seq_start_y => undef, start => $options{start} || 1, end => $options{end} || $options{align}->length(), y_num => undef, y_size => undef, footer_size => 110, footer_start => undef }; bless ($self, $class); die "new:Must supply alignment for drawing!\n" unless defined ($self->{align}); foreach my $seq ($self->{align}->each_seq) { $self->{id_length} = ( length($seq->id()) > $self->{id_length} ) ? length($seq->id()) : $self->{id_length}; if( $self->{reference_id} && ($seq->id() eq $self->{reference_id}) ) { @{$self->{ref_sequence}} = split //, $seq->seq; unshift @{$self->{sequences}}, $seq->seq; unshift @{$self->{seq_ids}}, $seq->id(); }else { push @{$self->{sequences}}, $seq->seq; push @{$self->{seq_ids}}, $seq->id(); } if(!defined($self->{seq_format})) { $self->{seq_format} = $seq->alphabet; } } if(!($self->{reference_id}) ) { @{$self->{ref_sequence}} = split //, ${$self->{sequences}}[0]; $self->{reference_id} = ${$self->{seq_ids}}[0]; } $self->{y_num} = ($self->{seq_length} > $self->{wrap}) ? ( sprintf( "%.0f", ( ($self->{seq_length} / $self->{wrap}) + .5) ) ) : 1; $self->{y_size} = ( ($self->{no_sequences} + $self->{pad_bottom}) * $self->{y_char_size}); $self->{seq_start_x} = ($self->{pad_left} + $self->{id_length} + $self->{x_label_space}) * $self->{x_char_size}; if( defined($self->{show_nonsynonymous}) ) # Extra column changes dimensions { $self->{seq_length_aa} = ($self->{seq_length} / 3) + $self->{seq_length}; # Consider length of sequence plus extra column every 3 nucleotides $self->{seq_start_y} = ($self->{pad_top} + length($self->{seq_length_aa}) + $self->{y_label_space}) * $self->{y_char_size}; $self->{width} = $self->{seq_start_x} + ((( $self->{wrap} / $self->{block_size}) + 1) * $self->{block_space}) + ( ($self->{wrap} + $self->{pad_right}) * ($self->{x_char_size} + 1.2) ) + ( ($self->{seq_length} / 3) * 2); # Needed to add this for width to fit whole sequence on one line }else { $self->{seq_start_y} = ($self->{pad_top} + length($self->{seq_length}) + $self->{y_label_space}) * $self->{y_char_size}; $self->{width} = $self->{seq_start_x} + ((( $self->{wrap} / $self->{block_size}) + 1) * $self->{block_space}) + ($self->{wrap} + $self->{pad_right}) * $self->{x_char_size}; } $self->{footer_start} = $self->{seq_start_y} + $self->{y_size} * $self->{y_num}; if(defined($self->{p_color}) && defined($self->{p_legend}) && $self->{p_legend}){ $self->{height} = $self->{seq_start_y} + $self->{footer_size} + $self->{y_size} * $self->{y_num}; }else{ $self->{height} = $self->{seq_start_y} + $self->{y_size} * $self->{y_num}; } $self->{image} = GD::Simple->new($self->{width},$self->{height}); $self->{image}->alphaBlending(1); $self->{image}->saveAlpha(1); $self->{image}->bgcolor($self->{bg_color}); $self->{image}->fgcolor($self->{fg_color}); $self->{image}->rectangle(0,0,$self->{width}-1, $self->{height} - 1); return $self; } #End new Subroutine######################################################### sub draw{ my $self = shift; die "draw:Must supply alignment for drawing!\n" unless defined ($self->{align}); if(defined($self->{x_label}) && $self->{x_label}) { $self->x_label(); } if(defined($self->{y_label}) && $self->{y_label}) { $self->y_label(); } if(defined($self->{domain_start}) && defined($self->{domain_end}) && not defined($self->{p_color}) ) { $self->_draw_domain(); } # if( defined($self->{show_nonsynonymous}) && ( $self->{seq_format} eq "protein" ) ) { die "draw:Option show_nonsynonymous only works with Nucleotide alignments!\n"; }elsif ( defined($self->{show_nonsynonymous}) ) { $self->{codon_table} = Bio::Tools::CodonTable->new(); $self->{missense_pos} = {}; # print STDERR "You are using option show_nonsynonymous. Option works best if wrap value is a multiple of 4.\n" } if(defined($self->{p_color}) && $self->{seq_format} eq "protein") { $self->_draw_colored_sequences(); if(defined($self->{p_legend}) && $self->{p_legend}) { $self->_draw_legend(); } }elsif(defined($self->{p_color}) && ($self->{seq_format} ne "protein")) { die "draw:Option p_color only works with Protein alignments!\n"; }else { $self->_draw_sequences(); } if(defined($self->{dm_label_start})) { $self->_domain_label(); } if($self->{output}) { open(OUTPUT, ">$self->{output}"); binmode OUTPUT; if(defined($self->{out_format})) { SWITCH: { if($self->{out_format} eq "png") {print OUTPUT $self->{image}->png; last SWITCH;} if($self->{out_format} eq "jpeg") {print OUTPUT $self->{image}->jpeg; last SWITCH;} if($self->{out_format} eq "gif") {print OUTPUT $self->{image}->gif; last SWITCH;} if($self->{out_format} eq "gd") {print OUTPUT $self->{image}->gd; last SWITCH;} } }else { print OUTPUT $self->{image}->png; } close OUTPUT; }else { binmode STDOUT; if(defined($self->{out_format})) { SWITCH: { if($self->{out_format} eq "png") {print STDOUT $self->{image}->png; last SWITCH;} if($self->{out_format} eq "jpeg") {print STDOUT $self->{image}->jpeg; last SWITCH;} if($self->{out_format} eq "gif") {print STDOUT $self->{image}->gif; last SWITCH;} if($self->{out_format} eq "gd") {print STDOUT $self->{image}->gd; last SWITCH;} } }else { print STDOUT $self->{image}->png; } }#End Output if/else #print "Left\tRight\tTop\tBottom\n"; #print $self->{pad_left}, "\t", $self->{pad_right}, "\t", $self->{pad_top}, "\t", $self->{pad_bottom}, "\n"; }; ########################################## #Draws Sequences sub _draw_sequences{ my $self = shift; my $block_num = 0; my $block_total = 0; my $print_char; $self->{image}->fgcolor($self->{fg_color}); for (my $i=0; $i < $self->{no_sequences}; $i++) { my @letters = split //, ${$self->{sequences}}[$i]; my $y_num = $self->{y_num}; #sprintf( "%.0f", ( ($self->{seq_length} / $self->{wrap}) + .5) ) - 1; my $y_char = $self->{y_size}; #( ($self->{no_sequences} + $self->{pad_bottom}) * $self->{y_char_size}); for(my $k=0; $k<=$y_num; $k++) { my $x_char = $k * $self->{wrap}; for (my $j=$x_char; $j <= ( ($x_char + $self->{wrap}) - 1); $j++) { last unless defined($letters[$j]); # If show_nonsynonymous is on, and this is the 3rd nucleotide, # save the codon and amino acid for comparison my ($codon, $aa); if ((defined($self->{show_nonsynonymous})) && ((($j+1) % 3) == 0)) { $codon = $letters[$j-2] . $letters[$j-1] . $letters[$j]; $aa = $self->{codon_table}->translate($codon); } if( $self->{reference} ) { if(${$self->{seq_ids}}[$i] eq $self->{reference_id}) { $print_char = $letters[$j]; }else { if($letters[$j] eq ${$self->{ref_sequence}}[$j]) { $print_char = $self->{match_char}; }else { $print_char = $letters[$j]; } } }else { $print_char = $letters[$j]; } if( ( ($j + 1) % ($self->{block_size})) == 0) { $block_num = $self->{block_space}; }else { $block_num = 0; } #print "J is: $j\n"; #print "Char is: $print_char\n"; my $new_x_pos = $self->{seq_start_x} + ( ($j - $x_char) * $self->{x_char_size}) + $block_total; my $new_y_pos = $self->{seq_start_y} + ($i * $self->{y_char_size}) + ($k * $y_char); $new_x_pos += ( ( floor( ($j-$x_char)/3 ) * $self->{x_char_size} ) + ( ( floor( ($j-$x_char)/3 ) ) * 6 )) if ( defined($self->{show_nonsynonymous}) ); $self->{image}->moveTo( $new_x_pos, $new_y_pos ); $self->{image}->font($self->{font}); $self->{image}->string($print_char); if ( (defined($self->{show_nonsynonymous})) && ((($j+1) % 3) == 0) ) { $new_x_pos += ($self->{x_char_size} + 3); $self->{image}->moveTo( $new_x_pos, $new_y_pos ); # If show_nonsynonymous is on, and this is the 3rd nucleotide # on reference, print the amino acid after the nucleotide if(($self->{reference}) && (${$self->{seq_ids}}[$i] eq $self->{reference_id})) { $self->{image}->font(gdMediumBoldFont); $self->{image}->string($aa); $self->{image}->font($self->{font}); }elsif ( ( $self->{reference} ) && ( ${$self->{seq_ids}}[$i] ne $self->{reference_id} ) ) { # In case current sequence is not reference my $ref_codon = ${$self->{ref_sequence}}[$j-2] . ${$self->{ref_sequence}}[$j-1] . ${$self->{ref_sequence}}[$j]; my $ref_aa = $self->{codon_table}->translate($ref_codon); if ( $ref_aa eq $aa ) # Synonymous mutation { $self->{image}->string($self->{match_char}); }else # Nonsynonymous mutation { $self->{image}->font(gdMediumBoldFont); $self->{image}->string($aa); $self->{image}->font($self->{font}); # Highlight nonsynonymous mutations by drawing a rectangle around them if ( ( ${$self->{seq_ids}}[$i] ne $self->{reference_id} ) && !( ${$self->{missense_pos}}{$j} ) ) { ${$self->{missense_pos}}{$j} = 1; $self->{image}->bgcolor(undef); $self->{image}->rectangle( $new_x_pos - 2, ( $new_y_pos - ( ( $self->{y_char_size} * ($i+1)) ) ) - 2, ( $new_x_pos + ( $self->{x_char_size} + 1) ), ( $new_y_pos + ( $self->{y_char_size} * ( $self->{no_sequences} - ( $i+1 ) ) ) ) + 2); $self->{image}->bgcolor($self->{bg_color}); } } }else # No reference sequence defined { $self->{image}->string($aa); } } if( defined($self->{labels}) && $i == ($self->{no_sequences} - 1)) { if(${$self->{labels}}{$j + 1}) { my $label = ${$self->{labels}}{$j + 1}; my $offset = defined($self->{dm_label_start}) ? 3 : 0; $self->{image}->moveTo($self->{seq_start_x} + ( ( ($j - $x_char) + 1.25) * $self->{x_char_size}) + $block_total, $self->{seq_start_y} + (($self->{no_sequences}) * $self->{y_char_size}) + ($k * $y_char) + ( (length($label) + $offset) * ($self->{x_char_size}) ) ); $self->{image}->font($self->{font}); $self->{image}->angle(-90); $self->{image}->string($label); $self->{image}->angle(0); } } $block_total += $block_num; } $block_total = 0; } } } # WARNING YH - This function has not been modified to work with show_nonsynonymous: needs test data to make sure it will work! ############################################## #Draw Domain Label sub _domain_label{ my $self = shift; my $start_block_total = 0; my $end_block_total = 0; my $wrap_block_total = 0; my $y_char = $self->{y_size};# ( ($self->{no_sequences} + $self->{pad_bottom}) * $self->{y_char_size}); for(my $i = 0; $i <= $#{$self->{dm_label_start}}; $i++) { my $start = ${$self->{dm_label_start}}[$i]; my $end = ${$self->{dm_label_end}}[$i]; my $y_num_start = int( $start / $self->{wrap}); my $y_num_end = int( $end / $self->{wrap}); my $x_num_start; if($start >= $self->{wrap}) { $x_num_start = ($start % $self->{wrap}) - 1; }else { $x_num_start = $start - 1; } my $x_num_end; if($end >= $self->{wrap}) { $x_num_end = ($end % $self->{wrap}); }else { $x_num_end = $end; } my $label = ${$self->{dm_labels}}[$i]; my $color = ${$self->{dm_label_color}}[$i] || ${$self->{dm_label_color}}[-1] || "silver"; my $label_x = (($x_num_end - $x_num_start) / 2) - (length($label) / 2); my $label_x_start = (($self->{wrap} - $x_num_start) / 2) - (length($label) / 2); my $label_x_end = ($x_num_end / 2) - (length($label) / 2); $start_block_total = ( ($x_num_start - ($x_num_start % $self->{block_size}) ) / $self->{block_size} ) * $self->{block_space}; $end_block_total = ( ($x_num_end - ($x_num_end % $self->{block_size}) ) / $self->{block_size} ) * $self->{block_space}; $wrap_block_total = ( ($self->{wrap} - ( ($self->{wrap} - 1) % $self->{block_size}) ) / $self->{block_size} ) * $self->{block_space}; $self->{image}->bgcolor($color); $self->{image}->fgcolor($color); if($y_num_start == $y_num_end) #if the label does not cross the wrap line { $self->{image}->rectangle( $self->{seq_start_x} + ( ($x_num_start) * $self->{x_char_size} ) + $start_block_total, $self->{seq_start_y} + (($self->{no_sequences}) * $self->{y_char_size}) + ($y_num_start * $y_char), $self->{seq_start_x} + (($x_num_end) * $self->{x_char_size}) + $end_block_total, $self->{seq_start_y} + (($self->{no_sequences} + 1) * $self->{y_char_size}) + ($y_num_start * $y_char)); $self->{image}->fgcolor($self->{fg_color}); $self->{image}->bgcolor($self->{bg_color}); $self->{image}->moveTo( $self->{seq_start_x} + ( ($x_num_start + $label_x) * $self->{x_char_size}) + $start_block_total, $self->{seq_start_y} + (($self->{no_sequences} + 1) * $self->{y_char_size}) + ($y_num_start * $y_char) ); $self->{image}->font($self->{font}); $self->{image}->string($label); }else { $self->{image}->rectangle( $self->{seq_start_x} + ( ($x_num_start) * $self->{x_char_size} ) + $start_block_total, $self->{seq_start_y} + (($self->{no_sequences}) * $self->{y_char_size}) + ($y_num_start * $y_char), $self->{seq_start_x} + (($self->{wrap}) * $self->{x_char_size}) + $wrap_block_total, $self->{seq_start_y} + (($self->{no_sequences} + 1) * $self->{y_char_size}) + ($y_num_start * $y_char)); $self->{image}->rectangle( $self->{seq_start_x} , $self->{seq_start_y} + (($self->{no_sequences}) * $self->{y_char_size}) + ($y_num_end * $y_char), $self->{seq_start_x} + (($x_num_end) * $self->{x_char_size}) + $end_block_total, $self->{seq_start_y} + (($self->{no_sequences} + 1) * $self->{y_char_size}) + ($y_num_end * $y_char)); $self->{image}->fgcolor($self->{fg_color}); $self->{image}->bgcolor($self->{bg_color}); $self->{image}->moveTo( $self->{seq_start_x} + ( ($x_num_start + $label_x_start) * $self->{x_char_size}) + $start_block_total, $self->{seq_start_y} + (($self->{no_sequences} + 1) * $self->{y_char_size}) + ($y_num_start * $y_char) ); $self->{image}->font($self->{font}); $self->{image}->string($label); $self->{image}->moveTo( $self->{seq_start_x} + ( $label_x_end * $self->{x_char_size}), $self->{seq_start_y} + (($self->{no_sequences} + 1) * $self->{y_char_size}) + ($y_num_end * $y_char) ); $self->{image}->font($self->{font}); $self->{image}->string($label); } } } ############################################## #Draw Y Label sub y_label{ my $self = shift; $self->{image}->fgcolor($self->{y_label_color}); my $y_num = $self->{y_num}; #sprintf( "%.0f" , (($self->{seq_length} / $self->{wrap}) + .5)) - 1; my $y_char = $self->{y_size}; # ( ($self->{no_sequences} + $self->{pad_bottom}) * $self->{y_char_size}); for(my $k=0; $k<$y_num; $k++) { for (my $i=0; $i< $self->{no_sequences}; $i++) { $self->{image}->moveTo($self->{pad_left}, $self->{seq_start_y} + ($i * $self->{y_char_size}) + ($k * $y_char) ); $self->{image}->font($self->{font}); $self->{image}->string(${$self->{seq_ids}}[$i]); } } } ##################################################### #Draw X Label sub x_label{ my $self = shift; my $block_num = 0; my $block_total = 0; $self->{image}->fgcolor($self->{x_label_color}); my $y_char = $self->{y_size}; # ( ($self->{no_sequences} + $self->{pad_bottom}) * $self->{y_char_size}); for (my $i=1; $i<= $self->{seq_length}; $i++) { my $y_num = floor( $i / $self->{wrap}); # Used to be int(), but perl documentation advises against this my $x_num; if($i >= $self->{wrap}) { $x_num = ($i % $self->{wrap}); }else { $x_num = $i; } my @digits = split //, reverse($i); if( ($i % $self->{block_size}) == 0) { $block_num = $self->{block_space}; }else { $block_num = 0; } if( (($i - 1) % $self->{block_size}) == 0) { for (my $j=0; $j<=$#digits; $j++) { if ( defined($self->{show_nonsynonymous}) ) { $self->{image}->moveTo($self->{seq_start_x} + $block_total + ( ($x_num-1) * $self->{x_char_size}) + ( ( floor( ($x_num-1)/3 ) * $self->{x_char_size} ) + ( ( floor( ($x_num-1)/3 ) ) * 6 )), ($self->{pad_top} + length($self->{seq_length_aa}) - $j) * $self->{y_char_size} + ($y_num * $y_char)); }else { $self->{image}->moveTo($self->{seq_start_x} + $block_total + ( ($x_num-1) * $self->{x_char_size}), ($self->{pad_top} + length($self->{seq_length}) - $j) * $self->{y_char_size} + ($y_num * $y_char)); } $self->{image}->font($self->{font}); $self->{image}->string($digits[$j]); } } if($x_num == 0) { $block_total = 0; }else { $block_total += $block_num; } } } #################################################### #Domain Highlighting sub _draw_domain{ my $self = shift; my $block_total = 0; my ($start, $end, $block_num); my $y_char = $self->{y_size}; # ( ($self->{no_sequences} + $self->{pad_bottom}) * $self->{y_char_size}); for (my $k=0; $k <= $#{$self->{domain_start}}; $k++) { #print STDERR join "\n", GD::Simple->color_names; my $dmc = $self->{domain_color}[$k] || $self->{domain_color}[-1] || "silver"; $start = ${$self->{domain_start}}[$k] - 1; $end = ${$self->{domain_end}}[$k] - 1; for (my $i=0; $i < $self->{no_sequences}; $i++) { for (my $j = $start; $j <= $end; $j++) { my $y_num = int( $j / $self->{wrap}); my $x_num; if($j >= $self->{wrap}) { $x_num = ($j % $self->{wrap}); }else { $x_num = $j; } #print "J: $j\nXNUM: $x_num\nYNUM: $y_num\n"; $block_total = ( ($x_num - ($x_num % $self->{block_size}) ) / $self->{block_size} ) * $self->{block_space}; $self->{image}->bgcolor($dmc); $self->{image}->fgcolor($dmc); if ( defined($self->{show_nonsynonymous}) ) { # NOTE To shade amino acids as well, change $x_num HERE and HERE to $x_num + 1 $self->{image}->rectangle( $self->{seq_start_x} + ( ($x_num ) * $self->{x_char_size} ) + $block_total - 1 + ( ( floor( $x_num / 3 ) * $self->{x_char_size} ) + ( ( floor( $x_num / 3 ) ) * 6 )), $self->{seq_start_y} + ( $i * $self->{y_char_size} ) - $self->{y_char_size} + ($y_num * $y_char) , $self->{seq_start_x} + (($x_num + 1) * $self->{x_char_size}) + $block_total - 1 + ( ( floor( ($x_num)/3 ) * $self->{x_char_size} ) + ( ( floor( ($x_num)/3 ) ) * 6 )), $self->{seq_start_y} + ( $i * $self->{y_char_size}) + ($y_num * $y_char)); }else { $self->{image}->rectangle( $self->{seq_start_x} + ( ($x_num ) * $self->{x_char_size} ) + $block_total - 1, $self->{seq_start_y} + ( $i * $self->{y_char_size} ) - $self->{y_char_size} + ($y_num * $y_char) , $self->{seq_start_x} + (($x_num + 1) * $self->{x_char_size}) + $block_total - 1, $self->{seq_start_y} + ( $i * $self->{y_char_size}) + ($y_num * $y_char)); } #$self->{image}->rectangle( $self->{seq_start_x} + ( ($j) * $self->{x_char_size} ) + $block_total, $self->{seq_start_y} + ($i - 1 * $self->{y_char_size}), $self->{seq_start_x} + (($j + 1) * $self->{x_char_size}) + $block_total , $self->{seq_start_y} + ( ($i) * $self->{y_char_size})); $self->{image}->fgcolor($self->{fg_color}); $self->{image}->bgcolor($self->{bg_color}); } $block_total = 0; } } } sub _draw_colored_sequences{ my $self = shift; my $block_num = 0; my $block_total = 0; my $print_char; my %colors; for my $values ( keys %PROTEIN_COLORS) { #print STDERR "$values : @{ $PROTEIN_COLORS{$values} }\n"; $colors{$values} = $self->{image}->colorAllocate(@{ $PROTEIN_COLORS{$values} }); } $self->{p_color_table} = \%colors; $self->{image}->fgcolor($self->{fg_color}); for (my $i=0; $i < $self->{no_sequences}; $i++) { my @letters = split //, ${$self->{sequences}}[$i]; my $y_num = $self->{y_num}; #sprintf( "%.0f", ( ($self->{seq_length} / $self->{wrap}) + .5) ) - 1; my $y_char = $self->{y_size}; #( ($self->{no_sequences} + $self->{pad_bottom}) * $self->{y_char_size}); for(my $k=0; $k<=$y_num; $k++) { my $x_char = $k * $self->{wrap}; for (my $j=$x_char; $j <= ( ($x_char + $self->{wrap}) - 1); $j++) { last unless defined($letters[$j]); $print_char = $letters[$j]; if( ( ($j + 1) % ($self->{block_size})) == 0) { $block_num = $self->{block_space}; }else { $block_num = 0; } #print "Chunk Space: $chunk_space\n"; $self->{image}->bgcolor($colors{$print_char}); $self->{image}->fgcolor($colors{$print_char}); $self->{image}->rectangle( $self->{seq_start_x} + ( ($j - $x_char) * $self->{x_char_size} ) + $block_total - 1 , $self->{seq_start_y} + ( $i * $self->{y_char_size} ) + ($k * $y_char) - $self->{y_char_size} , $self->{seq_start_x} + (($j - $x_char + 1) * $self->{x_char_size}) + $block_total - 1 , $self->{seq_start_y} + ($k * $y_char) + ( $i * $self->{y_char_size})); $self->{image}->moveTo($self->{seq_start_x} + ( ($j - $x_char) * $self->{x_char_size}) + $block_total, $self->{seq_start_y} + ($k * $y_char) + ($i * $self->{y_char_size}) ); $self->{image}->fgcolor($self->{fg_color}); $self->{image}->font($self->{font}); $self->{image}->string($print_char); if( defined($self->{labels}) && $i == ($self->{no_sequences} - 1)) { if(${$self->{labels}}{$j + 1}) { my $label = ${$self->{labels}}{$j + 1}; my $offset = defined($self->{dm_label_start}) ? 3 : 0; $self->{image}->moveTo($self->{seq_start_x} + ( ( ($j - $x_char) + 1.25) * $self->{x_char_size}) + $block_total, $self->{seq_start_y} + (($self->{no_sequences}) * $self->{y_char_size}) + ($k * $y_char) + ( (length($label) + $offset) * ($self->{x_char_size}) ) ); $self->{image}->font($self->{font}); $self->{image}->angle(-90); $self->{image}->string($label); $self->{image}->angle(0); } } $block_total += $block_num; } $block_total = 0; } } } sub _draw_legend{ my $self = shift; my $title_font = $FONT_TABLE{3}; my @l_order = ("Negatively Charged", "Positively Charged", "Hydrophobic", "Aromatic", "Found in Loops", "Large Polar Acids"); my %legend = ("Negatively Charged" => ["D" , "E"] , "Positively Charged" => ["K", "R"] , "Hydrophobic" => ["A","F","I","L","M","V","W","Y"] , "Aromatic" => ["F", "H", "W", "Y"] , "Found in Loops" => ["D", "G", "P", "S", "T"] , "Large Polar Acids" => ["H", "K", "N", "Q", "R"]); my $x1 = 2; my $x2 = 42; my $colors = $self->{p_color_table}; my $y_start = $self->{footer_start}; my $label = "Protein Color Legend"; $self->{image}->bgcolor($self->{bg_color}); $self->{image}->fgcolor($self->{fg_color}); $self->{image}->rectangle(1,$y_start, 70 * $self->{x_char_size}, $self->{height} - 2); $self->{image}->moveTo((35 - (length($label) / 2) ) * $self->{x_char_size} , $y_start + $self->{y_char_size}); $self->{image}->font($title_font); $self->{image}->string($label); my $count = 3; foreach my $c_label (@l_order) { if( ($count % 2) == 0) { $self->{image}->moveTo( $x2 * $self->{x_char_size}, $y_start + ( ($count - 1) * $self->{y_char_size})); $self->{image}->font($self->{font}); $self->{image}->string($c_label); my $i = 0; foreach my $chars(@{$legend{$c_label}}) { $self->{image}->bgcolor($$colors{$chars}); $self->{image}->fgcolor($$colors{$chars}); $self->{image}->rectangle( ($x2 + 20 + $i) * $self->{x_char_size}, $y_start + ( ($count - 2) * $self->{y_char_size}), ($x2 + 20 + $i + 1) * $self->{x_char_size}, $y_start + ( ($count -1) * $self->{y_char_size})); $self->{image}->bgcolor($self->{bg_color}); $self->{image}->fgcolor($self->{fg_color}); $i++; } }else { $self->{image}->moveTo($x1 * $self->{x_char_size} , $y_start + ($count * $self->{y_char_size})); $self->{image}->font($self->{font}); $self->{image}->string($c_label); my $i = 0; foreach my $chars(@{$legend{$c_label}}) { $self->{image}->bgcolor($$colors{$chars}); $self->{image}->fgcolor($$colors{$chars}); $self->{image}->rectangle( ($x1 + 20 + $i) * $self->{x_char_size}, $y_start + ( ($count - 1) * $self->{y_char_size}), ($x1 + 20 + $i + 1) * $self->{x_char_size}, $y_start + ( ($count) * $self->{y_char_size})); $self->{image}->bgcolor($self->{bg_color}); $self->{image}->fgcolor($self->{fg_color}); $i++; } } $count += 1; } } ######################################## #####ACCESSORS##### sub width{ my $self = shift; return $self->{image}->width if exists $self->{image}; } sub height{ my $self = shift; return $self->{image}->height if exists $self->{image}; } sub aln_length{ my $self = shift; return $self->{seq_length} if exists $self->{seq_length}; } sub aln_format{ my $self = shift; return $self->{seq_format} if exists $self->{seq_format}; } sub no_sequences{ my $self = shift; return $self->{no_sequences} if exists $self->{no_sequences}; } 1; __END__