Showing posts with label php. Show all posts
Showing posts with label php. Show all posts

24 July 2009

Ajax/PHP/Mysql/Canvas Drawing a circular genome, my notebook.

I've been asked to draw a circular map of the genome. Some tools already exist, for example circos, a Perl program.



Jan Aerts is also writing pARP, a circular genome browser using Ruby and ruby-processing:


My data are stored in big database and it might take some time before all the data are processed and displayed. So my idea was to call the server with some asynchronous ajax queries, retrieve the chunks of data and display each chunk as soon it is returned by the server as soon as it is available.

The code below is a proof of concept. This code is ugly, I wouldn't code things like this for a real piece of software. As a source of data I've used the snp129 and the knownGene tables of the UCSC stored in a mysql database. The server was implemented using PHP.

Client Side

When the document is loaded, the <canvas> element is resized. A first AJAX query is sent to retrieve an array of density of the SNPs on the human chromosome 1. The JSON response is processed, the maximum number of SNPs is found and each item of this array is displayed on the canvas. After that, a second AJAX query is sent to retrieve the density of the genes.
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<script><![CDATA[
/** the canvas element */
var canvas = null;
/** radius of the canvas */
var radius=500;
/** AJAX request */
var httpRequest=null;
/** Graphics context */
var g=null;
/** length of chrom1 */
var CHR1_LENGTH =248000000.0;
/** window length (pb) */
var windowLength=0;
/** first track is snp129 */
var database="snp129";

/** ajax callback */
function paintSnps()
{
if (httpRequest.readyState == 4) {
// everything is good, the response is received
if (httpRequest.status == 200)
{
var jsondata=eval("("+httpRequest.responseText+")");
var counts=jsondata.counts;
//get the maximum of item
var max=0;
for(var i=0;i< counts.length;++i)
{
if(counts[i].count > max) max= counts[i].count*1.0;
}
var r1= radius/2.0;
if(database=="knownGene")
{
r1+= 2+radius/4.0;
}
//loop over the items
for(var i=0;i< counts.length;++i)
{
var a1= Math.PI*2.0*i/(1.0*counts.length);
var a2= Math.PI*2.0*(i+1)/(1.0*counts.length);

var r2= r1+(counts[i].count/max)*(radius/4.0);
//draw the item
g.beginPath();
g.moveTo( radius + Math.cos(a1)*r1, radius + Math.sin(a1)*r1);
g.lineTo( radius + Math.cos(a1)*r2, radius + Math.sin(a1)*r2);
g.lineTo( radius + Math.cos(a2)*r2, radius + Math.sin(a2)*r2);
g.lineTo( radius + Math.cos(a2)*r1, radius + Math.sin(a2)*r1);
g.stroke();
g.fill();
}
//if it was snp, then look for knownGene, change the coors
if(database=="snp129")
{
database="knownGene";
g.fillStyle = "yellow";
g.strokeStyle = "blue";
setTimeout("fetchDB()",100);
}
}
else
{
//boum!!
}
}
else {
// still not ready
}

}

/** calls the AJAX request */
function fetchDB()
{
httpRequest= new XMLHttpRequest();
httpRequest.onreadystatechange = paintSnps;
httpRequest.open('GET', 'ucsc.php', true);
httpRequest.send("length="+windowLength+"database="+database);

}

/** init document */
function init()
{
canvas=document.getElementById("genome");
//resize canvas
canvas.setAttribute("width",2*radius);
canvas.setAttribute("height",2*radius);
if (!canvas.getContext) return;
g = canvas.getContext('2d');
//paint background
var lineargradient = g.createLinearGradient(radius,0,radius,2*radius);
lineargradient.addColorStop(0,'white');
lineargradient.addColorStop(1,'black');
g.fillStyle = lineargradient;
g.fillRect(0,0,2*radius,2*radius);
g.strokeStyle = "black";
g.strokeRect(0,0,2*radius,2*radius);
g.fillStyle = "red";
g.strokeStyle = "green";

var perimeter= 2*Math.PI*(radius/2.0);
windowLength = Math.round(CHR1_LENGTH/perimeter);

//launch the first ajax request
setTimeout("fetchDB()",100);
}


]]></script>
</head><body onload="init();">
<canvas id="genome" />
</body></html>

The server

The (ugly) PHP page is a simple script returning the density of the objects mapped on the chromosome 1 for a given table.
<?php
$con=NULL;

function cleanup()
{
if($con!=NULL) mysql_close($con);
flush;
exit;
}

header('Cache-Control: no-cache, must-revalidate');
header('Content-type: application/json');
header("Content-Disposition: attachment; filename=\"result.json\"");
header('Content-type: text/plain');

