npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nepali-geo-pro-max

v1.0.0

Published

नेपाली भूगोल — Nepal administrative divisions: 7 provinces, 77 districts, metropolitan / sub-metropolitan / municipalities / rural municipalities, with bilingual names (English + Devanagari), capital coordinates, fuzzy search, address formatter, address p

Readme

nepali-geo-pro-max

नेपाली भूगोल — pro-max edition

The complete Nepal administrative-divisions library. 7 provinces, 77 districts, metropolitan / sub-metropolitan / municipalities / rural municipalities — with bilingual names, postal codes, capital coordinates, fuzzy search, address formatter, parser, and validator.

npm version License: MIT TypeScript Zero deps Bilingual


📑 Table of Contents

  1. Highlights
  2. Install
  3. Quick Start
  4. Coverage
  5. Mental Model
  6. Lookups
  7. Fuzzy Search
  8. Address Formatting
  9. Address Parsing
  10. Address Validation
  11. Maps & GeoJSON (SVG render + point-in-polygon)
  12. Legacy Admin Layer (5 regions, 14 zones, 75 districts — bidirectional mapping)
  13. Full API Reference
  14. Recipes
  15. Contributing

✨ Highlights

  • 🇳🇵 Bilingual everywhere — every record has nameEn (Roman) + nameNe (Devanagari)
  • 📍 All 7 provinces + 77 districts (post-2017 federal restructuring)
  • 🏙️ All 753 local-level units — 6 metropolitan + 11 sub-metropolitan + 276 municipalities + 460 rural municipalities, with bilingual names and ward counts
  • 🏛️ Legacy admin layer — 5 development regions, 14 zones, 75 pre-2017 districts with bidirectional cross-walks to the modern federal hierarchy (for migrating archived datasets)
  • 🔍 Fuzzy search — Levenshtein-based, bilingual queries, alias-aware (KMC → KMC, Patan → Lalitpur)
  • 📮 Postal-code lookup — find a city by 5-digit code
  • 📝 Address formatter — short / long / postal styles, English or Nepali output
  • 📥 Address parser — free-form input → structured parts with confidence score
  • Address validator — checks province/district/local-unit/ward consistency
  • 🆔 Stable canonical IDsP3.D05.L01.W05 (modern) and R2 / Z05 / LD23 (legacy)
  • 🗺️ Maps included — official चुच्चे (chuche) map of Nepal (post-May-2020 boundary including Kalapani / Lipulekh / Limpiyadhura), bundled GeoJSON for 7 provinces + 77 districts + all 753 palikas, toSvg() for static/SSR + toSvgPaths() for interactive React/Vue/Svelte, plus (lat, lng) → district / palika point-in-polygon — works in Node, the browser, and edge runtimes
  • 📦 Tree-shakeable — pure ESM exports, sideEffects: false
  • 🎯 TypeScript-first — strict mode, branded types, full JSDoc
  • 🪶 Zero dependencies — Node, Deno, Bun, browsers, Workers

📦 Install

npm install nepali-geo-pro-max
pnpm add nepali-geo-pro-max
yarn add nepali-geo-pro-max
bun add nepali-geo-pro-max

Works with Node ≥ 14, modern browsers, Deno, Bun, Cloudflare Workers, Vercel Edge.


⚡ Quick Start

import {
  PROVINCES,
  getProvinces,
  getDistrictsByProvince,
  getLocalUnit,
  search,
  formatAddress,
  parseAddress,
  validateAddress,
} from "nepali-geo-pro-max";

// All 7 provinces
PROVINCES.length;                                    // 7

// Districts of Bagmati
getDistrictsByProvince("Bagmati").map((d) => d.nameEn);
// ["Bhaktapur", "Chitwan", "Dhading", ..., "Sindhupalchok"]

// Lookup by alias
getLocalUnit("KMC")?.nameEn;
// "Kathmandu Metropolitan City"

// Fuzzy + bilingual search
search("ललितपुर")[0]?.record.nameEn;                  // "Lalitpur"
search("Lalipur")[0]?.score;                          // ≈ 0.87  (typo tolerated)

// Format an address
formatAddress({
  province: "Bagmati",
  district: "Kathmandu",
  localUnit: "Kathmandu Metropolitan City",
  ward: 5,
});
// "Ward 5, Kathmandu Metropolitan City, Kathmandu, Bagmati Province"

// Same address in Nepali
formatAddress(parts, { locale: "ne" });
// "वडा ५, काठमाडौँ महानगरपालिका, काठमाडौँ, बागमती"

// Parse free-form input
parseAddress("Ward 5, KMC, Kathmandu, Bagmati").parts;
// { ward: 5, localUnit: "P3.D05.L01", district: "P3.D05", province: "P3" }

// Validate
validateAddress({ province: "Bagmati", district: "Kaski" }).errors;
// [{ field: "district", code: "mismatch", message: "..." }]

📊 Coverage

Current (post-2015 / 2017 federal)

| Level | v1.0 | Total in Nepal | Notes | |---|---|---|---| | Provinces | 7 / 7 ✅ | 7 | Full bilingual + capital + area + population | | Districts | 77 / 77 ✅ | 77 | Full bilingual + headquarters + aliases | | Metropolitan cities | 6 / 6 ✅ | 6 | Postal codes + coordinates + bilingual | | Sub-metropolitan cities | 11 / 11 ✅ | 11 | Postal codes + coordinates + bilingual | | Municipalities | 276 / 276 ✅ | 276 | Bilingual + ward counts | | Rural municipalities | 460 / 460 ✅ | 460 | Bilingual + ward counts | | All local-level units | 753 / 753 ✅ | 753 | Full 6+11+276+460 ✓ | | Postal codes (palika) | 409 / 753 (54.3%) | ~753 | All 17 metro/sub-metro + all 77 district HQs + 216 VDC-merger-derived palikas | | District postcode prefixes | 77 / 77 ✅ | 77 | First-2-digit lookup | | Wards (per palika) | derived ✅ | ~6,743 | Each palika carries wards: number; individual ward records generated on demand |

Two postal-code systems are shipped:

| Constant | Coverage | When to use | |---|---|---| | POSTAL_CODES | 409 / 753 (54.3%) | Mail addressing — the legacy 1991 codes that are printed on physical mail today | | POSTAL_CODES_2025 | 752 / 753 (99.9%) | Canonical palika lookups — federal-aligned PDDPP structure (province + district + palika) |

Legacy 1991 coverage maxes out at ~54% because most rural municipalities were formed in 2017 by merging former VDCs, and Nepal Post never opened a branch in many of those VDCs. We omit rather than guess for unverified palikas.

Legacy admin layer (for cross-walking archived data)

| Level | Shipped | Notes | |---|---|---| | Development Regions | 5 / 5 ✅ | Eastern, Central, Western, Mid-Western, Far-Western | | Zones | 14 / 14 ✅ | Mechi, Koshi, Sagarmatha, Janakpur, Bagmati, Narayani, Gandaki, Lumbini, Dhaulagiri, Rapti, Bheri, Karnali, Seti, Mahakali | | Legacy districts | 75 / 75 ✅ | With bidirectional cross-walks (Nawalparasi → Nawalpur + Parasi; Rukum → Eastern + Western Rukum) |


🧠 Mental Model

| Rule | Why | |---|---| | Hierarchy is strictly Province → District → LocalUnit → Ward. | Matches Nepal's federal structure. | | Every record has bilingual names. | Forms in Nepal often need Devanagari display + Roman storage. | | Lookups are name-tolerant. | Kavre finds "Kavrepalanchok"; Province 3 finds "Bagmati"; Patan finds "Lalitpur". | | IDs are stable and hierarchical. | P3.D05.L01.W05 survives renames. Use IDs for storage; show names to users. | | Search is bilingual + fuzzy. | search("ललितपुर") and search("Lalipur") both succeed. | | The parser stores IDs, the formatter displays names. | Round-trip: formatAddress(parseAddress(s).parts) regenerates clean text. | | Postal codes are 5-digit per Nepal Postal Service. | Kathmandu = 44600, Pokhara = 33700, Lalitpur = 44700, Bharatpur = 44200. |


🔎 Lookups

// Provinces
getProvinces();                               // all 7
getProvince("Bagmati");                       // by English name
getProvince(3);                               // by number 1-7
getProvince("बागमती");                         // by Devanagari
getProvince("Province 3");                    // by alias
getProvince("P3");                            // by id

// Districts
getDistricts();                               // all 77
getDistrict("Kathmandu");
getDistrict("Kavre");                         // alias resolves to Kavrepalanchok
getDistrictsByProvince("Bagmati");            // 13 districts

// Local units (palikas)
getLocalUnits();                              // 17 (metro + sub-metro in v1)
getLocalUnit("KMC");                          // alias
getLocalUnit("काठमाडौँ महानगरपालिका");
getLocalUnitsByDistrict("Kathmandu");
getLocalUnitsByProvince("Bagmati");

