← Examples
countries · Patterson projection · click · d3.geoCentroid · d3.geoBounds · borders

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. Click any country for every opt-in property the API returns — including population, gdp, and its land neighbours from the borders property — plus its centroid and bounding box, computed client-side (d3.geoCentroid / d3.geoBounds).

Click a country on the map above.

Neighbours come from the API's borders property — authoritative land-border adjacency (ISO2 codes), complete for every country including the ones whose geometry is stitched from a different source (France, Norway, the Netherlands, Morocco). Island nations correctly report no land borders. A neighbour too small to appear at this detail tier (e.g. Gibraltar) has its name filled in from the /v1/catalog endpoint — a complete ISO2 → name list.

API Call

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

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">click a country</pre>
  <script>
    const PROPS = "name,nameOfficial,iso2,iso3,isoNum,continent,subregion,areakm2,population,gdp,capital,capitalLat,capitalLng,currencies,languages,idd,demonym,borders";
    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");

    Promise.all([
      d3.json(url),
      d3.json("https://api.mapjson.com/v1/catalog"),   // complete iso2 → name list
    ]).then(([topo, catalog]) => {
      const fc = topojson.feature(topo, topo.objects.geo);
      // Neighbours come from the `borders` property (iso2 codes). Resolve to names via the
      // map data, and fall back to the catalog for territories absent at this detail tier
      // (e.g. Gibraltar), so a neighbour never shows as a bare code.
      const nameByIso2 = new Map(fc.features.map(f => [f.properties.iso2, f.properties.name]));
      for (const c of catalog) if (/^[A-Z]{2}$/.test(c.gid) && !nameByIso2.has(c.gid)) nameByIso2.set(c.gid, c.name);

      const projection = d3.geoPatterson().rotate([-11, 0]).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("click", (event, d) => {
          const p = d.properties;
          const centroid = d3.geoCentroid(d);           // [lon, lat]
          const bounds = d3.geoBounds(d);                // [[w,s],[e,n]]
          const neighbors = (p.borders || []).map(code => nameByIso2.get(code) || code);

          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>