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

bd-map-explorer

v1.0.1

Published

Bangladesh interactive map — Division, Zila, Upazila, Union drill-down with region isolation

Readme

bd-map-explorer

Interactive Bangladesh map for React apps. Drill down through the full admin hierarchy — Division (বিভাগ) → Zila/District (জেলা) → Upazila (উপজেলা) → Union (ইউনিয়ন) — with automatic region isolation: only Bangladesh (or the selected area) stays visible on the map.

npm version license


Table of contents


Features

  • Bangladesh-only view — India, Myanmar, and other countries are masked out
  • 4-level drill-down — Division → Zila → Upazila → Union
  • Accurate boundaries — Official upazila GeoJSON + union ADM4 data
  • Region isolation — Each click zooms and masks to only that admin area
  • Bilingual — English and Bangla labels in the sidebar (EN / BN toggle)
  • Breadcrumb navigation — Jump back to any parent level
  • Headless mode — Use useMapHierarchy + IsolatedAreaMap with your own UI
  • Area picker — Optional satellite map with polygon drawing (AreaMapPicker)
  • Works with Vite, CRA, Next.js (client-side)

Demo

Live source & demo app: github.com/sadikworkbd5622/Bd-map

git clone https://github.com/sadikworkbd5622/Bd-map.git
cd Bd-map/demo
npm install
npm run dev

Installation

npm install bd-map-explorer leaflet react-leaflet

Peer dependencies

Your app must already have (or install):

npm install react react-dom leaflet react-leaflet

| Package | Version | |---------|---------| | react | >= 18 | | react-dom | >= 18 | | leaflet | ^1.9.4 | | react-leaflet | ^5.0.0 |


Quick start

1. Import Leaflet CSS (required, once in your app entry):

import "leaflet/dist/leaflet.css";

2. Render the full explorer:

import { MapExplorer, I18nProvider } from "bd-map-explorer";

export default function App() {
  return (
    <I18nProvider>
      <div style={{ minHeight: "100vh", background: "#0b0c10" }}>
        <MapExplorer />
      </div>
    </I18nProvider>
  );
}

That’s it. Users can select divisions, zilas, upazilas, and unions from the sidebar; the map updates automatically.


How it works

bd-map-explorer separates navigation state from map rendering. A single hook (useMapHierarchy) owns the admin selection and loads the right GeoJSON for each level. The map (IsolatedAreaMap) receives that geometry and applies a visual mask so only the active region remains visible.

Architecture

flowchart LR
  subgraph UI["User interface"]
    SB["MapExplorerSidebar"]
    BC["Breadcrumb / EN·BN toggle"]
  end

  subgraph State["State & data"]
    H["useMapHierarchy"]
    LOC["Location lists\n(EN / BN JSON)"]
    API["Boundary API\n(CDN + bundled ADM4)"]
  end

  subgraph Map["Map layer"]
    IAM["IsolatedAreaMap"]
    MASK["Dynamic mask"]
    TILES["OpenStreetMap tiles"]
  end

  SB --> H
  BC --> H
  H --> LOC
  H --> API
  H -->|baseBoundary, activeRegion| IAM
  IAM --> MASK
  IAM --> TILES

| Layer | Responsibility | |-------|----------------| | MapExplorerSidebar | Lists divisions, zilas, upazilas, and unions; drives selection handlers | | useMapHierarchy | Tracks selected IDs, resolves labels, fetches boundaries, exposes activeRegion | | IsolatedAreaMap | Renders tiles, draws the region outline, masks everything outside the focus area | | Boundary API | Returns GeoJSON for Bangladesh, divisions, districts, upazilas, and unions |

Selection lifecycle

  1. User picks an admin unit in the sidebar (or via handleSelectDivision, handleSelectDistrict, etc.).
  2. The hook builds a selection key from the current division → zila → upazila → union path.
  3. Boundary data is fetched asynchronously from the CDN (divisions through upazilas) or from bundled ADM4 data (unions).
  4. activeRegion updates only when the loaded boundary matches the current selection, so fast clicks do not flash a parent region.
  5. The map fits bounds and applies a mask so neighbouring countries and unselected areas are hidden.

Region isolation

Isolation is not a simple zoom. IsolatedAreaMap computes a dynamic mask from the focus polygon:

  • No selectioncountryMaskBoundary (division-level polygons) keeps the view limited to Bangladesh.
  • Division / zila / upazila / union selectedactiveRegion becomes the focus polygon; everything outside it is covered with a solid overlay (maskOpacity: 1 by default).
  • Pan limitsMapBoundsEnforcer keeps the viewport within Bangladesh bounds.
  • ZoomfitMaxZoom scales with admin level (country → union) for a consistent framing.

What users see at each level

| User action | Map behavior | |-------------|--------------| | Initial load | Bangladesh only; India, Myanmar, and other neighbours masked | | Select division | Zoom to division; mask outside its boundary | | Select zila (district) | Zoom to district; parent division hidden | | Select upazila | Zoom to upazila; higher levels hidden | | Select union | Zoom to union; ADM4 boundary loaded on first use (~12 MB) | | Breadcrumb or Reset | Step up one level or return to full Bangladesh view |

Headless usage

You do not need MapExplorer for this flow. Use useMapHierarchy with your own UI and pass the returned boundaries into IsolatedAreaMap — the same state machine and masking logic apply.


Components

<MapExplorer />

All-in-one UI: sidebar + isolated map. Best for drop-in usage.

<MapExplorer className="optional-css-class" />

| Prop | Type | Default | Description | |------|------|---------|-------------| | className | string | "" | Extra CSS class on the root container |


<IsolatedAreaMap />

Map component only — pair with your own sidebar or useMapHierarchy.

<IsolatedAreaMap
  baseBoundary={h.baseBoundary}
  countryMaskBoundary={h.countryMaskBoundary}
  activeRegion={h.activeRegion}
  fitMaxZoom={11}
  showResetControl={true}
  onReset={h.resetAll}
  style={{ height: "100%", width: "100%" }}
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | baseBoundary | FeatureCollection | — | Full Bangladesh boundary (required) | | countryMaskBoundary | FeatureCollection | — | Coarse mask for country-level view | | activeRegion | FeatureCollection \| null | — | Currently selected region to show | | maskOpacity | number | 1 | Outside mask opacity (0–1) | | maskColor | string | #0b0c10 | Color outside selected region | | fitMaxZoom | number | 13 | Max zoom when fitting bounds | | showResetControl | boolean | false | Show “Zoom Out” button | | onReset | function | — | Called when reset button clicked | | mapKey | string | — | Force layer refresh on selection change | | className | string | — | Wrapper class | | style | object | — | Wrapper inline styles | | children | ReactNode | — | Extra map overlays |


<MapExplorerSidebar />

Sidebar only — pass the full object from useMapHierarchy.

const mapHierarchy = useMapHierarchy();
<MapExplorerSidebar mapHierarchy={mapHierarchy} />

<AreaMapPicker />

Satellite map with search, polygon drawing, and area calculation. Useful for land / plot selection UIs. Requires I18nProvider.


Hooks

useMapHierarchy()

Manages admin selection state and loads GeoJSON boundaries asynchronously.

import { useMapHierarchy } from "bd-map-explorer";

function MyComponent() {
  const h = useMapHierarchy();
  // ...
}

Return value

| Property | Type | Description | |----------|------|-------------| | lang | "en" \| "bn" | Current language | | setLang | function | Set language ("en" or "bn") | | baseBoundary | FeatureCollection | Full Bangladesh GeoJSON | | countryMaskBoundary | FeatureCollection | Division-level mask for country view | | activeRegion | FeatureCollection \| null | Boundary matching current selection | | isLoadingBoundary | boolean | true while boundary is loading | | isRegionStale | boolean | true when map is catching up to selection | | selectedDivisionId | string | Selected division ID | | selectedDistrictId | string | Selected zila ID | | selectedUpazilaId | string | Selected upazila ID | | selectedUnionId | string | Selected union ID | | availableDivisions | { value, title }[] | Divisions for current lang | | availableDistricts | { value, title }[] | Zilas in selected division | | availableUpazilas | { value, title }[] | Upazilas in selected zila | | availableUnions | { value, title }[] | Unions in selected upazila | | handleSelectDivision | (id) => void | Select division | | handleSelectDistrict | (id) => void | Select zila | | handleSelectUpazila | (id) => void | Select upazila | | handleSelectUnion | (id) => void | Select union | | resetAll | () => void | Clear selection → Bangladesh view |

Selection handlers

IDs come from bundled location data (locations.en.json / locations.bn.json). Example — Barisal division:

h.handleSelectDivision("10");   // Barisal
h.handleSelectDistrict("4");    // Barguna (within Barisal)

useAreaBoundary() / usePolygonDrawing()

Lower-level hooks for AreaMapPicker — search Nominatim, draw polygons, compute area.


Boundary API

Async functions that return GeoJSON FeatureCollection objects.

import {
  getBangladeshBoundary,
  getBangladeshMaskBoundary,
  getDivisionBoundary,
  getDistrictBoundary,
  getUpazilaBoundary,
  getUnionBoundary,
} from "bd-map-explorer";

getBangladeshBoundary()

All upazila polygons for Bangladesh.

const bd = await getBangladeshBoundary();

getBangladeshMaskBoundary()

Division-level polygons — used for reliable country-only masking.

const mask = await getBangladeshMaskBoundary();

getDivisionBoundary(divisionId)

// Barisal division (location id 10)
const barisal = await getDivisionBoundary("10");

getDistrictBoundary(districtTitle, options)

const barguna = await getDistrictBoundary("Barguna", {
  districtId: "4",      // from location data
  divisionId: "10",     // parent division (recommended)
});

getUpazilaBoundary(upazilaTitle, districtTitle, options)

const sadar = await getUpazilaBoundary("Barguna Sadar", "Barguna", {
  divisionId: "10",
  districtId: "4",
  upazilaId: "1",
});

getUnionBoundary(unionTitle, upazilaTitle, districtTitle, options)

const union = await getUnionBoundary(
  "Union Name",
  "Barguna Sadar",
  "Barguna",
  { upazilaBoundary: upazilaGeoJson }  // optional, improves accuracy
);

Union boundaries use bundled ADM4 data (~12 MB, lazy-loaded on first use).


Constants & utilities

import {
  BD_CENTER,
  BD_BOUNDS,
  DEFAULT_ZOOM,
  FIT_ZOOM_BY_LEVEL,
  MAP_MASK_COLOR,
  MAP_TILE_URL,
  REGION_OUTLINE_STYLE,
  LOCATION_DIVISION_TO_GEO_ID,
  BD_BOUNDARIES_URL,
  buildDynamicMask,
  computeDynamicBounds,
  calculateGeodesicArea,
  isPointInGeoJson,
  searchNominatim,
} from "bd-map-explorer";

FIT_ZOOM_BY_LEVEL

| Level | Max zoom | |-------|----------| | country | 7 | | division | 9 | | district | 11 | | upazila | 14 | | union | 17 |


Framework guides

Vite + React

No extra config. Import Leaflet CSS in main.jsx:

import "leaflet/dist/leaflet.css";
import { MapExplorer, I18nProvider } from "bd-map-explorer";

Create React App

Same as Vite. Add Leaflet CSS to index.js or App.js.

Next.js (App Router)

Leaflet requires the browser. Use dynamic import with ssr: false:

"use client";

import dynamic from "next/dynamic";
import "leaflet/dist/leaflet.css";
import { I18nProvider } from "bd-map-explorer";

const MapExplorer = dynamic(
  () => import("bd-map-explorer").then((m) => m.MapExplorer),
  { ssr: false, loading: () => <p>Loading map…</p> }
);

export default function MapPage() {
  return (
    <I18nProvider>
      <main style={{ height: "100vh" }}>
        <MapExplorer />
      </main>
    </I18nProvider>
  );
}

Next.js (Pages Router)

import dynamic from "next/dynamic";
import "leaflet/dist/leaflet.css";

const MapExplorer = dynamic(
  () => import("bd-map-explorer").then((m) => m.MapExplorer),
  { ssr: false }
);

Examples

Example 1 — Full explorer (simplest)

import "leaflet/dist/leaflet.css";
import { MapExplorer, I18nProvider } from "bd-map-explorer";

export default function MapPage() {
  return (
    <I18nProvider defaultLang="bn">
      <MapExplorer />
    </I18nProvider>
  );
}

Example 2 — Custom sidebar

import "leaflet/dist/leaflet.css";
import {
  useMapHierarchy,
  IsolatedAreaMap,
  MapExplorerSidebar,
} from "bd-map-explorer";

export default function CustomLayout() {
  const h = useMapHierarchy();

  return (
    <div style={{ display: "flex", height: "80vh" }}>
      <MapExplorerSidebar mapHierarchy={h} />
      <IsolatedAreaMap
        baseBoundary={h.baseBoundary}
        countryMaskBoundary={h.countryMaskBoundary}
        activeRegion={h.activeRegion}
        style={{ flex: 1 }}
      />
    </div>
  );
}

Example 3 — Boundary data only (no React map)

import { getDistrictBoundary } from "bd-map-explorer";

const dhakaZila = await getDistrictBoundary("Dhaka", {
  districtId: "18",
  divisionId: "30",
});

console.log(dhakaZila.features.length, "upazila polygons");

Example 4 — React to selection changes

import { useMapHierarchy } from "bd-map-explorer";
import { useEffect } from "react";

function LocationTracker() {
  const {
    selectedDivisionId,
    selectedDistrictId,
    availableDistricts,
    handleSelectDistrict,
  } = useMapHierarchy();

  useEffect(() => {
    if (selectedDivisionId === "30" && !selectedDistrictId) {
      // Auto-select Dhaka when Dhaka division is picked
      const dhaka = availableDistricts.find((d) => d.title === "Dhaka");
      if (dhaka) handleSelectDistrict(dhaka.value);
    }
  }, [selectedDivisionId, selectedDistrictId, availableDistricts]);

  return null;
}

Data sources

| Data | Source | Bundled | |------|--------|---------| | Division / Zila / Upazila boundaries | bangladesh-geojson (CDN) | Fetched at runtime | | Union boundaries (ADM4) | geoBoundaries simplified | Yes (unions-adm4.json) | | Location lists (EN/BN) | Bangladesh admin hierarchy JSON | Yes (locations.en.json, locations.bn.json) |

Division location IDs map to GeoJSON via LOCATION_DIVISION_TO_GEO_ID:

| Division | Location ID | Geo ID | |----------|-------------|--------| | Barisal | 10 | 1 | | Chattogram | 20 | 2 | | Dhaka | 30 | 3 | | Khulna | 40 | 4 | | Mymensingh | 45 | 8 | | Rajshahi | 50 | 5 | | Rangpur | 55 | 6 | | Sylhet | 60 | 7 |


Troubleshooting

Map is blank / grey tiles

  • Import Leaflet CSS: import "leaflet/dist/leaflet.css"
  • Ensure the map container has a height (e.g. height: 80vh)

window is not defined (Next.js)

Neighbouring countries visible

  • Use countryMaskBoundary + activeRegion from useMapHierarchy
  • Ensure maskOpacity is 1 (default)

Union boundary not found

  • First union click loads ~12 MB ADM4 data (may take a few seconds)
  • Union names must match bundled location data spelling

Map shows wrong region after fast clicking

  • useMapHierarchy handles race conditions internally; wait for isLoadingBoundary to become false

Development

Build from source

git clone https://github.com/sadikworkbd5622/Bd-map.git
cd Bd-map/packages/bd-map-explorer
npm install
npm run build

Link locally into another project

cd packages/bd-map-explorer
npm link

cd your-react-app
npm link bd-map-explorer

Publish new version

# Bump version in package.json first (e.g. 1.0.1)
npm run build
npm publish --access public

License

MIT © sadikworkbd5622


Links

  • npm: https://www.npmjs.com/package/bd-map-explorer
  • GitHub: https://github.com/sadikworkbd5622/Bd-map
  • Issues: https://github.com/sadikworkbd5622/Bd-map/issues