// Postal codes
findByPostalCode("44600")?.nameEn;            // "Kathmandu Metropolitan City"

// Hierarchy walk
getParents("P3.D05.L01");
// { province: <Bagmati>, district: <Kathmandu>, localUnit: <KMC> }

// Iterators
for (const p of eachProvince()) console.log(p.nameEn);

// Validation predicates
isValidProvince("Bagmati");                   // true
isValidWard("KMC", 5);                        // true (KMC has 32 wards)
isValidWard("KMC", 99);                       // false

🔍 Fuzzy Search

search("Lalitpur");
// [
//   { level: "district", record: <Lalitpur>, score: 1 },
//   { level: "local-unit", record: <Lalitpur Metropolitan City>, score: ~0.85 },
// ]

search("ललितपुर");                              // bilingual — same hits
search("Lalipur");                            // typo → "Lalitpur" wins with score ~0.87
search("KMC");                                // alias → KMC

// Filter by level
search("Janakpur", { levels: ["local-unit"] });

// Tune fuzzy threshold
search("xyz", { threshold: 0.6, limit: 5 });

📝 Address Formatting

const parts = {
  province: "Bagmati",
  district: "Kathmandu",
  localUnit: "Kathmandu Metropolitan City",
  ward: 5,
  postalCode: "44600",
};

formatAddress(parts);
// "Ward 5, Kathmandu Metropolitan City, Kathmandu, Bagmati Province"

formatAddress(parts, { style: "short" });
// "Ward 5, Kathmandu, Kathmandu, Bagmati"

formatAddress(parts, { style: "postal" });
// "Ward 5, Kathmandu Metropolitan City, Kathmandu, 44600, Bagmati Province"

formatAddress(parts, { locale: "ne" });
// "वडा ५, काठमाडौँ महानगरपालिका, काठमाडौँ, बागमती"

formatAddress(
  { house: "12", tole: "Thamel", ward: 26, district: "Kathmandu", province: "Bagmati" },
);
// "12, Thamel, Ward 26, Kathmandu, Bagmati Province"

📥 Address Parsing

parseAddress("Ward 5, Kathmandu Metropolitan City, Kathmandu, Bagmati");
// {
//   parts: { ward: 5, localUnit: "P3.D05.L01", district: "P3.D05", province: "P3" },
//   confidence: 0.9,
//   unmatched: [],
// }

parseAddress("वडा १०, काठमाडौँ").parts.ward;       // 10  (Devanagari ward number)
parseAddress("KMC").parts.province;                  // "P3"  (parents auto-filled)
parseAddress("44600, Kathmandu").parts.postalCode;   // "44600"

// Re-format from parsed parts:
const r = parseAddress("Ward 5, KMC, Kathmandu");
formatAddress(r.parts);
// "Ward 5, Kathmandu Metropolitan City, Kathmandu, Bagmati Province"

✅ Address Validation

validateAddress({
  province: "Bagmati",
  district: "Kathmandu",
  localUnit: "KMC",
  ward: 5,
});
// { ok: true, errors: [], resolved: { province, district, localUnit } }

validateAddress({ province: "Bagmati", district: "Kaski" });
// { ok: false, errors: [{ field: "district", code: "mismatch",
//   message: 'District "Kaski" is not in province "Bagmati"' }], ... }

validateAddress({ localUnit: "KMC", ward: 99 });
// { ok: false, errors: [{ field: "ward", code: "unknown",
//   message: 'Ward 99 out of range — "Kathmandu Metropolitan City" has wards 1..32' }] }

🗺️ Maps & GeoJSON

The package ships the official चुच्चे (chuche) map of Nepal — the post-May-2020 boundary that includes Kalapani, Lipulekh, and Limpiyadhura in Darchula district (Sudurpashchim Province). Simplified district + province GeoJSON, plus a zero-dep toSvg() renderer and point-in-polygon lookup. Works in Node, browsers, Bun, Deno, and edge runtimes.

5-line map

import { toSvg } from "nepali-geo-pro-max";
import { NEPAL_PROVINCES_GEO } from "nepali-geo-pro-max/geo/provinces";

const PROVINCE_COLORS = {
  P1: "#E63946",  // Koshi — crimson (Nepal flag)
  P2: "#F4A261",  // Madhesh — sand
  P3: "#2A9D8F",  // Bagmati — teal
  P4: "#457B9D",  // Gandaki — steel blue
  P5: "#E9C46A",  // Lumbini — saffron
  P6: "#7209B7",  // Karnali — royal purple
  P7: "#F77F00",  // Sudurpashchim — orange
};

const svg = toSvg(NEPAL_PROVINCES_GEO, {
  width: 960,
  fill: (f) => PROVINCE_COLORS[f.properties.id],
  background: "#F8FAFC",
});
fs.writeFileSync("nepal.svg", svg);

Districts coloured by province

import { toSvg } from "nepali-geo-pro-max";
import { NEPAL_DISTRICTS_GEO } from "nepali-geo-pro-max/geo/districts";

const svg = toSvg(NEPAL_DISTRICTS_GEO, {
  width: 960,
  fill: (f) => PROVINCE_COLORS[f.properties.provinceId],
  stroke: "#FFFFFF",
  strokeWidth: 0.6,
  background: "#F8FAFC",
  featureAttrs: (f) => ({
    "data-name": f.properties.nameEn,    // wire up tooltips / hover
    "data-id": f.properties.id,
  }),
});

Highlight a single district

toSvg(NEPAL_DISTRICTS_GEO, {
  fill: (f) => f.properties.id === "P3.D05" ? "#E63946" : "#E2E8F0",
  background: "#F8FAFC",
});

Dark theme

toSvg(NEPAL_DISTRICTS_GEO, {
  fill: (f) => PROVINCE_COLORS[f.properties.provinceId],
  stroke: "#0F172A",
  strokeWidth: 0.5,
  background: "#0F172A",
});

All 753 palikas

import { toSvg } from "nepali-geo-pro-max";
import { NEPAL_LOCAL_UNITS_GEO } from "nepali-geo-pro-max/geo/local-units";

const svg = toSvg(NEPAL_LOCAL_UNITS_GEO, {
  width: 1200,
  fill: (f) => PROVINCE_COLORS[f.properties.provinceId],
  stroke: "#FFFFFF",
  strokeWidth: 0.25,
  background: "#F8FAFC",
});

Palikas coloured by type

const TYPE_COLORS = {
  metropolitan: "#E63946",        // crimson — the 6 metros
  "sub-metropolitan": "#F77F00",  // orange — the 11 sub-metros
  municipality: "#457B9D",        // steel blue — 276 municipalities
  "rural-municipality": "#A8DADC",// pale teal — 460 rural municipalities
};

toSvg(NEPAL_LOCAL_UNITS_GEO, {
  width: 1200,
  fill: (f) => TYPE_COLORS[f.properties.type],
  stroke: "#FFFFFF",
  strokeWidth: 0.25,
  background: "#F8FAFC",
});

Point-in-polygon at palika resolution

import { findLocalUnitFeatureByCoords } from "nepali-geo-pro-max";
import { NEPAL_LOCAL_UNITS_GEO } from "nepali-geo-pro-max/geo/local-units";

const lu = findLocalUnitFeatureByCoords(NEPAL_LOCAL_UNITS_GEO, 27.7172, 85.3240);
// → { properties: {
//     nameEn: "Kathmandu",
//     nameNe: "काठमाडौँ महानगरपालिका",
//     districtId: "P3.D05",
//     provinceId: "P3",
//     type: "metropolitan"
//   }, ... }

Use in React / Vue / Svelte / Solid

The toSvg() helper above returns a string — perfect for SSR, static files, emails, or dangerouslySetInnerHTML. For interactive maps where you want hover, click, or per-district event handlers, use toSvgPaths() instead — it returns structured per-feature data your framework can render natively.

React

import { toSvgPaths } from "nepali-geo-pro-max";
import { NEPAL_DISTRICTS_GEO } from "nepali-geo-pro-max/geo/districts";
import { useMemo, useState } from "react";

const PROVINCE_COLORS = {
  P1: "#E63946", P2: "#F4A261", P3: "#2A9D8F", P4: "#457B9D",
  P5: "#E9C46A", P6: "#7209B7", P7: "#F77F00",
};

