Blog del Máster
en Tecnologías de la Información Geográfica y Ciencia de Datos
Espaciales

Web Mapping con MapLibre, Svelte y Google Sheet

En este post, vamos a mostrar como crear un proyecto de web mapping con MapLibre y Svelte, junto con datos contenidos en una hoja de Google Sheet. Continuando con el post Poblar una hoja de cálculo con Apps Script, utilizaremos estos datos para visualizar en un mapa web los micropueblos de Catalunya, es decir, aquellos municipios con menos de 500 habitantes.

Crear un proyecto con Svelte.js

Svelte.js es un framework frontend para el desarrollo de aplicaciones web reactivas, y que obviamente podemos también utilizar para nuestros proyectos de web mapping.

Para empezar un proyecto con Svelte, teclearemos en la terminal de nuestro sistema operativo la siguiente instrucción:

$ npx degit sveltejs/template micropueblos

Una vez creado, accedemos al directorio e instalamos las dependencias. Y finalmente, arrancamos el entorno local:

$ cd micropueblos
$ npm install
$ npm run dev

Accederemos a la aplicación a través de esta url: http://localhost:8080

Crear un mapa con MapLibre

MapLibre es una librería javascript, de código abierto, que permite la creación de mapas web interactivos.

Para instalar MapLibre en nuestro proyecto de Svelte, basta con utilizar esta instrucción:

$ npm i maplibre-gl

Una vez instalada la librería, crearemos un componente de Svelte en el que añadiremos el código que nos permitirá visualizar el mapa.

Al instalar Svelte, se han generado múltiples ficheros y directorios. Dentro del directorio src, crearemos un nuevo directorio con el nombre componentes. Y dentro de componentes, el archivo Map.svelte

El código del componente Map.svelte será el siguiente:

<script>
  import { onMount, onDestroy } from 'svelte'
  import { Map } from 'maplibre-gl';
  import 'maplibre-gl/dist/maplibre-gl.css';

  let mapa;
  let contendor;

  onMount(() => {

    const localizacion = { lng: 1.4, lat: 41.6, zoom: 7 };

    mapa = new Map({
      container: contendor,
      style: `https://demotiles.maplibre.org/style.json`,
      center: [localizacion.lng, localizacion.lat],
      zoom: localizacion.zoom
    });

  });

  onDestroy(() => {
    mapa.remove();
  });
</script>

<div class="map-wrap">
  <div class="map" id="map" bind:this={contendor}></div>
</div>

<style>

  .map-wrap {
    position: relative;
    width: 100%;
    height: 100vh; 
  }

  .map {
    position: absolute;
    width: 100%;
    height: 100%;
  }
</style>

En este código, creamos un objeto Map de MapLibre, al que le pasamos como opciones el contendor, es decir, el elemento HTML en el que se renderizará el mapa, así como el estilo, las coordenadas (longitud y latitud) y el nivel de zoom inicial.

También definimos, con estilos CSS, la posición y tamaño del mapa.

En el archivo App.svelte se realizará la importación de este nuevo componente, y se añadirá el contenido en la página:

<script>
	import Map from './components/Map.svelte';
	export let name;
</script>

<main>
	<Map/>
	<h1>Hello {name}!</h1>
	<p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>

Ahora, en el navegador (a través de la url http://localhost:8080) ya deberían visualizarse los cambios efectuados en la aplicación.

Añadir al mapa una capa VectorTile

Para representar los micropueblos de Catalunya, utilizaremos la cartografía de divisiones administrativas en formato VectorTile que ofrece el ICGC. En primer lugar, cargaremos la fuente de datos, y a continuación, añadiremos la capa. Por ello, en el componente Map.svelte, justo después de crear el objeto Map, añadiremos estas líneas de código:

mapa.on('load', function() {
    // añadir la fuente de datos 'municipalities' de tipo vector Tile 
    mapa.addSource('municipalities', {
        type: 'vector',
        url: 'https://openicgc.github.io/divisions_administratives.json'
    })
    // añadimos la capa boundary
    mapa.addLayer({
        'id': 'micropobles',
        'type': 'fill',
        'source': 'municipalities',
        'source-layer': 'boundary',
        'filter': [
            "all",
            ["==", "escala", "1M"],
            ["==", "class", "municipi"]
        ],
        'paint': {
            "fill-opacity": 0.9,
            "fill-color": "green",
            "fill-outline-color": "black"
        }
    })
})

Al añadir la capa, a partir de la fuente municipalities de la capa VectorTiles, aplicamos un filtro (filter) para utilizar los datos a escala 1M, así como las entidades de la clase municpi, Por otro lado, aplicamos también un estilo a esta capa, dejándolo, por ahora, con un relleno de color verde y un contorno negro.

El resultado es este:

Cartografía de municipios de Catalunya

Modificar el comportamiento del cursor

Podemos configurar el mapa para que el cursor cambie al pasar por encima las entidades vectoriales de la capa municipios. Para ello, basta con añadir estas líneas de código:

// modificar el estilo del cursor
mapa.on('mouseenter', 'micropobles', function () {
    mapa.getCanvas().style.cursor = 'pointer';
});

mapa.on('mouseleave', 'micropobles', function () {
    mapa.getCanvas().style.cursor = '';
});

Este comportamiento hará que resulte mas intuitivo hacer click sobre las entidades vectoriales para mostrar un popup de información una vez implementemos esta funcionalidad.

Utilizar datos de un Google Sheet

En esta aplicación hemos utilizado diversas fuentes de datos disponibles en la red (cartografía OSM y VectorTiles del ICGC). Mostraremos ahora como utilizar datos de un Google Sheet. Y lo haremos de forma dinámica, de tal modo que, al modificarse los valores del Google Sheet, también se actualizará el mapa.

Utilizaremos el Google Sheet creado en el post Poblar una hoja de cálculo con Apps Script, y en el que figuran todos los municipios de Catalunya, indicándose si son micropueblos o no (en el campo Micropoble).

Dentro de la función onMount, añadiremos las siguientes líneas de código. Con ellas, construimos la url de acceso al documento, y realizamos un fetch para obtener los datos. En la lista MICROPUEBLOS guardamos el nombre de los municipios que, acorde a los datos del Google Sheet, corresponden a micropueblos.

Para que funcione correctamente, habrá que remplazar el texto your-sheet-id por el identificador (id) del Google Sheet.

// Cargar datos de Google Sheet 
const MICROPUEBLOS = ["in", "name"] // array con los micropueblos
const sheetId = 'your-sheet-id'; // id del Google Sheet 
const base = `https://docs.google.com/spreadsheets/d/${sheetId}/gviz/tq?`; // url base
const sheetName = 'Main'; // nombre del Sheet
const query = encodeURIComponent('Select *'); // consulta
const url = `${base}&sheet=${sheetName}&tq=${query}`; // url completa
document.addEventListener('DOMContentLoaded', loadSheet); // ejecutamos la funcion una vez cargado el DOM 
    

/**
 * Función para obtener los datos del Google Sheet
 */
function loadSheet() {
    fetch(url)
    .then(res => res.text())
    .then(rep => {
    // Eliminamos el texto y extraemos únicamente el JSON 
    const jsonData = JSON.parse(rep.substring(47).slice(0, -2)); 
    const columnas = [];
    // Extraemos el nombre de las columnas 
    jsonData.table.cols.forEach((cabecera) => {
        if (cabecera.label) {
            let columna = cabecera.label;
            columnas.push(columna)
        }
    })
    // Extraer datos de los filas 
    jsonData.table.rows.forEach((rowData) => {
        const fila = {};
        columnas.forEach((ele, ind) => {
            fila[ele] = (rowData.c[ind] != null) ? rowData.c[ind].v : '';
        })
        if (fila.Micropoble == true) MICROPUEBLOS.push(fila.Nom_Municipi)
    })
  })
}

Finalmente, en el filtro de la capa micropueblos, añadiremos el array con los nombres correspondientes (MICROPUEBLOS).

mapa.addLayer({
    'id': 'micropobles',
    'type': 'fill',
    'source': 'municipalities',
    'source-layer': 'boundary',
    'filter': [
            "all",
            ["==", "escala", "1M"],
            ["==", "class", "municipi"],
            MICROPUEBLOS
        ],
    'paint': {
        "fill-opacity": 0.9,
        "fill-color": "green",
        "fill-outline-color": "black"
    }
})

Y este será el resultado. De la capa VectorTile de l’ICGC con los municipios de Catalunya únicamente se muestran aquellos que son micropueblos, acorde a los datos del Google Sheet.

Representación de los micropueblos con Svelte, Maplibre y Google Sheet.
Representación de los micropueblos de Catalunya

Popup de información

Para terminar la aplicación, podemos añadir un popup de información. De esta forma, al hacer click sobre un micropueblo, se mostrará su nombre.

Lo haremos utilizando el siguiente código:

mapa.on('click', 'micropobles', e => {
    const features = mapa.queryRenderedFeatures(e.point, { layers: ["micropobles"] });
      
    new Popup()
        .setLngLat(e.lngLat)
        .setHTML('Nombre: ' + features[0].properties.name)
        .addTo(mapa);
})

Conclusiones

En este post hemos mostrado como crear un mapa web utilizando Svelte y MapLibre, así como diferentes fuentes de datos.

También como usar datos de un Google Sheet para actualizar el mapa. De esta forma, sin necesidad de modificar el código de la aplicación ni mantener un backend con una base de datos, se puede llevar a cabo la actualización de la cartografía. Una opción a tener en cuenta para proyectos de webmapping relativamente simples y que deban implementarse con una infraestructura mínima.

Josep Sitjar
Geógrafo y máster en medio ambiente, análisis y gestión del territorio. Desarrollador de aplicaciones web. Trabajo en el Servicio de SIG y Teledetección (SIGTE) de la Universitat de Girona participando en numerosos proyectos técnicos vinculados al mundo de los SIG y al desarrollo de aplicaciones web map. En UNIGIS soy tutor de los módulos SIG y Teledetección y Programación de Aplicaciones Web Map II.


Suscríbete a nuestra newsletter