Bubble map with ggplot2



This article demonstrates how to construct a bubble map using ggplot2. It guides you through the process of plotting GPS coordinates on a map, with the bubbles’ size and color representing different data variables.

Bubble map section About Maps

About


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.

bubble map with R and ggplot2

Load data


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)
UK <- gisco_get_countries(country = "UK", resolution = 1)


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)
data <- world.cities %>% filter(country.etc == "UK")

Basic scatterplot map


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")

Basic bubble map


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")

Customized bubble map


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.

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
mybreaks <- c(0.02, 0.04, 0.08, 1, 7)

# 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)
  )

Interactive bubble map with plotly


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!

This mainly relies on the ggplotly() function that converts a ggplot2 object into a plotly, interactive object.

# 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)
p <- data %>%
  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")

p <- ggplotly(p, tooltip = "text")
p

# save the widget in a html file if needed.
# library(htmlwidgets)
# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/bubblemapUK.html"))

Going further


This post explains how to make a bubble map with ggplot2.

You might be interested in this post that combines bubble map with a cartogram, and more generally in the bubble map section of the gallery.

Related chart types


Map
Choropleth
Hexbin map
Cartogram
Connection
Bubble map



❤️ 10 best R tricks ❤️

👋 After crafting hundreds of R charts over 12 years, I've distilled my top 10 tips and tricks. Receive them via email! One insight per day for the next 10 days! 🔥