bd-geo-address 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,622 @@
1
+ # bd-geo-address
2
+
3
+ > Bangladesh address library — division/district/upazila/thana lookup, FHIR geocode resolution, Bangla name support, postal codes, geolocation. Zero dependencies. Works on Node.js, React, Next.js, Vue, Angular, NestJS, Deno and Bun.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/bd-geo-address.svg)](https://www.npmjs.com/package/bd-geo-address)
6
+ [![npm downloads](https://img.shields.io/npm/dm/bd-geo-address.svg)](https://www.npmjs.com/package/bd-geo-address)
7
+ [![license](https://img.shields.io/npm/l/bd-geo-address.svg)](./LICENSE)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/)
9
+
10
+ ---
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install bd-geo-address
16
+ # or
17
+ yarn add bd-geo-address
18
+ # or
19
+ pnpm add bd-geo-address
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Quick Start
25
+
26
+ ### Name-Based API
27
+
28
+ ```ts
29
+ import { districtsOf, upazilasOf, isUpazila, isThana, DivisionName } from "bd-geo-address"
30
+
31
+ // All districts in Dhaka division
32
+ districtsOf(DivisionName.Dhaka)
33
+ // → ['Dhaka', 'Gazipur', 'Kishoreganj', 'Manikganj', 'Munshiganj', 'Narayanganj', ...]
34
+
35
+ // Upazilas of Tangail district
36
+ upazilasOf("Tangail")
37
+ // → [{ upazila: 'Tangail Sadar', upazilaBn: 'টাঙ্গাইল সদর', district: 'Tangail', ... }, ...]
38
+
39
+ // Correctly distinguishes upazilas from metropolitan thanas
40
+ isUpazila("Savar") // → true
41
+ isUpazila("Gulshan") // → false (Gulshan is a metropolitan thana)
42
+ isThana("Gulshan") // → true
43
+ ```
44
+
45
+ ### FHIR Code API
46
+
47
+ ```ts
48
+ import { resolveCode, toAddressString, toShortAddress } from "bd-geo-address"
49
+
50
+ // App stores FHIR geocode in the database, resolves at runtime
51
+ const address = resolveCode("30262530")
52
+
53
+ address?.formatted.en
54
+ // → 'Kafrul, Dhaka North City Corporation, Dhaka, Dhaka Division'
55
+
56
+ address?.formatted.bn
57
+ // → 'কাফরুল, ঢাকা উত্তর সিটি কর্পোরেশন, ঢাকা, ঢাকা বিভাগ'
58
+
59
+ toShortAddress("30262530") // → 'Kafrul, Dhaka'
60
+ ```
61
+
62
+ ---
63
+
64
+ ## CommonJS Usage
65
+
66
+ ```js
67
+ const { allDivision, districtsOf, resolveCode, DivisionName } = require("bd-geo-address")
68
+
69
+ console.log(allDivision())
70
+ // → ['Dhaka', 'Chattogram', 'Mymensingh', 'Khulna', 'Rajshahi', 'Rangpur', 'Sylhet', 'Barisal']
71
+
72
+ console.log(resolveCode("30262530").formatted.short)
73
+ // → 'Kafrul, Dhaka'
74
+ ```
75
+
76
+ ---
77
+
78
+ ## API Reference
79
+
80
+ ### Division
81
+
82
+ ```ts
83
+ import {
84
+ allDivision, allDivisionBn,
85
+ divisionalDataOf, divisionalDataOfBn,
86
+ isValidDivision, isValidDivisionBn,
87
+ } from "bd-geo-address"
88
+
89
+ allDivision()
90
+ // → ['Dhaka', 'Chattogram', 'Mymensingh', 'Khulna', 'Rajshahi', 'Rangpur', 'Sylhet', 'Barisal']
91
+
92
+ allDivisionBn()
93
+ // → ['ঢাকা', 'চট্টগ্রাম', 'ময়মনসিংহ', 'খুলনা', 'রাজশাহী', 'রংপুর', 'সিলেট', 'বরিশাল']
94
+
95
+ divisionalDataOf("Sylhet")
96
+ // → { districts: ['Sylhet', 'Sunamganj', 'Habiganj', 'Moulvibazar'], upazilas: [...] }
97
+
98
+ divisionalDataOfBn("ঢাকা")
99
+ // → { districts: [...], upazilas: [...] }
100
+
101
+ isValidDivision("Dhaka") // → true
102
+ isValidDivision("InvalidName") // → false
103
+ isValidDivisionBn("ঢাকা") // → true
104
+ ```
105
+
106
+ ---
107
+
108
+ ### District
109
+
110
+ ```ts
111
+ import {
112
+ allDistricts, allDistrictsBn,
113
+ districtsOf, districtsOfBn,
114
+ isValidDistrict, isValidDistrictBn,
115
+ getDivisionOfDistrict, getDivisionOfDistrictBn,
116
+ } from "bd-geo-address"
117
+
118
+ allDistricts() // → all 64 district names in English
119
+ allDistrictsBn() // → all 64 district names in Bangla
120
+
121
+ districtsOf("Dhaka") // → ['Dhaka', 'Gazipur', 'Kishoreganj', ...]
122
+ districtsOfBn("ঢাকা") // → ['Dhaka', 'Gazipur', ...]
123
+
124
+ isValidDistrict("Tangail") // → true
125
+ isValidDistrict("Savar") // → false (upazila, not a district)
126
+
127
+ getDivisionOfDistrict("Tangail") // → 'Dhaka'
128
+ getDivisionOfDistrict("Cox's Bazar") // → 'Chattogram'
129
+ getDivisionOfDistrictBn("টাঙ্গাইল") // → 'Dhaka'
130
+ ```
131
+
132
+ ---
133
+
134
+ ### Upazila
135
+
136
+ ```ts
137
+ import {
138
+ allUpazila, allUpazilaNames, allUpazilaNamesBn,
139
+ upazilasOf, upazilasOfBn,
140
+ upazilaNamesOf, upazilaNamesOfBn,
141
+ upazilasOfDivision, upazilasOfDivisionBn,
142
+ upazilaNamesOfDivision, upazilaNamesOfDivisionBn,
143
+ isUpazila, isUpazilabn,
144
+ getUpazila, getUpazilabn,
145
+ getDistrictOfUpazila, getDistrictOfUpazilabn,
146
+ upazilaData,
147
+ } from "bd-geo-address"
148
+
149
+ allUpazila() // → all 595 upazila names
150
+
151
+ upazilasOf("Tangail")
152
+ // → [{ upazila: 'Tangail Sadar', upazilaBn: 'টাঙ্গাইল সদর', district: 'Tangail', ... }, ...]
153
+
154
+ upazilaNamesOf("Tangail") // → ['Tangail Sadar', 'Basail', 'Bhuapur', ...]
155
+ upazilaNamesOfBn("টাঙ্গাইল") // → Bangla upazila names for Tangail
156
+
157
+ upazilasOfDivision("Sylhet") // → all upazilas in Sylhet division
158
+
159
+ isUpazila("Savar") // → true
160
+ isUpazila("Gulshan") // → false (metropolitan thana)
161
+ isUpazilabn("সাভার") // → true
162
+
163
+ getUpazila("Savar")
164
+ // → { upazila: 'Savar', upazilaBn: 'সাভার', district: 'Dhaka', division: 'Dhaka', ... }
165
+
166
+ // Disambiguate names that appear in multiple divisions:
167
+ getUpazila("Mohammadpur", "Khulna")
168
+ // → { upazila: 'Mohammadpur', district: 'Magura', division: 'Khulna', ... }
169
+
170
+ getDistrictOfUpazila("Savar") // → 'Dhaka'
171
+ getDistrictOfUpazilabn("সাভার") // → 'Dhaka'
172
+
173
+ upazilaData // raw Upazila[] constant — all 595 objects
174
+ ```
175
+
176
+ ---
177
+
178
+ ### Thana
179
+
180
+ The 26 metropolitan police thanas across Dhaka (15), Chattogram (6), Khulna (3), and Rajshahi (2).
181
+
182
+ ```ts
183
+ import {
184
+ allThana, allThanaNames,
185
+ thanasOf, thanasOfBn,
186
+ thanaNamesOf, thanaNamesOfBn,
187
+ isThana, isThanabn,
188
+ getThana, getThanabn,
189
+ thanaData,
190
+ } from "bd-geo-address"
191
+
192
+ allThana() // → all 26 Thana objects
193
+ allThanaNames() // → ['Adabor', 'Badda', 'Banani', ..., 'Matihar']
194
+
195
+ thanasOf("Dhaka") // → 15 Dhaka thana objects
196
+ thanaNamesOf("Dhaka") // → ['Adabor', 'Badda', 'Banani', ...]
197
+ thanaNamesOfBn("ঢাকা") // → Bangla thana names
198
+
199
+ // isThana without district = true if it is a thana in any city
200
+ isThana("Gulshan") // → true
201
+ isThana("Kotwali", "Dhaka") // → true (disambiguate)
202
+ isThana("Kotwali", "Chattogram") // → true
203
+ isThanabn("গুলশান") // → true
204
+
205
+ getThana("Gulshan")
206
+ // → { thana: 'Gulshan', thanaBn: 'গুলশান', district: 'Dhaka', division: 'Dhaka', type: 'thana' }
207
+
208
+ getThana("Kotwali", "Chattogram")
209
+ // → { thana: 'Kotwali', district: 'Chattogram', ... }
210
+
211
+ thanaData // raw Thana[] constant — all 26 objects
212
+ ```
213
+
214
+ | City | Thanas |
215
+ |------------|--------|
216
+ | Dhaka | 15 |
217
+ | Chattogram | 6 |
218
+ | Khulna | 3 |
219
+ | Rajshahi | 2 |
220
+
221
+ ---
222
+
223
+ ### Search
224
+
225
+ ```ts
226
+ import { searchLocations } from "bd-geo-address"
227
+
228
+ searchLocations("pur")
229
+ // → matches any division, district, upazila, or thana containing "pur"
230
+ // → [{ name: 'Rangpur', type: 'division' }, { name: 'Mirpur', type: 'thana', district: 'Dhaka' }, ...]
231
+
232
+ searchLocations("সিলেট")
233
+ // → Bangla search works the same way
234
+ ```
235
+
236
+ ---
237
+
238
+ ### FHIR Code Resolution
239
+
240
+ ```ts
241
+ import { resolveCode, resolveCodes } from "bd-geo-address"
242
+
243
+ resolveCode("30262530")
244
+ // {
245
+ // fhirCode: '30262530',
246
+ // thana: { code: '30262530', name: 'Kafrul', level: 'thana', ... },
247
+ // city_corp:{ name: 'Dhaka North City Corporation', ... },
248
+ // district: { name: 'Dhaka', ... },
249
+ // division: { name: 'Dhaka', ... },
250
+ // formatted: {
251
+ // en: 'Kafrul, Dhaka North City Corporation, Dhaka, Dhaka Division',
252
+ // bn: 'কাফরুল, ঢাকা উত্তর সিটি কর্পোরেশন, ঢাকা, ঢাকা বিভাগ',
253
+ // short: 'Kafrul, Dhaka',
254
+ // shortBn: 'কাফরুল, ঢাকা'
255
+ // },
256
+ // postal: { postalCode: '1216', postOfficeName: 'Kafrul', ... }
257
+ // }
258
+
259
+ resolveCodes(["30262530", "10040009", "INVALID"])
260
+ // → [BDAddress, BDAddress, null]
261
+ ```
262
+
263
+ ---
264
+
265
+ ### Address Formatting
266
+
267
+ ```ts
268
+ import { toAddressString, toShortAddress, toCustomAddress } from "bd-geo-address"
269
+
270
+ toAddressString("30262530")
271
+ // → 'Kafrul, Dhaka North City Corporation, Dhaka, Dhaka Division'
272
+
273
+ toAddressString("30262530", "bn")
274
+ // → 'কাফরুল, ঢাকা উত্তর সিটি কর্পোরেশন, ঢাকা, ঢাকা বিভাগ'
275
+
276
+ toShortAddress("30262530") // → 'Kafrul, Dhaka'
277
+ toShortAddress("30262530", "bn") // → 'কাফরুল, ঢাকা'
278
+
279
+ toCustomAddress("30262530", ["thana", "district", "division"])
280
+ // → 'Kafrul, Dhaka, Dhaka Division'
281
+
282
+ toCustomAddress("30262530", ["thana", "district"], "bn")
283
+ // → 'কাফরুল, ঢাকা'
284
+ ```
285
+
286
+ ---
287
+
288
+ ### Lookup by FHIR Code
289
+
290
+ ```ts
291
+ import { getByCode, findByName, findByNameBn, search, getAllByLevel } from "bd-geo-address"
292
+
293
+ getByCode("30") // → { code: '30', name: 'Dhaka', level: 'division', ... }
294
+ getByCode("INVALID") // → null
295
+
296
+ findByName("Dhaka") // → [division, district, ...]
297
+ findByName("Dhaka", "district") // → [{ name: 'Dhaka', level: 'district', ... }]
298
+ findByNameBn("ঢাকা") // → same but by Bangla name
299
+
300
+ search("Dhaka") // → English first, then Bangla fallback
301
+
302
+ getAllByLevel("division") // → all 8 division BDLocation objects
303
+ getAllByLevel("district") // → all 64 district BDLocation objects
304
+ getAllByLevel("upazila") // → all upazila BDLocation objects
305
+ ```
306
+
307
+ ---
308
+
309
+ ### Hierarchy
310
+
311
+ ```ts
312
+ import { getParent, getChildren, getAllDescendants, getHierarchy } from "bd-geo-address"
313
+
314
+ getParent("1004") // → { code: '10', name: 'Barishal', level: 'division', ... }
315
+ getParent("10") // → null (divisions have no parent)
316
+
317
+ getChildren("10") // → all direct children of Barishal division
318
+ getAllDescendants("10") // → all locations under Barishal at any depth
319
+ getAllDescendants("10", "district") // → only districts
320
+ getAllDescendants("10", "upazila") // → only upazilas
321
+
322
+ getHierarchy("30262530")
323
+ // → BDAddress without the `formatted` field
324
+ ```
325
+
326
+ ---
327
+
328
+ ### Validation
329
+
330
+ ```ts
331
+ import { isValidCode, isValidName, isValidNameBn, isChildOf, getLevel } from "bd-geo-address"
332
+
333
+ isValidCode("30262530") // → true
334
+ isValidCode("INVALID") // → false
335
+
336
+ isValidName("Dhaka") // → true
337
+ isValidName("Dhaka", "division") // → true
338
+ isValidName("Dhaka", "upazila") // → false
339
+ isValidNameBn("ঢাকা", "division") // → true
340
+
341
+ isChildOf("10040009", "10") // → true (in Barishal division)
342
+ isChildOf("10040009", "1004") // → true (in Barguna district)
343
+ isChildOf("10040009", "30") // → false (not in Dhaka)
344
+
345
+ getLevel("10") // → 'division'
346
+ getLevel("1004") // → 'district'
347
+ getLevel("10040009") // → 'upazila'
348
+ getLevel("INVALID") // → null
349
+ ```
350
+
351
+ ---
352
+
353
+ ### Postal Codes
354
+
355
+ ```ts
356
+ import {
357
+ getByPostalCode, getPostalCode, getPostalCodeBn,
358
+ getPostalCodesByArea, getFhirCodeFromPostal, resolvePostalCode,
359
+ } from "bd-geo-address"
360
+
361
+ getByPostalCode("8710")
362
+ // → { postalCode: '8710', postOfficeName: 'Amtali', upazilaName: 'Amtali', districtName: 'Barguna', ... }
363
+
364
+ getPostalCode("Amtali") // → '8710'
365
+ getFhirCodeFromPostal("8710") // → '10040009' (Amtali upazila FHIR code)
366
+
367
+ getPostalCodesByArea("Barguna", "district") // → all postal entries in Barguna district
368
+ getPostalCodesByArea("Barisal", "division") // → all postal entries in Barisal division
369
+
370
+ resolvePostalCode("8710")
371
+ // → full BDAddress for Amtali (same as resolveCode('10040009'))
372
+ ```
373
+
374
+ ---
375
+
376
+ ### Geolocation
377
+
378
+ > Coordinate data requires running `npm run build:coords` with HDX shapefiles.
379
+
380
+ ```ts
381
+ import { getCoordinates, getNearestLocation, getLocationsWithinRadius } from "bd-geo-address"
382
+
383
+ getCoordinates("30") // → { lat: 23.8103, lng: 90.4125 }
384
+ getNearestLocation(23.7, 90.4) // → nearest BDLocation to the point
385
+ getNearestLocation(23.7, 90.4, "upazila") // → nearest upazila
386
+ getLocationsWithinRadius(23.7, 90.4, 50) // → all locations within 50 km
387
+ ```
388
+
389
+ ---
390
+
391
+ ## TypeScript Types
392
+
393
+ ```ts
394
+ import type {
395
+ DivisionName, // enum
396
+ DistrictName, // string literal union of all 64 districts
397
+ LocationLevel, // 'division' | 'district' | 'city_corp' | 'municipality' | 'upazila' | 'thana' | 'union' | 'paurasava'
398
+ Upazila,
399
+ Thana,
400
+ SearchResult,
401
+ BDLocation,
402
+ BDAddress,
403
+ BDPostalEntry,
404
+ } from "bd-geo-address"
405
+ ```
406
+
407
+ **`BDAddress`** — resolved FHIR geocode result:
408
+ ```ts
409
+ interface BDAddress {
410
+ fhirCode: string
411
+ division: BDLocation
412
+ district: BDLocation
413
+ city_corp?: BDLocation
414
+ municipality?: BDLocation
415
+ upazila?: BDLocation
416
+ thana?: BDLocation
417
+ union?: BDLocation
418
+ paurasava?: BDLocation
419
+ formatted: { en: string; bn: string; short: string; shortBn: string }
420
+ coordinates?: { lat: number; lng: number }
421
+ postal?: { postalCode: string; postOfficeName: string; postOfficeNameBn: string }
422
+ }
423
+ ```
424
+
425
+ **`DivisionName`** enum — use for IDE autocomplete:
426
+ ```ts
427
+ import { DivisionName } from "bd-geo-address"
428
+
429
+ DivisionName.Dhaka // → 'Dhaka'
430
+ DivisionName.Chattogram // → 'Chattogram'
431
+ // Dhaka, Chattogram, Mymensingh, Khulna, Rajshahi, Rangpur, Sylhet, Barisal
432
+ ```
433
+
434
+ ---
435
+
436
+ ## Framework Examples
437
+
438
+ ### React — Cascade Location Picker
439
+
440
+ ```tsx
441
+ import { useState } from "react"
442
+ import { allDivision, districtsOf, upazilasOf } from "bd-geo-address"
443
+
444
+ export function LocationPicker({ onChange }) {
445
+ const [division, setDivision] = useState("")
446
+ const [district, setDistrict] = useState("")
447
+
448
+ const districts = division ? districtsOf(division) : []
449
+ const upazilas = district ? upazilasOf(district) : []
450
+
451
+ return (
452
+ <div>
453
+ <select onChange={e => { setDivision(e.target.value); setDistrict("") }}>
454
+ <option value="">Division</option>
455
+ {allDivision().map(d => <option key={d} value={d}>{d}</option>)}
456
+ </select>
457
+ <select onChange={e => setDistrict(e.target.value)} disabled={!division}>
458
+ <option value="">District</option>
459
+ {districts.map(d => <option key={d} value={d}>{d}</option>)}
460
+ </select>
461
+ <select onChange={e => onChange(e.target.value)} disabled={!district}>
462
+ <option value="">Upazila</option>
463
+ {upazilas.map(u => (
464
+ <option key={u.upazila} value={u.upazila}>{u.upazilaBn} ({u.upazila})</option>
465
+ ))}
466
+ </select>
467
+ </div>
468
+ )
469
+ }
470
+ ```
471
+
472
+ ### Next.js — Server Component
473
+
474
+ ```tsx
475
+ import { resolveCode } from "bd-geo-address"
476
+
477
+ export default async function UserPage({ params }) {
478
+ const user = await fetchUser(params.id)
479
+ const address = resolveCode(user.locationCode)
480
+
481
+ return (
482
+ <div>
483
+ <p>{address?.formatted.en}</p>
484
+ <p>{address?.formatted.bn}</p>
485
+ </div>
486
+ )
487
+ }
488
+ ```
489
+
490
+ ### Vue 3
491
+
492
+ ```vue
493
+ <template>
494
+ <select v-model="selectedDivision">
495
+ <option v-for="d in divisions" :key="d" :value="d">{{ d }}</option>
496
+ </select>
497
+ <select v-model="selectedDistrict">
498
+ <option v-for="d in districts" :key="d" :value="d">{{ d }}</option>
499
+ </select>
500
+ </template>
501
+
502
+ <script setup lang="ts">
503
+ import { ref, computed } from "vue"
504
+ import { allDivision, districtsOf } from "bd-geo-address"
505
+
506
+ const selectedDivision = ref("")
507
+ const divisions = allDivision()
508
+ const districts = computed(() =>
509
+ selectedDivision.value ? districtsOf(selectedDivision.value) : []
510
+ )
511
+ </script>
512
+ ```
513
+
514
+ ### Node.js / Express
515
+
516
+ ```ts
517
+ import express from "express"
518
+ import { isValidCode, resolveCode, upazilasOf } from "bd-geo-address"
519
+
520
+ const app = express()
521
+
522
+ app.get("/address/:code", (req, res) => {
523
+ if (!isValidCode(req.params.code)) return res.status(400).json({ error: "Invalid geocode" })
524
+ res.json(resolveCode(req.params.code))
525
+ })
526
+
527
+ app.get("/upazilas/:district", (req, res) => {
528
+ res.json(upazilasOf(req.params.district))
529
+ })
530
+ ```
531
+
532
+ ### NestJS
533
+
534
+ ```ts
535
+ import { Injectable, BadRequestException } from "@nestjs/common"
536
+ import { resolveCode, isValidCode, BDAddress } from "bd-geo-address"
537
+
538
+ @Injectable()
539
+ export class LocationService {
540
+ resolve(fhirCode: string): BDAddress {
541
+ if (!isValidCode(fhirCode)) throw new BadRequestException("Invalid BD FHIR geocode")
542
+ return resolveCode(fhirCode)!
543
+ }
544
+ }
545
+ ```
546
+
547
+ ---
548
+
549
+ ## Database Dumps
550
+
551
+ For PHP, Python, Java, Go, Ruby or any non-JS project, database dumps are included in the package.
552
+
553
+ | File | Format |
554
+ |------|--------|
555
+ | `db/mysql/bangladesh-address.sql` | MySQL 8.0+ |
556
+ | `db/postgresql/bangladesh-address.sql` | PostgreSQL 13+ |
557
+ | `db/sqlite/bangladesh-address.db` | SQLite 3 binary |
558
+ | `db/mongodb/bangladesh-address.json` | MongoDB insertMany-ready |
559
+
560
+ **Schema:**
561
+ - `divisions` — 8 rows (code, name, name_bn, lat, lng)
562
+ - `districts` — 64 rows (code, name, name_bn, division_code, lat, lng)
563
+ - `locations` — 1519 rows (all other FHIR locations: city corps, upazilas, unions, etc.)
564
+ - `thanas` — 26 metropolitan thanas
565
+ - `postal_codes` — 1343 postal code entries
566
+
567
+ **MySQL example:**
568
+ ```bash
569
+ mysql -u root -p mydb < node_modules/bd-geo-address/db/mysql/bangladesh-address.sql
570
+ ```
571
+
572
+ **PostgreSQL example:**
573
+ ```bash
574
+ psql -U postgres mydb < node_modules/bd-geo-address/db/postgresql/bangladesh-address.sql
575
+ ```
576
+
577
+ **SQLite example:**
578
+ ```bash
579
+ cp node_modules/bd-geo-address/db/sqlite/bangladesh-address.db ./my-app.db
580
+ ```
581
+
582
+ **MongoDB example:**
583
+ ```js
584
+ const data = require("bd-geo-address/db/mongodb/bangladesh-address.json")
585
+ await db.collection("divisions").insertMany(data.divisions)
586
+ await db.collection("locations").insertMany(data.locations)
587
+ ```
588
+
589
+ ---
590
+
591
+ ## Data Coverage
592
+
593
+ | Level | Count |
594
+ |-------|-------|
595
+ | Divisions | 8 |
596
+ | Districts | 64 |
597
+ | City Corporations | 12 |
598
+ | Upazilas | 595 |
599
+ | Metropolitan Thanas | 26 |
600
+ | Unions | 848 |
601
+ | Postal codes | 1343 |
602
+
603
+ ---
604
+
605
+ ## Data Sources & Credits
606
+
607
+ - **FHIR geocodes** — [DGHS Bangladesh FHIR Server](https://fhir.dghs.gov.bd/core/CodeSystem-bd-geocodes.html)
608
+ - **Postal codes** — [saaiful/postcode-bd](https://github.com/saaiful/postcode-bd)
609
+ - **Coordinates** (optional) — [HDX Bangladesh Admin Boundaries](https://data.humdata.org/dataset/cod-ab-bgd)
610
+ - **Bangla names** — HDX shapefiles + manual curation
611
+
612
+ ---
613
+
614
+ ## Contributing
615
+
616
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on fixing data, adding tests, or improving the API.
617
+
618
+ ---
619
+
620
+ ## License
621
+
622
+ [MIT](./LICENSE)