Skip to content

Instantly share code, notes, and snippets.

@SpaceActuary
Last active February 10, 2019 01:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save SpaceActuary/795d614b4b298e32131e to your computer and use it in GitHub Desktop.
Save SpaceActuary/795d614b4b298e32131e to your computer and use it in GitHub Desktop.
Switchable Cartograms
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

forked from SpaceActuary's block: Pseudo-Demers Cartogram

id code shortname longname 2008Total 2008D 2008R 2012Total 2012D 2012R
1 AL Ala. Alabama 9 0 9 9 0 9
2 AK Alaska Alaska 3 0 3 3 0 3
4 AZ Ariz. Arizona 10 0 10 11 0 11
5 AR Ark. Arkansas 6 0 6 6 0 6
6 CA Calif. California 55 55 0 55 55 0
8 CO Colo. Colorado 9 9 0 9 9 0
9 CT Conn. Connecticut 7 7 0 7 7 0
10 DE Del. Delaware 3 3 0 3 3 0
11 DC D.C. District of Columbia 3 3 0 3 3 0
12 FL Fla. Florida 27 27 0 29 29 0
13 GA Ga. Georgia 15 0 15 16 0 16
15 HI Hawaii Hawaii 4 4 0 4 4 0
16 ID Idaho Idaho 4 0 4 4 0 4
17 IL Ill. Illinois 21 21 0 20 20 0
18 IN Ind. Indiana 11 11 0 11 0 11
19 IA Iowa Iowa 7 7 0 6 6 0
20 KS Kan. Kansas 6 0 6 6 0 6
21 KY Ky. Kentucky 8 0 8 8 0 8
22 LA La. Louisiana 9 0 9 8 0 8
23 ME Me. Maine 4 4 0 4 4 0
24 MD Md. Maryland 10 10 0 10 10 0
25 MA Mass. Massachusetts 12 12 0 11 11 0
26 MI Mich. Michigan 17 17 0 16 16 0
27 MN Minn. Minnesota 10 10 0 10 10 0
28 MS Miss. Mississippi 6 0 6 6 0 6
29 MO Mo. Missouri 11 0 11 10 0 10
30 MT Mont. Montana 3 0 3 3 0 3
31 NE Neb. Nebraska 5 1 4 5 0 5
32 NV Nev. Nevada 5 5 0 6 6 0
33 NH N.H. New Hampshire 4 4 0 4 4 0
34 NJ N.J. New Jersey 15 15 0 14 14 0
35 NM N.M. New Mexico 5 5 0 5 5 0
36 NY N.Y. New York 31 31 0 29 29 0
37 NC N.C. North Carolina 15 15 0 15 0 15
38 ND N.D. North Dakota 3 0 3 3 0 3
39 OH Ohio Ohio 20 20 0 18 18 0
40 OK Okla. Oklahoma 7 0 7 7 0 7
41 OR Ore. Oregon 7 7 0 7 7 0
42 PA Pa. Pennsylvania 21 21 0 20 20 0
44 RI R.I. Rhode Island 4 4 0 4 4 0
45 SC S.C. South Carolina 8 0 8 9 0 9
46 SD S.D. South Dakota 3 0 3 3 0 3
47 TN Tenn. Tennessee 11 0 11 11 0 11
48 TX Tex. Texas 34 0 34 38 0 38
49 UT Utah Utah 5 0 5 6 0 6
50 VT Vt. Vermont 3 3 0 3 3 0
51 VA Va. Virginia 13 13 0 13 13 0
53 WA Wash. Washington 11 11 0 12 12 0
54 WV W.Va. West Virginia 5 0 5 5 0 5
55 WI Wis. Wisconsin 10 10 0 10 10 0
56 WY Wyo. Wyoming 3 0 3 3 0 3
<!DOCTYPE html>
<meta charset="utf-8">
<title>Demers Cartogram</title>
<style>
body {
font-family: "Avenir", sans-serif;
}
.states {
fill: #b8b8b8;
stroke: #fff;
}
.states.hidden {
fill: none;
stroke: none;
}
rect {
/*fill: steelblue;*/
fill-opacity: .8;
stroke: #000;
stroke-width: 0px;
}
text.maptitle {
text-anchor: middle;
alignment-baseline: central;
font-size: 36px;
}
text.legendtitle {
font-weight: bold;
}
.node text {
text-anchor: middle;
alignment-baseline: central;
fill: #fff;
}
form {
position: absolute;
right: 10px;
top: 50px;
font-family: sans-serif;
}
</style>
<body>
<form>
<label><input type="radio" name="mode" value="mccann"> McCann</label>
<label><input type="radio" name="mode" value="demers" checked> Demers</label>
<label><input type="radio" name="mode" value="dorling"> Dorling</label>
<!--label><input type="radio" name="election" value="2008" > 2008</label>
<label><input type="radio" name="election" value="2012" checked> 2012</label-->
<label><input type="checkbox" name="showmap" value="dorling" checked> Show Map</label>
</form>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="//d3js.org/queue.v1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.10.0/d3-legend.min.js"></script>
<script id="grid" type="text/plain">
AK ME
VT NH
WA ID MT ND MN IL WI MI NY RI MA
OR NV WY SD IA IN OH PA NJ CT DE
CA UT CO NE MO KY WV VA MD
AZ NM KS AR TN NC SC DC
OK LA MS AL GA
HI TX FL PR
</script>
<script>
//console.clear()
var margin = {top: 50, right: 0, bottom: 20, 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()
.range([0, 50]);
var force = d3.layout.force()
.charge(0)
.gravity(0)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")" +
"scale(" + Math.min(width / 960, height / 500) + ")");
var path = d3.geo.path();
var grid = {};
d3.select("#grid").text().split("\n").forEach(function(line, i) {
var re = /\w+/g, m;
while (m = re.exec(line)) {
grid[m[0]] = [m.index / 3, i]
}
});
var gridWidth = d3.max(d3.values(grid), function(d) { return d[0]; }) + 1,
gridHeight = d3.max(d3.values(grid), function(d) { return d[1]; }) + 1,
cellSize = 55,
cellPadding = 5;
var title = svg.append("text")
.attr("class", "maptitle")
.attr("x", width / 2)
.attr("y", -(margin.top / 2))
.text("2012 Presidental Election Results");
var colorScale = d3.scale.linear()
.domain([1, 0])
.range(["steelblue", "crimson"]);
svg.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(" + (width - 150) + "," + (height - 100) + ")");
queue()
.defer(d3.csv, "data.csv")
.defer(d3.json, "us-state-centroids.json")
.defer(d3.json, "us.json")
.await(ready);
function ready(error, data, centroids, us) {
if (error) throw error;
//console.table(data);
var dataById = d3.map(data, function(d) { return +d.id; });
var totalD = d3.sum(data, function(d) { return +d["2012D"]; }),
totalR = d3.sum(data, function(d) { return +d["2012R"]; })
var legendLinear = d3.legend.color()
.shapeWidth(30)
.cells(2)
.title("Candidate (electoral votes)")
.labels(["Obama (" + totalD + " votes)",
"Romney (" + totalR + " votes)"])
.scale(colorScale);
svg.select(".legendLinear")
.call(legendLinear);
svg.append("path")
.attr("class", "states visible")
.datum(topojson.feature(us, us.objects.states))
.attr("d", path);
radius.domain([0, d3.max(data, function(d){ return +d["2012Total"];} )]);
//console.log(dataById);
var nodes = centroids.features
.filter(function(d) { return dataById.has(+d.id); })
.map(function(d) {
var stateData = dataById.get(+d.id),
state = stateData.code,
cell = grid[state],
point = projection(d.geometry.coordinates),
votes = stateData["2012Total"],
marginD = +stateData["2012D"] / +stateData["2012Total"],
color = colorScale(marginD);
return {
id: +d.id,
x: point[0], y: point[1],
x0: point[0], y0: point[1],
xx: cell[0] * cellSize + 200, yy: cell[1] * cellSize - (cellSize / 2),
r: radius(votes),
r0: radius(votes),
value: votes,
state: state,
color: color
};
});
//console.log(nodes);
force
.nodes(nodes)
.on("tick", tickDemers)
.start();
var node = svg.selectAll("g.node")
.data(nodes)
var nodeEnter = node.enter().append("g")
.attr("class", "node");
nodeEnter.append("rect")
.attr("width", function(d) { return d.r * 2; })
.attr("height", function(d) { return d.r * 2; })
.style("fill", function(d) { return d.color; })
.attr("rx", function(d) { return 0; })
.attr("title", function(d) { return d.state + ": " + d.value + " electoral college votes"})
.on("click", function(d,i){
console.log("d[" + d.id + "]", d.state, d.value)
});
nodeEnter.append("text")
.attr("x", function (d) { return d.r; })
.attr("y", function (d) { return d.r; })
.style("font-size", function(d) { return (1.5 * d.r - 5); })
.text( function (d) { return d.state; });
function tickDemers(e) {
node.each(gravity(e.alpha * .1))
.each(collideDemers(.5))
.attr("transform", function(d) {
return "translate(" + (d.x - d.r) + "," + (d.y - d.r) + ")";
});
}
function tickDorling(e) {
node.each(gravity(e.alpha * .1))
.each(collideDorling(.5))
.attr("transform", function(d) {
return "translate(" + (d.x - d.r) + "," + (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 collideDemers(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;
});
};
}
function collideDorling(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,
l = x * x + y * y,
r = nr + quad.point.r;
if (l < r * r) {
l = ((l = Math.sqrt(l)) - r) / l * k;
node.x -= x *= l;
node.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
d3.selectAll("input[type='radio']")
.on("change", function change() {
if(this.value === "mccann"){
force.stop();
node.transition().duration(1000)
.attr("transform", function(d) {
return "translate(" + d.xx + "," + d.yy + ")";
});
node.selectAll("rect").transition().duration(1000)
.attr("rx", function(d){ return 0; })
.attr("width", cellSize - cellPadding)
.attr("height", cellSize - cellPadding);
node.selectAll("text").transition().duration(1000)
.attr("x", (cellSize - cellPadding) / 2)
.attr("y", (cellSize - cellPadding) / 2)
.style("font-size", 20);
//force.on("tick", tickMcCann).resume()
} else if(this.value === "demers"){
node.transition().duration(1000)
.attr("transform", function(d) {
return "translate(" + (d.x - d.r) + "," + (d.y - d.r) + ")";
})
.each("end", function(){
force.on("tick", tickDemers).resume();
});
node.selectAll("rect").transition().duration(1000)
.attr("rx", function(d){ return 0; })
.attr("width", function(d){ return d.r * 2; })
.attr("height", function(d){ return d.r * 2; })
node.selectAll("text").transition().duration(1000)
.attr("x", function (d) { return d.r; })
.attr("y", function (d) { return d.r; })
.style("font-size", function(d) { return (1.5 * d.r - 5); });
} else {
node.transition().duration(1000)
.attr("transform", function(d) {
return "translate(" + (d.x - d.r) + "," + (d.y - d.r) + ")";
})
.each("end", function(){
force.on("tick", tickDorling).resume();
});
node.selectAll("rect").transition().duration(1000)
.attr("rx", function(d){ return d.r; })
.attr("width", function(d){ return d.r * 2; })
.attr("height", function(d){ return d.r * 2; })
node.selectAll("text").transition().duration(1000)
.attr("x", function (d) { return d.r; })
.attr("y", function (d) { return d.r; })
.style("font-size", function(d) { return (1.5 * d.r - 5); });
};
}
);
d3.selectAll("input[type='checkbox']")
.on("change", function change() {
if(d3.select(this).property("checked") === true){
console.log("Show Map")
d3.selectAll(".states")
.classed("hidden", false)
} else {
console.log("Hide Map")
d3.selectAll(".states")
.classed("hidden", true)
};
}
);
};
</script>
{
"01":{"id":"01","code":"AL","shortname":"Ala.","longname":"Alabama","value":9},
"02":{"id":"02","code":"AK","shortname":"Alaska","longname":"Alaska","value":3},
"04":{"id":"04","code":"AZ","shortname":"Ariz.","longname":"Arizona","value":11},
"05":{"id":"05","code":"AR","shortname":"Ark.","longname":"Arkansas","value":6},
"06":{"id":"06","code":"CA","shortname":"Calif.","longname":"California","value":55},
"08":{"id":"08","code":"CO","shortname":"Colo.","longname":"Colorado","value":9},
"09":{"id":"09","code":"CT","shortname":"Conn.","longname":"Connecticut","value":7},
"10":{"id":"10","code":"DE","shortname":"Del.","longname":"Delaware","value":3},
"11":{"id":"11","code":"DC","shortname":"D.C.","longname":"District of Columbia","value":3},
"12":{"id":"12","code":"FL","shortname":"Fla.","longname":"Florida","value":29},
"13":{"id":"13","code":"GA","shortname":"Ga.","longname":"Georgia","value":16},
"15":{"id":"15","code":"HI","shortname":"Hawaii","longname":"Hawaii","value":4},
"16":{"id":"16","code":"ID","shortname":"Idaho","longname":"Idaho","value":4},
"17":{"id":"17","code":"IL","shortname":"Ill.","longname":"Illinois","value":20},
"18":{"id":"18","code":"IN","shortname":"Ind.","longname":"Indiana","value":11},
"19":{"id":"19","code":"IA","shortname":"Iowa","longname":"Iowa","value":6},
"20":{"id":"20","code":"KS","shortname":"Kan.","longname":"Kansas","value":6},
"21":{"id":"21","code":"KY","shortname":"Ky.","longname":"Kentucky","value":8},
"22":{"id":"22","code":"LA","shortname":"La.","longname":"Louisiana","value":8},
"23":{"id":"23","code":"ME","shortname":"Me.","longname":"Maine","value":4},
"24":{"id":"24","code":"MD","shortname":"Md.","longname":"Maryland","value":10},
"25":{"id":"25","code":"MA","shortname":"Mass.","longname":"Massachusetts","value":11},
"26":{"id":"26","code":"MI","shortname":"Mich.","longname":"Michigan","value":16},
"27":{"id":"27","code":"MN","shortname":"Minn.","longname":"Minnesota","value":10},
"28":{"id":"28","code":"MS","shortname":"Miss.","longname":"Mississippi","value":6},
"29":{"id":"29","code":"MO","shortname":"Mo.","longname":"Missouri","value":10},
"30":{"id":"30","code":"MT","shortname":"Mont.","longname":"Montana","value":3},
"31":{"id":"31","code":"NE","shortname":"Neb.","longname":"Nebraska","value":5},
"32":{"id":"32","code":"NV","shortname":"Nev.","longname":"Nevada","value":6},
"33":{"id":"33","code":"NH","shortname":"N.H.","longname":"New Hampshire","value":4},
"34":{"id":"34","code":"NJ","shortname":"N.J.","longname":"New Jersey","value":14},
"35":{"id":"35","code":"NM","shortname":"N.M.","longname":"New Mexico","value":5},
"36":{"id":"36","code":"NY","shortname":"N.Y.","longname":"New York","value":29},
"37":{"id":"37","code":"NC","shortname":"N.C.","longname":"North Carolina","value":15},
"38":{"id":"38","code":"ND","shortname":"N.D.","longname":"North Dakota","value":3},
"39":{"id":"39","code":"OH","shortname":"Ohio","longname":"Ohio","value":18},
"40":{"id":"40","code":"OK","shortname":"Okla.","longname":"Oklahoma","value":7},
"41":{"id":"41","code":"OR","shortname":"Ore.","longname":"Oregon","value":7},
"42":{"id":"42","code":"PA","shortname":"Pa.","longname":"Pennsylvania","value":20},
"44":{"id":"44","code":"RI","shortname":"R.I.","longname":"Rhode Island","value":4},
"45":{"id":"45","code":"SC","shortname":"S.C.","longname":"South Carolina","value":9},
"46":{"id":"46","code":"SD","shortname":"S.D.","longname":"South Dakota","value":3},
"47":{"id":"47","code":"TN","shortname":"Tenn.","longname":"Tennessee","value":11},
"48":{"id":"48","code":"TX","shortname":"Tex.","longname":"Texas","value":38},
"49":{"id":"49","code":"UT","shortname":"Utah","longname":"Utah","value":6},
"50":{"id":"50","code":"VT","shortname":"Vt.","longname":"Vermont","value":3},
"51":{"id":"51","code":"VA","shortname":"Va.","longname":"Virginia","value":13},
"53":{"id":"53","code":"WA","shortname":"Wash.","longname":"Washington","value":12},
"54":{"id":"54","code":"WV","shortname":"W.Va.","longname":"West Virginia","value":5},
"55":{"id":"55","code":"WI","shortname":"Wis.","longname":"Wisconsin","value":10},
"56":{"id":"56","code":"WY","shortname":"Wyo.","longname":"Wyoming","value":3}
}
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment