uganda-locale
v0.9.1
Published
Open, versioned dataset and library of Uganda's administrative units — regions, districts, cities, counties, municipalities, subcounties, town councils, parishes, wards, villages, cells and zones.
Maintainers
Readme
uganda-locale
An open, versioned dataset (and eventually a small serving library) of Uganda's
administrative units — region → district/city → county/municipality →
subcounty/town council/division → parish/ward → village/cell → zone — built
because no single free, current, machine-readable source of this hierarchy
existed for software integration (form dropdowns, address validation,
reporting, etc.), and Uganda's units change often enough that hand-maintained
copies go stale fast. Every district/city also carries a subregion_id
pointing into a second, parallel classification — Uganda's 17
cultural/traditional sub-regions (Buganda, Acholi, Ankole, Busoga, ...), the
grouping UBOS itself uses for census reporting — since the 4 administrative
regions don't map onto historical/cultural boundaries.
Just want the data, not the code? Download
dist/uganda-locations-full.csvand open it in Excel or Google Sheets — one row per village (71,230 rows), with region/sub_region/district/county/constituency/subcounty/parish/village columns. That's the whole dataset in one file. (On the file's GitHub page, use the download/raw button to save it directly — GitHub doesn't fully render a spreadsheet preview for a file this size.)
Why this exists
Uganda's Electoral Commission is the most tactically complete government body
for this data — it maps down to the village level for every election. Most
free sources (Wikipedia, HDX/OCHA, Statoids) turned out to be either scoped to
district level only or several years stale below that — see
docs/DATA_QUALITY.md for the full source-by-source
breakdown, including what was checked and rejected.
The EC does in fact publish the real thing: a full village-level gazetteer
(ec.or.ug/admin-units), just not advertised as a "dataset" — it's a
3,019-page PDF. It's now been parsed, and validated twice over (see below).
What's actually in here right now
Run npm run build to compile data/ into dist/:
| Level | Count | Confidence |
|---|---|---|
| Region | 4 | verified |
| Sub-region (cultural/traditional, e.g. Buganda, Acholi, Ankole) | 17 | verified — see caveat in docs/DATA_QUALITY.md |
| District (incl. Kampala/KCCA) | 136 | verified — cross-checked against 3 independent current sources |
| City | 10 | verified |
| County / Municipality | 322 | verified — reconstructed from the EC gazetteer's constituency data, July 2022, 135/136 rural districts (Kampala has no county tier, like cities) |
| Subcounty / Town Council / Division | 2,191 | verified — EC gazetteer, July 2022, all 146 district-equivalent units |
| Parish / Ward | 10,717 | verified — EC gazetteer, July 2022, all 146 district-equivalent units |
| Village / Cell | 71,230 | verified — EC gazetteer, July 2022, all 146 district-equivalent units |
| Zone | 0 | not yet ingested — EC polling-station data is a separate source from the admin-unit gazetteer, see Roadmap |
Plus boundary polygons for map visualization (dist/geo/districts.geojson,
dist/geo/regions.geojson) — 136 district polygons, CC0, from
geoBoundaries (already includes Terego);
4 region polygons, dissolved from that same district geometry rather
than sourced separately, so they can never drift out of sync with
region_id. Nothing below district level: county/subcounty/parish/village
boundary data was evaluated and found too incomplete, too stale, or
unlicensed to ship — see docs/DATA_QUALITY.md for
the full list of sources checked and why each was or wasn't used.
Plus 2024 census population for every district and city (population
field: { year, male, female, total }), from UBOS's National Population
and Housing Census 2024 Final Report — the actual final count, not a
projection. Cross-checked three independent ways (per-row arithmetic, the
report's own prose, and an independent national-total reconciliation) with
zero discrepancies; see docs/DATA_QUALITY.md for
the full validation story.
Plus, shipped deliberately incomplete rather than withheld (see
docs/DATA_QUALITY.md for exact coverage numbers
and why each caps out where it does):
- Subcounty/town council/division boundaries — 1,249 of 2,191 (57.0%)
- Parish/ward boundaries — 423 of 10,717 (3.95%)
- Major road network (
dist/geo/roads.geojson) — 10,721 OpenStreetMap road segments (motorway through tertiary only; residential streets and informal paths/tracks excluded as too heavy and ~90%+ unnamed). Not tied to any administrative unit — a road crosses many districts/subcounties.
Plus country-level metadata (dist/country/uganda.json): ISO codes,
currency (UGX), calling code (+256), timezone (Africa/Kampala, UTC+3, no
DST), driving side, capital, languages, and flag/coat-of-arms images in SVG
and PNG (dist/country/assets/) — sourced from
mledoze/countries and Wikimedia
Commons, see docs/DATA_QUALITY.md. Note: the coat-of-arms image is
CC BY-SA 3.0, not this project's default CC-BY 4.0 — see
LICENSE-DATA.md before reusing it.
Every record has a confidence (verified / legacy / unverified) and a
status (operational / pending / ...) field. Consuming code should
filter on confidence explicitly rather than assume completeness — the one
remaining structural gap is one subcounty (Katikamu, Luwero) that spans two
constituencies like a city division and wasn't force-assigned a county name.
The subcounty → village layer is sourced from the EC's own "Verified
Administrative Units, July 2022" gazetteer and cross-validated twice: every
one of the ~2,198 "TOTAL VILLAGES IN X" checksums printed in the source
document itself matches, and fully re-aggregated per-district totals match
the EC's independently-published summary statistics exactly, for all 146
districts/cities with zero discrepancies. The county layer is reconstructed
from that same gazetteer's constituency data (outside city/Kampala
divisions, a constituency name in this document is the county name) and
was independently spot-checked against separate research across 13
districts, 11 exact matches and 2 refinements, 0 contradictions. Full story
in data/legacy/provenance/ec/README.md
and docs/DATA_QUALITY.md. All of it is dated July
2022, so it doesn't reflect any changes since (see docs/DATA_QUALITY.md
for what's unverified from 2023-2026, e.g. reported new town councils).
Run node scripts/build.mjs and check dist/data-quality-report.json for a
live coverage report — regenerated every build, not hand-maintained.
Repo layout
src/
index.mjs # npm package entry point (region..division)
deep.mjs # opt-in: adds parish/ward/cell
store.mjs # shared lazy-loading index over dist/*.json
web/
# minimal Next.js app / API — see web/README.md
data/
regions.csv # 4 regions
subregions.csv # 17 cultural/traditional sub-regions (Buganda, Acholi, Ankole, ...), parallel to regions
districts.csv # 136 districts + Kampala/KCCA, verified; carries both region_id and subregion_id
cities.csv # 10 second-generation cities, verified
sources.json # every source cited, with access date + what it was used/rejected for
ec/
administrative_units_ec2022.json # EC-verified county->subcounty->parish->village, all 146 units
geo/
districts.geojson # 136 district boundary polygons, CC0 (geoBoundaries-derived)
regions.geojson # 4 region boundary polygons, dissolved from districts.geojson by region_id
subcountys.geojson # 1,249/2,191 (57.0%) subcounty boundaries — partial by design, see DATA_QUALITY.md
parishs.geojson # 423/10,717 (3.95%) parish boundaries — partial by design, see DATA_QUALITY.md
roads.geojson # 10,721 major-road-network segments, OSM-derived, not tied to any admin unit
population/
uganda-nphc-2024-population.csv # 146 rows (136 districts + 10 cities), 2024 census male/female/total
country/
uganda.json # ISO codes, currency, calling code, timezone, etc.
assets/ # flag + coat of arms, SVG and PNG
legacy/
counties_2015_statoids.csv # superseded by data/ec/; kept for provenance only, ~2015 vintage
subcounties_legacy_source.csv # the project's original location.csv, kept for reference (superseded by data/ec/)
provenance/
ec/ # the EC PDF parser, validation logs, and regeneration instructions
schema/
administrative-unit.schema.json # the record shape everything compiles to
country.schema.json # the shape of data/country/uganda.json
scripts/
build.mjs # data/*.csv + data/ec/*.json + data/geo/*.geojson -> dist/
ingest-ec-admin-units.mjs # parsed EC village data -> schema-shaped units
ingest-district-boundaries.mjs # raw geoBoundaries district GeoJSON -> data/geo/districts.geojson
build-region-boundaries.mjs # dissolves data/geo/districts.geojson by region_id -> data/geo/regions.geojson
ingest-subcounty-boundaries.mjs # raw geoBoundaries ADM4 -> data/geo/subcountys.geojson (57.0% coverage)
ingest-parish-boundaries.mjs # raw HDX admin4 -> data/geo/parishs.geojson (3.95% coverage)
ingest-roads.mjs # raw HOTOSM roads shapefile -> data/geo/roads.geojson (major road network)
validate.mjs # structural + referential-integrity check of the build output
dist/ # generated by `npm run build`; mostly committed for direct use — see "Using the data" below
docs/
ROADMAP.md
DATA_QUALITY.md
CONTRIBUTING.mdUsing the data
Not writing code? Download dist/uganda-locations-full.csv
directly from this repo and open it in Excel or Google Sheets — one row per
village (71,230 rows), with region/sub_region/district/county/constituency/
subcounty/parish/village columns plus a confidence flag. That's the whole
dataset in one file.
Writing JS/TS? (Package prepared but not yet published to the npm
registry — install straight from GitHub for now, or npm link a local
clone.)
npm install github:kakandemanwell/ugandaimport * as uganda from "uganda-locale";
uganda.regions(); // 4 administrative regions
uganda.subregions(); // 17 cultural/traditional sub-regions (Buganda, Acholi, Ankole, ...)
uganda.districts(); // 136 districts (incl. Kampala) — each has region_id, subregion_id, population
uganda.districts().filter((d) => d.subregion_id === "subregion:acholi");
uganda.districts().find((d) => d.slug === "wakiso").population; // { year: 2024, male, female, total: 3411177 }
uganda.counties({ districtId: "district:mbarara" });
uganda.subcounties({ parentId: "county:mbarara-kashari-north-county" });
uganda.getAncestors("subcounty:hoima-buhanika"); // walk up to region
uganda.search("kampala"); // name/alias search
uganda.country(); // ISO codes, currency, flag paths, etc.The default import bundles region → district/city → county/municipality →
subcounty/town council/division (~1.3MB), plus the 17-record subregion
lookup table (subregion_id on every district/city points into it). Parish/ward/cell data (~5MB more)
is opt-in, so plain installs stay light:
import * as uganda from "uganda-locale/deep";
uganda.parishes({ parentId: "subcounty:hoima-buhanika" });
uganda.cells({ parentId: "parish:some-ward" }); // urban wards onlyBoundary polygons (for map visualization) are a separate opt-in too, since most consumers building dropdowns/lookups don't need geometry payloads:
import * as geo from "uganda-locale/geo";
geo.districtBoundaries(); // GeoJSON FeatureCollection, 136 features
geo.regionBoundaries(); // GeoJSON FeatureCollection, 4 features
geo.subcountyBoundaries(); // GeoJSON FeatureCollection, 1,249 features (57.0% coverage — partial, by design)
geo.parishBoundaries(); // GeoJSON FeatureCollection, 423 features (3.95% coverage — partial, by design)
geo.roads(); // GeoJSON FeatureCollection, 10,721 features (major road network; not tied to any admin unit)Rural village-level data (71,230 records, ~33MB as JSON) is not bundled
in the npm package — use dist/uganda-locations-full.csv from this repo, or
the API (below), instead. Every function returns plain objects shaped like
schema/administrative-unit.schema.json.
Just want an HTTP API? A minimal Next.js app in web/ exposes
the same data as JSON endpoints — see its README for routes once deployed.
Writing in another language, or want the raw files directly? Most of
dist/ is committed too, so you can use the data straight from a
clone/download without running anything:
uganda-locations-full.csv— full ancestry, one row per village (~6.6MB)uganda-locations.csv— flattened, loosely backward-compatible with the originallocation.csvshaperegions.json,subregions.json,districts.json,citys.json,countys.json,subcountys.json,town_councils.json,divisions.json,parishs.json,wards.json,cells.json— per-level exportsgeo/districts.geojson,geo/regions.geojson— boundary polygons for map visualization (CC0)data-quality-report.json— coverage/gaps, regenerated every buildcountry/uganda.json+country/assets/— country metadata and flag/coat-of-arms images
Two files are not committed because they're just heavier restatements of
the above (39MB and 33MB respectively, mostly redundant with the full CSV
and the smaller per-level files): the full unified uganda-locations.json
and the per-level villages.json. Get either by running:
npm run buildwhich regenerates all of dist/ from data/.
License
- Code (
scripts/,schema/): MIT - Data (
data/,dist/): CC-BY 4.0 — attribute UBOS, the Electoral Commission of Uganda, OCHA/HDX, and Wikipedia/Statoids as applicable perdata/sources.json.
Status
Early stage. Region through village is now verified for all (or nearly all)
district-equivalent units, and the compiled data is usable directly from
this repo (see "Using the data" above) or via the uganda-locale package
in this repo (not yet published to the npm registry — install from GitHub
in the meantime: npm install github:kakandemanwell/uganda). See
docs/ROADMAP.md for what's next — the zone/polling-station
layer and the hosted Vercel API.
