This repository is a reusable world-state baseline for map-first games and simulations.
The project is anchored to a fixed in-game start date:
gameStartDate:2025-01-01
The current app renders a province-based world map and loads canonical country data plus canonical province settlement data that future mechanics can build on.
Use this repository as a base for:
- grand strategy prototypes
- geopolitical and economic simulations
- scenario testing
- world-state tooling
The project is intentionally data-first right now. It is not yet a full game or simulation engine.
Implemented:
- Province-only world geometry rendering from
public/data/provinces.geojson - Derived country borders from province geometry in
public/data/country-borders.geojson - Country/province selection and inspection UI
- Multiple thematic map views
- Canonical merged country dataset in
public/data/canonical-country-data.json - Canonical province settlement dataset in
public/data/canonical-province-data.json - GHSL urban-centre, province-settlement, and province-raster-settlement rollups in
public/data/urban-centres.json,public/data/province-settlement-stats.json, andpublic/data/province-raster-settlement-stats.json - Natural Earth 1:10m strategic infrastructure stats, province-to-province connection graph, and frontend-ready visualization layers
Not implemented yet:
- simulation loop
- time progression
- diplomacy or AI systems
- save/load
- backend persistence
- React
- TypeScript
- Vite
- MapLibre GL JS
- Node.js data import/build scripts
npm install
npm run devProduction build:
npm run buildThe frontend primarily uses:
public/data/provinces.geojsonpublic/data/country-borders.geojsonpublic/data/canonical-country-data.jsonpublic/data/canonical-province-data.json
The source-specific dataset files remain in public/data/ for inspection, coverage auditing, and rebuilds. The map UI reads from the canonical country file for country statistics and political-system metadata, from the canonical province file for settlement and province-level infrastructure overlays, and lazily loads the frontend-ready infrastructure GeoJSON files when the infrastructure layer toggles are enabled.
Current health-system frontend usage:
- The inspector reads
country.healthSystemfrompublic/data/canonical-country-data.json - Country map modes currently expose:
Health Capacity,Hospital Beds / 1,000,Physicians / 1,000,Nurses & Midwives / 1,000,Health Spend per Capita, andHealth Spend (% GDP) - Health confidence remains in canonical data for simulation/data-quality use, but is not currently shown as a dedicated frontend map layer
Current health-emergency-preparedness frontend usage:
- The inspector reads
country.healthEmergencyPreparednessfrompublic/data/canonical-country-data.json - Country map modes currently expose:
Health Emergency Preparedness - This dataset is country-level only and currently reflects only the average WHO IHR SPAR score, not the full capacity-by-capacity SPAR breakdown
Current public-health-environment frontend usage:
- The inspector reads
country.publicHealthEnvironmentfrompublic/data/canonical-country-data.json - Country map modes currently expose:
Public Health Environment,Waterborne Disease Risk,Hygiene Transmission Risk,Service Reliability,Safely Managed Drinking Water,Safely Managed Sanitation,Basic Handwashing Facilities, andAccess to Electricity - This dataset is country-level only and should be treated as a strategic public-health environment / services baseline rather than local water infrastructure or household-level routing data
Each country in public/data/canonical-country-data.json can contain:
economydemographicsfiscalgovernancetradeStructuresecurityhealthSystemhealthEmergencyPreparednesspublicHealthEnvironmentpoliticalSystemsettlement
Each province in public/data/canonical-province-data.json currently contains:
provinceIdprovinceNamecountryIso3countryNameareaKm2settlementinfrastructure
The infrastructure object currently includes:
- strategic airport, port, rail, and highway stats
- a province-level strategic
connectivityScore - province-to-province transport connection summaries under
infrastructure.connections - an abstract
connectionScorederived from the strategic road and rail connection graph
The Natural Earth strategic-infrastructure pipeline now produces three different kinds of outputs:
public/data/infrastructure-stats.jsonProvince-level strategic infrastructure stats and the data that roll into canonical province and country infrastructure sections.public/data/infrastructure-connections.jsonAn abstract province-to-province strategic connection graph derived from Natural Earth 1:10m roads and railroads.public/data/infrastructure-airports.geojsonpublic/data/infrastructure-ports.geojsonpublic/data/infrastructure-railroads.geojsonpublic/data/infrastructure-highways.geojsonFrontend-ready visualization layers for actual map rendering.public/data/infrastructure-visual-layers-coverage.jsonCoverage and export summary for the frontend visualization layers.
The app uses these in two separate ways:
- Province thematic overlays:
connectivityScore,connectionScore, airport/port counts, highway-connected province count, rail-connected province count, connected-country count, and density views. - Actual infrastructure visual layers: airports, major airports, ports, major ports, railroads, and highways rendered directly on the map as points and lines.
Important scope note:
- These visual layers show generalized Natural Earth 1:10m strategic infrastructure.
- They are not OpenStreetMap-derived.
- They are not a routing network.
- They do not represent local or rural roads.
- They are intended for map visualization and high-level gameplay context, not turn-by-turn travel modeling.
- Point features are matched to provinces for metadata only.
Each numeric field is stored as:
valueyearsource
Political-system text and booleans are stored as Factbook-derived fields with a source.
Settlement caveat:
- Current GHSL-derived
urbanCentre*province and country settlement fields are UCDB urban-centre aggregates, whileraster*fields are full-province or full-country GHSL raster totals. settlementDataCompletenessandrasterSettlementDataCompletenessare intentionally separate.settlementDataCompleteness.value = "urban-centres-only"refers to the UCDB-only urban-centre rollup.rasterSettlementDataCompleteness.valuereports whether province-wide GHSL raster aggregation was available for population and built-up surface.
This is the primary map geometry used by the frontend.
- The checked-in file currently appears to be Natural Earth Admin-1 style province/state geometry.
- The file name inside the GeoJSON is
ne_10m_admin_1_states_provinces. - Country membership is inferred from province properties such as
admin,adm0_a3,sov_a3, and the derived__countryKey.
This file is generated from provinces by scripts/generateCountryBorders.mjs.
- Provinces are grouped by country key.
- Country polygons are dissolved with Turf
union. - If dissolve fails, the script falls back to combined geometry.
- The output is used for country border rendering overlays.
These are the datasets currently used by the project, the files they generate, and exactly how they feed the canonical build.
Script:
scripts/importWorldBankCountryStats.mjs
Generated files:
public/data/country-stats.jsonpublic/data/country-stats-coverage.json
Source behavior:
- Uses the World Bank API, source
2 - Prefers year
2024 - Falls back to year
2023 - Filters out aggregate/non-country rows
Imported indicators:
populationgdpCurrentUsdgdpPerCapitaCurrentUsdgdpGrowthAnnualPctinflationConsumerAnnualPctunemploymentPcturbanPopulationPctlifeExpectancyYearstradePctOfGdp
How it is used in canonical data:
- Always supplies
population - Always supplies
unemploymentPct - Always supplies
urbanPopulationPct - Always supplies
lifeExpectancyYears - Always supplies
tradePctOfGdp - Competes with IMF for
gdpCurrentUsd - Competes with IMF for
gdpPerCapitaCurrentUsd - Competes with IMF for
gdpGrowthAnnualPct - Competes with IMF for
inflationAnnualPct
Script:
scripts/importWorldBankGovernanceStats.mjs
Generated files:
public/data/governance-stats.jsonpublic/data/governance-stats-coverage.json
Source behavior:
- Uses the World Bank API, source
75 - Prefers year
2024 - Falls back to year
2023 - Filters out aggregate/non-country rows
Imported indicators:
voiceAndAccountabilitypoliticalStabilitygovernmentEffectivenessregulatoryQualityruleOfLawcontrolOfCorruption
How it is used in canonical data:
- Fully populates the
governancesection
Script:
scripts/importImfWeoStats.mjs
Generated files:
public/data/imf-weo-stats.jsonpublic/data/imf-weo-stats-coverage.json
Source behavior:
- Uses the IMF DataMapper API
- Prefers year
2024 - Falls back to year
2023 - Filters out aggregate/group rows
- Downloads indicator data in country chunks
Imported indicators:
realGdpGrowthPctgdpCurrentUsdBillionsgdpPerCapitaCurrentUsdinflationAverageConsumerPricesPctcurrentAccountBalancePctOfGdpgovernmentNetLendingBorrowingPctOfGdpgovernmentGrossDebtPctOfGdp
How it is used in canonical data:
- Fully populates the
fiscalsection - Supplies
currentAccountBalancePctOfGdp - Supplies
governmentNetLendingBorrowingPctOfGdp - Supplies
governmentGrossDebtPctOfGdp - Competes with WDI for
gdpCurrentUsd - Competes with WDI for
gdpPerCapitaCurrentUsd - Competes with WDI for
gdpGrowthAnnualPct - Competes with WDI for
inflationAnnualPct
Script:
scripts/importUnWppDemographics.mjs
Generated files:
public/data/un-wpp-demographics.jsonpublic/data/un-wpp-demographics-coverage.json
Source behavior:
- Probes the UN Data Portal API
- If API data endpoints are restricted, falls back to official WPP 2024 bulk files
- Discovers bulk-file URLs from the official downloads manifest
- Prefers year
2024 - Falls back to year
2023 - Uses the
Mediumvariant
Source files used by the importer:
WPP2024_Locations_notes.csvWPP2024_Demographic_Indicators_Medium.csv.gzWPP2024_PopulationByAge5GroupSex_Percentage_Medium.csv.gz
Directly imported indicators:
medianAgeYearsfertilityRateBirthsPerWomanpopulationGrowthRatePctnetMigration
Computed from age-structure percentages:
youthSharePctworkingAgeSharePctelderlySharePctchildDependencyRatiooldAgeDependencyRatiototalDependencyRatio
How it is used in canonical data:
- Fully populates the
demographicssection
Script:
scripts/importAtlasTradeProfiles.mjs
Generated files:
public/data/atlas-trade-profiles.jsonpublic/data/atlas-trade-profiles-coverage.json
Source behavior:
- Scans the official Atlas S3 bucket index
- Picks the best unilateral country-product dataset automatically
- Downloads the selected bulk file
- Detects the schema dynamically
- Prefers year
2024 - Falls back to year
2023 - If neither is present, uses the latest available year in the file
Current dataset note:
- The current import resolved to
country_hsproduct4digit_year.csv.zip - The current selected year is
2016
Imported and derived indicators:
totalExportsUsdtotalImportsUsdtradeBalanceUsdexportDiversityProductCountimportDiversityProductCountexportConcentrationHhiimportConcentrationHhieconomicComplexityIndex
Additional derived arrays:
topExportstopImports
How it is used in canonical data:
- Fully populates the
tradeStructuresection topExportsandtopImportsare capped to the top 10 products per flow
Script:
scripts/importFactbookPoliticalProfiles.mjs
Generated files:
public/data/factbook-political-profiles.jsonpublic/data/factbook-political-profiles-coverage.json- cached archive in
public/data/raw/factbook/factbook-source.zip
Source behavior:
- Downloads a GitHub-hosted Factbook JSON archive
- Tries
factbook/cache.factbook.jsonfirst - Falls back to
factbook/factbook.json - Parses the government section heuristically
- Matches Factbook entities to canonical ISO3 codes using aliases plus normalized country-name matching
Raw text fields imported:
governmentTypecapitaladministrativeDivisionsindependenceconstitutionlegalSystemsuffrageexecutiveBranchlegislativeBranchjudicialBranchpoliticalPartiesAndLeaderselectionsAppointmentsinternationalOrganizationParticipation
Normalized political fields derived from Factbook text:
governmentFamilyhasMonarchymonarchyTypehasParliamentlegislatureTypehasElectionshasUniversalSuffrageisFederalisRepublicisOnePartyStateisMilitaryRegimeheadOfStateTitleheadOfGovernmentTitle
How it is used in canonical data:
- Fully populates the
politicalSystemsection whenever a Factbook match exists
Script:
scripts/importWorldBankSecurityStats.mjs
Generated files:
public/data/security-stats.jsonpublic/data/security-stats-coverage.json
Source behavior:
- Uses the World Bank API, source
2 - Prefers year
2024 - Falls back to year
2023 - If neither is available for a field, uses the latest available non-null year
- Filters out aggregate/non-country rows
- Labels military spending and arms-transfer indicators as
World Bank WDI / SIPRI - Labels armed-forces personnel indicators as
World Bank WDI / IISS
Imported indicators:
militaryExpenditureUsdmilitaryExpenditurePctOfGdpmilitaryExpenditurePctOfGovtExpenditurearmedForcesPersonnelarmedForcesPctOfLaborForcearmsImportsSipriTivarmsExportsSipriTiv
Derived in the canonical builder:
militarySpendPerCapitaUsdmilitarySpendPerSoldierUsdmobilizationBasePct
How it is used in canonical data:
- Populates the
securitysection - Uses canonical WDI
populationplus imported security fields for derived indicators
Script:
scripts/importWorldBankHealthStats.mjs
Generated files:
public/data/health-stats.jsonpublic/data/health-stats-coverage.json
Source behavior:
- Uses the World Bank API, source
2 - Prefers year
2024 - Falls back to year
2023 - If neither is available for a field, uses the latest available non-null year
- Filters out aggregate/non-country rows
- Matches on ISO3 and preserves the selected year per field
- Country-level only; intended for strategic simulation mechanics rather than local hospital routing
Imported indicators:
hospitalBedsPer1000physiciansPer1000nursesMidwivesPer1000currentHealthExpenditurePerCapitaUsdcurrentHealthExpenditurePctOfGdp
Canonical fields:
- raw imported fields:
hospitalBedsPer1000,physiciansPer1000,nursesMidwivesPer1000,currentHealthExpenditurePerCapitaUsd,currentHealthExpenditurePctOfGdp - derived scores:
healthCapacityScore,medicalWorkforceScore,hospitalSurgeCapacityScore,outbreakTreatmentScore - derived data-quality / confidence signals:
healthDataFreshnessScore,healthFieldCoverageScore,healthCapacityScoreConfidence
How it is used in canonical data:
- Populates the
healthSystemsection - Leaves imported fields as
{ value, year, source } - Derives health capacity scores in the canonical builder with weighted averages over available components
- Uses the latest available non-null year when 2024/2023 are unavailable because health indicators often lag
- Separates estimated health capacity from confidence in that estimate
- Derives confidence from raw health-field coverage plus selected-year freshness
- Treats confidence as a gameplay/data-quality signal rather than a direct measure of real-world accuracy
Coverage diagnostics:
health-stats-coverage.jsonincludes per-field selected-year distributions- It also includes compact per-field year-age buckets:
>= 2023,2020-2022,2015-2019,2010-2014, and< 2010 - This is intended to help audit stale-but-usable health fields without inflating the main dataset
Script:
scripts/importWorldBankPublicHealthEnvironmentStats.mjs
Generated files:
public/data/public-health-environment-stats.jsonpublic/data/public-health-environment-stats-coverage.json
Source behavior:
- Uses the World Bank API, source
2 - Prefers year
2024 - Falls back to year
2023 - If neither is available, uses the latest available non-null year only when it is
>= 2022 - Leaves fields missing when the latest available non-null year is before
2022 - Filters out aggregate/non-country rows
- Matches on ISO3 and preserves the selected year per field
- Uses
World Bank WDI / WHO-UNICEF JMPfor water, sanitation, and handwashing fields - Uses
World Bank WDI / SDG7for electricity and clean-cooking fields - Country-level only; intended as a strategic public-health environment / basic-services baseline rather than local water infrastructure or household-level modeling
Imported indicators:
safelyManagedDrinkingWaterPctsafelyManagedSanitationPctbasicHandwashingFacilitiesPctaccessToElectricityPctruralElectricityAccessPcturbanElectricityAccessPctcleanCookingFuelAccessPct
Canonical fields:
- raw imported fields:
safelyManagedDrinkingWaterPct,safelyManagedSanitationPct,basicHandwashingFacilitiesPct,accessToElectricityPct,ruralElectricityAccessPct,urbanElectricityAccessPct,cleanCookingFuelAccessPct - derived scores:
publicHealthEnvironmentScore,waterborneDiseaseRiskScore,hygieneTransmissionRiskScore,serviceReliabilityScore - derived data-quality / confidence signals:
publicHealthEnvironmentFieldCoverageScore,publicHealthEnvironmentFreshnessScore,publicHealthEnvironmentScoreConfidence
How it is used in canonical data:
- Populates the
publicHealthEnvironmentsection - Leaves imported fields as
{ value, year, source } - Derives transparent weighted scores in the canonical builder and reweights over available components rather than forcing null when one input is missing
- Keeps higher values good for access/service scores and higher values bad for risk scores
Coverage diagnostics:
public-health-environment-stats-coverage.jsonincludes total eligible countries, matched-country count, countries with at least one field, per-field coverage counts, per-field selected-year distributions, per-field missing counts, and countries with no public-health-environment fields- It also includes per-field freshness buckets for
2024,2023,2022,missing, andstale_before_2022
Script:
scripts/importWhoIhrSparStats.mjs
Generated files:
public/data/who-ihr-spar-stats.jsonpublic/data/who-ihr-spar-stats-coverage.json
Source behavior:
- Uses the WHO Global Health Observatory OData API
- Reads indicator
SDGIHR2021 - Prefers year
2024 - Falls back to
2023, then2022 - Does not use observations before
2022 - Does not use
2025by default because Atlas Core is anchored togameStartDate: 2025-01-01 - Filters out global, regional, and non-country rows
- Matches only ISO3 codes already present in the project country-stat universe
- Country-level only; intended as a strategic preparedness baseline rather than local hospital capacity or province-scale outbreak spread modeling
Imported indicator:
ihrSparAverageScore
Canonical fields:
- raw imported field:
ihrSparAverageScore - derived preparedness fields:
outbreakPreparednessScore,outbreakPreparednessScoreConfidence
How it is used in canonical data:
- Populates the
healthEmergencyPreparednesssection - Leaves the raw WHO field as
{ value, year, source } - Sets
outbreakPreparednessScore = ihrSparAverageScorein the first pass - Derives
outbreakPreparednessScoreConfidencefrom freshness only:2024 => 100,2023 => 85,2022 => 70 - Keeps this layer separate from
healthSystem
Coverage diagnostics:
who-ihr-spar-stats-coverage.jsonincludes total eligible countries, matched-country count, countries with selected values, countries missing selected values, selected-year distribution, invalid/out-of-range value counts, aggregate/non-country rows skipped, and a capped unmatched-code sample
Scripts:
scripts/buildUrbanCentres.mjsscripts/importGhslSettlementData.mjsscripts/buildCanonicalProvinceData.mjs
Generated files:
public/data/urban-centres.jsonpublic/data/urban-centres-coverage.jsonpublic/data/province-settlement-stats.jsonpublic/data/province-settlement-stats-coverage.jsonpublic/data/canonical-province-data.jsonpublic/data/canonical-province-data-coverage.json
Source behavior:
- Uses the official GHSL UCDB release
GHS_UCDB_GLOBE_R2024A_V1_1.zip - Extracts
GHS_UCDB_GLOBE_R2024A.gpkg - Reads urban-centre records from the UCDB GeoPackage
- Matches urban-centre centroids to checked-in Natural Earth province polygons
- Builds province rollups from matched urban-centre records only
Urban-centre fields emitted:
idnamecountryIso3provinceIdlongitudelatitudepopulationbuiltUpAreaKm2populationDensityPerKm2isCapital
Province settlement fields emitted:
urbanCentrePopulationEstimateurbanCentrePopulationDensityPerKm2urbanCentreBuiltUpAreaKm2urbanCentreBuiltUpSharePcturbanCentreCountlargestUrbanCentreIdlargestUrbanCentreNamelargestUrbanCentrePopulationEstimatepopulationConcentrationHhisettlementDataCompleteness
How it is used in canonical data:
canonical-province-data.jsonis the frontend's province settlement inputcanonical-country-data.jsonrolls up UCDB-only country settlement summaries from canonical province data plus urban-centre records- Country settlement currently includes
urbanCentreCount,largestUrbanCentres,urbanCentreBuiltUpAreaKm2,urbanCentreBuiltUpSharePct,populationConcentrationHhi,provincePopulationCoveragePct, andsettlementDataCompleteness
Important semantic note:
- These are not whole-province population or built-up totals.
- They are matched urban-centre aggregates only.
- The field names intentionally use
urbanCentre*wording to avoid implying full raster coverage.
Scripts:
scripts/importGhslRasterSettlementData.mjsscripts/lib/ghslRaster.mjsscripts/buildCanonicalProvinceData.mjsscripts/buildCanonicalCountryData.mjs
Generated files:
public/data/province-raster-settlement-stats.jsonpublic/data/province-raster-settlement-stats-coverage.jsonpublic/data/raw/ghsl-raster/province-index-4326-30ss.geojsonpublic/data/raw/ghsl-raster/province-id-mask-population-4326-30ss.tifpublic/data/raw/ghsl-raster/province-id-mask-built-4326-30ss.tif
Temporary checkpoint files during processing:
public/data/province-raster-settlement-stats.partial.jsonpublic/data/province-raster-settlement-stats-progress.json
Source behavior:
- Uses GHSL 2025 30-arcsecond global rasters for population and built-up surface.
- Resolves rasters in this order: explicit local override path, cached file in
public/data/raw/ghsl-raster/, then download URL. - Accepts either ZIP archives or extracted GeoTIFFs for the population and built-up inputs.
- Uses GDAL by default for the fast path.
- Rasterizes province numeric ids onto each raster's native grid, then scans rows to aggregate province totals without point-in-polygon checks.
- Creates separate province-id masks for the population and built rasters so slightly different GHSL grids do not need to be force-aligned.
- Preserves a slower polygon fallback behind
GHSL_RASTER_USE_SLOW_POLYGON_MODE=1.
Raster fields emitted:
rasterPopulationEstimaterasterPopulationDensityPerKm2rasterBuiltUpSurfaceKm2rasterBuiltUpSurfaceSharePctrasterPopulationPerBuiltUpKm2rasterSettlementDataCompleteness
Derived canonical settlement fields that depend on raster population:
nonUrbanCentrePopulationEstimateurbanCentrePopulationSharePct
Runtime requirements and controls:
- GDAL is required for the fast importer path. If it is missing, the importer fails clearly unless
GHSL_RASTER_USE_SLOW_POLYGON_MODE=1is set. GHSL_POP_RASTER_PATHGHSL_BUILT_RASTER_PATHGHSL_POP_RASTER_URLGHSL_BUILT_RASTER_URLGHSL_RASTER_MAX_PROVINCESGHSL_RASTER_MAX_ROWSGHSL_RASTER_RESUME=1GHSL_RASTER_USE_SLOW_POLYGON_MODE=1
How it is used in canonical data:
canonical-province-data.jsonmerges UCDBurbanCentre*fields with province-wide GHSLraster*fields.- Province
nonUrbanCentrePopulationEstimateandurbanCentrePopulationSharePctare derived from raster population, treating missing UCDB urban-centre population as0when raster population exists. canonical-country-data.jsonrolls province raster totals up to country-levelraster*settlement fields and applies the same UCDB-as-zero rule for raster-derived non-urban and share metrics.
Script:
scripts/importNaturalEarthInfrastructure.mjs
Generated files:
public/data/infrastructure-stats.jsonpublic/data/infrastructure-stats-coverage.jsonpublic/data/infrastructure-connections.jsonpublic/data/infrastructure-connections-coverage.jsonpublic/data/infrastructure-airports.geojsonpublic/data/infrastructure-ports.geojsonpublic/data/infrastructure-railroads.geojsonpublic/data/infrastructure-highways.geojsonpublic/data/infrastructure-visual-layers-coverage.json- cached archives in
public/data/raw/natural-earth-infrastructure/
Source behavior:
- Downloads Natural Earth 1:10m cultural transport layers for roads, railroads, airports, and ports
- Caches the raw ZIP archives locally
- Matches airports and ports to province polygons, with a small nearest-province fallback for near-boundary points
- Splits roads and railroads into coordinate-to-coordinate segments and assigns segment length by midpoint province
- Densifies kept roads and railroads at a coarse interval to derive a province-to-province strategic connection graph
- Uses the Natural Earth roads layer as high-level strategic transport only and defensively filters obviously minor classes only when usable hierarchy signals exist
- Exports frontend-ready GeoJSON layers so the map UI does not need to parse shapefiles or raw archives
Province infrastructure fields emitted:
airports.countairports.majorCountairports.hasAirportports.countports.majorCountports.hasPortrail.lengthKmrail.densityKmPer1000Km2rail.hasRailroads.highwayLengthKmroads.densityKmPer1000Km2roads.hasHighwayconnectivityScoreconnections.highwayConnectedProvinceCountconnections.railConnectedProvinceCountconnections.connectedProvinceCountconnections.connectedCountryCountconnections.hasInternationalHighwayConnectionconnections.hasInternationalRailConnectionconnectionScore
Country infrastructure rollups emitted:
- airport and port totals plus province counts
- rail and highway totals plus country-level densities
- province-weighted strategic
connectivityScore connections.domesticHighwayEdgeCountconnections.domesticRailEdgeCountconnections.internationalHighwayEdgeCountconnections.internationalRailEdgeCountconnections.connectedCountryCountconnections.internationallyConnectedCountryIso3s
How it is used in canonical data:
canonical-province-data.jsoncarries the province-level strategic infrastructure bundle underprovince.infrastructurecanonical-country-data.jsonrolls province infrastructure up to country-level counts, lengths, densities, and connectivityinfrastructure-connections.jsonis the abstract province-to-province strategic road and rail graphinfrastructure-*.geojsonfiles are used by the frontend to render actual airports, ports, railroads, and highways as map layers- The infrastructure layer is intentionally strategic and generalized, not a street-level routing or rural-access dataset
The canonical merge step is implemented in scripts/buildCanonicalCountryData.mjs.
Input files:
public/data/country-stats.jsonpublic/data/governance-stats.jsonpublic/data/imf-weo-stats.jsonpublic/data/un-wpp-demographics.jsonpublic/data/atlas-trade-profiles.jsonpublic/data/factbook-political-profiles.jsonpublic/data/security-stats.jsonpublic/data/health-stats.jsonpublic/data/public-health-environment-stats.jsonpublic/data/who-ihr-spar-stats.jsonpublic/data/urban-centres.jsonpublic/data/canonical-province-data.json
Generated files:
public/data/canonical-country-data.jsonpublic/data/canonical-country-data-coverage.json
Country naming precedence in the canonical builder:
- WDI
- IMF
- WGI
- WPP
- Atlas
- Factbook
- Public health environment dataset
- fallback to ISO3
These four indicators exist in both WDI and IMF:
gdpCurrentUsdgdpPerCapitaCurrentUsdgdpGrowthAnnualPctinflationAnnualPct
The merge rule is:
- choose the newer year if one source is newer
- if years tie, choose the source with broader coverage for that indicator
- if coverage also ties, default to IMF
The canonical builder maps them as follows:
gdpCurrentUsd: WDIgdpCurrentUsdvs IMFgdpCurrentUsdBillions * 1_000_000_000gdpPerCapitaCurrentUsd: WDI vs IMF direct overlapgdpGrowthAnnualPct: WDIgdpGrowthAnnualPctvs IMFrealGdpGrowthPctinflationAnnualPct: WDIinflationConsumerAnnualPctvs IMFinflationAverageConsumerPricesPct
economy
population: WDIgdpCurrentUsd: WDI or IMF via overlap logicgdpPerCapitaCurrentUsd: WDI or IMF via overlap logicgdpGrowthAnnualPct: WDI or IMF via overlap logicinflationAnnualPct: WDI or IMF via overlap logicunemploymentPct: WDIurbanPopulationPct: WDIlifeExpectancyYears: WDItradePctOfGdp: WDI
demographics
- all demographic metrics: WPP
fiscal
- all fiscal metrics: IMF
governance
- all governance metrics: WGI
tradeStructure
- all trade-structure metrics and top product arrays: Atlas
security
- imported military spending and arms-transfer metrics: World Bank WDI / SIPRI
- imported armed-forces personnel metrics: World Bank WDI / IISS
- derived per-capita / per-soldier / mobilization metrics: canonical builder
healthSystem
- imported health capacity fields: World Bank WDI / WHO
medicalWorkforceScore:0.55 * norm(physiciansPer1000) + 0.45 * norm(nursesMidwivesPer1000)hospitalSurgeCapacityScore:norm(hospitalBedsPer1000)healthCapacityScore:0.30 * norm(hospitalBedsPer1000) + 0.25 * norm(physiciansPer1000) + 0.20 * norm(nursesMidwivesPer1000) + 0.15 * norm(currentHealthExpenditurePerCapitaUsd) + 0.10 * norm(currentHealthExpenditurePctOfGdp)outbreakTreatmentScore:0.60 * healthCapacityScore + 0.20 * norm(governance.governmentEffectiveness) + 0.10 * norm(governance.ruleOfLaw) + 0.10 * norm(infrastructure.connectivityScore)healthDataFreshnessScore: weighted average of year freshness factors across available raw health fields, multiplied by100healthFieldCoverageScore:available raw health field count / 5 * 100healthCapacityScoreConfidence:0.65 * healthFieldCoverageScore + 0.35 * healthDataFreshnessScore- score normalization uses winsorized percentile bounds with clamped
0..100output - missing score components are reweighted over available inputs instead of forcing null
- confidence does not directly modify
healthCapacityScore; downstream simulation systems can decide whether to apply confidence adjustments - the frontend currently emphasizes the raw health-capacity factors plus
healthCapacityScore; confidence remains available in canonical data but is not surfaced as a dedicated map layer
healthEmergencyPreparedness
- imported WHO field:
ihrSparAverageScore outbreakPreparednessScore: currently equalsihrSparAverageScoreoutbreakPreparednessScoreConfidence: freshness-only confidence where2024 => 100,2023 => 85,2022 => 70- the dataset is WHO self-assessment / self-reporting
- the dataset is intentionally country-level only and currently imports only the average SPAR score, not all 15 capacity scores
- this layer is kept separate from
healthSystemso strategic emergency preparedness is not conflated with hospital-treatment capacity
publicHealthEnvironment
- imported WASH fields: World Bank WDI / WHO-UNICEF JMP
- imported electricity and clean-cooking fields: World Bank WDI / SDG7
publicHealthEnvironmentScore: weighted average over available:0.25 * safelyManagedDrinkingWaterPct + 0.25 * safelyManagedSanitationPct + 0.20 * basicHandwashingFacilitiesPct + 0.15 * accessToElectricityPct + 0.15 * cleanCookingFuelAccessPctwaterborneDiseaseRiskScore:100 - (0.55 * safelyManagedDrinkingWaterPct + 0.45 * safelyManagedSanitationPct)reweighted over available componentshygieneTransmissionRiskScore:100 - (0.50 * basicHandwashingFacilitiesPct + 0.30 * safelyManagedSanitationPct + 0.20 * safelyManagedDrinkingWaterPct)reweighted over available componentsserviceReliabilityScore:0.50 * accessToElectricityPct + 0.20 * ruralElectricityAccessPct + 0.10 * urbanElectricityAccessPct + 0.20 * cleanCookingFuelAccessPctreweighted over available componentspublicHealthEnvironmentFieldCoverageScore:available raw field count / 7 * 100publicHealthEnvironmentFreshnessScore: average freshness over available raw fields where2024 => 100,2023 => 85,2022 => 70publicHealthEnvironmentScoreConfidence:0.65 * publicHealthEnvironmentFieldCoverageScore + 0.35 * publicHealthEnvironmentFreshnessScore- derived scores use the newest contributing raw-field year and source
Atlas Core derived from World Bank public health environment indicators - the dataset is intentionally country-level only and should be treated as a strategic baseline rather than local water infrastructure or household-level modeling
politicalSystem
- all text, boolean, and normalized political-system fields: CIA World Factbook
settlement
- province-derived urban-centre rollups: GHSL UCDB + Natural Earth province geometry
- largest urban-centre lists: GHSL UCDB
- province-derived raster population and built-up rollups: GHSL GHS-POP R2023A + GHSL GHS-BUILT-S R2023A
- derived non-urban and urban-share metrics: canonical province rollups built from raster population plus UCDB urban-centre population
Every import stage writes a coverage file to public/data/.
Current coverage outputs:
country-stats-coverage.jsongovernance-stats-coverage.jsonimf-weo-stats-coverage.jsonun-wpp-demographics-coverage.jsonatlas-trade-profiles-coverage.jsonfactbook-political-profiles-coverage.jsonsecurity-stats-coverage.jsonhealth-stats-coverage.jsonpublic-health-environment-stats-coverage.jsonwho-ihr-spar-stats-coverage.jsonurban-centres-coverage.jsonprovince-settlement-stats-coverage.jsonprovince-raster-settlement-stats-coverage.jsoninfrastructure-stats-coverage.jsoninfrastructure-connections-coverage.jsoninfrastructure-visual-layers-coverage.jsoncanonical-province-data-coverage.jsoncanonical-country-data-coverage.json
There is also an overlap audit script:
scripts/auditCountryStatsOverlap.mjs
It compares duplicated macro indicators across WDI and IMF to help validate merge decisions.
Run the full pipeline in this order:
npm run import:wdi
npm run import:wgi
npm run import:weo
npm run import:wpp
npm run import:atlas
npm run import:factbook
npm run import:security
npm run import:health
npm run import:public-health-environment
npm run import:ihr-spar
npm run build:urban-centres
npm run import:ghsl
npm run import:ghsl-raster
npm run import:infrastructure
npm run build:province-data
npm run build:country-data
npm run generate:country-borders
npm run buildOptional audit:
npm run audit:statsFor GHSL raster imports, the importer first checks local override paths, then cached files under public/data/raw/ghsl-raster/, then download URLs. Supported overrides:
GHSL_POP_RASTER_PATHGHSL_BUILT_RASTER_PATHGHSL_POP_RASTER_URLGHSL_BUILT_RASTER_URL
The fast raster importer also supports:
GHSL_RASTER_MAX_PROVINCES: limit province output for debug runsGHSL_RASTER_MAX_ROWS: limit raster row scanning for debug runsGHSL_RASTER_RESUME=1: resume from the checkpoint files if they existGHSL_RASTER_USE_SLOW_POLYGON_MODE=1: bypass GDAL and use the slower polygon-based fallback
While npm run import:ghsl-raster is running, it logs source resolution, ZIP extraction, raster metadata, and aggregation progress. It also writes checkpoint files so long runs can be resumed or inspected mid-run.
dev: start Vite dev serverbuild: run TypeScript build and Vite production buildpreview: preview the production buildimport:wdi: import World Bank WDI country indicatorsimport:wgi: import World Bank WGI governance indicatorsimport:weo: import IMF WEO / DataMapper indicatorsimport:wpp: import UN WPP demographicsimport:atlas: import Atlas trade structure dataimport:factbook: import CIA Factbook political-system dataimport:security: import World Bank security indicatorsimport:health: import World Bank health-system indicatorsimport:public-health-environment: import World Bank public-health-environment and basic-services indicatorsimport:ihr-spar: import WHO GHO IHR SPAR country-level preparedness scoresbuild:urban-centres: build GHSL urban-centre records and coverageimport:ghsl: build province settlement rollups from matched GHSL urban centresimport:ghsl-raster: build province-wide GHSL raster population and built-up settlement rollups with GDAL mask aggregation and checkpoint filesimport:infrastructure: build strategic infrastructure stats, connection graph, and frontend-ready Natural Earth visualization layersbuild:province-data: build canonical province settlement and infrastructure databuild:country-data: build canonical merged country data, including rolled-up infrastructuregenerate:country-borders: derive country borders from provincesaudit:stats: audit overlapping WDI/IMF macro indicators
public/data/
provinces.geojson
country-borders.geojson
country-stats.json
governance-stats.json
imf-weo-stats.json
un-wpp-demographics.json
atlas-trade-profiles.json
factbook-political-profiles.json
security-stats.json
health-stats.json
public-health-environment-stats.json
who-ihr-spar-stats.json
urban-centres.json
province-settlement-stats.json
province-raster-settlement-stats.json
infrastructure-stats.json
infrastructure-connections.json
infrastructure-airports.geojson
infrastructure-ports.geojson
infrastructure-railroads.geojson
infrastructure-highways.geojson
canonical-province-data.json
canonical-country-data.json
*-coverage.json
raw/natural-earth-infrastructure/
raw/ghsl-raster/
province-index-4326-30ss.geojson
province-id-mask-population-4326-30ss.tif
province-id-mask-built-4326-30ss.tif
scripts/
importWorldBankCountryStats.mjs
importWorldBankGovernanceStats.mjs
importImfWeoStats.mjs
importUnWppDemographics.mjs
importAtlasTradeProfiles.mjs
importFactbookPoliticalProfiles.mjs
importWorldBankSecurityStats.mjs
importWorldBankHealthStats.mjs
importWorldBankPublicHealthEnvironmentStats.mjs
importWhoIhrSparStats.mjs
buildUrbanCentres.mjs
importGhslSettlementData.mjs
importGhslRasterSettlementData.mjs
importNaturalEarthInfrastructure.mjs
buildCanonicalProvinceData.mjs
buildCanonicalCountryData.mjs
generateCountryBorders.mjs
auditCountryStatsOverlap.mjs
lib/infrastructureConnections.mjs
lib/ghslRaster.mjs
src/map/
GameCanvas.tsx
GameCanvas.css- The in-game start date is fixed to
2025-01-01, but source datasets have real-world publication lags. - WDI, WGI, IMF, and WPP all prefer
2024with2023fallback where needed. - Security stats prefer
2024, then2023, then the latest non-null year available per field. - Health-system stats prefer
2024, then2023, then the latest non-null year available per field because publication lags are common. - Public-health-environment stats prefer
2024, then2023, then the latest non-null year available per field only when that year is>= 2022. - WHO IHR SPAR stats prefer
2024, then2023, then2022, and intentionally do not use2025by default against the2025-01-01baseline. - Atlas currently resolves to
2016because the selected official file does not expose2024or2023. - Factbook matching is incomplete for some territories, oceans, and supranational entities.
- Province geometry is checked in directly and treated as ground truth by the current frontend.
- GHSL settlement coverage now separates UCDB urban-centre aggregates (
urbanCentre*) from full raster province and country totals (raster*). - The health-system dataset is intentionally country-level only and should not be treated as local hospital routing or province-scale capacity data.
- The WHO IHR SPAR dataset is self-assessment / self-reporting, country-level only, and should be treated as a strategic emergency-preparedness baseline rather than local hospital capacity or province-level disease-spread modeling.
- The current WHO IHR SPAR import only brings in the average SPAR score, not the full 15-capacity breakdown.
- The public-health-environment dataset is intentionally country-level only and should be treated as a strategic public-health environment / services baseline, not local water infrastructure or household-level modeling.
settlementDataCompletenessdescribes the UCDB urban-centre subset, whilerasterSettlementDataCompletenessdescribes province-wide GHSL raster coverage.- The fast GHSL raster importer depends on GDAL unless
GHSL_RASTER_USE_SLOW_POLYGON_MODE=1is used. - Natural Earth infrastructure is generalized 1:10m data intended for strategic map context.
- The visual infrastructure layers are not a detailed routing network and do not model local or rural accessibility.
- The province-to-province connection graph is an abstract transport graph derived from generalized Natural Earth roads and railroads.
- Keep
public/data/canonical-country-data.jsonas the single country-state input for gameplay systems. - Keep
public/data/canonical-province-data.jsonas the province settlement input for province-level overlays and inspectors. - Keep
public/data/infrastructure-*.geojsonas the frontend visualization inputs for actual strategic infrastructure layers. - Add new datasets through importer scripts rather than frontend-specific data patches.
- Extend
scripts/buildCanonicalCountryData.mjswhen adding new canonical fields. - Extend
scripts/buildCanonicalProvinceData.mjs,scripts/lib/ghslSettlement.mjs, andscripts/lib/ghslRaster.mjswhen adding better province settlement coverage. - Extend
scripts/importNaturalEarthInfrastructure.mjsandscripts/lib/infrastructureConnections.mjswhen adding or refining strategic infrastructure layers. - Preserve the province-first geometry model and treat country-level values as overlays.