
Visualizar rutas animadas en un visor web es una funcionalidad útil en muchos proyectos GIS. En este artículo vamos a ver cómo implementar una animación simple de un marcador que sigue una trayectoria, primero con coordenadas estáticas y luego con una ruta real obtenida desde un servicio de enrutamiento como OSRM.
Animación básica con coordenadas fijas
Veamos primero como podemos realizar una animación básica con coordenadas fijas. En esta demo, vamos a definir una ruta manualmente con coordenadas geográficas que simulan un recorrido peatonal por el centro de Madrid. A partir de esta lista, se proyectan los puntos al sistema de coordenadas del mapa (EPSG:3857) y que posteriormente utilizaremos para mover un marcador sobre el mapa mientras se dibuja la trayectoria recorrida:
const routeCoords = [
[-3.703790, 40.416775], // Puerta del Sol
[-3.704350, 40.415950], // Calle Mayor
[-3.707200, 40.415270], // Plaza Mayor
[-3.708060, 40.414300], // Calle de Toledo
[-3.709260, 40.413190], // Mercado de la Cebada
[-3.709900, 40.412340], // La Latina
[-3.710800, 40.411630], // Calle del Humilladero
[-3.711900, 40.411100], // Plaza de la Paja
[-3.712600, 40.410600], // Jardín del Príncipe de Anglona
];Estas coordenadas se convierten con ol.proj.fromLonLat() para usarse en el mapa, y se animan punto a punto con la ayuda de un temporizador, el cual podemos modificar para alterar la velocidad/cadencia de la animación:
function animate() {
if (index >= routeProjected.length) return;
const nextCoord = routeProjected[index];
marker.getGeometry().setCoordinates(nextCoord);
const currentLine = routeLine.getCoordinates();
routeLine.setCoordinates([...currentLine, nextCoord]);
vectorSource.changed();
index++;
setTimeout(animate, 800);
}El ejemplo completo lo podemos ver aquí:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Animación de ruta con OpenLayers</title>
<style>
html, body, #map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/ol@latest/ol.css">
<script
src="https://cdn.jsdelivr.net/npm/ol@latest/dist/ol.js"></script>
</head>
<body>
<div id="map"></div>
<script>
const routeCoords = [
[-3.703790, 40.416775], // Puerta del Sol
[-3.704350, 40.415950], // Calle Mayor
[-3.707200, 40.415270], // Plaza Mayor
[-3.708060, 40.414300], // Calle de Toledo
[-3.709260, 40.413190], // Mercado de la Cebada
[-3.709900, 40.412340], // La Latina
[-3.710800, 40.411630], // Calle del Humilladero
[-3.711900, 40.411100], // Plaza de la Paja
[-3.712600, 40.410600], // Jardín del Príncipe de Anglona
];
const routeProjected = routeCoords.map(c => ol.proj.fromLonLat(c));
let index = 0;
const routeLine = new ol.geom.LineString([routeProjected[0]]);
const routeFeature = new ol.Feature(routeLine);
const marker = new ol.Feature(new ol.geom.Point(routeProjected[0]));
marker.setStyle(new ol.style.Style({
image: new ol.style.Icon({
src: 'https://cdn-icons-png.flaticon.com/512/684/684908.png',
scale: 0.05,
anchor: [0.5, 1]
})
}));
routeFeature.setStyle(new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#ff0000',
width: 2
})
}));
const vectorSource = new ol.source.Vector({
features: [marker, routeFeature]
});
const vectorLayer = new ol.layer.Vector({
source: vectorSource
});
const map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({ source: new ol.source.OSM() }),
vectorLayer
],
view: new ol.View({
center: routeProjected[0],
zoom: 16
})
});
function animate() {
if (index >= routeProjected.length) return;
const nextCoord = routeProjected[index];
marker.getGeometry().setCoordinates(nextCoord);
const currentLine = routeLine.getCoordinates();
routeLine.setCoordinates([...currentLine, nextCoord]);
vectorSource.changed();
index++;
setTimeout(animate, 800);
}
animate();
</script>
</body>
</html>Como podemos ver, es un efecto muy sencillo de conseguir y no hemos necesitado de librerías externas más allá de OpenLayer y unas cuantas líneas de código.
Animar una ruta real con OSRM
Ahora que somos expertos en animación 😉, podemos extender el ejemplo anterior para obtener rutas que sigan el recorrido real de las calles (Como en una aplicación GPS). Para ello podemos utilizar un servicio como OSRM (Open Source Routing Machine). Este servicio genera trayectorias entre dos o más puntos teniendo en cuenta la red vial, con la ayuda de la API. Por ejemplo, en la siguiente petición, le pedimos al servicio que nos devuelva una ruta a partir de dos puntos, uno de partida y otro de destino con:
https://router.project-osrm.org/route/v1/driving/-3.70379,40.41678;-3.7099,40.41234?overview=full&geometries=geojsonLa respuesta incluye un objeto GeoJSON con la geometría de la ruta, que podemos leer simplemente con ol.format.GeoJSON:
const route = data.routes[0].geometry;
const routeFeature = new ol.Feature({
geometry: new ol.format.GeoJSON().readGeometry(route, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
})
});
A partir de aquí, ya es coser y cantar. La animación del marcador se hace igual que en el primer ejemplo, pero con coordenadas obtenidas dinámicamente. De este modo, el marcador sigue una ruta real por calles y cruces, mejorando la precisión y utilidad en escenarios de navegación o seguimiento.
Este segundo enfoque permite extender la funcionalidad con rutas largas, múltiples paradas o integración con bases de datos y APIs externas. También es fácilmente escalable para aplicaciones más complejas como seguimiento en tiempo real o visualización de datos históricos entre otras posibilidades.
Si lo quieres probar, aquí esta el ejemplo completo:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Ruta animada en OpenLayers</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/ol@latest/ol.css">
<script
src="https://cdn.jsdelivr.net/npm/ol@latest/dist/ol.js"></script>
<style>
html, body, #map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
// Crear el mapa
const map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([-3.70379, 40.41678]),
zoom: 15
})
});
// Obtener la ruta desde OSRM
fetch('http://router.project-osrm.org/route/v1/driving/-3.70379,40.41678;-3.7099,40.41234?overview=full&geometries=geojson')
.then(response => response.json())
.then(data => {
const route = data.routes[0].geometry;
// Crear una capa vectorial para la ruta
const routeFeature = new ol.Feature({
geometry: new ol.format.GeoJSON().readGeometry(route, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
})
});
routeFeature.setStyle(new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#ff0000',
width: 3
})
}));
const vectorSource = new ol.source.Vector({
features: [routeFeature]
});
const vectorLayer = new ol.layer.Vector({
source: vectorSource
});
map.addLayer(vectorLayer);
// Animar un marcador a lo largo de la ruta
const routeCoords = route.coordinates.map(coord => ol.proj.fromLonLat(coord));
const marker = new ol.Feature(new ol.geom.Point(routeCoords[0]));
marker.setStyle(new ol.style.Style({
image: new ol.style.Icon({
src: 'https://cdn-icons-png.flaticon.com/512/684/684908.png',
scale: 0.05,
anchor: [0.5, 1]
})
}));
const markerSource = new ol.source.Vector({
features: [marker]
});
const markerLayer = new ol.layer.Vector({
source: markerSource
});
map.addLayer(markerLayer);
let index = 0;
function moveMarker() {
if (index < routeCoords.length) {
marker.getGeometry().setCoordinates(routeCoords[index]);
index++;
setTimeout(moveMarker, 100);
}
}
moveMarker();
});
</script>
</body>
</html>Como hemos visto, la implementación de una animación sobre una ruta es muy sencilla y se apoya en funciones básicas de JavaScript y OpenLayers. El resto del código corresponde a la inicialización estándar del mapa, capas y geometrías, lo que permite adaptar fácilmente esta solución a distintos escenarios, desde demostraciones simples hasta aplicaciones más completas con rutas dinámicas.
¿Quieres comentarnos algo? Adelante!