¡Hola! Me parece que andas usando un bloqueador de anuncios =( ¿Nos desbloqueas? ¿Por qué?
F13

Funcion 13

Rastrea tu posición con HTML5 y Google Maps

Hace tiempo que no cuento ninguna batallita de BuSeViCi. Realmente tengo el proyecto abandonado… algún día lo retomaré supongo. El caso es que hoy me acordé de una funcionalidad que me costó bastante implementar.

La idea era sencilla, poder colocar un marcador en el lugar donde estaba el usuario e irlo moviendo conforme se moviera este, para que le fuera más sencillo ubicar una parada de autobús.

Veamos el proceso simplificado.

Solicitando la ubicación del usuario

Para obtener la ubicación en la tierra del usuario, primero necesitamos pedirle permiso para conservar su privacidad. No es algo que podamos saltarnos, está impuesto en el propio código del navegador. Tenemos dos funciones para hacerlo:

  • navigator.geolocation.getcurrentposition : Esta nos da la posición actual del usuario y nada más. Tras obtenerla (o no), llama a una función.
  • navigator.geolocation.watchposition : Esta nos vigila la posición del usuario, llama una función cada vez que esta cambia.

Parece que está claro que necesitamos la segunda, ¿no?

La función recibe los siguientes parámetros:

  1. Función a la que llamar si tenemos éxito obteniendo la posición
  2. Función a la que llamar si no tenemos éxito obteniendo la posición (quizá el usuario no dio permiso para ello)
  3. Objeto con propiedades adicionales de configuración.

Ahora vamos a ponernos manos a la obra:

(function($) {  
  var registrandoPosicion = false,
    idRegistroPosicion, ultimaPosicionUsuario, marcadorUsuario, mapa, div = document.getElementById('mapa');
  mapa = new google.maps.Map(div, {
    zoom: 13,
    center: new google.maps.LatLng(0, 0),
    mapTypeId: google.maps.MapTypeId.ROADMAP
  });

  function registrarPosicion() {
    if (registrandoPosicion) {
      registrandoPosicion = false;
      navigator.geolocation.clearWatch(idRegistroPosicion);
      limpiarUbicacion();
    } else {
      idRegistroPosicion = navigator.geolocation.watchPosition(exitoRegistroPosicion, falloRegistroPosicion, {
        enableHighAccuracy: true,
        maximumAge: 30000,
        timeout: 27000
      });
    }
  }

  function exitoRegistroPosicion(position) {
    if (!registrandoPosicion) {
      // Es la primera vez 
      registrandoPosicion = true;
      ultimaPosicionUsuario = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
      marcadorUsuario = new google.maps.Marker({
        position: ultimaPosicionUsuario,
        map: mapa
      });
    } else {
      var posicionActual = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
      ultimaPosicionUsuario = posicionActual;
      marcadorUsuario.setPosition(posicionActual);
    }
    mapa.panTo(ultimaPosicionUsuario);
  }

  function falloRegistroPosicion() {
    alert('No se pudo determinar la ubicación');
    limpiarUbicacion();
  }

  function limpiarUbicacion() {
    ultimaPosicionUsuario = new google.maps.LatLng(0, 0);
    if (marcadorUsuario) {
      marcadorUsuario.setMap(null);
      marcadorUsuario = null;
    }
  }
  $('#localizar').on('click', function(e) {
    e.preventDefault();
    registrarPosicion();
  });
})(jQuery);

Vamos a analizarlo poco a poco, como es costumbre. En primer lugar creamos variables, una que indica si estamos o no registrando la posición del usuario (por defecto no), la id del registro de la posición (la devuelve la función navigator.geolocation.watchPosition), la última posición del usuario, una variable para el marcador del usuario, otra para el mapa y la div donde estará el mapa. Hasta ahora nada raro.

Luego inicializamos el mapa. Si algo de esto te resulta raro, te recomiendo que le eches un ojo a los artículos sobre Google Maps que escribí.

Ahora nos encargamos de definir las funciones que llevarán las riendas. La primera es registrarPosición. Esta función se llamará al pulsar un botón y se encargará de hacer de interruptor. En caso de que estemos registrando la posición, dejaremos de hacerlo y borraremos los datos del usuario y en caso de que no, comenzaremos a hacerlo. Como ves, a la función watchPosition le pasamos un objeto adicional de parámetros.

{ enableHighAccuracy : true, maximumAge : 30000, timeout : 27000 }

En el primero indicamos a la función, que estamos usando un dispositivo con mucha precisión, como son los teléfonos móviles por ejemplo. El siguiente parámetro, maximumAge, nos sirve para indicar el tiempo (en milisegundos) entre cada medición. Esto nos ayuda a ahorrar ancho de banda y recursos del dispositivo. En nuestro caso, hacemos 2 mediciones por minuto. El último parámetro es el tiempo máximo que le damos al navegador para obtener la petición, en caso de que no pueda pasado ese tiempo (mala cobertura por ejemplo), pasaremos a llamar a la función falloRegistroPosicion.

La función exitoRegistroPosicion recibe como parámetro un objeto position. En caso de que estemos no estemos registrando la posición, porque es la primera vez que se llama a esa función, dejamos constancia de que lo estamos haciendo, guardamos la posición del usuario y creamos un marcador con la posición del usuario. En caso de que ya hayamos hecho ese paso alguna vez, simplemente movemos el marcador y finalmente centramos el mapa en el marcador del usuario.

La función de falloRegistroPosicion no tiene mucho misterio ya que únicamente hacemos un poco de limpieza y mandamos una alerta de que no ha sido posible obtener la posición.

limpiarUbicacion se encarga de deshacernos de cualquier rastro de la actividad del usuario, vaciando el marcador y la posición del usuario.

Finalmente, estamos atentos al click del botón con id localizar, cuando ocurra llamamos a la función registrarPosicion que inicia todo el proceso que ya hemos explicado.

¿Y dónde está la dificultad?

Bueno, ahora mismo no parece obvio pero si montas esto y lo visualizas desde un servidor en tu móvil con una 3G, el navegador acaba por petar. ¿Por qué?

Lo que ocurre es que cada 30 segundos se va actualizando la posición y actualizamos el marcador, para ayudar al usuario a situarse. Esa operación acaba por ser muy costosa para el navegador y acabará por colgarse. Una posible solución sería aumentar el tiempo de maximumAge al doble por ejemplo, pero reduciríamos la utilidad de la función.

La solución es…

Reaccionando solo si el usuario se ha movido X metros

function exitoRegistroPosicion(position) {  
  if (!registrandoPosicion) {
    // Es la primera vez 
    registrandoPosicion = true; 
    ultimaPosicionUsuario = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
    marcadorUsuario = new google.maps.Marker({
      position: ultimaPosicionUsuario,
      map: mapa
    });
  } else {
    var posicionActual = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
    if (google.maps.geometry.spherical.computeDistanceBetween(posicionActual, ultimaPosicionUsuario) > 100) { // Metros 
      ultimaPosicionUsuario = posicionActual;
      marcadorUsuario.setPosition(posicionActual);
    }
  }
  mapa.panTo(ultimaPosicionUsuario);
}

Como ves, con una simple línea, reaccionamos únicamente si el usuario se ha movido X metros, en este caso 100. Si quieres saber cómo medir la distancia entre dos marcadores con Google, no dejes de leer este artículo.

Como ves, es realmente sencillo registrar la posición de un usuario e ir reaccionando acordemente. Si el artículo te pareció interesante, útil o incluso equivocado, por favor considera el dejar un comentario. ¡Lo apreciaré mucho!

Programador Front-end en First + Third y Potato. Trabajando con JavaScript y HTML5 desde el corazón de Sevilla.

Comentarios ¡Únete a la conversación!