export function NepalMap() {
  const [hovered, setHovered] = useState<string | null>(null);
  const { paths, viewBox } = useMemo(
    () => toSvgPaths(NEPAL_DISTRICTS_GEO, {
      width: 800,
      fill: (f) => PROVINCE_COLORS[f.properties.provinceId],
    }),
    [],
  );

  return (
    <svg viewBox={viewBox} role="img" aria-label="Map of Nepal">
      {paths.map((p) => (
        <path
          key={p.id}
          d={p.d}
          fill={hovered === p.id ? "#000" : p.fill}
          stroke="#fff"
          strokeWidth={0.5}
          onMouseEnter={() => setHovered(p.id)}
          onMouseLeave={() => setHovered(null)}
          onClick={() => alert(p.feature.properties.nameEn)}
          style={{ cursor: "pointer", transition: "fill 0.15s" }}
        />
      ))}
    </svg>
  );
}

Vue 3

<script setup lang="ts">
import { toSvgPaths } from "nepali-geo-pro-max";
import { NEPAL_DISTRICTS_GEO } from "nepali-geo-pro-max/geo/districts";
import { ref } from "vue";

const PROVINCE_COLORS = {
  P1: "#E63946", P2: "#F4A261", P3: "#2A9D8F", P4: "#457B9D",
  P5: "#E9C46A", P6: "#7209B7", P7: "#F77F00",
};

const { paths, viewBox } = toSvgPaths(NEPAL_DISTRICTS_GEO, {
  width: 800,
  fill: (f) => PROVINCE_COLORS[f.properties.provinceId],
});

const hovered = ref<string | null>(null);
</script>

<template>
  <svg :viewBox="viewBox" role="img">
    <path
      v-for="p in paths"
      :key="p.id"
      :d="p.d"
      :fill="hovered === p.id ? '#000' : p.fill"
      stroke="#fff"
      stroke-width="0.5"
      @mouseenter="hovered = p.id"
      @mouseleave="hovered = null"
      @click="$emit('select', p.feature.properties.nameEn)"
      style="cursor: pointer"
    />
  </svg>
</template>

Svelte

<script lang="ts">
  import { toSvgPaths } from "nepali-geo-pro-max";
  import { NEPAL_DISTRICTS_GEO } from "nepali-geo-pro-max/geo/districts";

  const PROVINCE_COLORS = {
    P1: "#E63946", P2: "#F4A261", P3: "#2A9D8F", P4: "#457B9D",
    P5: "#E9C46A", P6: "#7209B7", P7: "#F77F00",
  } as const;

  const { paths, viewBox } = toSvgPaths(NEPAL_DISTRICTS_GEO, {
    width: 800,
    fill: (f) => PROVINCE_COLORS[f.properties.provinceId],
  });

  let hovered: string | null = null;
</script>

