This blog post provides some background to my attempt to create a simple 'constituency card' for every UK constituency in the run-up to an election that may or may not happen. This is at a time when, if I may be so bold, it can seem like everything is not as fine as it perhaps once was. I had the idea for this when I saw that there was a new set of official MP portraits that anyone could download and use but then other things got in the way. So, this is part open data experiment and part election prep, though mostly the former. Consider it my attempt to take lots of ugly data and turn it into useful information in an easy-to-digest way. See, I told you everything was fine.
Just want to see the maps? Okay, here you go. Want the underlying data files ? No problem.
|An example of one of the constituency cards|
|Things keep changing|
Here's an easy-to-remember short url you can use to go straight to the cards: bit.ly/constituencycards
What's the point of this?
I'm not a member of a political party and never have been so there is no underlying conspiracy here. Or at least, if there is, nobody told me about it. Aha, but why did I put the '% Swing required' figure on the individual constituency cards? I put it there because I think it's an important thing to know, for everyone with an interest in politics, regardless of whether you're the MP for Knowsley (George Howarth, Labour, majority: 42,214) or the MP for North East Fife (Stephen Gethins, SNP, majority: 2).
The point of this project (which is a spare time thing, not part of my day job) is to provide a single card for all 650 UK constituencies, which tells us who the MP is, what they look like, how they did at the last election or by-election, what it would take for the seat to change hands, plus a bit of other information.
On the last point, I calculated the straight-line distance from the centre of each constituency to the Palace of Westminster. The closest, unsurprisingly, is Cities of London and Westminster at 0.86 miles and the furthest is Orkney and Shetland at 581 miles.
Ideally, people will be able to click the link to the image files on their phone, tablet or computer and then flick between individual cards and make comparisons between places, find out more about individual constituencies and generally learn stuff.
How did I do this?
I compiled a list of official portraits using this blog post on the API, spent ages trying to figure it all out and then once I had a list of official photos, I added to it with other photos in the public domain because there isn't an official photo for every MP. Then I supplemented this data with information on current MPs from mySociety, then I put together a UK-wide geo file and chopped out the loughs of Northern Ireland so it looked right. I then added information on distance to Parliament and the size of each constituency, and mashed it all together. Then, in QGIS, I spent a while sorting the layout, editing and editing and tweaking and tweaking until arriving at the final result. The image below shows you what I ended up with.
|The labelling is always tricky|
Stuff wot I got wrong or not quite right enough
Sometimes the labelling isn't perfect. That is, sometimes places you might expect to be labelled are not, and some that you think should not be actually are. My labels file has 42,000 or so place names and I use a variety of rules and filters to decide what gets placed on each map but occasionally this doesn't work that well. I could devote tons of hours to it and make further improvements but I think I'm at the point where I'm happy enough with it.
Independent MPs. I'm sorry not to have given the different MPs, who are in different independent groupings, different colours but too many colours would not work well in my opinion. So this is not quite right but to be honest I found it hard to keep track of who is in what independent grouping and who is not.
Occasionally my swing figures are 0.1% out or so compared to some of the figures I've seen elsewhere online but in general they agree with the Target Seats lists on Election Polling.
Colours, maybe. I tried a few different versions without a white dim surrounding the featured constituency on each map but it became a riot of colour at times so I've gone with a 33% opacity white mask layer to dim it a bit. Sometimes this isn't perfect but I like it better than the alternatives, including a dark dim.
Style stuff, etc.
This can be controversial! But I'm quite chilled about it really. I tried my best to make them look good and also to build in some kind of logic and flow to the individual cards but of course they're never going to be perfect but I'm happy enough with them.
If you want to play around with the files you can find them in the files repo. You can style the files using the html colour codes in these - there is a colour for the winning party and one for the second placed party.
Decisions, etc. Well, the name of the constituency goes at the top left and the whole top row of each card is reserved for these names. I have sized everything so the longest constituency names (e.g. Cumbernauld, Kilsyth and Kirkintilloch East) don't run beyond the end of the map image below it. I have also placed the little black-dot-locator inset there as it is close to the name of the constituency and your eyes don't have to move much to locate it, and then you can scan down to the main map image.
I've given the sea a muted blue colour, which also applies to Northern Ireland loughs. I decided not to add lochs and lakes elsewhere as I couldn't decide where I would place the size cut off (e.g. include Loch Morar but not Loch Shin?). Too messy, but I made an exception for Northern Ireland because people are forever making maps without their loughs and they are really big and important.
I included a 1 mile scale bar not because I'm a devotee of the imperial system but because people know how far it is and for most constituencies the bar is big enough. The one mile scale bar is of course tiny on the Ross, Skye and Lochaber card and some of the other huge constituencies.
I included a bit of foreshore for Great Britain (didn't have the foreshore data for Northern Ireland) as I think this makes things look a bit better - usually - in coastal areas although of course it gives some places the appearance of having a lovely sandy beach when they actually don't. But I like to bequeath beaches to people who don't have them - in the spirit of mending the nation's divides.
You'll notice that in lots of maps you can see a nearby city. This is because I wanted people to be able to look at a map and say, for example, "Ah, so Aberavon is near Swansea" or "Oh, right, Meon Valley is kind of between Southampton and Chichester". I don't know about you but some of the constituency names I find quite baffling as they give little clue to the uninitiated where they actually are.
I also included a fairly dull building mask layer. The idea here is you can see the layout and form of the built environment and say things like "this is quite a densely built up area" or "this constituency is quite sparsely populated". You may know this already but I think it adds an extra dimension of knowledge for places I'm unfamiliar with so I like it. Just hard to get the balance right so it doesn't mess too much with the colours.
I decided it would be good to add a party-coloured frame round each photo in lieu of a legend and then the MP's majority below that in large bold text. The MP name and party go in the coloured box below the image. But then I thought it would be useful to show who came second and what kind of swing would be needed for the second place party to win. In some cases they are not likely to win, to say the least, but in a good few things are very close. I make it 171 constituencies (out of 650, so 26% of the total) where the required swing is less than 5%.
The descriptive text below each map has some other information I found useful and I thought I'd add some geographical context with distance from parliament and also area in square miles - these figures make more sense when you compare them with others I suppose. I calculated these in QGIS.
The font is Montserrat.
Not too much else to say here but I did use Excel for some vlookup type stuff but basically everything was done in QGIS using the Atlas tool and a range of quite messy looking functions. For example, where the required swing was below 0.05% I had to make sure two decimal places were displayed instead of the default one I used otherwise it would say 0.0% there. For that, I used this:
format_number( "REQSWING" , if( "REQSWING"< 0.06, 2,1))||'%'
|Making sure there were no 0.0% swing figures was fiddly|
I used a rule-based symbology for the place name labels - using this rule to only show places in the current Atlas feature:
intersects( @atlas_geometry ,$geometry)
And then to make sure cities outside the current Atlas feature were always showing I used this on a copy of the same layer, filtered to only show cities:
NOT intersects( @atlas_geometry ,$geometry)
But how on earth do you get data defined text colours? Well, I couldn't figure out a way to make this happen in QGIS Print Layout as it's not a default option on font colour so I figured out a workaround.
What I did was add the swing figure to the layout using the "REQSWING" variable in the layer driving the Atlas. I added this as black text. Then I added a white text box over the top of it. Then what I did was set the colour of this text box overlay to match the colour of the party that came second. This was easy because I already created a field called "secondcol" with the html colour code of the party that came second. Then I changed the Rendering blend mode to Screen so that the black text changes to whatever the colour of the second placed party is. Sounds complex but works perfectly - see below for screenshot.
|A workaround that works - I couldn't figure out another way|
You may also notice that in the five constituencies that have had a by-election since 2017 there is a little '(By-election)' indicator below each Majority figure. Since I created a field in my data table with data on when the last election was (i.e. either 2017, 2018 or 2019) I was able to add a text box with '(By-election)' in it and then set it to be 100% transparent if the value was 2017. Otherwise it is displayed. This is the text I used in the data defined over-ride box on Rendering for that.
WHEN "last_vote" = 2017 THEN 0
|Again, a bit fiddly but effective|
That's about it really. There's nothing super-fancy going on behind the scenes and of course I still haven't really figured out how to make friends with date and time formats. Drives me mad, but I got there in the end. See below for the mess that makes up the data-driven text at the bottom of each card (using the 'Render as HTML' tick box in QGIS).
- '<b>' || replace( "cname1" ,'Kingston upon H','H') || '</b> had an electorate of ' || format_number("elect17" ,0) || ' and a population of ' || format_number("pop17",0) || ' in 2017. The distance from the centre of the constituency to Parliament is ' || format_number( "mi_fr_pow" ,0) || ' miles, and the constituency covers an area of ' || format_number("sq_mi" ,0) || ' square miles. At the 2017 general election, this constituency was number ' || "dec_order" || ' out of 650 to declare the result, at ' || format_date( "just_time" ,'hh:mm') || ' on ' || format_date( "dec_time" ,'dddd d MMMM') || '.'
Whose shoulders am I standing on here?
Ordnance Survey, mySociety, the amazing team at the House of Commons Library, Philip Brown, Elvis Nyanzu, Alex Parsons, the Parliamentary Digital Service, the very nice voters of the UK, and of course the team who make QGIS.
Without the hard work, expertise, knowledge and experience of a wide variety of people and organisations it is impossible to do stuff like this. I am simply trying to bring data together and make it into useful information. Trying to contribute to the open data ecosystem I suppose.
Who will win the next General Election?