Haciendo mapas en R

Autor/a

Veronica Andreo

Fecha de publicación

31 de octubre de 2023

library(sf)
library(terra)
library(dplyr)
library(spData)
library(spDataLarge)
#remotes::install_github("r-tmap/tmap")
library(tmap)    # for static and interactive maps
library(leaflet) # for interactive maps
library(ggplot2) # tidyverse data visualization package
nz_elev = rast(system.file("raster/nz_elev.tif", package = "spDataLarge"))

Introducción

Un aspecto importante y satisfactorio de la investigación geográfica es la comunicación de los resultados. La elaboración de mapas, el arte de la cartografía, es una habilidad antigua que implica comunicación, atención al detalle y un elemento de creatividad. La creación de mapas estáticos en R es sencilla con la función plot(). Sin embargo, este capítulo se centra en la cartografía con paquetes dedicados a la creación de mapas. Específicamente, en este capítulo veremos en profundidad el paquete (tmap).

Además de ser divertida y creativa, la cartografía también tiene importantes aplicaciones prácticas. Un mapa cuidadosamente elaborado puede ser la mejor forma de comunicar los resultados de su trabajo, pero los mapas mal diseñados pueden dejar una mala impresión. Entre los problemas de diseño más comunes se encuentran la mala colocación, tamaño y legibilidad del texto y la selección descuidada de colores, como se indica en la guía de estilo del Journal of Maps. Además, un mal diseño de los mapas puede dificultar la comunicación de los resultados:

Los mapas de aspecto amateur pueden mermar la capacidad de su público para comprender información importante y debilitar la presentación de una investigación de datos profesional. Los mapas se utilizan desde hace miles de años con fines muy diversos. Algunos ejemplos históricos son los mapas de edificios y propiedad de la tierra de la antigua dinastía babilónica, hace más de 3.000 años, y el mapamundi de Ptolomeo en su obra maestra Geografía, hace casi 2.000 años.

Históricamente, la elaboración de mapas ha sido una actividad realizada únicamente por la élite o en su nombre. Esto ha cambiado con la aparición de software cartográfico de código abierto, como tmap en R y el “diseño de impresión” de QGIS, que permiten a cualquiera elaborar mapas de alta calidad. Los mapas suelen ser también la mejor manera de presentar los resultados de la investigación geocomputacional de forma accesible. La elaboración de mapas es, por tanto, una parte fundamental de la geocomputación y su énfasis no sólo en describir, sino también en cambiar el mundo.

Este capítulo muestra cómo hacer una amplia gama de mapas.

Mapas estáticos

Los mapas estáticos son el tipo más común de salida visual de la geocomputación. Suelen almacenarse en formatos estándar como .png y .pdf para salidas gráficas raster y vectoriales, respectivamente.

La función genérica plot() suele ser la forma más rápida de crear mapas estáticos a partir de objetos espaciales vectoriales y ráster. A veces, la simplicidad y la velocidad son prioritarias, especialmente durante la fase de desarrollo de un proyecto, y aquí es donde destaca plot(). El enfoque base de R es también extensible, con plot() ofreciendo docenas de argumentos. Otro enfoque es el paquete grid que permite un control de bajo nivel de los mapas estáticos.

tmap es un paquete de creación de mapas potente y flexible con valores predeterminados sensibles. Tiene una sintaxis concisa que permite la creación de mapas atractivos con un código mínimo que será familiar a los usuarios de ggplot2. También tiene la capacidad única de generar mapas estáticos e interactivos utilizando el mismo código a través de tmap_mode(). Por último, acepta una gama más amplia de clases espaciales (incluidos los objetos sf y terra) que otras alternativas como ggplot2.

Fundamentos de tmap

Al igual que ggplot2, tmap se basa en la idea de una “gramática de los gráficos”. Esto implica una separación entre los datos de entrada y la estética (cómo se visualizan los datos): cada conjunto de datos de entrada puede ser “mapeado” de diferentes maneras, incluyendo la ubicación en el mapa (definida por la “geometría” de los datos), el color y otras variables visuales. El elemento básico es tm_shape() (que define los datos de entrada: un objeto vectorial o ráster), seguido de uno o más elementos de capa como tm_fill() y tm_dots(). Esta estratificación se muestra en el siguiente fragmento:

# Add fill layer to nz shape
tm_shape(nz) +
  tm_fill() 
# Add border layer to nz shape
tm_shape(nz) +
  tm_borders() 
# Add fill and border layers to nz shape
tm_shape(nz) +
  tm_fill() +
  tm_borders() 

En este caso, el objeto que se pasa a tm_shape() es nz, un objeto sf que representa las regiones de Nueva Zelanda. Se añaden capas para representar nz visualmente, con tm_fill() y tm_borders() creando áreas sombreadas y contornos de bordes, respectivamente.

Se trata de un enfoque intuitivo para la creación de mapas: la tarea habitual de añadir nuevas capas se realiza mediante el operador de suma +, seguido de tm_*(). El asterisco (*) se refiere a una amplia gama de tipos de capas que tienen nombres autoexplicativos, entre los que se incluyen:

  • tm_fill(): áreas sombreadas para (mutli)polígonos
  • tm_borders(): contornos de bordes para (mutli)polígonos
  • tm_polygons(): ambos, áreas sombreadas y contornos de bordes para (mutli)polígonos
  • tm_lines(): líneas para (mutli)linestrings
  • tm_symbols(): símbolos para (mutli)puntos, (mutli)líneas y (mutli)polígonos
  • tm_raster(): celdas coloreadas de datos ráster (también existe tm_rgb() para rásters con tres capas)
  • tm_text(): información de texto para (mutli)puntos, (mutli)líneas y (mutli)polígonos

Objetos de mapa

Una característica útil de tmap es su capacidad para almacenar objetos que representan mapas. El siguiente fragmento de código lo demuestra guardando el último gráfico como un objeto de la clase tmap (nótese el uso de tm_polygons() que condensa tm_fill() + tm_borders() en una única función):

map_nz = tm_shape(nz) + tm_polygons()
class(map_nz)
## [1] "tmap"

El objeto map_nz puede graficarse posteriormente, por ejemplo añadiendo capas adicionales (como se muestra a continuación) o simplemente ejecutando map_nz en la consola, lo que equivale a print(map_nz).

Se pueden añadir nuevas shapes con + tm_shape(new_obj). En este caso, nuevo_obj representa un nuevo objeto espacial que se trazará sobre las capas precedentes. Cuando se añade una nueva forma de este modo, todas las funciones estéticas posteriores hacen referencia a ella, hasta que se añada otra nueva forma. Esta sintaxis permite la creación de mapas con múltiples formas y capas, como se ilustra en el siguiente fragmento de código que utiliza la función tm_raster() para trazar una capa raster (con col_alpha configurado para hacer la capa semitransparente):

map_nz1 = map_nz +
  tm_shape(nz_elev) + tm_raster(col_alpha = 0.7)

A partir del objeto map_nz creado previamente, el código anterior crea un nuevo objeto cartográfico map_nz1 que contiene otra forma (nz_elev) que representa la elevación media de Nueva Zelanda. Pueden añadirse más formas y capas, como se ilustra en el fragmento de código siguiente, que crea nz_water, que representa las aguas territoriales de Nueva Zelanda, y añade las líneas resultantes a un objeto cartográfico existente.

nz_water = st_union(nz) |>
  st_buffer(22200) |> 
  st_cast(to = "LINESTRING")
map_nz2 = map_nz1 +
  tm_shape(nz_water) + tm_lines()

No hay límite en el número de capas o formas que pueden añadirse a los objetos tmap, e incluso puede utilizarse la misma forma varias veces. El mapa final se crea añadiendo una capa que representa los puntos altos (almacenados en el objeto nz_height) al objeto map_nz2 creado previamente con tm_symbols() (véase ?tm_symbols para más detalles sobre las funciones de trazado de puntos de tmap). El mapa resultante, que tiene cuatro capas, se ilustra en el panel derecho de la Figura @ref(fig:tmlayers):

map_nz3 = map_nz2 +
  tm_shape(nz_height) + tm_symbols()

Una característica útil y poco conocida de tmap es que se pueden organizar varios objetos de mapa en un único “meta-plot” con tmap_arrange(). Esto se demuestra en el siguiente fragmento de código, que traza del map_nz1 al map_nz3, dando como resultado la siguiente figura.

tmap_arrange(map_nz1, map_nz2, map_nz3)

Maps with additional layers added to the final map of Figure 9.1.

También se pueden añadir más elementos con el operador +. Los ajustes estéticos, sin embargo, se controlan mediante argumentos a las funciones de capa.

Variables visuales

Los gráficos de la sección anterior muestran la configuración estética por defecto de tmap. Se utilizan tonos grises para las capas tm_fill() y tm_symbols() y una línea negra continua para representar las líneas creadas con tm_lines(). Por supuesto, estos valores por defecto y otros estéticos pueden ser anulados. El propósito de esta sección es mostrar cómo.

Hay dos tipos principales de estética de mapas: los que cambian con los datos y los que son constantes. A diferencia de ggplot2, que utiliza la función de ayuda aes() para representar estéticas variables, tmap acepta unos pocos argumentos estéticos, dependiendo del tipo de capa seleccionada:

  • fill: color de relleno de un polígono
  • col: color del borde de un polígono, línea, punto o trama
  • lwd: ancho de línea
  • lty: tipo de línea
  • size: tamaño de un símbolo
  • shape: forma de un símbolo

Además, podemos personalizar la transparencia de los colores de relleno y borde utilizando fill_alpha y col_alpha.

Para asignar una variable a una estética, pase su nombre de columna al argumento correspondiente, y para establecer una estética fija, pase en su lugar el valor deseado.1 En la próxima figura se ilustra el impacto de establecer estos valores fijos.

ma1 = tm_shape(nz) + tm_polygons(fill = "red")
ma2 = tm_shape(nz) + tm_polygons(fill = "red", fill_alpha = 0.3)
ma3 = tm_shape(nz) + tm_polygons(col = "blue")
ma4 = tm_shape(nz) + tm_polygons(lwd = 3)
ma5 = tm_shape(nz) + tm_polygons(lty = 2)
ma6 = tm_shape(nz) + tm_polygons(fill = "red", fill_alpha = 0.3,
                                 col = "blue", lwd = 3, lty = 2)
tmap_arrange(ma1, ma2, ma3, ma4, ma5, ma6)

The impact of changing commonly used fill and border aesthetics to fixed values.

Al igual que los gráficos R base, los argumentos que definen la estética también pueden recibir valores que varían. A diferencia del código R base que se muestra a continuación, los argumentos estéticos tmap no aceptarán un vector numérico:

plot(st_geometry(nz), col = nz$Land_area)  # works
tm_shape(nz) + tm_fill(col = nz$Land_area) # fails
#> Error: palette should be a character value

En su lugar, col (y otras estéticas que pueden variar, como lwd para capas de líneas y size para capas de puntos) requiere una cadena de caracteres que nombre un atributo asociado a la geometría que se va a trazar. Así, se obtendría el resultado deseado de la siguiente manera:

tm_shape(nz) + tm_fill(fill = "Land_area")

Cada variable visual tiene tres argumentos adicionales relacionados, con sufijos .scale, .legend y .free. Por ejemplo, la función tm_fill() tiene argumentos como fill, fill.scale, fill.legend y fill.free. El argumento .scale determina cómo se representan los valores proporcionados en el mapa y en la leyenda, mientras que el argumento .legend se utiliza para personalizar la configuración de la leyenda, como su título, orientación o posición. El argumento .free es relevante sólo para mapas con muchas facetas para determinar si cada faceta tiene la misma o diferente escala y leyenda.

Escalas

Las escalas controlan cómo se representan los valores en el mapa y en la leyenda, y dependen en gran medida de la variable visual seleccionada. Por ejemplo, cuando nuestra variable visual es col, entonces col.scale controla cómo los colores de los objetos espaciales están relacionados con los valores proporcionados; y cuando nuestra variable visual es size, entonces size.scale controla cómo los tamaños representan los valores proporcionados. Por defecto, la escala utilizada es tm_scale(), que selecciona la configuración visual dada automáticamente por el tipo de datos (factor, numérico y entero).

Veamos cómo funcionan las escalas personalizando los colores de relleno de los polígonos. La configuración del color es una parte importante del diseño de mapas, ya que puede influir mucho en la representación de la variabilidad espacial, como se ilustra en la figura de abajo.Esta figura muestra cuatro formas de colorear las regiones de Nueva Zelanda en función de la renta media, de izquierda a derecha (y se muestra en el fragmento de código siguiente):

  • La configuración por defecto utiliza saltos “bonitos”, que se describen en el párrafo siguiente
  • breaks permite establecer manualmente los saltos
  • n establece el número de intervalos en los que se clasifican las variables numéricas
  • values define el esquema de colores, por ejemplo, BuGn.
tm_shape(nz) + tm_polygons(fill = "Median_income")
tm_shape(nz) + tm_polygons(fill = "Median_income",
                        fill.scale = tm_scale(breaks = c(0, 30000, 40000, 50000)))
tm_shape(nz) + tm_polygons(fill = "Median_income",
                           fill.scale = tm_scale(n = 10))
tm_shape(nz) + tm_polygons(fill = "Median_income",
                           fill.scale = tm_scale(values = "BuGn"))

Illustration of settings that affect color settings. The results show (from left to right): default settings, manual breaks, n breaks, and the impact of changing the palette.
Nota

Todos los argumentos anteriores (breaks, n y values) también funcionan para otros tipos de variables visuales. Por ejemplo, values espera un vector de colores o un nombre de paleta para fill.scale o col.scale, un vector de tamaños para size.scale, o un vector de símbolos para shape.scale.

También podemos personalizar las escalas utilizando una familia de funciones que empiezan con el prefijo tm_scale_. Las más importantes son tm_scale_intervals(), tm_scale_continuous(), y tm_scale_categorical().

La función tm_scale_intervals() divide los valores de los datos de entrada en un conjunto de intervalos. Además de establecer manualmente los breaks, tmap permite a los usuarios especificar algoritmos para crear automáticamente los breaks con el argumento style. Éstas son algunas de las funciones de escala más útiles:

  • style = "pretty": la configuración por defecto, redondea los saltos a números enteros cuando es posible y los espacia uniformemente
  • style = "equal": divide los valores de entrada en intervalos de igual rango y es apropiado para variables con una distribución uniforme (no se recomienda para variables con una distribución sesgada, ya que el mapa resultante puede acabar teniendo poca diversidad de colores)
  • style = "quantile": garantiza que el mismo número de observaciones pertenezcan a cada categoría (con la desventaja potencial de que los intervalos de los intervalos pueden variar mucho)
  • style = "jenks": identifica grupos de valores similares en los datos y maximiza las diferencias entre categorías
  • style = "log10_pretty": una versión logarítmica común (el logaritmo en base 10) del estilo regular pretty utilizado para variables con una distribución sesgada a la derecha
Nota

Aunque style es un argumento de las funciones tmap, en realidad se origina como argumento en classInt::classIntervals() — véase la página de ayuda de esta función para más detalles.

Illustration of different interval scales’ methods set using the style argument in tmap.

La función tm_scale_continuous() presenta un gran número de colores sobre campos de color continuos y es particularmente adecuada para rásters continuos. En el caso de variables con distribución sesgada, también puede utilizar sus variantes: tm_scale_continuous_log() y tm_scale_continuous_log1p(). Por último, tm_scale_categorical() se diseñó para representar valores categóricos y garantiza que cada categoría reciba un color único.

Illustration of continuous and categorical scales in tmap.

Las paletas definen los rangos de color asociados a los bins y determinados por las funciones tm_scale_*(), y sus argumentos breaks y n descritos anteriormente. La paleta de colores por defecto se especifica en tm_layout(); sin embargo, puede cambiarse rápidamente usando el argumento values. Espera un vector de colores o un nuevo nombre de paleta de colores, que puede encontrarse interactivamente con cols4all::c4a_gui(). También puede añadir un - como prefijo del nombre de la paleta de colores para invertir el orden de la paleta.

Nota

Todos los valores por defecto de las variables visuales, como las paletas de colores por defecto para los diferentes tipos de variables de entrada, se pueden encontrar con tmap_options()$values.var.

Hay tres grupos principales de paletas de colores: categórica, secuencial y divergente, y cada uno de ellos sirve para un propósito diferente. También existe un cuarto grupo de paletas de colores, denominadas bivariadas. Se utilizan cuando queremos representar relaciones entre dos variables en un mapa. ] Las paletas categóricas constan de colores fácilmente distinguibles y son las más apropiadas para datos categóricos sin ningún orden concreto, como nombres de estados o clases de cobertura del suelo. Los colores deben ser intuitivos: los ríos deben ser azules, por ejemplo, y los pastos verdes. Evite demasiadas categorías: los mapas con grandes leyendas y muchos colores pueden resultar ininteligibles.

El segundo grupo son las paletas secuenciales. Siguen un gradiente, por ejemplo de colores claros a oscuros (los colores claros suelen representar valores más bajos), y son apropiadas para variables continuas (numéricas). Las paletas secuenciales pueden ser simples (greens va del azul claro al azul oscuro, por ejemplo) o multicolor/tono (yl_gn_bu es un gradiente del amarillo claro al azul pasando por el verde, por ejemplo), como se muestra en el fragmento de código siguiente — no se muestra la salida, ¡ejecute el código usted mismo para ver los resultados!

tm_shape(nz) + 
  tm_polygons("Median_income", fill.scale = tm_scale(values = "greens"))
tm_shape(nz) + 
  tm_polygons("Median_income", fill.scale = tm_scale(values = "yl_gn_bu"))

El tercer grupo, las paletas divergentes, suelen oscilar entre tres colores distintos (morado-blanco-verde en la figura @ref(fig:colpal)) y suelen crearse uniendo dos paletas secuenciales de un solo color con los colores más oscuros en cada extremo. Su principal objetivo es visualizar la diferencia con respecto a un punto de referencia importante, por ejemplo, una temperatura determinada, la mediana de los ingresos familiares o la probabilidad media de que se produzca una sequía. El valor del punto de referencia puede ajustarse en tmap utilizando el argumento midpoint.

tm_shape(nz) + 
  tm_polygons("Median_income",
              fill.scale = tm_scale_continuous(values = "pu_gn_div", 
                                               midpoint = 28000))

Examples of categorical, sequential and diverging palettes.

Hay dos principios importantes a tener en cuenta cuando se trabaja con colores: perceptibilidad y accesibilidad. En primer lugar, los colores de los mapas deben coincidir con nuestra percepción. Esto significa que ciertos colores se ven a través de nuestra experiencia y también de lentes culturales. Por ejemplo, los colores verdes suelen representar vegetación o tierras bajas y el azul se relaciona con el agua o lo frío. Las paletas de colores también deben ser fáciles de entender para transmitir la información con eficacia. Debe quedar claro qué valores son más bajos y cuáles más altos, y los colores deben cambiar gradualmente. En segundo lugar, los cambios de color deben ser accesibles al mayor número de personas. Por lo tanto, es importante utilizar paletas aptas para daltónicos siempre que sea posible.2

Leyendas

Una vez decidida nuestra variable visual y sus propiedades, debemos centrar nuestra atención en el estilo de la leyenda del mapa. Usando la función tm_legend(), podemos cambiar su título, posición, orientación, o incluso desactivarla. El argumento más importante de esta función es title, que establece el título de la leyenda asociada. En general, el título de una leyenda de mapa debe proporcionar dos informaciones: qué representa la leyenda y cuáles son las unidades de la variable presentada. El siguiente fragmento de código demuestra esta funcionalidad proporcionando un nombre más atractivo que el nombre de la variable Área_terrestre (nótese el uso de expression() para crear texto en superíndice):

legend_title = expression("Area (km"^2*")")
map_nza = tm_shape(nz) +
  tm_polygons(fill = "Land_area", fill.legend = tm_legend(title = legend_title))

La orientación por defecto de la leyenda en tmap es "vertical", sin embargo, también es posible una orientación alternativa de la leyenda, "horizontal". Aparte de eso, también podemos personalizar la ubicación de la leyenda utilizando el argumento position.

map_nza2 = tm_shape(nz) +
  tm_polygons(fill = "Land_area",
              fill.legend = tm_legend(title = legend_title,
                                      orientation = "landscape",
                                      position = tm_pos_out("center", "bottom")))

La posición de la leyenda (y también la posición de otros elementos del mapa en tmap) puede personalizarse utilizando una de las siguientes funciones. Las dos más importantes son

  • tm_pos_out(): por defecto, añade la leyenda fuera del área del marco del mapa. Podemos personalizar su ubicación con dos valores que representan la posición horizontal ("left", "center", o "right"), y la posición vertical ("bottom", "center", o "top")
  • tm_pos_in(): coloca la leyenda dentro del área del marco del mapa. Podemos decidir su posición usando dos argumentos, donde el primero puede ser "left", "center" o "right", y el segundo puede ser "bottom", "center", o "top".

Alternativamente, podemos simplemente proporcionar un vector de dos valores (o dos números entre 0 y 1) aquí – y en tal caso, la leyenda se pondrá dentro del marco del mapa.

Disposición

El diseño del mapa se refiere a la combinación de todos los elementos del mapa en un mapa cohesivo. Los elementos del mapa incluyen, entre otros, los objetos a cartografiar, el título, la barra de escala, la rejilla del mapa y los márgenes, mientras que los ajustes de color tratados en la sección anterior se refieren a la paleta y a los puntos de ruptura utilizados para afectar al aspecto del mapa. Ambos pueden dar lugar a cambios sutiles que pueden tener un impacto igualmente grande en la impresión que dejan sus mapas.

Los elementos adicionales del mapa, como las retículas, las flechas del norte, las barras de escala y los títulos del mapa, tienen sus propias funciones: tm_graticules(), tm_compass(), tm_scalebar() y tm_title().3

map_nz + 
  tm_graticules() +
  tm_compass(type = "8star", position = c("left", "top")) +
  tm_scalebar(breaks = c(0, 100, 200), text.size = 1, position = c("left", "top")) +
  tm_title("New Zealand")

Map with additional elements - a north arrow and scale bar.

tmap también permite cambiar una amplia variedad de configuraciones de diseño, algunas de las cuales, producidas usando el siguiente código (ver args(tm_layout) o ?tm_layout para una lista completa), se ilustran en la próxima figura:

map_nz + tm_layout(scale = 4)
map_nz + tm_layout(bg.color = "lightblue")
map_nz + tm_layout(frame = FALSE)

Layout options specified by (from left to right) title, scale, bg.color and frame arguments.

Los demás argumentos de tm_layout() permiten controlar muchos más aspectos del mapa en relación con el lienzo sobre el que se coloca. A continuación se indican algunos ajustes de diseño útiles:

  • Ajustes de márgenes, incluidos outer.margin e inner.margin.
  • Ajustes de fuente controlados por “fontface” y “fontfamily”.
  • Configuración de la leyenda, incluyendo opciones como legend.show (mostrar o no la leyenda) legend.orientation, legend.position, y legend.frame.
  • Anchura del marco (frame.lwd) y una opción para permitir líneas dobles (frame.double.line)
  • Ajustes de color que controlan “color.sepia.intensity” (el aspecto amarillento del mapa) y “color.saturation” (una escala de grises).

Illustration of selected layout options.

Mapas facetados

Los mapas facetados, también denominados “pequeños múltiplos”, se componen de muchos mapas dispuestos uno al lado del otro, y a veces apilados verticalmente. Las facetas permiten visualizar cómo cambian las relaciones espaciales con respecto a otra variable, como el tiempo. Las poblaciones cambiantes de los asentamientos, por ejemplo, pueden representarse en un mapa facetado en el que cada panel representa la población en un momento determinado. La dimensión temporal podría representarse mediante otra variable visual, como el color. Sin embargo, se corre el riesgo de saturar el mapa, ya que se superpondrían múltiples puntos (¡las ciudades no suelen moverse con el tiempo!).

Normalmente, todas las facetas individuales de un mapa facetado contienen los mismos datos geométricos repetidos varias veces, una por cada columna de los datos de atributos (éste es el método de trazado por defecto para los objetos sf). Sin embargo, las facetas también pueden representar geometrías cambiantes, como la evolución de un patrón de puntos a lo largo del tiempo. Este caso de uso del trazado facetado se ilustra a continuación.

urb_1970_2030 = urban_agglomerations |> 
  filter(year %in% c(1970, 1990, 2010, 2030))

tm_shape(world) +
  tm_polygons() +
  tm_shape(urb_1970_2030) +
  tm_symbols(fill = "black", col = "white", size = "population_millions") +
  tm_facets_wrap(by = "year", nrow = 2)

Faceted map showing the top 30 largest urban agglomerations from 1970 to 2030 based on population projections by the United Nations.

El fragmento de código anterior muestra las principales características de los mapas facetados creados con la función tm_facets_wrap():

  • Las formas que no tienen una variable de faceta se repiten (los países en world en este caso)
  • El argumento by que varía en función de una variable ("year" en este caso)
  • El parámetro nrow/ncol que especifica el número de filas y columnas en que deben organizarse las facetas

Alternativamente, es posible usar la función tm_facets_grid() que permite tener facetas basadas en hasta tres variables diferentes: una para rows, una para columns, y posiblemente una para pages.

Además de su utilidad para mostrar relaciones espaciales cambiantes, los mapas facetados también son útiles como base para mapas animados.

Inset maps

Un mapa de inserción o inset map es un mapa más pequeño que se representa dentro del mapa principal o junto a él. Puede servir para muchos fines distintos, como proporcionar un contexto o acercar algunas regiones no contiguas para facilitar su comparación. También pueden utilizarse para centrarse en una zona más pequeña con más detalle o para cubrir la misma zona que el mapa, pero representando un tema diferente.

En el siguiente ejemplo, creamos un mapa de la parte central de los Alpes del Sur de Nueva Zelanda. Nuestro mapa de inserción mostrará dónde se encuentra el mapa principal en relación con toda Nueva Zelanda. El primer paso es definir el área de interés, lo que puede hacerse creando un nuevo objeto espacial, nz_region.

nz_region = st_bbox(c(xmin = 1340000, xmax = 1450000,
                      ymin = 5130000, ymax = 5210000),
                    crs = st_crs(nz_height)) |> 
  st_as_sfc()

En el segundo paso, creamos un mapa base que muestra la zona de los Alpes del Sur de Nueva Zelanda. En él se expone el mensaje más importante.

nz_height_map = tm_shape(nz_elev, bbox = nz_region) +
  tm_raster(col.scale = tm_scale_continuous(values = "YlGn"),
            col.legend = tm_legend(position = c("left", "top"))) +
  tm_shape(nz_height) + tm_symbols(shape = 2, col = "red", size = 1) +
  tm_scalebar(position = c("left", "bottom"))

El tercer paso consiste en la creación del mapa de inserción. Aporta un contexto y ayuda a localizar la zona de interés. Es importante que este mapa indique claramente la ubicación del mapa principal, por ejemplo, indicando sus límites.

nz_map = tm_shape(nz) + tm_polygons() +
  tm_shape(nz_height) + tm_symbols(shape = 2, col = "red", size = 0.1) + 
  tm_shape(nz_region) + tm_borders(lwd = 3) +
  tm_layout(bg.color = "lightblue")

Una de las principales diferencias entre los gráficos normales (por ejemplo, los de dispersión) y los mapas es que los datos de entrada determinan la relación de aspecto de los mapas. Así, en este caso, tenemos que calcular las relaciones de aspecto de nuestros dos conjuntos de datos principales, nz_region y nz. La siguiente función, norm_dim() devuelve la anchura ("w") y la altura ("h") normalizadas del objeto (en unidades "snpc" entendidas en mi dispositivo gráfico).

library(grid)
norm_dim = function(obj){
    bbox = st_bbox(obj)
    width = bbox[["xmax"]] - bbox[["xmin"]]
    height = bbox[["ymax"]] - bbox[["ymin"]]
    w = width / max(width, height)
    h = height / max(width, height)
    return(unit(c(w, h), "snpc"))
}
main_dim = norm_dim(nz_region)
ins_dim = norm_dim(nz)

A continuación, sabiendo las relaciones de aspecto, tenemos que especificar los tamaños y ubicaciones de nuestros dos mapas - el mapa principal y el mapa de inserción - utilizando la función viewport(). Una ventana gráfica es parte de un dispositivo gráfico que utilizamos para dibujar los elementos gráficos en un momento dado. La ventana gráfica de nuestro mapa principal no es más que la representación de su relación de aspecto.

main_vp = viewport(width = main_dim[1], height = main_dim[2])

Por otro lado, la ventana gráfica del mapa de inserción necesita especificar su tamaño y ubicación. En este caso, haremos el mapa de inserción dos veces más pequeño que el principal multiplicando la anchura y la altura por 0,5, y lo situaremos a 0,5 cm de la parte inferior derecha del marco del mapa principal.

ins_vp = viewport(width = ins_dim[1] * 0.5, height = ins_dim[2] * 0.5,
                  x = unit(1, "npc") - unit(0.5, "cm"), y = unit(0.5, "cm"),
                  just = c("right", "bottom"))

Por último, combinamos los dos mapas creando un nuevo lienzo en blanco, imprimiendo el mapa principal y colocando el mapa insertado dentro de la ventana del mapa principal.

grid.newpage()
print(nz_height_map, vp = main_vp)
pushViewport(main_vp)
print(nz_map, vp = ins_vp)

Inset map providing a context - location of the central part of the Southern Alps in New Zealand.

El mapa de inserción puede guardarse en un archivo utilizando un dispositivo gráfico o la función tmap_save() y sus argumentos - insets_tm y insets_vp.

Los mapas de inserción también se utilizan para crear un mapa de zonas no contiguas. Probablemente, el ejemplo más utilizado sea un mapa de Estados Unidos, que consta de los Estados Unidos contiguos, Hawai y Alaska. Es muy importante encontrar la mejor proyección para cada inserto individual en este tipo de casos. Podemos utilizar US National Atlas Equal Area para el mapa de los Estados Unidos contiguos poniendo su código EPSG en el argumento projection de tm_shape().

us_states_map = tm_shape(us_states, crs = "EPSG:2163") + tm_polygons() + 
  tm_layout(frame = FALSE)

El resto de nuestros objetos, hawaii y alaska, ya tienen proyecciones adecuadas; por tanto, sólo tenemos que crear dos mapas distintos:

hawaii_map = tm_shape(hawaii) +
  tm_polygons() + 
  tm_title("Hawaii") +
  tm_layout(frame = FALSE, bg.color = NA, 
            title.position = c("LEFT", "BOTTOM"))
alaska_map = tm_shape(alaska) +
  tm_polygons() + 
  tm_title("Alaska") +
  tm_layout(frame = FALSE, bg.color = NA)

El mapa final se crea combinando y ordenando estos tres mapas:

us_states_map
print(hawaii_map, vp = grid::viewport(0.35, 0.1, width = 0.2, height = 0.1))
print(alaska_map, vp = grid::viewport(0.15, 0.15, width = 0.3, height = 0.3))

Map of the United States.

El código presentado más arriba es compacto y puede utilizarse como base para otros mapas de inserción, pero los resultados proporcionan una mala representación de las ubicaciones de Hawai y Alaska. Para un enfoque más detallado, consulte la viñeta us-map del geocompkg.

Mapas animados

Los mapas facetados pueden mostrar cómo cambian las distribuciones espaciales de las variables (por ejemplo, a lo largo del tiempo), pero el enfoque tiene desventajas. Las facetas se vuelven diminutas cuando hay muchas. Además, el hecho de que cada faceta esté físicamente separada en la pantalla o página hace que las diferencias sutiles entre facetas puedan ser difíciles de detectar.

Los mapas animados resuelven estos problemas. Aunque dependen de la publicación digital, esto está dejando de ser un problema a medida que aumenta el contenido en línea. Los mapas animados pueden seguir mejorando los informes en papel: siempre se puede enlazar a los lectores con una página web que contenga una versión animada (o interactiva) de un mapa impreso para que cobre vida. Hay varias formas de generar animaciones en R, incluso con paquetes de animación como gganimate, que se basa en ggplot2. Esta sección se centra en la creación de mapas animados con tmap porque su sintaxis le resultará familiar de las secciones anteriores y por la flexibilidad del enfoque.

La siguiente figura es un ejemplo sencillo de mapa animado. A diferencia del gráfico facetado, no aprieta varios mapas en una sola pantalla y permite al lector ver cómo evoluciona en el tiempo la distribución espacial de las aglomeraciones más pobladas del mundo (véase la versión animada en el sitio web del libro).

El mapa animado ilustrado anteriormente puede crearse utilizando las mismas técnicas tmap que generan mapas facetados. Hay dos diferencias, sin embargo, relacionadas con los argumentos en tm_facets():

  • nrow = 1, ncol = 1 se añaden para mantener un momento en el tiempo como una capa
  • free.coords = FALSE, que mantiene la extensión del mapa para cada iteración del mapa.

Estos argumentos adicionales se demuestran en el siguiente fragmento de código4:

urb_anim = tm_shape(world) + tm_polygons() + 
  tm_shape(urban_agglomerations) + tm_symbols(size = "population_millions") +
  tm_facets(by = "year", nrow = 1, ncol = 1, free.coords = FALSE)

El urb_anim resultante representa un conjunto de mapas separados para cada año. La etapa final consiste en combinarlos y guardar el resultado como un archivo .gif con tmap_animation(). El siguiente comando guarda la animación ilustrada:

tmap_animation(urb_anim, filename = "urb_anim.gif", delay = 25)

Mapas interactivos

Aunque los mapas estáticos y animados pueden animar los conjuntos de datos geográficos, los mapas interactivos pueden llevarlos a un nuevo nivel. La interactividad puede adoptar muchas formas, la más común y útil de las cuales es la posibilidad de desplazarse y hacer zoom en cualquier parte de un conjunto de datos geográficos superpuesto a un “mapa web” para mostrar el contexto. Los niveles de interactividad menos avanzados incluyen ventanas emergentes que aparecen al hacer clic en diferentes características, una especie de etiqueta interactiva. Los niveles más avanzados de interactividad incluyen la capacidad de inclinar y girar los mapas, como se demuestra en el ejemplo mapdeck a continuación, y la provisión de subparcelas “enlazadas dinámicamente” que se actualizan automáticamente cuando el usuario hace paneos y zooms.

Sin embargo, el tipo de interactividad más importante es la visualización de datos geográficos en mapas web interactivos o “deslizantes”. El lanzamiento del paquete leaflet en 2015 (que utiliza la biblioteca JavaScript leaflet) revolucionó la creación de mapas web interactivos desde dentro de R y una serie de paquetes se han construido sobre estos cimientos añadiendo nuevas características (por ejemplo, leaflet.extras) y haciendo que la creación de mapas web sea tan sencilla como la creación de mapas estáticos (por ejemplo, mapview y tmap). Esta sección ilustra cada enfoque en el orden inverso. Exploraremos cómo hacer mapas deslizantes con tmap (cuya sintaxis ya hemos aprendido), mapview, mapdeck y finalmente leaflet (que proporciona un control de bajo nivel sobre los mapas interactivos).

Una característica única de tmap es su capacidad para crear mapas estáticos e interactivos utilizando el mismo código. Los mapas se pueden ver de forma interactiva en cualquier punto cambiando al modo de vista, utilizando el comando tmap_mode("view"). Esto se demuestra en el siguiente código, que crea un mapa interactivo de Nueva Zelanda basado en el objeto tmap map_nz:

tmap_mode("view")
map_nz

Ahora que el modo interactivo ha sido ‘activado’, todos los mapas producidos con tmap se lanzarán (otra forma de crear mapas interactivos es con la función tmap_leaflet()). Entre las características destacables de este modo interactivo se incluye la posibilidad de especificar el mapa base con tm_basemap() (o tmap_options()) como se demuestra a continuación (resultado no mostrado):

map_nz + tm_basemap(server = "OpenTopoMap")

Una característica impresionante y poco conocida del modo de visualización de tmap es que también funciona con gráficos facetados. El argumento sync de tm_facets() puede utilizarse en este caso para producir múltiples mapas con ajustes de zoom y panorámica sincronizados, como se ilustra abajo:


world_coffee = left_join(world, coffee_data, by = "name_long")
facets = c("coffee_production_2016", "coffee_production_2017")

coffee <- 
  tm_shape(world_coffee) + tm_polygons(facets) + 
  tm_facets_wrap(nrow = 2, sync = TRUE)

coffee

Cambie tmap de nuevo al modo de trazado con la misma función:

tmap_mode("plot")

Si no domina tmap, la forma más rápida de crear mapas interactivos en R puede ser con mapview. El siguiente ‘one liner’ es una forma fiable de explorar interactivamente una amplia gama de formatos de datos geográficos:

mapview::mapview(nz)

mapview tiene una sintaxis concisa pero potente. Por defecto, dispone de algunas funciones estándar de los SIG, como información sobre la posición del ratón, consulta de atributos (mediante ventanas emergentes), barra de escala y botones de zoom a capa. También ofrece controles avanzados, como la posibilidad de “burst” conjuntos de datos en varias capas y la adición de varias capas con “+” seguido del nombre de un objeto geográfico. Además, permite colorear automáticamente los atributos mediante el argumento zcol. En esencia, puede considerarse una API de leaflet basada en datos (para más información sobre leaflet, véase más adelante). Dado que mapview siempre espera un objeto espacial (incluidos sf y SpatRaster) como primer argumento, funciona bien al final de expresiones canalizadas. Consideremos el siguiente ejemplo, en el que sf se utiliza para intersecar líneas y polígonos y, a continuación, se visualiza con mapview.

library(mapview)
oberfranken = subset(franconia, district == "Oberfranken")
trails |>
  st_transform(st_crs(oberfranken)) |>
  st_intersection(oberfranken) |>
  st_collection_extract("LINESTRING") |>
  mapview(color = "red", lwd = 3, layer.name = "trails") +
  mapview(franconia, zcol = "district") +
  breweries

Una cosa importante a tener en cuenta es que las capas de mapview se añaden mediante el operador + (similar a ggplot2 o tmap). Por defecto, mapview utiliza la biblioteca JavaScript leaflet para renderizar los mapas de salida, que es fácil de usar y tiene un montón de características. Sin embargo, algunas bibliotecas de renderizado alternativas podrían ser más performantes (trabajar más suavemente en conjuntos de datos más grandes). mapview permite establecer bibliotecas de renderizado alternativas ("leafgl" y "mapdeck") en mapviewOptions(). 5. Para más información sobre mapview, consulte el sitio web del paquete en: r-spatial.github.io/mapview/.

Existen otras formas de crear mapas interactivos con R. El paquete googleway, por ejemplo, proporciona una interfaz de mapas interactivos que es flexible y extensible (véase googleway-vignette para más detalles). Otro enfoque del mismo autor es mapdeck, que proporciona acceso al framework Deck.gl de Uber. Su uso de WebGL permite visualizar de forma interactiva grandes conjuntos de datos de hasta millones de puntos. El paquete utiliza Mapbox tokens de acceso, para lo cual debe registrarse antes de utilizar el paquete.

Nota

Ten en cuenta que el siguiente bloque asume que el token de acceso está almacenado en tu entorno R como MAPBOX=your_unique_key. Esto se puede añadir con usethis::edit_r_environ().

Una característica exclusiva de mapdeck es que ofrece perspectivas interactivas en 2,5D, como se ilustra en la figura (fig:mapdeck). Esto significa que puedes desplazarte, hacer zoom y rotar por los mapas, y ver los datos “extruidos” del mapa.

Por último, pero no por ello menos importante, está leaflet, que es el paquete de mapeo interactivo más maduro y utilizado en R. leaflet proporciona una interfaz de nivel relativamente bajo a la biblioteca JavaScript Leaflet y muchos de sus argumentos pueden entenderse leyendo la documentación de la biblioteca JavaScript original (véase leafletjs.com).

Los mapas Leaflet se crean con leaflet(), cuyo resultado es un objeto de mapa leaflet que puede ser canalizado a otras funciones leaflet. Esto permite añadir múltiples capas de mapas y ajustes de control de forma interactiva, como se demuestra en el siguiente código.

pal = colorNumeric("RdYlBu", domain = cycle_hire$nbikes)
leaflet(data = cycle_hire) |> 
  addProviderTiles(providers$CartoDB.Positron) |>
  addCircles(col = ~pal(nbikes), opacity = 0.9) |> 
  addPolygons(data = lnd, fill = FALSE) |> 
  addLegend(pal = pal, values = ~nbikes) |> 
  setView(lng = -0.1, 51.5, zoom = 12) |> 
  addMiniMap()

The leaflet package in action, showing cycle hire points in London. See interactive version online.

Notas

  1. Si hay un conflicto entre un valor fijo y un nombre de columna, el nombre de columna tiene preferencia. Esto se puede comprobar ejecutando el siguiente trozo de código después de ejecutar nz$red = 1:nrow(nz).↩︎

  2. Véanse las opciones “Visión del color” y el panel “Apto para daltónicos” en cols4all::c4a_gui().↩︎

  3. Otros elementos adicionales del mapa son tm_grid(), tm_logo() y tm_credits().↩︎

  4. También existe un atajo para este enfoque: tm_facets_pagewise()↩︎

  5. También puede intentar utilizar mapviewOptions(georaster = TRUE) para obtener visualizaciones de mayor rendimiento de grandes datos rasterizados↩︎