ALERT! A better, more complete version of this code can be found here: http://www.sagecraft-studios.com/2008/11/google-driving-directions-in-c---update.html
This is something I've been wanting to figure out for a while. What I'll be demonstrating in this post is how to get driving directions, distances, and trip durations from Google's mapping service... without using Javascript! The demonstration code below is just that - a demonstration. It is your job, dear reader, to make a friendly, accessible interface out of the underlying code.
Note: As far as I can tell, this is undocumented. If you're using this in a production environment, I would advise writing some unit tests in case URLs change, parameters change, etc...
You will need JSON.NET referenced to deserialize the data sent from Google.
using Newtonsoft.Json;
namespace xxxx
{
public class GDirections
{
private string _key = "xxxx";
private string _output = "js";
private string _requestFormat = "http://maps.google.com/maps/nav?key={0}&output={1}&q={2}";
private Hashtable _data = new Hashtable();
Our underlying data. Yes, you could certainly use a generic Dictionary rather than a Hashtable. In the code above, make sure to set _key to your Google Maps API key, which can be generated here.
/// <param name="path">A path in the format "From {a} to {b}". Example: "From Lexington, KY to Bowling Green, KY"</param>
public GDirections(string path)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(
string.Format(_requestFormat, _key, _output, Uri.EscapeUriString(path)));
request.Method = "GET";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string json = reader.ReadToEnd();
_data = readJson(new JsonReader(new StringReader(json)));
}
In our constructor, we basically just grab the response from a web page. (or in this case, JavaScript object) Using Json.NET, we deserialize the object and somehow stick it in a Hashtable.
private Hashtable readJson(JsonReader jreader)
{
Hashtable data = new Hashtable();
while (jreader.Read() && jreader.TokenType != JsonToken.EndObject)
{
string key = null;
object value = null;
if (jreader.TokenType == JsonToken.PropertyName)
{
key = (string)jreader.Value;
jreader.Read();
switch (jreader.TokenType)
{
case JsonToken.StartArray:
value = readJsonArray(jreader);
break;
case JsonToken.StartObject:
value = readJson(jreader);
break;
case JsonToken.Null:
case JsonToken.Undefined:
value = null;
break;
default:
value = jreader.Value;
break;
}
}
if (key != null)
data.Add(key, value);
}
return data;
}
private ArrayList readJsonArray(JsonReader jreader)
{
ArrayList value = new ArrayList();
while (jreader.Read() && jreader.TokenType != JsonToken.EndArray)
{
switch (jreader.TokenType)
{
case JsonToken.StartArray:
value.Add(readJsonArray(jreader));
break;
case JsonToken.StartObject:
value.Add(readJson(jreader));
break;
case JsonToken.Null:
case JsonToken.Undefined:
value.Add(null);
break;
default:
value.Add(jreader.Value);
break;
}
}
return value;
}
}
}
This is the "somehow stick it in a Hashtable" (or Dictionary
IDEALLY, we would want to create classes for each kind of object we get in our response. For example, Placemark, Status, Point, and Direction classes would need to be created. If even this is too much work, (which it shouldn't be if you are serious about implementing and fully using this functionality!) some more friendly accessor methods/properties shouldn't be a huge chore to do. For example, since we're using this thing just for driving time and duration, I implemented the following accessors
public decimal DrivingMeters
{
get { return Convert.ToDecimal(((Hashtable)Directions["Distance"])["meters"]); }
}
public TimeSpan DrivingTime
{
get { return new TimeSpan(0, 0, Convert.ToInt32(((Hashtable)Directions["Duration"])["seconds"])); }
}
public decimal DrivingMiles
{
get { return Math.Round(DrivingMeters * (decimal)0.000621371192, 2); } // according to Google.
}
private Hashtable Directions
{
get { return (Hashtable)_data["Directions"]; }
}
So yes, I'm basically saying take this foundation and make it your own. Make it friendly and then give it back to me. :)
