D3 地理地图缩放无法正常工作 (Canvas)

D3 Geo map zoom is not working correctly (Canvas)

我正在尝试在 canvas 中缩放地图。

var projection = d3.geoMercator()
projection.fitExtent([[margin.left, margin.top], [width, height]], land);
var path = d3.geoPath().projection(projection).context(context);

当我将平移和缩放应用到 canvas 上下文时,它工作得很好。但是当我调用 var latlong = projection.invert(d3.mouse(this)); 时,return 没有正确的 纬度和经度 ,因为投影没有相应地转换。

var zoom = d3.zoom()
    .scaleExtent([1, Infinity])
    .on("zoom", zoomByContext);

function zoomByContext() {
    var transform = d3.event.transform;
    context.clearRect(0, 0, width, height);
    context.save();
    context.translate(transform.x, transform.y);
    context.lineWidth = 0.5 / transform.k;
    context.scale(transform.k, transform.k);
    renderFeature();
    context.restore();
}

所以我尝试像打击一样重新投影投影。但是当我用下面的代码缩放时它会转到左上角。

var zoom = d3.zoom()
    .scaleExtent([1, Infinity])
    .on("zoom", zoomByProjection);

function zoomByProjection() {
    var transform = d3.event.transform;
    projection.translate([transform.x, transform.y]);
    projection.scale(scale * transform.k);
    renderFeature();
}

而且我这样称呼缩放

canvas.call(zoom, d3.zoomIdentity
.translate(projection.translate())
.scale(projection.scale()));

对于第一种方法,您需要先反转缩放,然后再将 xy 坐标转换为长纬度坐标:

var transform = d3.zoomTransform(this);
var xy = transform.invert(d3.mouse(this));         
var longlat = projection.invert(xy);

我们在像素坐标中获取鼠标位置,将其转​​换为缩放坐标,然后将其转换为地理坐标。

这应该证明以上内容:

var width = 960;
var height = 500;

var canvas = d3.select("canvas");
var context = canvas.node().getContext("2d")
var projection = d3.geoMercator();
var path = d3.geoPath(projection,context);
 

d3.json("https://unpkg.com/world-atlas@1/world/110m.json", function(error, world) {
  if (error) throw error;
  
renderFeature();

var zoom = d3.zoom()
    .scaleExtent([1, Infinity])
    .on("zoom", zoomByContext);

canvas.call(zoom);
 
  function zoomByContext() {
    var transform = d3.event.transform;
    context.clearRect(0, 0, width, height);
    context.save();
    context.translate(transform.x, transform.y);
    context.lineWidth = 0.5 / transform.k;
    context.scale(transform.k, transform.k);
    renderFeature();
    context.restore();
  }
  function renderFeature() {
    context.beginPath();
    path(topojson.mesh(world));
    context.stroke(); 
  }
  
  canvas.on("click", function() {
 var transform = d3.zoomTransform(this);
 var xy = transform.invert(d3.mouse(this));         
 var longlat = projection.invert(xy);
    console.log(longlat);
  })

  
  
});
<canvas width="960" height="500"></canvas>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>

第二种方法有点棘手,如果投影的平移是 [0,0],则您所采用的方法会奏效,但这种情况很少见。默认值为 [480,250](假设 canvas 为 960x500),并且 fitSizefitExtent 不通过修改旋转和居中来定位特征,而是平移。所以你需要在修改投影时考虑初始翻译(就像你对比例所做的那样):

var transform = d3.event.transform;
projection.translate([transform.x+translate[0]*transform.k, transform.y+translate[1]*transform.k]);
projection.scale(scale * transform.k);

这里translate是一个保存初始翻译值的数组

这里有一个演示可以证明以上内容:

var width = 960;
var height = 500;

var canvas = d3.select("canvas");
var context = canvas.node().getContext("2d")
var projection = d3.geoMercator().center([105,3]).scale(1200).translate([2000,0]);
var path = d3.geoPath(projection,context);
var scale = projection.scale();
var translate = projection.translate();



d3.json("https://unpkg.com/world-atlas@1/world/110m.json", function(error, world) {
  if (error) throw error;

renderFeature();
  
var zoom = d3.zoom()
    .scaleExtent([0.1, Infinity])
    .on("zoom", zoomByProjection);

canvas.call(zoom);
 
function zoomByProjection() {
    context.clearRect(0, 0, width, height);
    var transform = d3.event.transform;
    projection.translate([transform.x+translate[0]*transform.k, transform.y+translate[1]*transform.k]);
    projection.scale(scale * transform.k);
    renderFeature();
}
  
  function renderFeature() {
    context.beginPath();
    path(topojson.mesh(world));
    context.stroke(); 
  }

});
<canvas width="960" height="500"></canvas>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>