Dev.to WebDev 🛠 Dev 👁 0 📖 4 min read

Jigu — a world map that shows you exactly how much your map projection is lying

Every world map lies about area. The usual example is Greenland looking roughly the same size as Africa on a Mercator map, when Africa is actually 14 times larger. Mercator preserves angles (useful for navigation) at the

Every world map lies about area. The usual example is Greenland looking roughly the same size as Africa on a Mercator map, when Africa is actually 14 times larger. Mercator preserves angles (useful for navigation) at the cost of inflating area as you move away from the equator — and most of us have internalized that inflated version of the world without ever seeing the correction.

Jigu is a small interactive map built to make that distortion visible, draggable, and measurable, rather than just something you read about in a caption.

What it does

Jigu renders one world map where a glowing front sweeps from both poles toward the equator. Everything the front has already crossed is redrawn in Equal Earth projection; everything ahead of it is still Mercator. Both projections exist on screen at the same time, split by a line that moves — so you can watch the exact moment a country's shape and size change.

Three ways to interact with it:

  • Drag a country to a different latitude. Pull Greenland from 72°N down to the equator on the Mercator side and its displayed area drops from 22,683,547 km² to 2,168,727 km². Let go and it springs back home.
  • Switch between two visual platesnight (the default chart look) and ink (same drawing, pulled onto paper: cool stock, dark linework, a burnt amber accent instead of a glow).
  • Drop into a 3D globe. On the globe, "area as drawn" always equals "true area" — it's the one view with zero distortion, by definition.

Why this isn't just a d3 demo

A few implementation details in src/template.html are the actual interesting part of this project.

The blend weight is a function of latitude, not a single global t. The obvious way to morph between two projections is to interpolate one progress value across the whole map. That puts a shoulder in the silhouette and tears the polar cap off as its own trapezoid. Jigu blends width globally (by true area converted) but height locally, per latitude, which is what makes the front a genuine traveling boundary instead of a uniform fade.

Vertical position is not what gets interpolated — vertical scale is, and then it's integrated. Interpolating y directly introduces a w'(φ)·(eeY − mercY) term that blows up near the poles: the polar cap stretches ~9x and the map folds through itself if you try to hide it by narrowing the transition band. Instead:

dy/dφ = (1 − w(φ))·mercY′(φ)  +  w(φ)·eeY′(φ)

Both contributions are nonnegative, so the map geometrically cannot fold back on itself. This is evaluated over a 1600-sample table from −90° to +90°.

Distortion is measured per-polygon, not sampled at a point. A country isn't a point, and world-atlas represents Antarctica's centroid at the pole itself — where sec²φ diverges. Point-sampling gives Antarctica a Mercator inflation factor of ×2.7e+32 (yes, with exponent notation, because toFixed gives up past 1e21). Jigu instead measures the actual drawn polygon's planar area against its true spherical area:

factor = planarArea(projected) / (sphericalArea × scale at the equator)

That gives Antarctica a real measured factor of ×23.7 — still huge, but a number that means something.

Rendering is pure linework, on purpose. d3's spherical polygon clipping is the dominant cost. Filling all 225 country polygons every frame costs 61ms; rendering coastlines and borders as MultiLineString costs 6.8ms + 1.4ms combined. So nothing is filled per frame except whichever single country is currently selected — which happens to fit the aesthetic and keeps dragging at 112fps. Country picking itself uses an offscreen color-index buffer that's rebuilt only when the map settles, not every frame.

Stack

  • d3 for projections and geometry
  • three.js for the globe view (an unlit MeshBasicMaterial sphere skinned with the same 2D drawing code rendered plate-carrée into a texture — deliberately unshaded so it reads as a chart, not a rendered planet; disables itself cleanly on WebGL failure)
  • anime.js for interaction animation
  • Ships as a single self-contained index.html (~1.6MB) with geometry, country data, and brand assets inlined; the browser libraries load externally
npm ci            # Node.js 22.12+
npm run build     # regenerates src/countries.generated.json, writes index.html
npm run dev       # http://localhost:8931

index.html is committed as the actual deployed artifact — CI checks that rebuilding it produces zero diff.

Data sources, for transparency: geometry from world-atlas (Natural Earth, public domain), country attributes from world-countries (ODbL), population from the World Bank (CC BY 4.0).

License

Code is MIT. The brand marks under assets/logo/main/ identify the project specifically and aren't covered by that grant. Nine languages are shipped in the UI and not all of them have had native-speaker review yet, so translation corrections are a particularly welcome kind of PR — details in CONTRIBUTING.md.

🔗 Try it: https://rakkunn.github.io/Jigu/
🔗 Source: https://github.com/RAKKUNN/Jigu

Go drag Greenland down to the equator. Watch the number.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.