File Coverage

Bio/Search/Tiling/MapTileUtils.pm
Criterion Covered Total %
statement 217 316 68.6
branch 62 118 52.5
condition 17 31 54.8
subroutine 21 25 84.0
pod 7 12 58.3
total 324 502 64.5


line stmt bran cond sub pod time code
1             #$Id$
2             package Bio::Search::Tiling::MapTileUtils;
3 1     1   4 use strict;
  1         1  
  1         21  
4 1     1   2 use warnings;
  1         1  
  1         18  
5 1     1   3 use Exporter;
  1         1  
  1         64  
6              
7             BEGIN {
8 1     1   7 our @ISA = qw( Exporter );
9 1         1551 our @EXPORT = qw( get_intervals_from_hsps
10             interval_tiling
11             decompose_interval
12             containing_hsps
13             covering_groups
14             _allowable_filters
15             _set_attributes
16             _mapping_coeff);
17             }
18              
19             # tiling trials
20             # assumed: intervals are [$a0, $a1], with $a0 <= $a1
21             =head1 NAME
22              
23             Bio::Search::Tiling::MapTileUtils - utilities for manipulating closed intervals for an HSP tiling algorithm
24              
25             =head1 SYNOPSIS
26              
27             Not used directly.
28              
29             =head1 DESCRIPTION
30              
31             Not used directly.
32              
33             =head1 NOTE
34              
35             An "interval" in this module is defined as an arrayref C<[$a0, $a1]>, where
36             C<$a0, $a1> are scalar numbers satisfying C<$a0 E= $a1>.
37              
38             =head1 AUTHOR
39              
40             Mark A. Jensen - maj -at- fortinbras -dot- us
41              
42             =head1 APPENDIX
43              
44             =head2 interval_tiling
45              
46             Title : interval_tiling()
47             Usage : @tiling = interval_tiling( \@array_of_intervals )
48             Function: Find minimal set of intervals covering the input set
49             Returns : array of arrayrefs of the form
50             ( [$interval => [ @indices_of_collapsed_input_intervals ]], ...)
51             Args : arrayref of intervals
52              
53             =cut
54              
55             sub interval_tiling {
56 219 50   219 1 388 return unless $_[0]; # no input
57 219         174 my $n = scalar @{$_[0]};
  219         251  
58 219         203 my %input;
59 219         205 @input{(0..$n-1)} = @{$_[0]};
  219         767  
60 219         403 my @active = (0..$n-1);
61 219         201 my @hold;
62             my @tiled_ints;
63 0         0 my @ret;
64 219         350 while (@active) {
65 432         510 my $tgt = $input{my $tgt_i = shift @active};
66 432         433 push @tiled_ints, $tgt_i;
67 432         341 my $tgt_not_disjoint = 1;
68 432         588 while ($tgt_not_disjoint) {
69 499         349 $tgt_not_disjoint = 0;
70 499         791 while (my $try_i = shift @active) {
71 971         742 my $try = $input{$try_i};
72 971 100       884 if ( !are_disjoint($tgt, $try) ) {
73 104         124 $tgt = min_covering_interval($tgt,$try);
74 104         130 push @tiled_ints, $try_i;
75 104         183 $tgt_not_disjoint = 1;
76             }
77             else {
78 867         1370 push @hold, $try_i;
79             }
80             }
81 499 100       693 if (!$tgt_not_disjoint) {
82 432         681 push @ret, [ $tgt => [@tiled_ints] ];
83 432         498 @tiled_ints = ();
84             }
85 499         578 @active = @hold;
86 499         1063 @hold = ();
87             }
88             }
89 219         555 return @ret;
90             }
91              
92             =head2 decompose_interval
93              
94             Title : decompose_interval
95             Usage : @decomposition = decompose_interval( \@overlappers )
96             Function: Calculate the disjoint decomposition of a set of
97             overlapping intervals, each annotated with a list of
98             covering intervals
99             Returns : array of arrayrefs of the form
100             ( [[@interval] => [@indices_of_coverers]], ... )
101             Args : arrayref of intervals (arrayrefs like [$a0, $a1], with
102             Note : Each returned interval is associated with a list of indices of the
103             original intervals that cover that decomposition component
104             (scalar size of this list could be called the 'coverage coefficient')
105             Note : Coverage: each component of the decomp is completely contained
106             in the input intervals that overlap it, by construction.
107             Caveat : This routine expects the members of @overlappers to overlap,
108             but doesn't check this.
109              
110             =cut
111              
112             ### what if the input intervals don't overlap?? They MUST overlap; that's
113             ### what interval_tiling() is for.
114              
115             sub decompose_interval {
116 432 50   432 1 573 return unless $_[0]; # no input
117 432         308 my @ints = @{$_[0]};
  432         520  
118 432         379 my (%flat,@flat);
119             ### this is ok, but need to handle the case where a lh and rh endpoint
120             ### coincide...
121             # decomposition --
122             # flatten:
123             # every lh endpoint generates (lh-1, lh)
124             # every rh endpoint generates (rh, rh+)
125 432         457 foreach (@ints) {
126 536         964 $flat{$$_[0]-1}++;
127 536         608 $flat{$$_[0]}++;
128 536         1792 $flat{$$_[1]}++;
129 536         891 $flat{$$_[1]+1}++;
130             }
131             # sort, create singletons if nec.
132 432         300 my @a;
133 432         1100 @a = sort {$a<=>$b} keys %flat;
  3321         2882  
134             # throw out first and last (meeting a boundary condition)
135 432         464 shift @a; pop @a;
  432         376  
136             # look for singletons
137 432         527 @flat = (shift @a, shift @a);
138 432 100       738 if ( $flat[1]-$flat[0] == 1 ) {
139 1         3 @flat = ($flat[0],$flat[0], $flat[1]);
140             }
141 432         667 while (my $a = shift @a) {
142 394 100       459 if ($a-$flat[-2]==2) {
143 17         19 push @flat, $flat[-1]; # create singleton interval
144             }
145 394         519 push @flat, $a;
146             }
147 432 50 33     840 if ($flat[-1]-$flat[-2]==1 and @flat % 2) {
148 0         0 push @flat, $flat[-1];
149             }
150             # component intervals are consecutive pairs
151 432         295 my @decomp;
152 432         637 while (my $a = shift @flat) {
153 638         1286 push @decomp, [$a, shift @flat];
154             }
155              
156             # for each component, return a list of the indices of the input intervals
157             # that cover the component.
158 432         309 my @coverage;
159 432         716 foreach my $i (0..$#decomp) {
160 638         625 foreach my $j (0..$#ints) {
161 1316 100       1369 unless (are_disjoint($decomp[$i], $ints[$j])) {
162 846 100       917 if (defined $coverage[$i]) {
163 208         141 push @{$coverage[$i]}, $j;
  208         329  
164             }
165             else {
166 638         1256 $coverage[$i] = [$j];
167             }
168             }
169             }
170             }
171 432         623 return map { [$decomp[$_] => $coverage[$_]] } (0..$#decomp);
  638         1935  
172             }
173              
174             =head2 are_disjoint
175              
176             Title : are_disjoint
177             Usage : are_disjoint( [$a0, $a1], [$b0, $b1] )
178             Function: Determine if two intervals are disjoint
179             Returns : True if the intervals are disjoint, false if they overlap
180             Args : array of two intervals
181              
182             =cut
183              
184             sub are_disjoint {
185 2287     2287 1 1484 my ($int1, $int2) = @_;
186 2287 100 100     6220 return 1 if ( $$int1[1] < $$int2[0] ) || ( $$int2[1] < $$int1[0]);
187 950         1292 return 0;
188             }
189              
190             =head2 min_covering_interval
191              
192             Title : min_covering_interval
193             Usage : $interval = min_covering_interval( [$a0,$a1],[$b0,$b1] )
194             Function: Determine the minimal covering interval for two intervals
195             Returns : an interval
196             Args : two intervals
197              
198             =cut
199              
200             sub min_covering_interval {
201 104     104 1 97 my ($int1, $int2) = @_;
202 104         247 my @a = sort {$a <=> $b} (@$int1, @$int2);
  516         478  
203 104         181 return [$a[0],$a[-1]];
204             }
205              
206             =head2 get_intervals_from_hsps
207              
208             Title : get_intervals_from_hsps
209             Usage : @intervals = get_intervals_from_hsps($type, @hsp_objects)
210             Function: Return array of intervals of the form [ $start, $end ],
211             from an array of hsp objects
212             Returns : an array of intervals
213             Args : scalar $type, array of HSPI objects; where $type is one of 'hit',
214             'subject', 'query'
215              
216             =cut
217              
218             sub get_intervals_from_hsps {
219 219     219 1 172 my $type = shift;
220 219         171 my @hsps;
221 219 50       2592 if (ref($type) =~ /HSP/) {
    50          
222 0         0 push @hsps, $type;
223 0         0 $type = 'query';
224             }
225             elsif ( !grep /^$type$/,qw( hit subject query ) ) {
226 0         0 die "Unknown HSP type '$type'";
227             }
228 219 50       345 $type = 'hit' if $type eq 'subject';
229 219         296 push @hsps, @_;
230 219         187 my @ret;
231 219         285 foreach (@hsps) {
232             # push @ret, [ $_->start($type), $_->end($type)];
233 536         991 push @ret, [ $_->range($type) ];
234             }
235 219         585 return @ret;
236             }
237              
238             # fast, clear, nasty, brutish and short.
239             # for _allowable_filters(), _set_mapping()
240             # covers BLAST, FAST families
241             # FASTA is ambiguous (nt or aa) based on alg name only
242              
243             my $alg_lookup = {
244             'N' => { 'mapping' => [1,1],
245             'def_context' => ['p_','p_'],
246             'has_strand' => [1, 1],
247             'has_frame' => [0, 0]},
248             'P' => { 'mapping' => [1,1],
249             'def_context' => ['all','all'],
250             'has_strand' => [0, 0],
251             'has_frame' => [0, 0]},
252             'X' => { 'mapping' => [3, 1],
253             'def_context' => [undef,'all'],
254             'has_strand' => [1, 0],
255             'has_frame' => [1, 0]},
256             'Y' => { 'mapping' => [3, 1],
257             'def_context' => [undef,'all'],
258             'has_strand' => [1, 0],
259             'has_frame' => [1, 0]},
260             'TA' => { 'mapping' => [1, 3],
261             'def_context' => ['all',undef],
262             'has_strand' => [0, 1],
263             'has_frame' => [0, 1]},
264             'TN' => { 'mapping' => [1, 3],
265             'def_context' => ['p_',undef],
266             'has_strand' => [1,1],
267             'has_frame' => [0, 1]},
268             'TX' => { 'mapping' => [3, 3],
269             'def_context' => [undef,undef],
270             'has_strand' => [1, 1],
271             'has_frame' => [1, 1]},
272             'TY' => { 'mapping' => [3, 3],
273             'def_context' => [undef,undef],
274             'has_strand' => [1, 1],
275             'has_frame' => [1, 1]}
276             };
277            
278             =head2 _allowable_filters
279            
280             Title : _allowable_filters
281             Usage : _allowable_filters($Bio_Search_Hit_HitI, $type)
282             Function: Return the HSP filters (strand, frame) allowed,
283             based on the reported algorithm
284             Returns : String encoding allowable filters:
285             s = strand, f = frame
286             Empty string if no filters allowed
287             undef if algorithm unrecognized
288             Args : A Bio::Search::Hit::HitI object,
289             scalar $type, one of 'hit', 'subject', 'query';
290             default is 'query'
291              
292             =cut
293              
294             sub _allowable_filters {
295 0     0   0 my $hit = shift;
296 0         0 my $type = shift;
297 0   0     0 $type ||= 'q';
298 0 0       0 unless (grep /^$type$/, qw( h q s ) ) {
299 0         0 warn("Unknown type '$type'; returning ''");
300 0         0 return '';
301             }
302 0 0       0 $type = 'h' if $type eq 's';
303 0         0 my $alg = $hit->algorithm;
304              
305             # pretreat (i.e., kludge it)
306 0 0       0 $alg =~ /^RPS/ and ($alg) = ($alg =~ /\(([^)]+)\)/);
307              
308 0         0 for ($hit->algorithm) {
309 0 0       0 /MEGABLAST/i && do {
310 0         0 return qr/[s]/;
311             };
312 0 0       0 /(.?)BLAST(.?)/i && do {
313 0         0 return $$alg_lookup{$1.$2}{$type};
314             };
315 0 0       0 /(.?)FAST(.?)/ && do {
316 0         0 return $$alg_lookup{$1.$2}{$type};
317             };
318 0         0 do { # unrecognized
319 0         0 last;
320             };
321             }
322 0         0 return;
323             }
324              
325              
326             =head2 _set_attributes
327              
328             Title : _set_attributes
329             Usage : $tiling->_set_attributes()
330             Function: Sets attributes for invocant
331             that depend on algorithm name
332             Returns : True on success
333             Args : none
334             Note : setting based on the configuration table
335             %alg_lookup
336              
337             =cut
338              
339             sub _set_attributes {
340 519     519   445 my $self = shift;
341 519         834 my $alg = $self->hit->algorithm;
342              
343             # pretreat (i.e., kludge it)
344 519 50       890 $alg =~ /^RPS/ and ($alg) = ($alg =~ /\(([^)]+)\)/);
345            
346 519         671 for ($alg) {
347 519 100       767 /MEGABLAST/i && do {
348 4         11 ($self->{_mapping_query},$self->{_mapping_hit}) = (1,1);
349 4         14 ($self->{_def_context_query},$self->{_def_context_hit}) =
350             ('p_','p_');
351 4         12 ($self->{_has_frame_query},$self->{_has_frame_hit}) =
352             (0, 0);
353 4         9 ($self->{_has_strand_query},$self->{_has_strand_hit}) =
354             (1, 1);
355 4         7 last;
356             };
357 515 50       1696 /(.?)BLAST(.?)/i && do {
358             ($self->{_mapping_query},$self->{_mapping_hit}) =
359 515         358 @{$$alg_lookup{$1.$2}{mapping}};
  515         1762  
360             ($self->{_def_context_query},$self->{_def_context_hit}) =
361 515         433 @{$$alg_lookup{$1.$2}{def_context}};
  515         1300  
362             ($self->{_has_frame_query},$self->{_has_frame_hit}) =
363 515         443 @{$$alg_lookup{$1.$2}{has_frame}};
  515         1482  
364             ($self->{_has_strand_query},$self->{_has_strand_hit}) =
365 515         478 @{$$alg_lookup{$1.$2}{has_strand}};
  515         1085  
366 515         589 last;
367             };
368 0 0       0 /(.?)FAST(.?)/ && do {
369             ($self->{_mapping_query},$self->{_mapping_hit}) =
370 0         0 @{$$alg_lookup{$1.$2}{mapping}};
  0         0  
371             ($self->{_def_context_query},$self->{_def_context_hit}) =
372 0         0 @{$$alg_lookup{$1.$2}{def_context}};
  0         0  
373             ($self->{_has_frame_query},$self->{_has_frame_hit}) =
374 0         0 @{$$alg_lookup{$1.$2}{has_frame}};
  0         0  
375             ($self->{_has_strand_query},$self->{_has_strand_hit}) =
376 0         0 @{$$alg_lookup{$1.$2}{has_strand}};
  0         0  
377 0         0 last;
378             };
379 0         0 do { # unrecognized
380 0         0 $self->warn("Unrecognized algorithm '$alg'; defaults may not work");
381 0         0 ($self->{_mapping_query},$self->{_mapping_hit}) = (1,1);
382 0         0 ($self->{_def_context_query},$self->{_def_context_hit}) =
383             ('all','all');
384 0         0 ($self->{_has_frame_query},$self->{_has_frame_hit}) =
385             (0,0);
386 0         0 ($self->{_has_strand_query},$self->{_has_strand_hit}) =
387             (0,0);
388 0         0 return 0;
389 0         0 last;
390             };
391             }
392 519         901 return 1;
393             }
394            
395             sub _mapping_coeff {
396 6568     6568   4861 my $obj = shift;
397 6568         4350 my $type = shift;
398 6568         8548 my %type_i = ( 'query' => 0, 'hit' => 1 );
399 6568 50 33     25916 unless ( ref($obj) && $obj->can('algorithm') ) {
400 0         0 $obj->warn("Object type unrecognized");
401 0         0 return undef;
402             }
403 6568   50     7692 $type ||= 'query';
404 6568 50       26462 unless ( grep(/^$type$/, qw( query hit subject ) ) ) {
405 0         0 $obj->warn("Sequence type unrecognized");
406 0         0 return undef;
407             }
408 6568 50       8238 $type = 'hit' if $type eq 'subject';
409 6568         9586 my $alg = $obj->algorithm;
410              
411             # pretreat (i.e., kludge it)
412 6568 50       8264 $alg =~ /^RPS/ and ($alg) = ($alg =~ /\(([^)]+)\)/);
413            
414 6568         6650 for ($alg) {
415 6568 100       7571 /MEGABLAST/i && do {
416 520         918 return 1;
417             };
418 6048 50       13439 /(.?)BLAST(.?)/i && do {
419 6048         19519 return $$alg_lookup{$1.$2}{'mapping'}[$type_i{$type}];
420             };
421 0 0       0 /(.?)FAST(.?)/ && do {
422 0         0 return $$alg_lookup{$1.$2}{'mapping'}[$type_i{$type}];
423             };
424 0         0 do { # unrecognized
425 0         0 last;
426             };
427             }
428 0         0 return;
429             }
430              
431             # a graphical depiction of a set of intervals
432             sub _ints_as_text {
433 0     0   0 my $ints = shift;
434 0         0 my @ints = @$ints;
435 0         0 my %pos;
436 0         0 for (@ints) {
437 0         0 $pos{$$_[0]}++;
438 0         0 $pos{$$_[1]}++;
439             }
440            
441 0         0 my @pos = sort {$a<=>$b} keys %pos;
  0         0  
442 0         0 @pos = map {sprintf("%03d",$_)} @pos;
  0         0  
443             #header
444 0         0 my $max=0;
445 0 0       0 $max = (length > $max) ? length : $max for (@pos);
446 0         0 for my $j (0..$max-1) {
447 0         0 my $i = $max-1-$j;
448 0 0       0 my @line = map { substr($_, $j, 1) || '0' } @pos;
  0         0  
449 0         0 print join('', @line), "\n";
450             }
451 0         0 print '-' x @pos, "\n";
452 0         0 undef %pos;
453 0         0 @pos{map {sprintf("%d",$_)} @pos} = (0..@pos);
  0         0  
454 0         0 foreach (@ints) {
455 0         0 print ' ' x $pos{$$_[0]}, '[', ' ' x ($pos{$$_[1]}-$pos{$$_[0]}-1), ']', ' ' x (@pos-$pos{$$_[1]}), "\n";
456             }
457             }
458              
459              
460              
461             =head2 containing_hsps()
462              
463             Title : containing_hsps
464             Usage : @hsps = containing_hsps($interval, @hsps_to_search)
465             Function: Return a list of hsps whose coordinates completely contain the
466             given $interval
467             Returns : Array of HSP objects
468             Args : $interval : [$int1, $int2],
469             array of HSP objects
470              
471             =cut
472             # could be more efficient if hsps are assumed ordered...
473             sub containing_hsps {
474 12     12 1 12 my $intvl = shift;
475 12         23 my @hsps = @_;
476 12         11 my @ret;
477 12         16 my ($beg, $end) = @$intvl;
478 12         17 foreach my $hsp (@hsps) {
479 96         162 my ($start, $stop) = ($hsp->start, $hsp->end);
480 96 100 100     263 push @ret, $hsp if ( $start <= $beg and $end <= $stop );
481             }
482 12         43 return @ret;
483             }
484              
485              
486              
487             =head2 covering_groups()
488              
489             Title : covering_groups
490             Usage :
491             Function: divide a list of **ordered,disjoint** intervals (as from a
492             coverage map) into a set of disjoint covering groups
493             Returns : array of arrayrefs, each arrayref a covering set of
494             intervals
495             Args : array of intervals
496              
497             =cut
498              
499             sub covering_groups {
500 1     1 1 2 my @intervals = @_;
501 1 50       3 return unless @intervals;
502 1         2 my (@groups, $grp);
503 1         1 push @{$groups[0]}, shift @intervals;
  1         3  
504 1         1 $grp = $groups[0];
505 1         4 for (my $intvl = shift @intervals; @intervals; $intvl = shift @intervals) {
506 11 100       17 if ( $intvl->[0] - $grp->[-1][1] == 1 ) { # intervals are direcly adjacent
507 6         11 push @$grp, $intvl;
508             }
509             else {
510 5         6 $grp = [$intvl];
511 5         7 push @groups, $grp;
512             }
513             }
514 1         4 return @groups;
515             }
516              
517             1;
518             # need our own subsequencer for hsps.
519              
520             package Bio::Search::HSP::HSPI;
521              
522 1     1   6 use strict;
  1         1  
  1         21  
