kenyageo

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 10 Imported by: 1

README

kenya-geo

Go Reference CI

Kenya's 47 counties, 290 constituencies, 1,450 wards and 890 post offices for Go, with 2019 census figures and map coordinates. The data is compiled into your binary, so there is nothing to download or configure at runtime.

Features

  • Every county, constituency and ward, with IEBC ward codes and 2022 registered voters
  • 2019 census population by sex, land area and density for each county
  • ISO 3166-2 codes and former provinces
  • Centroids and bounding boxes for counties, centroids for wards
  • 890 post offices by postal code, each placed in a county
  • Forgiving name lookups: case, apostrophes, dashes and a trailing "County" don't matter
  • Fuzzy search across counties, constituencies, wards and post offices
  • No dependencies outside the standard library; safe for concurrent use

Install

go get github.com/rmiyoyo/kenya-geo

Requires Go 1.24 or later.

Usage

package main

import (
	"fmt"

	kenyageo "github.com/rmiyoyo/kenya-geo"
)

func main() {
	geo := kenyageo.Default()

	c, ok := geo.CountyByName("murang'a")
	if !ok {
		return
	}
	fmt.Println(c.Code, c.Name, c.Headquarters) // 21 Murang'a County Murang'a

	for _, name := range geo.Constituencies(c.Code) {
		fmt.Println(name, len(geo.WardsInConstituency(name)))
	}
}
Counties
geo.Counties()                 // all 47, ordered by county code
geo.CountyByCode(47)           // Nairobi County
geo.CountyByName("nairobi")    // the same county
c.Population, c.Density()      // 4397073, about 6247 people per km²
c.ISOCode, c.FormerProvince    // "KE-30", "Nairobi"
Constituencies and wards
geo.Constituencies(32)         // Nakuru's 11 constituencies, alphabetical
geo.WardsInCounty(32)          // []Ward
geo.WardsInConstituency("Kibra")
geo.WardByCode("0040")         // WAA, in Matuga, Kwale
w.Centroid                     // *LatLng, nil when unknown
w.RegisteredVoters2022         // *int, nil when unknown

Ward names repeat across the country (there are several CENTRAL and TOWNSHIP wards), so store ward codes, not names.

Post offices
geo.PostOffices("00100")       // Nairobi GPO
geo.PostOfficesInCounty(27)    // every post office in Uasin Gishu

A few postal codes are shared by two offices, so PostOffices returns a slice.

for _, m := range geo.Search("nakru", 10) {
	fmt.Println(m.Kind, m.Name, m.Score)
}

Results are ranked by how closely they match, and each carries the code of the county it belongs to.

Your own data

Load builds the same lookups from any readers, for example a newer copy of the files in data/:

geo, err := kenyageo.Load(countiesFile, wardsFile, postOfficesFile)

Command-line tool

go install github.com/rmiyoyo/kenya-geo/cmd/kenyageo@latest

kenyageo county nakuru    # constituencies and ward counts
kenyageo postcode 30100   # post office lookup
kenyageo search kibera    # fuzzy search

Data

File Contents
data/counties.json code, name, headquarters, ISO code, former province, population by sex, area, centroid, bounding box
data/wards.json ward code, name, constituency, county, 2022 registered voters, centroid
data/postoffices.json postal code, name, county, how the county was decided, location

The same JSON files can be used from any language.

Accuracy notes
  • Ward centroids are known for 1,432 of the 1,450 wards.
  • Centroids and bounding boxes are for placing labels and zooming maps. They can't tell you which ward a point falls in.
  • Post office locations are given only for the 553 offices matched to a real place. county_source says how each office's county was decided: geonames, location, manual or neighbours.
  • The source data had several errors, such as wards filed under the wrong constituency, mixed census years and a malformed ward code. They are corrected when the data is generated; see internal/gendata/fixes.go.

Sources

