by Jon Stoneman

A key feature of the Addison Lee Windows Phone application is to be able to view the route of you minicab journeys on an interactive map. As well as allowing the user to examine their journey, it provides real time indication of the minicab position, both while on route to collect the customer and throughout the journey. While this is a powerful feature, Windows Phone made the development extremely straightforward and I’m going to take you through the basic implementation in this article.

Begin by opening an existing project or creating a new Windows Phone Application in Visual Studio.

clip_image002

You then need to add an instance of a map control to a page.  This can be done in the designer by dragging the control from the toolbox but if you prefer to do things by hand, carry out the following steps:

  1. You must add a reference to Microsoft.Phone.Controls.Maps within the project. This is easily found by searching for “maps” in the add reference dialog provided by the awesome Productivity Power Tools. If you don’t have that extension installed – get it here. However… the Productivity Power Tools are only compatible with Visual Studio Professional and above, so they won’t work with Visual Studio Express for Windows Phone. Sorry!

     

  2. Having added the reference, you need to add the following to the PhoneApplicationPage declaration of any page you wish to use the Map control on:

    xmlns:maps="clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps"

     

  3. Add the following XAML in the appropriate location on the page:

           <maps:Map x:Name="JourneyMap" />

While simply adding the control is fine for prototyping, it will display a warning indicating that the control has not specified valid credentials.

clip_image003

To get rid of this warning you must create a bing maps key at https://www.bingmapsportal.com/ and then set the CredentialsProvider attribute of the Map control to that key. Don’t skip this step as we shall be using the key when calculating the route later.

We now require some data to populate the Map control with our journey details. For this example, we shall use a List of GeoCoordinates representing the start, a stop and the end of our journey. Add a project reference to System.Device and then add the following code within the OnNavigatedTo event of the page containing the Map control:

var stops = new List<GeoCoordinate>
{    
    new GeoCoordinate(51.503206, -0.12704),    
    new GeoCoordinate(51.500111, -0.14125),    
    new GeoCoordinate(51.501557, -0.17753)
};
 

These coordinates represent a typical journey starting at our home, 10 Downing Street, stopping off to pick up friends at Buckingham Palace and then getting dropped off at the Albert Hall… as you do.

We want the map control to render showing each of these stops but also the route between them. Start by adding two MapLayers to the Map control. These will hold the pins marking the stops and the route between the stops. It’s best to use two MapLayer objects so you can easily control which objects are rendered on top of other objects. We want our pins to be shown on top of our route so the route layer is added first.

_routeLayer = new MapLayer();
JourneyMap.Children.Add(_routeLayer);
 
var stopsLayer = new MapLayer();
JourneyMap.Children.Add(stopsLayer);
 

Note that _routeLayer is not defined here. This is because we’ll need to access it from another method later. Add the following member to the PhoneApplicationPage class to define the _routeLayer object:

MapLayer _routeLayer;
 

Next, we’ll add the pins indicating our journey’s stops to the stopsLayer MapLayer, setting their content to the number of the stop within our journey.

foreach (var stop in stops)
{    
   var pin = new Pushpin();    
   pin.Location = stop;     

 // While the pin contains only text (the stop number) we use a TextBlock to 
   // give us control over the formatting. 
   var pinContent = new TextBlock();    
   pinContent.Text = (stops.IndexOf(stop) + 1).ToString();    
   pinContent.Foreground = new SolidColorBrush(Color.FromArgb(255, 0, 191, 243));     

   pin.Content = pinContent;    
   stopsLayer.Children.Add(pin);
}

The zoom and position of the Map control should then be set so that all the pins are visible within the viewport. The Map control actually contains a method that does just that for us:

JourneyMap.SetView(LocationRect.CreateLocationRect(stops));
 

If you run the application at this point, you’ll see the control is getting initialised exactly as we need, except that the route is not being shown.

clip_image004

Routes are drawn using MapPolyLine objects which take a collection of GeoCoordinates indicating the points of the route. We can use the Bing Maps Route Service to calculate the route between our stops and provide the points of that route for use with the MapPolyLine. To use the Route Service, add a service reference to the RouteService service at http://dev.virtualearth.net/webservices/v1/routeservice/routeservice.svc setting the namespace to “RouteService”.

clip_image005

Add the following code to create the RouteRequest that we shall be submitting to the Route Service in order to obtain our route. Note that we are creating the Credentials required by the Route Service from the CredentialsProvider property of the Map control that we set earlier.

var routeRequest = new RouteService.RouteRequest();
routeRequest.Credentials = new Credentials {     
    ApplicationId = ((ApplicationIdCredentialsProvider)JourneyMap.CredentialsProvider).ApplicationId        
       };
// By default, the RouteRequest will not fetch the points we require to plot // the route so we must specify this in the options. routeRequest.Options = new RouteService.RouteOptions(); routeRequest.Options.RoutePathType = RouteService.RoutePathType.Points; // The stops on are journey are specified in the Waypoints property of the // RouteRequest routeRequest.Waypoints = new ObservableCollection<RouteService.Waypoint>(); foreach (var stop in stops) { routeRequest.Waypoints.Add( new RouteService.Waypoint { Location = new Microsoft.Phone.Controls.Maps.Platform.Location { Latitude = stop.Latitude, Longitude = stop.Longitude } }); }
 

We can now create a client for the Route Service and submit our RouteRequest. As this is an asynchronous method we must set the method that will handle the CalculatedMethodCompleted event.

var routeService = new RouteService.RouteServiceClient("BasicHttpBinding_IRouteService");
routeService.CalculateRouteCompleted += routeService_CalculateRouteCompleted;

routeService.CalculateRouteAsync(routeRequest);
 

The CalculatedMethodCompleted handler should create a MapPolyLine and populate its Location property using the points returned by the Route Service. In the Addison Lee application, we also modified the style of the route for consistency with the rest of the application.

private void routeService_CalculateRouteCompleted(object sender, RouteService.CalculateRouteCompletedEventArgs e)
{    
      // Check that the request was successful 
      if ((e.Result.ResponseSummary.StatusCode == RouteService.ResponseStatusCode.Success) && (e.Result.Result.Legs.Count != 0))
      {                        
          MapPolyline routeLine = new MapPolyline(); 

          routeLine.Locations = new LocationCollection();
          foreach (var point in e.Result.Result.RoutePath.Points) 
          {            
                routeLine.Locations.Add(new GeoCoordinate(point.Latitude, point.Longitude));       
          }                    

          SolidColorBrush routeBrush = new SolidColorBrush(Color.FromArgb(255, 0, 191, 243));                  
routeLine.Stroke = routeBrush; routeLine.StrokeThickness = 5.0; // Add the MapPolyLine to the route MapLayer to render it. _routeLayer.Children.Add(routeLine); } }
 

With that, our journey map is complete.

clip_image007

At the start of this article, I mentioned that the Addison Lee application provided real time indication of the minicab’s position. While I won’t detail this here, the implementation involves a DispatcherTimer object whose Tick event handler obtains the minicab’s location from Addison Lee’s systems and updates the Location property of the minicab’s PushPin object.

Resources:

Get the Productivity Power Tools

Author Bio

Jon StonemanJon Stoneman
CTO, Sequence