<svg {viewBox} role="img">
  {#each paths as p (p.id)}
    <path
      d={p.d}
      fill={hovered === p.id ? "#000" : p.fill}
      stroke="#fff" stroke-width="0.5"
      on:mouseenter={() => (hovered = p.id)}
      on:mouseleave={() => (hovered = null)}
      on:click={() => alert(p.feature.properties.nameEn)}
    />
  {/each}
</svg>

SSR / static / dangerouslySetInnerHTML

If you don't need interactivity, the simpler toSvg() returns a complete SVG string:

import { toSvg } from "nepali-geo-pro-max";
import { NEPAL_PROVINCES_GEO } from "nepali-geo-pro-max/geo/provinces";

const svg = toSvg(NEPAL_PROVINCES_GEO, { width: 800 });

// React: <div dangerouslySetInnerHTML={{ __html: svg }} />
// Next.js / Astro / SvelteKit: server-render once, embed in HTML
// Node script: fs.writeFileSync("nepal.svg", svg)

Point-in-polygon (lat, lng → district / province)

import {
  findDistrictFeatureByCoords,
  findProvinceFeatureByCoords,
} from "nepali-geo-pro-max";
import { NEPAL_DISTRICTS_GEO } from "nepali-geo-pro-max/geo/districts";
import { NEPAL_PROVINCES_GEO } from "nepali-geo-pro-max/geo/provinces";

findDistrictFeatureByCoords(NEPAL_DISTRICTS_GEO, 27.7172, 85.3240);
// → { properties: { id: "P3.D05", provinceId: "P3", nameEn: "Kathmandu" }, ... }

findProvinceFeatureByCoords(NEPAL_PROVINCES_GEO, 28.2096, 83.9856);
// → { properties: { id: "P4" }, ... }

Combine with the rest of the package:

import { findDistrictFeatureByCoords, getDistrict, getLocalUnitsByDistrict } from "nepali-geo-pro-max";
import { NEPAL_DISTRICTS_GEO } from "nepali-geo-pro-max/geo/districts";

const feature = findDistrictFeatureByCoords(NEPAL_DISTRICTS_GEO, lat, lng);
const district = feature && getDistrict(feature.properties.id);
const palikas = district && getLocalUnitsByDistrict(district.id);

toSvg() options

| Option | Default | Description | |---|---|---| | width | 800 | SVG width in px | | height | auto | If unset, derived from bbox aspect ratio | | padding | 8 | Padding around the map | | fill | "#e0e0e0" | colour string OR (feature) => string | | stroke | "#fff" | stroke colour | | strokeWidth | 0.5 | stroke width | | background | "none" | SVG background colour (skipped when "none") | | title | — | Accessible <title> element | | svgAttrs | {} | Extra root <svg> attributes | | featureAttrs | — | (f) => Record<string, string \| number> per <path> | | idPrefix | "path" | Used when a feature lacks an id |

Bundle strategy

| Import | Size (ESM) | Contents | |---|---|---| | nepali-geo-pro-max | ~232 KB | Everything except heavy GeoJSON | | nepali-geo-pro-max/geo | ~6 KB | Just toSvg, findFeatureByCoords, types | | nepali-geo-pro-max/geo/provinces | ~141 KB | NEPAL_PROVINCES_GEO only (7 polygons) | | nepali-geo-pro-max/geo/districts | ~151 KB | NEPAL_DISTRICTS_GEO only (77 polygons) | | nepali-geo-pro-max/geo/local-units | ~554 KB | NEPAL_LOCAL_UNITS_GEO only (753 palikas, simplified at tolerance 1e-4) |

Polygon GeoJSON loads only if you import the /geo/* subpaths — bundlers like Vite, esbuild, Rollup, and webpack tree-shake them out otherwise.

About the boundary

The maps ship the official चुच्चे (chuche) map of Nepal — the post-May-2020 boundary that incorporates Kalapani, Lipulekh, and Limpiyadhura in Darchula district (Sudurpashchim Province) per Nepal's constitutional amendment. All 77 federal-restructured districts and 753 post-2017 palikas.

Verified by tests:

  • Western beak (30.45°N, 80.60°E) is inside Darchula district ✓
  • Lipulekh Pass (30.222°N, 81.023°E) is inside Darchula ✓
  • Kalapani (30.20°N, 80.95°E) is inside Darchula ✓
  • Province bounding box reaches lat 30.47 (vs old map's ~29.93 for the same area) ✓

🏛️ Legacy Admin Layer (pre-2015 / pre-2017)

Nepal's federal restructuring (2015 constitution + 2017 local-level reorganization) replaced an older hierarchy that many archived datasets still use:

  • 5 Development Regions (विकास क्षेत्र) — abolished 2015
  • 14 Zones (अञ्चल) — abolished 2015
  • 75 Districts — pre-2017, before Nawalparasi & Rukum splits

The package ships this layer only for bidirectional cross-walking between legacy datasets and the modern federal hierarchy — it's not a complete legacy GIS layer (no VDCs, no coordinates, no headquarters). Migrate, then drop it.

import {
  getRegion, getZone, getLegacyDistrict,
  getZonesByRegion, getLegacyDistrictsByZone, getLegacyDistrictsByRegion,
  getCurrentDistrictsForLegacyDistrict,
  getLegacyDistrictForCurrentDistrict,
  crossWalk,
} from "nepali-geo-pro-max";

// Lookups (bilingual + alias-tolerant)
getRegion("Western")?.id;                  // "R3"
getRegion("पूर्वाञ्चल")?.id;                 // "R1"
getZone("Bagmati")?.regionId;               // "R2" (Central)
getLegacyDistrict("Kathmandu")?.zoneId;     // "Z05" (Bagmati)
getLegacyDistrict("Kavre")?.nameEn;         // "Kavrepalanchok" (alias)

// Hierarchy
getZonesByRegion("R1").map((z) => z.nameEn);
// ["Mechi", "Koshi", "Sagarmatha"]
getLegacyDistrictsByZone("Bagmati").length;          // 8
getLegacyDistrictsByRegion("Mid-Western").length;    // 15

// --- The headline: bidirectional cross-walk ---
getCurrentDistrictsForLegacyDistrict("Nawalparasi").map((d) => d.nameEn);
// ["Nawalpur", "Parasi"]               ← the 2017 split

getCurrentDistrictsForLegacyDistrict("Rukum").map((d) => d.nameEn);
// ["Eastern Rukum", "Western Rukum"]   ← split

getLegacyDistrictForCurrentDistrict("Nawalpur")?.nameEn;       // "Nawalparasi"
getLegacyDistrictForCurrentDistrict("Eastern Rukum")?.nameEn;  // "Rukum"

// Full chain
crossWalk("Kathmandu");
// {
//   legacy: <Kathmandu>,
//   current: [<Kathmandu (P3.D05)>],
//   zone: <Bagmati>,
//   region: <Central>,
// }

Region → Zone → Districts

| Region | Zones | Pre-2017 Districts | |---|---|---| | R1 Eastern (पूर्वाञ्चल) | Mechi · Koshi · Sagarmatha | 4 + 6 + 6 = 16 | | R2 Central (मध्यमाञ्चल) | Janakpur · Bagmati · Narayani | 6 + 8 + 5 = 19 | | R3 Western (पश्चिमाञ्चल) | Gandaki · Lumbini · Dhaulagiri | 6 + 6 + 4 = 16 | | R4 Mid-Western (मध्य-पश्चिमाञ्चल) | Rapti · Bheri · Karnali | 5 + 5 + 5 = 15 | | R5 Far-Western (सुदूर-पश्चिमाञ्चल) | Seti · Mahakali | 5 + 4 = 9 | | Total | 14 zones | 75 districts ✓ |


🧰 Full API Reference

| Function | Description | |---|---| | getProvinces() | All 7 provinces | | getProvince(query) | By id / number / name (any language) / alias | | getDistricts() | All 77 districts | | getDistrict(query) | By id / name / alias | | getDistrictsByProvince(p) | Districts in a province | | getLocalUnits() | All shipped local units | | getLocalUnit(query) | By id / name / alias | | getLocalUnitsByDistrict(d) | Local units in a district | | getLocalUnitsByProvince(p) | Local units in a province | | findByPostalCode(code) | Local unit by 5-digit code | | getParents(id) | Walk parent chain to province |

| Function | Description | |---|---| | eachProvince() | Iterable of provinces | | eachDistrict() | Iterable of districts | | eachLocalUnit() | Iterable of local units |

| Function | Description | |---|---| | isValidProvince(q) | True if recognized | | isValidDistrict(q) | True if recognized | | isValidLocalUnit(q) | True if recognized | | isValidWard(localUnit, ward) | True if ward in range |

| Function | Description | |---|---| | search(query, opts?) | Fuzzy + bilingual cross-level search |

SearchOptions: { levels?: ["province" \| "district" \| "local-unit"], limit?, threshold? }.

| Function | Description | |---|---| | formatAddress(parts, opts?) | Structured → string | | parseAddress(raw) | String → { parts, confidence, unmatched } | | validateAddress(parts) | { ok, errors, resolved } |

FormatAddressOptions: { locale?: "en" \| "ne", style?: "short" \| "long" \| "postal", separator? }.

| Function / Constant | Description | |---|---| | toSvg(fc, opts?) | Render a FeatureCollection to a self-contained SVG string (SSR, emails, static files) | | toSvgPaths(fc, opts?) | Project to structured { viewBox, paths: [{id, d, fill, feature}] } for React/Vue/Svelte rendering with event handlers | | computeBBox(fc) | [minLng, minLat, maxLng, maxLat] | | findProvinceFeatureByCoords(fc, lat, lng) | Point-in-polygon | | findDistrictFeatureByCoords(fc, lat, lng) | Point-in-polygon | | findLocalUnitFeatureByCoords(fc, lat, lng) | Point-in-polygon at palika resolution | | findFeatureByCoords(fc, lat, lng) | Generic — works on any FeatureCollection | | pointInGeometry(point, geometry) | Low-level: ray-cast on a polygon/multipolygon | | NEPAL_PROVINCES_GEO | nepali-geo-pro-max/geo/provinces — 7 provinces | | NEPAL_DISTRICTS_GEO | nepali-geo-pro-max/geo/districts — 77 districts | | NEPAL_LOCAL_UNITS_GEO | nepali-geo-pro-max/geo/local-units — 753 palikas |

Types: BBox, ToSvgOptions, SvgPath, SvgPathsResult, Position, Polygon, MultiPolygon, PolygonGeometry, Ring, ProvinceGeoFeature, DistrictGeoFeature, LocalUnitGeoFeature, ProvinceGeoFeatureCollection, DistrictGeoFeatureCollection, LocalUnitGeoFeatureCollection, AdminFeature.

| Function | Description | |---|---| | getRegions() | All 5 development regions | | getRegion(query) | By id / number 1-5 / name / alias | | getZones() | All 14 zones | | getZone(query) | By id / name / alias | | getZonesByRegion(region) | Zones inside a region | | getLegacyDistricts() | All 75 pre-2017 districts | | getLegacyDistrict(query) | By id / name / alias | | getLegacyDistrictsByZone(zone) | Districts inside a zone | | getLegacyDistrictsByRegion(region) | Districts inside a region | | getCurrentDistrictsForLegacyDistrict(legacy) | Legacy → 1 or 2 modern districts | | getLegacyDistrictForCurrentDistrict(current) | Modern → legacy | | crossWalk(legacy) | { legacy, current[], zone, region } | | eachRegion / eachZone / eachLegacyDistrict() | Iterators | | isValidRegion / isValidZone / isValidLegacyDistrict | Predicates |

| Constant | Description | |---|---| | PROVINCES | All 7 provinces | | DISTRICTS | All 77 districts | | LOCAL_UNITS | Shipped local units | | REGIONS | All 5 development regions | | ZONES | All 14 zones | | LEGACY_DISTRICTS | All 75 pre-2017 districts | | POSTAL_CODES | Legacy 1991 system — 409 palika → primary code | | POSTAL_CODES_2025 | New GPO federal-aligned system — 752 palika → 5-digit PDDPP code | | POSTAL_CODE_BRANCHES | 21 districts with full branch-postcode lists | | DISTRICT_POSTCODE_PREFIXES | All 77 districts → first 2 digits (legacy) | | DISTRICTS_BY_PROVINCE_COUNT | { P1: 14, P2: 8, ... } | | LOCAL_UNIT_TYPE_COUNTS | Nepal-wide counts | | TOTAL_LOCAL_UNITS | 753 | | TOTAL_LEGACY_DISTRICTS | 75 |

Current: Province, District, LocalUnit, Ward, LocalUnitType, ProvinceId, DistrictId, LocalUnitId, WardId, LatLng, AddressParts, AddressValidationResult, SearchHit.

Legacy: Region, Zone, LegacyDistrict, RegionId, ZoneId, LegacyDistrictId, CrossWalkResult.


🎯 Recipes

Province / district dropdown

import { getProvinces, getDistrictsByProvince } from "nepali-geo-pro-max";

function AddressForm() {
  const [provinceId, setProvinceId] = useState("");
  const provinces = getProvinces();
  const districts = provinceId ? getDistrictsByProvince(provinceId) : [];

  return (
    <>
      <select onChange={(e) => setProvinceId(e.target.value)}>
        {provinces.map((p) => (
          <option key={p.id} value={p.id}>{p.nameEn} ({p.nameNe})</option>
        ))}
      </select>
      <select disabled={!provinceId}>
        {districts.map((d) => (
          <option key={d.id} value={d.id}>{d.nameEn} ({d.nameNe})</option>
        ))}
      </select>
    </>
  );
}

Address autocomplete

import { search } from "nepali-geo-pro-max";

function suggestions(query: string) {
  return search(query, { limit: 8, threshold: 0.5 }).map((hit) => ({
    label: hit.record.nameEn + " (" + hit.record.nameNe + ")",
    value: hit.record.id,
    level: hit.level,
    score: hit.score,
  }));
}

Round-trip validation

import { parseAddress, validateAddress, formatAddress } from "nepali-geo-pro-max";

const userInput = "Ward 5, KMC, Kathmandu, Bagmati";
const { parts, confidence } = parseAddress(userInput);
const { ok, errors } = validateAddress(parts);

if (ok && confidence > 0.7) {
  const cleaned = formatAddress(parts);
  // Save `parts` (canonical IDs), display `cleaned`.
}

Postal-code reverse lookup

import { findByPostalCode, getParents } from "nepali-geo-pro-max";

const unit = findByPostalCode("44600");
if (unit) {
  const { province, district } = getParents(unit.id);
  console.log(`${unit.nameEn}, ${district?.nameEn}, ${province?.nameEn}`);
}

🤝 Contributing

PRs welcome. Common contributions:

  • Postal-code mappings for additional palikas (legacy 1991 system) — see src/data/postal-codes.ts
  • Bilingual aliases / spelling variants for districts and palikas
  • Bug fixes, type improvements, documentation polish
npm install
npm test
npm run typecheck
npm run build

📜 License

MIT © 2026 l3lackcurtains


Made with ❤️ for the Nepali developer community.

बनाइएको नेपाली डेभलपर समुदायको लागि।