D3 可缩放等值线图

D3 Zoomable choropleth map

我正在尝试使用 D3 制作一个可以放大和平移的非常基本的等值线图。虽然这看起来很微不足道,但网上没有正确的例子。这个例子 here 有一个非绑定的平移,我觉得它很尴尬。

这是我试过的。我设法更新了缩放系数,但地图没有改变。

HTML+风格

<!DOCTYPE html>
<meta charset="utf-8">
<style>
    body {
      margin: 0
    }

    svg {
      background: #fff;
    }

    .sphere {
      fill: #fff;
    }

    .land {
      fill: #000;
    }

    .map {
      fill: none;
      stroke: #fff;
      stroke-linejoin: round;
      stroke-linecap: round;
      vector-effect: non-scaling-stroke;
    }
    .country:hover{
      stroke: #ddd;
      stroke-width: 4px;
    }
    .legend{
        margin: 1%;
        padding: 1%;
    }
    .legendCells{
        padding: 10px;
        margin: 10px;
    }
    div.tooltip {
      color: #222; 
      background: #fff; 
      padding: .5em; 
      text-shadow: #f5f5f5 0 1px 0;
      border-radius: 2px; 
      box-shadow: 0px 0px 2px 0px #a6a6a6; 
      opacity: 0.9; 
      position: absolute;
    }
    .country{
        fill: "#000";
    }
</style>
<body>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.24.0/d3-legend.js"></script>

JS

var svg = d3.select("svg");
    width = +svg.attr("width"),
    height = +svg.attr("height");
// Map and projection
var path = d3.geoPath();
var projection = d3.geoMercator(width / 2 / Math.PI).translate([width / 2, height / 2])
var path = d3.geoPath().projection(projection);

const zoom = d3.zoom()
    .on('zoom', function(){
        var event = d3.event.transform
        console.dir(event)
      svg.attr('transform', event.transform);

    })
    .scaleExtent([1, 40]);

// Load external data and boot
d3.queue()
    .defer(d3.json, "http://enjalot.github.io/wwsd/data/world/world-110m.geojson")
    .await(ready);

function ready(error, topo) {
    if (error) throw error;
    console.dir(topo)
    // Draw the map
    drawmap(topo)

}

function drawmap(topo){
    map = svg.append("g")
        .attr("class", "map")
        .selectAll("path")
        .data(topo.features)
        
    svg.call(zoom);

    map.enter().append("path")
        .attr("fill","#000")
        .attr("d", path)
        .attr("class","country")
}

我看不出我做错了什么。我也不太确定它会产生预期的结果(即,不是上面提到的示例的错误行为)。任何帮助表示赞赏。

听起来您正在寻找的是在 d3.zoom() 上设置 translateExtent。将 zoom 的值更改为

const zoom = d3.zoom()
    .scaleExtent([1, 8])
    .translateExtent([[0, 0], [width, height]])
    .on('zoom', zoomed);

应该可以解决问题。这限制了平移,这样您就不能平移到地图的边缘之外。

我已经更新了您链接的块以包含此内容并使用 D3 v7:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">

  <style>
    body {
      margin: 0
    }

    svg {
      background: #eee;
    }

    .sphere {
      fill: #fff;
    }

    .land {
      fill: #000;
    }

    .boundary {
      fill: none;
      stroke: #fff;
      stroke-linejoin: round;
      stroke-linecap: round;
      vector-effect: non-scaling-stroke;
    }
  </style>

  <script src="https://d3js.org/d3.v7.js"></script>
  <script src="https://unpkg.com/topojson@3"></script>
</head>

<body>
  <script>
    const width = 500;
    const height = 500;

    const projection = d3.geoMercator()
        .translate([width / 2, height / 2])
        .scale((width - 1) / 2 / Math.PI);

    const path = d3.geoPath()
        .projection(projection);

    const zoom = d3.zoom()
        .scaleExtent([1, 8])
        .translateExtent([[0, 0], [width, height]])
        .on('zoom', zoomed);

    const svg = d3.select('body').append('svg')
        .attr('width', width)
        .attr('height', height);

    const g = svg.append('g');

    svg.call(zoom);

    d3.json('https://unpkg.com/world-atlas@2.0.2/countries-110m.json')
      .then(world => {
        g.append('path')
            .datum({ type: 'Sphere' })
            .attr('class', 'sphere')
            .attr('d', path);

        g.append('path')
            .datum(topojson.merge(world, world.objects.countries.geometries))
            .attr('class', 'land')
            .attr('d', path);

        g.append('path')
            .datum(topojson.mesh(world, world.objects.countries, (a, b) => a !== b))
            .attr('class', 'boundary')
            .attr('d', path);
      });

    function zoomed(event) {
      g
        .selectAll('path') // To prevent stroke width from scaling
        .attr('transform', event.transform);
    }
  </script>
</body>

</html>