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).

Yo soy pro-defensor de Mozilla Firefox, tanto por ética como por su rendimiento y el estar a la última en temas de desarrollo web (si eres desarrollador web prueba Firefox Developer, una maravilla). Así que el siguiente código está probado para Firefox y puede 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/{id}/tiles/{z}/{x}/{y}?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/{id}/tiles/{z}/{x}/{y}?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…