La localización o detección de nuestro entorno siempre ha ido muy ligado a los desarrollos de aplicaciones para dispositivos móviles, del extendido uso de navegadores como el conocido TomTom® hemos pasado al uso de aplicaciones que nos permiten realizar esas acciones en un único dispositivo con el que además podemos hacer fotos, actualizar la agenda, navegar por internet y si, también llamar, que para eso son nuestros teléfonos. Ahora en Windows Phone podemos desde mostrar un mapa al usuario, saber dónde se encuentra resolviendo su posición a direcciones comprensibles por el ser humano hasta llegar finalmente al cálculo de rutas para poder guiarle, y todo de un modo muy sencillo de implementar.
En Windows Phone contamos con varias herramientas que nos van a permitir realizar todas estas acciones desde nuestra aplicación, por un lado contamos con el control Bing Maps para Windows Phone con el cual podemos mostrar al usuario un mapa, tanto en modo callejero como en vista satélite, por otro lado, contamos con el sistema de localización de Windows Phone el cual nos va permitir obtener la posición del teléfono y complementariamente a estas dos opciones se nos une el API de Bing Maps que nos va a permitir resolver direcciones geográficas, calcular rutas, obtener nombre de calles o realizar búsquedas geo-localizadas. A lo largo de este artículo veremos cómo poder exprimir estas características en nuestras aplicaciones.
¿Dónde estamos?
En Windows Phone para obtener la posición del terminal contamos con el objeto System.Device.Location.GeoCoordinateWatcher [1]. Con Windows Mobile la única opción que teníamos para poder localizar el terminal era; o bien utilizar el GPS (en caso de que el teléfono contase con uno, lo cual no era muy común o estuviese conectado a un GPS externo), o bien implementar un sistema de localización por triangulación, utilizando las estaciones base de telefonía. Por suerte en Windows Phone esto ha cambiado radicalmente, como hemos dicho ahora contamos con el sistema de localización de Windows Phone el cual nos permite despreocuparnos de estar abriendo puertos, encender o apagar un receptor externo, estar consultando bases de datos o calculando potencias.
El objeto GeoCoordinateWatcher integra en un único punto de acceso tres sistemas de localización, poniéndolos a nuestra disposición siempre que estén disponibles. Desde la localización por WiFi o triangulación por celda, hasta el uso del GPS integrado del teléfono, pudiendo seleccionar que sistema deseamos utilizar modificando el valor de la propiedad DesiredAccuracy.
Además este objeto se encarga de velar por la integridad de nuestra batería como veremos a continuación.

Figura 1- Clase GeoCoordinateWatcher
Dentro de la clase GeoCoordinateWatcher destacan cinco propiedades que merece la pena repasar: DesiredAccuracy, MovementThreshold, Permission, Position y Status. Las tres últimas son de solo lectura y como veremos son a modo informativo. Estas propiedades nos permitirán comprobar el estado, precisión y tipo de localización modificando las dos primeras podremos controlar el consumo de recursos y a la postre de batería.
La propiedad DesiredAccuracy permite especificar la precisión a la que deseamos que trabaje el servicio de localización. Esta acepta dos valores, por defecto y alta, a mayor precisión mayor consumo, por lo que salvo en casos en los que necesitemos una posición exacta podremos establecer el valor por defecto, debemos tener en cuenta que si establecemos un valor “Alto” de precisión nos podemos encontrar con que nuestra aplicación tarde en responder o que el sistema no retorne ninguna posición, el valor High indica al GeoCoordinateWatcher que utilice el GPS, por lo que es posible que no retorne valores hasta obtener unas coordenadas precisas. El sistema puede tardar desde 15 segundos hasta dos minutos. El valor por defecto permite al sistema de localización utilizar la localización por WiFi o la ubicación por celda, con lo que los resultados pueden tener muy poca precisión. Por último decir que esta propiedad solo la podremos establecer en el constructor de la clase.
Con la propiedad MovementThreshold indicamos al sistema la distancia mínima (en metros) que debe desplazarse el terminal antes de generar un evento PositionChanged. Debemos tener en cuenta que no es lo mismo una aplicación pensada para ejecutarse sobre un vehículo que una que esté pensada para ejecutarse mientras se va caminando por la calle, y es que al igual que nos ocurre con la precisión a mas detalle, es decir, a menor MovementThreshold, mayor consumo de batería. El valor por defecto de esta propiedad es cero, por lo que en caso de no establecer un valor mayor estaremos recibiendo posiciones constantemente aunque no nos estemos moviendo. Microsoft recomienda no establecer un valor menor de 20. Una ventaja de la propiedad MovementThreshold es que nos permite modificarla una vez iniciado el proceso de captura de tramas, de este modo podremos variar la precisión, por ejemplo, en base a la velocidad que se está moviendo el usuario.
Por otro lado con las propiedades Permission, Position y Status podemos comprobar si el usuario ha concedido permiso para que la aplicación utilice el sistema de localización de Windows Phone, ver cuál es la última posición recibida, y con la propiedad Status ver el estado del servicio. De este modo podremos mostrar información al usuario sobre el estado del proceso, el objeto GeoCoordinateWatcher expone los eventos PositionChanged y StatusChanged para poder capturar los cambios de posición y estado en tiempo real.
Certificación en Marketplace
A nivel de la certificación en Marketplace las normas son muy claras, durante el inicio de nuestra aplicación, al menos la primera vez como mínimo, debemos preguntar al usuario si da permiso para utilizar el sistema de localización del teléfono, además debemos incluir la posibilidad de que el usuario pueda revocar este permiso. Lo más adecuado es que incluyamos esta posibilidad en la sección de configuración de nuestra aplicación. En el caso de no cumplir con estos dos requisitos nuestra aplicación no pasará el proceso de certificación.
Nota: El método TryStart incluye el parámetro suppressPermissionPrompt pero actualmente no está implementado. Este parámetro permitiría mostrar la pregunta del sistema para la concesión de acceso al servicio de localización, pero actualmente debemos hacerlo nosotros mismos.
Creando nuestra solución de localización
Para ponernos con las manos en la masa lo primero será crear un proyecto para Windows Phone sobre Silverlight en nuestro Visual Studio, una vez tengamos nuestro proyecto, para utilizar la clase GeoCoordinateWatcher tan solo debemos agregar la referencia a la librería System.Device.dll a nuestro proyecto.
Para nuestro ejemplo crearemos una clase a la cual llamaremos Localización, una vez creada agregaremos el código que podemos ver en el Listado 1
private GeoCoordinateWatcher _geoCoordinateWatcher;
public void IniciarLocalizacion()
{
//Default: WiFi y telefonía
//High: Solo GPS
_geoCoordinateWatcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
//Precisión en metros
_geoCoordinateWatcher.MovementThreshold = 10.0f;
_geoCoordinateWatcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(geoCoordinateWatcher_statusChanged);
_geoCoordinateWatcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(geoCoordinateWatcher_PositionChanged);
new Thread(hiloSegundoPlano).Start();
}
Listado 1
En este caso hemos instanciado nuestra clase GeoCoordinateWatcher para por un lado obtener una precisión alta, además le hemos indicado que se actualizará tan solo si nos desplazamos 10 metros. Como podéis observar lanzaremos la ejecución en un hilo secundario, de este modo podemos utilizar la función TryStart sin interferir en la experiencia de usuario. La función TryStart nos permite especificar un tiempo de espera tras el cual si el sistema no ha conseguido recuperar una posición valida, retornara false, de este modo podremos notificar al usuario que no es posible iniciar el sistema u obtener una dirección valida.
Una vez iniciado el proceso el sistema retorna dos tipos de eventos a los que podemos subscribirnos, por un lado aquellos referentes al estado del dispositivo y que recogeremos con el método geoCoordinateWatcher_statusChanged, y por otro los relacionados con la posición del terminal, estos los procesaremos en el método geoCoordinateWatcher_PositionChanged tal y como veis en el
Listado 2.
void geoCoordinateWatcher_statusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
switch (e.Status)
{
case GeoPositionStatus.Disabled:
// El sistema puede carecer de permisos.
break;
case GeoPositionStatus.Initializing:
// El sistema se está iniciando.
break;
case GeoPositionStatus.NoData:
// No tenemos datos validos.
break;
case GeoPositionStatus.Ready:
// El servicio está listo.
break;
}
}
void geoCoordinateWatcher_PositionChanged(object sender,
GeoPositionChangedEventArgs<GeoCoordinate> e)
{
string latitud = e.Position.Location.Latitude.ToString("0.0000000000");
string longitud = e.Position.Location.Longitude.ToString("0.0000000000");
string velocidad = e.Position.Location.Speed.ToString("0.0") + " metros por segundo";
string direccion = e.Position.Location.Course.ToString("0.0") + " grados";
string altitud = e.Position.Location.Altitude.ToString("0.0") + " altitud.";
}
Listado 2
El método PositionChanged recibe un parámetro basado en la clase GeoPosition esta está compuesta de un punto geográfico contenido en la clase GeoCoordinate y del TimeStamp del momento en que fue capturado. En cuanto a la precisión de cada coordenada recogida, dentro de la clase GeoCoordinate encontraremos dos valores interesantes VerticalAccuracy y HorizontalAccuracy, estos nos indicaran la precisión del punto recogido. Por ejemplo tan solo tendremos disponible la precisión vertical en caso de que utilicemos el GPS estableciendo la propiedad DesiredAccuracy como High.
Nota: La clase GeoCoordinate además de contener la longitud, latitud, altitud, velocidad y dirección incluye la función GetDistanceTo, esta función esta implementada para calcular la distancia desde el punto actual hasta otro, retornándonos el valor en metros.
Limitaciones del API de localización
Como la mayoría ya sabéis Windows Phone está diseñado para optimizar al máximo el consumo de batería, esto supone que no podremos (al menos a fecha de hoy con la versión Mango encima de la mesa) crear una aplicación de tracking que funcione con nuestra aplicación desactivada, ya que aunque ahora contemos con los Agentes en segundo plano, el API de geo-localización está limitada, de modo que aunque los agentes se pueden ejecutar de forma programada cada 30 minutos, la posición que nos retornara el objeto GeoCoordinateWatcher será la contenida en la cache del teléfono, esta cache se actualiza cada 15 minutos siempre y cuando este el servicio habilitado.
Una solución es deshabilitar el ApplicationIdleDetectionMode de modo que, siempre y cuando nuestra aplicación este en primer plano, aunque se apague la pantalla o pulsemos sobre el botón apagar de nuestro terminal nuestra aplicación continuará recogiendo datos.
PhoneApplicationService.Current.ApplicationIdleDetectionMode = IdleDetectionMode.Disabled;