代码之家  ›  专栏  ›  技术社区  ›  A Zibuda

d3力导向图-移除节点

  •  0
  • A Zibuda  · 技术社区  · 6 年前

    我正在尝试合并 this example 用于添加/删除力导向图中的节点。我可以很好地添加节点,出于某种原因,我还可以删除添加的节点,而不会产生任何问题。但是,当我尝试删除任何原始节点时,该节点不会从显示中删除,而且它还会断开许多其他节点的链接。但是,它和它的链接是根据日志从JSON中正确删除的。

    This example 使用与我相同的方法删除带有拼接的节点。

    Here 似乎也使用拼接方法,尽管我没有完全遵循它对过滤所做的其他操作。

    There is also this question 尽管答案只是地址相加,但这要求有点类似。

    我尝试在附加后退出/删除,但附加不起作用。我也试过用 .insert 而不是 .append . 这个 console.log 期间 update() 函数打印正在使用的JSON,它显示节点和链接都已被正确删除,但显示并不反映这一点。

    相关代码如下。

    初始化/更新图形

    //Get the SVG element
    var svg = d3.select("svg");
    
    var width = 960, height = 600;
    var color = d3.scaleOrdinal(d3.schemeCategory20);
    
    var link = svg.append("g").selectAll(".link");
    var node = svg.append("g").selectAll(".node");
    var label = svg.append("g").selectAll(".label");
    
    //Begin the force simulation
    var simulation = d3.forceSimulation()
        .force("link", d3.forceLink().id(function (d) { return d.id; }).distance(50).strength(0.3))
        .force("charge", d3.forceManyBody().strength(-15))
        .force("center", d3.forceCenter(width / 2, height / 2));
    
    //Highlight variables
    var highlight_color = "blue";
    var tHighlight = 0.05;
    
    var config;
    
    var linkedByIndex = {};
    
    //Get the data
    d3.json("/../../data.json", function (data) {
        //if (!localStorage.graph)
        //{
            localStorage.graph = JSON.stringify(data);
        //}
        update();
        forms();
    });
    
    function update() {
    
        config = JSON.parse(localStorage.graph);
        console.log(JSON.stringify(config));
        linkedByIndex = {};
        //Create an array of source,target containing all links
        config.links.forEach(function (d) {
            linkedByIndex[d.source + "," + d.target] = true;
            linkedByIndex[d.target + "," + d.source] = true;
        });
    
        //Draw links
        link = link.data(config.links);
        link.exit().remove();
        link = link.enter().append("line")
                .attr("class", "link")
                .attr("stroke-width", 2)
                .attr("stroke", "#888")
                //.attr("opacity", function (d) { if (d.target.radius > 7) { return 1 }; return 0; })
                .merge(link);         
    
    
        node = node.data(config.nodes);
        node.exit().remove();
        node = node.enter().append("circle")
                .attr("class", "node")
                .attr("r", function(d) { return d.radius; })
                .attr("fill", function (d) { return color(d.id); })
                .attr("stroke", "black")
            //  .attr("pointer-events", function (d) { if (d.radius <= 7) { return "none"; } return "visibleAll"; })
            //  .attr("opacity", function (d) { if (d.radius <= 7) { return 0; } return 1; })
                .call(d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended))
                .on("mouseover", mouseOver)
                .on("mouseout", mouseOut)
                .merge(node);
    
        label = label.data(config.nodes);
        label.exit().remove();
        label = label.enter().append("text")
                .attr("class", "label")
                .attr("dx", function (d) { return d.radius * 1.25; })
                .attr("dy", ".35em")
                .attr("opacity", function (d) { if (d.radius <= 7) { return 0; } return 1; })
                .attr("font-weight", "normal")
                .style("font-size", 10)
                .text(function (d) { return d.id; })
                .merge(label);
    
        //Add nodes to simulation
        simulation
            .nodes(config.nodes)
            .on("tick", ticked);
    
        //Add links to simulation
        simulation.force("link")
            .links(config.links);
    
        simulation.alphaTarget(0.3).restart();
    }
    
    //Animating by ticks function
    function ticked() {
        node
            .attr("cx", function (d) { return d.x = Math.max(d.radius, Math.min(width - d.radius, d.x)); })
            .attr("cy", function (d) { return d.y = Math.max(d.radius, Math.min(height - d.radius, 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; });
        label
            .attr("x", function (d) { return d.x = Math.max(d.radius, Math.min(width - d.radius, d.x)); })
            .attr("y", function (d) { return d.y = Math.max(d.radius, Math.min(height - d.radius, d.y)); });
    }
    
    //Using above array, check if two nodes are linked
    function isConnected(node1, node2) {
        return linkedByIndex[node1.id + "," + node2.id] || node1.index == node2.index;
    }
    

    添加/删除节点和链接。 通过这种方式添加节点和链接都非常好。

    function newNode(name, rad)
    {
        if (name == "")
        {
            alert("Must specify name");
            return;
        }
        console.log("Adding node with name: " + name + " and radius: " + rad);
        var temp = JSON.parse(localStorage.graph);
        temp.nodes.push({ "id": name, "radius": Number(rad) });
        localStorage.graph = JSON.stringify(temp);
        update();
    }
    
    function newLink(source, target)
    {
        var foundSource = false;
        var foundTarget = false;
    
        if (source == "" && target == "")
        {
            alert("Must specify source and target");
            return;
        }
        else if(source == "")
        {
            alert("Must specify source");
            return;
        }
        else if (target == "")
        {
            alert("Must specify target")
            return;
        }
    
        var temp = JSON.parse(localStorage.graph);
    
        for (var i=0; i < temp.nodes.length; i++)
        {
            if(temp.nodes[i]['id'] === source)
            {
                foundSource = true;
            }
    
            if(temp.nodes[i]['id'] === target)
            {
                foundTarget = true;
            }
        }
    
        if (foundSource && foundTarget) {
            temp.links.push({ "source": source, "target": target });
            localStorage.graph = JSON.stringify(temp);
            update();
        }
        else {
            alert("Invalid source or target");
            return;
        }
    
        return;
    }
    
    function removeLink(linkSource, linkTarget)
    {
    
    }
    
    function removeNode(nodeName)
    {
        var temp = JSON.parse(localStorage.graph);
        var found = false;
    
        if (nodeName == "")
        {
            alert("Must specify node name");
            return;
        }
    
        for(var i=0; i<temp.nodes.length; i++)
        {
            if(temp.nodes[i]['id'] === nodeName)
            {
                console.log("Removing node: " + nodeName);
                found = true;
                temp.nodes.splice(i, 1);
                temp.links = temp.links.filter(function (d) { return d.source != nodeName && d.target != nodeName; });
            }
        }
    
        if(!found)
        {
            alert("Node does not exist");
            return;
        }
    
        localStorage.graph = JSON.stringify(temp);
    
        update();
    }
    

    JSON数据的格式为

    {
      "nodes":[
       {
          "id": "id1",
           "radius": 5},
       {
          "id: "id2",
           "radius": 6}
    ],
    
    "links":[{
    "source": "id1",
    "target": "id2"    
    ]
    }
    
    1 回复  |  直到 6 年前
        1
  •  0
  •   Yaroslav Sergienko    6 年前

    默认情况下,d3按索引联接数据,因此删除节点后,它会错误地分配数据。解决方法是将第二个参数传递给 .data 功能。你需要更换

    node = node.data(config.nodes);
    

    具有

    node = node.data(config.nodes, d => d.id);
    

    也可以对链接执行此操作,但如果它们的样式相同(即,它们之间的差异仅在于 x1 , x2 , y1 y2 )这没什么区别。

    更新:对标签也应该这样做

    label = label.data(config.nodes, d => d.id);