Search code examples
javascriptd3.jssunburst-diagram

D3.js How to update chart display with new data?


I am trying to implement an update function for my D3 sunburst diagram where i can change the data that is displayed. I am able to successfully add or remove nodes.

However, i can't seem to be able to modify the existing data.

For example, if half of my chart is removed, i would like for the other half to fill the space leaved by the delete. Same thing goes when new data get added, i would like for the existing data to shrink and take less space

Here is my update function :

            function update(newData) {
                root = newData;
                node = root;

                g = svg.datum(root)
                    .selectAll("g")
                    .data(partition.nodes(root));

                var newG = g.enter()
                    .append("g");

                g.exit().selectAll("path").transition().duration(5000).attrTween("d", arcTween(0)).remove();                    

                path = g.selectAll("path").transition().duration(5000).attr("d", arc);

                newG.append("path")
                    .attr("d", arc);             
            };

Here is how the chart is built :

            function render(data) {
                root = data;
                node = root;

                width = $(".burst-chart-container").height();
                height = ($(".burst-chart-container").width() / 2);
                radius = Math.min(width, height) / 2;

                x = d3.scale.linear().range([0, 2 * Math.PI]);
                y = d3.scale.linear().range([0, radius]);

                rad = Math.min(width, height) / Math.PI - 25;

                partition = d3.layout.partition().sort(null).value(function (d) { return d.size; });

                arc = d3.svg.arc()
                    .startAngle(function (d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
                    .endAngle(function (d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
                    .innerRadius(function (d) { return Math.max(0, y(d.y)); })
                    .outerRadius(function (d) { return Math.max(0, y(d.y + d.dy)); });

                svg = d3.select(element[0]).append('svg')
                    .attr("width", width).attr("height", height)
                    .attr("class", "svgDashboard")
                    .append("g")
                    .attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")");                                     

                g = svg.datum(root).selectAll("g")
                    .data(partition.nodes)
                    .enter()
                    .append("g")

                path = g.append("path")
                    .attr("d", arc)
            }

I do know that path.attr("d",arc) should update the visual, but it doesn't work in my case.

I think that it has something to do with the partition layout who dosen't tell the existing arcs that they need to change, or the way that I do the selection to update the data, but I might be wrong.

Any help would be appreciated.


Solution

  • I found out that the path and text data were never updated. My update did only change g element data. To correct it, I simply take the parent data and put into into his child after I updated it.

                    g = svg.datum(root)
                        .selectAll("g")
                        .data(partition.nodes(root));
    
                    //Add this part to update child elements
                    g.selectAll("path").each(function () {
                        d3.select(this).datum(d3.select(this.parentNode).datum());
                    });
    
                    g.selectAll("text").each(function () {
                        d3.select(this).datum(d3.select(this.parentNode).datum());
                    });
    

    With those changes, i am able to call my attrTween function which update the visual with the new data.