# Map layers and integration

The globe uses MapLibre GL JS 6.14, with its ES-module worker bundled by Vite. Scroll and pinch change fractional zoom continuously. Geometry is tiled in a worker; the map does not redraw every coastline vertex through React on each frame. Country labels are local HTML labels, available without remote fonts. City labels use the public OpenFreeMap glyph service.

## Street map and coastlines

[OpenFreeMap](https://openfreemap.org/) supplies worldwide OpenStreetMap vector tiles through its public, keyless instance. The source uses the stable TileJSON endpoint `https://tiles.openfreemap.org/planet`, not a dated tile snapshot. Attribution to OpenFreeMap, OpenMapTiles and OpenStreetMap contributors is displayed on the map. OpenFreeMap permits commercial use, without registration or an API key; it does not provide an uptime guarantee. See its [quick start and attribution](https://openfreemap.org/quick_start/) and [OpenStreetMap's copyright page](https://www.openstreetmap.org/copyright).

The vector style draws real OSM inland water, roads, parks and buildings above the locally bundled atlas land. Ocean tiles sit below the atlas land so their simplified coastlines cannot cut through an island. Political outlines come from the same local atlas geometry as the game, rather than mixing a second provider's territorial claims. The selected-land highlight and silhouette use the **same separately documented local geometry**. Detailed context does not upgrade the precision of an older quiz polygon. Refer to each feature's source/geometry metadata and the dataset documentation.

The land base uses the existing atlas overview and shared detailed country geometry. The selected land base and its translucent highlight share the same GeoJSON source and switch detail levels together. This avoids coarse white wedges and color changes inside the outline, including while street tiles are unavailable. Satellite imagery and unrevealed silhouette rounds do not use the opaque land base.

During initial atlas loading, the app retains its bundled Natural Earth world map. `public/data/world-land.geojson` comes from [Natural Earth 50m land](https://github.com/nvkelso/natural-earth-vector/blob/master/geojson/ne_50m_land.geojson), which is [public domain](https://www.naturalearthdata.com/about/terms-of-use/). This fallback is an overview, not a street-scale coastline source.

## Global terrain

The **Terrain** checkbox beside Map and Satellite independently enables worldwide
elevation relief in either basemap. It starts off and retains its choice while
switching imagery or countries. Turning it off removes its DEM sources and
hillshading, so the default globe requests no global terrain tiles.

It uses the same [Mapzen / AWS Terrain Tiles](https://registry.opendata.aws/terrain-tiles/)
and [complete DEM source credits](public/data/study/terrain-attribution.html) as the
country terrain views. Terrarium DEM tiles are capped at zoom 14; source dates and
resolution vary. Hillshading is subtler over satellite imagery. The elevation mesh
uses 1.25× vertical exaggeration, while the current camera and navigation remain
unchanged. Separate mesh and hillshade source caches follow MapLibre's rendering
guidance. Terrain is geographical context, not a new measurement of national peaks.

## Satellite imagery

The default imagery is **Sentinel-2 cloudless, the 2016 mosaic**, containing modified Copernicus Sentinel data from **2016 and 2017**. Its native image resolution is approximately **10 metres**. It is historical satellite imagery, not current aerial photography. Overzooming beyond native resolution enlarges pixels and cannot reveal sub-metre details.

The exact WMTS layer is `s2cloudless_3857` (without a year in its identifier):

```
https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless_3857/default/g/{z}/{y}/{x}.jpg
```

The [official WMTS capabilities](https://tiles.maps.eox.at/wmts/1.0.0/WMTSCapabilities.xml) identify that layer as 2016 and CC BY 4.0. [EOX's current pricing page](https://cloudless.eox.at/pricing) permits direct use of service endpoints under their applicable licenses and explicitly assigns **CC BY 4.0 to 2016**. [The original data description](https://eox.at/2017/03/sentinel-2-cloudless-original-tiles-available/) gives its 10m resolution. The [2024 release's license section](https://eox.at/2025/03/sentinel-2-cloudless-2024/) gives the required 2016 attribution, included in the map:

> Sentinel-2 cloudless — https://s2maps.eu by EOX IT Services GmbH (Contains modified Copernicus Sentinel data 2016 & 2017)

The map links to [Sentinel-2 cloudless](https://s2maps.eu), [EOX](https://eox.at) and [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). The year and resolution also remain visible above the map switch. **Do not replace this URL with a newer dated EOX layer without checking the license:** the free 2018–2025 mosaics carry a noncommercial restriction.

If the imagery fails, the map attempts [NASA GIBS](https://nasa-gibs.github.io/gibs-api-docs/access-basics/) Blue Marble at approximately 500m resolution, clearly labeled as a fallback. NASA data use is described in its [open data policy](https://www.earthdata.nasa.gov/engage/open-data-services-software/data-use-policy). If both imagery sources fail, the vector/bundled map remains usable.

The default source also detects the geometric white/navy filler embedded in successful EOX tiles and reveals real NASA imagery beneath those pixels globally. Textured source colors remain intact. Detailed, dated Sentinel repairs cover the reported South Sandwich chain, Bouvet and Brazil's Atlantic outliers. Small copies of all bundled scenes remain visible from world zoom, then switch to their original resolution from zoom 8; attribution discloses the current display sampling. Polar scenes use calibrated visible reflectance to retain bright ice detail. See [the imagery audit and sources](docs/ISLAND_IMAGERY.md) for the 7,053-tile survey, processing and the coarser 500m backup's limits. Custom providers are preserved.

### Optional licensed provider

For another provider whose terms explicitly authorize your application, set these Vite environment variables before building:

```
VITE_SATELLITE_TILE_URL=https://your-authorized-provider.example/{z}/{x}/{y}.jpg
VITE_SATELLITE_ATTRIBUTION=Your provider's required attribution
VITE_SATELLITE_LABEL=Provider name · capture year · native resolution
VITE_SATELLITE_MAX_ZOOM=18
```

Both the URL and attribution are required before the custom source is enabled. These variables are public client configuration. Do not put private credentials in them. The app includes no Google private endpoints or unlicensed aerial imagery.

## Boundaries and grouping guides

### Remote-island corrections (October 2026)

Baker, Howland, Jarvis, Wake, Midway, Johnston, Palmyra, Kingman and Navassa use real, unsimplified [OpenStreetMap coastlines](https://www.openstreetmap.org/copyright), ODbL 1.0. The same coordinates feed their silhouettes, overview and detailed map. Baker's former displaced extra polygon has been removed. Individual source ways are linked in `/data/remote-islands/index.json`; `/data/remote-islands/coastlines.geojson` is the attributed derived database.

Each of the nine U.S. Minor Outlying Islands has an individual browsing identity and detailed coastline, while the game catalog retains the collective UMI answer. The island list puts the selected member first; its postcard uses that member’s local Sentinel-2 scene with observation date and attribution. The group’s old aggregate population is not presented as an island population. Selecting a member reveals all nine U.S. relationships with the same overseas-territory color. Acquisition context cites the [Department of the Interior](https://www.doi.gov/oia/islands/acquisitionprocess), with island-specific conservation and aviation sources.

Explore relationship endpoints reuse the primary map-name anchors, including the Marshall Islands. Hawaii has one group label at the area centroid of its dashed guide rather than repeated component labels.

At close zoom, locally bundled [Copernicus Sentinel-2 L2A true-color imagery](https://registry.opendata.aws/sentinel-2-l2a-cogs/) replaces white placeholders in the default EOX mosaic for these nine locations. Contains modified Copernicus Sentinel data (2024–2025), processed by ESA, with Cloud Optimized GeoTIFFs by Element 84; free and open under the Copernicus data policy. Each patch records its observation date, source asset, bounds, native resolution (10 m), and display sampling. Reprojection preserves real scene pixels; only the outer water margin fades into the surrounding mosaic. No generated terrain is used. The map displays the local observation date, and configured custom satellite providers are not overridden.

`scripts/repair-remote-islands.py` caches small sequential OSM reads and Earth Search STAC results at build time. Gameplay makes no OSM or STAC queries. `scripts/build-explore-groups.mjs` separately derives rounded visual guide envelopes, using 1–25 km padding proportional to island size and clipping near neighboring groups; they do not represent legal sea borders.

Real target geometry is filled and outlined. A separate dashed envelope and nearby-component connector layer helps identify separated archipelago components. These computed guides are explicitly labeled **group guides, not political boundaries**, and never replace the actual coastline. They require island metadata: mainland countries receive no dashed box. They split at the antimeridian and avoid connecting distant dependencies across the globe. Tiny islands use their true coastline as a thin waymark; there are no enlarged fake islands or blue point markers. The optional navigation grid draws geographic meridians/parallels with spacing that follows the visible region, including city scale.

Capital points are distinguished from municipal polygons. A real supplied `capital.boundary` or lazy `capital.boundaryUrl` is drawn in amber; there is no invented circular city boundary. If no municipal polygon is available, the map explicitly says so. `boundaryLabel` can describe a city-state or other applicable administrative extent. A fetched outline is cached in memory, and stale responses cannot replace the current city's outline.

The legacy administrative index supplies **60 actual outlines**. Most outlines are OpenStreetMap relations, licensed under [ODbL 1.0](https://opendatacommons.org/licenses/odbl/1-0/); Monaco and Vatican City reuse their documented city-state geometry. Every accepted OSM outline is assembled from complete source relation ways, validated as a closed polygon, checked against its city identity, and tested to contain the recorded capital point. Extents can be municipalities, special cities, city-states or capital districts; they do not necessarily coincide with the continuous built-up area. The current capital games instead receive sourced metropolitan built-up features through the common target interface. See the per-capital metropolitan metadata and [DATA_SOURCES.md](DATA_SOURCES.md) for that distinct footprint method; administrative outlines must not be relabeled as urban footprints.

`scripts/generate-capital-boundaries.py` performs one-time, cached, sequential build requests using Natural Earth city identities, Wikidata's [OpenStreetMap relation ID property](https://www.wikidata.org/wiki/Property:P402), and the public Overpass API. It stops when the provider returns a rate limit. The shipped app only fetches local GeoJSON and never queries Overpass. `public/data/capital-boundary-index.json` records availability and sources for every primary capital; `scripts/merge-capital-boundaries.py` attaches matching records without confusing multiple capitals in the same country.

## Map interface

`IslandMap` accepts `mode: 'name' | 'every' | 'find' | 'explore'`, with older mode aliases retained for compatibility. `islands` is a stable array of bounded overview features. `target` receives the resolved game's real shape; in Find mode it is a selected previous guess or the revealed answer, never the hidden answer. The same shape goes to the silhouette. High-resolution target data loads lazily in the renderer worker by URL; ordinary mode changes and typing must not parse the full world or reload geometry. See [MAP_PERFORMANCE.md](MAP_PERFORMANCE.md).

Optional props:

- `guessedIds: string[]` highlights completed lands and reveals their labels.
- `findGuesses: {id, distanceKm, correct?}[]` colors real guess polygons from light gray to dark green. `proximityContour` accepts the scoring worker's exact-distance contour; incomplete contours must not be presented as complete.
- `surroundingMap` controls Name mode's map reveal; `onZoomExtent` reports the greatest normalized zoom-out extent.
- `capital: { name, coordinates, boundary?, boundaryUrl?, boundaryLabel? }` is a legacy administrative-outline interface. New capital games use a real metropolitan target feature.
- `basemap: 'map' | 'satellite'` and `onBasemapChange` control the built-in switch.
- `disputedAreas`, `connections`, and `showConnections` accept explicitly sourced dispute context and current/historical relationship links. Link arcs are visual guides, never boundaries or transport routes.
- `onPerformance` exposes visible diagnostics without hidden browser globals.

Explore uses progressive GPU labels with fixed mainland interior anchors and renderer collision placement. Country names are no longer rebuilt as hundreds of HTML markers. Quiz hover does not disclose unknown names. Features and label data update only when their underlying data changes, not on every pan frame.

Name mode permits Satellite while the surrounding map is hidden. An exact inverse of the selected polygon masks all outside imagery, including interior holes and the antimeridian. Mask preparation code ships with the map rather than requesting a separate versioned module when Satellite is first selected. Raster visibility waits for the mask upload and its own loaded tiles, so cached imagery cannot briefly expose neighboring places; unrelated tile requests do not delay readiness. Switching rounds or leaving the isolated view cancels pending readiness callbacks. A failed or stalled preparation keeps the outline playable and offers a small retry control rather than a persistent map-blocking error. `polygon-clipping` 0.15.7 performs the mask difference; the mask is a display operation, not a replacement geographic outline. Surrounding map labels remain hidden until explicitly allowed.

## Lazy geographic study maps

Country facts offer five optional maps. No study map, terrain tile, or layer dataset loads until the corresponding button is opened. Only one study map is mounted at a time. Its pixel ratio is capped at 1, tile cache at 12 per source, with one retained zoom level. The view starts at the country's mainland and can step through related territories.

| Layer | Source and license | What the map represents |
|---|---|---|
| 3D terrain | [Mapzen / AWS Terrain Tiles](https://registry.opendata.aws/terrain-tiles/), derived from open national and global DEMs | Bare-earth elevation mosaic, commonly 30 m or coarser; exact resolution varies. Terrarium tiles have native maximum zoom 14. Terrain has 1.35× vertical exaggeration, while numeric readings are corrected back to metres. Higher elevation turns red. |
| Temperature | [Copernicus ERA5 reanalysis](https://cds.climate.copernicus.eu/datasets/reanalysis-era5-single-levels), 1990–2019 climatology via [WeatherBench 2](https://weatherbench2.readthedocs.io/en/latest/data-guide.html), CC BY 4.0. Contains modified Copernicus Climate Change Service information. | Average yearly 2 m air temperature on a 0.25° grid, about 28 km. Mountain tops and narrow valleys are smoothed, so the coldest pin marks the coldest grid cell, not a summit. ▲ marks the warmest and ▼ the coldest average grid point inside the outline; places smaller than a grid cell are sampled along their coast. Readouts switch to °F with imperial units. |
| Rock types | [Hartmann & Moosdorf, GLiM v1.0 (2012)](https://doi.org/10.1594/PANGAEA.788537), CC BY 3.0 | Dominant lithology in 0.5° cells, approximately 56 km north–south. A regional classification, not a local geological survey. Small islands may be absent. |
| Vegetation / land use | [NASA MODIS IGBP annual land cover](https://modis-land.gsfc.nasa.gov/landcover.html), 2024, NASA open-data policy | Seventeen classes at approximately 500 m: forests, shrublands, savannas, crops, cities, water, and others. Nearest-neighbor rendering retains categorical colors. |
| Ecoregions | [RESOLVE 2017 / Dinerstein, Olson et al.](https://ecoregions.world/), CC BY 4.0 | Simplified ecological-region polygons, colored by biome. The [OpenLayers redistribution](https://github.com/openlayers/data) retains 820 named regions; small islands and fine boundaries may be omitted. This is habitat context, not a current species-richness census. |

Terrain tiles use `https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png`. The map links to [the complete source credits](public/data/study/terrain-attribution.html), which reproduce the required credits from [Tilezen's attribution documentation](https://github.com/tilezen/joerd/blob/master/docs/attribution.md). They include USGS, NOAA, ArcticDEM, and licensed national elevation agencies. The terrain color scale follows the reported highest point when available, otherwise a sampled visible-terrain range; the latter must not be represented as a measured national peak.

The Terrain map contains the recorded highest and lowest points from Wikidata
P610 / P1589, including statement-level P625 coordinates and P2044 elevations
where provided. The bundled CC0 snapshot is `public/data/terrain-extremes.json`,
rebuilt with `node scripts/generate-terrain-extremes.mjs`. These pins are sourced
records, not extrema inferred from sparse DEM samples. Sea-level records name a
coast rather than a unique point: their pins use representative atlas shoreline
vertices, with that limitation visible in their details. Wider water-body records
likewise disclose representative boundary locations. Missing records stay
explicitly unavailable. Negative elevations and metric/imperial units are kept.

Hovering or keyboard-focusing a pin previews its details; clicking or tapping
opens a persistent, viewport-bounded detail card with source links. The map's High
and Low buttons locate records outside the current view (including national high
points on distant islands). Locating a point outside the selected outline reveals
the surrounding terrain; Reset restores the isolated full outline. The standalone
Elevation section is removed. The two separately selected Ascension / Tristan
islands use their own source entities instead of their shared parent identifier.
Macau's source item currently misplaces the decimal in its elevation; the snapshot
uses the [Macau government's 170 m Coloane Peak record](https://www.macaotourism.gov.mo/en/sightseeing/gardens/coloane-height-park-and-statue-of-a-ma)
instead, and links that source in the detail card.


MODIS is delivered by the [NASA GIBS WMTS service](https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/1.0.0/WMTSCapabilities.xml), layer `MODIS_Combined_L3_IGBP_Land_Cover_Type_Annual`, date `2024-01-01`, `GoogleMapsCompatible_Level8`. Its colors follow the [official IGBP colormap](https://gibs.earthdata.nasa.gov/colormaps/v1.3/MODIS_IGBP_Land_Cover_Type.xml).

`scripts/generate-study-layers.py` reproducibly downloads and converts the licensed GLiM grid into a 36 KB nearest-neighbor Web Mercator image, and reduces the licensed ecoregion redistribution to a 2.3 MB local GeoJSON. The latter retains region, biome, realm, and color while omitting historical conservation-status fields. Provenance and conversion details are saved in `public/data/study/sources.json`. `scripts/generate-temperature-layer.py` averages every stored hour and day of the ERA5 2 m temperature climatology into an annual mean, then writes a land-only colour image (`temperature-era5-mercator.png`), an exact-value image for hover readouts (`temperature-era5-values.png`, red/green = (°C + 100) × 10) and `public/data/temperature-extremes.json` with each place's mean, warmest and coldest grid points. No outlines or categories are invented.

## Worker delivery

`scripts/vendor-map-worker.mjs` copies the installed MapLibre worker and BSD license verbatim to `public/vendor`. Both maps load this same static ESM worker with a package-version cache key. This avoids Vite 8 injecting browser-only HMR code into a worker, which caused `window is not defined` and a blank map in development. Worker preparation runs before dev/build; tests verify the copy is identical to the installed package. No remote worker service is required.

## Matching neighboring borders

The game now uses geoBoundaries CGAZ ADM0 shared mainland boundaries, with
regional worker-loaded detail from zoom 6 and the same inland coordinates for
selected silhouettes and neighboring map outlines. OpenStreetMap still provides
roads, rivers and buildings, but its administrative lines are not mixed into
this boundary layer. Source conventions and simplification limits are documented
in [Shared country boundaries](docs/SHARED_BOUNDARIES.md).


## People & economy detail (October 2026)

The layer control is one disclosure; selecting Off creates no empty secondary
panel. Continuous color interpolation uses the same numeric stops as the legend.
Country totals and local population totals have independent, labeled scales.
Numeric observations are not interpolated across administrative boundaries.

- **Regional GDP and population:** DOSE v2.11 (Wenz et al.),
  [dataset and article](https://doi.org/10.1038/s41597-023-02323-8), CC BY 4.0.
  The import retains each country's latest published year (through 2020), with
  1,223 uniquely matched regions in 76 countries. Boundaries are Natural Earth
  1:10m admin-1 polygons (public domain), simplified for display. Only unique,
  exact normalized names within the same country are joined. This is a subset
  of DOSE's coverage; unmatched regions keep country shading.
- **U.S. county GDP:** BEA CAGDP2, all-industry GDP in thousands of current
  dollars, converted to dollars and divided by Census 2022 resident population.
  Both numerator and denominator refer to 2022. Public-domain federal data.
- **County population/density:** Census Vintage 2024 county population estimates.
  Density divides these totals by the published Census 2010 land area, converted
  from square miles to square kilometres. These are county averages, not cells.
- **Home values:** Census ACS 2020–2024 five-year median value of owner-occupied
  housing, in 2024 dollars. The [Foundation Soils compilation](https://github.com/rdoyle99/foundation-soils-data)
  is CC BY 4.0; the original federal observations are public domain. Values are
  neither median transaction prices nor a price-change index.
- **Income inequality, child poverty, unemployment, life expectancy and median
  household income:** County Health Rankings 2024 release, CC BY 4.0, retaining
  Census SAIPE, BLS and mortality-source definitions. Income inequality is an
  80th/20th percentile household income ratio, not a wealth Gini. Poverty is the
  percentage of children below the U.S. poverty threshold. Most observations
  refer to 2022; income inequality uses ACS 2018–2022; life expectancy is the 2019–2021 period estimate. Source years
  are visible on inspection; a release year is not substituted for a survey year.
- **Cost of living:** BEA SARPP all-items Regional Price Parities, 2024,
  public-domain federal data. 50 states plus DC; U.S. price level = 100. This
  compares price levels, not inflation, household budgets or a world price index.
- **Wealth concentration:** [World Inequality Database](https://wid.world/),
  top 1% share of net personal wealth, equal-split adults, 2024 estimates for
  194 atlas countries, exported May 2026. WID data are CC BY 4.0 and may include
  modeled estimates. These are national estimates. No local wealth is inferred
  from household income, housing values or neighboring countries.
- **Life expectancy (countries and territories):** World Bank World
  Development Indicators [SP.DYN.LE00.IN](https://data.worldbank.org/indicator/SP.DYN.LE00.IN),
  life expectancy at birth, total, July 2026 release (CC BY 4.0), which
  carries UN World Population Prospects estimates. Every one of the 216 matched
  atlas areas has a 2024 value. The World Bank host is blocked from the build
  environment, so the unmodified bulk CSV is read from a pinned GitHub mirror
  recorded in `provenance.json`. Territories without their own series, such as
  Réunion, Jersey or Taiwan, stay grey and never borrow a parent's value. The
  scale starts at 55 years (Nigeria is lowest at 54.6) and runs from straw
  through green to deep teal. U.S. counties keep their 2019–2021 estimates.
- **Country-card population maps:** pre-rendered, lossless WebP overviews in
  `public/data/population-overviews/`, generated by
  `scripts/build-population-overviews.mjs`. Each country opens one local image;
  external native detail is deferred until zoom. Most use JRC GHS-POP R2023A
  2020 estimates, people per native 3 arc-second cell (about 100 m). Where JRC
  rendering repeatedly fails, the manifest explicitly selects NASA/CIESIN GPW
  2020 instead, approximately 1 km, people/km², with its original color legend.
  The visible units, attribution and native zoom ceiling follow the selected
  dataset; the sources are not relabeled or merged into invented fine detail.
  Per-image geographic bounds, dimensions, byte lengths and SHA-256 hashes are
  retained in the public manifest and bundled source index. The generator's
  `--fallback-gpw` flag makes this source fallback explicit and reproducible.
  Follow generation with `python scripts/clip-population-overviews.py`
  (rasterio, NumPy and Pillow) and `node scripts/split-population-overviews.mjs`.
  Cached RGBA images contain their authentic land mask, avoiding low-zoom inverse
  polygon triangulation over disconnected country parts. Date-line images use
  separate canonical longitude pieces; content hashes version their URLs.
- **Optional population grid:** NASA GIBS `GPW_Population_Density_2020`, documented
  by [NASA Worldview](https://github.com/nasa-gibs/worldview/blob/main/config/default/common/config/metadata/layers/sedac/GPW_Population_Density_2020.md).
  GPWv4 / UN WPP-adjusted estimates use native 30 arc-second cells, approximately
  1 km north–south, for 2020. The native raster colors are preserved; the publisher
  legend is linked. Tiles are requested only when the grid is enabled. Zooming
  beyond the native raster level improves display size, not data resolution.
  Raster hover reads the publisher’s color-map range; it does not claim an exact numeric pixel measurement. A source failure
  removes the grid and retains administrative observations. The managed build
  environment blocks NASA egress, so live tile delivery requires browser QA.

The generator is `scripts/generate-people-economy.py`, followed by
`node scripts/generate-people-economy-manifest.mjs`. Download URLs and SHA-256
hashes are recorded in `data/statistics/provenance.json`. The input mirrors
retain the original publishers' definitions and licenses; their presence on
GitHub does not supersede those licenses. No private API keys are needed to
use the bundled observations. The older Census county boundary snapshot omits
changed identifiers, including newer Connecticut planning regions. Missing
observations remain missing, never zero. All local geometry loads only for the
selected metric and relevant zoom. Small areas may be absent at display scale.

## Flags (October 2026)

The People & economy menu's **Flags** layer paints each country and territory with its flag. Flags come from [hjnilsson/country-flags](https://github.com/hjnilsson/country-flags) 1.2.10 (npm `svg-country-flags`), Wikipedia SVGs in each flag's official proportions, in the public domain. `scripts/build-flag-map.mjs` copies the flags in use to `public/flags/proportional/` with prerendered 160 and 640 px PNGs (resvg), and groups every place's display coastline into landmass groups: islands within 75 km share one flag, fitted whole inside the group's Mercator bounds and centered on its land, with the flag's edge colors continued over any land beyond it. Places whose land is too small to read at continent scale get a flag pin until they are about 28 px across. Tiles are drawn in the browser on demand from the overview outlines below zoom 6 and the detailed display outlines from zoom 6. `public/data/flag-map.json` is 0.12 MB. Dhekelia and Akrotiri use the United Kingdom's flag and Guantánamo Bay the United States'; disputed or unclaimed areas without a flag in the atlas are hatched.

## Time zones (October 2026)

The People & economy menu's **Time zones** layer uses [timezone-boundary-builder](https://github.com/evansiroky/timezone-boundary-builder) release 2026b, `timezones-with-oceans-now`: boundaries from OpenStreetMap, licensed under [ODbL 1.0](https://opendatacommons.org/licenses/odbl/1-0/), with nautical zones at sea. The "now" edition merges zones whose clocks agree from 2026 onwards, so the world needs 65 regions. `scripts/build-time-zones.mjs` simplifies them to 3% with mapshaper and adds one label point per sizeable piece; `public/data/time-zones.json` is 1.9 MB. The app reads each region's current UTC offset from its tzid with the browser's `Intl` time zone database, so labels follow daylight saving time without a rebuild.

## Human Development Index (October 2026)

The People & economy menu's **Human Development Index** layer uses the UNDP [Human Development Report 2025](https://hdr.undp.org/data-center/documentation-and-downloads) composite indices complete time series (6 May 2025 data update), licensed under [CC BY 3.0 IGO](https://hdr.undp.org/terms-use). UNDP recalculates the whole series with every report, so all years from 1990 to 2023 come from this one release and are comparable. `scripts/build-hdi.mjs` checks the file's SHA-256, drops regional aggregates, and writes each country's series to `public/data/hdi.json` (30 kB) keyed by map area. Years without an estimate stay gray, and separately drawn territories never inherit a parent's value; North Korea, Monaco and Vatican City have no estimates. The color scale and UNDP's low, medium, high and very high groups (0.550, 0.700, 0.800) are fixed, so a color means the same in every year.

## Historical borders (October 2026)

The People & economy menu's **Historical borders** layer combines two datasets. From 1886 to 2019 it uses [CShapes 2.0](https://icr.ethz.ch/data/cshapes/) (Schvitz, Girardin, Rüegger, Weidmann, Cederman & Gleditsch, 2022, *International Studies Quarterly*), which records every state and colonial dependency with the exact day each border changed. ETH's download page is blocked from the cloud, so the build takes `cshapes_2_gw.topojson.xz` from the CRAN [`cshapes` 2.0 package](https://github.com/cran/cshapes), distributed under GPL (≥ 2). The app shows the map as it stood on 1 July of the chosen year. CShapes keeps one modern name per state, so `scripts/data/historical-border-names.json` gives era names (Belgian Congo, Gold Coast, Persia, Soviet Union, West Germany and so on), splitting a polygon in two where a name changes. Before 1886 the layer uses the 44 world snapshots (123,000 BC to 1880) of Andre Ourednik's [historical-basemaps](https://github.com/aourednik/historical-basemaps), GPL-3.0; unnamed polygons are left empty. `scripts/build-historical-borders.mjs` simplifies both with mapshaper (CShapes to 12% without `-clean`, since its dated polygons overlap on purpose; snapshots to 35%), colors them with a DSatur graph coloring so neighbours differ and colonies take a paler shade of their ruler's hue, and adds one label point per polity. `public/data/historical-borders/` holds `cshapes.json` (2.9 MB, filtered by date on the map) and one file per snapshot (8.3 MB in all, the largest 1.2 MB), loaded only when shown.

## Murder rate and Crime (October 2026)

The People & economy menu's **Murder rate** layer shows UNODC victims of intentional homicide per 100,000 people, 1990 to 2023, as republished in the World Bank World Development Indicators ([VC.IHR.PSRC.P5](https://data.worldbank.org/indicator/VC.IHR.PSRC.P5), [CC BY 4.0](https://datacatalog.worldbank.org/public-licenses)). The snapshot `scripts/data/unodc-homicide-rate.csv` comes from the open-numbers WDI mirror at commit 1bd7fac (built 18 October 2026), because the UNODC and World Bank hosts are not reachable from the build environment. Countries report in different years, so a year without a figure shows the most recent one from up to four years earlier, and the tooltip names its year. The Channel Islands series is left out because Jersey and Guernsey are drawn separately.

The **Crime** layer shows sub-factor 5.1 of the [World Justice Project Rule of Law Index](https://worldjusticeproject.org/rule-of-law-index/), "Crime is effectively controlled" (0 to 1), for the 2012 to 2024 editions, licensed under [CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0/). It combines household victimization and safety questions from WJP's General Population Poll with expert questionnaires, so unlike police-recorded crime counts it can be compared between countries. Scores are shown unchanged; the map only colors lower scores darker. `scripts/data/wjp-crime-control.csv` keeps the country, year and score from the WJP historical data file shipped in the WJP data team's [WJPr](https://github.com/ctoruno/WJPr) package (`data-raw/ROLI_data.xlsx`, SHA-256 d94f7a03…cb2). `scripts/build-crime.mjs` writes both layers to `public/data/homicide-rate.json` (32 kB) and `public/data/crime-control.json` (8 kB), keyed by map area; separately drawn territories never inherit a parent's value.

The **Test scores** layer and graph metric show the World Bank's [Harmonized Learning Outcomes](https://www.worldbank.org/en/publication/human-capital) (the Human Capital Index's harmonized test scores, 2010, 2017, 2018 and 2020 editions, 174 countries, [CC BY 4.0](https://datacatalog.worldbank.org/public-licenses)), which link PISA, TIMSS, PIRLS, PASEC, SACMEQ, LLECE and other tests to one PISA-like scale. The snapshot `scripts/data/hlo-harmonized-test-scores.csv` is the [Our World in Data copy](https://ourworldindata.org/grapher/average-harmonized-learning-outcome-scores). With the States style on, Canadian provinces show their PISA 2022 math, reading and science average from CMEC's *Measuring Up* report (tables 1.4, 3.4 and 3.5), Spanish autonomous communities the same from INEE's *Sistema estatal de indicadores de la educación 2024* (R2.1 to R2.3), and U.S. states NAEP 2024 grade 8 math and reading from the [NAEP Data Service](https://www.nationsreportcard.gov/). NAEP has its own scale, so U.S. states are colored against each other rather than against countries. Sources are in `scripts/data/pisa-2022-regions.csv` and `scripts/data/naep-2024-grade8.csv`.

The **Median IQ (estimated)** layer and graph metric show column QNW+SAS of David Becker's [NIQ dataset V1.3.2](https://viewoniq.org/) (2019), a revision of the data behind Lynn and Vanhanen's *IQ and the Wealth of Nations* (2002, 2012), with Britain = 100. **These figures are not reliable.** Many rest on one or two small samples, often of children in a single town or school, tested decades ago with tests normed in other countries; researchers such as Wicherts, Dolan and van der Maas (2010) found that better sample choices raise many scores by 10 points or more, and the original authors have been criticised for their views on race. The published figures are means; on a symmetric IQ scale the mean and the median are effectively the same, so the layer calls it the median. Countries with no IQ samples and no school-test data stay gray: the dataset's "+GEO" column, which fills them from neighbours, is not used. Each hover card lists how many samples and people the figure rests on. No per-state IQ is shown, because published state IQ estimates are only NAEP scores rescaled. `scripts/build-learning.mjs` writes `public/data/test-scores.json`, `public/data/national-iq.json` and `public/data/states/test-scores.geojson`.

## French Scattered Islands imagery repair (20 October 2026)

Bassas da India, Europa, Juan de Nova, Glorioso and Tromelin now use bundled,
georeferenced Sentinel-2 L2A true-color scenes where the older EOX mosaic has
missing or incomplete coverage. Bassas, Glorioso and Tromelin's inspected EOX
tiles returned HTTP 200 with a uniform ocean image, which cannot trigger a
network-error fallback. The local patches load from zoom 6 and retain actual
acquisition dates (2024–2025), scene IDs, public COG source URLs and native 10 m
resolution in `public/data/island-imagery/index.json`. Bassas is displayed at
approximately 10.15 m per pixel; the other four retain 10 m display sampling.
Their reef/lagoon water comes from observed pixels, with no invented land.

Regenerate with `scripts/repair-island-imagery.py --only fr-bassas-da-india
--only fr-europa --only fr-juan-de-nova --only fr-glorioso --only fr-tromelin`.
The [Element 84 / Copernicus collection](https://registry.opendata.aws/sentinel-2-l2a-cogs/)
provides free, full and open Sentinel data. Existing map attribution links to
the scene provenance. These historical images are not a live camera.

### Animated ocean currents

Ocean currents use real eastward (`uo`) and northward (`vo`) near-surface velocities from the E.U. Copernicus Marine Service / Mercator Ocean Global Ocean Physics Analysis and Forecast (DOI [10.48670/moi-00016](https://doi.org/10.48670/moi-00016)). The hourly 1/12° model is displayed using the publisher’s 1/3° downsampled grid, at about 0.5 m depth. Source coordinates and timestamps are checked before caching. No raster arrows or synthetic vector fields are used. Colors represent speed in m/s; moving trails follow the interpolated velocities at an accelerated visual speed. Six three-hour snapshots are prepared in the public environmental cache and refreshed hourly; the UI retains exact data times and reports a stale source. The standalone cache contains no account data.

The particle renderer is an original canvas implementation inspired by [Earth Nullschool](https://earth.nullschool.net/). It uses screen-space projection and midpoint integration, scales density to visible ocean, respects the globe horizon and missing cells, and supports reduced motion and an independent flow pause button. Nullschool source/assets are not redistributed.


Wildlife and plants begin loading automatically when a country is selected, independently of the other sidebar sources. Curated national animals, birds and plants appear first, with separate designation sources and prominent common names alongside scientific names. Traditional and heraldic emblems are identified explicitly. The licensed photograph index in `src/lib/nationalSpeciesPhotos.json` is refreshed by `scripts/cache-national-species-photos.mjs` from GBIF occurrence photographs and Wikimedia Commons metadata; each image retains photographer, photo-source and license links. Additional endemics come from iNaturalist research-grade observations and must have an explicit endemic establishment record for the exact country, an available common name and a CC0, CC BY or CC BY-SA photograph. Curated Iberian species are labeled as shared by Spain and Portugal. Random occurrence records are no longer used to imply national or endemic status. Country and image requests are cached and deduplicated; images warm at low priority, and iNaturalist requests are paced at approximately one per second. Coverage gaps and unavailable photographs remain explicit.

Country language charts retain every language/script group listed in Unicode CLDR 48, with classification and nearby relatives from Glottolog 5.2. Rebuild the checked-in source snapshot with `scripts/generate-country-languages.py`; redistribution terms are in `public/data/COUNTRY_LANGUAGES_LICENSE.txt`. Speaker percentages overlap; only pie geometry is normalized to summed reported uses. Worldwide users are explicitly modeled from covered CLDR populations, using the maximum percentage across script variants per territory, rounded to two significant figures. These are incomplete estimates, not an exhaustive language census or an endangered-language classification. Published Foreign Service Institute categories are sourced from the official State Department archive; unrated languages remain unrated. Country establishment uses sourced historical foundations rather than recent constitutions, with event explanations. Independence has an explicit eligibility list and its own comparison sample. Charts count unique eligible records at runtime.

### Global language overview

The Language overlay uses the full, solid color of the leading recorded group;
relative dominance and color strength apply only to Religion. Active ISO 639-3
macrolanguage mappings from [SIL International](https://iso639-3.sil.org/sites/iso639-3/files/downloads/iso-639-3-macrolanguages.tab)
are retained in `src/lib/languageMapGroups.json` and regenerated by the language
generator. Arabic and Chinese varieties, for example, share one map color.
Indonesian and Bosnian/Croatian/Serbian retain their distinct national standards;
language families, creoles and Japanese/Ryukyuan languages are not merged.
Individual language cards and source percentages remain separate.

Each group uses the maximum recorded share across its varieties and scripts in
each territory, never their sum. Grouped percentages below 100% are labeled as
minimums (≥). Worldwide estimates sum population × that maximum across all
covered territories, including places where the group is a minority. The legend
sorts by this modeled estimate, descending, rather than by country count or name.
Reported ties choose the group with the larger modeled worldwide estimate and
retain the tied names in the tooltip. A sole recorded language still gets its
color; missing observations remain gray.

Incomplete CLDR coverage cannot establish a reliable runner-up ranking, so the
Language tooltip shows the leader only. Japan's snapshot lists Japanese, Central
Okinawan and Korean but omits English and Chinese; the tooltip and sidebar chart
explicitly explain this gap without inventing percentages or a second-place
language. These use estimates include additional languages where recorded and
do not constitute an exhaustive census.
