Results of the parliamentary elections (for the Bundestag) 2013 in Leipzig.
Source: Stadt Leipzig
license: gpl-3.0 | |
height: 640 | |
border: no |
Results of the parliamentary elections (for the Bundestag) 2013 in Leipzig.
Source: Stadt Leipzig
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
.land { | |
fill: #ddd; | |
} | |
.border { | |
fill: none; | |
stroke-linejoin: round; | |
stroke-linecap: round; | |
} | |
.ortsteil { | |
stroke: #666; | |
stroke-width: 0.5px; | |
stroke-dasharray: 1 3; | |
} | |
.bezirk { | |
stroke: #fff; | |
stroke-width: 2px; | |
} | |
.wk { | |
stroke: #999; | |
stroke-width: 2px; | |
} | |
.bubble { | |
fill: brown; | |
opacity: .8; | |
stroke: #333; | |
stroke-width: .5px; | |
} | |
.bubble :hover { | |
stroke: #aaa; | |
} | |
.legend circle { | |
fill: none; | |
stroke: #ccc; | |
} | |
.legend text { | |
fill: #777; | |
font: 10px sans-serif; | |
text-anchor: middle; | |
} | |
</style> | |
<body> | |
<div id="option"> | |
<select onchange="update(this.value)"> | |
<option value="CDU" selected="selected">CDU</option> | |
<option value="SPD">SPD</option> | |
<option value="LIN">Die Linke</option> | |
<option value="GRU">Gruene</option> | |
<option value="AFD">AfD</option> | |
<option value="FDP">FDP</option> | |
<option value="NPD">NPD</option> | |
</select> | |
</div> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<script src="//d3js.org/topojson.v2.min.js"></script> | |
<script> | |
var radius = d3.scale.sqrt() | |
.domain([0, 25000]) | |
.range([0, 28]); | |
var color = d3.scale.linear() | |
.domain([0, 40]) | |
.range(["#fff", "#00ff00"]); | |
var colors = {CDU: "#333", SPD: "#FF0000", LIN: "#E3000F", GRU: '#64A12D', AFD: '#009ee0', FDP: '#ffed00', NPD: '#8b4513'}; | |
var maximum = {CDU: 40.66, SPD: 26.17, LIN: 30.43, GRU: 27.06, AFD: 13.17, FDP: 6.16, NPD: 6.22}; | |
var formatNumber = d3.format(",.0f"); | |
var width = 600, | |
height = 600, | |
padding = 0; | |
var path = d3.geo.path() | |
.projection(null); | |
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); | |
var legend = svg.append("g") | |
.attr("class", "legend") | |
.attr("transform", "translate(" + (width - 50) + "," + (height - 20) + ")") | |
.selectAll("g") | |
.data([25000, 10000, 3000]) | |
.enter().append("g"); | |
legend.append("circle") | |
.attr("cy", function(d) { return -radius(d); }) | |
.attr("r", radius); | |
legend.append("text") | |
.attr("y", function(d) { return -2 * radius(d); }) | |
.attr("dy", "1.2em") | |
.text(d3.format("2s")); | |
d3.json("ortsteile.json", function(error, le) { | |
if (error) | |
return console.error(error); | |
var ot = topojson.feature(le, le.objects.ot).features; | |
var nodes = ot.map (function(d) { | |
var point = path.centroid(d); | |
return { | |
x: point[0], y: point[1], | |
x0: point[0], y0: point[1], | |
r: radius(d.properties.POP), | |
n: d.properties.NAME, | |
p: { | |
LIN: d.properties.LIN, | |
SPD: d.properties.SPD, | |
CDU: d.properties.CDU, | |
GRU: d.properties.GRU, | |
FDP: d.properties.FDP, | |
AFD: d.properties.AFD, | |
NPD: d.properties.NPD | |
} | |
} | |
}); | |
svg.append("g") | |
.attr("class", "ot") | |
.selectAll(".ortsteile") | |
.data(ot) | |
.enter().append("path") | |
.attr("d", path) | |
.attr("fill", "#f6f4f2"); | |
svg.append("path") | |
.datum(topojson.mesh(le, le.objects.ot, function(a, b) { return a !== b; })) | |
.attr("class", "border ortsteil") | |
.attr("d", path); | |
svg.append("path") | |
.datum(topojson.mesh(le, le.objects.bz, function(a, b) { return a !== b; })) | |
.attr("class", "border bezirk") | |
.attr("d", path); | |
svg.append("path") | |
.datum(topojson.mesh(le, le.objects.wk, function(a, b) { return a !== b; })) | |
.attr("class", "border wk") | |
.attr("d", path); | |
force | |
.nodes(nodes) | |
.on("tick", tick) | |
.start() | |
var bubbles = svg.append("g") | |
.attr("class", "bubble"); | |
var node = bubbles.selectAll("circle") | |
.data(nodes) | |
.enter().append("circle") | |
.attr("r", function(d) { return d.r; }); | |
node.append("title") | |
.text(function(d) { return d.n + "\nCDU: " + d.p.CDU; }); | |
update('CDU'); | |
function tick(e) { | |
node.each(gravity(e.alpha * .1)) | |
.each(collide(.5)) | |
.attr("cx", function(d) { return d.x; }) | |
.attr("cy", function(d) { return d.y; }); | |
} | |
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, | |
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; | |
}); | |
}; | |
} | |
}); | |
var setColor = function (partei) { | |
color | |
.domain([0, maximum[partei]]) | |
.range(["#fff", colors[partei]]); | |
} | |
var update = function (partei) { | |
setColor(partei); | |
// achte auf korrekte Auswahl! | |
svg.select(".bubble") | |
.selectAll("circle") | |
.transition() | |
.duration(1000) | |
.attr("fill", function (d) { return color(d.p[partei]); }) | |
.selectAll("title") | |
.text (function(d){ return d.n + " - " + d.p[partei] + "%"; }); | |
} | |
</script> |