• Saltar a la navegación principal
  • Saltar al contenido principal
Geoinnova

Consultoría y Formación en SIG, Territorio y Medio Ambiente

  • Geoinnova
    • Quiénes somos
    • Coworking
    • Alianzas Estratégicas
    • Visor de proyectos Geoinnova
  • SIG y Desarrollo
    • Plugins QGIS
    • GeoNode
    • QElectricGIS
    • Planero Web
  • Consultoría Territorial
    • Planes de Emergencia Municipales de Protección Civil
    • Planes Urbanos de Actuación Municipal – PUAM
    • Evaluación Ambiental Estratégica de planes y programas
    • Minimización de Impacto Territorial y Declaración de Situación Individualizada en suelo no urbanizable
    • Turismo sostenible
  • Consultoría Ambiental
    • Trámites de Economía Circular
    • Prevención y gestión ambiental
    • Responsabilidad social corporativa
    • Estrategia de sostenibilidad
  • Formación
    • Todos los cursos
    • Formación para empresas
    • Máster y Diplomas
    • Cursos de QGIS Online Certificados
    • Cursos de programación, desarrollo SIG y geoservicios
    • Cursos de Teledetección
    • Cursos de Giswater
    • Cursos de SIG con ArcGIS
    • Cursos de Medio Ambiente
    • Campus
  • Divulgación
    • Blog
    • Tertulia Territorio Geoinnova
    • TFMs y otro trabajos de alumnos
    • Noticias
    • Webinars
    • Geolibrería
  • Contacto
    • Soporte – Tickets
Especialista Universitario 2 MATRICÚLATE AHORA
Programación y Desarrollo SIG SIG

Animar rutas o trayectorias con OpenLayers

13/05/2025 Deja un comentario
Contenidos ocultar
Animación básica con coordenadas fijas
Animar una ruta real con OSRM
OpenLayers + OSRM

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=geojson

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

Etiquetas: GeoJson, Openlayers, OSRM, web mapping

Apúntate a nuestra newsletter semanal

"*" señala los campos obligatorios

Este campo es un campo de validación y debe quedar sin cambios.
Consentimiento*

Artículos relacionados

proyecto-qgis-rediam-geoinnova
El proyecto base de QGIS de la REDIAM con toda la información ambiental de Andalucía que probablemente no conocías
16/03/2026
Proyecto FirePaths: visor cartográfico avanzado para el análisis del riesgo de incendios forestales
30/01/2026
Republicar un WMS externo en GeoServer
09/02/2026

Rafael García

Graduado como Técnico Superior en Desarrollo de aplicaciones Web y Administración de Sistemas y Redes por el IES Gran Capitán de Córdoba.

Con experiencia en el sector desde 2014 en desarrollo web fullstack, creación y desarrollo de plugins y temas personalizados para Wordpress y aplicaciones Android nativas e híbridas. Defensor del código abierto, nerd, artista 3D y entusiasta de la tecnología.

Actualmente forma parte del equipo de Programación y Desarrollo de Geoinnova.

Interacciones con los lectores

¿Quieres comentarnos algo? Adelante! Cancelar la respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Geoinnova
c/ Luis Morondo Urra nº 8 Bajo A
31006 Pamplona (Navarra)
info@geoinnova.org
+34 948 117 137

Horario de atención

Lunes a Viernes - 09:00 a 14:00 y de 16:00 a 19:00.
Fines de semana y Festivos - Exclusivamente por mail.
Horario de Verano (01 de julio al 30 de agosto): De lunes a viernes de 8 a 15h.

Otras páginas de interes

  • Promociones y descuentos en formación
  • Formación bonificada FUNDAE
  • Verificador y descarga de Certificados de aprovechamiento
  • Cursos certificable por QGIS.org

Legal

  • Aviso Legal
  • Política de privacidad
  • Política de seguridad
  • Política de cookies
  • Términos y condiciones
Entidad adherida a Confianza Online Certificado de inscripción en el Registro de huella de carbono, compensación y proyectos de absorción Logo Plazox Certificado de conformidad con el ENS

Todo el contenido publicado desde 2009 hasta 2026 en el Blog de Geoinnova por Geoinnova y colaboradores está licenciado bajo CC BY-NC-ND 4.0
Para usos específicos, contactar con Geoinnova.

© 2026 · Desarrollada por Geoinnova

Gestionar el Consentimiento de las Cookies
Utilizamos tecnologías como las cookies para almacenar y/o acceder a la información del dispositivo. Lo hacemos para mejorar la experiencia de navegación y para mostrar anuncios personalizados. El consentimiento a estas tecnologías nos permitirá procesar datos como el comportamiento de navegación o las identificaciones únicas en este sitio. No consentir o retirar el consentimiento, puede afectar negativamente a ciertas características y funciones.
Funcional Siempre activo
El almacenamiento o acceso técnico es estrictamente necesario para el propósito legítimo de permitir el uso de un servicio específico explícitamente solicitado por el abonado o usuario, o con el único propósito de llevar a cabo la transmisión de una comunicación a través de una red de comunicaciones electrónicas.
Preferencias
El almacenamiento o acceso técnico es necesario para la finalidad legítima de almacenar preferencias no solicitadas por el abonado o usuario.
Estadísticas
El almacenamiento o acceso técnico que es utilizado exclusivamente con fines estadísticos. El almacenamiento o acceso técnico que es utilizado exclusivamente con fines estadísticos anónimos. Sin una requerimiento, el cumplimiento voluntario por parte de su proveedor de servicios de Internet, o los registros adicionales de un tercero, la información almacenada o recuperada sólo para este propósito no se puede utilizar para identificarlo.
Marketing
El almacenamiento o acceso técnico es necesario para crear perfiles de usuario para enviar publicidad, o para rastrear al usuario en un sitio web o en varios sitios web con fines de marketing similares.
  • Administrar opciones
  • Gestionar los servicios
  • Gestionar {vendor_count} proveedores
  • Leer más sobre estos propósitos
Personalizar
  • {title}
  • {title}
  • {title}