• 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
Cursos SIG y Medio Ambiente APÚNTATE
Programación y Desarrollo SIG SIG Tecnología

MapLibre GL JS: Mostrar datos en tiempo real

04/11/2025 Deja un comentario

En este artículo vamos a crear un visor web sencillo con MapLibre GL JS que muestre datos en tiempo real geolocalizados. Para ello he elegido la API pública de OpenSky Network, que proporciona datos ADS-B de aviones a nivel mundial, aunque podríamos buscar cualquier otra fuente datos, como datos atmosféricos, estado del tráfico, etc. Un dato importante a tener en cuenta, es que esta API tiene limitaciones en cuanto a frecuencia y volumen de peticiones. Si queremos, podemos crear una cuenta de usuario y ampliar dichos límites, pero nos servirá perfectamente para ilustrar el ejemplo.

Contenidos ocultar
Crear nuestro mapa y cargar los datos
Mostrar información adicional con etiquetas y ventanas emergentes

Crear nuestro mapa y cargar los datos

El primer paso es inicializar un mapa básico con MapLibre GL JS y añadir una fuente GeoJSON que se actualizará con los datos obtenidos de OpenSky. Para simplificar la visualización, representaremos cada avión como un círculo azul en el mapa.

Para una prueba rápida, podemos utilizar estos estilos y el CDN de MapLibre en la cabecera del documento html:

<style>
    html, body {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
    }
    #map {
        width: 100%;
        height: 100%;
    }
</style>
<script src='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js'></script>
<link href='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css' rel='stylesheet' />
// Crear el mapa
const map = new maplibregl.Map({
    container: 'map',
    style: 'https://demotiles.maplibre.org/style.json',
    center: [0, 20],
    zoom: 2
});
// Cargar datos de vuelos
async function loadFlights() {
    // ¡La API pública tiene un límite de peticiones!
    const res = await fetch('https://opensky-network.org/api/states/all');
    const data = await res.json();
    // Transformar a GeoJSON
    const features = data.states
        .filter(d => d[5] !== null && d[6] !== null) // has lat/lon
        .map(d => ({
        type: 'Feature',
        geometry: { type: 'Point', coordinates: [d[5], d[6]] },
        properties: {
            callsign: d[1],
            origin_country: d[2],
            velocity: d[9],
            heading: d[10]
        }
        }));
    const geojson = { type: 'FeatureCollection', features };
    // Actualizar o agregar fuente
    if (map.getSource('flights')) {
        map.getSource('flights').setData(geojson);
    } else {
        map.addSource('flights', { type: 'geojson', data: geojson });
        
        // Layer de círculos (Representación de los aviones)
        map.addLayer({
            id: 'planes',
            type: 'circle',
            source: 'flights',
            paint: {
                // Escalar tamaño del círculo con el zoom
                'circle-radius': [
                'interpolate', ['linear'], ['zoom'],
                4, 3,
                8, 5,
                12, 7
                ],
                'circle-color': '#1e90ff',
                'circle-stroke-color': '#ffffff',
                'circle-stroke-width': 1
            }
        });
    }
}
map.on('load', async () => {
    // Cargar datos
    await loadFlights();
    // Actualizar cada 30s
    setInterval(loadFlights, 30000);
});

En este punto ya tenemos un mapa base y sobre él círculos que representan la posición actual de los vuelos detectados de forma mundial y actualizamos dicha capa cada 30 segundos. Este valor podría ser inferior, pero como se ha comentado al principio no queremos alcanzar los límites ni abusar de una API pública.

Visor mostrando todos los vuelos detectados sobre Europa

Vale, esto está muy bien, pero ¿tampoco nos dice mucho verdad?, lo interesante es que podemos seguir ampliando el visor con información o acciones adicionales.

Mostrar información adicional con etiquetas y ventanas emergentes

Podemos enriquecer el visor mostrando el identificador de vuelo (callsign) junto a cada punto y añadiendo un popup con información extra al hacer clic. Esto permite que el mapa no solo muestre posiciones, sino también detalles útiles para el usuario.

map.addLayer({
    id: 'flights-labels',
    type: 'symbol',
    source: 'flights',
    layout: {
        'text-field': ['get', 'callsign'],
        'text-size': 10,
        'text-offset': [0, 1.2]
    },
    paint: {
        'text-halo-color': '#ffffff',
        'text-halo-width': 1
    }
});

Ahora podemos ver los puntos junto con su identificador.

Vuelos con su identificadores

Y por último, permitamos inspeccionar algunos atributos del vuelo al pulsar sobre cada punto:

map.on('click', 'planes', (e) => {
    const p = e.features[0].properties;
    new maplibregl.Popup()
        .setLngLat(e.lngLat)
        .setHTML(`
        <strong>${p.callsign}</strong><br/>
        País: ${p.origin_country}<br/>
        Velocidad: ${Number(p.velocity_ms || 0).toFixed(0)} m/s
        `)
        .addTo(map);
});
map.on('mouseenter', 'planes', () => map.getCanvas().style.cursor = 'pointer');
map.on('mouseleave', 'planes', () => map.getCanvas().style.cursor = '');

Con estas pocas líneas el visor ya muestra los vuelos en tiempo real (cada 30s), permite identificarlos fácilmente en el mapa y consultar su información básica.

Mostrando información adicional al pulsar sobre un vuelo

En definitiva, esto no se queda aquí, podemos adicionalmente aprovecharnos del atributo «heading» y reemplazar el punto, por un icono de un avión y rotarlo para representar visualmente la dirección actual en la que se dirige el avión (Esta idea la dejo como ejercicio para el lector).

Etiquetas: maplibre, 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}