Source Used for Licence
KNBS 2019 Kenya Population and Housing Census, Volume I county population by sex, land area open data
geoBoundaries gbOpen KEN ADM1/ADM3 ISO codes, centroids, bounding boxes public domain
GeoNames postal codes post offices CC BY 4.0
IEBC county and ward lists codes, names, headquarters, wards, 2022 voters public record

Post office data is © GeoNames, used under CC BY 4.0.

Contributing

Issues and pull requests are welcome. To regenerate the data after changing a source or a fix:

go run ./internal/gendata   # downloads raw sources, applies fixes, writes data/*.json
go test ./...

Don't edit data/*.json by hand. Change the sources in data/source/ or the fixes in internal/gendata/ instead.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type County

type County struct {
	Code           int    `json:"code"`
	Name           string `json:"name"`
	ISOCode        string `json:"iso_code"`
	FormerProvince string `json:"former_province"`
	Headquarters   string `json:"headquarters"`

	Population         int     `json:"population"`
	PopulationMale     int     `json:"population_male"`
	PopulationFemale   int     `json:"population_female"`
	PopulationIntersex int     `json:"population_intersex"`
	AreaKm2            float64 `json:"area_km2"`

	Centroid LatLng     `json:"centroid"`
	BBox     [4]float64 `json:"bbox"`
}

func (County) Density

func (c County) Density() float64

type Data

type Data struct {
	// contains filtered or unexported fields
}

func Default

func Default() *Data

func Load

func Load(counties, wards, postOffices io.Reader) (*Data, error)

func (*Data) Constituencies

func (d *Data) Constituencies(countyCode int) []string

func (*Data) Counties

func (d *Data) Counties() []County

func (*Data) CountyByCode

func (d *Data) CountyByCode(code int) (County, bool)

func (*Data) CountyByName

func (d *Data) CountyByName(name string) (County, bool)
Example
c, ok := Default().CountyByName("taita taveta")
fmt.Println(c.Code, c.Name, c.Headquarters, ok)
Output:
6 Taita-Taveta County Mwatate true

func (*Data) PostOffices

func (d *Data) PostOffices(postalCode string) []PostOffice

func (*Data) PostOfficesInCounty

func (d *Data) PostOfficesInCounty(countyCode int) []PostOffice

func (*Data) Search

func (d *Data) Search(query string, limit int) []Match
Example
for _, m := range Default().Search("makadara", 3) {
	fmt.Printf("%s %s\n", m.Kind, m.Name)
}
Output:
constituency Makadara
post office Makadara
ward MJI WA KALE/MAKADARA

func (*Data) WardByCode

func (d *Data) WardByCode(code string) (Ward, bool)

func (*Data) WardsInConstituency

func (d *Data) WardsInConstituency(name string) []Ward

func (*Data) WardsInCounty

func (d *Data) WardsInCounty(countyCode int) []Ward

type Kind

type Kind int
const (
	KindCounty Kind = iota
	KindConstituency
	KindWard
	KindPostOffice
)

func (Kind) String

func (k Kind) String() string

type LatLng

type LatLng struct {
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
}

type Match

type Match struct {
	Kind       Kind
	Name       string
	Code       string
	CountyCode int
	Score      float64
}

type PostOffice

type PostOffice struct {
	Code         string  `json:"postal_code"`
	Name         string  `json:"name"`
	CountyCode   int     `json:"county_code"`
	CountySource string  `json:"county_source"`
	Location     *LatLng `json:"location"`
}

type Ward

type Ward struct {
	Code         string `json:"ward_code"`
	Name         string `json:"name"`
	Constituency string `json:"constituency_name"`
	CountyCode   int    `json:"county_code"`
	CountyName   string `json:"county_name"`

	RegisteredVoters2022 *int `json:"registered_voters_2022"`

	Centroid *LatLng `json:"centroid"`
}

Directories

Path Synopsis
cmd
kenyageo command
internal
gendata command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL