Tutorial “Enrutamiento con Bing Maps y Silverlight”

Quién más o quien menos alguna vez a dedicado un espacio de tiempo ha localizar un local,un lugar, un punto de interés o a preparar una ruta a la hora de viajar. Por otro lado son cada vez más las empresas que utilizan la geolocalización como instrumento de trabajo, es por este motivo que planteo la segunda parte de las opciones que nos ofrece el API de Bing Maps. En este artículo vamos a utilizar por un lado la geolocalización que he especificado en artículos anteriores y además vamos aprovechar el servicio de enrutamiento que pone a disposición de los desarrolladores Microsoft. De este modo podemos realizar una ruta entre los respectivos puntos que hemos geolicalizado previamente.

Para empezar deberéis asociar una cuenta Live ID y la URL de la página en la que vamos a utilizar la API de Bing Maps. Podéis ver los pasos que tenéis que seguir para disfrutar del API de Bing Maps aqui.

Una vez realizada la asociación, vamos a crear una una nueva aplicación Silverlight desde Visual Studio 2010. Para ello elegimos la plantilla Silverlight Businees Application, elegimos el nombre que queremos para dicho proyecto así como la ubicación del mismo como podemos ver a continuación:

image

Abrimos la página que se carga por defecto en el marco de trabajo de dicha aplicación(Home.Xaml), esta se encuentra ubicada dentro de la carpeta Views del lado del cliente.

