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
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.
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:
-
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.
Now changing the view of the map to Road Mode.
Now enter the locations between which the route is found.
The route in Road mode view is.
The route in Road mode view is.
Hope you would have enjoyed working this project. Do provide your feedback.