rggplot2scatterpiegeom-map

how can I merge world map and pie chart together?


I have two data sets with three variables. I want to show one of them as a heatmap and two as a pie chart, on a world map.

I used the below code but got an error. I would like to know how I can solve it.

library(ggplot2)
library(scatterpie)

world_map <- map_data("world")
df <- data.frame(Country = c("USA", "Canada", "Mexico"),
                          Cases = c(10, 20, 30))
df_piechart <- data.frame(long = c(-95, -110, -75),
                                   lat = c(37, 60, 20),
                                   size = c(5, 10, 15),
                                   Group1 = c(20, 30, 50),
                                   Group2 = c(30, 40, 30))

ggplot() +
 geom_map(dat = world_map, map = world_map, aes(map_id = region), color = "white", fill = "#d3d4d3", linewidth = 0.5, alpha=0.4 ) +
 geom_map(dat = df, map = world_map, aes(map_id = Country, fill = Cases), linewidth = 0.5) +
 scale_fill_gradient(low = "skyblue", high = "dodgerblue4", name = "Avian Cases") +
 geom_map(dat = world_map, map = world_map, aes(map_id = region), color = "white", fill = "#d3d4d3", linewidth = 0.5, alpha=0.0 ) +
 expand_limits(x = world_map$long, y = world_map$lat) +
 geom_scatterpie(data = df_piechart, aes(x = long, y = lat, r = size),
                          cols = colnames(df_piechart[, c(4:5)]), alpha = 0.8) +
theme(legend.text = element_text(size = 8, family = "serif"),
    legend.title = element_text(size = 8, face = "bold", family = "serif"),
    legend.key.height = unit(0.2, 'cm'), 
    legend.key.width = unit(0.2, 'cm')) +
theme_void()

error:

Error in `train_discrete()`:
! Continuous value supplied to a discrete scale

Solution

  • The use of (dynamic) fill= in other layer's aesthetics is breaking geom_scatterpie. This was discussed in 2021 at https://github.com/ericpante/marmap/issues/20#issuecomment-796591190, noting

    The error comes from a conflict between the fill colour palette you provide for the tiles of the base map (here, this is a continuous variable: for each depth value, a colour is associated), and the fill colour used for the pie charts (here, this is a discrete variable: for each level of your factor, Legal or Illegal, a fill colour is used).

    (Note that the author of that issue cross-posted the same problem to multiple packages, so this comment I linked is in a different package. The author of scatterpie linked to that issue in scatterpie#31.)

    Two options: (1) drop your desired fill= aesthetics, or (2) add ggnewscale::add_scale_fill() and continue to use your existing code.

    1. Remove fill=Cases in the second geom_map and scale_fill_gradient then you can plot it (though perhaps not with what you want):

      gg <- ggplot() +
       geom_map(dat = world_map, map = world_map, aes(map_id = region), color = "white", fill = "#d3d4d3", linewidth = 0.5, alpha=0.4 ) +
       geom_map(dat = df, map = world_map, aes(map_id = Country), linewidth = 0.5) + # , fill = Cases
       # scale_fill_gradient(low = "skyblue", high = "dodgerblue4", name = "Avian Cases") +
       geom_map(dat = world_map, map = world_map, aes(map_id = region), color = "white", fill = "#d3d4d3", linewidth = 0.5, alpha=0.0 ) +
       expand_limits(x = world_map$long, y = world_map$lat) +
       geom_scatterpie(data = df_piechart, aes(x = long, y = lat, r = size),
                                cols = colnames(df_piechart[, c(4:5)]), alpha = 0.8) +
       theme(legend.text = element_text(size = 8, family = "serif"),
          legend.title = element_text(size = 8, face = "bold", family = "serif"),
          legend.key.height = unit(0.2, 'cm'), 
          legend.key.width = unit(0.2, 'cm')) +
       theme_void()
      
      gg
      

      enter image description here

    2. Use ggnewscale (or some other package that allows you to have multiple color scales) and use add_new_fill(), as in

      gg <- ggplot() +
       geom_map(dat = world_map, map = world_map, aes(map_id = region), color = "white", fill = "#d3d4d3", linewidth = 0.5, alpha=0.4 ) +
       geom_map(dat = df, map = world_map, aes(map_id = Country, fill = Cases), linewidth = 0.5) +
       scale_fill_gradient(low = "skyblue", high = "dodgerblue4", name = "Avian Cases") +
       ggnewscale::new_scale_fill() +
       geom_map(dat = world_map, map = world_map, aes(map_id = region), color = "white", fill = "#d3d4d3", linewidth = 0.5, alpha=0.0 ) +
       expand_limits(x = world_map$long, y = world_map$lat) +
       geom_scatterpie(data = df_piechart, aes(x = long, y = lat, r = size),
                                cols = colnames(df_piechart[, c(4:5)]), alpha = 0.8) +
       theme(legend.text = element_text(size = 8, family = "serif"),
          legend.title = element_text(size = 8, face = "bold", family = "serif"),
          legend.key.height = unit(0.2, 'cm'), 
          legend.key.width = unit(0.2, 'cm')) +
       theme_void()
      
      gg
      

      with ggnewscale

    Side note, you should add coord_quickmap() so that the correct ratio is preserved,

    gg + coord_quickmap()
    

    fixed ratio