package Bio::EnsEMBL::GlyphSet::Videogram;
use strict;
use warnings;
no warnings 'uninitialized';
use base qw(Bio::EnsEMBL::GlyphSet::Videogram_legend);
use Carp;
####################################################################
### set true only while you're generating the vega karyotype image ##
#####################################################################
## end of vega karyotype static image colours ###
sub _init {
my ($self) = @_;
my $config = $self->{'config'};
my $i_w = $self->get_parameter('image_height');
my $c_w = $self->get_parameter('container_width');
return unless $c_w;
$self->_init_bump( '_bump_forward' );
$self->_init_bump( '_bump_reverse' );
my $col = undef;
my $white = 'white';
my $black = 'black';
my $bg = $self->get_parameter( 'bgcolor');
my $red = 'red';
$self->{'pix_per_bp'} = $i_w/$c_w;
my $im_width = $i_w;
my $top_margin = $self->get_parameter('top_margin');
my ($w,$h) = $self->{'config'}->texthelper->Vpx2bp('Tiny');
my $chr = $self->{'container'}->{'chr'} || $self->{'extras'}->{'chr'};
########## fetch the chromosome bands that cover this VC.
my $kba = $self->{'container'}->{'ka'};
my $bands = $kba->fetch_all_by_chr_name($chr);
my $slice_adaptor = $self->{'container'}->{'sa'};
my $slice = $slice_adaptor->fetch_by_region(undef, $chr) or
(carp "$slice_adaptor has no fetch_by_region(undef,$chr)" && return);
my $chr_length = $slice->length || 1;
########## bottom align each chromosome!
my $v_offset = $c_w - $chr_length;
my $bpperpx = $c_w / $config->get_parameter('image_height');
########## overcome a bottom border/margin problem....
my $done_1_acen = 0; # flag for tracking place in chromosome
my $wid = $self->my_config('width') || 24;
my $h_wid = int $wid/2;
my $padding = $self->my_config('padding') || 6;
my $style = $self->my_config('style') || q();
########## get text labels in correct place!
my $h_offset = $style eq 'text' ? $padding
: int($self->my_config('totalwidth') - $wid - ($self->get_parameter('band_labels') eq 'on' ? ($w * 6 + 4) : 0 ))/2
;
my @decorations;
if($padding) {
########## make sure that there is a blank image behind the chromosome so that the glyphset doesn't get "horizontally" squashed.
$self->push($self->Space({
'x' => $c_w - $chr_length/2,
'y' => $h_offset - $padding*1.5,
'width' => 1,
'height' => $padding * 3 + $wid,
'absolutey' => 1,
}));
}
my @bands = sort { $a->start <=> $b->start } @{$bands};
#########
# use this array to store bands created for vega annotation status; draw these last
#
my @annot_bands;
my $alt_stain = 25;
if(scalar @bands) {
for my $band (@bands) {
my $bandname = $band->name();
my $vc_band_start = $band->start() + $v_offset;
my $vc_band_end = $band->end() + $v_offset;
my $stain = lc( $band->stain() );
if ($stain =~ /Annotation/mx) {
push @annot_bands, $band;
next;
}
my @extra = ();
if( $self->get_parameter('band_links') eq 'yes' ) {
@extra = (
'href' => $self->_url({'type' => 'Location', 'action' => 'View', '__clear' => 1, 'r' => "$chr:$vc_band_start-$vc_band_end"}),
'title' => "Band: ".( $stain eq 'acen' ? 'Centromere' : $bandname )
);
}
unless ($stain) {
$stain = 'gpos' .$alt_stain;
$alt_stain = 100 - $alt_stain;
}
my $colour = $self->my_colour($stain);
if( $stain eq 'acen' ) {
if( $done_1_acen ) {
CORE::push @decorations, $self->Poly({
'points' => [ $vc_band_start, $h_offset + $h_wid, $vc_band_end, $h_offset, $vc_band_end, $h_offset + $wid, ],
'colour' => $colour,
'absolutey' => 1,
@extra
});
} else {
CORE::push @decorations, $self->Poly({
'points' => [ $vc_band_start, $h_offset, $vc_band_end, $h_offset + $h_wid, $vc_band_start, $h_offset + $wid, ],
'colour' => $colour,
'absolutey' => 1,
@extra
});
$done_1_acen = 1;
}
} elsif ($stain eq 'stalk') {
CORE::push @decorations, $self->Poly({
'points' => [ $vc_band_start, $h_offset, $vc_band_end, $h_offset + $wid, $vc_band_end, $h_offset, $vc_band_start, $h_offset + $wid, ],
'colour' => $colour,
'absolutey' => 1,
@extra
});
CORE::push @decorations, $self->Rect({
'x' => $vc_band_start,
'y' => $h_offset + int $wid/4,
'width' => $vc_band_end - $vc_band_start,
'height' => $h_wid,
'colour' => $colour,
'absolutey' => 1,
@extra
});
} else {
if (($self->get_parameter('hide_bands') || 'no') eq 'yes') {
$stain = 'gneg';
$colour = $self->my_colour('gneg');
}
my $R = $vc_band_start;
my $T = $bpperpx * ( (int $vc_band_end/$bpperpx) - (int $vc_band_start/$bpperpx) );
$self->push($self->Rect({
'x' => $R,
'y' => $h_offset,
'width' => $T,
'height' => $wid,
'colour' => $colour,
'absolutey' => 1,
@extra
}));
$self->push($self->Line({
'x' => $R,
'y' => $h_offset,
'width' => $T,
'height' => 0,
'colour' => $black,
'absolutey' => 1,
}));
$self->push($self->Line({
'x' => $R,
'y' => $h_offset+$wid,
'width' => $T,
'height' => 0,
'colour' => $black,
'absolutey' => 1,
}));
}
################################################################## only add the band label if the box is big enough to hold it...
if( $self->get_parameter('band_labels') eq 'on' && ## Only if turned on
$stain !~ /^(acen|tip|stalk)$/ && ## Not on "special" bands
$h < $vc_band_end - $vc_band_start ## Only if the box is big enough!
) {
$self->push($self->Text({
'x' => ($vc_band_end + $vc_band_start - $h)/2,
'y' => $h_offset+$wid+4,
'width' => $h,
'height' => $w * length($bandname),
'font' => 'Tiny',
'colour' => $black,
'text' => $bandname,
'absolutey' => 1,
}));
}
}
} else {
foreach (0, $wid) {
$self->push($self->Line({
'x' => $v_offset-1,
'y' => $h_offset+$_,
'width' => $chr_length,
'height' => 0,
'colour' => $black,
'absolutey' => 1,
}));
}
}
#########
# lastly draw annotation status bands (if uncommented the colour definition)
#
for my $band (@annot_bands) {
my $bandname = $band->name();
my $vc_band_start = $band->start() + $v_offset;
my $vc_band_end = $band->end() + $v_offset;
my $stain = $band->stain();
my $R = $vc_band_start;
my $T = $bpperpx * ( (int $vc_band_end/$bpperpx) - (int $vc_band_start/$bpperpx) );
$self->push($self->Rect({
'x' => $R,
'y' => $h_offset,
'width' => $T,
'height' => $wid,
'colour' => $self->my_colour($stain),
'absolutey' => 1,
}));
$self->push($self->Line({
'x' => $R,
'y' => $h_offset,
'width' => $T,
'height' => 0,
'colour' => $black,
'absolutey' => 1,
}));
$self->push($self->Line({
'x' => $R,
'y' => $h_offset+$wid,
'width' => $T,
'height' => 0,
'colour' => $black,
'absolutey' => 1,
}));
}
foreach (@decorations) {
$self->push($_);
}
############################################### Draw the ends of the ideogram
# $self->unshift($self->Rect({
# 'x' => $v_offset,
# 'width' => $chr_length,
# 'y' => $h_offset - $wid / 2,
# 'height' => 2 * $wid,
# 'colour' => 'blue',
# 'absolutey' => 1,
# 'absoluteheight' => 1,
# 'href' => $self->_url({ 'type' => 'Location', 'action' => 'Chromosome', '__clear' => 1, 'r' => $chr }),
# 'title' => "Chromosome: $chr"
# }));
for my $end (
( @bands && $bands[ 0]->stain() eq 'tip' ? () : 0 ),
( @bands && $bands[-1]->stain() eq 'tip' ? () : 1 )
) {
my $direction = $end ? -1 : 1;
my %partials = map { uc($_) => 1 } @{ $self->species_defs->PARTIAL_CHROMOSOMES || [] };
my %artificials = map { uc($_) => 1 } @{ $self->species_defs->ARTIFICIAL_CHROMOSOMES || [] };
if ($partials{uc $chr}) {
########## draw jagged ends for partial chromosomes resolution dependent scaling
my $mod = ($wid < 16) ? 0.5 : 1;
for my $i (1..8*$mod) {
my $x = $v_offset + $chr_length * $end - 4 * (($i % 2) - 1) * $direction * $bpperpx * $mod;
my $y = $h_offset + $wid/(8*$mod) * ($i - 1);
my $width = 4 * (-1 + 2 * ($i % 2)) * $direction * $bpperpx * $mod;
my $height = $wid/(8*$mod);
########## overwrite karyotype bands with appropriate triangles to produce jags
$self->push($self->Poly({
'points' => [ $x, $y, $x + $width * (1 - ($i % 2)),$y + $height * ($i % 2), $x + $width, $y + $height, ],
'colour' => $bg,
'absolutey' => 1,
'absoluteheight' => 1,
}));
########## the actual jagged line
$self->push($self->Line({
'x' => $x,
'y' => $y,
'width' => $width,
'height' => $height,
'colour' => $black,
'absolutey' => 1,
'absoluteheight' => 1,
}));
}
########## black delimiting lines at each side
foreach (0, $wid) {
$self->push($self->Line({
'x' => $v_offset,
'y' => $h_offset + $_,
'width' => 4,
'height' => 0,
'colour' => $black,
'absolutey' => 1,
'absolutewidth' => 1,
}));
}
} elsif ( ($artificials{uc($chr)}) ||
($end == 0 && @bands && $bands[0]->stain() eq 'ACEN') ||
($end == 1 && @bands && $bands[-1]->stain() eq 'ACEN') ||
($end == 0 && $chr =~ /Q|q/mx) ||
($end == 1 && $chr =~ /P|p/mx)
) {
########## draw blunt ends for artificial chromosomes or chr arms
my $x = $v_offset + $chr_length * $end - 1;
my $y = $h_offset;
my $width = 0;
my $height = $wid;
$self->push($self->Line({
'x' => $x,
'y' => $y,
'width' => $width,
'height' => $height,
'colour' => $black,
'absolutey' => 1,
'absolutewidth' => 1,
}));
} else {
########## round ends for full chromosomes
my $max_rows = ( $chr_length / $bpperpx /2 ); ## MAXIMUMROWS.....
my @lines = $wid < 16 ?
( [8,6],[4,4],[2,2] ) :
( [8,5],[5,3],[4,1],[3,1],[2,1],[1,1],[1,1],[1,1] );
for my $I ( 0..$#lines ) {
if($I > $max_rows) {
next;
}
my ($bg_x, $black_x) = @{$lines[$I]};
my $xx = $v_offset + $chr_length * $end + ($I+.5 * $end) * $direction * $bpperpx + ($end ? $bpperpx : 10);
$self->push($self->Line({
'x' => $xx,
'y' => $h_offset,
'width' => 0,
'height' => $wid * $bg_x/24 -1,
'colour' => 'background1',
'absolutey' => 1,
}));
$self->push($self->Line({
'x' => $xx,
'y' => $h_offset + 1 + $wid * (1-$bg_x/24),
'width' => 0,
'height' => $wid * $bg_x/24 -1,
'colour' => 'background1',
'absolutey' => 1,
}));
$self->push($self->Line({
'x' => $xx,
'y' => $h_offset + $wid * $bg_x/24,
'width' => 0,
'height' => $wid * $black_x/24 -1,
'colour' => $black,
'absolutey' => 1,
}));
$self->push($self->Line({
'x' => $xx,
'y' => $h_offset + 1 + $wid * (1-$bg_x/24-$black_x/24),
'width' => 0,
'height' => $wid * $black_x/24 -1,
'colour' => $black,
'absolutey' => 1,
}));
}
}
}
#######################################
# Do the highlighting bit at the end!!!
#######################################
if(defined $self->{'highlights'} && $self->{'highlights'} ne q()) {
for my $highlight_set (reverse @{$self->{'highlights'}}) {
my $highlight_style = $style || $highlight_set->{'style'};
my $type = "highlight_$highlight_style";
my $aggregate_colour = $config->{'_aggregate_colour'};
if($highlight_set->{$chr}) {
# Firstly create a highlights array which contains merged entries!
my @temp_highlights = @{$highlight_set->{$chr}};
my @highlights;
if($highlight_set->{'merge'} && $highlight_set->{'merge'} eq 'no') {
@highlights = @temp_highlights;
} else {
my @bin_flag;
my $bin_length = $padding * ( $highlight_style eq 'arrow' ? 1.5 : 1 ) * $bpperpx;
my $is_aggregated = 0;
foreach (@temp_highlights) {
my $bin_id = int (2 * $v_offset+ $_->{'start'}+$_->{'end'}) / 2 / $bin_length;
if ($bin_id < 0) {
$bin_id = 0;
}
if(my $offset = $bin_flag[$bin_id]) { # We already have a highlight in this bin - so add this one to it!
## Build zmenu
my $zmenu_length = keys %{$highlights[$offset-1]->{'zmenu'}};
for my $entry (sort keys %{$_->{'zmenu'}}) {
if($entry eq 'caption') {
next;
}
my $value = $_->{'zmenu'}->{$entry};
$entry =~ s/\d\d+://mx;
$highlights[$offset-1]->{'zmenu'}->{ sprintf q(%03d:%s), $zmenu_length++, $entry } = $value;
if ($highlights[$offset-1]->{'start'} > $_->{'start'}) {
$highlights[$offset-1]->{'start'} = $_->{'start'};
}
if ($highlights[$offset-1]->{'end'} < $_->{'end'}) {
$highlights[$offset-1]->{'end'} = $_->{'end'};
}
}
## Deal with colour aggregation
if ($_->{'col'} eq $aggregate_colour) {
$is_aggregated = 1;
}
if ($is_aggregated) {
$highlights[$offset-1]->{'col'} = $aggregate_colour;
}
}
else { # We don't
push @highlights, $_;
$bin_flag[$bin_id] = @highlights;
$is_aggregated = 0;
}
}
}
#########
# Now we render the points
#
my $high_flag = 'l';
my @starts = map { $_->{'start'} } @highlights;
my @sorting_keys = sort { $starts[$a] <=> $starts[$b] } 0..$#starts;
my @flags = ();
my $flag = 'l';
foreach (@sorting_keys) {
$flags[$_] = $flag = $flag eq 'l' ? 'r' : 'l';
}
foreach (@highlights) {
my $start = $v_offset + $_->{'start'};
my $end = $v_offset + $_->{'end'};
if ($highlight_style eq 'arrow') {
$high_flag = shift @flags;
$type = "highlight_${high_flag}h$highlight_style";
}
my $zmenu = $_->{'zmenu'};
my $col = $_->{'col'};
#########
# dynamic require of the right type of renderer
#
if($self->can($type)) {
my $g = $self->$type( {
'chr' => $chr,
'start' => $start,
'end' => $end,
'mid' => ($start+$end)/2,
'h_offset' => $h_offset,
'wid' => $wid,
'padding' => $padding,
'padding2' => $padding * $bpperpx * sqrt(3)/2,
'href' => $_->{'href'},
'col' => $col,
'id' => $_->{'id'},
'strand' => $_->{'strand'},
} );
$g and $self->push($g);
}
}
}
}
}
$self->minx( $v_offset );
return;
}
1;