Alexandra Gore

Creating beautiful map visualizations with gganimate and ggmap

Introduction

This is an annotated notebook that walks through the high-level workflow of wrangling data to create time-based gganimate visualizations. It will also offer suggestions for creating aesthetically pleasing ggplot objects.

The final output of this notebook is an animated map visualization of the 2018 sakura (cherry blossom) blooming season in Japan.

How to follow along

Note: if you would like to follow along interactively at home, git clone the following repository: https://https:/github.com/akg314/sakura, then cd into viz/animated-chart-by-year and open up geocharts.Rmd in your editor.

Let’s begin! 行きましよう!

Loading packages and data

# load packages
library(ggmap) 
library(lubridate)

Run the script below to preprocess the sakura flower & bloom data and fix date formatting.

# the script will create a dataframe object named `df` in the global environment
source('../dfprep.R')

# read in geocoded location data
loc_names <- read_csv('../../data/geocoded_locations.csv')

# load saved map image data
load('jpmapimage')

Uncomment and run the code below the first time you run this notebook in order to initialize some necessary objects (map data and flower data) that can subsequently be loaded above.

# code to initalize some of the objects loaded above 
# (run only once to avoid more calls than necessary to the google maps API)
# get maps and geocode data
# register_google(key=YOUR_KEY_HERE)
# jp <- get_map('Japan',5) 
# loc_names <- unique(df$l_name)
# japan_locations <- cbind(loc_names,geocode(loc_names))
# TO DO: need to recode this location: 高田, shows up as China
# write_csv(japan_locations,'../../data/geocoded_locations.csv')
# save(c(jp),file='jpmapimage')

Let’s take a look at the sakura data.

head(df)

Data cleaning and processing

Here we’ll use some slick dplyr syntax to filter the dataframe to just last year’s data for the purpose of making an animated visualization of last year’s sakura bloom across Japan.

# filter to one year, remove locations whether either bloom or flowering data
# is missing
# join with geolocation data

lastyr <- df %>% filter(year == '2018' & !is.na(b_date) & !is.na(f_date)) %>% left_join(.,loc_names, by = c('l_name'='loc_names'))

We need to change the data format in order to create a visualization later on.

This is done in the sourced script, and the execution is summarized below.

We’ll create a new column that tracks the passage of time in the blooming season, named day_counter. For each value of day_counter, a row corresponding to each location is constructed.

We’ll also create a variable that tracks the bloom status for each location at the specified value of day_counter.

We’ll name this variable color_value, and we’ll use its value later to track the bloom status for locations over time in the map visualization by varying the color of the points on the map.

# to make data frame sorted by date with color value for bloom status
source('createDaysDF.R') 

Constructing a visualization

Time to construct the visualization!

# set colors for sakura bloom range - we'll add white later to 
# represent haven't flowered yet; brown is for done blooming
colors <- c('#fde0dd','#dd3497','#D2B48C')

library(gganimate)
sakura2018 <- jp %>% ggmap() + 
  geom_point(data = df_days,
             aes(x=lon,y=lat,color=color_value,group=l_code),
             size = 6) + 
  scale_color_gradientn(name="Blossom Status",colors=colors,na.value='white',labels = c('Not yet','Flowering','In full bloom','Finished')) + theme(
        panel.spacing = unit(3, "lines"),
              # Remove panel border
        panel.border = element_blank(),  
        # Remove panel grid lines
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        # Remove panel background
        panel.background = element_blank(),
        # remove axis elements
        axis.line = element_blank(),
        axis.ticks = element_blank(),
        axis.title = element_blank(),
        axis.text = element_blank(),
        
        # title element styling
        plot.title = element_text(size = 24, margin = margin(t=4,b = -22),color="#49006a",face='italic'),
        plot.subtitle = element_text(size = 24, color = "grey10", margin = margin(b = 8),hjust=.985),
        #strip.text = element_text(size = 12),
        text = element_text(family = "Georgia"),
        legend.direction = "vertical",
        legend.title = element_text(size = 18,colour = "grey30"),
        legend.text = element_text(size = 18,colour = "grey20"),
        legend.key.height = unit(2.5,'cm'),
        #legend.key.size = unit(2,'cm'),
        legend.position = c(.83,.4),
        legend.box=margin(rep(100,5)),
        legend.key = element_rect(fill = NA),
        legend.background = element_rect(color ='#fff7f3') 
  ) + 
  guides(colour = guide_legend(title.position = "top",override.aes= list(size = 10))) + 
  #animation code and code relying on animation data
  transition_time(day_counter) + ease_aes("linear") + labs(subtitle = 'Date: {frame_time}',title = 'Cherry Blossoms, 2018') 

animate(sakura2018, height = 800, width =800,nframes=100,fps=7,start_pause=10,end_pause=10)

#anim_save("sakura2018.gif")

Animation notes

Most of the hard work lies in preprocessing the data and transforming it into a format suitable for feeding it to the gganimate library. Once the setup is complete, using gganimate is quite simple.

Above we add a transition over time using the day_counter and vary the color of each point on the map using the value of color_value.

Some helpful tips for working with the gganimate package are as follows:

  • Reduce the value for nframes when working on the visualization to speed up the rendering time. Once you’re happy with the appearance, bump this up.
  • Add appropriate values for start_pause and end_pause to allow the viewers’s eyes settle before the animation begins
  • Adjust to an appropriate value of fps (frames-per-second) so that the visualization isn’t too frantic - this is a visualization of cherry blossoms, after all. It should be somewhat relaxing to watch.

Aesthetic tips for making visualizations

Some helpful tips for making great-looking visualizations are as follows:

  • Don’t be afraid to explore theme() when creating a plot using ggplot or ggmap, the parameters are straightforward to understand, and the API affords a lot of power to specify the layout and appearance of your plot exactly as you prefer. I like to begin by rendering my graph without specifying any customizations in theme(), and then to jot some notes down on the visual aspects of the plot I find unappealing. Next, I make a batch of tweaks to theme() to achieve the desired layout.

  • If you haven’t used theme() before, spend a bit of time tweaking parameters one by one to see how each change adjusts the graph’s appearance.

  • The website http://colorbrewer2.org is a great site for browsing aesthetically pleasing and accessible color palettes.