THT75
THT75

Reputation: 46

D3 Multiple Pie Chart Updates

I am quite new to D3 but have been working through some mbostocks examples but hitting an issue when trying to update multiple pie charts. I can generate these fine from my data array but when I want to update them I run into an issue.

The issue is quite simple but I am a little stuck on how to fix this. I have run up my code in js fiddle that can be found here. You will see that in my example I build three pies, then wait 3 seconds and update these to new data. The issue I have is that all pies always seem to get updated with the same data.

I believe this is due to the way I am making the path selection in order to update the pie. it looks like I am updating each all the paths each time with each data array so they all end up being updated with the last dataset in my array.

If anyone knows how I can update this in order to correctly build the pies I would be very grateful of any help, pointers or comments.

var data = [
  [3, 4, 5, 9],
  [1, 7, 3, 4],
  [4, 3, 2, 1],
];


function getData() {
    // Generate some random data to update the pie with
    tdata = []
    for(i in data) {
        rdata = []
        for(c in data[i]) {
            rdata.push(Math.floor((Math.random() * 10) + 1) )
        }
        tdata.push(rdata)
    }
    return tdata
}

// ------------

var m = 10,
    r = 100

var mycolors = ["red","#FF7F00","#F5CC11","#D61687","#1E93C1","#64B72D","#999999"]

var arc = d3.svg.arc()
            .innerRadius(r / 2)    
            .outerRadius(r)

var pie = d3.layout.pie()
    .value(function(d) { return d; })
    .sort(null);  

var svg = d3.select("body").selectAll("svg")
    .data(data)
    .enter()
        .append("svg")
        .attr("width", (r + m) * 2)
        .attr("height", (r + m) * 2)
        .attr("id", function(d,i) {return 'pie'+i;})
        .append("svg:g")
            .attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");

var path = svg.selectAll("path")
    .data(pie)
    .enter()
        .append("svg:path")
        .attr("d", arc)
        .style("fill", function(d, i) { return mycolors[i]; })
        .each(function(d) { this._current = d; }); // store the initial angles

var titles = svg.append("svg:text") 
    .attr("class", "title") 
    .text(function(d,i) {return i;}) 
    .attr("dy", "5px")  
    .attr("text-anchor", "middle") 


// -- Do the updates
//------------------------
setInterval(function() {
  change()
}, 3000);


function change() {
    // Update the Pie charts with random data
    piedata = getData()
    svg.each(function(d,i) {
        path = path.data(pie(piedata[i]))
        path.transition().duration(1000).attrTween("d", arcTween); 
        })
    // temp, print new array to screen
    tdata = ""
    for(x in piedata) {
        tdata += "<strong>"+x+":</strong> "+piedata[x]+"<br>"
    }
    $('#pieData').html(tdata)
}

function arcTween(a) {
  var i = d3.interpolate(this._current, a);
  this._current = i(0);
  return function(t) {
    return arc(i(t));
  };
}

Upvotes: 0

Views: 1493

Answers (1)

THT75
THT75

Reputation: 46

Right, I finally got this working and am posting the working solution incase others are trying to do the same thing.

I expect this might not be the best nor most efficient way of doing it but this is going to be fine for what I need (at this point). But if anyone still has any better solutions it would be good to hear from you.

I ended up selecting the paths based on a unique id that I gave the individual SVG elements which I created, then just updated these paths only. Sounds simple now when I say it like this but did have me stumped for a while.

function change() {
    // Update the Pie charts with random data
    var newdata = getData()
    for(x in newdata) {
        var npath = d3.select("#pie"+x).selectAll("path").data(pie(newdata[x]))
        npath.transition().duration(1000).attrTween("d", arcTween); // redraw the arcs
    }
}

Full working copy can be found at http://jsfiddle.net/THT75/nskwwbnf/

Upvotes: 2

Related Questions