Using GoogleMaps Directions API to get accurate distance
Example of using GoogleMaps Directions API for calculating the distance
In situations where distance between two geo coordinates needs to be found most of the people are using mathematical function to do this. However there is a major disadvantage in using this method.
using System; private double distance(double lat1, double lon1, double lat2, double lon2, char unit) { double theta = lon1 - lon2; double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta)); dist = Math.Acos(dist); dist = rad2deg(dist); dist = dist * 60 * 1.1515; if (unit == 'K') { dist = dist * 1.609344; } else if (unit == 'N') { dist = dist * 0.8684; } return (dist); } private double deg2rad(double deg) { return (deg * Math.PI / 180.0); } private double rad2deg(double rad) { return (rad / Math.PI * 180.0); } Console.WriteLine(distance(32.9697, -96.80322, 29.46786, -98.53506, "M")); Console.WriteLine(distance(32.9697, -96.80322, 29.46786, -98.53506, "K")); Console.WriteLine(distance(32.9697, -96.80322, 29.46786, -98.53506, "N"));
By using mathematical function you are getting straight line distance between two locations. This means you are getting the shortest distance which would be exact only in cases you are flying to a place A to place B. Most likely that is not the case if you are trying to find the nearest location in a range of lat's say 100km.
Much better and accurate way is to use Google Maps Directions API. This will give you the distance between locations based on the road and type of traveling you are going to use. Beside the distance you will get a lot of useful data along with distance, pretty much all the info you get when you are calculating the distance direly on GoogleMaps website.
When you get your JSON result, the most elegant way is to combine it to an object model build in C#. To cut the work for this I usually use http://json2csharp.com/ service.
Google Directions API also supports XML output, but as it is a bit obsolete we will not deal handling of it in this article
With this service you will get the following set of classes in C sharp (some small revokes like renaming might be required after service processes JSON). You can use the following code out of the box as I already generated it and created object model.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace GoogleMaps.Directions.Model { public class Northeast { public double lat { get; set; } public double lng { get; set; } } public class Southwest { public double lat { get; set; } public double lng { get; set; } } public class Bounds { public Northeast northeast { get; set; } public Southwest southwest { get; set; } } public class Distance { public string text { get; set; } public int value { get; set; } } public class Duration { public string text { get; set; } public int value { get; set; } } public class EndLocation { public double lat { get; set; } public double lng { get; set; } } public class StartLocation { public double lat { get; set; } public double lng { get; set; } } public class Distance2 { public string text { get; set; } public int value { get; set; } } public class Duration2 { public string text { get; set; } public int value { get; set; } } public class EndLocation2 { public double lat { get; set; } public double lng { get; set; } } public class Polyline { public string points { get; set; } } public class StartLocation2 { public double lat { get; set; } public double lng { get; set; } } public class Step { public Distance2 distance { get; set; } public Duration2 duration { get; set; } public EndLocation2 end_location { get; set; } public string html_instructions { get; set; } public Polyline polyline { get; set; } public StartLocation2 start_location { get; set; } public string travel_mode { get; set; } public string maneuver { get; set; } } public class Leg { public Distance distance { get; set; } public Duration duration { get; set; } public string end_address { get; set; } public EndLocation end_location { get; set; } public string start_address { get; set; } public StartLocation start_location { get; set; } public List<Step> steps { get; set; } public List<object> via_waypoint { get; set; } } public class OverviewPolyline { public string points { get; set; } } public class Route { public Bounds bounds { get; set; } public string copyrights { get; set; } public List<Leg> legs { get; set; } public OverviewPolyline overview_polyline { get; set; } public string summary { get; set; } public List<object> warnings { get; set; } public List<object> waypoint_order { get; set; } } public class RootObject { public List<Route> routes { get; set; } public string status { get; set; } } }
However this approach as much as it gives accurate results has some disadvantages.
- Your solution depends on Google product
- No absolutely free
Acoording to Goolge these are limitations for free use of Route API:
- 2,500 directions requests per 24 hour period
- Up to 8 waypoints allowed in each request. Waypoints are not available for transit directions
- 10 requests per second.
More details about the pricing you can find at https://developers.google.com/maps/documentation/directions/#Limits
The most elegant solution to the disadvantages for both mathematic function and Google Directions API would be a hybrid solution which combines both approaches with logic that if code fails to get directions from Google, it uses mathematic function.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.IO; using GoogleMaps.Directions.Model; namespace GoogleMaps.Directions { public static class DirectionsProxy { private static double GetDistance(double startLatitude, double endLatitude, double startLongitude, double endLongitude) { try { string apiUrl = "http://maps.googleapis.com/maps/api/directions/json?origin={0},{1}&destination={2},{3}&sensor=false"; apiUrl = string.Format(apiUrl, startLatitude, startLongitude, endLatitude.ToString(), endLongitude.ToString() ); WebRequest request = HttpWebRequest.Create(apiUrl); WebResponse response = request.GetResponse(); StreamReader reader = new StreamReader(response.GetResponseStream()); System.Web.Script.Serialization.JavaScriptSerializer parser = new System.Web.Script.Serialization.JavaScriptSerializer(); string responseStringData = reader.ReadToEnd(); RootObject responseData = parser.Deserialize<RootObject>(responseStringData); if (responseData != null) { double distance = responseData.routes.Sum(r => r.legs.Sum(l => l.distance.value)); if (distance == 0) { throw new Exception("Google cannot find road route"); } return distance; } else { throw new Exception("Unable to get location from google"); } } catch (Exception ex) { int R = 6371; Double dLat = (endLatitude - startLatitude).ToRadian(); Double dLon = (endLongitude - startLongitude).ToRadian(); var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(startLatitude.ToRadian()) * Math.Cos(endLatitude.ToRadian()) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2); var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); var d = R * c; return d * 1000; } } public static Double ToRadian(this Double number) { return (number * Math.PI / 180); } } }
This way you would be able to always provide the data to a client even if Google service is not responding.
References
Disclaimer
Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.
Comments for this article