Skip to content

Tutorial: Uso de Leaflet

Introducción

Leaflet es una librería JavaScript de código abierto, ligera y versátil, diseñada específicamente para la creación de mapas interactivos en la web. Con un peso de apenas 42 KB, se caracteriza por su excelente rendimiento y compatibilidad con todos los dispositivos, desde ordenadores de escritorio hasta smartphones y tablets. Su arquitectura modular y su API bien documentada permiten a desarrolladores tanto principiantes como avanzados integrar funcionalidades cartográficas complejas en aplicaciones web con relativa facilidad. Leaflet sirve como el "esqueleto" básico sobre el cual se pueden añadir diversas capas de datos y plugins de funcionalidad extendida.

La librería destaca por su capacidad de integrarse con múltiples fuentes de datos geoespaciales y proveedores de mapas base. Soporta nativamente formatos vectoriales como GeoJSON, KML y GPX, además de permitir la visualización de teselas (tiles) de diversos servicios como OpenStreetMap, Mapbox, Google Maps o cualquier servidor WMS. A través de su ecosistema de plugins, Leaflet puede extenderse para manejar formatos más especializados como shapefiles, implementar análisis espacial, crear mapas de calor, incorporar líneas de tiempo animadas o añadir controles de dibujo y medición avanzados.

Ejemplo sencillo

El siguiente ejemplo representa un ejemplo sencillo de la implementación de Leaflet para visualización de datos geoespaciales. La página HTML carga las librerías necesarias de Leaflet desde un CDN, incluyendo tanto los estilos CSS como el código JavaScript, con atributos de integridad para garantizar seguridad. El elemento central es un contenedor div que funcionará como el mapa, configurado con una altura fija de 500 píxeles. La inicialización del mapa se centra en coordenadas aproximadas de Costa Rica ([10.6, -85.58]) con un nivel de zoom 5, proporcionando una vista regional como punto de partida antes de cargar los datos específicos.

El núcleo funcional reside en la carga dinámica de datos GeoJSON mediante la API Fetch. El código solicita un archivo local llamado 'poligonos.geojson' y, tras convertirlo a formato JSON, añade su contenido al mapa como capa vectorial. La función fitBounds() ajusta automáticamente la vista para encuadrar perfectamente todos los elementos geográficos cargados, garantizando que el usuario vea la totalidad de los datos desde el primer momento.

html
<!DOCTYPE html>
<html>
	<head>
		<title>Ejemplo Sencillo</title>
	<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
	     integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
	     crossorigin=""/>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
        integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
        crossorigin=""></script>
	</head>
	<body>
		<div id="map" style="height: 500px;"></div>
		<script>
		// Inicializar mapa
		var map = L.map('map').setView([10.6, -85.58], 5);

		// Deshabilitar mapa base
		map.removeControl(map.attributionControl);

		// Cargar GeoJSON externo
		fetch('poligonos.geojson')
		.then(response => response.json())
		.then(data => {
		  // Añadir GeoJSON al mapa
		  L.geoJSON(data).addTo(map);
		  
		  // Ajustar vista a los datos
		  map.fitBounds(L.geoJSON(data).getBounds());
		})
		.catch(error => {
		  console.error('Error cargando GeoJSON:', error);
		});
		</script>
	</body>
</html>

Múltiples capas de datos

Este ejemplo representa una implementación minimalista de Leaflet diseñada para visualizar múltiples capas de datos geoespaciales en formato GeoJSON. La estructura base mantiene la simplicidad del ejemplo original, inicializando un mapa centrado en Costa Rica sin capa base de teselas y eliminando los controles de atribución por defecto. La innovación principal radica en la creación de un L.featureGroup() que actúa como contenedor unificado para todas las capas vectoriales, permitiendo gestionarlas colectivamente como una sola entidad dentro del mapa.

La funcionalidad clave se implementa mediante la función loadGeoJSON(), que encapsula el proceso de carga asíncrona de archivos GeoJSON mediante Fetch API. Por cada archivo cargado exitosamente, se crea una capa Leaflet que inmediatamente se integra al grupo principal, y la vista del mapa se reajusta automáticamente para englobar la totalidad de los datos acumulados.

html
<!DOCTYPE html>
<html>
	<head>
		<title>Ejemplo Múltiples Capas GeoJSON</title>
	<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
	     integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
	     crossorigin=""/>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
        integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
        crossorigin=""></script>
	</head>
	<body>
		<div id="map" style="height: 500px;"></div>
		<script>
		// Inicializar mapa
		var map = L.map('map').setView([10.6, -85.58], 5);

		// Deshabilitar mapa base
		map.removeControl(map.attributionControl);

		// Grupo para todas las capas
		var allLayers = L.featureGroup().addTo(map);

		// Función para cargar una capa GeoJSON
		function loadGeoJSON(url) {
			fetch(url)
			.then(response => response.json())
			.then(data => {
				// Añadir GeoJSON al grupo de capas
				var layer = L.geoJSON(data);
				allLayers.addLayer(layer);
				
				// Ajustar vista a todos los datos
				map.fitBounds(allLayers.getBounds());
			})
			.catch(error => {
				console.error('Error cargando ' + url + ':', error);
			});
		}

		// Cargar múltiples capas GeoJSON
		loadGeoJSON('poligonos.geojson');
		loadGeoJSON('puntos.geojson');
		loadGeoJSON('lineas.geojson');
		</script>
	</body>
