← Examples
countries · capitals · node.js · server-side

World Capitals

Every country's capital plotted from the capital, capitalLat and capitalLng properties. The map below is rendered client-side like every other example — but the code shown is a complete Node.js script that produces the same SVG on a server. The API is plain JSON over HTTPS and d3-geo needs no DOM, so the exact same call and projection code run anywhere: cron jobs, build steps, PDF pipelines, or an <img> endpoint.

API Call

https://api.mapjson.com/v1/geo?properties=name,capital,capitalLat,capitalLng

Code — Node.js

Run with node capitals.mjs > capitals.svg (Node 18+, npm install d3 topojson-client). No browser, no DOM, no headless Chrome — d3's path generator returns plain strings.

// capitals.mjs — render a world capitals map to SVG, no browser required
// npm install d3 topojson-client
// node capitals.mjs > capitals.svg
import * as d3 from "d3";
import { feature } from "topojson-client";

const API = "https://api.mapjson.com/v1/geo?properties=name,capital,capitalLat,capitalLng";
const topo = await (await fetch(API)).json();
const fc = feature(topo, topo.objects.geo);

const width = 960, height = 500;
const projection = d3.geoNaturalEarth1().fitExtent([[8, 8], [width - 8, height - 8]], fc);
const path = d3.geoPath(projection);   // no canvas context → returns SVG path strings

const esc = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;");

const countries = fc.features.map((f) =>
  `<path d="${path(f)}" fill="#d9d0be" stroke="#fff" stroke-width="0.5"/>`);

const capitals = fc.features
  .filter((f) => f.properties.capitalLat != null)
  .map((f) => {
    const p = f.properties;
    const [x, y] = projection([p.capitalLng, p.capitalLat]);
    return `<circle cx="${x.toFixed(1)}" cy="${y.toFixed(1)}" r="2" fill="#b22222">` +
           `<title>${esc(p.capital)} — ${esc(p.name)}</title></circle>`;
  });

process.stdout.write(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}">
<rect width="${width}" height="${height}" fill="#cfe0ea"/>
${countries.join("\n")}
${capitals.join("\n")}
</svg>
`);