Search code examples
rtidyversemapsgischoropleth

Creating a global choropleth map for only a selection of countries in r using maps package


I have a data frame with two column, such as the below df as an example, and I want to use maps::map() in order to create a global choropleth map. I want color to be assigned to those countries exist in the df, and for rest just empty colourless polygons. But the result is quite different with what I expected. I have tried with below script: Also, why wolrd map has such low quality? is there a way to make this better?

df <- tribble(
  ~Country, ~Value,
  "Jordan", 35,
  "USA", 84,
  "Turkey", 65,
  "Pakistan", 43
)

percent_map <- function(var, color, legend.title, min = 0, max = 100) {
  
  # generate vector of fill colors for map
  shades <- colorRampPalette(c("white", color))(100)
  
  # constrain gradient to percents that occur between min and max
  var <- pmax(var, min)
  var <- pmin(var, max)
  percents <- as.integer(cut(var, 100, 
                             include.lowest = TRUE, ordered = TRUE))
  fills <- shades[percents]
  
  # plot choropleth map
  maps::map("world", fill = TRUE, col = fills, 
            resolution = 0,
            myborder = 0, mar = c(0,0,0,0))
  
  # overlay state borders
  maps::map("world", col = "white", fill = FALSE, add = TRUE,
            lty = 1, lwd = 1, 
            myborder = 0, mar = c(0,0,0,0))
}

percent_map(var = df$Value, 
            color = "darkgreen", 
            legend.title = "Global Map for Value", 
            min = 0, max = 100 
)

enter image description here


Solution

  • What's happening here is that you take a Value column from your tibble and get a vector of 4 values, c(35, 84, 65, 43), loosing any reference to actual countries. After generating fills, there's still a vector of length 4 ( c("#FFFFFF", "#006400", "#619F61", "#D5E5D5") ), which will be recycled over every country polygon. I.e. every country gets a value from your vector of 4, so 1/4 of those will end up as white and 3/4 will be some shade of green.

    If it doesn't have to be a base plot and ggplot would do, you could still use data from maps::map() (though it is from 2013, so not exactly up-to-date):

    library(dplyr)
    library(ggplot2)
    df <- tribble(
      ~Country, ~Value,
      "Jordan", 35,
      "USA", 84,
      "Turkey", 65,
      "Pakistan", 43
    )
    
    # gets data from maps::map()
    map_data("world") %>% 
      # join map dataframe with values tabl
      left_join(df, by = join_by(region == Country)) %>% 
      ggplot(aes(long, lat)) +
      geom_polygon(aes(group = group, fill = Value), color ="grey80") +
      scale_fill_gradient(low = "white", 
                          high = "darkgreen", 
                          limits = c(1,100), 
                          na.value = "grey99") +
      coord_sf() +
      theme_bw()
    

    If it has to be a base plot but it's OK to use sf and some other data source (like Natural Earth / rnaturalearth package), perhaps something like this:

    
    library(sf)
    df$Country[df$Country == "USA"] <- "United States of America"
    
    ne_world <- rnaturalearth::ne_countries(returnclass = "sf") %>% 
      left_join(df, by = join_by(admin == Country))
    
    plot(ne_world["Value"],
         border = 'grey80',
         pal = colorRampPalette(c("white", "darkgreen"))(100),
         breaks = c(0:100))
    

    You may also want to check tmap or sf plotting vignette

    Created on 2023-05-12 with reprex v2.0.2