523 1     1   3 use warnings;
  1         1  
  1         28  
524              
525             =head2 matches_MT
526              
527             Title : matches_MT
528             Usage : $hsp->matches($type, $action, $start, $end)
529             Purpose : Get the total number of identical or conserved matches
530             in the query or sbjct sequence for the given HSP. Optionally can
531             report data within a defined interval along the seq.
532             Returns : scalar int
533             Args :
534             Comments : Relies on seq_str('match') to get the string of alignment symbols
535             between the query and sbjct lines which are used for determining
536             the number of identical and conservative matches.
537             Note : Modeled on Bio::Search::HSP::HSPI::matches
538              
539             =cut
540              
541             sub matches_MT {
542 1     1   4 use integer;
  1         1  
  1         6  
543 2664     2664 0 4166 my( $self, @args ) = @_;
544 2664         6719 my($type, $action, $beg, $end) = $self->_rearrange( [qw(TYPE ACTION START END)], @args);
545 2664         4647 my @actions = qw( identities conserved searchutils );
546            
547             # prep $type
548 2664 50       3709 $self->throw("Type not specified") if !defined $type;
549 2664 50       13584 $self->throw("Type '$type' unrecognized") unless grep(/^$type$/,qw(query hit subject));
550 2664 50       3803 $type = 'hit' if $type eq 'subject';
551              
552             # prep $action
553 2664 50       3169 $self->throw("Action not specified") if !defined $action;
554 2664 50       16987 $self->throw("Action '$action' unrecognized") unless grep(/^$action$/, @actions);
555              
556 2664         1890 my ($len_id, $len_cons);
557 2664         3520 my $c = Bio::Search::Tiling::MapTileUtils::_mapping_coeff($self, $type);
558 2664 50 66     16517 if ((defined $beg && !defined $end) || (!defined $beg && defined $end)) {
    100 66        
      33        
      66        
559 0         0 $self->throw("Both start and end are required");
560             }
561             elsif ( (!defined($beg) && !defined($end)) || !$self->seq_str('match') ) {
562             ## Get data for the whole alignment.
563             # the reported values x mapping
564 1688 100       3007 $self->debug("Sequence data not present in report; returning data for entire HSP") unless $self->seq_str('match');
565 1688         3048 ($len_id, $len_cons) = map { $c*$_ } ($self->num_identical, $self->num_conserved);
  3376         4341  
566 1688         2321 for ($action) {
567 1688 100       2730 $_ eq 'identities' && do {
568 450         1140 return $len_id;
569             };
570 1238 100       1451 $_ eq 'conserved' && do {
571 450         1075 return $len_cons;
572             };
573 788 50       1036 $_ eq 'searchutils' && do {
574 788         1895 return ($len_id, $len_cons);
575             };
576 0         0 do {
577 0         0 $self->throw("What are YOU doing here?");
578             };
579             }
580             }
581             else {
582             ## Get the substring representing the desired sub-section of aln.
583 976         1579 my($start,$stop) = $self->range($type);
584 976 50 33     2834 if ( $beg < $start or $stop < $end ) {
585 0         0 $self->throw("Start/stop out of range [$start, $stop]");
586             }
587              
588             # handle gaps
589 976         1537 my $match_str = $self->seq_str('match');
590 976 100       1525 if ($self->gaps) {
591             # strip the homology string of gap positions relative
592             # to the target type
593 764         1051 $match_str = $self->seq_str('match');
594 764         1070 my $tgt = $self->seq_str($type);
595 764         1238 my $encode = $match_str ^ $tgt;
596 764         694 my $zap = '-'^' ';
597 764         3114 $encode =~ s/$zap//g;
598 764         2168 $tgt =~ s/-//g;
599 764         1020 $match_str = $tgt ^ $encode;
600             # match string is now the correct length for substr'ing below,
601             # given that start and end are gapless coordinates in the
602             # blast report
603             }
604              
605 976         812 my $seq = "";
606 976         1431 $seq = substr( $match_str,
607             int( ($beg-$start)/Bio::Search::Tiling::MapTileUtils::_mapping_coeff($self, $type) ),
608             int( 1+($end-$beg)/Bio::Search::Tiling::MapTileUtils::_mapping_coeff($self, $type) )
609             );
610              
611 976 50       1414 if(!CORE::length $seq) {
612 0         0 $self->throw("Undefined sub-sequence ($beg,$end). Valid range = $start - $stop");
613             }
614              
615 976         6368 $seq =~ s/ //g; # remove space (no info).
616 976         1340 $len_cons = (CORE::length $seq)*(Bio::Search::Tiling::MapTileUtils::_mapping_coeff($self,$type));
617 976         3278 $seq =~ s/\+//g; # remove '+' characters (conservative substitutions)
618 976         1493 $len_id = (CORE::length $seq)*(Bio::Search::Tiling::MapTileUtils::_mapping_coeff($self,$type));
619 976         1013 for ($action) {
620 976 50       1231 $_ eq 'identities' && do {
621 0         0 return $len_id;
622             };
623 976 50       1127 $_ eq 'conserved' && do {
624 0         0 return $len_cons;
625             };
626 976 50       1237 $_ eq 'searchutils' && do {
627 976         2533 return ($len_id, $len_cons);
628             };
629 0         0 do {
630 0         0 $self->throw("What are YOU doing here?");
631             };
632             }
633             }
634             }
635              
636             1;
637              
638             package Bio::LocatableSeq;
639 1     1   413 use strict;
  1         1  
  1         15  
