Political landscape I

About 47.6 million people were registered as entitled to vote in the United Kingdom’s general election on 12 December 2019 and about 32.1 million did so. I wanted to use Haskell to view the outcome.

Results

The House of Commons Library publishes the results of the election as two comma-separated values (CSV) files, the smaller by constituency (including the identity of the winning candidate) and the larger by all candidates.

I needed only the smaller file, which provided for each constituency the number of votes for each of the 12 largest political parties (grouping other votes under ‘Other’), the number of invalid votes and the size of the electorate.

Four of the 3,320 candidates did not identify as male or female, although none of them were elected as a member of parliament (MP):

I used the type Party to represent the party affiliations of the candidates, with the constructors defined in the same order as the parties were listed in the CSV:

The result for each constituency could be a hold for a party or a gain from another party:

I used an array indexed by Party to represent valid votes by party.

I used the type ConstituencyResult to represent the results of constituencies:

I used a (strict) hash map to represent the results of the election:

The package cassava provides a Haskell library to parse CSV files. The type of each field needs to be an instance of FromField and the type of each record needs to be an instance of FromRecord. For example, to parse data such as “Con hold” or “Con gain from Lab Coop”:

or to parse data such as “2019-12-13 02:30:00”:

The hash map is made from the Vector of records. The records must capture the constituency (the hash map key) and its result. This uses the RecordWildCards language extension:

Cartogram

UK parliamentary constituencies vary considerably by area or size of electorate. The outcome of the general election can be viewed using a cartogram where each constituency is represented by a regular hexagon of the same size – a ‘hex map’.

Versions of such hex maps are published by ODI Leeds and Ben Flanagan of ERSI. I used the latter.

The ERSI hex map is provided as an ERSI Shapefile. That specification is overwrought for a hex map. ODI Leeds defines a simple JSON (JavaScript Object Notation) format for hex maps – HexJSON.

I used Matthew Bloch’s mapshaper tool to view the ERSI Shapefile and export as a file in the GeoJSON format. With the assistance of the geojson package (GeoJSON) and the aeson package (JSON), I wrote a utility to read the contents of the GeoJSON file, transform the features to a HexJSON format and write the transformed data to a JSON file.

The transformation calculated the centre of each feature (specifically, the average of the vertices) and, allowing for rounding differences, the minimum horizontal and vertical distances between centres. This allowed the centres to be mapped to a hexagonal grid.

Diagrams

I used the diagrams project to render the hex maps as a Scalable Vector Graphics (SVG) image.

The function Diagrams.TwoD.Text.text returns a primitive text diagram but that diagram takes up no space. I needed to know the size of text in legends, so I used the SVGFonts package and the function textSVG, which returns a Path.

The key functions that produce a hex map are styleHexMap and styleHexMap', where the former is the latter specialised to Constituency:

An example of a function that yields Constituency -> HexStyle is:

Winners hexmap