block by harrystevens 7518a2ee0ad35172b8ff4782a6ccd2df

Scale Bar With Zooming

Full Screen

This block uses d3-geo-scale-bar with d3-zoom to make a map with a scale bar that updates as the user zooms in and out.

index.html

<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      display: table;
      margin: 0 auto;
    }

    svg {
      overflow: visible;
    }

    .state {
      fill: #eee;
      stroke: #ccc;
    }
  </style>
</head>
<body>
  <script src="https://d3js.org/d3.v6.min.js"></script>
  <script src="https://unpkg.com/topojson-client@3"></script>
  <script src="https://unpkg.com/d3-geo-scale-bar@1"></script>
  <script>
    const projection = d3.geoAlbers();
    const path = d3.geoPath(projection);

    const scaleBar = d3.geoScaleBar()
        .projection(projection)
        .left(.1)
        .top(.9)
        .units(d3.geoScaleMiles)
        .tickFormat(d => d)
        .zoomClamp(false);

    const zoom = d3.zoom()
        .scaleExtent([1, 20])
        .on("zoom", event => {
          const { transform } = event;
          g.attr("transform", transform);
          scaleBar.zoomFactor(transform.k);
          bar.call(scaleBar);
        });

    const svg = d3.select("body").append("svg");
    const g = svg.append("g");
    const bar = svg.append("g");

    d3.json("ne_50m_admin_1_states_provinces_lakes.json")
      .then(data => {
        const geojson = topojson.feature(data, data.objects.ne_50m_admin_1_states_provinces_lakes);
        
        const states = g.selectAll(".state")
            .data(geojson.features)
          .enter().append("path")
            .attr("class", "state");

        draw();
        addEventListener("resize", draw);

        function draw(){
          const width = Math.min(innerWidth, 800);
          const height = width * .63;

          projection.fitSize([width, height], geojson);
          scaleBar.size([width, height]);
          zoom.translateExtent([[0, 0], [width, height]]);

          svg
              .attr("width", width)
              .attr("height", height)
              .call(zoom);

          bar.call(scaleBar);

          states
              .attr("d", path)
              .on("mouseover", function(){
                d3.select(this).style("stroke", "black").raise();
              })
              .on("mouseout", function(){
                d3.select(this).style("stroke", "#ccc");
              });
        }
      });
  </script>

</body>
</html>