【问题标题】:D3 force directed graph, different shape according to data and value given?D3力有向图,根据数据和给出的值不同的形状?
【发布时间】:2013-09-01 19:39:07
【问题描述】:

我已经制作了一个力导向图,我想更改包含"entity":"company" 的数据的节点形状,以便它们具有矩形形状,而没有这部分数据的另一个节点将是现在的圆形。

您可以在此处查看我的工作示例,其中只有圆形节点:http://jsfiddle.net/dzorz/uWtSk/

我尝试在部分代码中添加带有if else 语句的矩形,我将形状附加到节点,如下所示:

function(d)
    {
        if (d.entity == "company")
        {
            node.append("rect")
                .attr("class", function(d){ return "node type"+d.type})
                .attr("width", 100)
                .attr("height", 50)
                .call(force.drag);
        }
        else
        {
        node.append("circle")
            .attr("class", function(d){ return "node type"+d.type})
            .attr("r", function(d) { return radius(d.value) || 10 })
            //.style("fill", function(d) { return fill(d.type); })
            .call(force.drag);
        }
    }

但后来我在任何节点上都没有得到任何形状。

什么是正确的设置方法?

整个代码如下所示: 脚本:

var data = {"nodes":[
                        {"name":"Action 4", "type":5, "slug": "", "value":265000},
                        {"name":"Action 5", "type":6, "slug": "", "value":23000},
                        {"name":"Action 3", "type":4, "slug": "", "value":115000},
                        {"name":"Yahoo", "type":1, "slug": "www.yahoo.com", "entity":"company"},
                        {"name":"Google", "type":1, "slug": "www.google.com", "entity":"company"},
                        {"name":"Action 1", "type":2, "slug": "",},
                        {"name":"Action 2", "type":3, "slug": "",},
                        {"name":"Bing", "type":1, "slug": "www.bing.com", "entity":"company"},
                        {"name":"Yandex", "type":1, "slug": "www.yandex.com)", "entity":"company"}
                    ], 
            "links":[
                        {"source":0,"target":3,"value":10},
                        {"source":4,"target":3,"value":1},
                        {"source":1,"target":7,"value":10},
                        {"source":2,"target":4,"value":10},
                        {"source":4,"target":7,"value":1},
                        {"source":4,"target":5,"value":10},
                        {"source":4,"target":6,"value":10},
                        {"source":8,"target":4,"value":1}
                        ]
               }    



    var w = 560,
        h = 500,
        radius = d3.scale.log().domain([0, 312000]).range(["10", "50"]);

    var vis = d3.select("body").append("svg:svg")
        .attr("width", w)
        .attr("height", h);

        vis.append("defs").append("marker")
        .attr("id", "arrowhead")
        .attr("refX", 17 + 3) /*must be smarter way to calculate shift*/
        .attr("refY", 2)
        .attr("markerWidth", 6)
        .attr("markerHeight", 4)
        .attr("orient", "auto")
        .append("path")
            .attr("d", "M 0,0 V 4 L6,2 Z"); //this is actual shape for arrowhead

    //d3.json(data, function(json) {
        var force = self.force = d3.layout.force()
            .nodes(data.nodes)
            .links(data.links)
            .distance(100)
            .charge(-1000)
            .size([w, h])
            .start();



        var link = vis.selectAll("line.link")
            .data(data.links)
            .enter().append("svg:line")
            .attr("class", function (d) { return "link" + d.value +""; })
            .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; })
            .attr("marker-end", function(d) {
                                                if (d.value == 1) {return "url(#arrowhead)"}
                                                else    { return " " }
                                            ;});


        function openLink() {
        return function(d) {
            var url = "";
            if(d.slug != "") {
                url = d.slug
            } //else if(d.type == 2) {
                //url = "clients/" + d.slug
            //} else if(d.type == 3) {
                //url = "agencies/" + d.slug
            //}
            window.open("//"+url)
        }
    }




        var node = vis.selectAll("g.node")
            .data(data.nodes)
          .enter().append("svg:g")
            .attr("class", "node")
            .call(force.drag);

        node.append("circle")
          .attr("class", function(d){ return "node type"+d.type})
            .attr("r", function(d) { return radius(d.value) || 10 })
          //.style("fill", function(d) { return fill(d.type); })
          .call(force.drag);

        node.append("svg:image")
            .attr("class", "circle")
            .attr("xlink:href", function(d){ return d.img_href})
            .attr("x", "-16px")
            .attr("y", "-16px")
            .attr("width", "32px")
            .attr("height", "32px")
            .on("click", openLink());

        node.append("svg:text")
            .attr("class", "nodetext")
            .attr("dx", 0)
            .attr("dy", ".35em")
            .attr("text-anchor", "middle")
            .text(function(d) { return d.name });

        force.on("tick", function() {
          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; });

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

