如何使用D3布局非树层次结构

Joh*_*our 22 javascript d3.js

D3具有针对严格树的有向图的各种布局,例如:

A
|\
B C
 / \
D   E
Run Code Online (Sandbox Code Playgroud)

我需要绘制一个不是树的节点层次结构,但它是一个有向无环图.这是树布局的问题,因为有几个分支会聚:

A
|\
B C
 \|
  D
Run Code Online (Sandbox Code Playgroud)

有没有人知道一般层次结构的D3布局?或者,一些聪明的黑客攻击现有的treelayout?我注意到GraphVis很好地处理了这种情况,但D3产生的图形更符合这里的要求.

Phi*_*rte 11

您可以创建自己的代码,而不必依赖D3布局来完成它.

在jsFiddle中提供了一个例子.这个例子非常简单,需要稍微努力才能适应更复杂的例子.

可以通过相对较少的努力来重新处理该示例以处理分层数据.

这是我在jsFiddle中使用的代码:

 // Sample data set
var json = {
    nodes: [{
        name: 'A'},
    {
        name: 'B'},
    {
        name: 'C'},
    {
        name: 'D'}],
    links: [{
        source: 'A',
        target: 'B'},
    {
        source: 'A',
        target: 'C'},
    {
        source: 'B',
        target: 'D'},
    {
        source: 'C',
        target: 'D'}
                                                                                   ]

};

var vis = d3.select('#vis').attr('transform', 'translate(20, 20)');

// Build initial link elements - Build first so they are under the nodes
var links = vis.selectAll('line.link').data(json.links);
links.enter().append('line').attr('class', 'link').attr('stroke', '#000');

// Build initial node elements
var nodes = vis.selectAll('g.node').data(json.nodes);
nodes.enter().append('g').attr('class', 'node').append('circle').attr('r', 10).append('title').text(function(d) {
    return d.name;
});

// Store nodes in a hash by name
var nodesByName = {};
nodes.each(function(d) {
    nodesByName[d.name] = d;
});

// Convert link references to objects
links.each(function(link) {
    link.source = nodesByName[link.source];
    link.target = nodesByName[link.target];
    if (!link.source.links) {
        link.source.links = [];
    }
    link.source.links.push(link.target);
    if (!link.target.links) {
        link.target.links = [];
    }
    link.target.links.push(link.source);
});

// Compute positions based on distance from root
var setPosition = function(node, i, depth) {
    if (!depth) {
        depth = 0;
    }
    if (!node.x) {
        node.x = (i + 1) * 40;
        node.y = (depth + 1) * 40;
        if (depth <= 1) {
            node.links.each(function(d, i2) {
                setPosition(d, i2, depth + 1);
            });
        }

    }

};
nodes.each(setPosition);

// Update inserted elements with computed positions
nodes.attr('transform', function(d) {
    return 'translate(' + d.x + ', ' + d.y + ')';
});

links.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;
});
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的例子,菲尔!我最终做了一些非常相似的事情.事实证明我真正想要的布局算法是在GraphViz中实现的.它有python绑定,但它们不起作用.相反,我做了以下内容:1)表格图形为DOT语言(http://www.graphviz.org/content/dot-language)2)通过命令行将图形传递给graphviz的点命令,命令行执行布局并放置(x, y)坐标到DOT 3)将DOT重新格式化为javascript对象,嵌入到页面4)使用D3根据DOT中的(x,y)坐标放置节点.这非常适用于非常大的图形. (3认同)

Ben*_*yde 7

正如这个例子:" 强制定向树 "说明有一个经常有效的技巧.在该示例中,在每个节拍上调整力方向的行为,使得节点根据链路的方向稍微向上或向下漂移.如图所示,这对树木做得很好,但我发现它对于非循环图也很有效.没有承诺,但它可能有所帮助.


Gle*_*enn 1

一般来说,对于树和数据层次结构,您只需要在 B 和 C 的子级列表中包含“D”即可。

创建节点列表时,请确保返回一个唯一的 ID,以便“D”不会出现两次。

vis.selectAll("g.node").data(nodes, function(d) { return d.id; });
Run Code Online (Sandbox Code Playgroud)

然后当你打电话时

var links = tree.links(nodes)
Run Code Online (Sandbox Code Playgroud)

您应该让 D 作为“目标”出现两次(B 和 C 分别作为“源”),这会导致单个节点“D”有两行。