2017-03-25 15 views
0

我有以下簡單的圖形可視化:D3通過點擊拓展力網絡 - 不工作

sample graph

首先,只有「N1」正在連接到它的鄰居「N2」

二點擊'n2'後,網絡通過增加兩條邊'n2' - >'n3'和'n2' - >'n4'進行擴展。

第三,單擊'n3'後,網絡通過添加兩個邊緣'n3' - >'n1'和'n3' - >'n5'。我想我可以創建一個可見的節點數組。通過單擊節點,該節點名稱將被添加到數組中。然後在tick()函數中,該數組中的可見節點將用於設置網絡。

但是,當我用下面的代碼這樣做:

// return currently visible nodes as the source nodes 
// e.g. called by 
// var visible_seeds = seeding(nodes); 
// 
function seeding(nodes){ 
    var visible = []; 
    nodes.forEach(function(n){ 
     if(n.name=='n1') 
     visible.push(n); 
    }); 
    return visible; 
} 

上述功能,然後調用如下:

var visible_seeds = seeding(nodes); 

var force = d3.layout.force() 
.nodes(d3.values(visible_seeds)) 
.links(links) 
.size([width, height]) 
.linkDistance(60) 
.charge(-300) 
.on("tick", tick) 
.start(); 

它給人的錯誤

index.html TypeError:'undefined' is not a function (evaluating 'nodes.forEach(function(n){...')

是什麼正確的方式來實現我的目標?謝謝你!

p.s.我絕對是D3和JavaScript的新手。

由D3讀入的數據是從一個CSV文件:

source,target,value 
n1,n2,1.0 
n2,n3,1.0 
n2,n4,1.0 
n3,n1,1.0 
n3,n5,1.0 

我的代碼是基於例如在: http://bl.ocks.org/d3noob/5155181

添加 - 源代碼 - 問題發生在函數播種()

<!DOCTYPE html> 
<meta charset="utf-8"> 
<script src="http://d3js.org/d3.v3.js"></script> 
<style> 

path.link { 
    fill: none; 
    stroke: #666; 
    stroke-width: 1.5px; 
} 

path.link.twofive { 
    opacity: 0.25; 
} 

path.link.fivezero { 
    opacity: 0.50; 
} 

path.link.sevenfive { 
    opacity: 0.75; 
} 

path.link.onezerozero { 
    opacity: 1.0; 
} 

circle { 
    fill: #ccc; 
    stroke: #fff; 
    stroke-width: 1.5px; 
} 

text { 
    fill: #000; 
    font: 10px sans-serif; 
    pointer-events: none; 
} 

</style> 
<body> 
<script> 


// get the data 
d3.csv("force.csv", function(error, links) { 

var nodes = {}; 

// Compute the distinct nodes from the links. 
links.forEach(function(link) { 
    link.source = nodes[link.source] || (nodes[link.source] = {name: link.source, vis:0}); 
    link.target = nodes[link.target] || (nodes[link.target] = {name: link.target, vis:0}); 
    link.value = +link.value; 
}); 

//nodes["n1"] = {name:"n1", vis:1}; 
var vis = ['n1']; 

var width = 960, 
    height = 500; 

function seeding(){ 
var visible = []; 
nodes.forEach(function(n){ 
    if(n.name=='n1') 
     visible.push(n); 
}); 
return visible; 
} 

var visible_seeds = seeding(); 

var force = d3.layout.force() 
    .nodes(d3.values(visible_seeds)) 
    //.nodes(d3.values(nodes)) 
    .links(links) 
    .size([width, height]) 
    .linkDistance(60) 
    .charge(-300) 
    .on("tick", tick) 
    .start(); 

// Set the range 
var v = d3.scale.linear().range([0, 100]); 

// Scale the range of the data 
v.domain([0, d3.max(links, function(d) { return d.value; })]); 

// asign a type per value to encode opacity 
links.forEach(function(link) { 
    if (v(link.value) <= 25) { 
     link.type = "twofive"; 
    } else if (v(link.value) <= 50 && v(link.value) > 25) { 
     link.type = "fivezero"; 
    } else if (v(link.value) <= 75 && v(link.value) > 50) { 
     link.type = "sevenfive"; 
    } else if (v(link.value) <= 100 && v(link.value) > 75) { 
     link.type = "onezerozero"; 
    } 
}); 

var svg = d3.select("body").append("svg") 
    .attr("width", width) 
    .attr("height", height); 

// build the arrow. 
svg.append("svg:defs").selectAll("marker") 
    .data(["end"])  // Different link/path types can be defined here 
    .enter().append("svg:marker") // This section adds in the arrows 
    .attr("id", String) 
    .attr("viewBox", "0 -5 10 10") 
    .attr("refX", 15) 
    .attr("refY", -1.5) 
    .attr("markerWidth", 6) 
    .attr("markerHeight", 6) 
    .attr("orient", "auto") 
    .append("svg:path") 
    .attr("d", "M0,-5L10,0L0,5"); 

// add the links and the arrows 
var path = svg.append("svg:g").selectAll("path") 
    .data(force.links()) 
    .enter().append("svg:path") 
    .attr("class", function(d) { return "link " + d.type; }) 
    .attr("marker-end", "url(#end)"); 

// define the nodes 
var node = svg.selectAll(".node") 
    .data(force.nodes()) 
    .enter().append("g") 
    .attr("class", "node") 
    .on("click", click) 
    .on("dblclick", dblclick) 
    .call(force.drag); 

// add the nodes 
node.append("circle") 
    .attr("r", 5); 

// add the text 
node.append("text") 
    .attr("x", 12) 
    .attr("dy", ".35em") 
    .text(function(d) { return d.name; }); 

// add the curvy lines 
function tick() { 
    path.attr("d", function(d) { 
     var dx = d.target.x - d.source.x, 
      dy = d.target.y - d.source.y, 
      dr = Math.sqrt(dx * dx + dy * dy); 
     return "M" + 
      d.source.x + "," + 
      d.source.y + "A" + 
      dr + "," + dr + " 0 0,1 " + 
      d.target.x + "," + 
      d.target.y; 
    }); 

    node 
     .attr("transform", function(d) { 
      return "translate(" + d.x + "," + d.y + ")"; }); 
} 

// action to take on mouse click 
function click() { 
    d3.select(this).select("text").transition() 
     .duration(750) 
     .attr("x", 22) 
     .style("fill", "steelblue") 
     .style("stroke", "lightsteelblue") 
     .style("stroke-width", ".5px") 
     .style("font", "20px sans-serif"); 
    d3.select(this).select("circle").transition() 
     .duration(750) 
     .attr("r", 16) 
     .style("fill", "lightsteelblue"); 
} 

// action to take on mouse double click 
function dblclick() { 
    d3.select(this).select("circle").transition() 
     .duration(750) 
     .attr("r", 6) 
     .style("fill", "#ccc"); 
    d3.select(this).select("text").transition() 
     .duration(750) 
     .attr("x", 12) 
     .style("stroke", "none") 
     .style("fill", "black") 
     .style("stroke", "none") 
     .style("font", "10px sans-serif"); 
} 

}); 

</script> 
</body> 
</html> 
+0

'類型錯誤: '未定義' 是不是一個function'意味着節點是** **不是一個JavaScript數組。如果是'd3'選擇,請嘗試'nodes.each(function ...)',否則您需要向我們展示更多代碼。 – Mark

+0

感謝您的評論!代碼被添加到問題中。 – askmeasku

回答

0

首先,您的錯誤的來源是因爲nodes是一個JavaScript對象(不是數組),它沒有forEach方法。

其次,我不確定你要採用什麼方法。你開始一個節點的力佈局,然後呢?點擊你附加其他節點?你將如何處理鏈接?

Intead,我建議只是點擊節點和鏈接的可見性。這裏有一個快速的實現:

<!DOCTYPE html> 
 
<meta charset="utf-8"> 
 
<script src="http://d3js.org/d3.v3.js"></script> 
 
<style> 
 

 
path.link { 
 
    fill: none; 
 
    stroke: #666; 
 
    stroke-width: 1.5px; 
 
} 
 

 
path.link.twofive { 
 
    opacity: 0.25; 
 
} 
 

 
path.link.fivezero { 
 
    opacity: 0.50; 
 
} 
 

 
path.link.sevenfive { 
 
    opacity: 0.75; 
 
} 
 

 
path.link.onezerozero { 
 
    opacity: 1.0; 
 
} 
 

 
circle { 
 
    fill: #ccc; 
 
    stroke: #fff; 
 
    stroke-width: 1.5px; 
 
} 
 

 
text { 
 
    fill: #000; 
 
    font: 10px sans-serif; 
 
    pointer-events: none; 
 
} 
 

 
</style> 
 
<body> 
 
<script> 
 

 

 
// get the data 
 
//d3.csv("force.csv", function(error, links) { 
 

 
var links = [{"source":"n1","target":"n2","value":"1.0"},{"source":"n2","target":"n3","value":"1.0"},{"source":"n2","target":"n4","value":"1.0"},{"source":"n3","target":"n1","value":"1.0"},{"source":"n3","target":"n5","value":"1.0 "}]; 
 

 
var nodes = {}; 
 

 
// Compute the distinct nodes from the links. 
 
links.forEach(function(link) { 
 
    link.source = nodes[link.source] || (nodes[link.source] = {name: link.source, vis:0}); 
 
    link.target = nodes[link.target] || (nodes[link.target] = {name: link.target, vis:0}); 
 
    link.value = +link.value; 
 
}); 
 

 
//nodes["n1"] = {name:"n1", vis:1}; 
 
