Skip to content

Instantly share code, notes, and snippets.

@jdesilvio
Created June 21, 2015 00:29
Show Gist options
  • Save jdesilvio/5178bfc01827b4be88e2 to your computer and use it in GitHub Desktop.
Save jdesilvio/5178bfc01827b4be88e2 to your computer and use it in GitHub Desktop.
D3.js Chord Diagram - Modified from Mike Bostock's example
<!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