user3333257
user3333257

Reputation: 51

D3js - Binding Data to Multiple Donut Charts

Longtime lurker, first time posting.

I've created a fiddle to illustrate what I discuss below.

I can create a single donut chart using data returned from a database. That's great, but I really need to be able to create 4 donut charts showing the percent filled for each site. As you can see in the fiddle when I create my foreground arc like so:

    // Foreground arc
    svg.selectAll("svg")
    svg.append("path")
      .datum({endAngle: prctFilled * twoPi})
      .attr("d", arc)
      .style("fill", "orange");

I am only getting the last value from my json dataset and it's being displayed in all 4 donut charts.

My first thought was to create an array of prctFilled and then iterate through the values in my foreground arc. Something like this:

data.forEach(function (d) {
    prctFilled.push(1 - ((d.capacity - d.filled) / d.capacity));

});

When I check the console I can see the values are in there [0.09999999999999998, 0.25, 0.5, 0.75] but I'm not sure how to iterate through them in the code for my foreground arc. To be honest, I don't even know if that's what I'm supposed to do. I've been wrestling with this for several days now and I can't remember everything I've tried but I can give you a list of several examples I looked at (unfortunately my reputation is less than 10 so I can't post more than 2 links)

From Mike Bostock: 3 Little Circles tutorial, Donut Multiples, Pie Multiples

http://jsfiddle.net/RKHnt/3/ - this seemed very similar to what I want to do

I've also looked at the following questions:

(use https://stackoverflow.com/questions/ and then plug in the number):

15264575

21733536

16894068 - at one point I was getting NaN errors but I haven't been able to duplicate those for the purpose of this post.

At this point I'm so confused it's possible the answer is right in front of me and I just don't see it. Very frustrating. Any help is greatly appreciated. I'm really struggling with this. I've never posted before so if more information is needed I'll be happy to provide it. Thank you!

Upvotes: 0

Views: 1767

Answers (2)

user3333257
user3333257

Reputation: 51

Solved my problem!

Here's the working fiddle: http://jsfiddle.net/qYGPC/12/

What I Did

Basically, it came down to my foreground path. At the top of my code I didn't specify the .endAngle parameter in my arc generator.

var arc = d3.svg.arc()
    .innerRadius(22.5)
    .outerRadius(30)
    .startAngle(0);

That's because I was going to do it in my background and foreground paths. So, since the background is a complete circle its .endAngle is 2*Pi.

// Background arc
svg.selectAll("svg")
svg.append("path")
    .datum({endAngle: twoPi})
    .style("fill", "#e6e6e6")
    .attr("d", arc);

I knew I had to do the same thing in my foreground path but I couldn't figure out where I needed to specify .endAngle. To add to my confusion I thought I needed to iterate through my code and calculate my "percent filled" and then use that result in my foreground path. I did need to do that but my code was in the wrong spot. I had originally put it at the top of my code like so:

data.forEach(function (d) {
  prctFilled = (1 - ((d.capacity - d.filled) / d.capacity));
});

Well, that's exactly why the last value in my JSON was being applied to all 4 charts! I needed to kill that code. I also needed to kill

.datum({endAngle: twoPi * prctFilled})

in my foreground path, and, instead, include a function that iterates through my dataset and returns the "percent filled" as an attribute in my foreground path (and remembering to multiply it by 2Pi). Like so:

// Foreground arc
svg.selectAll("svg")
svg.append("path")
    .attr("d", arc.endAngle(function (d) {return (twoPi * (1 - ((d.capacity - d.filled) / d.capacity)));}))
    .style("fill", "orange");

Once I did that I got the answer I was looking for.

Here's the code (sans dataset):

var width = 120,
    height = 62.5,
    twoPi = 2 * Math.PI;

var arc = d3.svg.arc()
    .innerRadius(22.5)
    .outerRadius(30)
    .startAngle(0);


var svg = d3.select("body").selectAll("svg")
    .data(data)
    .enter().append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

// Background arc
svg.selectAll("svg")
svg.append("path")
    .datum({endAngle: twoPi})
    .style("fill", "#e6e6e6")
    .attr("d", arc);

// Foreground arc
svg.selectAll("svg")
svg.append("path")
    .attr("d", arc.endAngle(function (d) {return (twoPi * (1 - ((d.capacity - d.filled) / d.capacity)));}))
    .style("fill", "orange");

// Add the site names to the center of the chart
svg.append("text")
    .attr("dy", ".35em")
    .style("text-anchor", "middle")
    .text(function (d) {return d.site;});

And once again here's the working fiddle: http://jsfiddle.net/qYGPC/12/

NOTE: In case anyone is wondering, I needed to get a "percent filled" result for each site. If I hadn't subtracted ((d.capacity - d.filled) / d.capacity) from 1 I would've gotten the "percent remaining" instead of "percent filled". This may be obvious to most people but I thought I'd throw it out there.

Thanks for the replies. I hope this helps someone out there who's struggling.

Upvotes: 3

cerberos
cerberos

Reputation: 8035

If you want 4 charts you should use 4 datasets, split the json you have into dataHmi, dataPoplar etc. Then work out how to build one donut chart, this should help http://bl.ocks.org/j0hnsmith/5591130

Upvotes: 0

Related Questions