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 maps
package that provides the boundaries of every world country.
# Libraries
library(ggplot2)
library(dplyr)
# Get the world polygon and extract UK
library(maps)
UK <- map_data("world") %>% filter(region=="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)
data <- world.cities %>% filter(country.etc=="UK")
ggplot2
makes it a breeze to map both information on the same graph. Use geom_polygon
for the shape of UK first, and add your scatterplot 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_polygon(data = UK, aes(x=long, y = lat, group = group), fill="grey", alpha=0.3) +
geom_point( data=data, aes(x=long, y=lat)) +
theme_void() + ylim(50,59) + coord_map()
# Second graphic with names of the 10 biggest cities
library(ggrepel)
ggplot() +
geom_polygon(data = UK, aes(x=long, y = lat, group = group), 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) + coord_map() +
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.
# virids package for the color palette
library(viridis)
# Left: use size and color
ggplot() +
geom_polygon(data = UK, aes(x=long, y = lat, group = group), 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(trans="log") +
theme_void() + ylim(50,59) + coord_map()
# Center: reorder your dataset first! Big cities appear later = on top
data %>%
arrange(pop) %>%
mutate( name=factor(name, unique(name))) %>%
ggplot() +
geom_polygon(data = UK, aes(x=long, y = lat, group = group), 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(trans="log") +
theme_void() + ylim(50,59) + coord_map() + theme(legend.position="none")
# Right: just use arrange(desc(pop)) instead
data %>%
arrange(desc(pop)) %>%
mutate( name=factor(name, unique(name))) %>%
ggplot() +
geom_polygon(data = UK, aes(x=long, y = lat, group = group), 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(trans="log") +
theme_void() + ylim(50,59) + coord_map() + 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 redondante, 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
map %>%
ggplot() +
geom_polygon(data = UK, aes(x=long, y = lat, group = group), 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(option="magma", trans="log", breaks=mybreaks, name="Population (in M)" ) +
theme_void() + ylim(50,59) + coord_map() +
guides( colour = guide_legend()) +
ggtitle("The 1000 biggest cities in the UK") +
theme(
legend.position = c(0.85, 0.8),
text = element_text(color = "#22211d"),
plot.background = element_rect(fill = "#f5f5f2", color = NA),
panel.background = element_rect(fill = "#f5f5f2", color = NA),
legend.background = element_rect(fill = "#f5f5f2", color = NA),
plot.title = element_text(size= 16, hjust=0.1, color = "#4e4d47", margin = margin(b = -0.1, t = 0.4, l = 2, unit = "cm")),
)
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)
p <- data %>%
ggplot() +
geom_polygon(data = UK, aes(x=long, y = lat, group = group), 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,15)) +
scale_color_viridis(option="inferno", trans="log" ) +
scale_alpha_continuous(trans="log") +
theme_void() +
ylim(50,59) +
coord_map() +
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"))