Cytoscape.js 是一个开源的 JavaScript 图形库,用以数据分析和可视化。Cytoscape.js 可以轻松的继承到网站或 Web 应用中,实现交互的可视化图形。
使用cytoscape.js至少需要cytoscape.js 和 jquery.js 这两个js文件,两者都可以在http://cytoscape.github.io/cytoscape.js/下载得到
关于cytoscape的方法官网http://cytoscape.github.io/cytoscape.js/已有详细的介绍,在此也不再多言,接下来讲的是关于cytoscape.js在实际应用中的例子。
首先先把一个简单的代码例子展示一遍:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script src="cytoscape.js-2.0.4/jQuery-min.js"></script>
<script src="cytoscape.js-2.0.4/cytoscape.min.js"></script>
<script src="cytoscape.js-2.0.4/cytoscape.js"></script>
<script src="cytoscape.js-2.0.4/arbor.js"></script>
<title>Graph</title>
<STYLE TYPE="text/css">
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
}
</STYLE>
</head>
<body>
<div id="cy"></div>
<script type="text/javascript">
</script>
</body>
<script type="text/javascript">
$('#cy').cytoscape({
elements: [
{ // node n1
group: 'nodes', // 'nodes' for a node, 'edges' for an edge
data: { // element data (put dev data here)
id: 'n1', // mandatory for each element, assigned automatically on undefined
parent: 'nparent', // indicates the compound node parent id; not defined => no parent
weight: 0,
},
position: { // the model position of the node (optional on init, mandatory after)
x: 100,
y: 100
},
selected: false, // whether the element is selected (default false)
selectable: true, // whether the selection state is mutable (default true)
locked: false, // when locked a node's position is immutable (default false)
grabbable: true, // whether the node can be grabbed and moved by the user
classes: 'foo bar' // a space separated list of class names that the element has
},
{ // node n2
group: 'nodes',
data: { id: 'n2' , weight: 10},
renderedPosition: { x: 100, y: 200 } // can alternatively specify position in rendered on-screen pixels
},
{ // node n3
group: 'nodes',
data: { id: 'n3', parent: 'nparent', weight: 60, },
position: { x: 123, y: 234 }
},
{ // node nparent
group: 'nodes',
data: { id: 'nparent' }
},
{ // edge e1
group: 'edges',
data: {
id: 'e1',
source: 'n1', // the source node id (edge comes from this node)
target: 'n2' // the target node id (edge goes to this node)
}
}
],
// so we can see the ids
style: [
{
selector: 'node',
css: {
'content': 'data(weight)',
'background-color':'red'
}
}
]
});
</script>
</html>
需要指出的是不会有人会像上面这个例子一样事先把所有的数据都黏贴在准备展示的web页面上(这么说有点绝对了,但大多数人都会选择将数据存在数据库中或哪里,需要时获取)。因此第一步改造就是要将数据部分的代码从网页中抽出,转而用一种更灵活的方式来注入数据。
cytoscape.js提供了一个cy.add()的功能能够随时添加点边信息,因此我们只需要将数据临时导入即可,具体的方法如下所示:
var cy = $("#cy").cytoscape("get");
到此,我们已经可以顺利的地将数据从我们想要的地方注入到页面中,接下来就是如何更好的展示数据了
首先就是结点的信息,假设结点代表基因,为了更好的显示基因之间的表达量的不同,我们可以从颜色上将他们区分开来,mapData()这个方法可以使相关属性基于指定参数值而发生渐变式变化,比如我们可以是结点背景颜色根据结点的权值(weight)的大小发生从红到蓝的渐变式变化,我们也可以是结点的大小随这结点的高度值(height)而发生相应的变化,具体实施代码如下:
'background-color': 'mapData(weight,0,50,blue,red)'
【当weight接近于0时背景颜色是蓝的,当weight接近于50时背景颜色是红色,在两者之间则偏向于紫色】
如果不考虑网络分布的话,到此就算是大功告成了,但是这样事实上,当网络展示的时候会发现所有的点都集中在一条水平向上,这使得网络特别混乱,无法很好的展示点与点之间的连接关系,因此cytoscape,js提供了多种分布cy.layout()效果(当然你也可以自己定义一个),具体效果可到http://cytoscape.github.io/cytoscape.js/#core/visuals/cy.layout进行预览。
在此我们选择circle效果进行演示,代码如下:
options = {
name: 'circle',
fit: true, // whether to fit the viewport to the graph
ready: undefined, // callback on layoutready
stop: undefined, // callback on layoutstop
rStepSize: 10, // the step size for increasing the radius if the nodes don't fit on screen
padding: 30, // the padding on fit
startAngle: 3/2 * Math.PI, // the position of the first node
counterclockwise: false // whether the layout should go counterclockwise (true) or clockwise (false)
};
cy.layout( options );
另外,cytoscape.js还提供了删减结点边,计算结点度数等一系列有趣的功能,感兴趣的话可以附加这些功能到网页上,在这里就不多做操作了。
下面是网页的完整代码:
<%@ page language="java" import="java.util.*,hmu.Graphbeans.*" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script src="js/jQuery-min.js"></script>
<script src="js/cytoscape.min.js"></script>
<title>Graph</title>
<STYLE TYPE="text/css">
#cy {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
}
</STYLE>
</head>
<body>
<div id="cy"></div>
</body>
<script type="text/javascript">
$('#cy').cytoscape({
style: cytoscape.stylesheet()
.selector('node')
.css({
'content': 'data(id)',
'font-family': 'helvetica',
'font-size': 20,
'text-valign': 'center',
'opacity':0.7,
'color': '#333333',
'width':100,
'height':100,
'border-color': '#fff',
'background-color': 'mapData(weight,0,50,blue,red)',
'border-width':10
})
.selector(':selected')
.css({
'content': 'data(weight)',
'background-color': '#000',
'line-color': '#000',
'target-arrow-color': '#000',
'text-outline-color': '#000'
})
.selector('node:selected')
.css({
'content': 'data(weight)',
'background-color': 'green',
'text-outline-color': '#000'
})
.selector('edge')
.css({
'width': 2,
'target-arrow-shape': 'triangle',
'width':'3',
'line-color':'blue'
})
// so we can see the ids
});
var cy = $("#cy").cytoscape("get");
<% ArrayList<Node> nodes = (ArrayList<Node>)session.getAttribute("nodeInfor");
for(int i=0;i<nodes.size();i++){
%>
var tmpId="<%=nodes.get(i).getId()%>";
var tmpWeight = <%=nodes.get(i).getWeight()%>;
cy.add({group: "nodes", data: { id: tmpId , weight: tmpWeight}});
<%
}
%>
<% ArrayList<Edge> edges = (ArrayList<Edge>)session.getAttribute("edgeInfor");
for(int i=0;i<edges.size();i++){
%>
var tmpSource="<%=edges.get(i).getSource()%>";
var tmpTarget="<%=edges.get(i).getTarget()%>";
cy.add({ group: "edges", data: { source: tmpSource, target: tmpTarget } });
<%
}
%>
options = {
name: 'circle',
fit: true, // whether to fit the viewport to the graph
ready: undefined, // callback on layoutready
stop: undefined, // callback on layoutstop
rStepSize: 10, // the step size for increasing the radius if the nodes don't fit on screen
padding: 30, // the padding on fit
startAngle: 3/2 * Math.PI, // the position of the first node
counterclockwise: false // whether the layout should go counterclockwise (true) or clockwise (false)
};
cy.layout( options );
</script>
</html>