File Coverage

blib/lib/Bio/Das/Segment.pm
Criterion Covered Total %
statement 72 212 33.9
branch 11 98 11.2
condition 1 22 4.5
subroutine 20 51 39.2
pod 13 40 32.5
total 117 423 27.6


line stmt bran cond sub pod time code
1             package Bio::Das::Segment;
2              
3             # $Id: Segment.pm,v 1.26 2010/06/29 19:42:48 lstein Exp $
4 1     1   6 use strict;
  1         1  
  1         31  
5 1     1   5 use Bio::Root::Root;
  1         3  
  1         86  
6 1     1   6 use Bio::Das::SegmentI;
  1         2  
  1         24  
7 1     1   5 use Bio::Das::Util 'rearrange';
  1         2  
  1         55  
8 1     1   6 use File::Basename 'basename';
  1         2  
  1         88  
9 1     1   1205 use Data::Dumper 'Dumper';
  1         8692  
  1         109  
10 1     1   12 use File::Spec;
  1         3  
  1         31  
11 1     1   7 use File::Path 'mkpath';
  1         2  
  1         53  
12 1     1   5 use vars qw(@ISA $VERSION);
  1         3  
  1         85  
13             @ISA = qw(Bio::Root::Root Bio::Das::SegmentI);
14              
15             $VERSION = 0.91;
16              
17 1     1   6 use overload '""' => 'asString';
  1         2  
  1         11  
18             *abs_ref = *refseq = \&ref;
19             *abs_start = \&start;
20             *abs_end = *stop = \&end;
21             *abs_strand= \&strand;
22             *toString = \&asString;
23              
24 1     1   154 use constant DEBUG=>0;
  1         2  
  1         2983  
