Mapas insertados con ggplot2

Hoy os presento un breve post sobre cómo podemos posicionar un territorio ultraperiférico cerca del mapa principal o insertar un mapa de orientación. En este ejemplo usamos el típico mapa de España en el que se situan las Islas Canarias en el suroeste de la península.

Paquetes

Paquete Descripción
tidyverse Conjunto de paquetes (visualización y manipulación de datos): ggplot2, dplyr, purrr,etc.
mapSpain Límites administrativos de España a diferentes niveles
sf Simple Feature: importar, exportar y manipular datos vectoriales
giscoR Límites administrativos del mundo
patchwork Simple gramática para combinar ggplots separados en el mismo gráfico
rmapshaper Cliente de la librería ‘mapshaper’ para operaciones geoespaciales
# instalamos los paquetes si hace falta
if(!require("tidyverse")) install.packages("tidyverse")
if(!require("mapSpain")) install.packages("mapSpain")
if(!require("sf")) install.packages("sf")
if(!require("giscoR")) install.packages("giscoR")
if(!require("patchwork")) install.packages("patchwork")
if(!require("rmapshaper")) install.packages("rmapshaper")

# paquetes
library(sf)
library(giscoR)
library(mapSpain)
library(tidyverse)
library(patchwork)
library(rmapshaper)

Opción 1

Podemos encontrar fácilmente algunos límites administrativos de estados como España, en los que se ha cambiado la posición geográfica real de un territorio alejado, como ocurre con las Islas Canarias. El paquete mapSpain por defecto desplaza las islas al suroeste de la Península Ibérica, una posición habitual que vemos en muchos mapas. No obstante, estos límites vectoriales con desplazmiento no los podemos usar en todos los supuestos, dado que se trata de una posición geográfica falsa no es apto para cálculos espaciales ni tampoco para otras proyecciones.

Obtenemos los límites vectoriales usando la función esp_get_prov() para el nivel provincial con el código de proyección EPSG:4326 (WGS84). En la construcción del mapa vía ggplot2 simplemente añadimos el objeto a la geometría geom_sf() específicamente diseñado para el manejo de objetos vectoriales de clase sf.

# limites provincias con desplazmiento de Canarias
esp <- esp_get_prov(epsg = 4326)

# mapa básico
ggplot(esp) +
  geom_sf(colour = "white", linewidth = .2) +
    theme_void()

mapSpain también incluye una función para obtener la caja de separación (esp_get_can_box()) con el objetivo de indicar la falsa localización. Con la función gisco_get_countries() obtenemos los límites globales de países para añadirlo como contexto geográfico, aunque lo recortamos a la extensión de la Península Ibérica. Puede sorprender ver un recorte curvado usando la proyección WGS84, pero se debe a que se usa geometría esférica por defecto en todas las operaciones con sf (sf_use_s2()).

# añadimos la caja de Canarias y los límites del entorno
can_bx <- esp_get_can_box(epsg = 4326)
entorno <- gisco_get_countries(resolution = "10") %>% 
             st_crop(xmin = -10, xmax = 5, ymin = 34, ymax = 45)

# con Canarias desplazadas
ggplot(esp) +
  geom_sf(data = entorno, fill = "grey70", colour = NA) +
  geom_sf(colour = "white", linewidth = .2) +
  geom_sf(data = can_bx, linewidth = .3, colour = "grey80") +
  theme_void()

Opción 2

La forma más correcta es crear un objeto para el mapa insertado, aquí las Islas Canarias, y otro para el mapa principal, España peninsular y las Islas Baleares. En la función esp_get_prov() debemos indicar que nos devuelva los límites sin desplazamiento con el agrumento moveCAN = FALSE. Primero, construimos el mapa de Canarias, filtrando la comunidad autónoma. Las geometrías geom_hline() y geom_vline() dibujarán la línea de separación hacia la penínula. El segundo paso consiste en crear el mapa principal excluyendo las Islas Canarias. Después falta por incluir el mapa de Canarias como objeto empleando la función annotation_custom(). El objeto ggplot lo debemos convertir a un grob con ggplotGrob() e indicar el área de posición (los extremos de X y Y) en el sistema de coordenadas del mapa principal. Esta forma se puede usar para todos los tipos de mapas.

# límites de provincias sin desplazmiento de Canarias
esp <- esp_get_prov(epsg = 4326, moveCAN = F)

# mapa de Canarias
can <-  filter(esp, nuts2.name == "Canarias") %>%
          ggplot() +
            geom_vline(xintercept = -13.3, colour = "grey80") +
            geom_hline(yintercept = 29.5, colour = "grey80") +
            geom_sf(fill = "red", colour = "white") +
            coord_sf(expand = F) +
            theme_void() 
can

# añadir mapa ggplot con annotation_custom() posición absoluta según SRC
filter(esp, nuts2.name != "Canarias") %>%
  ggplot() +
  geom_sf(data = entorno, fill = "grey70", colour = NA) +
  geom_sf(colour = "white", linewidth = .2) +
  annotation_custom(ggplotGrob(can),
                    xmin = -14, xmax = -9,
                    ymin = 33, ymax = 38) +
  theme_void()

Si queremos proyectar el mapa principal, únicamente debemos proyectar antes el área de posición del mapa insertado.

# caja de posición con algun ajuste 
pos <- c(xmin = -13.5, ymin = 32.5, xmax = -8.5, ymax = 37.5) 
class(pos) <- "bbox" # definimos como bbox

# reproyectamos a  LAEA Europe EPSG:3035
pos_prj <- st_as_sfc(pos) %>% 
  st_set_crs(4326) %>%
  st_transform(3035) %>% 
  st_bbox()

# creamos el mapa final
filter(esp, nuts2.name != "Canarias") %>%
  ggplot() +
  geom_sf(data = entorno, fill = "grey70", colour = NA) +
  geom_sf(colour = "white", linewidth = .2) +
  annotation_custom(ggplotGrob(can),
                    xmin = pos_prj[1], xmax = pos_prj[3],
                    ymin = pos_prj[2], ymax = pos_prj[4]) +
  coord_sf(crs = 3035) +
  theme_void()

Opción 3

La última opción para insersar un mapa secundario es el uso de la función inset_element() del paquete patchwork. La gran diferencia con el método anterior es la posición relativa, lo que limita el uso. En este caso no se deberían representar símbolos proporcionales dado que la insertación relativa no mantiene las mismas dimensiones del mapa principal.

# limites provincias
esp <- esp_get_prov(epsg = 4326, moveCAN = F)

# mapa Canarias
can <-  filter(esp, nuts2.name == "Canarias") %>%
          ggplot() +
            geom_vline(xintercept = -13.3, colour = "grey80") +
            geom_hline(yintercept = 29.5, colour = "grey80") +
            geom_sf(fill = "red", colour = "white") +
            coord_sf(expand = F) +
            theme_void() 

# mapa principal
m <- filter(esp, nuts2.name != "Canarias") %>%
  ggplot() +
  geom_sf(colour = "white", linewidth = .2) +
  theme_void()

# insertar con posición relativa 
m + inset_element(can, left = -.1, bottom = 0, 
                  right = .2, top = .2, 
                  align_to = "full")

Globo terráqueo como mapa insertado

La única dificultad aquí consiste en la proyección ortogonal preservando la geometría visible de la tierra. El primer paso es la creación del “océano” usando el radio de la tierra desde el punto 0,0. Después únicamente debemos recortar la parte visible y reproyectar los límites. En la definición de la proyección ortogonal es posible centrar en diferentes latitudes y longitudes cambiando los valores de +lat_0 y +lon_0. La funciones ms_innerlines() del paquete rmapshaper crean fácilmente los límites internos de polígonos, lo que es recommendable para evitar el desdibuje de pequeñas áreas.

# límites globales de países
wld <- gisco_get_countries(resolution = "20")

# definición de la proyección ortogonal
ortho_crs <-'+proj=ortho +lat_0=30 +lon_0=0.5 +x_0=0 +y_0=0 +R=6371000 +units=m +no_defs +type=crs'

# creación del oceano 
ocean <- st_point(x = c(0,0)) %>%
            st_buffer(dist = 6371000) %>% # radio Tierra
              st_sfc(crs = ortho_crs)
plot(ocean)

# recortamos la tierra visible y reproyectamos
world <-   st_intersection(wld, st_transform(ocean, 4326)) %>%
            st_transform(crs = ortho_crs) %>% 
            mutate(dummy = ifelse(NAME_ENGL == "Spain", "yes", "no"))

plot(world)

# obtenemos únicamente los límites internos
world_line <- ms_innerlines(world)
plot(world_line)

# mapa principal de España 
wld_map <- ggplot(world) +
            geom_sf(data = ocean, fill = "#deebf7", linewidth = .2) +
            geom_sf(aes(fill = dummy), 
                    colour = NA,
                    show.legend = F) +
            geom_sf(data = world_line, linewidth = .05, colour = "white") +
            scale_fill_manual(values = c("grey50", "red")) + 
            theme_void()

# insertamos el globo marcando la localización de España 
m + inset_element(wld_map, left = 0.65, bottom = 0.82, right = 1.1, top = 1, align_to = "full")

Buy Me A Coffee

Dr. Dominic Royé
Dr. Dominic Royé
Investigador y responsable de ciencia de datos

Relacionado