In the Windows Phone 8 SDK, two classes are fundamental in calculating a route: The GeocodeQuery
class allows you to convert a street address to a GeoCoordinate
, and the RouteQuery
class finds the shortest path between two GeoCoordinates
, with any number of way points in between.
The GeocodeQuery
and RouteQuery
class are located in the namespace Microsoft.Phone.Maps.Services.GeocodeQuery
.
In the sample for this section, a custom class named RouteCalculator
is used to calculate the route between two street addresses. The RouteCalculator
class has a single method named CalculateAsync
(see Listing 18.4).
The GeocodeQuery
class has two required properties: SearchTerm
and GeoCoordinate
. SearchTerm
allows you to specify the street address or geographic feature you would like to locate, and the GeoCoordinate
property produces results closest to that coordinate.
CalculateAsync
performs two Geocode Queries for its fromAddress
and toAddress
parameters.
CalculateAsync
is designed to be consumed asynchronously and returns an awaitable Task
object. Notice that the async keyword is not used in this case. For this method we take full control of the asynchrony by leveraging a TaskCompletionSource
object. After each of the three asynchronous operations complete, if an exception was raised during the operation, or the operation was canceled, the TaskCompletionSource
object is modified. This passes the error or cancellation back to the caller.
After the two geocode queries complete, a RouteQuery
is initialized using the GeoCoordinates
representing the fromAddress
and the toAddresses
parameters. If a route is returned, the result is assigned to the TaskCompletionSource
.
Caution
At the time of writing, if you attempt to use the GeocodeQuery
class to perform two queries at the same time, the QueryCompletedEventArgs
is not correctly populated. This occurs even when using two different instances of the GeocodeQuery
class. This prevents you from using Rx, for example, to coordinate or ‘Zip’ more than one geocode operation at a time.
public Task<RouteCalculationResult> CalculateAsync(
string fromAddress, string toAddress, GeoCoordinate hintCoordinate)
{
ArgumentValidator.AssertNotNullOrEmpty(fromAddress, "fromAddress");
ArgumentValidator.AssertNotNullOrEmpty(toAddress, "toAddress");
ArgumentValidator.AssertNotNull(hintCoordinate, "hintCoordinate");
var coordinates = new List<GeoCoordinate>();
GeocodeQuery geocodeQuery = new GeocodeQuery
{
SearchTerm = fromAddress,
GeoCoordinate = hintCoordinate
};
var source = new TaskCompletionSource<RouteCalculationResult>();
geocodeQuery.QueryCompleted += delegate(
object sender, QueryCompletedEventArgs<IList<MapLocation>> args)
{
if (args.Cancelled)
{
geocodeQuery.Dispose();
source.SetCanceled();
return;
}
if (args.Error != null)
{
geocodeQuery.Dispose();
source.SetException(args.Error);
return;
}
IList<MapLocation> mapLocations = args.Result;
if (mapLocations == null)
{
geocodeQuery.Dispose();
source.SetResult(null);
return;
}
coordinates.Add(args.Result.First().GeoCoordinate);
if (coordinates.Count < 2)
{
geocodeQuery.SearchTerm = toAddress;
geocodeQuery.QueryAsync();
return;
}
geocodeQuery.Dispose();
var routeQuery = new RouteQuery
{
Waypoints = new List<GeoCoordinate>
{
coordinates[0],
coordinates[1]
}
};
routeQuery.QueryCompleted += delegate(
object s, QueryCompletedEventArgs<Route> e)
{
RouteCalculationResult result;
if (e.Error == null)
{
result = new RouteCalculationResult(e.Result);
}
else
{
var error = new RouteCalculationError(
e.Error.Message, e.Error);
result = new RouteCalculationResult(null, error);
}
routeQuery.Dispose();
source.SetResult(result);
};
routeQuery.QueryAsync();
};
geocodeQuery.QueryAsync();
return source.Task;
}
3.144.39.144