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         22  
4 1     1   3 use warnings;
  1         2  
  1         16  
5 1     1   3 use Exporter;
  1         1  
  1         69  
6              
7             BEGIN {
8 1     1   8 our @ISA = qw( Exporter );
9 1         1527 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 402 return unless $_[0]; # no input
57 219         177 my $n = scalar @{$_[0]};
  219         288  
58 219         202 my %input;
59 219         155 @input{(0..$n-1)} = @{$_[0]};
  219         935  
60 219         772 my @active = (0..$n-1);
61 219         206 my @hold;
62             my @tiled_ints;
63 0         0 my @ret;
64 219         511 while (@active) {
65 432         609 my $tgt = $input{my $tgt_i = shift @active};
66 432         491 push @tiled_ints, $tgt_i;
67 432         290 my $tgt_not_disjoint = 1;
68 432         639 while ($tgt_not_disjoint) {
69 499         366 $tgt_not_disjoint = 0;
70 499         812 while (my $try_i = shift @active) {
71 971         715 my $try = $input{$try_i};
72 971 100       874 if ( !are_disjoint($tgt, $try) ) {
73 104         155 $tgt = min_covering_interval($tgt,$try);
74 104         132 push @tiled_ints, $try_i;
75 104         175 $tgt_not_disjoint = 1;
76             }
77             else {
78 867         1494 push @hold, $try_i;
79             }
80             }
81 499 100       676 if (!$tgt_not_disjoint) {
82 432         711 push @ret, [ $tgt => [@tiled_ints] ];
83 432         445 @tiled_ints = ();
84             }
85 499         657 @active = @hold;
86 499         1153 @hold = ();
87             }
88             }
89 219         630 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 671 return unless $_[0]; # no input
117 432         379 my @ints = @{$_[0]};
  432         482  
118 432         356 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         499 foreach (@ints) {
126 536         1064 $flat{$$_[0]-1}++;
127 536         769 $flat{$$_[0]}++;
128 536         1797 $flat{$$_[1]}++;
129 536         1325 $flat{$$_[1]+1}++;
130             }
131             # sort, create singletons if nec.
132 432         311 my @a;
133 432         1228 @a = sort {$a<=>$b} keys %flat;
  3314         3193  
134             # throw out first and last (meeting a boundary condition)
135 432         527 shift @a; pop @a;
  432         378  
136             # look for singletons
137 432         607 @flat = (shift @a, shift @a);
138 432 100       810 if ( $flat[1]-$flat[0] == 1 ) {
139 1         2 @flat = ($flat[0],$flat[0], $flat[1]);
140             }
141 432         721 while (my $a = shift @a) {
142 394 100       496 if ($a-$flat[-2]==2) {
143 17         25 push @flat, $flat[-1]; # create singleton interval
144             }
145 394         574 push @flat, $a;
146             }
147 432 50 33     806 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         376 my @decomp;
152 432         721 while (my $a = shift @flat) {
153 638         1364 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         418 my @coverage;
159 432         776 foreach my $i (0..$#decomp) {
160 638         710 foreach my $j (0..$#ints) {
161 1316 100       1398 unless (are_disjoint($decomp[$i], $ints[$j])) {
162 846 100       1029 if (defined $coverage[$i]) {
163 208         168 push @{$coverage[$i]}, $j;
  208         305  
164             }
165             else {
166 638         1161 $coverage[$i] = [$j];
167             }
168             }
169             }
170             }
171 432         640 return map { [$decomp[$_] => $coverage[$_]] } (0..$#decomp);
  638         1878  
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 1525 my ($int1, $int2) = @_;
186 2287 100 100     6376 return 1 if ( $$int1[1] < $$int2[0] ) || ( $$int2[1] < $$int1[0]);
187 950         1361 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 118 my ($int1, $int2) = @_;
202 104         551 my @a = sort {$a <=> $b} (@$int1, @$int2);
  516         471  
203 104         212 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 269 my $type = shift;
220 219         204 my @hsps;
221 219 50       2701 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       379 $type = 'hit' if $type eq 'subject';
229 219         335 push @hsps, @_;
230 219         210 my @ret;
231 219         326 foreach (@hsps) {
232             # push @ret, [ $_->start($type), $_->end($type)];
233 536         1122 push @ret, [ $_->range($type) ];
234             }
235 219         591 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   438 my $self = shift;
341 519         799 my $alg = $self->hit->algorithm;
342              
343             # pretreat (i.e., kludge it)
344 519 50       971 $alg =~ /^RPS/ and ($alg) = ($alg =~ /\(([^)]+)\)/);
345            
346 519         788 for ($alg) {
347 519 100       833 /MEGABLAST/i && do {
348 4         11 ($self->{_mapping_query},$self->{_mapping_hit}) = (1,1);
349 4         11 ($self->{_def_context_query},$self->{_def_context_hit}) =
350             ('p_','p_');
351 4         11 ($self->{_has_frame_query},$self->{_has_frame_hit}) =
352             (0, 0);
353 4         8 ($self->{_has_strand_query},$self->{_has_strand_hit}) =
354             (1, 1);
355 4         7 last;
356             };
357 515 50       1864 /(.?)BLAST(.?)/i && do {
358             ($self->{_mapping_query},$self->{_mapping_hit}) =
359 515         489 @{$$alg_lookup{$1.$2}{mapping}};
  515         1905  
360             ($self->{_def_context_query},$self->{_def_context_hit}) =
361 515         479 @{$$alg_lookup{$1.$2}{def_context}};
  515         1311  
362             ($self->{_has_frame_query},$self->{_has_frame_hit}) =
363 515         431 @{$$alg_lookup{$1.$2}{has_frame}};
  515         1454  
364             ($self->{_has_strand_query},$self->{_has_strand_hit}) =
365 515         421 @{$$alg_lookup{$1.$2}{has_strand}};
  515         1210  
366 515         773 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         658 return 1;
393             }
394            
395             sub _mapping_coeff {
396 6568     6568   4965 my $obj = shift;
397 6568         5312 my $type = shift;
398 6568         9331 my %type_i = ( 'query' => 0, 'hit' => 1 );
399 6568 50 33     26624 unless ( ref($obj) && $obj->can('algorithm') ) {
400 0         0 $obj->warn("Object type unrecognized");
401 0         0 return undef;
402             }
403 6568   50     7787 $type ||= 'query';
404 6568 50       27289 unless ( grep(/^$type$/, qw( query hit subject ) ) ) {
405 0         0 $obj->warn("Sequence type unrecognized");
406 0         0 return undef;
407             }
408 6568 50       7914 $type = 'hit' if $type eq 'subject';
409 6568         9700 my $alg = $obj->algorithm;
410              
411             # pretreat (i.e., kludge it)
412 6568 50       8664 $alg =~ /^RPS/ and ($alg) = ($alg =~ /\(([^)]+)\)/);
413            
414 6568         7250 for ($alg) {
415 6568 100       8099 /MEGABLAST/i && do {
416 520         976 return 1;
417             };
418 6048 50       13053 /(.?)BLAST(.?)/i && do {
419 6048         19990 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 11 my $intvl = shift;
475 12         25 my @hsps = @_;
476 12         7 my @ret;
477 12         20 my ($beg, $end) = @$intvl;
478 12         16 foreach my $hsp (@hsps) {
479 96         187 my ($start, $stop) = ($hsp->start, $hsp->end);
480 96 100 100     288 push @ret, $hsp if ( $start <= $beg and $end <= $stop );
481             }
482 12         40 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       5 return unless @intervals;
502 1         2 my (@groups, $grp);
503 1         1 push @{$groups[0]}, shift @intervals;
  1         3  
504 1         2 $grp = $groups[0];
505 1         5 for (my $intvl = shift @intervals; @intervals; $intvl = shift @intervals) {
506 11 100       13 if ( $intvl->[0] - $grp->[-1][1] == 1 ) { # intervals are direcly adjacent
507 6         12 push @$grp, $intvl;
508             }
509             else {
510 5         7 $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   5 use strict;
  1         2  
  1         14  
523 1     1   3 use warnings;
  1         1  
  1         27  
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         8  
543 2664     2664 0 3853 my( $self, @args ) = @_;
544 2664         7801 my($type, $action, $beg, $end) = $self->_rearrange( [qw(TYPE ACTION START END)], @args);
545 2664         4848 my @actions = qw( identities conserved searchutils );
546            
547             # prep $type
548 2664 50       3614 $self->throw("Type not specified") if !defined $type;
549 2664 50       14335 $self->throw("Type '$type' unrecognized") unless grep(/^$type$/,qw(query hit subject));
550 2664 50       3624 $type = 'hit' if $type eq 'subject';
551              
552             # prep $action
553 2664 50       3535 $self->throw("Action not specified") if !defined $action;
554 2664 50       17753 $self->throw("Action '$action' unrecognized") unless grep(/^$action$/, @actions);
555              
556 2664         1827 my ($len_id, $len_cons);
557 2664         3833 my $c = Bio::Search::Tiling::MapTileUtils::_mapping_coeff($self, $type);
558 2664 50 66     17414 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       3239 $self->debug("Sequence data not present in report; returning data for entire HSP") unless $self->seq_str('match');
565 1688         3167 ($len_id, $len_cons) = map { $c*$_ } ($self->num_identical, $self->num_conserved);
  3376         4342  
566 1688         2419 for ($action) {
567 1688 100       2397 $_ eq 'identities' && do {
568 450         1236 return $len_id;
569             };
570 1238 100       1550 $_ eq 'conserved' && do {
571 450         1098 return $len_cons;
572             };
573 788 50       1015 $_ eq 'searchutils' && do {
574 788         1873 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         1922 my($start,$stop) = $self->range($type);
584 976 50 33     2845 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         1633 my $match_str = $self->seq_str('match');
590 976 100       1606 if ($self->gaps) {
591             # strip the homology string of gap positions relative
592             # to the target type
593 764         1140 $match_str = $self->seq_str('match');
594 764         1166 my $tgt = $self->seq_str($type);
595 764         1394 my $encode = $match_str ^ $tgt;
596 764         603 my $zap = '-'^' ';
597 764         3440 $encode =~ s/$zap//g;
598 764         2151 $tgt =~ s/-//g;
599 764         995 $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         854 my $seq = "";
606 976         1403 $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       1654 if(!CORE::length $seq) {
612 0         0 $self->throw("Undefined sub-sequence ($beg,$end). Valid range = $start - $stop");
613             }
614              
615 976         6697 $seq =~ s/ //g; # remove space (no info).
616 976         1202 $len_cons = (CORE::length $seq)*(Bio::Search::Tiling::MapTileUtils::_mapping_coeff($self,$type));
617 976         3327 $seq =~ s/\+//g; # remove '+' characters (conservative substitutions)
618 976         1189 $len_id = (CORE::length $seq)*(Bio::Search::Tiling::MapTileUtils::_mapping_coeff($self,$type));
619 976         1060 for ($action) {
620 976 50       1322 $_ eq 'identities' && do {
621 0         0 return $len_id;
622             };
623 976 50       1241 $_ eq 'conserved' && do {
624 0         0 return $len_cons;
625             };
626 976 50       1375 $_ eq 'searchutils' && do {
627 976         2731 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   407 use strict;
  1         1  
  1         15  
640 1     1   3 use warnings;
  1         1  
  1         251  
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 2845 my $self = shift;
672 12         12 my $tag = shift;
673              
674 12 50       29 if( !defined $self->{'_as_feat'} ) {
675 0         0 $self->{'_as_feat'} = [];
676             }
677 12 50       18 if ($tag) {
678 0 0       0 return map { $_->primary_tag eq $tag ? $_ : () } @{$self->{'_as_feat'}};
  0         0  
  0         0  
679             }
680             else {
681 12         8 return @{$self->{'_as_feat'}};
  12         48  
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       38 $self->{'_as_feat'} = [] unless $self->{'_as_feat'};
723 12         18 foreach my $feat ( @feat ) {
724 24 50       80 if( !$feat->isa("Bio::SeqFeatureI") ) {
725 0         0 $self->throw("$feat is not a SeqFeatureI and that's what we expect...");
726             }
727 24         39 $feat->attach_seq($self);
728 24         16 push(@{$self->{'_as_feat'}},$feat);
  24         690  
729             }
730 12         16 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;