We’re still working on looking at empty properties around Wales, and so while we wait for the FOI request results to come in, I thought it would be interesting to do a bit of basic mapping. Normally, if I want to create a choropleth I reach straight for d3 and my collection of topojson, but we’re still very early in the course, and we haven’t covered d3 yet (we go into it in some detail in next semester’s visualisation course). As we haven’t covered d3 yet, we need a simple solution, and fortunately the leaflet API makes it very easy to draw polygons on top of a map; all we need to know are the coordinates of the shape that we want to draw.
So, first we need to grab boundary files for the parishes around Wales. A quick hunt through the bafflingly obtuse ONS geoportal brings us to the generalised parish boundaries (E+W). Although it doesn’t seem immediately obvious from that page, there is a download link there that allows us to obtain shapefiles containing the boundary data for every parish in England and Wales. Unfortunately, these files are in a rather complicated shapefile format, when all we really need is a list of coordinates that we can throw into some JavaScript. We could extract and transform this data using command line tools, but as this is an early demo, we’ll use some graphical tools to do the work. So, first of all we open up the shapefile in our favourite GIS software:
This is all the parishes for England and Wales, and we only want the boundaries for Wales, so the next thing we’ll do is extract those. Looking at the attribute table, we see that each parish has a code connecting it to it’s Local Authority District (the LAD14CD). Using a simple filter on the ‘LAD14CD’, we can extract all those parishes that are in a local authority district in Wales, by selecting only those LAD14CDs that begin with a ‘W’:
This gives us our Welsh parishes:
Now we can save this selection as geoJSON, which is a nicer format to work with than ESRI shapefiles, and will easily be handled by Leaflet. While we’re at it, we can convert the coordinates of the boundary data to WGS84 (which essentially gives us Lat,Lng coordinates we can use with our map):
For this example (because we’ve only had a response from Cardiff Council so far), we only need to deal with the Cardiff parishes, so for simplicity we’ll extract the Cardiff parishes from our large geoJSON file into a smaller Cardiff specific file. A quick bit of Python looking for all the parishes with a LAD14CD of ‘W06000015’ is all that’s needed here:
import json parishes = json.load(open('Wales_Parish.geojson', 'r')) cardiff_parishes = {'type': parishes['type'], 'crs': parishes['crs'], 'features': []} for feature in parishes['features']: if feature['properties']['LAD14CD'] == 'W06000015': cardiff_parishes['features'].append(feature) json.dump(cardiff_parishes, open('Cardiff_Parish.geojson', 'w'))
This geojson is all we need to display the parish boundaries on our map. In fact, if we edit the geojson file to include
var parishes = {ALL_OUR_GEOJSON_DATA}
We can import this directly into a webpage and load it into a map with leaflet relatively easily using the geoJson function in leaflet:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>Empty Properties</title> <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" /> <style> html, body, #map { height: 100%; width: 100%; } </style> </head> <body> <div id="map"></div> <script src="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script> <script src="cardiff_parish.js"></script> <script> // create map and centre on Cardiff var map = L.map('map').setView([51.455, -3.19], 12); L.geoJson(parishes).addTo(map); // add some mapbox tiles var tileLayer = L.tileLayer('http://{s}.tiles.mapbox.com/v3/' + 'YOUR_MAPBOX_API_KEY' + '/{z}/{x}/{y}.png', { attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://mapbox.com">Mapbox</a>', maxZoom: 18 }).addTo(map); </script> </body> </html>
This gives us a nice map of Cardiff with the parish boundaries:
All we need to do now is alter the colour of our parishes based on the number of empty properties within that parish. So, we go back to the data we extracted preciously, which gave us the total number of empty properties in each parish. We can go back to our code that extracts the Cardiff parishes from the large geojson file, and this time whenever we extract a Cardiff parish, we add a property to the geoJson feature with its value from the empty properties data. We also add min and max values across the whole set of parishes:
import json import pandas parishes = json.load(open('Wales_Parish.geojson', 'r')) cardiff_parishes = {'type': parishes['type'], 'crs': parishes['crs'], 'features': [], 'properties':{}} parish_totals = pandas.read_csv('parish_totals.csv', index_col=0) cardiff_parishes['properties']['min'] = parish_totals['value'].min() cardiff_parishes['properties']['max'] = parish_totals['value'].max() for feature in parishes['features']: if feature['properties']['LAD14CD'] == 'W06000015': parish_name = feature['properties']['PARNCP14NM'].strip().upper() feature['properties']['empty_total'] = parish_totals.loc[parish_name]['value'] cardiff_parishes['features'].append(feature) json.dump(cardiff_parishes, open('Cardiff_Parish.geojson', 'w'))
Then, we set up a colour scale in our JavaScript code for creating the map (based off a single-hue colorbrewer scale), and style each shape according to its value by adding a style function that gets called by Leaflet when it is drawing each geoJson feature:
<script> // create map and centre on Cardiff var map = L.map('map').setView([51.455, -3.19], 12); var divisor = parishes.properties.max / 9; var colour_scale = ["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"]; L.geoJson(parishes, { style: function(feature){ var colour = colour_scale[Math.round((feature.properties.empty_total/divisor)-1)]; return {color: colour, fillOpacity: 0.4, weight: 2} } }).addTo(map); // add some mapbox tiles var tileLayer = L.tileLayer('http://{s}.tiles.mapbox.com/v3/' + 'YOUR_MAPBOX_API_KEY' + '/{z}/{x}/{y}.png', { attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://mapbox.com">Mapbox</a>', maxZoom: 18 }).addTo(map); </script>
And with a refresh of our map page, there we have a choropleth of the parishes in Cardiff, coloured by the number of empty properties:
This is a nice quick example that has allowed us to begin thinking about mapping data, and some of the issues surrounding such mappings, before we begin to study them it in detail next semester. As more of the data is returned from our FOI requests, we can start expanding this visualisation across Wales.