How It's Built
No frameworks, no build step, no server. A deliberate architecture choice that makes the dashboard run anywhere โ from a field laptop with no internet to a hosted showcase URL.
Architecture
Four decisions that shaped everything
Static-first, zero dependencies
Vanilla HTML/CSS/JS with libraries pulled from CDN at runtime. There is nothing to compile and nothing to break โ the entire app is a handful of HTML pages, three JS modules and static data, themed by one shared stylesheet.
Per-country data islands
Each country owns a folder (data/SN, ML, KH) with its
customers, neighbours and boundaries. Pages load exactly one island via script tag โ a country
switch never re-downloads the world.
Python as the offline factory
Heavy geodata work โ GHSL rasters, Kontur hexagons, population grids โ happens in a Python pipeline that emits slim JSON/JS. The browser never touches a raster.
Static by design
The site serves a fixed, first-party dataset from static files: no database, no write path and no user accounts, so the classic injection surface starts at zero.
Codebase
The whole app, mapped
location-impact-explorer/
โโโ index.html # public landing ยท pilot statistics
โโโ admin.html # SPA dashboard ยท metrics ยท inventory ยท settings
โโโ map.html / _ml / _kh # Leaflet interactive maps, one per country
โโโ showcase.html + docs-* # this documentation set
โโโ site.css # design tokens & shared UI components
โโโ site.js # navbar, reveal animations, landing map
โโโ app.js # map engine ยท filters ยท markers ยท layers
โโโ admin.js # KPIs ยท charts ยท registry ยท update checks
โโโ utils.js # shared helpers for fetch, formatting and errors
โโโ countries.js # single source of country config
โโโ data/
โ โโโ SN | ML | KH/ # customers.js/json + neighbours per country
โ โโโ *_admin*.js # admin-1/admin-2 boundaries as GeoJSON
โโโ scripts/ # python generators + classifiers
โโโ dev/ # in-browser self-test suite
Data Pipeline
From raw rasters to a running dashboard
-
Acquire open geodata
GHSL SMOD settlement model, Kontur population hexagons, WorldPop grids and OCHA administrative boundaries โ all openly licensed, versions pinned in Settings.
-
Generate customer datasets
scripts/generate_*_data.pyproduces realistic synthetic registries per country โ names, GPS points near real settlements, usage telemetry, stove economics. -
Classify every location
classify_locations.pyapplies the DEGURBA cascade (GHSL SMOD โ Kontur density โ WorldPop) and writes the decision and its reasoning into each record. -
Simplify for the browser
simplify_pop.pyand friends reduce boundary geometry until the whole dataset ships in kilobytes, not megabytes. -
Tune without code
The Settings page exposes classification thresholds; saving them prints the exact CLI command to regenerate โ analysts iterate without touching source.
Engineering Notes
Details we refuse to hand-wave
๐ก Self-checking data sources
The Settings page queries the HDX API live and compares remote modification dates against pinned local versions โ stale science is surfaced, not silently shipped.
๐งช Regression suite
A Playwright runner (dev/browser_test.py) drives all eleven public routes on
desktop and mobile viewports before every deploy โ page loads, datasets, badges and console
cleanliness.
๐พ Works offline, syncs later
Script-tag data loading sidesteps CORS entirely; export/import round-trips the full registry as JSON for field laptops with no connectivity.
๐ Security posture
Demonstration data only, CDN dependencies version-pinned, no secrets in the client and nothing to log into โ checked before every public deploy.
Stack
Libraries and why
| Library | Version | Job |
|---|---|---|
| Leaflet | 1.9.4 (pinned) | Interactive mapping engine |
| Leaflet.markercluster | 1.4.1 (pinned) | Cluster hundreds of markers smoothly |
| Chart.js | 4.4.1 (pinned) | Doughnut + horizontal bar analytics |
| Font Awesome | 6.4.0 (pinned) | Iconography |
| Python 3.12 | build-time | Data generation, classification, simplification |