Friday 20 May 2011

Leaflet

Edit: The code below is for a very early version of Leaflet and is not the best way to use Leaflet now. Much has been improved and extended. The range of plugins has grown enormously too.  Take a look at the Leaflet site for more information.

Vladimir Agafonkin announced that CloudMade have released Leaflet, a new open source JavaScript library for displaying map tiles as a slippy map. I've used OpenLayers up to now, so I thought I'd take a look at it.

I have a love-hate relationship with JavaScript. It is great that there is a powerful language that runs in browsers, but the environment it runs in is horrible: DOM, various browsers supporting various features, a fiddly run-time that makes debugging horrible etc. Firebug helps.

I decided that a reasonable test was to reproduce a slippy map page I already have to see how it works with Leaflet. That would help me compare with OpenLayers, but does run the risk of me using Leaflet in an OpenLayers way, which I hope I've avoided. It also only covers a small part of what Leaflet offers, but I seems like a start.

My page needs to display a slippy map with markers on it. A side bar will display details and photo for any marker that is clicked and a small message bar will explain the current situation. To get the layout I used a very simple HTML page and a simple style sheet. I tend to not embed styles in HTML or JavaScript, but to use either IDs or Classes to apply styles and declare these in separate style sheets, sometime more than one. I rarely embed JavaScript in HTML either, other than calling functions in events, so JavaScript goes into separate files too. All of these files are available to download, see the links at the end. The Leaflet JavaScript and style sheet are also included. The map is intended to show the locations and information about a series of sculptures around the City of Hull, as part of the Philip Larkin celebrations. They are no longer in place, but I had the data so I used it.

The first part seemed very easy: display a slippy map. The map is displayed in an HTML div. Ours has an id of map. The code to make the map appear is:

var map;
var hull = new L.LatLng(53.775, -0.356);
function initmap() {
map = new L.Map('map');
var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
var osmAttrib='Map data © openstreetmap contributors';
var osm = new L.TileLayer(osmUrl,{minZoom:8,maxZoom:18,attribution:osmAttrib});

map.setView(hull,11);
map.addLayer(osm);
}

That is a fairly simple piece of code to display a slippy map. The variables map and hull were defined outside of the function so they are available to other functions as global variables. The single most confusing part is probably the line

map = new L.Map('map')

The first part is a map variable used throughout the code. The second (L.Map) is the leaflet-defined object for a map. This defines how the map object behaves. The last part ('map') is because the div that displays the map has an id of map, which is in the HTML code. The URL includes {s} for the server prefix needed, {z}, {x} & {y} for the zoom, x and y tile coordinate. This allows various tile providers' tile structure to be used.  Now the map needs markers.

Adding markers with Leaflet is very straightforward when you have the longitude and latitude for each marker. However, displaying a map with hundreds or even thousands of markers slows things down a lot, so I like to only add the markers that are visible in the currently displayed part of the map. You can get this by calling the getBounds() method of the map object. This returns the north and south latitude and the east and west longitude that describe the edges of the displayed map. Of course this can change as the slippy map is dragged to a new position or the map is zoomed in or out. So the way to handle this change is to respond to map events. The one which seems to fit the bill for us is moveend. This is called whenever the map is moved, including zooming. We supply a function that is called whenever the moveend event is fired. This is achieved by adding the line

map.on('moveend', onMapMove);

Now, whenever the the map is moved our function onMapMove will be called. In our example onMapMove simply calls the function askForToads() which gets the location of the markers we want. I use this extra layer of functions to make debugging easier.

We still don't know where the markers are to be placed, I do that by using AJAX to request the information about all the markers that fall within the bounds of the current map display. askForToads calls the getBounds() method of our map object, extracts the north, south, east and west coordinates and wraps it up into a call to a PHP routine that finds the list of markers and returns this in a JSON format. This returned list is processed by the stateChanged() function which is called as part of the AJAX process. The JSON provides a neat way to return this data, because simply calling eval on the returned string turns it into an array of, in our case, marker data. I then remove any markers already shown and recreate the new markers one at a time, attaching the data for that marker to a data field and recording each marker in an array (toadlayers.push). Working through the array allows me to delete the markers when the map changes.

You now need to respond to each marker that is clicked. This means that the click event needs to be responded to. This needs the line

toadmark.on('click',markerClick);

to ensure that any marker that gets clicked is responded to by the markerClick() function. Here the details from the data field are displayed in the side bar.

Throughout the process the message div displays messages about the progress of fetching the marker info and how many toad markers are being displayed.

This may seem a lot of work to display the markers, but we are making two substantial changes to the normal system of displaying markers, firstly we are only displaying some markers. If there were thousands of potential markers spread across a wide area this would make the process manageable by only displaying a few at a time. Secondly instead of just displaying a popup we are making something outside of the map div change, which opens up a wide range of actions.

How does this compare with OpenLayers? Most of it is simpler. OL provides an interface to AJAX which makes the process a bit simpler. It also puts a collection of markers together into a single layer which is easier to manage - Leaflet makes each marker independent so I had to keep track of the markers I had added. I like Leaflet, and for many things it is something I will use.

You can find the code examples on github If you want the database definition or even the data I can supply that too. You can see the working example here.

Saturday 14 May 2011

PSMA and open data don't mix

I've been trying to encourage local authorities to consider using openstreetmap for a while on and off. It seems that Ordnance Survey (OS) recognised the growing competition and have forestalled these efforts. Local authorities use maps so much that they often have a dedicated GIS team. They base most of their activities on OS maps, which is not surprising, OS being the national mapping agency of Great Britain. Until recently the local authorities had to negotiate with OS about what products they wanted to licence. However the UK Government announced that this was to change for the current financial year and a new ten-year Public Sector Mapping Agreement (PSMA) was being introduced.

This agreement allows various public sector bodies access to core geographic datasets from OS, free at the point of use. This sounds very appealing to local authorities since they get to use any of the OS datasets included in the core set as and when they want and they don't have to worry about the cost. What could be wrong with that?

Well actually it is a very shrewd move by OS. The money to fund their licences with public bodies mostly came from central government. Most money that local authorities spend comes from central government. Council tax, for example, only covers about a quarter of local authority spending. The Government have naturally taken away the money they used to give to public bodies for OS licences and put it into the central pot to fund the PSMA. OS receive that cash, so they get the same money as ever, but now have much less effort in renewing the licences each year.

The really smart part is the way OS have tied the hands of local authorities into using OS datasets. When local authorities paid for an OS product, if they found a replacement the brave ones could have chosen not to licence the OS data and use the replacement instead. That would have saved them money - a great incentive. Now they get the OS datasets seemingly for free so why look at anything else?

Why does this matter? Well it matters a great deal if you are interested in open access to data from local authorities and other public bodies. If they want to publish information as text or tables they can do as they please, but as soon as they want to display the data as a graphic overlaid on a map their only option is to use an OS map as the background and suddenly they can't release the data as they choose because they are bound up by the draconian restrictions OS place on their map data. The local authority are not likely to use any other form of map, without these restrictions, because of the effort to move away from the seemingly-free OS datasets.

I am sure that for most of their work OS data suits local authorities very well. I'm not well placed to know if the PSMA is good value for money overall - clearly it is not free as some local authorities have claimed. I want to see a viable way of publishing data from a public body in an open way on maps that is not restricted by the licence of another public body, OS. PSMA has made that less likely to happen and has strengthened the hand of OS as a dominant GIS provider.