5 /tools/sequence.mas - mason module to display a sequence, either as
6 fasta text or as nicely formatted html, with optional highlighting of
15 the sequence [Bio::Seq object]
19 the title of this section. Default is sequence id.
21 =item highlight_description
23 short plain-text description of the meaning of the highlighted regions,
26 =item highlight_coords
28 a list of ONE-BASED refs to start/end coords, [start, end], [start,
29 end], ... Will be highlighted and sequence will be trimmed to show
30 only a window around these regions, unless whole_seq is set below.
34 Display the entire sequence, do not cut out windows around the
35 matches. Default false.
39 width in characters to render the sequence, default 90
43 optional source name for the sequence, example "BLAST database such
44 and such", or "Tomato unigene build blah blah"
48 Optional URL to which a sequence could be POSTed to pre-fill the BLAST
60 $highlight_description => 'matches'
61 $highlight_coords => [ ]
63 $source => '<span class="ghosted">not recorded</span>'
70 <&| /page/info_section.mas, title => $title || $seq->id(), collapsible=> $collapsable, collapsed=> $collapsed &>
72 <% info_table_html( 'Sequence ID' => link_identifier($seq->id) || $seq->id,
73 'Length' => commify_number( $seq->length ),
80 <% info_table_html( 'Description' => $seq->desc || '<span class="ghosted">none</span>',
81 highlighted_regions( $highlight_coords, $highlight_description ),
86 <div style="margin: 0.4em 0"> </div>
88 % for my $r ( @regions ) {
89 % my $w = $r->{window};
90 % my $highlight_count = sum map $_->end - $_->start +1, @{$r->{abs_ranges}};
92 <&| /page/info_section.mas,
93 title => "Residues ".commify_number( $w->start ).' - '.commify_number( $w->end ),
95 subtitle => commify_number( $w->end - $w->start + 1 ).' residues shown, '.commify_number( $highlight_count || 0 ).' highlighted',
97 % my $wseq = $w->start == 1 && $w->end == $seq->length ? $seq : $seq->trunc( $w->start, $w->end );
98 % if( $wseq->length > 200_000 ) {
99 <span class="ghosted">Region too large to display.</span>
101 <& /sequence/with_markup.mas,
105 styles => { highlight => [ '<span class="highlighted">', '</span>'] },
106 regions => [ map ['highlight', $_->start-1, $_->end], @{ $r->{rel_ranges} } ],
107 blast_url => $blast_url,
116 # sanitize the coords array (add 0 to all the numbers in it, forcing them to be purely numeric)
117 my @highlight_coords = map [map $_+0, @$_], @$highlight_coords;
119 # calculate the pieces of the sequence that we will display, and that we will highlight
120 my @regions = seq_windows_and_ranges( $seq, \@highlight_coords, $width );
125 use List::Util qw/ min max sum /;
126 use List::MoreUtils qw/ minmax /;
128 use Storable 'dclone';
132 use CXGN::Page::FormattingHelpers qw(
136 use CXGN::Tools::Identifiers qw/ link_identifier /;
137 use CXGN::Tools::Text qw/ commify_number /;
139 use constant LINE_WINDOW_EXPAND => 5;
141 sub coords_to_ranges {
142 my ( $seq, $coords ) = @_;
145 Bio::Range->new( -start => max( 1, $_->[0]),
146 -end => min( $seq->length, $_->[1] ),
149 sort { $a->[0] <=> $b->[0] }
153 sub seq_windows_and_ranges {
154 my ( $seq, $coords, $width ) = @_;
156 unless ( $coords && @$coords ) {
158 window => Bio::Range->new( -start => 1, -end => $seq->length ),
164 # change the coords into ranges and sort by start coord, also
165 # clamping them to the sequence length if needed, and merge any
167 my @ranges = Bio::Range->unions( coords_to_ranges( $seq, $coords ) );
169 # calculate the windows of context for highlighted regions
171 # calculate 1-based non-overlapping ranges of *sequence* we will display
172 sort { $a->start <=> $b->start }
176 my $match = Bio::Range->new(
178 ( POSIX::floor(($r->start-1)/$width) - LINE_WINDOW_EXPAND ),
180 -end => min( $seq->length/$width,
181 ( POSIX::floor(($r->end)/$width ) + 1 + LINE_WINDOW_EXPAND ),
187 # decorate the windows with the other things that are needed to render them
191 # find ranges appicable to this window
193 grep { $_->start >= $window->start && $_->end <= $window->end }
196 # make another set of ranges that are relative to the window
197 my @rel_window_ranges = map {
199 $r->start( $r->start - $window->start + 1 );
200 $r->end( $r->end - $window->start + 1 );
204 { window => $window, abs_ranges => \@window_ranges, rel_ranges => \@rel_window_ranges }
209 sub highlighted_regions {
210 my ( $coords, $highlight_description ) = @_;
211 return unless $coords && @$coords;
214 "Highlighted Regions ($highlight_description)" =>
216 map "$_->[0]-$_->[1]",