Skip to main content

Rwanda Geo Data

Rwanda's administrative divisions for JavaScript/TypeScript and Python: 5 provinces → 30 districts → 416 sectors → 2,149 cells → 14,837 villages.

Zero dependencies. Lookups are case-insensitive and diacritic-insensitive. District names are unique, so the province argument is optional below district level.

Install

npm install @linkfy-connect/rwanda-geo-data      # JavaScript / TypeScript
pip install rwanda-geo-data                      # Python

Works in Node and in every bundler-based framework (React, Next.js, Vue, Nuxt, Angular, Svelte, Astro, React Native, Vite, webpack), with ESM, CommonJS and TypeScript types included.

Functions

JavaScript Python Returns
getCountry() get_country() "Rwanda"
getProvinces() get_provinces() all provinces
getDistricts() get_districts() all districts
getDistrictsByProvince(province) get_districts_by_province(province) districts of a province
getSectors() get_sectors() all sectors (unique names)
getSectorsByDistrict(province, district) get_sectors_by_district(province, district) sectors of a district
getCells() get_cells() all cells (unique names)
getCellsBySector(province, district, sector) get_cells_by_sector(province, district, sector) cells of a sector
getVillages() get_villages() all villages (unique names)
getVillagesByCell(province, district, sector, cell) get_villages_by_cell(province, district, sector, cell) villages of a cell
search(query, { levels, limit }) search(query, levels=None, limit=50) matches at any level
getRandomLocation() get_random_location() a random full address
countLocations() count_locations() number of divisions per level
getProvinceOf(district) get_province_of(district) province name
isValidAddress({...}) is_valid_address(...) true / false

Shorter calls also work: leave out the province, for example getSectorsByDistrict('Bugesera') or getCellsBySector('Bugesera', 'Gashora'). A wrong province for a district returns an empty list.

JavaScript

import {
  getCountry, getProvinces, getDistrictsByProvince, getSectorsByDistrict,
  getCellsBySector, getVillagesByCell, search, getRandomLocation, countLocations,
} from '@linkfy-connect/rwanda-geo-data';

getCountry();                                                   // 'Rwanda'
getProvinces();                                                 // ['East', 'Kigali', 'North', 'South', 'West']
getDistrictsByProvince('East');                                 // ['Bugesera', 'Gatsibo', ...]
getSectorsByDistrict('East', 'Bugesera');                       // ['Gashora', 'Juru', ...]
getCellsBySector('East', 'Bugesera', 'Gashora');                // ['Biryogo', 'Kabuye', ...]
getVillagesByCell('East', 'Bugesera', 'Gashora', 'Biryogo');    // ['Bidudu', 'Biryogo', ...]

countLocations();   // { provinces: 5, districts: 30, sectors: 416, cells: 2149, villages: 14837 }
getRandomLocation();
// { province: 'East', district: 'Bugesera', sector: 'Gashora', cell: 'Biryogo', village: 'Bidudu' }

Multi-word, case-insensitive and diacritic-insensitive. Every word must appear in the item's name or in the names above it, so "gashora bugesera" finds the Gashora sector and everything inside it. Exact name matches come first, then closer matches, then shallower levels.

search('gashora');
search('gashora bugesera', { limit: 10 });
search('kigali', { levels: ['district', 'sector'] });
// [{ level: 'sector', name: 'Gashora', province: 'East', district: 'Bugesera', sector: 'Gashora' }, ...]

React: cascading dropdowns

import { useState } from 'react';
import {
  getProvinces, getDistrictsByProvince, getSectorsByDistrict, getCellsBySector, getVillagesByCell,
} from '@linkfy-connect/rwanda-geo-data';

const LEVELS = ['province', 'district', 'sector', 'cell', 'village'];

export default function AddressPicker() {
  const [a, setA] = useState({});

  // Changing a level clears every level below it.
  const choose = (level, value) => {
    const keep = LEVELS.slice(0, LEVELS.indexOf(level));
    setA({ ...Object.fromEntries(keep.map((k) => [k, a[k]])), [level]: value });
  };

  const options = {
    province: getProvinces(),
    district: a.province ? getDistrictsByProvince(a.province) : [],
    sector: a.district ? getSectorsByDistrict(a.province, a.district) : [],
    cell: a.sector ? getCellsBySector(a.province, a.district, a.sector) : [],
    village: a.cell ? getVillagesByCell(a.province, a.district, a.sector, a.cell) : [],
  };

  return LEVELS.map((level) => (
    <select key={level} value={a[level] || ''} disabled={!options[level].length}
            onChange={(e) => choose(level, e.target.value)}>
      <option value="">Select {level}</option>
      {options[level].map((o) => <option key={o}>{o}</option>)}
    </select>
  ));
}

Vue 3

<script setup>
import { ref, computed } from 'vue';
import { getProvinces, getDistrictsByProvince } from '@linkfy-connect/rwanda-geo-data';
const province = ref('');
const districts = computed(() => getDistrictsByProvince(province.value));
</script>
<template>
  <select v-model="province"><option v-for="p in getProvinces()" :key="p">{{ p }}</option></select>
  <select><option v-for="d in districts" :key="d">{{ d }}</option></select>
</template>

Angular, Svelte, Next.js, Nuxt and React Native use the same import. In Next.js and Nuxt it also works in server code.

The dataset is about 190 kB in the bundle (roughly 40 kB gzipped). To keep it out of the first load, import it lazily: const geo = await import('@linkfy-connect/rwanda-geo-data').

Python

import rwanda_geo_data as geo

geo.get_country()                                              # 'Rwanda'
geo.get_districts_by_province("East")                          # ['Bugesera', 'Gatsibo', ...]
geo.get_sectors_by_district("East", "Bugesera")                # ['Gashora', 'Juru', ...]
geo.get_cells_by_sector("East", "Bugesera", "Gashora")         # ['Biryogo', 'Kabuye', ...]
geo.get_villages_by_cell("East", "Bugesera", "Gashora", "Biryogo")

geo.search("gashora bugesera", limit=10)
geo.get_random_location()
geo.count_locations()
geo.DATA                                                       # raw nested dict

FastAPI

from fastapi import FastAPI
import rwanda_geo_data as geo

app = FastAPI()

@app.get("/districts/{province}")
def districts(province: str):
    return geo.get_districts_by_province(province)

@app.get("/search")
def search(q: str):
    return geo.search(q, limit=20)

Django and Flask work the same way: call these functions in any view, serializer or form validator.

Development

npm test            # builds dist/, syncs data to python/, runs JS tests
cd python && python -m unittest discover -s tests

data/rwanda-geo-data.json is the single source of truth. Edit it, then run npm run build: this regenerates dist/ and copies the data into the Python package.

Author

Developed by LinkFy Connect (github.com/megacodesdev).

Issues and contributions: https://github.com/megacodesdev/Rwanda-Geo-Data/issues

License

MIT © LinkFy Connect

Metadata

Release files for rwanda-geo-data 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rwanda-geo-data 1.1.0
File Size Uploaded
rwanda_geo_data-1.1.0.tar.gz 79.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rwanda-geo-data 1.1.0
File Interpreter ABI Platform
rwanda_geo_data-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 155.8 kB

Release files / rwanda_geo_data-1.1.0.tar.gz

Download URL rwanda_geo_data-1.1.0.tar.gz
Size 79.2 kB
Tags Source
SHA-256 checksum
How to use checksums
b359a3cceb52fe2b9229c2c33d061ffc1b28e2b07e26f4a4db7e8a5591e9dffe
BLAKE2b-256 checksum
How to use checksums
b48c0cdde30ec89293cc00d510a9dfac187fea16c676135d2c7983f9f9fc6f84
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release files / rwanda_geo_data-1.1.0-py3-none-any.whl

Download URL rwanda_geo_data-1.1.0-py3-none-any.whl
Size 76.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
64cbe974baa9701586e7609caec3c2bdec95e21c1afcfce0b78041268ba084d2
BLAKE2b-256 checksum
How to use checksums
ecf491c0e389bd518f233392d76cd578a84c273a1d0031f8e763b51781f56b63
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

1.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page