Berlin Election Map: Every. Single. Vote.

Federal Election 2021 (party list votes)

I love electoral maps.

But there is one thing that is often bothering me: Too much abstraction. I wanted to create a map that is as detailed and accurate as one could get.

Here is what I did:

I decided to show every single voter in Berlin from the recent federal election. Of course, we don't know everybody's exact location. But here is what we have:

  1. Results for every electoral district
  2. Geometries of these districts
  3. Data about population density in Berlin's neigbourhoods.

With 2 and 3 and some magic from the QGIS intersection function I created a new layer that includes both, the electoral and the population density data:

My idea was to generate random dots within the electoral districts according to the number of voters for each party in that district. But instead of just picking entirely random coordinates we can use the popuplation data and limit our algorithm to residential aereas so that we don't put anybody into the Mueggelsee or the Spree.

And we can also use the data to weight our algorithm: Densely resided panel buildings should get more points, of course, than some single-family houses in the neighbourhood.

How can we find random points in a certain area? With polygon triangulation! (Here is brilliant explanation that helped me a lot.) Your browser did that to generate the 1.8 million dots in the map above.

That's quite a lot of computing that can easily freeze your browser for a couple of seconds – if you don't outsource the task to a Web Worker that can solve it in the background while keeping your browser responsive.

The dots are then brought onto a Mapbox map with deck.gl – a visualization framework specialized on large datasets.

And last but not least: With a strict separation from the data, I poured the whole logic into a reusable React component.

jsx
<Map
title="Federal Election 2021 (party list votes)"
theme="dark"
polygonLayers={districtIntersections}
scatterLayers={parties}
data={votes}
matchKeys={{
station: ["BEZ", "UWB"],
postal: ["BEZ", "BWB"]
}}
weightKey="pd" // population density
chunkGroupKey="BEZ"
initialViewState={{
latitude: 52.518611,
longitude: 13.408333,
zoom: 11,
pitch: 50,
}}
/>

<<<