---
title: "countryatlas: Joining World Data to Maps on the ISO Spine"
author: |
  Youzhi Yu
  <span style="font-size: 0.8em;">University of Chicago</span>
output:
  rmarkdown::html_vignette:
    toc: true
    toc_depth: 3
    number_sections: true
vignette: >
  %\VignetteIndexEntry{countryatlas: Joining World Data to Maps on the ISO Spine}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  message = FALSE,
  warning = FALSE,
  fig.width = 7,
  fig.height = 4,
  fig.align = "center",
  dpi = 96
)
library(countryatlas)
library(ggplot2)
library(dplyr)
```

# Abstract {-}

Joining country-level data across independent sources is deceptively hard: the
same country is spelled `"US"`, `"U.S."`, `"United States"` and
`"United States of America"`, and a naïve join treats them as different
entities. **countryatlas** resolves this friction by adopting ISO 3166 codes as
a universal join key and by stitching together three otherwise disjoint
resources — map geometry, World Bank development indicators, and a comprehensive
country-code crosswalk — into a single, map-ready table. This vignette presents
the package's design philosophy, its core functional vocabulary, and worked
examples spanning data assembly, the join engine, diagnostics, reference data,
analysis helpers and the grammar of honest cartographic displays — the
companion vignettes take up the remaining map types, projections and query
interface in detail. All examples run offline against a bundled data snapshot.

# Introduction

The package rests on a single conviction: *if a task does not make it easier to
get country data onto a map — or to make that map honest — it does not belong
here*. Concretely, three packages are combined:

* **`ggplot2::map_data("world")`** (or Natural Earth via `sf`) supplies polygon
  geometry, i.e. *where* countries are;
* **`WDI`** supplies World Bank indicators, i.e. *what is true* about them;
* **`countrycode`** supplies the crosswalk of ISO codes, continents and regions
  that makes a reliable join possible.

Three design commitments follow. First, **the happy path is one call**:
`world_data(2020)` returns a tibble ready to map. Second, **the ISO code is the
spine**: every function speaks `iso3c`/`iso2c` internally and exposes it, so
anything the package produces joins to anything else — and to the user's own
data. Third, **no country is lost silently**: entities that map backends spell
idiosyncratically are *matched* through a curated override table rather than
dropped, and unmatched values are reported explicitly.

To keep every example reproducible without a network connection, this vignette
uses the bundled `world_snapshot` dataset, a curated set of indicators for one
recent year.

```{r}
snapshot <- world_snapshot$countries
dplyr::glimpse(snapshot)
```

# Core data assembly

## `world_data()`

The headline function is generalised but backward-compatible. The classic call
returns the polygon-backed, enriched tibble exactly as before:

```{r eval = FALSE}
# Live World Bank API call (not evaluated here to keep the vignette offline):
world_data(2020)
world_data(
  2020,
  indicator = c(life_exp = "SP.DYN.LE00.IN", co2 = "EN.GHG.CO2.PC.CE.AR5"),
  geometry  = "sf",
  region    = "Africa"
)
```

The `indicator` argument accepts one or many WDI codes; a **named** vector
drives clean column names. A range of years (`2000:2020`) yields a panel keyed
on `iso3c` and `year`. The `geometry` argument switches between the classic
`"polygon"` backend, a modern `"sf"` backend with real projections, and
`"none"` for pure analysis.

## `country_data()` and `attach_geometry()`

For analysis you usually want one tidy row per country, not ~99,000 polygon
vertices. `country_data()` provides exactly that, and geometry is attached only
at draw time:

```{r}
mapdf <- attach_geometry(snapshot, geometry = "polygon")
dim(mapdf)
```

# Visualising: the choropleth and beyond

## One-line choropleths

`world_map()` encapsulates the plotting boilerplate and offers several honest
styles. A continuous fill on a skewed indicator hides most of the variation, so
binned and quantile styles are first-class:

```{r}
world_map(mapdf, gdp_per_capita, style = "quantile",
          title = "GDP per capita (quantile bins)")
```

```{r}
world_map(mapdf, continent, style = "categorical")
```

## Proportional-symbol maps

Totals (population, total emissions) are misrepresented by a choropleth because
large values hide in small countries. A bubble map at country centroids is the
right idiom:

```{r}
bubble_map(snapshot, population)
```

## Equal-area tile grids

Tiny states vanish on a geographic map. An equal-area tile grid gives every
country the same visual weight:

```{r fig.height = 5}
tile_map(snapshot, life_expectancy)
```

## Flow maps

Origin–destination data (trade, migration, flights) is drawn as great-circle
arcs, with both endpoints resolved to centroids automatically:

```{r}
flows <- data.frame(
  from   = c("China", "Germany", "Brazil", "India"),
  to     = c("United States", "France", "Japan", "United Kingdom"),
  weight = c(500, 200, 150, 120)
)
flow_map(flows, from, to, weight)
```

# The join engine

The package's mission, exposed for the reader's own data. Given a frame keyed on
messy names, `standardize_country()` attaches ISO codes and classifications:

```{r}
messy <- data.frame(
  nation = c("U.S.", "S. Korea", "Czechia", "Kosovo", "Cote d'Ivoire"),
  value  = c(10, 8, 6, 4, 7)
)
standardize_country(messy, nation, warn = FALSE)
```

`join_world()` goes one step further — auto-detecting the country column,
standardising it and attaching geometry — while `country_join()` reconciles two
independent tables that each key on country names:

```{r}
left  <- data.frame(country = c("Czechia", "South Korea"), gdp = c(1, 2))
right <- data.frame(nation  = c("Czech Republic", "Korea, Rep."), pop = c(10, 51))
country_join(left, right, country, nation)
```

# Diagnostics: never lose a country silently

`check_country_match()` is a pre-flight report; `country_overrides()` is the
curated match table that replaces the old drop-list; and `audit_coverage()`
reports missingness before a half-empty map is published.

```{r}
check_country_match(c("USA", "Cote d'Ivoire", "Yugoslavia", "Wakanda"))
```

`repair_country_names()` acts on that report: it substitutes the closest known
country name for confident misses only, and attaches a record of every change:

```{r}
repair_country_names(c("Brzil", "Germny", "United States"), verbose = FALSE)
```

```{r}
audit_coverage(snapshot)$na_rates
```

The entities the previous version dropped — Kosovo, Micronesia, the Virgin
Islands and a dozen others — are now matched:

```{r}
dropped <- c("Kosovo", "Micronesia", "Virgin Islands", "Canary Islands",
             "Saint Martin")
