diff --git a/superset-frontend/plugins/plugin-chart-country-map/scripts/build.py b/superset-frontend/plugins/plugin-chart-country-map/scripts/build.py index bcc667eb9b4..caa3dadfc34 100755 --- a/superset-frontend/plugins/plugin-chart-country-map/scripts/build.py +++ b/superset-frontend/plugins/plugin-chart-country-map/scripts/build.py @@ -871,6 +871,13 @@ def write_manifest(targets: list[tuple[str, int]]) -> Path: manifest_path = OUTPUT_DIR / "manifest.json" manifest_path.write_text(json.dumps(manifest, indent=2)) + + # Also write a copy into the plugin source tree so the control + # panel can `import manifest from '../data/manifest.json'` without + # an async fetch at chart-edit time. + plugin_data_dir = SCRIPT_DIR.parent / "src" / "data" + plugin_data_dir.mkdir(parents=True, exist_ok=True) + (plugin_data_dir / "manifest.json").write_text(json.dumps(manifest, indent=2)) log( f"\nWrote manifest.json — {len(worldviews)} worldview(s), " f"{sum(len(v) for v in countries_by_wv.values())} country files, " diff --git a/superset-frontend/plugins/plugin-chart-country-map/src/data/manifest.json b/superset-frontend/plugins/plugin-chart-country-map/src/data/manifest.json new file mode 100644 index 00000000000..596b79303af --- /dev/null +++ b/superset-frontend/plugins/plugin-chart-country-map/src/data/manifest.json @@ -0,0 +1,263 @@ +{ + "ne_pinned_tag": "v5.1.2", + "ne_pinned_sha": "f1890d9f152c896d250a77557a5751a93d494776", + "build_timestamp_utc": "2026-05-13T00:31:41.547398+00:00", + "worldviews": [ + "ukr" + ], + "admin_levels": [ + 0, + 1 + ], + "countries_by_worldview": { + "ukr": [ + "AFG", + "AGO", + "AIA", + "ALB", + "ALD", + "AND", + "ARE", + "ARG", + "ARM", + "ASM", + "ATA", + "ATF", + "ATG", + "AUS", + "AUT", + "AZE", + "BDI", + "BEL", + "BEN", + "BFA", + "BGD", + "BGR", + "BHR", + "BHS", + "BIH", + "BLR", + "BLZ", + "BMU", + "BOL", + "BRA", + "BRB", + "BRN", + "BTN", + "BWA", + "CAF", + "CAN", + "CHE", + "CHL", + "CHN", + "CIV", + "CMR", + "COD", + "COG", + "COK", + "COL", + "COM", + "CPV", + "CRI", + "CUB", + "CYP", + "CZE", + "DEU", + "DJI", + "DMA", + "DNK", + "DOM", + "DZA", + "ECU", + "EGY", + "ERI", + "ESP", + "EST", + "ETH", + "FIN", + "FJI", + "FRA", + "FSM", + "GAB", + "GBR", + "GEO", + "GHA", + "GIN", + "GMB", + "GNB", + "GNQ", + "GRC", + "GRD", + "GRL", + "GTM", + "GUY", + "HKG", + "HND", + "HRV", + "HTI", + "HUN", + "IDN", + "IND", + "IOA", + "IRL", + "IRN", + "IRQ", + "ISL", + "ISR", + "ITA", + "JAM", + "JOR", + "JPN", + "KAZ", + "KEN", + "KGZ", + "KHM", + "KIR", + "KNA", + "KOR", + "KOS", + "KWT", + "LAO", + "LBN", + "LBR", + "LBY", + "LCA", + "LIE", + "LKA", + "LSO", + "LTU", + "LUX", + "LVA", + "MAR", + "MDA", + "MDG", + "MDV", + "MEX", + "MHL", + "MKD", + "MLI", + "MLT", + "MMR", + "MNE", + "MNG", + "MNP", + "MOZ", + "MRT", + "MSR", + "MUS", + "MWI", + "MYS", + "NAM", + "NCL", + "NER", + "NGA", + "NIC", + "NLD", + "NOR", + "NPL", + "NRU", + "NZL", + "OMN", + "PAK", + "PAN", + "PER", + "PHL", + "PLW", + "PNG", + "POL", + "PRK", + "PRT", + "PRY", + "PSX", + "PYF", + "QAT", + "ROU", + "RUS", + "RWA", + "SAU", + "SDN", + "SDS", + "SEN", + "SGP", + "SHN", + "SLB", + "SLE", + "SLV", + "SMR", + "SOM", + "SPM", + "SRB", + "STP", + "SUR", + "SVK", + "SVN", + "SWE", + "SWZ", + "SYC", + "SYR", + "TCA", + "TCD", + "TGO", + "THA", + "TJK", + "TKM", + "TLS", + "TON", + "TTO", + "TUN", + "TUR", + "TWN", + "TZA", + "UGA", + "UKR", + "UMI", + "URY", + "USA", + "UZB", + "VCT", + "VEN", + "VIR", + "VNM", + "VUT", + "WLF", + "WSM", + "YEM", + "ZAF", + "ZMB", + "ZWE" + ] + }, + "regional_aggregations": [ + { + "country": "FRA", + "set_id": "regions", + "worldview": "ukr", + "size_bytes": 32077 + }, + { + "country": "ITA", + "set_id": "regions", + "worldview": "ukr", + "size_bytes": 32116 + }, + { + "country": "PHL", + "set_id": "regions", + "worldview": "ukr", + "size_bytes": 31805 + }, + { + "country": "TUR", + "set_id": "nuts_1", + "worldview": "ukr", + "size_bytes": 23036 + } + ], + "composites": [ + { + "id": "france_overseas", + "worldview": "ukr", + "size_bytes": 322058 + } + ] +} \ No newline at end of file diff --git a/superset-frontend/plugins/plugin-chart-country-map/src/plugin/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-country-map/src/plugin/controlPanel.tsx index d3d1ae84f16..604e19463e1 100644 --- a/superset-frontend/plugins/plugin-chart-country-map/src/plugin/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-country-map/src/plugin/controlPanel.tsx @@ -24,24 +24,54 @@ import { D3_FORMAT_OPTIONS, getStandardizedControls, } from '@superset-ui/chart-controls'; +import manifest from '../data/manifest.json'; // ---------------------------------------------------------------------- -// Choice tables +// Choice tables — sourced from the build pipeline's manifest.json // -// These are hardcoded for the POC. A follow-up commit will replace them -// with options pulled from the build pipeline's manifest.json so adding -// a new worldview or country only requires a build script run, not a -// plugin code change. +// The manifest is regenerated by every `./scripts/build.sh` run; adding +// a new worldview or country to the YAML configs and re-running build +// makes the new option appear in the UI without touching this file. // ---------------------------------------------------------------------- -const WORLDVIEW_CHOICES: Array<[string, string]> = [ - ['ukr', t('Ukraine (default — Crimea as Ukrainian)')], - ['default', t('Natural Earth Default')], - // Future: pull the rest from the build manifest. Available NE - // worldviews include arg, bdg, bra, chn, deu, egy, esp, fra, gbr, - // grc, idn, ind, isr, ita, jpn, kor, mar, nep, nld, pak, pol, prt, - // pse, rus, sau, swe, tur, twn, ukr, usa, vnm. -]; +interface CountryMapManifest { + worldviews: string[]; + admin_levels: number[]; + countries_by_worldview: Record; + regional_aggregations: Array<{ + country: string; + set_id: string; + worldview: string; + size_bytes: number; + }>; + composites: Array<{ id: string; worldview: string; size_bytes: number }>; +} + +const M = manifest as CountryMapManifest; + +// Display-name labels for worldviews. Anything not listed here renders +// as the worldview code itself in the dropdown. +const WORLDVIEW_LABELS: Record = { + ukr: t('Ukraine (default — Crimea as Ukrainian)'), + default: t('Natural Earth Default'), + ind: t('India'), + pak: t('Pakistan'), + chn: t('China'), + rus: t('Russia'), + isr: t('Israel'), + pse: t('Palestine'), + tur: t('Türkiye'), + jpn: t('Japan'), + kor: t('Korea'), + usa: t('United States'), + fra: t('France'), + gbr: t('United Kingdom'), +}; + +const WORLDVIEW_CHOICES: Array<[string, string]> = M.worldviews.map(wv => [ + wv, + WORLDVIEW_LABELS[wv] || wv, +]); const ADMIN_LEVEL_CHOICES: Array<[string, string]> = [ [String(0), t('Countries (Admin 0)')], @@ -49,68 +79,150 @@ const ADMIN_LEVEL_CHOICES: Array<[string, string]> = [ ['aggregated', t('Aggregated regions')], ]; -// ISO_A3 country codes that have at least 2 subdivisions in NE Admin 1. -// Hardcoded snapshot — should come from build manifest in follow-up. -const COUNTRY_CHOICES: Array<[string, string]> = [ - ['USA', t('United States')], - ['CAN', t('Canada')], - ['MEX', t('Mexico')], - ['BRA', t('Brazil')], - ['ARG', t('Argentina')], - ['GBR', t('United Kingdom')], - ['FRA', t('France')], - ['DEU', t('Germany')], - ['ITA', t('Italy')], - ['ESP', t('Spain')], - ['NLD', t('Netherlands')], - ['BEL', t('Belgium')], - ['CHE', t('Switzerland')], - ['AUT', t('Austria')], - ['POL', t('Poland')], - ['SWE', t('Sweden')], - ['NOR', t('Norway')], - ['FIN', t('Finland')], - ['DNK', t('Denmark')], - ['PRT', t('Portugal')], - ['GRC', t('Greece')], - ['TUR', t('Türkiye')], - ['UKR', t('Ukraine')], - ['RUS', t('Russia')], - ['CHN', t('China (incl. Taiwan / HK / Macau)')], - ['JPN', t('Japan')], - ['KOR', t('South Korea')], - ['IND', t('India')], - ['IDN', t('Indonesia')], - ['THA', t('Thailand')], - ['VNM', t('Vietnam')], - ['PHL', t('Philippines')], - ['MYS', t('Malaysia')], - ['AUS', t('Australia')], - ['NZL', t('New Zealand')], - ['ZAF', t('South Africa')], - ['EGY', t('Egypt')], - ['MAR', t('Morocco')], - ['NGA', t('Nigeria')], - ['KEN', t('Kenya')], - ['ETH', t('Ethiopia')], - ['LVA', t('Latvia')], -]; - -// Maps a country to its available regional aggregation sets, sourced -// from regional_aggregations.yaml. Hardcoded snapshot. -const REGION_SET_CHOICES_BY_COUNTRY: Record> = { - TUR: [['nuts_1', t('NUTS-1 statistical regions')]], - FRA: [['regions', t('Administrative regions')]], - ITA: [['regions', t('Administrative regions')]], - PHL: [['regions', t('Administrative regions')]], +// English country names by ISO_A3, for friendly dropdown labels. Codes +// not listed here render as the raw ISO code (rare; missing entries +// here aren't a correctness problem). +const COUNTRY_LABELS: Record = { + AFG: 'Afghanistan', + ARG: 'Argentina', + AUS: 'Australia', + AUT: 'Austria', + BEL: 'Belgium', + BGD: 'Bangladesh', + BGR: 'Bulgaria', + BIH: 'Bosnia and Herzegovina', + BLR: 'Belarus', + BOL: 'Bolivia', + BRA: 'Brazil', + CAN: 'Canada', + CHE: 'Switzerland', + CHL: 'Chile', + CHN: 'China', + COL: 'Colombia', + CRI: 'Costa Rica', + CUB: 'Cuba', + CYP: 'Cyprus', + CZE: 'Czechia', + DEU: 'Germany', + DNK: 'Denmark', + DOM: 'Dominican Republic', + ECU: 'Ecuador', + EGY: 'Egypt', + ESP: 'Spain', + EST: 'Estonia', + ETH: 'Ethiopia', + FIN: 'Finland', + FRA: 'France', + GBR: 'United Kingdom', + GHA: 'Ghana', + GRC: 'Greece', + GTM: 'Guatemala', + HND: 'Honduras', + HRV: 'Croatia', + HUN: 'Hungary', + IDN: 'Indonesia', + IND: 'India', + IRL: 'Ireland', + IRN: 'Iran', + IRQ: 'Iraq', + ISL: 'Iceland', + ISR: 'Israel', + ITA: 'Italy', + JPN: 'Japan', + KAZ: 'Kazakhstan', + KEN: 'Kenya', + KGZ: 'Kyrgyzstan', + KHM: 'Cambodia', + KOR: 'South Korea', + LAO: 'Laos', + LBN: 'Lebanon', + LTU: 'Lithuania', + LVA: 'Latvia', + MAR: 'Morocco', + MEX: 'Mexico', + MMR: 'Myanmar', + MNG: 'Mongolia', + MYS: 'Malaysia', + NGA: 'Nigeria', + NLD: 'Netherlands', + NOR: 'Norway', + NPL: 'Nepal', + NZL: 'New Zealand', + PAK: 'Pakistan', + PER: 'Peru', + PHL: 'Philippines', + POL: 'Poland', + PRT: 'Portugal', + PRY: 'Paraguay', + ROU: 'Romania', + RUS: 'Russia', + SAU: 'Saudi Arabia', + SDN: 'Sudan', + SRB: 'Serbia', + SVK: 'Slovakia', + SVN: 'Slovenia', + SWE: 'Sweden', + SYR: 'Syria', + THA: 'Thailand', + TUR: 'Türkiye', + TWN: 'Taiwan', + UKR: 'Ukraine', + URY: 'Uruguay', + USA: 'United States', + UZB: 'Uzbekistan', + VEN: 'Venezuela', + VNM: 'Vietnam', + YEM: 'Yemen', + ZAF: 'South Africa', + ZWE: 'Zimbabwe', }; -// Composite maps from composite_maps.yaml. Each composite is an -// alternative selection for a country (sits alongside the regular -// country picker). -const COMPOSITE_CHOICES: Array<[string, string]> = [ - ['france_overseas', t('France (with overseas territories)')], -]; +const formatCountry = (code: string): string => + COUNTRY_LABELS[code] ? `${COUNTRY_LABELS[code]} (${code})` : code; + +// Build country choices from the union of all worldview manifests +// (typically the same set since Admin 1 is one global file, but +// future per-worldview Admin 1 outputs would naturally differ). +const COUNTRY_CHOICES: Array<[string, string]> = (() => { + const all = new Set(); + Object.values(M.countries_by_worldview).forEach(codes => + codes.forEach(c => all.add(c)), + ); + return Array.from(all) + .sort() + .map<[string, string]>(c => [c, formatCountry(c)]); +})(); + +// Region-set labels keyed by `` (TUR's `nuts_1` etc.). +const REGION_SET_LABELS: Record = { + nuts_1: t('NUTS-1 statistical regions'), + regions: t('Administrative regions'), +}; + +// Build {country: [(set_id, label), ...]} from manifest. +const REGION_SET_CHOICES_BY_COUNTRY: Record> = ( + () => { + const out: Record> = {}; + M.regional_aggregations.forEach(r => { + out[r.country] = out[r.country] || []; + out[r.country].push([ + r.set_id, + REGION_SET_LABELS[r.set_id] || r.set_id, + ]); + }); + return out; + } +)(); + +// Composite-map labels keyed by ``. +const COMPOSITE_LABELS: Record = { + france_overseas: t('France (with overseas territories)'), +}; + +const COMPOSITE_CHOICES: Array<[string, string]> = M.composites.map(c => [ + c.id, + COMPOSITE_LABELS[c.id] || c.id, +]); // NE NAME_ language codes available across most features. const NAME_LANGUAGE_CHOICES: Array<[string, string]> = [