2014_03_11: it’s progress Jim, but not as we know it

So I spent most of yesterday trying to find what causes my work to crash Safari under iOS7: turns out it’s the gizmo that clusters the CC markers. Not going to cure that in a hurry!

Then I tried for several hours to add the info features from leaflet’s chloropleth/geoJSON example to my script. I got the LAs to highlight on mouseover, but not to de-highlight on mouseout. Nor could I get the data control to pick out data from the LA geoJSON file.

So I reasoned ‘if I can’t add their stuff to mine, can I add my stuff to theirs?’ That is, could I swap in my LA and CC data sets and use leaflet’s code to colour them and pick out data to be displayed in their external control? This seemed to go OK until I added in the geocoder (the bit for entering an address to zoom to that area of the map. This code failed, so everything that should have been processed after it wasn’t even reached.

I went back to trying to add leaflet’s functions to my otherwise functional code. Still no joy.

I refactored my code so it was in a more logical order:

  1. preparatory functions,
  2. drawing basic map
  3. adding scale, geocoder, reset and help controls to map
  4. adding LA data layer
  5. adding CC data layer
  6. adding layers on/off control

and made the reset control call a URL from a simple configuration file, so that when the client actually puts this work online, they only need to update the configuration file, not hack around in the reset script.

I still couldn’t get the mouseout bit to work. I knew this code was being called: if I replaced it with document.write(“rude word”); then rude words were written.

So this evening I revisited leaflet’s example, determined to get it to work. This example has all the functionality built into a script in the html file, not a separate ‘external’ script. Not really the way I want but I’m running out of time…

I realised that the geocoder was being called but just failing somewhere. I’m not sure how I worked it out but the fail point was that geocoder calls a function in my main external script to limit its searches to Scotland – the same bounds as are applied to my map. (Without this search-area limiting, searching for EH10 postcodes shows Walthamstow.) But this script is never invoked, so the function isn’t callable. So instead of calling that function, I’ve copied it into the geocoder. Now that works! And so do all the other bits. I can make the reset script call the configuration file, so long as they are in the right order in the html header (i.e. configuration before the reset function that depends on it – so perhaps the issue was that the geocoder was calling the main script before it was available.

There was another wrinkle adding in the CC marker code. Something doesn’t like a variable called location. Changing that to ccLocation worked.

So here is the whole lot working – but with the javascript embedded in the HTML.

My task for tomorrow is to get the javascript into a separate file, so that this works, cos right now it doesn’t. Then write some documentation, then write a talk for the OKFN meet-up tomorrow.

2014_03_04 progress re LA colours, calculating LA extrema and switching off unwanted polygons for markerClusterGroups

We’re into March! After meeting with the client, a few changes are needed.


Working from the chloropleth example, I’ve chosen 5 colours (cyan = #0080ff, red = #ff0000′, yellow = #ffff00, blue = #0000ff’ and green = #00ff00). This choice was inspired by Education Scotland’s map.)

In the geoJSON file, each feature (i.e. each LA) has an extra line in the ‘properties’ piece: “colour_code”:3 //BMR 2014_02_21.

The code to decide colours is
function getLAcolour(d) {
  return d == 1  ? ‘#0080ff’:
            d == 2  ? ‘#ff0000’:
           d == 3  ? ‘#ffff00’:
           d == 4  ? ‘#0000ff’:
} // end getLAcolour()

Then there’s a function to create a style object:
function laStyle(feature) {
    return {
        fillColor: getLAcolour(feature.properties.colour_code),
       weight: 2,
       opacity: 1,
       color: ‘white’,
       dashArray: ‘3’,
       fillOpacity: 0.7,
       fillOpacity: 0.4
} //end laStyle(feature)

Then the geoJSON function does the heavy lifting:
L.geoJson(laBoundaryData, {style: laStyle}).addTo(laLayerGroup);

So the whole effect looks like this:Screen Shot 2014-03-04 at 16.28.40It’s deliberately similar in look to this map of constituencies and MSP activities:Screen Shot 2014-03-04 at 16.29.32I’ve also moved the link to © data and acknowledgements to a second line in the acks piece at the bottom of the map, so far less screen area is taken up by non-map things. The instructions will go onto a pop-out pane access from a big ? on an edge of the map. Currently they clutter things:Screen Shot 2014-03-04 at 16.34.14

Calculating LA boundaries and extrema: how I did it

I do not claim this was the most efficient way! The following is an expanded write-up of a previous blog entry.

  1. Obtain up to date shape files from the Ordnance Survey (BoundaryLine is the relevant product.)
  2. Start a new project in QGIS.
  3. Do Layer > Add vector layer and select district_borough_unitary region.
  4. Make the layer editable.
  5. Select and cut out the non-Scottish features.
  6. Save the project as Scotland.
  7. Select each LA in turn, then do Vector > Geometry tools > Simplify geometries. The options I used were:Screen Shot 2014-03-04 at 17.07.58 but with a different file for each LA. For example, Shetland’s 160,883 vertices were reduced to 5416.
  8. Select the newly-created object, then do Layer > Save selection as vector file, with optionsScreen Shot 2014-03-04 at 17.09.30

So now I had 32 separate geoJSON files. To combine them, I used the process described here to make a single laBoundaryData.js file. That’s my geoJSON file on which the above styling magic works.

Finding LA extrema

In future versions of this map, I’ll want to zoom into individual LAs. The easy way to do this will be (I think) to find the furthest north, south, east, west points of each LA. So I adapted my code to find Scotland’s extrema to work on each LA:
function drawMap() {
    //BMR 2014_02_25
    //find extreme points
    var north = 0;
    var south = 90;
    var west = 90;
    var east = -90;
    for (var i = 0; i < coordinates.length; i++) {
        var laDatum = coordinates[i];
        // the other way round from the getScotlandBounds because geoJSON latlongs are in the opposite order to leaflet latlogns
        var longitude = laDatum[0];
        var latitude = laDatum[1];

        if (latitude > north) {
            north = latitude;
        //end if

        if (latitude < south) {
            south = latitude;
        //end if

        if (longitude < west) {
            west = longitude;
        //end if

        if (longitude > east) {
            east = longitude;
        //end if
    //end for

    document.write(“north = ” + north + “, south = ” + south + “, west = ” + west + “, east = ” + east);

}// end drawMap

It’s a complete hack but I copied, pasted and edited the results into my laData.js file to get line such as
    [“Angus”, “http://www.angus.gov.uk/commcouncil&#8221;, 
        56.986816427679120, 56.46164866362316, -3.407021822671358, -2.420365421269425],

If I’d been clever, I’d have made the script write the extrema data to the file. But this is a one-off and so it would have taken longer to write code to do it than to do it myself.

Switching off unwanted polygons for markerClusterGroups

Awe and respect to the leaflet programming folk. I feared I might need to dig into their actual code to switch off these unwanted polygons. But it’s as simple as adding an option object to the otherwise unoptioned code. That is, from this


to this

L.markerClusterGroup({showCoverageOnHover: false});

2014_02_21: even more LA boundary progress

So a good conversation with Napier’s visualisation expert and I’m now more aware of some of the ways to make my code more bombproof – too be implemented this weekend, if paper-writing and having a life allow.

Meanwhile I’ve completed colouring the LA boundaries. Here’s some pretty pictures:

Neither LAs nor CCs switched on

Neither LAs nor CCs switched on

CCs switched on

CCs switched on

LAs switched on

LAs switched on

both switched on

both switched on

I’m not too bothered that this uses 6 colours when there should be a four-colour solution. I’d be more picky about the actual colours used. Changing the colours is easy – just changing up to 6 values in a function in my main script. Changing which LAs each colour is applied to involves opening the huge geoJSON file full of LA data, then finding, say, East Ayrshire, then changing the value of colour_code immediately below it.

2014_02_21: more LA boundary progress

Manually copying and pasting 32 sets of bits of file is fraught with difficulty, as any fule kno.  So there had to be an easier way to assemble the individual LA geoJSON files into one javascript file. I did a lot of comparison of the brackets and guts of the working-so-far leaflet example and my geoJSON files. My file began with 

“type”: “FeatureCollection”,
“crs”: { “type”: “name”, “properties”: { “name”: “urn:ogc:def:crs:OGC:1.3:CRS84” } },

We only want one FeatureCollection, with 32 features for the 32 LA boundaries. So in a new laBoundaryData.js file, containing var laBoundaryData = { }; from the first LA file insert

      “type”: “FeatureCollection”,
      “crs”: {
            “type”: “name”,
            “properties”: {
                  “name”: “urn:ogc:def:crs:OGC:1.3:CRS84” } },
      “features”: [{
            “type”: “Feature”,
            “properties”: {
                  “NAME”: “Aberdeen City”,
                  “AREA_CODE”: “UTA”,
                  “DESCRIPTIO”: “Unitary Authority”,
                  “FILE_NAME”: “ABERDEEN_CITY”,
                  “NUMBER”: 7.0,
                  “NUMBER0”: 34.0,
                  “POLYGON_ID”: 122136.0,
                  “UNIT_ID”: 30421.0,
                  “CODE”: “S12000033”,
                  “HECTARES”: 20561.013,
                  “AREA”: 1990.394,
                  “TYPE_CODE”: “AA”,
                  “TYPE_COD0”: null,
                  “DESCRIPT1”: null,
                  “colour_code”:2         //BMR 2014_02_21
            “geometry”: {
                  “type”: “MultiPolygon”,
                                    “coordinates”: [ [ [ [ -2.360870189456887, 57.10919031061777  ….]]]] } },

Note the comma. After it, paste { “type”: “Feature”, “properties”: ….]]]] } } from the next LA file, then add a comma. Then rinse and repeat.

Far from elegant but much easier than isolating wee bits from each file.

2014_02_20 more LA boundary progress

I spent a while today trying to drop a marker at the user’s entered location. It’s easy to do on(zoomed) add marker but removing it at the next zoom is so far beyond me. Back to fighting with LA borders – the theatre of battle being styling them

I knew from leaflet’s example that leaflet does style geoJSON – so the issue must have been with my geoJSON files. When in doubt, copy: I copied leaflets’ code for colouring US state geoJSON data, substituted in the Scottish LA names and a piece to colour them by code rather than population density, and lo and behold it works.

Truth – it took a while to isolate the co-ordinate lines, so I’m only a wee way into substituting in the LA co-ordinates. But here’s the proof:

Screen Shot 2014-02-20 at 22.51.16

2014_02_19: LA boundary progress

With thanks to Leaflet.js forum members Matt and Simon.

I was encouraged to try again to obtain up-to-date LA boundary data from the OS. This time I obtained a set of shape files. The relevant shape file was district_borough_unitary_region.shx. I opened that in QGIS, removed the non-Scottish areas and saved the result as Scotland.qgs. I then selected and saved each LA in turn as a geoJSON vector file (with CRS = WGS84/EPSG4326 as before). So this got me up-to-date accurate LA boundaries. But these are quite large files: averaging 2MB each. (Of course Highland was much bigger than Clackmannashire, for example.)

To obtain simplified files, I selected each LA in turn, then did Vector > Geoprocessing tools > Simplify (options: ‘use only selected features’, tolerance = 50, ‘save to new file’, ‘add to canvas’) so that I had a patchwork of separate LAs:

simplified LAsI then selected each simplified LA in turn and saved them as geoJSON files. The resulting files are about a twentieth of the size of the unsimplified files. Yet zooming right in shows an acceptable (to me) fit to the unsimplified boundaries and coastlines. Yeehah.

We have layers

Screen Shot 2014-02-19 at 14.53.03And now the LA group is off by default, while the locater and layer controls are expanded by default:

Screen Shot 2014-02-19 at 15.32.29