This post explains how to build a bubble map with ggplot2. It takes a list of GPS coordinates and plot them on top of a map, with size and color mapped to data.
This post explains how to make a bubble map with ggplot2.
A bubble map is like a bubble chart, but with a map in the background. As input you need:
This post provides a step-by-step approach to build the map beside, showing the 1000 biggest cities of the UK.
The first step is to get the boundaries of the zone that interests you. Several option are available in R, as extensively described in the background map section of the gallery.
Briefly, you can find this information somewhere under the shapefile format or under the geoJSON format. You can also load google like backgrounds with the ggmap library.
This post uses the last approach: it loads the giscoR
package that provides the boundaries of every world country.
# Libraries
library(ggplot2)
library(dplyr)
# Get the world polygon and extract UK
library(giscoR)
<- gisco_get_countries(country = "UK", resolution = 1) UK
The second step is to load a data frame with the info of the bubble
you want to draw. The maps
library provides a list of the
biggest cities in the world. Let’s use it to get information on the
UK.
# Get a data frame with longitude, latitude, and size of bubbles (a bubble = a city)
library(maps)
<- world.cities %>% filter(country.etc == "UK") data
ggplot2
makes it a breeze to map both information on the
same graph. Use geom_sf()
for the shape of UK first, and
add your scatter plot on it with geom_point()
.
On the right figure, note the use of the ggrepel
library
to avoid overlapping between city names.
# Left chart
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(data = data, aes(x = long, y = lat)) +
theme_void() +
ylim(50, 59)
# Second graphic with names of the 10 biggest cities
library(ggrepel)
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(data = data, aes(x = long, y = lat, alpha = pop)) +
geom_text_repel(
data = data %>% arrange(pop) %>% tail(10),
aes(x = long, y = lat, label = name), size = 5
+
) geom_point(
data = data %>% arrange(pop) %>% tail(10), aes(x = long, y = lat),
color = "red", size = 3
+
) theme_void() +
ylim(50, 59) +
theme(legend.position = "none")
Now we want to add another information. The number of inhabitant per city will be mapped to the colour and the size of the bubbles. Note that the order of city matters! It is advised to show the most important information on top (center). This can been done sorting your dataset before making the plot.
# Left: use size and color
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(data = data, aes(x = long, y = lat, size = pop, color = pop)) +
scale_size_continuous(range = c(1, 12)) +
scale_color_viridis_c(trans = "log") +
theme_void() +
ylim(50, 59)
# Center: reorder your dataset first! Big cities appear later = on top
%>%
data arrange(pop) %>%
mutate(name = factor(name, unique(name))) %>%
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(aes(x = long, y = lat, size = pop, color = pop), alpha = 0.9) +
scale_size_continuous(range = c(1, 12)) +
scale_color_viridis_c(trans = "log") +
theme_void() +
ylim(50, 59) +
theme(legend.position = "none")
# Right: just use arrange(desc(pop)) instead
%>%
data arrange(desc(pop)) %>%
mutate(name = factor(name, unique(name))) %>%
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(aes(x = long, y = lat, size = pop, color = pop), alpha = 0.9) +
scale_size_continuous(range = c(1, 12)) +
scale_color_viridis_c(trans = "log") +
theme_void() +
ylim(50, 59) +
theme(legend.position = "none")
We can custom a little bit this figure for a better looking result (first image of this post).
Note that here the legend shows both the size, the color and the transparency on the same circles. This is possible only if these 3 informations are redundant, with the same name, transformation and breaks.
# Create breaks for the color scale
<- c(0.02, 0.04, 0.08, 1, 7)
mybreaks
# Reorder data to show biggest cities on top
<- data %>%
data arrange(pop) %>%
mutate(name = factor(name, unique(name))) %>%
mutate(pop = pop / 1000000)
# Build the map
%>%
data ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(aes(x = long, y = lat, size = pop, color = pop, alpha = pop),
shape = 20, stroke = FALSE
+
) scale_size_continuous(
name = "Population (in M)", trans = "log",
range = c(1, 12), breaks = mybreaks
+
) scale_alpha_continuous(
name = "Population (in M)", trans = "log",
range = c(0.1, .9), breaks = mybreaks
+
) scale_color_viridis_c(
option = "magma", trans = "log",
breaks = mybreaks, name = "Population (in M)"
+
) theme_void() +
guides(colour = guide_legend()) +
ggtitle("The 1000 biggest cities in the UK") +
theme(
legend.position = c(1, 0.6),
text = element_text(color = "#22211d"),
plot.margin = margin(r = 2, l = 2, unit = "cm"),
plot.background = element_rect(fill = "#f5f5f2", color = NA),
panel.background = element_rect(fill = "#f5f5f2", color = NA),
plot.title = element_text(size = 14, hjust = 0.5, color = "#4e4d47"),
legend.title = element_text(size = 8),
legend.text = element_text(size = 8)
)
Last but not least, plotly
allows to quickly get an
interactive version. This is really handy since it allows to zoom on the
map and hover a city to know its name and population!
# Load the plotly package
library(plotly)
# Rorder data + Add a new column with tooltip text
<- data %>%
data arrange(pop) %>%
mutate(name = factor(name, unique(name))) %>%
mutate(mytext = paste(
"City: ", name, "\n",
"Population: ", pop,
sep = ""
))
# Make the map (static)
<- data %>%
p ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(aes(
x = long, y = lat, size = pop, color = pop, text = mytext,
alpha = pop
+
)) scale_size_continuous(range = c(1, 9)) +
scale_color_viridis_c(option = "inferno", trans = "log") +
scale_alpha_continuous(trans = "log") +
theme_void() +
theme(legend.position = "none")
<- ggplotly(p, tooltip = "text")
p
p
# save the widget in a html file if needed.
# library(htmlwidgets)
# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/bubblemapUK.html"))