Skip to content

Instantly share code, notes, and snippets.

@andrewhassan
Created May 21, 2013 14:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewhassan/5620353 to your computer and use it in GitHub Desktop.
Save andrewhassan/5620353 to your computer and use it in GitHub Desktop.
Grouped Pie Chart Function. Takes in data and some parameters for the pie chart. It will plot the data and return an update function that can be called with new data. Note: Adding new data to the dataset when calling the update function will cause uncoloured slices to appear. Also, this function requires d3.js.
/**
* Create Pie function
*
* This function will create a grouped pie graph on call and return an update function that can be called with new data/
*
* Params:
* data: The data for the chart in the form [{"label": "label", "value": 123, "group": "Group Name"},...]
* w: The width of the svg element
* h: The heigh of the svg element
* animationDuration: The amount of time (in ms) that you want the animation to take when updating data
* donutChart: An optional boolean set to true if you want a donut shape. Note that if the value is anything but undefined,
* it will be interpretted as true.
* colors: An optional array of colours in "#FFFFFF" form
*
* Requires: D3.js
*/
function createPie(data, w, h, animationDuration, donutChart, colors) {
// Set up dimensions and colours
var r = Math.min(w, h)/4;
if(typeof (colors) === "undefined") {
color = ["#16a085", "#34495e", "#95a5a6", "#c0392b", "#2ecc71", "#9b59b6", "#e67e22", "#f1c40f", "#1abc9c", "#2c3e50"];
}
else {
color = colors;
}
var donut;
if (typeof (donutChart) === "undefined") {
donut = false;
}
else
{
donut = true;
}
var groups = generateGroupData(data);
var svg = d3.select("body").append("svg").attr("class", "pie-chart");
var groupSvg = d3.select(".pie-chart")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + w/2 + ", " + h/2 + ")");
/* GROUP PIE CHART */
var groupPie = d3.layout.pie()
.sort(function(a, b) { return a.group>b.group; })
.value(function(d) { return d.value; });
var groupArc = d3.svg.arc()
.innerRadius(1.1*r)
.outerRadius(1.4*r);
var groupPath = groupSvg.selectAll("path")
.data(groupPie(groups))
.enter().append("path")
.attr("fill", function(d, i) { return color[i]; })
.attr("d", groupArc)
.each(function(d) { this._groupcurrent = d; });
var groupLabel = groupSvg.selectAll("text")
.data(groupPie(groups))
.enter().append("text")
.attr("transform", function(d) {
var c = groupArc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x*x + y*y);
return "translate(" + (x/h * 1.5*r) + ',' +
(y/h * 1.5*r) + ")";
})
.attr("text-anchor", function(d) {
// are we past the center?
return (d.endAngle + d.startAngle)/2 > Math.PI ?
"end" : "start";
})
.text(function(d, i) { return d.data.group; })
.attr("style", "font-family:Lato")
.each(function(d) { this._grouplabelcurrent = d; });
/* DATA PIE CHART */
var dataSvg = d3.select(".pie-chart")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + w/2 + ", " + h/2 + ")");
var dataPie = d3.layout.pie()
.sort(function(a, b) { return a.group>b.group; })
.value(function(d) { return d.value; });
var dataArc = d3.svg.arc()
.innerRadius((donut) ? 0.5*r : 0)
.outerRadius(r);
var dataPath = dataSvg.selectAll("path")
.data(dataPie(data))
.enter().append("path")
.attr("fill", function(d, i) { return color[i + groups.length]; })
.attr("d", dataArc)
.each(function(d) { this._datacurrent = d; });
var dataLabel = dataSvg.selectAll("text")
.data(dataPie(data))
.enter().append("text")
.attr("transform", function(d) {
var c = dataArc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x*x + y*y);
return "translate(" + (x/h * 0.7 * r) + ',' +
(y/h * 0.7 * r) + ")";
})
.attr("text-anchor", function(d) {
// are we past the center?
return (d.endAngle + d.startAngle)/2 > Math.PI ?
"end" : "start";
})
.text(function(d, i) { return d.data.label; })
.attr("style", "font-family:Lato")
.each(function(d) { this._datalabelcurrent = d; });
var groupLegend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", 200)
.attr("height", 200)
.selectAll("g")
.data(groups)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
groupLegend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) { return color[i]; });
groupLegend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d.group; })
.attr("style", "font-family:Lato");
var dataLegend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", 200)
.attr("height", 200)
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
dataLegend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) { return color[i + groups.length]; });
dataLegend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d.label; })
.attr("style", "font-family:Lato");
/* GROUP DATA PROCESSING FUNCTIONS */
function generateGroupData (d) {
// Find the distinct groups and their cumulative values
result = [];
// For each data in the dataset, add it to the groups array if it's group hasn't been added before,
// otherwise add it's value to the corresponding group in the groups array.
for (var i = 0; i < d.length; i++) {
var arrayIndex = findInArray(result, d[i].group, "group");
if (arrayIndex == -1) {
result.push({"group": d[i].group,
"value": d[i].value});
}
else {
result[arrayIndex] = {"group": d[i].group,
"value": d[i].value + result[arrayIndex].value};
}
}
// Sort the group alphabetically by group name (this matches the sort for the pie chart below)
result = result.sort(function(a, b) { return a.group>b.group; });
return result;
}
function findInArray(arr, val, field) {
for (var idx = 0; idx < arr.length; idx++) {
if (arr[idx][field] == val) {
return idx;
}
}
return -1;
}
/* TRANSITION TWEENING FUNCTIONS */
function groupArcTween(a) {
var i = d3.interpolate(this._groupcurrent, a);
this._groupcurrent = i(0);
return function(t) {
return groupArc(i(t));
};
}
function groupTransformTween(a) {
var i = d3.interpolate(this._grouplabelcurrent, a);
this._grouplabelcurrent = i(0);
return function(d) {
var c = groupArc.centroid(i(d)),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x*x + y*y);
return "translate(" + (x/h * 1.5*r) + ',' +
(y/h * 1.5*r) + ")";
};
}
function dataArcTween(a) {
var i = d3.interpolate(this._datacurrent, a);
this._datacurrent = i(0);
return function(t) {
return dataArc(i(t));
};
}
function dataTransformTween(a) {
var i = d3.interpolate(this._datalabelcurrent, a);
this._datalabelcurrent = i(0);
return function(d) {
var c = dataArc.centroid(i(d)),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x*x + y*y);
return "translate(" + (x/h * 0.7*r) + ',' +
(y/h * 0.7*r) + ")";
};
}
/* UPDATE DATA METHOD */
return function (data) {
var groups = generateGroupData(data);
dataPath = dataPath.data(dataPie(data));
dataLabel = dataLabel.data(dataPie(data));
groupPath = groupPath.data(groupPie(groups));
groupLabel = groupLabel.data(groupPie(groups));
dataPath.transition().duration(animationDuration).attrTween("d", dataArcTween);
dataLabel.transition().duration(animationDuration).attrTween("transform", dataTransformTween);
dataLabel.attr("transform", function(d) {
var c = dataArc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x*x + y*y);
return "translate(" + (x/h * 0.7*r) + ',' +
(y/h * 0.7*r) + ")";
})
.attr("text-anchor", function(d) {
// are we past the center?
return (d.endAngle + d.startAngle)/2 > Math.PI ?
"end" : "start";
});
groupPath.transition().duration(animationDuration).attrTween("d", groupArcTween);
groupLabel.transition().duration(animationDuration).attrTween("transform", groupTransformTween);
groupLabel.attr("transform", function(d) {
var c = groupArc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x*x + y*y);
return "translate(" + (x/h * 1.5*r) + ',' +
(y/h * 1.5*r) + ")";
})
.attr("text-anchor", function(d) {
// are we past the center?
return (d.endAngle + d.startAngle)/2 > Math.PI ?
"end" : "start";
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment