The world's two biggest currency zones, from one opt-in currencies property. Dollar users in green — not just the United States, but Ecuador, El Salvador, Timor-Leste, Panama, Cambodia, and a string of Caribbean and Pacific territories. Euro users in blue — the eurozone plus microstates like San Marino and Vatican City, and two unilateral adopters that aren't in the EU at all: Kosovo and Montenegro. Zimbabwe's multi-currency basket includes both. Zoom to find the small ones; hover any country for its actual currency list.
https://api.mapjson.com/v1/geo?layer=countries&detail=medium&properties=name,currencies
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>USD vs Euro</title>
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js"></script>
<style>
body { margin: 0; background: #cfe0ea; }
svg { width: 100%; height: 100vh; display: block; }
.tooltip {
position: fixed;
background: #1a1a1a;
color: #fafafa;
font-family: monospace;
font-size: 11px;
padding: 6px 10px;
pointer-events: none;
opacity: 0;
transition: opacity 0.1s;
line-height: 1.6;
}
</style>
</head>
<body>
<div class="tooltip" id="tooltip"></div>
<svg id="map"></svg>
<script>
const url = "https://api.mapjson.com/v1/geo?layer=countries&detail=medium&properties=name,currencies";
const uses = (d, code) => (d.properties.currencies || []).some((c) => c.code === code);
const cls = (d) => uses(d, "USD") && uses(d, "EUR") ? "both" : uses(d, "USD") ? "usd" : uses(d, "EUR") ? "eur" : "bg";
const FILL = { usd: "#4a8f6b", eur: "#3d6fa8", both: "#b08f3e", bg: "#ddd8cc" };
const STROKE = { usd: "#2f6b4c", eur: "#28517e", both: "#8a6b25", bg: "#f0ece6" };
const width = 960, height = 500;
const tooltip = document.getElementById("tooltip");
const svg = d3.select("#map").attr("viewBox", `0 0 ${width} ${height}`);
const g = svg.append("g");
const projection = d3.geoEqualEarth().rotate([-11, 0]).scale(160).translate([width / 2, height / 2 + 20]);
const path = d3.geoPath(projection);
g.append("path").datum({ type: "Sphere" }).attr("fill", "#22485e").attr("d", path);
g.append("path").datum(d3.geoGraticule()()).attr("fill", "none")
.attr("stroke", "#a9c3d1").attr("stroke-width", 0.3).attr("d", path);
function showTooltip(event, d) {
const currencies = d.properties.currencies;
tooltip.style.left = (event.clientX + 12) + "px";
tooltip.style.top = (event.clientY - 10) + "px";
tooltip.style.opacity = 1;
tooltip.innerHTML =
`<strong>${d.properties.name || d.properties.gid}</strong><br>` +
(currencies?.length ? currencies.map((c) => `${c.name} (${c.code})`).join(", ") : "no currency data");
}
d3.json(url).then(topo => {
const features = topojson.feature(topo, topo.objects.geo).features;
const order = { bg: 0, eur: 1, usd: 2, both: 3 };
features.sort((a, b) => order[cls(a)] - order[cls(b)]); // colored zones draw over the neutral base
g.selectAll(".country").data(features).join("path")
.attr("d", path)
.attr("fill", (d) => FILL[cls(d)])
.attr("stroke", (d) => STROKE[cls(d)])
.attr("stroke-width", (d) => cls(d) === "bg" ? 0.5 : 0.8)
.style("cursor", (d) => cls(d) === "bg" ? null : "pointer")
.on("mousemove", showTooltip).on("mouseleave", () => tooltip.style.opacity = 0);
});
</script>
</body>
</html>