Created
May 21, 2013 14:44
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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