24 February 2006

UCSC Genome Browser + SVG

I wrote a PHP script to display tracks from the UCSC Genome Browser using SVG and the public mysql connection to their database. As Firefox now supports the SVG format, this drawing can be displayed in your web browser.




01ucsc2svgInkscape
Pictures can be exported as a SVG file and edited with a SVG tool such as Inkscape or Adobe Illustrator


02ucsc2svgInkscape
SVG is a vectorial format: Vector graphics editors allow to rotate, move, mirror, stretch, skew, generally perform affine transformations of objects, change z-order and combine the primitives into more complex objects.


Updated 2010-08-12: source code

<?php

/*

author:

- Pierre Lindenbaum PhD plindenbaum (at) yahoo (dot) fr http://www.integragen.com

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
``Software''), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

The name of the authors when specified in the source files shall be
kept unmodified.

THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL 4XT.ORG BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.


$Id: $
$Author: $
$Revision: $
$Date: $
$Locker: $
$RCSfile: $
$Source: $
$State: $
$Name: $
$Log: $


*************************************************************************/

$sqllimitnumber="50";
$sqllimit=" limit ".$sqllimitnumber;
$fontsize="14";
$chrs= array(
"chr1","chr2","chr3","chr4","chr5","chr6","chr7","chr8","chr9","chr10",
"chr11","chr12","chr13","chr14","chr15","chr16","chr17","chr18","chr19","chr20",
"chr21","chr22","chrX","chrY"
);
$tables= array(
"knownGene"=>"full",
"all_mrna"=>"packed",
"refGene"=>"full",
"bacEndPairs"=>"full",
"fishClones"=>"full",
"stsMap"=>"full",
"snp"=>"packed"
);

$viewAs= array("full","packed","hide");

/**
*
* Item an item on the genome
*
*/
class Item
{
var $name;
var $track;
//constructor
function Item(&$track)
{
$this->track=$track;
}

//get 5' bound
function getStart()
{
return -1;
}

//get 3' bound
function getEnd()
{
return -1;
}

//returns wether 2 Item overlap
function overlap($other)
{
return (!($this->getEnd()<$other->getStart() || $other->getEnd()<$this->getStart()));
}

//returns wether 2 Item overlap on screen
function overlapOnScreen($other)
{
$start1= $this->base2pixel(min($this->getStart(),$this->getEnd()));
$end1= 1+$this->base2pixel(max($this->getStart(),$this->getEnd()));

$start2= $other->base2pixel(min($other->getStart(),$other->getEnd()));
$end2= 1+$other->base2pixel(max($other->getStart(),$other->getEnd()));

return (!($end1 < $start2 || $end2 < $start1));
}

//return a URL for this item
function getURL()
{
return "http://www.ncbi.nlm.nih.gov/gquery/gquery.fcgi?term=".htmlentities($this->name);
}

//convert a base position to the screen
function base2pixel($base)
{
$left= $this->track->browser->leftMarginWidth();
$screenw= $this->track->browser->genomeWidth();
return $left+$screenw* (($base - $this->track->browser->start)/($this->track->browser->end - $this->track->browser->start));
}


//force a pixel to be in the drawing area
function trimPixel($pix)
{
$pix= min($this->track->browser->getWidth(),$pix);
$pix= max($this->track->browser->leftMarginWidth(),$pix);
return $pix;
}

//return the height of this item on the screen
function getHeight()
{
return $this->track->browser->featureHeight;
}

//writes symbol for orientation
function writeStrand($out,$pixx1,$pixx2,$midy)
{
if($this->strand=="?") return;
//write orientation
for($i=$pixx1; $i<= $pixx2;$i+=10)
{
fwrite($out,"<svg:use x='".$i."' y='".$midy."' xlink:href='#".($this->strand=='-'?"minus":"plus")."'/>");
}
}

//write a svg:a link containg $svg
function writeAnchor($out,$svg)
{
fwrite($out,"<svg:a xlink:href='".$this->getURL()."' xlink:title='".htmlentities($this->name)."'>".
$svg."</svg:a>"
);
}
}

