File Coverage

blib/lib/Bio/Coordinate/GeneMapper.pm
Criterion Covered Total %
statement 285 393 72.5
branch 93 160 58.1
condition 63 122 51.6
subroutine 31 34 91.1
pod 15 15 100.0
total 487 724 67.2


line stmt bran cond sub pod time code
1             package Bio::Coordinate::GeneMapper;
2             our $AUTHORITY = 'cpan:BIOPERLML';
3             $Bio::Coordinate::GeneMapper::VERSION = '1.007001';
4 1     1   1042 use utf8;
  1         2  
  1         4  
5 1     1   27 use strict;
  1         1  
  1         16  
6 1     1   4 use warnings;
  1         0  
  1         21  
7 1     1   4 use Bio::Coordinate::Result;
  1         1  
  1         15  
8 1     1   4 use Bio::Location::Simple;
  1         1  
  1         15  
9 1     1   458 use Bio::Coordinate::Graph;
  1         2  
  1         29  
10 1     1   432 use Bio::Coordinate::Collection;
  1         1  
  1         36  
11 1     1   6 use Bio::Coordinate::Pair;
  1         1  
  1         16  
12 1     1   4 use Bio::Coordinate::ExtrapolatingPair;
  1         1  
  1         21  
13 1     1   4 use parent qw(Bio::Root::Root Bio::Coordinate::MapperI);
  1         1  
  1         4  
