Introduction

Our exercise crazy friend, Tim, bought a really crazy watch by Garmin called the Forerunner 201. It’s basically a sweat-proof GPS unit for your wrist to help you keep excellent records of your workout activities. Best part: you can hook it up to your computer and download an XML file of the latitudes, longitudes, altitudes, times, calories burned, and some other goodies for each workout. A few days ago, Tim mentioned that he was disappointed with the services offered for visualizing the data from his Forerunner. And so in an effort to help out both Tim and other web developers working on Google Map API implementations, we created this tutorial based off a practical application.

HTML Form Builder

See it in Action

The gMap Workout Tracker!

Our implementation loads the XML file and draws a polyline between each point on a small delay to give the animated effect and route taken. We thought it would be cool to spice it up and added some other calculations like total time, total distance, pace, markers at different colored intervals, a loading bar, and stats.

Note: This demo draws out just a small segment of one of Tim’s bike rides (the one he gave us was for a 96 mile behemoth!). Because the device spits out data points every few feet, we figure it’ll be more useful to developers to start with something a bit more managable.

Bugs/Concerns

  • The demo map has ~140 points being drawn. After about 200 points, the script starts to chug. If you’re going to map 96 miles worth of data, it might be a good idea to grab some food. We’re pretty sure it’s because only a certain amount of markers/polylines can be drawn at optimal speed. Eh.

  • The distance measurement is not super accurate. The math might be off. This is why some of the markers have weird spacing issues. Because speed is calculated from distance and time, that might be off too. This was mentioned to us by Tim, who thinks they must be because he’s pretty sure he rides MUCH faster than the stats indicate. Fair enough.

  • Altitude is not taken into consideration in the distance.

  • The map just doesn’t work in Safari. It’ll just close your browser. Working on it.

  • The half mile markers are set at the nearest point after a half a mile and not at the half a mile point itself.

How it Works

XML FILE

Let’s start by looking at an excerpt of the XML data given to us by the Forerunner device. Because the Forerunner combines all 7 or so different rides/runs in one xml file, we recommend breaking up each run into it’s own file.

<Run>
   <Notes></Notes>
   <Lap>
       <StartTime>2004-12-30T22:36:58Z</StartTime>       <Duration>PT1418.230S</Duration>
       <Length>5144.500</Length>
       <Calories>367</Calories>
   </Lap>
   <Track>
       <Trackpoint>
           <Position>               <Latitude>28.09127</Latitude>
               <Longitude>-82.42928</Longitude>
               <Altitude>12.154</Altitude>
           </Position>
           <Time>2004-12-30T22:36:58Z</Time>
       </Trackpoint>
       <Trackpoint>           <Position>
               <Latitude>28.09125</Latitude>
               <Longitude>-82.42928</Longitude>
               <Altitude>8.789</Altitude>
           </Position>
           <Time>2004-12-30T22:37:00Z</Time>
       </Trackpoint>     </Track>
</Run>

In Forerunner, you can track individual rides/runs by the ‘’ element. There are multiple ‘laps’ in the ‘Run’ separated by minutes rather than seconds and do not give a great idea of your route. A ‘Run’ also contains ‘Trackpoints’ taken every couple of seconds and give a much more reliable picture. Every other or third ‘Trackpoint’ can actually give a fairly realistic representation of the ride.

In the demo, we only used the ‘Trackpoints’ to map our data. You should be able to load an entire run or runs into the script with no problems.

FUNCTIONS

‘onLoad()’ is where all the fun begins and the map is created, displayed, and centered. The map is first created using the ‘GMap()’ constructor.

function onLoad() {
    map = new GMap(document.getElementById("map"));

After that the map controls which allow us to pan/zoom and switch between Map and Satellite mode are added.

control = new GLargeMapControl();map.addControl(control);
map.addControl(new GMapTypeControl());

Next, the map is centered at the proper geographical position and the zoom level set.

map.centerAndZoom(new GPoint(-82.45126, 27.94351), 4);

Once the map is in place, let’s load the info to be displayed.

loadInfo();

‘function loadInfo()’ should look familiar to those of you with any AJAX experience. The XML file is loaded in using the ‘request.open()’ function and once loaded in is stored as the variable ‘xmlDoc’.

var request = GXmlHttp.create();request.open("GET", "bike1.xml", true);
request.onreadystatechange = function() {
if (request.readyState == 4) {var xmlDoc = request.responseXML;

After the file is loaded we create an array of ‘markers’ consisting of the ‘Trackpoints’ found in the XML file.

 markers =xmlDoc.documentElement.getElementsByTagName("Trackpoint");

Once we know where all the markers are, we need to do something with them and head over to the ‘plotPoint()’ function.

window.setTimeout(plotPoint,timeOut);

The ‘plotPoint()’ function loops through the markers array and extracts the latitude and longitude from each ‘Trackpoint’.

var Lat = markers[i].getElementsByTagName("Latitude");
var Lng = markers[i].getElementsByTagName("Longitude");Lat = Lat[0].firstChild.nodeValue;
Lng = Lng[0].firstChild.nodeValue;

A ‘GPoint’ containing these latitude and longitude values is then created and passed to the ‘createMarker()’ function in order to generate the actual marker icon.

var point = new GPoint(Lng, Lat);
marker = createMarker(point,i,markers.length);

We have a point but now need to draw the line or add a ‘polyline’. To do this we extract the latitude and longitude from the next marker. This point is then stored as ‘point1’.

if (i < markers.length-2){
     var Lat1 = markers[i+1].getElementsByTagName("Latitude");
 var Lng1 = markers[i+1].getElementsByTagName("Longitude"); Lat1 = Lat1[0].firstChild.nodeValue;
 Lng1 = Lng1[0].firstChild.nodeValue;     var point1 = new GPoint(Lng1, Lat1);

After the next point is created and stored as ‘point1’, create an array containing the current point and next point.

var points=[point, point1];

With the position of the two points held inside of the variable ‘points’, we can create a polyline between the two points and overlay it onto the map using ‘map.addOverlay()’ and ‘new GPolyline()’. The color of the polyline changes after the XML file’s hits its halfway point.

if (i < markers.length/2){
     map.addOverlay(new GPolyline(points, "#336633",2,1));
}else{
 map.addOverlay(new GPolyline(points, "#000099",2,1));
}

Inside of the ‘plotPoint()’ function are calls to the calculateDistance, loadingPercentage, and calculateTime functions. Hopefully, these are pretty self explanatory and if not shoot me an email or post a comment for an explanation.

calculateDistance(Lng, Lat, Lng1, Lat1)loadingPercentage(i);calculateTime(i);

‘function createMarker()’ creates and displays the icons/balloons you see on the map. The begin, end, and checkpoint icons are created using the ‘GIcon()’ constructor.

var icon = new GIcon();
icon.shadow = "images/mm_20_shadow.png";
icon.iconSize = new GSize(12, 20);
icon.shadowSize = new GSize(22, 20);
icon.iconAnchor = new GPoint(6, 18);    
icon.infoWindowAnchor = new GPoint(9, 5);

The icons are then placed on the map if the point passed in happens to be a beginning, finishing, or checkpoint point. To do this we create a marker using the point passed into the function and the ‘new GMarker()’ constructor.

var marker = new GMarker(point);

After the marker is created, it is placed on top of the map using ‘map.addOverlay()’.

map.addOverlay(marker);

The little bubble and text that appears after a marker is clicked is registered using ‘GEvent.addListener()’.

GEvent.addListener(marker, "click", function() {
    marker.openInfoWindowHtml("Finish");});

Conclusion

We had a blast getting this working. For more information definitely check out Google’s API documentation. The Google Map API makes it very easy to create a map, generate points, and overlay those points on a map. They have tons of code examples. Definitely have to thank them for making it easy.

Obviously, this implementation has a ton of potential outisde of just excercise and workout tracking. It wouldn’t be difficult to adapt the functions to parse any kind of XML file with latititude, longitude and time data. Road trips. Scavenger hunts. Surveying. Truck routes. Package tracking. Whatever you want. Once you get the hang of it, anything’s possible.

HTML Form Builder
Chris Campbell

Visualize Your Exercise Routes with Google Maps by Chris Campbell

This entry was posted 5 years ago and was filed under Features.
Comments are currently closed.

· 21 Comments! ·

  1. Sam Judson · 5 years ago

    Wow, thats impressive. Not only does it do something really good but it makes it look good at the same time :)

    I found this as well which I thought might interest: gMapPedometer

  2. Ryan Campbell · 5 years ago

    Thanks Sam.

    We actually used some of the distance functions in the gMapPedometer to help us create this. They were a great resource.

  3. Ryan Stewart · 5 years ago

    Great work. Now I just need to figure out how to get one of those watches (and the bike mount to go with it). :heels turning::

    Kudos to Tim on being such a fast runner as well. A+ all around.

  4. Michael · 5 years ago

    Great application. I bought a forerunner last year, and ever since I’ve been trying to figure a way to plot my routes. When GMaps came out, I fiddled with it a bit, but didn’t have time to do something like this. Great Job!!!

  5. Tim Sabat · 5 years ago

    I accept all Kudos, but that is a biking speed. That is the beginning 6 miles of a 100 mile ride.

    I plan on getting an anti-gravity suit to go along with my GPS watch so I can achieve speeds of 15+ MPH on foot… Until that day comes, I will stick to the bike.

    The watch is nice. It can be found on Froogle for less than $120. The mount can be found here

    I extend my respect to Particletree for creating such a smooth UI for the application. If you would like to see how NOT to implement this solution, go here

  6. Ben · 5 years ago

    This is great!!! and nice blog BTW, I am now a regular reader.

  7. Stephan Segraves · 5 years ago

    Very cool!

    Does Google do the drawing of the lines? I know you posted the Polylines part but does Google actually decide the best route between the two points (which road gets you there fastest)?

  8. Ryan Campbell · 5 years ago

    Stephan – Given 2 points, Google draws a straight line. As far as I know, there is no artificial intelligence involved, and actual roads are not taken into consideration. Our map just has so many points so close to one another that it looks like a curving route along roads, but it’s just a bunch of small pixel lines being connected.

  9. Craig Bruce · 5 years ago

    Looking at what has been achieved here, I am considering analysing similar data produced from my (recently purchased) Suunto G9 GPS golf watch. It can provide a series of coordinates per hole, corresponding to the resting point of the ball after each stroke.

    I’ve checked and the course that I have data for is covered quite nicely by google maps….my question is whether anyone may have already done this (it’s Monday and I’m feeling lazy :-)

  10. Patrick Haney · 5 years ago

    I’ve seen a lot of uses of Google Maps, but this one has to be the coolest yet. I’m sure most people don’t have their own GPS units yet, but in the future I could see this being the sort of thing used to map trips and walking tours.

  11. Steve Speirs · 5 years ago

    What a cool application! One quick question, what does the following refer to in the workoutTracker.php source code?

    Many thanks and keep up the good work!

    Steve

  12. Steve Speirs · 5 years ago

    Sorry, the code didn’t show up. I was just asking about the reference to /shortstat/inc.stats.php in the source code.

    Thanks,

    Steve

  13. Ryan Campbell · 5 years ago

    Oh, you can ignore that line. It was there to track how many people viewed the Map, but we forgot to take it out for the download. Sorry about that.

  14. Nick Scott · 5 years ago

    Hi Guys. This is great.

    I have a Garmin 201. I’m using it to record all my training runs as part of my build up to London Marathon 2006. I’ve created a blog to try to raise awareness and money for NSPCC in their campaign against cruelty to children.

    I’d like to use your idea to map runs so that it makes my site more interactive and encourages people to visit and sponsor me.

    Problem - I know nothing about languages, programming etc. - I’m an accountant! Is there something I can download to help me? I’m lost with what you’re discussing above.

    I’d appreciate your help.

  15. Chris Campbell · 5 years ago

    Hi Nick,

    Somehow your Garmin should be able to export an XML file. I would guess it is in the same format as my friend’s. Save that xml file to your webserver. Next, in the javascript file, change this line

    request.open(“GET”, “bike1.xml”, true);

    to request.open(“GET”, “locationofyourxmlfile.xml”, true);

    Hopefully this helps you out and good luck with your cause.

  16. Kris · 5 years ago

    Any progress on Safari support? Do you know what particular code kills Safari?

  17. GioSico · 5 years ago

    http://www.gpsbabel.org/ can export to Garmin Log XML format which can then be used here.

  18. GioSico · 5 years ago

    Is there a way to pause or delay the route drawing before start so as to give the map time to load.

    I was able to add map.setMapType(G_SATELLITE_TYPE);

    to only show SAT images as there are no maps for my area yet.

    Nice work

  19. Raymond · 4 years ago

    Is it easy to add an elevation graph to the bottom of the google map? It’ll be cool to see the elevation change while the google map draws.

    Very cool tool!

  20. J · 4 years ago

    This is very neat!

  21. Ronán · 4 years ago

    Excellent! This is just the type of thing I was looking for. It seems it’s built for version 1, has anyone configured the code for version 2?