Leaflet_shiny

Entrada en la que os mostraré cómo hacer un mapa con Leaflet en R que además añadimos a un Shiny para poder filtrar datos de forma interactiva. Ya mostramos en el blog cómo crear mapas marcando coordenadas con Leaflet y R de forma muy sencilla y hoy damos una vuelta de tuerca a aquella entrada: las coordenadas que deseamos representar tienen, además, algún factor por el que hay especial interés en realizar un filtrado del mapa. Para ilustrar el ejemplo nos vamos a ir al Centro de descargas del Centro Nacional de Información Geográfica y nos bajamos del servidor los datos municipales, en concreto a Nomenclátor Geográfico de Municipios y Entidades de Población; descargamos el archivo y tenemos un ZIP que contiene un CSV llamado MUNICIPIOS.CSV.

El ejercicio va a consistir en lo más simple: marcar los municipios filtrando por provincia, nada más, pero el resultado será un Shiny que podréis aprovechar para sofisticar vuestros mapas. El programa total es:

library(shiny)
library(shinyWidgets)
library(leaflet)
library(dplyr)

# Importación
ub <- "MUNICIPIOS.csv"
municipios <- read.csv2(ub)

# Siempre tendremos latitud y longitud
municipios$latitud <- as.numeric(municipios$LATITUD_ETRS89)
municipios$longitud <- as.numeric(municipios$LONGITUD_ETRS89)

# Factores para filtros
filtro1 <- municipios %>% arrange(COD_INE) %>% select(PROVINCIA) %>% unique()

# Shiny
ui <- fluidPage(
  p(),
  titlePanel("Municipios"),
  fluidRow(
    column(width = 3,
           pickerInput("prov", "Seleccionar provincia", choices = filtro1$PROVINCIA, 
                       selected = filtro1$PROVINCIA[1], options = list(`actions-box` = TRUE), multiple = TRUE)),
    mainPanel(leafletOutput("mapa", height = 800))
  )
)

server <- function(input, output, session) {
  # Definimos los puntos a representar
  puntos <- eventReactive(input$prov, {
    fil <- municipios %>% dplyr::filter(PROVINCIA %in% input$prov)
    return(fil)
  }, ignoreNULL = FALSE)

  # Realizamos el mapa
  output$mapa <- renderLeaflet({
    leaflet() %>% 
      addTiles() %>% 
      setView(lng = -3.68444444, lat = 40.30861111, zoom = 7) %>% 
      addProviderTiles(providers$CartoDB.Positron) %>% 
      addMarkers(data = puntos(), lng = ~longitud, lat = ~latitud)
  })
}

shinyApp(ui, server)

Ahora vamos paso a paso.

Datos de origen

library(shiny)
library(shinyWidgets)
library(leaflet)

# Importación
ub <- "MUNICIPIOS.csv"
municipios <- read.csv2(ub)

# Siempre tendremos latitud y longitud
municipios$latitud <- as.numeric(municipios$LATITUD_ETRS89)
municipios$longitud <- as.numeric(municipios$LONGITUD_ETRS89)

En nuestros datos con las coordenadas siempre tendremos el campo latitud y longitud como numéricos. Básico.

Creación del filtro

# Factores para filtros
filtro1 <- municipios %>% arrange(COD_INE) %>% select(PROVINCIA) %>% unique()

Muy simple, necesitamos los nombres de las provincias en un objeto al que luego llamaremos al crear el filtro; un truco: dad orden.

Fluid layout

ui <- fluidPage(
  p(),
  titlePanel("Municipios"),
  fluidRow(
    column(width = 3,
           pickerInput("prov", "Seleccionar provincia", choices = filtro1$PROVINCIA, 
                       options = list(`actions-box` = TRUE), multiple = TRUE)),
    mainPanel(leafletOutput("mapa", height = 800))
  )
)

El formato más sencillo: título, fluidRow y mainPanel. En fluidRow vamos a poner los elementos del panel de la izquierda; a sólo una columna ponemos un pickerInput, que es el tipo de filtro que he elegido y que suelo elegir habitualmente cuando puedo tener múltiples selecciones; podéis investigar más los filtros. El resultado de este filtro será nuestro input$prov y necesita datos y elecciones. Buscad documentación sobre pickerInput, pero si no queréis complicaros la existencia, tal cual. Si queréis añadir más filtros, añadid más; en mi caso no complico mucho copiando y pegando. En mainPanel especificamos qué tipo de output queremos ver, en este caso leafletOutput, y el nombre del mapa resultante será mapa; sí destaco el uso de height = 800 para ajustarlo mejor en pantalla.

Server

server <- function(input, output, session) {
  # Definimos los puntos a representar
  puntos <- eventReactive(input$prov, {
    fil <- municipios %>% dplyr::filter(PROVINCIA %in% input$prov)
    return(fil)
  }, ignoreNULL = FALSE)

  # Realizamos el mapa
  output$mapa <- renderLeaflet({
    leaflet() %>% 
      addTiles() %>% 
      setView(lng = -3.68444444, lat = 40.30861111, zoom = 7) %>% 
      addProviderTiles(providers$CartoDB.Positron) %>% 
      addMarkers(data = puntos(), lng = ~longitud, lat = ~latitud)
  })
}

shinyApp(ui, server)

Vemos que puntos será el conjunto de datos para presentar las coordenadas en el mapa; se creará en el momento en el que modifiquemos el filtro; por ello usamos eventReactive; si cambiamos, se desarrolla el dashboard. Añadimos el código necesario para crear un data frame con el filtrado filter(PROVINCIA %in% input$prov) y siempre finalizamos con los elementos de nuestros “datos reactivos”. El mapa será nuestra salida Leaflet y el código es simple, ya se vio en el blog, y empleamos addMarkers(data = puntos()), que es el conjunto de datos “reactivo” donde tenemos siempre longitud, latitud. Si deseamos añadir más elementos en nuestros datos reactivos lo hacemos en el cbind o directamente devolviendo el data frame filtrado; prefiero no complicarlo en esta ocasión.

Y el resultado de todo esto ya le habéis visto. En fin, yo usando Shiny; quién me ha visto y quién me ve defendiendo el mundo libre. Subiré el código a Git y haré un vídeo porque sobre estos cimientos se pueden hacer maravillas.