← BACK TO WRITING
26 JUL 2026·4 min·personal

The Default World Map Draws the Wrong India

I added a travel map to this site in an afternoon. The dataset every tutorial reaches for shipped Jammu & Kashmir truncated — here's why, and the five lines that fix it.

I wanted the fridge-magnet map — the one with pins in every place you've been — as a page on this site. The stack is well-trodden: d3-geo for the projection, topojson-client to unpack the geometry, and the world-atlas package for the countries themselves. Server-render the SVG, ship no JavaScript, done in an afternoon.

Then I looked at it. India was highlighted, and the crown was missing. Jammu & Kashmir had been sliced off along the Line of Control.

Why it happens

world-atlas is a packaging of Natural Earth, the public-domain dataset behind an enormous share of the world maps you've ever seen on the web. It is excellent, and it is the default in every tutorial, every starter repo, every Stack Overflow answer about drawing countries in D3.

Natural Earth draws de-facto boundaries — control on the ground, not sovereign claims. That's a defensible editorial policy for a global dataset, and it's why the same file will also disappoint a Chinese, Pakistani, Israeli, or Moroccan reader in their own particular way. There's no neutral world map; there's only a choice of whose claim you render, and Natural Earth has made one.

For anything published from India, that choice is wrong. Official depiction — full J&K, Ladakh, Aksai Chin — is the expected standard here, not a stylistic preference, and maps that show otherwise have drawn objections. If you're building for an Indian audience, the default dataset is a bug you inherit silently.

The fix

Get the official boundaries. Two good sources:

  • DataMeet's maps repo — the community-maintained standard for Indian boundary data, CC-BY.
  • udit-001/india-maps-data — the same boundaries, repackaged as a single ready-to-use TopoJSON with all 36 states and UTs (and every district, if you ever want that granularity).

Vendor the file into your repo — don't fetch it at runtime — and merge the states into one national outline:

import { merge } from "topojson-client";
import indiaTopo from "@/data/india.topo.json";
 
const india = {
  type: "Feature",
  properties: {},
  geometry: merge(indiaTopo, indiaTopo.objects.states.geometries),
};

topojson.merge dissolves the shared internal borders and hands back a single polygon: the country, drawn correctly.

Then, when rendering the world, skip Natural Earth's India and paint yours on top:

{countries.features.map((f) =>
  f.properties.name === "India" ? null : <path d={path(f)} />
)}
<path d={path(india)} fill="#B8830F" fillOpacity={0.22} />

Order matters — painting last means your outline wins wherever the two datasets disagree along the border. Total cost: one vendored file and about five lines.

While you're at it: India deserves its own frame

A second thing the world map can't do is show you India properly. The country is roughly 3,000 km across, which at world scale is about 8% of the canvas — my hometown and my current city, half a subcontinent apart, rendered as one smudge. Pins for Kanyakumari and Srinagar are indistinguishable.

So there are two maps on the page now: the world, with India tinted but pinless, and a second frame with India projected to fill it, state boundaries drawn in, every city its own pin. Same data file, filtered by country. If your domestic travel is most of your travel — and if you live in India, it probably is — the zoomed view is the one that actually tells the story.

One unrelated rule, learned the same afternoon

A public travel map is a broadcast about where you are. A pin that appears before a trip happens is an announcement that your house will shortly be empty.

So the rule for mine: a place is added only after the trip ends. I have flights booked for October; those pins go up in November. It costs nothing and it closes an obvious hole — the kind that's easy to miss when you're thinking about projections and hover states rather than about who else reads your site.

The part I actually want to remember

I didn't catch the boundary myself while building. The map got made, it looked good, and I nearly moved on. It was only when I sat back and looked at the thing as a reader — as an Indian reader — that the missing crown jumped out.

Defaults are decisions someone else made about your work, and they arrive invisibly. The dataset had an editorial policy. The tutorial that recommended it didn't mention that. It rendered beautifully, passed the build, and was wrong.

Look at the output. Not the code — the output.