standardize_country(data.frame(region = dropped), region, warn = FALSE)
```

Dissolved entities get first-class treatment too. The `historical` column of
`check_country_match()` flags them — including `"USSR"`, which countrycode
silently resolves to Russia's `RUS` (so Soviet-era data becomes Russian data
without a warning) — and `dissolve_country()` expands them to their successor
states via the curated `historical_codes` crosswalk:

```{r}
check_country_match(c("USSR", "Czechoslovakia"))
dissolve_country("Yugoslavia")
```

# Reference data and code translation

`convert_country()` exposes the full countrycode vocabulary with first-class
shortcuts for the high-value schemes:

```{r}
convert_country(c("Japan", "Brazil", "Germany"), to = "flag")
convert_country(c("Japan", "Brazil", "Germany"), to = "currency")
```

Country-group membership is a curated, dated table:

```{r}
country_groups("G7")
in_group(c("France", "United States", "Japan", "Brazil"), "EU")
```

The whole `countrycode` codelist is exposed as a tidy, pipeable lookup with
`country_codes()`, and the World Bank indicator catalogue is searchable offline
with `wdi_search()`:

```{r}
head(country_codes(c("continent", "currency")))
head(wdi_search("renewable energy"), 3)
```

The package also bundles `country_meta` (static per-country attributes),
`common_indicators` (a friendly indicator catalogue), `country_groups_tbl`,
`world_tiles` and `historical_codes` (the dissolved-entity crosswalk used
above).

# Analysis helpers

Small, in-spirit transforms that keep an analysis from leaving the package
mid-pipeline. `per_capita()` disarms the commonest footgun in this data — *is
this map just a population map?* — by normalising a total before it is plotted.
Supply the denominator, or omit it and the relevant years of `SP.POP.TOTL` are
fetched for you:

```{r}
emissions <- data.frame(
  iso3c = c("USA", "CHN", "IND"),
  co2   = c(4.7e6, 1.1e7, 2.7e6),      # total kt
  pop   = c(331e6, 1412e6, 1408e6)
)
per_capita(emissions, co2, pop)
```

```{r}
snapshot |>
  rank_countries(gdp_per_capita) |>
  filter(rank <= 5) |>
  select(country, gdp_per_capita, rank, percentile)
```

```{r}
snapshot |>
  aggregate_regions(population, by = "region", fun = "sum")
```

For panel data, `growth_rate()` (year-on-year or CAGR), `index_to()` (rebase a
series so the base year = 100) and `share_of_world()` (share of the year's
world total) cover the standard comparative moves, each computed per country:

```{r}
panel <- data.frame(
  iso3c = rep(c("USA", "CHN"), each = 3),
  year  = rep(2019:2021, 2),
  gdp   = c(100, 104, 109, 60, 66, 73)
)
panel |>
  growth_rate(gdp) |>
  index_to(gdp, base_year = 2019)
```

And `complete_years()` fills the panel gaps that would otherwise make an
animation flicker or a join silently drop years — by grid completion,
carry-forward or linear interpolation (`lag_by_country()` and
`diff_by_country()` round out the panel toolkit):

```{r}
patchy <- data.frame(iso3c = "USA", year = c(2019L, 2021L), gdp = c(100, 110))
complete_years(patchy, 2019:2021, method = "linear")
```

## Inequality, correlation and convergence

Three questions this data is constantly asked: *how unequal is the world*,
*what moves together*, and *are poor countries catching up?* `gini()` and
`theil()` measure inequality across countries — weight by population and they
describe inequality between people; `theil()` decomposes exactly into
between/within components:

```{r}
gini(snapshot$gdp_per_capita, weights = snapshot$population)
theil(snapshot$gdp_per_capita, weights = snapshot$population,
      groups = snapshot$continent)
```

`correlate_indicators()` screens indicator pairs (pairwise-complete, with the
per-pair `n` reported so a correlation computed on 12 countries cannot
masquerade as a world fact):

```{r}
correlate_indicators(snapshot)
```

For panels, `beta_convergence()` runs the classic growth-on-initial-level
regression (returning the implied convergence speed and half-life) and
`sigma_convergence()` tracks whether cross-country dispersion is actually
narrowing. And on the spatial side, `morans_i()` measures whether neighbouring
countries have similar values, using the package's own `country_borders()`
adjacency — no `spdep` needed.

# Performance and offline use

World Bank fetches are memoised with an optional on-disk cache, and multiple
indicators are fetched in parallel where the platform supports forking. The
bundled `world_snapshot` makes every example here run without the network. The
cache can be cleared with `clear_wdi_cache()`.

# Conclusion

`countryatlas` keeps its original soul — ISO codes as the universal join key,
one call to a map-ready table — and extends it into a complete toolkit: any
indicator and any year span, a modern `sf` backend, an exposed join engine for
the user's own data, honest diagnostics, curated reference data, analysis
helpers, and a full vocabulary of projected, area-honest maps.

# Session information {-}

```{r}
sessionInfo()
```
