Exploring the various datasets available by GlobPOP project (1990-2022)
Maps
Geocomputation
{geodata}
Author
Aditya Dahiya
Published
February 14, 2025
Introduction
The GlobPOP dataset is a comprehensive global gridded population dataset spanning from 1990 to 2022, developed by researchers Luling Liu, Xin Cao, Shijie Li, and Na Jie from Beijing Normal University. This dataset offers high-precision spatial resolution at 30 arcseconds (approximately 1 km at the equator) and is available in both population count and density formats. The data fusion framework integrates five existing population products—GHS-POP, GRUMP, GPWv4, LandScan, and WorldPop—using cluster analysis and statistical learning methods to enhance accuracy. The dataset is accessible in GeoTIFF format. For more detailed information, refer to the published paper. (Liu et al. 2024).
Code
# Data wrangling & visualizationlibrary(tidyverse) # Data manipulation & visualization# Spatial data handlinglibrary(sf) # Import, export, and manipulate vector datalibrary(terra) # Import, export, and manipulate raster data# ggplot2 extensionslibrary(tidyterra) # Helper functions for using terra with ggplot2# Final plot toolslibrary(scales) # Nice Scales for ggplot2library(fontawesome) # Icons display in ggplot2library(ggtext) # Markdown text in ggplot2library(showtext) # Display fonts in ggplot2library(patchwork) # Composing Plotsbts =42# Base Text Sizesysfonts::font_add_google("Roboto Condensed", "body_font")sysfonts::font_add_google("Oswald", "title_font")sysfonts::font_add_google("Saira Extra Condensed", "caption_font")showtext::showtext_auto()# A base Colourbg_col <-"white"seecolor::print_color(bg_col)# Colour for highlighted texttext_hil <-"grey30"seecolor::print_color(text_hil)# Colour for the texttext_col <-"grey20"seecolor::print_color(text_col)theme_set(theme_minimal(base_size = bts,base_family ="body_font" ) +theme(text =element_text(colour ="grey30",lineheight =0.3,margin =margin(0,0,0,0, "pt") ),plot.title =element_text(hjust =0.5 ),plot.subtitle =element_text(hjust =0.5 ) ))# Caption stuff for the plotsysfonts::font_add(family ="Font Awesome 6 Brands",regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf"))github <-""github_username <-"aditya-dahiya"xtwitter <-""xtwitter_username <-"@adityadahiyaias"social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color: {text_hil}'>{github_username} </span>")social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color: {text_hil}'>{xtwitter_username}</span>")plot_caption <-paste0("**Data**: Center for International Earth Science Information Network, Columbia University"," | **Code:** ", social_caption_1, " | **Graphics:** ", social_caption_2 )rm(github, github_username, xtwitter, xtwitter_username, social_caption_1, social_caption_2)
33-year population density rasters
Getting Population Density Rasters. Code from researchers on how the data was compiled.
Code
# 1990 to 2022 year Global Population Density 30 sec arc resolution# Set Working Directory to a temporary one ---------------------------setwd("C:/Users/dradi/Desktop/pop_raster_data_temp")year_ranges <-1990:2021# ALERT: Downloads Massive amounts of Data ---------------------------for (i in year_ranges) { url <-paste0("https://zenodo.org/records/11179644/files/GlobPOP_Count_30arc_", i, "_I32.tiff" ) output_file <-paste0("GlobPOP_Count_30arc_", i, "_I32.tiff")# Attempt to download the file with error handlingtryCatch({download.file(url, output_file, mode ="wb")cat("Successfully downloaded:", output_file, "\n") }, error =function(e) {cat("Error downloading:", output_file, "-", conditionMessage(e), "\n") })}# --------------------------------------------------------------------# Get the rasters into Routput_file <-paste0("GlobPOP_Count_30arc_", i, "_I32.tiff")for (i in year_ranges) {paste0("rast_", i) |>assign( terra::rast(output_file) |> terra::crop(delhi_map) |> terra::mask(delhi_map, touches =FALSE) )}rast_1990[rast_1990 <=0] <-0.01rast_2000[rast_2000 <=0] <-0.01rast_2010[rast_2010 <=0] <-0.01rast_2022[rast_2022 <=0] <-0.01
Getting Delhi (India) vector map - boundaries
Code
# Get Delhi Map from GADM / {geodata} with geodata::gadm()# District Wise Mapdelhi_map <- geodata::gadm(country ="India",level =2,path =tempdir()) |>st_as_sf() |> janitor::clean_names() |>filter(name_1 =="NCT of Delhi") |>select(name_1, geometry)# ALERT: Downloads Massive amounts of Data-----------------------------# Set Working Directory to a temporary one ----------------------------# TEMPORARY DIRECTORY NAME HERE: setwd() ## Get {osmextract} data for Delhi - to plot roadslines_delhi <- osmextract::oe_get(place ="Delhi",layer ="lines",# download_directory = "C:/Users/dradi/OneDrive/Desktop/pop_raster_data_temp",download_directory ="C:/Users/dradi/Desktop/pop_raster_data_temp")# ---------------------------------------------------------------------# Categorizing Roads by widths and importancewid0 <-c("motorway_link", "motorway" , "corridor")wid1 <-c("trunk", "primary", "primary_link", "trunk_link")wid2 <-c("secondary_link", "secondary")wid3 <-c("tertiary", "tertiary_link")roads_delhi <- lines_delhi |>filter(!is.na(highway)) |>filter(highway %in%c(wid0, wid1, wid2)) |>mutate(width_var =case_when( highway %in% wid0 ~"wid0", highway %in% wid1 ~"wid1", highway %in% wid2 ~"wid2", highway %in% wid3 ~"wid3",.default =NA ) ) |>filter(!is.na(width_var)) |># Create a width_var to plot widths and # transparency as per importancemutate(width_var =fct( width_var,levels =paste0("wid", 0:3) ) ) |>st_intersection(delhi_map)rm(wid0, wid1, wid2, wid3)rm(lines_delhi)# Get vector map of Delhi from stored data# delhi_map <- read_sf(# here::here(# "data", "india_map", # "India_State_Boundary.shp"# )# ) |> # filter(State_Name == "Delhi") |> # st_transform("EPSG:4326")
Compiling a raster for Delhi with years as multiple layers
Code
# Code to compile the rasters of different years into a single rasterfor (selected_year in1990:2022) {# Construct file path file_path <-paste0("GlobPOP_Count_30arc_", selected_year, "_I32.tiff")# Check if the raster file exists before proceedingif (file.exists(file_path)) {# Load, crop, and mask the raster safely rast_obj <-tryCatch({rast(file_path) |> terra::crop(delhi_map) |> terra::mask(delhi_map, touches =FALSE) }, error =function(e) {message(paste("Skipping year", selected_year, "due to error:", e$message))return(NULL) })# If raster loading was successful, process furtherif (!is.null(rast_obj)) {assign(paste0("rast_", selected_year), rast_obj)# Modify raster values safelyassign(paste0("rast_", selected_year), get(paste0("rast_", selected_year)) |> (\(x) { x[x <=0] <-0.01; x })() )message(paste("Processed raster for year", selected_year)) } } else {message(paste("Skipping year", selected_year, "as file does not exist")) }}# Compiling the multiple rasters into a Multi-layered Raster ----------------# Define years rangeyears <-1990:2022# Initialize an empty SpatRaster objectrast_stack <-NULL# Loop through each year and add the raster if it existsfor (y in years) { rast_name <-paste0("rast_", y) # Construct variable nameif (exists(rast_name)) { # Check if raster exists rast <-get(rast_name) # Retrieve rasterif (is.null(rast_stack)) { rast_stack <- rast # Initialize with first available raster } else { rast_stack <-c(rast_stack, rast) # Append to SpatRaster } } else {message(paste("Skipping year", y, "as raster is missing")) }}# Convert the names of the layers into year numbers for better plottingnames(rast_stack) <-str_extract(names(rast_stack), "(?<!\\d)(199[0-9]|20[0-2][0-9])(?!\\d)")varnames(rast_stack) <-str_extract(varnames(rast_stack), "(?<!\\d)(199[0-9]|20[0-2][0-9])(?!\\d)")writeRaster( rast_stack,filename ="delhi_pop_rast_multiyears.tiff")
Figure 1: This graphic shows the population density of Delhi (India) compared from 1990 to 2020, on a transformed fill scale where darker colours represent more densely populated areas. Overlain on top are the major arterial roads of Delhi, taken from Open Street Maps data. An evident pattern is urban densification along major arterial roads, particularly towards north-west of Central Delhi, and especially along its Ring-Road.
Plotting changes in population density over time
This code computes the difference between consecutive raster layers using map2() from the {purrr} package.
Code
# Assuming delhi_rast_stack is a SpatRasteryears <-names(delhi_rast_stack) |>as.numeric()# Compute differences between consecutive years# {purrr}'s map2() function iterates over two vectors simultaneously.rast_diff_list <-map2(# Removes the first element, keeping all years except the first .x = years[-1], # Removes the last element, keeping all years except the last.y = years[-length(years)], # Each iteration computes the following.f =~ {delhi_rast_stack[[as.character(.x)]] - delhi_rast_stack[[as.character(.y)]]})# Convert the list to a raster stackdelhi_rast_diff <-rast(rast_diff_list)# Rename layers to indicate change years (e.g., "Change_1991" for 1991-1990)names(delhi_rast_diff) <- years[-1]varnames(delhi_rast_diff) <- years[-1]delhi_rast_diffrm(years, rast_diff_list)
Plotting the differences (i.e. change in population density)
Liu, Luling, Xin Cao, Shijie Li, and Na Jie. 2024. “A 31-Year (19902020) Global Gridded Population Dataset Generated by Cluster Analysis and Statistical Learning.”Scientific Data 11 (1). https://doi.org/10.1038/s41597-024-02913-0.