使用新元素更新图形D3会使用错误的节点创建边缘

Stu*_*Cat 8 javascript d3.js

我的代码创建了一个图形并在每个节点上创建了一个枢轴点,如果你双击它们,它将获取与该节点相关的更多数据,并希望创建新的链接.现在这是我遇到的问题:

在此输入图像描述 在此输入图像描述

我点击了一个最外面的节点,但由于某种原因,新链接被附加到第一个节点(蓝色节点).知道为什么会这样吗?

function draw_graph(graph) {
    var color = d3.scaleOrdinal(d3.schemeCategory20);

    var svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height"),
        node,
        link;

    svg.append('defs').append('marker')
        .attrs({
            'id': 'arrowhead',
            'viewBox': '-0 -5 10 10',
            'refX': 13,
            'refY': 0,
            'orient': 'auto',
            'markerWidth': 13,
            'markerHeight': 13,
            'xoverflow': 'visible'
        })
        .append('svg:path')
        .attr('d', 'M 0,-5 L 10 ,0 L 0,5')
        .attr('fill', '#999')
        .style('stroke', 'none');

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

    update(graph.links, graph.nodes);

    svg.selectAll('circle').on('dblclick', function () {
        var pivot_id = ($(this).siblings('title').text())
        console.log('pivoting on', pivot_id)
        pivot_search(pivot_id)
    });


    function update(links, nodes) {
        link = svg.selectAll(".link")
            .data(links)
            .enter()
            .append("line")
            .attr("class", "link")
            .attr('marker-end', 'url(#arrowhead)')


        edgepaths = svg.selectAll(".edgepath")
            .data(links)
            .enter()
            .append('path')
            .attrs({
                'class': 'edgepath',
                'fill-opacity': 0,
                'stroke-opacity': 0,
                'id': function (d, i) {
                    return 'edgepath' + i
                }
            })
            .style("pointer-events", "none");

        edgelabels = svg.selectAll(".edgelabel")
            .data(links)
            .enter()
            .append('text')
            .style("pointer-events", "none")
            .attrs({
                'class': 'edgelabel',
                'id': function (d, i) {
                    return 'edgelabel' + i
                },
                'font-size': 10,
                'fill': '#aaa'
            });

        node = svg.selectAll(".node")
            .data(nodes)
            .enter()
            .append("g")
            .attr("class", "node")
            .call(d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
            );

        node.append("circle")
            .attr("r", 5)
            .attr("fill", function (d) {
                return color(d.group);
            })


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

        node.append("text")
            .attr("dy", -3)
            .text(function (d) {
                return d.label;
            });


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

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

    }

    function ticked() {
        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 + ")";
            });

        edgepaths.attr('d', function (d) {
            return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
        });

        edgelabels.attr('transform', function (d) {
            if (d.target.x < d.source.x) {
                var bbox = this.getBBox();

                rx = bbox.x + bbox.width / 2;
                ry = bbox.y + bbox.height / 2;
                return 'rotate(180 ' + rx + ' ' + ry + ')';
            }
            else {
                return 'rotate(0)';
            }
        });
    }

    function dragstarted(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart()
        d.fx = d.x;
        d.fy = d.y;
    }

    function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }


}

function pivot_search(entity_id) {
    var json = {
        'nodes': [],
        'links': [],
    }
    get_entities({'id': entity_id})
        .done(function (data) {
            json.nodes.push({
                'label': data['results'][0]['label'],
                'id': data['results'][0]['id'],
                'group': data['results'][0]['entity_type'],
            })
            get_entities({
                'related_entities': entity_id,
                'related_entities__entity_instance__entity_type__strong_entity': true,
                'page_size': 500
            })
                .done(function (data) {
                    for (var i = 0; i < data['results'].length; i++) {
                        json.nodes.push({
                            'label': data['results'][i]['label'],
                            'id': data['results'][i]['id'],
                            'group': data['results'][i]['entity_type'],
                        })
                        json.links.push({
                            'source': entity_id,
                            'target': data['results'][i]['id'],

                        })
                    }
                    draw_graph(json)
                })
        })
}
Run Code Online (Sandbox Code Playgroud)

编辑:进一步调查似乎是用新数据替换现有节点链接并创建新的可能重复的节点.

link = svg.selectAll('.link')
            .data(links, function (d) {
                return d.id;
            })
            .enter()
            .append('line')
            .attr('class', 'link')
            .attr('marker-end', 'url(#arrowhead)')


        edgepaths = svg.selectAll('.edgepath')
            .data(links)
            .enter()
            .append('path')
            .attrs({
                'class': 'edgepath',
                'fill-opacity': 0,
                'stroke-opacity': 0,
                'id': function (d, i) {
                    return 'edgepath' + i
                }
            })
            .style('pointer-events', 'none');

        node = svg.selectAll('.node')
            .data(nodes, function (d) {
                return d.id;
            })
            .enter()
            .append('g')
            .attr('class', 'node')
            .call(d3.drag()
                .on('start', dragstarted)
                .on('drag', dragged)
            );
Run Code Online (Sandbox Code Playgroud)

我添加了一个ID来帮助处理节点重复,但现在我有索引根位移的问题.

在此输入图像描述 在此输入图像描述

th3*_*guy 1

您遇到的问题似乎是由于您的数据可能在 D3 的数据连接功能中重复造成的。解决该问题的最佳方法可能是在 D3 绑定数据中的每个节点之前为其创建一个“UUID / GUID”(请参阅​​此处的示例)。完成此操作后,您可以绑定数据并使用数据连接的键指定函数(请参阅此处了解说明)告诉 D3 使用您为每个对象创建的 UUID / GUID 值来保证一致性。从此,你应该能够更轻松地处理亲子关系。

编辑#1

由于这适用于重复值,因此您可能遇到的下一个问题是因为对“源”对象的引用未按照 D3 期望的方式设置。在 D3 中,链接的“source”属性是对实际源对象的引用,您只需在其中提供 ID 值(请参阅此处了解 D3v4 文档参考)。尝试提供对数组中实际源对象的引用,这应该可以修复它。

编辑#2

您是正确的,因为您正在处理进入可视化的新数据,但我不认为您正在处理现有或旧数据(意味着不再相关的数据点,并且需要删除节点/链接)。在这种情况下,请尝试使用 Mike Bostock(D3.js 库的原始创建者)提供的以下示例更新您的代码,然后在完成后返回报告。您看到的新节点可能只是需要删除的旧节点,因为它们不再有与其绑定的子节点,因此 D3js 将它们视为实际上需要删除的“新”或“现有”节点已删除。