I’ve been asked a few times about making interactive maps in R. I thought I’d follow up on an example I wrote up for static bathymetric maps with a (very) quick example of interactive mapping with leaflet in R. More details are available at the awesome R leaflet website.
Step 1: make some data
Here’s some test data to plot. These structures could, of course, be replaced by real data.
# make a simple track line
lin = data.frame(lon = c(-65.17536, -65.37423, -65.64541, -66.06122, -66.15161),
lat = c(43.30837, 42.94679, 42.87448, 42.92871, 42.72985))
# make a few points
pts = data.frame(lon = c(-65.3, -65.7, -64.1),
lat = c(43.4, 43, 42.9))
# build a polygon (in this case the 'Roseway Basin Area To Be Avoided')
ply = data.frame(lon = c(-64.916667, -64.983333, -65.516667, -66.083333),
lat = c(43.266667, 42.783333, 42.65, 42.866667))
Step 2: build a basemap
Now let’s build a basemap. This is achieved with the leaflet()
function, and a series of pipes (%>%
) to sequentially add more features. Hopefully the comments adequately describe each feature. The whole thing is stored as the variable map
.
# required libraries
library(leaflet, quietly = T, warn.conflicts = F)
# start basemap
map <- leaflet() %>%
# add ocean basemap
addProviderTiles(providers$Esri.OceanBasemap) %>%
# add another layer with place names
addProviderTiles(providers$Hydda.RoadsAndLabels, group = 'Place names') %>%
# add graticules from a NOAA webserver
addWMSTiles(
"https://gis.ngdc.noaa.gov/arcgis/services/graticule/MapServer/WMSServer/",
layers = c("1-degree grid", "5-degree grid"),
options = WMSTileOptions(format = "image/png8", transparent = TRUE),
attribution = NULL,group = 'Graticules') %>%
# focus map in a certain area / zoom level
setView(lng = -65, lat = 43, zoom = 7) %>%
# add layers control
addLayersControl(overlayGroups = c('Place names',
'Graticules',
'Points',
'Lines',
'Polygons'),
options = layersControlOptions(collapsed = FALSE),
position = 'topright') %>%
# list groups to hide on startup
hideGroup(c('Place names'))
# show map
map
Step 3: add data to basemap
Adding data to the map is relatively straightforward. It’s nearly the same as adding features to the basemap. The main difference is that you need to actually overwrite and update the variable map
each time you add something new, hence the map <- map %>%
with each data type.
# add points
map <- map %>%
addCircleMarkers(data = pts, ~lon, ~lat,
weight = 0.5,
col = 'black',
fillColor = 'darkslategrey',
radius = 4,
fillOpacity = 0.9,
stroke = T,
label = ~paste0('Point at: ',
as.character(round(lat,3)), ', ',
as.character(round(lon,3))),
group = 'Points')
# add lines
map <- map %>%
addPolylines(data = lin, ~lon, ~lat,
weight = 3,
color = 'red',
popup = 'This is a line!',
smoothFactor = 3,
group = 'Lines')
# add polygons
map <- map %>%
addPolygons(data=ply, lng=~lon, lat=~lat,
weight = 1,
color = 'grey',
fillColor = 'grey',
fill = T,
fillOpacity = 0.25,
stroke = T,
dashArray = c(5,5),
smoothFactor = 3,
options = pathOptions(clickable = F),
group = 'Polygons')
# show map
map
Step 4: add extra features
There are a ton of cool features that can be added. Check out packages like leaflet_extras
for more details. These are a few that I find very useful.
# add more features
map <- map %>%
# add a map scalebar
addScaleBar(position = 'topright') %>%
# add measurement tool
addMeasure(
primaryLengthUnit = "kilometers",
secondaryLengthUnit = 'miles',
primaryAreaUnit = "hectares",
secondaryAreaUnit="acres",
position = 'topleft')
# show map
map
Step 5: saving the map
The fact that these maps can be saved as stand-alone html widgets is pretty incredible. This means they can be treated effectively as a normal image file, but retain their interactivity. It also makes them very, very easy to embed in a web page.
I recently also discovered the mapshot()
function that saves a simple static screenshot, which can be very nice for writing in formats that don’t support interactive graphics. Directly saving the map made here is a little ugly because of all the buttons and such, but just about all of those things can be easily removed if so desired.
Uncomment either chunk depending on your desired output format.
# # # save a stand-alone, interactive map as an html file
# library(htmlwidgets)
# saveWidget(widget = map, file = 'map.html', selfcontained = T)
# # # save a snapshot as a png file
# library(mapview)
# mapshot(map, file = 'map.png')