Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behavior of ggplot's geom_polygon when using gganimate

Say that I have these data. One for shading counties of a state and the other for plotting points.

library(tidyverse)
library(gganimate)
devtools::install_github("UrbanInstitute/urbnmapr")
library(urbanmapr)

#1. counties dataset for the shading of the background
data(counties)
#keep only Texas counties
counties <- filter(counties, state_fips==48)

#2. dots dataset for dots over time
dots <- data.frame(group = c(rep(1,3), rep(2,3)), 
                    lat = c(rep(32, 3), rep(33, 3)),
                    long = c(rep(-100, 3), rep(-99,3)), 
                    year = c(1:3, 1:3))

I then plot them (first not using gganimate):

ggplot() +
    geom_polygon(data = counties, aes(long, lat, group = county_fips, fill = as.numeric(county_fips))) +
    scale_fill_gradient(low = "navy", high = "lightskyblue3")   +   
    geom_point(data = dots, aes(long, lat, group=interaction(long, lat), color=year), 
               size=6, show.legend = FALSE) + 
    theme_bw() +
    scale_color_gradientn(colours = c("red", "yellow", "darkgreen")) +
    coord_map() +
    labs(subtitle = paste('Year: {frame_time}')) +  
    theme(plot.subtitle = element_text(hjust = 0.8, vjust=-10, size=30)) +  
    theme(panel.background = element_rect(fill = 'white')) +
    theme(panel.grid = element_blank(),axis.title = element_blank(),
          axis.text = element_blank(),axis.ticks = element_blank(),
          panel.border = element_blank())+
    theme(legend.position = c(0.15, .15)) +
    theme(legend.key.size = unit(2,"line"),legend.title=element_text(size=16), 
          legend.text=element_text(size=14))    +
    labs(fill = "abc")

Here is the result:

enter image description here

The problem comes in when I try to animate the dots using gganimate:

map <- ggplot() +
    geom_polygon(data = counties, aes(long, lat, group = county_fips, fill = as.numeric(county_fips))) +
    scale_fill_gradient(low = "navy", high = "lightskyblue3")   +
    geom_point(data = dots, aes(long, lat, group=interaction(long, lat), color=year), 
               size=6, show.legend = FALSE) + 
    theme_bw() +
    scale_color_gradientn(colours = c("red", "yellow", "darkgreen")) +
    coord_map() +
    labs(subtitle = paste('Year: {frame_time}')) +  
    theme(plot.subtitle = element_text(hjust = 0.8, vjust=-10, size=30)) +  
    theme(panel.background = element_rect(fill = 'white')) +
    theme(panel.grid = element_blank(),axis.title = element_blank(),
          axis.text = element_blank(),axis.ticks = element_blank(),
          panel.border = element_blank())+
    theme(legend.position = c(0.15, .15)) +
    theme(legend.key.size = unit(2,"line"),legend.title=element_text(size=16), 
          legend.text=element_text(size=14))    +
    labs(fill = "abc") +    
    transition_time(year) +
    shadow_mark(size=6)     

anim_save("output/test.gif", map, end_pause=6, width = 800, height = 800, duration=8)

Note that it is exactly the same except for the transition_time and shadow_mark lines. Here is the result:

enter image description here

The background colors are VERY different. It might be the case that the colors are reversed or something; I am not sure. However, in my real example using many more dots and different values for the shading, the shading has little if any resemblance to the actual data. What in the world is going on and how can I fix it?

like image 904
bill999 Avatar asked Oct 15 '22 09:10

bill999


1 Answers

I'm not sure what exactly the problem is, but polygons can be problematic, because they tend to violate the tidy data rule of "one row per observation". Also, as a general principle, I would recommend using geom_sf() whenever you're working with geospatial data. Below is code that reproduces your animation using geom_sf().

In theory, I should have been able to plot the points with geom_sf() as well, but I ran into another issue that looks like a bug with gganimate, so I had to work around and extract the projected coordinates for the points and plot with geom_point().

library(tidyverse)
library(gganimate)
library(sf)
#> Linking to GEOS 3.7.2, GDAL 2.4.2, PROJ 5.2.0

# 1. get shape files for texas
library(tidycensus)
options(tigris_use_cache = TRUE)

# download counties for Texas
counties <- get_acs(
  state = 48, # omit to download all US counties
  geography = "county", year = 2015, geometry = TRUE,
  variables = "B19013_001" # need to specify some variable to download, here median income
)
#> Getting data from the 2011-2015 5-year ACS

# 2. dots dataset for dots over time
dots <- data.frame(
  group = c(rep(1,3), rep(2,3)), 
  lat = c(rep(32, 3), rep(33, 3)),
  long = c(rep(-100, 3), rep(-99,3)), 
  year = c(1:3, 1:3)
)

# add geometry column
dots_sf <- dots %>%
  mutate(
    geometry = st_sfc(
      map2(lat, long, function(lat, long) st_point(x = c(long, lat))),
      crs = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"
    ) %>%
      st_transform(crs = 3083) # CRS appropriate for Texas, https://epsg.io/3083
  ) %>%
  st_as_sf()

# and extract transformed coords
dots_sf_transf <- dots_sf %>%
  mutate(
    long_transf = map_dbl(geometry, function(x) x[[1]]),
    lat_transf = map_dbl(geometry, function(x) x[[2]])
  )


# 3. static plot
p <- ggplot() +
  geom_sf(data = counties, aes(fill = as.numeric(GEOID))) +
  scale_fill_gradient(low = "navy", high = "lightskyblue3")   +   
  geom_point(
    data = dots_sf_transf, 
    aes(x = long_transf, y = lat_transf, group = group, color = year),
    size = 6, show.legend = FALSE
  ) + 
  scale_color_gradientn(colours = c("red", "yellow", "darkgreen")) +
  coord_sf(crs = 3083) +
  labs(subtitle = "Year: {frame_time}", fill = "abc") +  
  theme_void() +
  theme(legend.position = c(0.15, .15))
p

# animation
p + transition_time(year) + shadow_mark(size=12)  

Created on 2019-11-03 by the reprex package (v0.3.0)

like image 55
Claus Wilke Avatar answered Oct 18 '22 13:10

Claus Wilke