css:

.link10 { stroke: #ccc; stroke-width: 3px; stroke-dasharray: 3, 3; }
.link1 { stroke: #000; stroke-width: 3px;}
.nodetext { pointer-events: none; font: 10px sans-serif; }

.node.type1 {
  fill:brown;
}
.node.type2 {
  fill:#337147;
}
.node.type3 {
  fill:blue;
}
.node.type4 {
  fill:red;
}

.node.type5 {
    fill:#1BC9E0;
}

.node.type6 {
    fill:#E01B98;
}

image.circle {
    cursor:pointer;
}

您可以编辑我在帖子开头链接的 jsfiddle...

【问题讨论】:

  • 您似乎没有使用代码来更改 jsfiddle 中的形状。您能否发布一个演示该问题的帖子?
  • 将代码放在哪里以及如何调用它确实很重要。
  • 为了消除误解,我将代码中的 node.append("circle") .attr("class", function(d){ return "node type"+d.type}) .attr("r", function(d) { return radius(d.value) || 10 }) //.style("fill", function(d) { return fill(d.type); }) .call(force.drag); 替换为您可以在帖子开头看到的第一个块
  • 您在上面发布的代码定义了一个函数。你是怎么调用那个函数的?

标签: javascript d3.js shapes force-layout


【解决方案1】:

我比你领先一步:)

我使用“路径”而不是“圆圈”或“矩形”解决了您的问题,您可以查看我的解决方案,也许可以帮助我解决我遇到的问题...

D3 force-directed graph: update node position

【讨论】:

    【解决方案2】:

    这里的解决方案:http://jsfiddle.net/Bull/4btFx/1/

    我通过向每个节点添加一个类,然后对每个类使用“selectAll”来添加形状来实现这一点。在下面的代码中,我添加了一个类“节点”和一个由我的 JSON (d.type) 返回的类,它是“rect”或“ellipse”。

      var node = container.append("g")
      .attr("class", "nodes")
      .selectAll(".node")
      .data(graph.nodes)
      .enter().append("g")
      .attr("class", function(d) {
         return d.type + " node";
      })
      .call(drag);
    

    然后你可以为每个类的所有元素添加形状:

      d3.selectAll(".rect").append("rect")
      .attr("width", window.nodeWidth)
      .attr("height", window.nodeHeight)
      .attr("class", function(d) { 
         return "color_" + d.class 
      });
    
      d3.selectAll(".ellipse").append("rect")
      .attr("rx", window.nodeWidth*0.5)
      .attr("ry", window.nodeHeight*0.5)
      .attr("width", window.nodeWidth)
      .attr("height", window.nodeHeight)
      .attr("class", function(d) { 
         return "color_" + d.class 
      });
    

    在上面的示例中,我使用带半径的矩形来绘制椭圆,因为它以与矩形相同的方式居中它们。但它也适用于其他形状。在我链接的 jsfiddle 中,居中是关闭的,但形状是正确的。

    【讨论】:

      【解决方案3】:

      我使用从Filtering in d3.js on bl.ocks.org 收集的filter 方法实现了此行为。

      initGraphNodeShapes() {
        let t = this;
      
        let graphNodeCircles =
          t.graphNodesEnter
            .filter(d => d.shape === "circle")
            .append("circle")
            .attr("r", 15)
            .attr("fill", "green");
      
        let graphNodeRects =
          t.graphNodesEnter
            .filter(d => d.shape === "rect")
            .append("rect")
            .attr("width", 20)
            .attr("height", 10)
            .attr("x", -10) // -1/2 * width
            .attr("y", -5) // -1/2 * height
            .attr("fill", "blue");
      
        return graphNodeCircles.merge(graphNodeRects);
      }
      

      我在initGraphNodeShapes 调用中有这个,因为我的代码相对较大并且经过重构。 t.graphNodesEnter 是对数据连接 enter() 在别处调用后的数据选择的引用。如果您需要更多上下文,请联系我。另外,我使用d => ... 版本,因为我使用的是启用 lambdas 的 ES6。如果您使用的是 ES6 之前的版本,则必须将其更改为 function(d)... 表单。

      【讨论】:

        【解决方案4】:

        这是一篇较旧的帖子,但我在 2020 年 7 月尝试将这个概念与 D3 v5 一起使用时遇到了同样的麻烦。这是我的解决方案,以防其他人尝试构建力导向图,我同时使用了这两种方法圆形和矩形元素代表不同类型的节点:

        方法是创建元素,然后在调用力模拟时分别定位它们(​​因为圆形具有 cx、cy 和 r 属性,而矩形具有 x、y、宽度和高度)。大部分代码都遵循媒体上这篇博文中的示例:https://medium.com/ninjaconcept/interactive-dynamic-force-directed-graphs-with-d3-da720c6d7811

        仅供参考,我之前已将 'svg' 声明为 d3.select("some div with id or class"),以及一些未显示的读取数据的辅助函数(setNodeSize、setNodeColor)。我已经使用 D3.filter 方法检查数据中的布尔字段 - 节点是初始还是否?

        力模拟实例:

        const simulation = d3.forceSimulation()
        //the higher the strength (if negative), greater distance between nodes.
        .force('charge', d3.forceManyBody().strength(-120)) 
        //places the chart in the middle of the content area...if not it's top-left
        .force('center', d3.forceCenter(width / 2, height / 2))
        

        创建圆节点:

        const nodeCircles = svg.append('g')
        .selectAll('circle')
        .data(nodes)
        .enter()
        .filter(d => d.initial)
        .append('circle')
        .attr('r', setNodeSize)
        .attr('class', 'node')
        .attr('fill', setNodeColor)
        .attr('stroke', '#252525')
        .attr('stroke-width', 2)
        

        然后创建矩形节点:

        const nodeRectangles = svg.append('g')
        .selectAll('rect')
        .data(nodes)
        .enter()
        .filter(d => !d.initial)
        .append('rect')
        .attr('width', setNodeSize)
        .attr('height', setNodeSize)
        .attr('class', 'node')
        .attr('fill', setNodeColor)
        .attr('stroke', '#252525')
        .attr('stroke-width', 2)
        

        然后在调用模拟时:

        simulation.nodes(nodes).on("tick", () => {
        nodeCircles
            .attr("cx", node => node.x)
            .attr("cy", node => node.y)
        nodeRectangles
            .attr('x', node => node.x)
            .attr('y', node => node.y)
            .attr('transform', 'translate(-10, -7)')
        

        当然,还有更多内容可以添加行/链接、文本标签等。请随时联系我获取更多代码。上面列出的中等帖子非常有帮助!

        【讨论】:

          猜你喜欢
          • 2013-12-05
          • 2017-06-27
          • 1970-01-01
          • 2017-11-25
          • 1970-01-01
          • 1970-01-01
          • 2021-02-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多