package EnsEMBL::Web::Component::Gene::ComparaTree;
use strict;
use warnings;
no warnings "uninitialized";
use base qw(EnsEMBL::Web::Component::Gene);
use EnsEMBL::Web::Constants;
use Bio::AlignIO;
use IO::Scalar;
sub _init {
my $self = shift;
$self->cacheable( 0 );
$self->ajaxable( 1 );
}
sub caption {
return undef;
}
sub _get_details {
my $self = shift;
my $object = $self->object;
my $member = $object->get_compara_Member;
return (undef, $self->_error(
'No compara member',
q(<p>Unable to render gene tree as gene is not in the Comparative genomics database</p>)
)) unless $member;
my $tree = $object->get_ProteinTree;
return (undef,$self->_error(
'Gene not in protein tree',
q(<p>This gene has no orthologues in Ensembl Compara, so a gene tree cannot be built.</p>)
)) unless $tree;
my $node = $tree->get_leaf_by_Member($member);
return(undef,$self->_error(
'Gene not in tree',
sprintf( q(<p>Member %s not in tree %s</p>), $member->stable_id, $tree->node_id )
)) unless $node;
return ($member,$tree,$node);
}
sub content {
my $self = shift;
my $object = $self->object;
#----------
# Get the Member and ProteinTree objects
#----------
# Draw the tree
my ( $member,$tree,$node ) = $self->_get_details;
return $tree if !defined $member;
my $wuc = $object->image_config_hash( 'genetreeview' );
my $image_width = $self->image_width || 800;
my $collapsability = $object->param('collapsability') || 'gene';
$wuc->set_parameters({
'container_width' => $image_width,
'image_width', => $image_width,
'slice_number', => '1|1',
});
#$wuc->tree->dump("GENE TREE CONF", '([[caption]])');
my @highlights = ($object->stable_id, $member->genome_db->dbID);
# Keep track of collapsed nodes
my $collapsed_nodes = $object->param('collapse');
my $collapsed_to_gene = $self->_collapsed_nodes($tree,$node, 'gene');
my $collapsed_to_para = $self->_collapsed_nodes($tree,$node, 'paralogs');
my $collapsed_to_dups = $self->_collapsed_nodes($tree,$node, 'duplications');
unless( defined( $collapsed_nodes ) ){ #Examine collapsabilty
$collapsed_nodes = $collapsed_to_gene if( $collapsability eq 'gene');
$collapsed_nodes = $collapsed_to_para if( $collapsability eq 'paralogs');
$collapsed_nodes = $collapsed_to_dups if( $collapsability eq 'duplications');
$collapsed_nodes ||= '';
}
push @highlights, $collapsed_nodes || undef;
my $image = $self->new_image
( $tree, $wuc, [@highlights] );
return if $self->_export_image($image, 'no_text');
# $image->cacheable = 'yes';
$image->image_type = 'genetree';
$image->image_name = ($object->param('image_width')).'-'.$object->stable_id;
$image->imagemap = 'yes';
$image->{'panel_number'} = 'tree';
$image->set_button( 'drag', 'title' => 'Drag to select region' );
my $li_tmpl = qq(
<li><a href="%s">%s</a></li>);
my @view_links;
push @view_links, sprintf( $li_tmpl,
$object->_url({'collapse'=>$collapsed_to_gene}),
'View current gene only');
push @view_links, sprintf( $li_tmpl,
$object->_url({'collapse'=>$collapsed_to_para}),
'View paralogs of current gene');
push @view_links, sprintf( $li_tmpl,
$object->_url({'collapse'=>$collapsed_to_dups}),
'View all duplication nodes');
push @view_links, sprintf( $li_tmpl,
$object->_url({'collapse'=>''}),
'View fully expanded tree');
my $view_options_html = sprintf( qq(
<div style="margin-top:1em"><b>View options:</b><br/>
<small><ul>%s</ul></small>
Use the 'configure page' link in the left panel to set the default. Further options are available from menus on individual tree nodes.</div>), join( '', @view_links) );
return $image->render . $view_options_html ;
}
sub _collapsed_nodes{
# Takes the ProteinTree and node related to this gene and a view action
# ('gene', 'paralogs', 'duplications' ) and returns the list of
# tree nodes that should be collapsed according to the view action.
# TODO: Move to Object::Gene, as the code is shared by the ajax menus
my $self = shift;
my $tree = shift;
my $node = shift;
my $action = shift;
$tree->isa('Bio::EnsEMBL::Compara::ProteinTree')
|| die( "Need a ProteinTree, not a $tree" );
$node->isa('Bio::EnsEMBL::Compara::AlignedMember')
|| die( "Need an AlignedMember, not a $node" );
my %collapsed_nodes;
my %expanded_nodes;
if( $action eq 'gene' ){ # View current gene
foreach my $adj( @{$node->get_all_adjacent_subtrees} ){
$collapsed_nodes{$adj->node_id} = $_;
}
}
elsif( $action eq 'paralogs' ){ # View all paralogs
my $gdb_id = $node->genome_db_id;
foreach my $leaf( @{$tree->get_all_leaves} ){
if( $leaf->genome_db_id == $gdb_id ){
foreach my $ancestor( @{$leaf->get_all_ancestors} ){
$expanded_nodes{$ancestor->node_id} = $ancestor;
}
foreach my $adjacent( @{$leaf->get_all_adjacent_subtrees} ){
$collapsed_nodes{$adjacent->node_id} = $adjacent;
}
}
}
}
elsif( $action eq 'duplications' ){ # View all duplications
foreach my $tnode( @{$tree->get_all_nodes} ){
next if $tnode->is_leaf;
if($tnode->get_tagvalue('dubious_duplication') or
! $tnode->get_tagvalue('Duplication') ){
$collapsed_nodes{$tnode->node_id} = $tnode;
next;
}
$expanded_nodes{$tnode->node_id} = $tnode;
foreach my $ancestor( @{$tnode->get_all_ancestors} ){
$expanded_nodes{$ancestor->node_id} = $ancestor;
}
}
}
my $collapsed_node_ids
= join( ',', grep{! $expanded_nodes{$_}} keys %collapsed_nodes );
return $collapsed_node_ids;
}
sub content_align {
my $self = shift;
my $object = $self->object;
#----------
# Get the ProteinTree object
my ( $member,$tree,$node ) = $self->_get_details;
return $tree if !defined $member;
#----------
# Return the text representation of the tree
my $htmlt = q(
<p>Multiple sequence alignment in "<i>%s</i>" format:</p>
<p>The sequence alignment format can be configured using the
'configure page' link in the left panel.<p>
<pre>%s</pre>);
#----------
# Determine the format
my %formats = EnsEMBL::Web::Constants::ALIGNMENT_FORMATS();
my $mode = $object->param('text_format');
$mode = 'fasta' unless $formats{$mode};
my $fmt_caption = $formats{$mode} || $mode;
my $align_format = $mode;
my $formatted; # Variable to hold the formatted alignment string
my $SH = IO::Scalar->new(\$formatted);
#print $SH "FOO\n";
my $aio = Bio::AlignIO->new( -format => $align_format, -fh => $SH );
$aio->write_aln( $tree->get_SimpleAlign );
return $object->param('_format') eq 'Text'
? $formatted
: sprintf( $htmlt, $fmt_caption, $formatted )
;
}
sub content_text {
my $self = shift;
my $object = $self->object;
#----------
# Get the ProteinTree object
my ( $member,$tree,$node ) = $self->_get_details;
return $tree if !defined $member;
#----------
# Template for the section HTML
my $htmlt = q(
<p>The following is a representation of the tree in "<i>%s</i>" format</p>
<p>The tree representation can be configured using the
'configure page' link in the left panel.<p>
<pre>%s</pre>);
#----------
# Return the text representation of the tree
my %formats = EnsEMBL::Web::Constants::TREE_FORMATS();
my $mode = $object->param('tree_format');
$mode = 'newick' unless $formats{$mode};
my $fn = $formats{$mode}{'method'};
my $fmt_caption = $formats{$mode}{caption} || $mode;
my @params = ( map { $object->param( $_ ) }
@{ $formats{$mode}{'parameters'} || [] } );
my $string = $tree->$fn(@params);
if( $formats{$mode}{'split'} && $object->param('_format') ne 'Text') {
my $reg = '(['.quotemeta($formats{$mode}{'split'}).'])';
$string =~ s/$reg/$1\n/g;
}
return $object->param('_format') eq 'Text'
? $string
: sprintf( $htmlt, $fmt_caption, $string )
;
}
1;