【问题标题】:D3 Graph Layout - Tree Structure With multiple ParentD3 图形布局 - 具有多个父级的树结构
【发布时间】:2017-02-10 14:40:31
【问题描述】:

让我首先声明我对 D3 和 Javascript 完全陌生。通过一点点实验,我试图开发一个类似于树的结构,其中节点可以有多个父节点。

输入:

带有 nodes 和 links 信息的 Json,例如,

 var graph = {
    "nodes": [  { "id" : 0},
                { "id" : 1},
                { "id" : 2},
                { "id" : 3},
                { "id" : 4},
                { "id" : 5}
            ],
    "links": [  { "target": 5, "source":  1 },
                { "target":  2, "source":  0 },
                { "target": 3, "source":  4 },
                { "target": 1, "source":  2},
                { "target":  1, "source":  0 },
                
            ]
    };

注意:这只是作为我如何获取数据的参考,可能是数千个节点。

输出:

我希望输出像一个树结构,有点像this,也就是说节点必须绘制在一个深度/级别的结构中,根位于顶部,同级节点以这样的方式排列尽量减少链接重叠。

编辑:上面提供的输入的输出是 假设零是根。

我面临的问题:

  1. 由于我只给出节点及其源和目标,而不是它们的坐标,我必须解析整个列表计算级别,然后根据该级别划分高度,然后绘制节点。处理此类问题的最有效方法是什么?我应该深入研究算法解决方案吗?

或者

  1. 在 D3 中是否有一些绘图函数将此 JSON 作为输入并完成其余工作?

请注意节点可以有多个父节点,我的主要重点是以适当/分层顺序绘制这些节点,所以我面临的主要问题是计算所有节点的 X/Y 坐标如上所述输入。

请帮助我更好地理解这个主题,谢谢。

【问题讨论】:

  • 我无法计算出您的预期输出。 假设零是根节点,它会是look like this。由于父/子关系失败,3 到 4 消失了……这是您的预期输出吗?
  • @Mark 实际上输入仅供参考,因为我的输入...它可能包含数千个节点,例外的输出是根节点应该出现在顶部,然后是它的子节点,以此类推,链接重叠最小。希望我能够解释自己;)
  • 但是我的图表是给定您的示例数据的预期输出吗?
  • @Mark 不完全,第 1 级应该包含 0 作为根,然后它的较低级别 2 应该有 2 和 1,就像你有但是最后的第 3 级应该只有 5 与 1 连接,有不应该是第 3 级的另一个 1...感谢您指出我将使用给定输入的正确输出更新问题。
  • 我建议画一张图。看了你最后的评论,我更加困惑了,原来我是:)

标签: javascript json d3.js layout


【解决方案1】:

整天都在思考这个问题。使用内置的d3 构造,我认为this 与您将获得的差不多。我已获取您的数据并将其转换为版本 4 示例:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .links line {
    stroke: #aaa;
    stroke-width: 5px;
  }
  
  .nodes circle {
    pointer-events: all;
    stroke: none;
  }
</style>
<svg width="600" height="300"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
  var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

  var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) {
      return d.id;
    }))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2))
    .force("y", d3.forceY())

  var graph = {
    "nodes": [{
      "id": 0
    }, {
      "id": 1
    }, {
      "id": 2
    }, {
      "id": 3
    }, {
      "id": 4
    }, {
      "id": 5
    }],
    "links": [{
        "target": 5,
        "source": 1
      }, {
        "target": 2,
        "source": 0
      }, {
        "target": 3,
        "source": 4
      }, {
        "target": 1,
        "source": 2
      }, {
        "target": 1,
        "source": 0
      },

    ]
  };

  var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line");

  var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", 10);

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

  simulation
    .nodes(graph.nodes)
    .on("tick", ticked);

  simulation.force("link")
    .links(graph.links);

  function ticked() {

    var k = 6 * simulation.alpha();

    // Push sources up and targets down to form a weak tree.
    link
      .each(function(d) {
        d.source.y -= k, d.target.y += k;
      })
      .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>

如果你不想要移动和定居的效果,你可以预先计算布局,使其成为static。


我还尝试将您的数据转换为分层格式以供 d3.tree 使用,但复制您的图像时运气不佳:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .node circle {
    fill: #999;
  }
  
  .node text {
    font: 10px sans-serif;
  }
  
  .node--internal circle {
    fill: #555;
  }
  
  .node--internal text {
    text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
  }
  
  .link {
    fill: none;
    stroke: #555;
    stroke-opacity: 0.4;
    stroke-width: 1.5px;
  }
</style>
<svg width="300" height="300"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
  var graph = {
    "nodes": [{
      "id": 0
    }, {
      "id": 1
    }, {
      "id": 2
    }, {
      "id": 3
    }, {
      "id": 4
    }, {
      "id": 5
    }],
    "links": [{
      "target": 5,
      "source": 1
    },{
      "target": 2,
      "source": 0
    }, {
      "target": 3,
      "source": 4
    }, {
      "target": 1,
      "source": 2
    }, {
      "target": 1,
      "source": 0
    }]
  };

  var root = {
    id: 0,
    data: graph.nodes[0],
    children: []
  }

  function recurChild(obj) {
    graph.links.forEach(function(d) {
      if (d.source === obj.id) {
        var c = {
          id: d.target,
          data: graph.nodes[d.target],
          children: []
        };
        obj.children.push(c);
        recurChild(c);
      }
    });
  }
  recurChild(root);

  root = d3.hierarchy(root);

  var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    g = svg.append("g").attr("transform", "translate(40,0)");

  var tree = d3.tree()
    .size([height, width - 160]);

  tree(root);
  
  console.log(root.descendants())

  var link = g.selectAll(".link")
    .data(root.descendants().slice(1))
    .enter().append("path")
    .attr("class", "link")
    .attr("d", function(d) {
      return "M" + d.y + "," + d.x + "C" + (d.y + d.parent.y) / 2 + "," + d.x + " " + (d.y + d.parent.y) / 2 + "," + d.parent.x + " " + d.parent.y + "," + d.parent.x;
    });

  var node = g.selectAll(".node")
    .data(root.descendants())
    .enter().append("g")
    .attr("class", function(d) {
      return "node" + (d.children ? " node--internal" : " node--leaf");
    })
    .attr("transform", function(d) {
      return "translate(" + d.y + "," + d.x + ")";
    })

  node.append("circle")
    .attr("r", 2.5);

  node.append("text")
    .attr("dy", 3)
    .attr("x", function(d) {
      return d.children ? -8 : 8;
    })
    .style("text-anchor", function(d) {
      return d.children ? "end" : "start";
    })
    .text(function(d) {
      return d.data.id;
    });
</script>

d3.hierarchy 确实意味着,没有多个父结构的直接层次结构。

【讨论】:

  • 酷!它似乎适用于少量节点,让我尝试更多的节点,将返回结果。
  • 我点击了您指向 Force Directed Tree 的链接,这更符合我正在寻找的答案,但问题是节点数量过多,它有点变成了一个集群......有没有办法提供每个节点的深度属性,比如一级?
  • @Mohit,你有任何编码的例子吗?我试图用随机数据重新创建,但没有正确的父/子结构,它是一团糟。在上面的代码var k = 6 * simulation.alpha();中,6是每个节点的强制距离。
  • 假设我使用此数据api.myjson.com/bins/rewu 绘制图形...主要问题是根的深度,因此子节点的深度在刷新时会有所不同。现在,如果我希望根始终位于顶部并且其深度以垂直方式,那么?我能让自己变得容易理解吗? ;)
猜你喜欢
  • 2023-03-22
  • 2015-12-20
  • 1970-01-01
  • 1970-01-01
  • 2017-09-17
  • 1970-01-01
  • 2013-12-09
  • 1970-01-01
  • 2022-11-07
相关资源
最近更新 更多