</html>

Mapa categorizado

Este es un ejemplo de creación de un mapa coroplético interactivo y colorido de la densidad de población de los estados de EE. UU. con la ayuda de GeoJSON y algunos controles personalizados. El ejemplo configura un mapa centrado en Estados Unidos ([37.8, -96] con zoom nivel 4) y define una función getColor() que asigna colores escalonados (de amarillo claro a rojo oscuro) basándose en valores de densidad de población. La clave del coropleto está en la función style(), que aplica estos colores al relleno de cada región (fillColor) usando la densidad poblacional (feature.properties.density) extraída de los datos geoespaciales.

Los datos geográficos (geometrías de estados y sus densidades) se cargan desde un archivo externo us-states.js que define la variable statesData. Finalmente, L.geoJson() convierte estos datos GeoJSON en capas vectoriales en el mapa, aplicando el estilo definido para visualizar las diferencias de densidad poblacional mediante variaciones de color, creando así una representación visual inmediata de los datos demográficos.

html
<!DOCTYPE html>
<html lang="en">
<head>
	<base target="_top">
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	
	<title>Choropleth Tutorial - Leaflet</title>
	
    <link rel="stylesheet" 
          href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" 
          integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" 
          crossorigin=""/>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" 
            integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" 
            crossorigin=""></script>
	<style>
		html, body {
			height: 100%;
			margin: 0;
		}
		.leaflet-container {
			height: 400px;
			width: 600px;
			max-width: 100%;
			max-height: 100%;
		}
	</style>
</head>
<body>

<div id='map'></div>
<script type="text/javascript" src="us-states.js"></script>
<script type="text/javascript">
	const map = L.map('map').setView([37.8, -96], 4);

	// get color depending on population density value
	function getColor(d) {
		return d > 1000 ? '#800026' :
			d > 500  ? '#BD0026' :
			d > 200  ? '#E31A1C' :
			d > 100  ? '#FC4E2A' :
			d > 50   ? '#FD8D3C' :
			d > 20   ? '#FEB24C' :
			d > 10   ? '#FED976' : '#FFEDA0';
	}

	function style(feature) {
		return {
			weight: 2,
			opacity: 1,
			color: 'white',
			dashArray: '3',
			fillOpacity: 0.7,
			fillColor: getColor(feature.properties.density)
		};
	}
	/* global statesData */
	const geojson = L.geoJson(statesData, {
		style,
	}).addTo(map);
</script>
</body>
</html>

Etiquetas interactivas

Este ejemplo implementa etiquetas interactivas mediante el parámetro onEachFeature en la capa GeoJSON. Por cada polígono cargado, se ejecuta una función que genera dinámicamente un popup con toda la información contenida en las propiedades de la figura. El sistema recorre automáticamente todas las propiedades disponibles y las presenta formateadas en HTML, mostrando tanto los nombres de los campos como sus valores correspondientes. Finalmente, el mapa ajusta automáticamente su vista para encuadrar perfectamente todos los polígonos cargados usando la función fitBounds().

html
<!DOCTYPE html>
<html>
	<head>
		<title>Ejemplo Sencillo con Etiquetas</title>
	<link rel="stylesheet" 
       href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
	     integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
	     crossorigin=""/>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
        integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
        crossorigin=""></script>
	</head>
	<body>
		<div id="map" style="height: 500px;"></div>
		<script>
		// Inicializar mapa
		var map = L.map('map').setView([10.6, -85.58], 5);

		// Deshabilitar mapa base
		map.removeControl(map.attributionControl);

		// Cargar GeoJSON externo
		fetch('poligonos.geojson')
		.then(response => response.json())
		.then(data => {
		  // Añadir GeoJSON al mapa con popups interactivos
		  L.geoJSON(data, {
			onEachFeature: function(feature, layer) {
			  // Crear contenido del popup con los datos de las propiedades
			  if (feature.properties) {
				var popupContent = 
           '<div style="font-weight: bold;">Información del Polígono</div>';
				
				// Iterar sobre todas las propiedades para mostrarlas en el popup
				for (var key in feature.properties) {
				  if (feature.properties.hasOwnProperty(key)) {
					popupContent += '<br><b>' + key + ':</b> ' + feature.properties[key];
				  }
				}
				
				// Asignar el popup a la capa
				layer.bindPopup(popupContent);
			  }
			}
		  }).addTo(map);
		  
		  // Ajustar vista a los datos
		  map.fitBounds(L.geoJSON(data).getBounds());
		})
		.catch(error => {
		  console.error('Error cargando GeoJSON:', error);
		});
		</script>
	</body>
</html>