我想在运行时更新强制定向布局的链接和节点。但这种行为很奇怪,因为有时它不会添加新的链接,有时也不会删除旧的链接。你有什么建议吗?
任何帮助都将不胜感激。
这是我的代码:
network.js
// Network View size
var width = 1280,
height = 500
var radius = 200;
var robj = 8;
var scaled_radius = d3.scale.linear()
.domain([0, 7000000000000])
.range([10, 40]);
var svg_network = d3.select(document.createElementNS(d3.ns.prefix.svg, "svg"))
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.gravity(1.0)
.distance(100)
.charge(-60)
.size([width, height]);
var timestamp_info = svg_network.append("text")
.attr("dx", 10)
.attr("dy", 10);
function graph_network_start(flowz)
{
/* ------------------------ */
/* DATA PREPROCESSING */
/* ------------------------ */
flowz = network_preprocess(flowz)
/* ------------------------ */
/* NODE POSITIONING */
/* ------------------------ */
nodePositioning(flowz.activeNodes);
var x = d3.scale.linear().domain([0, flowz.activeNodes.length]).range([0, 180]);
// Bind link and node data to DOM elements
var link = svg_network.selectAll(".link").data(flowz.flow, function(d) { return d.source + "-" + d.target; });
var node = svg_network.selectAll("g.node").data(flowz.activeNodes)//, function(d,i) {return i;});
/* ------------------------ */
/* UPDATE LINKS */
/* ------------------------ */
link.exit().transition().duration(10).remove();
link.enter().append("line")
.attr("id",function(d){return d.source.ID + "-" + d.target.ID;})
.attr("class", "link")
.style("opacity",0)
.transition()
.duration(1000)
.style("opacity",1);
/* ------------------------ */
/* UPDATE NODES */
/* ------------------------ */
node.exit().transition().duration(100).remove();
var newNode = node.enter().append("svg:g")
.attr("id", function(d) {return d.ID})
.attr("class", "node")
.call(force.drag).on("click", function(d){
if (nodes[d.ID].name == "Workstations Site 1") {
selectedSite=1; bubble_visualize(selectedTimestamp, selectedSite);showMode(2);
} else if (nodes[d.ID].name == "Workstations Site 2") {
selectedSite=2; bubble_visualize(selectedTimestamp, selectedSite);showMode(2);
} else if (nodes[d.ID].name == "Workstations Site 3") {
selectedSite=3; bubble_visualize(selectedTimestamp, selectedSite);showMode(2);
}})
.on("mouseover", fade(.1)).on("mouseout", fade(1))
newNode.append("circle")
.attr("r", 8) //function(d){return scaled_radius(d.output)})
.style("fill", color)
.style("stroke", "black" )
.style("opacity",0)
.transition()
.duration(1000)
.style("opacity",1);
newNode.append("text")
.attr("dx", robj + 2)
.attr("dy", ".1em")
.text(function(d) { return nodes[d.ID].name })
.style("opacity",0)
.transition()
.duration(1000)
.style("opacity",1);
//if (nodes[d.ID].name == "Workstations Site" || nodes[d.ID].name == "Workstations Site 2" || nodes[d.ID].name == "Workstations Site 3") return nodes[d.ID].name });
/* ------------------------ */
/* UPDATE INFOS */
/* ------------------------ */
timestamp_info.text(function(d) { return new Date(flowz.timestamp).toString()});
force
.nodes(flowz.activeNodes)
.links(flowz.flow)
.on("tick", tick);
force.start();
var linkedByIndex = {};
flowz.flow.forEach(function(d) {
linkedByIndex[d.source + "," + d.target] = 1;
});
function color(d){
return "steelblue"
}
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
function tick()
{
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
function fade(opacity) {
return function(d) {
node.style("stroke-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
};
}
function nodePositioning(nodesparam){
for (var i=0; i<nodesparam.length; i++)
{
if(nodes[nodesparam[i].ID].name == "Internet")
{
nodesparam[i].x=width/2;
nodesparam[i].y=30;
}
else if (nodes[nodesparam[i].ID].name == "Firewall")
{
nodesparam[i].x=width/2;
nodesparam[i].y=50;
}
else if (nodes[nodesparam[i].ID].name == "172.0.0.1")
{
nodesparam[i].x=width/2;
nodesparam[i].y=120;
}
else
{
nodesparam[i].x = width/2 + (radius - robj) * Math.cos(Math.PI + i * (Math.PI / nodesparam.length +1)) ;
nodesparam[i].y = height/2 + (radius - robj) * Math.sin(Math.PI + i * (Math.PI / nodesparam.length +1)) ;
}
nodesparam[i].fixed = true;
}
}
function positionX(node, index)
{
if(node.name == "Internet" || node.name == "Firewall" || node.name == "127.0.0.1")
{
return width/2;
}
else
{
return (width/2 + (radius - robj) * Math.cos(Math.PI + index * (Math.PI / nodes.length +1))) ;
}
}
function positionY(node, index)
{
if(node.name == "Internet")
{
return 30;
}
else if (node.name == "Firewall")
{
return 50;
}
else if (node.name == "172.0.0.1")
{
return 120;
}
else
{
return (height/2 + (radius - robj) * Math.sin(Math.PI + index * (Math.PI / nodes.length + 1))) ;;
}
}
function network_preprocess(flowz){
for(var i=+0; i<flowz.flow.length;i++)
{
for(var j=0; j<flowz.activeNodes.length;j++)
{
if(flowz.activeNodes[j].ID == flowz.flow[i].source)
flowz.flow[i].source = j;
if(flowz.activeNodes[j].ID == flowz.flow[i].target)
flowz.flow[i].target = j;
}
}
return flowz
}
}
links.json摘录
[
{
"timestamp": 1364795760000,
"flow": [
{
"source": 0,
"target": 1,
"value": 15540
}
],
"activeNodes": [
{
"output": 15540,
"ID": 0
},
{
"output": 0,
"ID": 1
}
]
},
{
"timestamp": 1364795880000,
"flow": [
{
"source": 2,
"target": 1,
"value": 2960
},
{
"source": 0,
"target": 1,
"value": 14800
}
],
"activeNodes": [
{
"output": 14800,
"ID": 0
},
{
"output": 0,
"ID": 1
},
{
"output": 2960,
"ID": 2
}
]
}
]
编辑:1 我可以缩小范围。我的代码正在销毁links.source和links.target元素。由于某种原因,它们从int更改为对象:
时间[0]的输出:
Object {source: 3, target: 4, value: 213143231}
Object {source: 5, target: 4, value: 448560}
当我转到另一个数据集并回到时间[0]时:
Object {source: Object, target: Object, value: "213143231"}
Object {source: Object, target: Object, value: "448560"}
甚至将值更改为字符串。也许它发生在这段代码摘录中:
function graph_network_start(flowz)
{
flowz = network_preprocess(flowz);
...
var link = svg_network.selectAll(".link").data(flowz.flow, function(d) { console.log(d); return d.source + "-" + d.target; });
var node = svg_network.selectAll("g.node").data(flowz.activeNodes, function(d) {return d.ID})
...
force
.nodes(flowz.activeNodes)
.links(flowz.flow)
.on("tick", tick);
force.start();
function network_preprocess(flowz){
/*
We have to remove not existing nodes and add new nodes to our network nodes
*/
// Remove every node which does not exist anymore
var activeSet = new HashSet();
activeSet.addAll(flowz.activeNodes);
var toRemove = network_nodes.complement(activeSet).values();
for(var i=0; i<toRemove.length; i++)
{
network_nodes.remove(toRemove[i]);
}
// Add new nodes
var activenodes = flowz.activeNodes;
for(var i=0; i<activenodes.length; i++)
{
if (! network_nodes.contains(activenodes[i]) )
{
network_nodes.add(activenodes[i])
}
}
// Order nodes
var ufzuffu = network_nodes.values();
ufzuffu.sort(function(a,b){
var keya = a.ID;
var keyb = b.ID;
if(keya < keyb) return -1;
if(keya > keyb) return 1;
return 0;
});
flowz.activeNodes = ufzuffu;
// Edit links
for(var i=0; i<flowz.flow.length;i++)
{
for(var j=0; j<flowz.activeNodes.length;j++)
{
if(flowz.activeNodes[j].ID == flowz.flow[i].source)
{
flowz.flow[i].source = +j;
continue;
}
if(flowz.activeNodes[j].ID == flowz.flow[i].target)
{
flowz.flow[i].target = +j;
}
}
}
return flowz
}
}
编辑2
嗯..看起来我发现了一个解决方法:
var link = svg_network.selectAll(".link").data(flowz.flow, function(d) {
if(d.source.hasOwnProperty("ID"))
return d.source.index + "-" + d.target.index;
else
return d.source + "-" + d.target;
});
它有效,但我仍然不知道它为什么随机(?)生成整数对象..