我正在关注D3的Force-based Label Placement教程。该示例带有随机生成的节点和随机链接。我正在尝试用我的数据替换这些随机元素。下面是代码,我根据原始代码中给出的信息(即获得注释的部分)注释掉原始部分以添加我自己的数据。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<meta charset="utf-8">
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.6.0"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?2.6.0"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js?2.6.0"></script>
</head>
<body>
<script type="text/javascript" charset="utf-8">
var w = 960, h = 500;
var labelDistance = 0;
var vis = d3.select("body").append("svg:svg").attr("width", w).attr("height", h);
var nodes = [];
var labelAnchors = [];
var labelAnchorLinks = [];
var links = [];
// for(var i = 0; i < 30; i++) {
// var node = {
// label : "node " + i
// };
// nodes.push(node);
// labelAnchors.push({
// node : node
// });
// labelAnchors.push({
// node : node
// });
// };
// for(var i = 0; i < nodes.length; i++) {
// for(var j = 0; j < i; j++) {
// if(Math.random() > .95)
// links.push({
// source : i,
// target : j,
// weight : Math.random()
// });
// }
// labelAnchorLinks.push({
// source : i * 2,
// target : i * 2 + 1,
// weight : 1
// });
// };
// my own code to insert data starts here.
nodes = [
{label : "A"},
{label : "B"},
{label : "C"}
];
labelAnchors = [
{node : "A"},
{node : "B"},
{node : "C"}
];
links = [
{source : "A", target : "B", weight: 10},
{source : "B", target : "C", weight: 1},
{source : "C", target : "A", weight: 5}
];
labelAnchorLinks = [
{source : "A", target : "B", weight: 10},
{source : "B", target : "C", weight: 1},
{source : "C", target : "A", weight:5}
];
// my own code to insert data ends here.
var force = d3.layout.force().size([w, h]).nodes(nodes).links(links).gravity(1).linkDistance(50).charge(-3000).linkStrength(function(x) {
return x.weight * 10
});
force.start();
var force2 = d3.layout.force().nodes(labelAnchors).links(labelAnchorLinks).gravity(0).linkDistance(0).linkStrength(8).charge(-100).size([w, h]);
force2.start();
var link = vis.selectAll("line.link").data(links).enter().append("svg:line").attr("class", "link").style("stroke", "#CCC");
var node = vis.selectAll("g.node").data(force.nodes()).enter().append("svg:g").attr("class", "node");
node.append("svg:circle").attr("r", 5).style("fill", "#555").style("stroke", "#FFF").style("stroke-width", 3);
node.call(force.drag);
// bind data to links
var anchorLink = vis.selectAll("line.anchorLink").data(labelAnchorLinks)//.enter().append("svg:line").attr("class", "anchorLink").style("stroke", "#999");
// bind data and text to circles
var anchorNode = vis.selectAll("g.anchorNode").data(force2.nodes()).enter().append("svg:g").attr("class", "anchorNode");
anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF");
anchorNode.append("svg:text").text(function(d, i) {
return i % 2 == 0 ? "" : d.node.label
}).style("fill", "#555").style("font-family", "Arial").style("font-size", 12);
var updateLink = function() {
this.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;
});
}
var updateNode = function() {
this.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
force.on("tick", function() {
force2.start();
node.call(updateNode);
anchorNode.each(function(d, i) {
if(i % 2 == 0) {
d.x = d.node.x;
d.y = d.node.y;
} else {
var b = this.childNodes[1].getBBox();
var diffX = d.x - d.node.x;
var diffY = d.y - d.node.y;
var dist = Math.sqrt(diffX * diffX + diffY * diffY);
var shiftX = b.width * (diffX - dist) / (dist * 2);
shiftX = Math.max(-b.width, Math.min(0, shiftX));
var shiftY = 5;
this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")");
}
});
anchorNode.call(updateNode);
link.call(updateLink);
anchorLink.call(updateLink);
});
</script>
</body>
</html>
所以我得到的错误是TypeError: neighbors[o.source.index] is undefined
......出了什么问题?
答案 0 :(得分:0)
您没有采用示例数据结构,因此图表会中断。
让我们一块一块地看一下。
首先,原始节点生成器:
for(var i = 0; i < 30; i++) {
var node = {
label : "node " + i
};
nodes.push(node);
labelAnchors.push({
node : node
});
labelAnchors.push({
node : node
});
};
这里你部分正常,节点数组很好,但labelAnchor
是你第一个偏离示例的地方:
labelAnchors = [
{node : "A"},
{node : "B"},
{node : "C"}
];
这不会复制节点,也不会将节点放在较新的对象中:{node:node}
。您可以通过以下方式解决此问题:
var labelAnchors = [];
nodes.forEach(function(n) {
labelAnchors.push({node:n},{node:n});
})
现在让我们回顾一下原始链接的生成方式:
for(var i = 0; i < nodes.length; i++) {
for(var j = 0; j < i; j++) {
if(Math.random() > .95)
links.push({
source : i,
target : j,
weight : Math.random()
});
}
labelAnchorLinks.push({
source : i * 2,
target : i * 2 + 1,
weight : 1
});
};
您的代码中的第一个问题是您将字符串指定为节点ID,这会导致链接无法访问与其连接的节点。在d3v2中我们需要使用目标节点的索引(在d3v3 / 4/5中我们可以设置节点的id属性以避免需要跟踪索引)。
要修复我们可以使用的主要链接:
links = [
{source : 0, target : 1, weight: Math.random()},
{source : 1, target : 2, weight: Math.random()},
{source : 2, target : 0, weight: Math.random()}
];
最后,我们可以看到该示例对索引使用了一些操作来链接锚点和标签。您最初使用的是字符串,但我们需要使用这些修改后的索引。来源将是i * 2,目标将是i * 2 + 1:
labelAnchorLinks = [
{source : 0*2, target : 0*2+1, weight: 1},
{source : 1*2, target : 1*2+1, weight: 1},
{source : 2*2, target : 2*2+1, weight: 1}
];
或者,由于标签锚链接必须将锚节点与正确的标签节点链接,我们可以自动设置:
var labelAnchorLinks = [];
nodes.forEach(function(d,i) {
var link = {};
link.source = i * 2;
link.target = i * 2 + 1;
link.weight = 1;
labelAnchorLinks.push(link);
})
鉴于这一切,我们应该能够获得一个有效的图表:http://bl.ocks.org/Andrew-Reid/137fffb14cf52e5d3f16c554ada81c67