← Examples
countries · rivers · lakes · multiple layers · hover

Physical Features

North America composed from three API calls using Promise.all — countries as the base, overlaid with rivers and lakes. Hover any river or lake to see its name.

countries
rivers
lakes

API Calls

https://api.mapjson.com/v1/geo?layer=countries&filter=north-america&detail=medium
https://api.mapjson.com/v1/geo?layer=rivers&detail=medium
https://api.mapjson.com/v1/geo?layer=lakes&detail=medium

Code

<!DOCTYPE html>
<html>
<head>
  <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>
    svg { width: 100%; display: block; background: #b8d4e0; }
    #tooltip {
      position: fixed; background: #000; color: #fff;
      font-size: 12px; padding: 4px 8px; pointer-events: none; opacity: 0;
    }
  </style>
</head>
<body>
  <div id="map"></div>
  <div id="tooltip"></div>
  <script>
    const API = "https://api.mapjson.com/v1/geo";
    const width = 960, height = 600;
    const svg = d3.select("#map").append("svg").attr("viewBox", `0 0 ${width} ${height}`);
    const tooltip = d3.select("#tooltip");

    const projection = d3.geoMercator().center([-97, 52]).scale(500).translate([width / 2, height / 2]);
    const path = d3.geoPath().projection(projection);

    function showTip(event, name) {
      tooltip.style("opacity", 1).text(name)
        .style("left", event.clientX + 12 + "px").style("top", event.clientY - 8 + "px");
    }
    function hideTip() { tooltip.style("opacity", 0); }

    Promise.all([
      d3.json(API + "?layer=countries&filter=north-america&detail=medium"),
      d3.json(API + "?layer=rivers&detail=medium"),
      d3.json(API + "?layer=lakes&detail=medium"),
    ]).then(([countriesTopo, riversTopo, lakesTopo]) => {
      svg.append("g")
        .selectAll("path")
        .data(topojson.feature(countriesTopo, countriesTopo.objects.geo).features)
        .join("path").attr("d", path).attr("fill", "#d9d0be").attr("stroke", "#fff").attr("stroke-width", 0.5);

      svg.append("g")
        .selectAll("path")
        .data(topojson.feature(riversTopo, riversTopo.objects.geo).features)
        .join("path").attr("d", path).attr("fill", "none").attr("stroke", "#5a9ab5").attr("stroke-width", 1)
        .style("cursor", "crosshair")
        .on("mouseover", (event, d) => showTip(event, d.properties.name))
        .on("mousemove", (event, d) => showTip(event, d.properties.name))
        .on("mouseout", hideTip);

      svg.append("g")
        .selectAll("path")
        .data(topojson.feature(lakesTopo, lakesTopo.objects.geo).features)
        .join("path").attr("d", path).attr("fill", "#7ab8d0").attr("stroke", "none")
        .style("cursor", "crosshair")
        .on("mouseover", (event, d) => showTip(event, d.properties.name))
        .on("mousemove", (event, d) => showTip(event, d.properties.name))
        .on("mouseout", hideTip);
    });
  </script>
</body>
</html>