← Examples
countries · Patterson projection · hover · d3.geoCentroid · d3.geoBounds · topojson.neighbors

Country Details

World countries on the Patterson projection (d3.geoPatterson, from the d3-geo-projection extension) — a compromise cylindrical designed to keep high-latitude countries from ballooning the way they do on Mercator. Hover any country for every opt-in property the API returns, plus three things computed client-side that the API doesn't provide at all: its centroid and bounding box (d3.geoCentroid / d3.geoBounds), and its topologically adjacent neighbors (topojson.neighbors, derived from shared arcs in the same response — no separate borders dataset).

Hover a country on the map above.

Neighbor detection is exact — it comes from arcs literally shared between two borders in the topology — with one honest gap: a handful of countries (France, Norway, the Netherlands, Morocco/Western Sahara) get their boundary from a different source than their neighbors, so the shared-arc check has nothing to match. Try France to see how that's reported, rather than silently showing an empty list.

API Call

https://api.mapjson.com/v1/geo?layer=countries&detail=medium&properties=name,nameOfficial,iso2,iso3,isoNum,continent,subregion,areakm2,capital,capitalLat,capitalLng,currencies,languages,idd,demonym

Code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Country Details</title>
  <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-geo-projection@4/dist/d3-geo-projection.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; }
  </style>
</head>
<body>
  <svg id="map"></svg>
  <pre id="out">hover a country</pre>
  <script>
    const PROPS = "name,nameOfficial,iso2,iso3,isoNum,continent,subregion,areakm2,capital,capitalLat,capitalLng,currencies,languages,idd,demonym";
    const url = `https://api.mapjson.com/v1/geo?layer=countries&detail=medium&properties=${PROPS}`;

    const width = 960, height = 500;
    const svg = d3.select("#map").attr("viewBox", `0 0 ${width} ${height}`);
    const out = document.getElementById("out");

    d3.json(url).then(topo => {
      const objects = topo.objects.geo.geometries;
      const fc = topojson.feature(topo, topo.objects.geo);

      // Adjacency from the topology's shared arcs — no separate borders file
      const neighborIdx = topojson.neighbors(objects);

      const projection = d3.geoPatterson().fitExtent([[8, 8], [width - 8, height - 8]], fc);
      const path = d3.geoPath(projection);

      svg.selectAll("path")
        .data(fc.features)
        .join("path")
        .attr("d", path)
        .attr("fill", "#d9d0be")
        .attr("stroke", "#fff")
        .attr("stroke-width", 0.5)
        .on("mousemove", (event, d) => {
          const i = fc.features.indexOf(d);
          const p = d.properties;
          const centroid = d3.geoCentroid(d);           // [lon, lat]
          const bounds = d3.geoBounds(d);                // [[w,s],[e,n]]
          const neighbors = neighborIdx[i].map(j => fc.features[j].properties.name);

          out.textContent = JSON.stringify({
            ...p,
            centroid: centroid.map(v => +v.toFixed(2)),
            boundingBox: bounds.map(pt => pt.map(v => +v.toFixed(2))),
            neighbors,
          }, null, 2);
        });
    });
  </script>
</body>
</html>