Finding Route Between Two Locations in Windows Phone 7 Using Bing Maps


Hi guys, in the last article you learned how to locate a point in the Bing Map and then add a customized pushpin to this location. Taking a leap further here in this article you'll know how to plot two points on the map and calculate the route between them. This article would clear all the queries regarding Bing Map's route problems. Though it may seem too messy with code but going through each line clearly illustrates the core concepts used and why and how to be used while working with Windows Phone 7 using Bing Maps.
Follow the steps below.

Step 1 : First of all you need to know all the basic requirements for running a Windows Phone 7 application in your machine and for this refer to the previous articles. Also you must know that this project is made in Windows Phone 7.1 version so mind any discrepancies arising due to version mismatch.

Step 2 : Then open Visual Studio

  • Select new project
  • Select your preferred language
  • Select the Silverlight for Windows Phone application on the left pane and Windows Phone application in right pane.
  • Name the project
new projct.gif
 

Step 3 : Now you land up in a screen showing the Windows Phone application and XAML coding into different partition. Now you may change the look of your application as you wish.

design view.gif
 

Step 4 : Now drag and drop the map control from the toolbox into the phone application.

  • Add a textbox and in the properties change the name of this textbox as "fromtxtbx". This text box holds the value of the starting location.
  • Add another textbox and in the properties change the name of this textbox as "totxtbx". This text box holds the value of the final destination location.
  • Add a button, name it "Change view". This is to change the mode of view of the map.
  • Add another button and name it "Route" to find the address on the Bing Map.

Step 5 : Now before we start the coding ahead you need to get the credentials for using the Bing map in Windows Phone application. To know how to get the credentials see this article . If you don't get the credentials then an irritating white strip keeps appearing over the map urging you to get the credentials for using the map control.

Step 6 : When you have the Bing map key, get the credential key and put it here in the XAML part and find the following:

<my:Map Height="482" HorizontalAlignment="Left" Margin="6,6,0,0" Name="map1" VerticalAlignment="Top" Width="444" CredentialsProvider= "type the bing map key you got here"/>

Step 7 : Now add the following references:

  • Go to Solution Explorer

  • Also add a service reference--> Right click on reference

  • Add service reference

SERVICES.gif 

  • In the address bar type- http://dev.virtualearth.net/webservices/v1/routeservice/routeservice.svc/mex. This is to use the Route related classes in the application. Name this service reference as routeservice.

  • Add another service reference http://dev.virtualearth.net/webservices/v1/geocodeservice/geocodeservice.svc/mex. This is to use the Geocode related classes in the application. Name this service reference as geocodeservice.

Step 8 : Add the following namespaces to the project:

  • using System;

  • using System.Windows;

  • using System.Windows.Media;

  • using Microsoft.Phone.Controls;

  • using Microsoft.Phone.Controls.Maps;

  • using Microsoft.Phone.Controls.Maps.Platform;

  • using System.Device.Location;

  • using System.Windows.Shapes;

  • using findroute.geocodeservice;

  • using System.Windows.Media.Imaging;

Step 9 : Now here we start first of all globally declare a Location class object so that it can be used throughout the code.

Location location = new Location();  // Global declaration

        // Constructor

        public MainPage()

        {

            InitializeComponent();

            map1.LogoVisibility = Visibility.Collapsed;

            map1.CopyrightVisibility = Visibility.Collapsed;

            map1.Mode = new AerialMode();

        }

Step 10 : Now define a void type method named "Geocode". This method accepts a geocode query string as well as a "waypoint index", which will be used to track each asynchronous geocode request.

private void Geocode(string strAddress, int waypointIndex)

        {

            // Here we create the service variable and set the callback method using the GeocodeCompleted property.

            findroute.geocodeservice.GeocodeServiceClient geocodeService = new findroute.geocodeservice.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");

            geocodeService.GeocodeCompleted += new EventHandler<findroute.geocodeservice.GeocodeCompletedEventArgs>(geocodeService_GeocodeCompleted);

           // Here we Set the credentials and the geocode query,which could be an address or location.

            findroute.geocodeservice.GeocodeRequest geocodeRequest = new findroute.geocodeservice.GeocodeRequest();

            geocodeRequest.Credentials = new Credentials();

            geocodeRequest.Credentials.ApplicationId = ((ApplicationIdCredentialsProvider)map1.CredentialsProvider).ApplicationId;

            geocodeRequest.Query = strAddress;

          // Now Making the asynchronous Geocode request, using the 'waypoint index' as

            //   the user state to track this request and allow it to be identified when the response is returned.

            geocodeService.GeocodeAsync(geocodeRequest, waypointIndex);

        }


Step 11 : Now in order to store the result in to a variable which could be used later to calculate route between two points and this variable is declared as of internal type.

internal geocodeservice.GeocodeResult[] geocodeResults; 

Step 12 : Now we define another Private type method to act as a callback method to calculate the route.

private void geocodeService_GeocodeCompleted(object sender, geocodeservice.GeocodeCompletedEventArgs e)

        {

            // Retrieve the user state of this response (the 'waypoint index') to identify which geocode request it corresponds to

             int waypointIndex = System.Convert.ToInt32(e.UserState);

            // Retrieve the GeocodeResult for this response and store it in the global variable geocodeResults, using

            //   the waypoint index to position it in the array.

            geocodeResults[waypointIndex] = e.Result.Results[0];

            // Look at each element in the global gecodeResults array to figure out if more geocode responses still

            //   need to be returned.

           bool doneGeocoding = true;

           foreach (geocodeservice.GeocodeResult gr in geocodeResults)

            {

                if (gr == null)

                {

                    doneGeocoding = false;

                }

            }

             // If the geocodeResults array is totally filled, then calculate the route.

            if (doneGeocoding)

                CalculateRoute(geocodeResults);

        }

Step 13 : Now we define another private type method to request for the geocode of the locations provided by the user i.e "From" and "To" point locations in the map:

private void calculate_Click(object sender, RoutedEventArgs e)

        {

            //Initialize the length of the results array. In this sample we have two waypoints.

            geocodeResults = new geocodeservice.GeocodeResult[2];

           // Make the two Geocode requests using the values of the text boxes. Also pass the waypoint indexes

            //   of these two values within the route.

            Geocode(fromtxtbx.Text, 0);

            Geocode(totxtbx.Text, 1);

        }

Step 14 : Now we have to calculate the route between the two points. In this method we first create the service variable and set the callback method using the CalculateRouteCompleted property, then set a token to validate the credentiality of the map, so here again you have to enter the credential key of the map.

private void CalculateRoute(geocodeservice.GeocodeResult[] results)

        {
            routeservice.RouteServiceClient routeService = new routeservice.RouteServiceClient("BasicHttpBinding_IRouteService");                       
            routeService.CalculateRouteCompleted += new EventHandler<routeservice.CalculateRouteCompletedEventArgs>(routeService_CalculateRouteCompleted);         
// Set the token.
            routeservice.RouteRequest routeRequest = new routeservice.RouteRequest();
            routeRequest.Credentials = new Credentials();
            routeRequest.Credentials.ApplicationId = "ENTER THE CREDENTIAL KEY HERE";
  
     // Return the route points so the route can be drawn.
            routeRequest.Options = new routeservice.RouteOptions();
            routeRequest.Options.RoutePathType = routeservice.RoutePathType.Points;
     
   // Set the waypoints of the route to be calculated using the Geocode Service results stored in the geocodeResults variable.
            routeRequest.Waypoints = new System.Collections.ObjectModel.ObservableCollection<routeservice.Waypoint>();
            foreach (geocodeservice.GeocodeResult result in results)
            {
                routeRequest.Waypoints.Add(GeocodeResultToWaypoint(result));
            }
     
// Make the CalculateRoute asnychronous request.
          routeService.CalculateRouteAsync(routeRequest);

        }

Step 15 : Now here comes the interesting part where we know the way points between the two locations and then using this data we mark route between these points. Look at the code below.

private routeservice.Waypoint GeocodeResultToWaypoint(geocodeservice.GeocodeResult result)

        {

            routeservice.Waypoint waypoint = new routeservice.Waypoint();
            waypoint.Description = result.DisplayName;
            waypoint.Location = new Location();
            waypoint.Location.Latitude = result.Locations[0].Latitude;
            waypoint.Location.Longitude = result.Locations[0].Longitude;
            return waypoint;

        }


Step 16 : Now this is a very important part of the whole project where we locate the points, use the calculated the distance between these points, the waypoints between these points and then finally draw a polyline between these points to denote the route from start position to the end position. Here we use the map layering in which to draw the route and then add the route line to the new layer. We also use a rectangle which encompasses the route. This is used later to set the map view. Through this rectangle we calculate all the possible geocode positions of the points i.e.  east, west, north, south, centre. Then for each geocode result (which are the waypoints of the route), draw a dot on the map using ellipse property. Finally set the map view using the rectangle which bounds the rendered route. Also set the center and zoomlevel of the location on the map.


private void routeService_CalculateRouteCompleted(object sender, routeservice.CalculateRouteCompletedEventArgs e)

        {
           
// If the route calculate was a success and contains a route, then draw the route on the map.
            if ((e.Result.ResponseSummary.StatusCode == routeservice.ResponseStatusCode.Success) & (e.Result.Result.Legs.Count != 0))
            {
               
// Set properties of the route line you want to draw.
                Color routeColor = Colors.Blue;
                SolidColorBrush routeBrush = new SolidColorBrush(routeColor);
                MapPolyline routeLine = new MapPolyline();
                routeLine.Locations = new LocationCollection();
                routeLine.Stroke = routeBrush;
                routeLine.Opacity = 0.50;
                routeLine.StrokeThickness = 5.0;
              
// Retrieve the route points that define the shape of the route.
                foreach (Location p in e.Result.Result.RoutePath.Points)
                {
                    routeLine.Locations.Add(new Location { Latitude = p.Latitude, Longitude = p.Longitude });

                }

               // Add a map layer in which to draw the route.

                MapLayer myRouteLayer = new MapLayer();
                map1.Children.Add(myRouteLayer);
           
// Add the route line to the new layer.
                myRouteLayer.Children.Add(routeLine);
           
// Figure the rectangle which encompasses the route. This is used later to set the map view.
                double centerlatitude = (routeLine.Locations[0].Latitude + routeLine.Locations[routeLine.Locations.Count - 1].Latitude) / 2;
                double centerlongitude = (routeLine.Locations[0].Longitude + routeLine.Locations[routeLine.Locations.Count - 1].Longitude) / 2;
                Location centerloc = new Location();
                centerloc.Latitude = centerlatitude;
                centerloc.Longitude = centerlongitude;
                double north, south, east, west;

            
if ((routeLine.Locations[0].Latitude > 0) && (routeLine.Locations[routeLine.Locations.Count - 1].Latitude > 0))
                {

                    north = routeLine.Locations[0].Latitude > routeLine.Locations[routeLine.Locations.Count - 1].Latitude ? routeLine.Locations[0].Latitude : routeLine.Locations[routeLine.Locations.Count - 1].Latitude;

                    south = routeLine.Locations[0].Latitude < routeLine.Locations[routeLine.Locations.Count - 1].Latitude ? routeLine.Locations[0].Latitude : routeLine.Locations[routeLine.Locations.Count - 1].Latitude;

                }

                else

                {

                    north = routeLine.Locations[0].Latitude < routeLine.Locations[routeLine.Locations.Count - 1].Latitude ? routeLine.Locations[0].Latitude : routeLine.Locations[routeLine.Locations.Count - 1].Latitude;

                    south = routeLine.Locations[0].Latitude > routeLine.Locations[routeLine.Locations.Count - 1].Latitude ? routeLine.Locations[0].Latitude : routeLine.Locations[routeLine.Locations.Count - 1].Latitude;

             }

                if ((routeLine.Locations[0].Longitude < 0) && (routeLine.Locations[routeLine.Locations.Count - 1].Longitude < 0))

                {

                    west = routeLine.Locations[0].Longitude < routeLine.Locations[routeLine.Locations.Count - 1].Longitude ? routeLine.Locations[0].Longitude : routeLine.Locations[routeLine.Locations.Count - 1].Longitude;

                    east = routeLine.Locations[0].Longitude > routeLine.Locations[routeLine.Locations.Count - 1].Longitude ? routeLine.Locations[0].Longitude : routeLine.Locations[routeLine.Locations.Count - 1].Longitude;

                }

                else

                {

                    west = routeLine.Locations[0].Longitude > routeLine.Locations[routeLine.Locations.Count - 1].Longitude ? routeLine.Locations[0].Longitude : routeLine.Locations[routeLine.Locations.Count - 1].Longitude;

                    east = routeLine.Locations[0].Longitude < routeLine.Locations[routeLine.Locations.Count - 1].Longitude ? routeLine.Locations[0].Longitude : routeLine.Locations[routeLine.Locations.Count - 1].Longitude;

                }

                // For each geocode result (which are the waypoints of the route), draw a dot on the map.

                foreach (geocodeservice.GeocodeResult gr in geocodeResults)

                {

                    Ellipse point = new Ellipse();

                    point.Width = 10;

                    point.Height = 10;

                    point.Fill = new SolidColorBrush(Colors.Red);

                    point.Opacity = 0.65;

                    location.Latitude = gr.Locations[0].Latitude;

                    location.Longitude = gr.Locations[0].Longitude;

                    MapLayer.SetPosition(point, location);

                    MapLayer.SetPositionOrigin(point, PositionOrigin.Center);

                   // Add the drawn point to the route layer.                   

                    myRouteLayer.Children.Add(point);

                }

                // Set the map view using the rectangle which bounds the rendered route.

                double latitude = 0.0;

                double longtitude = 0.0;

                map1.SetView(location, 12);

                map1.Center = location;

                GeoCoordinate CurrentLocCoordinate = new System.Device.Location.GeoCoordinate(latitude, longtitude);

            }

        }

Step 17 : To manually change the zoom level of the map we add two buttons for serving this purpose.


private
void button2_Click(object sender, RoutedEventArgs e)

        {

            double zoom;

            zoom = map1.ZoomLevel;

            map1.ZoomLevel = ++zoom;

        }

 private void button3_Click(object sender, RoutedEventArgs e)

        {

            double zoom;

            zoom = map1.ZoomLevel;

            map1.ZoomLevel = --zoom;

        }


Step 18 :
Add a button named as "view" having the content "Change view" to change the view of the map from Aerial to Road mode. On the click event of this button write the following code.

private void view_Click(object sender, RoutedEventArgs e)

        {

            if (map1.Mode is RoadMode)

            {

                map1.Mode = new AerialMode(true);

           }

            else

            {

                map1.Mode = new RoadMode();

            }

        }

Step 19 : Now debug the project and see the following output. Initially the view of the map is Aerial Mode.

 aerial view.gif


Now changing the view of the map to Road Mode.


road mode.gif


Now enter the locations between which the route is found.


enter location.gif


The route in Road mode view is.


route road view.gif


The route in Road mode view is.


route aerial.gif


Hope you would have enjoyed working this project. Do provide your feedback.

 


Similar Articles