EOM
return;
}
#----------------------------------------------------------------------
sub _make_zoom_image {
#----------------------------------------------------------------------
# This subroutine crates the content of the zoom frame. It expects an
# - array reference that contains the indeces of data matrix rows to display;
# - string that contains an html-formatted message for the frame to display;
# - array reference
my $self = shift;
my ($rows_ra, $message, $corr_ra) = @_;
my $js = script({-language => "JavaScript"},
"function info(txt) { parent.t.document.infobox.text.value=(txt) };")."\n";
# This section creates a table with each row showing data for a gene
# get image rows requested
my $row_images = $self->_make_row_image ($rows_ra);
# mark up the image rows and and create html table using feature info
my $html_table = $self->_make_html_table($rows_ra, $row_images, $corr_ra);
$self->_makeExptImageMap();
$self->_start_html;
print $js;
print $message;
print $self->{'exptMap'};
print table({-border => 0,
-cellspacing => 2,
-cellpadding => 0,
-align => 'LEFT'},
Tr(
$self->{'exptImage'}, "\n",
td({-colspan => 1,
-align => 'left'},
$self->_contrast_legend
), "\n"
), "\n",
Tr(
td({-colspan => 2,
-valign => 'top'}, $html_table
), "\n"
), "\n"
)."\n";
print end_html();
}
#--------------------------------------------------------------------
sub _make_row_image {
#--------------------------------------------------------------------
# This method takes as input an array reference that should contain an
# ordered index of rows requested from the matrix image.
# The image (matrix image) is broken up into individual row images and
# and the ones pointed to by the array indeces are passed on for saving
# and upon saving returned to the calling function
my $self = shift;
my $index_aref = shift;
my $source_image = $self->_dataset->image("matrix");
my $width = $self->_dataset->width();
my ($height, @row_images);
# the height for the ROW images = 1
$height = 1;
# break up the source image into image rows
for (my $i = 0; $i <= $#$index_aref; $i++ ) {
my $image_row = new GD::Image($width, $height);
$image_row->copy($source_image, 0, 0, 0, $$index_aref[$i], $width, $height);
push (@row_images, $image_row);
}
$self->_save_img_files($index_aref, \@row_images);
}
#----------------------------------------------------------------------
sub _save_img_files{
#----------------------------------------------------------------------
# This subroutine saves the image files that are passed in in the form of
# an array reference. In addition it expects another array reference that
# is an index file specifying which rows of the matrix image the array images
# came from.
my $self = shift;
my ($ids_ra, $images_ra) = @_;
my (@image_files);
my $imgSuffix = $self->_imgType;
for (my $i = 0; $i <= $#$ids_ra; $i++) {
my $img_file = $self->_name.$$ids_ra[$i].'.'.$imgSuffix; # name of row image
$img_file =~ s/\//_/g; # substitute underscores for slashes in dataset name
my $path = $self->_tmpPath.$img_file; # path to row image in temp directory
my $image = $$images_ra[$i]->$imgSuffix(); # the row image object
if ((!(-e $path)) || (-z $path)) { # if image file does not exist
open( OUT,">$path" ) || warn "Cannot print temp image: $!\n";
binmode OUT;
print OUT $image;
close(OUT);
}
push (@image_files, $img_file);
}
return \@image_files;
}
#----------------------------------------------------------------------
sub _makeExptImageMap {
#----------------------------------------------------------------------
##This will create the content of the infobox: when the mouse is over some
## experiment in the zoom frame this will display the name of the
## appropriate experiment.
my $self = shift;
return if ((defined $self->{'exptImage'}) && (defined $self->{'exptMap'}));
my ($x1, $y1, $x2, @map, $area, $map, $onMouseOver, $imgWidth, $exptImage, $expImgHeight, $height);
# The next section creates the experiment image map
# first, determine the size of the image
($imgWidth, $expImgHeight) = $self->_dataset->image("expt_info")->getBounds();
$imgWidth = $self->_dataset->width() * $self->_zoom_x;
$height = ($expImgHeight > $kExpImgHeight) ? $expImgHeight : $kExpImgHeight;
# then make the html for the image
$exptImage = img({-src => $self->_imgURL.$self->_name.'/'.$self->_dataset->fileBaseName.".expt_info.".$self->_imgType,
-width => $imgWidth,
-ismap => '',
-usemap => '#EXPT',
-border => '0'});
$exptImage = td({-colspan => '1',
-valign => 'bottom',
-align => 'left',
-height => $height}, $exptImage
)."\n";
$self->{'exptImage'} = $exptImage;
# and create the image map
my $script_name = $self->{'info'}->{'scriptname'};
$y1 = 0;
for (my $i=0; $i < $self->_dataset->width; $i++){
$x1 = int ($i * $self->_zoom_x);
$x2 = int ( ($i * $self->_zoom_x) + $self->_zoom_x);
my $text = $self->_dataset->experiment($i, "NAME");
$text =~ s/\"//g;
$onMouseOver = "onMouseOver=\"javascript:info('EXPT: ".$text."')\"";
$area = "\n";
push @map, $area;
}
$map = "";
$self->{'exptMap'} = $map;
}
#----------------------------------------------------------------------
sub _row_image_markup {
#----------------------------------------------------------------------
## This subroutine will mark up the row image in the zoom window:
## - for the infobox: when the mouse is over some gene in the zoom frame
## the name of the appropriate gene is shown.
## - for the correlations display when a gene is clicked on
my $self = shift;
my $i = shift; # which row in the matrix image
my $image_ref = shift; # the row image
my $width = shift; # the width of the image display
my ($onMouseOver, $area);
# display the row image
$image_ref = img({-src => $self->_tmpURL.$image_ref,
-height => $self->_zoom_y,
-width => $width,
-border => '0'});
# the javascript to display the content of the infoBox for the row
$onMouseOver = $self->_dataset->getFeature($i, "NAME");
$onMouseOver =~ s/\"//g;
$onMouseOver = "onMouseOver=\"javascript:info('GENE: ".$onMouseOver."')\"";
# construct the url for corr in case the row is clicked on
$area = "\n{'info'}->{'scriptname'};
$area .= "?n=".$self->_name."&a=c&seed=$i".$self->{'info'}->{'url_params'}.'" ';
$area .= $onMouseOver.">\n".$image_ref."";
}
#--------------------------------------------------------------------
sub _make_html_table {
#--------------------------------------------------------------------
# This method takes a list of vector indices and returns a completely
# marked up HTML version. The markup is done according to a displayConfig
# file that is associated with this explorer object / dataset.
my $self = shift;
my $index_ref = shift; # the indeces of the rows in data matrix that are requested
my $image_ra = shift; # the corresponding row images
my $corr_ra = shift; # the corresponding correlation values (may be empty)
my ($row, @rows, $row_table, $row_image);
my $imgWidth = $self->_dataset->width() * $self->_zoom_x;
# for every row, do the markup
for (my $i=0; $i <= $#$index_ref; $i++) {
# if there is correlation value for this row
if ($corr_ra) {
$row = $self->_feature_markup( $$index_ref[$i], $$corr_ra[$i] );
}
# if there is no corralation to display
else{
$row = $self->_feature_markup( $$index_ref[$i] );
}
# markup the image row
$row_image = $self->_row_image_markup($$index_ref[$i], $$image_ra[$i], $imgWidth);
# make a row in an html table
push @rows, Tr(
td(
$row_image
), "\n",
td({-height=>$self->_zoom_y},
$row
), "\n",
)."\n";
}
# make the table from the marked up row
$row_table = table({-border => '0',
-cellborder => '0',
-cellpadding => '0',
-cellspacing => '0'},
@rows)."\n";
}
#--------------------------------------------------------------------
sub _searchTool {
#--------------------------------------------------------------------
# This sub creates the searchtool for the toolbar and returns it as
# html, as a table cell.
my $self = shift;
my (@keys_used, %desc_used, $searchform, $cell1);
@keys_used = $self->_dataset->getFeatureKeys(); # get the keys used here and sort by (future) value
unshift @keys_used, "ALL"; # add ALL to search all
# and sort by (future) value
@keys_used = sort {$self->{'style'}->{$a}->{'display'} cmp $self->{'style'}->{$b}->{'display'}} @keys_used;
foreach (@keys_used) { # descriptions for used keys
$desc_used{$_} = $self->{'style'}->{$_}->{'display'};
}
# make the html form
$searchform = start_form(-action => $self->{'info'}->{'self'},
-target => 'z');
$searchform .= "| ";
$searchform .= b("Search for ");
$searchform .= textfield(-size => '8',
-maxlength => '20',
-name => 'q');
$searchform .= b(" in ");
$searchform .= popup_menu(-values => \@keys_used,
-labels => \%desc_used,
-name => 'f',
-default => 'ALL');
$searchform .= submit(-label => 'go');
$searchform .= hidden(-name => 'a',
-value => 's',
-override => 1);
$searchform .= $self->_hide_params();
$searchform .= " | ";
$searchform .= end_form();
$searchform = font({-face => 'verdana,arial,sans-serif',
-size => 2}, $searchform
);
chomp($searchform);
return $searchform;
}
#----------------------------------------------------------------------
sub _changeTool {
#----------------------------------------------------------------------
# This subroutine will create the change radar scale tool for the toolbar
# and returns it as html, as a table cell.
my $self = shift;
my (%change_scale, @scale_values, $changeform, %display);
# labels for changing radar frame scale
%change_scale = ( "0.5" => " 50%",
"1" => "100%",
"2" => "200%",
"4" => "400%");
foreach (sort keys %change_scale){
# display the percentages
$display{sprintf("%2.2f", $_ * $self->_radar_x)} = $change_scale{$_};
# submit the new rx values
push (@scale_values, sprintf("%2.2f", $_ * $self->_radar_x));
}
# make the html form for this tool - it is formatted as a table cell
$changeform = start_form(-action => $self->{'info'}->{'self'},
-target => '_top');
$changeform .= "";
$changeform .= "Radar ";
$changeform .= popup_menu(-values => \@scale_values,
-labels => \%display,
-name => 'rx',
-default => '1');
$changeform .= " wide ";
$changeform .= submit(-label => 'change');
$changeform .= $self->_hide_params();
$changeform .= " | ";
$changeform .= end_form();
$changeform = font({-face => 'verdana,arial,sans-serif',
-size => 2}, $changeform
);
chomp($changeform);
return $changeform;
}
#----------------------------------------------------------------------
sub _infobox {
#----------------------------------------------------------------------
# This sub creates the infobox tool for the toolbar and returns it as
# html, as a table cell.
my $self = shift;
my $infobox;
# the html form for this tool
$infobox = start_form({-name => 'infobox'},
""
);
$infobox .= ""; # It is a 2 column wide cell
$infobox .= textfield({-size => '100',
-name => 'text',
-value => 'Put your mouse over elements to see more information here'}
);
$infobox .= " | ";
$infobox .= end_form;
chomp($infobox);
return $infobox;
}
#--------------------------------------------------------------------
sub _feature_markup {
#--------------------------------------------------------------------
# This subroutine will markup as html the annotations for a row in the
# zoom table
my $self = shift;
my $feature_num = shift; # the index of the row to markup in the feature table
my $corr = @_ ? shift : undef; # if there is a corr value provided
my ($data, $spacer, $fieldsUsed, $row, $field);
# flag for flipping the row background color
$flip = $flip ? 0 : 1;
# the fields that are actually used for this dataset
$fieldsUsed = $self->_style_keys;
# first format the spacer that will separate the individual fields
$spacer = $self->_markup_cell(" ", 'NUM', $flip);
# if there is a correlation value, mark it up
$row .= $self->_markup_cell($corr, 'CORR', $flip) if ($corr);
$row .= $spacer;
# mark up each field
foreach $field (@$fieldsUsed) {
# the piece of the that needs markup
$data = $self->_dataset->getFeature($feature_num, $field);
if (length($data) > $kMaxAnnotFieldLength){ # if the data is too long to display
$data = substr($data, 0, $kMaxAnnotFieldLength);
$data .= "... ";
}
$row .= $self->_markup_cell($data, $field, $flip);
$row .= $spacer;
}
return $row;
}
#---------------------------------------------------------------------------------------
sub _markup_cell {
#---------------------------------------------------------------------------------------
# This function returns a TABLE DATA marked up piece of HTML.
# usage: markup_cell ( the piece of data to markup, the type of data, flag to format bkground in table )
my $self = shift;
my $data = shift;
my $colname = shift;
my $flip = shift;
my ($color, $bgcolor, $url, $content, $href, $cell, $height, $width, $name, $image,
%anchor_attr, $target, $font_face, $font_size, $font_color, $bold);
$color = exists($self->{'style'}->{$colname}->{color}) ? $self->{'style'}->{$colname}->{color} : "white";
$bgcolor = exists($self->{'style'}->{$colname}->{bgcolor}) ? $self->{'style'}->{$colname}->{bgcolor} : ($flip ? "white" : "#EEEEEE");
$url = $self->{'style'}->{$colname}->{'url'};
# if data empty or no data was found, return minimal cell content immediately
if (!defined($data) || $data =~ /$colname/i) {
$cell = td({-height => $self->_zoom_y,
-nowrap => '',
-bgcolor => $bgcolor},
,"");
return $cell;
}
# print $data, br, $colname, br;
$target = exists($self->{'style'}->{$colname}->{target}) ? $self->{'style'}->{$colname}->{target} : undef;
$font_face = exists($self->{'style'}->{$colname}->{font_face}) ? $self->{'style'}->{$colname}->{font_face} : undef;
$font_size = exists($self->{'style'}->{$colname}->{font_size}) ? $self->{'style'}->{$colname}->{font_size} : 1;
$font_color = exists($self->{'style'}->{$colname}->{font_color}) ? $self->{'style'}->{$colname}->{font_color} : "black";
$bold = exists($self->{'style'}->{$colname}->{bold});
$content = $data;
# if an image was defined for this field, we make an HTML element (the images
# have to be in the common image dir
if ($self->{'style'}->{$colname}->{image}){
if ($colname eq "CORR") { # format the bar for correlations
$height = $self->_zoom_y - 2;
$width = ($data - 0.4) * 40; # only corrs above 0.5 are given, but make sure something is shown
$image = img({-border => 0,
-height => $height,
-width => $width,
-alt => "correlation = ".$data,
-name => $data,
-onmouseover => "javascript:info('CORR: ".$data."')",
-src => $self->_commonImageURL.$self->{'style'}->{$colname}->{image}}
);
}
else {
$image = img({-border => 0,
-src => $self->_commonImageURL.$self->{'style'}->{$colname}->{image}}
);
}
}
# build a URL when present in the displayConfig file
if ($self->{'style'}->{$colname}->{url_append} || $self->{'style'}->{$colname}->{url_replace}) {
if ($self->{'style'}->{$colname}->{url_append}){
$href = "http://".$url.$data;
}
elsif ($self->{'style'}->{$colname}->{url_replace}){
$url =~ s/XXX/$data/;
$href = "http://".$url;
}
$anchor_attr{'-href'} = $href;
if ($target){
$anchor_attr{'-target'} = $target;
}
# if an image has been defined, replace the text with it
if ($image){
$content = a(\%anchor_attr, "\n".$image);
}
else {
$content = a(\%anchor_attr, $data);
}
}
# if an image, but no link was requested, make only the image table cell content
# this case more exceptional, we use it for the correlation display bars
if ($image && !$href) {
$content = $image;
}
# if bold was requested, add tags
if ($bold && !$image) {
$content = b($content);
}
if ($font_face || $font_size) {
$content = font(
{-face => $font_face,
-size => $font_size},
$content
);
}
$cell = td(
{-height => $self->_zoom_y,
-bgcolor => $bgcolor,
-nowrap => ''},
"\n", $content
)."\n";
}
#--------------------------------------------------------------------
sub _style_keys {
#--------------------------------------------------------------------
# This subroutine will return the a list of the column names that are
# actually used in the feature table for annotation of the dataset. They
# are returned as elements of an array in the order they are to be
# displayed (that is determined by the order they are listed in the
# displayConfig file).
my $self = shift;
my (@keyStyles, @feature_keys, @ordered_style_keys);
# load displayConfig file if needed
$self->_load_displayConfig() unless (defined($self->{'style'}));
# all potential columns
@keyStyles = keys %{ $self->{'style'} };
# the ones used for annotation
@feature_keys = $self->_dataset->getFeatureKeys();
# put the ones used in an ordered array
foreach (@feature_keys) {
$ordered_style_keys[$self->{'style'}->{$_}->{'order'}] = $_;
}
return (\@ordered_style_keys);
}
#--------------------------------------------------------------------
sub _load_displayConfig {
#--------------------------------------------------------------------
# This subroutine parses a markup displayConfig file that can be user defined.
# The displayConfig file contains information on the rendering and linking of
# the fields within an HTML table. This information is used in the
# _table_html_markup function.
# The displayConfig file is expected as a test file containing records in the
# following format:
#
# # comment line
# colname = name of the column from feature info file
# display = how the colname should be displayed in the seach box
# bgcolor = background color of table cell
# color = text color
# url = a URL to use for building a link with the column content
# image = an image that replaces the cell content
# url_append = One value that gets appended to the URL (or self, see below)
# url_replace = what value/text should be put into the placeholder
# positions in the URL string. The values have to be '|' delimited.
# The placeholders have to be three uppercase letters,
# starting with Z and descending (ZZZ, YYY, XXX, WWW etc.)
# There can be multiple replacement values for each URL.
# They will be put in in the given order.
# A special replacement value is 'self', which is the column value.
# url_target = target window for the URL to be build.
# font = desired font for display
# //
#
my $self = shift;
my (%tmphash, @sections, $counter, $displayConfig);
$displayConfig = $self->_displayConfig;
if (!$displayConfig) {
# if no displayConfig file is defined use the keys that are in the feature table
my @feature_keys = $self->_dataset->getFeatureKeys;
foreach (@feature_keys ) {
$self->{'style'}->{ $_ }->{bgcolor} = "white";
# and display them accordingly
$self->{'style'}->{ $_ }->{'display'} = $_;
}
return;
}
# read in the displayConfig file
open (IN, $displayConfig) || die("Could not open displayConfig $displayConfig: ", $!, "!");
$/ = '//'; # temporarily set the line end delimiter to '//'.
# This is the divider between sections in our file
@sections = ();
close(IN);
$/ = "\n"; # set the line end delimiter back to newline
$counter = 0; # counter, because we need to store the order in which
# the markup is encountered in the displayConfig file
# get rid of characters, lines not needed, for each key
foreach (@sections) {
my @rows = split(/\n/, $_);
foreach (@rows){
chomp; # no newline
s/#.*//; # no comment
s/\/\///; # no record delimiter
s/^\s+//; # no leading white
s/\s+$//; # no trailing white
# s/ //g;
next unless length; # anything left?
my ($var, $value) = split(/\s*=\s*/, $_, 2);
# print '_var_', $var, '_value_', $value, br;
$tmphash{$var} = $value;
}
my $colname = $tmphash{'colname'};
delete $tmphash{'colname'}; # remove this key/value pair from hash
foreach (keys %tmphash) {
$self->{'style'}->{ $colname }->{$_} = $tmphash{$_};
}
# if display was not specified, use column name
if (!exists $self->{'style'}->{ $colname }->{'display'}){
$self->{'style'}->{ $colname }->{'display'} = $colname;
}
# store the number at which this columname occured in the file
# this will later determine the output order
$self->{'style'}->{ $colname }->{'order'} = $counter++;
undef %tmphash;
}
}
#--------------------------------------------------------------------
sub _hide_params {
#--------------------------------------------------------------------
# This sub will hide the parameters form the url
my $self = shift;
my $hidden_fields;
$hidden_fields = hidden({'-name' => 'rx', -value => $self->_radar_x });
$hidden_fields .= hidden({'-name' => 'ry', -value => $self->_radar_y });
$hidden_fields .= hidden({'-name' => 'zx', -value => $self->_zoom_x });
$hidden_fields .= hidden({'-name' => 'zy', -value => $self->_zoom_y });
$hidden_fields .= hidden({'-name' => 'n', -value => $self->_name });
return $hidden_fields;
}
#----------------------------------------------------------------------
sub _get_search_hits {
#----------------------------------------------------------------------
# This subroutine retrives the hits based on the search query entered in
# the searchbox.
# \@hits = _get_search_hits (\@query, $field)
#
# where - @query contains the words used for searching, they will
# be combined by logical 'AND'
# - $field selected field for search
my $self = shift;
my ($query, $field) = @_;
my (@hit_bitvec, $resultvec, @hits, @query);
# if the user submitted a multi word query we search each element separately
# here we extract the space separated words from the query parameter field
@query = @$query;
for (my $i=0; $i <= $#query; $i++){ # for each query word
# initialize a bitvector (one bit for each gene in dataset) by setting the last bit
vec( $hit_bitvec[$i], $self->_dataset->height + 1, 1) = 1;
# dataset->search() returns an index of features that matched the search term
@hits = $self->_dataset->search($query[$i], $field);
for(my $j=0; $j <= $#hits; $j++){
# set the nth bit in the vector to store the hit information
vec($hit_bitvec[$i], $hits[$j], 1) = 1;
}
undef (@hits);
}
# initialize the result bitvector with the bitvector for the first search term
$resultvec = $hit_bitvec[0];
# logically AND all consecutive bitvectors
for (my $k=1; $k <= $#hit_bitvec; $k++) {
$resultvec = $resultvec & $hit_bitvec[$k];
}
# read the resultvector back out into an array
for (my $l=0; $l <= $self->_dataset->height; $l++) {
if (vec($resultvec,$l,1)) {
push @hits, $l;
}
}
return \@hits;
}
#--------------------------------------------------------------------
sub _contrast_legend {
#--------------------------------------------------------------------
# This sub will display the appropriate scalebar. Once is created, it
# is reused.
# The scale is made as a table with 2 rows; one row contains the values,
# the other the corresponding images (pixels).
my $self = shift;
return if (defined $self->{'scale'});
my ($scaleImage, $table, $width, $height, @pixelnames, @pixels,
$pixels, $step, $colScheme, @rows, @bla, $contrast);
# define a few constants and load the scale image
$contrast = $self->_dataset->contrast;
$colScheme = $self->_dataset->colorScheme;
$scaleImage = $self->_load_image($self->_commonImageDir.'scale_'.$colScheme.'.'.$self->_imgType);
($width, $height) = $scaleImage->getBounds();
# break up the image into pixels
for (my $i = 0; $i < $width; $i++){
$pixels[$i] = GD::Image->new(1, 1);
$pixels[$i]->copy($scaleImage, 0, 0, $i, 0, 1, 1);
push (@pixelnames, 'pixel'.$colScheme.$i);
}
# save the pixels and get their path in $pixels
$pixels = $self->_save_img_files(\@pixelnames, \@pixels);
# find out the fold change numbers
$step = $contrast / 4;
$rows[0] = " 1:1 "; # this is the center - no change
for (my $i = $step; $i <= $contrast; $i += $step) {
push @rows, " > ".sprintf("%2.1f", 2**$i)." "; # * step increase
unshift @rows, " < 1/".sprintf("%2d", 2**$i)." "; # 1/step decrease
}
# format the output as a table
for (my $i = 0; $i <= $#rows; $i++) {
push (@bla, td({-cellpadding => '5',
-height => '14',
-width => '50',
-bgcolor => 'white',
-border => 3,
-align => 'center'},
font({-face => 'verdana,arial,sans-serif',
-size => '1'}
),
$rows[$i]
)."\n"
);
$$pixels[$i] = td({-background => $self->_tmpURL.$$pixels[$i],
-height => '15'}
)."\n";
}
$table = table({-border => '0',
-cellspacing => '0',
-cellpadding => '0'},
Tr(
b(@bla)
), "\n",
Tr(
@$pixels
), "\n"
)."\n";
$self->{'scale'} = $table;
}
#--------------------------------------------------------------------
sub _load_image {
#--------------------------------------------------------------------
# this sub will load an image, file name should contain the full path
my ($self, $filename) = @_;
open(IN, $filename) || die "cannot open image within explorer: $filename! $!\n";
my $image = ($self->_imgType eq 'gif') ? GD::Image->newFromGif(*IN) : GD::Image->new(*IN);
return $image;
}
#--------------------------------------------------------------------
sub _dataset {
#--------------------------------------------------------------------
my $self = shift;
return $self->{dataset};
}
#----------------------------------------------------------------------
sub _name {
#----------------------------------------------------------------------
# Returns the name of the dataset.
my $self = shift;
return $self->_dataset->name();
}
#--------------------------------------------------------------------
sub _get_path {
#--------------------------------------------------------------------
# Returns the URL of the script. (Removes everything after the last '/')
my $url = shift;
$url =~ /(.*\/).+$/;
return $1;
}
#--------------------------------------------------------------------
sub _path_remove {
#--------------------------------------------------------------------
# Removes the path info (everything up to the last '/')
my $file = shift;
my $position = rindex $file, "/";
my $name = substr $file, $position + 1;
return $name;
}
#--------------------------------------------------------------------
sub _untaint {
#--------------------------------------------------------------------
# Removes shell characters from a string.
my $value = shift;
$value =~ s/[!@\$%\^&\*\(\)?#|~]//g;
return $value;
}
#----------------------------------------------------------------------
sub _message {
#----------------------------------------------------------------------
# a small sub to return marked up message for display
my $self = shift;
my $message = shift;
$message = table({-cellpadding => 3},
Tr(
td({-colspan => '2',
-height => '20',
-bgcolor => 'beige'},
font({-face => 'arial,sans-serif',
-size => 3}, b($message)
), "\n"
), "\n"
), "\n"
)."\n";
return $message;
}
#--------------------------------------------------------------------
sub _js_bracket {
#--------------------------------------------------------------------
# This sub creates the javascript to display the bracket in the radar
# frame at the position where the image was clicked on. The bracket is
# displayed on the right edge of the window.
my $self = shift;
my $window_y = shift; # the distance between the bars (bracket in pixels
my $height = shift; # heigth of the image in pixels
my $offset = 3 * $self->_radar_y;
my $js = <<"EOM";
EOM
my $style = << "EOS"; # postition the various elements in the frame
EOS
return $js, $style;
}
1; # return a true value to make perl happy
__END__
#####################################################################
#
# POD Documentation from here on down
#
#####################################################################
=pod
=head1 Name
Microarray::Explorer - class for viewing clustered expression data over the web
=head1 Abstract
The Explorer class belongs to the package Microarray. It functions as
a web viewer for dataset objects from the same package. Following the
Model/View/Controller concept, the explorer class is the
View/Controller, whereas the dataset class is the Model.
=head2 Intended Behaviour
Explorer will display the dataset object in a web browser in a
frameset of 3 frames.
The frames are: - radar frame - left side of the window
- toolbar frame - top of right side of window
- zoom frame - lower part of right side of window
=head3 Radar frame
The radar frame displays the whole dataset as an image map. The genes
(clones, ...) are shown are the rows of the image, while the
experiments are shown as the columns. Clicking the image will have 2
effects:
- the expression patterns for the next XXX genes starting at
position of the click are displayed in the zoom frame
- the top of a small bracket on the right side of the radar
frame is positioned at the height of the click and the bracket
shows the XXX genes selected and magnified on the zoom frame.
The size of the image is maximized both horizontally and vertically.
=head3 Toolbar frame
The toolbar allows actions on either the radar frame or the zoom
frame.
For the radar frame it allows a simple customization: changing the
width of the radar image. Selecting any of the allowed percentages
changes the current width of the image proprotionately.
For the zoom frame, it provides a search tool. The various fields of
annotations or all of them for the genes can be searched for a
string. The string can be entered in a text field. The string may
contain more than one terms, spaces are interpreted as term
separators. The terms are conbined using logical 'AND'. The hits
resulting from the search are displayed in the zoom frame, as
expression patterns. The number of hits displayed in the zoom window
is limited to 200 hits and the length of each term in te search string
should be at least 2 characters long.
In addition, the toolbar frame contains an Info Box that displays
various textual information dependent on the position of the mouse
pointer over the zoom frame. It can display:
- gene information in the NAME field when the mouse is positioned
over an image row
- experiment info, if positioned above the experiment image map
- correlation of a genes expression pattern to that of the top
gene in to zoom window displaying correlations, when the pointer
is above is above the correlation image.
=head3 Zoom frame
The zoom frame displays expression patterns and annotations for genes
in the dataset. It can display genes selected from:
- radar frame; it displays a given number of genes starting from the
genes whose image was clicked on
- the toolbar; the result of the search performed using the searchtool
is displayed here
- the zoom frame itself; when a row is clicked on in the radar frame,
the genes with the highest correlation in their expression pattern
are displayed
Information about various elements in the zoom frame is displayed in
the Info Box in the toolbar, dependent on the position of the mouse:
- experiment info
- gene info
- correlation value
=head1 Class Methods
=head2 New
This is the constructor for the Explorer class.
Usage: $explObj = Microarray::Explorer->New( dataset => $dataset,
config => $config,
displayConfig => $displayConfig};
Here $dataset should be a Microarray::CdtDataset object, $config is a
Microarray::Config object, while $displayConfig is the name of a text
file that contains info concerning how to mark up various elements of
gene annotations. It is expected that the displayConfig will be found
in the $config->gxDataPath directory.
=head1 Instance Methods
=head2 print_frameset
This subroutine will print the whole display window anew.
usage: $explorer->print_frameset();
=head2 radar
This subroutine creates the content of the radar frame.
usage: $explorer->radar();
=head2 toolbar
This sub creates the toolbar frame
usage: $explorer->toolbar();
=head2 zoom
This subroutine creates the content of the zoom frame when called from
the radar frame.
usage: $explorer->zoom();
=head2 search_feature
This method takes a a string of query words and the name of the
feature field to search. This way, one can search for gene symbols,
gene names and/or GenBank accession numbers or any or all fields in
the feature description.
usage: $explorer->search_feature("myWords", $field);
"myWords" - can have multiple words separated by space
(they are combined using logical 'AND')
$field - has to be one value of %kDespcriptions
The resulting hits are displayed in the zoom frame. If the number of
hits is too high, only kNumberOfMaxHits are displayed. The words in
the query string have to be separated by spaces and the length of the
words has to be more than 1 character.
=head2 neighbors
This method generates the html and images to display the most
correlated gene expression vectors versus a seed vector. When a gene
is clicked on in the zoom window, the most highly correlated gens are
retrieved and their expression patterns are displayed in the zoom
frame.
usage: $explorer->neighbors();
The gene that was clicked on in the zoom frame is determined from the
cgi parameter 'seed'. The 'seed' gene is displayed as the first row on
the zoom image, with a perfect correlation of 1. The genes with the
highest correlations are displayed in the order of the value of their
correlation. All genes with correlation > 0.5 are displayed. The
correlation value is graphically indicated by the length of the orange
bar positioned on the right side of the expression pattern of the
gene, and the value can be displayed in the info bos by positioning
the mouse above the orange bar of a gene of interest.
=head1 Authors
Original work: Christian Rees
Re-write: Janos Demeter
jdemeter@genome.stanford.edu
=cut