A smooth transition between chloropleth and cartogram

[This article was first published on Blog – The R Graph Gallery, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

This post describes how to make a smooth transition GIF between a chloropleth map and a cartogram. It starts by doing a basic map of Africa and then distorts country size using the cartogram library. Ggplot2 is then used to make a nice chloropleth version. Finally the tweenr and the gganimate libraries build a smooth transition between both maps. At the end of this post, you should obtain a .gif file that looks like this:

Before starting, we need a few libraries:

# Load libraries
library(cartogram)    # for the cartogram
library(ggplot2)      # to realize the plots
library(broom)        # from geospatial format to data frame
library(tweenr)       # to create transition dataframe between 2 states
library(gganimate)    # To realize the animation
library(maptools)     # world boundaries coordinates
library(viridis)      # for a nice color palette

 

A basic map of Africa


 

 

The maptools library provides all the information we need to draw a map of Africa. All the country boundaries are stored in the ‘world_simpl’ object. Let’s load this object, keep only Africa, and draw a basic representation.

We only need 3 lines of code to start!

# Get the shape file of Africa
data(wrld_simpl)
afr=wrld_simpl[wrld_simpl$REGION==2,]

# A basic representation
plot(afr)

 

 

Compute cartogram boundaries


 

The afr object is a spatial object. Thus it has a ‘data slot’ that gives a few information concerning each region. You can visualise this info typing afr@data in our case. You will see that a column called POP2005 is present, providing the number of inhabitants per country in 2005.

Using this information we can use the cartogram library to build… a cartogram! Basically, it will distort the shape of every country proportionally to its number of inhabitants. The output is a new geospatial object that we can map like we’ve done before.

As you can see in the image on the left, Nigeria appears way bigger on this map, since it has a population of about 141M inhabitants!

# construct a cartogram using the population in 2005
afr_cartogram <- cartogram(afr, "POP2005", itermax=7)

# A basic representation
plot(afr_cartogram)

 

A nicer representation using ggplot2


Let’s improve the appearance of these 2 maps using the ggplot2 library. Note that ggplot2 uses data frame and not geospatial object. The transformation to a data frame is done using the tidy function of the broom library. Since it does not transfer the data slot automatically, we merge it afterward.

The geom_polygon function is used to draw map data. See the graph #327 of the gallery for more explanation on chloropleth maps with ggplot2.

# Transform these 2 objects in dataframe, plotable with ggplot2
afr_cartogram_df <- tidy(afr_cartogram) %>% left_join(. , afr_cartogram@data, by=c("id"="ISO3")) 
afr_df <- tidy(afr) %>% left_join(. , afr@data, by=c("id"="ISO3")) 

# And using the advices of chart #331 we can custom it to get a better result:
ggplot() +
  geom_polygon(data = afr_df, aes(fill = POP2005/1000000, x = long, y = lat, group = group) , size=0, alpha=0.9) +
  theme_void() +
  scale_fill_viridis(name="Population (M)", breaks=c(1,50,100, 140), guide = guide_legend( keyheight = unit(3, units = "mm"), keywidth=unit(12, units = "mm"), label.position = "bottom", title.position = 'top', nrow=1)) +
  labs( title = "Africa", subtitle="Population per country in 2005" ) +
  ylim(-35,35) +
  theme(
    text = element_text(color = "#22211d"), 
    plot.background = element_rect(fill = "#f5f5f4", color = NA), 
    panel.background = element_rect(fill = "#f5f5f4", color = NA), 
    legend.background = element_rect(fill = "#f5f5f4", color = NA),
    plot.title = element_text(size= 22, hjust=0.5, color = "#4e4d47", margin = margin(b = -0.1, t = 0.4, l = 2, unit = "cm")),
    plot.subtitle = element_text(size= 13, hjust=0.5, color = "#4e4d47", margin = margin(b = -0.1, t = 0.4, l = 2, unit = "cm")),
    legend.position = c(0.2, 0.26)
  ) +
  coord_map()

# You can do the same for afr_cartogram_df

 

Compute several intermediate maps


The goal being to make a smooth animation between the 2 maps, we need to create a multitude of intermediate maps using interpolation. This is possible thanks to the awesome tweenr library. (See a few examples in the animation section of the gallery).

At the end we’ve got a big data frame which contains enough information to draw 30 maps. Three of these maps are presented above.

# Give an id to every single point that compose the boundaries
afr_cartogram_df$id=seq(1,nrow(afr_cartogram_df))
afr_df$id=seq(1,nrow(afr_df))

# Bind both map info in a data frame. 3 states: map --> cartogram --> map
data=rbind(afr_df, afr_cartogram_df, afr_df)

# Set transformation type + time
data$ease="cubic-in-out"
data$time=rep(c(1:3), each=nrow(afr_df))

# Calculate the transition between these 2 objects?
dt <- tween_elements(data, time='time', group='id', ease='ease', nframes = 30)

# check a few frame
ggplot() + geom_polygon(data = dt %>% filter(.frame==0) %>% arrange(order), aes(fill = POP2005, x = long, y = lat, group = group) , size=0, alpha=0.9)
ggplot() + geom_polygon(data = dt %>% filter(.frame==5) %>% arrange(order), aes(fill = POP2005, x = long, y = lat, group = group) , size=0, alpha=0.9)
ggplot() + geom_polygon(data = dt %>% filter(.frame==10) %>% arrange(order), aes(fill = POP2005, x = long, y = lat, group = group) , size=0, alpha=0.9)

 

Make the animation with gganimate


The last step consists at building the 30 maps and compile them in a .gif file. This is done using the gganimate library. This library uses another aesthetic: frame. A new plot is made for each frame, that allows us to build the gif afterwards.

# Plot
p=ggplot() + 
  geom_polygon(data = dt  %>% arrange(order) , aes(fill = POP2005/1000000, x = long, y = lat, group = group, frame=.frame) , size=0, alpha=0.9) +
  theme_void() +
  scale_fill_viridis(name="Population (M)", breaks=c(1,50,100, 140), guide = guide_legend( keyheight = unit(3, units = "mm"), keywidth=unit(12, units = "mm"), label.position = "bottom", title.position = 'top', nrow=1)) +
  labs( title = "Africa", subtitle="Population per country in 2005" ) +
  ylim(-35,35) +
  theme(
    text = element_text(color = "#22211d"), 
    plot.background = element_rect(fill = "#f5f5f4", color = NA), 
    panel.background = element_rect(fill = "#f5f5f4", color = NA), 
    legend.background = element_rect(fill = "#f5f5f4", color = NA),
    plot.title = element_text(size= 22, hjust=0.5, color = "#4e4d47", margin = margin(b = -0.1, t = 0.4, l = 2, unit = "cm")),
    plot.subtitle = element_text(size= 13, hjust=0.5, color = "#4e4d47", margin = margin(b = -0.1, t = 0.4, l = 2, unit = "cm")),
    legend.position = c(0.2, 0.26)
  ) +
  coord_map()

# Make the animation
animation::ani.options(interval = 1/9)
gganimate(p, "Animated_Africa.gif",  title_frame = F)

Done! You should have the gif in your working directory.

 

Conclusion


This post uses several concepts that are extensively described in the R graph gallery:

  • The chloropleth map section gives several examples of chloropleth maps, using different input types and several tools
  • The cartogram section gives further explanation about cartograms
  • The animation section explains more deeply how tweenR and gganimate work
  • The map section is a good starting point if you are lost in the map related packages jungle

If you are interested in dataviz, feel free to visit the gallery, or to follow me on twitter!

 

 

To leave a comment for the author, please follow the link and comment on their blog: Blog – The R Graph Gallery.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)