Code
pacman::p_load(
leaflet
,gapminder
,echarts4r
,tidyverse
,ggiraph
,widgetframe
,ggthemes
,plotly
,viridis
,DT
)October 30, 2025
This document explores making interactive maps using R. There are a number of R packages that can be used to generate such visualizations. We consider just a small number (not exhaustively).
We use gapminder dataset from {gapminder} package. It is contains data from 187 countries covering the periods 1952 - 2007 with the following columns:
The second dataset is the meteorite landings from Tidytuesday project from the Meteoritical Society of NASA. It comes with the following variables:
First we organize the datasets:
Joining with `by = join_by(country)`
Warning in inner_join(., maps::iso3166 %>% select(a3, mapname), by = c(code = "a3")): Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 605 of `x` matches multiple rows in `y`.
ℹ Row 2 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
"many-to-many"` to silence this warning.
Gapminder dataset:
Rows: 45716 Columns: 10
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (5): name, name_type, class, fall, geolocation
dbl (5): id, mass, year, lat, long
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Meteorite dataset:
{ggiraph}To turn a static choropleth map by invoking a tooltip when we hover the pointer over a country, we can use {ggiraph} - geom_polygon_interactive() & girafe and {widgetframe} - framewidget() packages.
It is useful creating a reusable theming function:
theme_helper <- function(){
theme(
axis.line = element_blank(),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank(),
plot.background = element_rect(fill = "snow", color = NA),
panel.background = element_rect(fill= "snow", color = NA),
plot.title = element_text(size = 16, hjust = 0.5),
plot.subtitle = element_text(size = 12, hjust = 0.5),
plot.caption = element_text(size = 8, hjust = 1),
legend.title = element_text(color = "grey40", size = 8),
legend.text = element_text(color = "grey40", size = 7, hjust = 0),
legend.position = c(0.05, 0.25),
plot.margin = unit(c(0.5,2,0.5,1), "cm"))
}life_exp_map <- gapminder_df %>%
filter(year == 2007) %>%
right_join(world, by = c(mapname = "region")) %>%
ggplot() +
geom_polygon_interactive(
color = "white", size = 0.01,
aes(long, lat, group = group, fill = lifeExp,
tooltip = sprintf("%s<br/>%s", country, lifeExp))
) +
theme_void() +
scale_fill_viridis(option = "B") +
labs(
title = "Life Expectancy",
subtitle = "Year: 2007",
caption = "Source: gapminder.org",
fill = "Years"
) Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
ℹ The deprecated feature was likely used in the ggiraph package.
Please report the issue at <https://github.com/davidgohel/ggiraph/issues>.
Print it interactively
{plotly}{plotly} on top of tooltips also allows zooming, lasso/box selections or downloading the map as .png.
life_exp07 <- gapminder_df %>%
filter(year == 2007) %>%
select(mapname, code, lifeExp)
p_07 <- plot_geo(life_exp07)
p_07 <- p_07 %>% add_trace(
z = ~lifeExp, color = ~ lifeExp, colors = 'Oranges',
text = ~mapname, locations = ~ code
) %>% colorbar(title = "Years")
p_07 <- p_07 %>%
layout(
title = 'Life Expectancy in 2007 <br>Source:<a href= "https://www.gapminder.org"> gapminder.org</a>', geo = p_07
)
p_07{plotly}meteorites_fell <- meteorites %>%
filter(fall == "Fell")
meteorites_map <- list(
#scope = 'usa',
projection = list(type = 'Mercator'),
showland = TRUE,
landcolor = toRGB("grey80")
)
meteo_map <- plot_geo(meteorites_fell, lat = ~lat, lon = ~long)
meteo_map <- meteo_map %>% add_markers(
text = ~paste(paste("Name:", name),
paste("Year:", year),
paste("Mass:", mass), sep = "<br />"),
color = ~mass, symbol = I("circle-dot"), size = I(8), hoverinfo = "text"
)
meteo_map <- meteo_map %>% colorbar(title = "Mass")Warning: Ignoring 10 observations
{echarts4r}df <- gapminder %>%
mutate(Name = recode_factor(country,
`Congo, Dem. Rep.`= "Dem. Rep. Congo",
`Congo, Rep.`= "Congo",
`Cote d'Ivoire`= "Côte d'Ivoire",
`Central African Republic`= "Central African Rep.",
`Yemen, Rep.`= "Yemen",
`Korea, Rep.`= "Korea",
`Korea, Dem. Rep.`= "Dem. Rep. Korea",
`Czech Republic`= "Czech Rep.",
`Slovak Republic`= "Slovakia",
`Dominican Republic`= "Dominican Rep.",
`Equatorial Guinea`= "Eq. Guinea"))
df %>% group_by(year) %>%
e_chart(Name, timeline = TRUE) %>%
e_map(lifeExp) %>%
e_visual_map(
min = 30, max = 90, type = "piecewise"
) %>%
e_title("Life expectancy by country and year", left = "center") %>%
e_tooltip(
trigger = "item", formatter = e_tooltip_choro_formatter()
)@online{okola2025,
author = {Okola, Basil},
title = {Interactive Maps {(R)}},
date = {2025-10-30},
url = {https://bokola.github.io/posts/2025-10-30-interactive-maps/},
langid = {en}
}