2 #################################General Description#########################################
4 =head1 Name and Description
6 Alignment -- packages for analyzing, optimizing and displaying sequence alignments
12 Chenwei Lin (cl295@cornell.edu)
18 align, image_object, align_seq, ruler, chart
20 Packages align_seq, ruler and chart inherit from image_object
24 ##############################End of General Description######################################
36 ################################Package align###############################################
37 =head1 Package CXGN::Alignment::align
39 The basic element of the align object is an array of align_seq.
41 Its attributes include: align_name, width (pixel), height(pixel), image, align_seqs, ruler, chart, conserved_seq, sv_overlap, sv_identtity, start_value and end_value
43 It functionality includes:
47 2. Calculation and output of pairwise similaity and putative splice variant pairs based on similarity.
49 3. Hide some alignment sequences so that they are not included in the analysis.
51 4. Select a range of sequences to be analyzed.
53 5. Asses how a align_seq member overlap with other align_seq members.
55 6. Calculate the non-gap mid point of each align sequence and group the sequences according to their overlap.
60 package CXGN
::Alignment
::align
;
62 =head2 Constructer new()
68 Synopsis: my $align = CXGN::Alignment::align->new(
72 type=>$type, #'nt' or 'pep'
75 Description: Upon constructing a align object, it sets align_name, width and height using arguments. It also genertes a image object of {length} and {height} and sets the default value of sv_overlap, sv_idnentity (the minimum number overlapping aa and percentage identity for two align_seq to be considered as putattive splice variant), sv_indel_limit (the min aa indel length for two sequences to be condiered as splice variants, instead of allele) and start_value (the start value of the ruler and align_seq objects(s))
77 Returns: an CXGN::Alignment::align object
84 my $self = bless {}, $class;
86 ##################set attibutes from parameter
87 $self->{align_name
} = $args{align_name
};
88 $self->{width
} = $args{width
};
89 $self->{height
} = $args{height
};
90 $self->{type
} = $args{type
};
92 ##################set defaults
93 #splice variants criteria
94 $self->{sv_overlap
} = 20; #set for amino acid
95 $self->{sv_identity
} = 95;
96 $self->{indel_limit
} = 4; #set for amono acid
97 $self->{start_value
} = 1; #the end_value is initiated when the first align_seq is added
100 $self->{horizontal_offset
} = 30;
101 $self->{vertical_offset
} = 30;
103 ##################define some 'empty' attributes that will be asigned later
104 @
{$self->{align_seqs
}} = ();
107 $self->{conserved_seq
} = ();
108 $self->{seq_length
} = ();
114 =head2 Setters and getters
116 get_align_name(), set_align_name()
118 get_image(), set_image()
120 get_width(), set_width()
122 get_height(), set_height()
126 get_sv_criteria(), set_sv_criteria()
128 get_start_value(), set_start_value(), check_start_value()
130 get_end_value(), set_end_value(), check_end_value()
132 get_horizontal_offset(), set_horizontal_offset(), get_vertical_offset(), set_vertical_offset()
134 Those for align_name, image, sv_overlap and sv_identity, height, width, horizontal_offset, vertical_offset are straightforward, while the setters for start_value, end_value are not simple, since these attributes are related to and/or restricted by other attibutes. seq_length is determined by the members of @align_seqs therefore can not be reset.
140 return $self->{align_name
};
146 $self->{align_name
} = $name;
151 return $self->{image
};
156 my $image = new GD
::Image
($self->{width
}, $self->{height
});#generate a new image object using the current width and height attributes of the align object
157 $self->{image
} = $image;
162 return $self->{width
};
167 return $self->{height
};
170 =head3 set_width(), set_height()
172 Synopsis: set_width($x), set_height($x)
174 Description: sets the attributes {width} and {height}. Since the {iamge} attribute's width and height is related to {width} and {height}, when {width} and {height} are set, a {iamge} is re-generated. Otherwise, setting the {width} and {height} won't have any real effect.
182 $self->{width
} = shift;
188 $self->{height
} = shift;
192 =head3 set_sv_criteria()
194 Synopsis: $align->set_sv_criteria($x, $y, $z), while $x is minimum oberlap, $y is a percentage similarity and $z is the minimal amino acid indel length to be considered as splice variant
196 Description: set the putative splice variants standard, the minimum overlapping bases and percentage identity (sv_overlap and sv_identity). The sub checks if the values are correct before setting the arributes
202 sub set_sv_criteria
{
204 my ($overlap, $identity, $indel) = @_;
207 die "Overlap must be greater than 0!\n";
209 elsif (($identity < 0) || ($identity > 100)){
210 die "Percentage identity must be greater than 0 and less than 100\n";
213 die "Indel limit must be greater than 0!\n";
216 $self->{sv_overlap
} = $overlap;
217 $self->{sv_identity
} = $identity;
218 $self->{indel_limit
} = $indel;
222 sub get_sv_criteria
{
224 return $self->{sv_overlap
}, $self->{sv_identity
}, $self->{sv_indel_limit
};
227 sub set_horizontal_offset
{
229 $self->{horizontal_offset
} = shift;
232 sub get_horizontal_offset
{
234 return $self->{horizontal_offset
};
237 sub set_vertical_offset
{
239 $self->{vertical_offset
} = shift;
242 sub get_vertical_offset
{
244 return $self->{vertical_offset
};
247 sub get_start_value
{
249 return $self->{start_value
};
254 return $self->{end_value
};
257 sub check_start_value
{
262 print "$self->{id} start_value less than 0, reset to 0\n";
267 sub check_end_value
{
270 if ($value > length $self->{align_seqs
}[0]->{seq
}){
271 $value = length $self->{align_seqs
}[0];
272 print "value greater than sequence length, reset to sequence length\n";
279 return $self->{seq_length
};
282 =head3 set_start_value(), set_end_value()
284 Synopsis: set_start_value($x), set_end_value($x)
286 Description: set the start_value and end_value. Check if the input value is correct before setting the attributes. Since the {start_value} and {end_value} attributes of the {ruler} and {align_seqs} members must be the same as those of the align object, the set subs call the set_start_value and set_end_value of the {ruler} and all members of {align_seqs}
292 sub set_start_value
{
295 $value = $self->check_start_value($value);#check if the start value is correct
296 $self->{start_value
} = $value; #set the start_value of the included ruler
298 foreach (@
{$self->{align_seqs
}}) { #set the start_value of each included align_seq
299 $_->set_start_value($value);
306 #$value = $self->check_end_value($value);#check if the end value is correct
307 $self->{end_value
} = $value;
309 foreach (@
{$self->{align_seqs
}}) { #set the start_value of each included align_seq
310 $_->set_end_value($value);
315 =head2 Subroutines to add attributes to align object
317 add_align_seq(), _add_ruler(), _add_cvg_chart, _add_conserved_seq_obj()
321 =head3 add_align_seq()
323 Synopsis: $align->add_align_seq($align_seq), while align_seq is an align_seq object
325 Description: Add align_seq to align object. The align_seq objects are stored in an array align_seqs. Once the first align_seq object is added, nly align_seq object of the same length can be further added. At the same time, the end_value is set to be the same as the sequence length of the first align_seqs member added.
334 if (! @
{$self->{align_seqs
}}){
336 #if there is no members in @align_seqs, reset the end_value of overall alignment to the length of this sequence
337 $self->set_end_value($member->get_end_value());
338 $self->{seq_length
} = length ($member->get_select_seq());
340 #adjust the vertical and horizontal offsets and length
341 $member->set_horizontal_offset($self->{horizontal_offset
});
342 #$member->set_vertical_offset($self->{vertical_offset} + 50);
343 $member->set_length($self->{width
} - 250);
344 #add the align_seq to @align_seqs
345 push @
{$self->{align_seqs
}}, $member;
349 #the length of the align_seq must the the same as the first member in @align_seqs, otherwise it won't be added
351 if ( length($member->get_seq()) == $self->{seq_length
}){
353 #set the start_value and end_value is set to the current values of overall align and adjust the image length of sequence according to the width of overall alignment
354 $member->set_start_value($self->get_start_value());
355 $member->set_end_value($self->get_end_value());
356 $member->set_length($self->{width
} - 250);
358 #adjust the vertical and horizontal offsets
359 my $last_member = int @
{$self->{align_seqs
}} - 1;
360 my $last_v_offset = $self->{align_seqs
}[$last_member]->get_vertical_offset();
361 $member->set_horizontal_offset($self->{horizontal_offset
});
362 #$member->set_vertical_offset($last_v_offset + 15);
364 push @
{$self->{align_seqs
}}, $member;
367 my $id = $member->get_id();
370 # die "error in adding sequence $id, length not the same as overall alignment, skip\n";
377 Synopsis: $align->_add_ruler($x,$y), while $x is the vertical offset and $y is the height of the ruler.
379 Description: Add a ruler to the align object, the start_value and end_value are set to the same as those of the align. If no align_seq has been added to the align object, the seq_length, start_value and end_value of the align are not set (see sub add_align_seq), then a ruler can not be added.
387 my $v_offset = shift;
390 my $ruler = CXGN
::Alignment
::ruler
->new (
391 horizontal_offset
=>$self->{horizontal_offset
},
392 vertical_offset
=>$v_offset,
393 length=>($self->{width
} - 250),
395 start_value
=>$self->{start_value
},
396 end_value
=>$self->{end_value
}
398 ($self->{type
} eq 'pep') and ($ruler->set_unit('aa'));
400 $self->{ruler
} = $ruler;
403 =head3 _add_cvg_chart()
405 Synopsis: $align->_add_ruler($x,$y,$z), while $x is the vertical offset, $y is the id and $z is a hash reference whose key is a interger (a position) and value is a percentage
407 Description: Add a chart representing coverage by member align_seq. The start_value and end_value are set to the same as those of the align. The coverage of each align postion is repreesnted by a hash reference passed to the subroutine. The key of the hash is the align postion and the values are percentage converage.
416 my $v_offset = shift; #vertical offset
418 my $hash_ref = shift;
420 my $chart = CXGN
::Alignment
::chart
->new (
421 horizontal_offset
=>$self->{horizontal_offset
},
422 vertical_offset
=>$v_offset,
423 length=>($self->{width
} - 250),
425 start_value
=>$self->{start_value
},
426 end_value
=>$self->{end_value
},
430 $self->{chart
} = $chart;
434 =head3 _add_conserved_seq_obj()
436 Synopsis: $align->_add_conserved_seq_obj($x), while $x is the vertical offset.
438 Description: Add a align_seq object representing the conserved sequence of the @align_seqs. The seq of this object is generated by another subroutine get_conserved_seq. If the sequence at a position is not conserved among all present members, it is repreesnted by - in conserved_seq. This object is NOT a member of @align_seqs.
444 sub _add_conserved_seq_obj
{
446 my $v_offset = shift;
448 my $seq = $self->get_conserved_seq();
449 my $seq_obj = CXGN
::Alignment
::align_seq
->new (
450 horizontal_offset
=>$self->{horizontal_offset
},
451 vertical_offset
=>$v_offset,
452 length=>($self->{width
} - 250),
454 start_value
=>$self->{start_value
},
455 end_value
=>$self->{end_value
},
456 id
=>'Overall Conserved Sequence',
460 $seq_obj->set_color(0,0,122);
461 $self->{conserved_seq
} = $seq_obj;
464 =head2 Subroutines to search and ouput ids of @align_seq members
466 is_id_member(), is_member(), id_to_member(), get_member_ids(), get_nonhidden_member_ids, get_hidden_member_ids(), get_member_species(), get_member_urls()
470 =head3 is_id_member()
472 Synopsis: is_id_member($id)
474 Description: Does any of the align_seqs member have the same id as $id?
476 Returns: 0 for true and -1 for false
483 foreach (@
{$self->{align_seqs
}}) {
484 if (($_->{id
}) eq $id) {
494 Synopsis: is_member($align_seq), while $align_seq is an align_seq object
496 Description: is $align_seq already a align_seqs member?
498 Returns: 0 for true and -1 for false
505 foreach (@
{$self->{align_seqs
}}) {
513 =head3 id_to_member()
515 Synopsis: $align->id_to_member($id);
517 Description: check if a align member has the id $id and return the align member
519 Returns: an align object
526 foreach (@
{$self->{align_seqs
}}) {
527 if (($_->{id
}) eq $id) {
534 =head3 get_member_ids()
536 Synopsis: $align->get_member_ids()
538 Description: Returns ids of all align_seqs members
540 Returns: an array of ids of all non-hidden @align_seq members
548 foreach (@
{$self->{align_seqs
}}){
549 my $id = $_->get_id();
558 my $number = int (@
{$self->{align_seqs
}});
563 =head3 get_nonhidden_member_ids()
565 Synopsis: $align->get_nonhidden_member_ids()
567 Description: Returns ids of align_seqs members that are not hidden
569 Returns: an array of ids of all non-hidden @align_seq members
573 sub get_nonhidden_member_ids
{
577 foreach (@
{$self->{align_seqs
}}){
578 if ($_->is_hidden() ne 'yes') {
579 my $id = $_->get_id();
586 sub get_nonhidden_member_nr
{
589 foreach (@
{$self->{align_seqs
}}){
590 ($_->is_hidden() ne 'yes') and $number++;
595 =head3 get_hidden_member_ids()
597 Synopsis: $align->get_hidden_member_ids()
599 Description: Returns ids of align_seqs members that are hidden
601 Returns: an array of ids of all hidden @align_seq members
606 sub get_hidden_member_ids
{
610 foreach (@
{$self->{align_seqs
}}){
611 if ($_->is_hidden() eq 'yes') {
612 my $id = $_->get_id();
619 sub get_hidden_member_nr
{
623 foreach (@
{$self->{align_seqs
}}){
624 ($_->is_hidden() eq 'yes') and $number++;
630 =head3 get_member_species()
632 Synopsis: $align->get_member_species()
634 Description: Return the species of each member of @align_seqs
636 Returns: A hash reference whose keys are ids and values are species
641 sub get_member_species
{
644 my %member_species = ();
645 foreach (@
{$self->{align_seqs
}}){
647 my $species = $_->get_species();
648 $member_species{$id} = $species;
650 return \
%member_species;
653 sub get_member_urls
{
657 foreach (@
{$self->{align_seqs
}}) {
659 my $url = $_ ->get_url();
660 $member_url{$id} = $url;
666 =head2 Image processing subs of the package
668 render(), render_png(), render_jpg(), render_png_file(), render_jpg_file(), write_image_map()
674 Synopsis: $align->render($o) where $o represnts option, 'c' for complete, 'a' for alignment only and 's' for simple (only the ruler, coverage chart and conserved sequence, no individual members of @align_seqs)
676 Description: it does the following
677 1. Generage, set and render a ruler
678 2. Generate, set and render a chart representing coverge
679 3. Generate, set and render a align_seq object representing conserved sequence
680 4. Render all non-hidden members of the @aign_seqs
689 my $option = shift;#'c' for complete, 's' for simple, only chart and conserved seq, 'a' for alignment oly
692 ($option eq 'a' || $option eq 'c' || $option eq 's') or die "Please enter correct option! 'a' for alignment only, 'c' for complate and 's' for chart and conserved seq only!\n";
694 #adjust the image height according to @align_seqs
695 if ($option eq 'c' || $option eq 'a') {
696 my $nr_member = $self->get_nonhidden_member_nr;
698 ($nr_member == 0) and exit "There is no sequence in align!\n";
699 ($option eq 'c') and $self->set_height($nr_member * 15 + 200);
700 ($option eq 'a') and $self->set_height($nr_member * 15 + 100);
703 $self->set_height(150);
706 #Generate a image object for the image_object (align_seq, ruler and chart) to render
707 $self->{image
} = GD
::Image
->new(
710 or die "Can't generate imag\n";
712 # the first color located is the background color, white
713 $self->{white
} = $self->{image
}->colorResolve(255,255,255);
715 $self->{image
}->filledRectangle(0,0 ,$self->{width
}, $self->{height
}, $self->{white
});
717 #add and render a ruler
718 $self->_add_ruler(50, $self->{height
} - 20);
720 $self->{ruler
}->render($self->{image
});
722 #add and render a chart to indicate percentage coverage
723 if ($option eq 'c' || $option eq 's') {
724 my $hash_ref = $self->get_ngap_pct();
725 $self->_add_cvg_chart(70, "Coverage %", $hash_ref);
726 $self->{chart
}->render($self->{image
});
730 #add a sequence represnting the conserved region and render it
731 if ($option eq 'c' || $option eq 's') {
732 $self->_add_conserved_seq_obj(120);
733 $self->{conserved_seq
}->render($self->{image
});
736 #adjust vertical offset and height of each non hidden align_seqs member and render the member
737 if ($option eq 'c' ||$option eq 'a') {
739 ($option eq 'c') and $align_v_offset = 170;
740 ($option eq 'a') and $align_v_offset = 70;
741 foreach my $as (@
{$self->{align_seqs
}}) {
742 if ($as->is_hidden() ne 'yes') {
743 $as->set_vertical_offset($align_v_offset);
745 $as->render($self->{image
});
746 $align_v_offset += 15;
753 =head3 render_png(), render_jpg()
755 Synopsis: $align->render_jpg(), $align->render_png
757 Description: Render itself and convert print out png or jpg
763 sub render_separate_png_files
{
765 my $background_filepath = shift;
767 $self->{display_type
} = "separate";
770 open(WF
, ">", $background_filepath);
771 print WF
$self->{image
}->png();
774 my $mfp = $background_filepath;
775 $mfp =~ s/\.[^\.]+$//;
777 my @member_imgs = ();
778 foreach(@
{$self->{members
}}){
779 $_->set_left_margin($self->get_left_margin());
780 $_->set_top_margin($_->get_height()/2);
782 my $id = $_->get_id();
785 my $img_path = $mfp . "." . md5_hex
($id) . ".png";
786 push(@member_imgs, $img_path);
787 open(WF
, ">$img_path" );
788 my $w = $_->get_width()
789 + $self->get_label_gap()
790 + $self->get_right_margin();
791 + $self->get_left_margin();
793 my $h = $_->get_height();
795 my $image = GD
::Image
->new($w, $h)
796 or die "Can't generate image\n";
797 my $white = $image->colorAllocate(255,255,255);
798 $image->filledRectangle(0,0,$w,$h,$white);
799 $image->transparent($white);
802 print WF
$image->png();
812 $self->render($option);
813 print $self->{image
}->png();
820 $self->render($option);
821 print $self->{image
}->jpeg();
824 =head3 render_png_file(), render_jpg_file()
826 SYnopsis: $align->render_png_file($file_name, $option), $align->render_jpg_file($file_name, $option)
828 Description: take a filename as arguments, render itself and output pgn or jpg image to the file.
834 sub render_png_file
{
836 my $filename = shift;
839 $self->render($option);
840 open (F
, ">$filename") || die "Can't open $filename for writing!!! Check write permission in dest directory.";
841 print F
$self->{image
}->png();
846 sub render_jpg_file
{
848 my $filename = shift;
851 $self ->render($option);
852 open (F
, ">$filename") || die "Can't open $filename for writing!!! Check write permission in dest directory.";
853 print F
$self->{image
}->jpeg();
859 =head3 write_image_map()
861 Synopsis: $align->write_image_map()
863 Description: get the image map string of each non-hidden @align_seqs, concat them and return as a single string
869 sub write_image_map
{
873 $map_content = "<map name=\"align_image_map\" id=\"align_image_map\">\n"; #XHTML 1.0+ requires id; name is for backward compatibility -- Evan, 1/8/07
874 foreach (@
{$self->{align_seqs
}}) {
875 ($_->is_hidden() eq 'yes') and next;
876 my $string = $_->get_image_string();
877 $map_content .= $string . "\n";
879 $map_content .= "</map>\n";
883 =head2 Subroutines to analyze sequences of @align_seq and output result
885 get_member_similarity(), get_sv_candidates(), get_allele_candidates(), get_overlap_score(), get_all_overlap_score(), get_all_medium(), get_all_range(), get_seqs(), get_nopad_seqs(), get_overlap_seqs(), get_overlap_nums(), get_ngap_pct(), get_all_ngap_length, get_conserved_seq_obj()
889 =head3 get_member_similarity()
891 Sysopsis: $align->get_member_similarity($al_sq) where $al_sq is an object of of algn_seq and member of @align_seqs
893 Description: To output pair-wise similarities (overlap base, percentage indentity)of the member which is specified as argument between other members of @align_seq.
895 Returns: two hash references, one for overlap bases and the other for percentage indentity. The key of both hashes are the ids of other non hidden members of @align_seqs
899 sub get_member_similarity
{
905 ($self->is_member($al_sq) != 0) and exit "Not a member!\n";
907 foreach (@
{$self->{align_seqs
}}) {
908 ($_ == $al_sq) and next;
909 my ($ol, $ip) = $al_sq->calculate_similarity($_);
910 my $other_id = $_->get_id();
911 $member_ol{$other_id} = $ol;
912 $member_ip{$other_id} = $ip;
915 return \
%member_ol, \
%member_ip;
918 =head3 get_sv_candidates()
920 Synopsis: $align->get_sv_candidates()
921 Description: make pairwise comparison between members of @align_seq of the same species. If the pair have enough overlap, and the percentage indentity is high enough, and they have enough insertion-deletion (specified as parameter), they are considered as putative splice variant pair
923 Returns: 3 hash references
924 1. for overlap bases, a 2-D hash, the two keys are the ids of putative pslice variant pair.
925 2. for indentity percentage, also 2-D
926 3. for species, the key is the species of the putative splice variant pair.
930 sub get_sv_candidates
{
933 my ($indel, $overlap);
934 ($self->{type
} eq 'pep') ?
($indel = '-' x
$self->{indel_limit
}) : ($indel = '---' x
$self->{indel_limit
});
935 ($self->{type
} eq 'pep') ?
($overlap = $self->{sv_overlap
}) : ($overlap = $self->{sv_overlap
} * 3);
937 my %sv_candidate_ob = ();
938 my %sv_candidate_pi = ();
939 my %sv_candidate_sp = ();
941 foreach (my $i = 0; $i < @
{$self->{align_seqs
}}; $i++){
942 foreach (my $j = $i + 1; $j < @
{$self->{align_seqs
}}; $j++){
943 (($self->{align_seqs
}[$i]->get_species()) ne ($self->{align_seqs
}[$j]->get_species())) and next;
944 my ($ol_seq1, $ol_seq2) = $self->{align_seqs
}[$i]->get_clean_align_seq($self->{align_seqs
}[$j]);
946 (!(($ol_seq1 =~ /$indel/) || ($ol_seq2 =~ /$indel/))) and next;
948 my ($self_id, $other_id) = ($self->{align_seqs
}[$i]->get_id(), $self->{align_seqs
}[$j]->get_id());
949 my ($ob, $pi) = $self->{align_seqs
}[$i]->calculate_similarity($self->{align_seqs
}[$j]);
950 if ( ($ob >= $overlap) && ($pi >= $self->{sv_identity
})){
951 $sv_candidate_ob{$self_id}{$other_id} = $ob;
952 $pi = sprintf("%.2f", $pi);#truncate the number to two digits after the decimal point
953 $sv_candidate_pi{$self_id}{$other_id} = $pi;
954 $sv_candidate_sp{$self_id} = $self->{align_seqs
}[$i]->get_species;
958 return \
%sv_candidate_ob, \
%sv_candidate_pi, \
%sv_candidate_sp;
961 =head3 get_allele_candidates()
963 Synopsis: $align->get_allele_candidates()
965 Description: make pairwise comparison between members of @align_seq of the same species. If the pair have enough overlap, and the percentage indentity is high enough, and they only have short insertion-deletion (specified as parameter), they are considered as putative allele pair
967 Returns: 3 hash references
968 1. for overlap bases, a 2-D hash, the two keys are the ids of putative alllele pair.
969 2. for indentity percentage, also 2-D
970 3. for species, the key is the species of the putative allele pair.
974 sub get_allele_candidates
{
977 my ($indel, $overlap);
978 ($self->{type
} eq 'pep') ?
($indel = '-' x
$self->{indel_limit
}) : ($indel = '---' x
$self->{indel_limit
});
979 ($self->{type
} eq 'pep') ?
($overlap = $self->{sv_overlap
}) : ($overlap = $self->{sv_overlap
} * 3);
981 my %al_candidate_ob = ();
982 my %al_candidate_pi = ();
983 my %al_candidate_sp = ();
984 foreach (my $i = 0; $i < @
{$self->{align_seqs
}}; $i++){
985 foreach (my $j = $i + 1; $j < @
{$self->{align_seqs
}}; $j++){
986 (($self->{align_seqs
}[$i]->get_species()) ne ($self->{align_seqs
}[$j]->get_species())) and next;
987 my ($ol_seq1, $ol_seq2) = $self->{align_seqs
}[$i]->get_clean_align_seq($self->{align_seqs
}[$j]);
989 (($ol_seq1 =~ /$indel/) || ($ol_seq2 =~ /$indel/)) and next; #skip if the aequence pair have long indel
991 my ($self_id, $other_id) = ($self->{align_seqs
}[$i]->get_id(), $self->{align_seqs
}[$j]->get_id());
992 my ($ob, $pi) = $self->{align_seqs
}[$i]->calculate_similarity($self->{align_seqs
}[$j]);
993 if ( $ob >= $overlap && ($pi >= $self->{sv_identity
})){
994 $al_candidate_ob{$self_id}{$other_id} = $ob;
995 $pi = sprintf("%.2f", $pi);#truncate the number to two digits after the decimal point
996 $al_candidate_pi{$self_id}{$other_id} = $pi;
997 $al_candidate_sp{$self_id} = $self->{align_seqs
}[$i]->get_species;
1001 return \
%al_candidate_ob, \
%al_candidate_pi, \
%al_candidate_sp;
1005 =head3 get_overlap_score();
1007 Synopsis: $align->get_overlap_score($align) where $align_seq is an object of align
1009 Description: Calculate the overlap score of a member of @ align_seqs, which is specified as parameter. At a particular position of the target sequence(s1) has an overlap (not a gap) in another sequence (s2), s1 gets 1 point for alignment score. The total alignment score of s1 is the sum of all its non-gap positions.
1015 sub get_overlap_score
{
1019 $self->is_member($al_sq) != 0 and exit "$al_sq->{id} is NOT a member!\n";
1020 $al_sq->{hide
} eq 'yes' and exit "$self->{id} is hiden!\n";
1022 foreach ( my $i = $self->{start_value
}-1; $i < $self->{end_value
}-1; $i++){
1023 my $base = substr($al_sq->get_seq(), $i, 1);
1024 $base eq '-' and next;
1025 foreach (@
{$self->{align_seqs
}}) {
1026 $_ == $al_sq and next;
1027 $_->{hide
} eq 'yes' and next;
1028 my $other_base = substr ($_->get_seq(), $i, 1);
1029 if ($other_base ne '-') {
1037 =head3 get_all_overlap_score()
1039 Synopsis: $align->get_all_overlap_score()
1041 Description: score of all the non-hiden members in @align_seq
1043 Returns: a hash reference whose key is the id of a @align_seq member and the value is the overlap score
1047 sub get_all_overlap_score
{
1050 my %member_score = ();
1051 foreach (@
{$self->{align_seqs
}}){
1052 ($_->is_hidden() eq 'yes') and next;
1053 my $score = $self->get_overlap_score($_);
1054 my $id = $_->get_id();
1055 $member_score{$id} = $score;
1057 return \
%member_score;
1061 =head3 get_all_medium
1063 Synopsis: $align->get_all_medium()
1065 Description: Output the medium position of each alignment sequence
1067 Returns: a hash reference whose key is the id of a @align_seq member and the value is the medoium
1071 sub get_all_medium
{
1074 my %member_medium = ();
1075 foreach (@
{$self->{align_seqs
}}){
1076 if ($_->is_hidden() ne 'yes') {
1077 my $medium = $_->get_medium();
1078 my $id = $_->get_id();
1079 $member_medium{$id} = $medium;
1082 return \
%member_medium;
1085 =head3 get_all_range
1087 Synopsis: $align->get_all_range()
1089 Description: Output the start and end of characters of each @align_seqs member
1091 Returns: two hash references whose keys are the id of a @align_seq member and the value is the start and end position respectively
1099 my %member_head = ();
1100 my %member_end = ();
1101 foreach (@
{$self->{align_seqs
}}) {
1102 if (($_->is_hidden()) ne 'yes'){
1103 my ($head, $end) = $_->get_range();
1104 my $id = $_->get_id();
1105 $member_head{$id} = $head;
1106 $member_end{$id} = $end;
1109 return \
%member_head, \
%member_end;
1115 Synopsis: $align->get_seqs()
1117 Description: Output the alignment sequences (padded with gaps) of @align_seqs which are are not hidden, in the range specified by start_value and end_value
1119 Returns: a hash reference whose key is the id of @align_seqs members and the value is the alignment sequence
1125 (! @
{$self->{align_seqs
}}) and exit "No align_seqs member.\n";
1127 my %member_seqs = ();
1128 foreach (@
{$self->{align_seqs
}}) {
1129 if ($_->{hide
} eq 'no') {
1130 my $id = $_->get_id();
1131 my $seq = $_->get_select_seq();
1132 $member_seqs{$id} = $seq;
1135 return \
%member_seqs;
1139 =head3 get_nopad_seqs()
1141 Synopsis: $align->get_nopad_seqs()
1143 Description: Output the 'original' sequences (with gaps removed) of @align_seqs which are are not hidden, in the range specified by start_value and end_value
1145 Returns: a hash reference whose key is the id of @align_seqs members and the value is the sequence
1149 sub get_nopad_seqs
{
1151 (! @
{$self->{align_seqs
}}) and exit "No align_seqs member.\n";
1153 my %member_seqs = ();
1154 foreach (@
{$self->{align_seqs
}}) {
1155 if ($_->{hide
} eq 'no') {
1156 my $id = $_->get_id();
1157 my $seq = $_->get_select_seq();
1159 $member_seqs{$id} = $seq;
1162 return \
%member_seqs;
1165 =head3 get_overlap_seqs()
1167 Sysnopsis: $align-> get_overlap_seqs()
1169 Description: for each non hidden member of @align_seqs, get the sequences that overlap with all the other non hiden member of @align_seqs, in the range from start_value to end_value
1171 Returns: a hash reference whose jkey ois the id of @align_seqs member and the value is the overlap sequence
1175 sub get_overlap_seqs
{
1177 (! @
{$self->{align_seqs
}}) and exit "No align_seqs member.\n";
1180 foreach (my $i = $self->{start_value
} - 1; $i < $self->{end_value
} - 1; $i++){
1181 my %single_base = ();
1183 foreach (@
{$self->{align_seqs
}}) {
1184 $_->is_hidden() eq 'yes' and next;
1185 my $base = substr($_->get_seq(), $i, 1);
1191 $single_base{$_->get_id()} = $base;
1195 foreach (keys %single_base) {
1196 if (!defined $overlap_seqs{$_}) {
1197 $overlap_seqs{$_} = $single_base{$_};
1200 $overlap_seqs{$_} .= $single_base{$_};
1205 return \
%overlap_seqs;
1208 =head3 get_overlap_num()
1210 Synopsis: $align->get_overlap_num()
1212 Description: count the number of bases that overlap between all the non hidden @align_seqs members, in the range from start_value to end)_value
1218 sub get_overlap_num
{
1220 my $overlap_count = 0;
1221 (! @
{$self->{align_seqs
}}) and exit "No align_seqs member.\n";
1223 #look for the first non-hiden member in @align_seqs
1225 foreach (@
{$self->{align_seqs
}}) {
1226 ($_->is_hidden() eq 'yes') and next;
1231 foreach (my $i = $self->{start_value
} - 1; $i < $self->{end_value
} - 1; $i++){
1232 my $base = substr($select->get_seq(), $i, 1);
1233 $base eq '-' and next;
1235 foreach (@
{$self->{align_seqs
}}) {
1236 ($_ == $select) and next;
1237 ($_->is_hidden() eq 'yes') and next;
1238 my $other_base = substr($_->get_seq(), $i, 1);
1239 if ($other_base eq '-') {
1248 return $overlap_count;
1251 =head3 get_ngap_pct()
1253 Synopsis: $align->get_ngap_pct()
1255 Description: go from start_value to end_value, get the percentage coverage by @align_seqs. A position is covered by a member when it has a non gap at the position.
1257 Returns: a hash reference whose key is the position and values are the percentage coverage
1264 my %value_hash = ();
1266 (! @
{$self->{align_seqs
}}) and exit "No align_seqs member.\n";
1268 my $total_nhidden_member = 0;
1269 foreach (@
{$self->{align_seqs
}}) {
1270 ($_->is_hidden() eq 'yes') and next;
1271 $total_nhidden_member++;
1274 foreach (my $i = $self->{start_value
} - 1; $i < $self->{end_value
}; $i++){
1276 foreach (@
{$self->{align_seqs
}}) {
1277 $_->is_hidden() eq 'yes' and next;
1278 my $seq = $_->get_seq();
1279 my $base = substr($seq, $i, 1);
1280 ($base ne '-') and $ngap_count++;
1282 my $pct = $ngap_count / $total_nhidden_member * 100;
1283 $value_hash{$i} = sprintf("%.2f", $pct);
1286 return \
%value_hash;
1290 =head3 get_all_nogap_length()
1292 Synopsis: $align->get_all_nogap_length()
1294 Description: go from start_value to end_value, get the sequence length without gap of @align_seqs member..
1296 Returns: a hash reference whose key is the id and value is the length
1302 sub get_all_nogap_length
{
1305 my %member_ng_len = ();
1306 foreach (@
{$self->{align_seqs
}}) {
1307 $_->is_hidden() eq 'yes' and next;
1308 $member_ng_len{$_->get_id()} = $_->get_nogap_length();
1311 return \
%member_ng_len;
1316 =head3 get_conserved_seq()
1318 Synopsis: $align->get_conserved_seq()
1320 Description: go through each postion from start_value to end_value of non hidden member of @align_seqs. If all members have the same seq at the position, get the seq, otherwise put a gap (-) in the position.
1322 Returns: s string of sequence
1326 sub get_conserved_seq
{
1330 (! @
{$self->{align_seqs
}}) and exit "No align_seqs member.\n";
1332 my $total_nhidden_member = 0;
1333 foreach (@
{$self->{align_seqs
}}) {
1334 ($_->is_hidden() eq 'yes') and next;
1335 $total_nhidden_member++;
1337 ($total_nhidden_member == 0) and exit "All members are hidden!\n";
1339 foreach (my $i = $self->{start_value
}-1; $i < $self->{end_value
}; $i++){
1342 my $conserved_base = '-';
1343 foreach (@
{$self->{align_seqs
}}) {
1344 $_->is_hidden() eq 'yes' and next;
1345 my $base = substr($_->get_seq(), $i, 1);
1348 if (!defined $na_count{$base}) {
1349 $na_count{$base} = 1;
1356 if ((int keys %na_count == 1) && ($base_count > 1)) {
1357 foreach (keys %na_count) {
1358 $conserved_base = $_;
1361 $seq .= $conserved_base;
1364 $seq = '-' x
($self->{start_value
} - 1) . $seq; #fill in the position before start_value
1370 ################################End of Package align#####################################
1377 ###############################Package image_object#######################################
1379 =head1 Package CXGN::Alignment::image_object
1381 The base class for align_seq and ruler.
1383 Its attributes include: horizontal_offset, vertical_offset, length (pixel), height (pixel), horizontal_offset, vertical_offset, color, label_corlor
1387 package CXGN
::Alignment
::image_object
;
1389 =head2 Constructer new()
1391 Synopsis: my $img_obj = Alignment::image_object->new(
1392 horizontal_offset=>$x,
1393 vertical_offset=>$y,
1396 start_value=>$start_value,
1397 end_value=>$end_value
1400 Returns: a Alignment::image_object object
1407 my $self = bless {}, $class;
1409 $self->{horizontal_offset
} = $args{horizontal_offset
};
1410 $self->{vertical_offset
} = $args{vertical_offset
};
1411 $self->{length} = $args{length};
1412 $self->{height
} = $args{height
};
1413 $self->{start_value
} = $args{start_value
};
1414 $self->{end_value
} = $args{end_value
};
1420 =head2 Setters and getters
1422 set_horizontal_offset(), set_vertical_offset, set_color(), set_label_color(), set_length(), set_start_value
1424 get_horizontal_offset(), get_vertical_offset(), get_label_color(), set_label_color(), get_length(), get_start_value()
1430 ($self->{color
}[0], $self->{color
}[1], $self->{color
}[2]) = @_;
1433 sub set_label_color
{
1435 ($self->{label_color
}[0], $self->{label_color
}[1], $self->{label_color
}[2]) = @_;
1438 sub set_start_value
{
1440 $self->{start_value
} = shift;
1445 $self->{end_value
} = shift;
1448 sub get_start_value
{
1450 return $self->{start_value
};
1455 return $self->{end_value
};
1458 sub get_horizontal_offset
{
1460 if (!exists($self->{horizontal_offset
})) { $self->{horizontal_offset
} = 0; }
1461 return $self->{horizontal_offset
};
1464 sub set_horizontal_offset
{
1466 $self->{horizontal_offset
} = shift;
1469 sub get_vertical_offset
{
1471 return $self->{vertical_offset
};
1474 sub set_vertical_offset
{
1476 $self->{vertical_offset
} = shift;
1481 if (!exists($self->{length})) { $self->{length} = 0; }
1482 return $self->{length};
1488 $self->{length} = shift;
1493 return $self->{height
};
1498 $self->{height
} = shift;
1502 =head2 Subs for image display
1504 set_enclosing_rect(), get_enclosing_rect, render(), get_image_string()
1509 sub set_enclosing_rect
{
1511 ($self->get_horizontal_offset(), $self->get_vertical_offset(), $self->{width
}, $self->{height
}) = @_;
1514 sub get_enclosing_rect
{
1516 return ($self->get_horizontal_offset(), $self->get_vertical_offset(), $self->get_horizontal_offset() + $self->get_length() + 150, $self->get_vertical_offset() + $self->get_height);#to include the label space, $x plus 150
1524 sub get_image_string
{
1527 my $coords = join ",", ($self->get_enclosing_rect());
1529 if ($self->get_url()) {
1530 $string = "<area shape=\"rect\" coords=\"".$coords."\" href=\"".$self->get_url()."\" alt=\"".$self->get_id()."\" />";
1536 =head3 _calculate scaling_factor()
1538 Synopsis: $self->_calculate_scaling_factor()
1540 Description: calculate the scaling factor, set the scaling_factor attribute and return the scaling factor. private, called by render
1542 Returns: a number, scaling factor.
1546 sub _calculate_scaling_factor
{
1548 my $dist = ($self->{end_value
} - $self->{start_value
}) + 1;
1549 if ($dist ==0) { return 0; }
1550 $self->{scaling_factor
} = $self->{length}/$dist;
1551 return $self->{scaling_factor
};
1556 ##############################End of Package image_object###################################
1563 ##############################Package align_seq#############################################
1565 =head1 Package Alignment::align_seq
1567 Inherit from image_object. Its special attributes include id, seq, species, seq_line_width, label_spacer, hide and url
1571 package CXGN
::Alignment
::align_seq
;
1572 use base
qw( CXGN::Alignment::image_object );
1574 =head2 COnstructer new()
1576 Synopsis: my $al_sq = Alignment::align_seq->new(
1577 horizontal_offset=>$x,
1578 vertical_offset=>$y,
1581 start_value=>$start_value,
1582 end_value=>$end_value
1588 Returns: a CXGN::Alignment::align_seq object
1595 my $self = $class->SUPER::new
(@_);
1597 $self->{id
} = $args{id
};
1598 $self->{seq
} = $args{seq
};
1599 $self->{species
} = $args{species
};
1602 $self->{font
} = GD
::Font
->Small();
1603 $self->set_color (0, 0, 255);
1604 $self->set_label_color(0, 0, 0);
1605 $self->set_seq_line_width(8);
1606 $self->{label_spacer
} = 20;
1607 $self->{hide
} = 'no';
1609 #define an empty attributes
1615 =head2 Setters and getters
1617 set_seq_line_width(), get_seq_lien_width(), set_label_spacer(), get_label_spacer(), is_hide(), hide_seq(), unhide_seq(), set_url(), get_url(), set_id(), get_id(), set_species(), get_species(), set_seq(), get_seq(), get_select_seq()
1621 sub set_label_spacer
{
1623 $self ->{label_spacer
} = shift;
1626 sub get_label_spacer
{
1628 if (!exists $self->{label_spacer
}){
1629 $self->{label_spacer
} = 0;
1631 return $self->{label_spacer
};
1634 sub set_seq_line_width
{
1636 $self->{seq_line_width
} = shift;
1638 sub get_seq_line_width
{
1640 if (!exists $self->{seq_line_width
}){
1641 $self->{seq_line_width
} = 1;
1643 return $self->{seq_line_width
};
1648 $self->{id
} = shift;
1658 $self->{species
} = shift;
1663 return $self->{species
};
1668 $self->{seq
} = shift;
1673 return $self->{seq
};
1676 sub get_select_seq
{
1678 my $id = $self->{id
};
1679 my $seq = $self->{seq
};
1680 $seq = substr($seq, $self->{start_value
} - 1, $self->{end_value
} - $self->{start_value
} + 1);
1687 Synopsis: align_seq->is_hidden();
1689 Description: check if itself is hidden
1691 Returns: 'yes' if the sequence is hidden and 'n' if not. It is set to 'no' by default when the object is constructed.
1697 return $self->{hide
};
1703 $self->{url
} = shift;
1708 return $self->{url
};
1713 $self->{hide
} = 'yes';
1718 $self->{hide
} = 'no';
1722 =head2 Image display subs
1730 Synopsis: align_seq->render($image) where $iamge is an image object
1732 Description: render the align_seq object
1741 my $color = $image->colorResolve($self->{color
}[0], $self->{color
}[1], $self->{color
}[2]);
1742 my $label_color = $image->colorResolve($self->{label_color
}[0], $self->{label_color
}[1], $self->{label_color
}[2]);
1743 my $align_seq = substr ($self->{seq
}, $self->{start_value
}-1, $self->{end_value
}-$self->{start_value
}+1);
1744 my $seq_id = $self->{id
};
1745 $self->_calculate_scaling_factor();
1747 for ($i = 0; $i < length ($align_seq); $i++){
1749 #draw sequence, a line if the base is a gap and a rectangle otherwise
1750 my $base = substr($align_seq, $i, 1);
1753 $image->line($i*$self->{scaling_factor
}+$self->{horizontal_offset
}, $self->{vertical_offset
}, ($i+1)*$self->{scaling_factor
}+$self->{horizontal_offset
}, $self->{vertical_offset
}, $color);
1756 $image->filledRectangle($i*$self->{scaling_factor
}+$self->{horizontal_offset
}, $self->{vertical_offset
}-$self->{seq_line_width
}/2, ($i+1)*$self->{scaling_factor}+$self->{horizontal_offset}, $self->{vertical_offset}+ $self->{seq_line_width}/2, $color);
1760 #add sequence name, first chop the sequence name to up to 30 characters. add '..' if the name is longer
1762 my $full_id = $self->{id
} . ' ' . $self->{species
};
1763 if ((length $full_id) >= 30) {
1764 $show_id = substr ($full_id, 0, 30);
1768 $show_id = $full_id;
1771 $image->string($self->{font
}, $i*$self->{scaling_factor
}+ $self->{horizontal_offset
} + $self->{label_spacer
}, $self->{vertical_offset
} - $self->{seq_line_width
} / 2, $show_id, $label_color);
1775 =head2 Sequence analyzing subs
1777 get_nogap_length(), calculate_similarity(), get_medium(), get_range()
1779 All analysis are in the range from start_value to end_value
1783 sub get_nogap_length
{
1785 my $no_gap_seq = $self->get_select_seq();
1786 $no_gap_seq =~ s/-//g;
1787 return length ($no_gap_seq);
1790 =head3 calculate_similarity()
1792 Synopsis: $align_seq->calculate_similarity($other_align_seq) where $other_align_seq is another align_seq object
1794 Description: get the overlap base number and idnetical percentage of the seq of two align_seq objects.
1796 Returns: an integer (number of overlap base) and a float (percentage indentity)
1800 sub calculate_similarity
{
1802 my $other_seq = shift;
1803 ($self->get_length() != $other_seq->get_length()) and exit "$self->{id} and $other_seq->{id} sequences are not of the same length, align them first!\n";
1804 my ($overlap_base, $identical_base) = (0, 0);
1805 for (my $i = $self->{start_value
} - 1; $i < $self->{end_value
}-1; $i++){
1806 my $self_base = substr ($self->{seq
}, $i, 1);
1807 my $other_base = substr($other_seq->{seq
}, $i, 1);
1808 (($self_base eq '-') || ($other_base eq '-')) and next;
1810 ($self_base eq $other_base) and $identical_base++;
1812 if ($overlap_base != 0){
1813 return $overlap_base, $identical_base/ $overlap_base * 100;
1816 return $overlap_base, $identical_base;
1820 =head3 $align_seq->get_clean_align_seq()
1822 Synopsis: $align_seq->get_clean_align_seq($align_seq2) where $align_seq2 is another align_seq object
1824 Description: goes through the two alignment sequences, in the conmmon range, and leave out common gaps.
1826 Returns: two 'clean' overlap sequences, with common gaps removed.
1830 sub get_clean_align_seq
{
1832 my $other_seq = shift;
1834 ($self->get_length() != $other_seq->get_length()) and exit "$self->{id} and $other_seq->{id} sequences are not of the same length, align them first!\n";
1835 my ($start1, $end1) = $self->get_range();##Takes care of start_value and end_value
1836 my ($start2, $end2) = $other_seq->get_range();
1839 ($start1 > $start2) ?
($start = $start1) : ($start = $start2);
1840 ($end1 < $end2) ?
($end = $end1) : ($end = $end2);
1842 my $id1 = $self->get_id();
1843 my $id2 = $other_seq->get_id();
1846 for (my $i=$start-1; $i<$end-1; $i++) {
1847 my $base1 = substr ($self->get_seq(), $i, 1);
1848 my $base2 = substr ($other_seq->get_seq(), $i, 1);
1850 ($base1 eq '-' && $base2 eq '-') and next;
1855 return $seq1, $seq2;
1860 Synopsis: $align_seq->get_medium()
1862 Description: calculate the middle point of non-gap bases of the align_seq
1871 my $non_gap_len = 0;
1872 my $seq = $self->get_select_seq();
1873 while ($seq =~ /[A-Z]/gi) {
1877 my $non_gap_mid = int ($non_gap_len / 2);
1878 my $non_gap_count = 0;
1881 foreach ( my $i = $self->{start_value
}-1; $i < $self->{end_value
}; $i++){
1882 my $base = substr($self->{seq
}, $i, 1);
1883 ($base ne '-') and $non_gap_count++;
1884 if ($non_gap_count > $non_gap_mid) {
1890 $mid = $mid + $self->{start_value
};
1896 Synopsis: $align_seq->get_range()
1898 Description: get the postion of the first non gap character and the last non gap character, from start_value to end_value.
1900 Returns: two intgers representing two positions.
1906 my ($base_start, $base_end);
1908 my $seq = $self->get_select_seq();
1910 my ($head_gaps) = $seq =~ /^(-+)/;
1911 my ($tail_gaps) = $seq =~ /(-+)$/;
1912 (!defined $head_gaps) and $head_gaps = '';
1913 (!defined $tail_gaps) and $tail_gaps = '';
1914 $base_start = $self->get_start_value() + length ($head_gaps);
1915 $base_end = $self->get_end_value() - length ($tail_gaps);
1917 return $base_start, $base_end;
1921 ###################################End of Package align_seq####################################
1928 ##################################Package ruler###############################################
1930 =head1 Package Alignment::ruler
1932 This class is inherited from image_object. Its special attributes include label_side, unit, label_spacing and tick_spacing. The ruler is horizontal only.
1936 package CXGN
::Alignment
::ruler
;
1938 use base
qw( CXGN::Alignment::image_object);
1939 =head2 Constructor new()
1941 Synopsis: my $ruler = Alignment::ruler->new(
1942 horizontal_offset=>$x,
1943 vertical_offset=>$y,
1949 Returns: a Alignment::ruler object
1956 my $self = $class->SUPER::new
(@_);
1959 $self->{font
} = GD
::Font
->Small();
1960 $self->set_color (127, 127, 127);
1961 $self->set_label_color (0,0, 0);
1962 $self->{label_side
} = "up";
1963 $self->{unit
} = "bp";
1964 $self->{label_spacing
} = 50;
1965 $self->{tick_spacing
} = 50;
1970 =head2 Setters and getters
1972 set_labels_up(), set_labels_down(), set_unit("my_unit"), get_unit(), set_label_spacing, get_label_spacing, set_tick_spacing, get_tick_spacing
1978 $self->{label_side
} = "up";
1981 sub set_labels_down
{
1983 $self ->{label_side
} = "down";
1988 $self->{unit
} = shift;
1993 return $self->{unit
};
1996 sub set_label_spacing
{
1998 $self->{label_spacing
} = shift;
2001 sub get_label_spacing
{
2003 return $self->{label_spacing
};
2006 sub set_tick_spacing
{
2008 $self->{tick_spacing
} = shift;
2011 sub get_tick_spacing
{
2013 return $self->{tick_spacing
};
2016 =head2 Image display sub render()
2018 Synopsis: $ruler->render($img) where $img is a image object
2020 Description: draws ruler line, ticks (goes to near the bottom of the image), label and unit,
2028 my $color = $image->colorResolve($self->{color
}[0], $self->{color
}[1], $self->{color
}[2]);
2030 #####################Draw ruler line
2031 $image->line($self->{horizontal_offset
}, $self->{vertical_offset
}, $self->{horizontal_offset
} + $self->{length}, $self->{vertical_offset
}, $color);
2033 #####################Draw ticks and tick labels
2034 #Reset tick and spacing, depending on the length
2035 $self->{tick_spacing
} = ((int (($self->{end_value
} - $self->{start_value
} + 1) / 1000))+ 1) * 100;
2036 #$self->{label_spacing} = ($self->{tick_spacing}) * 2;
2038 #####################Determine the scaling factor. Increment label spaing by 10 until the longest label (maxim value) is shorter than label spacing
2039 $self->_calculate_scaling_factor();
2040 if ($self->{scaling_factor
}) {
2041 #otherwise this is an infinite loop....
2042 while (($self->{label_spacing
} * $self->{scaling_factor
}) < ($self->{font
}->width() * length ($self->{end_value
})+2)) { $self->{label_spacing
} +=50; }
2045 my $tick_number = int($self->{end_value
}-$self->{start_value
})/$self->{tick_spacing
} + 1;
2046 for (my $i = 0; $i < $tick_number - 1; $i++) {
2047 my $x = $self->{horizontal_offset
} + (($i*$self->{tick_spacing
})*$self->{scaling_factor
});
2048 $image->dashedLine($x, $self->{vertical_offset
}-2, $x, $self->{height
}, $color); #draw the tick
2049 if ( $i*$self->{tick_spacing
} % $self->{label_spacing
} == 0){#Draw tick label
2050 my $tick_label = $i*$self->{tick_spacing
} + $self->{start_value
} - 1;
2051 my $horizontal_adjust = $self->{font
}->width * length ($tick_label)/2;
2052 my $tick_label_x = $x - $horizontal_adjust;
2054 if ($self->{label_side
} eq 'down'){
2055 $tick_label_y = $self->{vertical_offset
} + 1;
2058 $tick_label_y = $self->{vertical_offset
} - 1 - $self->{font
}->height;
2060 $image->string($self->{font
}, $tick_label_x, $tick_label_y, $tick_label, $color);
2065 my $unit_label = "[".$self->{unit
}."]";
2066 my $unit_label_x = $self->{horizontal_offset
} + ($self->{length}- $self->{font
}->width() * length($unit_label))/2;
2068 if ($self->{label_side
} eq 'down'){
2069 $unit_label_y = $self->{horizontal_offset
} + 1 + $self->{font
}->height;
2072 $unit_label_y = $self->{horizontal_offset
} - 1 - $self->{font
}->height*2;
2074 $image->string($self->{font
}, $unit_label_x, $unit_label_y, $unit_label, $color);
2078 ###################################End of Package ruler#####################################
2085 ##################################Package chart#############################################
2086 =head1 Package CXGN::Alignment::chart
2088 Inherit from image_object. Its special attributes include id and hash_ref. The keys of the hash is position and the vaule is a percentage.
2092 package CXGN
::Alignment
::chart
;
2094 use base
qw( CXGN::Alignment::image_object);
2096 =head2 Constructer new()
2098 Synopsis: my $chart= Alignment::chart->new(
2099 horizontal_offset=>$x,
2100 vertical_offset=>$y,
2103 start_value=>$start_value,
2104 end_value=>$end_value
2108 Returns: a Alignment::chart object
2115 my $self = $class->SUPER::new
(@_);
2117 $self->{id
} = $args{id
};
2118 $self->{hash_ref
} = $args{hash_ref
};
2121 $self->{font
} = GD
::Font
->Small();
2122 $self->set_color (0, 0, 122);
2123 $self->set_label_color(0, 0, 0);
2124 $self->{label_spacer
} = 20;
2125 $self->{hide
} = 'no';
2129 =head2 Setters and getters
2131 set_id(), get_id(), set_hash_ref(), get_hash_ref()
2139 $self->{id
} = shift;
2149 $self->{hash_ref
} = shift;
2154 return $self->{hash_ref
};
2157 =head2 Image displaying sub
2165 Synopsis
: $chart->render($img) where
$img is an image object
2167 Description
: it draws a base line
and a
100% line from start_value to end_value
. Then it get all the hash elements whose
keys are
in the reange from strt_value to end_value
and draw a rectangle reprensting the vlaue
.
2177 my $ref = $self->{hash_ref
};
2178 my %chart_hash = %$ref;
2180 my $color = $image->colorResolve($self->{color
}[0], $self->{color
}[1], $self->{color
}[2]);
2181 my $label_color = $image->colorResolve($self->{label_color
}[0], $self->{label_color
}[1], $self->{label_color
}[2]);
2183 my $seq_id = $self->{id
};
2184 $self->_calculate_scaling_factor();
2186 $image->line($self->{horizontal_offset
}, $self->{vertical_offset
}, $self->{horizontal_offset
} + $self->{length}, $self->{vertical_offset
}, $color); #Draw the 0% line
2187 $image->line($self->{horizontal_offset
}, $self->{vertical_offset
} + 33.3, $self->{horizontal_offset
} + $self->{length}, $self->{vertical_offset
}+ 33.3, $color); #Draw the 100% line
2190 for ($i = $self->{start_value
} - 1; $i < $self->{end_value
}; $i++){
2192 (!defined $chart_hash{$i}) and ($chart_hash{$i} = 0);
2193 $adjust_i = $i - $self->{start_value
} + 1; #adjust horizontal offset according to start_value
2195 $image->filledRectangle($adjust_i*$self->{scaling_factor
}+$self->{horizontal_offset
}, $self->{vertical_offset
}, ($adjust_i+1)*$self->{scaling_factor
}+$self->{horizontal_offset
}, $self->{vertical_offset
}+$chart_hash{$i}/3, $color);
2199 #add sequence name, first chop the sequence name to up to 30 characters. add '..' if the name is longer
2201 if ((length ($self->{id
})) >= 30) {
2202 $show_id = substr ($self->{id
}, 0, 30);
2206 $show_id = $self->{id
};
2209 $image->string($self->{font
}, ($adjust_i + 1) *$self->{scaling_factor
}+ $self->{horizontal_offset
} + $self->{label_spacer
}, $self->{vertical_offset
} - $self->{seq_line_width
} / 2, $show_id, $label_color);
2212 ################################End of Package chart###########################################