A small case-study site, built in days, exploring the kind of work Fika's Digital team does: turning geospatial data and ML into maps that tell a non-technical decision-maker where infrastructure investment matters most. A landing page frames the problem, then three working map prototypes prove the value on a single click.
Live structure: / (case study) → /prioritizer · /map-goes-dark · /time-to-care
Drag one slider, "how many bridges can you fund?", and watch candidate crossings get funded in priority order over a Zambian region. The first handful connect almost everyone cut off from health care; then the curve flattens. You discover Fika's 80/30 rule by dragging: ~31% of the bridges reach ~80% of the people, and cost-per-person climbs from ~$69 to ~$181 as you chase the long tail (the Pareto Trap). Built entirely on Fika's real model outputs (88 candidate bridges, 6,276 population hexes, travel-time-saved per site); the only added assumption is an illustrative flat cost per bridge.
Drag a divider to compare the official map (OpenStreetMap) with Fika's WaterNet AI-detected waterways over a Zambian district. 80% of the waterways WaterNet finds here are missing from OpenStreetMap, the very rivers that isolate communities, invisible on the map governments plan from.
Tap a rural community in Chama District, Zambia and see how long it really takes to reach the nearest clinic or school on foot, then toggle how a single river crossing changes it. Luchenga Village goes from a 5h 41m walk to school down to 1h 34m with one bridge.
The three interlock: WaterNet (02) reveals the rivers that isolate people, the Prioritizer (01) decides which crossings to fund first, and Time to Care (03) shows what one of them means for a single village.
| Decision | Rationale |
|---|---|
| Svelte / SvelteKit | Fika's main frontend. One unified app, three routes, shared design system. |
| MapLibre GL JS | Open-source, token-free twin of Mapbox GL JS (which Fika uses). The public repo, live link and recordings all work with no access token. |
| Pre-computed, static data | No live routing, no live ML inference. Python does the heavy work offline; the site is a fast, serverless static build. |
| Real open data | OpenStreetMap geography + Fika's published WaterNet outputs (CC-BY-4.0). Nothing is invented. |
| Honest, illustrative models | Travel times and coverage gaps are labelled illustrative. Clarity for a decision maker, not false precision. |
data-prep/
probe_districts.py # picked Chama on OSM-coverage evidence
fetch_osm.py # real OSM communities/clinics/schools/rivers -> GeoJSON
build_travel_times.py # Time to Care: where would a crossing help most?
finalize_web.py # lean web bundle -> app/static/data (~200 KB)
waternet/
prep_mapdark.py # reads WaterNet COG over Chama, builds the reveal layer
# + the headline: % of detected waterways absent from OSM
prioritizer/
build_prioritizer.py # ranks Fika's real candidate bridges -> impact sequence JSON
- The Prioritizer: consumes Fika's published model run for a Zambian region
(
t5-analysison their public S3 bucket): 88 candidate bridges, 6,276 H3 population hexes, and per-hex travel-time-saved. A bridge "connects" a hex only when it actually reduces that hex's travel time to health care, so the greedy funding order and the cumulative curve are real, not modelled by us. Only the per-bridge cost is illustrative. Yields the 80/30 sequence the slider walks. - Time to Care model: major rivers (
waterway=river) are barriers; streams are wadeable. For each community, find where a candidate crossing helps most - walk-straight-with-a-bridge vs. detour-to-the-nearest-existing-crossing, at 4.5 km/h with a 1.3× path-sinuosity factor. Every figure is reproducible from OSM geometry; none is hand-typed. - Map Goes Dark: reads only the Chama window from WaterNet's remote Cloud-Optimized GeoTIFF (no gigabyte download), thresholds confident waterways (>0.5), masks to the district, and rasterises OSM waterways to compute the share of the detected network with no OSM equivalent (80%).
cd app
npm install
npm run dev # http://localhost:5173
npm run build # production build (every route prerendered)The app lives in the app/ subdirectory and uses @sveltejs/adapter-vercel, so:
- Import the repo in Vercel.
- Set Root Directory to
app(the only required setting; SvelteKit is then auto-detected). - Deploy. Every route is prerendered, so it serves as a static, serverless site.
For any other static host (Netlify, GitHub Pages, S3), swap the adapter line in
app/svelte.config.jsfor@sveltejs/adapter-static(adapter({ fallback: 'index.html' })) and serveapp/build.
Regenerate data (optional, the shipped bundles are committed):
python3 -m venv data-prep/.venv && source data-prep/.venv/bin/activate
pip install shapely rasterio pyproj pillow matplotlib
python3 data-prep/probe_districts.py
python3 data-prep/fetch_osm.py
python3 data-prep/build_travel_times.py
python3 data-prep/finalize_web.py
python3 data-prep/waternet/prep_mapdark.py
# The Prioritizer (downloads Fika's t5-analysis outputs from their public bucket):
python3 data-prep/prioritizer/build_prioritizer.pyDeploying under a subpath (e.g. a GitHub Pages project site)? Set
paths.baseinapp/svelte.config.js; all asset/data URLs already use SvelteKit'sbase.
- Geography, © OpenStreetMap contributors (ODbL).
- WaterNet detected waterways, Fika / Better Planet Laboratory, CC-BY-4.0, via source.coop/fika/waternet (paper arXiv 2412.00050).
- Bridge prioritisation, Fika's
t5-analysisoutputs (candidate bridges, population, travel-time-saved) from their publicpublic-b2p-geodataS3 bucket. - Travel times & coverage gaps, illustrative, generated by the models above. In The Prioritizer, the prioritisation and impact are real; only per-bridge cost is illustrative.
A job-application prototype: small and finished over large and unfinished. Out of scope by design, live routing, multiple districts, accounts, real-time ML inference, cloud infrastructure. Candidate crossings and the coverage comparison are illustrative, not engineered conclusions.
app/
src/routes/ / · prioritizer · map-goes-dark · time-to-care
src/lib/prioritizer/ PrioritizerMap, ImpactCurve
src/lib/mapdark/ SwipeMap
src/lib/timetocare/ Map, StoryCard, Intro, Legend
src/lib/ shared: basemap, format, design tokens (app.css)
static/prioritizer/ ranked bridges + cumulative impact sequence
static/mapdark/ WaterNet reveal layer + OSM waterways + stats
static/data/ Time to Care bundle
data-prep/ the offline Python pipeline



