Skip to content

Instantly share code, notes, and snippets.

@SpaceActuary
Last active July 14, 2017 11:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SpaceActuary/5252383292ed0331def34c9d2066a56f to your computer and use it in GitHub Desktop.
Save SpaceActuary/5252383292ed0331def34c9d2066a56f to your computer and use it in GitHub Desktop.
Org Bubbles
license: mit
ID PID Level Position Location Type
0 0 1 5.5 2 M
1 0 2 5.5 1 M
2 1 3 3 1 M
3 1 3 9 1 N
4 1 3 7 1 N
5 1 3 5.5 1 M
6 5 4 3 1 N
7 5 4 8 2 N
8 6 5 1 3 M
9 6 5 3 1 M
10 6 5 5 1 N
11 7 5 7 1 N
12 7 5 9 2 M
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body {
margin:0;
position:fixed;
top:0;
right:0;
bottom:0;
left:0;
font-family: sans-serif;
}
.selected {
fill: none;
}
.links line {
stroke: #000;
stroke-width: 3px;
}
.button {
min-width: 130px;
padding: 4px 5px;
cursor: pointer;
text-align: center;
font-size: 13px;
border: 1px solid #e0e0e0;
text-decoration: none;
}
.button.active {
background: #000;
color: #fff;
}
</style>
</head>
<body>
<div id="toolbar">
<button id="all" class="button">All</button>
<button id="Location" class="button">By Location</button>
<button id="Org" class="button active">By Org</button>
<button id="Type" class="button">By Type</button>
</div>
<script>
console.clear()
var w = 960, h = 500;
var radius = 25;
var color = d3.scaleOrdinal(d3.schemeCategory20);
var centerScale = d3.scalePoint().padding(1).range([0, w]);
var xScale = d3.scaleLinear().range([w * .2, w * .8]);
var yScale = d3.scaleLinear().range([h * .2, h * .8]);
var forceStrength = 0.05;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h)
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.ID; })
.strength(0))
.force("collide",d3.forceCollide( function(d){
return d.r + 8 }).iterations(16)
)
.force("charge", d3.forceManyBody())
.force("y", d3.forceY().y(h / 2))
.force("x", d3.forceX().x(w / 2))
d3.csv("data.csv", function(data){
graph = {
"nodes": data,
"links": data.map(function(d){
return {source: d.ID, target: d.PID};
})
};
data.forEach(function(d){
d.r = radius;
d.x = w / 2;
d.y = h / 2;
})
console.table(data);
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return 3; });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", radius)
.style("fill", function(d, i){ return color(d.ID); })
.style("stroke", function(d, i){ return color(d.ID); })
.style("stroke-width", 5)
.style("pointer-events", "all")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function ticked() {
//console.log("tick")
//console.log(data.map(function(d){ return d.x; }));
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; });
}
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function dragstarted(d,i) {
//console.log("dragstarted " + i)
if (!d3.event.active) simulation.alpha(1).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d,i) {
//console.log("dragged " + i)
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d,i) {
//console.log("dragended " + i)
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
var me = d3.select(this)
console.log(me.classed("selected"))
me.classed("selected", !me.classed("selected"))
d3.selectAll("circle")
.style("fill", function(d, i){ return color(d.ID); })
d3.selectAll("circle.selected")
.style("fill", "none")
}
function groupBubbles() {
hideTitles();
link.transition().duration(500)
.style("stroke-opacity", 0)
// @v4 Reset the 'x' force to draw the bubbles to the center.
simulation.force('x', d3.forceX().strength(forceStrength).x(w / 2));
// @v4 We can reset the alpha value and restart the simulation
simulation.alpha(1).restart();
}
function splitBubbles(byVar) {
centerScale.domain(data.map(function(d){ return d[byVar]; }));
if(byVar == "all"){
hideTitles()
} else {
showTitles(byVar, centerScale);
}
link.transition().duration(500)
.style("stroke-opacity", 0)
// @v4 Reset the 'x' force to draw the bubbles to their year centers
simulation
.force('x', d3.forceX().strength(forceStrength).x(function(d){
return centerScale(d[byVar]);
})).
force('y', d3.forceY().strength(forceStrength).y(h / 2));
// @v4 We can reset the alpha value and restart the simulation
simulation.alpha(2).restart();
}
function orgBubbles() {
console.log("orgBubbles")
xScale.domain(d3.extent(graph.nodes, function(d){ return +d.Position; }));
yScale.domain(d3.extent(graph.nodes, function(d){ return +d.Level; }));
link.transition().duration(2000)
.style("stroke-opacity", 1)
// @v4 Reset the 'x' force to draw the bubbles to their year centers
simulation
.force('x', d3.forceX().strength(forceStrength).x(function(d){
return xScale(d.Position);
}))
.force('y', d3.forceY().strength(forceStrength).y(function(d){
return yScale(d.Level);
}));
// @v4 We can reset the alpha value and restart the simulation
simulation.alpha(2).restart();
}
function hideTitles() {
svg.selectAll('.title').remove();
}
function showTitles(byVar, scale) {
// Another way to do this would be to create
// the year texts once and then just hide them.
var titles = svg.selectAll('.title')
.data(scale.domain());
titles.enter().append('text')
.attr('class', 'title')
.merge(titles)
.attr('x', function (d) { return scale(d); })
.attr('y', 40)
.attr('text-anchor', 'middle')
.text(function (d) { return byVar + ' ' + d; });
titles.exit().remove()
}
function setupButtons() {
d3.selectAll('.button')
.on('click', function () {
// Remove active class from all buttons
d3.selectAll('.button').classed('active', false);
// Find the button just clicked
var button = d3.select(this);
// Set it as the active button
button.classed('active', true);
// Get the id of the button
var buttonId = button.attr('id');
console.log(buttonId)
// Toggle the bubble chart based on
// the currently clicked button.
if(buttonId == "Org"){
orgBubbles()
} else {
splitBubbles(buttonId);
}
});
}
setupButtons()
orgBubbles()
})
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment