|
//////////////////////////////////////////////////////////// |
|
//////////////////////// Set-up //////////////////////////// |
|
//////////////////////////////////////////////////////////// |
|
var screenWidth = $(window).width(), |
|
mobileScreen = (screenWidth > 400 ? false : true); |
|
|
|
var margin = {left: 0, top: 20, right: 0, bottom: 0}, |
|
svgWidth = 960, |
|
svgHeight = 500 |
|
width = svgWidth - margin.left - margin.right, |
|
height = svgHeight - margin.top - margin.bottom; |
|
|
|
var svg = d3.select("#chart").append("svg") |
|
.attr("width", svgWidth) |
|
.attr("height", svgHeight); |
|
|
|
var wrapper = svg.append("g").attr("class", "chordWrapper") |
|
.attr("transform", "translate(" + (width / 2 + margin.left) + "," + (height / 2 + margin.top) + ")");; |
|
|
|
var outerRadius = Math.min(width, height) / 3, |
|
innerRadius = outerRadius * 0.95, |
|
pullOutSize = 50, |
|
opacityDefault = 1, //default opacity of chords |
|
opacityLow = 0.2; //hover opacity of those chords not hovered over |
|
|
|
//////////////////////////////////////////////////////////// |
|
////////////////////////// Data //////////////////////////// |
|
//////////////////////////////////////////////////////////// |
|
|
|
var Names = ["Asia Pacific - 31%","Europe - 27%","North America - 25%","Latin America - 4%","Middle East - 3%","Other - 10%","","Other - 10%","Middle East - 3%","Latin America - 4%","Asia Pacific - 21%","North America - 27%","Europe - 35%",""]; |
|
|
|
var respondents = 100, |
|
emptyPerc = 0.4, //What % of the circle should become empty |
|
emptyStroke = Math.round(respondents*emptyPerc), |
|
es = emptyStroke, |
|
areaToIndex = {}; |
|
Names.forEach(function(d, i) { |
|
areaToIndex[d] = i; |
|
}) |
|
matrix = []; |
|
var matrix = [ |
|
[00,00,00,00,00,00,00,00,00,00,31,00,00,00], //Asia Pacific '16 |
|
[00,00,00,00,00,00,00,00,00,00,00,00,27,00], //Europe '16 |
|
[00,00,00,00,00,00,00,00,00,00,00,25,00,00], //North America '16 |
|
[00,00,00,00,00,00,00,00,00,04,00,00,00,00], //Latin America '16 |
|
[00,00,00,00,00,00,00,00,03,00,00,00,00,00], //Meadle East '16 |
|
[00,00,00,00,00,00,00,10,00,00,00,00,00,00], //Other '16 |
|
[00,00,00,00,00,00,00,00,00,00,00,00,00,es], //Dummy stroke |
|
[00,00,00,00,00,10,00,00,00,00,00,00,00,00], //Other 2k |
|
[00,00,00,00,03,00,00,00,00,00,00,00,00,00], //Middle East 2k |
|
[00,00,00,04,00,00,00,00,00,00,00,00,00,00], //Latin America 2k |
|
[21,00,00,00,00,00,00,00,00,00,00,00,00,00], //Asia Pacific 2k |
|
[00,00,27,00,00,00,00,00,00,00,00,00,00,00], //North America 2k |
|
[00,35,00,00,00,00,00,00,00,00,00,00,00,00], //Europe 2k |
|
[00,00,00,00,00,00,es,00,00,00,00,00,00,00] //Dummy stroke |
|
]; |
|
//Calculate how far the Chord Diagram needs to be rotated clockwise to make the dummy |
|
//invisible chord center vertically |
|
var offset = (2 * Math.PI) * (emptyStroke/(respondents + emptyStroke))/4; |
|
|
|
//Custom sort function of the chords to keep them in the original order |
|
function customSort(a,b) { |
|
return 1; |
|
}; |
|
|
|
//Custom sort function of the chords to keep them in the original order |
|
var chord = customChordLayout() //d3.layout.chord()//Custom sort function of the chords to keep them in the original order |
|
.padding(.02) |
|
.sortChords(d3.descending) //which chord should be shown on top when chords cross. Now the biggest chord is at the bottom |
|
.matrix(matrix); |
|
|
|
var arc = d3.arc() |
|
.innerRadius(innerRadius) |
|
.outerRadius(outerRadius) |
|
.startAngle(startAngle) //startAngle and endAngle now include the offset in degrees |
|
.endAngle(endAngle); |
|
|
|
var path = stretchedChord() |
|
.radius(innerRadius) |
|
.startAngle(startAngle) |
|
.endAngle(endAngle) |
|
.pullOutSize(pullOutSize); |
|
|
|
//////////////////////////////////////////////////////////// |
|
//////////////////// Draw outer Arcs /////////////////////// |
|
//////////////////////////////////////////////////////////// |
|
|
|
var g = wrapper.selectAll("g.group") |
|
.data(chord.groups) |
|
.enter().append("g") |
|
.attr("class", "group") |
|
.on("mouseover", fade(opacityLow)) |
|
.on("mouseout", fade(opacityDefault)); |
|
|
|
g.append("path") |
|
.style("fill", function(d,i) { return (Names[i] === "" ? "none" : "lightgrey"); }) |
|
.style("pointer-events", function(d,i) { return (Names[i] === "" ? "none" : "auto"); }) |
|
.attr("d", arc) |
|
.attr("transform", function(d, i) { //Pull the two slices apart |
|
d.pullOutSize = pullOutSize * ( d.startAngle + 0.001 > Math.PI ? -1 : 1); |
|
return "translate(" + d.pullOutSize + ',' + 0 + ")"; |
|
}); |
|
|
|
|
|
//////////////////////////////////////////////////////////// |
|
////////////////////// Append Names //////////////////////// |
|
//////////////////////////////////////////////////////////// |
|
|
|
//The text also needs to be displaced in the horizontal directions |
|
//And also rotated with the offset in the clockwise direction |
|
g.append("text") |
|
.each(function(d) { d.angle = ((d.startAngle + d.endAngle) / 2) + offset;}) |
|
.attr("dy", ".35em") |
|
.attr("class", "titles") |
|
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : "start"; }) |
|
.attr("transform", function(d,i) { |
|
var c = arc.centroid(d); |
|
var sign = Math.sign(d.pullOutSize); |
|
var dy = 0; |
|
if (Names[d.index].indexOf("Latin America") != -1) { dy = 4; } |
|
if (Names[d.index].indexOf("Middle East") != -1) { dy = 9; } |
|
if (Names[d.index].indexOf("Other") != -1) { dy = 10; } |
|
|
|
return "translate(" + (c[0] + d.pullOutSize) + "," + c[1] + ")" |
|
+ "translate(" + [sign*15, dy] + ")" |
|
}) |
|
.text(function(d,i) { return Names[i]; }); |
|
|
|
//////////////////////////////////////////////////////////// |
|
//////////////////// Draw inner chords ///////////////////// |
|
//////////////////////////////////////////////////////////// |
|
|
|
var chords = wrapper.selectAll("path.chord") |
|
.data(chord.chords().sort(function(c0,c1){ |
|
return c1.source.index - c0.source.index; |
|
})) |
|
.enter().append("path") |
|
.attr("class", "chord") |
|
.style("stroke", "none") |
|
.style("fill", function(d) { |
|
var name = Names[d.source.index]; |
|
if (name.indexOf("Asia") !== -1) { |
|
return "#9BBF85"; |
|
} else if (name.indexOf("Europe") !== -1) { |
|
return "#B3589A"; |
|
} else if (name.indexOf("North America") !== -1) { |
|
return "#B3589A"; |
|
} |
|
else { |
|
return "lightgrey"; |
|
} |
|
}) |
|
.style("opacity", function(d) { return (Names[d.source.index] === "" ? 0 : opacityDefault); }) //Make the dummy strokes have a zero opacity (invisible) |
|
.style("pointer-events", function(d,i) { return (Names[d.source.index] === "" ? "none" : "auto"); }) //Remove pointer events from dummy strokes |
|
.attr("d", path); |
|
|
|
//////////////////////////////////////////////////////////// |
|
///////////////////////// Tooltip ////////////////////////// |
|
//////////////////////////////////////////////////////////// |
|
/* |
|
//Arcs |
|
g.append("title") |
|
.text(function(d, i) {return Math.round(d.value) + " people in " + Names[i];}); |
|
|
|
//Chords |
|
chords.append("title") |
|
.text(function(d) { |
|
return ""+ Names[d.target.index] + ": from " + Math.round(d.source.value)+ "% to " + Math.round(d.target.value) + "%"; |
|
}); |
|
*/ |
|
//////////////////////////////////////////////////////////// |
|
////////////////// Extra Functions ///////////////////////// |
|
//////////////////////////////////////////////////////////// |
|
|
|
//Include the offset in de start and end angle to rotate the Chord diagram clockwise |
|
function startAngle(d) { return d.startAngle + offset; } |
|
function endAngle(d) { return d.endAngle + offset; } |
|
|
|
// Returns an event handler for fading a given chord group |
|
function fade(opacity) { |
|
return function(d, i) { |
|
svg.selectAll("path.chord") |
|
.filter(function(d) { return d.source.index !== i && d.target.index !== i && Names[d.source.index] !== ""; }) |
|
.transition("fadeOnArc") |
|
.style("opacity", opacity); |
|
}; |
|
}//fade |