Leaflet mapas dinamicos
Indice
Leaflet
Leaflet es indiscutiblemente el lider de las librerías de código abierto para la creación de mapas dinámicos libres. Está escrita en JavaScript y por tanto pensada para internet desde sus entrañas.
Esta maravilla de código se puede acceder y gestionar desde R con la librería library(leaflet) de manera super simple y al complementarse con R tenemos enormes posibilidades de creacción.
Primeros pasos
Hacer un plano con leaflet es muy fácil:
- cargar la librería:
leaflet()
- llamar a la función que carga la capa base:
addTiles()
Una nota previa, leaflet hace uso intensivo de la función tubería (pipe in english), por lo que es conveniente que sepas de qué va el símbolo %>% de los paquetes dplyr o tidyverse.
# para instalar leaflet:
# install.packages("leaflet")
# Cargamos el paquete
library(leaflet)
# carga del plano base
leaflet() %>% addTiles()
A partir de aquí todo consiste en añadir funciones de carga de capas o funciones que personalizan la forma de visualizar el mapa o las capas.
Cargar capas en el mapa
Vamos explicar cómo visualizar datos en un mapa. Los más sencillos son pintar los datos de una tabla xy. Partimos de una tabla (data.frame) que tiene dos campos con las coordenadas lon-lat (x-y).
Pasando la tabla como argumento a la función leaflet y añadiendo una función de trazado de marcas podemos pintarlo en un mapa dinámico. Leaflet detecta los campos con nombres como lat lon, pero en caso de no detectarlos siempre se puede especificar con en la función: addMarkers(lng=df$lon,lat=df$lat...)
Para el caso de puntos como una tabla x-y podemos usar las funciones addMarkers
o addCircleMarkers
indistintamente. Cada una tiene varias opciones de personalización. Veamos un ejemplo:
# creamos una data.frame con puntos aleatorios en la zona de Levante (España)
df = data.frame(
lat = rnorm(50, mean=39,sd=0.5),
lng = rnorm(50,mean=0,sd=1),
size = runif(50, 5, 20),
color= sample(colors(),50)
)
# creamos un objeto de mapa leaflet
m = leaflet(df) %>% addTiles() # añade el mapa por defecto de OpenStreetMap
# añadimos Una capa de marcas con los datos
# la capa tendrá una popup con el texto marcado
m %>% addMarkers(popup= ~paste("Hola mundo", size) )
# Cambiamos las marcas por circulos, que tengan un tamaño y color diferente según los valores
# creamos una paleta de color continua colorNumeric es funcion de leaflet
pal <- colorNumeric(palette = "Spectral", domain = c(df$size), reverse = TRUE)
m %>% addCircleMarkers(radius = ~size, color = ~pal(size), fill = ~pal(size),popup= ~paste0("mi tamaño es:",size))
Con los comandos addMarkers
, y addCircleMarkers
hemos añadido una capa de puntos, pero podemos añadir polígonos, lineas…controles, capas WMS, cada una tiene su función específica delectura de los datos origen:
addControl
: Añade controles HTML al mapaaddTiles
: Añade la capa baseaddWMSTiles
: Añade capa WMS al mapaaddPopups
: Añade popups a una capaaddMarkers
: Añade marcas de posición e iconos al mapaaddLabelOnlyMarkers
: Añade etiquetas al mapaaddCircleMarkers
: Añade marcas de circulos al mapahighlightOptions
: añade las opciones de subrayado al pasar por encima de una capa (highligh)addCircles
: Añade circulos al mapaaddPolylines
: Añade capa de polilíneas al mapaaddRectangles
: Añade rectangulos al mapaaddPolygons
: Añade capa de poligonos al mapaaddGeoJSON
: Añade datos GeoJSON al mapaaddTopoJSON
: Añade capas TopoJSON al mapa
Añadir capas shp y kml
La forma más sencilla de leer y pintar una capa en leaflet es usando la función st_read()
del paquete sf
. Esta función lee multitud de formatos de SIG y convierte la capa en objeto sf.
Vamos a ver dos ejemplos de capas una de puntos en fomato kml y otra de polígonos en formato shp.
library(sf)
# lectura de una capa kml
capa_kml<-st_read('../../static/capas/Restaurantes.kml')
## Reading layer `Restaurantes' from data source `C:\R\publicaciones\enrdados\static\capas\Restaurantes.kml' using driver `KML'
## Simple feature collection with 35 features and 2 fields
## geometry type: POINT
## dimension: XYZ
## bbox: xmin: -3.689788 ymin: 37.56571 xmax: 2.176187 ymax: 41.3794
## epsg (SRID): 4326
## proj4string: +proj=longlat +datum=WGS84 +no_defs
# vemos los nombres de las variables
names(capa_kml)
## [1] "Name" "Description" "geometry"
#pintar las capas
leaflet() %>%
addTiles() %>%
addMarkers(data = capa_kml,
label = capa_kml$Name,
labelOptions =
labelOptions(style = list("font-weight" = "normal",
padding = "3px 8px"),textsize = "12px"))
# lectura de un shp
capa_shp<-st_read('../../static/capas/confederaciones.shp')
## Reading layer `confederaciones' from data source `C:\R\publicaciones\enrdados\static\capas\confederaciones.shp' using driver `ESRI Shapefile'
## Simple feature collection with 9 features and 1 field
## geometry type: MULTIPOLYGON
## dimension: XY
## bbox: xmin: -12071.73 ymin: 3988549 xmax: 1019462 ymax: 4858225
## epsg (SRID): NA
## proj4string: NA
names(capa_shp)
## [1] "ID" "geometry"
# como no tiene un sistema de referencia debemos ponerle uno
# la capa es en UTM en España y probeblemente epgs 25830
st_crs(capa_shp)<-25830
# transformamos las coordenadas a geográficas lon-lat
capa_shp<-st_transform(capa_shp,crs=4326)
m<-leaflet() %>%
addTiles() %>%
addPolygons(data = capa_shp,stroke = TRUE, fillOpacity = 0.8,
highlight = highlightOptions(weight = 3,
color = "red",
bringToFront = TRUE))
m
Si nos fijamos hemos cambiado la funcion de lectura, para en un caso pintar marcas con addMarkers
y en otro pintar poligonos con addPolygons
.
El problema con los objetos sf es que hay un bug en la librería de leaflet y a veces una cada da problemas principalmente con la clase de objetos de polígonos y polilíneas.
Hay dos maneras de solucionar el problema, leer la capa como sp en lugar de sf, eso se puede hacer de varias formas os dejamos un par de ejemlos:
# con la libraría rgdal
library(rgdal)
# usando la función readOGR se pueden leer shp,
# leemos la capa Rios.shp en la carpeta data
capa_rios<-readOGR(dsn = 'C:/data',layer = 'Rios',verbose=FALSE)
# con la libraría raster
library(raster)
capa_rios2 <- raster::shapefile("data/Rios.shp")
# Otra opcion es convertir en GEOJSON
capaGeoJSON<-toGeoJSON(system.file(package="leafletR", "files", "data/Rios.kml"))
mapview
A mi, personalmente me parece una mejor solución usar la librería mapview
. Este paquete añade una nueva función a leaflet que permite leer un objeto sf sin problemas addFeatures()
. Vamos a ver un ejemplo con una capa de lineas que da problemas con la lectura con la función de addPolylines()
# cargamos lalibrería mapview
library(mapview)
# lectura de una capa kml
capa_kml<-st_read('../../static/capas/canal_postrasvase.kml')
## Reading layer `canal_postrasvase.kml' from data source `C:\R\publicaciones\enrdados\static\capas\canal_postrasvase.kml' using driver `KML'
## Simple feature collection with 1 feature and 2 fields
## geometry type: LINESTRING
## dimension: XYZ
## bbox: xmin: -1.863226 ymin: 37.41523 xmax: -0.7457735 ymax: 38.16088
## epsg (SRID): 4326
## proj4string: +proj=longlat +datum=WGS84 +no_defs
# vemos los nombres de las variables
names(capa_kml)
## [1] "Name" "Description" "geometry"
capa_kml$Name<-"red postrasvase"
leaflet(capa_kml)%>%
addProviderTiles("Esri.WorldTopoMap") %>%
addFeatures(capa_kml,color = "red",weight=12,label = ~capa_kml$Name)
Opciones de personalización
Para personalizar un mapa existen multitud de opciones de configuración, vamos a ver solo las más habituales.
Capa base
Una de las caracteristicas más interesantes es cambiar el mapa de fondo por defecto a cualquiera de los mapas libres disponibles. Esto se hace con el comando sutituyendo addTiles()
por addProviderTiles()
.
# Cambiamos fondo a tipo toner
leaflet(df) %>% addProviderTiles(providers$Stamen.Toner) %>%
addCircleMarkers(radius = 7, color = "red",
popup= paste("adios",df$size) )
En estos momentos existen 137 mapas de fondo disponibles, ¿ cómo saberlo?, pues ejecutando el comando names(providers)
, que enumera todos los proveedores de capas base.
a |
---|
OpenStreetMap |
OpenStreetMap.Mapnik |
OpenStreetMap.BlackAndWhite |
OpenStreetMap.DE |
OpenStreetMap.CH |
OpenStreetMap.France |
OpenStreetMap.HOT |
OpenStreetMap.BZH |
OpenInfraMap |
OpenInfraMap.Power |
Añadir Grupos
Para dejar a la elección del usuario el mapa de fondo, podemos hacer grupos y mostrar un selector de capa base en el plano, también podemos hacer grupos en las capas que vams añadiendo al mapa como marcas, poligonos, lineas. Los grupos se forman con la función addLayersControl
, en la que está el parámetro baseGroups
para las capas de fondos y overlayGroups
para el resto de capas:
leaflet(df) %>%
addProviderTiles("Stamen.TonerLite",group = "Toner") %>%
addProviderTiles("HikeBike", group = "Bike") %>%
addProviderTiles("Esri", group = "Esri") %>%
addProviderTiles("Stamen.Watercolor", group="Acuarela") %>%
addCircleMarkers(radius = 4, color = "red",group="capapuntos", popup= ~paste0("mi tamaño es:",size)) %>%
addLayersControl(overlayGroups = c("capapuntos"),baseGroups = c("Toner", "Bike", "Esri","Acuarela"))
Otras opciones generales: zoom, busqueda, medida, escala
Los mapas con Leaflet son personalizables en muchos aspectos, podemos añadir un escala gráfica, el norte, leyendas, fijar el zoom máximo y mínimo, fijar una caja de visualización del mapa (fitBounds), fijar el punto central de inicio del mapa (setView) …
# fijar un punto central y un zoom
leaflet() %>%
addTiles() %>%
setView(lng = -73.98575,
lat = 40.74856,
zoom = 13)
# fijar una caja de inicio, fitbounds
leaflet() %>%
addTiles() %>%
fitBounds(
lng1 = -73.910, lat1 = 40.773,
lng2 = -74.060, lat2 = 40.723)
# limitar zooms y permitir o no el dragging con la mano
leaflet(options =
leafletOptions(dragging = FALSE,
minZoom = 14,
maxZoom = 18)) %>%
addProviderTiles("CartoDB") %>%
setView(lng = -73.98575, lat = 40.74856, zoom = 18)
# Establecer max caja en un mapa
leaflet() %>%
setMaxBounds(lng1 = dc_hq$lon[2] + .05,
lat1 = dc_hq$lat[2] + .05,
lng2 = dc_hq$lon[2] - .05,
lat2 = dc_hq$lat[2] - .05)
# opcion de limpiar el mapa
m %>% clearBounds() %>% clearMarkers()
Ejemplo completo con algunas personalizaciones:
# establer valores generales de zoom minimo y maximo
leaflet(df) %>%
setView(lng = -0.15, lat = 40.14, zoom = 8)%>%
addProviderTiles("Stamen.TonerLite",group = "Toner") %>%
addProviderTiles("HikeBike", group = "Bike") %>%
addProviderTiles("Esri", group = "Esri") %>%
addProviderTiles("Stamen.Watercolor", group="Acuarela") %>%
addLayersControl(baseGroups = c("OSM", "Bike", "Esri","Acuarela")) %>% addCircleMarkers(radius = 5, color = ~pal(size), opacity = 0.8,
popup= ~paste0("mi tamaño es: ",signif(size,2)))%>%
addLegend(pal = pal,
values = df$size,
opacity = 0.75,
title = "Tamaño",
position = "topleft")%>%
# añade el pluggin con opciones de medicion
addMeasure(position = "bottomleft",
primaryLengthUnit = "meters",
primaryAreaUnit = "sqmeters",
activeColor = "#3D535D",
completedColor = "#7D4479")
Popups con html
Otra funcionalidad importante es que podemos usar html en la popups y hacerlas muy atractivas. Por ejemplo usando el tag <br/>
para crear una linea de corte. o usar la función sprintf
para generar una lista completa de las eqtiquetas formateadas a partir de los valores de la tabla o capa de origen.
# Añadimos directamente código html
m<-leaflet(df) %>% addTiles() %>%
addCircleMarkers(data = df, radius = 2, color = ~color,
popup = ~paste0("el color es:", "<br/>", color))
m
# creamos una etiqueta compleja en html con sprintf
labels <- sprintf(
"Color:<strong>%s</strong><br/> tamaño: %g m<sup>2</sup>",
df$color, df$size
) %>% lapply(htmltools::HTML)
m %>% addCircleMarkers(data = df, radius = 2, color = ~color,
label = labels)
Colores
Leaflet tiene funciones específicas para colorear escalas en los mapas. Hay tres funciones básicas:
colorFactor
para colorear variables discretascolorNumeric
para escalas continuas numéricascolorQuantile
para mejorar la coloracion en conjuntos con sesgo
# creamos escala discreta
str(df)
## 'data.frame': 50 obs. of 4 variables:
## $ lat : num 38.5 39.4 40.2 39.2 38.8 ...
## $ lng : num 1.143 0.561 1.761 2.86 0.233 ...
## $ size : num 11.22 19.63 5.72 10.61 10.21 ...
## $ color: Factor w/ 50 levels "azure1","cornsilk4",..: 4 8 29 33 40 27 14 7 3 25 ...
# creamos un factor sobre el valor size con 3 cortes de valores
df$tamano<-cut(df$size,3)
# Creo una escala discreta
pal <- colorFactor(palette = c("red", "blue", "#9b4a11"),
levels = levels(df$tamano))
# pinto el mapa con leaflet
leaflet(df) %>%
addProviderTiles(providers$Stamen.Terrain) %>%
addCircleMarkers(radius = 5,
color = ~pal(tamano),
popup = ~paste0("Tamano=", "<br/>", signif(size,2))) %>%
addLegend(position = "bottomright",
title = "Leyenda dato",
pal = pal,
values = levels(df$tamano))
Si lo que queremos es añadir una escala continua usaremos colorNUmeric()
.
# ejemplo de escala continua
palcontinua <- colorNumeric(palette = "Reds", domain = df$size, reverse = TRUE)
leaflet(df) %>%
addProviderTiles(providers$Stamen.TonerLite) %>%
addCircleMarkers(radius = 7, color = ~palcontinua(size), label = ~size) %>%
addLegend(title = "Leyenda escala continua", pal = palcontinua, values = df$size,
position = "bottomright")
en el argumento palette poemos especificar el nombre de alguna de las paletas predefinidas del paquete RColorBrewer
, para verlas:
# see http://colorbrewer2.org/ for interactive examples
library(RColorBrewer)
display.brewer.all()
leaflet.extras
leaflet.extras es una librería complementaria a Leaflet que aporta algunos widget (artilugios) interesantes como funciones de busqueda en el mapa.
library(leaflet.extras)
library(htmltools) #a para htmlescape que limpia los datos de html
#Lee la capa restaurantes
restaurantes<-st_read("../../static/capas/Restaurantes.kml")
## Reading layer `Restaurantes' from data source `C:\R\publicaciones\enrdados\static\capas\Restaurantes.kml' using driver `KML'
## Simple feature collection with 35 features and 2 fields
## geometry type: POINT
## dimension: XYZ
## bbox: xmin: -3.689788 ymin: 37.56571 xmax: 2.176187 ymax: 41.3794
## epsg (SRID): 4326
## proj4string: +proj=longlat +datum=WGS84 +no_defs
# pinta el mapa con opcion de busqueda
leaflet(restaurantes) %>%
addProviderTiles(providers$Esri.WorldImagery) %>%
addCircleMarkers(radius = 7, group="comida",
color ="blue", label = ~htmlEscape(Name)) %>%
setView(lng = -1.128575,
lat = 37.984047,
zoom = 14)%>%
addSearchFeatures(targetGroups = "comida")
Podemos decirle que busque en un grupo determinado con el parametro tagetGruop y además indicar opciones como el zoom