countryatlas 2.0.0
A major release that wires countryatlas into the database-rendering world via ‘ggsql’, widens the map vocabulary, and fixes several correctness issues found by auditing 1.0.0. The version is bumped to 2.0.0 because the bug fixes change the output of world_map() (quantile binning), bubble_map() / flow_map() (de-duplicated symbols), geom_country_labels() (label placement) and convert_country() (override-only entities) — code that depended on the old behaviour may see different maps or values.
New: database-side rendering with ggsql
-
as_ggsql_source()exports a curated, ISO-reconciled, WDI-joined table (withsfgeometry WKB-encoded) as a ggsql source — a DuckDB connection, a Parquet file, or a nanoarrow stream. countryatlas does the reconciliation ggsql’s static bundled world can’t; ggsql does the database push-down and Vega-Lite output countryatlas doesn’t. -
world_query()emits aggsqlspatial query (VISUALISE … DRAW spatial PROJECT TO … SCALE … LABEL …) — a dependency-free string builder. -
interactive_map(engine = "ggsql")registers the data and renders the map in DuckDB, returning a Vega-Lite widget. -
ggsql,duckdb,DBIandnanoarroware optionalSuggests. See the new countryatlas and ggsql vignette.
New: maps, projections and helpers
-
globe_map()— an orthographic globe choropleth, withbackend = "sf"(smoothest limb) orbackend = "polygon"(needs onlymaps+mapproj). -
spin_globe()— a rotating-globe animated GIF (oneglobe_map()frame per central longitude, assembled withgifskiormagick). -
facet_map()— small-multiple choropleths (the static counterpart toanimate_world()). -
wdj_crs()gains eight projections (mercator,winkel_tripel,eckert4,gall_peters,orthographic,azimuthal_equal_area,north_polar,south_polar);world_map()/world_geometry()accept them all. -
locate_country()— point-in-polygon lookup tagginglon/latwithiso3c. -
repair_country_names()— the “act on it” companion tocheck_country_match(): auto-applies confident string-distance fixes. -
country_join_all()— reduce-join many messy country tables on the ISO spine. -
growth_rate(),index_to(),share_of_world()— panel analysis helpers. -
country_overrides()— preferred name forwdj_overrides()(kept as an alias) after the rename to countryatlas. -
country_groups_tblgainsMercosur,GCC,NordicandVisegrad. -
country_borders()— a tidy adjacency edge list built from polygon topology (sf::st_touches()), withneighbors()for a vectorised per-country lookup. -
distance_between()— great-circle (haversine) distance between two countries’ centroids; needs neithersfnor the network. -
dorling_map()— the Dorling cartogram promoted to a first-class verb, withk/itermaxtuning;cartogram_map()itself gains...passthrough to the underlyingcartogram::cartogram_*()call.
New: historical entities, inequality and spatial statistics
-
historical_codes— a curated, dated crosswalk of dissolved entities (Soviet Union, Yugoslavia, Czechoslovakia, East Germany, Netherlands Antilles, North/South Yemen, pre-2011 Sudan, United Arab Republic, Tanganyika/Zanzibar, North/South Vietnam, Serbia and Montenegro) to their successor states, with retired ISO codes where they existed. Kosovo is included among the Yugoslav successors on a territory basis (documented). -
dissolve_country()— resolve a mixed vector of historical and modern names to successoriso3crows (one-to-many, dated); modern names pass through as single rows, so a whole messy column pipes in unchanged. -
check_country_match()gains ahistoricalcolumn. It flags dissolved entities even when countrycode “matches” them — the headline case is"USSR", which countrycode silently resolves to Russia’sRUS, so Soviet-era data becomes Russian data with no warning. -
correlate_indicators()— pairwise indicator correlations on the spine (pearson/spearman, pairwise-complete, per-pairn), tidy long output. -
beta_convergence()/sigma_convergence()— the two standard convergence diagnostics: the growth-on-initial-level regression (with implied convergence speed and half-life) and per-year cross-country dispersion. -
gini()andtheil()— inequality across countries, population-weightable;theil()decomposes exactly into between/within components when a grouping (continent, income) is supplied. -
lag_by_country()/diff_by_country()— panel lag and difference grouped byiso3cand ordered byyear, completing the panel toolkit aroundgrowth_rate()/index_to()/complete_years(). -
morans_i()— global Moran’s I with a permutation pseudo-p-value, computed on the row-standardisedcountry_borders()adjacency. Nospdepdependency: the weights come from the package’s own curated topology. -
spike_map()— triangular spikes at country centroids (height ∝ value), the overplotting-resistant cousin ofbubble_map(); needs onlymaps. -
convert_country()acceptsto = "name_<lang>"("name_fr","name_es","name_zh", …) for localized country names via countrycode’s CLDR tables. -
world_map(style = "binned")legends now show SI-formatted breaks (4M, not4e+06) whenscalesis installed; the continuous scale uses the same formatter.
Bug fixes
-
country_join()/country_join_all()no longer cross-join rows whoseiso3cisNA: unmatched countries used to collapse to a singleNAkey and fan out into a Cartesian product. The joins now passna_matches = "never"(#4). -
country_join_all()validates the length oforigin(must be 1 or one per table) instead of failing with a cryptic “missing value where TRUE/FALSE needed” error (#16). -
join_world()’s auto-detection (detect_country_col()) honours the candidate priority order instead of picking the first column by data-frame position, so aregioncolumn no longer shadows a realcountrycolumn (#6). -
standardize_country(add = ...)accepts any rawcountrycodedestination (e.g."iso3n") again instead of erroring with “subscript out of bounds” (#5). -
standardize_country(origin = "iso3c")now validates codes: strings that are not real ISO 3166-1 alpha-3 codes becomeNA(and are flagged bywarn) rather than passing through uppercased and unchecked (#12). -
country_data(latest = TRUE)/world_data(latest = TRUE)for a single year now returns each country’s most recent non-NAvalue: the fetch window is widened so an earlier observation can actually be found (#7). -
fetch_wdi()keepsiso2c/countryfor a country that appears only in a non-first indicator (they are coalesced across indicators) instead of leaving themNA(#8). -
world_map()/globe_map()withstyle = "quantile"/"jenks"no longer error on a constant, single-country, or all-NAvalue column; degenerate breaks now fall back to a single bin (#9). -
flow_map()returns the base map (instead of erroring) when no origin-destination pair resolves to a centroid (#10). -
aggregate_regions(fun = "min"/"max")returnsNAfor an all-NAgroup instead ofInf/-Inf(#11). -
share_of_world()returnsNA(notNaN/Inf) when the (per-year) total is zero or non-finite (#13). -
gini()returnsNAwith a warning for negative input rather than a value outside the documented[0, 1]range (#14). -
spike_map()no longer producesNaNspike coordinates when every height is zero (#15). -
world_query()honourstransformeven whenpalette = NULL, emitting a standaloneSCALE fill VIA <transform>clause (#17). -
world_map(style = "quantile"/"jenks")computed breaks over polygon vertices, so a country’s geometric complexity biased the quantiles and the bins held unequal numbers of countries. Breaks are now computed on one value per country. -
bubble_map(backend = "sf")placed bubbles in projected metres on a degrees base map (off the map). The base map and bubbles now share one projected CRS viacoord_sf(). - Polygon centroids returned more than one row for ten
iso3ccodes (overrides map several names — Azores/Madeira → PRT — to one code), fanning out joins inbubble_map()/flow_map(). Centroids are now one antimeridian-safe row per country (the largest piece). -
geom_country_labels()placed labels at the bounding-box midpoint over all of a country’s pieces, so the US / Fiji / NZ labels drifted into the wrong ocean. Labels now sit on each country’s largest piece. -
projection = "plate_carree"built an incoherent PROJ string (+proj=longlat … +units=m); it is now true equirectangular (+proj=eqc). -
convert_country()only appliedwdj_overrides()forto = "iso3c", so override-only entities (e.g. “Canary Islands”, “Azores”, “Bonaire”) returnedNAfor every other destination (continent, region, iso2c, flag, currency, country name, …). It now resolves the override-correctediso3cfirst and derives every other destination from that. - Kosovo’s
XKXneeded extra care: it has no row at all incountrycode::codelist, so deriving destinations purely via theiso3cround-trip above isNAfor everything — which would have regressedflag/region/country, since 1.0.0 already resolved those via direct name matching (verified against the actual 1.0.0 code).convert_country()now recovers from the original name when theiso3cround-trip comes back empty, and fillsiso2c/continent(which neither path classifies) from the same curated fallbackstandardize_country()uses. Net effect versus 1.0.0: zero regressions, plus newly-workingcontinent/iso2cfor Kosovo — which also fixeslocate_country(..., add = "continent")for points inside it. -
interactive_map(..., tooltip = )was accepted but silently ignored by every engine (pre-dating 2.0.0). The"ggiraph"and"leaflet"engines now use the suppliedtooltipcolumn, defaulting tofillas before when omitted. -
world_data(overrides = )(andattach_geometry(overrides = )) accepted a custom name -> iso3c override set but silently ignored it (pre-dating 2.0.0) – the geometry backend always matched with the defaultwdj_overrides(). The override set now flows through to both the polygon andsfmatchers, so a custom mapping actually changes which polygons a country claims. -
repair_country_names()no longer records a no-op “repair” when a dissolved entity’s own name (e.g. “Yugoslavia”, which exists in the codelist but has no ISO code) comes back as its closest suggestion;dissolve_country()is the right tool there and is what the report now points to.
Housekeeping
- New hex logo, drawn by the package itself (
data-raw/hex_logo.R): an orthographic globe —globe_map()’s projection — carrying a viridis choropleth ofworld_snapshotGDP per capita on Natural Earth geometry joined byattach_geometry(), withspike_map()-style population spikes rising off the horizon and the binned-legend swatches under the wordmark. - The
gdp_per_capita_2015compatibility alias (a one-cycle deprecation shim from 1.0.0) is now opt-in: setoptions(countryatlas.gdp_compat = TRUE)to restore it. The default isFALSE, soworld_data()no longer emits a duplicate column. -
world_snapshotrefreshed to year 2024 (was 2022) and rebuilt with the latest WDI data and curated overrides. -
country_groups_tblmembership date bumped to 2026-06-01 (was 2024-01-01). -
?world_snapshotwas out of sync with the rebuilt data (missing the “Snapshot year: 2024” note); regenerated. - Fixed a stray orphaned code fence at the end of the countryatlas and ggsql vignette that broke its markdown structure.
- README and vignettes now demonstrate every exported function:
wdi_search(),country_codes(),complete_years(),growth_rate()/index_to(),repair_country_names(),country_join_all(),locate_country()andfacet_map()gained worked examples, and the vignettes prefercountry_overrides()over the soft-deprecatedwdj_overrides(). The README’s rendered output and figures were stale (pre-dating the quantile-breaks fix and thegdp_per_capita_2015opt-in) and have been re-rendered from the 2.0.0 code.
countryatlas 1.0.0
CRAN release: 2026-06-24
A single, comprehensive release that takes the package from a one-function proof of concept to a complete toolkit for joining world data to maps. The spirit is unchanged — ISO codes as the universal join key, one call to a map-ready table — but pushed to its full potential.
Breaking-ish changes
-
world_data()is generalised but backward-compatible:world_data(2020)still returns the classic polygon-backed, GDP-per-capita tibble. The only visible change is the column namegdp_per_capita_2015→gdp_per_capita. A one-cycle deprecation shim keepsgdp_per_capita_2015available as an alias (toggle withoptions(countryatlas.gdp_compat = FALSE)). - The 16 regions the previous version silently dropped (Kosovo, Micronesia, the Virgin Islands, Saint Martin, Bonaire/Saba/Sint Eustatius, the Canary Islands, Madeira/Azores, …) are now matched via [
wdj_overrides()] instead of deleted, so they appear on maps. Diffs of map output will show increased coverage.
New: core data assembly
-
world_data()gainsindicator(one or many WDI codes; named vectors drive clean column names), multi-year panels, ansfbackend (geometry = "sf"),regionsubsetting,latest, projections and caching. -
country_data()— the lightweight, one-row-per-country analysis table. -
world_geometry()— projected, region-subset geometry (countries, centroids, coastline, borders, graticule, ocean).
New: diagnostics
-
check_country_match(),wdj_overrides(),audit_coverage()— never lose a country silently.
New: reference data & translation
-
convert_country()(flags, currency, tld, research codes),country_codes(),country_groups()/in_group(),wdi_search(). - Bundled datasets:
world_snapshot,country_meta,common_indicators,country_groups_tbl,world_tiles.
New: visualization
-
world_map()(continuous / binned / quantile / jenks / categorical),bubble_map(),bivariate_map(),cartogram_map(),tile_map(),flow_map(),animate_world(),interactive_map(),geom_country_labels(),theme_world_map().
Performance & offline
- WDI fetches are memoised with an optional on-disk cache; multiple indicators are fetched in parallel (
parallel::mclapply) where supported. Seeclear_wdi_cache(). - The bundled
world_snapshotlets every example, test and vignette run offline and deterministically.
Engineering
- Namespace hygiene (targeted
@importFrominstead of blanket@import). - Input validation with friendly
cli/rlangerrors. - A
testthat(3e) suite; network calls are skipped offline and on CRAN. - Vignettes and a
pkgdownsite. - Refreshed CI: R-CMD-check, test-coverage and pkgdown workflows.
- Heavy spatial dependencies (
sf,rnaturalearth,cartogram,biscale,geofacet,gganimate,leaflet, …) are all inSuggestsand gated byrlang::check_installed(), so the base install stays light.
Group memberships in country_groups_tbl are point-in-time as of 2024-01-01.
