Skip to main content

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),
});
Loading globe…

How keys are matched​

countryColors keys are matched case-insensitively, in this order:

  1. Your own countryKey function, if provided
  2. ISO alpha-2 code: "IN", "in"
  3. Numeric id: "356"
  4. 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.

Loading globe…

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" },
],
}

Talk to a real person

Live human support. Choose what you need help with.