Lo primero vamos añadir el servicio de geolocalización a nuestro proyecto. Para ello presionamos con el botón derecho sobre la carpeta Refereces. Elegimos Add Service Reference. En la ventana emergente, debemos introducir en la sección Address la siguiente URL del servicio de Geolocalización (http://dev.virtualearth.net/webservices/v1/geocodeservice/GeocodeService.svc). Presionamos sobre el botón Go para comprobar que la comunicación con el servicio se realiza de forma correcta. El servicio lo identificaremos con el nombre GeocodeService que debemos introducirlo en el apartado NameSpace:

image

Presionamos en OK y ya tenemos preparado el servicio para su posterior utilización.

Seguidamente vamos añadir los credenciales de la API de Bing Maps en los recursos de aplicación y sobre el propio objeto Map.

Abrimos App.xaml, dentro de la etiqueta <ResourceDictionary>

introducimos <sys:String x:Key="MisCredenciales">Clave Cuenta Bing Maps</sys:String>

siendo el resultado final de dicho archivo el siguiente:

  1.    <Application
  2. x:Class="Rutas.App"
  3. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5. xmlns:app="clr-namespace:Rutas"
  6. xmlns:sys="clr-namespace:System;assembly=mscorlib"
  7. Startup="Application_Startup"
  8. UnhandledException="Application_UnhandledException">
  9.  
  10.     <Application.Resources>
  11.         <ResourceDictionary>
  12.             <ResourceDictionary.MergedDictionaries>
  13.                 <ResourceDictionary Source="Assets/Styles.xaml"/>
  14.                 <ResourceDictionary>
  15.                     <app:ResourceWrapper x:Key="ResourceWrapper" />
  16.                     <app:NotOperatorValueConverter x:Key="NotOperatorValueConverter" />
  17.                     <sys:String x:Key="MisCredenciales">Clave Cuenta Bing Maps</sys:String>
  18.                 </ResourceDictionary>
  19.             </ResourceDictionary.MergedDictionaries>
  20.                         </ResourceDictionary>
  21.     </Application.Resources>
  22.  
  23. </Application>

Más adelante utilizaremos dichos credenciales en el objeto Map como un recurso del proyecto.

Ahora vamos a centrarnos en la interfaz de usuario, primeramente vamos referenciar las librerías de Bing Maps( que previamente hemos añadido, puedes ver aquí donde encuentran las misma) para poder realizar las diferentes acciones de Geocodificación. Dentro de la etiqueta <Page> introducimos la referencia:

  1. xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

Seguidamente, vamos a definir la estructura de los diferentes objetos que incluiremos dentro de la interfaz de usuario de Home.xaml, para ello añadimos el siguiente fragmento de código:

  1. <Grid x:Name="LayoutRoot">
  2.         <Grid.ColumnDefinitions>
  3.             <ColumnDefinition Width="271*"/>
  4.             <ColumnDefinition Width="100*"/>
  5.         </Grid.ColumnDefinitions>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="255*"/>
  8.             <RowDefinition Height="45*"/>
  9.         </Grid.RowDefinitions>
  10.         <map:Map x:Name="mapa" Center="40.225,-1.794" ZoomLevel="6" Mode="Road" Grid.Row="0" Grid.ColumnSpan="2">
  11.             <map:Map.CredentialsProvider>
  12.                 <map:ApplicationIdCredentialsProvider ApplicationId="{StaticResource MisCredenciales}"/>
  13.             </map:Map.CredentialsProvider>
  14.         </map:Map>
  15.         <TextBox x:Name="txbPI" Grid.Row="1" Grid.Column="0" Margin="10" BorderBrush="Black" FontSize="21.333" FontFamily="Verdana"/>
  16.         <Button x:Name="btnGeocode" Content="Añadir Localizador" Grid.Row="1" Grid.Column="1" Margin="5" Click="btnGeocode_Click"/>
  17.     </Grid>

Como podéis observar hemos añadido el objeto Map que se encargará de representar el mapa donde se localizarán los diferentes puntos que el usuario busque. También hemos añadido un TextBox para que el usuario pueda introducir los lugares ha buscar y un botón que se encargará de realizar las diferentes acciones para representar los diferentes lugares en el objeto Map. Para generar el evento del botón btnGeocode, presionamos doble click cobre el mismo. De este modo nos desplazaremos al código Behin de la aplicación (Home.xaml.cs). Dentro de dicho evento debemos introducir el siguiente fragmento de código:

  1. private void btnGeocode_Click(object sender, System.Windows.RoutedEventArgs e)
  2.         {
  3.             GeocodeService.GeocodeServiceClient geocodeClient = new GeocodeService.GeocodeServiceClient
  4.  
  5. ("CustomBinding_IGeocodeService");
  6.  
  7.             GeocodeService.GeocodeRequest resquest = new GeocodeService.GeocodeRequest();
  8.             resquest.Query = txbPI.Text;
  9.             resquest.Credentials = new Microsoft.Maps.MapControl.Credentials();
  10.             resquest.Credentials.ApplicationId = App.Current.Resources["MisCredenciales"] as string;
  11.  
  12.             geocodeClient.GeocodeCompleted += new EventHandler<GeocodeService.GeocodeCompletedEventArgs>(geocodeClient_GeocodeCompleted);
  13.             geocodeClient.GeocodeAsync(resquest);
  14.  
  15.         }

Por otro lado debemos de referenciar las librería de Bing Maps dentro del código Behind para aprovechar su funcionalidad, para ello añadimos la siguiente directiva:

  1. using Microsoft.Maps.MapControl;

Como podemos observar creamos un objeto GeocodeServiceClient, que nos permite comunicar la aplicación con el servicio GeocodeService. Dicho servicio requiere del Objeto GeocodeRequest que envía la petición de un nuevo localizador a través del texto introducido en el TextBox. Pero este acceso al servicio no sería posible si no enviaríamos los credenciales de la cuenta Bing Maps. Por último la petición al servicio se ha de realizar de forma asíncrona, cuando la petición regresa la controlamos a través del controlador de eventos geocodeClient_GeocodeCompleted.

Ahora vamos a introducir el  evento citado con anterioridad. En el, obtenemos un array con una serie de coincidencias con el texto introducido en el TextBox. Como solo nos interesa el primer elemento, a través de LINQ obtenemos ese primer elemento. Tomamos la longitud y latitud de ese elemento, creamos en la interfaz de usuario un nuevo localizador. El último paso es ofrecer la mejor vista de ese localizador por lo que el objeto mapa realizará un Zoom sobre dicho punto de interés a fin de mejorar la experiencia de usuario.

  1. private void geocodeClient_GeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)
  2.         {
  3.             GeocodeService.GeocodeResponse response = e.Result;
  4.             if (response.Results.Count > 0)
  5.             {
  6.                 GeocodeService.GeocodeResult result = response.Results.First();
  7.                 if (result.Locations.Count > 0)
  8.                 {
  9.                     Pushpin pushpin = new Pushpin();
  10.                     Location location = new Location(result.Locations.First().Latitude, result.Locations.First().Longitude);
  11.                     pushpin.Location = location;
  12.                     mapa.Children.Add(pushpin);
  13.                     mapa.SetView(result.BestView);
  14.                     
  15.                 }
  16.             }
  17.         }

Ejecutamos la aplicación, accedemos a la sección Logística desde el menú de Navegación. Introducimos en el TextBox por ejemplo Cein, Navarra y el resultado es el siguiente:

 

image

En esta parte del articulo vamos desarrollar la realización de rutas entre lo diferentes puntos que interesen al usuario.Empezamos haciendo clic con el botón derecho sobre la carpeta Reference, elegimos Add Service Reference.En la ventana emergente, debemos introducir en la sección Address la siguiente URL del servicio de enrutamiento (http://dev.virtualearth.net/webservices/v1/routeservice/routeservice.svc ). Presionamos sobre el botón Go para comprobar que la comunicación con el servicio se realiza de forma correcta. El servicio lo identificaremos con el nombre RouteService que debemos introducirlo en el apartado NameSpace:

image

Seguidamente vmos a crear una clase con los parámetros que necesita el servicio para su correcto funcionamiento. Realizamos clic con el botón derecho sobre nuestro proyecto del lado del cliente. Elegimos Añadir->Nuevo elemento, de las plantillas que Visual Studio 2010 nos ofrece por defecto, elegimos Clase, nombrándola como UbicacionEntrega.cs.

image

Dentro de la misma incluiremos el siguiente código:

  1. using System;
  2. using System.Net;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Documents;
  6. using System.Windows.Ink;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Animation;
  10. using System.Windows.Shapes;
  11. using Microsoft.Maps.MapControl;
  12.  
  13. namespace SilverlightBusinessApplication
  14. {
  15.     public class UbicacionEntrega
  16.     {
  17.         public string Address { get; set; }
  18.  
  19.         public Location Location { get; set; }
  20.  
  21.     }
  22. }

El siguiente paso que vamos a dar, es la actualización del interfaz de usuario. Tenemos que introducir nuevos elemento y cambiar la distribución de los anteriores. El resultado de la nueva interfaz de usuario es la siguiente:

 

  1. <Grid x:Name="LayoutRoot">
  2.         <Grid.ColumnDefinitions>
  3.             <ColumnDefinition Width="271*"/>
  4.             <ColumnDefinition Width="100*"/>
  5.         </Grid.ColumnDefinitions>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="188*"/>
  8.             <RowDefinition Height="67*"/>
  9.             <RowDefinition Height="45*"/>
  10.         </Grid.RowDefinitions>
  11.         <map:Map x:Name="mapa" Center="40.225,-1.794" ZoomLevel="6" Mode="Road" Grid.RowSpan="2">
  12.             <map:Map.CredentialsProvider>
  13.                 <map:ApplicationIdCredentialsProvider ApplicationId="{StaticResource MisCredenciales}"/>
  14.             </map:Map.CredentialsProvider>
  15.             <map:MapLayer x:Name="routeLayer"/>
  16.         </map:Map>
  17.         <TextBox x:Name="txtAddress" Grid.Row="2" Margin="10,10,10,68" BorderThickness="3" Background="White" />
  18.         <Button x:Name="btnGeoCode" Content="Añadir Localizador" Grid.Row="2" Grid.Column="1" Margin="5" Click="btnGeocode_Click"/>
  19.         <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Text="Entregas" TextWrapping="Wrap" Grid.Column="1" />
  20.         <ListBox x:Name="lbxDestinations" Margin="5,20,8,7" Grid.Column="1" Grid.RowSpan="1">
  21.             <ListBox.ItemTemplate>
  22.                 <DataTemplate>
  23.                     <StackPanel>
  24.                         <TextBlock Text="{Binding Address}" TextWrapping="Wrap"/>
  25.                         <StackPanel Orientation="Horizontal">
  26.                             <Button Content="Subir" Tag="{Binding}" x:Name="ButtonUp" Click="ButtonUp_Click"/>
  27.                             <Button Content="Bajar" Tag="{Binding}" x:Name="ButtonDown" Click="ButtonDown_Click"/>
  28.                             <Button Content="Borrar" Tag="{Binding}" x:Name="ButtonRemove" Click="ButtonRemove_Click"/>
  29.                         </StackPanel>
  30.                     </StackPanel>
  31.                 </DataTemplate>
  32.             </ListBox.ItemTemplate>
  33.         </ListBox>
  34.         <StackPanel Grid.Column="1" Grid.Row="1" Orientation="Vertical" d:LayoutOverrides="Height">
  35.             <CheckBox x:Name="cbxTraffic" Content="Evitar Tráfico"/>
  36.             <Button x:Name="btnCalculateRoute" Click="btnCalculateRoute_Click" Height="22" Content="Calcular Ruta" Margin="5,5,5,0"/>
  37.             <TextBlock x:Name="txtTotalTime" Text="Tiempo Total:" Margin="5,15,5,0" TextWrapping="Wrap"
  38.                        FontSize="13.333" HorizontalAlignment="Center" Width="337" Height="49" />
  39.            
  40.         </StackPanel>
  41.     </Grid>

Hemos empezado por un cambio de la distribución de los elementos dentro del <Grid> principal. Luego hemos añadido un objeto ListBox, dentro del mismo hemos definido una plantilla para cada elemento que se agregue al ListBox. Dicha plantilla se compone del nombre de la Localización (que es obtenido directamente de una colección de elementos)y tres botones. Dos para desplazar el localizador entre los diferentes Items del ListBox y el tercero para borrar el localizador.

También hemos añadido un objeto CheckBox para que el usuario pueda evitar los atascos de tráfico que pueda haber en la ruta elegida. Un botón que será el disparador de las acciones de cálculo de ruta. El último objeto es un TextBlock quese encarga de mostrar al usuario el tiempo que va tardar en realizar la ruta entre los diferentes localizadores que previamente ha seleccionado.

Un elemento que hemos introducido y que no he nombrado pero si destacado es <map:MapLayer x:Name="routeLayer"/>. Dicho elemento nos permitirá la representación de la ruta, sin tener que añadir esta como un hijo del objeto Map. Esto otorga una independencia a la hora de mostrar, ocultar o actualizar la ruta. Ya que si el usuario realiza una serie de comportamientos la ruta se redibuja nuevamente y no confunde al usuario.

 

El siguiente paso es añadir las nuevas funciones del botón Añadir Localizador. Para ello añadimo dentro del constructor de logistica.xaml.cs una colección que es del tipo UbicacionEntrega y un miembro que guarda la respuesta del servicio.

  1. private ObservableCollection<UbicacionEntrega> _locations = new ObservableCollection<UbicacionEntrega>();
  2.         private RouteService.RouteResponse _currentRoute;

También añadimos la siguiente referencia:

  1. using System.Collections.ObjectModel;

Dentro del evento click del Botón que realiza la inclusión de nuevos localizadores, vamos a incluir el borrado de la capa de la que hemos hablado con anterioridad, debido a que se va dibujar una nueva cada vez que añadimos un nuevo localizador. También hemos añadido la cultura con la que se va mostrar la interfaz de usuario. Por último pasamos los credenciales para todos los objetos incluidos en Map. El resultado final del evento click del botón es el siguiente:

  1. private void btnGeocode_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             routeLayer.Children.Clear();//borra el hijo que en ese momento esta representado en la capa
  4.  
  5.             GeocodeService.GeocodeServiceClient geocodeClient = new GeocodeService.GeocodeServiceClient
  6.  
  7.             ("CustomBinding_IGeocodeService");
  8.  
  9.             GeocodeService.GeocodeRequest resquest = new GeocodeService.GeocodeRequest();
  10.             resquest.Culture = mapa.Culture;//otorga la cultura del S.O. al mapa
  11.             resquest.Query = txtAddress.Text;
  12.             resquest.Credentials = new Microsoft.Maps.MapControl.Credentials();//pasamos los credenciales
  13.             resquest.Credentials.ApplicationId = App.Current.Resources["MisCredenciales"] as string;
  14.  
  15.             geocodeClient.GeocodeCompleted += new EventHandler<GeocodeService.GeocodeCompletedEventArgs>(geocodeClient_GeocodeCompleted);
  16.             geocodeClient.GeocodeAsync(resquest);
  17.         }

En el controlado de eventos creamos una variable del tipo de la clase con los parámetros obtenidos en la interfaz de usuario. A la etiqueta del localizador le introducimos los valores introducidos con anterioridad. Por último añadimos el localizador a la colección. Siendo este el aspecto final:

  1. private void geocodeClient_GeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)
  2.         {
  3.             GeocodeService.GeocodeResponse response = e.Result;
  4.             if (response.Results.Count > 0)
  5.             {
  6.                 GeocodeService.GeocodeResult result = response.Results.First();
  7.                 if (result.Locations.Count > 0)
  8.                 {
  9.                     Pushpin pushpin = new Pushpin();
  10.                     Location location = new Location(result.Locations.First().Latitude, result.Locations.First().Longitude);
  11.                     pushpin.Location = location;
  12.                     mapa.Children.Add(pushpin);
  13.                     mapa.SetView(result.BestView);
  14.                     UbicacionEntrega dl = new UbicacionEntrega() { Address = this.txtAddress.Text, Location = location };
  15.                     //Creamos una variable con los parametros de la clase obtenidos de los objetos de la
  16.                     //interfaz de usuario
  17.                     pushpin.Tag = dl;//introducimso el valor de la etiqueta del localizador
  18.                     this._locations.Add(dl);//añadimos el localizador a la colección de localizadores
  19.                 }
  20.             }
  21.         }