14              
15             # ABSTRACT: Transformations between gene related coordinate systems.
16             # AUTHOR: Heikki Lehvaslaiho
17             # OWNER: Heikki Lehvaslaiho
18             # LICENSE: Perl_5
19              
20              
21             # first set internal values for all translation tables
22              
23             our %COORDINATE_SYSTEMS = (
24             peptide => 10,
25             propeptide => 9,
26             frame => 8,
27             cds => 7,
28             negative_intron => 6,
29             intron => 5,
30             exon => 4,
31             inex => 3,
32             gene => 2,
33             chr => 1,
34             );
35              
36             our %COORDINATE_INTS = (
37             10 => 'peptide',
38             9 => 'propeptide',
39             8 => 'frame',
40             7 => 'cds',
41             6 => 'negative_intron',
42             5 => 'intron',
43             4 => 'exon',
44             3 => 'inex',
45             2 => 'gene',
46             1 => 'chr'
47             );
48              
49             our $TRANSLATION = $COORDINATE_SYSTEMS{'cds'}. "-". $COORDINATE_SYSTEMS{'propeptide'};
50              
51             our $DAG = {
52             10 => [],
53             9 => [10],
54             8 => [],
55             7 => [8, 9],
56             6 => [],
57             5 => [6],
58             4 => [7],
59             3 => [4, 5],
60             2 => [3, 4, 5, 7],
61             1 => [2],
62             };
63              
64             our $NOZERO_VALUES = {
65             0 => 0,
66             'in' => 1,
67             'out' => 2,
68             'in&out' => 3,
69             };
70              
71             our $NOZERO_KEYS = {
72             0 => 0,
73             1 => 'in',
74             2 => 'out',
75             3 => 'in&out',
76             };
77              
78              
79             sub new {
80 7     7 1 9554 my($class,@args) = @_;
81 7         30 my $self = $class->SUPER::new(@args);
82              
83             # prime the graph
84 7         123 my $graph = Bio::Coordinate::Graph->new();
85 7         23 $graph->hash_of_arrays($DAG);
86 7         18 $self->graph($graph);
87              
88 7         22 my($in, $out, $peptide_offset, $exons,
89             $cds, $nozero, $strict) =
90             $self->_rearrange([qw(IN
91             OUT
92             PEPTIDE_OFFSET
93             EXONS
94             CDS
95             NOZERO
96             STRICT
97             )],
98             @args);
99              
100             # direction of mapping when going chr to protein
101 7         128 $self->{_direction} = 1;
102              
103 7 100       21 $in && $self->in($in);
104 7 100       18 $out && $self->out($out);
105 7 100       10 $cds && $self->cds($cds);
106 7 50 66     22 $exons && ref($exons) =~ /ARRAY/i && $self->exons(@$exons);
107 7 100       13 $peptide_offset && $self->peptide_offset($peptide_offset);
108 7 50       15 $nozero && $self->nozero($nozero);
109 7 50       9 $strict && $self->strict($strict);
110              
111 7         27 return $self; # success - we hope!
112             }
113              
114              
115             sub in {
116 16     16 1 5654 my ($self,$value) = @_;
117 16 50       34 if( defined $value) {
118             $self->throw("Not a valid input coordinate system name [$value]\n".
119             "Valid values are ". join(", ", keys %COORDINATE_SYSTEMS ))
120 16 50       60 unless defined $COORDINATE_SYSTEMS{$value};
121              
122 16         26 $self->{'_in'} = $COORDINATE_SYSTEMS{$value};
123             }
124 16         35 return $COORDINATE_INTS{ $self->{'_in'} };
125             }
126              
127              
128             sub out {
129 22     22 1 6679 my ($self,$value) = @_;
130 22 50       43 if( defined $value) {
131             $self->throw("Not a valid input coordinate system name [$value]\n".
132             "Valid values are ". join(", ", keys %COORDINATE_SYSTEMS ))
133 22 50       45 unless defined $COORDINATE_SYSTEMS{$value};
134              
135 22         32 $self->{'_out'} = $COORDINATE_SYSTEMS{$value};
136             }
137 22         44 return $COORDINATE_INTS{ $self->{'_out'} };
138             }
139              
140              
141             sub strict {
142 2     2 1 3 my ($self,$value) = @_;
143 2 50       5 if( defined $value) {
144 0 0       0 $value ? ( $self->{'_strict'} = 1 ) : ( $self->{'_strict'} = 0 );
145             ## update in each mapper !!
146             }
147 2   50     17 return $self->{'_strict'} || 0 ;
148             }
149              
150              
151             sub nozero {
152 2     2 1 2029 my ($self,$value) = @_;
153              
154 2 50       7 if (defined $value) {
155             $self->throw("Not a valid value for nozero [$value]\n".
156 0         0 "Valid values are ". join(", ", keys %{$NOZERO_VALUES} ))
157 2 50       7 unless defined $NOZERO_VALUES->{$value};
158 2         3 $self->{'_nozero'} = $NOZERO_VALUES->{$value};
159             }
160              
161 2   100     8 my $res = $self->{'_nozero'} || 0;
162 2         8 return $NOZERO_KEYS->{$res};
163             }
164              
165              
166             sub graph {
167 7     7 1 9 my ($self,$value) = @_;
168 7 50       15 if( defined $value) {
169 7 50       23 $self->throw("Not a valid graph [$value]\n")
170             unless $value->isa('Bio::Coordinate::Graph');
171 7         11 $self->{'_graph'} = $value;
172             }
173 7         8 return $self->{'_graph'};
174             }
175              
176              
177             sub peptide {
178 0     0 1 0 my ($self, $value) = @_;
179 0 0       0 if( defined $value) {
180 0 0       0 $self->throw("I need a Bio::LocationI, not [". $value. "]")
181             unless $value->isa('Bio::LocationI');
182              
183 0 0       0 $self->throw("Peptide start not defined")
184             unless defined $value->start;
185 0         0 $self->{'_peptide_offset'} = $value->start - 1;
186              
187 0 0       0 $self->throw("Peptide end not defined")
188             unless defined $value->end;
189 0         0 $self->{'_peptide_length'} = $value->end - $self->{'_peptide_offset'};
190              
191             my $a = $self->_create_pair
192             ('propeptide', 'peptide', $self->strict,
193 0         0 $self->{'_peptide_offset'}, $self->{'_peptide_length'} );
194 0         0 my $mapper = $COORDINATE_SYSTEMS{'propeptide'}. "-". $COORDINATE_SYSTEMS{'peptide'};
195 0         0 $self->{'_mappers'}->{$mapper} = $a;
196             }
197             return Bio::Location::Simple->new
198             (-seq_id => 'propeptide',
199             -start => $self->{'_peptide_offset'} + 1 ,
200 0         0 -end => $self->{'_peptide_length'} + $self->{'_peptide_offset'},
201             -strand => 1,
202             -verbose => $self->verbose,
203             );
204             }
205              
206              
207             sub peptide_offset {
208 2     2 1 4 my ($self,$offset, $len) = @_;
209 2 50       6 if( defined $offset) {
210 2 50       12 $self->throw("I need an integer, not [$offset]")
211             unless $offset =~ /^[+-]?\d+$/;
212 2         5 $self->{'_peptide_offset'} = $offset;
213              
214 2 50       5 if (defined $len) {
215 0 0       0 $self->throw("I need an integer, not [$len]")
216             unless $len =~ /^[+-]?\d+$/;
217 0         0 $self->{'_peptide_length'} = $len;
218             }
219              
220             my $a = $self->_create_pair
221 2         7 ('propeptide', 'peptide', $self->strict, $offset, $self->{'_peptide_length'} );
222 2         7 my $mapper = $COORDINATE_SYSTEMS{'propeptide'}. "-". $COORDINATE_SYSTEMS{'peptide'};
223 2         5 $self->{'_mappers'}->{$mapper} = $a;
224             }
225 2   50     8 return $self->{'_peptide_offset'} || 0;
226             }
227              
228              
229             sub peptide_length {
230 0     0 1 0 my ($self, $len) = @_;
231 0 0       0 if( defined $len) {
232 0 0 0     0 $self->throw("I need an integer, not [$len]")
233             if defined $len && $len !~ /^[+-]?\d+$/;
234 0         0 $self->{'_peptide_length'} = $len;
235             }
236 0         0 return $self->{'_peptide_length'};
237             }
238              
239              
240             sub exons {
241 5     5 1 3812 my ($self,@value) = @_;
242 5         15 my $cds_mapper = $COORDINATE_SYSTEMS{'gene'}. "-". $COORDINATE_SYSTEMS{'cds'};
243             my $inex_mapper =
244 5         13 $COORDINATE_SYSTEMS{'gene'}. "-". $COORDINATE_SYSTEMS{'inex'};
245             my $exon_mapper =
246 5         9 $COORDINATE_SYSTEMS{'gene'}. "-". $COORDINATE_SYSTEMS{'exon'};
247             my $intron_mapper =
248 5         10 $COORDINATE_SYSTEMS{'gene'}. "-". $COORDINATE_SYSTEMS{'intron'};
249             my $negative_intron_mapper =
250 5         10 $COORDINATE_SYSTEMS{'intron'}. "-". $COORDINATE_SYSTEMS{'negative_intron'};
251 5         8 my $exon_cds_mapper = $COORDINATE_SYSTEMS{'exon'}. "-". $COORDINATE_SYSTEMS{'cds'};
252              
253 5 50       12 if(@value) {
254 5 50 33     56 if (ref($value[0]) &&
      33        
255             $value[0]->isa('Bio::SeqFeatureI') and
256             $value[0]->location->isa('Bio::Location::SplitLocationI')) {
257 0         0 @value = $value[0]->location->each_Location;
258             } else {
259 5 50       15 $self->throw("I need an array , not [@value]")
260             unless ref \@value eq 'ARRAY';
261 5 50 33     30 $self->throw("I need a reference to an array of Bio::LocationIs, not to [".
262             $value[0]. "]")
263             unless ref $value[0] and $value[0]->isa('Bio::LocationI');
264             }
265              
266             #
267             # sort the input array
268             #
269             # and if the used has not defined CDS assume it is the complete exonic range
270 5 100 66     15 if (defined $value[0]->strand &&
271             $value[0]->strand == - 1) { #reverse strand
272 6         10 @value = map { $_->[0] }
273 5         27 sort { $b->[1] <=> $a->[1] }
274 2         34 map { [ $_, $_->start] }
  6         47  
275             @value;
276              
277 2 50       6 unless ($self->cds) {
278 0         0 $self->cds(Bio::Location::Simple->new
279             (-start => $value[-1]->start,
280             -end => $value[0]->end,
281             -strand => $value[0]->strand,
282             -seq_id => $value[0]->seq_id,
283             -verbose => $self->verbose,
284             )
285             );
286             }
287             } else { # undef or forward strand
288 9         13 @value = map { $_->[0] }
289 9         44 sort { $a->[1] <=> $b->[1] }
290 3         53 map { [ $_, $_->start] }
  9         82  
291             @value;
292 3 50       10 unless ($self->cds) {
293 0         0 $self->cds(Bio::Location::Simple->new
294             (-start => $value[0]->start,
295             -end => $value[-1]->end,
296             -strand => $value[0]->strand,
297             -seq_id => $value[0]->seq_id,
298             -verbose => $self->verbose,
299             )
300             );
301             }
302              
303             }
304              
305 5         11 $self->{'_chr_exons'} = \@value;
306              
307             # transform exons from chromosome to gene coordinates
308             # but only if gene coordinate system has been set
309 5         5 my @exons ;
310             #my $gene_mapper = $self->$COORDINATE_SYSTEMS{'chr'}. "-". $COORDINATE_SYSTEMS{'gene'};
311 5         7 my $gene_mapper = "1-2";
312 5 50       11 if (defined $self->{'_mappers'}->{$gene_mapper} ) {
313              
314 5         6 my $tmp_in = $self->{'_in'};
315 5         5 my $tmp_out = $self->{'_out'};
316 5         14 my $tmp_verb = $self->verbose;
317 5         35 $self->verbose(0);
318              
319 5         36 $self->in('chr');
320 5         7 $self->out('gene');
321 5         14 @exons = map {$self->map($_) } @value;
  15         22  
322              
323 5         7 $self->{'_in'} = ($tmp_in);
324 5         6 $self->{'_out'} = ($tmp_out);
325 5         11 $self->verbose($tmp_verb);
326             } else {
327 0         0 @exons = @value;
328             }
329              
330 5         49 my $cds_map = Bio::Coordinate::Collection->new;
331 5         11 my $inex_map = Bio::Coordinate::Collection->new;
332 5         11 my $exon_map = Bio::Coordinate::Collection->new;
333 5         11 my $exon_cds_map = Bio::Coordinate::Collection->new;
334 5         11 my $intron_map = Bio::Coordinate::Collection->new;
335 5         11 my $negative_intron_map = Bio::Coordinate::Collection->new;
336              
337 5         6 my $tr_end = 0;
338 5         6 my $coffset;
339             my $exon_counter;
340 0         0 my $prev_exon_end;
341              
342 5         6 for my $exon ( @exons ) {
343 15         295 $exon_counter++;
344              
345             #
346             # gene -> cds
347             #
348              
349 15         50 my $match1 = Bio::Location::Simple->new
350             (-seq_id =>'gene' ,
351             -start => $exon->start,
352             -end => $exon->end,
353             -strand => 1,
354             -verbose=> $self->verbose);
355              
356 15         2207 my $match2 = Bio::Location::Simple->new
357             (-seq_id => 'cds',
358             -start => $tr_end + 1,
359             -end => $tr_end + $exon->end - $exon->start +1,
360             -strand=>$exon->strand,
361             -verbose=>$self->verbose);
362              
363 15         2132 $cds_map->add_mapper(Bio::Coordinate::Pair->new
364             (-in => $match1,
365             -out => $match2,
366             )
367             );
368              
369 15 100 100     43 if ($exon->start <= 1 and $exon->end >= 1) {
370 5         102 $coffset = $tr_end - $exon->start + 1;
371             }
372 15         178 $tr_end = $tr_end + $exon->end - $exon->start + 1;
373              
374             #
375             # gene -> intron
376             #
377              
378 15 100       228 if (defined $prev_exon_end) {
379 10         30 my $match3 = Bio::Location::Simple->new
380             (-seq_id => 'gene',
381             -start => $prev_exon_end + 1,
382             -end => $exon->start -1,
383             -strand => $exon->strand,
384             -verbose => $self->verbose);
385              
386 10         1390 my $match4 = Bio::Location::Simple->new
387             (-seq_id => 'intron'. ($exon_counter -1),
388             -start => 1,
389             -end => $exon->start - 1 - $prev_exon_end,
390             -strand =>$exon->strand,
391             -verbose => $self->verbose,);
392              
393             # negative intron coordinates
394 10         1320 my $match5 = Bio::Location::Simple->new
395             (-seq_id => 'intron'. ($exon_counter -1),
396             -start => -1 * ($exon->start - 2 - $prev_exon_end) -1,
397             -end => -1,
398             -strand => $exon->strand,
399             -verbose => $self->verbose);
400              
401 10         1335 $inex_map->add_mapper(Bio::Coordinate::Pair->new
402             (-in => $match3,
403             -out => $match4
404             )
405             );
406 10         22 $intron_map->add_mapper(Bio::Coordinate::Pair->new
407             (-in => $self->_clone_loc($match3),
408             -out => $self->_clone_loc($match4)
409             )
410             );
411 10         40 $negative_intron_map->add_mapper(Bio::Coordinate::Pair->new
412             (-in => $self->_clone_loc($match4),
413             -out => $match5
414             ));
415              
416             }
417              
418             # store the value
419 15         46 $prev_exon_end = $exon->end;
420              
421             #
422             # gene -> exon
423             #
424 15         161 my $match6 = Bio::Location::Simple->new
425             (-seq_id => 'exon'. $exon_counter,
426             -start => 1,
427             -end => $exon->end - $exon->start +1,
428             -strand => $exon->strand,
429             -verbose=> $self->verbose,);
430              
431 15         2206 my $pair2 = Bio::Coordinate::Pair->new(-in => $self->_clone_loc($match1),
432             -out => $match6
433             );
434 15         45 my $pair3 = Bio::Coordinate::Pair->new(-in => $self->_clone_loc($match6),
435             -out => $self->_clone_loc($match2)
436             );
437 15         57 $inex_map->add_mapper(Bio::Coordinate::Pair->new
438             (-in => $self->_clone_loc($match1),
439             -out => $match6
440             )
441             );
442 15         47 $exon_map->add_mapper(Bio::Coordinate::Pair->new
443             (-in => $self->_clone_loc($match1),
444             -out => $self->_clone_loc($match6)
445             )
446             );
447 15         60 $exon_cds_map->add_mapper(Bio::Coordinate::Pair->new
448             (-in => $self->_clone_loc($match6),
449             -out => $self->_clone_loc($match2)
450             )
451             );
452              
453             }
454              
455             # move coordinate start if exons have negative values
456 5 100       148 if ($coffset) {
457 4         12 foreach my $m ($cds_map->each_mapper) {
458 12         140 $m->out->start($m->out->start - $coffset);
459 12         205 $m->out->end($m->out->end - $coffset);
460             }
461              
462             }
463              
464 5         70 $self->{'_mappers'}->{$cds_mapper} = $cds_map;
465 5         7 $self->{'_mappers'}->{$exon_cds_mapper} = $exon_cds_map;
466 5         7 $self->{'_mappers'}->{$inex_mapper} = $inex_map;
467 5         7 $self->{'_mappers'}->{$exon_mapper} = $exon_map;
468 5         7 $self->{'_mappers'}->{$intron_mapper} = $intron_map;
469 5         17 $self->{'_mappers'}->{$negative_intron_mapper} = $negative_intron_map;
470             }
471 5   50     85 return @{$self->{'_chr_exons'}} || 0;
472             }
473              
474              
475             sub _clone_loc { # clone a simple location
476 150     150   9162 my ($self,$loc) = @_;
477              
478 150 50       382 $self->throw("I need a Bio::Location::Simple , not [". ref $loc. "]")
479             unless $loc->isa('Bio::Location::Simple');
480              
481 150         328 return Bio::Location::Simple->new
482             (-verbose => $self->verbose,
483             -seq_id => $loc->seq_id,
484             -start => $loc->start,
485             -end => $loc->end,
486             -strand => $loc->strand,
487             -location_type => $loc->location_type
488             );
489             }
490              
491              
492             sub cds {
493 11     11 1 76 my ($self,$value) = @_;
494 11 100       24 if( defined $value) {
495 6 100 33     62 if ($value =~ /^[+-]?\d+$/ ) {
    50          
496 3         13 my $loc = Bio::Location::Simple->new(-start=>$value, -end => $value,
497             -verbose=>$self->verbose);
498 3         373 $self->{'_cds'} = $loc;
499             }
500             elsif (ref $value && $value->isa('Bio::RangeI') ) {
501 3         7 $self->{'_cds'} = $value;
502             } else {
503 0         0 $self->throw("I need an integer or Bio::RangeI, not [$value]")
504             }
505             # strand !!
506 6         8 my $len;
507              
508             $len = $self->{'_cds'}->end - $self->{'_cds'}->start +1
509 6 50       17 if defined $self->{'_cds'}->end;
510              
511             my $a = $self->_create_pair
512             ('chr', 'gene', 0,
513             $self->{'_cds'}->start-1,
514             $len,
515 6         170 $self->{'_cds'}->strand);
516 6         24 my $mapper = $COORDINATE_SYSTEMS{'chr'}. "-". $COORDINATE_SYSTEMS{'gene'};
517 6         11 $self->{'_mappers'}->{$mapper} = $a;
518              
519             # recalculate exon-based mappers
520 6 50       15 if ( defined $self->{'_chr_exons'} ) {
521 0         0 $self->exons(@{$self->{'_chr_exons'}});
  0         0  
522             }
523              
524             }
525 11   50     36 return $self->{'_cds'} || 0;
526             }
527              
528              
529             sub map {
530 35     35 1 7042 my ($self,$value) = @_;
531 35         36 my ($res);
532 35 50 33     194 $self->throw("Need to pass me a Bio::Location::Simple or ".
      33        
533             "Bio::Location::Simple or Bio::SeqFeatureI, not [".
534             ref($value). "]")
535             unless ref($value) && ($value->isa('Bio::Location::Simple') or
536             $value->isa('Bio::Location::SplitLocationI') or
537             $value->isa('Bio::SeqFeatureI'));
538             $self->throw("Input coordinate system not set")
539 35 50       82 unless $self->{'_in'};
540             $self->throw("Output coordinate system not set")
541 35 50       59 unless $self->{'_out'};
542             $self->throw("Do not be silly. Input and output coordinate ".
543             "systems are the same!")
544 35 50       64 unless $self->{'_in'} != $self->{'_out'};
545              
546 35         71 $self->_check_direction();
547              
548 35 50       146 $value = $value->location if $value->isa('Bio::SeqFeatureI');
549 35   100     85 $self->debug( "=== Start location: ". $value->start. ",".
550             $value->end. " (". ($value->strand || ''). ")\n");
551              
552             # if nozero coordinate system is used in the input values
553 35 100 66     1463 if ( defined $self->{'_nozero'} &&
      66        
554             ( $self->{'_nozero'} == 1 || $self->{'_nozero'} == 3 ) ) {
555 1 50 33     5 $value->start($value->start + 1)
556             if defined $value->start && $value->start < 1;
557 1 50 33     23 $value->end($value->end + 1)
558             if defined $value->end && $value->end < 1;
559             }
560              
561 35         80 my @steps = $self->_get_path();
562 35         143 $self->debug( "mapping ". $self->{'_in'}. "->". $self->{'_out'}.
563             " Mappers: ". join(", ", @steps). "\n");
564              
565 35         444 foreach my $mapper (@steps) {
566 45 100       195 if ($mapper eq $TRANSLATION) {
    50          
567 2 50       6 if ($self->direction == 1) {
568              
569 2         5 $value = $self->_translate($value);
570 2         336 $self->debug( "+ $TRANSLATION cds -> propeptide (translate) \n");
571             } else {
572 0         0 $value = $self->_reverse_translate($value);
573 0         0 $self->debug("+ $TRANSLATION propeptide -> cds (reverse translate) \n");
574             }
575             }
576             # keep the start and end values, and go on to next iteration
577             # if this mapper is not set
578             elsif ( ! defined $self->{'_mappers'}->{$mapper} ) {
579             # update mapper name
580 0         0 $mapper =~ /\d+-(\d+)/; my ($counter) = $1;
  0         0  
581 0         0 $value->seq_id($COORDINATE_INTS{$counter});
582 0         0 $self->debug( "- $mapper\n");
583             } else {
584             #
585             # the DEFAULT : generic mapping
586             #
587              
588 43         129 $value = $self->{'_mappers'}->{$mapper}->map($value);
589              
590 43 100 66     318 $value->purge_gaps
      66        
591             if ($value && $value->isa('Bio::Location::SplitLocationI') &&
592             $value->can('gap'));
593              
594 43 50 33     155 $self->debug( "+ $mapper (". $self->direction. "): start ".
595             $value->start. " end ". $value->end. "\n")
596             if $value && $self->verbose > 0;
597             }
598             }
599              
600             # if nozero coordinate system is asked to be used in the output values
601 35 100 66     412 if ( defined $value && defined $self->{'_nozero'} &&
      66        
      66        
602             ( $self->{'_nozero'} == 2 || $self->{'_nozero'} == 3 ) ) {
603              
604 1 50 33     3 $value->start($value->start - 1)
605             if defined $value->start && $value->start < 1;
606 1 50 33     39 $value->end($value->end - 1)
607             if defined $value->end && $value->end < 1;
608             }
609              
610             # handle merging of adjacent split locations!
611              
612 35 100 100     183 if (ref $value eq "Bio::Coordinate::Result" && $value->each_match > 1 ) {
    100 66        
613 7         7 my $prevloc;
614 7         7 my $merging = 0;
615 7         9 my $newvalue;
616             my @matches;
617 7         16 foreach my $loc ( $value->each_Location(1) ) {
618 16 100       648 unless ($prevloc) {
619 7         9 $prevloc = $loc;
620 7         11 push @matches, $prevloc;
621 7         9 next;
622             }
623 9 100 66     18 if ($prevloc->end == ($loc->start - 1) &&
624             $prevloc->seq_id eq $loc->seq_id) {
625 5         127 $prevloc->end($loc->end);
626 5         84 $merging = 1;
627             } else {
628 4         85 push @matches, $loc;
629 4         7 $prevloc = $loc;
630             }
631             }
632 7 100       16 if ($merging) {
633 4 50       13 if (@matches > 1 ) {
634 0         0 $newvalue = Bio::Coordinate::Result->new;
635 0         0 map {$newvalue->add_sub_Location} @matches;
  0         0  
636             } else {
637 4         13 $newvalue = Bio::Coordinate::Result::Match->new
638             (-seq_id => $matches[0]->seq_id,
639             -start => $matches[0]->start,
640             -end => $matches[0]->end,
641             -strand => $matches[0]->strand,
642             -verbose => $self->verbose,);
643             }
644 4         651 $value = $newvalue;
645             }
646             }
647             elsif (ref $value eq "Bio::Coordinate::Result" &&
648             $value->each_match == 1 ){
649 5         10 $value = $value->match;
650             }
651              
652 35         76 return $value;
653             }
654              
655              
656             sub direction {
657 2     2 1 2 my ($self) = @_;
658 2         6 return $self->{'_direction'};
659             }
660              
661              
662             sub swap {
663 1     1 1 3 my ($self,$value) = @_;
664              
665 1         3 ($self->{'_in'}, $self->{'_out'}) = ($self->{'_out'}, $self->{'_in'});
666 1         2 map { $self->{'_mappers'}->{$_}->swap } keys %{$self->{'_mappers'}};
  1         8  
  1         5  
667              
668             # record the changed direction;
669 1         1 $self->{_direction} *= -1;
670              
671 1         4 return 1;
672             }
673              
674              
675             sub to_string {
676 0     0 1 0 my ($self) = shift;
677              
678 0         0 print "-" x 40, "\n";
679              
680             # chr-gene
681 0         0 my $mapper_str = 'chr-gene';
682 0         0 my $mapper = $self->_mapper_string2code($mapper_str);
683              
684 0         0 printf "\n %-12s (%s)\n", $mapper_str, $mapper ;
685 0 0       0 if (defined $self->cds) {
686 0 0       0 my $end = $self->cds->end -1 if defined $self->cds->end;
687 0   0     0 printf "%16s%s: %s (%s)\n", ' ', 'gene offset', $self->cds->start-1 , $end || '';
688 0   0     0 printf "%16s%s: %s\n", ' ', 'gene strand', $self->cds->strand || 0;
689             }
690              
691             # gene-intron
692 0         0 $mapper_str = 'gene-intron';
693 0         0 $mapper = $self->_mapper_string2code($mapper_str);
694 0         0 printf "\n %-12s (%s)\n", $mapper_str, $mapper ;
695              
696 0         0 my $i = 1;
697 0         0 foreach my $pair ( $self->{'_mappers'}->{$mapper}->each_mapper ) {
698 0         0 printf "%8s :%8s -> %-12s\n", $i, $pair->in->start, $pair->out->start ;
699 0         0 printf "%8s :%8s -> %-12s\n", '', $pair->in->end, $pair->out->end ;
700 0         0 $i++;
701             }
702              
703             # intron-negative_intron
704 0         0 $mapper_str = 'intron-negative_intron';
705 0         0 $mapper = $self->_mapper_string2code($mapper_str);
706 0         0 printf "\n %-12s (%s)\n", $mapper_str, $mapper ;
707              
708 0         0 $i = 1;
709 0         0 foreach my $pair ( $self->{'_mappers'}->{$mapper}->each_mapper ) {
710 0         0 printf "%8s :%8s -> %-12s\n", $i, $pair->in->start, $pair->out->start ;
711 0         0 printf "%8s :%8s -> %-12s\n", '', $pair->in->end, $pair->out->end ;
712 0         0 $i++;
713             }
714              
715             # gene-exon
716 0         0 $mapper_str = 'gene-exon';
717 0         0 $mapper = $self->_mapper_string2code($mapper_str);
718 0         0 printf "\n %-12s (%s)\n", $mapper_str, $mapper ;
719              
720 0         0 $i = 1;
721 0         0 foreach my $pair ( $self->{'_mappers'}->{$mapper}->each_mapper ) {
722 0         0 printf "%8s :%8s -> %-12s\n", $i, $pair->in->start, $pair->out->start ;
723 0         0 printf "%8s :%8s -> %-12s\n", '', $pair->in->end, $pair->out->end ;
724 0         0 $i++;
725             }
726              
727             # gene-cds
728 0         0 $mapper_str = 'gene-cds';
729 0         0 $mapper = $self->_mapper_string2code($mapper_str);
730 0         0 printf "\n %-12s (%s)\n", $mapper_str, $mapper ;
731              
732 0         0 $i = 1;
733 0         0 foreach my $pair ( $self->{'_mappers'}->{$mapper}->each_mapper ) {
734 0         0 printf "%8s :%8s -> %-12s\n", $i, $pair->in->start, $pair->out->start ;
735 0         0 printf "%8s :%8s -> %-12s\n", '', $pair->in->end, $pair->out->end ;
736 0         0 $i++;
737             }
738              
739             # cds-propeptide
740 0         0 $mapper_str = 'cds-propeptide';
741 0         0 $mapper = $self->_mapper_string2code($mapper_str);
742 0         0 printf "\n %-12s (%s)\n", $mapper_str, $mapper ;
743 0         0 printf "%9s%-12s\n", "", '"translate"';
744              
745             # propeptide-peptide
746 0         0 $mapper_str = 'propeptide-peptide';
747 0         0 $mapper = $self->_mapper_string2code($mapper_str);
748 0         0 printf "\n %-12s (%s)\n", $mapper_str, $mapper ;
749 0         0 printf "%16s%s: %s\n", ' ', "peptide offset", $self->peptide_offset;
750              
751 0         0 print "\nin : ", $self->in, "\n";
752 0         0 print "out: ", $self->out, "\n";
753 0         0 my $dir;
754 0 0       0 $self->direction ? ($dir='forward') : ($dir='reverse');
755 0         0 printf "direction: %-8s(%s)\n", $dir, $self->direction;
756 0         0 print "\n", "-" x 40, "\n";
757              
758 0         0 1;
759             }
760              
761              
762             sub _mapper_code2string {
763 1     1   1689 my ($self, $code) = @_;
764 1         9 my ($a, $b) = $code =~ /(\d+)-(\d+)/;
765 1         8 return $COORDINATE_INTS{$a}. '-'. $COORDINATE_INTS{$b};
766              
767             }
768              
769              
770             sub _mapper_string2code {
771 1     1   3 my ($self, $string) =@_;
772 1         9 my ($a, $b) = $string =~ /([^-]+)-(.*)/;
773 1         6 return $COORDINATE_SYSTEMS{$a}. '-'. $COORDINATE_SYSTEMS{$b};
774             }
775              
776              
777             sub _create_pair {
778 8     8   88 my ($self, $in, $out, $strict, $offset, $length, $strand ) = @_;
779 8   50     29 $strict ||= 0;
780 8   100     24 $strand ||= 1;
781 8   100     18 $length ||= 20;
782              
783 8         37 my $match1 = Bio::Location::Simple->new
784             (-seq_id => $in,
785             -start => $offset+1,
786             -end => $offset+$length,
787             -strand => 1,
788             -verbose => $self->verbose);
789              
790 8         1062 my $match2 = Bio::Location::Simple->new
791             (-seq_id => $out,
792             -start => 1,
793             -end => $length,
794             -strand => $strand,
795             -verbose => $self->verbose);
796              
797 8         976 my $pair = Bio::Coordinate::ExtrapolatingPair->new
798             (-in => $match1,
799             -out => $match2,
800             -strict => $strict,
801             -verbose => $self->verbose,
802             );
803              
804 8         14 return $pair;
805             }
806              
807              
808             sub _translate {
809 3     3   518 my ($self,$value) = @_;
810              
811 3 50 33     18 $self->throw("Need to pass me a Bio::Location::Simple or ".
      33        
812             "Bio::Location::SplitLocationI, not [". ref($value). "]")
813             unless defined $value &&
814             ($value->isa('Bio::Location::Simple') || $value->isa('Bio::Location::SplitLocationI'));
815              
816 3         3 my $seqid = 'propeptide';
817              
818 3 50       11 if ($value->isa("Bio::Location::SplitLocationI") ) {
819 0         0 my $split = Bio::Location::Split->new(-seq_id=>$seqid);
820 0         0 foreach my $loc ( $value->each_Location(1) ) {
821 0         0 my $match = Bio::Location::Simple->new
822             (-start => int ($loc->start / 3 ) +1,
823             -end => int ($loc->end / 3 ) +1,
824             -seq_id => $seqid,
825             -strand => 1,
826             -verbose => $self->verbose,
827             );
828 0         0 $split->add_sub_Location($match);
829             }
830 0         0 return $split;
831              
832             } else {
833 3         11 return new Bio::Location::Simple(-start => int($value->start / 3 )+1,
834             -end => int($value->end / 3 )+1,
835             -seq_id => $seqid,
836             -strand => 1,
837             -verbose=> $self->verbose,
838             );
839             }
840             }
841              
842              
843             sub _frame {
844 1     1   665 my ($self,$value) = @_;
845              
846 1 50 33     17 $self->throw("Need to pass me a Bio::Location::Simple or ".
      33        
847             "Bio::Location::SplitLocationI, not [". ref($value). "]")
848             unless defined $value &&
849             ($value->isa('Bio::Location::Simple') || $value->isa('Bio::Location::SplitLocationI'));
850              
851 1         2 my $seqid = 'propeptide';
852              
853 1 50       6 if ($value->isa("Bio::Location::SplitLocationI")) {
854 0         0 my $split = Bio::Location::Split->new(-seq_id=>$seqid);
855 0         0 foreach my $loc ( $value->each_Location(1) ) {
856              
857 0         0 my $match = Bio::Location::Simple->new
858             (-start => ($value->start-1) % 3 +1,
859             -end => ($value->end-1) % 3 +1,
860             -seq_id => 'frame',
861             -strand => 1,
862             -verbose=> $self->verbose);
863 0         0 $split->add_sub_Location($match);
864             }
865 0         0 return $split;
866             } else {
867 1         4 return new Bio::Location::Simple(-start => ($value->start-1) % 3 +1,
868             -end => ($value->end-1) % 3 +1,
869             -seq_id => 'frame',
870             -strand => 1,
871             -verbose => $self->verbose,
872             );
873             }
874             }
875              
876              
877             sub _reverse_translate {
878 1     1   962 my ($self,$value) = @_;
879              
880 1 50 33     11 $self->throw("Need to pass me a Bio::Location::Simple or ".
      33        
881             "Bio::Location::SplitLocationI, not [". ref($value). "]")
882             unless defined $value &&
883             ($value->isa('Bio::Location::Simple') || $value->isa('Bio::Location::SplitLocationI'));
884              
885 1         2 my $seqid = 'cds';
886              
887 1 50       5 if ($value->isa("Bio::Location::SplitLocationI")) {
888 0         0 my $split = Bio::Location::Split->new(-seq_id=>$seqid);
889 0         0 foreach my $loc ( $value->each_Location(1) ) {
890              
891 0         0 my $match = Bio::Location::Simple->new
892             (-start => $value->start * 3 - 2,
893             -end => $value->end * 3,
894             -seq_id => $seqid,
895             -strand => 1,
896             -verbose => $self->verbose,
897             );
898 0         0 $split->add_sub_Location($match);
899             }
900 0         0 return $split;
901              
902             } else {
903 1         5 return new Bio::Location::Simple(-start => $value->start * 3 - 2,
904             -end => $value->end * 3,
905             -seq_id => $seqid,
906             -strand => 1,
907             -verbose => $self->verbose,
908             );
909             }
910             }
911              
912              
913             sub _check_direction {
914 35     35   36 my ($self) = @_;
915              
916 35         37 my $new_direction = 1;
917 35 100       71 $new_direction = -1 if $self->{'_in'} > $self->{'_out'};
918              
919 35 100       63 unless ($new_direction == $self->{_direction} ) {
920 4         6 map { $self->{'_mappers'}->{$_}->swap } keys %{$self->{'_mappers'}};
  22         73  
  4         18  
921             # record the changed direction;
922 4         19 $self->{_direction} *= -1;
923             }
924 35         40 1;
925             }
926              
927              
928             sub _get_path {
929 35     35   30 my ($self) = @_;
930              
931 35   50     67 my $start = $self->{'_in'} || 0;
932 35   50     56 my $end = $self->{'_out'} || 0;
933              
934             # note the order
935             # always go from smaller to bigger: it makes caching more efficient
936 35         29 my $reverse;
937 35 100       55 if ($start > $end) {
938 3         7 ($start, $end) = ($end, $start );
939 3         5 $reverse++;
940             }
941              
942 35         28 my @mappers;
943 35 100 100     141 if (exists $self->{'_previous_path'} and
944             $self->{'_previous_path'} eq "$start$end" ) {
945             # use cache
946 18         15 @mappers = @{$self->{'_mapper_path'}};
  18         38  
947             } else {
948 17         12 my $mapper;
949 17         18 my $prev_node = '';
950             @mappers =
951 41         53 map { $mapper = "$prev_node-$_"; $prev_node = $_; $mapper; }
  41         49  
  41         68  
952 17         56 $self->{'_graph'}->shortest_path($start, $end);
953 17         25 shift @mappers;
954              
955 17         28 $self->{'_previous_path'} = "$start$end";
956 17         29 $self->{'_mapper_path'} = \@mappers;
957             }
958              
959 35 100       97 $reverse ? return reverse @mappers : return @mappers;
960             }
961              
962             1;
963              
964             __END__