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

  1. Acquire open geodata

    GHSL SMOD settlement model, Kontur population hexagons, WorldPop grids and OCHA administrative boundaries โ€” all openly licensed, versions pinned in Settings.

  2. Generate customer datasets

    scripts/generate_*_data.py produces realistic synthetic registries per country โ€” names, GPS points near real settlements, usage telemetry, stove economics.

  3. Classify every location

    classify_locations.py applies the DEGURBA cascade (GHSL SMOD โ†’ Kontur density โ†’ WorldPop) and writes the decision and its reasoning into each record.

  4. Simplify for the browser

    simplify_pop.py and friends reduce boundary geometry until the whole dataset ships in kilobytes, not megabytes.

  5. 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