GEOG5870/1M: Web-based GIS A course on web-based mapping

Adding information to polygons

The previous section illustrated the use of polygons by adding two simple polygons to the map. However, the polygons provided no extra functionality, and were both shaded in the same way. By shading polygons in different colours, we can add thematic meaning to the map, as we did with differential shading of lines. We can also present further information by adding information to be included on a pop-up window. The following listing shows the code of a revised polygon example; this uses the same data file as the previous example (here's the associated files: poly_eg2.html; poly_eg1_data.js).

Code for poly_eg2_setup.js
var map; // The map object
var myPoly;
var myCentreLat = 53.81;
var myCentreLng = -1.52;
var initialZoom = 12;

function infoCallback(infowindow) {
   return function() {
      infowindow.open(map);
   };
}

/*
* addPoly
*/
function addPoly(polyPath,myInfo,line_colour,fill_colour) {
   /*
    * Construct the polygon
   */
   myPoly = new google.maps.Polygon({
      paths: polyPath,
      strokeColor: line_colour,
      strokeOpacity: 0.8,
      strokeWeight: 3,
      fillColor: fill_colour,
      fillOpacity: 0.35
   });

   var infowindow = new google.maps.InfoWindow({
      content: myInfo,
      position: polyPath[0]
   });
   google.maps.event.addListener(myPoly,
      'click', infoCallback(infowindow));

   myPoly.setMap(map);
}

function initialize() {

   var myCentre =
      new google.maps.LatLng(myCentreLat,myCentreLng);

   var myOptions = {
      zoom: initialZoom,
      center: myCentre,
      mapTypeId: google.maps.MapTypeId.ROADMAP
   };

   map = new google.maps.Map(
      document.getElementById("map_canvas"),myOptions);

   for (id in os_polydata) {

      var polyPath = []; // An empty array

      /*
      * Read through points
      */
      var thisBoundary = os_polydata[id].boundary;

      for (pt in thisBoundary) {
         var osPt = new OSRef(thisBoundary[pt].easting,
            thisBoundary[pt].northing);
         var llPt = osPt.toLatLng(osPt);
         llPt.OSGB36ToWGS84();
         var myLatLng = new google.maps.LatLng(llPt.lat,llPt.lng);
         polyPath.push(myLatLng);
      }

      var fillShade = 256*(os_polydata[id].value/100);
      var fillShadeHex = parseInt(fillShade).toString(16);
      var fillColour = '#'+fillShadeHex+fillShadeHex+fillShadeHex;

      var info = "<div><h1>" + os_polydata[id].title +
         "</h1><p>" + os_polydata[id].description +
         "</p></div>";
      addPoly(polyPath,info,"#000000",fillColour);
   }
}

This example adds a function for adding polygons to the map that is similar to the function used to add multiple markers. The function is called addPoly(), and it takes as its parameters an array of LatLng points (polyPath), some text to be used for an infowindow, a line colour and a fill colour. Again, we have to use a callback function to make sure that we get distinct infowindows for each polygon, however the function used is slightly different to that used in the markers example (and thus should really have been given a different name!). Infowindows can be attached to markers, or to any given LatLng point on the map; they cannot be attached in a general manner to a polygon: we have to say where we want them to pop up.

We can do this by setting the position parameter of the infowindow object.

   var infowindow = new google.maps.InfoWindow({
      content: myInfo,
      position: polyPath[0]
   });

Here we have used the first point in our polygon (polyPath[0]), although a more appealing placement would probably require us to iterate through all points and calculate a centroid. Given that the position is set, we do not need to supply a marker reference to the callback function.

   google.maps.event.addListener(myPoly,
      'click', infoCallback(infowindow));

The setup file also illustrates the dynamic generation of a fill shade from a value read from the data file (in this case, os_polydata[id].value).

      var fillShade = 256*(os_polydata[id].value/100);
      var fillShadeHex = parseInt(fillShade).toString(16);
      var fillColour = '#'+fillShadeHex+fillShadeHex+fillShadeHex;

First , we convert the value into a number between 0 and 256. The example above assumes that all values are in the range 0 to 100. We then convert this to a hexadecimal number: we use the builtin JavaScript function parseInt() to convert fillShade to an integer, and then run the method toString(16) on the result of parseInt(). The method toString() converts a number to a string. It can take an optional radix (base) value as a parameter; hexadecimal is base 16, so using toString(16) converts a numeric value to a string containing a hexadecimal version of that number. Finally, we construct a string called fillColour as a standard Google Maps colour reference: a '#' plus three hex values for red, green and blue (hex numbers are often preceded by a '#'). Here we have used the same value for red, green and blue, so we will get a variable shade of grey.

Having calculated a fill colour, we call our addPoly() function for each polygon.

The code in the figure above is relatively simple – it creates only shades of grey, and as mentioned, it assumes that the attribute term will vary between 0 and 100.Some basic error correction for values outside this range is included. The variable fillValue is used as a term to determine the shading level, and is intially set as a copy of the raw data value. If this value is less than 0 or greater than 100, it is modified to be 0 or 100 respectively. However, a better approach would be to have a preparatory loop that reads through the data file and determines the range of the shading variable, and then sets the shading accordingly.

Increasing the number of polygons

The above example used two polygons only. It is possible to add more polygons, and also possible to add much more complex polygons than simple squares. Look at the listing below; it contains extracts from a larger data file, that contains boundary definitions for five wards in central and north Leeds. It is loaded by an HTML and map setup file that are identical to those used in the previous example apart from a) the designation of the appropriate data file and b) the initial zoom level specified (poly_eg3.html; poly_eg3_setup.js).

Extract of code from poly_eg3_data.js
var os_polydata = [{
'title': 'Kirkstall',
'description': '08DAFR',
'value': 27,
'boundary': [
{'easting':424937.406702199,'northing': 437355.594025358},
{'easting':425253.500142493,'northing': 437616.812329601},
{'easting':425343.187182577,'northing': 437517.593897509},
{'easting':425753.812206959,'northing': 437609.499945594},
{'easting':426193.406191368,'northing': 437497.90647349},
{'easting':426149.000431327,'northing': 437284.687145292},
{'easting':426434.000111593,'northing': 436914.812200947},
{'easting':426354.499823518,'northing': 436667.999528717},
{'easting':426469.593327626,'northing': 436420.281640487},
{'easting':426607.312111754,'northing': 436494.812456556},
{'easting':426763.000047899,'northing': 436317.593896391},
{'easting':427104.000240217,'northing': 436471.312680534},
{'easting':427125.687536237,'northing': 436375.312680445},
{'easting':427039.812848157,'northing': 436340.687144413},
{'easting':427186.000112293,'northing': 436297.499944372},
{'easting':427056.906480173,'northing': 436077.094184167},
{'easting':427119.687920231,'northing': 435708.093735823},
{'easting':427820.687600884,'northing': 435260.500263407},
{'easting':428024.592625074,'northing': 434796.093734974},
{'easting':428505.687281522,'northing': 434549.687590744},
{'easting':428096.906481141,'northing': 434024.187174255},
{'easting':427612.593392690,'northing': 434195.312934414},
{'easting':427315.093744413,'northing': 434180.687142401},
{'easting':427323.906288421,'northing': 434343.187750552},
{'easting':427155.999984265,'northing': 434714.500390898},
{'easting':426998.499568118,'northing': 434630.40643882},
{'easting':426748.812527886,'northing': 434694.593830879},
{'easting':426432.844015591,'northing': 435168.656679321},
{'easting':426276.312303446,'northing': 435118.312743274},
{'easting':426101.093615282,'northing': 435268.406567414},
{'easting':425949.781231142,'northing': 435610.500391732},
{'easting':426075.812079259,'northing': 435800.499495909},
{'easting':426049.687791235,'northing': 435984.687400081},
{'easting':425899.312367095,'northing': 436006.593832101},
{'easting':425253.812462493,'northing': 436409.000232476},
{'easting':424902.094062166,'northing': 436409.093416476},
{'easting':424774.906094047,'northing': 436665.499944715},
{'easting':424453.312749748,'northing': 436835.093800873},
{'easting':424557.812973845,'northing': 437049.499945073},
{'easting':424937.406702199,'northing': 437355.594025358}]
},
{
'title': 'Weetwood',
'description': '08DAGG',
'value': 12,
'boundary': [
{'easting':428588.310769599,'northing': 436488.17488855},
{'easting':427825.078512888,'northing': 436514.798888575},
{'easting':427480.093936567,'northing': 436298.187048373},
{'easting':427080.406256195,'northing': 436298.687784373},
{'easting':427104.000240217,'northing': 436471.312680534},
{'easting':426763.000047899,'northing': 436317.593896391},
{'easting':426607.312111754,'northing': 436494.812456556},
{'easting':426500.312303654,'northing': 436394.500392462},
{'easting':426354.499823518,'northing': 436667.999528717},
{'easting':426434.000111593,'northing': 436914.812200947},
//...
{'easting':428943.187185929,'northing': 435455.312167588},
{'easting':428573.898993585,'northing': 434715.399462899}]
}
]

The co-ordinates were derived in a fairly lengthy manual manner: the polygon data were derived from a set of polygon boundaries downloaded from UKBorders (although fortunately, there are easier ways than this of converting polygon data). The MIF file format available from UKBorders presents the polygons as a series of easting, northing pairs, with one pair per line. For a relatively small set of wards such as this, it was possible to edit the resulting file manually, to prefix each line with "{'easting': " and to add other bits of text as required. However, this is time consuming and is clearly not a suitable approach for larger amounts of data. The other properties (the title, description, and fill colour) was added manually for each polygon. Here's what the data looks like:

Ward level polygon example

For the sake of simplicity, the polygons have been shaded in various intensities of grey. It would of course be quite easy to work out different red, green and blue values, in order to use colours.

Whilst polygons are useful, there are limits on how many can be added to a map before it becomes unwieldy (slow to load and respond to clicks).


[ Next: Additional overlay types]
[Course Index]