简体   繁体   English

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

[英]D3 updating graph with new elements create edges with the wrong nodes

My code creates a graph and creates a pivot point on each node, if you double click them it'll fetch more data associated with that node and hopefully creates new links. 我的代码创建了一个图形并在每个节点上创建了一个枢轴点,如果双击它们,它将获取与该节点关联的更多数据,并希望创建新的链接。 Now here's the problem I'm running into: 现在这是我遇到的问题:

在此处输入图片说明 在此处输入图片说明

I clicked on one of the outermost nodes but for some reason the new links were being attached to the first node (the blue one). 我单击了最外面的一个节点,但是由于某种原因,新链接被附加到第一个节点(蓝色的)。 Any idea why this is happening? 知道为什么会这样吗?

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

EDIT: Upon further investigations seems like it's replacing the existing node links with the new data and creating new potentially duplicate nodes. 编辑:经过进一步调查,似乎是用新数据替换现有节点链接并创建新的潜在重复节点。

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

I added an ID to help deal with node duplication, but now I have a problem with index root displacement. 我添加了一个ID以帮助处理节点重复,但是现在索引根移位存在问题。

在此处输入图片说明 在此处输入图片说明

It appears that the problem that you are having is due to the fact that your data might be duplicating within D3's data-join functionality. 您似乎遇到的问题是由于您的数据可能在D3的数据联接功能内重复而造成的。 It is likely that the best way to solve the issue is to create a "UUID / GUID" for each node in your data before D3 binds it (see here for an example). 解决此问题的最佳方法可能是在D3绑定数据之前为数据中的每个节点创建一个“ UUID / GUID”(请参见此处的示例)。 Once you have done that, then you can bind the data and use the data-join's key-specify function (see here for an explanation) to tell D3 to use the UUID / GUID values you created for each object to guarantee consistency. 完成此操作后,您可以绑定数据并使用数据联接的键指定功能(请参见此处以获取解释)告诉D3使用为每个对象创建的UUID / GUID值,以确保一致性。 From there, you should be able to handle the parent-child relationships easier. 从那里,您应该能够更轻松地处理父子关系。

Edit #1 编辑#1

Since that worked for the duplicate values, the next problem that you are likely having is because the reference to the "source" object is not set up the way that D3 would expect. 由于这适用于重复值,因此您可能遇到的下一个问题是,对“源”对象的引用未按D3期望的方式设置。 In D3, the link's "source" property is a reference to the actual source object, where you are just providing the ID value (see here for the D3v4 docs reference). 在D3中,链接的“源”属性是对实际源对象的引用,您只是在其中提供ID值(有关D3v4文档参考,请参见此处 )。 Try providing the reference to the actual source object within the array and that should fix it. 尝试提供对数组中实际源对象的引用,并且应该对其进行修复。

Edit #2 编辑#2

You are correct in that you are handling NEW data coming into the visualization, but I don't think that you're handling EXISTING or OLD (meaning, data points that are no longer relevant and the nodes / links need to be removed). 您是正确的,因为您正在处理进入可视化的新数据,但我认为您不是在处理EXISTING或OLD(这意味着不再相关的数据点以及需要删除的节点/链接)。 In this case, try updating your code with the following example from Mike Bostock (the original creator of the D3.js library) here and then report back once that is done. 在这种情况下,请尝试使用此处的示例从Mike Bostock(D3.js库的原始创建者)中更新以下代码,然后在完成后进行报告。 It is possible that the new nodes that you are seeing are simply the older nodes that need to be removed since they no longer have the children tied to them, so D3js sees them as "new" or "existing" nodes that actually need to be removed. 您看到的新节点很可能只是需要删除的较旧节点,因为它们不再与子节点绑定,因此D3js将它们视为实际上需要被删除的“新”或“现有”节点。删除。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM