Presentación

Es habitual que las organizaciones, de acuerdo a sus intereses, estructuren el territorio en entidades que no coinciden con las unidades administrativas oficiales. Esto puede generar un problema a la hora de visualizar sus datos y métricas en informes y cuadros de mando.

En este trabajo se presentan diversos desarrollos a partir de la librería MapSpain, donde por agregación y desagregación de las unidades administrativas estándar (comunidades autónomas, provincias y municipios) se pueden definir nuevas entidades de acuerdo a las necesidades de cada momento. Es también posible incorporar otras subdivisiones menores no incluidas en la librería MapSpain, como barrios de ciudades o comarcas no coincidentes con las oficialmente definidas.

Las nuevas entidades pueden a su vez mantener la estructura de las entidades menores que las conforman, o bien fusionarlas completamente dentro de la nueva entidad. Esto permite tanto el modificar el nivel de detalle al que se presentan los datos, como redefinir las visualizaciones si se produce un cambio en la estructura territorial de la organización.



Preparación del entorno

Establecer workdir:

setwd("~/R/Creacion de mapas personalizados de Espana con R y MapSpain")

Cargar librerías necesarias:

library (mapSpain)
library (tidyverse)
library (sf)
library (knitr)
library (readxl)
library (kableExtra)
library (rsconnect)



Visión general

La librería mapSpain permite crear mapas de España utilizando diferentes niveles administrativos: municipio, comarca, provincia, CCAA y país.

Un aspecto importante es que los mapas no están como data sets, sino que se invocan desde un conjunto de funciones referidas a los niveles mencionados.

Por ejemplo, es posible representar un mapa de España directamente desde la función esp_get_country () de la librería mapSpain.

esp_get_country() %>% ggplot() + geom_sf() + theme_minimal()

Es también posible crear primero un objeto a partir de dicha función y operar posteriormente con él. En este caso, vamos a utilizar la función esp_get_ccaa(), que contiene los datos de las comunidades autónomas, y vamos a asignarla al objeto “ccaa”.

ccaa <- esp_get_ccaa()
ccaa %>% ggplot() + geom_sf() + theme_minimal()

Vamos a examinar el objeto “ccaa”. Empezamos por ver su contenido.

names(ccaa)
##  [1] "codauto"           "iso2.ccaa.code"    "nuts1.code"       
##  [4] "nuts2.code"        "ine.ccaa.name"     "iso2.ccaa.name.es"
##  [7] "iso2.ccaa.name.ca" "iso2.ccaa.name.gl" "iso2.ccaa.name.eu"
## [10] "nuts2.name"        "cldr.ccaa.name.en" "cldr.ccaa.name.es"
## [13] "cldr.ccaa.name.ca" "cldr.ccaa.name.ga" "cldr.ccaa.name.eu"
## [16] "ccaa.shortname.en" "ccaa.shortname.es" "ccaa.shortname.ca"
## [19] "ccaa.shortname.ga" "ccaa.shortname.eu" "nuts1.name"       
## [22] "geometry"

Hay 22 columnas. Las 21 primeras son codificaciones de las comunidades autónomas en diversos estándares e idiomas. La última, geometry, contiene los puntos que definen al polígono correspondiente a cada una de ellas.

Vamos a seleccionar 3 de esas columnas, para mostrar su contenido. La sintaxis es la típica del universo tidyverse.

ccaa <- ccaa %>% select(codauto, ine.ccaa.name, geometry)
head(ccaa) %>% 
  kable("html", align=c('l', 'r','r')) %>% 
  kable_styling(full_width = FALSE, position="left")%>% 
  row_spec(0, background = "#F3E2A9")
codauto ine.ccaa.name geometry
15 01 Andalucía MULTIPOLYGON (((-4.268895 3…
7 02 Aragón POLYGON ((-0.724501 42.9201…
2 03 Asturias, Principado de MULTIPOLYGON (((-4.512301 4…
14 04 Balears, Illes MULTIPOLYGON (((3.176714 39…
19 05 Canarias MULTIPOLYGON (((-11.18653 3…
3 06 Cantabria MULTIPOLYGON (((-3.153338 4…

Codauto es el código de la ccaa, ine.ccaa.name es su nombre según las tablas normalizadas del INE y geometry la definición de los polígonos que la conforma.

Ahora vamos a ver que clase de objeto es.

class(ccaa)
## [1] "sf"         "data.frame"

Es un objeto con una naturaleza dual. Por un lado es un objeto espacial, y, por otro, una tabla de datos. Esto nos ha permitido tanto representarlo directamente en el sistema gráfico ggplot mediante la función geom_sf(), como operar sobre el con la sintaxis propia del universo tidyverse. Esta última propiedad es la que va a permitir desarrollar fácilmente mapas personalizados a partir de otras estructuras previas.



Contenido

En este trabajo se muestran tres posibles maneras de crear mapas personalizados:



Agrupación de unidades administrativas existentes o estándar

En este ejemplo vamos a suponer que se nos pide un mapa de España con 4 zonas, constituida cada una de ellas por diversas comunidades autónomas. En concreto, son las zonas Norte, Sur, Centro y Levante.

A partir del objeto ccaa ya creado, vamos a ver los nombres de las comunidades autónomas.

ccaa$ine.ccaa.name
##  [1] "Andalucía"                   "Aragón"                     
##  [3] "Asturias, Principado de"     "Balears, Illes"             
##  [5] "Canarias"                    "Cantabria"                  
##  [7] "Castilla y León"             "Castilla - La Mancha"       
##  [9] "Cataluña"                    "Comunitat Valenciana"       
## [11] "Extremadura"                 "Galicia"                    
## [13] "Madrid, Comunidad de"        "Murcia, Región de"          
## [15] "Navarra, Comunidad Foral de" "País Vasco"                 
## [17] "Rioja, La"                   "Ceuta"                      
## [19] "Melilla"

La estrategia va a ser crear 4 objetos, uno por cada zona, seleccionando en cada uno de ellos las ccaa correspondientes. Evidentemente, esta asignación ya ha sido proporcionada por el “cliente”.

Crear Zona Norte

Norte <- ccaa %>% select (codauto, ine.ccaa.name) %>% 
  filter (ine.ccaa.name %in% c("Galicia", "Asturias, Principado de", 
                               "Cantabria", "País Vasco" )) 

Vemos la tabla resultante.

head(Norte) %>% 
  kable("html", align=c('l', 'l','r')) %>% 
  kable_styling(full_width = FALSE, position="left")%>% 
  row_spec(0, background = "#F3E2A9")
codauto ine.ccaa.name geometry
03 Asturias, Principado de MULTIPOLYGON (((-4.512301 4…
06 Cantabria MULTIPOLYGON (((-3.153338 4…
12 Galicia MULTIPOLYGON (((-7.699736 4…
16 País Vasco MULTIPOLYGON (((-2.412847 4…

Para que el objeto Norte esté completo y listo para su utilización, hay que añadirle dos nuevas columnas, conteniendo un código y un nombre para la zona.

Norte <- Norte %>% 
  mutate(cod_zona = 1,
         name_zona = "Zona Norte")

Comprobamos.

head(Norte) %>% 
  kable("html", align=c('l', 'l','r')) %>% 
  kable_styling(full_width = FALSE, position="left")%>% 
  row_spec(0, background = "#F3E2A9")
codauto ine.ccaa.name geometry cod_zona name_zona
03 Asturias, Principado de MULTIPOLYGON (((-4.512301 4… 1 Zona Norte
06 Cantabria MULTIPOLYGON (((-3.153338 4… 1 Zona Norte
12 Galicia MULTIPOLYGON (((-7.699736 4… 1 Zona Norte
16 País Vasco MULTIPOLYGON (((-2.412847 4… 1 Zona Norte

Podemos representar el objeto para ver el resultado.

Norte %>%
  ggplot () +
  geom_sf() +
  theme_minimal()

Repetimos esta operación para el resto de zonas.

Zona Sur

Sur <- ccaa %>%  
  select (codauto, ine.ccaa.name) %>% 
  filter (ine.ccaa.name %in% c("Andalucía", "Canarias", "Ceuta", "Melilla")) %>% 
  mutate(cod_zona = 2,
         name_zona = "Zona Sur")

Zona Centro

Centro <- ccaa %>%  
  select (codauto, ine.ccaa.name) %>% 
  filter (ine.ccaa.name %in% c("Castilla y León", "Rioja, La", 
                               "Navarra, Comunidad Foral de", "Aragón", 
                               "Madrid, Comunidad de", "Castilla - La Mancha", 
                               "Extremadura")) %>%
  mutate(cod_zona = 3,
         name_zona = "Zona Centro")

Zona Levante

Levante <- ccaa %>%   
  select (codauto, ine.ccaa.name) %>%   
  filter (ine.ccaa.name %in% c( "Comunitat Valenciana", "Murcia, Región de",
                                "Cataluña",  "Balears, Illes" )) %>% 
  mutate(cod_zona = 4,
         name_zona = "Zona Levante")

Unir las zonas

Ahora unimos las zonas que se han creado, creadndo un nuevo objeto, mapa_zonas, utilizando la función rbind() de R base.

mapa_zonas <- rbind(Norte, Sur, Centro, Levante)

Vemos la tabla que henos creado.

head(mapa_zonas) %>%    
  kable("html", align=c('l', 'l','r')) %>%    
  kable_styling(full_width = FALSE, position="left") %>%    
  row_spec(0, background = "#F3E2A9")
codauto ine.ccaa.name cod_zona name_zona geometry
03 Asturias, Principado de 1 Zona Norte MULTIPOLYGON (((-4.512301 4…
06 Cantabria 1 Zona Norte MULTIPOLYGON (((-3.153338 4…
12 Galicia 1 Zona Norte MULTIPOLYGON (((-7.699736 4…
16 País Vasco 1 Zona Norte MULTIPOLYGON (((-2.412847 4…
01 Andalucía 2 Zona Sur MULTIPOLYGON (((-4.268895 3…
05 Canarias 2 Zona Sur MULTIPOLYGON (((-11.18653 3…

Podemos representar el objeto para ver el mapa resultante.

mapa_zonas %>% 
  ggplot() +
  geom_sf(aes(fill = name_zona)) +
  theme_minimal()



Fusionar o no fusionar

Las nuevas entidades pueden a su vez mantener la estructura de las entidades menores que las conforman, o bien fusionarlas completamente dentro de la nueva entidad. Para entender las implicaciones de esta decisión, vamos a poner un ejemplo.

Tenemos una tabla con los resultados de cada zona, por ejemplo, el volumen de ventas.

Cargamos los datos.

datos_zonas_figurados <- read_excel("./data/datos zonas figurados.xlsx")

head(datos_zonas_figurados)
## # A tibble: 4 x 2
##   cod_zona  Ventas
##      <dbl>   <dbl>
## 1        1 2000000
## 2        2 1500000
## 3        3 3200000
## 4        4  890000

Los unimos al mapa por zonas.

mapa_zonas_datos <-left_join(mapa_zonas, datos_zonas_figurados)

Esta es la tabla resultante:

head(mapa_zonas_datos) %>%    
  kable("html", align=c('l', 'l','c', 'l', 'r','c'    )) %>%    
  kable_styling(full_width = FALSE, position="left") %>%    
  row_spec(0, background = "#F3E2A9")
codauto ine.ccaa.name cod_zona name_zona Ventas geometry
03 Asturias, Principado de 1 Zona Norte 2000000 MULTIPOLYGON (((-4.512301 4…
06 Cantabria 1 Zona Norte 2000000 MULTIPOLYGON (((-3.153338 4…
12 Galicia 1 Zona Norte 2000000 MULTIPOLYGON (((-7.699736 4…
16 País Vasco 1 Zona Norte 2000000 MULTIPOLYGON (((-2.412847 4…
01 Andalucía 2 Zona Sur 1500000 MULTIPOLYGON (((-4.268895 3…
05 Canarias 2 Zona Sur 1500000 MULTIPOLYGON (((-11.18653 3…

La representamos.

mapa_zonas_datos %>% 
  ggplot() +
  geom_sf(aes(fill = name_zona)) +
  theme_minimal()

Parece correcto, pero, ¿qué ocurre si queremos poner en cada zona el valor de las ventas?

mapa_zonas_datos %>%    
  ggplot() +   
  geom_sf(aes(fill = name_zona, label = Ventas), alpha = 0.5) +   
  geom_sf_text(aes(label = Ventas)) +
  theme_minimal()

La etiqueta se ha representado en cada comunidad autónoma. Esto es así, porque en la tabla que conforma el objeto mapa_zonas, estas siguen existiendo de forma independiente. Para evitar este problema hay que fusionarlas.

mapa_zonas_datos <- mapa_zonas_datos %>%
  group_by(cod_zona, name_zona) %>%
  summarise(Ventas = mean(Ventas)) %>%
  st_cast()

Comprobamos en el mapa.

mapa_zonas_datos %>%    
  ggplot() +   
  geom_sf(aes(fill = name_zona, label = Ventas), alpha = 0.5) +   
  geom_sf_text(aes(label = Ventas)) +
  theme_minimal()

El formato es claramente mejorable, pero el problema se ha solucionado.

Esto no quiere decir que siempre sea necesario fusionar las entidades espaciales. Puede ser buena idea disponer de las dos capas de objetos. Superponiéndolas, es posible mostrar el detalle de los contornos de cada ccaa sin que las etiquetas se repitan.

mapa_zonas_datos %>%    
  ggplot() +   
  geom_sf(aes(fill = name_zona, label = name_zona), alpha = 0.5) +   
  geom_sf_text(aes(label = name_zona)) + 
  geom_sf(data = mapa_zonas, color = "darkgrey", fill = NA) +
  theme_minimal()



Desagregación de niveles administrativos existentes

En este ejemplo vamos a establecer del paquete mapSpain un objeto con el detalle de las provincias de España para crear una división de las provincias de Andalucía. La distribución será Andalucía Oriental y Andalucía Occidental.

Establecemos del paquete mapSpain un objeto con el detalle de provincias de España.

provincias <- esp_get_prov()

Dibujamos el mapa de España por provincias.

provincias %>%
  ggplot + 
  geom_sf () +
  labs (fill="",
      title= "Distribución de provincias de España") +
  theme_minimal()+
  theme (
    axis.title = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank() )

De este paquete, vamos a extraer la CCAA de Andalucía para crear un nuevo objeto y poder trabajar con él.

provincias_Andalucía <- provincias %>%
  select (nuts2.name, ine.prov.name, cpro) %>%
  filter (nuts2.name == "Andalucía")

Dibujamos el mapa de las provincias de Andalucía.

provincias_Andalucía  %>%
  ggplot + 
  geom_sf (data=provincias_Andalucía ,
           fill = "lightblue") +
  labs (fill="",
        title= "Distribución de provincias en Andalucía") +
  geom_sf_text (aes(label=ine.prov.name)) +
  theme_minimal()+
  theme (
    axis.title = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank() )

Vamos a dividir las provincias de Andalucía en: Oriental (Córdoba, Jaén, Granada y Almería) y Occidental (Huelva, Sevilla, Cadiz y Málaga).

provincias_Andalucía2 <- provincias_Andalucía %>%
  mutate (división = ifelse (ine.prov.name %in% c("Córdoba", "Jaén", "Granada", "Almería"),
                            "Oriental", "Occidental" ))

Como hemos visto con anterioridad, las nuevas entidades pueden a su vez mantener la estructura de las entidades menores que las conforman, o bien fusionarlas completamente dentro de la nueva entidad. Decidimos fusionar polígonos para que sólo muestre el contorno de esta agrupación de provincias y no las entidades menores de las cuales está formado.

Andalucía <- provincias_Andalucía2 %>%
  group_by(división) %>%
  summarise (n=1) %>%
  st_cast()

Dibujamos esta nueva estructura.

Andalucía %>%
  ggplot + 
  geom_sf (aes(fill = división)) +
  labs (fill="",
        title= "Nueva distribución creada de Andalucía") +
  geom_sf_text (aes(label= división)) +
  theme_minimal()+
  theme (
    axis.title = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank(),
    legend.position = "none")



Importando datos externos que no contiene el paquete mapSpain

En este ejemplo vamos a incorporar datos externos de los distritos de Barcelona Ciudad encontrados en la página de Open Data del Ayuntamiento de Barcelona. La información se puede encontrar en este enlace.

Una vez importados los datos, vamos a extraer del paquete mapSpain datos de los municipios de la comarca del Barcelonés. Crearemos un objeto nuevo excluyendo el municipio de Barcelona Capital para luego poder incorporar los datos de los distritos.

Cargamos los datos externos de los distritos de Barcelona ciudad.

distritos_Barcelona <- st_read("data/Distritos Barcelona Capital")
## Reading layer `0301040100_Districtes_UNITATS_ADM' from data source 
##   `C:\Users\SAG0000\Documents\R\Creacion de mapas personalizados de Espana con R y MapSpain\data\Distritos Barcelona Capital' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 10 features and 48 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 420812.5 ymin: 4574282 xmax: 435480.4 ymax: 4591066
## Projected CRS: ETRS89 / UTM zone 31N

Seleccionamos sólo las columnas que nos interesan.

distritos_Barcelona <- distritos_Barcelona %>%
  select(CODI_UA, NOM, geometry)

Añadimos una nueva columna para luego poder diferenciar distritos de municipios.

distritos_Barcelona <- distritos_Barcelona %>%
  mutate (tipo= "distrito")

Extraemos los municipios del Barcelonés de mapSpain, excepto Barcelona Capital, y añadimos una columna para poder diferenciar municipios de distritos, como hemos hecho anteriormente con los distritos.

Barcelones <- esp_get_munic() %>% 
  filter(name %in% c("Badalona", 
                     "Santa Coloma de Gramenet", 
                     "Hospitalet de Llobregat, L'",
                     "Sant Adrià de Besòs")) %>%
  mutate(tipo = "municipio")

Representamos este objeto.

ggplot() + 
  geom_sf(data = Barcelones, aes(fill = name)) +
  geom_sf_text(data = Barcelones, aes(label = name)) +
  theme_minimal() +
  labs(fill = "", 
       title = "Municipios de la comarca del Barcelonés", 
       subtitle = "Sin el municipio de Barcelona capital") +
  theme(
    axis.title = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank(),
    legend.position = "none") 

Seleccionamos las mismas columnas que necesitamos y en el mismo orden que en los distritos.

Barcelones <- Barcelones %>% 
  select(cmun, name, tipo, geometry)

Cambiamos el nombre de las columnas para que coincidan y así después poder crear un nuevo objeto sin problemas.

Barcelones <- Barcelones %>%
  rename(CODI_UA=cmun) %>%
  rename (NOM=name) 

Unificamos los sistemas de coordenadas (asignar a distritos Barcelona los atributos de Barcelonés).

(El CRS es un dato que puede obtenerse directamente de un objeto espacial que lo tenga informado, mediante la función st_crs). En este caso:

st_crs(Barcelones)
## Coordinate Reference System:
##   User input: EPSG:4258 
##   wkt:
## GEOGCRS["ETRS89",
##     DATUM["European Terrestrial Reference System 1989",
##         ELLIPSOID["GRS 1980",6378137,298.257222101,
##             LENGTHUNIT["metre",1]]],
##     PRIMEM["Greenwich",0,
##         ANGLEUNIT["degree",0.0174532925199433]],
##     CS[ellipsoidal,2],
##         AXIS["geodetic latitude (Lat)",north,
##             ORDER[1],
##             ANGLEUNIT["degree",0.0174532925199433]],
##         AXIS["geodetic longitude (Lon)",east,
##             ORDER[2],
##             ANGLEUNIT["degree",0.0174532925199433]],
##     USAGE[
##         SCOPE["Spatial referencing."],
##         AREA["Europe - onshore and offshore: Albania; Andorra; Austria; Belgium; Bosnia and Herzegovina; Bulgaria; Croatia; Cyprus; Czechia; Denmark; Estonia; Faroe Islands; Finland; France; Germany; Gibraltar; Greece; Hungary; Ireland; Italy; Kosovo; Latvia; Liechtenstein; Lithuania; Luxembourg; Malta; Moldova; Monaco; Montenegro; Netherlands; North Macedonia; Norway including Svalbard and Jan Mayen; Poland; Portugal; Romania; San Marino; Serbia; Slovakia; Slovenia; Spain; Sweden; Switzerland; United Kingdom (UK) including Channel Islands and Isle of Man; Vatican City State."],
##         BBOX[32.88,-16.1,84.17,40.18]],
##     ID["EPSG",4258]]

Como el Barcelonés tiene el EPSG:4258 asignamos a distritos_Barcelona el mismo.

distritos_Barcelona <- st_transform(distritos_Barcelona, crs = 4258) 

Unimos los dos objetos (Distritos_Barcelona y Barcelonés)

Barcelones <- rbind(Barcelones, distritos_Barcelona)

Por último, representamos nuestro nuevo objeto que contiene los municipios del Barcelonés y los distritos que forman Barcelona capital en un mapa.

ggplot() + 
  geom_sf(data = Barcelones, aes(fill = tipo)) +
  geom_sf_text(data = Barcelones,
               aes(label = NOM), size = 3) +
  theme_minimal() +
  labs(fill = "", 
       title = "El Barcelonés", 
       subtitle = "Municipios y Distritos de Barcelona capital") +
  theme(
    axis.title = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank())