// One-shot helper (not a runtime dependency): normalize a Natural Earth admin-0 // GeoJSON into public/countries.geo.json with `id` = ISO_A3 and properties.name. // Usage: node scripts/normalize-geojson.mjs import { readFileSync, writeFileSync } from 'node:fs'; const input = process.argv[2]; if (!input) { console.error('usage: node scripts/normalize-geojson.mjs '); process.exit(1); } const geo = JSON.parse(readFileSync(input, 'utf8')); // ISO_A3 can be missing/empty for a few territories; fall back to a small table // keyed by the Natural Earth `NAME` value, then to the (ISO_A2) feature id. const NAME_TO_A3 = { 'South Sudan': 'SSD', 'Somaliland': 'SOL', 'N. Cyprus': 'CNX', 'Antarctica': 'ATA', 'Western Sahara': 'ESH', 'Falkland Is.': 'FLK', 'Falkland Islands': 'FLK', 'Heard & Island McDonald Is.': 'HMD', 'French Southern & Antarctic Lands': 'ATF', 'Saint Helena, Ascension & Tristan da Cunha': 'SHN', 'Bouvet Is.': 'BVT', 'Pitcairn Is.': 'PCN', 'S. Geo. de Las Malvinas (Falkland Is.)': 'FLK', }; let withA3 = 0; let fallback = 0; const fallbackNames = []; const out = { type: 'FeatureCollection', features: geo.features.map((f) => { const p = f.properties ?? {}; let iso3 = (p.ADM0_A3 ?? p.ISO_A3 ?? p.ISO_A3_EH ?? p.SOV_A3 ?? '').toString().trim().toUpperCase(); const name = (p.ADMIN ?? p.NAME ?? f.id ?? 'Unknown').toString().trim(); if (!iso3) { iso3 = NAME_TO_A3[name] ?? (typeof f.id === 'string' && f.id.length === 2 ? f.id.toUpperCase() : 'ZZZ'); fallback++; fallbackNames.push(name); } else { withA3++; } // Trim properties to the two fields the client actually uses (keeps the file small). const feature = { type: 'Feature', id: iso3, properties: { name }, geometry: f.geometry, }; return feature; }), }; writeFileSync(new URL('../public/countries.geo.json', import.meta.url), JSON.stringify(out)); console.log(`features=${out.features.length} withA3=${withA3} fallback=${fallback}`); if (fallbackNames.length) console.log('fallbacks:', [...new Set(fallbackNames)].join(', ')); const size = JSON.stringify(out).length; console.log(`output bytes=${size}`);