Mercurial > repos > mahtabm > ensembl
comparison variant_effect_predictor/Bio/Graphics/Panel.pm @ 0:1f6dce3d34e0
Uploaded
| author | mahtabm |
|---|---|
| date | Thu, 11 Apr 2013 02:01:53 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:1f6dce3d34e0 |
|---|---|
| 1 package Bio::Graphics::Panel; | |
| 2 | |
| 3 use strict; | |
| 4 use Bio::Graphics::Glyph::Factory; | |
| 5 use Bio::Graphics::Feature; | |
| 6 use GD;; | |
| 7 | |
| 8 | |
| 9 use constant KEYLABELFONT => gdMediumBoldFont; | |
| 10 use constant KEYSPACING => 5; # extra space between key columns | |
| 11 use constant KEYPADTOP => 5; # extra padding before the key starts | |
| 12 use constant KEYCOLOR => 'wheat'; | |
| 13 use constant KEYSTYLE => 'bottom'; | |
| 14 use constant KEYALIGN => 'left'; | |
| 15 use constant GRIDCOLOR => 'lightcyan'; | |
| 16 use constant MISSING_TRACK_COLOR =>'gray'; | |
| 17 | |
| 18 my %COLORS; # translation table for symbolic color names to RGB triple | |
| 19 | |
| 20 # Create a new panel of a given width and height, and add lists of features | |
| 21 # one by one | |
| 22 sub new { | |
| 23 my $class = shift; | |
| 24 my %options = @_; | |
| 25 | |
| 26 $class->read_colors() unless %COLORS; | |
| 27 | |
| 28 my $length = $options{-length} || 0; | |
| 29 my $offset = $options{-offset} || 0; | |
| 30 my $spacing = $options{-spacing} || 5; | |
| 31 my $bgcolor = $options{-bgcolor} || 0; | |
| 32 my $keyfont = $options{-key_font} || KEYLABELFONT; | |
| 33 my $keycolor = $options{-key_color} || KEYCOLOR; | |
| 34 my $keyspacing = $options{-key_spacing} || KEYSPACING; | |
| 35 my $keystyle = $options{-key_style} || KEYSTYLE; | |
| 36 my $keyalign = $options{-key_align} || KEYALIGN; | |
| 37 my $allcallbacks = $options{-all_callbacks} || 0; | |
| 38 my $gridcolor = $options{-gridcolor} || GRIDCOLOR; | |
| 39 my $grid = $options{-grid} || 0; | |
| 40 my $flip = $options{-flip} || 0; | |
| 41 my $empty_track_style = $options{-empty_tracks} || 'key'; | |
| 42 my $truecolor = $options{-truecolor} || 0; | |
| 43 | |
| 44 if (my $seg = $options{-segment}) { | |
| 45 $offset = eval {$seg->start-1} || 0; | |
| 46 $length = $seg->length; | |
| 47 } | |
| 48 | |
| 49 $offset ||= $options{-start}-1 if defined $options{-start}; | |
| 50 $length ||= $options{-stop}-$options{-start}+1 | |
| 51 if defined $options{-start} && defined $options{-stop}; | |
| 52 | |
| 53 return bless { | |
| 54 tracks => [], | |
| 55 width => $options{-width} || 600, | |
| 56 pad_top => $options{-pad_top}||0, | |
| 57 pad_bottom => $options{-pad_bottom}||0, | |
| 58 pad_left => $options{-pad_left}||0, | |
| 59 pad_right => $options{-pad_right}||0, | |
| 60 length => $length, | |
| 61 offset => $offset, | |
| 62 gridcolor => $gridcolor, | |
| 63 grid => $grid, | |
| 64 bgcolor => $bgcolor, | |
| 65 height => 0, # AUTO | |
| 66 spacing => $spacing, | |
| 67 key_font => $keyfont, | |
| 68 key_color => $keycolor, | |
| 69 key_spacing => $keyspacing, | |
| 70 key_style => $keystyle, | |
| 71 key_align => $keyalign, | |
| 72 all_callbacks => $allcallbacks, | |
| 73 truecolor => $truecolor, | |
| 74 flip => $flip, | |
| 75 empty_track_style => $empty_track_style, | |
| 76 },$class; | |
| 77 } | |
| 78 | |
| 79 sub pad_left { | |
| 80 my $self = shift; | |
| 81 my $g = $self->{pad_left}; | |
| 82 $self->{pad_left} = shift if @_; | |
| 83 $g; | |
| 84 } | |
| 85 sub pad_right { | |
| 86 my $self = shift; | |
| 87 my $g = $self->{pad_right}; | |
| 88 $self->{pad_right} = shift if @_; | |
| 89 $g; | |
| 90 } | |
| 91 sub pad_top { | |
| 92 my $self = shift; | |
| 93 my $g = $self->{pad_top}; | |
| 94 $self->{pad_top} = shift if @_; | |
| 95 $g; | |
| 96 } | |
| 97 sub pad_bottom { | |
| 98 my $self = shift; | |
| 99 my $g = $self->{pad_bottom}; | |
| 100 $self->{pad_bottom} = shift if @_; | |
| 101 $g; | |
| 102 } | |
| 103 | |
| 104 sub flip { | |
| 105 my $self = shift; | |
| 106 my $g = $self->{flip}; | |
| 107 $self->{flip} = shift if @_; | |
| 108 $g; | |
| 109 } | |
| 110 | |
| 111 # values of empty_track_style are: | |
| 112 # "suppress" -- suppress empty tracks entirely (default) | |
| 113 # "key" -- show just the key in "between" mode | |
| 114 # "line" -- draw a thin grey line | |
| 115 # "dashed" -- draw a dashed line | |
| 116 sub empty_track_style { | |
| 117 my $self = shift; | |
| 118 my $g = $self->{empty_track_style}; | |
| 119 $self->{empty_track_style} = shift if @_; | |
| 120 $g; | |
| 121 } | |
| 122 | |
| 123 sub key_style { | |
| 124 my $self = shift; | |
| 125 my $g = $self->{key_style}; | |
| 126 $self->{key_style} = shift if @_; | |
| 127 $g; | |
| 128 } | |
| 129 | |
| 130 # public routine for mapping from a base pair | |
| 131 # location to pixel coordinates | |
| 132 sub location2pixel { | |
| 133 my $self = shift; | |
| 134 my $end = $self->end + 1; | |
| 135 my @coords = $self->{flip} ? map { $end-$_ } @_ : @_; | |
| 136 $self->map_pt(@coords); | |
| 137 } | |
| 138 | |
| 139 # numerous direct calls into array used here for performance considerations | |
| 140 sub map_pt { | |
| 141 my $self = shift; | |
| 142 my $offset = $self->{offset}; | |
| 143 my $scale = $self->{scale} || $self->scale; | |
| 144 my $pl = $self->{pad_left}; | |
| 145 my $pr = $self->{width} - $self->{pad_right}; | |
| 146 my $flip = $self->{flip}; | |
| 147 my $length = $self->{length}; | |
| 148 my @result; | |
| 149 foreach (@_) { | |
| 150 my $val = $flip ? int (0.5 + $pr - ($length - ($_- 1)) * $scale) : int (0.5 + $pl + ($_-$offset-1) * $scale); | |
| 151 $val = $pl-1 if $val < $pl; | |
| 152 $val = $pr+1 if $val > $pr; | |
| 153 push @result,$val; | |
| 154 } | |
| 155 @result; | |
| 156 } | |
| 157 | |
| 158 sub map_no_trunc { | |
| 159 my $self = shift; | |
| 160 my $offset = $self->{offset}; | |
| 161 my $scale = $self->scale; | |
| 162 my $pl = $self->{pad_left}; | |
| 163 my $pr = $self->{width} - $self->{pad_right}; | |
| 164 my $flip = $self->{flip}; | |
| 165 my $length = $self->{length}; | |
| 166 my $end = $offset+$length; | |
| 167 my @result; | |
| 168 foreach (@_) { | |
| 169 my $val = $flip ? int (0.5 + $pl + ($end - ($_- 1)) * $scale) : int (0.5 + $pl + ($_-$offset-1) * $scale); | |
| 170 push @result,$val; | |
| 171 } | |
| 172 @result; | |
| 173 } | |
| 174 | |
| 175 sub scale { | |
| 176 my $self = shift; | |
| 177 $self->{scale} ||= ($self->{width}-$self->pad_left-$self->pad_right)/($self->length); | |
| 178 } | |
| 179 | |
| 180 sub start { shift->{offset}+1} | |
| 181 sub end { $_[0]->start + $_[0]->{length}-1} | |
| 182 | |
| 183 sub offset { shift->{offset} } | |
| 184 sub width { | |
| 185 my $self = shift; | |
| 186 my $d = $self->{width}; | |
| 187 $self->{width} = shift if @_; | |
| 188 $d; | |
| 189 # $d + $self->pad_left + $self->pad_right; | |
| 190 } | |
| 191 | |
| 192 sub left { | |
| 193 my $self = shift; | |
| 194 $self->pad_left; | |
| 195 } | |
| 196 sub right { | |
| 197 my $self = shift; | |
| 198 $self->width - $self->pad_right; | |
| 199 } | |
| 200 | |
| 201 sub spacing { | |
| 202 my $self = shift; | |
| 203 my $d = $self->{spacing}; | |
| 204 $self->{spacing} = shift if @_; | |
| 205 $d; | |
| 206 } | |
| 207 | |
| 208 sub key_spacing { | |
| 209 my $self = shift; | |
| 210 my $d = $self->{key_spacing}; | |
| 211 $self->{key_spacing} = shift if @_; | |
| 212 $d; | |
| 213 } | |
| 214 | |
| 215 sub length { | |
| 216 my $self = shift; | |
| 217 my $d = $self->{length}; | |
| 218 if (@_) { | |
| 219 my $l = shift; | |
| 220 $l = $l->length if ref($l) && $l->can('length'); | |
| 221 $self->{length} = $l; | |
| 222 } | |
| 223 $d; | |
| 224 } | |
| 225 | |
| 226 sub gridcolor {shift->{gridcolor}} | |
| 227 | |
| 228 sub all_callbacks { shift->{all_callbacks} } | |
| 229 | |
| 230 sub add_track { | |
| 231 my $self = shift; | |
| 232 $self->_do_add_track(scalar(@{$self->{tracks}}),@_); | |
| 233 } | |
| 234 | |
| 235 sub unshift_track { | |
| 236 my $self = shift; | |
| 237 $self->_do_add_track(0,@_); | |
| 238 } | |
| 239 | |
| 240 sub insert_track { | |
| 241 my $self = shift; | |
| 242 my $position = shift; | |
| 243 $self->_do_add_track($position,@_); | |
| 244 } | |
| 245 | |
| 246 | |
| 247 # create a feature and factory pair | |
| 248 # see Factory.pm for the format of the options | |
| 249 # The thing returned is actually a generic Glyph | |
| 250 sub _do_add_track { | |
| 251 my $self = shift; | |
| 252 my $position = shift; | |
| 253 | |
| 254 # due to indecision, we accept features | |
| 255 # and/or glyph types in the first two arguments | |
| 256 my ($features,$glyph_name) = ([],undef); | |
| 257 while ( @_ && $_[0] !~ /^-/) { | |
| 258 my $arg = shift; | |
| 259 $features = $arg and next if ref($arg); | |
| 260 $glyph_name = $arg and next unless ref($arg); | |
| 261 } | |
| 262 | |
| 263 my %args = @_; | |
| 264 my ($map,$ss,%options); | |
| 265 | |
| 266 foreach (keys %args) { | |
| 267 (my $canonical = lc $_) =~ s/^-//; | |
| 268 if ($canonical eq 'glyph') { | |
| 269 $map = $args{$_}; | |
| 270 delete $args{$_}; | |
| 271 } elsif ($canonical eq 'stylesheet') { | |
| 272 $ss = $args{$_}; | |
| 273 delete $args{$_}; | |
| 274 } else { | |
| 275 $options{$canonical} = $args{$_}; | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 $glyph_name = $map if defined $map; | |
| 280 $glyph_name ||= 'generic'; | |
| 281 | |
| 282 local $^W = 0; # uninitialized variable warnings under 5.00503 | |
| 283 | |
| 284 my $panel_map = | |
| 285 ref($map) eq 'CODE' ? sub { | |
| 286 my $feature = shift; | |
| 287 return 'track' if eval { defined $feature->primary_tag && $feature->primary_tag eq 'track' }; | |
| 288 return 'group' if eval { defined $feature->primary_tag && $feature->primary_tag eq 'group' }; | |
| 289 return $map->($feature); | |
| 290 } | |
| 291 : ref($map) eq 'HASH' ? sub { | |
| 292 my $feature = shift; | |
| 293 return 'track' if eval { defined $feature->primary_tag && $feature->primary_tag eq 'track' }; | |
| 294 return 'group' if eval { defined $feature->primary_tag && $feature->primary_tag eq 'group' }; | |
| 295 return eval {$map->{$feature->primary_tag}} || 'generic'; | |
| 296 } | |
| 297 : sub { | |
| 298 my $feature = shift; | |
| 299 return 'track' if eval { defined $feature->primary_tag && $feature->primary_tag eq 'track' }; | |
| 300 return 'group' if eval { defined $feature->primary_tag && $feature->primary_tag eq 'group' }; | |
| 301 return $glyph_name; | |
| 302 }; | |
| 303 | |
| 304 $self->_add_track($position,$features,-map=>$panel_map,-stylesheet=>$ss,-options=>\%options); | |
| 305 } | |
| 306 | |
| 307 sub _add_track { | |
| 308 my $self = shift; | |
| 309 my ($position,$features,@options) = @_; | |
| 310 | |
| 311 # build the list of features into a Bio::Graphics::Feature object | |
| 312 $features = [$features] unless ref $features eq 'ARRAY'; | |
| 313 | |
| 314 # optional middle-level glyph is the group | |
| 315 foreach my $f (grep {ref $_ eq 'ARRAY'} @$features) { | |
| 316 next unless ref $f eq 'ARRAY'; | |
| 317 $f = Bio::Graphics::Feature->new( | |
| 318 -segments=>$f, | |
| 319 -type => 'group' | |
| 320 ); | |
| 321 } | |
| 322 | |
| 323 # top-level glyph is the track | |
| 324 my $feature = Bio::Graphics::Feature->new( | |
| 325 -segments=>$features, | |
| 326 -start => $self->offset+1, | |
| 327 -stop => $self->offset+$self->length, | |
| 328 -type => 'track' | |
| 329 ); | |
| 330 | |
| 331 my $factory = Bio::Graphics::Glyph::Factory->new($self,@options); | |
| 332 my $track = $factory->make_glyph(-1,$feature); | |
| 333 | |
| 334 splice(@{$self->{tracks}},$position,0,$track); | |
| 335 return $track; | |
| 336 } | |
| 337 | |
| 338 sub height { | |
| 339 my $self = shift; | |
| 340 my $spacing = $self->spacing; | |
| 341 my $key_height = $self->format_key; | |
| 342 my $empty_track_style = $self->empty_track_style; | |
| 343 my $key_style = $self->key_style; | |
| 344 my $bottom_key = $key_style eq 'bottom'; | |
| 345 my $between_key = $key_style eq 'between'; | |
| 346 my $draw_empty = $empty_track_style =~ /^(line|dashed)$/; | |
| 347 my $keyheight = $self->{key_font}->height; | |
| 348 my $height = 0; | |
| 349 for my $track (@{$self->{tracks}}) { | |
| 350 my $draw_between = $between_key && $track->option('key'); | |
| 351 my $has_parts = $track->parts; | |
| 352 next if !$has_parts && ($empty_track_style eq 'suppress' | |
| 353 or $empty_track_style eq 'key' && $bottom_key); | |
| 354 $height += $keyheight if $draw_between; | |
| 355 $height += $self->spacing; | |
| 356 $height += $track->layout_height; | |
| 357 } | |
| 358 | |
| 359 # get rid of spacing under last track | |
| 360 $height -= $self->spacing unless $bottom_key; | |
| 361 return $height + $key_height + $self->pad_top + $self->pad_bottom; | |
| 362 } | |
| 363 | |
| 364 sub gd { | |
| 365 my $self = shift; | |
| 366 my $existing_gd = shift; | |
| 367 | |
| 368 local $^W = 0; # can't track down the uninitialized variable warning | |
| 369 | |
| 370 return $self->{gd} if $self->{gd}; | |
| 371 | |
| 372 my $width = $self->width; | |
| 373 my $height = $self->height; | |
| 374 | |
| 375 my $gd = $existing_gd || GD::Image->new($width,$height, | |
| 376 ($self->{truecolor} && GD::Image->can('isTrueColor') ? 1 : ()) | |
| 377 ); | |
| 378 | |
| 379 my %translation_table; | |
| 380 for my $name ('white','black',keys %COLORS) { | |
| 381 my $idx = $gd->colorAllocate(@{$COLORS{$name}}); | |
| 382 $translation_table{$name} = $idx; | |
| 383 } | |
| 384 | |
| 385 $self->{translations} = \%translation_table; | |
| 386 $self->{gd} = $gd; | |
| 387 if ($self->bgcolor) { | |
| 388 $gd->fill(0,0,$self->bgcolor); | |
| 389 } elsif (eval {$gd->isTrueColor}) { | |
| 390 $gd->fill(0,0,$translation_table{'white'}); | |
| 391 } | |
| 392 | |
| 393 my $pl = $self->pad_left; | |
| 394 my $pt = $self->pad_top; | |
| 395 my $offset = $pt; | |
| 396 my $keyheight = $self->{key_font}->height; | |
| 397 my $bottom_key = $self->{key_style} eq 'bottom'; | |
| 398 my $between_key = $self->{key_style} eq 'between'; | |
| 399 my $left_key = $self->{key_style} eq 'left'; | |
| 400 my $right_key = $self->{key_style} eq 'right'; | |
| 401 my $empty_track_style = $self->empty_track_style; | |
| 402 my $spacing = $self->spacing; | |
| 403 | |
| 404 # we draw in two steps, once for background of tracks, and once for | |
| 405 # the contents. This allows the grid to sit on top of the track background. | |
| 406 for my $track (@{$self->{tracks}}) { | |
| 407 my $draw_between = $between_key && $track->option('key'); | |
| 408 next if !$track->parts && ($empty_track_style eq 'suppress' | |
| 409 or $empty_track_style eq 'key' && $bottom_key); | |
| 410 $gd->filledRectangle($pl, | |
| 411 $offset, | |
| 412 $width-$self->pad_right, | |
| 413 $offset+$track->layout_height | |
| 414 + ($between_key ? $self->{key_font}->height : 0), | |
| 415 $track->tkcolor) | |
| 416 if defined $track->tkcolor; | |
| 417 $offset += $keyheight if $draw_between; | |
| 418 $offset += $track->layout_height + $spacing; | |
| 419 } | |
| 420 | |
| 421 $self->draw_grid($gd) if $self->{grid}; | |
| 422 | |
| 423 $offset = $pt; | |
| 424 for my $track (@{$self->{tracks}}) { | |
| 425 my $draw_between = $between_key && $track->option('key'); | |
| 426 my $has_parts = $track->parts; | |
| 427 next if !$has_parts && ($empty_track_style eq 'suppress' | |
| 428 or $empty_track_style eq 'key' && $bottom_key); | |
| 429 | |
| 430 if ($draw_between) { | |
| 431 $offset += $self->draw_between_key($gd,$track,$offset); | |
| 432 } | |
| 433 | |
| 434 elsif ($self->{key_style} =~ /^(left|right)$/) { | |
| 435 $self->draw_side_key($gd,$track,$offset,$self->{key_style}); | |
| 436 } | |
| 437 | |
| 438 $self->draw_empty($gd,$offset,$empty_track_style) | |
| 439 if !$has_parts && $empty_track_style=~/^(line|dashed)$/; | |
| 440 | |
| 441 $track->draw($gd,0,$offset,0,1); | |
| 442 $self->track_position($track,$offset); | |
| 443 $offset += $track->layout_height + $spacing; | |
| 444 } | |
| 445 | |
| 446 | |
| 447 $self->draw_bottom_key($gd,$pl,$offset) if $self->{key_style} eq 'bottom'; | |
| 448 | |
| 449 return $self->{gd} = $gd; | |
| 450 } | |
| 451 | |
| 452 sub boxes { | |
| 453 my $self = shift; | |
| 454 my @boxes; | |
| 455 my $offset = 0; | |
| 456 | |
| 457 my $pl = $self->pad_left; | |
| 458 my $pt = $self->pad_top; | |
| 459 my $between_key = $self->{key_style} eq 'between'; | |
| 460 my $bottom_key = $self->{key_style} eq 'bottom'; | |
| 461 my $empty_track_style = $self->empty_track_style; | |
| 462 my $keyheight = $self->{key_font}->height; | |
| 463 my $spacing = $self->spacing; | |
| 464 | |
| 465 for my $track (@{$self->{tracks}}) { | |
| 466 my $draw_between = $between_key && $track->option('key'); | |
| 467 next if !$track->parts && ($empty_track_style eq 'suppress' | |
| 468 or $empty_track_style eq 'key' && $bottom_key); | |
| 469 $offset += $keyheight if $draw_between; | |
| 470 my $boxes = $track->boxes(0,$offset+$pt); | |
| 471 $self->track_position($track,$offset); | |
| 472 push @boxes,@$boxes; | |
| 473 $offset += $track->layout_height + $self->spacing; | |
| 474 } | |
| 475 return wantarray ? @boxes : \@boxes; | |
| 476 } | |
| 477 | |
| 478 sub track_position { | |
| 479 my $self = shift; | |
| 480 my $track = shift; | |
| 481 my $d = $self->{_track_position}{$track}; | |
| 482 $self->{_track_position}{$track} = shift if @_; | |
| 483 $d; | |
| 484 } | |
| 485 | |
| 486 # draw the keys -- between | |
| 487 sub draw_between_key { | |
| 488 my $self = shift; | |
| 489 my ($gd,$track,$offset) = @_; | |
| 490 my $key = $track->option('key') or return 0; | |
| 491 my $x = $self->{key_align} eq 'center' ? $self->width - (CORE::length($key) * $self->{key_font}->width)/2 | |
| 492 : $self->{key_align} eq 'right' ? $self->width - CORE::length($key) | |
| 493 : $self->pad_left; | |
| 494 $gd->string($self->{key_font},$x,$offset,$key,1); | |
| 495 return $self->{key_font}->height; | |
| 496 } | |
| 497 | |
| 498 # draw the keys -- left or right side | |
| 499 sub draw_side_key { | |
| 500 my $self = shift; | |
| 501 my ($gd,$track,$offset,$side) = @_; | |
| 502 my $key = $track->option('key') or return; | |
| 503 my $pos = $side eq 'left' ? $self->pad_left - $self->{key_font}->width * CORE::length($key)-3 | |
| 504 : $self->width - $self->pad_right+3; | |
| 505 $gd->string($self->{key_font},$pos,$offset,$key,1); | |
| 506 } | |
| 507 | |
| 508 # draw the keys -- bottom | |
| 509 sub draw_bottom_key { | |
| 510 my $self = shift; | |
| 511 my ($gd,$left,$top) = @_; | |
| 512 my $key_glyphs = $self->{key_glyphs} or return; | |
| 513 | |
| 514 my $color = $self->translate_color($self->{key_color}); | |
| 515 $gd->filledRectangle($left,$top,$self->width - $self->pad_right,$self->height-$self->pad_bottom,$color); | |
| 516 $gd->string($self->{key_font},$left,KEYPADTOP+$top,"KEY:",1); | |
| 517 $top += $self->{key_font}->height + KEYPADTOP; | |
| 518 | |
| 519 $_->draw($gd,$left,$top) foreach @$key_glyphs; | |
| 520 } | |
| 521 | |
| 522 # Format the key section, and return its height | |
| 523 sub format_key { | |
| 524 my $self = shift; | |
| 525 return 0 unless $self->key_style eq 'bottom'; | |
| 526 | |
| 527 return $self->{key_height} if defined $self->{key_height}; | |
| 528 | |
| 529 my $suppress = $self->{empty_track_style} eq 'suppress'; | |
| 530 my $between = $self->{key_style} eq 'between'; | |
| 531 | |
| 532 if ($between) { | |
| 533 my @key_tracks = $suppress | |
| 534 ? grep {$_->option('key') && $_->parts} @{$self->{tracks}} | |
| 535 : grep {$_->option('key')} @{$self->{tracks}}; | |
| 536 return $self->{key_height} = @key_tracks * $self->{key_font}->height; | |
| 537 } | |
| 538 | |
| 539 elsif ($self->{key_style} eq 'bottom') { | |
| 540 | |
| 541 my ($height,$width) = (0,0); | |
| 542 my %tracks; | |
| 543 my @glyphs; | |
| 544 | |
| 545 # determine how many glyphs become part of the key | |
| 546 # and their max size | |
| 547 for my $track (@{$self->{tracks}}) { | |
| 548 | |
| 549 next unless $track->option('key'); | |
| 550 next if $suppress && !$track->parts; | |
| 551 | |
| 552 my $glyph; | |
| 553 if (my @parts = $track->parts) { | |
| 554 $glyph = $parts[0]->keyglyph; | |
| 555 } else { | |
| 556 my $t = Bio::Graphics::Feature->new(-segments=> | |
| 557 [Bio::Graphics::Feature->new(-start => $self->offset, | |
| 558 -stop => $self->offset+$self->length)]); | |
| 559 my $g = $track->factory->make_glyph(0,$t); | |
| 560 $glyph = $g->keyglyph; | |
| 561 } | |
| 562 next unless $glyph; | |
| 563 | |
| 564 | |
| 565 $tracks{$track} = $glyph; | |
| 566 my ($h,$w) = ($glyph->layout_height, | |
| 567 $glyph->layout_width); | |
| 568 $height = $h if $h > $height; | |
| 569 $width = $w if $w > $width; | |
| 570 push @glyphs,$glyph; | |
| 571 | |
| 572 } | |
| 573 | |
| 574 $width += $self->key_spacing; | |
| 575 | |
| 576 # no key glyphs, no key | |
| 577 return $self->{key_height} = 0 unless @glyphs; | |
| 578 | |
| 579 # now height and width hold the largest glyph, and $glyph_count | |
| 580 # contains the number of glyphs. We will format them into a | |
| 581 # box that is roughly 3 height/4 width (golden mean) | |
| 582 my $rows = 0; | |
| 583 my $cols = 0; | |
| 584 my $maxwidth = $self->width - $self->pad_left - $self->pad_right; | |
| 585 while (++$rows) { | |
| 586 $cols = @glyphs / $rows; | |
| 587 $cols = int ($cols+1) if $cols =~ /\./; # round upward for fractions | |
| 588 my $total_width = $cols * $width; | |
| 589 my $total_height = $rows * $width; | |
| 590 last if $total_width < $maxwidth; | |
| 591 } | |
| 592 | |
| 593 # move glyphs into row-major format | |
| 594 my $spacing = $self->key_spacing; | |
| 595 my $i = 0; | |
| 596 for (my $c = 0; $c < $cols; $c++) { | |
| 597 for (my $r = 0; $r < $rows; $r++) { | |
| 598 my $x = $c * ($width + $spacing); | |
| 599 my $y = $r * ($height + $spacing); | |
| 600 next unless defined $glyphs[$i]; | |
| 601 $glyphs[$i]->move($x,$y); | |
| 602 $i++; | |
| 603 } | |
| 604 } | |
| 605 | |
| 606 $self->{key_glyphs} = \@glyphs; # remember our key glyphs | |
| 607 # remember our key height | |
| 608 return $self->{key_height} = | |
| 609 ($height+$spacing) * $rows + $self->{key_font}->height +KEYPADTOP; | |
| 610 } | |
| 611 | |
| 612 else { # no known key style, neither "between" nor "bottom" | |
| 613 return $self->{key_height} = 0; | |
| 614 } | |
| 615 } | |
| 616 | |
| 617 sub draw_empty { | |
| 618 my $self = shift; | |
| 619 my ($gd,$offset,$style) = @_; | |
| 620 $offset += $self->spacing/2; | |
| 621 my $left = $self->pad_left; | |
| 622 my $right = $self->width-$self->pad_right; | |
| 623 my $color = $self->translate_color(MISSING_TRACK_COLOR); | |
| 624 if ($style eq 'dashed') { | |
| 625 $gd->setStyle($color,$color,gdTransparent,gdTransparent); | |
| 626 $gd->line($left,$offset,$right,$offset,gdStyled); | |
| 627 } else { | |
| 628 $gd->line($left,$offset,$right,$offset,$color); | |
| 629 } | |
| 630 $offset; | |
| 631 } | |
| 632 | |
| 633 # draw a grid | |
| 634 sub draw_grid { | |
| 635 my $self = shift; | |
| 636 my $gd = shift; | |
| 637 | |
| 638 my $gridcolor = $self->translate_color($self->{gridcolor}); | |
| 639 my @positions; | |
| 640 if (ref $self->{grid} eq 'ARRAY') { | |
| 641 @positions = @{$self->{grid}}; | |
| 642 } else { | |
| 643 my ($major,$minor) = $self->ticks; | |
| 644 my $first_tick = $minor * int(0.5 + $self->start/$minor); | |
| 645 for (my $i = $first_tick; $i < $self->end; $i += $minor) { | |
| 646 push @positions,$i; | |
| 647 } | |
| 648 } | |
| 649 my $pl = $self->pad_left; | |
| 650 my $pt = $self->pad_top; | |
| 651 my $pb = $self->height - $self->pad_bottom; | |
| 652 local $self->{flip} = 0; | |
| 653 for my $tick (@positions) { | |
| 654 my ($pos) = $self->map_pt($tick); | |
| 655 $gd->line($pos,$pt,$pos,$pb,$gridcolor); | |
| 656 } | |
| 657 } | |
| 658 | |
| 659 # calculate major and minor ticks, given a start position | |
| 660 sub ticks { | |
| 661 my $self = shift; | |
| 662 my ($length,$minwidth) = @_; | |
| 663 | |
| 664 $length = $self->{length} unless defined $length; | |
| 665 $minwidth = gdSmallFont->width*7 unless defined $minwidth; | |
| 666 | |
| 667 my ($major,$minor); | |
| 668 | |
| 669 # figure out tick mark scale | |
| 670 # we want no more than 1 major tick mark every 40 pixels | |
| 671 # and enough room for the labels | |
| 672 my $scale = $self->scale; | |
| 673 | |
| 674 my $interval = 1; | |
| 675 | |
| 676 while (1) { | |
| 677 my $pixels = $interval * $scale; | |
| 678 last if $pixels >= $minwidth; | |
| 679 $interval *= 10; | |
| 680 } | |
| 681 | |
| 682 # to make sure a major tick shows up somewhere in the first half | |
| 683 # | |
| 684 $interval *= .5 if ($interval > 0.5*$length); | |
| 685 | |
| 686 return ($interval,$interval/10); | |
| 687 } | |
| 688 | |
| 689 # reverse of translate(); given index, return rgb triplet | |
| 690 sub rgb { | |
| 691 my $self = shift; | |
| 692 my $idx = shift; | |
| 693 my $gd = $self->{gd} or return; | |
| 694 return $gd->rgb($idx); | |
| 695 } | |
| 696 | |
| 697 sub translate_color { | |
| 698 my $self = shift; | |
| 699 my @colors = @_; | |
| 700 if (@colors == 3) { | |
| 701 my $gd = $self->gd or return 1; | |
| 702 return $self->colorClosest($gd,@colors); | |
| 703 } | |
| 704 elsif ($colors[0] =~ /^\#([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i) { | |
| 705 my $gd = $self->gd or return 1; | |
| 706 my ($r,$g,$b) = (hex($1),hex($2),hex($3)); | |
| 707 return $self->colorClosest($gd,$r,$g,$b); | |
| 708 } | |
| 709 else { | |
| 710 my $color = $colors[0]; | |
| 711 my $table = $self->{translations} or return 1; | |
| 712 return defined $table->{$color} ? $table->{$color} : 1; | |
| 713 } | |
| 714 } | |
| 715 | |
| 716 # workaround for bad GD | |
| 717 sub colorClosest { | |
| 718 my ($self,$gd,@c) = @_; | |
| 719 return $self->{closestcache}{"@c"} if exists $self->{closestcache}{"@c"}; | |
| 720 return $self->{closestcache}{"@c"} = $gd->colorClosest(@c) if $GD::VERSION < 2.04; | |
| 721 my ($value,$index); | |
| 722 for (keys %COLORS) { | |
| 723 my ($r,$g,$b) = @{$COLORS{$_}}; | |
| 724 my $dist = ($r-$c[0])**2 + ($g-$c[1])**2 + ($b-$c[2])**2; | |
| 725 ($value,$index) = ($dist,$_) if !defined($value) || $dist < $value; | |
| 726 } | |
| 727 return $self->{closestcache}{"@c"} = $self->{translations}{$index}; | |
| 728 } | |
| 729 | |
| 730 sub bgcolor { | |
| 731 my $self = shift; | |
| 732 return unless $self->{bgcolor}; | |
| 733 $self->translate_color($self->{bgcolor}); | |
| 734 } | |
| 735 | |
| 736 sub set_pen { | |
| 737 my $self = shift; | |
| 738 my ($linewidth,$color) = @_; | |
| 739 return $self->{pens}{$linewidth,$color} if $self->{pens}{$linewidth,$color}; | |
| 740 | |
| 741 my $pen = $self->{pens}{$linewidth} = GD::Image->new($linewidth,$linewidth); | |
| 742 my @rgb = $self->rgb($color); | |
| 743 my $bg = $pen->colorAllocate(255,255,255); | |
| 744 my $fg = $pen->colorAllocate(@rgb); | |
| 745 $pen->fill(0,0,$fg); | |
| 746 $self->{gd}->setBrush($pen); | |
| 747 return gdBrushed; | |
| 748 } | |
| 749 | |
| 750 sub png { | |
| 751 my $gd = shift->gd; | |
| 752 $gd->png; | |
| 753 } | |
| 754 | |
| 755 sub read_colors { | |
| 756 my $class = shift; | |
| 757 while (<DATA>) { | |
| 758 chomp; | |
| 759 last if /^__END__/; | |
| 760 my ($name,$r,$g,$b) = split /\s+/; | |
| 761 $COLORS{$name} = [hex $r,hex $g,hex $b]; | |
| 762 } | |
| 763 } | |
| 764 | |
| 765 sub color_name_to_rgb { | |
| 766 my $class = shift; | |
| 767 my $color_name = shift; | |
| 768 $class->read_colors() unless %COLORS; | |
| 769 return unless $COLORS{$color_name}; | |
| 770 return wantarray ? @{$COLORS{$color_name}} | |
| 771 : $COLORS{$color_name}; | |
| 772 } | |
| 773 | |
| 774 sub color_names { | |
| 775 my $class = shift; | |
| 776 $class->read_colors unless %COLORS; | |
| 777 return wantarray ? keys %COLORS : [keys %COLORS]; | |
| 778 } | |
| 779 | |
| 780 1; | |
| 781 | |
| 782 __DATA__ | |
| 783 white FF FF FF | |
| 784 black 00 00 00 | |
| 785 aliceblue F0 F8 FF | |
| 786 antiquewhite FA EB D7 | |
| 787 aqua 00 FF FF | |
| 788 aquamarine 7F FF D4 | |
| 789 azure F0 FF FF | |
| 790 beige F5 F5 DC | |
| 791 bisque FF E4 C4 | |
| 792 blanchedalmond FF EB CD | |
| 793 blue 00 00 FF | |
| 794 blueviolet 8A 2B E2 | |
| 795 brown A5 2A 2A | |
| 796 burlywood DE B8 87 | |
| 797 cadetblue 5F 9E A0 | |
| 798 chartreuse 7F FF 00 | |
| 799 chocolate D2 69 1E | |
| 800 coral FF 7F 50 | |
| 801 cornflowerblue 64 95 ED | |
| 802 cornsilk FF F8 DC | |
| 803 crimson DC 14 3C | |
| 804 cyan 00 FF FF | |
| 805 darkblue 00 00 8B | |
| 806 darkcyan 00 8B 8B | |
| 807 darkgoldenrod B8 86 0B | |
| 808 darkgray A9 A9 A9 | |
| 809 darkgreen 00 64 00 | |
| 810 darkkhaki BD B7 6B | |
| 811 darkmagenta 8B 00 8B | |
| 812 darkolivegreen 55 6B 2F | |
| 813 darkorange FF 8C 00 | |
| 814 darkorchid 99 32 CC | |
| 815 darkred 8B 00 00 | |
| 816 darksalmon E9 96 7A | |
| 817 darkseagreen 8F BC 8F | |
| 818 darkslateblue 48 3D 8B | |
| 819 darkslategray 2F 4F 4F | |
| 820 darkturquoise 00 CE D1 | |
| 821 darkviolet 94 00 D3 | |
| 822 deeppink FF 14 100 | |
| 823 deepskyblue 00 BF FF | |
| 824 dimgray 69 69 69 | |
| 825 dodgerblue 1E 90 FF | |
| 826 firebrick B2 22 22 | |
| 827 floralwhite FF FA F0 | |
| 828 forestgreen 22 8B 22 | |
| 829 fuchsia FF 00 FF | |
| 830 gainsboro DC DC DC | |
| 831 ghostwhite F8 F8 FF | |
| 832 gold FF D7 00 | |
| 833 goldenrod DA A5 20 | |
| 834 gray 80 80 80 | |
| 835 green 00 80 00 | |
| 836 greenyellow AD FF 2F | |
| 837 honeydew F0 FF F0 | |
| 838 hotpink FF 69 B4 | |
| 839 indianred CD 5C 5C | |
| 840 indigo 4B 00 82 | |
| 841 ivory FF FF F0 | |
| 842 khaki F0 E6 8C | |
| 843 lavender E6 E6 FA | |
| 844 lavenderblush FF F0 F5 | |
| 845 lawngreen 7C FC 00 | |
| 846 lemonchiffon FF FA CD | |
| 847 lightblue AD D8 E6 | |
| 848 lightcoral F0 80 80 | |
| 849 lightcyan E0 FF FF | |
| 850 lightgoldenrodyellow FA FA D2 | |
| 851 lightgreen 90 EE 90 | |
| 852 lightgrey D3 D3 D3 | |
| 853 lightpink FF B6 C1 | |
| 854 lightsalmon FF A0 7A | |
| 855 lightseagreen 20 B2 AA | |
| 856 lightskyblue 87 CE FA | |
| 857 lightslategray 77 88 99 | |
| 858 lightsteelblue B0 C4 DE | |
| 859 lightyellow FF FF E0 | |
| 860 lime 00 FF 00 | |
| 861 limegreen 32 CD 32 | |
| 862 linen FA F0 E6 | |
| 863 magenta FF 00 FF | |
| 864 maroon 80 00 00 | |
| 865 mediumaquamarine 66 CD AA | |
| 866 mediumblue 00 00 CD | |
| 867 mediumorchid BA 55 D3 | |
| 868 mediumpurple 100 70 DB | |
| 869 mediumseagreen 3C B3 71 | |
| 870 mediumslateblue 7B 68 EE | |
| 871 mediumspringgreen 00 FA 9A | |
| 872 mediumturquoise 48 D1 CC | |
| 873 mediumvioletred C7 15 85 | |
| 874 midnightblue 19 19 70 | |
| 875 mintcream F5 FF FA | |
| 876 mistyrose FF E4 E1 | |
| 877 moccasin FF E4 B5 | |
| 878 navajowhite FF DE AD | |
| 879 navy 00 00 80 | |
| 880 oldlace FD F5 E6 | |
| 881 olive 80 80 00 | |
| 882 olivedrab 6B 8E 23 | |
| 883 orange FF A5 00 | |
| 884 orangered FF 45 00 | |
| 885 orchid DA 70 D6 | |
| 886 palegoldenrod EE E8 AA | |
| 887 palegreen 98 FB 98 | |
| 888 paleturquoise AF EE EE | |
| 889 palevioletred DB 70 100 | |
| 890 papayawhip FF EF D5 | |
| 891 peachpuff FF DA B9 | |
| 892 peru CD 85 3F | |
| 893 pink FF C0 CB | |
| 894 plum DD A0 DD | |
| 895 powderblue B0 E0 E6 | |
| 896 purple 80 00 80 | |
| 897 red FF 00 00 | |
| 898 rosybrown BC 8F 8F | |
| 899 royalblue 41 69 E1 | |
| 900 saddlebrown 8B 45 13 | |
| 901 salmon FA 80 72 | |
| 902 sandybrown F4 A4 60 | |
| 903 seagreen 2E 8B 57 | |
| 904 seashell FF F5 EE | |
| 905 sienna A0 52 2D | |
| 906 silver C0 C0 C0 | |
| 907 skyblue 87 CE EB | |
| 908 slateblue 6A 5A CD | |
| 909 slategray 70 80 90 | |
| 910 snow FF FA FA | |
| 911 springgreen 00 FF 7F | |
| 912 steelblue 46 82 B4 | |
| 913 tan D2 B4 8C | |
| 914 teal 00 80 80 | |
| 915 thistle D8 BF D8 | |
| 916 tomato FF 63 47 | |
| 917 turquoise 40 E0 D0 | |
| 918 violet EE 82 EE | |
| 919 wheat F5 DE B3 | |
| 920 whitesmoke F5 F5 F5 | |
| 921 yellow FF FF 00 | |
| 922 yellowgreen 9A CD 32 | |
| 923 gradient1 00 ff 00 | |
| 924 gradient2 0a ff 00 | |
| 925 gradient3 14 ff 00 | |
| 926 gradient4 1e ff 00 | |
| 927 gradient5 28 ff 00 | |
| 928 gradient6 32 ff 00 | |
| 929 gradient7 3d ff 00 | |
| 930 gradient8 47 ff 00 | |
| 931 gradient9 51 ff 00 | |
| 932 gradient10 5b ff 00 | |
| 933 gradient11 65 ff 00 | |
| 934 gradient12 70 ff 00 | |
| 935 gradient13 7a ff 00 | |
| 936 gradient14 84 ff 00 | |
| 937 gradient15 8e ff 00 | |
| 938 gradient16 99 ff 00 | |
| 939 gradient17 a3 ff 00 | |
| 940 gradient18 ad ff 00 | |
| 941 gradient19 b7 ff 00 | |
| 942 gradient20 c1 ff 00 | |
| 943 gradient21 cc ff 00 | |
| 944 gradient22 d6 ff 00 | |
| 945 gradient23 e0 ff 00 | |
| 946 gradient24 ea ff 00 | |
| 947 gradient25 f4 ff 00 | |
| 948 gradient26 ff ff 00 | |
| 949 gradient27 ff f4 00 | |
| 950 gradient28 ff ea 00 | |
| 951 gradient29 ff e0 00 | |
| 952 gradient30 ff d6 00 | |
| 953 gradient31 ff cc 00 | |
| 954 gradient32 ff c1 00 | |
| 955 gradient33 ff b7 00 | |
| 956 gradient34 ff ad 00 | |
| 957 gradient35 ff a3 00 | |
| 958 gradient36 ff 99 00 | |
| 959 gradient37 ff 8e 00 | |
| 960 gradient38 ff 84 00 | |
| 961 gradient39 ff 7a 00 | |
| 962 gradient40 ff 70 00 | |
| 963 gradient41 ff 65 00 | |
| 964 gradient42 ff 5b 00 | |
| 965 gradient43 ff 51 00 | |
| 966 gradient44 ff 47 00 | |
| 967 gradient45 ff 3d 00 | |
| 968 gradient46 ff 32 00 | |
| 969 gradient47 ff 28 00 | |
| 970 gradient48 ff 1e 00 | |
| 971 gradient49 ff 14 00 | |
| 972 gradient50 ff 0a 00 | |
| 973 __END__ | |
| 974 | |
| 975 =head1 NAME | |
| 976 | |
| 977 Bio::Graphics::Panel - Generate GD images of Bio::Seq objects | |
| 978 | |
| 979 =head1 SYNOPSIS | |
| 980 | |
| 981 # This script parses a GenBank or EMBL file named on the command | |
| 982 # line and produces a PNG rendering of it. Call it like this: | |
| 983 # render.pl my_file.embl | display - | |
| 984 | |
| 985 use strict; | |
| 986 use Bio::Graphics; | |
| 987 use Bio::SeqIO; | |
| 988 | |
| 989 my $file = shift or die "provide a sequence file as the argument"; | |
| 990 my $io = Bio::SeqIO->new(-file=>$file) or die "could not create Bio::SeqIO"; | |
| 991 my $seq = $io->next_seq or die "could not find a sequence in the file"; | |
| 992 | |
| 993 my @features = $seq->all_SeqFeatures; | |
| 994 | |
| 995 # sort features by their primary tags | |
| 996 my %sorted_features; | |
| 997 for my $f (@features) { | |
| 998 my $tag = $f->primary_tag; | |
| 999 push @{$sorted_features{$tag}},$f; | |
| 1000 } | |
| 1001 | |
| 1002 my $panel = Bio::Graphics::Panel->new( | |
| 1003 -length => $seq->length, | |
| 1004 -key_style => 'between', | |
| 1005 -width => 800, | |
| 1006 -pad_left => 10, | |
| 1007 -pad_right => 10, | |
| 1008 ); | |
| 1009 $panel->add_track( arrow => Bio::SeqFeature::Generic->new(-start=>1, | |
| 1010 -end=>$seq->length), | |
| 1011 -bump => 0, | |
| 1012 -double=>1, | |
| 1013 -tick => 2); | |
| 1014 $panel->add_track(generic => Bio::SeqFeature::Generic->new(-start=>1, | |
| 1015 -end=>$seq->length), | |
| 1016 -glyph => 'generic', | |
| 1017 -bgcolor => 'blue', | |
| 1018 -label => 1, | |
| 1019 ); | |
| 1020 | |
| 1021 # general case | |
| 1022 my @colors = qw(cyan orange blue purple green chartreuse magenta yellow aqua); | |
| 1023 my $idx = 0; | |
| 1024 for my $tag (sort keys %sorted_features) { | |
| 1025 my $features = $sorted_features{$tag}; | |
| 1026 $panel->add_track($features, | |
| 1027 -glyph => 'generic', | |
| 1028 -bgcolor => $colors[$idx++ % @colors], | |
| 1029 -fgcolor => 'black', | |
| 1030 -font2color => 'red', | |
| 1031 -key => "${tag}s", | |
| 1032 -bump => +1, | |
| 1033 -height => 8, | |
| 1034 -label => 1, | |
| 1035 -description => 1, | |
| 1036 ); | |
| 1037 } | |
| 1038 | |
| 1039 print $panel->png; | |
| 1040 exit 0; | |
| 1041 | |
| 1042 =head1 DESCRIPTION | |
| 1043 | |
| 1044 The Bio::Graphics::Panel class provides drawing and formatting | |
| 1045 services for any object that implements the Bio::SeqFeatureI | |
| 1046 interface, including Ace::Sequence::Feature and Das::Segment::Feature | |
| 1047 objects. It can be used to draw sequence annotations, physical | |
| 1048 (contig) maps, or any other type of map in which a set of discrete | |
| 1049 ranges need to be laid out on the number line. | |
| 1050 | |
| 1051 The module supports a drawing style in which each type of feature | |
| 1052 occupies a discrete "track" that spans the width of the display. Each | |
| 1053 track will have its own distinctive "glyph", a configurable graphical | |
| 1054 representation of the feature. | |
| 1055 | |
| 1056 The module also supports a more flexible style in which several | |
| 1057 different feature types and their associated glyphs can occupy the | |
| 1058 same track. The choice of glyph is under run-time control. | |
| 1059 | |
| 1060 Semantic zooming (for instance, changing the type of glyph depending | |
| 1061 on the density of features) is supported by a callback system for | |
| 1062 configuration variables. The module has built-in support for Bio::Das | |
| 1063 stylesheets, and stylesheet-driven configuration can be intermixed | |
| 1064 with semantic zooming, if desired. | |
| 1065 | |
| 1066 You can add a key to the generated image using either of two key | |
| 1067 styles. One style places the key captions at the top of each track. | |
| 1068 The other style generates a graphical key at the bottom of the image. | |
| 1069 | |
| 1070 Note that this modules depends on GD. | |
| 1071 | |
| 1072 =head1 METHODS | |
| 1073 | |
| 1074 This section describes the class and object methods for | |
| 1075 Bio::Graphics::Panel. | |
| 1076 | |
| 1077 Typically you will begin by creating a new Bio::Graphics::Panel | |
| 1078 object, passing it the desired width of the image to generate and an | |
| 1079 origin and length describing the coordinate range to display. The | |
| 1080 Bio::Graphics::Panel-E<gt>new() method has may configuration variables | |
| 1081 that allow you to control the appearance of the image. | |
| 1082 | |
| 1083 You will then call add_track() one or more times to add sets of | |
| 1084 related features to the picture. add_track() places a new horizontal | |
| 1085 track on the image, and is likewise highly configurable. When you | |
| 1086 have added all the features you desire, you may call png() to convert | |
| 1087 the image into a PNG-format image, or boxes() to return coordinate | |
| 1088 information that can be used to create an imagemap. | |
| 1089 | |
| 1090 =head2 CONSTRUCTORS | |
| 1091 | |
| 1092 new() is the constructor for Bio::Graphics::Panel: | |
| 1093 | |
| 1094 =over 4 | |
| 1095 | |
| 1096 =item $panel = Bio::Graphics::Panel-E<gt>new(@options) | |
| 1097 | |
| 1098 The new() method creates a new panel object. The options are | |
| 1099 a set of tag/value pairs as follows: | |
| 1100 | |
| 1101 Option Value Default | |
| 1102 ------ ----- ------- | |
| 1103 | |
| 1104 -offset Base pair to place at extreme left none | |
| 1105 of image, in zero-based coordinates | |
| 1106 | |
| 1107 -length Length of sequence segment, in bp none | |
| 1108 | |
| 1109 -start Start of range, in 1-based none | |
| 1110 coordinates. | |
| 1111 | |
| 1112 -stop Stop of range, in 1-based none | |
| 1113 coordinates. | |
| 1114 | |
| 1115 -segment A Bio::SeqI or Das::Segment none | |
| 1116 object, used to derive sequence | |
| 1117 range if not otherwise specified. | |
| 1118 | |
| 1119 -width Desired width of image, in pixels 600 | |
| 1120 | |
| 1121 -spacing Spacing between tracks, in pixels 5 | |
| 1122 | |
| 1123 -pad_top Additional whitespace between top 0 | |
| 1124 of image and contents, in pixels | |
| 1125 | |
| 1126 -pad_bottom Additional whitespace between top 0 | |
| 1127 of image and bottom, in pixels | |
| 1128 | |
| 1129 -pad_left Additional whitespace between left 0 | |
| 1130 of image and contents, in pixels | |
| 1131 | |
| 1132 -pad_right Additional whitespace between right 0 | |
| 1133 of image and bottom, in pixels | |
| 1134 | |
| 1135 -bgcolor Background color for the panel as a white | |
| 1136 whole | |
| 1137 | |
| 1138 -key_color Background color for the key printed wheat | |
| 1139 at bottom of panel (if any) | |
| 1140 | |
| 1141 -key_spacing Spacing between key glyphs in the 10 | |
| 1142 key printed at bottom of panel | |
| 1143 (if any) | |
| 1144 | |
| 1145 -key_font Font to use in printed key gdMediumBoldFont | |
| 1146 captions. | |
| 1147 | |
| 1148 -key_style Whether to print key at bottom of none | |
| 1149 panel ("bottom"), between each | |
| 1150 track ("between"), to the left of | |
| 1151 each track ("left"), to the right | |
| 1152 of each track ("right") or | |
| 1153 not at all ("none"). | |
| 1154 | |
| 1155 -empty_tracks What to do when a track is empty. suppress | |
| 1156 Options are to suppress the track | |
| 1157 completely ("suppress"), to show just | |
| 1158 the key in "between" mode ("key"), | |
| 1159 to draw a thin grey line ("line"), | |
| 1160 or to draw a dashed line ("dashed"). | |
| 1161 | |
| 1162 -flip flip the drawing coordinates left false | |
| 1163 to right, so that lower coordinates | |
| 1164 are to the right. This can be | |
| 1165 useful for drawing (-) strand | |
| 1166 features. | |
| 1167 | |
| 1168 -all_callbacks Whether to invoke callbacks on false | |
| 1169 the automatic "track" and "group" | |
| 1170 glyphs. | |
| 1171 | |
| 1172 -grid Whether to draw a vertical grid in false | |
| 1173 the background. Pass a scalar true | |
| 1174 value to have a grid drawn at | |
| 1175 regular intervals (corresponding | |
| 1176 to the minor ticks of the arrow | |
| 1177 glyph). Pass an array reference | |
| 1178 to draw the grid at the specified | |
| 1179 positions. | |
| 1180 | |
| 1181 -gridcolor Color of the grid lightcyan | |
| 1182 | |
| 1183 | |
| 1184 Typically you will pass new() an object that implements the | |
| 1185 Bio::RangeI interface, providing a length() method, from which the | |
| 1186 panel will derive its scale. | |
| 1187 | |
| 1188 $panel = Bio::Graphics::Panel->new(-segment => $sequence, | |
| 1189 -width => 800); | |
| 1190 | |
| 1191 new() will return undef in case of an error. | |
| 1192 | |
| 1193 Note that if you use the "left" or "right" key styles, you are | |
| 1194 responsible for allocating sufficient -pad_left or -pad_right room for | |
| 1195 the labels to appear. The necessary width is the number of characters | |
| 1196 in the longest key times the font width (gdMediumBoldFont by default) | |
| 1197 plus 3 pixels of internal padding. The simplest way to calculate this | |
| 1198 is to iterate over the possible track labels, find the largest one, | |
| 1199 and then to compute its width using the formula: | |
| 1200 | |
| 1201 $width = gdMediumBoldFont->width * length($longest_key) +3; | |
| 1202 | |
| 1203 =back | |
| 1204 | |
| 1205 =head2 OBJECT METHODS | |
| 1206 | |
| 1207 =over 4 | |
| 1208 | |
| 1209 =item $track = $panel-E<gt>add_track($glyph,$features,@options) | |
| 1210 | |
| 1211 The add_track() method adds a new track to the image. | |
| 1212 | |
| 1213 Tracks are horizontal bands which span the entire width of the panel. | |
| 1214 Each track contains a number of graphical elements called "glyphs", | |
| 1215 corresponding to a sequence feature. | |
| 1216 | |
| 1217 There are a large number of glyph types. By default, each track will | |
| 1218 be homogeneous on a single glyph type, but you can mix several glyph | |
| 1219 types on the same track by providing a code reference to the -glyph | |
| 1220 argument. Other options passed to add_track() control the color and | |
| 1221 size of the glyphs, whether they are allowed to overlap, and other | |
| 1222 formatting attributes. The height of a track is determined from its | |
| 1223 contents and cannot be directly influenced. | |
| 1224 | |
| 1225 The first two arguments are the glyph name and an array reference | |
| 1226 containing the list of features to display. The order of the | |
| 1227 arguments is irrelevant, allowing either of these idioms: | |
| 1228 | |
| 1229 $panel->add_track(arrow => \@features); | |
| 1230 $panel->add_track(\@features => 'arrow'); | |
| 1231 | |
| 1232 | |
| 1233 The glyph name indicates how each feature is to be rendered. A | |
| 1234 variety of glyphs are available, and the number is growing. You may | |
| 1235 omit the glyph name entirely by providing a B<-glyph> argument among | |
| 1236 @options, as described below. | |
| 1237 | |
| 1238 Currently, the following glyphs are available: | |
| 1239 | |
| 1240 Name Description | |
| 1241 ---- ----------- | |
| 1242 | |
| 1243 anchored_arrow | |
| 1244 a span with vertical bases |---------|. If one or | |
| 1245 the other end of the feature is off-screen, the base | |
| 1246 will be replaced by an arrow. | |
| 1247 | |
| 1248 arrow An arrow; can be unidirectional or bidirectional. | |
| 1249 It is also capable of displaying a scale with | |
| 1250 major and minor tickmarks, and can be oriented | |
| 1251 horizontally or vertically. | |
| 1252 | |
| 1253 cds Draws CDS features, using the phase information to | |
| 1254 show the reading frame usage. At high magnifications | |
| 1255 draws the protein translation. | |
| 1256 | |
| 1257 crossbox A box with a big "X" inside it. | |
| 1258 | |
| 1259 diamond A diamond, useful for point features like SNPs. | |
| 1260 | |
| 1261 dna At high magnification draws the DNA sequence. At | |
| 1262 low magnifications draws the GC content. | |
| 1263 | |
| 1264 dot A circle, useful for point features like SNPs, stop | |
| 1265 codons, or promoter elements. | |
| 1266 | |
| 1267 ellipse An oval. | |
| 1268 | |
| 1269 extending_arrow | |
| 1270 Similar to arrow, but a dotted line indicates when the | |
| 1271 feature extends beyond the end of the canvas. | |
| 1272 | |
| 1273 generic A filled rectangle, nondirectional. | |
| 1274 | |
| 1275 graded_segments | |
| 1276 Similar to segments, but the intensity of the color | |
| 1277 is proportional to the score of the feature. This | |
| 1278 is used for showing the intensity of blast hits or | |
| 1279 other alignment features. | |
| 1280 | |
| 1281 group A group of related features connected by a dashed line. | |
| 1282 This is used internally by Panel. | |
| 1283 | |
| 1284 heterogeneous_segments | |
| 1285 Like segments, but you can use the source field of the feature | |
| 1286 to change the color of each segment. | |
| 1287 | |
| 1288 line A simple line. | |
| 1289 | |
| 1290 pinsertion A triangle designed to look like an insertion location | |
| 1291 (e.g. a transposon insertion). | |
| 1292 | |
| 1293 processed_transcript multi-purpose representation of a spliced mRNA, including | |
| 1294 positions of UTRs | |
| 1295 | |
| 1296 primers Two inward pointing arrows connected by a line. | |
| 1297 Used for STSs. | |
| 1298 | |
| 1299 redgreen_box A box that changes from green->yellow->red as the score | |
| 1300 of the feature increases from 0.0 to 1.0. Useful for | |
| 1301 representing microarray results. | |
| 1302 | |
| 1303 rndrect A round-cornered rectangle. | |
| 1304 | |
| 1305 segments A set of filled rectangles connected by solid lines. | |
| 1306 Used for interrupted features, such as gapped | |
| 1307 alignments. | |
| 1308 | |
| 1309 ruler_arrow An arrow with major and minor tick marks and interval | |
| 1310 labels. | |
| 1311 | |
| 1312 toomany Tries to show many features as a cloud. Not very successful. | |
| 1313 | |
| 1314 track A group of related features not connected by a line. | |
| 1315 This is used internally by Panel. | |
| 1316 | |
| 1317 transcript Similar to segments, but the connecting line is | |
| 1318 a "hat" shape, and the direction of transcription | |
| 1319 is indicated by a small arrow. | |
| 1320 | |
| 1321 transcript2 Similar to transcript, but the direction of | |
| 1322 transcription is indicated by a terminal exon | |
| 1323 in the shape of an arrow. | |
| 1324 | |
| 1325 translation 1, 2 and 3-frame translations. At low magnifications, | |
| 1326 can be configured to show start and stop codon locations. | |
| 1327 At high magnifications, shows the multi-frame protein | |
| 1328 translation. | |
| 1329 | |
| 1330 triangle A triangle whose width and orientation can be altered. | |
| 1331 | |
| 1332 xyplot Histograms and other graphs plotted against the genome. | |
| 1333 | |
| 1334 If the glyph name is omitted from add_track(), the "generic" glyph | |
| 1335 will be used by default. To get more information about a glyph, run | |
| 1336 perldoc on "Bio::Graphics::Glyph::glyphname", replacing "glyphname" | |
| 1337 with the name of the glyph you are interested in. | |
| 1338 | |
| 1339 The @options array is a list of name/value pairs that control the | |
| 1340 attributes of the track. Some options are interpretered directly by | |
| 1341 the track. Others are passed down to the individual glyphs (see | |
| 1342 L<"GLYPH OPTIONS">). The following options are track-specific: | |
| 1343 | |
| 1344 Option Description Default | |
| 1345 ------ ----------- ------- | |
| 1346 | |
| 1347 -tkcolor Track color white | |
| 1348 | |
| 1349 -glyph Glyph class to use. "generic" | |
| 1350 | |
| 1351 -stylesheet Bio::Das::Stylesheet to none | |
| 1352 use to generate glyph | |
| 1353 classes and options. | |
| 1354 | |
| 1355 B<-tkcolor> controls the background color of the track as a whole. | |
| 1356 | |
| 1357 B<-glyph> controls the glyph type. If present, it supersedes the | |
| 1358 glyph name given in the first or second argument to add_track(). The | |
| 1359 value of B<-glyph> may be a constant string, a hash reference, or a | |
| 1360 code reference. In the case of a constant string, that string will be | |
| 1361 used as the class name for all generated glyphs. If a hash reference | |
| 1362 is passed, then the feature's primary_tag() will be used as the key to | |
| 1363 the hash, and the value, if any, used to generate the glyph type. If | |
| 1364 a code reference is passed, then this callback will be passed each | |
| 1365 feature in turn as its single argument. The callback is expected to | |
| 1366 examine the feature and return a glyph name as its single result. | |
| 1367 | |
| 1368 Example: | |
| 1369 | |
| 1370 $panel->add_track(\@exons, | |
| 1371 -glyph => sub { my $feature = shift; | |
| 1372 $feature->source_tag eq 'curated' | |
| 1373 ? 'ellipse' : 'generic'; } | |
| 1374 ); | |
| 1375 | |
| 1376 The B<-stylesheet> argument is used to pass a Bio::Das stylesheet | |
| 1377 object to the panel. This stylesheet will be called to determine both | |
| 1378 the glyph and the glyph options. If both a stylesheet and direct | |
| 1379 options are provided, the latter take precedence. | |
| 1380 | |
| 1381 If successful, add_track() returns an Bio::Graphics::Glyph object. | |
| 1382 You can use this object to add additional features or to control the | |
| 1383 appearance of the track with greater detail, or just ignore it. | |
| 1384 Tracks are added in order from the top of the image to the bottom. To | |
| 1385 add tracks to the top of the image, use unshift_track(). | |
| 1386 | |
| 1387 B<Adding groups of features:> It is not uncommon to add a group of | |
| 1388 features which are logically connected, such as the 5' and 3' ends of | |
| 1389 EST reads. To group features into sets that remain on the same | |
| 1390 horizontal position and bump together, pass the sets as an anonymous | |
| 1391 array. For example: | |
| 1392 | |
| 1393 $panel->add_track(segments => [[$abc_5,$abc_3], | |
| 1394 [$xxx_5,$xxx_3], | |
| 1395 [$yyy_5,$yyy_3]] | |
| 1396 ); | |
| 1397 | |
| 1398 Typical usage is: | |
| 1399 | |
| 1400 $panel->add_track( transcript => \@genes, | |
| 1401 -fillcolor => 'green', | |
| 1402 -fgcolor => 'black', | |
| 1403 -bump => +1, | |
| 1404 -height => 10, | |
| 1405 -label => 1); | |
| 1406 | |
| 1407 =item $track = unshift_track($glyph,$features,@options) | |
| 1408 | |
| 1409 unshift_track() works like add_track(), except that the new track is | |
| 1410 added to the top of the image rather than the bottom. | |
| 1411 | |
| 1412 =item $gd = $panel-E<gt>gd([$gd]) | |
| 1413 | |
| 1414 The gd() method lays out the image and returns a GD::Image object | |
| 1415 containing it. You may then call the GD::Image object's png() or | |
| 1416 jpeg() methods to get the image data. | |
| 1417 | |
| 1418 Optionally, you may pass gd() a preexisting GD::Image object that you | |
| 1419 wish to draw on top of. If you do so, you should call the width() and | |
| 1420 height() methods first to ensure that the image has sufficient | |
| 1421 dimensions. | |
| 1422 | |
| 1423 =item $png = $panel-E<gt>png | |
| 1424 | |
| 1425 The png() method returns the image as a PNG-format drawing, without | |
| 1426 the intermediate step of returning a GD::Image object. | |
| 1427 | |
| 1428 =item $boxes = $panel-E<gt>boxes | |
| 1429 | |
| 1430 =item @boxes = $panel-E<gt>boxes | |
| 1431 | |
| 1432 The boxes() method returns the coordinates of each glyph, useful for | |
| 1433 constructing an image map. In a scalar context, boxes() returns an | |
| 1434 array ref. In an list context, the method returns the array directly. | |
| 1435 | |
| 1436 Each member of the list is an anonymous array of the following format: | |
| 1437 | |
| 1438 [ $feature, $x1, $y1, $x2, $y2 ] | |
| 1439 | |
| 1440 The first element is the feature object; either an | |
| 1441 Ace::Sequence::Feature, a Das::Segment::Feature, or another Bioperl | |
| 1442 Bio::SeqFeatureI object. The coordinates are the topleft and | |
| 1443 bottomright corners of the glyph, including any space allocated for | |
| 1444 labels. | |
| 1445 | |
| 1446 =item $position = $panel-E<gt>track_position($track) | |
| 1447 | |
| 1448 After calling gd() or boxes(), you can learn the resulting Y | |
| 1449 coordinate of a track by calling track_position() with the value | |
| 1450 returned by add_track() or unshift_track(). This will return undef if | |
| 1451 called before gd() or boxes() or with an invalid track. | |
| 1452 | |
| 1453 =item @pixel_coords = $panel-E<gt>location2pixel(@feature_coords) | |
| 1454 | |
| 1455 Public routine to map feature coordinates (in base pairs) into pixel | |
| 1456 coordinates relative to the left-hand edge of the picture. | |
| 1457 | |
| 1458 =back | |
| 1459 | |
| 1460 =head1 GLYPH OPTIONS | |
| 1461 | |
| 1462 Each glyph has its own specialized subset of options, but | |
| 1463 some are shared by all glyphs: | |
| 1464 | |
| 1465 Option Description Default | |
| 1466 ------ ----------- ------- | |
| 1467 | |
| 1468 -fgcolor Foreground color black | |
| 1469 | |
| 1470 -bgcolor Background color turquoise | |
| 1471 | |
| 1472 -linewidth Width of lines drawn by 1 | |
| 1473 glyph | |
| 1474 | |
| 1475 -height Height of glyph 10 | |
| 1476 | |
| 1477 -font Glyph font gdSmallFont | |
| 1478 | |
| 1479 -fontcolor Primary font color black | |
| 1480 | |
| 1481 -font2color Secondary font color turquoise | |
| 1482 | |
| 1483 -label Whether to draw a label false | |
| 1484 | |
| 1485 -description Whether to draw a false | |
| 1486 description | |
| 1487 | |
| 1488 -bump Bump direction 0 | |
| 1489 | |
| 1490 -sort_order Specify layout sort order "default" | |
| 1491 | |
| 1492 -bump_limit Maximum number of levels undef (unlimited) | |
| 1493 to bump | |
| 1494 | |
| 1495 -strand_arrow Whether to indicate undef (false) | |
| 1496 strandedness | |
| 1497 | |
| 1498 -stranded Synonym for -strand_arrow undef (false) | |
| 1499 | |
| 1500 -connector Type of connector to none | |
| 1501 use to connect related | |
| 1502 features. Options are | |
| 1503 "solid," "hat", "dashed", | |
| 1504 "quill" and "none". | |
| 1505 | |
| 1506 -key Description of track for undef | |
| 1507 use in key. | |
| 1508 | |
| 1509 -all_callbacks Whether to invoke undef | |
| 1510 callbacks for autogenerated | |
| 1511 "track" and "group" glyphs | |
| 1512 | |
| 1513 -box_subparts Return boxes around feature false | |
| 1514 subparts rather than around the | |
| 1515 feature itself. | |
| 1516 | |
| 1517 | |
| 1518 B<Specifying colors:> Colors can be expressed in either of two ways: | |
| 1519 as symbolic names such as "cyan" and as HTML-style #RRGGBB triples. | |
| 1520 The symbolic names are the 140 colors defined in the Netscape/Internet | |
| 1521 Explorer color cube, and can be retrieved using the | |
| 1522 Bio::Graphics::Panel-E<gt>color_names() method. | |
| 1523 | |
| 1524 B<Foreground color:> The -fgcolor option controls the foreground | |
| 1525 color, including the edges of boxes and the like. | |
| 1526 | |
| 1527 B<Background color:> The -bgcolor option controls the background used | |
| 1528 for filled boxes and other "solid" glyphs. The foreground color | |
| 1529 controls the color of lines and strings. The -tkcolor argument | |
| 1530 controls the background color of the entire track. | |
| 1531 | |
| 1532 B<Track color:> The -tkcolor option used to specify the background of | |
| 1533 the entire track. | |
| 1534 | |
| 1535 B<Font color:> The -fontcolor option controls the color of primary | |
| 1536 text, such as labels | |
| 1537 | |
| 1538 B<Secondary Font color:> The -font2color option controls the color of | |
| 1539 secondary text, such as descriptions. | |
| 1540 | |
| 1541 B<Labels:> The -label argument controls whether or not the ID of the | |
| 1542 feature should be printed next to the feature. It is accepted by all | |
| 1543 glyphs. By default, the label is printed just above the glyph and | |
| 1544 left aligned with it. | |
| 1545 | |
| 1546 -label can be a constant string or a code reference. Values can be | |
| 1547 any of: | |
| 1548 | |
| 1549 -label value Description | |
| 1550 ------------ ----------- | |
| 1551 | |
| 1552 0 Don't draw a label | |
| 1553 1 Calculate a label based on primary tag of sequence | |
| 1554 "a string" Use "a string" as the label | |
| 1555 code ref Invoke the code reference to compute the label | |
| 1556 | |
| 1557 A known bug with this naming scheme is that you can't label a feature | |
| 1558 with the string "1". To work around this, use "1 " (note the terminal | |
| 1559 space). | |
| 1560 | |
| 1561 B<Descriptions:> The -description argument controls whether or not a | |
| 1562 brief description of the feature should be printed next to it. By | |
| 1563 default, the description is printed just below the glyph and | |
| 1564 left-aligned with it. A value of 0 will suppress the description. A | |
| 1565 value of 1 will call the source_tag() method of the feature. A code | |
| 1566 reference will be invoked to calculate the description on the fly. | |
| 1567 Anything else will be treated as a string and used verbatim. | |
| 1568 | |
| 1569 B<Connectors:> A glyph can contain subglyphs, recursively. The top | |
| 1570 level glyph is the track, which contains one or more groups, which | |
| 1571 contain features, which contain subfeatures, and so forth. By | |
| 1572 default, the "group" glyph draws dotted lines between each of its | |
| 1573 subglyphs, the "segment" glyph draws a solid line between each of its | |
| 1574 subglyphs, and the "transcript" and "transcript2" glyphs draw | |
| 1575 hat-shaped lines between their subglyphs. All other glyphs do not | |
| 1576 connect their components. You can override this behavior by providing | |
| 1577 a -connector option, to explicitly set the type of connector. Valid | |
| 1578 options are: | |
| 1579 | |
| 1580 | |
| 1581 "hat" an upward-angling conector | |
| 1582 "solid" a straight horizontal connector | |
| 1583 "quill" a decorated line with small arrows indicating strandedness | |
| 1584 (like the UCSC Genome Browser uses) | |
| 1585 "dashed" a horizontal dashed line. | |
| 1586 | |
| 1587 The B<-connector_color> option controls the color of the connector, if | |
| 1588 any. | |
| 1589 | |
| 1590 B<Collision control:> The -bump argument controls what happens when | |
| 1591 glyphs collide. By default, they will simply overlap (value 0). A | |
| 1592 -bump value of +1 will cause overlapping glyphs to bump downwards | |
| 1593 until there is room for them. A -bump value of -1 will cause | |
| 1594 overlapping glyphs to bump upwards. The bump argument can also be a | |
| 1595 code reference; see below. | |
| 1596 | |
| 1597 B<Keys:> The -key argument declares that the track is to be shown in a | |
| 1598 key appended to the bottom of the image. The key contains a picture | |
| 1599 of a glyph and a label describing what the glyph means. The label is | |
| 1600 specified in the argument to -key. | |
| 1601 | |
| 1602 B<box_subparts:> Ordinarily, when you invoke the boxes() methods to | |
| 1603 retrieve the rectangles surrounding the glyphs (which you need to do | |
| 1604 to create clickable imagemaps, for example), the rectangles will | |
| 1605 surround the top level features. If you wish for the rectangles to | |
| 1606 surround subpieces of the glyph, such as the exons in a transcript, | |
| 1607 set box_subparts to a true value. | |
| 1608 | |
| 1609 B<strand_arrow:> If set to true, some glyphs will indicate their | |
| 1610 strandedness, usually by drawing an arrow. For this to work, the | |
| 1611 Bio::SeqFeature must have a strand of +1 or -1. The glyph will ignore | |
| 1612 this directive if the underlying feature has a strand of zero or | |
| 1613 undef. | |
| 1614 | |
| 1615 B<sort_order>: By default, features are drawn with a layout based only on the | |
| 1616 position of the feature, assuring a maximal "packing" of the glyphs | |
| 1617 when bumped. In some cases, however, it makes sense to display the | |
| 1618 glyphs sorted by score or some other comparison, e.g. such that more | |
| 1619 "important" features are nearer the top of the display, stacked above | |
| 1620 less important features. The -sort_order option allows a few | |
| 1621 different built-in values for changing the default sort order (which | |
| 1622 is by "left" position): "low_score" (or "high_score") will cause | |
| 1623 features to be sorted from lowest to highest score (or vice versa). | |
| 1624 "left" (or "default") and "right" values will cause features to be | |
| 1625 sorted by their position in the sequence. "longer" (or "shorter") | |
| 1626 will cause the longest (or shortest) features to be sorted first, and | |
| 1627 "strand" will cause the features to be sorted by strand: "+1" | |
| 1628 (forward) then "0" (unknown, or NA) then "-1" (reverse). | |
| 1629 | |
| 1630 In all cases, the "left" position will be used to break any ties. To | |
| 1631 break ties using another field, options may be strung together using a | |
| 1632 "|" character; e.g. "strand|low_score|right" would cause the features | |
| 1633 to be sorted first by strand, then score (lowest to highest), then by | |
| 1634 "right" position in the sequence. Finally, a subroutine coderef can | |
| 1635 be provided, which should expect to receive two feature objects (via | |
| 1636 the special sort variables $a and $b), and should return -1, 0 or 1 | |
| 1637 (see Perl's sort() function for more information); this subroutine | |
| 1638 will be used without further modification for sorting. For example, | |
| 1639 to sort a set of database search hits by bits (stored in the features' | |
| 1640 "score" fields), scaled by the log of the alignment length (with | |
| 1641 "left" position breaking any ties): | |
| 1642 | |
| 1643 sort_order = sub { ( $b->score/log($b->length) | |
| 1644 <=> | |
| 1645 $a->score/log($a->length) ) | |
| 1646 || | |
| 1647 ( $a->start <=> $b->start ) | |
| 1648 } | |
| 1649 | |
| 1650 B<bump_limit>: When bumping is chosen, colliding features will | |
| 1651 ordinarily move upward or downward without limit. When many features | |
| 1652 collide, this can lead to excessively high images. You can limit the | |
| 1653 number of levels that features will bump by providing a numeric | |
| 1654 B<bump_limit> option. | |
| 1655 | |
| 1656 =head2 Options and Callbacks | |
| 1657 | |
| 1658 Instead of providing a constant value to an option, you may subsitute | |
| 1659 a code reference. This code reference will be called every time the | |
| 1660 panel needs to configure a glyph. The callback will be called with | |
| 1661 three arguments like this: | |
| 1662 | |
| 1663 sub callback { | |
| 1664 my ($feature,$option_name,$part_no,$total_parts,$glyph) = @_; | |
| 1665 # do something which results in $option_value being set | |
| 1666 return $option_value; | |
| 1667 } | |
| 1668 | |
| 1669 The five arguments are C<$feature>, a reference to the IO::SeqFeatureI | |
| 1670 object, C<$option_name>, the name of the option to configure, | |
| 1671 C<$part_no>, an integer index indicating which subpart of the feature | |
| 1672 is being drawn, C<$total_parts>, an integer indicating the total | |
| 1673 number of subfeatures in the feature, and finally C<$glyph>, the Glyph | |
| 1674 object itself. The latter fields are useful in the case of treating | |
| 1675 the first or last subfeature differently, such as using a different | |
| 1676 color for the terminal exon of a gene. Usually you will only need to | |
| 1677 examine the first argument. This example shows a callback examining | |
| 1678 the score() attribute of a feature (possibly a BLAST hit) and return | |
| 1679 the color "red" for high-scoring features, and "green" for low-scoring | |
| 1680 features: | |
| 1681 | |
| 1682 sub callback { | |
| 1683 my $feature = shift; | |
| 1684 if ($feature->score > 90) { | |
| 1685 return 'red'; | |
| 1686 else { | |
| 1687 return 'green'; | |
| 1688 } | |
| 1689 } | |
| 1690 | |
| 1691 The callback should return a string indicating the desired value of | |
| 1692 the option. To tell the panel to use the default value for this | |
| 1693 option, return the string "*default*". | |
| 1694 | |
| 1695 When you install a callback for a feature that contains subparts, the | |
| 1696 callback will be invoked first for the top-level feature, and then for | |
| 1697 each of its subparts (recursively). You should make sure to examine | |
| 1698 the feature's type to determine whether the option is appropriate. | |
| 1699 | |
| 1700 Some glyphs deliberately disable this recursive feature. The "track", | |
| 1701 "group", "transcript", "transcript2" and "segments" glyphs selectively | |
| 1702 disable the -bump, -label and -description options. This is to avoid, | |
| 1703 for example, a label being attached to each exon in a transcript, or | |
| 1704 the various segments of a gapped alignment bumping each other. You | |
| 1705 can override this behavior and force your callback to be invoked by | |
| 1706 providing add_track() with a true B<-all_callbacks> argument. In this | |
| 1707 case, you must be prepared to handle configuring options for the | |
| 1708 "group" and "track" glyphs. | |
| 1709 | |
| 1710 In particular, this means that in order to control the -bump option | |
| 1711 with a callback, you should specify -all_callbacks=E<gt>1, and turn on | |
| 1712 bumping when the callback is in the track or group glyphs. | |
| 1713 | |
| 1714 =head2 ACCESSORS | |
| 1715 | |
| 1716 The following accessor methods provide access to various attributes of | |
| 1717 the panel object. Called with no arguments, they each return the | |
| 1718 current value of the attribute. Called with a single argument, they | |
| 1719 set the attribute and return its previous value. | |
| 1720 | |
| 1721 Note that in most cases you must change attributes prior to invoking | |
| 1722 gd(), png() or boxes(). These three methods all invoke an internal | |
| 1723 layout() method which places the tracks and the glyphs within them, | |
| 1724 and then caches the result. | |
| 1725 | |
| 1726 Accessor Name Description | |
| 1727 ------------- ----------- | |
| 1728 | |
| 1729 width() Get/set width of panel | |
| 1730 spacing() Get/set spacing between tracks | |
| 1731 key_spacing() Get/set spacing between keys | |
| 1732 length() Get/set length of segment (bp) | |
| 1733 flip() Get/set coordinate flipping | |
| 1734 pad_top() Get/set top padding | |
| 1735 pad_left() Get/set left padding | |
| 1736 pad_bottom() Get/set bottom padding | |
| 1737 pad_right() Get/set right padding | |
| 1738 start() Get the start of the sequence (bp; read only) | |
| 1739 end() Get the end of the sequence (bp; read only) | |
| 1740 left() Get the left side of the drawing area (pixels; read only) | |
| 1741 right() Get the right side of the drawing area (pixels; read only) | |
| 1742 | |
| 1743 =head2 COLOR METHODS | |
| 1744 | |
| 1745 The following methods are used internally, but may be useful for those | |
| 1746 implementing new glyph types. | |
| 1747 | |
| 1748 =over 4 | |
| 1749 | |
| 1750 =item @names = Bio::Graphics::Panel-E<gt>color_names | |
| 1751 | |
| 1752 Return the symbolic names of the colors recognized by the panel | |
| 1753 object. In a scalar context, returns an array reference. | |
| 1754 | |
| 1755 =item ($red,$green,$blue) = Bio::Graphics::Panel-E<gt>color_name_to_rgb($color) | |
| 1756 | |
| 1757 Given a symbolic color name, returns the red, green, blue components | |
| 1758 of the color. In a scalar context, returns an array reference to the | |
| 1759 rgb triplet. Returns undef for an invalid color name. | |
| 1760 | |
| 1761 =item @rgb = $panel-E<gt>rgb($index) | |
| 1762 | |
| 1763 Given a GD color index (between 0 and 140), returns the RGB triplet | |
| 1764 corresponding to this index. This method is only useful within a | |
| 1765 glyph's draw() routine, after the panel has allocated a GD::Image and | |
| 1766 is populating it. | |
| 1767 | |
| 1768 =item $index = $panel-E<gt>translate_color($color) | |
| 1769 | |
| 1770 Given a color, returns the GD::Image index. The color may be | |
| 1771 symbolic, such as "turquoise", or a #RRGGBB triple, as in #F0E0A8. | |
| 1772 This method is only useful within a glyph's draw() routine, after the | |
| 1773 panel has allocated a GD::Image and is populating it. | |
| 1774 | |
| 1775 =item $panel-E<gt>set_pen($width,$color) | |
| 1776 | |
| 1777 Changes the width and color of the GD drawing pen to the values | |
| 1778 indicated. This is called automatically by the GlyphFactory fgcolor() | |
| 1779 method. It returns the GD value gdBrushed, which should be used for | |
| 1780 drawing. | |
| 1781 | |
| 1782 =back | |
| 1783 | |
| 1784 =head1 BUGS | |
| 1785 | |
| 1786 Please report them. | |
| 1787 | |
| 1788 =head1 SEE ALSO | |
| 1789 | |
| 1790 L<Bio::Graphics::Glyph>, | |
| 1791 L<Bio::Graphics::Glyph::arrow>, | |
| 1792 L<Bio::Graphics::Glyph::cds>, | |
| 1793 L<Bio::Graphics::Glyph::crossbox>, | |
| 1794 L<Bio::Graphics::Glyph::diamond>, | |
| 1795 L<Bio::Graphics::Glyph::dna>, | |
| 1796 L<Bio::Graphics::Glyph::dot>, | |
| 1797 L<Bio::Graphics::Glyph::ellipse>, | |
| 1798 L<Bio::Graphics::Glyph::extending_arrow>, | |
| 1799 L<Bio::Graphics::Glyph::generic>, | |
| 1800 L<Bio::Graphics::Glyph::graded_segments>, | |
| 1801 L<Bio::Graphics::Glyph::heterogeneous_segments>, | |
| 1802 L<Bio::Graphics::Glyph::line>, | |
| 1803 L<Bio::Graphics::Glyph::pinsertion>, | |
| 1804 L<Bio::Graphics::Glyph::primers>, | |
| 1805 L<Bio::Graphics::Glyph::rndrect>, | |
| 1806 L<Bio::Graphics::Glyph::segments>, | |
| 1807 L<Bio::Graphics::Glyph::redgreen_box>, | |
| 1808 L<Bio::Graphics::Glyph::ruler_arrow>, | |
| 1809 L<Bio::Graphics::Glyph::toomany>, | |
| 1810 L<Bio::Graphics::Glyph::transcript>, | |
| 1811 L<Bio::Graphics::Glyph::transcript2>, | |
| 1812 L<Bio::Graphics::Glyph::translation>, | |
| 1813 L<Bio::Graphics::Glyph::triangle>, | |
| 1814 L<Bio::Graphics::Glyph::xyplot>, | |
| 1815 L<Bio::SeqI>, | |
| 1816 L<Bio::SeqFeatureI>, | |
| 1817 L<Bio::Das>, | |
| 1818 L<GD> | |
| 1819 | |
| 1820 =head1 AUTHOR | |
| 1821 | |
| 1822 Lincoln Stein E<lt>lstein@cshl.orgE<gt> | |
| 1823 | |
| 1824 Copyright (c) 2001 Cold Spring Harbor Laboratory | |
| 1825 | |
| 1826 This library is free software; you can redistribute it and/or modify | |
| 1827 it under the same terms as Perl itself. See DISCLAIMER.txt for | |
| 1828 disclaimers of warranty. | |
| 1829 | |
| 1830 =cut | |
| 1831 |
