File Coverage

blib/lib/GenOO/Gene.pm
Criterion Covered Total %
statement 138 192 71.8
branch 24 48 50.0
condition 16 18 88.8
subroutine 18 24 75.0
pod 0 11 0.0
total 196 293 66.8


line stmt bran cond sub pod time code
1             # POD documentation - main docs before the code
2              
3             =head1 NAME
4              
5             GenOO::Gene - Gene object
6              
7             =head1 SYNOPSIS
8              
9             # This object represents a gene (collection of transcripts)
10             # It extends the L<GenOO::GenomicRegion> object
11            
12             # To initialize
13             my $gene = GenOO::Gene->new(
14             name => undef, #required
15             species => undef,
16             strand => undef, #can be inferred from transcripts
17             chromosome => undef, #can be inferred from transcripts
18             start => undef, #can be inferred from transcripts
19             stop => undef, #can be inferred from transcripts
20             copy_number => undef, #defaults to 1
21             sequence => undef,
22             description => undef,
23             transcripts => reference to an array of L<GenOO::Transcript> objects
24             );
25              
26             =head1 DESCRIPTION
27              
28             GenOO::Gene describes a gene. A gene is defined as a genomic region (it has the strand, chromosome, start and stop
29             attributes required by L<GenOO::GenomicRegion>) as well as collection of L<GenOO::Transcript> objects. The genomic
30             location attributes can be inferred by the locations of the contained transcripts. The start position of the gene
31             will be the smallest coordinate of all the contained transcripts etc.
32             Whenever a transcript is added to a gene object the genomic coordinates of the gene are automatically updated.
33             It is a good idea NOT to set the genomic location of the gene directly but to let it be inferred by the transcripts.
34              
35             =head1 EXAMPLES
36             # Create a new gene object
37             my $gene = GenOO::Gene->new(
38             name => '2310016C08Rik',
39             description => 'hypoxia-inducible gene 2 protein isoform 2',
40             transcripts => [
41             GenOO::Transcript->new(
42             id => 'uc012eiw.1',
43             strand => 1,
44             chromosome => 'chr6',
45             start => 29222487,
46             stop => 29225448,
47             coding_start => 29222571,
48             coding_stop => 29224899,
49             biotype => 'coding',
50             splice_starts => [29222487,29224649],
51             splice_stops => [29222607,29225448]
52             ),
53             GenOO::Transcript->new(
54             id => 'uc009bdd.2',
55             strand => 1,
56             chromosome => 'chr6',
57             start => 29222625,
58             stop => 29225448,
59             coding_start => 29224705,
60             coding_stop => 29224899,
61             biotype => 'coding',
62             splice_starts => [29222625,29224649],
63             splice_stops => [29222809,29225448]
64             )
65             ],
66             );
67            
68             # Get gene information
69             $gene->strand; # 1
70             $gene->chromosome; # chr6
71             $gene->start; # 29222487
72             $gene->stop; # 29225448
73              
74             =cut
75              
76             # Let the code begin...
77              
78             package GenOO::Gene;
79             $GenOO::Gene::VERSION = '1.5.1';
80 1     1   4 use Moose;
  1         1  
  1         7  
81 1     1   2980 use namespace::autoclean;
  1         1  
  1         9  
82              
83             extends 'GenOO::GenomicRegion';
84              
85             has 'name' => (
86             isa => 'Str',
87             is => 'rw',
88             required => 1
89             );
90              
91             has 'description' => (
92             isa => 'Str',
93             is => 'rw'
94             );
95              
96             has 'transcripts' => (
97             isa => 'ArrayRef[GenOO::Transcript]',
98             is => 'rw',
99             default => sub {[]}
100             );
101              
102             has 'strand' => (
103             is => 'rw',
104             builder => '_find_strand',
105             clearer => '_clear_strand',
106             lazy => 1,
107             );
108              
109             has 'chromosome' => (
110             is => 'rw',
111             builder => '_find_chromosome',
112             clearer => '_clear_chromosome',
113             lazy => 1,
114             );
115              
116             has 'start' => (
117             is => 'rw',
118             builder => '_find_start',
119             clearer => '_clear_start',
120             lazy => 1,
121             );
122              
123             has 'stop' => (
124             is => 'rw',
125             builder => '_find_stop',
126             clearer => '_clear_stop',
127             lazy => 1,
128             );
129              
130             has 'exonic_regions' => (
131             traits => ['Array'],
132             is => 'ro',
133             builder => '_build_exonic_regions',
134             clearer => '_clear_exonic_regions',
135             handles => {
136             all_exonic_regions => 'elements',
137             exonic_regions_count => 'count',
138             },
139             lazy => 1
140             );
141              
142             has 'intronic_regions' => (
143             traits => ['Array'],
144             is => 'ro',
145             builder => '_build_intronic_regions',
146             clearer => '_clear_intronic_regions',
147             handles => {
148             all_intronic_regions => 'elements',
149             intronic_regions_count => 'count',
150             },
151             lazy => 1
152             );
153              
154             has 'utr5_exonic_regions' => (
155             traits => ['Array'],
156             is => 'ro',
157             builder => '_build_utr5_exonic_regions',
158             clearer => '_clear_utr5_exonic_regions',
159             handles => {
160             all_utr5_exonic_regions => 'elements',
161             utr5_exonic_regions_count => 'count',
162             },
163             lazy => 1
164             );
165              
166             has 'cds_exonic_regions' => (
167             traits => ['Array'],
168             is => 'ro',
169             builder => '_build_cds_exonic_regions',
170             clearer => '_clear_cds_exonic_regions',
171             handles => {
172             all_cds_exonic_regions => 'elements',
173             cds_exonic_regions_count => 'count',
174             },
175             lazy => 1
176             );
177              
178             has 'utr3_exonic_regions' => (
179             traits => ['Array'],
180             is => 'ro',
181             builder => '_build_utr3_exonic_regions',
182             clearer => '_clear_utr3_exonic_regions',
183             handles => {
184             all_utr3_exonic_regions => 'elements',
185             utr3_exonic_regions_count => 'count',
186             },
187             lazy => 1
188             );
189              
190             #######################################################################
191             ######################## Interface Methods ########################
192             #######################################################################
193             sub coding_transcripts {
194 2     2 0 758 my ($self) = @_;
195            
196 2 50       49 if (defined $self->transcripts) {
197 2         4 return [grep {$_->is_coding} @{$self->transcripts}];
  4         17  
  2         36  
198             }
199             else {
200 0         0 warn "No transcripts found for ".$self->name."\n";
201 0         0 return undef;
202             }
203             }
204              
205             sub non_coding_transcripts {
206 1     1 0 771 my ($self) = @_;
207            
208 1 50       28 if (defined $self->transcripts) {
209 1         2 return [grep {not $_->is_coding} @{$self->transcripts}];
  2         7  
  1         20  
210             }
211             else {
212 0         0 warn "No transcripts found for ".$self->name."\n";
213 0         0 return undef;
214             }
215             }
216              
217             sub add_transcript {
218 350     350 0 443 my ($self, $transcript) = @_;
219            
220 350 50 33     1879 if (defined $transcript and ($transcript->isa('GenOO::Transcript'))) {
221 350         254 push @{$self->transcripts}, $transcript;
  350         8324  
222 350         725 $self->_reset;
223             }
224             else {
225 0         0 warn 'Object "'.ref($transcript).'" is not a GenOO::Transcript ... skipped';
226             }
227             }
228              
229             sub constitutive_exonic_regions {
230 1     1 0 799 my ($self) = @_;
231            
232 1         3 my %counts;
233 1         2 foreach my $transcript (@{$self->transcripts}) {
  1         28  
234 2         3 foreach my $exon (@{$transcript->exons}) {
  2         40  
235 4         15 $counts{$exon->location}++;
236             }
237             }
238            
239 1         3 my @constitutive_exons;
240 1         6 my $transcript_count = @{$self->transcripts};
  1         43  
241 1         3 foreach my $transcript (@{$self->transcripts}) {
  1         36  
242 2         3 foreach my $exon (@{$transcript->exons}) {
  2         72  
243 4 100 100     14 if (exists $counts{$exon->location} and ($counts{$exon->location} == $transcript_count)) {
244 1         41 push @constitutive_exons, GenOO::GenomicRegion->new(
245             strand => $exon->strand,
246             chromosome => $exon->chromosome,
247             start => $exon->start,
248             stop => $exon->stop
249             );
250            
251 1         7 delete $counts{$exon->location};
252             }
253             }
254             }
255 1         8 return \@constitutive_exons;
256             }
257              
258             sub constitutive_coding_exonic_regions {
259 1     1 0 755 my ($self) = @_;
260            
261 1         2 my %counts;
262 1         2 foreach my $transcript (@{$self->transcripts}) {
  1         28  
263 2         4 foreach my $exon (@{$transcript->exons}) {
  2         51  
264 4         23 $counts{$exon->location}++;
265             }
266             }
267            
268 1         2 my @constitutive_exons;
269 1         4 my $transcript_count = @{$self->coding_transcripts};
  1         2  
270 1         2 foreach my $transcript (@{$self->transcripts}) {
  1         21  
271 2         3 foreach my $exon (@{$transcript->exons}) {
  2         39  
272 4 100 100     11 if (exists $counts{$exon->location} and ($counts{$exon->location} == $transcript_count)) {
273 1         21 push @constitutive_exons, GenOO::GenomicRegion->new(
274             strand => $exon->strand,
275             chromosome => $exon->chromosome,
276             start => $exon->start,
277             stop => $exon->stop
278             );
279            
280 1         6 delete $counts{$exon->location};
281             }
282             }
283             }
284 1         4 return \@constitutive_exons;
285             }
286              
287             sub has_coding_transcript {
288 1     1 0 1488 my ($self) = @_;
289            
290 1         3 foreach my $transcript (@{$self->transcripts}) {
  1         47  
291 1 50       14 if ($transcript->is_coding) {
292 1         7 return 1;
293             }
294             }
295            
296 0         0 return 0;
297             }
298              
299             sub exonic_length {
300 1     1 0 1345 my ($self) = @_;
301            
302 1         3 my $exonic_length = 0;
303 1         62 foreach my $region ($self->all_exonic_regions) {
304 3         116 $exonic_length += $region->length
305             }
306            
307 1         8 return $exonic_length;
308             }
309              
310             sub intronic_length {
311 1     1 0 1220 my ($self) = @_;
312            
313 1         5 my $intronic_length = 0;
314 1         58 foreach my $region ($self->all_intronic_regions) {
315 2         73 $intronic_length += $region->length
316             }
317            
318 1         8 return $intronic_length;
319             }
320              
321             sub utr5_exonic_length {
322 0     0 0 0 my ($self) = @_;
323            
324 0         0 my $exonic_length = 0;
325 0         0 foreach my $region ($self->all_utr5_exonic_regions) {
326 0         0 $exonic_length += $region->length
327             }
328            
329 0         0 return $exonic_length;
330             }
331              
332             sub cds_exonic_length {
333 0     0 0 0 my ($self) = @_;
334            
335 0         0 my $exonic_length = 0;
336 0         0 foreach my $region ($self->all_cds_exonic_regions) {
337 0         0 $exonic_length += $region->length
338             }
339            
340 0         0 return $exonic_length;
341             }
342              
343             sub utr3_exonic_length {
344 0     0 0 0 my ($self) = @_;
345            
346 0         0 my $exonic_length = 0;
347 0         0 foreach my $region ($self->all_utr3_exonic_regions) {
348 0         0 $exonic_length += $region->length
349             }
350            
351 0         0 return $exonic_length;
352             }
353              
354             #######################################################################
355             ######################### Private methods ##########################
356             #######################################################################
357             sub _find_strand {
358 355     355   361 my ($self) = @_;
359            
360 355         324 my $strand;
361 355 50       7973 if (defined $self->transcripts) {
362 355         7232 $strand = $self->transcripts->[0]->strand;
363             }
364            
365 355 50       637 if (not defined $strand) {
366 0         0 die "No strand found for ".$self->name."\n";
367             }
368             else {
369 355         7278 return $strand;
370             }
371             }
372              
373             sub _find_chromosome {
374 355     355   338 my ($self) = @_;
375            
376 355         273 my $chromosome;
377 355 50       7681 if (defined $self->transcripts) {
378 355         7596 $chromosome = $self->transcripts->[0]->chromosome;
379             }
380            
381 355 50       638 if (not defined $chromosome) {
382 0         0 die "No chromosome found for ".$self->name."\n";
383             }
384             else {
385 355         7344 return $chromosome;
386             }
387             }
388              
389             sub _find_start {
390 193     193   208 my ($self) = @_;
391            
392 193         165 my $start;
393 193 50       4136 if (defined $self->transcripts) {
394 193         161 foreach my $transcript (@{$self->transcripts}) {
  193         4013  
395 374 100 100     4717 if ((not defined $start) or ($start > $transcript->start)) {
396 194         4629 $start = $transcript->start;
397             }
398             }
399             }
400            
401 193 50       348 if (not defined $start) {
402 0         0 die "No start found for ".$self->name."\n";
403             }
404             else {
405 193         4044 return $start;
406             }
407             }
408              
409             sub _find_stop {
410 193     193   179 my ($self) = @_;
411            
412 193         159 my $stop;
413 193 50       4273 if (defined $self->transcripts) {
414 193         144 foreach my $transcript (@{$self->transcripts}) {
  193         4070  
415 374 100 100     4721 if ((not defined $stop) or ($stop < $transcript->stop)) {
416 235         5345 $stop = $transcript->stop;
417             }
418             }
419             }
420            
421 193 50       319 if (not defined $stop) {
422 0         0 die "No stop found for ".$self->name."\n";
423             }
424             else {
425 193         3907 return $stop;
426             }
427             }
428              
429             sub _build_exonic_regions {
430 4     4   10 my ($self) = @_;
431            
432 4         9 my @all_exons;
433 4         7 foreach my $transcript (@{$self->transcripts}) {
  4         160  
434 8         12 foreach my $exon (@{$transcript->exons}) {
  8         296  
435 16         31 push @all_exons, $exon;
436             }
437             }
438            
439 4         27 return $self->_merge_exons(\@all_exons);
440             }
441              
442             sub _build_intronic_regions {
443 2     2   4 my ($self) = @_;
444            
445 2         4 my @intronic_regions;
446 2         104 my @e_regions = sort {$a->start <=> $b->start} $self->all_exonic_regions;
  6         164  
447              
448 2 50       9 return [] if @e_regions < 2;
449              
450 2         15 for (my $i = 0; $i < @e_regions-1; $i++) {
451 4         110 push @intronic_regions, GenOO::GenomicRegion->new(
452             strand => $self->strand,
453             chromosome => $self->chromosome,
454             start => $e_regions[$i]->stop + 1,
455             stop => $e_regions[$i+1]->start - 1,
456             );
457             }
458            
459 2         78 return \@intronic_regions;
460             }
461              
462             sub _build_utr5_exonic_regions {
463 0     0   0 my ($self) = @_;
464            
465 0         0 my @all_exons;
466 0         0 foreach my $transcript (@{$self->transcripts}) {
  0         0  
467 0 0       0 next if !$transcript->is_coding;
468 0 0       0 next if !defined $transcript->utr5;
469 0         0 foreach my $exon (@{$transcript->utr5->exons}) {
  0         0  
470 0         0 push @all_exons, $exon;
471             }
472             }
473            
474 0         0 return $self->_merge_exons(\@all_exons);
475             }
476              
477             sub _build_cds_exonic_regions {
478 0     0   0 my ($self) = @_;
479            
480 0         0 my @all_exons;
481 0         0 foreach my $transcript (@{$self->transcripts}) {
  0         0  
482 0 0       0 next if !$transcript->is_coding;
483 0         0 foreach my $exon (@{$transcript->cds->exons}) {
  0         0  
484 0         0 push @all_exons, $exon;
485             }
486             }
487            
488 0         0 return $self->_merge_exons(\@all_exons);
489             }
490              
491             sub _build_utr3_exonic_regions {
492 0     0   0 my ($self) = @_;
493            
494 0         0 my @all_exons;
495 0         0 foreach my $transcript (@{$self->transcripts}) {
  0         0  
496 0 0       0 next if !$transcript->is_coding;
497 0 0       0 next if !defined $transcript->utr3;
498 0         0 foreach my $exon (@{$transcript->utr3->exons}) {
  0         0  
499 0         0 push @all_exons, $exon;
500             }
501             }
502            
503 0         0 return $self->_merge_exons(\@all_exons);
504             }
505              
506             sub _merge_exons {
507 4     4   9 my ($self, $exons) = @_;
508            
509 4         32 my @sorted_exons = sort{$a->start <=> $b->start} @$exons;
  20         701  
510            
511 4         7 my @exonic_regions;
512 4         15 foreach my $exon (@sorted_exons) {
513 16         20 my $merge_region = $exonic_regions[-1];
514 16 100 100     94 if (defined $merge_region and
515             $merge_region->overlaps_with_offset($exon, 1, 1)) {
516              
517 4 50       422 $merge_region->stop($exon->stop) if $exon->stop > $merge_region->stop;
518             }
519             else {
520 12         429 push @exonic_regions, GenOO::GenomicRegion->new(
521             strand => $exon->strand,
522             chromosome => $exon->chromosome,
523             start => $exon->start,
524             stop => $exon->stop,
525             );
526             }
527             }
528            
529 4         165 return \@exonic_regions;
530             }
531              
532             sub _reset {
533 350     350   325 my ($self) = @_;
534            
535 350         8705 $self->_clear_strand;
536 350         8812 $self->_clear_chromosome;
537 350         8218 $self->_clear_start;
538 350         8063 $self->_clear_stop;
539 350         9252 $self->_clear_exonic_regions;
540 350         9524 $self->_clear_intronic_regions;
541 350         9991 $self->_clear_utr5_exonic_regions;
542 350         9664 $self->_clear_cds_exonic_regions;
543 350         9627 $self->_clear_utr3_exonic_regions;
544             }
545              
546             __PACKAGE__->meta->make_immutable;
547              
548             1;