D3图表布局 - 具有多个父项的树结构

时间:2016-10-02 14:59:44

标签: javascript json d3.js layout

首先让我说明我对D3和Javascript完全不熟悉。通过一点点实验,我试图开发一个Tree Like结构,其中catch可以有多个父节点。

输入:

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,就像说节点必须以深度/层次结构绘制,其中根位于顶部,同一级节点排列在这样的结构中最小化链接重叠的方法。

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

我面临的问题:

  1. 因为我只获得节点及其来源&目标,而不是他们的坐标,我必须通过整个列表解析计算级别,然后根据该级别划分高度,然后绘制节点。处理这类问题最有效的方法是什么?我应该深入研究算法解决方案吗?
  2. 或者

    1. D3中是否有一些图形绘制函数,它将此JSON作为输入并完成其余工作?
    2. 请注意,节点可以有多个父节点,我的主要焦点是以正确/层次顺序绘制这些节点,因此我面临的主要问题是计算所有节点的X / Y坐标。输入如上所述。

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

1 个答案:

答案 0 :(得分:2)

一整天都在考虑这件事。使用内置的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实际上意味着没有多个父结构的直接层次结构。