Generate a map of the UK showing the general election voting distribution of 2005, 2010 and 2015. Existing election result maps did not really show the subtlety in voting, this is due to the first-past-the-post voting system (winner-takes-all).
Gather election data (number of votes per constituency per party & turnout percentage)
Produce textures
– One per constituency
– Number of pixels per party (pixel colour) proportional to vote count
Find a programmatically usable map of the UK and its constituencies
Link constituencies in the data to the constituencies on the map
Alter map to apply this custom texture per constituency
Java Program
[pullquote]I spent a good while trying to ‘debug’ missing data for one constituency in Northern Ireland, which turned out to be a lake (Lough Neagh)[/pullquote]
The program does the following (see below for the source);
Read in a map of the UK in SVG format
For each election year
Read in election data
Look up party colours (no vote = BLACK)
For each constituency
Calculate a voting distribution
Generate a ‘voting distribution’ image
Link the constituencies in the image map with the generated images
I used these Creative Commons images from Wikipedia as a base. The image format is SVG, which is an XML formatted vector graphics image. This allows for programatic access to the image description data.
2005 UK Election Map – Results by Constituency2015 UK Election Map – Results by Constituency
Read in and Writing to a Map of the UK in SVG
As the SVG image format is XML, we can use the standard XML tools in Java, in this case DOM + XPath.
XPath xpath = XPathFactory.newInstance().newXPath();
// Iterate over all constituency SVG paths using XPath to select the DOM nodes
NodeList pathNodes = ((NodeList) xpath.compile("//path")
.evaluate(doc, XPathConstants.NODESET));
// Regular DOM node
Node firstPathNode = pathNodes.item(0);
Adding an XML Namespace
/**
* Add XLink namespace to XML document
* xmlns:xlink="http://www.w3.org/1999/xlink"
*/
private static void addXlinkNamespace(Document doc) {
Element root = doc.getDocumentElement();
Attr attr = doc.createAttribute("xmlns:xlink");
attr.setValue("http://www.w3.org/1999/xlink");
root.setAttributeNodeNS(attr);
}
Processing Excel Spreadsheet
Add Apache POI – the Java API for Microsoft Documents – to the Maven pom.xml:
A number of constituency changes were made before 2010 – meaning that not all 2005 data could be applied to the 2015 UK constituency map. The 2005 UK constituency map did not have the required meta data to link the data to the SVG image, so I included a lookup file using the table data on the 2005 UK constituency map page.
Textures
Pixels represent the party colour and appear in frequency proportional to the votes.
// http://en.wikipedia.org/wiki/Wikipedia:Index_of_United_Kingdom_political_parties_meta_attributes
{
'CON': '#0087DC',
'LAB': '#DC241f',
'SNP': '#FFFF00',
'LD': '#FDBB30',
'GREEN': '#6AB023',
'UKIP': '#70147A',
'PC': '#008142', // Plaid Cymru
'SF': '#008800', // Sinn Fein
'DUP': '#D46A4C', // Democratic Unionist Party
'UUP': '#9999FF', // Ulster Unionist Party
'SDLP': '#99FF66', // Social Democratic and Labour Party
'TUSC': '#EC008C', // Trade Unionist and Socialist Coalition
'BNP': '#00008B', // British National Party
'CPA': '#AA00AA', // Christian Peoples Alliance
'CSA': '#F84651', // Cannabis is Safer than Alcohol
'Eng Dem': '#915F6D', // English Democrats
'IND': '#DDDDDD'
}
Load String to String Map from a JSON file:
public static Map<String, String> loadStringMapFromJsonFile(String filePath) throws IOException {
Gson gson = new GsonBuilder().create();
Type type = new TypeToken<Map<String, String>>(){}.getType();
try(Reader reader = new FileReader(filePath)){
return gson.fromJson(reader, type);
}
}
I also used it to parse the data from the Guardian website. You create a plain Java object (POJO) with a default constructor, that represents the data. In this case I had to specify a date format, as the source JSON used a format different from the default.
public static <T> T fromJson(Reader reader, Class<T> classOfT, String dateFormat) throws IOException {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setDateFormat(dateFormat);
Gson gson = gsonBuilder.create();
T jsonDataObject;
try{
jsonDataObject = gson.fromJson(reader, classOfT);
}finally{
if(reader != null)
reader.close();
}
return jsonDataObject;
}
If a JSON field name starts with a number (invalid for Java fields), you can add an annotation to link the fields, like: