Skip to content

feat(country-map): retire the notebook in favor of per-country build scripts - #45607

Open
rusackas wants to merge 17 commits into
masterfrom
draft/country-map-scripts
Open

rusackas wants to merge 17 commits into
masterfrom
draft/country-map-scripts

Conversation

@rusackas

@rusackas rusackas commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

SUMMARY

Country Map GeoJSON Generator.ipynb special-cased ~26 countries in one growing notebook, mixing shared helper functions, per-country correction logic, and inline .plot() previews. #45602 fixed the worst symptom (the whole file diffing as binary), but a 30-section notebook was still awkward to review a single-country change in, and painful to resolve real conflicts in.

This replaces it entirely with per-country Python scripts, a few shared modules, and an orchestrator CLI (build_maps.py). The notebook is deleted - npm run update-maps and the "Adding a New Country" docs now point at the script pipeline instead.

Layout

superset-frontend/plugins/plugin-chart-country-map/scripts/
  build_maps.py                 # orchestrator CLI
  maps/
    catalog.py                   # the ~214-country plain catalog + aliases
    assemble.py                   # combines catalog + module overrides, purge logic
    context.py, data.py            # Natural Earth loading
    helpers.py                      # shared geometry helpers
    output.py                        # simplify + save_geojson
    ts_writer.py                      # writes countries.ts + docs/data/countries.json
    preview.py                         # --preview: thumbnails + contact sheet
    countries/                          # one module per special-cased country (23 total)

Validation

This is the part that matters most, given it touches shipped map data. Two independent checks:

  1. Against master. Ran the full pipeline against live Natural Earth + every pinned/live community source. 197 of 201 src/countries/*.geojson files (one, madagascar.geojson, is a separate story below) matched what's currently committed byte-for-byte.
  2. Against the real notebook. Patched a copy of the actual notebook - only to skip interactive plotting and reuse the same cached data, no logic changes - and ran it end to end with nbconvert. Diffed its real output directly against this script pipeline's output: all 201 comparable GeoJSON files, countries.ts, and docs/data/countries.json came out byte-for-byte identical.

That second check is the important one: it proves the 4 files that don't match committed master aren't bugs in this conversion - they're places where upstream Natural Earth data or the notebook's own current logic had already drifted from what's committed, before this PR existed. Running the unmodified notebook today reproduces the exact same output this script pipeline does.

What actually changes in this PR (map data)

  • russia.geojson: Karelia and Murmansk now use their 1:10m boundaries instead of 1:50m, per the existing NE#929 workaround - only those two regions' geometry changes (confirmed feature-by-feature).
  • south_africa.geojson: Western Cape's geometry shifts by a handful of vertices - upstream Natural Earth coastline refinement. The Prince Edward Islands reposition still applies correctly; the other 8 provinces are byte-identical.
  • philippines.geojson (fixed, see below): now 83 real provinces instead of raw Natural Earth's 118 mixed provinces/highly-urbanized-cities.
  • madagascar.geojson (re-sourced, see below): same 23 regions, now from WHO's current endpoint instead of a dead one.

(Two earlier revisions of this PR also touched iran.geojson and uk.geojson - both have since been corrected back to match what's currently shipped; see their commit messages for why.)

I spot-checked all of the above visually (before/after renders) - no missing or distorted regions. Please still look at the rendered diffs before merging, especially south_africa.geojson since it reflects upstream data this codebase doesn't control.

Two bugs found along the way - both now fixed

  • countries.ts/docs/data/countries.json: the notebook's current countryOptions cell was missing a case for italy_regions_and_autonomous_provinces, even though the nicer "Italy (regions and autonomous provinces)" label is what's shipping today. Restored in ts_writer.py (see its own commit) since it's a pure regression with no upside.
  • philippines.geojson: the notebook fetched a community source with the Philippines' 83 provinces already corrected (Maguindanao split into del Norte/del Sur, the Davao split, Compostela Valley renamed to Davao de Oro) and even renamed its columns to match - but never put the result anywhere the final output dict could pick it up. The shipped file was still raw, outdated Natural Earth ADM2 data, complete with stray highly-urbanized-city polygons (Manila, Quezon City, Caloocan, etc. as their own "provinces"). philippines.py now returns that corrected source as the philippines entry.
  • madagascar.geojson: this one wasn't a bug in the notebook, but it surfaced while regenerating - the WHO AFRO GDB service this module queried (Hosted/AFROGDB_PUBLIC_DATA) has been retired on WHO's end and now 404s, independent of anything in this repo (the unmodified notebook hits the exact same error). Repointed at WHO's current public replacement (Hosted/AFROGDB_published_boundary, layer latest_boundary_adm1), which exposes the same fields and returns the same 23 regions - confirmed name-for-name against what was previously committed. Regenerated; boundaries are visually unchanged.

Deliberate behavior changes from the notebook (all documented in-code)

  • latvia.py, india.py, kenya.py: external-source fetch failures now raise instead of silently falling back to outdated Natural Earth boundaries (kenya.py's notebook cell already did this; latvia/india's didn't - applied for consistency).

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A (no UI surface; see Validation above for the before/after render check on the 4 changed countries)

TESTING INSTRUCTIONS

cd superset-frontend/plugins/plugin-chart-country-map/scripts
pip install geopandas shapely matplotlib requests
python3 build_maps.py --preview --out-dir /tmp/maps-out
# compare /tmp/maps-out/*.geojson against src/countries/*.geojson

Or just npm run update-maps for the real thing.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

🤖 Generated with Claude Code

@netlify

netlify Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit a0947c4
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ae526db38428cc0f06639a8
😎 Deploy Preview https://deploy-preview-45607--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.74%. Comparing base (7f2b04f) to head (dd2e9d5).
⚠️ Report is 17 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #45607   +/-   ##
=======================================
  Coverage   84.73%   84.74%           
=======================================
  Files        3069     3069           
  Lines      205819   205896   +77     
  Branches    48408    48429   +21     
=======================================
+ Hits       174403   174483   +80     
+ Misses      28122    28115    -7     
- Partials     3294     3298    +4     
Flag Coverage Δ
hive 34.49% <ø> (-0.02%) ⬇️
javascript 81.00% <ø> (+<0.01%) ⬆️
mysql 52.60% <ø> (?)
postgres 52.61% <ø> (+0.01%) ⬆️
presto 36.22% <ø> (-0.02%) ⬇️
python 87.76% <ø> (+<0.01%) ⬆️
sqlite 52.37% <ø> (-0.01%) ⬇️
unit 82.53% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@rusackas rusackas changed the title feat(country-map): prototype per-country build scripts as a notebook alternative feat(country-map): retire the notebook in favor of per-country build scripts Oct 22, 2026
@github-actions github-actions Bot added doc Namespace | Anything related to documentation dependencies:npm labels Oct 22, 2026
@rusackas
rusackas force-pushed the draft/country-map-scripts branch from d96d732 to b6710b7 Compare October 22, 2026 21:22
@rusackas
rusackas marked this pull request as ready for review October 23, 2026 07:03
@codeant-ai-for-open-source

codeant-ai-for-open-source Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

CodeAnt PR Risk: Low Risk

  • The PR appears safe to merge; subset builds now write only selected modules, preventing unselected maps from being overwritten.

Assessed commit: 9d7f199b4876

validate_catalog(ctx, built)
_purge_stale_files(ctx, built, args.out_dir)

resolved = resolve_all(ctx, built, include_plain=selected is None)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two regressions fixed during this review, the --countries subset rewriting corrected maps from raw Natural Earth data and the bare-filename makedirs crash, have no test, and nothing in CI runs these scripts: the existing Jest tests only read the committed artifacts. Reverting include_plain=selected is None or dropping an or "." fallback would pass every check. Could a small pytest run main() against fixtures, once with --countries france over an existing corrected usa.geojson (asserting it, countries.ts and the docs JSON are untouched), and once in a temp cwd with --countries-ts-path countries.ts, and be wired into an existing Python CI job?

rusackas and others added 7 commits October 30, 2026 16:18
…alternative

Country Map GeoJSON Generator.ipynb mixes ~30 country sections, shared
helpers, and inline preview plotting in one growing notebook that (even
after #45602) is still awkward to review and extend as a whole.

This prototypes an alternative shape: one script per country that needs
real correction logic, a couple of shared modules (Natural Earth loading,
geometry helpers), and a small orchestrator CLI - instead of one big
notebook. Converts 4 of the ~26 special-cased countries, picked to cover
distinct patterns rather than easy ones:

- morocco: external source pinned to a commit, with a regex ISO-code fix
  and a validation pass
- italy: one section producing two shipped outputs (regions, and regions
  with autonomous provinces split out)
- latvia: an external source that can legitimately fail to load (and,
  deliberately, now does fail loud instead of silently degrading - see
  the module docstring)
- usa: the classic "flying islands" case, exercising the shared
  `reposition` geometry helper

Validated: `build_maps.py --preview` against live Natural Earth + the
pinned community sources produces GeoJSON for all 5 outputs
(morocco, italy_regions, italy_regions_and_autonomous_provinces, latvia,
usa) that is byte-for-byte identical to what's currently committed under
src/countries/, and renders a working per-country thumbnail + contact
sheet.

Not yet covered, deliberately out of scope for this prototype: the
~190 plain countries that don't need special-casing, the ~22 remaining
special-cased ones, writing countries.ts/docs/data/countries.json, and
retiring the notebook. This is scoped to proving the pattern works
before committing to the rest of that conversion.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…catalog

Completes the notebook-to-scripts conversion started in the previous
commit: all ~26 special-cased sections now have their own module under
maps/countries/ (china, finland, france, india, iran, kenya, madagascar,
malawi, netherlands, norway, philippines, portugal, russia, south_africa,
spain, turkey, uk, ukraine, vietnam, alongside the already-converted
morocco/italy/latvia/usa), plus:

- maps/catalog.py: the ~214-country plain catalog (ported verbatim from
  the notebook's "Country names used in file names" cell) and the alias/
  region-map metadata needed to resolve them.
- maps/assemble.py: combines the plain catalog with registered module
  overrides, replicating the notebook's get_gdf()/purge-single-
  subdivision-countries logic exactly, including deleting a stale output
  file if a country drops to one subdivision upstream.
- maps/ts_writer.py: writes countries.ts and docs/data/countries.json,
  byte-for-byte matching the notebook's own JS/JSON templates (the
  uk/usa display-name branch especially - it's one combined condition
  using .toUpperCase(), not two separate hardcoded cases).
- helpers.py gains the remaining shared geometry helpers the new country
  modules need: replace_column, apply_bounds, shift_geom,
  create_region_polygons.
- context.py/data.py now carry the raw (un-merged) 1:10m/1:50m admin-1
  datasets too, needed by russia.py's custom resolution mix.
- build_maps.py now runs the full pipeline (plain catalog + every
  registered module) by default, writing to the same repo-relative
  paths the notebook always has, and purges stale files for any country
  that drops below 2 subdivisions.

Two more deliberate, documented behavior changes beyond latvia.py's
fail-loud fix (see its docstring): india.py and kenya.py now also raise
on a failed external fetch instead of silently falling back to outdated
Natural Earth boundaries - kenya.py's notebook cell already did this;
india.py's didn't, so this applies the same fix for consistency rather
than leaving one external-fetch pattern quietly degrading.

Validated two ways:
1. Against master: ran the full pipeline against live Natural Earth +
   every pinned/live community source (Madagascar's live WHO query is
   separately down right now - HTTP 404, confirmed independent of this
   change, see the next commit). 197/201 other files matched the
   currently-committed src/countries/*.geojson byte-for-byte.
2. Against a fresh run of the actual notebook: patched only to skip
   interactive plotting and reuse the same cached Natural Earth data
   (no logic changes), then diffed its real output against this script
   pipeline's output directly. All 201 comparable GeoJSON files,
   countries.ts and docs/data/countries.json came out byte-for-byte
   identical.

That second comparison is the important one: it confirms the 4 files
that don't match committed master (iran, russia, uk, south_africa) -
plus a missing "Italy (regions and autonomous provinces)" display-name
override in countries.ts - aren't bugs in this port. They're places
where upstream Natural Earth data or the notebook's own current cell
logic has drifted since src/countries/ was last actually regenerated
and committed. Running the unmodified notebook today reproduces the
exact same output this script pipeline does. The next commit actually
regenerates from this pipeline and itemizes what changes and why.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found while validating the full conversion against a fresh run of the
actual notebook: the notebook's current countryOptions/docs-json cells
no longer have a case for "italy_regions_and_autonomous_provinces", even
though the nicer "Italy (regions and autonomous provinces)" string is
what's actually shipping in countries.ts and docs/data/countries.json
today. Running the unmodified notebook right now would silently replace
it with the generic fallback's "Italy (regions) And Autonomous
Provinces" in both places.

Unlike the Iran/Russia/UK/South Africa data differences (a separate,
deliberate commit) this one has no correctness upside - it's a pure
regression with nothing to recommend it - so it's restored directly in
ts_writer.py rather than reproduced.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nt sources

Ran build_maps.py for real against live Natural Earth data (Madagascar's
module temporarily unregistered for this one run only - its live WHO
source is independently returning HTTP 404 right now; its existing
geojson is untouched). Only these 4 of 202 files actually changed, all
previously confirmed (see the conversion commit) to exactly match what
running the unmodified notebook produces today - this isn't new
behavior, just the first time it's actually been regenerated and
committed:

- iran.geojson: Alborz's iso_3166_2 moves from IR-32 to IR-30. Natural
  Earth's raw data currently has Alborz and Tehran sharing IR-07; the
  existing correction cell fixes that by reassigning Alborz to IR-30
  (freeing IR-09 for Razavi Khorasan, already correct). IR-32 predates
  that fix actually being applied to the committed file.
- russia.geojson: Karelia and Murmansk now use their 1:10m boundaries
  instead of 1:50m, per the existing NE#929 workaround
  (nvkelso/natural-earth-vector#929) - only those two regions' geometry
  changes, confirmed by diffing feature-by-feature.
- uk.geojson: one unitary authority's name flips from "Wirral" to
  "Halton" at ISO code GB-WRL - an upstream Natural Earth data change,
  not something this codebase computes. Visually spot-checked; no
  missing or distorted regions.
- south_africa.geojson: Western Cape's geometry shifts by a handful of
  vertices - upstream Natural Earth coastline refinement, not a logic
  change (confirmed the Prince Edward Islands reposition still applies
  correctly; the other 8 provinces are byte-identical).

Please look at the rendered diffs before merging, same as any other
country-map regen - flagging uk.geojson and south_africa.geojson
especially, since those reflect upstream data changes this codebase
doesn't control rather than a documented, intentional fix like iran/
russia.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The scripts conversion (previous commits) is validated byte-identical
to the notebook's own current output, so there's nothing left for the
notebook to do that build_maps.py doesn't already cover:

- Deletes Country Map GeoJSON Generator.ipynb.
- package.json's update-maps script now runs build_maps.py instead of
  nbconvert.
- plugin-chart-country-map/README.md and the admin docs page's
  "Adding a New Country" guide now point at scripts/maps/countries/ and
  --countries/--preview for iterating on one country, instead of the
  notebook. Most countries need no new module at all - just a plain
  catalog entry in scripts/maps/catalog.py.

Not touched: the versioned docs snapshots under
user_docs_versioned_docs/ and admin_docs_versioned_docs/, which are
frozen historical doc states, not live docs.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…e guard

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eojson

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rusackas and others added 8 commits October 30, 2026 16:21
… output

The notebook this replaces fetched a community source with
Philippines' 83 provinces already corrected (Maguindanao split into
del Norte/del Sur, the Davao split, Compostela Valley renamed to
Davao de Oro) and renamed its columns, but the result was never put
in the dict that fed the final output. The shipped philippines.geojson
was still raw, outdated Natural Earth ADM2 data, complete with stray
highly-urbanized-city polygons.

philippines.py now returns that corrected source as the "philippines"
entry (alongside the existing, unrelated philippines_regions dissolve),
so it actually reaches resolve_all() instead of being silently
discarded.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rvice

The WHO AFRO GDB service this module queried (Hosted/AFROGDB_PUBLIC_DATA)
has been retired on WHO's end and now 404s - confirmed independently of
this codebase, and it broke the same way for the unmodified notebook
this replaces.

Hosted/AFROGDB_published_boundary (layer 2, "latest_boundary_adm1") is
WHO's current public hosted replacement: same field names
(level_id/display_name/boundary_type_id/iso3_code) and the same 23
Madagascar regions, confirmed by comparing region names one-for-one
against the previously-committed madagascar.geojson. Regenerated the
file from the new endpoint; region boundaries are visually unchanged.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…s subset build

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… and command dirs

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…o prerequisites

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… update-country-maps

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@rusackas
rusackas force-pushed the draft/country-map-scripts branch from a3ba9e2 to a0947c4 Compare October 30, 2026 23:23
@github-actions github-actions Bot added the github_actions Pull requests that update GitHub Actions code label Oct 30, 2026
…nd ring winding

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ched

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies:npm doc Namespace | Anything related to documentation github_actions Pull requests that update GitHub Actions code plugins preset-io size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants