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.2';
80 1     1   8 use Moose;
  1         2  
  1         10  
81 1     1   5105 use namespace::autoclean;
  1         3  
  1         10  
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 1179 my ($self) = @_;
195            
196 2 50       77 if (defined $self->transcripts) {
197 2         6 return [grep {$_->is_coding} @{$self->transcripts}];
  4         27  
  2         60  
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 1182 my ($self) = @_;
207            
208 1 50       37 if (defined $self->transcripts) {
209 1         6 return [grep {not $_->is_coding} @{$self->transcripts}];
  2         9  
  1         25  
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 702 my ($self, $transcript) = @_;
219            
220 350 50 33     1721 if (defined $transcript and ($transcript->isa('GenOO::Transcript'))) {
221 350         557 push @{$self->transcripts}, $transcript;
  350         8415  
222 350         938 $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 1192 my ($self) = @_;
231            
232 1         8 my %counts;
233 1         5 foreach my $transcript (@{$self->transcripts}) {
  1         40  
234 2         10 foreach my $exon (@{$transcript->exons}) {
  2         66  
235 4         13 $counts{$exon->location}++;
236             }
237             }
238            
239 1         4 my @constitutive_exons;
240 1         2 my $transcript_count = @{$self->transcripts};
  1         37  
241 1         2 foreach my $transcript (@{$self->transcripts}) {
  1         34  
242 2         5 foreach my $exon (@{$transcript->exons}) {
  2         48  
243 4 100 100     14 if (exists $counts{$exon->location} and ($counts{$exon->location} == $transcript_count)) {
244 1         30 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         4 delete $counts{$exon->location};
252             }
253             }
254             }
255 1         6 return \@constitutive_exons;
256             }
257              
258             sub constitutive_coding_exonic_regions {
259 1     1 0 1237 my ($self) = @_;
260            
261 1         7 my %counts;
262 1         7 foreach my $transcript (@{$self->transcripts}) {
  1         39  
263 2         5 foreach my $exon (@{$transcript->exons}) {
  2         61  
264 4         54 $counts{$exon->location}++;
265             }
266             }
267            
268 1         11 my @constitutive_exons;
269 1         3 my $transcript_count = @{$self->coding_transcripts};
  1         13  
270 1         13 foreach my $transcript (@{$self->transcripts}) {
  1         39  
271 2         39 foreach my $exon (@{$transcript->exons}) {
  2         61  
272 4 100 100     20 if (exists $counts{$exon->location} and ($counts{$exon->location} == $transcript_count)) {
273 1         38 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         5 delete $counts{$exon->location};
281             }
282             }
283             }
284 1         16 return \@constitutive_exons;
285             }
286              
287             sub has_coding_transcript {
288 1     1 0 1138 my ($self) = @_;
289            
290 1         3 foreach my $transcript (@{$self->transcripts}) {
  1         33  
291 1 50       8 if ($transcript->is_coding) {
292 1         10 return 1;
293             }
294             }
295            
296 0         0 return 0;
297             }
298              
299             sub exonic_length {
300 1     1 0 1225 my ($self) = @_;
301            
302 1         13 my $exonic_length = 0;
303 1         53 foreach my $region ($self->all_exonic_regions) {
304 3         89 $exonic_length += $region->length
305             }
306            
307 1         6 return $exonic_length;
308             }
309              
310             sub intronic_length {
311 1     1 0 1152 my ($self) = @_;
312            
313 1         9 my $intronic_length = 0;
314 1         57 foreach my $region ($self->all_intronic_regions) {
315 2         53 $intronic_length += $region->length
316             }
317            
318 1         7 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   612 my ($self) = @_;
359            
360 355         418 my $strand;
361 355 50       7869 if (defined $self->transcripts) {
362 355         7748 $strand = $self->transcripts->[0]->strand;
363             }
364            
365 355 50       810 if (not defined $strand) {
366 0         0 die "No strand found for ".$self->name."\n";
367             }
368             else {
369 355         7613 return $strand;
370             }
371             }
372              
373             sub _find_chromosome {
374 355     355   586 my ($self) = @_;
375            
376 355         529 my $chromosome;
377 355 50       8241 if (defined $self->transcripts) {
378 355         8084 $chromosome = $self->transcripts->[0]->chromosome;
379             }
380            
381 355 50       800 if (not defined $chromosome) {
382 0         0 die "No chromosome found for ".$self->name."\n";
383             }
384             else {
385 355         7775 return $chromosome;
386             }
387             }
388              
389             sub _find_start {
390 193     193   311 my ($self) = @_;
391            
392 193         271 my $start;
393 193 50       4271 if (defined $self->transcripts) {
394 193         275 foreach my $transcript (@{$self->transcripts}) {
  193         4156  
395 374 100 100     4909 if ((not defined $start) or ($start > $transcript->start)) {
396 194         4735 $start = $transcript->start;
397             }
398             }
399             }
400            
401 193 50       461 if (not defined $start) {
402 0         0 die "No start found for ".$self->name."\n";
403             }
404             else {
405 193         4091 return $start;
406             }
407             }
408              
409             sub _find_stop {
410 193     193   302 my ($self) = @_;
411            
412 193         263 my $stop;
413 193 50       4387 if (defined $self->transcripts) {
414 193         265 foreach my $transcript (@{$self->transcripts}) {
  193         4202  
415 374 100 100     4815 if ((not defined $stop) or ($stop < $transcript->stop)) {
416 235         5590 $stop = $transcript->stop;
417             }
418             }
419             }
420            
421 193 50       391 if (not defined $stop) {
422 0         0 die "No stop found for ".$self->name."\n";
423             }
424             else {
425 193         4017 return $stop;
426             }
427             }
428              
429             sub _build_exonic_regions {
430 4     4   11 my ($self) = @_;
431            
432 4         11 my @all_exons;
433 4         12 foreach my $transcript (@{$self->transcripts}) {
  4         116  
434 8         20 foreach my $exon (@{$transcript->exons}) {
  8         192  
435 16         32 push @all_exons, $exon;
436             }
437             }
438            
439 4         14 return $self->_merge_exons(\@all_exons);
440             }
441              
442             sub _build_intronic_regions {
443 2     2   6 my ($self) = @_;
444            
445 2         7 my @intronic_regions;
446 2         83 my @e_regions = sort {$a->start <=> $b->start} $self->all_exonic_regions;
  6         149  
447              
448 2 50       9 return [] if @e_regions < 2;
449              
450 2         8 for (my $i = 0; $i < @e_regions-1; $i++) {
451 4         96 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         74 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   31 my ($self, $exons) = @_;
508            
509 4         29 my @sorted_exons = sort{$a->start <=> $b->start} @$exons;
  20         504  
510            
511 4         9 my @exonic_regions;
512 4         13 foreach my $exon (@sorted_exons) {
513 16         30 my $merge_region = $exonic_regions[-1];
514 16 100 100     98 if (defined $merge_region and
515             $merge_region->overlaps_with_offset($exon, 1, 1)) {
516              
517 4 50       94 $merge_region->stop($exon->stop) if $exon->stop > $merge_region->stop;
518             }
519             else {
520 12         298 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         125 return \@exonic_regions;
530             }
531              
532             sub _reset {
533 350     350   596 my ($self) = @_;
534            
535 350         9369 $self->_clear_strand;
536 350         9845 $self->_clear_chromosome;
537 350         8873 $self->_clear_start;
538 350         8771 $self->_clear_stop;
539 350         9714 $self->_clear_exonic_regions;
540 350         10007 $self->_clear_intronic_regions;
541 350         10281 $self->_clear_utr5_exonic_regions;
542 350         10211 $self->_clear_cds_exonic_regions;
543 350         10103 $self->_clear_utr3_exonic_regions;
544             }
545              
546             __PACKAGE__->meta->make_immutable;
547              
548             1;