Portal de mapas personalizado: una guía de principio a fin

Publicado el 12 de abril de 2022 Actualizado el 20 de junio de 2022

Portal de mapas personalizado: una imagen guía de principio a fin

Este artículo describe cómo crear un geoportal con un mapa personalizado, datos propios, leyenda de mapa, búsqueda de nombres de lugares, la vista de satélite de un edificio seleccionado y una vista de calle. Al leerlo, aprenderás a transformar los datos en MBTiles, subirlos a tu cuenta de MapTiler Cloud y visualizarlos en una aplicación desarrollada con MapLibre GL JS.

Tu mapa final tendrá este aspecto (haz clic en cualquier edificio para ver la magia):

Descargar los datos del mapa

Lo primero que haremos será descargar los datos de algunos edificios del Catastro de España. En el ejemplo, utilizaremos los datos del municipio de Sant Feliu de Guixols.

Los datos se pueden descargar en el siguiente enlace: SANT FELIU DE GUIXOLS/A.ES.SDGC.BU.17170.zip

Una vez descargados los datos, descomprimiremos el archivo zip. Dentro del archivo zip, hay un archivo llamado A.ES.SDGC.BU.17170.building.gml con la información del edificio; este archivo está en formato GML.

Convertir un archivo GML en MBTiles

Para transformar el archivo GML en MBTiles tenemos varias opciones:

  • MapTiler Desktop: nos permite transformar nuestros datos en MBTiles o GeoPackage fácilmente. También podemos subir directamente los datos transformados a nuestra cuenta en la nube. Si quieres saber más, consulta el artículo Generación de tiles vectoriales (básico ).
  • GDAL/ogr2ogr: si tiene instalado GDAL/ogr2ogr en su ordenador también puede transformar el archivo GML en un MBTiles utilizando el siguiente comando:

ogr2ogr -f MVT guixols.mbtiles A.ES.SDGC.BU.17170.building.gml -dsco MAXZOOM=18 -dsco MINZOOM=9 -mapFieldType DateTime=String
  • MapTiler Engine: esta herramienta está incluida en la versión pro de MapTiler Desktop. Crea flujos de trabajo automatizados con toda la potencia de MapTiler Desktop. Si quieres saber más sobre MapTiler Engine, consulta el artículo Uso de MapTiler Engine . Para transformar el archivo gml a mbtiles utilizando el MapTiler Engine debe ejecutar el siguiente comando:

maptiler -o guixols.mbtiles A.ES.SDGC.BU.17170.building.gml

Como vemos, MapTiler Engine se encarga de calcular los niveles de zoom óptimos para nuestros datos y de realizar la correspondiente transformación de los tipos de datos. En el ejemplo de GDAL, tenemos que definir los niveles de zoom e indicar las transformaciones de los tipos de datos alfanuméricos para que sean tipos de datos válidos en MBTiles.

Sube tus geodatos a MapTiler Cloud

Ya hemos mencionado que puedes cargar datos directamente en MapTiler Cloud desde MapTiler Desktop; otra forma de cargar MBTiles es a través de la API de administración de MapTiler Cloud .

There are numerous ways to send your requests to the API; whether you are a fan of API clients or go with the good old curl, don’t forget to set the Authorization header in the form of Token {YOUR_TOKEN} so we know it’s you making the requests. To make your life easier, we have also created a CLI utility to upload the tilesets.

Hacer todo el proceso manualmente a través de curl implica hacer múltiples llamadas a la API. Por ejemplo, llamar a la API de administración para iniciar la ingesta. La API de administración devuelve una URL de Google Drive para subir el archivo. A continuación, llamar a la API de Google Drive para subir el archivo y, por último, llamar a la API de administración para procesar el archivo.

Para facilitarte la vida, hemos desarrollado la utilidad MapTiler Cloud CLI para subir los tilesets. Esta herramienta de código abierto está desarrollada en Python y permite automatizar el proceso de subida de datos a la nube. Puedes acceder al código en el repositorio GitHub de MapTiler Cloud CLI.

Utilizaremos la herramienta CLI de MapTiler Cloud para subir los datos a la nube.

Consulte el artículo Cómo cargar MBTiles o GeoPackage a MapTiler Cloud utilizando la API para obtener más detalles sobre cómo instalar y utilizar la herramienta CLI.

Una vez que hemos instalado la herramienta CLI debemos iniciar el entorno virtual donde está instalada la herramienta. A continuación, ejecutamos el siguiente comando para cargar el archivo MBTiles en el MapTiler Cloud:


maptiler-cloud --token=TU_TOKEN_CREDEN tiles ingiere guixols.mbtiles

Desarrollar una aplicación con un mapa para mostrar sus datos

Para visualizar los datos de MapTiler Cloud vamos a realizar una aplicación utilizando la librería MapLibre GL JS.

Crear un mapa

Lo primero que vamos a hacer es crear un mapa donde podamos visualizar nuestros datos. Para cargar la cartografía de referencia de nuestro mapa llamaremos a la API de MapTiler Cloud . MapTiler Cloud tiene muchos estilos de mapas base listos para usar que se adaptan a una amplia gama de casos de uso.

Si nunca has trabajado con MapLibre te recomendamos que consultes el tutorial Cómo empezar con MapLibre GL JS.

Crea un archivo llamado index.html y copia el siguiente código:

In the above example, we are calling the MapTiler Cloud API to load the base map https://api.maptiler.com/maps/basic/style.json?key=${apiKey}

Abra la aplicación en un navegador y debería ver un mapa como el de la siguiente imagen

Crear mapa ejemplo básico

Crear el control de geocodificación

Vamos a añadir un control que nos permita buscar ubicaciones. Para ello, vamos a utilizar la API de geocodificación.

Para facilitar el uso de la API de geocodificación hemos desarrollado el componente geocodificador que puede integrar fácilmente en sus aplicaciones.

Para cargar el componente geocodificador añada las líneas resaltadas en la cabecera del archivo index.html.

Una vez cargado el componente vamos a crear un control personalizado de MapLibre para añadirlo al mapa.

Justo después de donde añadimos el control de navegación del mapa. Escribe las líneas resaltadas:


//add navigation control
map.addControl(new maplibregl.NavigationControl(), 'top-right');

class searchControl {
  onAdd(map) {
    this._map = map;
    this._container = document.createElement('div');
    this._container.className = 'maplibregl-ctrl';
    const _input = document.createElement('input');
    this._container.appendChild(_input);
    const geocoder = new maptiler.Geocoder({
      input: _input,
      key: apiKey
    });
    geocoder.on('select', function(item) {
      map.fitBounds(item.bbox);
    });
    return this._container;
  }

  onRemove() {
    this._container.parentNode.removeChild(this._container);
    this._map = undefined;
  }
}
map.addControl(new searchControl(), 'top-left');

Ahora debería ver el control de búsqueda en la esquina superior izquierda del mapa.

Visualice los datos del mapa

Para añadir nuestros datos al mapa debemos declarar primero una nueva fuente de datos y luego añadir una nueva capa con la información de esta fuente de datos.

La inicialización del mapa en la página indica al navegador que solicite el estilo. Esto puede llevar algún tiempo dependiendo de la rapidez con la que el servidor pueda responder a esa petición de estilo y del tiempo que tarde el navegador en renderizar el mapa (normalmente milisegundos). Estos recursos son remotos por lo que se ejecutan de forma asíncrona, es importante asegurarse de que el estilo está cargado antes de ejecutar más código.

El objeto mapa puede informar al navegador sobre ciertos eventos que ocurren cuando el estado del mapa cambia. Uno de estos eventos es el evento de carga, este evento se dispara cuando el estilo se ha cargado en el mapa.

Añadir una fuente de datos al mapa

Mediante el método map.on('load', callback function) podemos asegurar que no se ejecute el resto del código callback hasta que se produzca ese evento.


map.addControl(new searchControl(), 'top-left');

map.on('load', () => {
  // the rest of the code will go in here
});

Por lo tanto, debemos llamar a la función addSource dentro de una función map.on('load') para que la nueva fuente no se cargue antes de que se renderice el mapa.


map.on('load', () => {
  // the rest of the code will go in here
  map.addSource("building_source", {
  "type": "vector",
  "url": `https://api.maptiler.com/tiles/YOUR_TILESET_ID/tiles.json?key=${apiKey}`
  });
});

En el fragmento de código anterior estamos utilizando la API de MapTiler Cloud para hacer referencia al TileJSON que se generó al cargar nuestros datos en MapTiler Cloud.

Añadir una capa al mapa

Una vez definida la fuente de datos podemos añadir la capa al mapa. Para ello utilizaremos la función addLayer. Justo después de la función addSource escribiremos las siguientes líneas:


map.on('load', () => {
  // the rest of the code will go in here
  map.addSource("building_source", {
  "type": "vector",
  "url": `https://api.maptiler.com/tiles/YOUR_TILESET_ID/tiles.json?key=${apiKey}`
  });

  map.addLayer({
    "id": "building_pol",
    "type": "fill",
    "source": "building_source",
    "source-layer": "Building",
    "layout": {
    "visibility": "visible"
    },
    "paint": {
    "fill-color": "#3A1888",
    "fill-opacity": [
      "literal",
      0.6
    ]
    },
    "filter": ["all",
    ["==", "$type", "Polygon"]
    ],
  }, "label_airport");
});

En el fragmento de código anterior estamos añadiendo la capa de edificios justo antes de la capa de etiquetas del aeropuerto. Lo hacemos para que los polígonos de los edificios aparezcan en el mapa debajo de las etiquetas y no oculten los textos de los nombres de las calles, los barrios, etc.

Recargue la aplicación y escriba Sant Feliu de Guíxols en el buscador y seleccione la primera entrada de la lista de resultados. La aplicación hará un zoom sobre el municipio de Sant Feliu de Guíxols y verá la capa de edificios.

Mapa de la capa de edificios

Mostrar información del edificio al hacer clic

Ya hemos visto cómo añadir y mostrar sus propios datos en un mapa. Lo siguiente que vamos a hacer es mostrar información relacionada con un edificio al hacer clic sobre él. Para ello, utilizaremos el evento de clic del mapa.

Después de añadir la capa al mapa escribiremos el siguiente código:

En este fragmento de código, estamos definiendo una función (createPopupContent) para crear el contenido a mostrar y estamos registrando el evento de clic del mapa para nuestra capa de edificio.

Para mejorar la experiencia del usuario vamos a hacer que el puntero del cursor cambie al pasar por encima de un edificio; así el usuario sabrá que puede interactuar con los edificios.

Escribe lo siguiente después de donde añadimos el evento de clic:


map.on('click', 'building_pol', function (e) {
  const content = createPopupContent(e.features[0]);
  new maplibregl.Popup()
  .setLngLat(e.lngLat)
  .setHTML(content)
  .addTo(map);
});

map.on('mouseenter', 'building_pol', () => {
  map.getCanvas().style.cursor = 'pointer';
});

map.on('mouseleave', 'building_pol', () => {
  map.getCanvas().style.cursor = '';
});

Crear un mapa coroplético de los edificios

Para crear el mapa coropleto de los edificios, vamos a cambiar el estilo de la capa de edificios y utilizar los datos alfanuméricos asociados a los edificios para clasificarlos y pintarlos según el valor de un campo determinado. En este caso, vamos a utilizar el campo currentUse para hacer la clasificación.

También puedes utilizar las aplicaciones de MapTiler Cloud para estilizar tus datos y crear un mapa con ellos

Personaliza tu mapa

Si miramos los datos alfanuméricos de la capa de edificios veremos que para el campo currentUse hay 6 valores posibles. Crearemos un array con estos valores y les asignaremos un texto y un color.

Justo después de donde declaramos la variable con la clave de la API escribimos lo siguiente:


const apiKey = 'YOUR_API_KEY';

const categories_field = "currentUse";
const categories = [
  {id: "1_residential", text: "Residential", color: "#FFD65F"},
  {id: "2_agriculture", text: "Agriculture", color: "#35C186"},
  {id: "3_industrial", text: "Industrial", color: "#805CC2"},
  {id: "4_1_office", text: "Office", color: "#FF8F65"},
  {id: "4_2_retail", text: "Retail", color: "#3388F1"},
  {id: "4_3_publicServices", text: "Public Services", color: "#E25041"},
];

Con esta matriz de categorías y colores, vamos a crear la función createFillColor para generar una expresión que determine el color con el que pintaremos los edificios.


  {id: "4_2_retail", text: "Retail", color: "#3388F1"},
  {id: "4_3_publicServices", text: "Public Services", color: "#E25041"},
];

const createFillColor = (categories, categories_field) => {
  const colors = categories.reduce((agg, item) => {
    agg.push(item.id);
    agg.push(item.color);
    return agg;
  }, []);
  return [
    "match",
    [
      "get",
      categories_field
    ],
    ...colors,
    "#ccc"
  ]
}

Modificar la propiedad fill-color de la capa de edificios para que en lugar de tener un color definido ("#3A1888") utilice la función createFillColor para determinar el valor.


"paint": {
  "fill-color": createFillColor(categories, categories_field),
  "fill-opacity": [
    "literal",
    0.6
  ]
},

Al recargar la aplicación veremos que tenemos los edificios pintados de diferentes colores según su uso.

Mapa coroplético de edificios

Crear una leyenda de mapa interactiva

Crearemos una leyenda interactiva en el mapa, para mostrar los colores de la clasificación, y también cambiaremos la visualización de qué categorías están activas o inactivas.

Lo primero que haremos es crear una función para crear el filtro de la capa. Utilizaremos la propiedad filtro de la capa para determinar qué categorías queremos mostrar en el mapa. Esto nos permitirá cambiar la visualización de las categorías activas e inactivas.


    ...colors,
    "#ccc"
  ]
}

const createFilter = (categories, categories_field) => {
  const filters = categories.reduce((agg, item) => {
    agg.push(["in", categories_field, item.id]);
    return agg;
  }, []);
  return [
    "all",
    ["==", "$type", "Polygon"],
    ["any",
      ...filters
    ]
  ]
}

Modificar la propiedad del filtro de la capa de edificios


"paint": {
    "fill-color": createFillColor(categories, categories_field),
    "fill-opacity": [
      "literal",
      0.6
    ]
  },
  "filter": createFilter(categories, categories_field),
}, "label_airport");

Crear el control de la leyenda

Antes de crear el control de la leyenda crearemos un par de funciones que nos permitirán alternar la visibilidad de las categorías. Junto a la creación de la función createFilter escribe estas líneas:


    ["any",
      ...filters
    ]
  ]
}

const removeAtIndex = (arr, index) => {
  const copy = [...arr];
  copy.splice(index, 1);
  return copy;
};

const toggle = (arr, item, getValue = item => item) => {
  const index = arr.findIndex(i => getValue(i) === getValue(item));
  if (index === -1) return [...arr, item];
  return removeAtIndex(arr, index);
}

Justo debajo de donde añadimos el control de búsqueda copia lo siguiente:


map.addControl(new searchControl(), 'top-left');

class legendControl {
  constructor (categories, field) {
    this.categories = categories;
    this.field = field;
  }

  onAdd(map) {
    this._map = map;
    this._container = document.createElement('div');
    this._container.className = 'maplibregl-ctrl';
    const _fragment = document.createDocumentFragment();
    const _nav = document.createElement('nav');
    _nav.className = 'maplibregl-ctrl filter-group';
    _nav.id = 'filter-group';
    _fragment.appendChild(_nav);
    this.categories.forEach(element => {
      const _input = document.createElement('input');
      _input.type = "checkbox";
      _input.id = element.id;
      _input.className = "input-layers";
      _input.checked = true;
      const this_ = this;
      _input.addEventListener('change', function (e) {
        this_.updateLegend(e.target.id);
      });
      const _label = document.createElement('label');
      _label.htmlFor = element.id;
      const _text = document.createTextNode(element.text);
      const _legend = document.createElement('i');
      _legend.style.backgroundColor = element.color;
      _label.appendChild(_text);
      _label.appendChild(_legend);
      _nav.appendChild(_input);
      _nav.appendChild(_label);
    });
    this._container.appendChild(_fragment);
    return this._container;
  }

  onRemove() {
    this._container.parentNode.removeChild(this._container);
    this._map = undefined;
  }

  updateLegend(id) {
    let filter = this._map.getFilter('building_pol');
    if (filter){
      const [any, ...filters] = filter[2];
      filter[2] = [any, ...toggle(filters, ["in", this.field, id], (item) => item[2])];
      this._map.setFilter('building_pol', filter);
    }
  }
}
map.addControl(new legendControl(categories, categories_field), 'bottom-left');

Ahora vamos a dar estilo a nuestro control de leyenda. En la sección de estilo de la página escriba:


#map {position: absolute; top: 0; right: 0; bottom: 0; left: 0;}

.filter-group {
  font: 12px/20px 'Ubuntu', sans-serif;
  font-weight: 400;
  position: absolute;
  bottom: 25px;
  z-index: 1;
  border-radius: 4px;
  width: 150px;
  color: rgba(51, 51, 89, 1);
  box-shadow: 0px 15px 68px rgba(51, 51, 89, 0.15);
  background: rgba(255, 255, 255, 0.9);
}
.filter-group input[type='checkbox']:first-child + label {
  border-radius: 6px 6px 0 0;
}
.filter-group label:last-child {
  border-radius: 0 0 6px 6px;
  border: none;
}
.filter-group input[type='checkbox'] {
  display: none;
}
.filter-group input[type='checkbox'] + label {
  display: block;
  cursor: pointer;
  padding: 10px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.25);
  text-transform: capitalize;
}
.filter-group input[type='checkbox'] + label i {
  width: 18px;
  height: 18px;
  float: right;
  margin-right: 0 8px;
  opacity: 0.7;
}
.filter-group input[type='checkbox'] + label:hover {
  background-color: rgba(49, 112, 254, 0.05);
}
.filter-group input[type='checkbox'] + label:before {
  content: '';
  margin-right: 15px;
}
.filter-group input[type='checkbox']:checked + label:before {
  content: '✔';
  margin-right: 5px;
}

Recargue la página, el control de la leyenda debe estar en la esquina inferior izquierda del mapa. Si hacemos clic en alguna de las categorías de la leyenda veremos que la visibilidad de los edificios de esa categoría cambia.

Crear una leyenda de mapa interactiva

Mostrar una imagen de la vista de satélite del edificio seleccionado

Para crear la imagen de la vista aérea del edificio utilizaremos la API de MapTiler Cloud Static Maps.

La API de mapas estáticos no está disponible en el plan gratuito. Contrata uno de nuestros planes para que puedas disfrutar de todas las funciones disponibles en el MapTiler Cloud

Modificaremos la información mostrada al seleccionar un edificio para mostrar la imagen de la vista de satélite del mismo.

Para crear la imagen de la vista de satélite del edificio, necesitamos conocer la caja delimitadora del edificio. Vamos a utilizar la librería Turf.js que nos permite obtener el bounding box de una geometría.

En la cabecera de la página copie la siguiente línea para añadir la biblioteca Turf:

Añade la siguiente línea al inicio de la función createPopupContent:

En la función createPopupContent añade la siguiente línea al final de return justo después de la imagen que muestra la propiedad documentLink

Además, vamos a cambiar la información que se muestra en el campo Uso para que se muestre el texto de la categoría en lugar del código identificador. Para ello, vamos a crear una función que devuelva el texto de la categoría seleccionada.

Justo después de la función toggle copie las siguientes líneas:


  if (index === -1) return [...arr, item];
  return removeAtIndex(arr, index);
}

const getUse = (use, categories) => {
  return categories.reduce((agg, item) => {
  return item.id === use ? item.text : agg;
  }, "Unknown");
}

Llamaremos a esta función para mostrar la información en el campo Uso.

Actualice la aplicación y haga clic en un edificio. Verá la información del edificio junto con una imagen de la vista aérea del mismo.

Crear una vista de satélite del edificio

Consulta el repositorio de Github para obtener el código completo del visor

Resumen

En este artículo hemos visto cómo crear un mapa interactivo con nuestros datos utilizando la API MapTiler Cloud junto con la librería MapLibre.

Lo hemos hecho:

  • Utilizar la API de administración para cargar nuestros datos
  • Subir un mapa base gracias a la API de Mapas
  • Utilice el componente API de geocodificación para buscar lugares
  • Visualizar nuestros datos como mosaicos vectoriales utilizando la API de mosaicos
  • Crear un mapa Choropleth
  • Crear una leyenda interactiva
  • Crear una imagen de la vista de satélite de un edificio utilizando la API de Static Maps

Más información sobre MapTiler Cloud API

API de administración

Cómo cargar MBTiles o GeoPackage en MapTiler Cloud utilizando la API

Cómo hacer mapas con la API de MapTiler Cloud - Casos de uso y ejemplos

Wladimir Szczerban

Promotor Senior de Desarrollo
Publicado el 12 de abril de 2022

Descubra MapTiler en francés.

Visite maptiler.fr

Přečtěte si více v češtině

Ver en maptiler.cz

Read more in English

Visite MapTiler.es

Más en Holandés

Ir a MapTiler.nl

日本語で詳細をみる

maptiler.jp へ

Encontrará más información

auf MapTiler.de

ديزملا فشتكإ

maptiler.ae ىلإ لقتنا

Explorar MapTiler en Suiza

Visite MapTiler.ch