jQuery Store Locator with Google Maps API, Google Distance Matrix API, HTML5 GeoLocation & Google Maps KML file as data source

I haven't updated this blog for nearly 4 years, however, I thought I'd post this in case some web developer like myself out there is looking for a "client-side only" solution for a simple Store Locator. When I was looking for it, most of the examples I found online were using server-side scripting, mostly either PHP or ASP .NET but there was only one great example on how this could be achieved using just JavaScript, jQuery AJAX with a simple HTML form and XML file as a data source. The example I am talking about is Bjorn Holine’s jQuery Store Locator Plugin. His code works like a charm and is released as an open github repo… Which is why I forked it JI decided to create my own version in order to satisfy several requirements I was facing:

·         I didn’t want to create a data file with a custom format, nor did I want to have a database to store my locations. I’ve created my own Google map and let the business guy collaborate – i.e. he added all of the stores. J So, I simply went to this map on maps.google.com and clicked on “KML” link. This let me download a file, which was basically an XML file containing all the information I needed – store information and geographical coordinates. I saved this file as stores.xml

Google Maps KML file

 ·         I wanted to work out the closest N stores and show not only their details but also distance from user’s location in miles. My users can't fly ;), therefore this distance would have to be for driving routes, hence I couldn’t just use Great-Circle Distance formula and had to use Google Distance Matrix API instead.

·         Another main change is that I’ve used the HTML5 GeoLocation to find out what the user’s location was. This feature is particularly useful for mobile browsers as most modern phones have GPS built-in, hence location would be determined very accurately. Albeit on desktop machines, I think it just uses your IP address to work out where you are – I found that sometimes it locates me on a street near where I was, but sometimes in another city, 70 miles away. Naturally, I left the ability to override user’s location using an input textbox where you could specify pretty much anything Google Maps API could locate you by – a postcode (zip code), street name, city/town, geographical coordinates etc. - you get the gist.

 

Other Features include:

·         Fully client-side application with XML file data source. No server-side scripting or database required. Not quite true, of course, since it uses Google’s APIs, which must be server-side services, but it can deployed as a HTML app because all API calls are made via jQuery AJAX.

·         This is a jQuery plugin; hence jQuery reference is required in order for it to work :) It also requires a Google Maps JavaScript API v3 reference for the mapping stuff

·         Takes in a Google Maps KML file as a source of store locations developers.google.com/kml/documentation/mapsSupport

·         Uses HTML5 GeoLocation to determine user's location when applicable

·         Uses Google Geocoding API developers.google.com/maps/documentation/geocoding to work out user's location from supplied address details

·         Uses Google Distance Matrix Service to work out the driving distances to store locations

·         Uses jQuery.Deferred() object api.jquery.com/category/deferred-object to work around the Google Distance Matrix Service's limitation of 25 destinations in one service call, thus making it multiple service calls asynchronously when required

·         Shows user's location on the map (0 marker)

·         Works out and displays nearest N (configurable variable) shops with their details in the list, showing the <name>, <description> from the data source file, driving distance in miles and a link to Google Map with driving directions

·         Shows nearest store locations on the maps with A-Z marker pins

·         Automatically chooses appropriate Map Zoom level to fit all locations on the map

 

Now let's see what is under the bonnet.

Here's a sample location inside KML file's structure:

	
  Optimax Laser Eye Surgery - Aberdeen
  
    Optimax Laser Eye Clinic Golden Square City Centre, 
    Aberdeen AB10 1RD
  
  
    Optimax Laser Eye Clinic - Aberdeen
    2 Golden Square
    City Centre, Aberdeen AB10 1RD
    0870 514 3314
  

    -2.104521,57.145737,0.000000
  

You'll need a couple of script file references, preferebly at the bottom of your <body> block, place this code:




jQuery Code to read your data file:
$.ajax({
    type: "GET",
    url: 'stores.xml',
    dataType: "xml",
    success: function (xml) {
        _locationset = new Array();
        $(xml).find('Placemark').each(function (i) {
            var shop = {
                Name: $(this).find('name').text(),
                //Take the lat lng from the user, geocoded above
                LatLng: new google.maps.LatLng(
                  $(this).find('coordinates').text().split(",")[1],
                  $(this).find('coordinates').text().split(",")[0]),
                Description: $(this).find('description').text(),
                Marker: null,
                Distance: null
            };
            _locationset.push(shop);
        });...
    });

Google Distance Matrix API currently has a limit of max 25 destinations per call, this method below would be called Asynchronosly for each set of 25 locations from above _locationset array. This utilises jQuery.Deferred() object introduced in jQuery 1.5. The Distance Matrix Service call looks like this:

function (startIndex, origin, destinations) {
  var token = $.Deferred();
  var service = new google.maps.DistanceMatrixService();
  service.getDistanceMatrix({
    origins: [origin],
    destinations: destinations,
    travelMode: google.maps.TravelMode.DRIVING,
    unitSystem: google.maps.UnitSystem.IMPERIAL
    }, function (response, status) {
      if (response && response.rows.length) {
        var results = response.rows[0].elements;
        $.each(results, function (j, val) {
          if (results[j].status != "ZERO_RESULTS") {
              _locationset[startIndex + j].Distance = 
                 GoogleMapDistanceTextToNumber(results[j].distance.text);
          }
        });
      token.resolve();
    }
  });
 return token.promise();
};

GeoLocation on initial page load:

           
$(document).ready(function () {
  // Try HTML5 geolocation
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function (position) {
      var g = new GoogleGeocode();
      var latlng = new google.maps.LatLng(position.coords.latitude, 
        position.coords.longitude);
      g.geocodeLatLng(latlng, function (address) {
        if (address) {
          showAddress(address);
        } else {
          //Unable to geocode
          handleNoGeolocation('Error: Unable to geocode address');
        }
      });
      // do the mapping stuff
      mapping(position.coords.latitude, position.coords.longitude);
  }, function () {
    handleNoGeolocation("Tracking of location was not allowed.");
  });
} else {
    // Browser doesn't support Geolocation
    handleNoGeolocation(false);
  }
});
There's more to it, of course, but these are main functions. 
You can download attached source code or pull it from my bitbucket repo:
https://bitbucket.org/ruslanss/jquery-google-maps-distance-matrix-store-locator

ssh://hg@bitbucket.org/ruslanss/jquery-google-maps-distance-matrix-store-locator
And you can also view a demo here: optimax.apphb.com/demo.html
If you have any queries, leave a comment or hit me on twitter: @funky_rus

15 Comments

  • Good work man, I will lab with it tomorrow for a project, then I will send you back the result. I will do some modifications and add jQuery Mobile. ... you have my mail on the websites..

  • You pretty much rock! This is awesome!!!! Thank you.

  • This is fantastic! Do you know if its possible to use the FEATURES functionality with KML? For instance showing / hiding locations that have wheelchair access?

    Thanks!

  • I also just noticed that if the returned distance is in feet, it ranks that location lowest, thinking its in miles.

  • This is great, I am having some problems when my XML file contains over 50 locations though.

    It seems that although the limit of 25 is used N times, over 50 results sometimes causes the call to:

    GeoCodeCalc.CalcDistanceGoogle(new google.maps.LatLng(orig_lat, orig_lng)

    to not get a response, so the following:

    $.when.apply($, subFunctionTokens).then(function () {
    callback(true);
    });

    isn't set, and the function never returns to the calling function, so no rror message is generated, it just stops and results aren't mapped.

    It's odd that at 50 results I get about a 60% success rate, but over 100 results gives me total failure.

    Thanks

  • This looks like a great plugin. However, I'm experiencing the same thing as Graham. The plugin doesn't function if I have 50+ locations. Any idea how to fix that? Thanks!

  • This is really interesting, You are a very skilled blogger.
    I've joined your rss feed and look forward to seeking more of your fantastic post. Also, I have shared your web site in my social networks!

  • I don't know if it's just me or if perhaps everybody
    else encountering issues with your website.
    It appears as if some of the written text on your posts are
    running off the screen. Can someone else please provide feedback and let me know if this is happening to them too?
    This may be a problem with my web browser because I've had this happen previously. Thank you

  • Attractive section of content. I just stumbled upon your weblog and in accession capital to assert that I get actually enjoyed account your blog posts.
    Any way I'll be subscribing to your feeds and even I achievement you access consistently quickly.

  • These are in fact great ideas in on the topic of blogging.
    You have touched some pleasant things here. Any way keep up wrinting.

  • Unquestionably believe that which you said.
    Your favorite reason appeared to be on the internet the easiest thing
    to be aware of. I say to you, I certainly get annoyed while people think about
    worries that they plainly do not know about.
    You managed to hit the nail upon the top and also defined out
    the whole thing without having side effect , people
    can take a signal. Will probably be back to get more.
    Thanks

  • Its like you learn my thoughts! You seem to know a lot approximately this, like you wrote the e-book in it or something.
    I believe that you could do with a few percent to force the message home a bit,
    however other than that, this is wonderful blog.

    A fantastic read. I'll definitely be back.

  • A person necessarily help to make severely posts I'd state. That is the first time I frequented your web page and so far? I amazed with the research you made to make this particular post amazing. Wonderful task!

  • I've been surfing online more than 4 hours today, yet I never found any interesting article like yours. It is pretty worth enough for me. Personally, if all webmasters and bloggers made good content as you did, the internet will be a lot more useful than ever before.

  • Good day! Would you mind if I share your blog with my zynga group?

    There's a lot of people that I think would really appreciate your content. Please let me know. Many thanks

Comments have been disabled for this content.