Vamos a trabajar con los tres botones que pueden tener cada elemento que se añade al Listbox.

Los botones de desplazamiento (Subir, Bajar) del localizador tienen el mismo comportamiento, salvo la posición que ha de ocupar el elemento cuando presionamos alguno de los citados botones.

 

  1. private void ButtonUp_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             Button btnSender = sender as Button;
  4.             UbicacionEntrega dl = btnSender.Tag as UbicacionEntrega;
  5.             SubirLocalizador(dl);
  6.  
  7.         }
  8.  
  9.         private void ButtonDown_Click(object sender, RoutedEventArgs e)
  10.         {
  11.             Button btnSender = sender as Button;
  12.             UbicacionEntrega dl = btnSender.Tag as UbicacionEntrega;
  13.             BajarLocalizador(dl);
  14.         }

En el evento click de los botones, creamos una variable de tipo botón para obtener todo su contenido a través de la propiedad etiqueta. Finalmente enviamos a los respectivos métodos la variable como UbicacionEntrega.

 

  1. private void SubirLocalizador(UbicacionEntrega dl)
  2.         {
  3.             MoverLocalizador(dl, -1);
  4.         }
  5.         private void BajarLocalizador(UbicacionEntrega dl)
  6.         {
  7.             MoverLocalizador(dl, 1);
  8.         }
  9.         private void MoverLocalizador(UbicacionEntrega dl, int direction)
  10.         {
  11.             BorrarRuta();
  12.             if (this._locations.Count > 1)
  13.             {
  14.                 int origIndex = this._locations.IndexOf(dl);
  15.                 this._locations.Remove(dl);
  16.                 this._locations.Insert(origIndex + direction, dl);
  17.             }
  18.         }
  19.         private void BorrarRuta()
  20.         {
  21.             this._currentRoute = null;
  22.             routeLayer.Children.Clear();
  23.             this.txtTotalTime.Text = "";
  24.         }

En función de si queremos subir o bajar el localizador en el Listbox, llamaremos a un método u otro. Ambos métodos llaman a su vez a MoverLocalizador. Primeramente borra la ruta que hay reflejada en el mapa, así como el tiempo en que se tarda en realizar la ruta. Seguidamente se comprueba que haya más de un elemento en la colección. Si es así, borra el elemento de la lista y sitúa la copia del elemento que está en memoria en la posición correspondiente.

Ahora vamos con el botón de eliminación de un localizador. En el evento click de botón a eliminar en primera instancia elimina la ruta dibujada sobre el mapa. Seguidamente borra el localizador de la colección. Finalmente busca el localizador en el objeto Mapa y es borrado de la interfaz de usuario.

  1. private void ButtonRemove_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             Button btnSender = sender as Button;
  4.             UbicacionEntrega dl = btnSender.Tag as UbicacionEntrega;
  5.             BorrarRuta();//elimina la ruta de la Interfaz de Usuario
  6.             //quitar elemento de la colección
  7.             this._locations.Remove(dl);
  8.             //buscar el marcador
  9.             var pushpinsToDelete = mapa.Children.OfType<Pushpin>().Where(x => x.Location == dl.Location);
  10.             //borrar el marcador de la interfaz usuario
  11.             pushpinsToDelete.ToList().ForEach(x => mapa.Children.Remove(x));
  12.         }

 

Por último vamos a calcular y representar la ruta en la interfaz de usuario.Empezando por generar el evento clic del botón “Calcular Ruta”, para ello realizamos doble clic sobre este en el área de diseño de Logistica.xaml.

Nos situamos en el evento click del botón que se encarga de calcular las diferentes rutas del departamento de logística situado en Logistica.xaml.cs. La primera parte del código que vamos a introducir en el mencionado evento, es esencialmente igual, que la que hemos utilizado para el botón que añade localizadores. La única diferencia es que en vez de usar el servicio GeocodeService utilizamos el servicio RouteService

  1. private void btnCalculateRoute_Click(object sender, RoutedEventArgs e)
  2.         {
  3.  
  4.             routeLayer.Children.Clear();
  5.  
  6.  
  7.             RouteService.RouteServiceClient routeServiceClient = new RouteService.RouteServiceClient("CustomBinding_IRouteService");
  8.             routeServiceClient.CalculateRouteCompleted += new EventHandler<RouteService.CalculateRouteCompletedEventArgs>(routeServiceClient_CalculateRouteCompleted);
  9.  
  10.             RouteService.RouteRequest routeRequest = new RouteService.RouteRequest();
  11.             routeRequest.Culture = mapa.Culture;
  12.             routeRequest.Credentials = new Credentials();
  13.             routeRequest.Credentials.ApplicationId = App.Current.Resources["MisCredenciales"] as string;
  14.  
  15.            
  16.  
  17.         }

Inmediatamente después del evento click con el que hemos trabajado vamos a implementar en controlador de eventos routeServiceClient_CalculateRouteCompleted, añadiendo el siguiente fragmento de código:

 

  1. private void routeServiceClient_CalculateRouteCompleted(object sender, RouteService.CalculateRouteCompletedEventArgs e)
  2.         {
  3.             if (e.Error == null)
  4.             {
  5.                 long timeInSeconds = e.Result.Result.Summary.TimeInSeconds;
  6.                 //obtiene el tiempo de la ruta del servicio
  7.                 TimeSpan t = new TimeSpan(0, 0, int.Parse(timeInSeconds.ToString()));
  8.                 this.txtTotalTime.Text = string.Format("Tiempo Estimado: {0}", t.ToString());
  9.                 //da formato a la variable del tiempo de la ruta y lo incluye en el objeto
  10.                 //que lo mostrará en la Interfaz de Usuario
  11.                 DrawRoute(e);
  12.                 //llamada al método que dibuja la ruta
  13.  
  14.                 this._currentRoute = e.Result;
  15.             }
  16.         }

Volvemos al evento clic del botón de cálculo de ruta. En el introducimos las opciones de cálculo de ruta que vamos a pedir al servicio. Esta petición va a ser para que la ruta se calcule a través de puntos (Localizadores).

 

  1. routeRequest.Options = new RouteService.RouteOptions();
  2.             routeRequest.Options.RoutePathType = RouteService.RoutePathType.Points;
  3.  
  4.             routeRequest.ExecutionOptions = new RouteService.ExecutionOptions();
  5.             routeRequest.ExecutionOptions.SuppressFaults = true;

Seguidamente, vamos a contemplar la opción del control de tráfico, que en el interfaz de usuario ofrecemos al usuario final. Introducimos el siguiente código dentro del evento click del botón:

 

  1. if (this.cbxTraffic.IsChecked.Value)
  2.             {
  3.                 routeRequest.Options.TrafficUsage = RouteService.TrafficUsage.TrafficBasedRouteAndTime;
  4.             }
  5.             else
  6.             {
  7.                 routeRequest.Options.TrafficUsage = RouteService.TrafficUsage.None;
  8.             }

Ahora que tenemos todas las opciones en nuestro objeto RouteRequest, es momento de añadir los localizadores como puntos para realizar el cálculo de la ruta(todo ello dentro del evento clic usado con anterioridad). El siguiente código establece las propiedades de los puntos de la ruta como una ObservableCollection de puntos. Además usa LINQ para recorrer todos los elementos contenidos en la lista _locations (que hemos definido en el constructor). Finalmente pasaremos todos los elementos al método GeocodeResultToWaypoint.

 

  1. routeRequest.Waypoints = new System.Collections.ObjectModel.ObservableCollection<RouteService.Waypoint>();
  2.             this._locations.ToList().ForEach(x => routeRequest.Waypoints.Add(GeocodeResultToWaypoint(x)));

