package EnsEMBL::Web::ImageConfig;
use strict;
use Data::Dumper;
use Storable qw( nfreeze freeze thaw);
use Sanger::Graphics::TextHelper;
use Bio::EnsEMBL::Registry;
use EnsEMBL::Web::OrderedTree;
use EnsEMBL::Web::RegObj;
use EnsEMBL::Web::Tools::Misc;
use Digest::MD5 qw(md5_hex);
my $reg = "Bio::EnsEMBL::Registry";
our @TRANSCRIPT_TYPES = qw(transcript alignslice_transcript tsv_transcript gsv_transcript TSE_transcript gene);
our $alignment_renderers = [
'off' => 'Off',
'normal' => 'Normal',
'labels' => 'Labels',
'half_height' => 'Half height',
'stack' => 'Stacked',
'unlimited' => 'Stacked unlimited',
'ungrouped' => 'Ungrouped',
'ungrouped_labels' => 'Ungrouped with labels',
];
our $MEMD = EnsEMBL::Web::Cache->new;
#########
# 'general' settings contain defaults.
# 'user' settings are restored from cookie if available
# 'general' settings are overridden by 'user' settings
#
sub new {
my $class = shift;
my $adaptor = shift;
my $can_attach_user = shift;
my $species = @_ ? shift : ($ENV{'ENSEMBL_SPECIES'} || '');
my $type = $class =~/([^:]+)$/ ? $1 : $class;
my $style = $adaptor->get_species_defs->ENSEMBL_STYLE || {};
my $self = {
'_colourmap' => $adaptor->colourmap,
'_font_face' => $style->{GRAPHIC_FONT} || 'Arial',
'_font_size' => ( $style->{GRAPHIC_FONTSIZE} * $style->{GRAPHIC_LABEL} ) || 20,
'_texthelper' => new Sanger::Graphics::TextHelper,
'_db' => $adaptor->get_adaptor,
'type' => $type,
'species' => $species,
'species_defs' => $adaptor->get_species_defs,
'exturl' => $adaptor->exturl,
'general' => {},
'user' => {},
'_useradded' => {}, # contains list of added features....
'_r' => undef, # $adaptor->{'r'} || undef,
'no_load' => undef,
'storable' => 1,
'altered' => 0,
## Core objects... { for setting URLs .... }
'_core' => undef,
## Glyphset tree... { Tree of glyphsets to render.... }
'_tree' => EnsEMBL::Web::OrderedTree->new(),
## Generic parameters... { Generic parameters for glyphsets.... }
'_parameters' => {},
## Better way to store cache {
'_cache' => {}
};
bless($self, $class);
## Check to see if we have a user/session saved copy of tree....
## Load tree from cache...
## If not check to see if we have a "common" saved copy of tree
## If not generate and cache it!
## If we have a (user/session) modify the common tree
## Cache the user/session version.
########## init sets up defaults in $self->{'general'}
## Check memcached for defaults
if (my $defaults = $MEMD ? $MEMD->get("::${class}::$species") : undef) {
$self->{$_} = $defaults->{$_} for keys %$defaults;
} else {
## No cached defaults found,
## so initialize them
$self->init if $self->can('init');
## And cahce
if ($MEMD) {
my $defaults = {
_tree => $self->{'_tree'},
_parameters => $self->{'_parameters'},
general => $self->{'general'},
};
$MEMD->set(
"::${class}::$species",
$defaults,
undef,
'IMAGE_CONFIG',
$species,
);
}
}
$self->{'no_image_frame'}=1;
## At this point tree doesn't depend on session/user....
# if( $can_attach_user ) {
if ($class =~ /ImageConfig::V/) {
$self->load_user_vert_tracks( $adaptor );
}
else {
$self->load_user_tracks( $adaptor );
}
## Add user defined data sources.....
## Now tree does depend on session/user...
#
########## load sets up user prefs in $self->{'user'}
# $self->load() unless(defined $self->{'no_load'});
return $self;
}
sub load_user_vert_tracks {
## We load less data on vertical drawing code, as it shows regions
## at a much smaller scale. We also need to distinguish between
## density features, rendered as separate tracks, and pointers,
## which are part of the karyotype track
## NB. SEE E::W::Document::Image for old density track code (sub add_tracks)
my( $self, $session ) = @_;
my $menu = $self->get_node('user_data');
return unless $menu;
## First, get all the data
my (@user_tracks, $track_keys);
## Add in temporary data
my %types = (upload => 'filename', url => 'url');
foreach my $type (keys %types) {
my @tracks = $session->get_data('type' => $type);
foreach my $track (@tracks) {
my $track_info = {
'id' => 'temp-'.$type.'-'.$track->{'code'},
'species' => $track->{'species'},
'code' => $type,
'status' => 'tmp',
};
$track_info->{'render'} = EnsEMBL::Web::Tools::Misc::style_by_filesize($track->{'filesize'});
if ($track->{'name'}) {
$track_info->{'name'} = $track->{'name'};
}
else {
my $other = $types{$type};
$track_info->{'name'} = $track->{$other};
}
push @user_tracks, $track_info;
}
}
## Add saved tracks, if any
if (my $user = $session->get_adaptor->get_user) {
foreach my $type (keys %types) {
my $method = $type.'s';
my @records = $user->$method;
foreach my $record (@records) {
my $track_info = {
'id' => 'user-'.$type.'-',
'species' => $record->species,
'code' => $type,
'status' => 'user',
};
$track_info->{'render'} = EnsEMBL::Web::Tools::Misc::style_by_filesize($record->filesize);
$track_info->{'id'} .= $record->id;
if ($record->name) {
$track_info->{'name'} = $record->name;
}
else {
if (ref($record) =~ /Upload/) {
$track_info->{'name'} = $record->filename;
}
elsif (ref($record) =~ /URL/) {
$track_info->{'name'} = $record->url;
}
else {
warn "!!! UNKNOWN RECORD TYPE ".ref($record);
}
}
push @user_tracks, $track_info;
}
}
}
my @density_renderers = (
'off' => 'Off',
'density_line' => 'Density plot - line graph',
'density_bar' => 'Density plot - filled bar chart',
'density_outline' => 'Density plot - outline bar chart',
);
my @highlight_renderers = @density_renderers;
push @highlight_renderers, (
'highlight_lharrow' => 'Arrow on lefthand side',
'highlight_rharrow' => 'Arrow on righthand side',
'highlight_bowtie' => 'Arrows on both sides',
'highlight_wideline' => 'Line',
'highlight_widebox' => 'Box',
);
## Now, add these tracks to the menu as checkboxes
foreach my $entry (@user_tracks) {
push @$track_keys, $entry->{'id'};
if ($entry->{'species'} eq $self->{'species'}) {
my $settings = {
'_class' => $entry->{'status'},
'glyphset' => 'Vgeneric',
'colourset' => 'classes',
'sub_type' => 'tmp',
'file' => $entry->{'filename'},
'format' => $entry->{'format'},
'caption' => $entry->{'name'},
'description' => '',
'display' => 'off',
'strand' => 'b'
};
if ($entry->{'render'} eq 'density') {
$settings->{'renderers'} = \@density_renderers;
}
else {
$settings->{'renderers'} = \@highlight_renderers;
}
$menu->append( $self->create_track($entry->{'id'}, $entry->{'name'}, $settings ));
}
}
}
sub load_user_tracks {
my( $self, $session ) = @_;
my $menu = $self->get_node('user_data');
return unless $menu;
my $DAS = $EnsEMBL::Web::RegObj::ENSEMBL_WEB_REGISTRY->get_all_das();
foreach my $source ( sort { ($a->caption||$a->label) cmp ($b->caption||$b->label) } values %$DAS ) {
next if $self->get_node('das_'.$source->logic_name);
$source->is_on($self->{'type'}) || next;
$self->add_das_track( 'user_data', $source );
}
my $user = $EnsEMBL::Web::RegObj::ENSEMBL_WEB_REGISTRY->get_user(); # #EnsEMBL::Web::Data::User->new($ENV{'ENSEMBL_USER_ID'});
### Get the tracks that are temporarily stored - as "files" not in the DB....
## Firstly "upload data" not yet committed to the database...
## Then those attached as URLs to either the session or the User
## Now we deal with the url sources... again flat file!
my %url_sources = ();
my %user_sources = ();
my @t = $session->get_data( 'type' => 'url' );
foreach my $entry (@t) {
next unless $entry->{'species'} eq $self->{'species'};
$url_sources{$entry->{'url'}} = {
'source_name' => $entry->{'name'}||$entry->{'url'},
'source_type' => 'session'
};
}
@t = $session->get_data( 'type' => 'upload' );
foreach my $entry (@t) {
next unless $entry->{'species'} eq $self->{'species'};
if( $entry->{'analyses'} ) {
my @analyses = split /, /, $entry->{'analyses'};
foreach my $analysis (@analyses) {
$user_sources{$analysis} = {
'source_name' => $entry->{'name'},
'source_type' => 'session',
'assembly' => $entry->{'assembly'}
};
}
} else {
$menu->append( $self->create_track( 'tmp_'.$entry->{'code'}, $entry->{'name'}, {
'_class' => 'tmp',
'glyphset' => '_flat_file',
'colourset' => 'classes',
'sub_type' => 'tmp',
'file' => $entry->{'filename'},
'format' => $entry->{'format'},
'caption' => $entry->{'name'},
'renderers' => $alignment_renderers,
'description' => '
Data that has been temporarily uploaded to the web server.',
'display' => 'normal',
'strand' => 'b'
})) if $entry->{'species'} eq $self->{'species'};
}
}
if( $user ) {
my @t = $user->urls;
foreach my $entry (@t) {
next unless $entry->species eq $self->{'species'};
$url_sources{$entry->url} = {
'source_name' => $entry->name||$entry->url,
'source_type' => 'user'
};
}
my @t = $user->uploads;
foreach my $entry (@t) {
next unless $entry->species eq $self->{'species'};
my @analyses = split /, /, $entry->analyses;
foreach my $analysis (@analyses) {
$user_sources{$analysis} = {
'source_name' => $entry->name,
'source_type' => 'user',
'assembly' => $entry->assembly
};
}
}
}
foreach (sort { $url_sources{$a}{'source_name'} cmp $url_sources{$b}{'source_name'} } keys %url_sources ) {
my $k = 'url_'.md5_hex( $self->{'species'}.':'.$_ );
$self->_add_flat_file_track( $menu, 'url', $k, $url_sources{$_}{'source_name'},
sprintf ( '
Data retrieved from an external webserver.
This data is attached to the %s, and comes from URL: %s', CGI::escapeHTML( $url_sources{$_}{'source_type'}), CGI::escapeHTML( $_ ) ),
'url' => $_
);
}
if( keys %user_sources ) { ## We now need to get a userdata adaptor to get the analysis info!
my $dbs = EnsEMBL::Web::DBSQL::DBConnection->new( $self->{'species'} );
my $dba = $dbs->get_DBAdaptor('userdata');
my $an_adaptor = $dba->get_adaptor( 'Analysis' );
my @tracks;
foreach my $logic_name ( keys %user_sources ) {
my $analysis = $an_adaptor->fetch_by_logic_name($logic_name);
next unless $analysis;
push @tracks, ['user_'.$logic_name, $analysis->display_label, {
'_class' => 'user',
'glyphset' => '_user_data',
'colourset' => 'classes',
'sub_type' => 'user',
'renderers' => $alignment_renderers,
'source_name' => $user_sources{$logic_name}{'source_name'},
'logic_name' => $logic_name,
'caption' => $analysis->display_label,
'data_type' => $analysis->module,
'description' => sprintf ( '
Data uploaded by a user, in data set %s.
%s', CGI::escapeHTML($user_sources{$logic_name}{'source_name'}), CGI::escapeHTML($analysis->description) ),
'display' => 'normal',
'style' => $analysis->web_data,
'strand' => 'b'
}];
}
foreach( sort {
lc($a->[2]{'source_name'}) cmp lc($b->[2]{'source_name'}) ||
lc($a->[1] ) cmp lc($b->[1] )
} @tracks ) {
$menu->append($self->create_track(@$_));
}
}
return;
}
sub _add_flat_file_track {
my( $self, $menu, $sub_type, $key, $name, $description, %X ) = @_;
$menu ||= $self->get_node('user_data');
return unless $menu;
my $X = $self->create_track( $key, $name, {
'display' => 'normal',
'strand' => 'b',
'_class' => 'url',
'glyphset' => '_flat_file',
'colourset' => 'classes',
'caption' => $name,
'sub_type' => $sub_type,
'renderers' => $alignment_renderers,
'description' => $description,
%X
});
if( $X ) {
$menu->append($X);
};
}
sub update_from_input {
my( $self, $input ) = @_;
if( $input->param('reset') ) {
return $self->tree->flush_user;
}
my $flag = 0;
foreach my $node ($self->tree->nodes) {
my $key = $node->key;
if( defined $input->param($key) ) {
$flag += $node->set_user( 'display', $input->param( $key ) );
}
}
$self->altered = 1 if $flag;
return $flag;
}
#=============================================================================
# General setting/getting cache values...
#=============================================================================
sub cache {
my $self = shift;
my $key = shift;
$self->{'_cache'}{$key} = shift if @_;
return $self->{'_cache'}{$key}
}
#=============================================================================
# General setting/getting parameters...
#=============================================================================
sub set_parameters {
my( $self, $params ) = @_;
foreach (keys %$params) {
$self->{'_parameters'}{$_} = $params->{$_};
}
}
sub get_parameters {
my $self = shift;
return $self->{'_parameters'};
}
sub get_parameter {
my($self,$key) = @_;
return $self->{'_parameters'}{$key};
}
sub set_parameter {
my($self,$key,$value) = @_;
$self->{'_parameters'}{$key} = $value;
}
#-----------------------------------------------------------------------------
# Specific parameter setting - image width/container width
#-----------------------------------------------------------------------------
sub title {
my $self = shift;
$self->set_parameter( 'title', shift ) if @_;
return $self->get_parameter( 'title' );
}
sub container_width {
my $self = shift;
$self->set_parameter( 'container_width', shift ) if @_;
return $self->get_parameter( 'container_width' );
}
sub image_width {
my $self = shift;
$self->set_parameter( 'image_width', shift ) if @_;
return $self->get_parameter( 'image_width' );
}
sub slice_number {
my $self = shift;
$self->set_parameter( 'slice_number', shift ) if @_;
return $self->get_parameter( 'slice_number' );
}
sub get_track_key {
my( $self, $prefix, $obj ) = @_;
my $logic_name = $obj->gene ? $obj->gene->analysis->logic_name : $obj->analysis->logic_name;
my $db = $obj->get_db();
my $db_key = 'DATABASE_'.uc($db);
my $key = $obj->species_defs->databases->{$db_key}{'tables'}{'gene'}{'analyses'}{$logic_name}{'web'}{'key'} || $logic_name;
return join '_', $prefix, $db, $key;
}
sub modify_configs {
my( $self, $nodes, $config ) = @_;
foreach my $conf_key ( @$nodes ) {
my $n = $self->get_node( $conf_key );
next unless $n;
foreach my $t ( $n->nodes ) {
next unless $t->get('node_type') eq 'track';
foreach ( keys %$config) {
$t->set($_,$config->{$_});
}
}
}
}
sub _update_missing {
my( $self,$object ) = @_;
my $count_missing = grep { $_->get('display') eq 'off' || !$_->get('display') } $self->glyphset_configs;
my $missing = $self->get_node( 'missing' );
if( $missing ) {
$missing->set( 'text' => $count_missing > 0 ? "There are currently $count_missing tracks turned off." : "All tracks are turned on" );
}
my $info = sprintf "%s %s version %s.%s (%s) %s: %s - %s",
$self->species_defs->ENSEMBL_SITETYPE,
$self->species_defs->SPECIES_BIO_NAME,
$self->species_defs->ENSEMBL_VERSION,
$self->species_defs->SPECIES_RELEASE_VERSION,
$self->species_defs->ASSEMBLY_NAME,
$object->seq_region_type_and_name,
$object->thousandify($object->seq_region_start),
$object->thousandify($object->seq_region_end) ;
my $information = $self->get_node( 'info' );
if( $information ) {
$information->set( 'text' => $info );
}
return { 'count' => $count_missing, 'information' => $info };
}
#=============================================================================
# General setting tree stuff...
#=============================================================================
sub tree {
return $_[0]{_tree};
}
### create_menus - takes an "associate array" i.e. ordered key value pairs
### to configure the menus to be seen on the display..
### key and value pairs are the code and the text of the menu...
sub create_menus {
my( $self, @list ) = @_;
while( my( $key, $caption ) = splice(@list,0,2) ) {
$self->create_submenu( $key, $caption );
}
}
### load_tracks - loads in various database derived tracks;
### loop through core like dbs, compara like dbs, funcgen like dbs;
### variation like dbs
sub load_tracks {
my $self = shift;
my $species = $ENV{'ENSEMBL_SPECIES'};
my $dbs_hash = $self->species_defs->databases;
my $multi_hash = $self->species_defs->multi_hash;
foreach my $db ( @{$self->species_defs->core_like_databases||[]} ) {
next unless exists $dbs_hash->{$db};
my $key = lc(substr($db,9));
## Look through tables in databases and add data from each one...
$self->add_dna_align_feature( $key,$dbs_hash->{$db}{'tables'} ); # To cDNA/mRNA, est, RNA, other_alignment trees ##DONE
# $self->add_ditag_feature( $key,$dbs_hash->{$db}{'tables'} ); # To ditag_feature tree ##DONE
$self->add_gene( $key,$dbs_hash->{$db}{'tables'} ); # To gene, transcript, align_slice_transcript, tsv_transcript trees
$self->add_marker_feature( $key,$dbs_hash->{$db}{'tables'} ); # To marker tree ##DONE
$self->add_qtl_feature( $key,$dbs_hash->{$db}{'tables'} ); # To marker tree ##DONE
$self->add_misc_feature( $key,$dbs_hash->{$db}{'tables'} ); # To misc_feature tree ##DONE
$self->add_oligo_probe( $key,$dbs_hash->{$db}{'tables'} ); # To oligo tree ##DONE
$self->add_prediction_transcript( $key,$dbs_hash->{$db}{'tables'} ); # To prediction_transcript tree ##DONE
$self->add_protein_align_feature( $key,$dbs_hash->{$db}{'tables'} ); # To protein_align_feature_tree ##DONE
$self->add_protein_feature( $key,$dbs_hash->{$db}{'tables'} ); # To protein_feature_tree ## 2 do ##
$self->add_repeat_feature( $key,$dbs_hash->{$db}{'tables'} ); # To repeat_feature tree ##DONE
$self->add_simple_feature( $key,$dbs_hash->{$db}{'tables'} ); # To simple_feature tree ##DONE
$self->add_assemblies( $key,$dbs_hash->{$db}{'tables'} ); # To sequence tree! ## 2 do ##
$self->add_decorations( $key,$dbs_hash->{$db}{'tables'} );
}
foreach my $db ( @{$self->species_defs->compara_like_databases||[]} ) {
next unless exists $multi_hash->{$db};
my $key = lc(substr($db,9));
## Configure dna_dna_align features and synteny tracks
$self->add_synteny( $key,$multi_hash->{$db}, $species ); # Add to synteny tree ##DONE
$self->add_alignments( $key,$multi_hash->{$db}, $species ); # Add to compara_align tree ##DONE
}
foreach my $db ( @{$self->species_defs->funcgen_like_databases||[]} ) {
next unless exists $dbs_hash->{$db};
my $key = lc(substr($db,9));
## Configure
$self->add_regulation_feature( $key,$dbs_hash->{$db}{'tables'}, $species ); # Add to regulation_feature tree
}
foreach my $db ( @{$self->species_defs->variation_like_databases||[]} ) {
next unless exists $dbs_hash->{$db};
my $key = lc(substr($db,9));
## Configure variation features
$self->add_variation_feature( $key,$dbs_hash->{$db}{'tables'} ); # To variation_feature tree
}
}
sub load_configured_das {
my $self=shift;
my @extra = @_;
## Now we do the das stuff - to append to menus (if the menu exists!!)
my $internal_das_sources = $self->species_defs->get_all_das;
foreach my $source ( sort { $a->caption cmp $b->caption } values %$internal_das_sources ) {
$source->is_on($self->{'type'}) || next;
$self->add_das_track( $source->category, $source, @extra );
}
}
sub add_das_track {
my( $self, $menu, $source, @extra ) = @_;
my $node = $self->get_node($menu);
if( !$node && grep { $menu eq $_ } @TRANSCRIPT_TYPES) {
for (@TRANSCRIPT_TYPES) {
$node = $self->get_node($_);
last if $node;
}
}
$node ||= $self->get_node('external_data');
return unless $node;
my $caption = $source->caption || $source->label;
my $desc = $source->description;
my $homepage = $source->homepage;
if ($homepage) {
$desc .= sprintf ' [<a href="%s">Homepage</a>]', $homepage;
}
my $t = $self->create_track( "das_".$source->logic_name,$source->label, {
@extra,
'_class' => 'DAS',
'glyphset' => '_das',
'display' => 'off',
'renderers' => ['off' => 'Off', 'nolabels' => 'No labels', 'labels' => 'Normal'],
'logicnames' => [ $source->logic_name ],
'caption' => $caption,
'description' => $desc,
});
$node->append($t) if $t;
}
#-----------------------------------------------------------------------------
# Functions to add tracks from core like databases....
#-----------------------------------------------------------------------------
sub _check_menus {
my $self = shift;
foreach( @_ ) {
return 1 if $self->tree->get_node( $_ );
}
return 0;
}
sub _merge {
my( $self, $_sub_tree, $sub_type ) = @_;
my $data = {};
my $tree = $_sub_tree->{'analyses'};
my $config_name = $self->{'type'};
foreach my $analysis (keys %$tree) {
my $sub_tree = $tree->{$analysis};
next unless $sub_tree->{'disp'}; ## Don't include non-displayable tracks
#local $Data::Dumper::Indent=0;
#warn Data::Dumper::Dumper($sub_tree->{'web'});
#warn ".... $sub_type {",$sub_tree->{'web'}{ $sub_type },"}";
next if exists $sub_tree->{'web'}{ $sub_type }{'do_not_display'};
my $key = $sub_tree->{'web'}{'key'} || $analysis;
foreach ( keys %{$sub_tree->{'web'}||{}} ) {
#warn "............ $_ ...............";
next if $_ eq 'desc';
if( $_ eq 'default' ) {
#warn ".... $_ $config_name : ",keys %{$sub_tree->{'web'}{$_}||{}};
if ( ref($sub_tree->{'web'}{$_}) eq 'HASH') {
$data->{$key}{'display'} ||= $sub_tree->{'web'}{$_}{$config_name};
}
else {
$data->{$key}{'display'} ||= $sub_tree->{'web'}{$_};
}
} else {
$data->{$key}{$_} ||= $sub_tree->{'web'}{$_}; # Longer form for help and configuration!
}
}
if( $sub_tree->{'web'}{'key'} ) {
if( $sub_tree->{'desc'} ) {
$data->{$key}{'html_desc'} ||= "<dl>\n";
$data->{$key}{'description'} ||= '';
$data->{$key}{'html_desc'} .= sprintf(
" <dt>%s</dt>\n <dd>%s</dd>\n",
CGI::escapeHTML( $sub_tree->{'web'}{'name'} ), # Description for pop-help - merged of all descriptions!!
CGI::escapeHTML( $sub_tree->{'desc'})
);
$data->{$key}{'description'}.= ($data->{$key}{'description'}?'; ':'').$sub_tree->{'desc'};
}
} else {
$data->{$key}{'description'} = $sub_tree->{'desc'};
$data->{$key}{'html_desc'} .= sprintf(
'<p>%s</p>',
CGI::escapeHTML( $sub_tree->{'desc'})
);
}
push @{$data->{$key}{'logic_names'}}, $analysis;
}
foreach my $key (keys %$data) {
$data->{$key}{'name'} ||= $tree->{$key}{'name'};
$data->{$key}{'caption'} ||= $data->{$key}{'name'} || $tree->{$key}{'name'};
$data->{$key}{'description'} .= '</dl>' if $data->{$key}{'description'} =~ '<dl>';
}
return ( [sort { $data->{$a}{'name'} cmp $data->{$b}{'name'} } keys %$data], $data );
}
sub add_assemblies {
my( $self, $key, $hashref ) = @_;
return unless $self->_check_menus( 'sequence' );
}
### add_dna_align_feature...
### loop through all core databases - and attach the dna align
### features from the dna_align_feature tables...
### these are added to one of four menus: cdna/mrna, est, rna, other
### depending whats in the web_data column in the database
sub add_dna_align_feature {
my( $self, $key, $hashref ) = @_;
return unless $self->_check_menus( 'dna_align_cdna' );
my( $keys, $data ) = $self->_merge( $hashref->{'dna_align_feature'} , 'dna_align' );
foreach my $key_2 ( @$keys ) {
my $K = $data->{$key_2}{'type'}||'other';
my $menu = $self->tree->get_node( "dna_align_$K" );
if( $menu ) {
$menu->append( $self->create_track( 'dna_align_'.$key.'_'.$key_2, $data->{$key_2}{'name'}, {
'db' => $key,
'glyphset' => '_alignment',
'sub_type' => lc($K),
'colourset' => 'feature',
'logicnames' => $data->{$key_2}{'logic_names'},
'caption' => $data->{$key_2}{'caption'},
'description' => $data->{$key_2}{'description'},
'display' => $data->{$key_2}{'display'}||'off', ## Default to on at the moment - change to off by default!
'renderers' => $alignment_renderers,
'strand' => 'b'
}));
}
}
}
### add_protein_align_feature...
### loop through all core databases - and attach the protein align
### features from the protein_align_feature tables...
sub add_protein_align_feature {
my( $self, $key, $hashref ) = @_;
return unless $self->_check_menus( 'protein_align' );
my( $keys, $data ) = $self->_merge( $hashref->{'protein_align_feature'}, 'protein_align_feature' );
my $menu = $self->tree->get_node( "protein_align" );
foreach my $key_2 ( @$keys ) {
$menu->append( $self->create_track( 'protein_'.$key.'_'.$key_2, $data->{$key_2}{'name'},{
'db' => $key,
'glyphset' => '_alignment',
'sub_type' => 'protein',
'object_type' => 'ProteinAlignFeature',
'colourset' => 'feature',
'logicnames' => $data->{$key_2}{'logic_names'},
'caption' => $data->{$key_2}{'caption'},
'description' => $data->{$key_2}{'description'},
'display' => $data->{$key_2}{'display'}||'off', ## Default to on at the moment - change to off by default!
'renderers' => $alignment_renderers,
'strand' => 'b'
}));
}
}
sub add_simple_feature {
my( $self, $key, $hashref ) = @_;
return unless $self->get_node( 'simple' );
my( $keys, $data ) = $self->_merge( $hashref->{'simple_feature'} );
my $menu = $self->tree->get_node( "simple" );
foreach my $key_2 ( @$keys ) {
$menu->append( $self->create_track( 'simple_'.$key.'_'.$key_2, $data->{$key_2}{'name'}, {
'db' => $key,
'glyphset' => '_simple',
'logicnames' => $data->{$key_2}{'logic_names'},
'colourset' => 'simple',
'caption' => $data->{$key_2}{'caption'},
'description' => $data->{$key_2}{'description'},
'display' => $data->{$key_2}{'display'}||'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw(off Off normal Normal)],
'strand' => 'r'
}));
}
}
sub add_prediction_transcript {
my( $self, $key, $hashref ) = @_;
return unless $self->get_node( 'prediction' );
my( $keys, $data ) = $self->_merge( $hashref->{'prediction_transcript'} );
my $menu = $self->tree->get_node( "prediction" );
foreach my $key_2 ( @$keys ) {
$menu->append( $self->create_track( 'transcript_'.$key.'_'.$key_2, $data->{$key_2}{'name'}, {
'db' => $key,
'glyphset' => '_prediction_transcript',
'logicnames' => $data->{$key_2}{'logic_names'},
'caption' => $data->{$key_2}{'caption'},
'colourset' => 'prediction',
'colour_key' => lc($key_2),
'description' => $data->{$key_2}{'description'},
'display' => $data->{$key_2}{'display'}||'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw(off Off), 'transcript_nolabel' => 'No labels', 'transcript_label' => 'With labels'],
'strand' => 'b'
}));
}
}
sub add_ditag_feature {
my( $self, $key, $hashref ) = @_;
return unless $self->_check_menus( 'ditag' );
my( $keys, $data ) = $self->_merge( $hashref->{'ditag_feature'} );
my $menu = $self->tree->get_node( 'ditag' );
foreach my $key_2 ( @$keys ) {
if( $menu ) {
$menu->append( $self->create_track( 'ditag_'.$key.'_'.$key_2, $data->{$key_2}{'name'}, {
'db' => $key,
'glyphset' => '_ditag',
'logicnames' => $data->{$key_2}{'logic_names'},
'caption' => $data->{$key_2}{'caption'},
'description' => $data->{$key_2}{'description'},
'display' => $data->{$key_2}{'display'}||'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw(off Off normal Normal)],
'strand' => 'b'
}));
}
}
}
### add_gene...
### loop through all core databases - and attach the gene
### features from the gene tables...
### there are a number of menus sub-types these are added to:
### * gene # genes
### * transcript # ordinary transcripts
### * alignslice_transcript # transcripts in align slice co-ordinates
### * tse_transcript # transcripts in collapsed intro co-ords
### * tsv_transcript # transcripts in collapsed intro co-ords
### * gsv_transcript # transcripts in collapsed gene co-ords
### depending on which menus are configured
sub add_gene {
my( $self, $key, $hashref ) = @_;
## Gene features end up in each of these menus..
return unless $self->_check_menus( @TRANSCRIPT_TYPES );
my( $keys, $data ) = $self->_merge( $hashref->{'gene'}, 'gene' );
my $flag = 0;
foreach my $type ( @TRANSCRIPT_TYPES ) {
my $menu = $self->get_node( $type );
next unless $menu;
foreach my $key_2 ( @$keys ) {
$menu->append( $self->create_track( $type.'_'.$key.'_'.$key_2, $data->{$key_2}{'name'}, {
'db' => $key,
'glyphset' => ($type=~/_/?'':'_').$type, ## QUICK HACK..
'logicnames' => $data->{$key_2}{'logic_names'},
'colours' => $self->species_defs->colour( 'gene' ),
'caption' => $data->{$key_2}{'caption'},
'colour_key' => $data->{$key_2}{'colour_key'},
'label_key' => $data->{$key_2}{'label_key'},
'description' => $data->{$key_2}{'description'},
'display' => $data->{$key_2}{'display'}||'off', ## Default to on at the moment - change to off by default!
'renderers' => $type eq 'transcript' ?
[qw(off Off),
'gene_nolabel' => 'No exon structure without labels',
'gene_label' => 'No exon structure with labels',
'transcript_nolabel' => 'Expanded without labels',
'transcript_label' => 'Expanded with labels',
'collapsed_nolabel' => 'Collapsed without labels',
'collapsed_label' => 'Collapsed with labels',
] :
[qw(off Off gene_nolabel), 'No labels', 'gene_label', 'With labels'],
'strand' => $type eq 'gene' ? 'r' : 'b'
}));
$flag=1;
}
}
## Need to add the gene menu track here....
if( $flag ) {
$self->add_track( 'information', 'gene_legend', 'Gene Legend', 'gene_legend', { 'strand' => 'r' } );
}
}
sub add_marker_feature {
my( $self, $key, $hashref ) = @_;
return unless $self->get_node( 'marker' );
my( $keys, $data ) = $self->_merge( $hashref->{'marker_feature'} );
my $menu = $self->get_node( 'marker' );
foreach my $key_2 (@$keys) {
$menu->append( $self->create_track( 'marker_'.$key.'_'.$key_2, $data->{$key_2}{'name'}, {
'db' => $key,
'glyphset' => '_marker',
'labels' => 'on',
'logicnames' => $data->{$key_2}{'logic_names'},
'caption' => $data->{$key_2}{'caption'},
'colours' => $self->species_defs->colour( 'marker' ),
'description' => $data->{$key_2}{'description'},
'priority' => $data->{$key_2}{'priority'},
'display' => $data->{$key_2}{'display'}||'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw(off Off normal Normal)],
'strand' => 'r'
}));
}
}
sub add_qtl_feature {
my( $self, $key, $hashref ) = @_;
return unless $self->get_node( 'marker' );
return unless $hashref->{'qtl'};
return unless $hashref->{'qtl'}{'rows'} > 0;
my $menu = $self->get_node( 'marker' );
$menu->append( $self->create_track( 'qtl_'.$key, 'QTLs', {
'db' => $key,
'glyphset' => '_qtl',
'caption' => 'QTLs',
'colourset' => 'qtl',
'description' => 'Quantative trait loci',
'display' => 'normal', ## Default to on at the moment - change to off by default!
'renderers' => [qw(off Off normal Normal)],
'strand' => 'r'
}));
}
sub add_misc_feature {
my( $self, $key, $hashref ) = @_;
#set some defaults and available tracks
my $default_tracks = {
'cytoview' => {'tilepath' => {'default' => 'normal'},
'encode' => {'threshold' => 'no'},
},
'contigviewbottom' => {'ntctgs' => {'available' => 'no'},
'encode' => {'threshold' => 'no'},}
};
return unless $self->get_node( 'misc_feature' );
my $config_name = $self->{'type'};
my $menu = $self->get_node('misc_feature');
## Different loop - no analyses - just misc_sets...
my $data = $hashref->{'misc_feature'}{'sets'};
foreach my $key_2 ( sort { $data->{$a}{'name'} cmp $data->{$b}{'name'} } keys %$data ) {
next if ($default_tracks->{$config_name}{$key_2}{'available'} eq 'no');
my $dets = {
'glyphset' => '_clone',
'db' => $key,
'set' => $key_2,
'colourset' => 'clone',
'caption' => $data->{$key_2}{'name'},
'description' => $data->{$key_2}{'desc'},
'max_length' => $data->{$key_2}{'max_length'},
'strand' => 'r',
'display' => $default_tracks->{$config_name}{$key_2}{'default'}||$data->{$key_2}{'display'}||'off',
'renderers' => [qw(off Off normal Normal)],
};
unless ($default_tracks->{$config_name}{$key_2}{'threshold'} eq 'no') {
$dets->{'outline_threshold'} = 350000;
}
$menu->append( $self->create_track( 'misc_feature_'.$key.'_'.$key_2, $data->{$key_2}{'name'}, $dets));
}
}
sub add_oligo_probe {
my( $self, $key, $hashref ) = @_;
return unless $self->get_node( 'oligo' );
my $menu = $self->get_node('oligo');
my $data = $hashref->{'oligo_feature'}{'arrays'};
my $description = $hashref->{'oligo_feature'}{'analyses'}{'AlignAffy'}{'desc'};
## Different loop - no analyses - base on probeset query results... = $hashref->{'oligo_feaature'}{'arrays'};
foreach my $key_2 ( sort keys %$data ) {
$menu->append( $self->create_track( 'oligo_'.$key.'_'.$key_2, $key_2, {
'glyphset' => '_oligo',
'db' => $key,
'sub_type' => 'oligo',
'array' => $key_2,
'object_type' => 'OligoProbe',
'colourset' => 'feature',
'description' => $description,
'caption' => $key_2,
'strand' => 'b',
'display' => 'off',
'renderers' => $alignment_renderers
}));
}
}
sub add_protein_feature {
my( $self, $key, $hashref ) = @_;
my %menus = (
'domain' => [ 'domain', 'P_domain', 'normal' ],
'feature' => [ 'feature', 'P_feature', 'normal' ],
'alignment' => [ 'alignment', 'P_domain', 'off' ],
'gsv_domain' => [ 'domain', 'gsv_domain', 'normal' ]
);
## We have two separate glyphsets in this in this case
## P_feature and P_domain - plus domains get copied onto gsv_domain as well...
return unless $self->_check_menus( keys %menus );
my( $keys, $data ) = $self->_merge( $hashref->{'protein_feature'} );
foreach my $menu_code ( keys %menus ) {
my $menu = $self->get_node( $menu_code );
next unless $menu;
my $type = $menus{$menu_code}[0];
my $gset = $menus{$menu_code}[1];
my $renderer = $menus{$menu_code}[2];
foreach my $key_2 ( @$keys ) {
next if $self->tree->get_node( $type.'_'.$key_2 );
next if $type ne ($data->{$key_2}{'type'}||'feature'); ## Don't separate by db in this case!
$menu->append( $self->create_track( $type.'_'.$key_2, $data->{$key_2}{'name'}, {
'strand' => $gset =~ /P_/ ? 'f' : 'b',
'depth' => 1e6,
'glyphset' => $gset,
'logicnames' => $data->{$key_2}{'logic_names'},
'name' => $data->{$key_2}{'name'},
'caption' => $data->{$key_2}{'caption'},
'colourset' => 'protein_feature',
'description' => $data->{$key_2}{'description'},
'display' => $renderer, ## Default to on at the moment - change to off by default!
'renderers' => [qw(off Off normal Normal)],
}));
}
}
}
sub add_repeat_feature {
my( $self, $key, $hashref ) = @_;
return unless $self->get_node( 'repeat' );
## Add generic feature track...
return unless $hashref->{'repeat_feature'}{'rows'}>0; ## We have repeats...
my $data = $hashref->{'repeat_feature'}{'analyses'};
my $menu = $self->get_node( 'repeat' );
$menu->append( $self->create_track( 'repeat_'.$key, "All repeats", {
'db' => $key,
'glyphset' => '_repeat',
'logicnames' => [undef], ## All logic names...
'types' => [undef], ## All repeat types...
'name' => 'All repeats',
'description' => 'All repeats',
'colourset' => 'repeat',
'display' => 'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw(off Off normal Normal)],
'optimizable' => 1,
'depth' => 0.5,
'bump_width' => 0,
'strand' => 'r'
}));
my $flag = keys %$data > 1;
foreach my $key_2 ( sort { $data->{$a}{'name'} cmp $data->{$b}{'name'} } keys %$data ) {
## Add track for each analysis ()... break down 1
if( $flag ) {
$menu->append( $self->create_track( 'repeat_'.$key.'_'.$key_2, $data->{$key_2}{'name'}, {
'db' => $key,
'glyphset' => '_repeat',
'logicnames' => [ $key_2 ], ## Restrict to a single supset of logic names...
'types' => [ undef ],
'name' => $data->{$key_2}{'name'},
'description' => $data->{$key_2}{'desc'},
'colours' => $self->species_defs->colour( 'repeat' ),
'display' => 'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw(off Off normal Normal)],
'optimizable' => 1,
'depth' => 0.5,
'bump_width' => 0,
'strand' => 'r'
}));
}
## Add track for each repeat_type ();
my $d2 = $data->{$key_2}{'types'};
if( keys %$d2 > 1 ) {
foreach my $key_3 ( sort keys %$d2 ) {
(my $key_3a = $key_3) =~ s/\W/_/g;
my $n = $key_3;
$n.= " (".$data->{$key_2}{'name'}.")" unless $data->{$key_2}{'name'} eq 'Repeats';
$menu->append( $self->create_track( 'repeat_'.$key.'_'.$key_2.'_'.$key_3a, $n,{
'db' => $key,
'glyphset' => '_repeat',
'logicnames' => [ $key_2 ],
'types' => [ $key_3 ],
'name' => $n,
'description' => $data->{$key_2}{'desc'}." ($key_3)",
'colours' => $self->species_defs->colour( 'repeat' ),
'display' => 'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw(off Off normal Normal)],
'optimizable' => 1,
'depth' => 0.5,
'bump_width' => 0,
'strand' => 'r'
}));
}
}
}
}
#----------------------------------------------------------------------#
# Functions to add tracks from compara like databases....
#----------------------------------------------------------------------#
sub add_synteny {
my( $self, $key, $hashref, $species ) = @_;
return unless $self->get_node( 'synteny' );
my @synteny_species = sort keys %{$hashref->{'SYNTENY'}{$species}||{}};
return unless @synteny_species;
my $menu = $self->get_node( 'synteny' );
my $self_label = $self->species_defs->species_label( $species, "no_formatting" );
foreach my $species ( @synteny_species ) {
( my $species_readable = $species ) =~ s/_/ /g;
my ($a,$b) = split / /, $species_readable;
my $caption = substr($a,0,1).".$b synteny";
my $label = $self->species_defs->species_label( $species, "no_formatting" );
( my $name = "Synteny with $label" ) =~ s/<.*?>//g;
$menu->append( $self->create_track( 'synteny_'.$species, $name, {
'db' => $key,
'glyphset' => '_synteny',
'species' => $species,
'species_hr' => $species_readable,
'caption' => $caption,
'description' => "<a href=\"/info/docs/compara/analyses.html#synteny\" class=\"cp-external\">Synteny regions</a> between $self_label and $label",
'colours' => $self->species_defs->colour( 'synteny' ),
'display' => 'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw(off Off normal Normal)],
'height' => 4,
'strand' => 'r'
}));
}
}
sub add_alignments {
my( $self, $key, $hashref,$species ) = @_;
return unless $self->_check_menus(qw( multiple_align pairwise_tblat pairwise_blastz pairwise_other ));
my $alignments = {};
my $self_label = $self->species_defs->species_label($species, "no_formatting");
my $regexp = $species =~ /^([A-Z])[a-z]*_([a-z]{3})/ ? "-?$1.$2-?" : 'xxxxxx';
foreach my $row (values %{$hashref->{'ALIGNMENTS'}}) {
next unless $row->{'species'}{$species};
if ($row->{'class'} =~ /pairwise_alignment/) {
my ($other_species) = grep { $species ne $_ } keys %{$row->{'species'}};
my $other_label = $self->species_defs->species_label($other_species, "no_formatting");
(my $other_species_hr = $other_species ) =~ s/_/ /g;
my $menu_key;
my $description;
if ($row->{'type'} =~ /BLASTZ/) {
$menu_key = 'pairwise_blastz';
$description = "<a href=\"/info/docs/compara/analyses.html\" class=\"cp-external\">BLASTz net pairwise alignments</a> between $self_label and $other_label.";
} elsif ($row->{'type'} =~ /TRANSLATED_BLAT/) {
$menu_key = 'pairwise_tblat';
$description = "<a href=\"/info/docs/compara/analyses.html\" class=\"cp-external\">Trans. BLAT net pairwise alignments</a> between $self_label and $other_label.";
} else {
$menu_key = 'pairwise_align';
$description = "<a href=\"/info/docs/compara/analyses.html\" class=\"cp-external\">Pairwise alignments</a> between $self_label and $other_label.";
}
(my $caption = $row->{'name'}) =~ s/blastz-net \(on.*?\)/BLASTz net/g;
$caption =~ s/translated-blat-net/Trans. BLAT net/g;
$caption =~ s/$regexp//;
$alignments->{$menu_key}{$row->{'id'}} = {
'db' => $key,
'glyphset' => '_alignment_pairwise',
'name' => $row->{'name'},
'caption' => $caption,
'type' => $row->{'type'},
'species_set_id' => $row->{'species_set_id'},
'species' => $other_species,
'species_hr' => $other_species_hr,
'_assembly' => $self->species_defs->other_species( $other_species, 'ENSEMBL_GOLDEN_PATH' ),
'class' => $row->{'class'},
'description' => $description,
'order' => $row->{'type'}.'::'.$other_species,
'colourset' => 'pairwise',
'strand' => 'r',
'display' => 'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw( off Off compact Compact normal Normal )],
};
} else {
my $n_species = grep { $_ ne 'Ancestral_sequences' } keys %{$row->{'species'}};
if ($row->{'conservation_score'}) {
my ($program) = $hashref->{'CONSERVATION_SCORES'}{$row->{'conservation_score'}}{'type'} =~ /(.+)_CONSERVATION_SCORE/;
$alignments->{'multiple_align'}{$row->{'id'}.'_scores'} = {
'db' => $key,
'glyphset' => '_alignment_multiple',
'name' => "Conservation score for ".$row->{'name'},
'short_name' => $row->{'name'},
'caption' => "$n_species way $program scores",
'type' => $row->{'type'},
'species_set_id' => $row->{'species_set_id'},
'method_link_species_set_id' => $row->{'id'},
'class' => $row->{'class'},
'conservation_score' => $row->{'conservation_score'},
'description' => "<a href=\"/info/docs/compara/analyses.html#conservation\" class=\"cp-external\">$program conservation scores</a> based on the ".$row->{'name'},
'colourset' => 'multiple',
'order' => sprintf('%12d::%s::%s', 1e12-$n_species*10, $row->{'type'}, $row->{'name'}),
'strand' => 'f',
'display' => $row->{'id'} == 364 ? 'signal_map' : 'off', ## Default to on at the moment - change to off by default!
'renderers' => [ 'off' => 'Off', 'signal_map' => 'Signal map' ]
};
$alignments->{'multiple_align'}{$row->{'id'}.'_constrained'} = {
'db' => $key,
'glyphset' => '_alignment_multiple',
'name' => "Constrained elements for ".$row->{'name'},
'short_name' => $row->{'name'},
'caption' => "$n_species way $program elements",
'type' => $row->{'type'},
'species_set_id' => $row->{'species_set_id'},
'method_link_species_set_id' => $row->{'id'},
'class' => $row->{'class'},
'constrained_element' => $row->{'constrained_element'},
'description' => "<a href=\"/info/docs/compara/analyses.html#conservation\" class=\"cp-external\">$program constrained elements</a> based on the ".$row->{'name'},
'colourset' => 'multiple',
'order' => sprintf( '%12d::%s::%s',1e12-$n_species*10+1, $row->{'type'}, $row->{'name'} ),
'strand' => 'f',
'display' => $row->{'id'} == 364 ? 'compact' : 'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw( off Off compact Normal )]
};
}
$alignments->{'multiple_align'}{$row->{'id'}} = {
'db' => $key,
'glyphset' => '_alignment_multiple',
'name' => $row->{'name'},
'short_name' => $row->{'name'},
'caption' => $row->{'name'},
'type' => $row->{'type'},
'species_set_id' => $row->{'species_set_id'},
'method_link_species_set_id' => $row->{'id'},
'class' => $row->{'class'},
'description' => "<a href=\"/info/docs/compara/analyses.html#conservation\">$n_species way whole-genome multiple alignments</a>.; ".
join("; ", sort map {$self->species_defs->species_label( $_, "no_formatting" )} grep { $_ ne 'Ancestral_sequences' } keys %{$row->{'species'}}),
'colourset' => 'multiple',
'order' => sprintf('%12d::%s::%s', 1e12-$n_species*10-1, $row->{'type'}, $row->{'name'}),
'strand' => 'f',
'display' => 'off', ## Default to on at the moment - change to off by default!
'renderers' => [qw( off Off compact Normal )],
};
}
}
foreach my $menu_key (keys %$alignments) {
my $menu = $self->get_node($menu_key);
next unless $menu;
foreach my $key_2 (sort {
$alignments->{$menu_key}{$a}{'order'} cmp $alignments->{$menu_key}{$b}{'order'}
} keys %{$alignments->{$menu_key}}) {
my $row = $alignments->{$menu_key}{$key_2};
$menu->append($self->create_track('alignment_' . $key . '_' . $key_2, $row->{'caption'}, $row));
}
}
}
sub add_option {
my( $self, $key, $caption, $values ) = @_;
return;
my $menu = $self->get_node( 'options' );
return unless $menu;
$menu->append( $self->create_option( $key, $caption, $values ) );
}
sub add_options {
my $self = shift;
return;
my $menu = $self->get_node( 'options' );
return unless $menu;
foreach my $row (@_) {
my ($key, $caption, $values ) = @$row;
$menu->append( $self->create_option( $key, $caption, $values ) );
}
}
sub create_track {
my ( $self, $code, $caption, $options ) = @_;
my $details = { 'name' => $caption, 'node_type' => 'track' };
foreach ( keys %{$options||{}} ) {
$details->{$_} = $options->{$_};
}
$details->{'strand'} ||= 'b'; # Make sure we have a strand setting!!
$details->{'display'} ||= 'normal'; # Show unless we explicitly say no!!
$details->{'renderers'}||= [qw(off Off normal Normal)];
$details->{'colours'} ||= $self->species_defs->colour( $options->{'colourset'} ) if exists $options->{'colourset'};
$details->{'glyphset'} ||= $code;
$details->{'caption'} ||= $caption;
return $self->tree->create_node( $code, $details );
}
sub add_track {
my( $self, $menu_key, $key, $caption, $glyphset, $params ) = @_;
my $menu = $self->get_node( $menu_key );
return unless $menu;
return if $self->get_node( $key ); ## Don't add duplicates...
$params->{'glyphset'} = $glyphset;
$menu->append( $self->create_track( $key, $caption, $params ) );
}
sub add_tracks {
my $self = shift;
my $menu_key = shift;
my $menu = $self->get_node( $menu_key );
return unless $menu;
foreach my $row (@_) {
my ( $key, $caption, $glyphset, $params ) = @$row;
next if $self->get_node( $key ); ## Don't add duplicates...
$params->{'glyphset'} = $glyphset;
$menu->append( $self->create_track( $key, $caption, $params ) );
}
}
#----------------------------------------------------------------------#
# Functions to add tracks from functional genomics like database....
#----------------------------------------------------------------------#
sub add_regulation_feature { ## needs configuring so tracks only display if data in species fg_database
my( $self, $key, $hashref, $species ) = @_;
return unless $self->get_node( 'functional' );
my ($keys, $data) = $self->_merge($hashref->{'analysis_description'});
my $menu = $self->get_node( 'functional' );
foreach my $key_2 ( @$keys ) {
my $K = $data->{$key_2}{'type'}||'other';
my $render = ['off'=> 'Off','normal' => 'Normal'];
my $legend_flag = 0;
my $wiggle_flag = 0;
my $cisred_flag = 0;
if ( $data->{$key_2}{'renderers'}) {
my %renderers = %{ $data->{$key_2}{'renderers'} };
my @temp;
foreach (keys %renderers){
my $value = $renderers{$_};
push (@temp, $_);
push (@temp, $value);
if ($_ =~/signal/){
unless ( $data->{$key_2}{'type'} =~/histone/ ){
$wiggle_flag = 1;
}
}
}
$render = \@temp;
}
if ($K =~/fg_reg/) { $legend_flag = 1; }
if ($data->{$key_2}{'description'} =~/cisRED/){ $cisred_flag = 1; }
$menu->append($self->create_track ($K.'_'.$key, sprintf($data->{$key_2}{'name'}||$data->{$key_2}{'logic_names'}),{
'db' => $key,
'glyphset' => $K,
'sources' => 'undef',
'strand' => 'r',
'labels' => 'on',
'depth' => $data->{$key_2}{'depth'}||0.5,
'colourset' => $data->{$key_2}{'colourset'}||$K,
'description' => $data->{$key_2}{'description'},
'display' => $data->{$key_2}{'display'}||'off',
'renderers' => $render,
}));
if ( $wiggle_flag ){
$menu->append($self->create_track ($K.'_'.$key. '_blocks', sprintf($data->{$key_2}{'name'} .' peaks'|| $data->{$key_2}{'logic_names'} .' peaks'),{
'db' => $key,
'glyphset' => $K,
'sources' => 'undef',
'strand' => 'r',
'labels' => 'on',
'depth' => $data->{$key_2}{'depth'}||0.5,
'colourset' => $data->{$key_2}{'colourset'}||$K,
'description' => $data->{$key_2}{'description'},
'display' => $data->{$key_2}{'display'}||'off',
'renderers' => ['off'=>'Off','compact'=>'Normal'],
}));
}
if ( $legend_flag ){
$self->add_track('information', 'fg_regulatory_features_legend', 'Reg. Features Legend', 'fg_regulatory_features_legend', {'strand' => 'r'});
}
if ($cisred_flag) {
$menu->append($self->create_track ($K.'_'.$key .'_search', 'cisRED Search Regions',{
'db' => $key,
'glyphset' => 'regulatory_search_regions',
'sources' => 'undef',
'strand' => 'r',
'labels' => 'on',
'depth' => 0.5,
'colourset' => 'regulatory_search_regions',
'description' => 'cisRED Search Regions',
'display' => 'off',
}));
}
}
return;
}
sub add_decorations {
my( $self, $key, $hashref ) = @_;
my $menu = $self->get_node( 'decorations' );
return unless $menu;
if( $key eq 'core' && $hashref->{'assembly_exception'}{'rows'} > 0 ) {
$menu->append( $self->create_track( 'assembly_exception_'.$key, 'Assembly exceptions',{
'db' => $key,
'glyphset' => 'assemblyexception',
'height' => 2,
'display' => 'normal',
'strand' => 'x',
'label_strand' => 'r',
'short_labels' => 0,
'description' => 'Haplotype (HAPs) and Pseudo autosomal regions (PARs)',
'colourset' => 'assembly_exception'
}));
}
if( $key eq 'core' && $hashref->{'karyotype'}{'rows'}>0 && ! $self->get_node('ideogram') ) {
$menu->append( $self->create_track( 'chr_band_'.$key, 'Chromosome bands',{
'db' => $key,
'glyphset' => 'chr_band',
'display' => 'normal',
'strand' => 'f',
'description' => 'Cytogenetic bands',
'colourset' => 'ideogram'
}));
}
}
#----------------------------------------------------------------------#
# Functions to add tracks from variation like databases....
#----------------------------------------------------------------------#
sub add_variation_feature {
my( $self, $key, $hashref ) = @_;
my $menu = $self->get_node( 'variation' );
return unless $menu;
return unless $hashref->{'variation_feature'}{'rows'} > 0;
$menu->append( $self->create_track( 'variation_feature_'.$key, sprintf( "All variations" ), {
'db' => $key,
'glyphset' => '_variation',
'sources' => undef,
'strand' => 'r',
'depth' => 0.5,
'bump_width' => 0,
'colourset' => 'variation',
'description' => 'Variation features from all sources',
'display' => 'off'
}));
$menu->append( $self->create_track( 'variation_feature_genotyped_'.$key, sprintf( "Genotyped variations" ), {
'db' => $key,
'glyphset' => '_variation',
'sources' => undef,
'strand' => 'r',
'depth' => 0.5,
'bump_width' => 0,
'filter' => 'genotyped',
'colourset' => 'variation',
'description' => 'Genotyped variation features from all sources',
'display' => 'off'
}));
foreach my $key_2 (sort keys %{$hashref->{'source'}{'counts'}||{}}) {
( my $k = $key_2 ) =~ s/\W/_/g;
$menu->append( $self->create_track( 'variation_feature_'.$key.'_'.$k, sprintf( "%s variations", $key_2 ), {
'db' => $key,
'glyphset' => '_variation',
'caption' => $key_2,
'sources' => [ $key_2 ],
'strand' => 'r',
'depth' => 0.5,
'bump_width' => 0,
'colourset' => 'variation',
'description' => sprintf( 'Variation features from the "%s" source', $key_2 ),
'display' => 'off'
}));
}
## add in read coverage wiggle plots
my @read_coverage_strains = split(/,/, $hashref->{'read_coverage_collection_strains'});
foreach my $strain_info (@read_coverage_strains) {
my ($strain_name, $sample_id) = split (/_/, $strain_info);
$menu->append($self->create_track('read_wiggle_'.$key."_".$strain_name, sprintf("RC %s", $strain_name ),{
'db' => $key,
'glyphset' => 'read_coverage',
'sources' => undef,
'strand' => 'r',
'labels' => 'on',
'colourset' => 'read_coverage',
'height' => 6,
'description' => 'Read Coverage for '. $strain_name,
'display' => 'off'
}));
}
$self->add_track( 'information', 'variation_legend', 'Variation Legend', 'variation_legend', { 'strand' => 'r' } );
}
## return a list of glyphsets...
sub glyphset_configs {
my $self = shift;
return grep { $_->data->{'node_type'} eq 'track' } $self->tree->nodes;
}
sub get_node {
my $self = shift;
return $self->tree->get_node(@_);
}
sub create_submenu {
my ($self, $code, $caption, $options ) = @_;
my $details = { 'caption' => $caption, 'node_type' => 'menu' };
foreach ( keys %{$options||{}} ) {
$details->{$_} = $options->{$_};
}
return $self->tree->create_node( $code, $details );
}
sub create_option {
my ( $self, $code, $caption, $values ) = @_;
$values ||= {qw(0 no 1 yes)};
return $self->tree->create_node( $code, {
'node_type' => 'option',
'caption' => $caption,
'name' => $caption,
'values' => $values,
});
}
sub _set_core { $_[0]->{'_core'} = $_[1]; }
sub core_objects { return $_[0]->{'_core'}; }
sub storable :lvalue {
### a
### Set whether this ViewConfig is changeable by the User, and hence needs to
### access the database to set storable do $view_config->storable = 1; in SC code...
$_[0]->{'storable'};
}
sub altered :lvalue {
### a
### Set to one if the configuration has been updated...
$_[0]->{'altered'};
}
sub TRIM { return sub { return $_[0]=~/(^[^\.]+)\./ ? $1 : $_[0] }; }
sub update_config_from_parameter {
my( $self, $string ) = @_;
my @array = split /\|/, $string;
shift @array;
return unless @array;
foreach( @array ) {
my( $key, $value ) = /^(.*):(.*)$/;
if( $key =~ /bump_(.*)/ ) {
$self->set( $1, 'compact', $value eq 'on' ? 0 : 1 );
} elsif( $key eq 'imagemap' || $key=~/^opt_/ ) {
$self->set( '_settings', $key, $value eq 'on' ? 1: 0 );
} elsif( $key =~ /managed_(.*)/ ) {
$self->set( $key, 'on', $value, 1 );
} else {
$self->set( $key, 'on', $value );
}
}
#$self->save; - deprecated
}
sub set_species {
my $self = shift;
$self->{'species'} = shift;
}
sub get_user_settings {
my $self = shift;
return $self->tree->user_data;
}
sub artefacts { my $self = shift; return @{ $self->{'general'}->{$self->{'type'}}->{'_artefacts'}||[]} };
sub remove_artefacts {
my $self = shift;
my %artefacts = map { ($_,1) } @_;
@{ $self->{'general'}->{$self->{'type'}}->{'_artefacts'} } =
grep { !$artefacts{$_} } $self->subsections( );
}
sub add_artefacts {
my $self = shift;
$self->_set( $_, 'on', 'on') foreach @_;
push @{$self->{'general'}->{$self->{'type'}}->{'_artefacts'}}, @_;
}
# add general and artefact settings
sub add_settings {
my $self = shift;
my $settings = shift;
foreach (keys %{$settings}) {
$self->{'general'}->{$self->{'type'}}->{$_} = $settings->{$_};
}
}
sub turn_on {
my $self = shift;
$self->_set( $_, 'on', 'on') foreach( @_ ? @_ : $self->subsections( 1 ) );
}
sub turn_off {
my $self = shift;
$self->_set( $_, 'on', 'off') foreach( @_ ? @_ : $self->subsections( 1 ) );
}
sub _set {
my( $self, $entry, $key, $value ) = @_;
$self->{'general'}->{$self->{'type'}}->{$entry}->{$key} = $value;
}
sub save {
my ($self) = @_;
warn "ImageConfig->save - Deprecated call now handled by session";
return;
}
sub reset {
my ($self) = @_;
$self->{'user'}->{$self->{'type'}} = {};
$self->altered = 1;
return;
}
sub reset_subsection {
my ($self, $subsection) = @_;
return unless(defined $subsection);
$self->{'user'}->{$self->{'type'}}->{$subsection} = {};
$self->altered = 1;
return;
}
sub subsections {
my ($self,$flag) = @_;
my @keys;
@keys = grep { /^managed_/ } keys %{$self->{'user'}} if $flag==1;
return @{$self->{'general'}->{$self->{'type'}}->{'_artefacts'}},@keys;
}
sub species_defs {
### a
my $self = shift;
return $self->{'species_defs'};
}
sub colourmap {
### a
my $self = shift;
return $self->{'_colourmap'};
}
sub image_height {
### a
my $self = shift;
$self->set_parameter('_height',shift) if @_;
return $self->get_parameter('_height');
}
sub bgcolor {
### a
my $self = shift;
$self->get_parameter( 'bgcolor' ) || 'background1';
}
sub bgcolour {
### a
my $self = shift;
return $self->bgcolor;
}
sub texthelper {
### a
my $self = shift;
return $self->{'_texthelper'};
}
sub scalex {
my $self = shift;
if(@_) {
$self->{'_scalex'} = shift;
$self->{'_texthelper'}->scalex($self->{'_scalex'});
}
return $self->{'_scalex'};
}
sub set_width {
my( $self, $val ) = @_;
$self->set_parameter( 'width', $val );
}
sub transform {
my $self = shift;
return $self->{'transform'};
}
1;