Nuevo control de Mapas y calculo de rutas en Windows Phone 8

Hola a todos!

Ya había pasado un tiempo sin escribir, es lo que tiene llegar a la fase final de un libro. De pronto el 5% del trabajo te ocupa el 100% del tiempo. Pero ya estamos aquí otra vez. Hoy quería hablar sobre más novedades y funcionalidades que podemos encontrar en los mapas de Windows Phone 8.

Soy un verdadero enamorado de los nuevos mapas incluidos con el sistema. Si has visto los ejemplos y slides de mis charlas o as acudido a alguna, habrás comprobado lo fácil que es crear mapas visualmente increíbles: con representación de edificios en tres dimensiones, controlando el ángulo de cámara, la inclinación del terreno, el tipo de información para peatones a mostrar… Pero lo realmente increíble de los mapas es que no son solo unos mapas sin más. Alrededor de ellos se encuentra toda una infraestructura que podemos usar en nuestras aplicaciones. En concreto hoy me quiero centrar en el cálculo de rutas y en la geo codificación inversa.

Microsoft.Phone.Maps.Services

Este namespace contiene todas las clases que usaremos para realizar consultas al sistema de mapas de Windows Phone 8. En particular nos centraremos en tres tipos: RouteQuery, que nos permitirá crear rutas con indicaciones, GeocodeQuery, que nos ayudará a resolver direcciones para obtener coordenadas geográficas y ReverseGeocodeQuery, que nos permitirá obtener direcciones a partir de coordenadas geográficas. En este primer artículo de la serie vamos a examinar en profundidad las rutas con RouteQuery.

RouteQuery

En Windows Phone 7.X la única forma, sencilla y mínimamente automatizada, de crear rutas A-B era usar el lanzador BingMapsDirectionsTask. Este lanzador recibía un punto inicial, un punto final y abría la aplicación de mapas del teléfono para mostrarlos. En Windows Phone 8 conservamos este lanzador, al que se ha añadido el MapsDirectionsTask, pero también tenemos la posibilidad de mostrar rutas dentro de un mapa en nuestra aplicación. Una ventaja de usar la clase RouteQuery es que podemos indicar más de dos puntos, inicial y final, y crear rutas con múltiples puntos intermedios.

En primer lugar tendremos que crear una nueva instancia de RouteQuery. Debemos indicar el modo de viaje mediante la propiedad TravelMode. Para ello usaremos el enumerado TravelMode, que nos ofrece dos opciones: Walking o Driving. A continuación también tendremos que indicar los puntos que compondrán al menos el inicio y final de la ruta mediante la propiedad Waypoints. Como mínimo debemos indicar dos puntos, pero podemos añadir n puntos intermedios que deseemos:

RouteQuery query = new RouteQuery()
{
    TravelMode = TravelMode.Walking,
    Waypoints = new List<GeoCoordinate>()
    {
        new GeoCoordinate(43.26614, -2.92451),
        new GeoCoordinate(43.26308, -2.94296)
    }
};

En el ejemplo anterior indicamos solo dos coordenadas y nuestro modo de viaje lo establecemos en “Walking”. Con esto ya hemos creado nuestro objeto RouteQuery. Ahora usaremos el método QueryAsync y manejaremos el evento QueryCompleted para obtener los resultados:

    ...
    query.QueryCompleted += query_QueryCompleted;
    query.QueryAsync();
}

void query_QueryCompleted(object sender, QueryCompletedEventArgs<Route> e)
{
    if (e.Error == null)
    {
        if (GetRouteCompleted != null)
            GetRouteCompleted(e.Result, null);
    }
}

En el manejador recibimos los resultados en el parámetro QueryCompletedEventArgs<Route>, en la propiedad Result. Es importante que comprobemos que no se ha producido ningún error. A continuación, como nos encontramos en una ViewModel y tenemos que añadir la ruta al mapa, lanzamos un evento que hemos definido en nuestra ViewModel llamado GetRouteCompleted.

En el code behind de nuestra vista, nos suscribiremos al evento cuando naveguemos a la página y lo eliminaremos al navegar fuera de ella:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    vm.GetRouteCompleted += vm_GetRouteCompleted;
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    vm.GetRouteCompleted -= vm_GetRouteCompleted;
}

En el manejador vm_GetRouteCompleted solo tendremos que usar el método AddRoute del elemento Map para añadir la ruta y que este la dibuje. Adicionalmente, para situar la cámara en el primer punto del recorrido obtenemos las geometrías que componen la ruta. En particular la primera de ellas, que indica el punto de inicio:

private void vm_GetRouteCompleted(object sender, EventArgs e)
{
    var route = new MapRoute((Route)sender);
    GeoCoordinate firstPoint = route.Route.Geometry.First();

    mapElement.Center = firstPoint;
    mapElement.AddRoute(route);
}

Y con esto ya tenemos completado el trabajo para mostrar nuestra ruta en pantalla. Antes de continuar, tenemos que añadir al manifiesto de nuestra aplicación dos capacidades: ID_CAP_MAP e ID_CAP_LOCATION para que funcione correctamente. Una vez añadidas, ejecuta la aplicación y el resultado debería ser algo parecido a lo siguiente:

image

Ya tenemos nuestra ruta calculada y dibujada en el mapa. Además con la posibilidad de poner el mapa en 3D y los edificios queda muy bien. Quizás hechas de menos algo: Indicaciones. Ya que tenemos la ruta, estaría muy bien tener también las indicaciones. Pues las tenemos! Están contenidas en una propiedad llamada Legs dentro de nuestra ruta. En nuestra ViewModel vamos a exponer una colección de RouteManeuvers, el objeto que indica una maniobra. y la vamos a rellenar de la siguiente forma:

void query_QueryCompleted(object sender, QueryCompletedEventArgs<Route> e)
{
    if (e.Error == null)
    {
        Maneuvers = new ObservableCollection<RouteManeuver>(e.Result.Legs.SelectMany(l => l.Maneuvers));

        if (GetRouteCompleted != null)
            GetRouteCompleted(e.Result, null);
    }
}

¿Por qué usar un SelectMany? La propiedad Legs es una colección. Esta comprende las instrucciones para ir de un punto hasta el siguiente. Si al realizar la consulta de nuestra ruta hemos indicado tres puntos (A,B,C) tendremos dos Legs: uno con las indicaciones de A-B y otro de B-C. Por lo tanto usamos SelectMany para obtener todas las indicaciones de todos los segmentos de la ruta. A continuación solo nos queda añadir a nuestra interfaz de usuario una lista que muestre los puntos:

<ListBox Grid.Column="1" ItemsSource="{Binding Maneuvers}"
         Background="#66000000">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Margin="0,0,2,5">
                <TextBlock Text="{Binding InstructionKind}"
                           Foreground="{StaticResource PhoneAccentBrush}">
                </TextBlock>
                <TextBlock Text="{Binding InstructionText}"
                           TextWrapping="Wrap">
                </TextBlock>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Principalmente cada objeto Maneuver nos expondrá dos propiedades: InstructionKind e InstructionText. InstructionKind indica el tipo de maniobra: TurnRight, TurnLeft, Start, End… mientras que InstructionText nos dará una descripción más detallada de la misma. Si ejecutamos ahora nuestra aplicación de ejemplo veremos el siguiente resultado:

image

Voila! de una forma rápida y sencilla tenemos nuestra ruta dibujada e indicaciones añadidas en la pantalla. Además del tipo de maniobra y la descripción, cada objeto Maneuver nos ofrece la longitud hasta el siguiente paso en metros y su posición geográfica, con lo que podríamos comprobar cuando nos acercamos al siguiente punto para usar la sintetización de voz de Windows Phone 8 y leer las instrucciones al usuario.

Conclusión

Esta vez, Microsoft ha hecho un gran trabajo con los mapas de Windows Phone 8. Con el apoyo de Nokia nos han dado la oportunidad de crear aplicaciones que hagan un uso intensivo de la cartografía y la localización. A continuación tenéis el ejemplo de este artículo para que le deis un repaso y juguéis con el aprovechando las vacaciones navideñas. Incluye uso de MVVM, Autofac, VMLocator, los mapas y las rutas!

Un saludo y Happy Coding!

5 comentarios sobre “Nuevo control de Mapas y calculo de rutas en Windows Phone 8”

Deja un comentario

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