/**
*
* SimpleItem such as knownGene
*
*/
class SimpleItem extends Item
{
var $chromStart;
var $chromEnd;
var $strand;

//constructor
function SimpleItem(&$track)
{
parent::Item( $track);
$this->strand="?";
}

//get 5' bound
function getStart()
{
return $this->chromStart;
}

//get 3' bound
function getEnd()
{
return $this->chromEnd;
}

//write as SVG
function toSVG($out,$y,$isPacked=TRUE)
{
fwrite($out,"<svg:g>\n");

if($isPacked==FALSE)
{
$this->writeAnchor($out,"<svg:text x='".($this->track->browser->trackMarginWidth()+($this->track->browser->itemMarginWidth()/2.0))."' y='".($y+$this->getHeight()-$GLOBALS["fontsize"]/2.0)."' text-anchor='middle'>".htmlentities($this->name)."</svg:text>");
}


$pixx1= $this->trimPixel($this->base2pixel($this->chromStart));
$pixx2= $this->trimPixel($this->base2pixel($this->chromEnd));
$midy= ($y+$this->getHeight()/2.0);

//write orientation
$this->writeStrand($out,$pixx1,$pixx2,$midy);



$h2= $this->getHeight()/5.0;


$svg = "<svg:rect x='".$pixx1."' y='".($midy-$h2)."' width='".($pixx2-$pixx1)."' height='".($h2*2)."' style='stroke:red;fill:url(#metal);'/>";

if($isPacked)
{
$this->writeAnchor($out,$svg);
}
else
{
fwrite($out,$svg);
}

fwrite($out,"</svg:g>");
}
}


/**
*
* BEDItem such as knownGene
*
*/
class BEDItem extends Item
{
var $txStart;
var $txEnd;
var $cdsStart;
var $cdsEnd;
var $exonCount;
var $exonStarts;
var $exonEnds;

//constructor
function BEDItem(&$track)
{
parent::Item($track);
}

//5' bound
function getStart()
{
return $this->txStart;
}

//3' bound
function getEnd()
{
return $this->txEnd;
}

//write as SVG
function toSVG($out,$y,$isPacked=TRUE)
{
fwrite($out,"<svg:g>");
if($isPacked==FALSE)
{
$this->writeAnchor($out,"<svg:text x='".($this->track->browser->trackMarginWidth()+($this->track->browser->itemMarginWidth()/2.0))."' y='".($y+$this->getHeight()-$GLOBALS["fontsize"]/2.0)."' text-anchor='middle'>".htmlentities($this->name)."</svg:text>"
);
}

$pixx1= $this->trimPixel($this->base2pixel($this->txStart));
$pixx2= $this->trimPixel($this->base2pixel($this->txEnd));
$midy= ($y+$this->getHeight()/2.0);

$this->writeStrand($out,$pixx1,$pixx2,$midy);

//write gene
fwrite($out,"<svg:line x1='".$pixx1."' y1='".$midy."' x2='".$pixx2."' y2='".$midy."' stroke='black'/>");

$pixx1= $this->trimPixel($this->base2pixel($this->cdsStart));
$pixx2= $this->trimPixel($this->base2pixel($this->cdsEnd));

//write mRNA
$h2= $this->getHeight()/17.0;
fwrite($out,"<svg:rect x='".$pixx1."' y='".($midy-$h2)."' width='".($pixx2-$pixx1)."' height='".($h2*2)."' stroke='black' fill='blue'/>");


//write translation
$h2= $this->getHeight()/6.0;
for($i=0;$i< $this->exonCount;++$i)
{
$pixx1= $this->trimPixel($this->base2pixel($this->exonStarts[$i]));
$pixx2= $this->trimPixel($this->base2pixel($this->exonEnds[$i]));
$svg="<svg:rect x='".$pixx1."' y='".($midy-$h2)."' width='".($pixx2-$pixx1)."' height='".($h2*2)."' style='stroke:red;fill:url(#metal);'/>";
if($isPacked==FALSE)
{
fwrite($out,$svg);
}
else
{
$this->writeAnchor($out,$svg);
}
}

fwrite($out,"</svg:g>");
}


}


