修改d3力导向图示例

5

我刚接触 JavaScript 和 D3.js,正在尝试理解它们的工作原理。我一直在玩这里的力导向图示例: http://bl.ocks.org/mbostock/4062045

我的目标是将 JSON 链接从数组编号更改为节点名称。我正在尝试可视化一个小型网络拓扑,而且已经设置好了所有节点的邻居。以下是我想要使用的 JSON 数据:

{
  "nodes":[
    {"name":"stkbl0001","group":1},
    {"name":"stkbl0002","group":1},
    {"name":"stkbl0003","group":1},
    {"name":"stkbl0004","group":1},
    {"name":"stkbl0005","group":1}
  ],
  "links":[
    {"source":"stkbl0001","target":"stkbl0005","value":3},
    {"source":"stkbl0002","target":"stkbl0005","value":3},
    {"source":"stkbl0003","target":"stkbl0005","value":3},
    {"source":"stkbl0004","target":"stkbl0005","value":3}
  ]

我真的不知道如何修改D3代码以将所有这些内容联系在一起。我看不到获取数组数字并将它们粘合为链接的部分。这可能是一个愚蠢的问题,但它会对我很有帮助!

编辑:

以下是我根据Lars Kotthoff的输入编写的javascript代码:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.node {
  stroke: #fff;
  stroke-width: 1.5px;
}

.link {
  stroke: #999;
  stroke-opacity: .6;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

var width = 960,
    height = 500;

var color = d3.scale.category20();

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

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

d3.json("mini.json", function(error, graph) {
  force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();

  var link = svg.selectAll(".link")
      .data(graph.links)
      .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.selectAll(".node")
      .data(graph.nodes)
      .enter().append("circle")
      .attr("class", "node")
      .attr("r", 5)
      .style("fill", function(d) { return color(d.group); })
      .call(force.drag);

  var nodeMap = {};
  nodes.forEach(function(d) { nodeMap[d.name] = d; });

  links.forEach(function(l) {
      l.source = nodeMap[l.source];
      l.target = nodeMap[l.target];
  })

  node.append("title")
      .text(function(d) { return d.name; });

  force.on("tick", function() {
    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("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  });
});
</script>

这段代码在第55行出现错误 (nodes.forEach(function(d) { nodeMap[d.name] = d; });),错误信息如下:

Uncaught ReferenceError: nodes is not defined
2个回答

11

这个链接 指向了一个基于你的示例的可工作示例。

关键代码放在力导向布局初始化之前:

var nodeMap = {};

graph.nodes.forEach(function(d) { nodeMap[d.name] = d; });

graph.links.forEach(function(l) {
    l.source = nodeMap[l.source];
    l.target = nodeMap[l.target];
})

force.nodes(graph.nodes)
    .links(graph.links)
    .start();

这样你就可以像使用原始格式一样使用你的数据格式(网络上许多示例都遵循原始格式,因此你可以轻松地将它们适应到你的格式中)。

(由于 jsfiddle 的限制,此示例未使用 json 文件;相反,函数 getData() 被制作为返回数据的方法;但这对你的问题并不重要;你也可以使用此解决方案处理 json 文件)

希望这可以帮到你。


2
这真的帮了很多。谢谢你提供的漂亮的jsfiddle。干得好!不幸的是,我的声望太低了,无法点赞。 - ThomasV
几个小时前应该在这里看一下,避免了代码实验的时间浪费! - matanster

0
D3提供了两种指定力导向图链接源和目标的方式。第一种方式是在节点数组中提供节点的索引,这种方式在您提供的示例中使用。当启动力导向布局时,此索引将被替换为实际节点的引用。第二种方式是显式地提供实际节点的引用。
要通过名称引用节点,您需要使用某些东西来解析该引用。例如:
var nodeMap = {};
graph.nodes.forEach(function(d) { nodeMap[d.name] = d; });

然后你可以这样做

graph.links.forEach(function(l) {
  l.source = nodeMap[l.source];
  l.target = nodeMap[l.target];
})

当然,您也可以使用此功能来定义要开始的链接

"links":[
 {"source":nodeMap["stkbl0001"],"target":nodeMap["stkbl0005"],"value":3}
]

谢谢 - 这绝对是朝着正确方向迈出的一步。不幸的是,我无法看到这段代码应该插入到上面提到的示例中的哪个位置。我已经像您在帖子底部所做的那样定义了链接。我应该在哪里插入 links.forEach(function(1).. 代码? - ThomasV
如果您按照上述定义变量,代码将在整个定义(nodeslinks)之后执行。 - Lars Kotthoff
抱歉,我仍然无法理解这个问题。我已经更新了上面的问题,并添加了插入您的代码时出现的错误。我可能做错了什么,但我太菜了,看不出来 :-/ - ThomasV
好的,在这种情况下,实际上需要使用 graph.nodesgraph.links。我会更新答案。 - Lars Kotthoff
谢谢你的帮助。这解决了一些节点的问题,但对于几个节点,我遇到了一些奇怪的错误。这可能是我的问题,所以我会进一步研究。再次感谢! - ThomasV
我认为在最后一行的目标后面有一个额外的引号。 - user1515295

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接