Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@dhoboy
Last active October 26, 2015 01:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhoboy/4341265f6d241e3d07d4 to your computer and use it in GitHub Desktop.
Save dhoboy/4341265f6d241e3d07d4 to your computer and use it in GitHub Desktop.
Overweight NY Students: #3

Each bubble is a School District total of the percentage of students either Overweight or Obese in New York State school systems, since 2010. Dataset here.

This is an example of the d3 Force layout with a quantized, divergent color scale. Color scale thanks to Color Brewer.

Bubbles get bigger, redder, and float to the top as the percentage of overweight/obese students goes up.

<html>
<head>
<script src="http://d3js.org/d3.v3.js"></script>
</head>
<body>
<script>
var w = 960,
h = 960,
format = d3.format(".2%"),
maxRadius = 20;
// pull in csv data
d3.csv("/d/283008a26997e97e0490/Student_Weight_Status_Category_Reporting_Results__Beginning_2010.csv", function(error, rows) {
var dataset = [], counter = 0;
rows.forEach(function(entry){
if ((entry["GRADE LEVEL"] == "DISTRICT TOTAL") && (entry["PCT OVERWEIGHT OR OBESE"] != "")) {
var pctOverObese = /[\d]+\.[\d]/.exec(entry["PCT OVERWEIGHT OR OBESE"]);
if (pctOverObese != null) {
pctOverObese = pctOverObese[0] / 100; // form percent mathematically
entry["pctOverObese"] = pctOverObese;
entry["pos"] = counter;
counter += 1;
dataset.push(entry);
}
}
});
// desired data is loaded. form scales based on it.
var xScale = d3.scale.linear()
.domain([0, dataset.length])
.range([w/4, (3*w)/4]);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d){ return d["pctOverObese"]; })])
.range([h, 0]);
var rScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d){ return d["pctOverObese"]; })])
.range([2, 20]);
var colorScale = d3.scale.quantize()
.domain([0, d3.max(dataset, function(d){ return d["pctOverObese"]; })])
.range(["#006837", "#1a9850", "#66bd63", "#fdae61", "#f46d43", "#d73027", "#a50026"]);
// set node positions
dataset.forEach(function(node){
node["x"] = xScale(node["pos"]);
node["y"] = yScale(node["pctOverObese"]);
});
// set up force layout
var force = d3.layout.force()
.nodes(dataset)
.size([w, h])
.gravity(.01)
.charge(-2)
.on("tick", tick)
.start();
// append the svg
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// create and draw nodes
var node = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("r", 0) // so the tween doesn't freak out
.style("fill", function(d) { return colorScale(d["pctOverObese"]); })
.style("stroke", "rgba(255,255,255,.5)")
node.transition()
.duration(750)
.delay(function(d, i) { return i * 2; })
.attrTween("r", function(d) {
var i = d3.interpolate(0, rScale(d["pctOverObese"]));
return function(t) { return d["r"] = i(t); };
});
function tick(e) {
node
.each(collide(.5)) // the collide parameter remains a mystery to me
.attr("cx", function(d){ return d["x"]; })
.attr("cy", function(d){ return d["y"]; })
.append("title")
.text(function(d) {
return d["AREA NAME"] + ": " + format(d["pctOverObese"]);
});
}
// how this works I have only a vague idea
function collide(alpha) {
var quadtree = d3.geom.quadtree(dataset);
return function(d) {
var r = d["r"] + maxRadius,
nx1 = d["x"] - r,
nx2 = d["x"] + r,
ny1 = d["y"] - r,
ny2 = d["y"] + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d["x"] - quad.point.x,
y = d["y"] - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d["r"] + quad.point.radius;
if (l < r) {
l = (l - r) / l * alpha;
d["x"] -= x *= l;
d["y"] -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
});
d3.select(self.frameElement).style("height", h + "px");
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment