File Coverage

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


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=
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=
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   205691 use strict;
  1         2  
  1         32  
112              
113 1     1   4 use Carp 'croak';
  1         2  
  1         47  
114 1     1   720 use Statistics::Descriptive;
  1         22035  
  1         35  
115 1     1   13 use IO::Seekable;
  1         2  
  1         82  
116 1     1   4 use File::Spec;
  1         3  
  1         20  
117 1     1   768 use Bio::Graphics::Wiggle;
  1         4  
  1         53  
118 1     1   1071 use Bio::Graphics::FeatureFile;
  1         4  
  1         108  
119 1     1   16 use Text::ParseWords();
  1         3  
  1         26  
120 1     1   1070 use File::stat;
  1         8921  
  1         7  
121 1     1   1617 use CGI 'escape';
  1         41888  
  1         12  
122              
123 1     1   195 use vars '%color_name';
  1         2  
  1         82  
124              
125             # If a WIG file is very large (> 5 Mb)
126 1     1   8 use constant BIG_FILE => 5_000_000;
  1         3  
  1         111  
127 1     1   7 use constant BIG_FILE_SAMPLES => 5_000; # number of probes to make
  1         2  
  1         83  
128 1     1   6 use constant DEFAULT_METHOD => 'microarray_oligo';
  1         2  
  1         70  
129 1     1   8 use constant DEFAULT_SOURCE => '.';
  1         2  
  1         6592  
130              
131             sub new {
132 1     1 1 676 my $class = shift;
133 1 50       4 my $base = shift
134             or croak "Usage: Bio::Graphics::Wiggle::Loader->new('/base/path','trackname')";
135 1   50     4 my $trackname = shift || 'track';
136 1   50     9 my $wigclass = shift || 'Bio::Graphics::Wiggle';
137 1 50 33     15 -d $base && -w _ or croak "$base is not a writeable directory";
138 1   33     14 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         1 my $d = $self->{allow_sampling};
151 1 50       4 $self->{allow_sampling} = shift if @_;
152 1         4 $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         2 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             push @lines,"glyph = ".
192 0 0       0 ($options->{visibility}=~/pack/ ? 'wiggle_density' : 'wiggle_xyplot');
193             }
194             push @lines,"key = $options->{name}"
195 0 0       0 if $options->{name};
196             push @lines,"description = $options->{description}"
197 0 0       0 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             push @lines,"smoothing = $options->{windowingFunction}"
213 0 0       0 if $options->{windowingFunction};
214            
215 0   0     0 my $smoothing_window = $options->{smoothingWindow} || 0;
216            
217             push @lines,"smoothing window = $options->{smoothingWindow}"
218 0 0       0 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 2 my $self = shift;
226 1         2 my $type = shift;
227 1         2 my ($method,$source) = @_;
228              
229 1   50     8 $method ||= DEFAULT_METHOD;
230 1   50     7 $source ||= DEFAULT_SOURCE;
231              
232 1   50     4 $type ||= 'featurefile';
233 1 50       9 $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       4 if ($type eq 'gff3') {
240 1         3 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         2 my $options = $tracks->{$track}{display_options};
248 1   33     14 my $name = $options->{name} ||= $track;
249 1         3 my $seqids = $tracks->{$track}{seqids};
250 1         9 my $note = escape($options->{description});
251 1         8 my @attributes;
252 1 50       5 push @attributes,qq(Name=$name) if defined $name;
253 1 50       3 push @attributes,qq(Note=$note) if defined $note;
254              
255             # data, sorted by chromosome
256 1         4 my @seqid = sort keys %$seqids;
257              
258 1         3 for my $seqid (@seqid) {
259 1 50       3 $seqid or next;
260 1         6 $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             push @lines,join "\t",($seqid,$source,$method,
264             $seqids->{$seqid}{start},
265             $seqids->{$seqid}{end},
266 1         8 '.','.','.',
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         6 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 504 my $self = shift;
292 1         2 my $infh = shift;
293 1         3 my $format = 'none';
294              
295 1         3 local $_;
296 1         15 LINE: while (<$infh>) {
297 1         3 chomp;
298 1 50       4 next if /^#/;
299 1 50       6 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       6 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         8 my $pos = tell($infh);
324             $self->minmax($infh,$format eq 'bed' ? $_ : '')
325             unless $self->{track_options}{chrom} &&
326 1 50 33     8 exists $self->current_track->{seqids}{$self->{track_options}{chrom}}{min};
    50          
327 1         59 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     18 redo LINE if defined $_ && /^(track|variableStep|fixedStep)/;
337             }
338              
339 1         30 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         1 my $line = shift;
369 1         5 my @tokens = shellwords($line);
370 1         2 shift @tokens;
371 1         3 my %options = map {split '='} @tokens;
  2         8  
372 1 50       4 exists $options{chrom} or croak "invalid variableStep line: need a chrom option";
373 1         3 $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 16 my $self = shift;
385 16   100     86 return $self->{tracks}{$self->{tracknum}} ||= {};
386             }
387              
388             sub minmax {
389 1     1 0 2 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         2 my $chrom = $self->{track_options}{chrom};
397              
398 1 50 33     3 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         1 my %stats;
408 1 50       30 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       251647 last if /^track/;
419 3809 50 33     8222 last if /chrom=(\S+)/ && $1 ne $chrom;
420 3809 50       15054 next if /^\#|fixedStep|variableStep/;
421 3809 50       14670 my @tokens = split(/\s+/,$_) or next;
422 3809 50       6534 my $seqid = @tokens > 3 ? $tokens[0] : $chrom;
423 3809         3757 my $value = $tokens[-1];
424 3809 50       5494 $value = $transform->($self,$value) if $transform;
425 3809   66     6894 $stats{$seqid} ||= Statistics::Descriptive::Sparse->new();
426 3809         8240 $stats{$seqid}->add_data($value);
427             }
428              
429 1         91 for my $seqid (keys %stats) {
430 1         6 $seqids->{$seqid}{min} = $stats{$seqid}->min();
431 1         10 $seqids->{$seqid}{max} = $stats{$seqid}->max();
432 1         6 $seqids->{$seqid}{mean} = $stats{$seqid}->mean();
433 1         7 $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 2 my $self = shift;
471 2         5 my $transform = $self->current_track->{display_options}{transform};
472 2 50       6 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             $self->current_track->{seqids}{$seqid}{start} = $start
523             unless exists $self->current_track->{seqids}{$seqid}{start}
524 0 0 0     0 and $self->current_track->{seqids}{$seqid}{start} < $start;
525              
526             $self->current_track->{seqids}{$seqid}{end} = $end
527             unless exists $self->current_track->{seqids}{$seqid}{end}
528 0 0 0     0 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             $chrom->{start} = $start
544 0 0 0     0 if !defined $chrom->{start} || $chrom->{start} > $start;
545 0         0 my $end = $chrom->{start} + $span - 1;
546             $chrom->{end} = $end
547 0 0 0     0 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             $self->current_track->{seqids}{$seqid}{end} =
571 0         0 $start + @buffer*$step - 1 + $span;
572             }
573              
574             sub process_variableline {
575 1     1 0 2 my $self = shift;
576 1         2 my $infh = shift;
577 1         4 my $seqid = $self->{track_options}{chrom};
578 1         5 my $chrom = $self->current_track->{seqids}{$seqid};
579 1         5 my $wigfile = $self->wigfile($seqid);
580 1         3 my $span = $wigfile->span;
581 1         4 my $transform = $self->get_transform;
582              
583 1         16 while (<$infh>) {
584 3809 50       10973 last if /^(track|variableStep|fixedStep)/;
585 3809 50       6271 next if /^#/;
586 3809         4073 chomp;
587 3809 50       17823 my ($start,$value) = split /\s+/ or next;
588 3809 50       6856 $value = $transform->($self,$value) if $transform;
589 3809 50       3826 eval {
590 3809         8479 $wigfile->set_value($start=>$value);
591 3809         7734 1;
592             } or croak "Data error on line $.: $_\nDetails: $@";
593              
594             # update span
595             $chrom->{start} = $start
596 3809 100 66     14691 if !defined $chrom->{start} || $chrom->{start} > $start;
597 3809         4305 my $end = $start + $span - 1;
598             $chrom->{end} = $end
599 3809 50 66     21194 if !defined $chrom->{end} || $chrom->{end} < $end;
600              
601             }
602              
603             $self->current_track->{seqids}{$seqid}{end}
604 1   33     11 ||= $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         15 my $ts = time();
611 1         3 my $current_track = $self->{tracknum};
612 1   50     4 my $tname = $self->{trackname} || 'track';
613 1 50       2 unless (exists $self->current_track->{seqids}{$seqid}{wig}) {
614 1         38 my $path = File::Spec->catfile($self->{base},"$tname\_$current_track.$seqid.$ts.wib");
615 1         3 my @stats;
616 1         4 foreach (qw(min max mean stdev)) {
617             my $value = $self->current_track->{seqids}{$seqid}{$_} ||
618 4   0     7 $self->{FILEWIDE_STATS}{$_} || next;
619 4         9 push @stats,($_=>$value);
620             }
621              
622 1   50     9 my $step = $self->{track_options}{step} || 1;
623             my $span = $self->{track_options}{span} ||
624             $self->{track_options}{step} ||
625 1   0     6 1;
626 1   50     3 my $trim = $self->current_track->{display_options}{trim} || 'stdev10';
627 1         3 my $transform = $self->current_track->{display_options}{transform};
628 1         5 my $class = $self->wigclass;
629 1 50       16 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       6 $wigfile or croak "Couldn't create wigfile $wigfile: $!";
646 1         4 $self->current_track->{seqids}{$seqid}{wig} = $wigfile;
647 1         3 $self->current_track->{seqids}{$seqid}{wigpath} = $path;
648             }
649 1         2 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 () {
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 2 my @args = @_;
676 1 50       4 return unless @args;
677 1         4 foreach(@args) {
678 1         3 s/^\s+//;
679 1         3 s/\s+$//;
680 1 50       4 $_ = '' unless defined $_;
681             }
682 1         6 my @result = Text::ParseWords::shellwords(@args);
683 1         178 return @result;
684             }
685              
686             1;
687              
688              
689             __DATA__