javascript

Mostrar ubicación en mapa en tiempo real – Programación

En el artículo de hoy te mostraré cómo mostrar la ubicación del usuario en un mapa en tiempo real e ir actualizando el marcador del mapa conforme el usuario se mueva.

Esto puede servir para varias cosas. La más simple es mostrarle al usuario su ubicación en el mapa, pero puede enfocarse a algo tan complejo como hacer algo como Uber (enviando la ubicación a un servidor y todo eso).

Por cierto, vamos a usar JavaScript y OpenLayers (alternativa a Google Maps): tecnologías gratuitas y open source que no nos obligan a aceptar licencias ni pagar.

Obviamente esto se puede usar en dispositivos móviles Android o iOS mientras tengan un navegador web decente. Y más tarde podemos convertir la app web en una PWA para que sea algo así como una app nativa (como hice con la app de transportes).

Pero bueno, vamos a lo interesante: cómo obtener la ubicación del usuario y mostrarla en el mapa mientras se mueve.

Puedes acceder a la demostración justo ahora desde este enlace. Te debería mostrar tu ubicación actual con el símbolo de un auto.

Explicación

Como mencioné en la introducción vamos a usar JavaScript para obtener la ubicación del usuario y suscribirnos a las actualizaciones de la misma. Recuerda que ya hice un post sobre ello en donde expliqué que solo se puede usar en localhost o en dominios con https.

Por otro lado vamos a usar OpenLayers, una alternativa gratuita a Google Maps. Funciona incluso en el lugar recóndito en donde vivo.

El algoritmo es simple:

Nos suscribimos a las actualizaciones de ubicación, y en cada actualización comprobamos si ya hemos creado el mapa. Si ya lo hemos creado entonces actualizamos el marcador y centramos el mapa, en caso de que no, creamos el mapa.

Ya hice un post sobre OpenLayers, te invito a leerlo.

Plantilla HTML

Comencemos viendo el HTML de la página. Tenemos el título y el div en donde vamos a montar el mapa. También estoy importando el archivo CSS y JS de OpenLayers.

<!--

    Programado por Luis Cabrera Benito 
  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
    
    Blog:       https://parzibyte.me/blog
    Ayuda:      https://parzibyte.me/blog/contrataciones-ayuda/
    Contacto:   https://parzibyte.me/blog/contacto/
-->
<!doctype html>
<html lang="es">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1,
            shrink-to-fit=no">
    <meta name="author" content="Parzibyte">
    <title>Ubicación en mapa - By Parzibyte</title>

    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="css/ol.css">
</head>

<body>
    <main role="main" class="container-fluid">
        <div class="row">
            <div class="col-12 text-center">
                <h1>
                    Mostrar ubicación en mapa
                </h1>
                <p id="estado"></p>
                <a href="https://parzibyte.me/blog">By Parzibyte</a>
            </div>
            <div class="col-12">
                <div id="mapa" style="min-height: 500; height: 600px;"></div>
            </div>
        </div>
    </main>
    <script type="text/javascript" src="js/ol.js"></script>
    <script type="text/javascript" src="js/script.js"></script>
</body>

</html>

Lo importante es el div con el id mapa. Por cierto, en estas últimas versiones de OpenLayers necesitamos que el div tenga un alto definido por nosotros.

Por cierto, también tengo el párrafo con id estado, en él mostraremos las últimas coordenadas obtenidas y la fecha y hora en la que fueron obtenidas.

Obteniendo ubicación

Primero veamos la solicitud de ubicación en donde indicamos cada cuánto deberíamos recibir actualizaciones, la precisión, etcétera.

const opcionesDeSolicitud = {
    enableHighAccuracy: true, // Alta precisión
    maximumAge: 0, // No queremos caché
    timeout: 5000 // Esperar solo 5 segundos
};

Después definimos dos funciones. Una en donde vamos a recibir las actualizaciones de ubicación (que será cuando el usuario se mueva) y otra en caso de error.


const onActualizacionDeUbicacion = ubicacion => {
    // Aquí ya tenemos la ubicación ;)
}

const onErrorDeUbicacion = err => {
    console.log("Error obteniendo ubicación: ", err);
}

Y finalmente lo solicitamos indicando los callbacks:

idWatcher = navigator.geolocation.watchPosition(onActualizacionDeUbicacion, onErrorDeUbicacion, opcionesDeSolicitud);

Por cierto, el idWatcher es importante en caso de que después quieras cancelar la solicitud de obtener la ubicación con JS. Ya lo expliqué con mayor detalle en el post que cité anteriormente.

Dibujando ubicación en mapa

Ahora veamos el cuerpo de la función onActualizacionDeUbicación que será invocada cada que la ubicación del usuario cambie.

Si es la primera vez que se invoca, vamos a dibujar el mapa. Agregamos una capa y a esa capa le agregamos el marcador que será una imagen.

if (!mapa) {
    // Primera vez, lo creamos y centramos
    mapa = new ol.Map({
        target: 'mapa', // el id del elemento en donde se monta
        layers: [
            new ol.layer.Tile({
                source: new ol.source.OSM()
            })
        ],
        view: new ol.View({
            center: ol.proj.fromLonLat([longitude, latitude]),
            zoom: ZOOM,
        })
    });
    marcador = new ol.Feature({
        geometry: new ol.geom.Point(
            ol.proj.fromLonLat([longitude, latitude])
        ),
    });
    marcador.setStyle(new ol.style.Style({
        image: new ol.style.Icon(({
            src: icono,
            scale: 0.5, // Aquí puedes ampliar o disminuir la imagen
        })),
    }));
    const ultimaCapa = new ol.layer.Vector({
        source: new ol.source.Vector({
            features: [marcador],
        }),
    });
    mapa.addLayer(ultimaCapa);
}

Y en caso de que no, simplemente centramos el mapa y actualizamos el marcador.

// Actualización de ubicación
mapa.getView().setCenter(ol.proj.fromLonLat([longitude, latitude]));
marcador.getGeometry().setCoordinates(ol.proj.fromLonLat([longitude, latitude]));
const fecha = formatearFecha(new Date(ubicacion.timestamp));
const registro = `Última actualización: ${fecha} en ${latitude},${longitude}`;
console.log(registro);
$estado.textContent = registro;

De ese modo cada que la ubicación del usuario cambie, el mapa se centrará en el usuario y el marcador se va a cambiar de lugar.

Por cierto, también estoy usando un formateador de fecha para mostrar la última vez que se accedió a la ubicación del usuario.

const formateador = new Intl.DateTimeFormat('es-MX', { dateStyle: 'medium', timeStyle: 'medium' });
const formatearFecha = fecha => formateador.format(fecha);

Ese timestamp está en ubicacion.timestamp. También recuerda que la longitud y latitud están en ubicacion.longitude y ubicacion.latitude respectivamente.

Poniendo todo junto

Mostrar ubicación de usuario en mapa usando JavaScript, GPS y OpenLayers

Ahora colocamos todo eso dentro de una función init en donde verificamos de igual manera si el navegador del usuario soporta esa característica. El código completo queda así:

const init = () => {
    if (!"geolocation" in navigator) {
        return alert("Tu navegador no soporta el acceso a la ubicación. Intenta con otro");
    }

    const ZOOM = 15;
    let mapa = null, marcador = null;
    const $estado = document.querySelector("#estado");
    const formateador = new Intl.DateTimeFormat('es-MX', { dateStyle: 'medium', timeStyle: 'medium' });
    const formatearFecha = fecha => formateador.format(fecha);

    const onActualizacionDeUbicacion = ubicacion => {
        const coordenadas = ubicacion.coords;
        let { latitude, longitude } = coordenadas;

        const icono = "ubicacion.png";
        if (!mapa) {
            // Primera vez, lo creamos y centramos
            mapa = new ol.Map({
                target: 'mapa', // el id del elemento en donde se monta
                layers: [
                    new ol.layer.Tile({
                        source: new ol.source.OSM()
                    })
                ],
                view: new ol.View({
                    center: ol.proj.fromLonLat([longitude, latitude]),
                    zoom: ZOOM,
                })
            });
            marcador = new ol.Feature({
                geometry: new ol.geom.Point(
                    ol.proj.fromLonLat([longitude, latitude])
                ),
            });
            marcador.setStyle(new ol.style.Style({
                image: new ol.style.Icon(({
                    src: icono,
                    scale: 0.5, // Aquí puedes ampliar o disminuir la imagen
                })),
            }));
            const ultimaCapa = new ol.layer.Vector({
                source: new ol.source.Vector({
                    features: [marcador],
                }),
            });
            mapa.addLayer(ultimaCapa);
        }
        // Actualización de ubicación
        mapa.getView().setCenter(ol.proj.fromLonLat([longitude, latitude]));
        marcador.getGeometry().setCoordinates(ol.proj.fromLonLat([longitude, latitude]));
        const fecha = formatearFecha(new Date(ubicacion.timestamp));
        const registro = `Última actualización: ${fecha} en ${latitude},${longitude}`;
        console.log(registro);
        $estado.textContent = registro;
    }

    const onErrorDeUbicacion = err => {
        console.log("Error obteniendo ubicación: ", err);
    }


    const opcionesDeSolicitud = {
        enableHighAccuracy: true, // Alta precisión
        maximumAge: 0, // No queremos caché
        timeout: 5000 // Esperar solo 5 segundos
    };

    idWatcher = navigator.geolocation.watchPosition(onActualizacionDeUbicacion, onErrorDeUbicacion, opcionesDeSolicitud);
}
init();

La demostración te la dejo por aquí, y el código fuente en mi GitHub. Si te gusta JavaScript te invito a leer más en mi blog.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

Entradas recientes

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

3 días hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

3 días hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

3 días hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

3 días hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

3 días hace

Solución: Apache – Server unable to read htaccess file

Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…

4 días hace

Esta web usa cookies.