Recently I have had to create a store locator for a website I have been working on. On the outset, it sounds easy. Simply add a number of points to the map and calculate the distance between a point of reference and the points you have added.
- Click here for demo.
- Download pointSorter.js.
Then the question comes up: How do you calculate distance? As the crow flies, or by road directions?
To test this out, I created a Google Maps Distance Calculator.
Road directions work, and are not too difficult to get from the API. However, they are dead slow and require callbacks. Sometimes a request may take up to 30 seconds, which means that it could take ages to render the distance between 10 or so points. Furthermore, sometimes the distance cannot be calculated at all.
So the more efficent and reliable way is to draw a line and measure it. That’s much easier and does not require a callback method:
var polyline = new GPolyline([ point1.getLatLng(), // starting point point2.getLatLng() // finishing point ], "#0000FF", // color 1 // opacity ); numDistance = polyline.getLength(); // in metres
I could go on about the intricacies of storing and sorting an array of points, but I won’t. Instead I have created a script that can do this for you. First off, you will need to get a google maps key. This is easy, you can do it here. Then, you need to call the google maps V2 API as follows:
<head> ... <script type="text/javascript" src="http://www.google.com/jsapi?key=__________________KEY_________________"></script> <script type="text/javascript"> // initialise google maps google.load("maps", "2"); </script> </head> ....
You will need to add some divs for containing the map and listing areas. By default, the map element is assumed to be a <div> tag and the listing container is a <table> tag.
<body> ... <div id="map"></div> <table id="table"></table> ... </body>
(We will get into how you can change the listing container later)
Then, you will need to include the pointSorter.js script as follows:
<head> ... </script> <script type="text/javascript" src="pointSorter.js"></script> <script type="text/javascript"> // set target divs pointSorter.setDiv("map"); pointSorter.setListingContainer("table"); // load maps, point sorter etc google.setOnLoadCallback(function () { // initialise point sorter pointSorter.init(); // add points ... }); </script> ... </head>
You may have noticed that I have not entered anything below “// add points” in the block above. There are two ways to add points:
// add point by coordinate pointSorter.addPoint( new GLatLng(37.423156,-122.084917), "Google Inc.","<strong>Google Inc. Head Office</strong>" ); // add point by address pointSorter.addPointByAddress( "701 First Avenue, Sunnyvale, California", "<strong>Yahoo! Head Office</strong>" );
Both methods take similar arguments. The first takes a GLatLng (point) object + a description, whilst the second takes an address and a description. The first method is much faster than the second as no request has to be made to the server to look up an address.
By now (after some style tweaking), your code may be looking something like this:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Maps Test</title> <style type="text/css"> body { font-family:sans-serif; font-size:0.8em; } /* ID Selectors */ #map { width:70%; height:500px; float:left; } #table { margin-top:1em; width:30%; height:500px; overflow-y:auto; float:right; } /* classes */ .info { color:#008000; } </style> <script type="text/javascript" src="http://www.google.com/jsapi?key=_____________KEY____________"></script> <script type="text/javascript" src="pointSorter.js"></script> <script type="text/javascript"> // initialise google maps google.load("maps", "2"); // set target divs pointSorter.setDiv("map"); pointSorter.setListingContainer("table"); // load maps, point sorter etc google.setOnLoadCallback(function () { // initialise point sorter pointSorter.init(); // add point by coordinate pointSorter.addPoint( new GLatLng(37.423156,-122.084917), "Google Inc.","<strong>Google Inc. Head Office</strong>" ); // add point by address pointSorter.addPointByAddress( "701 First Avenue, Sunnyvale, California", "<strong>Yahoo! Head Office</strong>" ); }); </script> </head> <body> <div id="map"></div> <table id="table"></table> </body> </html>
If you run this in your browser, you should see a map appear with two locations shown. Clicking on either will reveal the name of the location.
Now the only thing left to do is allow users to enter in an address. This is as simple as adding the method and form below. When the form is “submitted”, a search is made for the address.
<script type="text/javascript"> ... function setLocation (location) { // set point of reference pointSorter.setPointOfReference( location, "<strong>Reference Location</strong><p>"+location+"</p>" ); } </script> </head> <body> <form action="" onsubmit="setLocation(this.address.value);return false;"> <input type="text" name="address" /> <input type="submit" value="Set Reference Location" /> </form> ... </html>
When the form is submitted, a new point is added to the map and the distances are shown.
Here are some other tips about the script.
You can set icons for the map pointers by using the following methods:
// set icon for reference location pointSorter.setHomeIcon("http://www.google.com/mapfiles/dd-start.png", 20, 34); // set icon for surrounding point locations pointSorter.setPointerIcon("http://www.google.com/mapfiles/dd-end.png", 20, 34);
If you have a lot of locations, you can limit the display to only list locations within a certain distance from the reference point, e.g. 100km:
// distance in kilometres pointSorter.setDistanceThreshold(100)
if you are not going to use a table, you need to set the item template for listing items. The example below allows your items to be listed inside an unordered list (<ul>) tag:
pointSorter.setItemTemplate( "