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.


08 February 2006

Gene WIKI

In response to call for a public gene wiki, Tom Stambaugh created a Gene Wiki at http://www.zeetix.org/GeneFunctionWiki. Although I think this can be a problem for knowledge discovery [link1][link2], this wiki might be a good start and is better than nothing.

Multiplex PCR

A few monthes ago, I wrote a tool to find the best strategy for PCR in multiplex condition. The program takes as input a set of valid pairs of PCR primers for a set of targets and it builds the content of the PCR (a mix of primer), it finds how to pool the PCR in a gel in order to get distinct fragments. Some parameters are:

  • -m maximum number of fragments per dye
  • -D minimal distance between two fragments in a gel.
  • -T maximal difference of temperature in a PCR.
  • -c maximal number of PCR in a dye
  • -g maximal number of pair in PCR.
  • -f number of dyes
  • (...)


This software was created for a project that was canceled so I don't know if my algorithm works. I'm looking for people needing to peek primers for multiplex PCR in order to test my program (and write a paper if it works !...). Best would be someone on the Evry Genopole or near Paris.

07 February 2006

Psychohistory

'The scaling laws of human travel' by Brockmann & al. was published in Nature... Makes me think of Asimov'psychohistory in "Foundation" :-)

Where did I put my mammoth ?


The mitochondrial genome of a Mammoth was published in PLOS. This genome is available at NCBI with the accession number NC_007596

Philipp Angerer Interviewed in Nature

Philipp Angerer
There was a short interview in "Nature Jobs" of Philipp Angerer this week. He is one of the co-founder of the Young European Biotech Network, an information hub and a networking meeting point for researcher. YEBN is a registered group in LinkedIn and I'm directly connected to Philipp threw LinkedIn and OpenBC openbc. May be he knows someone that could be interested by my skills, may be I know someone that could be interested by his skills: that's the aim of such social networks.

BioMed Central launches Biology Direct

As seen on http://www.biomedcentral.com/info/update/:

BioMed Central is pleased to announce the launch of Biology Direct, a new online open access journal with a novel system of peer review. Biology Direct launches with publications in the fields of Systems Biology, Computational Biology, and Evolutionary Biology, with an Immunology section to follow soon. The journal considers original research articles, hypotheses, and reviews and will eventually cover the full spectrum of biology. Biology Direct is led by Editors-in-Chief David J Lipman, Director of the National Center Biotechnology Information (NCBI),

06 February 2006

Human Genome version 36



The version 36 of the human genome is about to be released. See [here].

WIKI & gene annotation

In a recent issue of Nature, Dr Kai Wang from Washington University suggested to use a WIKI as a way to annotate genes, proteins, etc... I agree, that would be really useful to share evreybody's knowledge about a specific domain in a wiki. But then, as a bioinformatician, I've got the feeling that it would than be a real problem to extract all this information for knowledge discovery. There are already many tools (see Jensen & al. for a recent review) trying to parse and analyse the abstracts of pubmed and I don't think it would be a good thing to make the same mistakes: publishers sould provide a RDF/semantic web version of the abstracts in order to be fully interpretable by computers.