Principales librerías usadas (y como referencia, otras que son importantes para el análisis geoespacial y la confección de mapas):
## Análisis, proceso y visualización de datos
library(tidyverse) # ggplot, dplyr...
library(magrittr) # %>%
library(summarytools) # provides functions to summarize numerical and categorical data.
## Librerías específicas
library(sf) # Simple Features
library(sp) # Classes and Methods for Spatial Data
library(leaflet) # LeafletJS interface for R
library(leaflet.providers) # Third-party tile providers
library(leaflet.extras) # LeafletJS plugins
library(rgdal) # R Geospatial' Data Abstraction Library
library(rnaturalearth) # Interface with Natural Earth map data
library(rnaturalearthdata) # World Vector Map Data from Natural Earth Used in 'rnaturalearth'
library(htmltools) # HTML generation and output
library(htmlwidgets) # Framework for creating HTML widgets
library(jsonlite) # A fast JSON parser and generator optimized for statistical data and the web
## Otras librerías importantes en análisis geoespacial
# library(raster) # Geographic Data Analysis and Modeling
# library(tmap) # Thematic maps
# library(tmaptools) # Tools for reading and processing spatial data
# library(mapview) # for interactive maps
# library(shiny) # for web applications
Mapas básicos inspirados en los ejemplos de la propia documentación de Leaflet para R
. Es recomendable tener también a mano la documentación de la librería original (JavaScript
).
# Uso básico
mdef <- leaflet() %>%
addTiles() %>% # Add default OpenStreetMap map tiles
setView(lng = -4.13244, lat = 40.5853, zoom = 12)
mdef
msta <- leaflet() %>%
setView(lng = -4.13244, lat = 40.5853, zoom = 12) %>%
addProviderTiles(providers$Stamen.Toner)
msta
mcdb <- leaflet() %>%
setView(lng = -4.13244, lat = 40.5853, zoom = 12) %>%
addProviderTiles(providers$CartoDB.Positron)
mcdb
mmix <- leaflet() %>%
setView(lng = -4.13244, lat = 40.5853, zoom = 12) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addProviderTiles(providers$Stamen.Toner,
options = providerTileOptions(opacity = 0.75))
mmix
Ejemplo (casi) tal cual aparece en la documentación, que utiliza una base de datos de localizaciones de terremotos en Fiyi - una de las muchas colecciones incluidas de serie en Base R datasets, y por tanto accesible a cualquier usuario de R
.
No | Variable | Estadísticas / Valores | Frec. (% sobre válidos) | Gráfico | Válido |
---|---|---|---|---|---|
1 | lat [numeric] |
Media (d-s) : -20.6 (5) min < mediana < max: -38.6 < -20.3 < -10.7 RI (CV) : 5.8 (-0.2) |
721 valores distintos | : . : : : : : . . : : : : . . . : : : : : : : |
1000 (100%) |
2 | long [numeric] |
Media (d-s) : 179.5 (6.1) min < mediana < max: 165.7 < 181.4 < 188.1 RI (CV) : 3.6 (0) |
605 valores distintos | . : : : : : . : : : : : : . . : : : : |
1000 (100%) |
3 | depth [integer] |
Media (d-s) : 311.4 (215.5) min < mediana < max: 40 < 247 < 680 RI (CV) : 444 (0.7) |
422 valores distintos | : : : : : : : : : : : : : . : . : : : |
1000 (100%) |
4 | mag [numeric] |
Media (d-s) : 4.6 (0.4) min < mediana < max: 4 < 4.6 < 6.4 RI (CV) : 0.6 (0.1) |
22 valores distintos | : : : : : : : : : : : : : : : . . |
1000 (100%) |
5 | stations [integer] |
Media (d-s) : 33.4 (21.9) min < mediana < max: 10 < 27 < 132 RI (CV) : 24 (0.7) |
102 valores distintos | : : : : : : . : : : : . . . |
1000 (100%) |
zona <- quakes[chull(quakes$long, quakes$lat),]
map <- leaflet(quakes) %>%
# Base groups
addTiles(group = "OSM (default)") %>%
addProviderTiles(providers$Stamen.Toner, group = "Toner") %>%
addProviderTiles(providers$Stamen.TonerLite, group = "Toner Lite") %>%
# Overlay groups
addCircles(~long, ~lat, ~10^mag/5, stroke = F, group = "Magnitud") %>%
addCircles(~long, ~lat, ~30*depth, stroke = F, color = "#0fd63d", group = "Profundidad") %>%
addPolygons(data = zona, lng = ~long, lat = ~lat,
fill = F, weight = 2, color = "#d914d5", group = "Zona") %>%
# Layers control
addLayersControl(
baseGroups = c("OSM (default)", "Toner", "Toner Lite"),
overlayGroups = c("Magnitud", "Profundidad", "Zona"),
options = layersControlOptions(collapsed = FALSE)
)
map
A continuación un plano parecido pero centrado en España, utilizando los datos del catálogo de terremotos del IGN a partir de 1800
, filtrado para terremotos con \(magnitud > 3\).
terr <- read.csv("data/terr18r.csv") # Bruto, terremotos desde 1800
terr <- terr[terr$mag > 3,] %>% na.omit(terr) # Filtros
zona <- terr[chull(terr$long, terr$lat),] # Zona sísmica
dfSummary(terr)
No | Variable | Estadísticas / Valores | Frec. (% sobre válidos) | Gráfico | Válido |
---|---|---|---|---|---|
1 | lat [numeric] |
Media (d-s) : 36.5 (3.7) min < mediana < max: 26 < 36.6 < 45 RI (CV) : 2.1 (0.1) |
6732 valores distintos | : : : . : : . : : : . : . |
8754 (100%) |
2 | long [numeric] |
Media (d-s) : -5.5 (6.1) min < mediana < max: -19.9 < -4.5 < 6 RI (CV) : 7.9 (-1.1) |
7546 valores distintos | : . : : : : . . : : : : : : . : . : : : : : : : : |
8754 (100%) |
3 | depth [integer] |
Media (d-s) : 21.3 (25.3) min < mediana < max: 1 < 14 < 663 RI (CV) : 24 (1.2) |
131 valores distintos | : : : : : |
8754 (100%) |
4 | mag [numeric] |
Media (d-s) : 3.6 (0.5) min < mediana < max: 3.1 < 3.5 < 7.8 RI (CV) : 0.7 (0.1) |
35 valores distintos | : : : . : : : : : . |
8754 (100%) |
## Lectura de GeoJSON
gaso <- readOGR("data/natural_gas_pipelines_j96.json", verbose = FALSE)
coms <- readOGR("https://raw.githubusercontent.com/telegeography/www.submarinecablemap.com/master/public/api/v2/cable/cable-geo.json", verbose = FALSE)
mapt <- leaflet(terr) %>%
setView(lng = 5.126953, lat = 42.57465, zoom = 4) %>%
setMaxBounds(lng1 = -19.482422, lat1 = 25.191689, lng2 = 20.156250, lat2 = 45.480676) %>%
# Base groups
addTiles(group = "OpenStreetMap") %>%
addProviderTiles(providers$Stamen.Toner, group = "Toner") %>%
addProviderTiles(providers$Stamen.TonerLite, group = "Toner Lite") %>%
# Overlay groups
addPolygons(data = zona, lng = ~long, lat = ~lat, fill = F, weight = 4.5, color = "#eb8509", group = "Zona sísmica") %>%
addCircles(~long, ~lat, ~10^mag/500, stroke = F, color = "#6b0b21", group = "Magnitud", fillOpacity = 0.6) %>%
addCircles(~long, ~lat, ~30*depth, stroke = F, color = "#0fd63d", group = "Profundidad") %>%
addPolylines(data = gaso, fillOpacity = 0.75, weight = 1.2, color = "red", group = "Gasoductos") %>%
addPolylines(data = coms, fillOpacity = 0.9, weight = 1.5, group = "Cables submarinos") %>%
# Layers control
addLayersControl(
baseGroups = c("OpenStreetMap", "Toner", "Toner Lite"),
overlayGroups = c("Zona sísmica", "Magnitud", "Profundidad", "Gasoductos", "Cables submarinos"),
options = layersControlOptions(collapsed = FALSE)
)
mapt
Para probar el uso del formato GeoJSON se han incluido también los principales gasoductos de Europa a este lado del Atlántico (fuente), y los principales cables submarinos de comunicaciones (fuente).
JavaScript
Es posible utilizar código JavaScript
a través de htmlwidgets::onRender
. En este ejemplo se utiliza para sincronizar el mapa guía con la selección activa como mapa base.
l <- leaflet() %>%
setView(lng = 5.126953, lat = 42.57465, zoom = 4)
esri <- grep("^Esri", providers, value = TRUE)
for (provider in esri) {
l <- l %>% addProviderTiles(provider, group = provider)
}
l %>%
addLayersControl(baseGroups = names(esri),
options = layersControlOptions(collapsed = FALSE)) %>%
addMiniMap(tiles = esri[[1]], toggleDisplay = TRUE,
position = "bottomleft") %>%
htmlwidgets::onRender("function(el, x) {var myMap = this;
myMap.on('baselayerchange',
function (e) {
myMap.minimap.changeLayer(L.tileLayer.provider(e.name));
})
}"
)
LeafletJS
Leaflet para R
incorpora las principales funcionalidades de LeafletJS
y unos pocos de sus plugins
, que no cubren ni de lejos el ecosistema de plugins de LeafletJS. La librería leaflet.extras
integra otro puñado de ellos.
Veamos un ejemplo (fuente) que usa uno de los ficheros incluidos en el propio paquete, un listado de más de 5000 aeropuertos medianos y grandes en todo el planeta (gist), y añadamos algunas funcionalidades extra.
lf <- leaflet() %>%
setView(0,0,2) %>%
addProviderTiles(providers$CartoDB.DarkMatterNoLabels)
airpDF <- read.csv('https://gist.githubusercontent.com/Eclectikus/22037610eda7ae617544e51d0d73fcfe/raw/937cf3e398a314929821db7fcc06eaab524c4263/airports.csv')
# Aeropuertos medios y grandes
## 100% seguro de que hay mejor forma de hacer esto,
## aquí se han creado localmente (y en el repositorio)
## dos ficheros correspondientes a aeropuertos grandes:
airpL <- airpDF[which(airpDF$type == "large_airport"),]
# write.csv(airpL, file = "data/airportsL.csv")
airpL <- readr::read_file('data/airportsL.csv')
## ... y medianos:
airpM <- airpDF[which(airpDF$type == "medium_airport"),]
# write.csv(airpM, file = "data/airportsM.csv")
airpM <- readr::read_file('data/airportsM.csv')
lf %>%
addLayersControl( # capas
overlayGroups = c("Aeropuertos grandes", "Aeropuertos medianos"),
options = layersControlOptions(collapsed = FALSE),
position = "bottomleft"
) %>%
## Plugin: leaflet-omnivore
addCSV( # aeropuertos grandes
airpL,
csvParserOptions('latitude_deg','longitude_deg'),
markerType = 'circleMarker',
stroke = FALSE, fillColor = 'hotpink', fillOpacity = 0.9,
markerOptions = leaflet::markerOptions(radius = 1.5),
group = "Aeropuertos grandes") %>%
addCSV( # aeropuertos medianos
airpM,
csvParserOptions('latitude_deg','longitude_deg'),
markerType = 'circleMarker',
stroke = FALSE, fillColor = 'blue', fillOpacity = 0.75,
markerOptions = leaflet::markerOptions(radius = 0.6),
group = "Aeropuertos medianos") %>%
## Plugin: Leaflet.EasyButton
addEasyButton(easyButton(
icon = "fa-map", title = "Mapa inicial",
onClick = JS("function(btn, map){ map.setZoom(2); }"))
) %>%
addEasyButton(easyButton(
icon = "fa-map-pin", title = "Localízame",
onClick = JS("function(btn, map){ map.locate({setView: true}); }"))
) %>%
## Plugin: Leaflet.fullscreen
addFullscreenControl(position = "topright", pseudoFullscreen = TRUE)
Mi impresión es que si realmente se necesita la funcionalidad completa de leafletJS
es mejor utilizar nativamente la librería original en el desarrollo. Si eventualmente se depende de knitr
y de leaflet para R
, y se necesita utilizar un plugin no incluido en leaflet.extras
, en principio es posible implementar la funcionalidad manualmente (ver este hilo). Pero no hoy.