Digitising a Route using Google Maps API
I wanted to add mapping features to my sports website My Race Times. I looked at the Ordnance Survey API as they have great background maps, but the free service has a limit of 1000 maps per month (at least it did when I looked at it). The Google service seemed well documented and I could find plenty of example code, so it seemed a safe bet.
To use this you need to register with Google. They give you a unique id that you include in your code. The Google map service is based entirely on HTML and JavaScript, making it completely platform independant. My site is written in PHP so that's where I've put this page.
You need to include the JavaScript library in your code:
<script src="http://maps.google.com/maps?file=api&v=2&sensor=false&key=xxx" type="text/javascript"> </script>
The v parameter specifies the version, and version 3 is now available. I believe that the sensor parameter should be set to true if you want to detect the location of a GPS-enabled device that is viewing your website. The key is provided by Google when you register.
To display a map you use an ordinary empty div tag. Give it an id and use the style attribute to size it:
<div id='map_canvas' style='width: 700; height: 500px;'></div>
When your page loads you need to call a JavaScript function to initialise your map. This is normally done by added an on load trigger to the body tag of your page. You should also add an on unload trigger to clean up at the end.
<body onload="initialize()" onunload="GUnload()">
In the initialize function you create a JavaScript object of type GMap2 (for version 2 of the API). If you make this a global variable by declaring it outside the function definition then you can access it from any function. The first parameter specifies the div in the html document where the map will be drawn and the second contains any parameters you wish to set when the map is created. This map was to allow people to digitise their own routes, so I chose a cross-hair type cursor (a big plus sign).
var map;
var lineShape;
var linePoints = [];
function initialize()
{
if (GBrowserIsCompatible())
{
map = new GMap2(document.getElementById("map_canvas"),
{draggableCursor: 'crosshair', draggingCursor: 'crosshair'});
var where=new GLatLng( 55.37183416340402, -3.4574317932128906 );
map.addControl(new GLargeMapControl());
map.addControl(new GMapTypeControl());
GEvent.addListener(map, 'click', mapClick);
map.setCenter( where, 5);
// Create marker icons
var lineIconStart = new GIcon(G_DEFAULT_ICON);
lineIconStart.image = "js/blue_MarkerS.png";
var lineIconFinish = new GIcon(G_DEFAULT_ICON);
lineIconFinish.image = "js/blue_MarkerF.png";
// Set up GMarkerOptions objects
lineStartMarkerOptions = { icon:lineIconStart };
lineFinishMarkerOptions = { icon:lineIconFinish };
//map.setUIToDefault();
linePoints = [];
readCoordinates();
drawCoordinates();
}
}
I created a new location variable called where centred on the mid-point of the UK, and set the map to centre itself on this at a scale of 5, which is large enough to see the whole of the country (even Shetland and the Channel Islands).
I added a couple of controls to the map: GLargMapControl allows you to zoom and pan easily; while GMapTypeControl allows you to switch between a line map and aerial photography or a composite of both.
In order to do the digitising I needed to know when someone clicked on the map, and this is done by added an event listener to the map. This calls a function which I imaginitively named mapClick(). There's a great book called Clean Code which will back me up with my naming conventions.
I wanted to use different markers, so I found a set on Benjamin Keen's website that worked a treat, with S for the start and F for the Finish.
The points are simply an array. When the page is assembled in PHP I read the points from a mySQL database into a hidden form field from a single text field. The points are stored in a KML type format or latitude longitude pair values with a space between them. Points are seperated by commas.
The readCoordinates() function parses this text field and populates the linePoints array. I've not shown the error checking to aid the readability of the code.
function readCoordinates()
{
var data = document.getElementById("line_route").value;
if( data.length > 0 )
{
var pairs = data.split(",");
for( i=0; i<(pairs.length); i++ )
{
var pair = pairs[i].split(" ");
var lat = pair[0];
var longi = pair[1];
var point = new GLatLng( lat, longi );
linePoints.push( point );
}
}
}
The drawCoordinates() function then converts this array into a GPolyline shape and plots it on the screen, adding the start and finish markers afterwards.
function drawCoordinates()
{
//Re-create Polyline
map.clearOverlays();
// Show start point if there is at least one point
if( linePoints.length > 0 )
{
lineStart = new GMarker(linePoints[0], lineStartMarkerOptions);
map.addOverlay(lineStart);
}
// Show end point if there is more than one point
if( linePoints.length > 1 )
{
lineFinish = new GMarker(linePoints[linePoints.length -1],
lineFinishMarkerOptions);
map.addOverlay(lineFinish);
lineShape = new GPolyline(linePoints, lineColor, lineWeight, opacity);
map.addOverlay(lineShape);
}
logCoordinates();
}
The logCoordinates() function just writes the points to the hidden form field for saving to the database.
function logCoordinates()
{
var data = "";
if( linePoints.length > 0 )
{
for (var i = 0; i<(linePoints.length); i++)
{
if( i > 0 ) data = data + ",";
var lat = linePoints[i].lat();
var longi = linePoints[i].lng();
data = data + lat + " " + longi;
}
}
document.getElementById("line_route").value = data;
}
Adding new points to the array is simply a matter of capturing the mapClick event that was added in the initialize() function and then redrawing the map to show the newly extended line:
function mapClick(marker, clickedPoint)
{
linePoints.push(clickedPoint);
drawCoordinates();
}
It took me a while to get my head round it, but once you get it it's quite simple. I hope you find this helpful.

