Entre los sensores que incorpora de serie cualquier smartphone hoy día, sin duda alguna destaca el sensor GPS como el que más funcionalidades extra puede dotar a nuestras aplicaciones. Desde mi punto de vista es de lo mejor que se ha incorporado a un teléfono móvil después de la cámara.
En este artículo quiero analizar distintos usos de posicionamiento que pueden requerir nuestras aplicaciones y ver cómo podemos conseguirlos cuando estamos desarrollando nuestra aplicación con Apache Cordova o PhoneGap.
Como funcionalidades asociadas a la localización pueden enumerarse muchas, pero podríamos decir que todas ellas están basadas en los siguientes escenarios:
- Posicionamiento puntual: es cuando necesitamos saber las coordenadas longitud y latitud del usuario de forma puntual, por ejemplo para adjuntarlas a una foto, un formulario, consultar una API que nos devuelva datos geolocalizados, etc.
- Seguimiento del usuario en primer plano: es cuando necesitamos monitorizar la posición del usuario mientras usa la aplicación porque probablemente esté moviéndose y necesitemos actualizarla en la interfaz de nuestra aplicación, probablemente actualizando dicha posición en un mapa. Un ejemplo claro de esto sería el juego Pokemon Go o Glas aplicaciones de mapas de Apple y Google.
- Seguimiento del usuario en background: es cuando necesitamos monitorizar completamente la ubicación del usuario, esté usando o no nuestra aplicación. En este escenario sería el sistema operativo quien se encargaría de ir ejecutando periódicamente nuestra aplicación para informarle de la posición del usuario.
- Geofence: aplicaciones que únicamente necesitan saber cuando el usuario pasa por ciertas zonas más o menos amplias, donde el sistema operativo únicamente avisará a nuestra aplicación si pasa por alguna de estas zonas.
Para poder abordar estos escenarios, vamos a ver los siguientes posibilidades:
- API HTML5
- Plugin oficial de Apache Cordova de Geolocalización
- Plugin para Geolocalización en background
- Plugin para Geofence.
Posicionamiento agresivo y consumo de batería.
Antes de continuar me gustaría comentar el principal problema asociado a los sensores GPS, especialmente cuando se hace seguimiento de la posición del usuario: el incremento en el consumo de energía y por tanto la reducción de su autonomía del dispositivo.
Hay que tener en cuenta que a día de hoy no es estrictamente necesario asociar sensores GPS a la posibilidad de obtener la localización del usuario, pues es posible realizar geolocalización mediante el uso de posicionamiento Wifi o posicionamiento basado en triangulación de estaciones de repetición móviles. Este tipo de localización no es tan exacta como la localización basada en sensores, pero su impacto en la batería es prácticamente nulo en comparación a la activación de GPS.
En este contexto, se suele hablar de posicionamiento agresivo el realizado por sensores GPS, el cual es muy exacto pero consume mucha batería, y posicionamiento no agresivo al realizado mediante triangulación wifi o repetidor, el cual puede no ser tan exacto aunque su consumo es muy reducido. En general es deseable hacer uso de posicionamiento agresivo únicamente cuando necesitemos un alto grado de exactitud en la posición del usuario.
API W3C HTML5 de Geolocalización
Con HTML5 podemos obtener la posición de un usuario desde el navegador web gracias a la API de Geolocalización definida por el W3C. Fue una de las primeras APIs incluidas en el conjunto de especificaciones de HTML5 y por tanto su disponibilidad en los navegadores actuales es bastante alta.
Es una API muy sencilla que que expone los siguientes métodos accesibles desde window.navigator.geolocation:
- getCurrentPosition: obtiene las coordenadas actuales del dispositivo sobre el que se ejecuta la aplicación.
- watchPosition: Monitorizar la posición del dispositivo, de forma que cada variación de coordenadas nos sea notificada.
- clearWatch: indicamos que ya no queremos seguir monitorizando y que cesen las llamadas de notificación de cambio de posición.
Esta API nos simplifica mucho la vida porque nos independiza de los complicados escenarios que puede haber detrás de la geolocalización, pues nos garantiza que se nos devolverá la “mejor” localización posible, en caso de poder obtener una. Si el dispositivo tiene sensores GPS, wifi o señal de radio los utilizará para obtener la posición en base a ellos, donde únicamente podremos saber la calidad o el nivel de precisión de dicha señal.
Veamos qué información podemos obtener a través de esta API. La función getCurrentPosition tiene la siguiente definición:
window.navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options) |
Como vemos, se trata de una función asíncrona donde le pasaremos una función a ejecutar en caso de éxito, una función a ejecutar en caso de fracaso, y un objeto con algunas opciones de configuración. En caso de éxito, a la función successCallback se le pasará un objeto con los siguientes datos:
- longitude: coordenada de longitud
- latitude: coordenada de latitud
- altitude: altitud en metros, o null si el dispositivo no es capaz de calcularla
- accuracy: nivel de exactitud de coordenadas, especificada en metros. A más metros menos exactitud
- altitudeAccuracy: exactitud en la altitud, también en metros.
- heading: dirección en la que se mueve el dispositivo, en grados centígrados. Si el dispositivo está quieto será NaN o null si el dispositivo no es capaz de obtener dicha información
- speed: velocidad en metros por segundo.
Como opciones, comentar que solo tendremos 3 a definir:
- enableHightAccuracy: booleano indicando si deseamos activar la geolocalización precisa. En la mayoría de implementaciones esto se traduce en hacer uso del GPS, es decir, si deseamos geoposicionamiento agresivo o no agresivo.
- timeout: tiempo máximo definido en milisegundos para invocar a successCallback con la información de posicionamiento. Si se supera, se invocará errorCallback.
- maximumAge: podremos indicar si admitimos valores de posicionamiento cacheados y el tiempo máximo permitido para dichos valores. Lo definimos también en milisegundos
Si aparte de conocer la posición del dispositivo deseamos hacerle un seguimiento, podremos hacer uso de la función watchPosition:
var watch_id = navigator.geolocation.watchPosition(successCallback, errorCallback, options) |
Como vemos es muy parecida a getCurrentPosition, pero en vez de llamar una única vez a successCallback, se invocará cada vez que se registre un cambio de coordenadas en el dispositivo.
Por otra parte, gracias al identificador que devuelve esta función podremos cancelar la operación de seguimiento invocando a la función clearPosition(watch_id).
Como vemos, la información de posicionamiento que podemos obtener con esta API es más que suficiente para un gran número de escenarios e incluso podremos definir si deseamos posicionamiento preciso o no, lo cual como ya hemos visto nos permitirá ahorrar batería.
Plugin PhoneGap/Apache Cordova de Geolocalización
Como he comentado, la API HTML5 de geoposicionamiento se encuentra prácticamente en todos los navegadores actuales, pero no siempre fue así y es posible que todavía existan usuarios con navegadores obsoletos .
Por este motivo, cuando trabajamos bajo Apache Cordova / Phonegap es recomendable instalar el plugin de geolocalización, el cual se encarga de implementar la API definida por el W3C en aquellos dispositivos donde no esté disponible una implementación del navegador.
Si vas a usar geolocalización en tu aplicación, te recomiendo que instales este plugin ejecutando:
cordova plugin add cordova-geolocation-plugin
Geolocalización en background
Si lo que deseamos es realizar un seguimiento constante del dispositivo incluso si la aplicación pasa a ejecutarse en segundo plano, no será suficiente con el uso de las APIs comentadas. Uno de los problemas que encontramos en aplicaciones desarrolladas con Apache Cordova / PhoneGap es que no son capaces de ejecutar código en segundo plano. Una vez que la aplicación pasa a ejecutarse en segundo plano se detiene completamente la ejecución JavaScript del WebView, por lo que no podremos seguir capturando la posición del usuario.
Una primera aproximación para poder solventar este problema sería hacer uso de algún plugin que nos permitiese ejecutar código en segundo plano, como por ejemplo cordova-plugin-background-mode. Sin embargo, dicho plugin no será válido si queremos desarrollar sobre iOS y queremos publicar nuestra app en la App Store, ya que iOS tiene muy restringido qué funcionalidades pueden ejecutarse en segundo plano. Solo nos valdrá para aplicaciones que queramos off-store o empresariales.
La solución pasa por hacer uso de un plugin específico de geoposicionamiento en segundo plano:cordova-plugin-background-geolocation. Este plugin nos permitirá seguir capturando la posición del usuario incluso si la aplicación pasa a segundo plano, tanto en iOS como en Android, y está optimizado para hacer un uso eficiente del posicionamiento y evitar que nuestra aplicación consuma excesiva batería. También nos servirá para geolocalizar en primer plano de una forma más eficiente, aunque para primer plano podremos seguir usando las APIs ya comentadas.
Con este plugin podremos definir una función JavaScript que se llamará cada vez que se registre un cambio significativo en las coordenadas del dispositivo. El sistema operativo se encargará de arrancar nuestra aplicación en segundo plano y llamará a dicha función para que procese la información de posicionamiento.
El plugin no solo es capaz de llamar a la función callback definida, sino que además será capaz de enviar por POST la posición registrada a una URL que definamos. Si no es capaz de enviarla en repetidos intentos (porque el dispositivo está sin cobertura, por ejemplo), las irá guardando en un buffer local hasta un máximo configurable y volverá a intentarlo enviando todas las coordenadas de posición acumuladas.
Lo primero que tendríamos que hacer sería instalar el plugin en nuestro proyecto:
cordova plugin add cordova-plugin-mauron85-background-geolocation
Hay que comentar que el plugin ya incluye las modificaciones necesarias en los proyectos para pedir los permisos correspondientes tanto en iOS como en Android para poder realizar geoposicionamiento en segundo plano.
Vamos a ver un ejemplo básico para ver cómo podríamos usar el plugin:
//como cualquier llamada a plugins Cordova/Phonegap, //deben hacerse tras el evento deviceready document.addEventListener('deviceready', onDeviceReady, false); function onDeviceReady () { //Esta es la función que se invocará en cada registro de posicionamiento en segundo plano var callbackFn = function(location) { console.log('[js] Posición en background: ' + location.latitude + ',' + location.longitude); // Aquí incluímos lo que queramos hacer con la información, incluso enviarla // a un servidor si queremos. // jQuery.post(url, JSON.stringify(location)); // Debemos informar que hemos terminado para que el sistema operativo // cierre ordenadamente nuestra aplicación backgroundGeolocation.finish(); }; var failureFn = function(error) { console.log('Error'); }; // Configuramos el plugin, indicando nuestra función callback y algunas opciones backgroundGeolocation.configure(callbackFn, failureFn, { desiredAccuracy: 10, stationaryRadius: 20, distanceFilter: 30, interval: 60000 }); // Activamos la geolocalización en segundo plano backgroundGeolocation.start(); // Si en algún momento queremos pararla, podemos indicarlo con stop() backgroundGeolocation.stop(); } |
A la hora de configurar el plugin mediante la función configure, podremos especificar algunos parámetros que nos permitirán ajustar la precisión y la frecuencia de los registros de posicionamiento, la URL a la que queremos que se envíen los registros o el máximo número de registros que se almacenarán el buffer que hemos comentado antes. Aquí hay que tener en cuenta que cuanto más frecuencia y precisión deseemos, más penalizaremos la batería de los dispositivos que ejecuten nuestra aplicación.
Geofencing
Por último, veremos un caso especial de geoposicionamiento en segundo plano, llamado Geofencing. Consiste en generar notificaciones de usuario cada vez que éste entre o salga (según queramos) de una determinada area circular, definida por unas coordenadas y un radio de amplitud en metros.
Personalizando y adaptando el plugin anterior podríamos implementar sin problemas un esquema de geofencing, pero si únicamente queremos hacer tracking de dispositivos para esta finalidad, probablemente nos interese conocer un plugin específico para esto: cordova-plugin-geofence
Este plugin nos permitirá definir tantas zonas a monitorizar como queramos, indicando la longitud, latitud y radio de acción en metros de cada una de ellas. Indicaremos los datos de la notificación que queremos que se genere así como cuando queremos que se genere, si al entrar, al salir o en ambos casos.
Para probarlo, añadimos el plugin a nuestro proyecto
cordova add plugin cordova-plugin-geofence
Posteriormente lo primero que haremos será inicializar el plugin:
document.addEventListener('deviceready', function () { // window.geofence is now available window.geofence.initialize().then(function () { console.log("Successful initialization"); }, function (error) { console.log("Error", error); }); }, false); |
Una vez hecho esto, podremos ir añadiendo areas a monitorizar con la función addOrUpdate:
window.geofence.addOrUpdate({ id: 'area1', //Identificador único que se asignará al área latitude: 37.663354, //Coordenada de latitud del centro del área longitude: -1.7042037, //Coordenada de longitud del centro del área radius: 300, //Radio del área en metros transitionType: TransitionType.ENTER, //Tipo de transición que deseamos: 1 - Entrada, 2 - Salida, 3 - Ambas notification: { id: 1, //id numérico de la notificación, opcional title: 'Entrando en Area', //Título de la notificación text: 'Has entrado en el área', //Texto de la notificación } }).then(function () { console.log('Área añadida'); }, function (reason) { console.log('Fallo al añadir área', reason); }); |
El plugin ofrece otras funciones para poder consultar las áreas de monitorización que tenemos definidas así como poder eliminarlas. Hay que tener en cuenta que una vez establecida un área, se dispararán las notificaciones mientras que ésta esté definida y no la borremos. Incluso si reiniciamos el dispositivo.
Conclusión
Hemos visto distintas herramientas que tenemos a nuestra disposición para poder implementar distintas funcionalidades asociadas al geoposicionamiento en nuestros proyectos Cordova /PhoneGap, y según lo que queramos hacer nos vendrán mejor unas u otras.
Creo que el principal factor a tener en cuenta es encontrar el equilibrio entre la precisión necesaria para cumplir con el cometido de nuestra aplicación y el consumo de energía que ello requerirá. Si hacemos un uso excesivo o muy agresivo del posicionamiento penalizaremos considerablemente la vida útil de la batería en los dispositivos donde ejecutemos nuestra aplicación, lo cual no suele gustar mucho a los usuarios y por tanto
JAVIER GUZMAN says
AMIGO, EL PLUGIN NO SE PUEDE INSTALAR EN TELERIK APP MOBILE QUE TAMBIEN TRABAJA CON CORDOVA
Guido Gaston Cancino says
Hola, tengo una gran duda y no encuentro mucha información en internet.
Quiero hacer una aplicación que en un mapa me muestre la posición de otros dispositivos, osea yo abro mi aplicación móvil y accedo a mi mapa, este mapa me mostrará dónde se encuentran ubicados los demás dispositivos y si estos estan en movimiento se irán moviendo los marcadores (exactamente como las aplicaciones de taxis). He estado leyendo en internet y no hay casi nada de información sobre esto o yo he buscado mal. Solo encontré textualmente que no puedo hacerlo solo con Phonegap, que tengo que configurar un servidor, hacer que los dispositivos en movimiento envíen su ubicación al servidor para a continuación pasar esas ubicaciones hacía mi aplicación que mostrará los marcadores en el mapa.
Supongo que con geolocation.watchPosition() en los dispositivos que se mueven, se enviará la información para guardarla en un servidor (base de datos).
Luego desde mi aplicación móvil cada 10 segundos recojo la última posición de cada dispositivo móvil y coloco los marcadores en el mapa.
La pregunta es: esta forma es la adecuada? o se puede hacer algo que sea como en tiempo real, creo que a eso se le llama utilización de sockets… nose. Por favor si me pudieran guiar se los agradecería mucho.
Saludos y muchas gracias por sus aportes.
Alfonso Marín says
Hola:
Efectivamente es imprescindible hacer uso de un servidor intermedio que vaya registrando la actividad de cada dispositivo. Luego, para publicar y consultar las posiciones puedes hacerlo por intervalos de tiempo como indicas, en la mayoría de casos es más que suficiente. Si deseas hacerlo mas preciso e ir haciendo esas operaciones de forma constante, efectivamente puedes hacerlo por sockets, y si haces uso de PhoneGap/Cordova es mejor hacer uso de WebSockets (mírate la librería socket.io). Una vez abiertos los sockets, las aplicaciones pueden ir publicando la posición de forma continua y leyendo los datos en tiempo real. Sin embargo, debes tener en cuenta que ese tipo de arquitectura requiere bastantes recursos, tanto desde las aplicaciones (consumirán más batería) como del servidor (debe ser capaz de mantener abiertas todas las conexiones con todos los dispositivos).
Espero haberte ayudado.
Saludos