Created
June 21, 2015 00:29
-
-
Save jdesilvio/5178bfc01827b4be88e2 to your computer and use it in GitHub Desktop.
D3.js Chord Diagram - Modified from Mike Bostock's example
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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Chord Diagram</title> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<style type="text/css"> | |
#circle circle { | |
fill: none; | |
pointer-events: all; | |
} | |
.group path { | |
stroke: #bbb; | |
stroke-width: 1px; | |
fill-opacity: .4; | |
} | |
.group path:hover { | |
fill-opacity: .8; | |
} | |
path.chord { | |
stroke: #000; | |
stroke-width: 1px; | |
} | |
#circle:hover path.fade { | |
display: none; | |
} | |
html { | |
min-width: 1040px; | |
} | |
body { | |
background: #fcfcfa; | |
color: #333; | |
font-family: Helvetica; | |
margin: 1em auto 4em auto; | |
position: relative; | |
width: 960px; | |
} | |
h1 { | |
color: #000; | |
font-family: Helvetica; | |
font-size: 64px; | |
font-weight: 300; | |
letter-spacing: -2px; | |
margin: .3em 0 .1em 0; | |
text-rendering: optimizeLegibility; | |
} | |
body > pre { | |
border-left: solid 2px #ccc; | |
padding-left: 18px; | |
margin: 2em 0 2em -20px; | |
} | |
svg { | |
font: 16px sans-serif; | |
font-family: Helvetica; | |
font-style: bold; | |
text-rendering: optimizeLegibility; | |
} | |
</style> | |
</head> | |
<body> | |
<h1 class=headline>Chord Diagram</h1> | |
<script> | |
var testImports; | |
var testMatrix; | |
var sizeTest; | |
var nameTest; | |
var indexTest; | |
var testLayout; | |
var width = 960, | |
height = 960, | |
outerRadius = Math.min(width, height) / 2 - 100, | |
innerRadius = outerRadius - 24; | |
var formatPercent = d3.format(".1%"); | |
var fill = d3.scale.category20c(); | |
var arc = d3.svg.arc() | |
.innerRadius(innerRadius) | |
.outerRadius(outerRadius); | |
var layout = d3.layout.chord() | |
.padding(.04) | |
.sortSubgroups(d3.descending) | |
.sortChords(d3.ascending); | |
var path = d3.svg.chord() | |
.radius(innerRadius); | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.append("g") | |
.attr("id", "circle") | |
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); | |
svg.append("circle") | |
.attr("r", outerRadius); | |
d3.json("headcountChord2.json", function(error, imports) { | |
testImports = imports; | |
var indexByName = d3.map(), | |
nameByIndex = d3.map(), | |
sizeByName = d3.map(), | |
matrix = [], | |
n = 0; | |
// Returns the Flare package name for the given class name. | |
function name(name) { | |
return name.substring(0, name.lastIndexOf(".")).substring(6); | |
} | |
// Compute a unique index for each package name. | |
imports.forEach(function(d) { | |
var s = d.size; | |
if (!indexByName.has(d = name(d.name))) { | |
nameByIndex.set(n, d); | |
indexByName.set(d, n++); | |
sizeByName.set(d, s); | |
} | |
}); | |
nameTest = nameByIndex; | |
sizeTest = sizeByName; | |
indexTest = indexByName; | |
// Construct a square matrix counting package imports. | |
imports.forEach(function(d) { | |
var source = indexByName.get(name(d.name)), | |
row = matrix[source], | |
size = d.size; | |
if (!row) { | |
row = matrix[source] = []; | |
for (var i = -1; ++i < n;) row[i] = 0; | |
} | |
//d.imports.forEach(function(d) { row[indexByName.get(name(d))] = sizeByName.get(name(d)); }); | |
d.imports.forEach(function(d) { row[indexByName.get(name(d[0]))] = d[1]; }); | |
}); | |
// Compute the chord layout. | |
layout.matrix(matrix); | |
testMatrix = matrix | |
testLayout = layout.matrix(matrix); | |
// Add a group per neighborhood. | |
var group = svg.selectAll(".group") | |
.data(layout.groups) | |
.enter().append("g") | |
.attr("class", "group") | |
.on("mouseover", mouseover); | |
// Add a mouseover title. | |
group.append("title").text(function(d, i) { | |
return nameByIndex.get(d.index) + " Headcount: " + sizeByName.get(nameByIndex.get(d.index)); | |
}); | |
// Add the group arc. | |
var groupPath = group.append("path") | |
.attr("id", function(d, i) { return "group" + i; }) | |
.attr("d", arc) | |
.style("fill", function(d, i) { return fill(d.index); }); | |
// Add a text label. | |
var groupText = group.append("text") | |
.each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; }) | |
.attr("dy", ".35em") | |
.attr("transform", function(d) { | |
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" | |
+ "translate(" + (innerRadius + 26) + ")" | |
+ (d.angle > Math.PI ? "rotate(180)" : ""); | |
}) | |
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; }) | |
.text(function(d) { return nameByIndex.get(d.index).toUpperCase(); }); | |
// Add the chords. | |
var chord = svg.selectAll(".chord") | |
.data(layout.chords) | |
.enter().append("path") | |
.attr("class", "chord") | |
.style("stroke", function(d) { return d3.rgb(fill(d.source.index)).darker(); }) | |
.style("fill", function(d) { return fill(d.source.index); }) | |
.attr("d", path); | |
//Add an elaborate mouseover title for each chord. | |
chord.append("title").text(function(d) { | |
return nameByIndex.get(d.source.index) + " → " + nameByIndex.get(d.target.index) + "\n" + | |
"Headcount: " + matrix[d.target.index][d.source.index]; | |
}); | |
function mouseover(d, i) { | |
chord.classed("fade", function(p) { | |
return p.source.index != i && p.target.index != i; | |
}); | |
} | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment