http://www.developer.com/

Back to article

Charting Your Course Using the Google Maps API


April 13, 2009

This ongoing series regarding building location-based web sites using PHP and the Google Maps API has covered quite a bit of ground, no pun intended, showing you how to plot locations on maps, convert addresses into their corresponding coordinates using a variety of solutions, and even build your own geocoding web service for fun and profit.

This installment tackles another fascinating aspect you'll undoubtedly encounter when building location-based solutions, namely distance calculation. Whether you want to simply plot the distance separating two points on the map, or perform more complex operations involving measuring the total distance spanning a more complex route, understanding how to perform these sorts of calculations will prove invaluable time and again. Along the way, you'll also learn how to enhance the user-friendliness of your maps using Ajax and various compelling features of the Google Maps API .

Calculating the Distance Between Two Points

Weather-permitting, most days I take a lunchtime stroll each day from my office, located in close proximity to the corner of Grandview Avenue and 3rd avenue, and denoted by the blue marker found in Figure 1. I walk down to the corner of Grandview Avenue and 5th Avenue, and then double back to the office. How can the distance between these two corners be determine programmatically?


Figure 1. Determining the distance between two points

For starters, a way to plot the two points on the map is needed. To do so, the Google Maps API can be used to add a listener, which will constantly poll the status of the rendered map, waiting for a particular event to occur, such as a mouse click. Clicking the mouse on top of the map will cause the listener to execute another JavaScript method. This listener can be embedded within the load() function, which automatically executes thanks to the onload() event handler attached to the page's <body> tag. The load() JavaScript function and two variables that will be used throughout the script looks like this:

<script type="text/javascript">
    // The Google Map
    var map;
    
    // The coordinate array
    var coordinates = []; 
    function load() {
      if (GBrowserIsCompatible()) {
        map = new GMap2(document.getElementById("map"));
        map.setCenter(new GLatLng(39.985127,-83.043144), 14);
        map.addControl(new GLargeMapControl());
        GEvent.addListener(map,"click", addMarker);
      }
    }
</script>
<body onload="load()">
...
</body>

By now everything in this function should be familiar, except for the last line. The GEvent.addListener() method attaches a click listener to the map object, which will cause a function named addMarker() to execute should the user click on the map.

Next the addMarker() function will be created. This function is responsible for responding to the listener by placing a marker at the location where the user clicked the mouse, and append the new marker's coordinates to the coordinates array. If the user previously added another marker by way of this function, addMarker() will also draw a line between the new and old marker.

function addMarker(overlay, latlng) {
  // Create a new marker based on the coordinates
  var marker = new GMarker(latlng);
    
  // Add the new marker to the map
  map.addOverlay(marker);
    
  // Create the new array element
  coordinates_array_element = latlng.lat() + "," + latlng.lng();
    
  // Add the new array element to the map
  coordinates.push(coordinates_array_element);
    
  // If more than one point on the map, connect the new point to the preceding point
  if (coordinates.length > 1) {
        
    // Draw a line between the current and previous marker
    coordinate_prev = coordinates[coordinates.length - 2];          
        
    var polyOptions = {geodesic:true};
    var polyline = new GPolyline([
      new GLatLng(latlng.lat(),latlng.lng()),
      new GLatLng(coordinate_prev.split(",")[0],coordinate_prev.split(",")[1])
      ], "#ff0000", 3, 1, polyOptions);
    map.addOverlay(polyline);
            
  }
}

Finally, a way is needed to calculate the distance between the two charted points. Because this is such a commonplace feature, the GoogleMapAPI PHP library used in previous articles offers a built-in method for calculating this distance. It can even do so using a variety of units, including miles (the default), kilometers, nautical miles, inches, and feet. Naturally, the calculation should be performed in a user-friendly fashion, which these days involves using a bit of Ajax to make the distance calculation a seamless process.

// Initiates the distance calculation process.
function calculateDistance()
{
    // Only two markers should be found on the map
    if (coordinates.length == 2) {
      
      // Join each element coordinates array with a vertical pipe
      var coords = coordinates.join("|");
      // Perform the Ajax call. The coordinates are passed as a GET parameter.
      // When completed, the postResponse() function is called.
      var myAjax = new Ajax.Request('distance.php',
        {method: 'get', parameters: {coords: coords},
        onComplete: postResponse});
        
    } else {
      document.getElementById("distance").innerHTML = "Two points are required.";
    }
}
// Update the current web page with the value returned from the PHP script.
// This value will be placed within the div identified by the distance id.
function postResponse(transport)
{
  if (transport.responseText != false)
  {
    document.getElementById("distance").innerHTML = "The distance is: " + transport.responseText + " miles.";
  } else {
    document.getElementById("distance").innerHTML = "Distance could not be calculated.";
  }
}  

In the calculateDistance() function you see the Ajax.Request() method is calling a script identified by distance.php. This script is displayed next:

<?php
  // Include the GoogleMapAPI library
  include("GoogleMapAPI.class.php");
  // Instantiate the GoogleMapAPI class, and set the API key
  $this->map = new GoogleMapAPI('map');
  $this->map->setAPIKey("YOUR_KEY_GOES_HERE");
  // Retrieve the $coords variable which has been passed through the GET method,
  // and turn the data back into an array
  $coordinates = explode("|", $_GET['coords']);
  // Parse the array to retrieve each set of coordinates
  $coord1 = explode(",", $coordinates[0]);
  $coord2 = explode(",", $coordinates[1]);
  // Calculate the distance
  $distance = $this->map->geoGetDistance($coord1[0], $coord1[1], $coord2[0], $coord2[1]);
  // Return the calculated distance. Note we are not using return! The data must be echoed.
  echo $distance;
?>

A screenshot of the completed solution in-action is shown in Figure 2. You can interact with the completed solution at http://mapping.wjgilmore.com/.


Figure 2. Determining the distance between two points

Measuring a Running Route

As an avid cyclist, one of my favorite web sites is Walk Jog Run, a great service that helps you to plan your running routes according to both distance and speed. I use this web site to plot the distance of not only short treks around the block, but also weekend rides which could average as many as 50 miles. Walk Jog Run is particularly friendly because it allows you to interact with the map directly by using the mouse to plot your route by using mouse-clicks to create the course. With each click both the segment and total route distance are calculated, painting a detailed picture of what this particular course will involve.

How might you go about modifying the distance.php script to calculate a route consisting of multiple segments? Other than lifting the restriction regarding the number of allowable markers in the calculateDistance() JavaScript function, the only changes you need to make all reside within the PHP script. Some additional logic will allow the distance of each segment to be calculated, and will tally those segments up to determine the total distance. The modified PHP script follows:

<?php
  // Include the GoogleMapAPI library
  include("GoogleMapAPI.class.php");
  $this->map = new GoogleMapAPI('map');
  $this->map->setAPIKey($this->config->google->params->map_key);
  // Revert the passed parameter back to an array
  $coordinates = explode("|", $this->_request->getParam('coords'));
  // Initialize the distance
  $distance = 0.0;
  // Initialize the coordinate placeholder
  $prevCoordinate = "";
  // Cycle through the $coordinates array, calculating the distance
  // between each segment
  foreach ($coordinates AS $coordinate)
  {
    // Are we on first array element?
    if ($coordinates[0] == $coordinate) {
     // Convert lat/long into array
        $prevCoordinate = $coordinate;           
    } else {
       $coord1 = explode(",", $coordinate);
       $coord2 = explode(",", $prevCoordinate);
       $distance += $this->map->geoGetDistance($coord1[0], $coord1[1], $coord2[0], $coord2[1]);
    }
  }
  echo $distance;
?>

Once the new script is in place, you can perform distance calculations such as that shown in Figure 3.


Figure 3. Taking a stroll around Goodale park

Conclusion

Thus concludes the latest installment of this occasional series. If you wind up doing anything interesting with this concept, I'd love to hear about it! Be sure to e-mail me at wjATwjgilmore.com with your comments.

About the Author

Jason Gilmore is founder of W.J. Gilmore, LLC, a publishing and consulting firm based out of Columbus, Ohio. Formerly Apress' open source editor, Jason fostered the development of more than 60 books, along the way helping to transform their open source line into one of the industry’s most respected publishing programs. He’s the author of several books, including the best-selling Beginning PHP and MySQL: From Novice to Professional (currently in its third edition), Beginning PHP and PostgreSQL: From Novice to Professional, and Beginning PHP and Oracle: From Novice to Professional.

Jason is cofounder of CodeMash, a nonprofit organization tasked with hosting an annual namesake developer’s conference.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date