var vis = ['n1']; 
 

 
var width = 300, 
 
    height = 300; 
 

 
for (key in nodes){ 
 
    var node = nodes[key]; 
 
    node.visible = (node.name === 'n1'); 
 
} 
 

 
var force = d3.layout.force() 
 
    .nodes(d3.values(nodes)) 
 
    .links(links) 
 
    .size([width, height]) 
 
    .linkDistance(60) 
 
    .charge(-300) 
 
    .on("tick", tick) 
 
    .start(); 
 

 
// Set the range 
 
var v = d3.scale.linear().range([0, 100]); 
 

 
// Scale the range of the data 
 
v.domain([0, d3.max(links, function(d) { return d.value; })]); 
 

 
// asign a type per value to encode opacity 
 
links.forEach(function(link) { 
 
    if (v(link.value) <= 25) { 
 
     link.type = "twofive"; 
 
    } else if (v(link.value) <= 50 && v(link.value) > 25) { 
 
     link.type = "fivezero"; 
 
    } else if (v(link.value) <= 75 && v(link.value) > 50) { 
 
     link.type = "sevenfive"; 
 
    } else if (v(link.value) <= 100 && v(link.value) > 75) { 
 
     link.type = "onezerozero"; 
 
    } 
 
}); 
 

 
var svg = d3.select("body").append("svg") 
 
    .attr("width", width) 
 
    .attr("height", height); 
 

 
// build the arrow. 
 
svg.append("svg:defs").selectAll("marker") 
 
    .data(["end"])  // Different link/path types can be defined here 
 
    .enter().append("svg:marker") // This section adds in the arrows 
 
    .attr("id", String) 
 
    .attr("viewBox", "0 -5 10 10") 
 
    .attr("refX", 15) 
 
    .attr("refY", -1.5) 
 
    .attr("markerWidth", 6) 
 
    .attr("markerHeight", 6) 
 
    .attr("orient", "auto") 
 
    .append("svg:path") 
 
    .attr("d", "M0,-5L10,0L0,5"); 
 

 
// add the links and the arrows 
 
var path = svg.append("svg:g").selectAll("path") 
 
    .data(force.links()) 
 
    .enter().append("svg:path") 
 
    .attr("class", function(d) { return "link " + d.type; }) 
 
    .attr("marker-end", "url(#end)") 
 
    .style("opacity", function(d){ 
 
     return d.target.visible && d.source.visible ? 1 : 0; 
 
    }) 
 

 
// define the nodes 
 
var node = svg.selectAll(".node") 
 
    .data(force.nodes()) 
 
    .enter().append("g") 
 
    .attr("class", "node") 
 
    .style("opacity", function(d){ 
 
     return d.visible ? 1 : 0; 
 
    }) 
 
    .on("click", click) 
 
    .call(force.drag); 
 

 
// add the nodes 
 
node.append("circle") 
 
    .attr("r", 5); 
 

 
// add the text 
 
node.append("text") 
 
    .attr("x", 12) 
 
    .attr("dy", ".35em") 
 
    .text(function(d) { return d.name; }); 
 

 
// add the curvy lines 
 
function tick() { 
 
    path.attr("d", function(d) { 
 
     var dx = d.target.x - d.source.x, 
 
      dy = d.target.y - d.source.y, 
 
      dr = Math.sqrt(dx * dx + dy * dy); 
 
     return "M" + 
 
      d.source.x + "," + 
 
      d.source.y + "A" + 
 
      dr + "," + dr + " 0 0,1 " + 
 
      d.target.x + "," + 
 
      d.target.y; 
 
    }); 
 

 
    node 
 
     .attr("transform", function(d) { 
 
      return "translate(" + d.x + "," + d.y + ")"; }); 
 
} 
 

 
// action to take on mouse click 
 
function click(d0) { 
 
    links.forEach(function(d1){ 
 
    console.log(d0, d1.source) 
 
    if (d1.source === d0) { 
 
     d1.target.visible = !d1.target.visible; 
 
    } 
 
    }); 
 
    node.style("opacity", function(d){ 
 
    return d.visible ? 1 : 0; 
 
    }); 
 
    path.style("opacity", function(d){ 
 
    return d.target.visible && d.source.visible ? 1 : 0; 
 
    }); 
 
} 
 

 
//}); 
 

 
</script> 
 
</body> 
 
</html>

+0

謝謝馬克,你幾乎解決了它。我想要的是增量顯示(通過擴展)圖。想象一下,不僅僅有5個節點 - 計算它們都可能很慢,所以我想從一個節點開始。通過點擊它,我們將加載它的相鄰節點,當然也會加載它們之間的鏈接。單擊其中一個相鄰節點將添加新的相鄰節點和邊,等等。 – askmeasku