/**
*
* AlignItem such as all_mrna
*
*/
class AlignItem extends Item
{
var $tStart;
var $tEnd;
var $blockCount;
var $blockSizes;
var $tStarts;

//constructor
function AlignItem(&$track)
{
parent::Item($track);
}

//5' bound
function getStart()
{
return $this->tStart;
}

//3' bound
function getEnd()
{
return $this->tEnd;
}

//write as SVG
function toSVG($out,$y,$isPacked=TRUE)
{
fwrite($out,"<svg:g>");
if($isPacked==FALSE)
{
$this->writeAnchor($out,"<svg:text x='".($this->track->browser->trackMarginWidth()+($this->track->browser->itemMarginWidth()/2.0))."' y='".($y+$this->getHeight()-$GLOBALS["fontsize"]/2.0)."' text-anchor='middle'>".htmlentities($this->name)."</svg:text>"
);
}

$pixx1= $this->trimPixel($this->base2pixel($this->getStart()));
$pixx2= $this->trimPixel($this->base2pixel($this->getEnd()));
$midy= ($y+$this->getHeight()/2.0);

$this->writeStrand($out,$pixx1,$pixx2,$midy);

//write gene
fwrite($out,"<svg:line x1='".$pixx1."' y1='".$midy."' x2='".$pixx2."' y2='".$midy."' stroke='black'/>");

//write blocks
$h2= $this->getHeight()/6.0;
for($i=0;$i< $this->blockCount;++$i)
{
$pixx1= $this->trimPixel($this->base2pixel($this->tStarts[$i]));
$pixx2= $this->trimPixel($this->base2pixel($this->tStarts[$i]+$this->blockSizes[$i]));
$svg="<svg:rect x='".$pixx1."' y='".($midy-$h2)."' width='".($pixx2-$pixx1)."' height='".($h2*2)."' style='stroke:red;fill:url(#metal);'/>";
if($isPacked==FALSE)
{
fwrite($out,$svg);
}
else
{
$this->writeAnchor($out,$svg);
}
}

fwrite($out,"</svg:g>");
}

}


/**
*
* class Track
* a track is a vector of Item
*
*/
class Track
{
var $name;
var $url;
var $browser;
var $items;
var $index2row;
var $nRows;

//constructor
function Track(&$browser,$name,$url)
{
$this->name = $name;
$this->url = $url;
$this->browser= $browser;
$this->items = array();
$this->index2row = null;
$this->nRows=-1;
}

//number of items
function getItemCount()
{
return count($this->items);
}

//add a new item in the track
function add($item)
{
$this->items[ $this->getItemCount() ] = $item;
$this->nRows=-1;
}

//return width on screen
function getWidth()
{
return $this->browser->getWidth();
}


//return height on screen
function getHeight()
{
if($this->isPacked())
{
return $this->nRows* $this->browser->featureHeight;
}
else
{
$h=0;
foreach($this->items as $K=>$V)
{
$h += $V->getHeight();
}
return $h;
}
}

//return wether is track has been packed
function isPacked()
{
return $this->nRows!=-1;
}

//pack this track
function packTrack()
{
$count= $this->getItemCount();
$this->index2row= array();
$this->nRows=1;
$this->index2row[0]=0;

for($i=1;$i< $count;$i++)
{
$itemi = $this->items[$i];
$choosenRow=0;
$done=FALSE;
while($done==FALSE)
{
$done=TRUE;
for($j=0;$j<$i;$j++)
{
if($this->index2row[$j]!=$choosenRow) continue;
$itemj = $this->items[$j];
if($itemi->overlapOnScreen($itemj)==TRUE)
{
$choosenRow++;
$done=FALSE;
break;
}
}
if($choosenRow>=$this->nRows)
{
$this->nRows++;
break;
}
}
$this->index2row[$i]=$choosenRow;
}
}


//write as SVG
function toSVG($out,$y)
{
$width = $this->getWidth();


fwrite($out,"<svg:g id=\"".$this->name."\">\n");
//track name
fwrite($out, "<svg:g>".
"<svg:a xlink:href='".$this->url."' xlink:title='".$this->name."'>".
"<svg:rect x='0' y='".$y."' width='".($this->browser->trackMarginWidth()).
"' height='".$this->getHeight()."' fill='rgb(240,240,255)'/>".
"</svg:a>".
"<svg:text x='0' y='0' text-anchor='middle' transform='translate(".(($this->browser->trackMarginWidth())/2.0).",".($y+$GLOBALS["fontsize"]/2.0+($this->getHeight())/2.0).") rotate(0)'>".$this->name."</svg:text>".
"</svg:g>\n"
);
fwrite($out,"<svg:rect x='".($this->browser->trackMarginWidth())."' y='".$y."' width='".($this->browser->itemMarginWidth()).
"' height='".$this->getHeight()."' fill='rgb(200,200,255)'/>\n");

if($this->isPacked()==TRUE)
{
$count= $this->getItemCount();
for($i=0;$i< $this->nRows;$i++)
{
for($j=0;$j<$count;$j++)
{
if($this->index2row[$j]!=$i) continue;
$this->items[$j]->toSVG($out,$y,TRUE);
}
$y+=$this->browser->featureHeight;
}

}
else
{
foreach($this->items as $K=>$V)
{
$V->toSVG($out,$y,FALSE);
$y+=$this->browser->featureHeight;
}
}
fwrite($out,"</svg:g>");
}
}

/**************
*
* Browser
* a browser is a vector of tracks
*
*/
class Browser
{
var $build;
var $chrom;
var $tracks;
var $start;
var $end;
var $featureHeight;

//constructor
function Browser($build,$chrom,$start,$end)
{
$this->build = $buid;
$this->chrom = $chrom;
$this->start = $start;
$this->end = $end;
$this->featureHeight=24;
$this->tracks=array();
}

//number of tracks
function getTrackCount()
{
return count( $this->tracks);
}

//add a new track
function add(&$track)
{
$this->tracks[ $this->getTrackCount() ]=& $track;
}

//width on screen
function getWidth()
{
return 800;
}

//height on screen
function getHeight()
{
$h=0;
foreach($this->tracks as $key=>$value)
{
$h+= $value->getHeight();
}
return $h;
}

//margin width for labels
function itemMarginWidth()
{
return $this->getWidth()/6.0;
}

//margin width for track
function trackMarginWidth()
{
return $this->getWidth()/6.0;
}

//left margin width = sum(item+track)
function leftMarginWidth()
{
return $this->trackMarginWidth()+$this->itemMarginWidth();
}

//drawing area
function genomeWidth()
{
return $this->getWidth()-$this->leftMarginWidth();
}

//pack the named track
function packTrack($name)
{
for($i=0;$i< count($this->tracks);$i++)
{
if($this->tracks[$i]->name==$name)
{
$this->tracks[$i]->packTrack();
}
}
}

//write as SVG
function toSVG($out)
{

if($out==NULL)
{
$out=fopen("php://output","w")or die ("stdout?");
}
$height=$this->getHeight();
$fh2 = ($this->featureHeight)/5.0;

fwrite($out, "<svg:svg xmlns:svg='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='".$this->getWidth()."' height='".$height."' stroke='black' stroke-width='0.5' font-size='".$GLOBALS["fontsize"]."'>".
"<svg:title>".$this->chrom.":".$this->start."-".$this->end."</svg:title>".
"<svg:desc>Genome Browser with SVG Pierre Lindenbaum plindenbaum (@) yahoo (dot) fr </svg:desc>".
"<svg:defs>".
"<svg:polyline id='plus' stroke='black' fill='none' points='".
"-".$fh2.",-".$fh2." ".
" 0,0 ".
"-".$fh2.",".$fh2."'/>".
"<svg:polyline id='minus' stroke='black' fill='none' points='".
"".$fh2.",-".$fh2." ".
" 0,0 ".
"".$fh2.",".$fh2."'/>".
"<svg:linearGradient x1='0%' y1='0%' x2='0%' y2='100%' id='metal'>\n".
"<svg:stop offset=\"5%\" stop-color=\"black\"/>\n".
"<svg:stop offset=\"50%\" stop-color=\"whitesmoke\"/>\n".
"<svg:stop offset=\"95%\" stop-color=\"black\"/>\n".
"</svg:linearGradient>\n".
"</svg:defs>"
);
fwrite($out,"<svg:rect x='0' y='0' ".
"width='".$this->getWidth()."' height='".$height."' stroke='blue' fill='white' ".
"/>"
);

//write vertical bar
fwrite($out,"<svg:g>");
for($i=1;$i<10;$i++)
{
$x= $this->leftMarginWidth()+($this->genomeWidth()/10.0)*$i;
fwrite($out,"<svg:line x1='".$x."' y1='0' x2='".$x."' y2='".$height."' stroke='blue' />");
}
fwrite($out,"</svg:g>");
$y=0;

foreach($this->tracks as $key=>$value)
{
$value->toSVG($out,$y);
$y+= $value->getHeight();
}
fwrite($out,"<svg:rect x='0' y='0' ".
"width='".$this->getWidth()."' height='".$height."' stroke='blue' fill='none' ".
"/>"
);
fwrite($out, "</svg:svg>");
}

}

/** return a POST parameter, convenient method that can be changed to __GET */
function getParameter($s)
{
return $_POST[$s];
}