640 1     1   3 use warnings;
  1         1  
  1         221  
641              
642             # mixin the Bio::FeatureHolderI implementation of
643             # Bio::Seq -- for get_tiled_aln
644              
645             =head2 get_SeqFeatures
646              
647             Title : get_SeqFeatures
648             Usage :
649             Function: Get the feature objects held by this feature holder.
650              
651             Features which are not top-level are subfeatures of one or
652             more of the returned feature objects, which means that you
653             must traverse the subfeature arrays of each top-level
654             feature object in order to traverse all features associated
655             with this sequence.
656              
657             Top-level features can be obtained by tag, specified in
658             the argument.
659              
660             Use get_all_SeqFeatures() if you want the feature tree
661             flattened into one single array.
662              
663             Example :
664             Returns : an array of Bio::SeqFeatureI implementing objects
665             Args : [optional] scalar string (feature tag)
666              
667              
668             =cut
669              
670             sub get_SeqFeatures{
671 12     12 0 2755 my $self = shift;
672 12         12 my $tag = shift;
673              
674 12 50       28 if( !defined $self->{'_as_feat'} ) {
675 0         0 $self->{'_as_feat'} = [];
676             }
677 12 50       15 if ($tag) {
678 0 0       0 return map { $_->primary_tag eq $tag ? $_ : () } @{$self->{'_as_feat'}};
  0         0  
  0         0  
679             }
680             else {
681 12         11 return @{$self->{'_as_feat'}};
  12         41  
682             }
683             }
684              
685             =head2 feature_count
686              
687             Title : feature_count
688             Usage : $seq->feature_count()
689             Function: Return the number of SeqFeatures attached to a sequence
690             Returns : integer representing the number of SeqFeatures
691             Args : None
692              
693              
694             =cut
695              
696             sub feature_count {
697 0     0 0 0 my ($self) = @_;
698 0 0       0 if (defined($self->{'_as_feat'})) {
699 0         0 return ($#{$self->{'_as_feat'}} + 1);
  0         0  
700             } else {
701 0         0 return 0;
702             }
703             }
704              
705             =head2 add_SeqFeature
706              
707             Title : add_SeqFeature
708             Usage : $seq->add_SeqFeature($feat);
709             $seq->add_SeqFeature(@feat);
710             Function: Adds the given feature object (or each of an array of feature
711             objects to the feature array of this
712             sequence. The object passed is required to implement the
713             Bio::SeqFeatureI interface.
714             Returns : 1 on success
715             Args : A Bio::SeqFeatureI implementing object, or an array of such objects.
716              
717              
718             =cut
719              
720             sub add_SeqFeature {
721 12     12 0 15 my ($self,@feat) = @_;
722 12 50       36 $self->{'_as_feat'} = [] unless $self->{'_as_feat'};
723 12         16 foreach my $feat ( @feat ) {
724 24 50       63 if( !$feat->isa("Bio::SeqFeatureI") ) {
725 0         0 $self->throw("$feat is not a SeqFeatureI and that's what we expect...");
726             }
727 24         41 $feat->attach_seq($self);
728 24         20 push(@{$self->{'_as_feat'}},$feat);
  24         35  
729             }
730 12         18 return 1;
731             }
732              
733             =head2 remove_SeqFeatures
734              
735             Title : remove_SeqFeatures
736             Usage : $seq->remove_SeqFeatures();
737             Function: Flushes all attached SeqFeatureI objects.
738              
739             To remove individual feature objects, delete those from the returned
740             array and re-add the rest.
741             Example :
742             Returns : The array of Bio::SeqFeatureI objects removed from this seq.
743             Args : None
744              
745              
746             =cut
747              
748             sub remove_SeqFeatures {
749 0     0 0   my $self = shift;
750              
751 0 0         return () unless $self->{'_as_feat'};
752 0           my @feats = @{$self->{'_as_feat'}};
  0            
753 0           $self->{'_as_feat'} = [];
754 0           return @feats;
755             }
756              
757             1;