Skip to content

A world map

Real country shapes in fixed scenes: Natural Earth’s 110m countries from world-atlas, projected once with d3-geo into one set of map units, and drawn as regions with their own outlines. Continents enter to their countries, which land exactly where the continent was drawn. Lesotho is a hole in South Africa’s outline, and each label sits at its country’s labelAt, the middle of its biggest piece.

This one is an app of its own, apps/world in the repository, built on the published package. The frame below runs it, built from exactly these files. Start with source.ts; data.ts is the projection, which the explorer never sees. main.tsx keeps the focus in the URL hash, builds a breadcrumb from onSceneChange, and shows the selected country from onNodeSelect.

Open it on its own page
apps/world/src/source.ts
import { createSceneSource, type ExplorerNode, type SceneSource } from "@data-xplorer/core";
import { continentByName, continents, WORLD, type Place } from "./data";
/**
* The world as fixed scenes of regions: continents at the top, each entered to its countries. A
* focus is a continent's name. Every scene is in the same map units, and a continent's scene is
* bounded by that continent's own box, so its countries land exactly where it was drawn.
*/
type ContinentName = string;
// No `ramp` or `color`: each region keeps the explorer's stable colour of its own, so neighbours
// stand apart, as on a political map.
function region(place: Place, drillsInto: ContinentName | null, sublabel: string | null): ExplorerNode<ContinentName> {
return {
id: place.name,
label: place.name,
sublabel,
weight: 1, // a fixed scene sizes nothing by weight
href: `#${encodeURIComponent(place.name)}`,
drillsInto,
shape: "region",
box: place.box,
outline: place.outline,
labelAt: place.labelAt,
};
}
export function worldMapSource(): SceneSource<ContinentName> {
return createSceneSource<ContinentName>({
load(focus) {
if (focus === null) {
return {
id: "world",
heading: "The world",
layout: "fixed",
bounds: WORLD,
clusters: continents.map((c) => region(c, c.name, `${c.countries.length} countries`)),
nodes: [],
edges: [],
};
}
const continent = continentByName.get(focus);
if (!continent) throw new Error(`no continent ${focus}`);
return {
id: continent.name,
heading: continent.name,
layout: "fixed",
bounds: continent.box,
clusters: [],
nodes: continent.countries.map((country) => region(country, null, null)),
edges: [],
};
},
parentOf: (focus) => ({ kind: "anchored", focus: null, anchorId: focus }),
});
}
export const decodeContinent = (hash: string): ContinentName | null => (continentByName.has(hash) ? hash : null);