【问题标题】:How do I group the nodes in a force directed graph如何在力有向图中对节点进行分组
【发布时间】:2016-02-07 05:53:21
【问题描述】:

我有一个小提琴 - http://jsfiddle.net/npmarkunda/pqobc0zv/

如何根据节点所属的组显示节点。

什么是“组”?为什么边缘既有“源”又有“目标”——这些值是什么?为什么链接有“价值”?链接不只是未加权的边缘吗?我无法理解数据存储的 JSON 结构。

以及如何将文本呈现为文本而不是 SVG。

var graph;
    function myGraph() {

        // Add and remove elements on the graph object
        this.addNode = function (id) {
            nodes.push({"id": id});
            update();
        };

        this.removeNode = function (id) {
            var i = 0;
            var n = findNode(id);
            while (i < links.length) {
                if ((links[i]['source'] == n) || (links[i]['target'] == n)) {
                    links.splice(i, 1);
                }
                else i++;
            }
            nodes.splice(findNodeIndex(id), 1);
            update();
        };

        this.removeLink = function (source, target) {
            for (var i = 0; i < links.length; i++) {
                if (links[i].source.id == source && links[i].target.id == target) {
                    links.splice(i, 1);
                    break;
                }
            }
            update();
        };

        this.removeallLinks = function () {
            links.splice(0, links.length);
            update();
        };

        this.removeAllNodes = function () {
            nodes.splice(0, links.length);
            update();
        };

        this.addLink = function (source, target, value) {
            links.push({"source": findNode(source), "target": findNode(target), "value": value});
            update();
        };

        var findNode = function (id) {
            for (var i in nodes) {
                if (nodes[i]["id"] === id) return nodes[i];
            }
            ;
        };

        var findNodeIndex = function (id) {
            for (var i = 0; i < nodes.length; i++) {
                if (nodes[i].id == id) {
                    return i;
                }
            }
            ;
        };

        // set up the D3 visualisation in the specified element
        var w = 600,
                h = 650;

        var color = d3.scale.category10();

        var vis = d3.select("body")
                .append("svg:svg")
                .attr("width", w)
                .attr("height", h)
                .attr("id", "svg")
                .attr("pointer-events", "all")
                .attr("viewBox", "0 0 " + w + " " + h)
                .attr("perserveAspectRatio", "xMinYMid")
                .append('svg:g');

        var force = d3.layout.force();

        var nodes = force.nodes(),
                links = force.links();

        var update = function () {
            var link = vis.selectAll("line")
                    .data(links, function (d) {
                        return d.source.id + "-" + d.target.id;
                    });

            link.enter().append("line")
                    .attr("id", function (d) {
                        return d.source.id + "-" + d.target.id;
                    })
                    .attr("stroke-width", function (d) {
                        return d.value / 10;
                    })
                    .attr("class", "link");
            link.append("title")
                    .text(function (d) {
                        return d.value;
                    });
            link.exit().remove();

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

            var nodeEnter = node.enter().append("g")
                    .attr("class", "node")
                    .call(force.drag);

            nodeEnter.append("svg:circle")
                    .attr("r", 12)
                    .attr("id", function (d) {
                        return "Node;" + d.id;
                    })
                    .attr("class", "nodeStrokeClass")
                    .attr("fill", function(d) { return color(d.id); });

            nodeEnter.append("svg:text")
                    .attr("class", "textClass")
                    .attr("x", 14)
                    .attr("y", ".31em")
                    .text(function (d) {
                        return d.id;
                    });

            node.exit().remove();

            force.on("tick", function () {

                node.attr("transform", function (d) {
                    return "translate(" + d.x + "," + d.y + ")";
                });

                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;
                        });
            });

            // Restart the force layout.
            force
                    .gravity(.01)
                    .charge(-80000)
                    .friction(0)
                    .linkDistance( function(d) { return d.value * 10 } )
                    .size([w, h])
                    .start();
        };


        // Make it all go
        update();
    }

    function drawGraph() {

        graph = new myGraph("#svgdiv");
        graph.addNode('Sophia');
        graph.addNode('Daniel');
        graph.addNode('Ryan');
        graph.addNode('Lila');
        graph.addNode('Suzie');
        graph.addNode('Riley');
        graph.addNode('Grace');
        graph.addNode('Dylan');
        graph.addNode('Mason');
        graph.addNode('Emma');
        graph.addNode('Alex');
        graph.addLink('Alex', 'Ryan', '20');
        graph.addLink('Sophia', 'Ryan', '20');
        graph.addLink('Daniel', 'Ryan', '20');
        graph.addLink('Ryan', 'Lila', '30');
        graph.addLink('Lila', 'Suzie', '20');
        graph.addLink('Suzie', 'Riley', '10');
        graph.addLink('Suzie', 'Grace', '30');
        graph.addLink('Grace', 'Dylan', '10');
        graph.addLink('Dylan', 'Mason', '20');
        graph.addLink('Dylan', 'Emma', '20');
        graph.addLink('Emma', 'Mason', '10');
        graph.addLink('Grace', 'Daniel', '5');
        graph.addLink('Alex', 'Mason', '35');
        keepNodesOnTop();

        // callback for the changes in the network
        var step = -1;
        function nextval()
        {
            step++;
            return 2000 + (1500*step); // initial time, wait time
        }
   
    }

    drawGraph();

    // because of the way the network is created, nodes are created first, and links second,
    // so the lines were on top of the nodes, this just reorders the DOM to put the svg:g on top
    function keepNodesOnTop() {
        $(".nodeStrokeClass").each(function( index ) {
            var gnode = this.parentNode;
            gnode.parentNode.appendChild(gnode);
        });
    }
    function addNodes() {
        d3.select("svg")
                .remove();
         drawGraph();
    }
