share      tweet      post

Bristol have entered a period of consultancy with a proposal that aims to close 7 of the 28 libraries in the authority. Whilst any actions appear to be temporarily postponed: (plans to close seven Bristol libraries on hold), there are no guarantees those libraries will stay open.

Along with survey data from a 'Bristol library futures' public consultation, the proposal draws from a number of data sources, including some geographic (GIS) data.

GIS data

In the proposals, locations of libraries and the Bristol authority boundary lines are used to visualise library coverage. The libraries are plotted with circular radius of 1.5 miles, to show the plans still give majority coverage across the authority. Using a radius as a measure comes with various issues:

The 1.5 mile figure is given as one that is better than a 'recommended minimum' of 2 miles:

Good geographical access across the city with all residents being within 1.5 miles* of a library.
*2 mile access was the recommended minimum distance advised by the Secretary of State response to Bolton MBC following a local inquiry – CMS 231060/DC 31 May 2013 Bristol Libraries for the Future proposals for consultation.

It's hard to track down this 'recommended minimum' (or should that be maximum?). A letter from Vaizey to Bolton Council, with the same reference number, reveals a reference to 2 miles but within the context of Bolton:

96% of the population will be within 2 miles of a library, and that almost the entire borough is within 20 minutes journey of a library by public transport, plus walking time to the nearest bus stop. Car ownership is also high in Bolton and other transport services are provided for the elderly and disabled. The Secretary of State views this as sufficient... Ed Vaizey to Councillor Morris

In this case the distance was assessed with the following qualifying factors, specific to the local area:

Visualising the map data

The basic map within the PDF provided by Bristol council provides a visualisation of coverage, but doesn't allow residents to see (for example) what a 1 mile radius for each library would look like, or explore in detail which areas get the highest coverage. Or to see the effect on coverage that closing 7 of the libraries would have.

To independently visualise these variations, a couple of datasets need to be used:

With those two layers, the data can be plotted on a web map, with some fairly short HTML and JavaScript. A couple of HTML buttons and functions provide the means to change the radius, based on pre-defined values (in this case 1 mile, 1.5 miles, and 2 miles).

The associated map is shown below, with the code sample underneath.


Plugins used

The HTML below uses a number of JavaScript files. These are:

HTML code

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
    <link href='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-label/v0.2.1/leaflet.label.css' rel='stylesheet' />
    <style>
        #map { width: 100%; height: 500px; border: 1px solid #ccc; }
        .customLabel {color: #ccc !important; border: none; background: none; font-size: 7px; }
    </style>
</head>
<body>
    <div class="container">
        <button onclick="showProposedLibraries()">show proposed libraries</button>
        <button onclick="showAllLibraries()">add closures</button> | 
        <button onclick="changeRadius(1609)">1 mile radius</button>
        <button onclick="changeRadius(2413.5)">1.5 mile radius</button>
        <button onclick="changeRadius(3218)">2 mile radius</button>
        <div id="map"></div>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
    <script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
    <script src='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-label/v0.2.1/leaflet.label.js'></script>
    <script src="mapfunctions.js"></script>
    <script src="bristolBoundary.js"></script>
    <script src="bristolLibraries.js"></script>
    <script src="proj4.js"></script>
</body>
</html>

Map functions

var closingCircles, libraryCircles, map, closingDisplayed = false;

$(document).ready(function () {
    // set up the leaflet map.  initial view of Bristol centre.
    map = L.map('map').setView([51.45, -2.60], 12);
    L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}' + (L.Browser.retina ? '@2x' : '') + '.png', {
        attribution: '© OSM contributors, CartoDB'
    }).addTo(map);

    // add the boundary layer for Bristol
    L.geoJson(bristolBoundary, {
        weight: 2, fill: true, fillOpacity: 0.7, fillColor: '#FFF', color: '#999999', clickable: false,
        pointToLayer: function (feature, latlng) {
            return L.circleMarker(latlng, { opacity: 0, fillOpacity: 0, clickable: false });
        }
    }).addTo(map);
    // By default use 1609 metres
    addLayers(1609);
});

function showProposedLibraries() {
    // remove the libraries suggested for closure
    if (closingDisplayed) map.removeLayer(closingCircles);
    closingDisplayed = false;
}

function showAllLibraries() {
    // add the libraries suggested for closure
    if (!closingDisplayed) map.addLayer(closingCircles);
    closingDisplayed = true;
}

function changeRadius(rad) {
    // remove everything and add again with specified radius
    map.removeLayer(libraryCircles);
    if (closingDisplayed) map.removeLayer(closingCircles);
    addLayers(rad);
}

function addLayers(radius) {

    // adds all the layers from the bristolLibraries data included on the page.
    closingCircles = L.layerGroup();
    libraryCircles = L.layerGroup();

    $.each(bristolLibraries, function () {
        var latlng = NEToLL(this.attributes.X, this.attributes.Y);
        var displayRadiusOptions = { clickable: false, weight: 1, stroke: false, fillColor: '#6699FF' };
        var displayMarkerOptions = { weight: 1, fillColor: '#6699FF' };
        var popupText = '<h4>' + this.value + '</h4>' + this.attributes.Street + ', ' + this.attributes.Locality + ', ' + this.attributes.Town + ', ' + this.attributes.Postcode;
        if (this.closing) {
            closingCircles.addLayer(L.circle([latlng.latitude, latlng.longitude], radius, displayRadiusOptions));
            closingCircles.addLayer(L.circleMarker([latlng.latitude, latlng.longitude], displayMarkerOptions).setRadius(5).bindPopup(popupText));
        }
        else {
            libraryCircles.addLayer(L.circle([latlng.latitude, latlng.longitude], radius, displayRadiusOptions));
            libraryCircles.addLayer(L.circleMarker([latlng.latitude, latlng.longitude], displayMarkerOptions).setRadius(5).bindPopup(popupText));
        }
    });

    map.addLayer(libraryCircles);
    if (closingDisplayed) map.addLayer(closingCircles);
}

// function to convert between british national grid and lat/lng
// uses proj4js plugin
function NEToLL(east, north) {
    proj4.defs("NationalGrid", "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs");
    var res = proj4('NationalGrid', 'WGS84', [east, north]);
    return { latitude: res[1], longitude: res[0] };
}

comments powered by Disqus