]> git.donarmstrong.com Git - biopieces.git/blob - code_perl/Maasha/KISS/Track.pm
77d823b1b20f0d4799a8332ac0da410fc1e47555
[biopieces.git] / code_perl / Maasha / KISS / Track.pm
1 package Maasha::KISS::Track;
2
3 # Copyright (C) 2009 Martin A. Hansen.
4
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19 # http://www.gnu.org/copyleft/gpl.html
20
21
22 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> DESCRIPTION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
23
24
25 # Routines for creating KISS tracks.
26
27
28 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
29
30
31 use warnings;
32 use strict;
33 use Data::Dumper;
34 use Maasha::Common;
35 use Maasha::Calc;
36 use vars qw( @ISA @EXPORT );
37
38 @ISA = qw( Exporter );
39
40
41 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
42
43
44 sub track_ruler
45 {
46     my ( $draw_metrics,   # hashref with image draw metrics
47          $cookie,         # browser cookie
48        ) = @_;
49
50     my ( $beg, $end, $factor, $step, $i, $txt, $x, @ruler );
51
52     $beg = $cookie->{ 'NAV_START' };
53     $end = $cookie->{ 'NAV_END' };
54
55     $factor = $draw_metrics->{ 'IMG_WIDTH' } / ( $end - $beg );
56     
57     $step = 10;
58
59     while ( ( $end - $beg ) / $step > 20 )
60     {
61         $step *= 5;
62     }
63
64     for ( $i = $beg; $i < $end; $i++ )
65     {
66         if ( ( $i % $step ) == 0 )
67         {
68             $txt = Maasha::Calc::commify( $i ) . "|";
69             $x   = sprintf( "%.0f", ( $i - $beg - length $txt ) * $factor );
70
71             if ( $x > 0 and $x + ( $draw_metrics->{ 'RULER_FONT_SIZE' } * length $txt ) < $draw_metrics->{ 'IMG_WIDTH' } )
72             {
73                 push @ruler, {
74                     type      => 'text',
75                     txt       => $txt,
76                     font_size => $draw_metrics->{ 'RULER_FONT_SIZE' },
77                     color     => $draw_metrics->{ 'RULER_COLOR' },
78                     x1        => $x,
79                     y1        => $draw_metrics->{ 'TRACK_OFFSET' },
80                 };
81             }
82         }
83     }
84
85     $draw_metrics->{ 'TRACK_OFFSET' } += $draw_metrics->{ 'TRACK_SPACE' };
86
87     return wantarray ? @ruler : \@ruler;
88 }
89
90
91 sub track_seq
92 {
93     my ( $draw_metrics,   # hashref with image draw metrics
94          $cookie,         # browser cookie
95        ) = @_;
96
97     my ( $file, $fh, $seq, @chars, $factor, $i, @seq_list );
98
99     if ( $cookie->{ 'NAV_END' } - $cookie->{ 'NAV_START' } + 1 <= 220 )
100     {
101         $file = path_seq( $cookie );
102         $fh   = Maasha::Filesys::file_read_open( $file );
103         $seq  = Maasha::Filesys::file_read( $fh, $cookie->{ 'NAV_START' } - 1, $cookie->{ 'NAV_END' } - $cookie->{ 'NAV_START' } + 1 );
104         close $fh;
105
106         @chars = split //, $seq;
107
108         $factor = $draw_metrics->{ 'IMG_WIDTH' } / @chars;
109
110         for ( $i = 0; $i < @chars; $i++ ) {
111             push @seq_list, {
112                 type      => 'text',
113                 txt       => $chars[ $i ],
114                 font_size => $draw_metrics->{ 'SEQ_FONT_SIZE' },
115                 color     => $draw_metrics->{ 'SEQ_COLOR' },
116                 x1        => sprintf( "%.0f", $i * $factor ),
117                 y1        => $draw_metrics->{ 'TRACK_OFFSET' },
118             };
119         }
120
121         $draw_metrics->{ 'TRACK_OFFSET' } += $draw_metrics->{ 'TRACK_SPACE' };
122
123         return wantarray ? @seq_list : \@seq_list;
124     }
125     else
126     {
127         return;
128     }
129 }
130
131
132 sub track_feature
133 {
134     my ( $track,
135          $draw_metrics,
136          $cookie,
137        ) = @_;
138
139     # Returns a list.
140
141     my ( $index, $count, $track_name, $start, $end, $entries, $features );
142
143     $start = $cookie->{ 'NAV_START' };
144     $end   = $cookie->{ 'NAV_END' };
145
146     $index = Maasha::KISS::IO::kiss_index_retrieve( "$track/track_data.kiss.index" );
147     $count = Maasha::KISS::IO::kiss_index_count( $index, $start, $end );
148
149     $track_name = ( split "/", $track )[ -1 ];
150     $track_name =~ s/^\d+_//;
151     $track_name =~ s/_/ /g;
152
153     $features = [ {
154         type      => 'text',
155         txt       => $track_name,
156         font_size => $draw_metrics->{ 'SEQ_FONT_SIZE' },
157         color     => $draw_metrics->{ 'SEQ_COLOR' },
158         x1        => 0,
159         y1        => $draw_metrics->{ 'TRACK_OFFSET' },
160     } ];
161
162     $draw_metrics->{ 'TRACK_OFFSET' } += 10;
163
164     if ( $count > 5000 )
165     {
166         $entries  = Maasha::KISS::IO::kiss_index_get_blocks( $index, $start, $end );
167         push @{ $features }, Maasha::KISS::Track::track_feature_histogram( $draw_metrics, $start, $end, $entries );
168     }  
169     else
170     {
171         $entries  = Maasha::KISS::IO::kiss_index_get_entries( "$track/track_data.kiss", $index, $start, $end );
172         push @{ $features }, Maasha::KISS::Track::track_feature_linear( $draw_metrics, $start, $end, $entries );
173     }  
174
175     return wantarray ? @{ $features } : $features;
176 }
177
178
179 sub track_feature_linear
180 {
181     my ( $draw_metrics,   # hashref with image draw metrics
182          $beg,            # base window beg
183          $end,            # base window end
184          $entries,        # list of unsorted KISS entries 
185        ) = @_;
186
187     # Returns a list.
188     
189     my ( $factor, $entry, $y_step, @ladder, $y_max, $w, $x1, $y1, $x2, $y2, @features );
190
191     @{ $entries } = sort { $a->{ 'S_BEG' } <=> $b->{ 'S_BEG' } or $a->{ 'S_END' } <=> $b->{ 'S_END' } } @{ $entries };
192
193     $factor = $draw_metrics->{ 'IMG_WIDTH' } / ( $end - $beg );
194     $y_step = 0;
195     $y_max  = 0;
196
197     foreach $entry ( @{ $entries } )
198     {
199         $w = sprintf( "%.0f", ( $entry->{ 'S_END' } - $entry->{ 'S_BEG' } + 1 ) * $factor );
200
201         if ( $w >= 1 )
202         {
203             $x1 = sprintf( "%.0f", ( $entry->{ 'S_BEG' } - $beg ) * $factor );
204
205             for ( $y_step = 0; $y_step < @ladder; $y_step++ ) {
206                 last if $x1 >= $ladder[ $y_step ] + 1; 
207             }
208
209             $y1 = $draw_metrics->{ 'TRACK_OFFSET' } + ( $draw_metrics->{ 'FEAT_WIDTH' } * $y_step );
210
211             push @features, {
212                 type       => 'rect',
213                 line_width => $draw_metrics->{ 'FEAT_WIDTH' },
214                 color      => $draw_metrics->{ 'FEAT_COLOR' },
215                 title      => $entry->{ 'Q_ID' },
216                 x1         => $x1,
217                 y1         => $y1,
218                 x2         => $x1 + $w,
219                 y2         => $y1 + $draw_metrics->{ 'FEAT_WIDTH' },
220             };
221
222             $y_max = Maasha::Calc::max( $y_max, $y_step * $draw_metrics->{ 'FEAT_WIDTH' } );
223
224             push @features, feature_align( $entry, $beg, $y1, $factor, $draw_metrics->{ 'FEAT_WIDTH' } ) if $entry->{ 'ALIGN' } ne '.';
225
226             $ladder[ $y_step ] = $x1 + $w;
227         }
228     }
229
230     $draw_metrics->{ 'TRACK_OFFSET' } += $y_max + $draw_metrics->{ 'TRACK_SPACE' };
231
232     return wantarray ? @features : \@features;
233 }
234
235
236 sub feature_align
237 {
238     # 17:A>T
239
240     my ( $entry,         # Partial KISS entry
241          $beg,           # base window beg
242          $y_offset,      # y axis draw offset
243          $factor,        # scale factor
244          $feat_height,   # hight of feature in pixels
245        ) = @_;
246
247     # Returns a list.
248
249     my ( $w, $align, $pos, $nt_before, $nt_after, $x1, @features );
250
251     $w = sprintf( "%.0f", 1 * $factor );
252
253     if ( $w >= 1 )
254     {
255         foreach $align ( split /,/, $entry->{ 'ALIGN' } )
256         {
257             if ( $align =~ /(\d+):([ATCGN-])>([ATCGN-])/ )
258             {
259                 $pos       = $1;
260                 $nt_before = $2;
261                 $nt_after  = $3;
262             }
263             else
264             {
265                 Maasha::Common::error( qq(BAD align descriptor: "$align") );
266             }
267
268             $x1 = sprintf( "%.0f", ( $entry->{ 'S_BEG' } + $pos - $beg ) * $factor );
269
270             push @features, {
271                 type       => 'rect',
272                 line_width => $feat_height,
273                 color      => [ 1, 0, 0 ],
274                 title      => $align,
275                 x1         => $x1,
276                 y1         => $y_offset,
277                 x2         => $x1 + $w,
278                 y2         => $y_offset + $feat_height,
279             };
280
281             if ( $w > $feat_height )
282             {
283                 push @features, {
284                     type       => 'text',
285                     font_size  => $feat_height + 2,
286                     color      => [ 0, 0, 0 ],
287                     txt        => $nt_after,
288                     x1         => $x1 + sprintf( "%.0f", ( $w / 2 ) ) - $feat_height / 2,
289                     y1         => $y_offset + $feat_height,
290                 };
291             }
292         }
293     }
294
295     return wantarray ? @features : \@features;
296 }
297
298
299 sub track_feature_histogram
300 {
301     my ( $draw_metrics,   # hashref with image draw metrics
302          $min,            # minimum base position
303          $max,            # maximum base position
304          $blocks,         # list of blocks
305        ) = @_;
306
307     # Returns a list.
308
309     my ( $hist_height, $bucket_width, $bucket_count, $min_bucket, $factor, $factor_heigth, $max_height, $block, $bucket_beg, $bucket_end, $i, @buckets, $h, $x, @hist );
310
311     return if $max <= $min;
312
313     $hist_height  = 100;   # pixels
314     $bucket_width = 5;
315     $bucket_count = $draw_metrics->{ 'IMG_WIDTH' } / $bucket_width;
316     $factor       = ( $draw_metrics->{ 'IMG_WIDTH' } / $bucket_width ) / ( $max - $min + 1 );
317
318     $min_bucket = 999999999;
319     $max_height = 0;
320
321     foreach $block ( @{ $blocks } )
322     {
323         $bucket_beg = int( $block->{ 'BEG' } * $factor );
324         $bucket_end = int( $block->{ 'END' } * $factor );
325
326         $min_bucket = Maasha::Calc::min( $min_bucket, $bucket_beg );
327
328         for ( $i = $bucket_beg; $i <= $bucket_end; $i++ )
329         {
330             $buckets[ $i ] += $block->{ 'COUNT' };
331
332             $max_height = Maasha::Calc::max( $max_height, $buckets[ $i ] );
333         }
334     }
335
336     if ( $max_height > 0 )
337     {
338         $factor_heigth = $hist_height / $max_height;
339
340         $x = 0;
341
342         for ( $i = $min_bucket; $i < @buckets; $i++ )
343         {
344             if ( defined $buckets[ $i ] )
345             {
346                 $h = sprintf( "%.0f", $buckets[ $i ] * $factor_heigth );
347
348                 if ( $h >= 1 )
349                 {
350                     push @hist, {
351                         type       => 'line',
352                         line_width => $bucket_width,
353                         color      => $draw_metrics->{ 'FEAT_COLOR' },
354                         title      => "Features: $buckets[ $i ]",
355                         x1         => $x,
356                         y1         => $draw_metrics->{ 'TRACK_OFFSET' } + $hist_height,
357                         x2         => $x,
358                         y2         => $draw_metrics->{ 'TRACK_OFFSET' } + $hist_height - $h,
359                     };
360                 }
361             }
362
363             $x += $bucket_width;
364         }
365     }
366
367     $draw_metrics->{ 'TRACK_OFFSET' } += $hist_height + $draw_metrics->{ 'TRACK_SPACE' };
368
369     return wantarray ? @hist : \@hist;
370 }
371
372
373 sub path_seq
374 {
375     my ( $cookie,
376        ) = @_;
377
378     # Returns a string.
379
380     my ( $path );
381
382     die qq(ERROR: no USER in cookie.\n)     if not $cookie->{ 'USER' };
383     die qq(ERROR: no CLADE in cookie.\n)    if not $cookie->{ 'CLADE' };
384     die qq(ERROR: no GENOME in cookie.\n)   if not $cookie->{ 'GENOME' };
385     die qq(ERROR: no ASSEMBLY in cookie.\n) if not $cookie->{ 'ASSEMBLY' };
386     die qq(ERROR: no CONTIG in cookie.\n)   if not $cookie->{ 'CONTIG' };
387
388     $path = join( "/",
389         $cookie->{ 'DATA_DIR' },
390         "Users",
391         $cookie->{ 'USER' },
392         $cookie->{ 'CLADE' },
393         $cookie->{ 'GENOME' },
394         $cookie->{ 'ASSEMBLY' },
395         $cookie->{ 'CONTIG' },
396         "Sequence",
397         "sequence.txt"
398     );
399     
400     die qq(ERROR: no such file: "$path".\n) if not -e $path;
401
402     return $path;
403 }
404
405
406 sub path_tracks
407 {
408     my ( $cookie,
409        ) = @_;
410
411     # Returns a list.
412     #
413     my ( $path, @tracks );
414
415     die qq(ERROR: no USER in cookie.\n)     if not $cookie->{ 'USER' };
416     die qq(ERROR: no CLADE in cookie.\n)    if not $cookie->{ 'CLADE' };
417     die qq(ERROR: no GENOME in cookie.\n)   if not $cookie->{ 'GENOME' };
418     die qq(ERROR: no ASSEMBLY in cookie.\n) if not $cookie->{ 'ASSEMBLY' };
419     die qq(ERROR: no CONTIG in cookie.\n)   if not $cookie->{ 'CONTIG' };
420
421     $path = join( "/",
422         $cookie->{ 'DATA_DIR' },
423         "Users",
424         $cookie->{ 'USER' },
425         $cookie->{ 'CLADE' },
426         $cookie->{ 'GENOME' },
427         $cookie->{ 'ASSEMBLY' },
428         $cookie->{ 'CONTIG' },
429         "Tracks",
430     );
431
432     if ( -d $path )
433     {
434         @tracks = Maasha::Filesys::ls_dirs( $path );
435
436         @tracks = grep { $_ !~ /\/\.\.?$/ } @tracks;
437
438         return wantarray ? @tracks : \@tracks;
439     }
440     else
441     {
442         return wantarray ? () : [];
443     }
444 }
445
446 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
447
448 1;
449