File Coverage

Bio/Tools/Glimmer.pm
Criterion Covered Total %
statement 158 173 91.3
branch 87 102 85.2
condition 23 38 60.5
subroutine 18 20 90.0
pod 4 4 100.0
total 290 337 86.0


line stmt bran cond sub pod time code
1             #
2             # BioPerl module for Bio::Tools::Glimmer
3             #
4             # Please direct questions and support issues to
5             #
6             # Cared for by Jason Stajich
7             #
8             # Copyright Jason Stajich
9             #
10             # You may distribute this module under the same terms as perl itself
11              
12             # POD documentation - main docs before the code
13              
14             =head1 NAME
15              
16             Bio::Tools::Glimmer - parser for Glimmer 2.X/3.X prokaryotic and
17             GlimmerM/GlimmerHMM eukaryotic gene predictions
18              
19             =head1 SYNOPSIS
20              
21             use Bio::Tools::Glimmer;
22              
23             # file
24             my $parser = Bio::Tools::Glimmer->new(-file => $file);
25             # filehandle:
26             $parser = Bio::Tools::Glimmer->new( -fh => \*INPUT );
27             # provide a sequence identifier (Glimmer 2.X)
28             my $parser = Bio::Tools::Glimmer->new(-file => $file, -seqname => seqname);
29             # force format (override automatic detection)
30             my $parser = Bio::Tools::Glimmer->new(-file => $file, -format => 'GlimmerM');
31              
32             # parse the results
33             # note: this class is-a Bio::Tools::AnalysisResult which implements
34             # Bio::SeqAnalysisParserI, i.e., $glimmer->next_feature() is the same
35              
36             while(my $gene = $parser->next_prediction()) {
37             # For eukaryotic input (GlimmerM/GlimmerHMM), $gene will be an instance
38             # of Bio::Tools::Prediction::Gene, which inherits off
39             # Bio::SeqFeature::Gene::Transcript, and $gene->exons() will return an
40             # array of Bio::Tools::Prediction::Exon objects.
41             # For prokaryotic input (Glimmer2.X/Glimmer3.X), $gene will be an
42             # instance of Bio::SeqFeature::Generic
43              
44             # all exons (eukaryotic only):
45             @exon_arr = $gene->exons();
46             # initial exons only
47             @init_exons = $gene->exons('Initial');
48             # internal exons only
49             @intrl_exons = $gene->exons('Internal');
50             # terminal exons only
51             @term_exons = $gene->exons('Terminal');
52             }
53              
54             =head1 DESCRIPTION
55              
56             This is a module for parsing Glimmer, GlimmerM and GlimmerHMM predictions.
57             It will create gene objects from the prediction report which can
58             be attached to a sequence using Bioperl objects, or output as GFF
59             suitable for loading into Bio::DB::GFF for use with Gbrowse.
60              
61             Glimmer is open source and available at
62             L.
63              
64             GlimmerM is open source and available at
65             L.
66              
67             GlimmerHMM is open source and available at
68             L.
69              
70             Note that Glimmer 2.X will only process the first
71             sequence in a fasta file, and the prediction report does not contain any
72             sort of sequence identifier
73              
74             Note that Glimmer 3.X produces two output files. This module only parses
75             the .predict file.
76              
77              
78             =head1 FEEDBACK
79              
80             =head2 Mailing Lists
81              
82             User feedback is an integral part of the evolution of this and other
83             Bioperl modules. Send your comments and suggestions preferably to
84             the Bioperl mailing list. Your participation is much appreciated.
85              
86             bioperl-l@bioperl.org - General discussion
87             http://bioperl.org/wiki/Mailing_lists - About the mailing lists
88              
89             =head2 Support
90              
91             Please direct usage questions or support issues to the mailing list:
92              
93             I
94              
95             rather than to the module maintainer directly. Many experienced and
96             reponsive experts will be able look at the problem and quickly
97             address it. Please include a thorough description of the problem
98             with code and data examples if at all possible.
99              
100             =head2 Reporting Bugs
101              
102             Report bugs to the Bioperl bug tracking system to help us keep track
103             of the bugs and their resolution. Bug reports can be submitted via
104             email or the web:
105              
106             https://github.com/bioperl/bioperl-live/issues
107              
108             =head1 AUTHOR - Jason Stajich
109              
110             Email jason-at-bioperl-dot-org
111              
112             =head1 CONTRIBUTORS
113              
114             Torsten Seemann
115              
116             Mark Johnson
117              
118             =head1 APPENDIX
119              
120             The rest of the documentation details each of the object methods.
121             Internal methods are usually preceded with a _
122              
123             =cut
124              
125              
126             # Let the code begin...
127              
128              
129             package Bio::Tools::Glimmer;
130 1     1   688 use strict;
  1         2  
  1         25  
131              
132 1     1   4 use Bio::Factory::FTLocationFactory;
  1         1  
  1         17  
133 1     1   3 use Bio::Tools::Prediction::Gene;
  1         2  
  1         14  
134 1     1   4 use Bio::Tools::Prediction::Exon;
  1         1  
  1         16  
135              
136 1     1   4 use base qw(Bio::Tools::AnalysisResult);
  1         1  
  1         1667  
137              
138             sub _initialize_state {
139 5     5   10 my($self,@args) = @_;
140              
141             # first call the inherited method!
142 5         14 my $make = $self->SUPER::_initialize_state(@args);
143              
144 5         13 $self->{'_preds_parsed'} = 0;
145             # array of pre-parsed predictions
146 5         14 $self->{'_preds'} = [];
147             }
148              
149             =head2 new
150              
151             Title : new
152             Usage : my $obj = Bio::Tools::Glimmer->new();
153             Function: Builds a new Bio::Tools::Glimmer object
154             Returns : an instance of Bio::Tools::Glimmer
155             Args : format ('Glimmer', 'GlimmerM', 'GlimmerHMM'), seqname
156              
157              
158             =cut
159              
160             sub new {
161 5     5 1 18 my($class,@args) = @_;
162              
163 5         27 my $self = $class->SUPER::new(@args);
164              
165 5         18 my ($format, $seqname, $seqlength, $detail) =
166             $self->_rearrange([qw(FORMAT SEQNAME SEQLENGTH DETAIL)], @args);
167              
168             # override automagic format detection
169 5 0 0     15 if (defined($format) &&
      33        
170             (($format eq 'Glimmer') ||
171             ($format eq 'GlimmerM') ||
172             ($format eq 'GlimmerHMM'))
173             ) {
174 0         0 $self->_format($format);
175             }
176            
177 5 100       10 if (defined($detail)) {
178 2         9 $self->_format('Glimmer');
179 2         5 $self->_detail_file($detail);
180             }
181            
182             # hardwire seq_id when creating gene and exon objects (Glimmer 2.X)
183 5 100       12 $self->_seqname($seqname) if defined($seqname);
184            
185             # store the length of the input sequence (Glimmer 2.X)
186 5 100       14 $self->_seqlength($seqlength) if defined($seqlength);
187            
188 5         13 return $self;
189             }
190              
191             =head2 analysis_method
192              
193             Usage : $glimmer->analysis_method();
194             Purpose : Inherited method. Overridden to ensure that the name matches
195             /glimmer/i.
196             Returns : String
197             Argument : n/a
198              
199             =cut
200              
201             #-------------
202             sub analysis_method {
203             #-------------
204 0     0 1 0 my ($self, $method) = @_;
205 0 0 0     0 if($method && ($method !~ /glimmer/i)) {
206 0         0 $self->throw("method $method not supported in " . ref($self));
207             }
208 0         0 return $self->SUPER::analysis_method($method);
209             }
210              
211             =head2 next_feature
212              
213             Title : next_feature
214             Usage : while($gene = $glimmer->next_feature()) {
215             # do something
216             }
217             Function: Returns the next gene structure prediction of the Glimmer result
218             file. Call this method repeatedly until FALSE is returned.
219              
220             The returned object is actually a SeqFeatureI implementing object.
221             This method is required for classes implementing the
222             SeqAnalysisParserI interface, and is merely an alias for
223             next_prediction() at present.
224              
225             Example :
226             Returns : A Bio::Tools::Prediction::Gene object.
227             Args :
228              
229             =cut
230              
231             sub next_feature {
232 0     0 1 0 my ($self,@args) = @_;
233             # even though next_prediction doesn't expect any args (and this method
234             # does neither), we pass on args in order to be prepared if this changes
235             # ever
236 0         0 return $self->next_prediction(@args);
237             }
238              
239             =head2 next_prediction
240              
241             Title : next_prediction
242             Usage : while($gene = $glimmer->next_prediction()) {
243             # do something
244             }
245             Function: Returns the next gene structure prediction of the Glimmer result
246             file. Call this method repeatedly until FALSE is returned.
247              
248             Example :
249             Returns : A Bio::Tools::Prediction::Gene object.
250             Args :
251              
252             =cut
253              
254             sub next_prediction {
255 114     114 1 11465 my ($self) = @_;
256 114         103 my $gene;
257              
258             # if the prediction section hasn't been parsed yet, we do this now
259 114 100       151 $self->_parse_predictions() unless $self->_predictions_parsed();
260            
261             # get next gene structure
262 114         167 $gene = $self->_prediction();
263 114         236 return $gene;
264             }
265              
266             =head2 _parse_predictions
267              
268             Title : _parse_predictions()
269             Usage : $obj->_parse_predictions()
270             Function: Parses the prediction section. Automatically called by
271             next_prediction() if not yet done.
272             Example :
273             Returns :
274              
275             =cut
276              
277             sub _parse_predictions {
278              
279 5     5   9 my ($self) = @_;
280              
281            
282 5         21 my %method = (
283             'Glimmer' => '_parse_prokaryotic',
284             'GlimmerM' => '_parse_eukaryotic',
285             'GlimmerHMM' => '_parse_eukaryotic',
286             '_DEFAULT_' => '_parse_eukaryotic',
287             );
288            
289 5         10 my $format = $self->_format();
290            
291 5 100       11 if (!$format) {
292            
293 3         13 while (my $line = $self->_readline()) {
294              
295 233 100       679 if ( $line =~ /^Glimmer\S*\s+\(Version\s*\S+\)/ ) {
    100          
    100          
    50          
296 1         3 $format = 'GlimmerM';
297 1         11 $self->_pushback($line);
298 1         2 last;
299             }
300             elsif ( $line =~ /^Glimmer\S*$/ ) {
301 1         2 $format = 'GlimmerHMM';
302 1         4 $self->_pushback($line);
303 1         2 last;
304             }
305             elsif ($line =~ /^Putative Genes:$/) {
306 1         3 $format = 'Glimmer';
307 1         3 $self->_pushback($line);
308 1         2 last;
309             }
310             elsif ($line =~ /^>(\S+)/) {
311 0         0 $format = 'Glimmer';
312 0         0 $self->_pushback($line);
313 0         0 last;
314             }
315            
316             }
317            
318             }
319              
320             my $method =
321 5 50       15 (exists($method{$format})) ? $method{$format} : $method{'_DEFAULT_'};
322              
323 5         17 return $self->$method();
324            
325             }
326              
327              
328             =head2 _parse_eukaryotic
329              
330             Title : _parse_eukaryotic()
331             Usage : $obj->_parse_eukaryotic()
332             Function: Parses the prediction section. Automatically called by
333             next_prediction() if not yet done.
334             Example :
335             Returns :
336              
337             =cut
338              
339             sub _parse_eukaryotic {
340 2     2   5 my ($self) = @_;
341              
342 2         3 my ($gene,$seqname,$seqlen,$source,$lastgenenum);
343            
344 2         5 while(defined($_ = $self->_readline())) {
345 310 100 100     4672 if( /^(Glimmer\S*)\s+\(Version\s*(\S+)\)/ ) {
    100          
    100          
    100          
    100          
    50          
346 1         4 $source = "$1_$2";
347 1         2 next;
348             } elsif( /^(GlimmerHMM\S*)$/ ) { # GlimmerHMM has no version
349 1         3 $source = $1;
350 1         2 next;
351             } elsif(/^Sequence name:\s+(.+)$/ ) {
352 2         4 $seqname = $1;
353 2         5 next;
354             } elsif( /^Sequence length:\s+(\S+)/ ) {
355 2         4 $seqlen = $1;
356 2         3 next;
357             } elsif( m/^(Predicted genes)|(Gene)|\s+\#/ || /^\s+$/ ) {
358 66         124 next;
359            
360             } elsif( # GlimmerM/HMM gene-exon prediction line
361             /^\s+(\d+)\s+ # gene num
362             (\d+)\s+ # exon num
363             ([\+\-])\s+ # strand
364             (\S+)\s+ # exon type
365             (\d+)\s+(\d+) # exon start, end
366             \s+(\d+) # exon length
367             /ox ) {
368 238         943 my ($genenum,$exonnum,$strand,$type,$start,$end,$len) =
369             ( $1,$2,$3,$4,$5,$6,$7);
370 238 100 100     700 if( ! $lastgenenum || $lastgenenum != $genenum) {
371 54 100       128 $self->_add_prediction($gene) if ( $gene );
372 54         235 $gene = Bio::Tools::Prediction::Gene->new
373             (
374             '-seq_id' => $seqname,
375             '-primary_tag' => "gene",
376             '-source_tag' => $source,
377             '-tag' => { 'Group' => "GenePrediction$genenum"},
378             );
379             }
380 238 100       1187 my $exon = Bio::Tools::Prediction::Exon->new
381             ('-seq_id' => $seqname,
382             '-start' => $start,
383             '-end' => $end,
384             '-strand' => $strand eq '-' ? '-1' : '1',
385             '-source_tag' => $source,
386             '-primary_tag'=> 'exon',
387             '-tag' => { 'Group' => "GenePrediction$genenum"},
388             );
389 238         903 $gene->add_exon($exon,lc($type));
390 238         729 $lastgenenum = $genenum;
391             }
392             }
393 2 50       8 $self->_add_prediction($gene) if( $gene );
394 2         6 $self->_predictions_parsed(1);
395             }
396              
397             =head2 _parse_prokaryotic
398              
399             Title : _parse_prokaryotic()
400             Usage : $obj->_parse_prokaryotic()
401             Function: Parses the prediction section. Automatically called by
402             next_prediction() if not yet done.
403             Example :
404             Returns :
405              
406             =cut
407              
408             sub _parse_prokaryotic {
409 3     3   5 my ($self) = @_;
410              
411             # default value, possibly overriden later
412 3         5 my $source = 'Glimmer';
413              
414             # Store the sequence length(s) here, either from the
415             # seqlength arg to the constructor, or from the
416             # Glimmer 3.X detail file
417 3         4 my %seqlength = ( );
418            
419             # Glimmer 2.X does not provide a sequence identifer
420             # in the prediction report (will default to unknown
421             # if not specified in the seqname arg to the
422             # constructor
423             #
424             # Glimmer 2.X does not report the length of the
425             # input sequence, either (will default to undef
426             # if not specified in the seqlength arg to the
427             # constructor
428 3         7 my $seqname = $self->_seqname();
429 3         6 my $seqlength = $self->_seqlength();
430              
431 3 100       6 if (defined($seqlength)) {
432 1         2 $seqlength{$seqname} = $seqlength
433             }
434              
435             # Parse the detail file, if we have one (Glimmer 3.X)
436 3         6 my $detail_file = $self->_detail_file();
437            
438 3 100       7 if (defined($detail_file)) {
439              
440 2         12 my $io = Bio::Root::IO->new(-file => $detail_file);
441 2         3 my $seqname;
442            
443 2         5 while (defined($_ = $io->_readline())) {
444 222 100       322 if ($_ =~ /^>(\S+)/) {
445 2         6 $seqname = $1;
446 2         5 next;
447             }
448              
449 220 100 100     526 if (defined($seqname) && ($_ =~ /^Sequence length = (\d+)$/)) {
450 2         7 $seqlength{$seqname} = $1;
451 2         3 next;
452             }
453             }
454             }
455            
456 3         17 my $location_factory = Bio::Factory::FTLocationFactory->new();
457            
458 3         7 while(defined($_ = $self->_readline())) {
459             # Glimmer 3.X does provide a sequence identifier -
460             # beware whitespace at the end (comes through from
461             # the fasta file)
462 59 100 66     391 if ($_ =~ /^Putative Genes:$/) {
    100          
    50          
463 1         2 $source = 'Glimmer_2.X';
464 1         2 next;
465             }
466             # Glimmer 3.X sequence identifier
467             elsif ($_ =~ /^>(\S+)/) {
468 2         6 $seqname = $1;
469 2         5 $seqlength = $seqlength{$seqname};
470 2         3 $source = 'Glimmer_3.X';
471 2         4 next;
472             }
473             elsif (
474             # Glimmer 2.X prediction
475             (/^\s+(\d+)\s+ # gene num
476             (\d+)\s+(\d+)\s+ # start, end
477             \[([\+\-])(\d{1})\s+ # strand, frame
478             /ox ) ||
479             # Glimmer 3.X prediction
480             (/^[^\d]+(\d+)\s+ # orf (numeric portion)
481             (\d+)\s+(\d+)\s+ # start, end
482             ([\+\-])(\d{1})\s+ # strand, frame
483             ([\d\.]+) # score
484             /ox)) {
485 56         204 my ($genenum,$start,$end,$strand,$frame,$score) =
486             ( $1,$2,$3,$4,$5,$6 );
487              
488 56         69 my $circular_prediction = 0;
489              
490             # Check for a circular prediction before we
491             # start fiddling with the coordinates
492 56 100       78 if ($strand eq '+') {
493 43 100       98 if ($start > $end) {
494 2         3 $circular_prediction = 1;
495             }
496             }
497             else {
498 13 50       28 if ($start < $end) {
499 0         0 $circular_prediction = 1;
500             }
501             }
502              
503 56 100       89 if ($circular_prediction) {
504 2 50       5 unless (defined($seqlength)) {
505 0         0 $self->throw("need to know the sequence length to handle wraparound genes");
506             }
507             }
508            
509             # Glimmer 2.X predictions do not include
510             # the stop codon - this might extend the
511             # prediction off either end of the sequence.
512             # This works fine even on circular/wraparound
513             # predictions.
514 56 100       78 if ($source eq 'Glimmer_2.X') {
515 25 100       36 if ($strand eq '+') {
516 19         20 $end += 3;
517             }
518             else {
519 6         8 $end -= 3;
520             }
521             }
522              
523             # We might have extended a Glimmer 2.X prediction
524             # beyond the boundaries of the input sequence.
525             # Also, Glimmer 3.X (with -X) will output predictions
526             # with coordinates less than 1 or greater than the
527             # length of the sequence.
528 56         61 my ($fst, $fend);
529 56         66 foreach my $coord ($start, $end) {
530 112 100 66     308 if ($coord < 1) {
    100          
531 1         1 $coord = '<1';
532 1         3 $fst++;
533             } elsif (defined($seqlength) && ($coord > $seqlength)) {
534 1         2 $coord = ">$seqlength";
535 1         3 $fend++;
536             }
537             }
538            
539 56         51 my $location_string;
540              
541 56 100       78 if ($circular_prediction) {
542 2 50       5 if ($strand eq '+') {
543 2         6 $location_string = "join($start..$seqlength,1..$end)";
544             }
545             else {
546 0         0 $location_string = "join($start..1,$seqlength..$end)";
547             }
548             }
549             else {
550             # start must always be less than end for gene locations
551 54 50 66     125 if ($strand eq '-' && !$fst && !$fend && $start > $end) {
      66        
      33        
552 13         26 ($start, $end) = ($end, $start);
553             }
554 54         86 $location_string = "$start..$end";
555             }
556            
557 56         126 my $location_object =
558             $location_factory->from_string($location_string);
559            
560             # convert glimmer's frame range from 1-3 to SeqFeature's 0-2.
561 56         109 $frame--;
562            
563 56 100 100     370 my $gene = Bio::SeqFeature::Generic->new
564             (
565             '-seq_id' => $seqname,
566             '-location' => $location_object,
567             '-strand' => $strand eq '-' ? '-1' : '1',
568             '-frame' => $frame,
569             '-source_tag' => $source,
570             '-display_name' => "orf$genenum",
571             '-primary_tag'=> 'gene',
572             '-tag' => { 'Group' => "GenePrediction_$genenum"},
573             '-score' => $score || undef
574             );
575            
576 56         141 $self->_add_prediction($gene)
577             }
578             }
579            
580 3         7 $self->_predictions_parsed(1);
581             }
582              
583             =head2 _prediction
584              
585             Title : _prediction()
586             Usage : $gene = $obj->_prediction()
587             Function: internal
588             Example :
589             Returns :
590              
591             =cut
592              
593             sub _prediction {
594 114     114   128 my ($self) = @_;
595              
596 114 100 66     181 return unless(exists($self->{'_preds'}) && @{$self->{'_preds'}});
  114         228  
597 110         104 return shift(@{$self->{'_preds'}});
  110         186  
598             }
599              
600             =head2 _add_prediction
601              
602             Title : _add_prediction()
603             Usage : $obj->_add_prediction($gene)
604             Function: internal
605             Example :
606             Returns :
607              
608             =cut
609              
610             sub _add_prediction {
611 110     110   157 my ($self, $gene) = @_;
612              
613 110 50       170 if(! exists($self->{'_preds'})) {
614 0         0 $self->{'_preds'} = [];
615             }
616 110         100 push(@{$self->{'_preds'}}, $gene);
  110         283  
617             }
618              
619             =head2 _predictions_parsed
620              
621             Title : _predictions_parsed
622             Usage : $obj->_predictions_parsed
623             Function: internal
624             Example :
625             Returns : TRUE or FALSE
626              
627             =cut
628              
629             sub _predictions_parsed {
630 119     119   143 my ($self, $val) = @_;
631              
632 119 100       167 $self->{'_preds_parsed'} = $val if $val;
633 119 50       160 if(! exists($self->{'_preds_parsed'})) {
634 0         0 $self->{'_preds_parsed'} = 0;
635             }
636 119         208 return $self->{'_preds_parsed'};
637             }
638              
639             =head2 _seqname
640              
641             Title : _seqname
642             Usage : $obj->_seqname($seqname)
643             Function: internal (for Glimmer 2.X)
644             Example :
645             Returns : String
646              
647             =cut
648              
649             sub _seqname {
650 4     4   6 my ($self, $val) = @_;
651              
652 4 100       10 $self->{'_seqname'} = $val if $val;
653 4 100       7 if(! exists($self->{'_seqname'})) {
654 2         4 $self->{'_seqname'} = 'unknown';
655             }
656 4         6 return $self->{'_seqname'};
657             }
658              
659             =head2 _seqlength
660              
661             Title : _seqlength
662             Usage : $obj->_seqlength($seqlength)
663             Function: internal (for Glimmer 2.X)
664             Example :
665             Returns : String
666              
667             =cut
668              
669             sub _seqlength {
670 4     4   6 my ($self, $val) = @_;
671              
672 4 100       9 $self->{'_seqlength'} = $val if $val;
673 4         7 return $self->{'_seqlength'};
674             }
675              
676             =head2 _format
677              
678             Title : _format
679             Usage : $obj->_format($format)
680             Function: internal
681             Example :
682             Returns : String
683              
684             =cut
685              
686             sub _format {
687 7     7   11 my ($self, $val) = @_;
688              
689 7 100       13 $self->{'_format'} = $val if $val;
690              
691 7         12 return $self->{'_format'};
692             }
693              
694             =head2 _detail_file
695              
696             Title : _detail_file
697             Usage : $obj->_detail_file($filename)
698             Function: internal (for Glimmer 3.X)
699             Example :
700             Returns : String
701              
702             =cut
703              
704             sub _detail_file {
705 5     5   7 my ($self, $val) = @_;
706              
707 5 100       11 $self->{'_detail_file'} = $val if $val;
708 5         6 return $self->{'_detail_file'};
709             }
710              
711             1;