.link {
            stroke: #999;
            stroke-width: 1px;
        }

        .node {
            stroke: #999;
            stroke-width: 1px;
        }
        .textClass {
            stroke: #555;
            font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif;
            font-weight: normal;
            font-size: 14px;
        }
  <script src="http://d3js.org/d3.v3.min.js"></script>
<button onclick="addNodes()">Restart Animation</button>

【问题讨论】:

    标签: d3.js force-layout


    【解决方案1】:

    我使用单独的 forcePoints 将我的节点聚集在特定位置,它涉及每个组的额外 X 和 Y 值数组

    var forcePoint = [
    "1":{"x":"200", "y":"400"},
    "2":{"x":"300", "y":"600"},
    ];//etc.
    

    然后我将节点定位在分配给数据中位置属性的 forcePoint 周围。在这种情况下,位置 1 的节点在 SVG 上聚集到 200,400,位置 2 的节点聚集到 300,600。实际的数组是在模拟的上一步中创建的,但你明白了。

    var position = d3.forceSimulation(nodes)
    .force('x', d3.forceX((d)=>forcePoint[d.location][0].cx)
    .strength(0.8))
    .force('y', d3.forceY((d)=>forcePoint[d.location][1].cy)
    .strength(0.8))
    .force("collide", d3.forceCollide(R * 2));
    position.nodes(graph.cells).on('tick', function() {
        nodes.attr('transform', (d)=>{
        return 'translate(' + (d.x) + ',' + (d.y) + ')';
       });
    

    您还可以将它们全部链接到组内的中心节点。

    【讨论】:

      【解决方案2】:

      D3 Force Layout : How to force a group of node to stay in a given area

      链接是节点之间的连接。链接的源值和目标值指定箭头应指向的方向。长度或距离,或您添加到链接 JSON 对象的任何自定义属性通常用于指定“所需的”链接距离,但您也可以指定与重力设置一起使用的权重。

      【讨论】:

        【解决方案3】:

        D3js 有一个linkDistance 选项,允许根据值设置两个节点之间的距离。

        对于我的例子,我必须设置这个linkDistance(function(d) { return (d.value); })

        【讨论】:

          猜你喜欢
          • 2012-03-07
          • 2023-01-24
          • 1970-01-01
          • 2015-05-17
          • 1970-01-01
          • 1970-01-01
          • 2012-04-22
          • 2019-04-23
          • 2018-05-12
          相关资源
          最近更新 更多