How it works
The naive approach ships 2.1 MB of GeoJSON and re-runs 55,000-vertex
point-in-polygon tests every time the map changes. naksha does the geometry
once, at build time — and settles a few decisions further up the stack
that are easy to get quietly wrong.
Geometry becomes a bitmap
District polygons are rasterised at build time into an indexed image where
the pixel value is the district id and 0 means outside Nepal.
At runtime, one array lookup answers inside or outside and
which district at the same time — no turf, no proj4, no polygons.
1775 × 1024 cells · 0.45 km per cell · 42 KB shipped
Bounded cost, whatever you draw
The dot field is sampled per viewport at a fixed budget rather than resolved
on one global grid — a global grid fine enough for all 753 local levels would
need ~14,700 dots, which is a solid fill, not a dotted map. Cost is bounded
by the dot count, so it never scales with geometry complexity.
Interaction doesn't undo that. Dots collapse into one path per colour, and the
dot under the pointer is found by arithmetic — the viewBox is
0 0 cols rows, so the cell is
floor(x), floor(y). Hover and click add no nodes; the
highlight adds one.
~1,110 dots · built in < 2 ms · ~94 DOM nodes
Pins snap and cluster by default
At a national view one dot covers ~134 km², so three points across the
Kathmandu valley land on the same dot. Pins that silently stacked would be a
lie about your own data, so snapping and clustering are one operation and
the badge always reports what is underneath.
Snapping knows its districts, too. A dot belongs to whichever district
covers most of its cell, so a border town can fall in a cell its
neighbour wins — Lahan is in Siraha, but its cell is mostly Saptari.
naksha moves the pin to the nearest dot of the district the coordinate
is really in, never further than one dot spacing, so a pin can't
contradict its own label.
38 locations · 7 provinces · 5 in Kathmandu on one dot
Synchronous, so SSR just works
The raster is run-length encoded and inlined as base64. Deflate would be 35%
smaller but needs an async inflate, which forces every consumer into a loading
state or a client-only boundary. naksha decodes synchronously, so it renders
inside a React server component with no "use client".
No Image · no canvas · no fetch · no top-level await
Labels erase the data they cover
A label needs a halo of the background colour to stay readable over the
field — and that halo deletes the dots underneath it. On a dot
map the field is the data, so this is not cosmetic. Measured across all
77 district labels, the default above erases a mean of
11.7 dots per label — and 74 of the 77 bury dots of the very
district they name.
So labelPlacement is a choice about what to give up, not a
style preference. avoid-region refuses to cover the district
the label names and spills onto a neighbour; clear refuses to
cover any dot and walks off the outline, trailing a leader line. Both cost
distance from the pin. Try all four in the demo.
own dots erased · above 5.0 · avoid-region 0.1 · clear 0.0
Every district headquarters, placed
74 of the 77 districts ship their headquarters as a real
coordinate on Region.hqAt, in both scripts, alongside the
OCHA p-code. Pin a district town without owning a gazetteer — and without
a geocoder, which naksha deliberately does not have.
The three without one are Nawalparasi East and both Rukums: the upstream
point set predates the 2015 splits, so no point in it honestly belongs to
them. Reaching for regionAnchor instead would put a town at
its district's centre of mass, which is a different place — so naksha
returns null rather than a plausible lie.
74/77 HQ coordinates · 77 p-codes · 2 scripts