/* performs a query in gene tracks */
function doGeneQuery($con,&$browser,$build,$trackname)
{
$prompt="select ".
"name,strand,txStart,txEnd,cdsStart,cdsEnd,exonCount,exonStarts,exonEnds".
" from ".
" ".$build.".".$trackname.
" where ".
" chrom=\"".mysql_escape_string($browser->chrom) ."\" and not(".
" txEnd < \"".mysql_escape_string($browser->start)."\" or \"".
mysql_escape_string($browser->end)."\" < txStart ".
") ".$GLOBALS["sqllimit"];
//echo "<!-- ".$prompt." -->";
$result = mysql_query($prompt,$con);
if(!$result)
{
echo "<!-- Bad query ".$prompt." -->";
}
else
{
$track= new Track($browser,$trackname,"http://www.genome.ucsc.edu/cgi-bin/hgTrackUi?g=".$trackname);
while($row=mysql_fetch_array($result))
{
$item= new BEDItem(&$track);
$item->name= $row[0];
$item->strand= $row[1];
$item->txStart = $row[2];
$item->txEnd = $row[3];
$item->cdsStart = $row[4];
$item->cdsEnd = $row[5];
$item->exonCount= $row[6];
$item->exonStarts= split(",",$row[7]);
$item->exonEnds= split(",",$row[8]);
$track->add( $item);
}
$browser->add(& $track);

}
}

/* performs a generic query */
function doSimpleQuery($con,&$browser,$build,$trackname,$N,$T,$C,$S,$E)
{
$prompt="select ".
"$N,".($T==NULL?"\"?\"":$T).",$S,$E ".
" from ".
" ".$build.".".$trackname.
" where ".
" $C=\"".mysql_escape_string($browser->chrom) ."\" and not(".
" $E < \"".mysql_escape_string($browser->start)."\" or \"".
mysql_escape_string($browser->end)."\" < $S ".
") ".$GLOBALS["sqllimit"];
//echo "<!-- ".$prompt." -->";
$result = mysql_query($prompt,$con);
if(!$result)
{
echo "<!-- Bad query ".$prompt." -->";
}
else
{
//echo "<!-- query ".$prompt." -->";
$track= new Track($browser,$trackname,"http://www.genome.ucsc.edu/cgi-bin/hgTrackUi?g=".$trackname);
while($row=mysql_fetch_array($result))
{
$item= new SimpleItem(&$track);
$item->name= $row[0];
$item->strand= ($T==NULL?"?":$row[1]);
$item->chromStart = $row[2];
$item->chromEnd = $row[3];
$track->add( $item);
}
$browser->add(& $track);
}
}

/* performs a alignment query */
function doAlignQuery($con,&$browser,$build,$trackname,$N,$T,$C,$S,$E,$BC,$BS,$BL)
{
$prompt="select ".
"$N,".($T==NULL?"\"?\"":$T).",$S,$E,$BC,$BS,$BL".
" from ".
" ".$build.".".$trackname.
" where ".
" $C=\"".mysql_escape_string($browser->chrom) ."\" and not(".
" $E < \"".mysql_escape_string($browser->start)."\" or \"".
mysql_escape_string($browser->end)."\" < $E ".
") ".$GLOBALS["sqllimit"];
//echo "<!-- ".$prompt." -->";
$result = mysql_query($prompt,$con);
if(!$result)
{
echo "<!-- Bad query ".$prompt." -->";
}
else
{
$track= new Track($browser,$trackname,"http://www.genome.ucsc.edu/cgi-bin/hgTrackUi?g=".$trackname);
while($row=mysql_fetch_array($result))
{
$item= new AlignItem(&$track);
$item->name= $row[0];
$item->strand= $row[1];
$item->tStart = $row[2];
$item->tEnd = $row[3];
$item->blockCount= $row[4];
$item->tStarts= split(",",$row[5]);
$item->blockSizes= split(",",$row[6]);
$track->add( $item);
}
$browser->add(& $track);

}
}

$svgonly= getParameter("svgonly");
//$build= getParameter("build"); you can un-comment this
if(!isset($build)) $build="hg17";
if(isset($build)) $build=trim($build);
$chrom= getParameter("chrom");
if(isset($chrom)) $chrom=trim($chrom);
$start= getParameter("start");
if(isset($start)) $start=intval($start);
$end= getParameter("end");
if(isset($end)) $end=intval($end);

if(
isset($chrom) &&
isset($start) &&
isset($end) &&
$start<$end
)
{
$title=$chrom.":".$start."-".$end;
}
else
{
$title=NULL;
}

