Generar un mapa con Python rápidamente de forma automatizada es una enorme ventaja a todas luces.
El hecho de poder mostrar información geográfica en mapas de forma simple y a mayor velocidad que con un GIS de escritorio facilita las cosas a gran cantidad de profesionales.
Esto es especialmente relevante si debemos visualizar una gran cantidad de información de manera repetitiva o si tan sólo deseamos inspeccionar nuestros datos.
Matplotlib y Geopandas en Python
Esto es posible con unas pocas líneas de código con Python y haciendo uso de apenas dos librerías tan conocidas como matplotlib y geopandas.
- Matplotlib es una librería de Python para producir visualizaciones 2D de información de cualquier estilo: gráficos de barras, ternarios, de líneas, temporales, diagramas de dispersión… ¡y también mapas!
- Geopandas es una extensión de la librería pandas de Python para permitir el uso de archivos y operaciones espaciales. Depende, entre otros, de librerías como Shapely, Descartes y Fiona, sobre los que basa su funcionamiento.
Antes de trabajar con estas librerías, debemos asegurarnos de que sus dependencias están instaladas.
En este breve artículo, trataremos de producir un mapa con Python sencillo con las dos librerías mencionadas. Utilizaremos Jupyter Notebook, instalado en local, para mostrar el proceso y visualizar los resultados a medida que avancemos.
Puedes descargar los datos (provenientes del INE) tratados en este tutorial directamente desde aquí:
Creando un mapa simple con Python
Una vez esté arrancado Jupyter Notebook abriremos una nueva libreta e importaremos ambas librerías.
import geopandas as gpd
import matplotlib.pyplot as plt
Posteriormente, cargaremos los datos almacenados en la carpeta desde donde hayamos arrancado Jupyter Notebook. En este caso, para crear un mapa con Python, trabajaremos con datos de natalidad de las provincias españolas:
# Cargar la capa temática
natalidad = "datos/natalidad.geojson"
map_data = gpd.read_file(natalidad)
map_data.head()
Utilizamos geopandas para leer el archivo GeoJSON de origen de los datos, mostramos las cabeceras y las primeras filas con el método .head(). Deberíamos obtener una vista preliminar como la siguiente:
Observamos que existen múltiples columnas de atributos: la Comunidad Autónoma, la Provincia, su código, el valor de natalidad con el que representaremos el mapa de coropletas y finalmente la geometría implícita de tipo polígono.
Posteriormente, configuraremos el tamaño de la figura que devolverá como resultado y establecemos algunos parámetros como los títulos del gráfico y de los ejes:
# Control del tamaño de la figura del mapa
fig, ax = plt.subplots(figsize=(10, 10))
# Control del título y los ejes
ax.set_title('Natalidad por Provincias en España, 2018',
pad = 20,
fontdict={'fontsize':20, 'color': '#4873ab'})
ax.set_xlabel('Longitud')
ax.set_ylabel('Latitud')
# Mostrar el mapa finalizado
map_data.plot(column='NAT2018', cmap='plasma', ax=ax, zorder=5)
Para mostrar el mapa inicial, utilizamos el método .plot() de matplotlib, indicando como columna a mostrar la correspondiente a la natalidad del año 2018. Escogemos también una paleta de colores para representar los datos de dicha columna.
Este es un mapa simple que muestra correctamente la información deseada. Sin embargo, faltan algunos elementos para terminar de dotar de mayor contexto espacial la vista de mapa y una leyenda que permita entender la escala de visualización de los datos.
Para ello, podemos introducir algunos cambios en el código de Python en Jupyter Notebook:
# Control del tamaño de la figura del mapa
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
# Control del encuadre (área geográfica) del mapa
ax.axis([-12, 5, 32, 48])
# Control del título y los ejes
ax.set_title('Natalidad por Provincias en España, 2018',
pad = 20,
fontdict={'fontsize':20, 'color': '#4873ab'})
ax.set_xlabel('Longitud')
ax.set_ylabel('Latitud')
# Añadir la leyenda separada del mapa
from mpl_toolkits.axes_grid1 import make_axes_locatable
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.2)
# Generar y cargar el mapa
map_data.plot(column='NAT2018', cmap='plasma', ax=ax,
legend=True, cax=cax, zorder=5)
# Cargar un mapa base con contornos de países
oceanos = "natural_earth/ne_50m_ocean.shp"
map_oceanos = gpd.read_file(oceanos)
map_oceanos.plot(ax=ax, color='#89c0e8', zorder=0)
Como vemos en el código mostrado, se han introducido mejoras en cuanto al encuadre (ahora enfocando sólo la Península Ibérica y Baleares). La lista dentro de ax.axis() supone las coordenadas de latitud y longitud mínimas y máximas para establecer el encuadre del mapa.
Asimismo, se ha creado una leyenda separada del recuadro del gráfico y, finalmente, se ha añadido una capa base que funcione como referencia espacial bajo la capa de natalidad por provincias.
El resultado obtenido con los cambios implementados en el mapa sería el siguiente:
Por supuesto, sigue tratándose de un mapa de coropletas realmente simple, que muestra una única capa de información. Debemos entender que este tipo de librerías no sirven para sustituir a un GIS de escritorio ni mucho menos.
Sin embargo, podemos llegar a utilizar Python para extender las funcionalidades de análisis y visualización de datos fuera de un entorno GIS habitual, implementar el código dentro de un script y automatizar tareas de visualización de datos, ahorrando un tiempo considerable.
Existen multitud de librerías y soluciones de visualización de datos espaciales en Python y otros lenguajes como JavaScript o R, por ejemplo.
Si quieres aprender acerca de cómo generar visualizaciones o mapas interactivos con Python, visita nuestro artículo de visualización de datos espaciales en Plotly.
También puedes echar un vistazo a nuestro artículo sobre cómo generar un mapa para calcular y visualizar el índice NDVI de vegetación en R.
He descargado el archivo de los datos de la natalidad, para recrear el mismo mapa en mi computadora. Lo he llevado a mi carpeta especial para leer en python y cuando corro el código que ustedes muestran:
# Cargar la capa temática
natalidad = «datos/natalidad.geojson»
map_data = gpd.read_file(natalidad)
map_data.head()
me aparece que no existe ningún archivo con ese nombre. También intenté solo escribiendo:
natalidad = natalidad.geojson
pero no lo encuentra, como si no existiera.
alguna idea?
Hola Esteban,
gracias por tu comentario. Desconozco cómo almacenarías los datos de natalidad, pero es probable que el error tenga origen en las rutas de acceso al archivo. Deberías asegurarte que el directorio de trabajo permite acceder a las rutas que utilizamos como ejemplo emulando la misma estructura, o adaptando las rutas relativas a la forma en que almacenes los archivos. De igual forma, la ruta y el archivo deberían ir especificados entre comillas, es decir: variable = «ruta/al/archivo/archivo.formato». Si sigue sin funcionar, especifica la ruta al directorio completa.
Espero que sea de ayuda.
Un saludo!
guarda el archivo en la raiz y luego colocas :
map_data = gpd.read_file(«./natalidad.geojson»)
Muchas gracias Raúl por el código!
Intentando reproducir tu ejemplo, me daba error puesto que no tenía el archivo ne_50m_ocean.shp. Tras algo de lectura sobre geopandas y averiguar lo que es un shapefile, he encontrado dicho archivo en un zip que se puede descargar en: https://www.naturalearthdata.com/downloads/50m-physical-vectors/50m-ocean/
Por si a alguien más le es útil!
Gracias Juan, me resultó util.
Ya puestos, alguien conoce alguna fuente de shapefiles etc. ?
Saludos
Hola amigos, estoy iniciándome en un proyecto relacionado con mapas interactivos 2d y quisiera saber qué software puedo utilizar para crearlo, lo que tengo en mente es algo visualmente parecido a google maps, pero a menor escala, digamos un campus universitario, estaré muy agradecido por cualquier información que puedan facilitarme, de igual manera estaré indagando en este blog, gracias por sus aportes.
Hola Simón,
gracias por tu comentario. Por lo que comentas entiendo que tu idea es realizar un visor cartográfico centrado sobre un área pequeña, es decir, a una escala cartográfica bastante grande. Sin duda, la mejor opción es apostar por tecnologías de web mapping para configurar un mapa interactivo. Puedes echar un vistazo a nuestro artículo sobre librerías Javascript para web mapping. Decantándote por esta opción debes tener cierto conocimiento en tecnologías web front-end o del lado del cliente (básicamente HTML, CSS y JavaScript).
Si decides apostar por librerías JavaScript para web mapping, tanto Leaflet como OpenLayers pueden ser buenas opciones. Revisa su documentación y ejemplos en sus correspondientes sitios web. Puedes revisar también nuestro artículo sobre la creación de un visor sencillo desde QGIS con qgis2web. Si dominas QGIS, puedes utilizar ese plugin para montar la estructura de la aplicación en pocos minutos y pasar a modificar todo lo necesario para personalizarla en cuanto a aspecto y fuentes de datos.
Alternativamente, existe una plataforma desarrollada por el ICGC (Instituto Cartográfico y Geológico de Catalunya) denominada Instamaps que permite montar visores sencillos de datos de forma guiada y fácil, sin necesidad de tocar código. En todo caso, siempre tendrás opciones comerciales como Carto o la plataforma ESRI para configurar dichos mapas.
Espero que te ayude. Un saludo!
Hola, podra compartir el shp del mapa base
Gracias
Hola,
Tengo una capa con el shp de mi país (provincias de mi país)
Y tengo una capa con un archivo que por registro tiene latitud y longitud.
Estoy usando folium en python y necesito ver cada provincia con un color más oscuro dependiendo de la cantidad de registros con latitud y longitud que estén dentro del polígono de provincia.
Podrían por favor darme alguna sugerencia.
Gracias.