简体   繁体   English

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

[英]How to layout a non-tree hierarchy with D3

D3 has a variety of layouts for directed graphs that are strict trees, such as the following: D3具有针对严格树的有向图的各种布局,例如:

A
|\
B C
 / \
D   E

I need to draw a hierarchy of nodes that is not a tree, but is a directed acyclic graph. 我需要绘制一个不是树的节点层次结构,但它是一个有向无环图。 This is a problem for a tree layout, because several of the branches converge: 这是树布局的问题,因为有几个分支会聚:

A
|\
B C
 \|
  D

Does anyone know of a D3 layout for general hierarchies? 有没有人知道一般层次结构的D3布局? Or alternatively, some clever hack to the existing treelayout? 或者,一些聪明的黑客攻击现有的treelayout? I've noticed GraphVis handles this situation well, but D3 produces a graph that better suits the requirements here. 我注意到GraphVis很好地处理了这种情况,但D3产生的图形更符合这里的要求。

You could create your own code without having to rely on a D3 layout in order to get it done. 您可以创建自己的代码,而不必依赖D3布局来完成它。

I've provided an example in a jsFiddle . 在jsFiddle中提供了一个例子 The example is pretty simplistic and would need to be worked a little bit to accommodate more complex examples. 这个例子非常简单,需要稍微努力才能适应更复杂的例子。

The example could be re-worked to process hierarchical data as well with relatively little effort. 可以通过相对较少的努力来重新处理该示例以处理分层数据。

Here is the code I have used in the 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;
});

As this example: " Force Directed Trees " illustrates there is a trick that often works. 正如这个例子:“ 强制定向树 ”说明有一个经常有效的技巧。 In the example the force direction's behavior is adjusted on each tick so that nodes drift slightly up or down depending on the direction of the links. 在该示例中,在每个节拍上调整力方向的行为,使得节点根据链路的方向稍微向上或向下漂移。 As shown this will do a fine job for trees, but I've found it also works tolerable well for acyclic graphs. 如图所示,这对树木做得很好,但我发现它对于非循环图也很有效。 No promises, but it may help. 没有承诺,但它可能有所帮助。

Speaking generally of trees and the data hierarchy, you just need to have "D" in the children list for both B and C. 一般说来是树和数据层次结构,你只需要在B和C的子列表中都有“D”。

Creating your node list, make sure you have a unique id returned so that "D" doesn't appear twice. 创建节点列表,确保返回了唯一的ID,以便“D”不会出现两次。

vis.selectAll("g.node").data(nodes, function(d) { return d.id; });

Then when you call 然后当你打电话

var links = tree.links(nodes)

you should get D appearing as the "target" twice (with B and C as the "source" respectively) which results in two lines to the single node "D". 你应该将D作为“目标”出现两次(分别用B和C作为“源”),这导致两行到单个节点“D”。

I was able to do this using a combination of Dagre( https://github.com/dagrejs/dagre ) and cytoscape 我能够使用Dagre( https://github.com/dagrejs/dagre )和cytoscape的组合来做到这一点

There's a library called Dagre-D3 that you can use as a renderer for this library so it looks like the D3 solution you want. 有一个名为Dagre-D3的库,您可以将其用作此库的渲染器,因此它看起来像您想要的D3解决方案。

Check out this fiddle to see the basic implementation with Dagre and Cytoscape: https://jsfiddle.net/KateJean/xweudjvm/ 看看这个小提琴,看看Dagre和Cytoscape的基本实现: https ://jsfiddle.net/KateJean/xweudjvm/

var cy = cytoscape({
  container: document.getElementById('cy'),
  elements: {
          nodes: [
            { data: { id: '1' } },
            { data: { id: '2' } },
            { data: { id: '3' } },
            { data: { id: '4' } },
            { data: { id: '5' } },
            { data: { id: '6' } },
            { data: { id: '7' } },
            { data: { id: '8' } },
            { data: { id: '9' } },
            { data: { id: '10' } },
            { data: { id: '11' } },
            { data: { id: '12' } },
            { data: { id: '13' } },
            { data: { id: '14' } },
            { data: { id: '15' } },
            { data: { id: '16' } },
            { data: { id: '17' } },
            { data: { id: '18' } }
          ],
          edges: [
            { data: { source: '1', target: '2' } },
            { data: { source: '1', target: '3' } },
            { data: { source: '2', target: '4' } },
            { data: { source: '4', target: '5' } },
            { data: { source: '4', target: '6' } },
            { data: { source: '5', target: '6' } },
            { data: { source: '5', target: '7' } },
            { data: { source: '7', target: '8' } },
            { data: { source: '3', target: '9' } },
            { data: { source: '3', target: '10' } },
            { data: { source: '10', target: '11' } },
            { data: { source: '11', target: '12' } },
            { data: { source: '12', target: '13' } },
            { data: { source: '12', target: '14' } },
            { data: { source: '14', target: '15' } },
            { data: { source: '15', target: '16' } },
            { data: { source: '16', target: '17' } },
            { data: { source: '16', target: '18' } }

          ]
        },
  layout: {
    name: "dagre",
    rankDir: 'TB' //love this. you can quickly change the orientation here from LR(left to right) TB (top to bottom), RL, BT. Great dropdown option for users here. 
  },
  style: [{
    selector: 'node',
    style: {
      'label': 'data(id)',
      'width': '30%',
      'font-size': '20px',
      'text-valign': 'center',
      'shape': 'circle',
      'background-color': 'rgba(113,158,252,1)', 
      'border': '2px grey #ccc'
    }
  }, {
    selector: 'edge',
    style: {
      'width': 2,
      'line-color': '#ccc',
      'target-arrow-color': '#ccc',
      'target-arrow-shape': 'triangle'
    }
  }]
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM