package Bio::EnsEMBL::VDrawableContainer;
use strict;
use Bio::EnsEMBL::GlyphSet::Videogram;
sub new {
my ($class, $Container, $Config, $highlights, $strandedness, $spacing) = @_;
if(!defined $Container) {
warn qq(Bio::EnsEMBL::DrawableContainer::new No container defined);
return;
}
if(!defined $Config) {
warn qq(Bio::EnsEMBL::DrawableContainer::new No Config object defined);
return;
}
my $self = {
'vc' => $Container,
'glyphsets' => [],
'config' => $Config,
'timer' => $Config->{'species_defs'}->timer,
'prefix' => 'Bio::EnsEMBL',
'spacing' => $spacing || $Config->get_parameter('spacing') || 0,
};
#warn "self->{'spacing'} $self->{'spacing'}";
bless($self, $class);
########## loop over all the glyphsets the user wants:
my $tmp = {};
$Container->{'web_species'} ||= $ENV{'ENSEMBL_SPECIES'};
my @chromosomes = ($Container->{'chr'});
my $flag = 0;
if( $Config->get_parameter('all_chromosomes') eq 'yes' ) {
@chromosomes = @{$Config->{species_defs}->other_species($Container->{'web_species'}, 'ENSEMBL_CHROMOSOMES')||[] };
$flag = 1;
}
my $pos = 100000;
my $row = '';
my $scalex = $Config->get_parameter('image_height') / $Config->get_parameter('container_width');
$Config->{'transform'}->{'scalex'} = $scalex;
$Config->texthelper->{'_scalex'} = $scalex;
$Config->{'transform'}->{'absolutescalex'} = 1; # $Config->{'_image_height'} / $Config->image_width();
$Config->{'transform'}->{'translatex'} += $Config->get_parameter('top_margin');
my @glyphsets;
my @configs = $Config->glyphset_configs;
my %chr_glyphset_counts = ();
foreach my $chr ( @chromosomes ) {
$Container->{'chr'} = $chr;
for my $row_config (@configs) {
########## create a new glyphset for this row
my $glyphset = $row_config->get('glyphset')||$row_config->code;
my $classname = qq($self->{'prefix'}::GlyphSet::$glyphset);
next unless $self->dynamic_use( $classname );
my $EW_Glyphset;
eval { # Generic glyphsets need to have the type passed as a fifth parameter...
$EW_Glyphset = new $classname({
'container' => $Container,
'chr' => $chr,
'config' => $Config,
'my_config' => $row_config,
'strand' => 0,
'extra' => {},
'highlights' => $highlights,
'row' => $row,
});
$EW_Glyphset->{'chr'} = $chr;
};
if($@ || !$EW_Glyphset) {
my $reason = $@ || "No reason given just returns undef";
warn "GLYPHSET: glyphset $classname failed (@{[$self->{container}{web_species}]}/$ENV{'ENSEMBL_SCRIPT'} at ".gmtime()."\nGLYPHSET: $reason";
next;
}
#$EW_Glyphset->render_normal();
$EW_Glyphset->render();
push @glyphsets, $EW_Glyphset;
$chr_glyphset_counts{$chr}++;
}
}
########## sort out the resulting mess
$spacing = $self->{'spacing'};
########## go ahead and do all the database work
my $yoffset = 0;
## Firstly lets work how many entries to draw per row!
## Then work out the minimum start for each of these rows
## We then shift up all these points up by that many base
## pairs to close up any gaps
my( $max_gs_chr ) = sort { $b<=>$a } values %chr_glyphset_counts;
my $glyphsets = @glyphsets;
my $GS = $Config->get_parameter( 'group_size' ) || $max_gs_chr;
my $entries_per_row = $Config->get_parameter( 'columns' ) || ( int( ($glyphsets/$GS - 1) / ($Config->get_parameter('rows') || 1) + 1 ) * $GS );
$entries_per_row = $max_gs_chr if $max_gs_chr > $entries_per_row;
## warn ".... $max_gs_chr .... $GS, $entries_per_row ...";
my $entry_no = 0;
$Config->set_parameter('max_height', 0);
$Config->set_parameter('max_width', 0);
my @min = ();
my @max = ();
my $row_count = 0;
my $row_index = 0;
my $current_chr = undef;
for my $glyphset (@glyphsets) {
if( $current_chr ne $glyphset->{'chr'} ) { ## Can we fit all the chr stuff in!
$row_count += $chr_glyphset_counts{$glyphset->{'chr'}};
if( $row_count > $entries_per_row ) {
$row_index++;
$row_count = 0;
}
$current_chr = $glyphset->{'chr'};
}
$glyphset->{'row_index'} = $row_index;
$min[$row_index] = $glyphset->minx() if(!defined $min[$row_index] || $min[$row_index] > $glyphset->minx() );
}
## Close up gap!
# my $translateX = shift @row_min;
my $translateX = shift @min;
$Config->{'transform'}->{'translatex'} -= $translateX * $scalex; #$xoffset;
my $xoffset = -$translateX * $scalex;
my $row_index = 0;
for my $glyphset (@glyphsets) {
if( $row_index != $glyphset->{'row_index'} ) { ## We are on a new row - so reset the yoffset [horizontal] to 0
$row_index = $glyphset->{'row_index'};
$yoffset = 0;
my $translateX = shift @min;
$xoffset += $Config->image_width() - $translateX * $scalex;
## Shift down - and then close up gap!
$Config->{'transform'}->{'translatex'} += $Config->image_width() - $translateX * $scalex; #$xoffset;
}
$Config->set_parameter( 'max_width', $xoffset + $Config->get_parameter('image_width') );
########## set up the label for this strip
########## first we get the max width of label in characters
my $feature_type_1 = $glyphset->my_config('feature_type') || ( $glyphset->my_config('keys') ? $glyphset->my_config('keys')->[0] : undef );
my $feature_type_2 = $glyphset->my_config('feature_type_2') || ( $glyphset->my_config('keys') ? $glyphset->my_config('keys')->[1] : undef );
my $label_1 = $glyphset->my_config('label') || ( $feature_type_1 ? $glyphset->my_colour( $feature_type_1, 'text' ) : undef );
my $label_2 = $glyphset->my_config('label_2') || ( $feature_type_2 ? $glyphset->my_colour( $feature_type_2, 'text' ) : undef );
if( $glyphset->{'my_config'}->key eq 'Videogram' && $flag ) {
$label_1 = $glyphset->{'chr'};
}
my $gw = length( length($label_2) > length($label_1) ? $label_2 : $label_1 );
if($gw>0) {
########## and convert it to pels
$gw = $Config->texthelper->width('Small');
########## If the '_label' position is not 'above' move the labels below the image
my $label_x = $Config->get_parameter('label') eq 'above' ? 0 : $Config->get_parameter('image_height');
$label_x += 4 - $Config->get_parameter('top_margin');
my $label_y = ($glyphset->maxy() + $glyphset->miny() - $gw ) / 2;
my $colour_1 = $glyphset->my_config('colour') ||
( $feature_type_1 ? $glyphset->my_colour( $feature_type_1, 'label' ) : undef );
my $colour_2 = $glyphset->my_config('colour_2') ||
( $feature_type_2 ? $glyphset->my_colour( $feature_type_2, 'label' ) : undef );
$glyphset->push($glyphset->Text({
'x' => $label_x / $scalex,
'y' => ($glyphset->maxy() + $glyphset->miny() - length($label_1)*$gw ) / 2,
'height' => $gw * length($label_1),
'font' => 'Small',
'text' => $label_1,
'absolutey' => 1,
'colour' => $colour_1
})) if $label_1;
$glyphset->push($glyphset->Text({
'x' => ( $label_x + 2 + $Config->texthelper->height('Tiny') )/ $scalex,
'y' => ($glyphset->maxy() + $glyphset->miny() - length($label_2)*$gw ) / 2,
'height' => $gw * length($label_2),
'font' => 'Small',
'text' => $label_2,
'absolutey' => 1,
'colour' => $colour_2
})) if $label_2;
}
########## remove any whitespace at the top of this row
$Config->{'transform'}->{'translatey'} = -$glyphset->miny() + $spacing/2 + $yoffset;
$glyphset->transform();
########## translate the top of the next row to the bottom of this one
$yoffset += $glyphset->height() + $spacing;
$Config->set_parameter('max_height', $yoffset + $spacing ) if( $yoffset + $spacing > $Config->get_parameter('max_height') );
}
$self->{'glyphsets'} = \@glyphsets;
########## Store the maximum "width of the image"
return $self;
}
########## render does clever drawing things
sub render {
my ($self, $type) = @_;
########## build the name/type of render object we want
my $renderer_type = qq(Bio::EnsEMBL::VRenderer::$type);
########## dynamic require of the right type of renderer
return unless $self->dynamic_use( $renderer_type );
########## big, shiny, rendering 'GO' button
my $renderer = $renderer_type->new(
$self->{'config'},
$self->{'vc'},
$self->{'glyphsets'}
);
return $renderer->canvas();
}
sub config {
my ($self, $Config) = @_;
$self->{'config'} = $Config if(defined $Config);
return $self->{'config'};
}
sub glyphsets {
my ($self) = @_;
return @{$self->{'glyphsets'}};
}
sub dynamic_use {
my( $self, $classname ) = @_;
my( $parent_namespace, $module ) = $classname =~/^(.*::)(.*?)$/;
no strict 'refs';
return 1 if $parent_namespace->{$module.'::'}; # return if already used
eval "require $classname";
if($@) {
warn "VVDrawableContainer: failed to use $classname\nVVDrawableContainer: $@";
return 0;
}
$classname->import();
return 1;
}
1;
=head1 RELATED MODULES
See also: Bio::EnsEMBL::GlyphSet Bio::EnsEMBL::Glyph WebUserConfig
=head1 AUTHOR - Roger Pettett
Email - rmp@sanger.ac.uk
=cut