if($title==NULL || isset($svgonly)==FALSE)
{
header("Content-type: application/xhtml+xml");
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<link rel="alternate" type="application/rss+xml" href="../rss/rss.txt"/>
<meta name="dc.description" content="UCSC Genome Browser with SVG"/>
<meta name="dc.keywords" content="UCSC; Genome Browser; goldenpath; SVG; PHP;mozilla; firefox; genomics; bioinformatics; integragen"/>
<meta name="dc.author" content="Pierre Lindenbaum"/>
<title><?php echo ($title==NULL?"UCSC Genome Browser with SVG":$title); ?></title>
</head>
<body>
<h1>Displaying Data from the UCSC Genome Browser With SVG</h1>
<h4>Pierre Lindenbaum PhD, 2006</h4>
<div align="center" style="font-size:9pt; background-color:#DDDDDD;border-color:black; border-width:1px; ; border-style:solid;"><br/>
<?php
if(isset($sqllimitnumber))
{
echo "<br/>For security reason, the number of items per track is limited to <b style='color:red;'>$sqllimitnumber</b>.<br/><br/>";
}
?>
<form method="POST">
<input type="hidden" name="build" value="hg17" />
Assembly:<span style='border-color:gray; border-width:1px; ; border-style:dashed;background-color:white;'><?php echo $build; ?></span>
<label for="chrom">Chromosome:</label><select name="chrom" id="chrom">
<?php

echo "<input type=\"hidden\" name=\"_rand\" value=\"".time()."\"/>";


foreach($chrs as $K)
{
echo "<option value=\"".$K."\"";
if(isset($chrom) && $K==$chrom) echo " selected=\"true\" style='background-color:#EE00EE'";
echo ">".$K."</option>";
}
?>
</select>
<label for="start">Start:</label><input type="text" id="start" name="start" value="<?php echo (isset($start)?$start:910000); ?>"/>
<label for="end">End:</label><input type="text" id="end" name="end" value="<?php echo (isset($end)?$end:930000); ?>"/>
<?php
echo "<br/><table><tr>";
foreach($tables as $T=>$D)
{
$v=getParameter($T);
if(isset($v) && in_array($v,$viewAs))
{
$D=$v;
$tables[$T]=$D;
}
echo "<th><label for='$T'>$T</label></th><td><select id='$T' name='$T'>";
foreach($viewAs as $V)
{
echo "<option value='$V'";
if($V==$D) echo " selected=\"true\" style='background-color:#EE00EE' ";
echo ">".$V."</option>";
}
echo "</select></td>\n";
}
echo "</tr></table>";
?>
<input type="submit" name="Submit" value='Display as XHTML'/>
<input type="submit" name="svgonly" value='Save As SVG' />
<br/><br/></form>
</div>
<p/>
<div align="center" style="font-size:9pt; background-color:#DDDDFF;border-color:#DDDDDD; border-width:1px; ; border-style:solid;"><br/>
<?php
}
else
{
header("Content-disposition: attachment; filename=\"".$chrom."_".$start."_".$end."\"");
header("Content-type: image/svg+xml");

?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<?php
}

if(
isset($chrom) &&
isset($start) &&
isset($end) &&
$start<$end
)
{
if(isset($svgonly)==FALSE)
{
echo "<a style='font-size:200%;' href='http://www.genome.ucsc.edu/cgi-bin/hgTracks?clade=vertebrate&amp;org=Human&amp;db=$build&amp;position=$chrom%3A$start-$end&amp;pix=1000'>($build)$title</a><br/>";
}
$con = mysql_connect("genome-mysql.cse.ucsc.edu", "genome");
if(!$con)
{
die("Cannot connect :=".mysql_error($con));
}
else
{
$browser= new Browser($build,$chrom,$start,$end);

if($tables["knownGene"]!="hide")
{
doGeneQuery($con,&$browser,$build,"knownGene");
if($tables["knownGene"]=="packed") $browser->packTrack("knownGene");
}
if($tables["refGene"]!="hide")
{
doGeneQuery($con,&$browser,$build,"refGene");
if($tables["refGene"]=="packed") $browser->packTrack("refGene");
}

if($tables["all_mrna"]!="hide")
{
doAlignQuery($con,&$browser,$build,"all_mrna","qName","strand","tName","tStart","tEnd","blockCount","tStarts","blockSizes");
if($tables["all_mrna"]=="packed") $browser->packTrack("all_mrna");
}

if($tables["bacEndPairs"]!="hide")
{
doSimpleQuery($con,&$browser,$build,"bacEndPairs","name","strand","chrom","chromStart","chromEnd");
if($tables["bacEndPairs"]=="packed") $browser->packTrack("bacEndPairs");
}

if($tables["fishClones"]!="hide")
{
doSimpleQuery($con,&$browser,$build,"fishClones","name",NULL,"chrom","chromStart","chromEnd");
if($tables["fishClones"]=="packed") $browser->packTrack("fishClones");
}

if($tables["stsMap"]!="hide")
{
doSimpleQuery($con,&$browser,$build,"stsMap","name",NULL,"chrom","chromStart","chromEnd");
if($tables["stsMap"]=="packed") $browser->packTrack("stsMap");
}
if($tables["snp"]!="hide")
{
doSimpleQuery($con,&$browser,$build,"snp125","name","strand","chrom","chromStart","chromEnd");
if($tables["snp"]=="packed") $browser->packTrack("snp");
}

$browser->toSVG(NULL);

mysql_close($con);
}


}