$con = mysql_connect('localhost', 'anonymous', '');
if (!$con) {
echo "{status:'Error',message:'". mysql_error()."'}";
cleanup();
}
if(!mysql_select_db('hg18', $con))
{
echo "{status:'Error',message:'cannot select db'}";
cleanup();
}
$database="snp129";
if(isset($_GET["database"]))
{
$database=$_GET["database"];
}


$length=1E6;
if(isset($_GET["length"]))
{
$length= (int)$_GET["length"];
}
if($length<=0) $length=1E6;

$nameStart="chromStart";
if($database=="knownGene")
{
$nameStart="txStart";
}


$sql="SELECT CAST(ROUND(".$nameStart."/".$length.") AS SIGNED INTEGER )*".$length.",count(*) from ".$database." where ".
" chrom=\"chr1\" ".
" group by CAST(ROUND(".$nameStart."/".$length.") AS SIGNED INTEGER )*".$length.
" order by 1"
;

$result = mysql_query($sql ,$con );

if(!$result)
{
echo "{status:'Error',message:'".mysql_error($con) ."'}";
cleanup();
}

$found=FALSE;


echo "{status:'OK',";
echo "length:".$length.",";
echo "counts:[";

while ($row = mysql_fetch_array($result))
{
if($found) echo ",\n";
$found=TRUE;
echo "{chromStart:".$row[0].",count:".$row[1]."}";
}

echo "]}";

cleanup();

?>
And here is the kind of JSON document returned by the server:
{status:'OK',
length:1000000,
counts:[
{chromStart:0,count:6191},
{chromStart:1000000,count:8897},
{chromStart:2000000,count:5559},
{chromStart:3000000,count:6671},
{chromStart:4000000,count:6398},
{chromStart:5000000,count:5462},
{chromStart:6000000,count:5678},
{chromStart:7000000,count:4737},
{chromStart:8000000,count:5313},
{chromStart:9000000,count:5148},
{chromStart:10000000,count:4055},
{chromStart:11000000,count:5012},
{chromStart:12000000,count:5363},
{chromStart:13000000,count:10165},

(...)

{chromStart:239000000,count:5502},
{chromStart:240000000,count:6173},
{chromStart:241000000,count:7928},
{chromStart:242000000,count:3800},
{chromStart:243000000,count:5503},
{chromStart:244000000,count:7120},
{chromStart:245000000,count:6148},
{chromStart:246000000,count:6015},
{chromStart:247000000,count:5337}
]
}

Result




That's it

PS: Hum, yes I know , it's not as fast/beautiful as GenoDive that was introduced at Biohackathon.



Pierre

04 January 2009

An extension for MediaWiki: displaying a DNA sequence

This post is about a new extension for MediaWiki (the wiki engine of wikipedia written in PHP). This was the first extension I wrote: this extension add a new custom tag <dnaseq> and it simply displays a DNA sequence. Here is a screenshot of this extension installed in my local mediawiki.


and the source code for this extension is available here:


First we tell MediaWiki about this new extension in ${MWROOT}/LocalSettings.php
require_once("$IP/extensions/dnaseq/dnaseq.php");

Then we install this new feature which is a new TAG named <dnaseq>. Each time the mediawiki will find a <dnaseq> , the function myRenderDnaSequence will be called.
$wgHooks['ParserFirstCallInit'][] = 'myDnaSequence';
(...)
function myDnaSequence()
{
global $wgParser;
$wgParser->setHook( 'dnaseq', 'myRenderDnaSequence' );
return true;
}

myRenderDnaSequence is the function returning the formatted DNA sequence:
function myRenderDnaSequence( $input, $args, $parser )
{
if($input==null) return "";
$len= strlen($input);
$n=0;
$html="<div style='padding: 10px; font-size:10px; border-width: thin; border: 1px black solid; white-space: pre;background-color: white;font-family: courier, monospace;line-height:13px; font-size:12px;'>";
for($i=0;$i< $len;$i++)
{
$c = $input[$i];
if(ctype_space($c) || ctype_digit($c)) continue;
if($n % 60 == 0)
{
if($n!=0) $html.="<br/>";
$html.= sprintf("%06d ",($n+1));
}
else if($n % 10 ==0)
{
$html.=" ";
}
$n++;
switch(strtolower($c))
{
case "a":
$html.="<span style='color:green;'>".$c."</span>";
break;
case "c":
$html.="<span style='color:blue;'>".$c."</span>";
break;
case "g":
$html.="<span style='color:black;'>".$c."</span>";
break;
case "t":
case "u":
$html.="<span style='color:red'>".$c."</span>";
break;
default:
$html.="<span style='text-decoration:blink;color:gray'>".$c."</span>";
break;
}
if($n % 60 == 0)
{
$html.= sprintf(" %06d",($n));
}
}
$html .= "</div>";
return $html;
}


That's it.
Pierre

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
}