En el pasado, para detectar la localización de un dispositivo cliente desde una aplicación web, era necesario inspeccionar la dirección IP del cliente y averiguar, usualmente mediante un servicio externo, dónde se encontraba el dispositivo.
Sin embargo, junto con el HTML5, el W3C ha desarrollado un conjunto de APIs que permiten obtener la información de posicionamiento geográfico con JavaScript, desde el lado del cliente del dispositivo, es decir, un iPhone, un Android o incluso los navegadores de escritorio convencionales.
La funcionalidad de estas APIs depende del grado de implementación en los navegadores web, pero actualmente la mayoría de los navegadores modernos ya están adoptando estas funcionalidades y prestan soporte nativo a la geolocalización HTML5.
Recursos
- Especificaciones de geolocalización del W3C: W3C Geolocation API
La API de geolocalización no identifica cómo se ha determinado la información de ubicación. Dicha información puede obtenerse vía GPS, por triangulación de antenas de telefonía o puntos de acceso Wi-FI, mediante la dirección IP u otras técnicas, así como combinando varias de ellas.
Si el dispositivo no dispone de GPS, el navegador utilizará triangulación de antenas de telefonía, la dirección IP o los puntos de acceso Wi-FI cercanos y pasará esa información a un servicio externo de geolocalización para obtener la posición.
Si el dispositivo dispone de GPS, el navegador es libre de utilizarlo, pero no está obligado a ello, por lo que no está garantizado que el navegador use geolocalización basada en GPS simplemente porque dispone de un chip GPS. En caso de priorizar la precisión utilizará el GPS, pero si prioriza la rapidez o un menor consumo de energía utilizará otros métodos.
Desconocer cómo se logró la geolocalización no suele ser un problema, pero la exactitud de la información de ubicación puede variar mucho dependiendo de la técnica utilizada.
La API de geolocalización reside en el objeto navigator en JavaScript:
- window.navigator.geolocation
La API de geolocalización proporciona dos maneras de trabajar:
- Método navigator.geolocation.getCurrentPosition: permite obtener una única posición.
- Método navigator.geolocation.watchPosition: permite obtener la posición reiteradamente, cada vez que el dispositivo cambia de ubicación. Se cancela con el método navigator.geolocation.clearWatch.
La API de geolocalización define varias propiedades para el objeto position, pero sólo tres de ellas han sido implementadas consistentemente por la mayoría de navegadores:
- position.coords.latitude
- position.coords.longitude
- position.coords.accuracy
Ejemplo con getCurrentPosition()
El método getCurrentPosition() permite obtener la localización actual del cliente.
Vamos a ver un ejemplo con getCurrentPosition():
- La etiqueta DTD <!DOCTYPE html> define la página como un documento HTML5:
<!DOCTYPE html>
- El evento JavaScript onclick del botón btnGet inicia el proceso de consulta de la ubicación actual llamando a la función get_current_position():
<div> <button id="btnGet" onclick="get_current_position()">Find my location</button> </div>
Esto es lo que se ve en este punto:
- Usaremos el objeto navigator.geolocation para detectar si el navegador soporta geolocalización HTML5:
if (navigator.geolocation) { // geolocation IS available navigator.geolocation.getCurrentPosition(success, error, options); } else { // geolocation IS NOT available geo_is_not_available(); }
- El método getCurrentPosition() acepta tres parámetros:
navigator.geolocation.getCurrentPosition(success, error, options);
Cuando se llama a este método, si tiene éxito ejecuta la función success pasándole el objeto position como argumento. Si el intento falla, ejecuta la función error (si existe) pasándole el objeto error como argumento.
El parámetro options es una cadena que permite establecer algunas opciones de geolocalización, como:
- maximumAge: tiempo (en milisegundos) para almacenar en caché la posición obtenida. Valores: 0 (por defecto).
- timeout: tiempo (en milisegundos) máximo de espera hasta que el servicio de ubicación responda. Valores: 0 (por defecto).
- enableHighAccuracy: establece si el navegador debe intentar utilizar los servicios de localización de alta precisión (por ejemplo, GPS) cuando sean compatibles con el navegador y el dispositivo. Valores: true, false (por defecto).
- La función geo_success() se encarga de hacer algo con el objeto position. Por ejemplo, podría mostrar un mensaje emergente con los datos obtenidos:
function geo_success(position) { alert('Latitude: ' + position.coords.latitude + '\xB0\n' + 'Longitude: ' + position.coords.longitude + '\xB0\n' + 'Accuracy: ' + position.coords.accuracy + ' m'); }
Esto es lo que veríamos:
Otra opción sería mostrar un mapa, por ejemplo de Google Static Map API:
function geo_success(position) { var latitude = position.coords.latitude; var longitude = position.coords.longitude; var accuracy = position.coords.accuracy; document.getElementById("out").innerHTML = '<p>Latitude: ' + latitude + '°<br>Longitude: ' + longitude + '°<br>Accuracy: ' + accuracy + ' m</p>'; var img = new Image(); img.src = "http://maps.google.com/maps/api/staticmap?sensor=false¢er=" + latitude + "," + longitude + "&zoom=14&size=600x200&markers=color:blue|label:S|" + latitude + ',' + longitude; document.getElementById("out").appendChild(img); }
Este sería el resultado:
- La función geo_error() se encarga de manejar los errores. Puede ocurrir un error si el usuario no comparte los datos de geolocalización (PERMISSION_DENIED), si el sistema no puede detectar la posición actual (POSITION_UNAVAILABLE), si se supera el tiempo máximo de espera (TIMEOUT), un error desconocido (UNKNOWN_ERROR) o por otros motivos. Por ejemplo, para mostrar el error haremos:
function geo_error(error) { alert('ERROR(' + error.code + '): ' + error.message); }
Si se produce algún error nos mostrará un aviso:
Debemos tener en cuenta que el navegador pide autorización al usuario para compartir la ubicación con la aplicación, por lo que si el usuario no comparte los datos de geolocalización obtendremos un error:
- Si el navegador no soporta geolocalización HTML5 se ejecutará la función geo_is_not_available(). Por ejemplo, podría mostrar una alerta emergente con un mensaje de error:
function geo_is_not_available() { alert('Geolocation is not supported by your browser'); }
En un navegador que no soporte geolocalización HTML5 veremos este aviso:
- Geolocalización basada en la dirección IP
En aquellos navegadores que no soportan la geolocalización HTML5, para conocer la ubicación del cliente tendremos que utilizar un servicio de geolocalización externa basado en la dirección IP. Estos servicios mapean las direcciones IP a ubicaciones geográficas utilizando grandes bases de datos de geolocalización.
Generalmente hacen un buen trabajo, pero a veces pueden sufrir problemas debido a que la dirección IP está asociada a una ubicación incorrecta, la dirección IP sólo se puede asociar a un área geográfica muy amplia o la dirección IP no aparece en la base de datos y no puede ser mapeada.
Aunque cuando se utiliza un servicio de geolocalización externa la precisión no es tan buena como cuando se usa la geolocalización nativa del dispositivo, e incluso puede no funcionar el servicio, cuando el GPS o la triangulación no están disponibles son una buena alternativa.
Existen muchos servicios de geolocalización externa. Por ejemplo, podemos combinar l2.io para obtener la IP del cliente y geoplugin.com para la geolocalización basada en IP.
Para obtener la IP del cliente con l2.io incluiremos una etiqueta <script> que al cargar la URL asigna a la variable global my_ip, pasada como parámetro, el valor de la IP del cliente:
<script type="text/javascript" src="http://l2.io/ip.js?var=my_ip"></script>
En JavaScript podremos usar la variable global my_ip:
// my_ip: direccion IP obtenida recurriendo a un servicio externo alert(my_ip);
Una vez que tenemos la IP del cliente, para la geolocalización basada en IP con geoplugin.com incluiremos una etiqueta <script> que nos proporcionará una serie de funciones de geolocalización basadas en IP, que podremos utilizar en nuestra página:
<script type="text/javascript" language="JavaScript" src="http://www.geoplugin.net/javascript.gp"></script>
En JavaScript podremos usar diversas funciones de geolocalización, como:
var my_latitude = geoplugin_latitude(); // '42.814098' var my_longitude = geoplugin_longitude(); // '-1.6412' var my_city = geoplugin_city(); // 'Pamplona' var my_region = geoplugin_region(); // 'Navarre' var my_countryName = geoplugin_countryName(); // 'Spain'
Una vez que tengamos la localización del cliente, enviaremos el resultado a la función geo_success():
var position = { coords : { latitude: my_latitude, longitude: my_longitude }, address : { city: my_city, region: my_region, country: my_countryName } }; geo_success(position);
El ejemplo completo del método getCurrentPosition() será el siguiente:
<!DOCTYPE html> <html> <head> <script type="text/javascript" src="http://l2.io/ip.js?var=my_ip"></script> <script type="text/javascript" language="JavaScript" src="http://www.geoplugin.net/javascript.gp"></script> <script> function get_current_position() { document.getElementById("out").innerHTML = "<p>Locating...</p>"; var geo_options = {enableHighAccuracy:true, maximumAge:30000, timeout:27000}; if (navigator.geolocation) { // geolocation IS available navigator.geolocation.getCurrentPosition(geo_success, geo_error, geo_options); } else { // geolocation IS NOT available geo_is_not_available(); } } function geo_success(position) { var latitude = position.coords.latitude; var longitude = position.coords.longitude; var accuracy = position.coords.accuracy; document.getElementById("out").innerHTML = '<p>Latitude: ' + latitude + '°<br>Longitude: ' + longitude + '°<br>Accuracy: ' + accuracy + ' m</p>'; var img = new Image(); img.src = "http://maps.google.com/maps/api/staticmap?sensor=false¢er=" + latitude + "," + longitude + "&zoom=14&size=600x200&markers=color:blue|label:S|" + latitude + ',' + longitude; document.getElementById("out").appendChild(img); } function geo_error(error) { document.getElementById("out").innerHTML = '<p>ERROR(' + error.code + '): ' + error.message + '</p>'; } function geo_is_not_available() { var my_status = geoplugin_status(); // '200' var my_latitude = geoplugin_latitude(); // '42.814098' var my_longitude = geoplugin_longitude(); // '-1.6412' var my_city = geoplugin_city(); // 'Pamplona' var my_region = geoplugin_region(); // 'Navarre' var my_countryName = geoplugin_countryName(); // 'Spain' if (my_status == 404 ) { // 200 - OK // 206 - Only country data returned, no city values found // 404 - No data found for the IP var error = { code : 404, message : "Error 404 - No data found for the IP" }; geo_error(error); return; } var position = { coords : { latitude: my_latitude, longitude: my_longitude }, address : { city: my_city, region: my_region, country: my_countryName } }; geo_success(position); } </script> </head> <body> <div> <button id="btnGet" onclick="get_current_position()">Find my location</button> </div> <div id="out"></div> </body> </html> |
Ejemplo con watchPosition() y clearWatch()
Los métodos watchPosition() y clearWatch() permiten obtener la ubicación cada vez que el dispositivo cambia de ubicación, en vez de recibir sólo una localización.
Vamos a ver un ejemplo con watchPosition() y clearWatch():
- El método watchPosition() tiene los mismos parámetros que el método getCurrentPosition(). La diferencia está en que el método watchPosition() vuelve a proporcionar la ubicación e invocar las funciones callback (success o error, si existe) cada vez que la posición del dispositivo cambia.
El método watchPosition() devuelve un valor entero que identifica de forma unívoca el proceso de vigilancia:
watchProcess = navigator.geolocation.watchPosition(success, error, options);
- Cuando se llama al método clearWatch() con el identificador que devuelve el método watchPosition(), entonces watchPosition() deja de solicitar nuevas posiciones y deja de invocar las funciones callback:
navigator.geolocation.clearWatch(watchProcess);
El ejemplo completo del método watchPosition() será el siguiente:
<!DOCTYPE html> <html> <head> <script> var watchProcess = null; function init_watch_position() { document.getElementById("out").innerHTML = "<p>Locating...</p>"; var geo_options = {enableHighAccuracy:true, maximumAge:30000, timeout:27000}; if (navigator.geolocation) { // geolocation IS available if (watchProcess == null) { watchProcess = navigator.geolocation.watchPosition(geo_success, geo_error, geo_options); } } else { // geolocation IS NOT available geo_is_not_available(); } } function stop_watch_position() { if (watchProcess != null) { navigator.geolocation.clearWatch(watchProcess); watchProcess = null; } } function geo_success(position) { var latitude = position.coords.latitude; var longitude = position.coords.longitude; var accuracy = position.coords.accuracy; document.getElementById("out").innerHTML = '<p>Latitude: ' + latitude + '°<br>Longitude: ' + longitude + '°<br>Accuracy: ' + accuracy + ' m</p>'; var img = new Image(); img.src = "http://maps.google.com/maps/api/staticmap?sensor=false¢er=" + latitude + "," + longitude + "&zoom=14&size=600x200&markers=color:blue|label:S|" + latitude + ',' + longitude; document.getElementById("out").appendChild(img); } function geo_error(error) { document.getElementById("out").innerHTML = '<p>ERROR(' + error.code + '): ' + error.message + '</p>'; } function geo_is_not_available() { document.getElementById("out").innerHTML = "<p>Geolocation is not supported by your browser</p>"; } </script> </head> <body> <div> <button id="btnInitWatch" onclick="init_watch_position()">Monitor my location</button> <button id="btnStopWatch" onclick="stop_watch_position()">Stop monitoring</button> </div> <div id="out"></div> </body> </html> |
Muy buen aporte, gracias
Maravilloso aporte…
No se si sería mucho pedir, y desde mi desconocimiento del Javascrip…
Me gustaría añadir a este código un boton (despues de un “Stop monitoring”), para lanzar la longitud y latitud a otro fichero php, para recoger el dato como GET o POST…
De no ser posible esto, propongo subir a un Base de datos Mysql estos dos datos….
Gracias…