City lights globe
More than 7,000 cities and towns, each glowing in proportion to its population, on a globe that turns until you grab it. The bright clusters are the world's great urban regions: the Ganges plain, eastern China, Java, the Nile delta, and the corridor from Boston to Washington.
Build it yourself
The style is a few layers of its own in MapLibre's globe projection: dark land and water from the map tiles, then Natural Earth's populated places drawn twice, once as wide, blurred circles for the glow and once as small, bright ones for the cities. It was tested with MapLibre GL JS 6.15 in Chromium.
What you need
Nothing but a web server to serve the page: the map tiles and Natural Earth's data need no key.
Make the page
Make a folder, and in it index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>City lights globe</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@6.15.0/dist/maplibre-gl.css">
</head>
<body style="margin: 0; background: #05070d">
<div id="map" style="height: 100vh"></div>
<script type="module">
import * as maplibregl from "https://cdn.jsdelivr.net/npm/maplibre-gl@6.15.0/+esm";
const cities =
"https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_10m_populated_places_simple.geojson";
const size = (small, large) => ["interpolate", ["linear"], ["sqrt", ["get", "pop_max"]], 0, small, 6000, large];
const map = new maplibregl.Map({
container: "map",
style: {
version: 8,
projection: { type: "globe" },
sky: { "atmosphere-blend": 0.6, "sky-color": "#0b1020", "horizon-color": "#1c2541", "fog-color": "#0b1020" },
sources: {
protomaps: { type: "vector", url: "https://tiles.mapricot.com/planet.json" },
cities: { type: "geojson", data: cities, attribution: '<a href="https://www.naturalearthdata.com/">Natural Earth</a>' },
},
layers: [
{ id: "background", type: "background", paint: { "background-color": "#0b1020" } },
{ id: "earth", type: "fill", source: "protomaps", "source-layer": "earth", paint: { "fill-color": "#1a2036" } },
{
id: "water",
type: "fill",
source: "protomaps",
"source-layer": "water",
filter: ["==", ["geometry-type"], "Polygon"],
paint: { "fill-color": "#0b1020" },
},
{
id: "glow",
type: "circle",
source: "cities",
paint: {
"circle-radius": ["interpolate", ["linear"], ["zoom"], 1, size(1.5, 18), 6, size(5, 60)],
"circle-color": "#ff922b",
"circle-blur": 1,
"circle-opacity": 0.45,
},
},
{
id: "lights",
type: "circle",
source: "cities",
paint: {
"circle-radius": ["interpolate", ["linear"], ["zoom"], 1, size(0.5, 3), 6, size(1.5, 9)],
"circle-color": "#fff4e6",
},
},
],
},
center: [60, 25],
zoom: 2,
});
let turning = !matchMedia("(prefers-reduced-motion: reduce)").matches;
map.on("mousedown", () => (turning = false));
map.on("touchstart", () => (turning = false));
map.on("wheel", () => (turning = false));
function turn() {
if (!turning) return;
const center = map.getCenter();
map.setCenter([center.lng - 0.04, center.lat]);
requestAnimationFrame(turn);
}
map.once("load", turn);
</script>
</body>
</html>
projectionin the style makes the map a globe, andskygives it a faint atmosphere at the edge.- The page loads Natural Earth's 7,300 places, about 5 MB, straight from its GitHub repository. Save the file next to the page and point
citiesat it to load it from your own server instead. - Both circle layers size by the square root of the population, so a circle's area tracks the number of people. Where cities sit close together, their glows add up.
- The globe turns by moving the map's center a little each frame, and stops for good once you drag or zoom. It doesn't turn at all if your system asks for reduced motion.
Open it
Serve the folder with npx serve or python3 -m http.server, and open the address it prints. Opened straight from the file, the map stays blank, as Open the page explains. You should see the globe from above India, glowing with cities and turning slowly.
Data
- Cities and their populations: Natural Earth populated places (public domain)
- Land, water, and borders: OpenStreetMap contributors (ODbL) and Natural Earth, in Mapricot's map tiles