if($title==NULL || isset($svgonly)==FALSE)
{
if($title==NULL) {
?>

<div align="left">This <a href="http://www.php.net">PHP</a> script display tracks from the <a href="http://www.genome.ucsc.edu/">UCSC Genome Browser</a> using <a href="http://www.w3.org/Graphics/SVG/">SVG</a> and the <a href="http://genome.ucsc.edu/FAQ/FAQdownloads#download29">public mysql connection to their database</a>. As <a
href="http://www.mozilla.org">Firefox</a> <a href="http://developer.mozilla.org/en/docs/SVG_in_Firefox_1.5"> now supports the SVG</a> format, this drawing can be displayed in your
web browser.</div>

<br/>
<p style="font-size:200%;"><u>Must</u> be viewed with <u><b>Firefox 1.5 or higher</b></u> : <a href="http://www.spreadfirefox.com/?q=affiliates&amp;id=0&amp;t=45"><img alt="Get Firefox!" title="Get Firefox!" src="http://sfx-images.mozilla.org/affiliates/Buttons/80x15/blue_1.gif" border="0"/></a></p>
<br/>
Pictures can be exported as a SVG file and edited with a SVG tool such as <a href="http://www.inkscape.org/" target="inkscape" >Inkscape</a> or <a target="illustrator" href="http://www.adobe.com/svg/tools.html">Adobe Illustrator</a>
<img src="01ucsc2svgInkscape.jpeg" alt="01ucsc2svgInkscape.jpeg"/><br/>
<br/>
SVG is a <a href="http://en.wikipedia.org/wiki/Vector_graphics">vectorial format</a>: <cite>Vector graphics editors allow to rotate, move, mirror, stretch, skew, generally perform affine transformations of objects, change z-order and combine the primitives into more complex objects.</cite><br/>
<img src="02ucsc2svgInkscape.jpeg" alt="02ucsc2svgInkscape.jpeg"/>
<br/>
<?php

echo "<div align='left'><h3>The PHP Code</h3><pre style='background-color:lightgray'>";
echo htmlspecialchars(file_get_contents("ucsc.php"));
echo "</pre></div>";


}
?>
</div>

<h3>How to cite this code?</h3>
<p>Displaying data from the UCSC GenomeBrowser using SVG: Pierre Lindenbaum 2006. Integragen</p>
<p><a href="http://www.genome.ucsc.edu/">UCSC GenomeBrowser</a>: The UCSC Genome Browser Database: update 2006. Nucleic Acids Res. 2006 Jan 1;34(Database issue):D590-8. PMID: <a href="http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&amp;db=pubmed&amp;dopt=Abstract&amp;list_uids=16381938">16381938</a></p>
<h3>Links</h3>
<ul>
<li><a href="http://www.genome.ucsc.edu/">UCSC GenomeBrowser</a>: The UCSC Genome Browser Database: update 2006. Nucleic Acids Res. 2006 Jan 1;34(Database issue):D590-8. PMID: <a href="http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&amp;db=pubmed&amp;dopt=Abstract&amp;list_uids=16381938">16381938</a></li>
<li><a href="http://www.integragen.com">Integragen</a></li>
<li><a href="http://www.urbigene.com">Home</a></li>
<li><a href="http://plindenbaum.blogspot.com">blog</a></li>
</ul>

<hr/>
<adress>
<a href="http://plindenbaum.blogspot.com">Pierre Lindenbaum PhD</a><br/>
lindenb ( at ) integragen (dot) com<br/>
<a href="http://www.integragen.com">Integragen</a><br/>
4, rue Pierre Fontaine
91000 EVRY.
</adress>
<div align="center"><a href="http://www.integragen.com"><img src="http://www.integragen.com/img//title.png"
border="1" /></a></div>


<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-XXXXX-2";
urchinTracker();
</script>

</body>
</html>
<?php
}

23 February 2006

Bioinformatics.Org announces the laureate of the 2006 Benjamin Franklin Award

http://bioinformatics.org/forums/forum.php?forum_id=4002

"The Benjamin Franklin Award in the Life Sciences is a humanitarian award presented annually by Bioinformatics.Org to an individual who has, in his or her practice, promoted free and open access to the materials and methods used in the life sciences....
...Bioinformatics.Org is proud to present the 2006 Benjamin Franklin Award in the Life Sciences to Michael Ashburner of Cambridge University. As expressed by his nominators, Prof. Ashburner has made fundamental contributions to many open access bioinformatics projects including FlyBase, the GASP project, the Gene Ontology project, and the Open Biological Ontologies project , and he was instrumental in the establishment of the European Bioinformatics Institute. He is also known for advocating open access to biological information.
"

Social Software at nature.com

While I looked at the new members in my LinkedIn network, I discovered that Nature Publishing employed a new "Product development manager for social software at nature.com". Nature has already created Connotea (Hi Timo!) but we may expect new social products from them ?

20 February 2006

20 February 2006 America shuts down

In the fiction "The flu pandemic: were we ready?" written by Declan Butler in Nature it was hypothesised an avian flu epidemy. In this fiction, on February 20th (today), the martial law was declared and the arnachie threatened in front of pharmacies. At this time there is only one duck contaminated by H5N1 which penetrated the French airspace... For the moment.... (I suspect my neighbours to hide a few sample of tamiflu :-)...)

That recalls me this great manga "20th century Boys" written by Naoki Urasawa, prize winner of the "Festival D' Angoulème", in which a human-engineered virus devastated the Earth and where the population was involved in a civil war to get an access to the vaccine.

20th Century Boys


17 February 2006

Bioinformatics Bioinformatique

People working in the field of bioinformatics in France can subscribe to the moderated Bioinfo mailing list. A good source of information for finding events, jobs, etc... related to bioinformatics in France. There is also the French Society of bioinformatics (SFBI) that was created last year but its activity seems morose.

16 February 2006

TreeMap Clustering in SVG with PHP

I've written a PHP script used to display a treemaps cluster using SVG. The code was inspired from [here]. As Firefox now supports the SVG format, the drawing can be displayed in the web browser.


The PHP code is available at http://www.urbigene.com/treemapphp/

SVG PHP TREEMAP
As an example, I've used data from www.postgenomic.com/ (Which journals are bloggers linking to most frequently?)



Did you read this paper ?

Following a post in Nodal Point, I found this paper:
Read before you cite by Simkin & al.
We report a method of estimating what percentage of people who cited a paper had actually read it. The method is based on a stochastic modeling of the citation process that explains empirical studies of misprint distributions in citations (which we show follows a Zipf law). Our estimate is only about 20% of citers read the original.

For example, an interesting statistic revealed in our study is that a lot of misprints are identical. Consider, for example, a 4-digit page number with one digit misprinted. There can be 104 such misprints. The probability of repeating someone else’s misprint accidentally is10-4 . There should be almost no repeat misprints by coincidence. One concludes that repeat misprints are due to copying some one else’s reference, without reading the paper in question.

phdcomics picture
Picture from phd comics


15 February 2006

PostGenomic

Here is a nice site : www.postgenomic.com created by Stew.
Postgenomic aggregates posts from life science blogs and then does useful and interesting things with that data.. Here you can find blogs linking to scientific papers (e.g. identified by a pubmed-id). In fact it is a kind of reverse connotea(Hi Timo !)/citeulike. Stew also suggested to use the "rel" attribute in html anchors to identify reviews...

Gene Ontology : bad try !

Yesterday I suggested to add a new term in GO to define 3'-mRNA binding. Bad try ! I didn't see there was a GO:0003730 mRNA 3'-UTR binding. But why don't GO:0008143 Poly(A) binding inherits this ?

13 February 2006

Gene Ontology

I've been playing with RDF/semantic web for a time and then today I had a glance with Gene Onotolgy (GO) although I don' have currently any project dealing with expression/clusering/etc... First of all, it seems that GO is stored using the OBO format rather than RDF. At this time I don't know the consequences of this but I imagine that I won't be able to use standard XML/RDF tools (XSL,DOM, Protege, Jena...), can GO handles OWL-like relationships (InverseFunctional, Symetric, Transitive...) ? Anyway is this really useful in GO ? I remember there was a paper published in Nature Genetics titled "Are the current ontologies in biology good ontologies?".

the failure of many bio-ontologies to follow international standards for ontology design and description is hampering their application and threatens to restrict their future use.


I also started my experience with GO by trying to submit a new term "3' RNA binding" in this ontology to be inserted between GO:0003723 RNA binding and GO:0008143 Poly(A) binding, in order to assign this new term to the Rotavirus Non structural protein 3 (NSP3) wich binds specificaly the 3' end of viral mRNAs. The term was not reviewed yet.