Glenn Moncrieff
Fynbos Node, South African Environmental Observation Network (SAEON)
Department of Botany and Zoology, Stellenbosch University
18 August 2016
Disclaimer: Much of this material is adapted/borrowed/stolen from this tutorial on leaflet for R
Leaflet is a popular JavaScript library for interactive maps. Many of the interactive maps you will encounter online will be built on leaflet libraries. Here is an example from the Guardian
With leaflet we can create interactive maps in Rmarkdown htmls, Shiny apps, or interact directly with maps in the Rstudio console.
Leaflet can render and allow interaction with objects such as lines, polygons, popups, markers, GeoJSON, and most of the R spatial data types we are used to such as SpatialPolygons, SpatialPoints, Rasters etc.
We can use interaction with leaflet maps such as zooming, panning, changing maps bound or mouse clicks to drive Shiny logic
library(leaflet)
mymap1 <- leaflet() %>%
setView(lat = -33.9249, lng= 18.4241, zoom = 8) %>% # set map view
addTiles() %>% # Add default OpenStreetMap map background tiles
addMarkers(lat = -34.1292, lng = 18.4462, popup="Home")
mymap1 # Print the mapThe basic usage of leaflet is:
leaflet().addTiles, addMarkers, addPolygons) to modify the map widget.mymap2 <- leaflet() %>%
setView(lat = -33.9249, lng= 18.4241, zoom = 8) %>% # set map view
addProviderTiles("OpenTopoMap") %>% # Add topographic map tiles
addMarkers(lat = -34.1292, lng = 18.4462, popup="Home")
mymap2 # Print the mapor
mymap3 <- leaflet() %>%
setView(lat = -33.9249, lng= 18.4241, zoom = 8) %>% # set map view
addProviderTiles("NASAGIBS.ViirsEarthAtNight2012") %>% # Add NASA nightview map tiles
addMarkers(lat = -34.1292, lng = 18.4462, popup="Home")
mymap3 # Print the mapHere you will find a viewer for all available base maps. All you have to do is change the argument in addProviderTiles
mymap is a leaflet widget - essentially a list of object that make up the map
class(mymap1)## [1] "leaflet" "htmlwidget"
str(mymap1) # Print widget## List of 8
## $ x :List of 3
## ..$ setView:List of 3
## .. ..$ : num [1:2] -33.9 18.4
## .. ..$ : num 8
## .. ..$ : list()
## .. .. ..- attr(*, "class")= chr "list"
## .. ..- attr(*, "class")= chr "list"
## ..$ calls :List of 2
## .. ..$ :List of 2
## .. .. ..$ method: chr "addTiles"
## .. .. ..$ args :List of 4
## .. .. .. ..$ : chr "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
## .. .. .. ..$ : NULL
## .. .. .. ..$ : NULL
## .. .. .. ..$ :List of 18
## .. .. .. .. ..$ minZoom : num 0
## .. .. .. .. ..$ maxZoom : num 18
## .. .. .. .. ..$ maxNativeZoom : NULL
## .. .. .. .. ..$ tileSize : num 256
## .. .. .. .. ..$ subdomains : chr "abc"
## .. .. .. .. ..$ errorTileUrl : chr ""
## .. .. .. .. ..$ tms : logi FALSE
## .. .. .. .. ..$ continuousWorld : logi FALSE
## .. .. .. .. ..$ noWrap : logi FALSE
## .. .. .. .. ..$ zoomOffset : num 0
## .. .. .. .. ..$ zoomReverse : logi FALSE
## .. .. .. .. ..$ opacity : num 1
## .. .. .. .. ..$ zIndex : NULL
## .. .. .. .. ..$ unloadInvisibleTiles: NULL
## .. .. .. .. ..$ updateWhenIdle : NULL
## .. .. .. .. ..$ detectRetina : logi FALSE
## .. .. .. .. ..$ reuseTiles : logi FALSE
## .. .. .. .. ..$ attribution : chr "© <a href=\"http://openstreetmap.org\">OpenStreetMap</a> contributors, <a href=\"http://creativecommons.org/licenses/by-sa"| __truncated__
## .. .. .. .. ..- attr(*, "class")= chr "list"
## .. .. .. ..- attr(*, "class")= chr "list"
## .. ..$ :List of 2
## .. .. ..$ method: chr "addMarkers"
## .. .. ..$ args :List of 9
## .. .. .. ..$ : num -34.1
## .. .. .. ..$ : num 18.4
## .. .. .. ..$ : NULL
## .. .. .. ..$ : NULL
## .. .. .. ..$ : NULL
## .. .. .. ..$ :List of 9
## .. .. .. .. ..$ clickable : logi TRUE
## .. .. .. .. ..$ draggable : logi FALSE
## .. .. .. .. ..$ keyboard : logi TRUE
## .. .. .. .. ..$ title : chr ""
## .. .. .. .. ..$ alt : chr ""
## .. .. .. .. ..$ zIndexOffset: num 0
## .. .. .. .. ..$ opacity : num 1
## .. .. .. .. ..$ riseOnHover : logi FALSE
## .. .. .. .. ..$ riseOffset : num 250
## .. .. .. .. ..- attr(*, "class")= chr "list"
## .. .. .. ..$ : chr "Home"
## .. .. .. ..$ : NULL
## .. .. .. ..$ : NULL
## .. .. .. ..- attr(*, "class")= chr "list"
## ..$ limits :List of 2
## .. ..$ lat: num [1:2] -34.1 -34.1
## .. ..$ lng: num [1:2] 18.4 18.4
## $ width : NULL
## $ height : NULL
## $ sizingPolicy :List of 6
## ..$ defaultWidth : chr "100%"
## ..$ defaultHeight: num 400
## ..$ padding : num 0
## ..$ viewer :List of 6
## .. ..$ defaultWidth : NULL
## .. ..$ defaultHeight: NULL
## .. ..$ padding : NULL
## .. ..$ fill : logi TRUE
## .. ..$ suppress : logi FALSE
## .. ..$ paneHeight : NULL
## ..$ browser :List of 4
## .. ..$ defaultWidth : NULL
## .. ..$ defaultHeight: NULL
## .. ..$ padding : NULL
## .. ..$ fill : logi TRUE
## ..$ knitr :List of 3
## .. ..$ defaultWidth : NULL
## .. ..$ defaultHeight: NULL
## .. ..$ figure : logi TRUE
## $ dependencies : NULL
## $ elementId : NULL
## $ preRenderHook: NULL
## $ jsHooks : list()
## - attr(*, "class")= chr [1:2] "leaflet" "htmlwidget"
## - attr(*, "package")= chr "leaflet"
leaflet functions to add map layers accept a data argument
library(rgdal) # for handling spatial objects
library(raster)# for rasters
library(knitr) # for tidy table
#load some spatial data
vegclear <- readOGR(dsn="/Users/glennmoncrieff/Documents/presentations/leaflet_CRUG",layer="vegclear_small")## OGR data source with driver: ESRI Shapefile
## Source: "/Users/glennmoncrieff/Documents/presentations/leaflet_CRUG", layer: "vegclear_small"
## with 948 features
## It has 123 fields
#lets work with he polygons first
class(vegclear)## [1] "SpatialPolygonsDataFrame"
## attr(,"package")
## [1] "sp"
crs(vegclear)## CRS arguments: +proj=longlat +ellps=WGS84 +no_defs
plot(vegclear,col=1:20,border=NA)vegdata <- vegclear@data
head(vegdata) %>% kable # print a tidy table| Prjct_N | NbalId | Area | Totl_Md | Ttl_Cst | Nmbr_f_ | Clrng_R | Frst_Dt | Lst_Dt_ | Initl_D | Initl_T | Initl_M | Initl_C | Intl_En | Int_C_1 | Frst_Dm | Frst_Tt | Frst_Md | Frst_Cs | Frst_E_ | Frst_Cn | Scnd_Dm | Scnd_Tt | Scnd_Md | Scnd_Cs | Scnd_En | Scnd_Cn | Thrd_Dm | Thrd_Tt | Thrd_Md | Thrd_Cs | Thrd_E_ | Thrd_Cn | Frth_Dm | Frth_Tt | Frth_Md | Frth_Cs | Frth_En | Frth_Cn | Ffth_Dm | Ffth_Tt | Ffth_Md | Ffth_Cs | Ffth_E_ | Ffth_Cn | Sxth_Dm | Sxth_Tt | Sxth_Md | Sxth_Cs | Sxth_E_ | Sxth_Cn | Svnth_D | Svnth_T | Svnth_M | Svnth_C | Svnth_E | Svn_C_1 | Egth_Dm | Egth_Tt | Egth_Md | Egth_Cs | Egth_E_ | Egth_Cn | Nnth_Dm | Nnth_Tt | Nnth_Md | Nnth_Cs | Nnth_E_ | Nnth_Cn | Tnth_Dm | Tnth_Tt | Tnth_Md | Tnth_Cs | Tnth_E_ | Tnth_Cn | Elvnt_D | Elvnt_T | Elvnt_M | Elvnt_C | Elvnt_E | Elv_C_1 | Twlft_D | Twlft_T | Twlft_M | Twlft_C | Twlft_E | Twl_C_1 | Thrtnth | Thrtn_1 | Thrtn_2 | Thrtn_3 | Thrtn_4 | Thrtn_5 | Fortnth | Frtnt_1 | Frtnt_2 | Frtnt_3 | Frtnt_4 | Frtnt_5 | IntlS_D | IntlS_T | IntlS_M | IntlS_C | IntlS_E | InS_C_1 | Nth_Dmn | Nth_Tt_ | Nth_Mdl | Nth_Cst | Nth_E_D | Nth_Cnt | Mantnnc | Mntnn_1 | Mntnn_2 | Mntnn_3 | Mntnn_4 | Mntnn_5 | Rehablt | Rhblt_1 | Rhblt_2 | Rhblt_3 | Rhblt_4 | Rhblt_5 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | SANP WfW TMNP N | G22B400002 | 14.84 | 90.96 | 18429.51 | 1 | No Restriction | 20130212 | 20130225 | Acacia podalyriifolia | 70 | 90.96 | 18429.51 | 20130225 | G22B400100304 | NA | 0.0 | 0.00 | 0.00 | NA | NA | NA | 0.0 | 0.00 | 0.00 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA |
| 1 | SANP WfW TMNP N | G22B400003 | 2.52 | 0.00 | 0.00 | 0 | No Restriction | NA | NA | NA | 0 | 0.00 | 0.00 | NA | NA | NA | 0.0 | 0.00 | 0.00 | NA | NA | NA | 0.0 | 0.00 | 0.00 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA |
| 2 | SANP WfW TMNP N | G22B400004 | 3.08 | 0.00 | 0.00 | 0 | No Restriction | NA | NA | NA | 0 | 0.00 | 0.00 | NA | NA | NA | 0.0 | 0.00 | 0.00 | NA | NA | NA | 0.0 | 0.00 | 0.00 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA |
| 3 | SANP WfW TMNP N | G22B400005 | 13.78 | 150.52 | 26657.00 | 2 | No Restriction | 20130212 | 20140218 | NA | 0 | 0.00 | 0.00 | NA | NA | Pinus spp | 17.0 | 90.08 | 16232.18 | 20130222 | G22B400100290 | Acacia saligna | 39.5 | 60.44 | 10424.82 | 20140218 | G22B400100410 | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA |
| 4 | SANP WfW TMNP N | G22B400006 | 13.85 | 678.08 | 139025.68 | 2 | No Restriction | 20130131 | 20140313 | NA | 0 | 0.00 | 0.00 | NA | NA | Acacia longifolia | 96.6 | 391.80 | 78214.26 | 20130228 | G22B400100291 | Acacia saligna | 202.0 | 286.28 | 60811.42 | 20140313 | G22B400100416 | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA |
| 5 | SANP WfW TMNP N | G22B400007 | 12.71 | 300.40 | 70364.79 | 1 | No Restriction | 20130528 | 20130624 | Acacia saligna | 157 | 300.40 | 70364.79 | 20130624 | G22B400100324 | NA | 0.0 | 0.00 | 0.00 | NA | NA | NA | 0.0 | 0.00 | 0.00 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA | NA | 0 | 0 | 0 | NA | NA |
#create color pallete
pal <- colorNumeric(
palette = "Blues",
domain = vegclear$Ttl_Cst
)
#add polygons to leaflet map
mymap5 <- leaflet() %>%
setView(lat = -33.9249, lng= 18.4241, zoom = 10) %>% # set map view
addProviderTiles("Esri.WorldImagery") %>%
addPolygons(data=vegclear,
stroke = FALSE, fillOpacity = 0.8, smoothFactor = 0.5,
color = ~pal(Ttl_Cst),
popup = paste0("Cost of clearing: ", as.character(vegclear$Ttl_Cst))
) %>%
addLegend("bottomright", pal = pal, values = vegclear$Ttl_Cst,
title = "Total cost of alien clearing",
labFormat = labelFormat(prefix = "R"),
opacity = 1
)
mymap5Leaflet uses the web mercator projection, and any spatial objects displayed by the leaflet widget appear in this projection.
#load raster
temp <- raster("/Users/glennmoncrieff/Documents/presentations/leaflet_CRUG/Tmax_jan_mean")
crs(temp)## CRS arguments:
## +proj=utm +zone=34 +south +datum=WGS84 +units=m +no_defs
## +ellps=WGS84 +towgs84=0,0,0
#raster color palette
pal <- colorNumeric(
palette = rev(heat.colors(20)),
domain = values(temp),
na.color = "transparent"
)
mymap6 <- leaflet() %>%
setView(lat = -33.9249, lng= 18.4241, zoom = 10) %>% # set map view
addTiles() %>%
addRasterImage(temp, colors = pal, opacity = 0.8) %>%
addLegend("bottomright", pal = pal, values = values(temp),title = "Jan maximum temp")
mymap6So leaflet reprojects for us anyway. But this comes at a performance cost.
#reproject for leaflet
temp_leaf <- projectRasterForLeaflet(temp)
crs(temp_leaf)## CRS arguments:
## +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0
## +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs
#without projecting for leaflet:
system.time(
mymap6 <- leaflet() %>%
setView(lat = -33.9249, lng= 18.4241, zoom = 10) %>% # set map view
addTiles() %>%
addRasterImage(temp, colors = pal, opacity = 0.8) %>%
addLegend("bottomright", pal = pal, values = values(temp),title = "Jan maximum temp"))## user system elapsed
## 2.498 0.514 3.118
#after reprojection
system.time(
mymap7 <- leaflet() %>%
setView(lat = -33.9249, lng= 18.4241, zoom = 10) %>% # set map view
addTiles() %>%
addRasterImage(temp_leaf, colors = pal, opacity = 0.8, project = FALSE) %>%
addLegend("bottomright", pal = pal, values = values(temp_leaf),title = "Jan maximum temp"))## user system elapsed
## 0.557 0.043 0.601
library(shiny)
ui <- fluidPage(
leafletOutput("mymap")
)
server <- function(input, output, session) {
map <- leaflet() %>%
setView(lat = -33.9249, lng= 18.4241, zoom = 8) %>% # set map view
addTiles() %>% # Add default OpenStreetMap map background tiles
addMarkers(lat = -34.1292, lng = 18.4462, popup="Home")
output$mymap <- renderLeaflet(map)
}
shinyApp(ui=ui, server=server)library(shiny)
ui <- fluidPage(
leafletOutput("mymap")
)
server <- function(input, output, session) {
map <- leaflet() %>%
setView(lat = -33.9249, lng= 18.4241, zoom = 8) %>% # set map view
addTiles() %>% # Add default OpenStreetMap map background tiles
addMarkers(lat = -34.1292, lng = 18.4462)
output$mymap <- renderLeaflet(map)
observeEvent(input$mymap_marker_click, {
leafletProxy("mymap") %>% clearMarkers()
})
}
shinyApp(ui=ui, server=server)In this way that we can use map interaction to drive shiny logic
When you click on a map, marker or polygon etc that event creates/updates the values of a list
the list will be named input$MAPID_OBJCATEGORY_EVENTNAME and will contain lat lng and id info
Vegetation mapping application at https://github.com/GMoncrieff/ShinyVeg