El último fragmento de código que vamos a incluir en el evento click del botón, es la llamada asíncrona al método RouteRequest del servicio de cálculo de ruta de Bing Maps pasándole el objeto routeRequest, como podemos observar a continuación:

  1. routeServiceClient.CalculateRouteAsync(routeRequest);

El siguiente paso es crear el método GeocodeResultToWaypoint que convierte el objeto UbicacionEntrega en el objeto Waypoint, que son los puntos necesarios para crear y calcular una ruta:

 

  1. private RouteService.Waypoint GeocodeResultToWaypoint(UbicacionEntrega deliverylocation)
  2.         {
  3.             RouteService.Waypoint waypoint = new RouteService.Waypoint();
  4.             waypoint.Description = deliverylocation.Address;
  5.             waypoint.Location = new Location();
  6.             waypoint.Location.Latitude = deliverylocation.Location.Latitude;
  7.             waypoint.Location.Longitude = deliverylocation.Location.Longitude;
  8.             return waypoint;
  9.         }

El último método que nos queda por implementar es el que se encarga de dibujar en la capa del objeto mapa la ruta para los diferentes localizadores. Añadimos el siguiente fragmento de código:

  1. private void DrawRoute(SilverlightBusinessApplication.RouteService.CalculateRouteCompletedEventArgs e)
  2.         {
  3.             if (e.Result.Result.Legs.Count > 0)
  4.             {
  5.                 //creamos un objeto MapPolyline llamado routeLine
  6.                 //y establecemos sus propiedades visuales
  7.                 Color routeColor = Colors.Blue;
  8.                 SolidColorBrush routeBrush = new SolidColorBrush(routeColor);
  9.                 MapPolyline routeLine = new MapPolyline();
  10.                 routeLine.Locations = new LocationCollection();
  11.                 routeLine.Stroke = routeBrush;
  12.                 routeLine.Opacity = 0.65;
  13.                 routeLine.StrokeThickness = 5.0;
  14.  
  15.                 double distance = e.Result.Result.Summary.Distance;
  16.                 long seconds = e.Result.Result.Summary.TimeInSeconds;
  17.  
  18.                 TimeSpan time = new TimeSpan(0, 0, int.Parse(seconds.ToString()));
  19.  
  20.                 //el mapa recupera una serie de puntos entre los localizadores
  21.                 //y los representa como una linea
  22.                 foreach (Location p in e.Result.Result.RoutePath.Points)
  23.                 {
  24.                     routeLine.Locations.Add(new Location(p.Latitude, p.Longitude));
  25.                 }
  26.                 //la linea es dibujada en la capa del mapa
  27.                 routeLayer.Children.Add(routeLine);
  28.                 //centramos el mapa acorde a la ruta, creando un rectangulo delimitador
  29.                 LocationRect rect = new LocationRect(routeLine.Locations[0], routeLine.Locations[routeLine.Locations.Count – 1]);
  30.  
  31.                 //definimos la vista de mapa utilizando los límites del rectángulo anterior
  32.                 mapa.SetView(rect);
  33.             }
  34.  
  35.         }

Por último en el evento de carga de la página vamos establecer el origen de los elementos del ListBox como la colección _locations, aunque previamente tenemos que generarlo en logística.xaml como podemos observar en la siguiente imagen:

 

image

En el código Behind del citado evento incluiremos el siguiente fragmento de código:

 

  1. private void Page_Loaded(object sender, RoutedEventArgs e)
  2.         {
  3.             this.lbxDestinations.ItemsSource = this._locations;
  4.  
  5.         }

Ejecutamos la aplicación ( F5 ), introducimos los diferentes puntos de entrega que queremos añadir a la ruta.

image 

Presionamos sobre calcular Ruta y podemos ver como la ruta se dibuja en el mapa y calcula el tiempo estimado de la misma.

image

Podemos personalizar las diferentes rutas con las opciones que ofrecemos al usuario final.

Podéis ver todo el material de este tutorial aquí (recordar debereis incluir el ID de Bing Maps en App.xaml, para que funcione de forma correcta objeto Mapa).

Un comentario sobre “Tutorial “Enrutamiento con Bing Maps y Silverlight””

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *