Calculating the Shortest Route Between Two Points

,

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.


LISTING 18.4. RouteCalculator.CalculateAsync Method


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;
}


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.9.12