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.
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.
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.
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.
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.
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.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.
Ver comentarios
Hola, la actualización de la ubicación muestra dos veces cada que abres el navegador, pero como se podría poner para actualizarlo constantemente?
Hola. Gracias por sus comentarios. Si tiene alguna consulta o duda, solicitud de creación de un programa o solicitud de cambio de software estoy para servirle en https://parzibyte.me/#contacto
Saludos!
como podria comparir esa ubicacion en tiempo real
Hola. Gracias por sus comentarios. Si tiene alguna consulta con gusto lo atiendo en https://parzibyte.me/#contacto
Saludos!