WorldMap
Every example on this page is live, drawn by the library's Storybook, where you can also change its props and see the result.
A monochromatic SVG map of the world, shading its countries using fill patterns and optional numeric labels. Supports three map projections: Natural Earth (default), Mercator, and Equirectangular. The map keeps its proportions, filling as much of its box as it can and centered in the rest, in any box it is laid out in. height="fill" sizes the box to the map instead.
Import
import {
WorldMap,
COUNTRY_NAMES,
countryNameFor,
worldMapAspect,
} from "~lib/data-display/maps/world-map";
Props
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
data | Partial<Record<CountryName, WorldMapEntry>> | Yes | N/A | Per-country shading and optional value |
projection | "natural-earth" | "mercator" | "equirectangular" | No | "natural-earth" | Map projection to use |
showValues | boolean | No | false | Render numeric labels at country centroids |
markers | MapMarker[] | No | N/A | Places to mark, each in its shape with an optional name beside it |
legend | MapLegendEntry[] | No | N/A | What each marker shape stands for, in a row under the map |
width | number | string | No | 300 | Component width (px or CSS string); with height="fill", the column's width unless given |
height | number | "fill" | string | No | 300 | Component height (px or CSS string), or "fill" to be as tall as its width makes it |
CountryName
Every country the map draws. Examples: "United States", "China", "India", "Brazil", "Russia", "Japan", "Germany", "United Kingdom", "Australia", "South Africa". COUNTRY_NAMES lists them all.
countryNameFor
countryNameFor(name: string): CountryName | undefined
The map's name for a country however it is written: a long or official form ("United States of America", "Korea, Republic of"), an ISO 3166-1 two- or three-letter code ("GB", "DEU"), the name it went by ("Burma", "Swaziland") or a common spelling ("Holland", "UK"), in any case, with or without accents. It returns nothing for a country the map does not draw, such as a city state too small for it, so a widget can tell which of its places it can shade and which it should mark with markers instead. Pass the names an installer picks or an API returns through it before keying data with them.
worldMapAspect
worldMapAspect(projection?: Projection): number
The width over the height of the countries in a projection (Natural Earth unless given), for a widget that sizes the map's box itself. height="fill" uses it for you.
WorldMapEntry
type WorldMapEntry = {
shading: "solid" | "diagonal" | "dots" | "crosshatch";
value?: number;
};
Projection
| Value | Description |
|---|---|
"natural-earth" | Pseudocylindrical compromise projection (default) - good general-purpose |
"mercator" | Conformal cylindrical - preserves angles but distorts area at high lats |
"equirectangular" | Simple lat/lon grid - minimal distortion near equator |
MapMarker
type MapMarker = {
latitude: number;
longitude: number;
label?: string;
shape?: "circle" | "square" | "triangle" | "diamond"; // "circle" by default
};
MapLegendEntry
type MapLegendEntry = {
shape: "circle" | "square" | "triangle" | "diamond";
label: string;
};
Examples
Default (Natural Earth)
With Values
Mercator
Equirectangular
All Patterns
Many Countries
With Markers
markers puts a dot on each place you give, so a place too small to shade, such as Monaco or Singapore, still shows. A label sits beside its dot where there is room for it, and is left out where it would run off the map or cover another marker.
On a map drawn small the labels are left out and only the dots show.
import { WorldMap } from "~lib/data-display/maps/world-map";
const Circuit = ({
name,
latitude,
longitude,
}: {
name: string;
latitude: number;
longitude: number;
}) => (
<WorldMap
data={{}}
markers={[{ latitude, longitude, label: name }]}
width="100%"
height="100%"
/>
);
Marker Shapes
A marker's shape tells kinds of place apart on one map, such as several species, teams or fleets. Every shape covers the same area, so none reads heavier than another. legend names what each shape stands for in a row under the map, in the order given, wrapping onto further rows when it outgrows the width. The legend is left out of a box too narrow or too short to hold it beside the map, where the map alone says more.
<WorldMap
data={{}}
markers={sightings.map(({ latitude, longitude, species }) => ({
latitude,
longitude,
shape: SHAPES[species],
}))}
legend={species.map((name) => ({ shape: SHAPES[name], label: name }))}
width="100%"
height="100%"
/>
Filling a Column's Width
height="fill" makes the box as tall as its width makes it at the countries' proportion, with the projection fitted to the countries alone, so a map across the top of a column takes no more height than it draws in and the text under it starts where the countries end. In a flex column it spans the column, or width when given, and is the first to shrink when the column runs short, the map then centered in the room that is left. A legend sits under the map, outside its proportion.
<div className="flex h-full flex-col gap-2">
<WorldMap data={data} height="fill" />
<Heading level={4}>Sightings</Heading>
</div>
Countries By Any Name
import { WorldMap, countryNameFor } from "~lib/data-display/maps/world-map";
const data = Object.fromEntries(
apiCountries.flatMap((written) => {
const country = countryNameFor(written);
return country ? [[country, { shading: "solid" }]] : [];
}),
);