File Coverage

lib/Bio/Graphics/Wiggle/Loader.pm
Criterion Covered Total %
statement 197 377 52.2
branch 46 178 25.8
condition 27 106 25.4
subroutine 27 42 64.2
pod 5 27 18.5
total 302 730 41.3


line stmt bran cond sub pod time code
1             package Bio::Graphics::Wiggle::Loader;
2              
3             =head1 SYNOPSIS
4              
5             my $loader = Bio::Graphics::Wiggle::Loader->new('/base/directory/for/wigfiles','wibfilename');
6             my $fh = IO::File->new('uploaded_file.txt');
7             $loader->load($fh);
8              
9             my $gff3_file = $loader->featurefile('gff3',$method,$source);
10             my $featurefile = $loader->featurefile('featurefile');
11             my @features = $loader->features();
12              
13             =head1 USAGE
14              
15             This module loads Bio::Graphics::Wiggle files from source files that
16             use Jim Kent's "WIG" format:
17              
18             http://genome.ucsc.edu/google/goldenPath/help/wiggle.html
19              
20             Several data sets can be grouped together in a single WIG source
21             file. The load() method accepts the path to a WIG source file, and
22             will create one or more .wib ("wiggle binary") databases of
23             quantitative data in the directory indicated when you created the
24             loader. Call the featurefile() method to return a text file in either
25             GFF3 or Bio::Graphics::FeatureFile format, suitable for loading into a
26             gbrowse database.
27              
28             =head2 METHODS
29              
30             =over 4
31              
32             =item $loader = Bio::Graphics::Wiggle::Loader->new('/base/directory' [,'my_data'])
33              
34             Create a new loader. The first argument specifies the base directory
35             in which the loaded .wib files will be created. The second argument
36             specifies the base name for the created .wib files, or "track" if not
37             specified.
38              
39             =item $loader->load($fh)
40              
41             Load the data from a source WIG file opened on a filehandle.
42              
43             =item $data = $loader->featurefile($type [,$method,$source])
44              
45             Return the data corresponding to a GFF3 or
46             Bio::Graphics::FeatureFile. The returned file will have one feature
47             per WIG track, and a properly formatted "wigfile" attribute that
48             directs Bio::Graphics to the location of the quantitative data.
49              
50             $type is one of "gff3" or "featurefile". In the case of "gff3", you
51             may specify an optional method and source for use in describing each
52             feature. In the case of "featurefile", the returned file will contain
53             GBrowse stanzas that describe a reasonable starting format to display
54             the data.
55              
56             =item @features = $loader->features
57              
58             Returns one or more Bio::Graphics::Features objects, which can be used to
59             create Bio::Graphics tracks with the wiggle_xyplot (and related) glyphs.
60              
61             =item $loader->allow_sampling(1)
62              
63             If allow_sampling() is passed a true value, then very large files
64             (more than 5 MB) will undergo a sampling procedure to find their
65             minimum and maximum values and standard deviation. Otherwise, file
66             will be read in its entirety to generate those statistics.
67              
68             =back
69              
70             =head2 EXTENSIONS
71              
72             Several extensions to the WIG format "track" declaration are recognized.
73              
74             =over 4
75              
76             =item transform=<transform>
77              
78             Specify a transform to be performed on all numeric data within this
79             track prior to loading into the binary wig file. Currently, the
80             following three declarations are recognized:
81              
82             transform=logtransform y' = 0 for y == 0
83             y' = log(y) for y > 0
84             y' = -log(-y) for y < 0
85            
86              
87             transform=logsquared y' = log(y**2) for y != 0
88             y' = 0 for y == 0
89            
90             transform=none y' = y (no transform - the default)
91              
92             =item trim=<trim>
93              
94             Specify a trimming function to be performed on the data prior to
95             scaling. Currently, the following trim functions are recognized:
96              
97             trim=stdev1 trim to plus/minus 1 standard deviation of the mean
98             trim=stdev2 trim to plus/minus 2 standard deviations of the mean (default)
99             trim=stdevN trim to plus/minus N standard deviations of the mean
100             trim=none no trimming
101              
102             =back
103              
104             Example entended track declaration:
105              
106             track type=wiggle_0 name="example" description="20 degrees, 2 hr" \
107             trim=stdev2 transform=logsquared
108              
109             =cut
110              
111 1     1   250532 use strict;
  1         3  
  1         39  
112              
113 1     1   5 use Carp 'croak';
  1         2  
  1         51  
114 1     1   2437 use Statistics::Descriptive;
  1         33266  
  1         32  
115 1     1   10 use IO::Seekable;
  1         2  
  1         69  
116 1     1   6 use File::Spec;
  1         2  
  1         20  
117 1     1   732 use Bio::Graphics::Wiggle;
  1         3  
  1         32  
118 1     1   877 use Bio::Graphics::FeatureFile;
  1         4  
  1         43  
119 1     1   12 use Text::ParseWords();
  1         2  
  1         54  
120 1     1   10338 use File::stat;
  1         10785  
  1         8  
121 1     1   2799 use CGI 'escape';
  1         22769  
  1         9  
122              
123 1     1   122 use vars '%color_name';
  1         2  
  1         52  
124              
125             # If a WIG file is very large (> 5 Mb)
126 1     1   6 use constant BIG_FILE => 5_000_000;
  1         3  
  1         49  
127 1     1   5 use constant BIG_FILE_SAMPLES => 5_000; # number of probes to make
  1         2  
  1         47  
128 1     1   5 use constant DEFAULT_METHOD => 'microarray_oligo';
  1         2  
  1         38  
129 1     1   4 use constant DEFAULT_SOURCE => '.';
  1         2  
  1         5344  
130              
131             sub new {
132 1     1 1 691 my $class = shift;
133 1 50       5 my $base = shift
134             or croak "Usage: Bio::Graphics::Wiggle::Loader->new('/base/path','trackname')";
135 1   50     5 my $trackname = shift || 'track';
136 1   50     10 my $wigclass = shift || 'Bio::Graphics::Wiggle';
137 1 50 33     27 -d $base && -w _ or croak "$base is not a writeable directory";
138 1   33     18 return bless {
139             base => $base,
140             tracks => {},
141             trackname => $trackname,
142             tracknum => '000',
143             track_options => {},
144             allow_sampling => 0,
145             wigclass => $wigclass,
146             },ref $class || $class;
147             }
148             sub allow_sampling {
149 1     1 1 2 my $self = shift;
150 1         2 my $d = $self->{allow_sampling};
151 1 50       4 $self->{allow_sampling} = shift if @_;
152 1         5 $d;
153             }
154             sub wigclass {
155 1     1 0 2 my $self = shift;
156 1         3 my $d = $self->{wigclass};
157 1 50       5 $self->{wigclass} = shift if @_;
158 1         3 return $d;
159             }
160 0     0 0 0 sub basedir { shift->{base} }
161 0     0 0 0 sub wigfiles { shift->{wigfiles} }
162             sub conf_stanzas {
163 0     0 0 0 my $self = shift;
164 0         0 my ($method,$source) = @_;
165 0   0     0 $method ||= DEFAULT_METHOD;
166 0   0     0 $source ||= DEFAULT_SOURCE;
167              
168 0         0 my $tracks = $self->{tracks};
169 0         0 my @lines = ();
170 0         0 for my $track (sort keys %$tracks) {
171              
172 0         0 my $options = $tracks->{$track}{display_options};
173 0   0     0 my $name = $options->{name} ||= $track;
174              
175 0   0     0 $options->{visibility} ||= 'dense';
176 0 0 0     0 $options->{color} ||= $options->{visibility} =~ /pack/i ? '255,0,0' : '0,0,0';
177 0 0 0     0 $options->{altColor} ||= $options->{visibility} =~ /pack/i ? '0,0,255' : '0,0,0';
178              
179             # stanza
180 0         0 push @lines,"[$track]";
181 0 0       0 if (my $graph_type = $options->{glyph}) {
182 0 0       0 if ($graph_type =~ /box/) {
183 0         0 push @lines, "glyph = wiggle_box";
184             }
185             else {
186 0 0       0 push @lines,"glyph = ".
187             ($graph_type =~/density/ ? 'wiggle_density' : 'wiggle_xyplot');
188             }
189             }
190             else {
191 0 0       0 push @lines,"glyph = ".
192             ($options->{visibility}=~/pack/ ? 'wiggle_density' : 'wiggle_xyplot');
193             }
194 0 0       0 push @lines,"key = $options->{name}"
195             if $options->{name};
196 0 0       0 push @lines,"description = $options->{description}"
197             if $options->{description};
198 0 0       0 if (my $color = $options->{color}) {
199 0         0 push @lines,"bgcolor=".format_color($color);
200             }
201 0 0       0 if (my $color = $options->{altColor}) {
202 0         0 push @lines,"fgcolor=" . format_color($color);
203             }
204 0 0 0     0 if (exists $options->{viewLimits} and my ($low,$hi) = split ':',$options->{viewLimits}) {
205 0         0 push @lines,"min_score = $low";
206 0         0 push @lines,"max_score = $hi";
207             }
208 0 0 0     0 if (exists $options->{maxHeightPixels} and my ($max,$default,$min) =
209             split ':',$options->{maxHeightPixels}) {
210 0         0 push @lines,"height = $default";
211             }
212 0 0       0 push @lines,"smoothing = $options->{windowingFunction}"
213             if $options->{windowingFunction};
214            
215 0   0     0 my $smoothing_window = $options->{smoothingWindow} || 0;
216            
217 0 0       0 push @lines,"smoothing window = $options->{smoothingWindow}"
218             if $options->{smoothingWindow};
219 0         0 push @lines,'';
220             }
221 0         0 return join "\n",@lines;
222             }
223              
224             sub featurefile {
225 1     1 1 3 my $self = shift;
226 1         2 my $type = shift;
227 1         3 my ($method,$source) = @_;
228              
229 1   50     9 $method ||= DEFAULT_METHOD;
230 1   50     6 $source ||= DEFAULT_SOURCE;
231              
232 1   50     4 $type ||= 'featurefile';
233 1 50       8 $type =~ /^(gff3|featurefile)$/i
234             or croak "featurefile type must be one of 'gff3' or 'featurefile'";
235              
236 1         2 my @lines;
237 1         3 my $tracks = $self->{tracks};
238              
239 1 50       6 if ($type eq 'gff3') {
240 1         4 push @lines,"##gff-version 3","";
241             }
242             else {
243 0         0 push @lines,$self->conf_stanzas($method,$source),"";
244             }
245              
246 1         7 for my $track (sort keys %$tracks) {
247 1         4 my $options = $tracks->{$track}{display_options};
248 1   33     44 my $name = $options->{name} ||= $track;
249 1         3 my $seqids = $tracks->{$track}{seqids};
250 1         10 my $note = escape($options->{description});
251 1         9 my @attributes;
252 1 50       6 push @attributes,qq(Name=$name) if defined $name;
253 1 50       4 push @attributes,qq(Note=$note) if defined $note;
254              
255             # data, sorted by chromosome
256 1         5 my @seqid = sort keys %$seqids;
257              
258 1         3 for my $seqid (@seqid) {
259 1 50       23 $seqid or next;
260 1         10 $tracks->{$track}{seqids}{$seqid}{wig}->write();
261 1         7 my $attributes = join ';',(@attributes,"wigfile=$seqids->{$seqid}{wigpath}");
262 1 50       4 if ($type eq 'gff3') {
263 1         11 push @lines,join "\t",($seqid,$source,$method,
264             $seqids->{$seqid}{start},
265             $seqids->{$seqid}{end},
266             '.','.','.',
267             $attributes
268             );
269             } else {
270 0         0 push @lines,'';
271 0         0 push @lines,"reference=$seqid";
272 0         0 push @lines,"$track $seqid.data $seqids->{$seqid}{start}..$seqids->{$seqid}{end} $attributes";
273             }
274              
275             }
276              
277             }
278              
279 1         13 return join("\n",@lines)."\n";
280             }
281              
282             sub features {
283 0     0 1 0 my $self = shift;
284 0         0 my $text = $self->featurefile('featurefile');
285 0         0 my $file = Bio::Graphics::FeatureFile->new(-text=>$text);
286 0         0 return $file->features;
287             }
288              
289              
290             sub load {
291 1     1 1 848 my $self = shift;
292 1         2 my $infh = shift;
293 1         2 my $format = 'none';
294              
295 1         2 local $_;
296 1         20 LINE: while (<$infh>) {
297 1         3 chomp;
298 1 50       4 next if /^#/;
299 1 50       7 next unless /\S/;
300              
301 1 50       4 if (/^track/) {
302 0         0 $self->process_track_line($_);
303 0         0 next;
304             }
305              
306 1 50       5 if (/^fixedStep/) {
307 0         0 $self->process_fixed_step_declaration($_);
308 0         0 $format = 'fixed';
309             }
310              
311 1 50       5 if (/^variableStep/) {
312 1         5 $self->process_variable_step_declaration($_);
313 1         2 $format = 'variable';
314             }
315              
316 1 50       8 if (/^\S+\s+\d+\s+\d+\s+-?[\dEe.]+/) {
317 0         0 $self->process_first_bedline($_);
318 0         0 $format = 'bed';
319             }
320              
321 1 50       4 if ($format ne 'none') {
322             # remember where we are, find min and max values, return
323 1         2 my $pos = tell($infh);
324 1 50 33     10 $self->minmax($infh,$format eq 'bed' ? $_ : '')
    50          
325             unless $self->{track_options}{chrom} &&
326             exists $self->current_track->{seqids}{$self->{track_options}{chrom}}{min};
327 1         66 seek($infh,$pos,0);
328              
329 1 50       6 $self->process_bed($infh,$_) if $format eq 'bed';
330 1 50       4 $self->process_fixedline($infh) if $format eq 'fixed';
331 1 50       8 $self->process_variableline($infh) if $format eq 'variable';
332              
333 1         4 $format = 'none';
334             }
335              
336 1 50 33     14 redo LINE if defined $_ && /^(track|variableStep|fixedStep)/;
337             }
338              
339 1         27 return 1;
340             }
341              
342             sub process_track_line {
343 0     0 0 0 my $self = shift;
344 0         0 my $line = shift;
345 0         0 my @tokens = shellwords($line);
346 0         0 shift @tokens;
347 0         0 my %options = map {split '='} @tokens;
  0         0  
348 0 0       0 $options{type} eq 'wiggle_0' or croak "invalid/unknown wiggle track type $options{type}";
349 0         0 delete $options{type};
350 0         0 $self->{tracknum}++;
351 0         0 $self->current_track->{display_options} = \%options;
352             }
353              
354             sub process_fixed_step_declaration {
355 0     0 0 0 my $self = shift;
356 0         0 my $line = shift;
357 0         0 my @tokens = shellwords($line);
358 0         0 shift @tokens;
359 0         0 my %options = map {split '='} @tokens;
  0         0  
360 0 0       0 exists $options{chrom} or croak "invalid fixedStep line: need a chrom option";
361 0 0       0 exists $options{start} or croak "invalid fixedStep line: need a start option";
362 0 0       0 exists $options{step} or croak "invalid fixedStep line: need a step option";
363 0         0 $self->{track_options} = \%options;
364             }
365              
366             sub process_variable_step_declaration {
367 1     1 0 2 my $self = shift;
368 1         2 my $line = shift;
369 1         4 my @tokens = shellwords($line);
370 1         2 shift @tokens;
371 1         3 my %options = map {split '='} @tokens;
  2         11  
372 1 50       4 exists $options{chrom} or croak "invalid variableStep line: need a chrom option";
373 1         5 $self->{track_options} = \%options;
374             }
375              
376             sub process_first_bedline {
377 0     0 0 0 my $self = shift;
378 0         0 my $line = shift;
379 0         0 my @tokens = shellwords($line);
380 0         0 $self->{track_options} = {chrom => $tokens[0]};
381             }
382              
383             sub current_track {
384 16     16 0 23 my $self = shift;
385 16   100     124 return $self->{tracks}{$self->{tracknum}} ||= {};
386             }
387              
388             sub minmax {
389 1     1 0 3 my $self = shift;
390 1         2 my ($infh,$bedline) = @_;
391 1         2 local $_;
392              
393 1         4 my $transform = $self->get_transform;
394              
395 1   50     3 my $seqids = ($self->current_track->{seqids} ||= {});
396 1         3 my $chrom = $self->{track_options}{chrom};
397              
398 1 50 33     5 if ($self->allow_sampling && (my $size = stat($infh)->size) > BIG_FILE) {
399 0         0 warn "Wiggle file is very large; resorting to genome-wide sample statistics for $chrom.\n";
400 0   0     0 $self->{FILEWIDE_STATS} ||= $self->sample_file($infh,BIG_FILE_SAMPLES);
401 0         0 for (keys %{$self->{FILEWIDE_STATS}}) {
  0         0  
402 0         0 $seqids->{$chrom}{$_} = $self->{FILEWIDE_STATS}{$_};
403             }
404 0         0 return;
405             }
406              
407 1         2 my %stats;
408 1 50       36 if ($bedline) { # left-over BED line
409 0         0 my @tokens = split /\s+/,$bedline;
410 0         0 my $seqid = $tokens[0];
411 0         0 my $value = $tokens[-1];
412 0 0       0 $value = $transform->($self,$value) if $transform;
413 0   0     0 $stats{$seqid} ||= Statistics::Descriptive::Sparse->new();
414 0         0 $stats{$seqid}->add_data($value);
415             }
416              
417 1         6 while (<$infh>) {
418 3809 50       374319 last if /^track/;
419 3809 50 33     11551 last if /chrom=(\S+)/ && $1 ne $chrom;
420 3809 50       20530 next if /^\#|fixedStep|variableStep/;
421 3809 50       15713 my @tokens = split(/\s+/,$_) or next;
422 3809 50       9659 my $seqid = @tokens > 3 ? $tokens[0] : $chrom;
423 3809         5869 my $value = $tokens[-1];
424 3809 50       7930 $value = $transform->($self,$value) if $transform;
425 3809   66     10608 $stats{$seqid} ||= Statistics::Descriptive::Sparse->new();
426 3809         14149 $stats{$seqid}->add_data($value);
427             }
428              
429 1         97 for my $seqid (keys %stats) {
430 1         5 $seqids->{$seqid}{min} = $stats{$seqid}->min();
431 1         11 $seqids->{$seqid}{max} = $stats{$seqid}->max();
432 1         9 $seqids->{$seqid}{mean} = $stats{$seqid}->mean();
433 1         10 $seqids->{$seqid}{stdev} = $stats{$seqid}->standard_deviation();
434             }
435             }
436              
437             sub sample_file {
438 0     0 0 0 my $self = shift;
439              
440 0         0 my ($fh,$samples) = @_;
441              
442 0         0 my $transform = $self->get_transform;
443              
444 0         0 my $stats = Statistics::Descriptive::Sparse->new();
445              
446 0         0 my $size = stat($fh)->size;
447 0         0 my $count=0;
448 0         0 while ($count < $samples) {
449 0 0       0 seek($fh,int(rand $size),0) or die;
450 0         0 scalar <$fh>; # toss first line
451 0         0 my $line = <$fh>; # next full line
452 0 0       0 $line or next;
453 0         0 my @tokens = split /\s+/,$line;
454 0         0 my $value = $tokens[-1];
455 0 0       0 next unless $value =~ /^[\d\seE.+-]+$/; # non-numeric
456 0 0       0 $value = $transform->($self,$value) if $transform;
457 0         0 $stats->add_data($value);
458 0         0 $count++;
459             }
460              
461             return {
462 0         0 min => $stats->min,
463             max => $stats->max,
464             mean => $stats->mean,
465             stdev => $stats->standard_deviation,
466             };
467             }
468              
469             sub get_transform {
470 2     2 0 5 my $self = shift;
471 2         7 my $transform = $self->current_track->{display_options}{transform};
472 2 50       7 return $self->can($transform) if $transform;
473             }
474              
475             # one and only transform currently defined
476             # Natural log of the square of the value.
477             # Return 0 if the value is 0
478             sub logsquared {
479 0     0 0 0 my $self = shift;
480 0         0 my $value = shift;
481 0 0       0 return 0 if $value == 0;
482 0         0 return log($value**2);
483             }
484              
485             sub logtransform {
486 0     0 0 0 my $self = shift;
487 0         0 my $value = shift;
488 0 0       0 return 0 if $value == 0;
489 0 0       0 if ($value < 0) {
490 0         0 return -log(-$value);
491             } else {
492 0         0 return log($value);
493             }
494             }
495              
496             sub process_bed {
497 0     0 0 0 my $self = shift;
498 0         0 my $infh = shift;
499 0         0 my $oops = shift;
500 0         0 my $transform = $self->get_transform;
501 0 0       0 $self->process_bedline($oops) if $oops;
502 0         0 while (<$infh>) {
503 0 0       0 last if /^track/;
504 0 0       0 next if /^#/;
505 0         0 chomp;
506 0         0 $self->process_bedline($_);
507             }
508             }
509              
510             sub process_bedline {
511 0     0 0 0 my $self = shift;
512 0         0 my ($line,$transform) = @_;
513              
514 0         0 my ($seqid,$start,$end,$value) = split /\s+/,$line;
515 0 0       0 $value = $transform->($self,$value) if $transform;
516 0         0 $start++; # to 1-based coordinates
517              
518 0         0 my $wigfile = $self->wigfile($seqid);
519 0         0 $wigfile->set_range($start=>$end, $value);
520              
521             # update span
522 0 0 0     0 $self->current_track->{seqids}{$seqid}{start} = $start
523             unless exists $self->current_track->{seqids}{$seqid}{start}
524             and $self->current_track->{seqids}{$seqid}{start} < $start;
525              
526 0 0 0     0 $self->current_track->{seqids}{$seqid}{end} = $end
527             unless exists $self->current_track->{seqids}{$seqid}{end}
528             and $self->current_track->{seqids}{$seqid}{end} > $end;
529             }
530              
531             sub process_fixedline {
532 0     0 0 0 my $self = shift;
533 0         0 my $infh = shift;
534 0         0 my $seqid = $self->{track_options}{chrom};
535 0         0 my $wigfile = $self->wigfile($seqid);
536 0         0 my $start = $self->{track_options}{start};
537 0         0 my $step = $self->{track_options}{step};
538 0         0 my $span = $wigfile->span;
539              
540             # update start and end positions
541 0   0     0 $self->{track_options}{span} ||= $wigfile->span || 1;
      0        
542 0         0 my $chrom = $self->current_track->{seqids}{$seqid};
543 0 0 0     0 $chrom->{start} = $start
544             if !defined $chrom->{start} || $chrom->{start} > $start;
545 0         0 my $end = $chrom->{start} + $span - 1;
546 0 0 0     0 $chrom->{end} = $end
547             if !defined $chrom->{end} || $chrom->{end} < $end;
548              
549 0         0 my $transform = $self->get_transform;
550              
551             # write out data in 500K chunks for efficiency
552 0         0 my @buffer;
553 0         0 while (<$infh>) {
554 0 0       0 last if /^(track|variableStep|fixedStep)/;
555 0 0       0 next if /^#/;
556 0         0 chomp;
557 0         0 push @buffer,$_;
558 0 0       0 if (@buffer >= 500_000) {
559 0 0       0 @buffer = map {$transform->($self,$_)} @buffer if $transform;
  0         0  
560 0         0 $wigfile->set_values($start=>\@buffer);
561 0         0 my $big_step = $step * @buffer;
562 0         0 $start += $big_step;
563 0         0 $self->current_track->{seqids}{$seqid}{end} = $start + $big_step - 1 + $span;
564 0         0 @buffer = (); # reset at the end
565             }
566              
567             }
568 0 0       0 @buffer = map {$transform->($self,$_)} @buffer if $transform;
  0         0  
569 0 0       0 $wigfile->set_values($start=>\@buffer) if @buffer;
570 0         0 $self->current_track->{seqids}{$seqid}{end} =
571             $start + @buffer*$step - 1 + $span;
572             }
573              
574             sub process_variableline {
575 1     1 0 3 my $self = shift;
576 1         2 my $infh = shift;
577 1         5 my $seqid = $self->{track_options}{chrom};
578 1         3 my $chrom = $self->current_track->{seqids}{$seqid};
579 1         6 my $wigfile = $self->wigfile($seqid);
580 1         5 my $span = $wigfile->span;
581 1         4 my $transform = $self->get_transform;
582              
583 1         20 while (<$infh>) {
584 3809 50       13446 last if /^(track|variableStep|fixedStep)/;
585 3809 50       7839 next if /^#/;
586 3809         4642 chomp;
587 3809 50       14780 my ($start,$value) = split /\s+/ or next;
588 3809 50       7666 $value = $transform->($self,$value) if $transform;
589 3809 50       4649 eval {
590 3809         10888 $wigfile->set_value($start=>$value);
591 3809         9224 1;
592             } or croak "Data error on line $.: $_\nDetails: $@";
593              
594             # update span
595 3809 100 66     20716 $chrom->{start} = $start
596             if !defined $chrom->{start} || $chrom->{start} > $start;
597 3809         6108 my $end = $start + $span - 1;
598 3809 50 66     26501 $chrom->{end} = $end
599             if !defined $chrom->{end} || $chrom->{end} < $end;
600              
601             }
602              
603 1   33     9 $self->current_track->{seqids}{$seqid}{end}
604             ||= $self->current_track->{seqids}{$seqid}{start};
605             }
606              
607             sub wigfile {
608 1     1 0 2 my $self = shift;
609 1         2 my $seqid = shift;
610 1         11 my $ts = time();
611 1         3 my $current_track = $self->{tracknum};
612 1   50     5 my $tname = $self->{trackname} || 'track';
613 1 50       4 unless (exists $self->current_track->{seqids}{$seqid}{wig}) {
614 1         29 my $path = File::Spec->catfile($self->{base},"$tname\_$current_track.$seqid.$ts.wib");
615 1         3 my @stats;
616 1         3 foreach (qw(min max mean stdev)) {
617 4   50     9 my $value = $self->current_track->{seqids}{$seqid}{$_} ||
618             $self->{FILEWIDE_STATS}{$_} || next;
619 4         11 push @stats,($_=>$value);
620             }
621              
622 1   50     12 my $step = $self->{track_options}{step} || 1;
623 1   50     6 my $span = $self->{track_options}{span} ||
624             $self->{track_options}{step} ||
625             1;
626 1   50     4 my $trim = $self->current_track->{display_options}{trim} || 'stdev10';
627 1         3 my $transform = $self->current_track->{display_options}{transform};
628 1         6 my $class = $self->wigclass;
629 1 50       17 unless ($class->can('new')) {
630 0         0 warn "loading $class";
631 0         0 eval "require $class";
632 0 0       0 die $@ if $@;
633             }
634 1         13 my $wigfile = $class->new(
635             $path,
636             1,
637             {
638             seqid => $seqid,
639             step => $step,
640             span => $span,
641             trim => $trim,
642             @stats,
643             },
644             );
645 1 50       7 $wigfile or croak "Couldn't create wigfile $wigfile: $!";
646 1         5 $self->current_track->{seqids}{$seqid}{wig} = $wigfile;
647 1         4 $self->current_track->{seqids}{$seqid}{wigpath} = $path;
648             }
649 1         4 return $self->current_track->{seqids}{$seqid}{wig};
650             }
651              
652             sub format_color {
653 0     0 0 0 my $rgb = shift;
654 0 0       0 return $rgb unless $rgb =~ /\d+,\d+,\d+/;
655 0         0 my ($r,$g,$b) = split ',',$rgb;
656 0         0 my $hex = '#'.join '',map {sprintf("%02X",$_)}($r,$g,$b);
  0         0  
657 0         0 return translate_color($hex);
658             }
659              
660             # use English names for the most common colors
661             sub translate_color {
662 0     0 0 0 my $clr = shift;
663 0 0       0 unless (%color_name) {
664 0         0 while (<DATA>) {
665 0         0 chomp;
666 0 0       0 my ($hex,$name) = split or next;
667 0         0 $color_name{$hex} = $name;
668             }
669             }
670 0   0     0 return $color_name{$clr} || $clr;
671             }
672              
673             # work around an annoying uninit variable warning from Text::Parsewords
674             sub shellwords {
675 1     1 0 3 my @args = @_;
676 1 50       3 return unless @args;
677 1         3 foreach(@args) {
678 1         3 s/^\s+//;
679 1         3 s/\s+$//;
680 1 50       6 $_ = '' unless defined $_;
681             }
682 1         8 my @result = Text::ParseWords::shellwords(@args);
683 1         218 return @result;
684             }
685              
686             1;
687              
688              
689             __DATA__
690             #000000 black
691             #FFFFFF white
692             #0000FF blue
693             #00FF00 green
694             #FF0000 red
695             #FFFF00 yellow
696             #00FFFF cyan
697             #FF00FF magenta
698             #C0C0C0 gray
699              
700              
701             __END__
702              
703             =head1 SEE ALSO
704              
705             L<Bio::Graphics::Wiggle>,
706             L<Bio::Graphics::Glyph::wiggle_xyplot>,
707             L<Bio::Graphics::Glyph::wiggle_density>,
708             L<Bio::Graphics::Panel>,
709             L<Bio::Graphics::Glyph>,
710             L<Bio::Graphics::Feature>,
711             L<Bio::Graphics::FeatureFile>
712              
713             =head1 AUTHOR
714              
715             Lincoln Stein E<lt>lstein@cshl.orgE<gt>.
716              
717             Copyright (c) 2007 Cold Spring Harbor Laboratory
718              
719             This package and its accompanying libraries is free software; you can
720             redistribute it and/or modify it under the terms of the GPL (either
721             version 1, or at your option, any later version) or the Artistic
722             License 2.0. Refer to LICENSE for the full license text. In addition,
723             please see DISCLAIMER.txt for disclaimers of warranty.
724              
725             =cut