3 # Copyright (C) 2007 Martin A. Hansen.
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # http://www.gnu.org/copyleft/gpl.html
22 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> DESCRIPTION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
25 # Routines to plot stuff with Gnuplot and SVG.
28 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
39 use vars qw ( @ISA @EXPORT );
47 @ISA = qw( Exporter );
50 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
53 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LINEPLOTS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
58 # Martin A. Hansen, January 2008.
60 # Plots a simple lineplot using Gnuplot.
62 my ( $data, # data table - each column will be plottet as one line.
63 $options, # options hash
64 $tmp_dir, # temporary directory
69 my ( $tmp_file, $pid, $fh_in, $fh_out, $cmd, $i, $line, @lines, $xtic_space, @plot_cmd, $title );
71 $tmp_dir ||= $ENV{ 'BP_TMP' };
73 $tmp_file = "$tmp_dir/lineplot_simple.tab";
75 $fh_out = Maasha::Filesys::file_write_open( $tmp_file );
77 map { print $fh_out join( "\t", @{ $_ } ), "\n" } @{ $data };
81 $options->{ "terminal" } ||= "dumb";
85 $pid = open2( $fh_out, $fh_in, $cmd );
89 print $fh_in "set terminal $options->{ 'terminal' }\n";
90 print $fh_in "set title \"$options->{ 'title' }\"\n" if $options->{ "title" };
91 print $fh_in "set xlabel \"$options->{ 'xlabel' }\"\n" if $options->{ "xlabel" };
92 print $fh_in "set ylabel \"$options->{ 'ylabel' }\"\n" if $options->{ "ylabel" };
93 print $fh_in "set logscale y\n" if $options->{ "logscale_y" };
94 print $fh_in "set grid\n" if not $options->{ "terminal" } eq "dumb";
95 print $fh_in "set autoscale\n";
96 print $fh_in "set xtics border in scale 0 nomirror rotate by 90 offset character 0, 0, 0\n";
98 for ( $i = 1; $i < scalar @{ $data->[ 0 ] } + 1; $i++ )
100 if ( ( $i % 5 ) == 0 )
102 $title = $options->{ 'keys' }->[ $i - 1 ] || $options->{ 'list' };
103 push @plot_cmd, qq("$tmp_file" using $i with lines title "$title");
107 print $fh_in "plot " . join( ", ", @plot_cmd ) . "\n";
111 while ( $line = <$fh_out> )
124 return wantarray ? @lines : \@lines;
128 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> HISTOGRAMS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
133 # Martin A. Hansen, August 2007.
135 # Plots a simple histogram using Gnuplot.
137 my ( $data, # list of [ xlabel, data value ] tuples
138 $options, # options hash
143 my ( $pid, $fh_in, $fh_out, $cmd, $i, $line, @lines );
145 $options->{ "terminal" } ||= "dumb";
149 $pid = open2( $fh_out, $fh_in, $cmd );
153 # print $fh_in "set terminal $options->{ 'terminal' } 10 \n"; # adsjust fontsize to 10 - find some other way to do this, because it don't work with SVG.
154 print $fh_in "set terminal $options->{ 'terminal' }\n";
155 print $fh_in "set title \"$options->{ 'title' }\"\n" if $options->{ "title" };
156 print $fh_in "set xlabel \"$options->{ 'xlabel' }\"\n" if $options->{ "xlabel" };
157 print $fh_in "set ylabel \"$options->{ 'ylabel' }\"\n" if $options->{ "ylabel" };
158 print $fh_in "set logscale y\n" if $options->{ "logscale_y" };
159 print $fh_in "set autoscale\n";
160 print $fh_in "unset key\n";
161 print $fh_in "set style fill solid\n";
162 print $fh_in "set style histogram title offset character 0, 0, 0\n";
163 print $fh_in "set style data histograms\n";
164 print $fh_in "set xtics border in scale 0 nomirror rotate by 90 offset character 0, 0, 0\n";
165 print $fh_in "plot '-' using 2:xticlabels(1)\n";
167 for ( $i = 0; $i < @{ $data }; $i++ )
169 print $fh_in join( "\t", "\"$data->[ $i ]->[ 0 ]\"", $data->[ $i ]->[ 1 ] ), "\n";
174 while ( $line = <$fh_out> )
185 return wantarray ? @lines : \@lines;
189 sub histogram_lendist
191 # Martin A. Hansen, August 2007.
193 # Plots a histogram using Gnuplot.
195 my ( $data, # list of [ xlabel, data value ] tuples
196 $options, # options hash
201 my ( $pid, $fh_in, $fh_out, $cmd, $i, $line, @lines, $xtic_space );
203 $options->{ "terminal" } ||= "dumb";
205 if ( $data->[ -1 ]->[ 0 ] <= 10 ) { # FIXME: some day figure the formula for this!
207 } elsif ( $data->[ -1 ]->[ 0 ] <= 100 ) {
209 } elsif ( $data->[ -1 ]->[ 0 ] <= 250 ) {
211 } elsif ( $data->[ -1 ]->[ 0 ] <= 500 ) {
213 } elsif ( $data->[ -1 ]->[ 0 ] <= 1000 ) {
215 } elsif ( $data->[ -1 ]->[ 0 ] <= 2500 ) {
217 } elsif ( $data->[ -1 ]->[ 0 ] <= 5000 ) {
219 } elsif ( $data->[ -1 ]->[ 0 ] <= 10000 ) {
221 } elsif ( $data->[ -1 ]->[ 0 ] <= 50000 ) {
223 } elsif ( $data->[ -1 ]->[ 0 ] <= 100000 ) {
225 } elsif ( $data->[ -1 ]->[ 0 ] <= 1000000 ) {
231 $pid = open2( $fh_out, $fh_in, $cmd );
233 print $fh_in "set terminal $options->{ 'terminal' }\n";
234 print $fh_in "set title \"$options->{ 'title' }\"\n" if $options->{ "title" };
235 print $fh_in "set xlabel \"$options->{ 'xlabel' }\"\n" if $options->{ "xlabel" };
236 print $fh_in "set ylabel \"$options->{ 'ylabel' }\"\n" if $options->{ "ylabel" };
237 print $fh_in "set logscale y\n" if $options->{ "logscale_y" };
238 print $fh_in "set autoscale\n";
239 print $fh_in "unset key\n";
240 print $fh_in "set style fill solid\n";
241 print $fh_in "set style histogram clustered gap 1 title offset character 0, 0, 0\n";
242 print $fh_in "set style data histograms\n";
243 print $fh_in "set xtics 0,$xtic_space border out nomirror\n";
245 print $fh_in "plot '-' using 1\n";
247 for ( $i = 0; $i < @{ $data }; $i++ )
249 $data->[ $i ]->[ 0 ] = "." if $data->[ $i ]->[ 0 ] % 10 != 0;
251 print $fh_in join( "\t", $data->[ $i ]->[ 1 ] ), "\n";
256 while ( $line = <$fh_out> )
267 return wantarray ? @lines : \@lines;
271 sub histogram_chrdist
273 # Martin A. Hansen, August 2007.
275 # Plots a histogram using Gnuplot.
277 my ( $data, # list of [ xlabel, data value ] tuples
278 $options, # options hash
283 my ( $pid, $fh_in, $fh_out, $cmd, $i, $line, @lines );
285 $options->{ "terminal" } ||= "dumb";
289 $pid = open2( $fh_out, $fh_in, $cmd );
291 print $fh_in "set terminal $options->{ 'terminal' }\n";
292 print $fh_in "set title \"$options->{ 'title' }\"\n" if $options->{ "title" };
293 print $fh_in "set xlabel \"$options->{ 'xlabel' }\"\n" if $options->{ "xlabel" };
294 print $fh_in "set ylabel \"$options->{ 'ylabel' }\"\n" if $options->{ "ylabel" };
295 print $fh_in "set logscale y\n" if $options->{ "logscale_y" };
296 print $fh_in "set autoscale\n";
297 print $fh_in "unset key\n";
298 print $fh_in "set style fill solid\n";
299 print $fh_in "set style histogram title offset character 0, 0, 0\n";
300 print $fh_in "set style data histograms\n";
301 print $fh_in "set xtics border in scale 0 nomirror rotate by 90 offset character 0, 0, 0\n";
303 print $fh_in "plot '-' using 2:xticlabels(1)\n";
305 for ( $i = 0; $i < @{ $data }; $i++ ) {
306 print $fh_in join( "\t", "\"$data->[ $i ]->[ 0 ]\"", $data->[ $i ]->[ 1 ] ), "\n";
311 while ( $line = <$fh_out> )
322 return wantarray ? @lines : \@lines;
326 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> KARYOGRAM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
331 # Martin A. Hansen, August 2007.
333 # Plot hits on a karyogram for a given genome.
335 my ( $data, # list of [ chr, beg, end ] triples
336 $options, # hashref with options
341 my ( $karyo_file, $svg, $features, $karyo );
343 if ( $options->{ "genome" } eq "hg18" )
345 $karyo_file = $ENV{ 'BP_DIR' } . "/bp_data/human_cytobands.txt";
347 elsif( $options->{ "genome" } eq "mm9" )
349 $karyo_file = $ENV{ 'BP_DIR' } . "/bp_data/mouse_cytobands.txt";
352 $karyo = parse_karyo_data( $karyo_file );
356 chromosome_layout( $svg, $karyo, $data );
364 # X q26.1 129700001 130200000 gneg
366 # color: /etc/X11/rgb.txt
371 my ( $fh, $chr, $line, $name, $beg, $end, $color, %features, %color_hash );
377 gpos75 => "DarkGrey",
378 gpos66 => "DarkGrey",
380 gpos33 => "LightGrey",
381 gpos25 => "LightGrey",
384 # gpos75 => "rgb(169,169,169)",
385 # gpos66 => "gray66",
386 # gpos66 => "#8e8e8e",
387 # gpos50 => "gray50",
388 # gpos33 => "#e3e3e3",
389 # gpos33 => "gray33",
390 # gpos25 => "gray25",
391 # stalk => "rgb(169,169,169)",
395 $fh = Maasha::Filesys::file_read_open( $file );
397 while ( $line = <$fh> )
401 next if $line =~ /^#/;
403 # ( $chr, $name, $beg, $end, $color ) = split "\t", $line;
404 ( $chr, $beg, $end, $name, $color ) = split "\t", $line;
406 # if ( $color =~ /^gpos(\d+)/ ) {
407 # $color = color_intensity( $1 );
408 # } elsif ( exists $color_hash{ $color } ) {
409 $color = $color_hash{ $color };
411 # die qq(ERROR: Unknown color->$color\n);
414 if ( exists $features{ $chr } )
416 push @{ $features{ $chr } }, [ $name, $beg, $end, $color ];
420 $features{ $chr } = [ [ $name, $beg, $end, $color ] ];
426 return wantarray ? %features : \%features;
432 # Martin A. Hansen, September 2007.
434 # Converts a gray scale intensity in percent to rgb.
436 my ( $percent, # color intensity
443 $num = int( $percent * 256 / 100 );
447 $hex = sprintf "%x", $num;
449 return "#$hex$hex$hex";
451 # return "rgb($num,$num,$num)";
457 # Martin A. Hansen, September 2005.
459 # initializes svg image.
461 # returns an image object
477 sub chromosome_layout
479 # Martin A. Hansen, January 2004 - August 2007.
481 # Plots all chromosomes in a single
483 my ( $svg, # image object
484 $karyo_list, # hashref with karyo data
485 $feat_list, # hashref with features
488 # returns an image object
490 my ( $layout_obj, $i, $x, $y, $max, $factor, $chr_len, $chr_width, $chr_cent, $chr, $feat, $karyo, @list, $A, $B );
492 $layout_obj = $svg->group(
496 $max = $karyo_list->{ "chr1" }->[ -1 ]->[ 2 ];
497 $factor = ( HEIGHT / 2 ) / $max;
498 $chr_width = ( HEIGHT / 4 ) / 13;
500 foreach $karyo ( keys %{ $karyo_list } ) {
501 map { $_->[ 1 ] *= $factor; $_->[ 2 ] *= $factor } @{ $karyo_list->{ $karyo } };
504 foreach $feat ( keys %{ $feat_list } ) {
505 map { $_->[ 0 ] *= $factor; $_->[ 1 ] *= $factor } @{ $feat_list->{ $feat } };
508 # @list = sort { $A = $a; $B = $b; $A =~ s/chr//; $B =~ s/chr//; $A <=> $B } keys %{ $karyo_list };
510 # splice @list, 0, 2;
511 # push @list, "chrX", "chrY";
518 $chr_len = $karyo_list->{ $chr }->[ -1 ]->[ 2 ];
519 $chr_cent = find_cent( $karyo_list->{ $list[ $i ] } );
521 $y = HEIGHT / 2 - $chr_len;
522 $x = ( WIDTH / ( @list + 2 ) ) * ( $i + 1 );
524 draw_chr( $layout_obj, $x, $y, $chr_len, $chr_width, $chr_cent, $chr, $karyo_list, $feat_list );
533 # Martin A. Hansen, December 2003.
535 # Finds the centromeric region in the karyo data.
539 my ( $acen, @nums, $cent );
541 @{ $acen } = grep { grep { /^DarkGrey$/ } @{ $_ } } @{ $list };
543 push @nums, $acen->[ 0 ]->[ 1 ];
544 push @nums, $acen->[ 0 ]->[ 2 ];
545 push @nums, $acen->[ 1 ]->[ 1 ];
546 push @nums, $acen->[ 1 ]->[ 2 ];
548 @nums = sort { $a <=> $b } @nums;
550 $cent = ( $nums[ 1 ] + $nums[ 2 ] ) / 2;
558 # Martin A. Hansen, December 2003.
560 # draws a whole cromosome with or without centromeric region
562 my ( $svg, # image object
565 $chr_len, # lenght of chromosome
566 $chr_width, # width of chromosome
567 $chr_cent, # position of centromeric region
569 $karyo_list, # hashref with karyo data
570 $feat_list, # hashref with features
573 # returns image object
575 my ( $chr_obj, $clip_obj, $gr_obj );
577 $chr_obj = $svg->group(
581 if ( exists $feat_list->{ $chr } ) {
582 draw_chr_feat( $chr_obj, $x, $y, $chr_width, $feat_list->{ $chr } );
585 $clip_obj = $chr_obj->clipPath(
586 id => $chr . "_clipPath",
589 $clip_obj->rectangle(
590 x => sprintf( "%.3f", $x ),
591 y => sprintf( "%.3f", $y ),
592 width => sprintf( "%.3f", $chr_width ),
593 height => sprintf( "%.3f", $chr_cent ),
598 $clip_obj->rectangle(
599 x => sprintf( "%.3f", $x ),
600 y => sprintf( "%.3f", $y + $chr_cent ),
601 width => sprintf( "%.3f", $chr_width ),
602 height => sprintf( "%.3f", $chr_len - $chr_cent ),
607 $gr_obj = $chr_obj->group(
608 "clip-path" => "url(#$chr" . "_clipPath)",
611 if ( exists $karyo_list->{ $chr } ) {
612 draw_karyo_data( $gr_obj, $x, $y, $chr_width, $karyo_list->{ $chr } );
616 x => sprintf( "%.3f", $x ),
617 y => sprintf( "%.3f", $y ),
618 width => sprintf( "%.3f", $chr_width ),
619 height => sprintf( "%.3f", $chr_cent ),
626 x => sprintf( "%.3f", $x ),
627 y => sprintf( "%.3f", $y + $chr_cent ),
628 width => sprintf( "%.3f", $chr_width ),
629 height => sprintf( "%.3f", $chr_len - $chr_cent ),
635 draw_chr_num( $chr_obj, $x, $y, $chr_len, $chr_width, $chr );
641 # Martin A. Hansen, December 2003.
643 # draws a cromosome number
645 my ( $svg, # image object
648 $chr_len, # lenght of chromosome
649 $chr_width, # width of chromosome
650 $chr, # chromosome number
653 # returns image object
655 my ( $chr_num, $chars, @a, $word_width );
660 $chars = @a = split "", $chr_num;
662 $word_width = ( $chars * 8 ) / 2;
665 x => sprintf("%.3f", $x + ( $chr_width / 2 ) - $word_width ),
666 y => sprintf("%.3f", $y + $chr_len + 15 ),
667 )->cdata( $chr_num );
673 # Martin A. Hansen, February 2004.
675 # Plots chromosome features
684 # returns an image object
686 my ( $feat_beg, $feat_end, $feat_height, $i, $color, $label );
688 for ( $i = 0; $i < @{ $list }; $i++ )
690 ( $label, $feat_beg, $feat_end, $color ) = @{ $list->[ $i ] };
692 $feat_height = $feat_end - $feat_beg;
695 x => sprintf("%.3f", $x ),
696 y => sprintf("%.3f", $y + $feat_beg ),
697 width => sprintf("%.3f", $chr_width ),
698 height => sprintf("%.3f", $feat_height ),
708 # Martin A. Hansen, February 2004.
710 # Plots chromosome features
719 # returns an image object
721 my ( $feat_beg, $feat_end, $feat_height, $i, $color, $height, $width, $x1, $y1, %lookup );
723 for ( $i = 0; $i < @{ $list }; $i++ )
725 ( $feat_beg, $feat_end, $color ) = @{ $list->[ $i ] };
727 $feat_height = $feat_end - $feat_beg;
729 $x1 = sprintf("%.0f", $x + ( $chr_width / 2 ) ),
730 $y1 = sprintf("%.0f", $y + $feat_beg ),
731 $width = sprintf("%.0f", ( $chr_width / 2 ) + 5 ),
732 $height = sprintf("%.0f", $feat_height );
738 if ( exists $lookup{ $x1 . $y1 } ) {
741 $lookup{ $x1 . $y1 } = 1;
757 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SEQUENCE LOGO <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
762 # Martin A. Hansen, August 2007.
764 # Calculates and renders a sequence logo in SVG format.
766 my ( $entries, # aligned sequence entries - list of tuples
771 my ( $type, $bit_max, $logo_data, $svg );
773 $type = Maasha::Seq::seq_guess_type( $entries->[ 0 ]->[ 1 ] );
775 if ( $type =~ /^p/i ) {
781 $logo_data = Maasha::Seq::seqlogo_calc( $bit_max, $entries );
783 $svg = Maasha::Plot::svg_init();
785 svg_draw_logo( $svg, $logo_data, $bit_max, $type );
786 svg_draw_logo_scale( $svg, $bit_max );
794 # Martin A. Hansen, October 2005.
796 # inititalizes SVG object, which is returned.
802 'font-weight' => 'normal',
803 'font-family' => 'Courier New',
814 # Martin A. Hansen, January 2007.
816 # Renders a sequence logo in SVG using a
817 # given data structure with logo details.
819 my ( $svg, # SVG object,
820 $logo_data, # data structure
821 $bit_max, # maximum bit height
822 $type, # sequence type
823 $nocolor, # render black and white - OPTIONAL
826 my ( $pos, $elem, $char, $char_height_bit, $char_height_px, $block, $x, $y, $scale_factor, $color );
830 foreach $pos ( @{ $logo_data } )
834 foreach $elem ( @{ $pos } )
836 ( $char, $char_height_bit ) = @{ $elem };
838 $char_height_px = $char_height_bit * ( 30 / $bit_max );
840 $block = $svg->group(
841 transform => "translate($x,$y)",
844 $scale_factor = $char_height_px / 7;
848 } elsif ( $type eq "dna" or $type eq "rna" ) {
849 $color = Maasha::Seq::color_nuc( $char );
851 $color = Maasha::Seq::color_pep( $char );
855 transform => "scale(1,$scale_factor)",
859 'font-weight' => 'bold',
860 fill => Maasha::Seq::color_palette( $color ),
864 $y -= $char_height_px;
872 sub svg_draw_logo_scale
874 # Martin A. Hansen, January 2007.
876 # draws the bit scale for the sequence logo
878 my ( $svg, # SVG object,
879 $bit_max, # maximum bit height
884 $scale = $svg->group(
885 transform => "translate(-10)",
888 'font-size' => '8px',
893 # transform => "translate(0,$logo_y)",
894 transform => "rotate(-90)",
909 for ( $i = 0; $i <= $bit_max; $i++ )
914 y1 => ( 30 / $bit_max ) * $i,
915 y2 => ( 30 / $bit_max ) * $i,
920 y => ( 30 / $bit_max ) * $i + 2,
924 )->cdata( $bit_max - $i );
929 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<