25              
26             sub new {
27 7     7 1 18 my $pack = shift;
28 7         22 my ($ref,$start,$stop,$version,$das,$dsn) = @_;
29 7         100 return bless {ref =>$ref,
30             start =>$start,
31             end =>$stop,
32             version=>$version,
33             das =>$das,
34             dsn =>$dsn,
35             },$pack;
36             }
37              
38 2     2 0 6 sub das { shift->{das} }
39             sub dsn {
40 2     2 0 4 my $self = shift;
41 2         6 $self->{dsn};
42             }
43 0     0 0 0 sub method { 'segment' }
44 0     0 0 0 sub source { 'das' }
45 0     0 0 0 sub attributes { }
46              
47             sub features {
48 1     1 1 1309 my $self = shift;
49              
50 1         5 my $das = $self->das;
51 1         4 my $dsn = $self->dsn;
52 1         3 my @args;
53 1 50 33     6 unless (defined $_[0] && $_[0] =~ /^-/) {
54 1 50       4 if (@_) {
55 0         0 @args = (-types => \@_);
56             } else {
57 1         4 my $types = $self->autotypes;
58 1         4 my $categories = $self->autocategories;
59 1 50       3 push @args,(-types => $types) if $types;
60 1 50       3 push @args,(-category=> $categories) if $categories;
61             }
62             } else {
63 0         0 @args = @_;
64             }
65 1         9 return $das->features(@args,
66             -dsn => $dsn,
67             -segment=> [$self]);
68             }
69              
70             sub get_seq_stream {
71 0     0 0 0 my $self = shift;
72 0         0 my @args = @_;
73 0         0 return $self->features(@args,-iterator=>1);
74             }
75              
76             sub source_tag {
77 0     0 0 0 return shift()->dsn;
78             }
79              
80             sub autotypes {
81 1     1 0 2 my $self = shift;
82 1         3 my $d = $self->{autotypes};
83 1 50       4 $self->{autotypes} = shift if @_;
84 1         2 $d;
85             }
86              
87             sub autocategories {
88 1     1 0 2 my $self = shift;
89 1         2 my $d = $self->{autocategories};
90 1 50       3 $self->{autocategories} = shift if @_;
91 1         2 $d;
92             }
93              
94             sub sequence {
95 0     0 0 0 my $self = shift;
96 0         0 my $das = $self->das;
97 0         0 my $dsn = $self->dsn;
98 0         0 return $das->sequence(@_,
99             -dsn => $dsn,
100             -segment=> [$self->asString]);
101             }
102              
103             sub dna {
104 1     1 1 6 my $self = shift;
105 1         5 my $das = $self->das;
106 1         4 my $dsn = $self->dsn;
107 1         5 return $das->dna(@_,
108             -dsn => $dsn,
109             -segment=> [$self->asString]);
110             }
111              
112             sub types {
113 0     0 1 0 my $self = shift;
114 0 0       0 my $das = $self->das or return;
115 0 0       0 my $dsn = $self->dsn or return;
116 0         0 return $das->types(@_,
117             -dsn => $dsn,
118             -segment=> [$self->asString]);
119             }
120              
121             sub ref {
122 1     1 1 2 my $self = shift;
123 1         3 my $d = $self->{ref};
124 1 50       4 $self->{ref} = shift if @_;
125 1         7 $d;
126             }
127             sub start {
128 0     0 1 0 my $self = shift;
129 0         0 my $d = $self->{start};
130 0 0       0 $self->{start} = shift if @_;
131 0         0 $d;
132             }
133             sub end {
134 0     0 1 0 my $self = shift;
135 0         0 my $d = $self->{end};
136 0 0       0 $self->{end} = shift if @_;
137 0         0 $d;
138             }
139 0     0 1 0 sub strand { 0 }
140 0     0 0 0 sub target { }
141 0     0 0 0 sub score { }
142 0     0 0 0 sub merged_segments { }
143             sub length {
144 0     0 1 0 my $self = shift;
145 0         0 $self->end-$self->start+1;
146             }
147             sub version {
148 0     0 0 0 my $self = shift;
149 0         0 my $d = $self->{version};
150 0 0       0 $self->{version} = shift if @_;
151 0         0 $d;
152             }
153             sub size {
154 0     0 0 0 my $self = shift;
155 0         0 my $d = $self->{size};
156 0 0       0 $self->{size} = shift if @_;
157 0   0     0 $d ||= $self->end-$self->start+1;
158 0         0 $d;
159             }
160             sub class {
161 0     0 0 0 my $self = shift;
162 0         0 my $d = $self->{class};
163 0 0       0 $self->{class} = shift if @_;
164 0         0 $d;
165             }
166             sub orientation {
167 0     0 0 0 my $self = shift;
168 0         0 my $d = $self->{orientation};
169 0 0       0 $self->{orientation} = shift if @_;
170 0         0 $d;
171             }
172             sub subparts {
173 0     0 0 0 my $self = shift;
174 0         0 my $d = $self->{subparts};
175 0 0       0 $self->{subparts} = shift if @_;
176 0         0 $d;
177             }
178             sub asString {
179 217     217 0 759 my $self = shift;
180 217         390 my $string = $self->{ref};
181 217 100       508 return "global" unless $string;
182 216 50       655 $string .= ":$self->{start}" if defined $self->{start};
183 216 50       579 $string .= ",$self->{end}" if defined $self->{end};
184 216         11885 $string;
185             }
186              
187              
188             ## Added for gbrowse interface
189             sub factory {
190 0     0 1   return shift->das;
191             }
192              
193             ## Added for gbrowse interface
194             sub name {
195 0     0 0   my $self = shift;
196 0           my $d = $self->{name};
197 0 0         $self->{name} = shift if @_;
198 0 0         $d || $self->toString();
199             }
200              
201             sub display_name {
202 0     0 1   shift->name;
203             }
204              
205             ## Added for gbrowse interface
206             sub info {
207 0     0 0   my $self = shift;
208 0           my $d = $self->{info};
209 0 0         $self->{info} = shift if @_;
210 0   0       return $d || "";
211             }
212              
213 0     0 0   sub get_SeqFeatures { return }
214              
215             ## Added for gbrowse interface
216             sub seq_id {
217 0     0 1   return shift->ref( @_ );
218             }
219              
220             ## Added for gbrowse interface
221             sub seq {
222 0     0 1   return shift->dna( @_ );
223             }
224              
225             # so that we can pass a whole segment to Bio::Graphics
226 0     0 0   sub type { 'Segment' }
227              
228 0     0 0   sub mtime { 0 }
229              
230 0     0 0   sub refs { }
231              
232             # this is working
233             sub render {
234 0     0 0   my $self = shift;
235 0           my ($panel,$position_to_insert,$options,$max_bump,$max_label) = @_;
236              
237 0 0         $max_bump = 50 unless defined $max_bump;
238 0 0         $max_label = 50 unless defined $max_label;
239 0 0         $options = 0 unless defined $options;
240 0 0         $panel->key_style('between') if $panel->key_style eq 'bottom'; # bottom key doesn't work with stylesheets
241              
242 0           my @COLORS = qw(cyan blue red yellow green wheat turquoise orange);
243              
244             # cache stylesheet
245 0           my $stylesheet = $self->get_cached_stylesheet;
246              
247 0 0 0       my @override = $options && CORE::ref($options) eq 'HASH' ? %$options : ();
248 0           my @new_tracks;
249              
250 0           my (%type_count,%tracks,%track_configs,$color);
251 0           my @f = $self->features;
252 0           for my $feature (@f) {
253              
254 0           warn "rendering $feature type = ",$feature->type," category = ",$feature->category if DEBUG;
255 0           warn "subtypes = ",join ' ',map {$_->type} $feature->get_SeqFeatures if DEBUG;
256              
257 0           my $type = $feature->type;
258 0           my $track_key = $type;
259 0   0       my $label = $type->label || $type->method_label;
260 0 0         $track_key .= ": ".$label if $label;
261              
262 0           $type_count{$type}++;
263 0 0         if (my $track = $tracks{$type}) {
264 0           $track->add_feature($feature);
265 0           next;
266             }
267              
268 0           my @config = (
269             -bgcolor => $COLORS[$color++ % @COLORS],
270             -label => 1,
271             -key => $track_key,
272             -stylesheet => $stylesheet,
273             -glyph => 'line',
274             );
275              
276              
277 0           eval {
278 0 0         if (defined($position_to_insert)) {
279 0           push @new_tracks,($tracks{$type} =
280             $panel->insert_track($position_to_insert++,$feature,@config));
281             } else {
282 0           push @new_tracks,($tracks{$type} =
283             $panel->add_track($feature,@config));
284             }
285             };
286 0 0         warn $@ if $@;
287             }
288              
289             # reconfigure bumping, etc
290 0           for my $type (keys %type_count) {
291 0           my $type_count = $type_count{$type};
292 0 0         my $do_bump = defined $track_configs{$type}{-bump} ? $track_configs{$type}{-bump}
    0          
    0          
    0          
    0          
    0          
    0          
293             : $options == 0 ? $type_count <= $max_bump
294             : $options == 1 ? 0
295             : $options == 2 ? 1
296             : $options == 3 ? 1
297             : $options == 4 ? 2
298             : $options == 5 ? 2
299             : 0;
300              
301 0           my $maxed_out = $type_count > $max_label;
302 0 0         my $conf_label = defined $track_configs{$type}{-label}
303             ? $track_configs{$type}{-label}
304             : 1;
305              
306 0 0 0       my $do_label = $options == 0 ? !$maxed_out && $conf_label
    0          
    0          
307             : $options == 3 ? 1
308             : $options == 5 ? 1
309             : 0;
310             # warn "type = $type, label = $do_label, do_bump = $do_bump";
311              
312 0           my $track = $tracks{$type};
313              
314 0           my $factory = $track->factory;
315 0 0         $factory->set_option(connector => 'none') if !$do_bump;
316 0           $factory->set_option(bump => $do_bump);
317 0           $factory->set_option(label => $do_label);
318             }
319 0           my $track_count = keys %tracks;
320 0 0         return wantarray ? ($track_count,$panel,\@new_tracks) : $track_count;
321             }
322              
323             sub get_cached_stylesheet {
324 0     0 0   my $self = shift;
325 0           my $tmpdir = File::Spec->tmpdir;
326 0           my $program = basename($0);
327 0           my $user = (getpwuid($>))[0];
328 0           my $url = $self->das->name.'/stylesheet';
329 0           foreach ($program,$user,$url) {
330 0           tr/a-zA-Z0-9_-/_/c;
331             }
332              
333 0           my $dir = File::Spec->catfile($tmpdir,"$program-$user");
334 0 0 0       mkpath($dir) or die "$dir: $!" unless -d $dir;
335 0           my $path = File::Spec->catfile($dir,$url);
336              
337 0           my $stylesheet;
338              
339 0           eval {
340              
341             # cache for 5 minutes
342 0           my $mtime = (stat($path))[9];
343 0 0 0       if ($mtime && ((time() - $mtime)/60) < 5.0) {
344 0 0         open my $f,'<',$path or die "$path: $!";
345 0           my $s;
346 0           $s .= $_ while <$f>;
347 0           close $f;
348 0           my $VAR1;
349 0           $stylesheet = eval "$s; \$VAR1";
350 0 0         warn $@ if $@;
351 0           utime undef,undef,$path;
352             }
353            
354             else {
355 0           $stylesheet = $self->das->stylesheet;
356 0           my $d = Data::Dumper->new([$stylesheet]);
357 0           $d->Purity(1);
358 0 0         open my $f,">",$path or die "$path: $!";
359 0           print $f $d->Dump;
360 0           close $f;
361             }
362            
363 0           return $stylesheet;
364             };
365              
366             # something went wrong, so revert to non-cached behavior
367 0           return $self->das->stylesheet;
368             }
369              
370             1;
371              
372             __END__
373              
374             =head1 NAME
375              
376             Bio::Das::Segment - Serial access to Bio::Das sequence "segments"
377              
378             =head1 SYNOPSIS
379              
380             # SERIALIZED API
381             my $das = Bio::Das->new(-server => 'http://www.wormbase.org/db/das',
382             -dsn => 'elegans',
383             -aggregators => ['primary_transcript','clone']);
384             my $segment = $das->segment('Chr1');
385             my @features = $segment->features;
386             my $dna = $segment->dna;
387              
388             =head1 DESCRIPTION
389              
390             The Bio::Das::Segment class is used to retrieve information about a
391             genomic segment from a DAS server. You may retrieve a list of
392             (optionally filtered) annotations on the segment, a summary of the
393             feature types available across the segment, or the segment's DNA
394             sequence.
395              
396             =head2 OBJECT CREATION
397              
398             Bio::Das::Segment objects are created by calling the segment() method
399             of a Bio::Das object created earlier. See L<Bio::Das> for details.
400              
401             =head2 OBJECT METHODS
402              
403             Once created, a number of methods allow you to query the segment for
404             its features and/or DNA.
405              
406             =over 4
407              
408             =item $ref= $segment->ref
409              
410             Return the reference point that establishes the coordinate system for
411             this segment, e.g. "chr1".
412              
413             =item $start = $segment->start
414              
415             Return the starting coordinate of this segment.
416              
417             =item $end = $segment->end
418              
419             Return the ending coordinate of this segment.
420              
421             =item @features = $segment->features(@filter)
422              
423             =item @features = $segment->features(-type=>$type,-category=>$category)
424              
425             The features() method returns annotations across the length of the
426             segment. Two forms of this method are recognized. In the first form,
427             the B<@filter> argument contains a series of category names to
428             retrieve. Each category may be further qualified by a regular
429             expression which will be used to filter features by their type ID.
430             Filters have the format "category:typeID", where the category and type
431             are separated by a colon. The typeID and category names are treated
432             as an unanchored regular expression (but see the note below). As a
433             special cse, you may use a type of "transcript" to fetch composite
434             transcript model objects (the union of exons, introns and cds
435             features).
436              
437             Example 1: retrieve all the features in the "similarity" and
438             "experimental" categories:
439              
440             @features = $segment->features('similarity','experimental');
441              
442             Example 2: retrieve all the similarity features of type EST_elegans
443             and EST_GENOME:
444              
445             @features = $segment->features('similarity:EST_elegans','similarity:EST_GENOME');
446              
447             Example 3: retrieve all similarity features that have anything to do
448             with ESTs:
449              
450             @features = $segment->features('similarity:EST');
451              
452             Example 4: retrieve all the transcripts and experimental data
453              
454             @genes = $segment->features('transcript','experimental')
455              
456             In the second form, the type and categories are given as named
457             arguments. You may use regular expressions for either typeID or
458             category. It is also possible to pass an array reference for either
459             argument, in which case the DAS server will return the union of the
460             features.
461              
462             Example 5: retrieve all the features in the "similarity" and
463             "experimental" categories:
464              
465             @features = $segment->features(-category=>['similarity','experimental']);
466              
467             Example 6: retrieve all the similarity features of type EST_elegans
468             and EST_GENOME:
469              
470             @features = $segment->features(-category=>'similarity',
471             -type =>/^EST_(elegans|GENOME)$/
472             );
473              
474             =item $dna = $segment->dna
475              
476             Return the DNA corresponding to the segment. The return value is a
477             simple string, and not a Bio::Sequence object. This method may return
478             undef when used with a DAS annotation server that does not maintain a
479             copy of the DNA.
480              
481             =item @types = $segment->types
482              
483             =item $count = $segment->types($type)
484              
485             This methods summarizes the feature types available across this
486             segment. The items in this list can be used as arguments to
487             features().
488              
489             Called with no arguments, this method returns an array of
490             Das::Segment::Type objects. See the manual page for details. Called
491             with a TypeID, the method will return the number of instances of the
492             named type on the segment, or undef if the type is invalid. Because
493             the list and count of types is cached, there is no penalty for
494             invoking this method several times.
495              
496             =back
497              
498              
499             =head1 AUTHOR
500              
501             Lincoln Stein <lstein@cshl.org>.
502              
503             Copyright (c) 2003 Cold Spring Harbor Laboratory
504              
505             This library is free software; you can redistribute it and/or modify
506             it under the same terms as Perl itself. See DISCLAIMER.txt for
507             disclaimers of warranty.
508              
509             =head1 SEE ALSO
510              
511             L<Bio::Das>
512              
513             =cut