But this time I just wanted to use client technologies and my source of data was Nature Network (hot topic !)
The first point was to fetch the data. I wrote a simple java parser which downloaded recursively all the profiles and groups starting from my own profile. I ignored a few pages containing some xml errors:
http://network.nature.com/profile/U078528F5: HTML parser error : htmlParseEntityRef: expecting ';' PubMed ID:(Museums&Web)
. My first idea was then to export this to FOAFnn.rdf.
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xm
lns:foaf="http://xmlns.com/foaf/0.1/" xmlns:nn="http://network.na
ture.com/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<foaf:Person rdf:about="http://network.nature.com/profile/U78424D59">
<foaf:Person rdf:about="http://network.nature.com/profile/lindenb">
<foaf:name>Lindenbaum Pierre</foaf:name>
<nn:job>Bioinformatician</nn:job>
<nn:affiliation>Integragen SA</nn:affiliation>
<foaf:weblog rdf:resource="http://plindenbaum.blogspot.com"/>
<foaf:img rdf:resource="http://network.nature.com/assets/user/000/000/037/large.png"/>
<dc:subject>computational</dc:subject>
<dc:subject>nnb</dc:subject>
<dc:subject>computational biology</dc:subject>
<dc:subject>network</dc:subject>
</foaf:Person>
(...)
<foaf:Group rdf:about="http://network.nature.com/group/bioinformatics">
<foaf:name>Bioinformatics</foaf:name>
<foaf:member rdf:resource="http://network.nature.com/profile/U7457FA5D"/>
<foaf:member rdf:resource="http://network.nature.com/profile/UAD17B888"/>
<foaf:member rdf:resource="http://network.nature.com/profile/U438F4261"/>
<foaf:member rdf:resource="http://network.nature.com/profile/U579CAC72"/>
</foaf:Group>
(...)
<nn:Connection rdf:nodeId="5">
<nn:contact rdf:resource="http://network.nature.com/profile/U2570065B"/>
<nn:contact rdf:resource="http://network.nature.com/profile/UDC37FBCB"/>
</nn:Connection>
<nn:Connection rdf:nodeId="6">
<nn:contact rdf:resource="http://network.nature.com/profile/UAB504518"/>
<nn:contact rdf:resource="http://network.nature.com/profile/UDC691EA2"/>
</nn:Connection>
(...)
I then transformed this rdf file into JSON using the following XSLT stylesheet.
foaf2json.xsl
<?xml version='1.0' ?>
<xsl:stylesheet
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:nn="http://network.nature.com/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
version='1.0'>
<xsl:output method='text'/>
<xsl:template match="rdf:RDF">
var network= {
"profile":[
<xsl:for-each select='foaf:Person'>
{
"id": "<xsl:value-of select="substring(@rdf:about,35)"/>",
"name":"<xsl:value-of select="foaf:name"/>",
"job":"<xsl:value-of select="nn:job"/>",
"affiliation":"<xsl:value-of select="nn:affiliation"/>",
"www":"<xsl:value-of select="foaf:weblog/@rdf:resource"/>",
"img":"<xsl:value-of select="foaf:img/@rdf:resource"/>",
"tags":[<xsl:for-each select='dc:subject'>"<xsl:value-of
select="."/>"<xsl:if
test="position()!=last()">,</xsl:if></xsl:for-each>],
"pos":{"x":0,"y":0},
"goal":{"x":0,"y":0},
"g":null
}
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
],
"group":[
<xsl:for-each select='foaf:Group'>
{
"uri": "<xsl:value-of select="substring(@rdf:about,33)"/>",
"name":"<xsl:value-of select="foaf:name"/>",
"members":[<xsl:for-each select='foaf:member'>"<xsl:value-of
select="substring(@rdf:resource,35)"/>"<xsl:if
test="position()!=last()">,</xsl:if></xsl:for-each>] }
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
],
"link":[
<xsl:for-each select='nn:Connection'>
["<xsl:value-of
select="substring(nn:contact[1]/@rdf:resource,35)"/>","<xsl:value-of
select="substring(nn:contact[2]/@rdf:resource,35)"/>"]<xsl:if
test="position()!=last()">,</xsl:if>
</xsl:for-each>
]
};
</xsl:template>
</xsl:stylesheet>
This gave me my json.js.
var network= {
"profile":[
{
"id": "lindenb",
"name":"Lindenbaum Pierre",
"job":"Bioinformatician",
"affiliation":"Integragen SA",
"www":"http://plindenbaum.blogspot.com",
"img":"http://network.nature.com/assets/user/000/000/037/large.png",
"tags":["computational","nnb","computational biology","network","science","cgh","social","rdf","virology","wiki","biology","lims","rotavirus","networking","protocol","c","gene ontoloy","community","semantic","web","molecular biology","linux","ontology","semantic web","social network","java","bioinformatics","microarray"
],
"pos":{"x":0,"y":0},
"goal":{"x":0,"y":0},
"g":null
}
},
(...)
What is cool with JSON is that this format stores the data but and it is also a big javascript object which can be used just like a regular variable.
The result is available here: http://www.urbigene.com/nn/network.xml.
Each object profile, displayed with a SVG icon, contains the position of the current icon
(profile[n].pos.x, profile[n].pos.y)
and the position where the icon should move (profile[n].goal.x, profile[n].goal.y)
. Each time an icon/tag/profile/job/affiliation/group is clicked, a function nextStep
is ivoked. This function calls the setTimeout method where each icon is moved sightly to its goal. This function calls itself recursively until all icons have reached their goal. This page was tested using Firefox 2.0.Updated 2010-08-12 : source code
foaf2json.xsl
<?xml version='1.0' ?>
<xsl:stylesheet
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:nn="http://network.nature.com/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
version='1.0'>
<xsl:output method='text'/>
<xsl:template match="rdf:RDF">
var network= {
"profile":[
<xsl:for-each select='foaf:Person'>
{
"id": "<xsl:value-of select="substring(@rdf:about,35)"/>",
"name":"<xsl:value-of select="foaf:name"/>",
"job":"<xsl:value-of select="nn:job"/>",
"affiliation":"<xsl:value-of select="nn:affiliation"/>",
"www":"<xsl:value-of select="foaf:weblog/@rdf:resource"/>",
"img":"<xsl:value-of select="foaf:img/@rdf:resource"/>",
"tags":[<xsl:for-each select='dc:subject'>"<xsl:value-of
select="."/>"<xsl:if
test="position()!=last()">,</xsl:if></xsl:for-each>],
"pos":{"x":0,"y":0},
"goal":{"x":0,"y":0},
"g":null
}
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
],
"group":[
<xsl:for-each select='foaf:Group'>
{
"uri": "<xsl:value-of select="substring(@rdf:about,33)"/>",
"name":"<xsl:value-of select="foaf:name"/>",
"members":[<xsl:for-each select='foaf:member'>"<xsl:value-of
select="substring(@rdf:resource,35)"/>"<xsl:if
test="position()!=last()">,</xsl:if></xsl:for-each>] }
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
],
"link":[
<xsl:for-each select='nn:Connection'>
["<xsl:value-of
select="substring(nn:contact[1]/@rdf:resource,35)"/>","<xsl:value-of
select="substring(nn:contact[2]/@rdf:resource,35)"/>"]<xsl:if
test="position()!=last()">,</xsl:if>
</xsl:for-each>
]
};
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:nn="http://network.nature.com/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
version='1.0'>
<xsl:output method='text'/>
<xsl:template match="rdf:RDF">
var network= {
"profile":[
<xsl:for-each select='foaf:Person'>
{
"id": "<xsl:value-of select="substring(@rdf:about,35)"/>",
"name":"<xsl:value-of select="foaf:name"/>",
"job":"<xsl:value-of select="nn:job"/>",
"affiliation":"<xsl:value-of select="nn:affiliation"/>",
"www":"<xsl:value-of select="foaf:weblog/@rdf:resource"/>",
"img":"<xsl:value-of select="foaf:img/@rdf:resource"/>",
"tags":[<xsl:for-each select='dc:subject'>"<xsl:value-of
select="."/>"<xsl:if
test="position()!=last()">,</xsl:if></xsl:for-each>],
"pos":{"x":0,"y":0},
"goal":{"x":0,"y":0},
"g":null
}
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
],
"group":[
<xsl:for-each select='foaf:Group'>
{
"uri": "<xsl:value-of select="substring(@rdf:about,33)"/>",
"name":"<xsl:value-of select="foaf:name"/>",
"members":[<xsl:for-each select='foaf:member'>"<xsl:value-of
select="substring(@rdf:resource,35)"/>"<xsl:if
test="position()!=last()">,</xsl:if></xsl:for-each>] }
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
],
"link":[
<xsl:for-each select='nn:Connection'>
["<xsl:value-of
select="substring(nn:contact[1]/@rdf:resource,35)"/>","<xsl:value-of
select="substring(nn:contact[2]/@rdf:resource,35)"/>"]<xsl:if
test="position()!=last()">,</xsl:if>
</xsl:for-each>
]
};
</xsl:template>
</xsl:stylesheet>
network.xml
<html
xmlns="http://www.w3.org/1999/xhtml"
>
<head>
<title>Nature Network With SVG: Pierre Lindenbaum</title>
<script type="text/javascript" src="nn.js"/>
<script type="text/javascript"><![CDATA[
/** the HTML namespace */
var HTML={
"NS":"http://www.w3.org/1999/xhtml"
};
/** the SVG namespace */
var SVG={
"NS":"http://www.w3.org/2000/svg"
};
/** the XLINK namespace */
var XLINK={
"NS":"http://www.w3.org/1999/xlink"
};
/** the html document */
var htmldoc=null;
/** the svg root */
var svgroot=null;
/** map uri2user */
var uri2user= new Array();
/** animation running */
var animated=false;
/** screen width */
var viewWidth=0.0;
/** ratio for animation */
var RATIO_ANIME=0.8;
/** time for animated */
var ELAPSED_TIME=10;
/** initialise the whole document */
function init()
{
htmldoc= this.document;
svgroot= document.getElementById("svgdoc");
var svgrect =document.getElementById("frameSVGRect");
viewWidth=this.innerWidth-30;
svgrect.setAttribute("width",viewWidth);
svgrect.setAttribute("height",this.innerHeight-100);
svgroot.setAttribute("width",1+viewWidth);
svgroot.setAttribute("height",1+this.innerHeight-100);
var v= network.profile;
var cosf= Math.cos;
var sinf= Math.sin;
//loop over users
for(var i=0;i< v.length;++i)
{
//initialize the uri2user array
uri2user[v[i].id]=v[i];
var angle= Math.random()*6.2;
v[i].pos.x= this.innerWidth/2.0 + this.innerWidth*cosf(angle);
v[i].pos.y= this.innerHeight/2.0 + this.innerHeight*sinf(angle);
var g = htmldoc.createElementNS(SVG.NS,"g");
v[i].g = g;
g.setAttribute("title",v[i].name+" "+v[i].job+" "+v[i].affiliation);
g.setAttribute("onmousedown","focusProfile(this);");
g.setAttribute("transform","translate(-50,-50)");
g.setAttribute("cursor","pointer");
g.setAttribute("id",v[i].id);
var r1= htmldoc.createElementNS(SVG.NS,"rect");
r1.setAttribute("x",0);
r1.setAttribute("y",0);
r1.setAttribute("width",41);
r1.setAttribute("height",50);
r1.setAttribute("fill","gray");
r1.setAttribute("stroke","black");
g.appendChild(r1);
if(v[i].img.length>0)
{
var img= htmldoc.createElementNS(SVG.NS,"image");
img.setAttributeNS(XLINK.NS,"xlink:href",v[i].img);
img.setAttribute("x",4);
img.setAttribute("y",4);
img.setAttribute("width",41-8);
img.setAttribute("height",50-8);
g.appendChild(img);
}
else
{
}
svgroot.appendChild(g);
}
focusProfileID("lindenb");
}
function focusProfile(g)
{
focusProfileID(g.id);
}
function createProfileShown()
{
var profileShown= new Array();
var v= network.profile;
for(var i=0;i< v.length;++i)
{
profileShown[v[i].id]=0;
}
return profileShown;
}
function focusProfileID( id)
{
if(animated) return;
showLayer("profileLayer");
var profileShown= createProfileShown();
profileShown[id]=1;
var user=uri2user[id];
if(user==null) return;
var a=null;
var tag = document.getElementById("profileImg");
if(user.img.length==0)
{
tag.setAttribute("src","http://network.nature.com/images/user_generic_large.gif");
}
else
{
tag.setAttribute("src",user.img);
}
tag = document.getElementById("profileAnchor");
tag.setAttribute("href","http://network.nature.com/profile/"+user.id);
tag.setAttribute("title","Go to http://network.nature.com/profile/"+user.id);
tag.setAttribute("target",user.id);
tag = document.getElementById("profileName");
tag.innerHTML= user.name;
tag = document.getElementById("profileJob");
tag.innerHTML="";
a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusJob(\""+user.job+"\");");
a.setAttribute("title",user.job);
a.appendChild(htmldoc.createTextNode(user.job));
tag = document.getElementById("profileAffiliation");
tag.innerHTML="";
a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusAffiliation(\""+user.affiliation+"\");");
a.setAttribute("title",user.affiliation);
a.appendChild(htmldoc.createTextNode(user.affiliation));
tag = document.getElementById("profileWWW");
tag.innerHTML= ""
if(user.www.length>0)
{
a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href",user.www);
a.setAttribute("title",user.www);
a.setAttribute("target",user.www);
a.appendChild(htmldoc.createTextNode(user.www));
}
tag = document.getElementById("profileTags");
tag.innerHTML= ""
for(var i=0;i< user.tags.length;i++)
{
var a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusTag(\""+user.tags[i]+"\");");
a.setAttribute("title",user.tags[i]);
a.appendChild(htmldoc.createTextNode(user.tags[i]));
if(i+1!= user.tags.length) tag.appendChild(htmldoc.createTextNode(" "));
}
var link=network.link;
for(var i=0;i< link.length;++i)
{
var L= link[i];
var other=null;
if(L[0]==id)
{
other =uri2user[L[1]];
}
else if(L[1]==id)
{
other =uri2user[L[0]];
}
if(other==null) continue;
profileShown[other.id]=1;
}
tag = document.getElementById("profileGroups");
tag.innerHTML= ""
var group=network.group;
for(var i=0;i< group.length;++i)
{
for(var j=0;j< group[i].members.length;++j)
{
if( group[i].members[j]==id)
{
var a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusGroupID(\""+group[i].uri+"\");");
a.setAttribute("title",group[i].name);
a.appendChild(htmldoc.createTextNode(group[i].name));
tag.appendChild(htmldoc.createTextNode(" "));
break;
}
}
}
launchAnimation(profileShown);
}
function focusGroupID(id)
{
if(animated) return;
showLayer("groupLayer");
var group=network.group;
tag = document.getElementById("groupLayer");
tag.innerHTML= ""
for(var i=0;i< group.length;++i)
{
if(group[i].uri!=id) continue;
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Group: "));
tag.appendChild(e);
var e= htmldoc.createElementNS(HTML.NS,"a");
e.setAttribute("href","http://network.nature.com/group/"+group[i].uri);
e.setAttribute("title","http://network.nature.com/group/"+group[i].uri);
e.setAttribute("target",group[i].uri);
e.appendChild(htmldoc.createTextNode(group[i].name));
tag.appendChild(e);
var profileShown= createProfileShown();
for(var j=0;j< group[i].members.length;++j)
{
profileShown[group[i].members[j]]=1;
}
break;
}
launchAnimation(profileShown);
}
function focusTag(id)
{
if(animated) return;
showLayer("tagLayer");
tag = document.getElementById("tagLayer");
tag.innerHTML= ""
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Tag: "));
tag.appendChild(e);
tag.appendChild(htmldoc.createTextNode(id));
var profileShown= createProfileShown();
var v= network.profile;
id=id.toLowerCase();
for(var i=0;i< v.length;++i)
{
var t=v[i].tags;
for(var j=0;j< t.length;++j)
{
if(t[j].toLowerCase()==id)
{
profileShown[v[i].id]=1;
break;
}
}
}
launchAnimation(profileShown);
}
function focusJob(id)
{
if(animated) return;
showLayer("jobLayer");
tag = document.getElementById("jobLayer");
tag.innerHTML= ""
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Job : "));
tag.appendChild(e);
tag.appendChild(htmldoc.createTextNode(id));
focusOnProperty("job",id);
}
function focusAffiliation(id)
{
if(animated) return;
showLayer("affiliationLayer");
tag = document.getElementById("affiliationLayer");
tag.innerHTML= ""
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Affiliation : "));
tag.appendChild(e);
tag.appendChild(htmldoc.createTextNode(id));
focusOnProperty("affiliation",id);
}
function focusOnProperty(prop,id)
{
var profileShown= createProfileShown();
var v= network.profile;
id=id.toLowerCase();
for(var i=0;i< v.length;++i)
{
var p = v[i][prop].toLowerCase();
if(p==id)
{
profileShown[v[i].id]=1;
}
}
launchAnimation(profileShown);
}
function showLayer(id)
{
var a=["profileLayer","groupLayer","tagLayer","affiliationLayer","jobLayer"];
for(var i=0;i< a.length;i++)
{
document.getElementById(a[i]).style.visibility=(a[i]==id?"visible":"hidden");
}
}
function launchAnimation(profileShown)
{
if(animated) return;
var v= network.profile;
var x=5;
var y=5;
var cosf= Math.cos;
var sinf= Math.sin;
//loop over users
for(var i=0;i< v.length;++i)
{
if(profileShown[v[i].id]==1)
{
v[i].goal.x=x;
v[i].goal.y=y;
x+=(5+41);
if(x+41>viewWidth)
{
x=5;
y+=(5+50);
}
}
else
{
var angle= Math.random()*6.2;
v[i].goal.x= this.innerWidth/2.0 + this.innerWidth*cosf(angle);
v[i].goal.y= this.innerHeight/2.0 + this.innerHeight*sinf(angle);
}
}
animated=true;
setTimeout('nextStep()', ELAPSED_TIME);
}
function nextStep()
{
if(animated==false) return;
var continueAnimation=0;
var v= network.profile;
var sqrt= Math.sqrt;
for(var i=0;i< v.length;++i)
{
var curr=v[i];
var x0= curr.pos.x;
var y0= curr.pos.y;
var x1= curr.goal.x;
var y1= curr.goal.y;
var dx= x1-x0;
var dy= y1-y0;
curr.pos.x = x0+ (dx-dx*RATIO_ANIME);
curr.pos.y = y0+ (dy-dy*RATIO_ANIME);
dx= x1-x0;
dy= y1-y0;
var dist = sqrt(dx*dx+dy*dy);
if(dist<4)
{
curr.pos.x= x1;
curr.pos.y= y1;
}
else
{
continueAnimation++;
}
v[i].g.setAttribute("transform","translate("+ v[i].pos.x+","+v[i].pos.y+")");
}
if(continueAnimation>0)
{
setTimeout('nextStep()', ELAPSED_TIME);
}
else
{
animated=false;
}
}
]]></script>
</head>
<body onload="init()">
<div style="font-size:10pt;"><span style="font-size:18pt;"><b>The Nature Network</b></span> created by <a href="http://plindenbaum.blogspot.com">Pierre Lindenbaum PhD 2007</a>. <cite>Learning a little more javascript and <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> I've used the <a href="nn.js">data</a> available from the <a href="http://network.nature.com/">Nature Network</a> on May 13, 2007 to display an interactive map of the network based on <a href="http://www.w3.org/Graphics/SVG/">SVG</a>, <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> and javascript</cite>. This page was tested on <a href="http://www.mozilla.com/en-US/firefox/">Firefox 2</a></div>
<div id="layers" style="position:relative;width:100%; height:100px; background:lightgray;">
<div id="groupLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="tagLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="jobLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="affiliationLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="profileLayer" style="position:absolute; top:0px; left:0px;width:100%; font-size:9pt;">
<table width="100%"><tr>
<td width="33">
<a id="profileAnchor">
<img id="profileImg" width="33" height="42" src="http://network.nature.com/images/user_generic_large.gif"/>
</a>
</td>
<td>
<b>Name :</b> <span id="profileName"></span>
<b>Job :</b> <span id="profileJob"></span>
<b>Affiliation :</b> <span id="profileAffiliation"></span>
<b>www :</b> <span id="profileWWW"></span>
<b>Tags :</b> <span id="profileTags"></span>
<b>Groups :</b> <span id="profileGroups"></span>
</td></tr></table></div>
</div><br/>
<svg id="svgdoc" xmlns="http://www.w3.org/2000/svg" version="1.1" width="100" height="100">
<defs>
<linearGradient id = "gradient1" x1 = "50%" y1 = "0%" x2 = "50%" y2 = "100%">
<stop stop-color = "white" offset = "0%"/>
<stop stop-color = "black" offset = "100%"/>
</linearGradient>
</defs>
<rect id="frameSVGRect" x="0" y="0" width="100" height="100" fill="lightgray" stroke="black"/>
</svg>
<div>Pierre Lindenbaum: plindenbaum ( a t ) yahoo ( d o t ) fr </div>
<!-- google analytics -->
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "XXXX-2";
urchinTracker();
</script>
<!-- google analytics -->
</body>
</html>
xmlns="http://www.w3.org/1999/xhtml"
>
<head>
<title>Nature Network With SVG: Pierre Lindenbaum</title>
<script type="text/javascript" src="nn.js"/>
<script type="text/javascript"><![CDATA[
/** the HTML namespace */
var HTML={
"NS":"http://www.w3.org/1999/xhtml"
};
/** the SVG namespace */
var SVG={
"NS":"http://www.w3.org/2000/svg"
};
/** the XLINK namespace */
var XLINK={
"NS":"http://www.w3.org/1999/xlink"
};
/** the html document */
var htmldoc=null;
/** the svg root */
var svgroot=null;
/** map uri2user */
var uri2user= new Array();
/** animation running */
var animated=false;
/** screen width */
var viewWidth=0.0;
/** ratio for animation */
var RATIO_ANIME=0.8;
/** time for animated */
var ELAPSED_TIME=10;
/** initialise the whole document */
function init()
{
htmldoc= this.document;
svgroot= document.getElementById("svgdoc");
var svgrect =document.getElementById("frameSVGRect");
viewWidth=this.innerWidth-30;
svgrect.setAttribute("width",viewWidth);
svgrect.setAttribute("height",this.innerHeight-100);
svgroot.setAttribute("width",1+viewWidth);
svgroot.setAttribute("height",1+this.innerHeight-100);
var v= network.profile;
var cosf= Math.cos;
var sinf= Math.sin;
//loop over users
for(var i=0;i< v.length;++i)
{
//initialize the uri2user array
uri2user[v[i].id]=v[i];
var angle= Math.random()*6.2;
v[i].pos.x= this.innerWidth/2.0 + this.innerWidth*cosf(angle);
v[i].pos.y= this.innerHeight/2.0 + this.innerHeight*sinf(angle);
var g = htmldoc.createElementNS(SVG.NS,"g");
v[i].g = g;
g.setAttribute("title",v[i].name+" "+v[i].job+" "+v[i].affiliation);
g.setAttribute("onmousedown","focusProfile(this);");
g.setAttribute("transform","translate(-50,-50)");
g.setAttribute("cursor","pointer");
g.setAttribute("id",v[i].id);
var r1= htmldoc.createElementNS(SVG.NS,"rect");
r1.setAttribute("x",0);
r1.setAttribute("y",0);
r1.setAttribute("width",41);
r1.setAttribute("height",50);
r1.setAttribute("fill","gray");
r1.setAttribute("stroke","black");
g.appendChild(r1);
if(v[i].img.length>0)
{
var img= htmldoc.createElementNS(SVG.NS,"image");
img.setAttributeNS(XLINK.NS,"xlink:href",v[i].img);
img.setAttribute("x",4);
img.setAttribute("y",4);
img.setAttribute("width",41-8);
img.setAttribute("height",50-8);
g.appendChild(img);
}
else
{
}
svgroot.appendChild(g);
}
focusProfileID("lindenb");
}
function focusProfile(g)
{
focusProfileID(g.id);
}
function createProfileShown()
{
var profileShown= new Array();
var v= network.profile;
for(var i=0;i< v.length;++i)
{
profileShown[v[i].id]=0;
}
return profileShown;
}
function focusProfileID( id)
{
if(animated) return;
showLayer("profileLayer");
var profileShown= createProfileShown();
profileShown[id]=1;
var user=uri2user[id];
if(user==null) return;
var a=null;
var tag = document.getElementById("profileImg");
if(user.img.length==0)
{
tag.setAttribute("src","http://network.nature.com/images/user_generic_large.gif");
}
else
{
tag.setAttribute("src",user.img);
}
tag = document.getElementById("profileAnchor");
tag.setAttribute("href","http://network.nature.com/profile/"+user.id);
tag.setAttribute("title","Go to http://network.nature.com/profile/"+user.id);
tag.setAttribute("target",user.id);
tag = document.getElementById("profileName");
tag.innerHTML= user.name;
tag = document.getElementById("profileJob");
tag.innerHTML="";
a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusJob(\""+user.job+"\");");
a.setAttribute("title",user.job);
a.appendChild(htmldoc.createTextNode(user.job));
tag = document.getElementById("profileAffiliation");
tag.innerHTML="";
a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusAffiliation(\""+user.affiliation+"\");");
a.setAttribute("title",user.affiliation);
a.appendChild(htmldoc.createTextNode(user.affiliation));
tag = document.getElementById("profileWWW");
tag.innerHTML= ""
if(user.www.length>0)
{
a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href",user.www);
a.setAttribute("title",user.www);
a.setAttribute("target",user.www);
a.appendChild(htmldoc.createTextNode(user.www));
}
tag = document.getElementById("profileTags");
tag.innerHTML= ""
for(var i=0;i< user.tags.length;i++)
{
var a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusTag(\""+user.tags[i]+"\");");
a.setAttribute("title",user.tags[i]);
a.appendChild(htmldoc.createTextNode(user.tags[i]));
if(i+1!= user.tags.length) tag.appendChild(htmldoc.createTextNode(" "));
}
var link=network.link;
for(var i=0;i< link.length;++i)
{
var L= link[i];
var other=null;
if(L[0]==id)
{
other =uri2user[L[1]];
}
else if(L[1]==id)
{
other =uri2user[L[0]];
}
if(other==null) continue;
profileShown[other.id]=1;
}
tag = document.getElementById("profileGroups");
tag.innerHTML= ""
var group=network.group;
for(var i=0;i< group.length;++i)
{
for(var j=0;j< group[i].members.length;++j)
{
if( group[i].members[j]==id)
{
var a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusGroupID(\""+group[i].uri+"\");");
a.setAttribute("title",group[i].name);
a.appendChild(htmldoc.createTextNode(group[i].name));
tag.appendChild(htmldoc.createTextNode(" "));
break;
}
}
}
launchAnimation(profileShown);
}
function focusGroupID(id)
{
if(animated) return;
showLayer("groupLayer");
var group=network.group;
tag = document.getElementById("groupLayer");
tag.innerHTML= ""
for(var i=0;i< group.length;++i)
{
if(group[i].uri!=id) continue;
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Group: "));
tag.appendChild(e);
var e= htmldoc.createElementNS(HTML.NS,"a");
e.setAttribute("href","http://network.nature.com/group/"+group[i].uri);
e.setAttribute("title","http://network.nature.com/group/"+group[i].uri);
e.setAttribute("target",group[i].uri);
e.appendChild(htmldoc.createTextNode(group[i].name));
tag.appendChild(e);
var profileShown= createProfileShown();
for(var j=0;j< group[i].members.length;++j)
{
profileShown[group[i].members[j]]=1;
}
break;
}
launchAnimation(profileShown);
}
function focusTag(id)
{
if(animated) return;
showLayer("tagLayer");
tag = document.getElementById("tagLayer");
tag.innerHTML= ""
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Tag: "));
tag.appendChild(e);
tag.appendChild(htmldoc.createTextNode(id));
var profileShown= createProfileShown();
var v= network.profile;
id=id.toLowerCase();
for(var i=0;i< v.length;++i)
{
var t=v[i].tags;
for(var j=0;j< t.length;++j)
{
if(t[j].toLowerCase()==id)
{
profileShown[v[i].id]=1;
break;
}
}
}
launchAnimation(profileShown);
}
function focusJob(id)
{
if(animated) return;
showLayer("jobLayer");
tag = document.getElementById("jobLayer");
tag.innerHTML= ""
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Job : "));
tag.appendChild(e);
tag.appendChild(htmldoc.createTextNode(id));
focusOnProperty("job",id);
}
function focusAffiliation(id)
{
if(animated) return;
showLayer("affiliationLayer");
tag = document.getElementById("affiliationLayer");
tag.innerHTML= ""
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Affiliation : "));
tag.appendChild(e);
tag.appendChild(htmldoc.createTextNode(id));
focusOnProperty("affiliation",id);
}
function focusOnProperty(prop,id)
{
var profileShown= createProfileShown();
var v= network.profile;
id=id.toLowerCase();
for(var i=0;i< v.length;++i)
{
var p = v[i][prop].toLowerCase();
if(p==id)
{
profileShown[v[i].id]=1;
}
}
launchAnimation(profileShown);
}
function showLayer(id)
{
var a=["profileLayer","groupLayer","tagLayer","affiliationLayer","jobLayer"];
for(var i=0;i< a.length;i++)
{
document.getElementById(a[i]).style.visibility=(a[i]==id?"visible":"hidden");
}
}
function launchAnimation(profileShown)
{
if(animated) return;
var v= network.profile;
var x=5;
var y=5;
var cosf= Math.cos;
var sinf= Math.sin;
//loop over users
for(var i=0;i< v.length;++i)
{
if(profileShown[v[i].id]==1)
{
v[i].goal.x=x;
v[i].goal.y=y;
x+=(5+41);
if(x+41>viewWidth)
{
x=5;
y+=(5+50);
}
}
else
{
var angle= Math.random()*6.2;
v[i].goal.x= this.innerWidth/2.0 + this.innerWidth*cosf(angle);
v[i].goal.y= this.innerHeight/2.0 + this.innerHeight*sinf(angle);
}
}
animated=true;
setTimeout('nextStep()', ELAPSED_TIME);
}
function nextStep()
{
if(animated==false) return;
var continueAnimation=0;
var v= network.profile;
var sqrt= Math.sqrt;
for(var i=0;i< v.length;++i)
{
var curr=v[i];
var x0= curr.pos.x;
var y0= curr.pos.y;
var x1= curr.goal.x;
var y1= curr.goal.y;
var dx= x1-x0;
var dy= y1-y0;
curr.pos.x = x0+ (dx-dx*RATIO_ANIME);
curr.pos.y = y0+ (dy-dy*RATIO_ANIME);
dx= x1-x0;
dy= y1-y0;
var dist = sqrt(dx*dx+dy*dy);
if(dist<4)
{
curr.pos.x= x1;
curr.pos.y= y1;
}
else
{
continueAnimation++;
}
v[i].g.setAttribute("transform","translate("+ v[i].pos.x+","+v[i].pos.y+")");
}
if(continueAnimation>0)
{
setTimeout('nextStep()', ELAPSED_TIME);
}
else
{
animated=false;
}
}
]]></script>
</head>
<body onload="init()">
<div style="font-size:10pt;"><span style="font-size:18pt;"><b>The Nature Network</b></span> created by <a href="http://plindenbaum.blogspot.com">Pierre Lindenbaum PhD 2007</a>. <cite>Learning a little more javascript and <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> I've used the <a href="nn.js">data</a> available from the <a href="http://network.nature.com/">Nature Network</a> on May 13, 2007 to display an interactive map of the network based on <a href="http://www.w3.org/Graphics/SVG/">SVG</a>, <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> and javascript</cite>. This page was tested on <a href="http://www.mozilla.com/en-US/firefox/">Firefox 2</a></div>
<div id="layers" style="position:relative;width:100%; height:100px; background:lightgray;">
<div id="groupLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="tagLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="jobLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="affiliationLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="profileLayer" style="position:absolute; top:0px; left:0px;width:100%; font-size:9pt;">
<table width="100%"><tr>
<td width="33">
<a id="profileAnchor">
<img id="profileImg" width="33" height="42" src="http://network.nature.com/images/user_generic_large.gif"/>
</a>
</td>
<td>
<b>Name :</b> <span id="profileName"></span>
<b>Job :</b> <span id="profileJob"></span>
<b>Affiliation :</b> <span id="profileAffiliation"></span>
<b>www :</b> <span id="profileWWW"></span>
<b>Tags :</b> <span id="profileTags"></span>
<b>Groups :</b> <span id="profileGroups"></span>
</td></tr></table></div>
</div><br/>
<svg id="svgdoc" xmlns="http://www.w3.org/2000/svg" version="1.1" width="100" height="100">
<defs>
<linearGradient id = "gradient1" x1 = "50%" y1 = "0%" x2 = "50%" y2 = "100%">
<stop stop-color = "white" offset = "0%"/>
<stop stop-color = "black" offset = "100%"/>
</linearGradient>
</defs>
<rect id="frameSVGRect" x="0" y="0" width="100" height="100" fill="lightgray" stroke="black"/>
</svg>
<div>Pierre Lindenbaum: plindenbaum ( a t ) yahoo ( d o t ) fr </div>
<!-- google analytics -->
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "XXXX-2";
urchinTracker();
</script>
<!-- google analytics -->
</body>
</html>
nn.js
var network= {
"profile":[
{
"id": "U78424D59",
"name":"Laurence Daheron",
"job":"",
"affiliation":"",
"www":"",
"img":"",
"tags":[],
"pos":{"x":0,"y":0},
"goal":{"x":0,"y":0},
"g":null
}
,
{
"id": "UAD17B888",
"name":"Duncan Hull",
"job":"Researcher",
"affiliation":"University of Manchester, UK",
"www":"http://www.cs.man.ac.uk/~hulld/",
"img":"http://network.nature.com/assets/user/000/001/271/large.png",
"tags":["bioinformatics"],
"pos":{"x":0,"y":0},
"goal":{"x":0,"y":0},
"g":null
}
(...)
],
"group":[
{
"uri": "G478D9F2D",
"name":"Physics Education",
"members":["U3CFE0669"] }
,
(...)
],
"link":[
["U78424D59","U363EDF9C"],
["U400B1F54","UE19877E8"],
(...)
]
};
nn.rdf
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:nn="http://network.nature.com/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<foaf:Person rdf:about="http://network.nature.com/profile/U78424D59">
<foaf:name>Laurence Daheron</foaf:name>
</foaf:Person>
<foaf:Person rdf:about="http://network.nature.com/profile/UAD17B888">
<foaf:name>Duncan Hull</foaf:name>
<nn:job>Researcher</nn:job>
<nn:affiliation>University of Manchester, UK</nn:affiliation>
<foaf:weblog rdf:resource="http://www.cs.man.ac.uk/~hulld/"/>
<foaf:img rdf:resource="http://network.nature.com/assets/user/000/001/271/large.png"/>
<dc:subject>bioinformatics</dc:subject>
</foaf:Person>
<foaf:Person rdf:about="http://network.nature.com/profile/U337FD68F">
<foaf:name>Peter Kirkpatrick</foaf:name>
<nn:job>Chief Editor</nn:job>
<nn:affiliation>Nature Reviews Drug Discovery</nn:affiliation>
<foaf:weblog rdf:resource="http://www.nature.com/nrd"/>
<dc:subject>biotechnology</dc:subject>
<dc:subject>drug discovery</dc:subject>
</foaf:Person>
(...)
<foaf:Group rdf:about="http://network.nature.com/group/G478D9F2D">
<foaf:name>Physics Education</foaf:name>
<foaf:member rdf:resource="http://network.nature.com/profile/U3CFE0669"/>
</foaf:Group>
<foaf:Group rdf:about="http://network.nature.com/group/SciDev">
<foaf:name>Science for Development</foaf:name>
<foaf:member rdf:resource="http://network.nature.com/profile/U9C60B47D"/>
<foaf:member rdf:resource="http://network.nature.com/profile/U78C3A2B2"/>
<foaf:member rdf:resource="http://network.nature.com/profile/U5F7D848C"/>
<foaf:member rdf:resource="http://network.nature.com/profile/UE8DA26E7"/>
</foaf:Group>
(...)
(...)
<nn:Connection rdf:nodeId="1">
<nn:contact rdf:resource="http://network.nature.com/profile/U78424D59"/>
<nn:contact rdf:resource="http://network.nature.com/profile/U363EDF9C"/>
</nn:Connection>
<nn:Connection rdf:nodeId="2">
<nn:contact rdf:resource="http://network.nature.com/profile/U400B1F54"/>
<nn:contact rdf:resource="http://network.nature.com/profile/UE19877E8"/>
</nn:Connection>
<nn:Connection rdf:nodeId="3">
<nn:contact rdf:resource="http://network.nature.com/profile/U400B1F54"/>
<nn:contact rdf:resource="http://network.nature.com/profile/U2929A0EA"/>
</nn:Connection>
(...)
</rdf:RDF>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:nn="http://network.nature.com/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<foaf:Person rdf:about="http://network.nature.com/profile/U78424D59">
<foaf:name>Laurence Daheron</foaf:name>
</foaf:Person>
<foaf:Person rdf:about="http://network.nature.com/profile/UAD17B888">
<foaf:name>Duncan Hull</foaf:name>
<nn:job>Researcher</nn:job>
<nn:affiliation>University of Manchester, UK</nn:affiliation>
<foaf:weblog rdf:resource="http://www.cs.man.ac.uk/~hulld/"/>
<foaf:img rdf:resource="http://network.nature.com/assets/user/000/001/271/large.png"/>
<dc:subject>bioinformatics</dc:subject>
</foaf:Person>
<foaf:Person rdf:about="http://network.nature.com/profile/U337FD68F">
<foaf:name>Peter Kirkpatrick</foaf:name>
<nn:job>Chief Editor</nn:job>
<nn:affiliation>Nature Reviews Drug Discovery</nn:affiliation>
<foaf:weblog rdf:resource="http://www.nature.com/nrd"/>
<dc:subject>biotechnology</dc:subject>
<dc:subject>drug discovery</dc:subject>
</foaf:Person>
(...)
<foaf:Group rdf:about="http://network.nature.com/group/G478D9F2D">
<foaf:name>Physics Education</foaf:name>
<foaf:member rdf:resource="http://network.nature.com/profile/U3CFE0669"/>
</foaf:Group>
<foaf:Group rdf:about="http://network.nature.com/group/SciDev">
<foaf:name>Science for Development</foaf:name>
<foaf:member rdf:resource="http://network.nature.com/profile/U9C60B47D"/>
<foaf:member rdf:resource="http://network.nature.com/profile/U78C3A2B2"/>
<foaf:member rdf:resource="http://network.nature.com/profile/U5F7D848C"/>
<foaf:member rdf:resource="http://network.nature.com/profile/UE8DA26E7"/>
</foaf:Group>
(...)
(...)
<nn:Connection rdf:nodeId="1">
<nn:contact rdf:resource="http://network.nature.com/profile/U78424D59"/>
<nn:contact rdf:resource="http://network.nature.com/profile/U363EDF9C"/>
</nn:Connection>
<nn:Connection rdf:nodeId="2">
<nn:contact rdf:resource="http://network.nature.com/profile/U400B1F54"/>
<nn:contact rdf:resource="http://network.nature.com/profile/UE19877E8"/>
</nn:Connection>
<nn:Connection rdf:nodeId="3">
<nn:contact rdf:resource="http://network.nature.com/profile/U400B1F54"/>
<nn:contact rdf:resource="http://network.nature.com/profile/U2929A0EA"/>
</nn:Connection>
(...)
</rdf:RDF>
Pierre, read this item much earlier, but only now realized that it is only JavaScript, unlike your earlier FOAF Java program! That's amazing!
ReplyDelete