Choropleth
Colour countries by value. The bundled geometry carries an ISO alpha-2 code, a numeric id and a name for each country, so most data joins without any mapping work.
import { createGlobe, colorScale } from "canvas-globe";
const visits = { IN: 940, US: 720, GB: 480, JP: 300, BR: 260 };
const scale = colorScale([0, 1000], ["#e0f2fe", "#0369a1"]);
createGlobe(canvas, {
mode: "map",
countryColors: Object.fromEntries(
Object.entries(visits).map(([iso, value]) => [iso, scale(value)]),
),
onCountryClick: (shape) => console.log(shape.iso, shape.name),
});
How keys are matched
countryColors keys are matched case-insensitively, in this order:
- Your own
countryKeyfunction, if provided - ISO alpha-2 code:
"IN","in" - Numeric id:
"356" - Country name:
"India","india"
countryColors: { IN: "#f00", Brazil: "#0f0", 250: "#00f" }
If your data uses an internal region ID or another key, map it yourself:
countryKey: (shape) => myRegionIdFor(shape.iso),
A function instead of a map
countryColor receives each country shape and returns a colour or null. It takes precedence over
countryColors.
createGlobe(canvas, {
countryColor: (shape) => {
const value = data[shape.iso];
return value == null ? null : scale(value);
},
});
Returning null falls back to the theme's land colour, which is how you show "no data".
Colour scales
colorScale(domain, range) builds a linear ramp. Multi-stop scales work too.
const scale = colorScale([0, 500, 1000], ["#f7fbff", "#6baed6", "#08306b"]);
scale(0); // "rgb(247,251,255)"
scale(250); // interpolated
scale(9999); // clamped to the last stop
scale("n/a"); // null: not a number
A distinct colour per country
countryColors: "auto" gives the printed-atlas look. It runs a greedy graph colouring over country
adjacency, so no two neighbours ever share a fill.
Supply your own palette with countryPalette:
createGlobe(canvas, {
countryColors: "auto",
countryPalette: ["#e8c39e", "#a8c8a0", "#e5b8b8", "#c5b8dd"],
});
Interaction
createGlobe(canvas, {
onCountryHover: (shape, position) => { /* shape is null on leave */ },
onCountryClick: (shape) => drillInto(shape.iso),
});
Adding either handler enables country hit testing and the hover highlight. Hit testing un-projects the pointer to a coordinate and ray-casts against the polygons, so it works identically on the globe, on every flat projection, and at any zoom.
You can also test a point directly:
globe.countryAt(x, y); // → shape or null
globe.unproject(x, y); // → [lon, lat] or null
A legend
legend: {
title: "Visits",
scale: { domain: [0, 1000], range: ["#e0f2fe", "#0369a1"] },
position: "bottom-left",
}
Or discrete swatches:
legend: {
title: "Tier",
items: [
{ color: "#0369a1", label: "Enterprise" },
{ color: "#7bbfe3", label: "Growth" },
],
}