Greenhouse gas emissions in context: Raw totals, income groups and human development
Abstract
This article analyses 2024 greenhouse gas emissions from the 2025 release of the Emissions Database for Global Atmospheric Research, separating total from per-capita emissions and reading both alongside income group and human development. It documents the data preparation, visualises regional totals, the largest emitters and their sectors, and reports the residual of each country from a descriptive association with the Human Development Index. None of these quantities estimates responsibility, causation or feasible mitigation.
National greenhouse gas figures can be reported as totals, per-capita values, sector compositions or associations with measures of development, and each of these descriptions answers a different question. None of them, on its own, measures historical responsibility, establishes causation or determines a fair mitigation burden.
This post uses the 2025 report of the Emissions Database for Global Atmospheric Research (EDGAR), a consistent inventory of emissions by country and sector, to describe emissions in 2024 (Crippa et al., 2025). The analysis combines those estimates with the World Bank income classification and with the Human Development Index (HDI) of the United Nations Development Programme. Its aim is to make the denominators and contextual variables explicit.
Development and Plots
The figures move from totals to per-capita emissions by income group, then to sector composition and finally to a descriptive association with human development, in which the HDI serves as a contextual covariate. Each figure caption gives the source and the unit.
Data and Preparation
All emissions come from the 2025 EDGAR booklet (Crippa et al., 2025), which runs from 1970 to 2024. Greenhouse gas here covers fossil CO2, CH4, N2O and fluorinated gases but excludes land use, land-use change and forestry. The gases are aggregated with the 100-year global warming potentials of the Fifth Assessment Report of the Intergovernmental Panel on Climate Change (Myhre et al., 2013). The booklet reports totals in million tonnes of CO2 equivalent per year (Mt CO2eq) and per-capita emissions in tonnes per person. It also includes the European Union (EU27) and the world as rows of their own, and their totals are used as reported. The world population and the world per-capita figure are instead recomputed from the territories. This puts the world per-capita figure slightly below EDGAR’s own, because the emissions of international aviation and shipping drop out of the numerator while the population stays the same.
The analysis reads three compact files that were derived from the booklet and from three context sources in a preparation step described at the end of this post. ghg_long.csv has one row per territory and year, with its continent, income group, total, per-capita figure, derived population and euro-area membership. hdi_2023.csv gives the HDI for 2023 from the 2025 Human Development Report (United Nations Development Programme, 2025), and sectors_2024.csv gives the 2024 emissions of each territory by EDGAR sector, summed over the four substances. Continents come from the ISO 3166 data set maintained by Duncalfe (2024) and income groups from the World Bank classification for fiscal year 2026 (World Bank, 2025).
The population of each territory and year is the total divided by the per-capita figure, which recovers the population series behind EDGAR’s own per-capita values without a further source. EDGAR reports Serbia and Montenegro (code SCG) and Sudan and South Sudan (code SDN) as combined territories. It likewise folds Liechtenstein into Switzerland, Andorra into Spain, Monaco into France, San Marino and the Holy See into Italy, and the State of Palestine into Israel. These rows are kept exactly as reported and are later compared with the HDI of the member whose code they carry, except Serbia and Montenegro, whose code belongs to neither member and is matched to Serbia by hand. The first chunk loads the packages, sets one theme for all static figures, and fixes the colours, taken from the palette of Okabe and Ito (2008), that each continent and income group keeps throughout.
library(dplyr)
library(tidyr)
library(ggplot2)
library(patchwork)
library(scales)
library(ggrepel)
library(ggtext)
library(plotly)
library(htmltools)
library(leaflet)
library(sf)
library(rnaturalearth)
# Holds the medium-scale boundaries that ne_countries() reads for Figure 6
library(rnaturalearthdata)
library(RColorBrewer)
library(knitr)
theme_set(theme_minimal(base_size = 13))
theme_update(
# Set the leaf elements, not the axis.title parent: under ggplot2 4.0 the parent no
# longer propagates the element_markdown class down to axis.title.x/axis.title.y, so
# the axes print a literal "CO<sub>2</sub>" instead of a subscript
axis.title.x = element_markdown(),
axis.title.y = element_markdown(angle = 90),
plot.title = element_text(size = 13, face = 'bold'),
plot.title.position = 'plot',
panel.grid.minor = element_blank(),
legend.position = 'bottom'
)
# ggrepel places labels partly at random, so an unseeded knit can move the
# end-of-line labels of figures 1 and 2. Some draws overlap two labels in the
# Low income panel of figure 2. This seed was checked to keep every repelled
# label apart at the figure sizes used below.
repel_seed <- 2025
continent_colours <- c(Asia = '#D55E00', America = '#0072B2', Europe = '#009E73',
Africa = '#E69F00', Oceania = '#CC79A7',
`International transport` = '#56B4E9')
income_colours <- c(`High income` = '#D55E00', `Upper middle income` = '#E69F00',
`Lower middle income` = '#0072B2', `Low income` = '#009E73')
fmt <- function(x, digits = 0) formatC(x, format = 'f', digits = digits, big.mark = ',')
pct <- function(x, digits = 1) paste0(fmt(x, digits), '%')
ordinal <- label_ordinal()
edgar_caption <- 'The data are from the 2025 EDGAR report (Crippa et al., 2025).'The next chunk reads the three files and separates EDGAR’s own EU27 and world rows from the territories. It then gives an income group to Curaçao and to Serbia and Montenegro, two territories that EDGAR reports under legacy codes absent from the World Bank classification. For fiscal year 2026, the World Bank classified Curaçao as high income and both Serbia and Montenegro as upper middle income (World Bank, 2025). The chunk also lists the jointly reported territories with their members, so that the map in Figure 6 can colour each member.
ghg <- read.csv('data/ghg_long.csv', stringsAsFactors = FALSE)
hdi <- read.csv('data/hdi_2023.csv', stringsAsFactors = FALSE)
sectors_2024 <- read.csv('data/sectors_2024.csv', stringsAsFactors = FALSE)
world <- ghg %>% filter(iso3 == 'GLOBAL TOTAL') %>% select(year, world_mt = total_mt)
eu27 <- ghg %>% filter(iso3 == 'EU27') %>% select(year, eu27_mt = total_mt)
world_2024 <- world$world_mt[world$year == 2024]
# Countries, plus international aviation (AIR) and shipping (SEA)
countries <- ghg %>% filter(!iso3 %in% c('EU27', 'GLOBAL TOTAL'))
# EDGAR reports two territories under legacy codes that match no economy in the
# World Bank classification, so the join by code left them without an income
# group. ANT is the former Netherlands Antilles, which EDGAR labels Curaçao, and
# SCG is Serbia and Montenegro. In the World Bank's file for fiscal year 2026
# (CLASS_2025_07_02.xlsx), Curaçao and Sint Maarten are both high income. Serbia
# and Montenegro are both upper middle income, as is Kosovo, which has no EDGAR
# row of its own. Each code therefore takes the group that all its classified
# successors share, whichever of them its row covers.
legacy_income_groups <- c(ANT = 'High income', SCG = 'Upper middle income')
countries <- countries %>%
mutate(income_group = coalesce(income_group, unname(legacy_income_groups[iso3])))
# Territories that EDGAR reports jointly under one code, with their members
combined <- tribble(
~iso3, ~member_iso3, ~member,
'SCG', 'SRB', 'Serbia', 'SCG', 'MNE', 'Montenegro',
'SDN', 'SDN', 'Sudan', 'SDN', 'SSD', 'South Sudan',
'ISR', 'ISR', 'Israel', 'ISR', 'PSE', 'State of Palestine',
'CHE', 'CHE', 'Switzerland', 'CHE', 'LIE', 'Liechtenstein',
'ESP', 'ESP', 'Spain', 'ESP', 'AND', 'Andorra',
'FRA', 'FRA', 'France', 'FRA', 'MCO', 'Monaco',
'ITA', 'ITA', 'Italy', 'ITA', 'SMR', 'San Marino', 'ITA', 'VAT', 'Holy See'
)
# A row without a continent would otherwise vanish silently from figure 1
stopifnot(!anyNA(countries$continent))
c(territories = n_distinct(setdiff(countries$iso3, c('AIR', 'SEA'))),
years = n_distinct(ghg$year), countries_with_hdi = sum(!is.na(hdi$hdi_2023)),
sector_rows = nrow(sectors_2024))#> territories years countries_with_hdi sector_rows
#> 208 55 193 1675# Derived here rather than beside the figures that draw them, because the
# captions of those figures quote them and a chunk option is evaluated
# before the chunk body runs.
no_income_group <- countries %>%
filter(year == 2024, !iso3 %in% c('AIR', 'SEA'), is.na(income_group))
# The world total includes international aviation and shipping, whose two rows
# carry no population, so the reference line uses the country total over the
# country population, keeping the numerator and the denominator on the same set.
countries_2024 <- countries %>% filter(year == 2024, !iso3 %in% c('AIR', 'SEA'))
world_per_capita <- sum(countries_2024$total_mt) / sum(countries_2024$population) * 1e6World and Regional Totals
Figure 1 shows the world total by continent since 1970, with international aviation and shipping as a category of their own, because EDGAR attributes them to no country. The lower panel expresses the emissions of the EU27 and of the euro area as a percentage of the world total, since in absolute terms the world total would dwarf the European series.
by_continent <- countries %>%
group_by(continent, year) %>%
summarise(total_mt = sum(total_mt), .groups = 'drop')
continent_order <- by_continent %>%
filter(year == 2024) %>%
arrange(desc(total_mt)) %>%
pull(continent)
by_continent <- by_continent %>%
mutate(continent = factor(continent, levels = continent_order))
continent_share <- function(cont, yr) {
100 * by_continent$total_mt[by_continent$continent == cont & by_continent$year == yr] /
world$world_mt[world$year == yr]
}
# Both panels print their labels in a right margin of the same width, and both
# fix the x limits to the years of the data, which the nudged labels would
# otherwise stretch, so that the years of the two panels line up vertically
label_space <- 95
# The two panels meet in the middle of the figure, where the only thing keeping
# Panel B's title off Panel A's year labels was two 5.5 pt margins. That gap was
# narrower than the one above the figure, so the pair read as one block with a
# stray line of text in it. The facing edges are widened and the outer top edge
# is dropped to nothing, since the page already sets a margin above the figure.
label_margin_top <- margin(0, label_space, 11, 5.5)
label_margin_bottom <- margin(11, label_space, 5.5, 5.5)
year_limits <- range(by_continent$year)
# One label per band, level with the middle of the band in the last year.
# position_stack(reverse = TRUE) stacks the first level at the bottom.
continent_labels <- by_continent %>%
filter(year == max(year)) %>%
arrange(continent) %>%
mutate(label_y = cumsum(total_mt) - total_mt / 2,
label = sub(' ', '\n', continent))
p1a <- ggplot(by_continent, aes(year, total_mt, fill = continent)) +
geom_area(position = position_stack(reverse = TRUE), colour = 'white', linewidth = 0.3) +
geom_text_repel(data = continent_labels, aes(year, label_y, label = label),
inherit.aes = FALSE, hjust = 0, nudge_x = 1.5, direction = 'y',
xlim = c(-Inf, Inf), ylim = c(-Inf, Inf), size = 3.8, lineheight = 0.9,
colour = 'grey20', segment.colour = 'grey50', segment.size = 0.3,
min.segment.length = 0, box.padding = 0.15, seed = repel_seed) +
scale_fill_manual(values = continent_colours, guide = 'none') +
scale_x_continuous(breaks = seq(1970, 2020, 10), limits = year_limits, oob = oob_keep,
expand = c(0, 0)) +
scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.04))) +
coord_cartesian(clip = 'off') +
# The panel carries only its letter, since the figure caption titles each panel
labs(title = 'A',
x = NULL, y = 'Mt CO<sub>2</sub>eq per year') +
theme(plot.margin = label_margin_top)
# Shares of the world total: EU27 from EDGAR's own row, euro area summed over members
shares <- world %>%
left_join(eu27, by = 'year') %>%
left_join(countries %>%
filter(euro_area) %>%
group_by(year) %>%
summarise(euro_area_mt = sum(total_mt), .groups = 'drop'),
by = 'year') %>%
mutate(EU27 = 100 * eu27_mt / world_mt,
`Euro area` = 100 * euro_area_mt / world_mt)
eu27_peak_year <- shares$year[which.max(shares$eu27_mt)]
eu27_peak_mt <- max(shares$eu27_mt)
eu27_value <- function(yr) shares$eu27_mt[shares$year == yr]
share_value <- function(bloc, yr) shares[[bloc]][shares$year == yr]
shares_long <- shares %>%
select(year, EU27, `Euro area`) %>%
pivot_longer(-year, names_to = 'bloc', values_to = 'share')
p1b <- ggplot(shares_long, aes(year, share, colour = bloc)) +
annotate('segment', x = eu27_peak_year, xend = eu27_peak_year, y = 0,
yend = share_value('EU27', eu27_peak_year), linetype = 'dotted', colour = 'grey40') +
annotate('text', x = eu27_peak_year + 1, y = 0.8,
label = paste0('Peak of EU27 emissions in absolute terms:\n',
eu27_peak_year, ', ', fmt(eu27_peak_mt), ' Mt'),
hjust = 0, vjust = 0, size = 3.6, colour = 'grey30', lineheight = 0.95) +
geom_line(linewidth = 1) +
geom_text_repel(data = filter(shares_long, year == max(year)),
aes(label = paste0(bloc, ', ', fmt(share, 1), '%')),
hjust = 0, nudge_x = 1.5, direction = 'y', segment.colour = NA,
xlim = c(-Inf, Inf), size = 3.8, show.legend = FALSE, seed = repel_seed) +
scale_colour_manual(values = c(EU27 = '#0072B2', `Euro area` = '#D55E00')) +
scale_x_continuous(breaks = seq(1970, 2020, 10), limits = year_limits, oob = oob_keep,
expand = c(0, 0)) +
scale_y_continuous(limits = c(0, NA), labels = function(x) paste0(x, '%'),
expand = expansion(mult = c(0, 0.08))) +
coord_cartesian(clip = 'off') +
labs(title = 'B',
x = NULL, y = 'Share of the world total') +
theme(legend.position = 'none', plot.margin = label_margin_bottom)
p1a / p1b +
plot_layout(heights = c(1.15, 1))
Figure 1: World Greenhouse Gas Emissions by Continent, 1970 to 2024 (Panel A), and EU27 and Euro-Area Emissions as a Percentage of the World Total (Panel B). The data are from the 2025 EDGAR report (Crippa et al., 2025). Values are in million tonnes of CO2 equivalent per year (Panel A) and in percentages of the world total (Panel B). The EU27 series is EDGAR’s own row, and the euro-area series is the sum of the 21 members of the euro area as of 2026, including Bulgaria.
World emissions reached 53,206 Mt CO2eq in 2024, 129% more than in 1970. Asia accounted for 58.2% of the 2024 total, up from 24.1% in 1970, while the share of Europe fell from 34.7% to 12.5% and that of America from 32.5% to 19.2%. International aviation and shipping added 2.6%. The EU27 emitted 3,165 Mt in 2024, 5.9% of the world total against 19.7% in 1970, and the euro area 4.6% against 14.7%. In absolute terms, EU27 emissions peaked in 1979 at 5,422 Mt and stayed within 8% of that peak throughout the 1980s. They have since fallen by 42%, or by 35% relative to 1990, the base year of the European Union’s climate targets. The EU27 share in Panel B falls further than this, because the world total grew by 85% over the same years.
Income Groups
Per-capita figures cannot be added across countries, so each income group is summarised by its population-weighted mean. The mean divides the total emissions of the group by its population, which gives the per-capita emissions of the group treated as one territory. Panel A of Figure 2 shows this quantity for the four World Bank income groups of fiscal year 2026 (World Bank, 2025). Panel B shows, for each group, its share of world emissions as a solid line and its share of world population as a dashed line. The distance between the two lines is the gap between what a group emits and what it would emit at the world average.
world_population <- countries %>%
filter(!is.na(population)) %>%
group_by(year) %>%
summarise(world_population = sum(population), .groups = 'drop')
by_income <- countries %>%
filter(!is.na(income_group)) %>%
group_by(income_group, year) %>%
summarise(total_mt = sum(total_mt), population = sum(population),
n_countries = n_distinct(iso3), .groups = 'drop') %>%
left_join(world, by = 'year') %>%
left_join(world_population, by = 'year') %>%
mutate(per_capita_t = total_mt / population * 1e6,
emission_share = 100 * total_mt / world_mt,
population_share = 100 * population / world_population,
income_group = factor(income_group, levels = names(income_colours)))
income_value <- function(group, yr, variable) {
by_income[[variable]][by_income$income_group == group & by_income$year == yr]
}
# The first year in which the upper-middle-income share of world emissions
# exceeds the high-income share, with the two groups side by side in each year
overtake_year <- by_income %>%
select(income_group, year, emission_share) %>%
pivot_wider(names_from = income_group, values_from = emission_share) %>%
filter(`Upper middle income` > `High income`) %>%
pull(year) %>%
min()
p2a <- ggplot(by_income, aes(year, per_capita_t, colour = income_group)) +
geom_line(linewidth = 1) +
geom_text_repel(data = filter(by_income, year == max(year)),
aes(label = paste0(income_group, ', ', fmt(per_capita_t, 1), ' t')),
hjust = 0, nudge_x = 1, direction = 'y', segment.colour = NA,
xlim = c(-Inf, Inf), size = 3.8, show.legend = FALSE, seed = repel_seed) +
scale_colour_manual(values = income_colours) +
scale_x_continuous(breaks = seq(1970, 2020, 10), expand = expansion(mult = c(0, 0.38))) +
scale_y_continuous(limits = c(0, NA), expand = expansion(mult = c(0, 0.05))) +
coord_cartesian(clip = 'off') +
labs(title = 'A',
x = NULL, y = 't CO<sub>2</sub>eq per person') +
theme(legend.position = 'none', plot.margin = margin(0, 5.5, 11, 5.5))
shares_by_income <- by_income %>%
select(income_group, year, emission_share, population_share) %>%
pivot_longer(c(emission_share, population_share), names_to = 'measure', values_to = 'share') %>%
mutate(measure = factor(measure, levels = c('emission_share', 'population_share'),
labels = c('of emissions', 'of population')))
p2b <- ggplot(shares_by_income, aes(year, share, colour = income_group, linetype = measure)) +
geom_line(linewidth = 0.9) +
geom_text_repel(data = filter(shares_by_income, year == max(year)),
aes(label = paste0(fmt(share, 0), '% ', measure)),
hjust = 0, nudge_x = 1, direction = 'y', segment.colour = NA,
xlim = c(-Inf, Inf), size = 3.5, show.legend = FALSE, seed = repel_seed) +
facet_wrap(~ income_group, nrow = 2) +
scale_colour_manual(values = income_colours, guide = 'none') +
scale_linetype_manual(values = c('solid', '22'), guide = 'none') +
scale_x_continuous(breaks = seq(1970, 2010, 20), expand = expansion(mult = c(0, 0.56))) +
scale_y_continuous(limits = c(0, NA), labels = function(x) paste0(x, '%'),
expand = expansion(mult = c(0, 0.08))) +
coord_cartesian(clip = 'off') +
labs(title = 'B',
x = NULL, y = 'Share of the world total') +
theme(legend.position = 'none', strip.text = element_text(hjust = 0, face = 'bold'),
panel.spacing.x = unit(1.5, 'lines'),
plot.margin = margin(11, 5.5, 5.5, 5.5))
p2a / p2b +
plot_layout(heights = c(1, 1.25))
Figure 2: Per-Capita Emissions by World Bank Income Group, 1970 to 2024 (Panel A), and Each Group’s Share of World Emissions and of World Population (Panel B). The data are from the 2025 EDGAR report (Crippa et al., 2025). Income groups follow the World Bank (2025) classification for fiscal year 2026. Values are in tonnes of CO2 equivalent per person per year (Panel A) and in percentages of the world total (Panel B). In Panel B, solid lines show the share of world emissions and dashed lines the share of world population. Population is the EDGAR total divided by the per-capita figure. The 12 territories without an income group are omitted.
In 2024, a resident of a high-income country emitted 12.5 t on average, 8.8 times the 1.4 t of a resident of a low-income country. The high-income mean peaked at 16.5 t in 1979, changed little from the early 1980s to the late 2000s and has fallen since. The upper-middle-income mean, for a group that includes China, rose from 4.6 t in 2000 to 8.6 t in 2024. The group’s share of world emissions overtook the high-income share in 2011. Panel B shows the imbalance between people and emissions: high-income countries hold 17.4% of the world’s population and emit 33.3% of the total. The lower-middle-income and low-income groups together hold 45.3% of the population and emit 17.1%. The 12 territories without an income group emitted 0.7% of the 2024 total between them. They include Ethiopia and Venezuela, both of which the World Bank left unclassified for fiscal year 2026, and small territories such as Guadeloupe and the Falkland Islands.
The 20 Largest Emitters
Figure 3 ranks countries by their 2024 total, coloured by continent, with each country’s share of the world total printed at the end of its bar and its per-capita emissions in a dot plot beside it. The two panels answer different questions, and a country near the top of one can sit well down the other.
top20 <- countries %>%
filter(year == 2024, !iso3 %in% c('AIR', 'SEA')) %>%
mutate(share = 100 * total_mt / world_2024) %>%
arrange(desc(total_mt)) %>%
slice_head(n = 20) %>%
mutate(country = factor(country, levels = rev(country)))
top_value <- function(code, variable) top20[[variable]][top20$iso3 == code]
p3a <- ggplot(top20, aes(total_mt, country, fill = continent)) +
geom_col(width = 0.72) +
geom_text(aes(label = pct(share)), hjust = -0.15, size = 3.5, colour = 'grey20') +
scale_fill_manual(values = continent_colours, breaks = names(continent_colours)) +
scale_x_continuous(labels = comma, expand = expansion(mult = c(0, 0.16))) +
labs(title = 'A',
x = 'Mt CO<sub>2</sub>eq', y = NULL, fill = NULL) +
theme(panel.grid.major.y = element_blank(),
plot.margin = margin(0, 5.5, 5.5, 5.5))
p3b <- ggplot(top20, aes(per_capita_t, country, colour = continent)) +
geom_vline(xintercept = world_per_capita, linetype = 'dotted', colour = 'grey40') +
geom_segment(aes(x = 0, xend = per_capita_t, yend = country), colour = 'grey75', linewidth = 0.5) +
geom_point(size = 3) +
# A borderless white box behind each value keeps the dotted average line from
# striking through the numbers that sit beside it
geom_label(aes(label = fmt(per_capita_t, 1)), hjust = -0.3, size = 3.5, colour = 'grey20',
fill = 'white', border.colour = NA, label.padding = unit(0.1, 'lines')) +
scale_colour_manual(values = continent_colours, guide = 'none') +
scale_x_continuous(expand = expansion(mult = c(0, 0.28))) +
labs(title = 'B', x = 't CO<sub>2</sub>eq per person', y = NULL) +
theme(axis.text.y = element_blank(), panel.grid.major.y = element_blank(),
plot.margin = margin(0, 5.5, 5.5, 5.5))
p3a + p3b +
plot_layout(widths = c(2.4, 1), guides = 'collect') &
theme(legend.position = 'bottom')
Figure 3: The 20 Largest Emitters in 2024, by Total Emissions (Panel A) and per Person (Panel B). The data are from the 2025 EDGAR report (Crippa et al., 2025). Values are in million tonnes of CO2 equivalent (Panel A) and in tonnes per person (Panel B), both for 2024. The percentage at the end of each bar in Panel A is the country’s share of the world total. The dotted line in Panel B is the population-weighted world average, 6.4 t per person.
China alone emitted 29.2% of the world total in 2024, the United States 11.1% and India 8.2%, and the 20 countries together accounted for 76%. Per-capita emissions reorder the list. Saudi Arabia, Australia and Canada, ranked 9th, 14th and 10th by total, emit 22.8, 22.3 and 19.8 t per person, more than the United States at 17.3 t and China at 10.8 t. India and Pakistan sit at 3.0 and 2.4 t. The population-weighted world average is 6.4 t per person, and 13 of the 20 countries lie above it.
What the Largest Emitters Emit
Figure 4 breaks the 2024 emissions of the eight largest emitters and of the world down by EDGAR sector, summed over the four substances. The composition explains part of the per-capita differences above. Where power generation dominates, emissions follow the fuel mix of the grid, whereas a large agricultural share reflects livestock and rice paddies, which no change of energy source removes.
sector_order <- sectors_2024 %>%
filter(iso3 == 'GLOBAL TOTAL') %>%
arrange(desc(mt)) %>%
pull(sector)
sector_colours <- setNames(c('#0072B2', '#E69F00', '#D55E00', '#009E73',
'#8C5BA8', '#CC79A7', '#56B4E9', '#A08C00'), sector_order)
# White text is unreadable on the lighter fills, so each segment label takes
# whichever of white or near-black has the higher WCAG contrast with its fill.
# The two contrasts are equal at a relative luminance of about 0.18.
relative_luminance <- function(hex) {
channel <- grDevices::col2rgb(hex) / 255
channel <- ifelse(channel <= 0.04045, channel / 12.92, ((channel + 0.055) / 1.055)^2.4)
colSums(channel * c(0.2126, 0.7152, 0.0722))
}
sector_text_colours <- setNames(ifelse(relative_luminance(sector_colours) > 0.18, 'grey10', 'white'),
names(sector_colours))
top8_iso3 <- as.character(top20$iso3[1:8])
sector_shares <- sectors_2024 %>%
filter(iso3 %in% c(top8_iso3, 'GLOBAL TOTAL')) %>%
group_by(iso3) %>%
mutate(share = 100 * mt / sum(mt)) %>%
ungroup() %>%
mutate(country = ifelse(iso3 == 'GLOBAL TOTAL', 'World', country),
country = factor(country, levels = rev(c('World', as.character(top20$country[1:8])))),
sector = factor(sector, levels = sector_order))
sector_share <- function(code, sec) sector_shares$share[sector_shares$iso3 == code & sector_shares$sector == sec]
largest_sector <- sector_shares %>%
filter(iso3 != 'GLOBAL TOTAL') %>%
group_by(country) %>%
slice_max(share, n = 1) %>%
ungroup()
# Country names that take the definite article in running prose.
article <- function(x) ifelse(grepl('^(United|Netherlands|Philippines|Gambia)', x), paste('the', x), x)
# The emitters whose largest sector is `sec`, and the share it takes in each,
# so that the name and the percentage in the prose always come from one lookup.
led_by <- function(sec) filter(largest_sector, sector == sec) %>% arrange(desc(share))
countries_led_by <- function(sec)
paste(article(as.character(led_by(sec)$country)), collapse = ' and ')
share_led_by <- function(sec, digits = 0) paste(pct(led_by(sec)$share, digits), collapse = ' and ')
# Counts from zero to nine are written as words in prose, and counts from 10
# upwards as numerals (APA 7, section 6.32).
spell <- function(n) {
words <- c('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine')
ifelse(n >= 0 & n <= 9, words[pmin(pmax(n, 0), 9) + 1], as.character(n))
}
ggplot(sector_shares, aes(share, country, fill = sector)) +
geom_col(width = 0.72, colour = 'white', linewidth = 0.5, position = position_stack(reverse = TRUE)) +
# The label layer keeps every row and blanks the small ones, because
# position_stack() restacks whatever data it is given: filtering the rows out
# here instead would place each remaining label as though the omitted sectors
# did not exist.
geom_text(aes(label = ifelse(share >= 8, round(share), ''), colour = sector),
position = position_stack(vjust = 0.5, reverse = TRUE), size = 3.5,
show.legend = FALSE) +
scale_fill_manual(values = sector_colours) +
scale_colour_manual(values = sector_text_colours, guide = 'none') +
scale_x_continuous(labels = function(x) paste0(x, '%'), expand = expansion(mult = c(0, 0.02))) +
guides(fill = guide_legend(nrow = 2, byrow = TRUE)) +
labs(x = NULL, y = NULL, fill = NULL) +
theme(panel.grid.major.y = element_blank(), legend.position = 'bottom',
plot.margin = margin(5.5, 14, 5.5, 5.5))
Figure 4: The 2024 Emissions of the World and the Eight Largest Emitters, by EDGAR Sector. The data are from the 2025 EDGAR report (Crippa et al., 2025). Values are percentages of each emitter’s total, from the sheet GHG_by_sector_and_country for 2024, summed over substances. Labels are shown for shares of 8% or more.
Power generation is the largest sector for the world as a whole, at 29% of emissions, and for five of the eight countries, with the largest shares in China (43%) and Japan (41%). Transport comes first in the United States only, at 29%, and agriculture in Brazil only, at 49%. Fuel exploitation, the extraction and processing of coal, oil and gas, leads in Iran only, at 22%. That last sector also accounts for 24% of Indonesia’s emissions and 19% of Russia’s, against 11% for the world. These compositions matter for the comparison that follows, because methane from livestock and fugitive emissions from fuel extraction do not scale with a country’s development in the way that power and transport do.
Emissions Against Human Development
Per-capita emissions rise with development, and the model in this section measures how far each country departs from the level that its development would predict. Figure 5 plots per-capita emissions on a logarithmic axis against the HDI for 2023. The emissions are averaged over 2015 to 2024 to smooth out the pandemic dip and other year-to-year variation, and each marker is scaled to the country’s 2024 population and coloured by continent. The chunk below fits the linear model log10(per_capita_t) ~ hdi_2023, whose line is drawn through the points, and stores each country’s residual, the vertical distance from the line in log10 units, for the map in Figure 6 and the tables after it.
hdi_data <- countries %>%
filter(!iso3 %in% c('AIR', 'SEA'), year >= 2015) %>%
group_by(iso3, country, continent) %>%
summarise(per_capita_t = mean(per_capita_t), .groups = 'drop') %>%
left_join(countries %>% filter(year == 2024) %>% select(iso3, total_mt, population),
by = 'iso3') %>%
# Jointly reported territories take the HDI of the member whose code they carry
mutate(hdi_iso3 = ifelse(iso3 == 'SCG', 'SRB', iso3)) %>%
left_join(hdi %>% select(hdi_iso3 = iso3, hdi_2023, hdicode), by = 'hdi_iso3')
n_no_hdi <- sum(is.na(hdi_data$hdi_2023))
hdi_data <- hdi_data %>% filter(!is.na(hdi_2023))
hdi_model <- lm(log10(per_capita_t) ~ hdi_2023, data = hdi_data)
hdi_data <- hdi_data %>%
mutate(fitted = fitted(hdi_model),
residual = residuals(hdi_model),
expected_t = 10^fitted,
ratio = 10^residual)
hdi_slope <- coef(hdi_model)[['hdi_2023']]
hdi_r2 <- summary(hdi_model)$r.squared
hdi_n <- nobs(hdi_model)
ratio_of <- function(code) hdi_data$ratio[hdi_data$iso3 == code]Figure 5 is a ggplot converted with ggplotly(), so hovering over a marker shows the country, its HDI, its per-capita emissions, its 2024 total and its ratio to the fitted line. Because the markers are scaled to population, the countries that dominate the world total also stand out on the plot.
fit_line <- tibble(hdi_2023 = seq(min(hdi_data$hdi_2023), max(hdi_data$hdi_2023), length.out = 50)) %>%
mutate(per_capita_t = 10^predict(hdi_model, newdata = .))
hdi_data <- hdi_data %>%
mutate(tooltip = paste0('<b>', country, '</b>',
'<br>HDI 2023: ', fmt(hdi_2023, 3),
'<br>Per capita, 2015 to 2024: ', fmt(per_capita_t, 1), ' t',
'<br>Total, 2024: ', fmt(total_mt), ' Mt',
'<br>', fmt(ratio, 2), ' times the level expected at its HDI'))
p5 <- ggplot(hdi_data, aes(hdi_2023, per_capita_t)) +
geom_line(data = fit_line, colour = 'grey40', linewidth = 0.8) +
geom_point(aes(size = population, colour = continent, text = tooltip), alpha = 0.7) +
scale_y_log10(labels = comma) +
scale_size_area(max_size = 20, guide = 'none') +
scale_colour_manual(values = continent_colours, name = NULL) +
# On two lines, because on one the title is longer than the plot is tall
labs(x = 'Human Development Index, 2023',
y = 'Per-capita emissions, 2015 to 2024\n(t CO<sub>2</sub>eq, log scale)') +
# A complete theme, so that ggplotly does not merge the ggtext elements of the global theme
theme_minimal(base_size = 13)
fig5 <- ggplotly(p5, tooltip = 'text') %>%
layout(legend = list(orientation = 'h', y = -0.18, x = 0.5, xanchor = 'center',
itemsizing = 'constant'),
margin = list(l = 80, r = 20, b = 70, t = 20),
hoverlabel = list(align = 'left')) %>%
# The full plotly bundle is about 3.4 MB, most of it chart types this figure
# does not use. partial_bundle() substitutes the smallest build that still
# covers the traces present. It fetches that build from the plotly CDN, so it
# falls back to the full bundle when the render has no network access.
lighter_bundle()
# knitr would otherwise fix the widget at fig.width x dpi, 768 px, which is
# wider than the 720 px text column and runs off the side of a phone screen.
# At 100% the widget follows its column and plotly redraws to that width.
fig5$width <- '100%'
# At the size that suits the desktop column, the x-axis title is wider than the
# plot on a 360 px phone and loses its last characters, so narrow screens get
# smaller axis titles
htmlwidgets::onRender(fig5, 'function(el) {
if (el.offsetWidth < 400) {
Plotly.relayout(el, {"xaxis.title.font.size": 15, "yaxis.title.font.size": 15});
}
}') %>%
# knitr applies fig.alt to static images only, so the widget carries the
# chunk's alt text in a paragraph that screen readers announce and the
# page hides (.sr-only is Bootstrap's)
htmlwidgets::prependContent(tags$p(class = 'sr-only', knitr::opts_current$get('fig.alt')))Interactive scatterplot of per-capita greenhouse gas emissions, averaged over 2015 to 2024 on a logarithmic axis, against the Human Development Index for 2023, with one marker per country sized by population and coloured by continent, and a fitted straight line rising from left to right.
Figure 5: Per-Capita Emissions Against the Human Development Index, With the Fitted Line. The data are from the 2025 EDGAR report (Crippa et al., 2025) and the 2025 Human Development Report (United Nations Development Programme, 2025). Values are in tonnes of CO2 equivalent per person per year, averaged over 2015 to 2024. Marker area is proportional to 2024 population.
The model is fitted to the 183 countries with both an HDI and emissions. The 25 territories without an HDI are left out: mostly dependencies and overseas territories such as Puerto Rico, Greenland and Macao, plus Taiwan, North Korea and Western Sahara. The slope is 2.12 log10 units per unit of HDI, so each additional 0.1 of HDI multiplies expected per-capita emissions by 1.63. The model explains 58% of the variance in log emissions (R2 = 0.58, n = 183). Of the countries, 78% lie within a factor of 2 of the line. Qatar and Kuwait emit 6.2 and 5.0 times the level expected at their HDI, Russia 2.5, and the United States 1.7. Sweden and Switzerland, by comparison, emit 0.45 and 0.42 times theirs. China sits at 1.8 and India at 0.85.
The residual describes how a country compares with others at the same HDI, and four caveats apply to any reading of it. The HDI includes gross national income, so it partly encodes the energy use that it is meant to contextualise, and the line therefore absorbs some of the effect of wealth that a reader might want to see. EDGAR is production-based, so the emissions embodied in goods that a country imports are attributed to the exporter, which favours importers of manufactured goods in this comparison and counts against exporters such as China. The exclusion of land use, land-use change and forestry matters for countries such as Brazil and Indonesia. Finally, the model is a single straight line through 183 points. Oil and gas exporters, pastoral economies, and small states whose per-capita figure depends on one plant or fleet all pull on it in ways that a fuller treatment would handle differently.
Mapping the Residuals
Figure 6 maps the residuals of the model on a diverging scale centred on zero, red where a country emits more than expected at its HDI and blue where it emits less. Hovering shows the residual in log10 units and the ratio 10residual, which is easier to read. Countries without an HDI or without emissions data are shown in light grey. The members of a territory that EDGAR reports jointly carry the value of that territory, and their hover text says so. Sudan and South Sudan, for example, both show the per-capita figure of the combined territory, compared with the HDI of Sudan.
# Natural Earth's medium geometry is about 3 MB of coordinates in the widget
# below. Simplifying it would cut that substantially, but every tolerance-based
# method available here damages the map: whole-feature st_simplify() silently
# drops 123 of the 1,620 polygon parts, among them 16 of Kiribati's 19 islands,
# and the per-part variants that keep them leave invalid geometries behind. A
# topology-aware simplifier such as rmapshaper::ms_simplify(keep_shapes = TRUE)
# is the tool for this; until the geometry can be checked under one, the map
# ships at full fidelity.
world_sf <- ne_countries(scale = 'medium', returnclass = 'sf') %>%
# France and Norway carry -99 as iso_a3 in Natural Earth, and iso_a3_eh holds
# their code. The recode is limited to those two, because iso_a3_eh also gives
# AUS to the Indian Ocean Territories and to the Ashmore and Cartier Islands,
# which would otherwise take on Australia's residual.
mutate(iso_a3 = ifelse(iso_a3 == '-99' & iso_a3_eh %in% c('FRA', 'NOR'), iso_a3_eh, iso_a3)) %>%
filter(iso_a3 != 'ATA') %>%
select(iso_a3, name_long) %>%
mutate(edgar_iso3 = coalesce(combined$iso3[match(iso_a3, combined$member_iso3)], iso_a3),
jointly = iso_a3 %in% combined$member_iso3) %>%
left_join(hdi_data %>% select(iso3, country, hdi_2023, per_capita_t, expected_t, residual, ratio),
by = c('edgar_iso3' = 'iso3'))
residual_limit <- max(abs(world_sf$residual), na.rm = TRUE)
residual_palette <- colorNumeric(rev(brewer.pal(11, 'RdBu')),
domain = c(-residual_limit, residual_limit),
na.color = '#d9d9d9')
map_labels <- with(world_sf, ifelse(
is.na(residual),
paste0('<b>', name_long, '</b><br>no data'),
paste0('<b>', name_long, '</b>',
ifelse(jointly, paste0('<br>Reported by EDGAR as ', country), ''),
'<br>HDI 2023: ', fmt(hdi_2023, 3),
'<br>Per capita, 2015 to 2024: ', fmt(per_capita_t, 1), ' t (expected ', fmt(expected_t, 1), ' t)',
'<br>Residual: ', formatC(residual, format = 'f', digits = 2, flag = '+'), ' log10 units',
'<br>', fmt(ratio, 2), ' times the level expected at its HDI')
# unname(): with() carries the sf row names onto the result, and htmlwidgets
# rejects a partially named list when it scans the widget for JS() markers
)) %>% unname() %>% lapply(HTML)
# A fixed zoom shows a 512 px wide world, which a phone crops on both sides, so
# the map instead fits the inhabited latitudes to its container at a fractional
# zoom. On a narrow screen the fitted world is only about 240 px tall, so the
# container is shorter there and the world is fitted above the legend, which
# would otherwise cover South America.
leaflet(world_sf, width = '100%', height = 520, elementId = 'residual-map',
options = leafletOptions(minZoom = 0, maxZoom = 6, zoomSnap = 0.1, zoomDelta = 0.5,
worldCopyJump = TRUE)) %>%
addPolygons(fillColor = ~ residual_palette(residual), fillOpacity = 0.9,
color = 'white', weight = 0.6, smoothFactor = 0.3,
label = map_labels,
highlightOptions = highlightOptions(weight = 2, color = '#333333', bringToFront = TRUE)) %>%
# One legend box, with the no-data swatch under the scale, covers less of the map
addLegend(pal = residual_palette, values = c(-residual_limit, residual_limit, NA),
na.label = 'no data', title = 'Residual (log10)', position = 'bottomleft',
opacity = 0.9) %>%
fitBounds(lng1 = -170, lat1 = -56, lng2 = 180, lat2 = 84) %>%
htmlwidgets::onRender(
'function(el, x) {
if (window.matchMedia("(max-width: 600px)").matches) {
this.fitBounds([[-56, -170], [84, 180]], {paddingBottomRight: [0, 150], animate: false});
}
}') %>%
# leaflet floats the colour scale and its labels without clearing the float,
# so the no-data row would otherwise sit beside the scale instead of under it.
# The site's dark theme gives every link near-white text, which would leave the
# white zoom buttons and the attribution blank, so the map's links stay dark.
htmlwidgets::prependContent(tags$style(HTML(
'.leaflet-container { background: #f4f6f8; }
#residual-map .info.legend > div:last-child { clear: both; padding-top: 4px; }
#residual-map .leaflet-control a { color: #333 !important; -webkit-text-fill-color: #333 !important;
text-decoration: none !important; }
@media (max-width: 600px) { #residual-map { height: 410px !important; } }'))) %>%
# As for Figure 5, the alt text reaches screen readers in a hidden paragraph
htmlwidgets::prependContent(tags$p(class = 'sr-only', knitr::opts_current$get('fig.alt')))Interactive world map in which each country is coloured by its residual from the model of per-capita emissions against the Human Development Index, red for higher than expected and blue for lower than expected, with countries lacking data in light grey.
Figure 6: The Residual of Each Country From the Fitted Line, Mapped. The data are from the 2025 EDGAR report (Crippa et al., 2025) and the 2025 Human Development Report (United Nations Development Programme, 2025). The boundaries are from Natural Earth (n.d.), read through the rnaturalearth and rnaturalearthdata packages (Massicotte & South, 2026; South et al., 2024). Values are residuals of log10 per-capita emissions (2015 to 2024 mean) from the linear model on the 2023 HDI. The ratio of actual to expected emissions is 10residual. Boundaries carry no political meaning.
The largest positive residual belongs to Palau, at 11.6 times its expected level, and the largest negative one to Saint Vincent and the Grenadines, at 0.27 times. Tables 1 and 2 list the 10 countries at each end. Oil and gas exporters fill most of the positive end, together with Palau, a small state of 23 thousand inhabitants by EDGAR’s count, whose per-capita figure is set by one power plant or one fleet. Two pastoral economies also appear there: in Mongolia, fuel extraction and livestock account for 73% of emissions, and in Chad, agriculture accounts for 90%. Methane from herds enters the total in full, and the model expects little of it at a low HDI, so a country whose emissions are mostly livestock lands above the line. Neither the small-state route nor the pastoral one makes these countries large emitters. Chad emits 101.2 Mt a year and Palau 1.5 Mt, against 51,847 Mt for all countries together, so the ratio ranks departures from the model and not contributions to the problem. The total column is in the tables to keep that in view, and it is worth reading before the ratio. The negative end mixes small island states with countries of South Asia and Sub-Saharan Africa whose emissions are low for their HDI. At the top of the HDI range, it also holds Switzerland, whose electricity comes largely from hydropower and nuclear plants and which EDGAR reports together with Liechtenstein.
# One table for each end of the residual, rather than one table of twenty rows
# split only by a word in its caption. The two ends are opposite findings, and
# running them together invited the reader to scan the whole list as a ranking.
residual_columns <- function(d) {
d %>% transmute(Country = country, Continent = continent,
`HDI 2023` = fmt(hdi_2023, 3), `Total 2024 (Mt)` = fmt(total_mt, 1),
`Per capita (t)` = fmt(per_capita_t, 2),
`Expected (t)` = fmt(expected_t, 2), Ratio = fmt(ratio, 2))
}
hdi_data %>%
slice_max(residual, n = 10) %>%
residual_columns() %>%
kable(align = 'llrrrrr',
caption = paste('The 10 Countries Emitting Most Above the Level Expected at Their HDI.',
'Total is the 2024 national figure in Mt CO<sub>2</sub>eq, per capita is the',
'2015 to 2024 mean in t CO<sub>2</sub>eq per person, expected is the value fitted',
'from the HDI and ratio is the actual value over the expected one. The ratio measures distance from',
'the model, not size: at a low HDI the model expects very little, so a',
'small total can produce a large ratio.'))| Country | Continent | HDI 2023 | Total 2024 (Mt) | Per capita (t) | Expected (t) | Ratio |
|---|---|---|---|---|---|---|
| Palau | Oceania | 0.786 | 1.5 | 60.73 | 5.23 | 11.61 |
| Qatar | Asia | 0.886 | 162.7 | 52.69 | 8.53 | 6.18 |
| Chad | Africa | 0.416 | 101.2 | 5.00 | 0.86 | 5.84 |
| Mongolia | Asia | 0.747 | 104.4 | 23.21 | 4.32 | 5.37 |
| Kuwait | Asia | 0.852 | 173.3 | 35.97 | 7.22 | 4.98 |
| Trinidad and Tobago | America | 0.807 | 33.6 | 27.38 | 5.80 | 4.72 |
| Bahrain | Asia | 0.899 | 64.4 | 38.15 | 9.09 | 4.20 |
| Turkmenistan | Asia | 0.764 | 114.8 | 19.02 | 4.70 | 4.05 |
| Brunei | Asia | 0.837 | 11.9 | 25.28 | 6.71 | 3.77 |
| Libya | Africa | 0.721 | 98.2 | 12.77 | 3.81 | 3.36 |
hdi_data %>%
slice_min(residual, n = 10) %>%
arrange(desc(residual)) %>%
residual_columns() %>%
kable(align = 'llrrrrr',
caption = paste('The 10 Countries Emitting Most Below the Level Expected at Their HDI.',
'The columns are as in Table 1.'))| Country | Continent | HDI 2023 | Total 2024 (Mt) | Per capita (t) | Expected (t) | Ratio |
|---|---|---|---|---|---|---|
| Switzerland and Liechtenstein | Europe | 0.970 | 43.0 | 5.40 | 12.86 | 0.42 |
| Dominica | America | 0.761 | 0.2 | 1.94 | 4.63 | 0.42 |
| Comoros | Africa | 0.603 | 0.9 | 0.89 | 2.14 | 0.42 |
| Democratic Republic of the Congo | Africa | 0.522 | 58.5 | 0.56 | 1.44 | 0.39 |
| Bangladesh | Asia | 0.685 | 220.8 | 1.21 | 3.19 | 0.38 |
| Grenada | America | 0.791 | 0.2 | 1.92 | 5.36 | 0.36 |
| Sri Lanka | Asia | 0.776 | 37.2 | 1.68 | 4.98 | 0.34 |
| Kiribati | Oceania | 0.644 | 0.1 | 0.87 | 2.61 | 0.33 |
| Rwanda | Africa | 0.578 | 8.3 | 0.59 | 1.89 | 0.31 |
| Saint Vincent and the Grenadines | America | 0.798 | 0.2 | 1.52 | 5.55 | 0.27 |
Whereas a few recurring causes account for Table 1, Table 2 has no single cause. Its countries span HDI values from 0.52 to 0.97 and have little in common beyond their distance below the line.
Discussion
Read together, the figures give a contextual account of emissions. Total emissions describe the size of each country’s annual contribution to the global total, and per-capita values describe the average emissions of its residents. Sector shares suggest where mitigation could begin, and the HDI residuals show departures from a broad relationship between development and emissions. These are complementary summaries, and their interpretation depends on the boundary choices documented above, especially the exclusion of land use, land-use change and forestry, and the treatment of international transport.
How the Figures Were Produced
The EDGAR sheets store years as columns, so each was reshaped to one row per territory and year before the totals and per-capita figures were joined by country code and year. Population-weighted means and population shares were computed from the derived population, and the world population is the sum over the territories that report a per-capita figure. Continents, income groups and the HDI were joined by ISO 3166 alpha-3 code. A few exceptions were handled by hand. Taiwan has no region in the ISO 3166 data set and was assigned to Asia. EDGAR has no row for Kosovo and files Curaçao under the code of the former Netherlands Antilles. The jointly reported territories keep the code under which EDGAR reports them. The model of Figure 5 is an ordinary least-squares regression of log10 per-capita emissions, averaged over 2015 to 2024, on the HDI for 2023, fitted to 183 countries. The document was rendered with R version 4.6.1 (2026-06-24 ucrt) and the packages dplyr 1.2.1, tidyr 1.3.2, ggplot2 4.0.3, patchwork 1.3.2, scales 1.4.0, ggrepel 0.9.8, ggtext 0.1.2, plotly 4.12.1, htmltools 0.5.9, leaflet 2.2.3, sf 1.1.2, rnaturalearth 1.2.0, rnaturalearthdata 1.0.0, RColorBrewer 1.1.3, knitr 1.51 and rmarkdown 2.31.
References
Crippa, M., Guizzardi, D., Pagani, F., Banja, M., Muntean, M., Schaaf, E., Quadrelli, R., Risquez Martin, A., Taghavi-Moharamli, P., Köykkä, J., Grassi, G., Rossi, S., Melo, J., Oom, D., Branco, A., Suárez-Moreno, M., Sedano, F., San-Miguel, J., Manca, G., … Pekar, F. (2025). GHG emissions of all world countries: 2025 report (EUR 40443). Publications Office of the European Union. https://doi.org/10.2760/9816914
Duncalfe, L. (2024). ISO-3166-Countries-with-Regional-Codes (Version 10.0) [Data set]. GitHub. https://github.com/lukes/ISO-3166-Countries-with-Regional-Codes
Massicotte, P., & South, A. (2026). rnaturalearth: World map data from Natural Earth (Version 1.2.0) [Computer software]. Comprehensive R Archive Network (CRAN). https://doi.org/10.32614/CRAN.package.rnaturalearth
Myhre, G., Shindell, D., Bréon, F.-M., Collins, W., Fuglestvedt, J., Huang, J., Koch, D., Lamarque, J.-F., Lee, D., Mendoza, B., Nakajima, T., Robock, A., Stephens, G., Takemura, T., & Zhang, H. (2013). Anthropogenic and natural radiative forcing. In T. F. Stocker, D. Qin, G.-K. Plattner, M. Tignor, S. K. Allen, J. Boschung, A. Nauels, Y. Xia, V. Bex, & P. M. Midgley (Eds.), Climate change 2013: The physical science basis. Contribution of Working Group I to the Fifth Assessment Report of the Intergovernmental Panel on Climate Change (pp. 659–740). Cambridge University Press. https://doi.org/10.1017/CBO9781107415324.018
Natural Earth. (n.d.). Admin 0 – countries (1:50m cultural vectors) [Data set]. https://www.naturalearthdata.com/downloads/50m-cultural-vectors/50m-admin-0-countries-2/
Okabe, M., & Ito, K. (2008). Color universal design (CUD): How to make figures and presentations that are friendly to colorblind people. Jfly. https://jfly.uni-koeln.de/color/
South, A., Schramm, M., & Massicotte, P. (2024). rnaturalearthdata: World vector map data from Natural Earth used in ‘rnaturalearth’ (Version 1.0.0) [Computer software]. Comprehensive R Archive Network (CRAN). https://doi.org/10.32614/CRAN.package.rnaturalearthdata
United Nations Development Programme. (2025). Human development report 2025: A matter of choice: People and possibilities in the age of AI. United Nations. https://doi.org/10.18356/9789211542639
World Bank. (2025). World Bank country and lending groups: Current classification by income (Version 2025-07-02) [Data set]. https://datacatalogfiles.worldbank.org/ddh-published/0037712/DR0095333/CLASS_2025_07_02.xlsx
Comments are provided by Disqus and are not loaded automatically. Loading them connects your browser to Disqus, which may use cookies and process data under its privacy policy. See this site's privacy notice for details.