Actualizando los mapas a la versión de Windows Phone 8

Alberto Diaz Martin

http://geeks.ms/blogs/adiazmartin/

Especialidades:

Microsoft MVP SharePoint Server (https://mvp.support.microsoft.com/profile/Alberto.Diaz Martin)
Microsoft Certified Technology Specialist - Microsoft SharePoint Server 2010, Configuration
Microsoft Certified Technology Specialist - Microsoft Windows SharePoint 3.0, Configuration
Microsoft Certified Technology Specialist - SQL Server 2005
SharePoint, Colaboración, Búsquedas, FAST ESP, Business Intelligence, C#, Entity Framework, LINQ

LinkedIn: http://es.linkedin.com/in/albertodiazmartin

Microsoft MVP

Windows Phone 8 tiene una nueva API de mapas con muchas más funcionalidades que los anteriores, entre las que podemos destacar las siguientes:

Los mapas son vectoriales y el usuario puede descargar los mapas para tenerlos Offline

Incluyen servicios de búsqueda de lugares y cálculos de rutas, con lo que obteniendo la nueva clave de aplicación, desde el centro de desarrollo de Windows Phone, nos permite utilizar también estos servicios.

Si tenemos aplicaciones en Windows Phone 7 que hagan uso de mapas, podemos plantearnos la actualización a esta nueva versión, sólo disponible para Windows Phone 8, y que nuestros usuarios puedan beneficiarse de estas nuevas capacidades desde la nueva versión. Veamos cómo debemos hacer el cambio de mapas y funcionalidades. Tened en cuenta que es una actualización opcional, ya que el control de mapas de Windows Phone 7 funciona perfectamente en Windows Phone 8.

Actualizando el mapa

Si analizamos el código de nuestro control de mapa de Windows Phone 7, posiblemente hagamos uso de Bindings para mostrar los Pushpin en el map, mediante el control MapItemsControl.ItemTemplate, y así tener desacoplada la vista de nuestro código.

<my:Map  x:Name="MyMap"  CredentialsProvider="{Binding CredentialsProvider}"
ZoomLevel="{Binding Zoom, Mode=TwoWay}"
Center="{Binding Center, Mode=TwoWay}">
<my:MapItemsControl x:Name="PushpinEstaciones" 
ItemsSource="{Binding EstacionesPushpin}" >
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:Pushpin x:Name="pushpin" Style="{StaticResource CirclePushpin}" Background="#FF4499ff"

Location="{Binding Location}" Tap="EstacionesTap">
<Image Source="{Binding Rotulo, Converter={StaticResource LogoConverterImage}}" Stretch="Fill" />
</my:Pushpin>
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
</my:Map>

Como hijo del control de mapas, utilizábamos el control MapItemsControl enlazado a nuestro ViewModel con el ItemsSource. Cuando migremos a Windows Phone 8, y actualicemos los mapas, nos encontramos con que no tenemos este control, MapItemsControl, y que tendremos que crear nuestros puntos en código.

foreach (var item in viewModel.EstacionesPushpin)
{
var estacionButton = new Button() {Content = item.Rotulo};
//Creamos una overlay con el botón
MapOverlay MyOverlay = new MapOverlay();
MyOverlay.Content = estacionButton;
//Establecemos la posición del elemento
MyOverlay.GeoCoordinate = item.Location;
//Creamos una capa y le añadimos el overlay
MapLayer MyLayer = new MapLayer();
MyLayer.Add(MyOverlay);
MyMap.Layers.Add(MyLayer);
}

Una mejor aproximación, manteniendo la idea de desacoplamiento que usábamos en Windows Phone 7 y recomendada en la MSDN, la encontramos en unas extensiones para el mapa que han incluido en el WindowsPhone Toolkit. Con estas extensiones, podemos utilizar el MapItemsControl y enlazar su ItemsSource en código con la colección de nuestro ViewModel, porque no está implementado el Binding. Veamos cómo quedaría:

<maps:Map x:Name="MyMap" ZoomLevel="{Binding Zoom, Mode=TwoWay}" Center="{Binding Center, Mode=TwoWay}">
<maptk:MapExtensions.Children>
<maptk:MapItemsControl Name="PushpinEstaciones">
<maptk:MapItemsControl.ItemTemplate>
<DataTemplate>
<maptk:Pushpin GeoCoordinate="{Binding Location}" Style="{StaticResource CirclePushpin}"
Background="#FF4499ff" Tap="EstacionesTap" >
<Image Source="{Binding Rotulo, Converter={StaticResource LogoConverterImage}}" Stretch="Fill" />
</maptk:Pushpin>
</DataTemplate>
</maptk:MapItemsControl.ItemTemplate>
</maptk:MapItemsControl>
</maptk:MapExtensions.Children>
</maps:Map>

En la carga de la página, enlazamos el MapItemsControl a los datos.

PushpinEstaciones.ItemsSource = viewModel.EstacionesPushpin;

Además, tenemos que asegurarnos que antes de que enlacemos los datos se ejecuta un método que configura los controles de las extensiones:

/// <summary>
/// Setup the map extensions objects.
/// All named objects inside the map extensions will have its references properly set
/// </summary>
/// <param name="map">The map that uses the map extensions</param>
private void MapExtensionsSetup(Microsoft.Phone.Maps.Controls.Map map)
{
 ObservableCollection<DependencyObject> children = MapExtensions.GetChildren(map);
var runtimeFields = this.GetType().GetRuntimeFields();
 
foreach (DependencyObject i in children)
{
var info = i.GetType().GetProperty("Name");
 
if (info != null)
{
string name = (string)info.GetValue(i);
 
if (name != null)
{
foreach (FieldInfo j in runtimeFields)
{
if (j.Name == name)
{
j.SetValue(this, i);
break;
}
}
                    }
}
}
}

Dos opciones a elegir y, particularmente, ninguna me convence del todo. La primera porque metemos lógica de presentación en el código de la vista y la segunda porque no me convence el método que configura la extensión, porque estamos trabajando con un Toolkit que siempre ha tenido pequeños bugs y porque no tiene el Binding implementado.

Actualizando los servicios de Geocode

Para obtener la dirección de un punto geográfico, latitude y longitude, en Windows Phone 7 necesitábamos usar un servicio externo, por ejemplo, BingMaps. El servicio de Geocode tiene un método ReverseGeocode que usábamos en Windows Phone 7 para obtener la dirección, todo esto desde el servicio web de Bing.

var geoRequest = new Service.Geocode.ReverseGeocodeRequest
{
Credentials = new Credentials { ApplicationId = BingAppId },
Location = new GeocodeLocation { Latitude = MyLocation.Latitude, Longitude = MyLocation.Longitude }
};
 
var service = new Service.Geocode.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
service.ReverseGeocodeCompleted += OnReverseGeocodeCompleted;
service.ReverseGeocodeAsync(geoRequest);

y recibimos la dirección en el evento ReverseGeocodeCompleted.

if (ev.Result != null)
{
var result = ev.Result.Results.FirstOrDefault();
if (result != null)
{
CurrentAddress = result.Address;
}
 
}

Podemos plantearnos mantener este servicio en la versión de Windows Phone 8 o hacer uso de los métodos que nos ofrece el API para este tipo de funcionalidades.

La nueva API tiene un espacio de nombres, Microsoft.Phone.Map.Services, con los métodos necesarios para reemplazar el servicio web de Bing.

var reverseGeocodeQuery = new ReverseGeocodeQuery();
reverseGeocodeQuery.GeoCoordinate = MyLocation;
reverseGeocodeQuery.QueryCompleted += OnReverseGeocodeCompleted;
reverseGeocodeQuery.QueryAsync();

y, similar al método anterior, recibimos la dirección en el evento QueryCompleted.

if (e.Error == null && e.Result.Count > 0)
{
CurrentAddress = e.Result[0].Information.Address;
}

Mapas desconectados

Con esta nueva versión, se incluye la posibilidad de tener los mapas en modo offline, esto es, los mapas de los países seleccionados descargados en el móvil. Esto nos da, como usuarios, dos principales ventajas, ahorro en nuestra tarifa de datos ya que no tenemos que hacer uso de ella cuando usemos mapas en nuestro móvil y, cuando estemos en roaming, disponer del servicio de mapas sin necesidad de consumir datos a un precio prohibitivo.

Los mapas pueden ser descargados desde la configuración de mapas de nuestro Windows Phone 8 o podemos ofrecerle al usuario esa posibilidad, en nuestra aplicación, añadiendo una tarea que tenemos para este procedimiento.

var mapDownloaderTask = new Microsft.Phone.Tasks.MapDownloaderTask();
mapUpdaterTask.Show();

Así, permitimos que el usuario descargue el mapa de su país y poder navegar en modo desconectado desde nuestra aplicación o desde cualquiera que utilice los mapas.

Herramientas

Ya que hablamos de actualizar a Windows Phone 8, necesitaremos principalmente el SDK de esta versión. Esta versión, instala Visual Studio Express y los emuladores necesarios para Windows Phone 8 y Windows Phone 7.

El nuevo Api de mapas se encuentra en el SDK y es parte del Framework .NET de Windows Phone, con lo que no necesitas añadir la referencia a este en tu proyecto, ya que se encuentra disponible en él.

Referencias

Migrar apps de Windows Phone 7 a Windows Phone 8 rápidamente. https://blogs.msdn.com/b/esmsdn/archive/2012/12/06/migrar-apps-de-windows-phone-7-a-windows-phone-8-r-225-pidamente.aspx

Mapas y controles de navegación para Windows Phone 8. https://msdn.microsoft.com/en-us/library/windowsphone/develop/jj207045(v=vs.105).aspx

| Página de inicio |Artículos Técnicos | Comunidad