Calcular rutas con OpenStreetMap partiendo de la geolocalización del usuario

Comparte este post

Hace unos días veíamos los conceptos básicos para integrar OpenStreetMap en nuestras aplicaciones web.

Hoy vamos a ir un paso más allá y veremos un ejemplo de cómo calcular rutas partiendo de la geolocalización del usuario hasta el punto destino que estará predefinido (por ejemplo, nuestro negocio, un punto de interés, etc.).

Lo primero, debemos agregar un nuevo plugin que se encargará de realizar los cálculos de ruta y dibujar el trazado: Leaflet Routing Machine.

Este sería el código base de nuestra página HTML, con las llamadas tanto a las librerías de Leaflet como a las de Routing Machine.

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css" />
    <link rel="stylesheet" href="https://unpkg.com/leaflet-routing-machine@latest/dist/leaflet-routing-machine.css" />
    <script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine@latest/dist/leaflet-routing-machine.js"></script>
    <title>Calcular rutas</title>
    <style>
        #mapa{
            height: 600px;
        }
    </style>
</head>

<body>
    <div id="mapa"></div>
</body>
</html>

Comprobar que el dispositivo (navegador) soporta geolocalización

Aquí me voy a centrar en la documentación de Mozilla donde no he tenido ningún problema para geolocalizarme con Firefox (en Chrome/Chromium si he tenido problemas para geolocalizarme debido a unas limitaciones que ha impuesto Google a la geolocalización a través de HTML5).

El siguiente código está probado para Firefox y podría no funcionar correctamente en otros navegadores.

<script>
    if(navigator.geolocation){
        //Funciona la geolocalización
    }
    else{
        //No funciona la geolocalización
    }
</script>

Con el código anterior comprobamos si la geolocalización está disponible en el dispositivo o navegador web del usuario, en caso afirmativo dibujaremos un mapa donde se mostrará la ruta calculada desde la posición del usuario hasta nuestra ubicación de interés.

En caso de que la geolocalización no funcione, podemos dibujar un mapa normal con la ubicación de nuestro destino (sin calcular rutas).

<script>
    if(navigator.geolocation){
        navigator.geolocation.getCurrentPosition(function(position){
            var latitude = position.coords.latitude;
            var longitude = position.coords.longitude;

            var mymap = L.map('mapa', {
                center: [latitude, longitude],
                zoom: 12
            });

            L.tileLayer('https://api.mapbox.com/styles/v1//tiles///?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
                maxZoom: 25,
                attribution: 'Datos del mapa de &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a>, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' + 'Imágenes © <a href="https://www.mapbox.com/">Mapbox</a>', 
                id: 'mapbox/streets-v11'
            }).addTo(mymap);

            L.Routing.control({
                waypoints: [
                    L.latLng(latitude, longitude),
                    L.latLng(37.17059, -3.60552)
                ],
                language: 'es'
            }).addTo(mymap);
        });
    }
    else{
        var mymap = L.map('mapa', {
            center: [37.17059, -3.60552],
            zoom: 17
        });

        L.tileLayer('https://api.mapbox.com/styles/v1//tiles///?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
            maxZoom: 25,
            attribution: 'Datos del mapa de &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a>, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' + 'Imágenes © <a href="https://www.mapbox.com/">Mapbox</a>', 
            id: 'mapbox/streets-v11'
        }).addTo(mymap);
    }
</script>

Obtener las coordenadas del usuario

Vamos a comentar un poco el codigo. En caso de que la geolocalización esté disponible, el método que obtiene la ubicación del usuario es getCurrentPosition, al que le pasamos una función de callback con un parámetro (position) en el que podemos obtener tanto la latitud como la longitud por separado:

navigator.geolocation.getCurrentPosition(function(position){
    var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;
});

A continuación dibujamos el mapa posicionándolo en la ubicación del usuario:

var mymap = L.map('mapa', {
    center: [latitude, longitude],
    zoom: 12
});

Hay que tener en cuenta que la geolocalización a través de IP puede no ser muy fiable. Si la central a la que estás conectado se encuentra en tu misma población, la geolocalización te ubicará más o menos en tu pueblo, pero por ejemplo, mi conexión ADSL proviene de una red Air Wifi, cuya central se encuentra en otra ciudad, así que el navegador me geolocaliza en esa otra ciudad…

Esto no ocurre con el navegador de mi smartphone si además tengo activada la Ubicación, en este caso me geolocaliza a la perfección.

Tras dibujar el mapa llamamos al Routing Machine pasándole las dos coordenadas a calcular:

Llamar al método para calcular la ruta

L.Routing.control({
    waypoints: [
        L.latLng(latitude, longitude), //dirección obtenida del usuario
        L.latLng(37.17059, -3.60552) //dirección fija de destino
    ],
    language: 'es'
}).addTo(mymap);

No olvides especificar el código de idioma para que las indicaciones aparezcan en español.

NOTA IMPORTANTE:

El cálculo de ruta se origina en un servidor OSRM de demostración cuya finalidad es la de probar la API, de hecho si abrimos la consola del inspector de elementos nos avisa de esto:

You are using OSRM’s demo server. Please note that it is NOT SUITABLE FOR PRODUCTION USE. Refer to the demo server’s usage policy: https://github.com/Project-OSRM/osrm-backend/wiki/Api-usage-policy

To change, set the serviceUrl option.

Please do not report issues with this server to neither Leaflet Routing Machine or OSRM – it’s for demo only, and will sometimes not be available, or work in unexpected ways.

Please set up your own OSRM server, or use a paid service provider for production.

El texto nos advierte que no es recomendable usarlo para entornos de producción, ya que el servidor puede no estar disponible en determinados momentos.

Por lo que si queremos implementar el cálculo de rutas en una aplicación web de producción, deberíamos configurar nuestro propio servidor OSRM en nuestro servicio de hosting (consultar al soporte técnico) y modificar la variable serviceURL dentro del leaflet-routing-machine.js

Calcular rutas enviando las coordenadas a OpenStreetMap.org

Si no queremos configurar un servidor OSRM siempre podemos hacer el cálculo de rutas directamente en la web de openstreetmap.org.

Para ello podemos enviar a través de un formulario HTML, las coordenadas mediante GET al action=”https://www.openstreetmap.org/directions?engine=fossgis_osrm_car” si queremos hacer la ruta en coche o a “https://www.openstreetmap.org/directions?engine=fossgis_osrm_foot” si queremos calcular la ruta a pie.

La cadena con las coordenadas tiene que tener la siguiente síntaxis:

latitud1,longitud1;latitud2,longitud2, por ejemplo:

37.17618,-3.96473;37.1894,-3.7216

Las coordenadas del usuario tendrán que enviarse en el formulario de forma dinámica, es decir, a través de una variable, mientras que las coordenadas de destino pueden especificarse estáticamente.

Para el ejemplo, vamos a crear una página HTML con el formulario, y un archivo de funciones .JS que obtendrá las coordenadas del usuario, las pasará al formulario y lo enviará a openstreetmap.org. En el código he integrado jQuery para hacer las escuchas al botón y enviar los datos.

Página HTML:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"
		integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
        crossorigin="anonymous">
    </script>
    <script src="js/funciones.js"></script>
    <title>Document</title>
</head>
<body>
    <h2>Cómo llegar</h2>

    <form id="formulario_rutas" method="get" action="https://www.openstreetmap.org/directions?engine=fossgis_osrm_car">    
        <input id="coordenadas" type="hidden" name="route" value=""/>
        <input id="calcular_ruta" type="button" value="Cómo llegar"/>
    </form>
</body>
</html>

Crearemos un formulario con un campo oculto (hidden) que contendrá como valor la cadena que pasaremos a openstreetmap ya formateada como éste espera recibirla. Y un botón que al hacer clic ejecute todo el proceso.

Archivo JS:

"use strict";

$(document).ready(function(){
    $("#calcular_ruta").click(function(){
        if(navigator.geolocation){
            navigator.geolocation.getCurrentPosition(function(position){
                var latitude = position.coords.latitude;
                var longitude = position.coords.longitude;

                var cadenaRuta = latitude+","+longitude+";37.1894,-3.7216";
                $("#coordenadas").val(cadenaRuta);
                $("#formulario_rutas").submit();
            });
        }
    });
});

En el archivo de funciones, captamos el clic al botón del formulario, geolocalizamos al usuario y construimos la cadenaRuta que pasaremos como valor al campo hidden (coordenadas) del formulario. Por último hacemos un submit al formulario y OpenStreetMap hará la magia.

No queda integrado en nuestra web como sí pasaría con un servidor OSRM, pero al menos es una solución elegante.

Paso a paso y no sin alguna dificultad vamos avanzando hacia la no dependencia del todopoderoso GoogleMaps. Seguiremos próximamente…

14 Comments

  • Buenas Daniel, muy buen tutotrial. Mi duda es si con Open Street Map se puede crear rutas, es decir, poner más de dos puntos en la misma ruta.

    El ejemplo sería una ruta turística, primero vamos a la catedral y luego al museo, seguidamente a esta calle emblemática y así hasta crear una ruta.

    • A

      Hola Luis, realmente no he profundizado con la API más allá de estos artículos que publiqué, pero seguramente se pueda hacer, es cuestión de mirar en la API y sobre todo buscar ejemplos prácticos.

  • buenas, no se puede cargar una direccion? y calcular la longitud y latitud?

  • Diego Murrugarra

    Estimado Daniel, buenos días. Quería consultarte y disculpa mi ignoracia cómo se puede implementar un servidor OSRM en mi hosting ya que la información que haz compartido me ha sido de mucha utilidad y quisiera ejecutar estos conocimientos en un proyecto real.

  • Hola Daniel!.
    Mi nombre es Lucía y me encuentro realizando una práctica para la obtención de un Masterd de programación web.
    Siguiendo los pasos que propones he conseguido insertar el mapa con la localización de la empresa y la geolocalización del cliente, pero no encuentro la forma de poder incluir una imagen (mi logotipo), en lugar del marcador que aparece por defecto y añadirle a este un popup con información sobre la empresa. ¿Serías tan amable de indicarme el código que debería añadir?. Seguro nos sirve a muchos usuarios. GRACIAS por anticipado!!!

    • A
      Daniel, Gestionatuweb.net

      Hola Lucía, visita este artículo https://www.gestionatuweb.net/integra-openstreetmap-en-tus-aplicaciones-web-con-leaflet-y-mapbox/ ahí se explica cómo personalizar el marcador y el popup al hacer clic sobre él.

      Un saludo!

      • Hola Daniel, gracias por tu ayuda me ha servido de mucho en la elaboración de un prototipo. Sabes he seguido paso a paso tu blog incluido el link que nos recomiendas en este comentario y tengo buenos resultados sin embargo quiero personalizar mi market y lo he conseguido; pero al ya generar la ruta con la localización del usuario, el marker personalizado sale independiente al de la ruta generada. Podrías guiarme, te lo agradezco mucho.

        Saludos

  • Muchas gracias por el aporte! muy bien explicado , me ha servido y funcionado perfectamente.
    No llevo mucho programando y todo que son programas externos suele dar problemas y con google maps que se ha vuelto de pago no es rentable… osm es una alternativa bastante buena. Gracias de nuevo

    • A
      Daniel, Gestionatuweb.net

      Gracias a ti por tu comentario.

      Para que OpenStreetMap crezca es necesario que colaboremos haciendo aportaciones sobre nuestro entorno. A GoogleMaps le hicimos gran parte del trabajo añadiendo localizaciones y punto de interés. Es hora de hacerlo con OpenStreetMap.

  • Muy buena explicación, he seguido tus indicaciones al igual que varios ejemplos similares que he encontrado en la web, pero el resultado siempre es el mismo me dibuja el mapa y los puntos pero no dibuja la ruta entre los dos puntos, alguna idea de lo que puede ser?

    También me gustaría saber como puedo conseguir la distancia y el tiempo entre dos direcciones, no entre dos coordenadas, de manera interna, ya he utilizado añadiendo geocoder: L.Control.Geocoder.nominatim({}) al L.Routing.control pero con el mismo resultado me sale el cuadro para introducir las direcciones y esto lo hace bien, pero no dibuja la ruta en el mapa.

    Gracias de antemano por la ayuda.

    • A
      Daniel, Gestionatuweb.net

      Hola Luis, pues no sabría decirte por qué no te dibuja la línea, habría que ver el código por si hay algún error o falta algo.

      En lo de resolver distancias y tiempo no te puedo orientar pues es algo que aún no he estudiado, ahora es que ando justo de tiempo, pero lo probaré próximamente.

      Gracias por tu comentario.

      • Por fin lo he conseguido Sergio, el certificado del servidor de OSM está caducado, por eso no me funcionaba ningún ejemplo incluido el tuyo. Utilizando el servidor de mapbox si me funciona por fin!!. gracias por tu tiempo.

        • A
          Daniel, Gestionatuweb.net

          Perfecto, yo también desconocía que caducaba el certificado del servidor OSM de pruebas, por eso comento que para proyectos en producción sería interesante instalar nuestro propio OMS.

          Saludos.

      • Hola Daniel, gracias por responder tan rápido, estoy escribiendo el primer ejemplo siguiendo las lineas que has puesto, podrías mandarme el fichero con el código del primer ejemplo que te funciona para compararlo con el mio, me estoy volviendo loco y no encuentro el fallo.

        Gracias.

Deja tu comentario