Skip to content

Instantly share code, notes, and snippets.

@SpaceActuary
Created March 24, 2016 20:36
Show Gist options
  • Save SpaceActuary/d1c5aaacb3ede5b8812a to your computer and use it in GitHub Desktop.
Save SpaceActuary/d1c5aaacb3ede5b8812a to your computer and use it in GitHub Desktop.
Pseudo-Demers Cartogram
license: gpl-3.0

This is not a true Demers cartogram; it lacks links between adjacent features. Instead of trying to preserve connectedness, this pseudo-cartogram tries to preserve locality, putting each square as close as possible to its origin without overlapping.

forked from mbostock's block: Pseudo-Demers Cartogram

<!DOCTYPE html>
<meta charset="utf-8">
<title>Demers Cartogram</title>
<style>
rect {
fill: #eee;
stroke: #000;
stroke-width: 1.5px;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
// Ratio of Obese (BMI >= 30) in U.S. Adults, CDC 2008
var valueById = [
NaN, .187, .198, NaN, .133, .175, .151, NaN, .100, .125,
.171, NaN, .172, .133, NaN, .108, .142, .167, .201, .175,
.159, .169, .177, .141, .163, .117, .182, .153, .195, .189,
.134, .163, .133, .151, .145, .130, .139, .169, .164, .175,
.135, .152, .169, NaN, .132, .167, .139, .184, .159, .140,
.146, .157, NaN, .139, .183, .160, .143
];
var margin = {top: 0, right: 0, bottom: 0, left: 0},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
padding = 3;
var projection = d3.geo.albersUsa();
var radius = d3.scale.sqrt()
.domain([0, d3.max(valueById)])
.range([0, 30]);
var force = d3.layout.force()
.charge(0)
.gravity(0)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("/mbostock/raw/3750900/us-state-centroids.json", function(error, states) {
if (error) throw error;
var nodes = states.features
.filter(function(d) { return !isNaN(valueById[+d.id]); })
.map(function(d) {
var point = projection(d.geometry.coordinates),
value = valueById[+d.id];
if (isNaN(value)) fail();
return {
x: point[0], y: point[1],
x0: point[0], y0: point[1],
r: radius(value),
value: value
};
});
force
.nodes(nodes)
.on("tick", tick)
.start();
var node = svg.selectAll("rect")
.data(nodes)
.enter().append("rect")
.attr("width", function(d) { return d.r * 2; })
.attr("height", function(d) { return d.r * 2; });
function tick(e) {
node.each(gravity(e.alpha * .1))
.each(collide(.5))
.attr("x", function(d) { return d.x - d.r; })
.attr("y", function(d) { return d.y - d.r; });
}
function gravity(k) {
return function(d) {
d.x += (d.x0 - d.x) * k;
d.y += (d.y0 - d.y) * k;
};
}
function collide(k) {
var q = d3.geom.quadtree(nodes);
return function(node) {
var nr = node.r + padding,
nx1 = node.x - nr,
nx2 = node.x + nr,
ny1 = node.y - nr,
ny2 = node.y + nr;
q.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== node)) {
var x = node.x - quad.point.x,
y = node.y - quad.point.y,
lx = Math.abs(x),
ly = Math.abs(y),
r = nr + quad.point.r;
if (lx < r && ly < r) {
if (lx > ly) {
lx = (lx - r) * (x < 0 ? -k : k);
node.x -= lx;
quad.point.x += lx;
} else {
ly = (ly - r) * (y < 0 ? -k : k);
node.y -= ly;
quad.point.y += ly;
}
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment