Topic: Creating interactive maps with the
leaflet package in R.
A map is a visual representation of geographical information. Maps help us understand where objects are located and how locations relate to one another.
Examples include:
A static map is a fixed image. Users generally cannot zoom, pan, or click individual locations to reveal more information.
An interactive map allows users to zoom, pan, select markers, explore information, and switch between layers.
Leaflet is an open-source JavaScript library for interactive maps.
The R package leaflet provides an interface for building
these maps directly from R.
Its main capabilities include:
Leaflet is useful when we want to combine statistical analysis in R with geographical visualization in a browser.
Consider a public health department studying healthcare accessibility. A Leaflet map could show hospitals, primary health centres, and geographical health indicators. A user could click a facility to view its name and services.
Other applications include crime analysis, environmental monitoring, delivery planning, agriculture, disaster management, and tourism.
install.packages("leaflet")
install.packages("dplyr")
install.packages("htmlwidgets")
Load packages in each new R session:
library(leaflet)
library(dplyr)
library(htmlwidgets)
install.packages() downloads and installs a
package.library() loads an installed package into the current R
session.A geographical location is often represented using two coordinates:
Kochi is approximately at latitude 9.93° N and longitude 76.27° E.
A common source of errors is exchanging these coordinates. In
Leaflet, arguments are typically provided as longitude
(lng) followed by latitude (lat).
leaflet() %>%
addTiles() %>%
setView(
lng = 76.2673,
lat = 9.9312,
zoom = 11
)
| Function | Purpose |
|---|---|
leaflet() |
Creates a map widget. |
%>% |
Passes the previous result into the next operation. |
addTiles() |
Adds a background tile layer. |
setView() |
Sets the initial centre and zoom level. |
A smaller zoom value shows a wider region; a larger value shows more local detail.
leaflet() %>%
addTiles() %>%
setView(lng = 76.2673, lat = 9.9312, zoom = 5)
leaflet() %>%
addTiles() %>%
setView(lng = 76.2673, lat = 9.9312, zoom = 12)
A data frame can store the locations and descriptions we want to visualize.
places <- data.frame(
name = c(
"Fort Kochi", "Marine Drive",
"Lulu Mall", "Kochi Metro"
),
lat = c(9.9658, 9.9816, 10.0261, 9.9726),
lng = c(76.2423, 76.2760, 76.3084, 76.2999),
category = c(
"Heritage", "Boardwalk",
"Shopping", "Transport"
)
)
knitr::kable(places, caption = "Illustrative locations for the Leaflet demonstration")
| name | lat | lng | category |
|---|---|---|---|
| Fort Kochi | 9.9658 | 76.2423 | Heritage |
| Marine Drive | 9.9816 | 76.2760 | Boardwalk |
| Lulu Mall | 10.0261 | 76.3084 | Shopping |
| Kochi Metro | 9.9726 | 76.2999 | Transport |
Each row represents one location:
name: location name.lat: latitude.lng: longitude.category: descriptive category.leaflet(data = places) %>%
addTiles() %>%
addMarkers(
lng = ~lng,
lat = ~lat
)
The tilde (~) tells Leaflet to use a variable from the
supplied data frame. Leaflet plots one marker per row.
leaflet(data = places) %>%
addTiles() %>%
addMarkers(
lng = ~lng,
lat = ~lat,
popup = ~paste0(
"<b>", name, "</b><br>",
"Category: ", category
),
label = ~name
)
popup: information displayed when a marker is
clicked.label: a short label displayed when the pointer hovers
over a marker.paste0(): combines strings without automatically
inserting spaces.<b> and <br>: HTML tags for
bold text and line breaks inside the pop-up.Circle markers are useful when displaying many locations or numerical measurements.
leaflet(data = places) %>%
addTiles() %>%
addCircleMarkers(
lng = ~lng,
lat = ~lat,
radius = 7,
color = "blue",
fillColor = "lightblue",
fillOpacity = 0.8,
popup = ~name
)
| Parameter | Meaning |
|---|---|
radius |
Circle size in pixels. |
color |
Border color. |
fillColor |
Interior color. |
fillOpacity |
Transparency of the fill, from 0 to 1. |
popup |
Information displayed on clicking. |
The purpose of data visualization is not merely to plot locations. It is to communicate meaningful patterns in the data.
health_centres <- data.frame(
name = c(
"Centre A", "Centre B", "Centre C",
"Centre D", "Centre E"
),
lat = c(9.93, 9.95, 9.97, 10.00, 10.02),
lng = c(76.27, 76.29, 76.25, 76.31, 76.28),
patients = c(120, 250, 80, 310, 180)
)
knitr::kable(
health_centres,
caption = "Synthetic healthcare data"
)
| name | lat | lng | patients |
|---|---|---|---|
| Centre A | 9.93 | 76.27 | 120 |
| Centre B | 9.95 | 76.29 | 250 |
| Centre C | 9.97 | 76.25 | 80 |
| Centre D | 10.00 | 76.31 | 310 |
| Centre E | 10.02 | 76.28 | 180 |
leaflet(health_centres) %>%
addTiles() %>%
addCircleMarkers(
lng = ~lng,
lat = ~lat,
radius = ~sqrt(patients) / 2,
color = "darkred",
fillColor = "tomato",
fillOpacity = 0.7,
popup = ~paste(
name,
"Patients:", patients,
sep = "<br>"
)
)
The radius increases with the square root of the patient count. This produces a more moderate change in circle size than mapping raw counts directly to radius.
places$color <- ifelse(
places$category == "Heritage", "red",
ifelse(
places$category == "Shopping", "blue",
ifelse(
places$category == "Transport", "green",
"orange"
)
)
)
leaflet(places) %>%
addTiles() %>%
addCircleMarkers(
lng = ~lng,
lat = ~lat,
radius = 8,
color = ~color,
fillOpacity = 0.8,
popup = ~paste(name, category, sep = "<br>")
)
Assigning colors by category helps users recognize different types of
locations. For larger projects, named color palettes are usually easier
to maintain than nested ifelse() statements.
leaflet(places) %>%
addTiles() %>%
addCircleMarkers(
lng = ~lng,
lat = ~lat,
radius = 8,
color = ~color,
fillOpacity = 0.8,
popup = ~name
) %>%
addLegend(
position = "bottomright",
colors = c("red", "orange", "blue", "green"),
labels = c(
"Heritage", "Promenade",
"Shopping", "Transport"
),
title = "Place Category"
)
A legend explains the visual encoding. Without one, users may see different colors but not know what they represent.
Different tile providers give maps different visual appearances. Provider availability and usage terms should be checked before using them in a public application.
leaflet() %>%
addTiles(group = "Standard Map") %>%
addProviderTiles(
providers$CartoDB.Positron,
group = "Light Map"
) %>%
addLayersControl(
baseGroups = c("Standard Map", "Light Map"),
options = layersControlOptions(
collapsed = FALSE
)
)
addProviderTiles() adds a map style from a supported
tile provider.group assigns a name to a layer.addLayersControl() creates a control for switching
between base layers.When hundreds of points appear close together, markers may overlap. Clustering groups nearby markers at lower zoom levels.
leaflet(places) %>%
addTiles() %>%
addMarkers(
lng = ~lng,
lat = ~lat,
popup = ~name,
clusterOptions = markerClusterOptions()
)
This small dataset has only a few locations, so clustering may not be visually obvious. Clustering is more useful when a map contains many points.
The map can be displayed in RStudio’s Viewer pane. For sharing, save a Leaflet widget as a standalone HTML file.
library(leaflet)
library(htmlwidgets)
map <- leaflet(places) %>%
addTiles() %>%
addMarkers(
lng = ~lng,
lat = ~lat,
popup = ~name
)
saveWidget(
map,
file = "interactive_map.html",
selfcontained = TRUE
)
The HTML file can be opened in a browser. Depending on the environment and installed dependencies, self-contained export may require additional software such as Pandoc. Background tiles generally still require internet access.
A district health department wants to visualize five healthcare facilities and the number of patients visiting each facility.
Students will create a map, display facilities, represent patient counts using circle sizes, and add pop-ups.
facilities <- data.frame(
facility = c(
"PHC A", "PHC B", "PHC C",
"Community Health Centre", "District Hospital"
),
latitude = c(9.93, 9.95, 9.97, 10.00, 10.02),
longitude = c(76.27, 76.29, 76.25, 76.31, 76.28),
patients = c(120, 250, 80, 310, 180)
)
knitr::kable(
facilities,
caption = "Synthetic facility data"
)
| facility | latitude | longitude | patients |
|---|---|---|---|
| PHC A | 9.93 | 76.27 | 120 |
| PHC B | 9.95 | 76.29 | 250 |
| PHC C | 9.97 | 76.25 | 80 |
| Community Health Centre | 10.00 | 76.31 | 310 |
| District Hospital | 10.02 | 76.28 | 180 |
These facility names, coordinates, and patient counts are synthetic teaching data.
leaflet(facilities) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitude,
lat = ~latitude,
radius = ~sqrt(patients) / 2,
color = "darkblue",
fillColor = "skyblue",
fillOpacity = 0.8,
popup = ~paste0(
"<b>", facility, "</b><br>",
"Patients: ", patients
),
label = ~facility
) %>%
addLegend(
position = "bottomright",
colors = "skyblue",
labels = "Healthcare facility",
title = "Legend"
)
sqrt(patients) / 2 and
sqrt(patients) / 3.setView() and
different zoom levels.saveWidget().