BLOG

Google Maps UK Postcode Selector with jQuery

10 August 2009 by Stuart Cam

I've put together a web-based UK postcode selector using the KML features of Google Maps and jQuery:

jQuery + Google Maps UK Postcode Selector

Preview the page and download the project zip (231kb).

Firstly, I should give a little background on the UK postal addressing system. The UK postcode database has been put together with the help of public tax money and yet this data is not freely available. In order to obtain and use the Postcode Address File (PAF) from Royal Mail you need to part with a significant amount of money. This is a major pain point for web developers looking to create geolocated services and could be why the US has a lot more exciting applications - they have free access to their zip code data. Surely there must be a better way to manage this data?

After a significant amount of Googling I managed to track down a KML file of the UK postcode boundaries from Map Logic. Using this data and some undocumented Google Map API features it was possible to put together a fairly intuitive post code selector.

Google Maps can accept the address of a KML file (or KMZ - which is a zipped KML file with an alternate file extension). The basic idea is that you can load the KMZ file, intercept polygon load events and opening of the info window. The majority of the work is done using the Google Maps API and some jQuery:

function setUpMap(){

  if (GBrowserIsCompatible()) {

    var mapUrl = "http://blog.codebrain.co.uk/ukpostcodes.kmz";
    var gOverlays = [];
    var isMouseOverMap = false;
    var corner1 = new GLatLng(48.5,-11);
    var corner2 = new GLatLng(60,3.6);
    var allowedBounds = new GLatLngBounds(corner1, corner2);
    var startingLatLong = new GLatLng(52.5,-2.7);
    var startingZoomLevel = 6;

    var map = new GMap2(document.getElementById("map_canvas"));
    map.enableScrollWheelZoom();
    map.addControl(new GSmallMapControl());
    map.setCenter(startingLatLong, startingZoomLevel);

    var overlay = new GGeoXml(mapUrl);
    map.addOverlay(overlay);

    map.addMapType(G_PHYSICAL_MAP);
    map.setMapType(map.getMapTypes()[2]);

    $.event.special.hover.delay = 100;
    $.event.special.hover.delay = 600;

    GEvent.addListener(map,"addoverlay",function(overlay) {
      if (overlay.name && !overlay.isKnown) {
        gOverlays.push(overlay);
        overlay.isKnown = true;
        if(gOverlays.length == 192) {
          var hash = {};
          for(i=0;i<gOverlays.length;i++)
          {
            (function(){
              var overlay = gOverlays[i];
              if(hash[overlay.name]===undefined){
                hash[overlay.name] = "";
                var node = $('#' + overlay.name).next();
                var text = node.html();
                node.html($('<a href="#">' + text + '</a>')
                  .hover(function(){
                        GEvent.trigger(overlay, "click");
                      },function() {
                        map.closeInfoWindow();
                      }));
              }
            })();
          }
        }
      }
    });

    GEvent.addListener(map, "mouseover", function() {
      isMouseOverMap=true;
    });

    GEvent.addListener(map, "mouseout", function() {
      isMouseOverMap=false;
      map.closeInfoWindow();
    });

    GEvent.addListener(map,"infowindowprepareopen", function(iwtabs) {
      var tab = $(iwtabs[0]).get(0);
      var elem = tab.contentElem;
      var postalcode = elem.firstChild.innerHTML;
      if(isMouseOverMap){
        $("#side_bar").stop()
                .scrollTo($('#' + postalcode).parents(".regionTitle"),
                750,
                {onAfter:function(){
                      $('#' + postalcode).parents(".code")
                                 .effect("highlight", {}, 5000);
                         }
                    });
      }
    });

    GEvent.addListener(map, "move", function() {
      if (allowedBounds.contains(map.getCenter())) {
        return;
      }
      var C = map.getCenter();
      var X = C.lng();
      var Y = C.lat();
      var AmaxX = allowedBounds.getNorthEast().lng();
      var AmaxY = allowedBounds.getNorthEast().lat();
      var AminX = allowedBounds.getSouthWest().lng();
      var AminY = allowedBounds.getSouthWest().lat();
      if (X < AminX) {X = AminX;}
      if (X > AmaxX) {X = AmaxX;}
      if (Y < AminY) {Y = AminY;}
      if (Y > AmaxY) {Y = AmaxY;}
      map.setCenter(new GLatLng(Y,X));
    });
  }
}

$(document).ready(function(){

  function removeSelectedPostalCode(code){
    $("#" + code).check('off');
    $("#lst" + code).parent().remove();
  }

  function addSelectedPostalCode(obj){
    var code = $(obj).attr('ID');
    var name = $(obj).next().text();
    $("#checkeditems").append($('<div class="remove">' +
              '<a href="#" id="lst' + code + '">' + name + '</a>' +
              '</div>').click(function(){
                             removeSelectedPostalCode(code);
                      }));
  }

  $('div.code input:checkbox').click(function(){
    if(this.checked){
      addSelectedPostalCode(this);
    }
    else {
      var code = $(this).attr('ID');
      removeSelectedPostalCode(code);
    }
  });

  $("div.code input:checked").each(function(){
                    addSelectedPostalCode(this);
                  });

  setUpMap();

});

$(window).unload(function(){
  GUnload();
});

The Google Maps KML implementation is rather limited. I had originally wanted to alter the shading on a particular post code region when it was selected. I did have a go at using the GeoXML Parser API to address each enclosed region, but the browser crawled to a halt trying to parse the 700Kb uncompressed KML file into separate polygons!

There is a minor issue in addressing regions which have multiple enclosed areas - for example Kirkwall in the far north of Scotland. Unfortunately the Google API loads the polygon data in a fairly random order so I was unable to choose which region I'd like to navigate to when the region is selected in the text list.

Tags: , , , ,

Categories: Google Maps | JavaScript | jQuery


© Codebrain 2017. All Rights Reserved. Registered in England: 07744920. VAT: GB 119 4078 13