如何更新 d3 投影以匹配缩放变换?

问题描述 投票:0回答:1

这是我的地图的缩放处理程序:

const zoom = d3.zoom()
    .scaleExtent([1,25])
    .translateExtent([[width * -0.5, height * -0.5], [width * 1.5,height*1.5]])
    .on('zoom', (ev) => {
       svg.selectAll('path').attr('transform', ev.transform);  
    })

它使用事件中的变换参数更新 svg 中的路径。这很好用,但是如果我使用

projection(point)
或类似的方法返回一个点的 x,y 坐标,那么它们将是不正确的。

我意识到我需要更新我的投影来更新缩放/平移行为。

如果我在任何缩放之前记录原始地图平移,

const origTrans = projection.translate();
,然后应用 x,y 变换,那么我就能够正确同步顶部缩放级别的投影(即 k=1)。

.on("end", (ev)=> {
  projection.translate([origTrans[0] + ev.transform.x * ev.transform.k, origTrans[1] + ev.transform.y * ev.transform.k]);
  const c =  projection([-3.3632, 55]);
  svg.append("circle")
    .attr("cx", c[0])
    .attr("cy", c[1])
    .attr("r", 9)
    .attr("fill", "red");
  }); 

我不清楚缩放级别与投影比例有何关系。我无法实现同样的目标

我尝试过一些事情,例如-

projection.scale(ev.transform.k)
,或
projection.scale(projection.scale() *  ev.transform.k)
- 我假设还有更多内容?如果有帮助的话,我正在使用 geoMercator 进行投影。

javascript d3.js d3-geo
1个回答
2
投票

仔细重新阅读您的问题,您可能会使问题变得复杂。投影的缩放和平移可以完全独立于 SVG 的缩放状态。

相互引用一个会产生比其价值更多的问题,部分是因为动态坐标系的数量增加,部分是因为您可能需要在整个拖动事件中连续重新计算投影点之类的事情(取决于方法,这可能是滞后的)。


我对问题的理解是:您的 SVG 路径重新缩放,但您需要提取、交互、更新或绘制 SVG 上的特定点和/或非路径元素以反映它们的新位置。

为什么不对圆/点/其他元素使用与路径相同的方法?为此,我将创建一个新的

g
来保存所有可缩放元素、路径,否则,对其应用缩放变换,这样缩放本身就会为您处理所有缩放:

let zoomG = svg.append('g');

zoom.on('zoom', (ev) => {
   zoomG.attr('transform', ev.transform);  
 })

zoomG 内的子级的任何坐标都将使用投影中的投影像素值来表示。然后将zoomG整体根据zoom进行变换

例如,下面绘制了一些路径和一个圆圈(伦敦)作为起点。无论缩放状态如何,单击地图上的任意位置都会正确绘制新加坡(它将在几秒钟后消失,直到再次单击),而现有要素将正确平移和缩放。

var svg = d3.select("svg")
  .attr("width", 500)
  .attr("height", 500)

var projection = d3.geoMercator()
    .scale(500 / 2 /Math.PI )
    .translate([250,250])
    
let zoomG = svg.append('g');

let zoom = d3.zoom()
  .on("zoom", (ev)=> zoomG.attr('transform', ev.transform))

  
svg.call(zoom);
  
svg.on("click", function(ev) {
    let xy = d3.pointer(ev, zoomG.node());
    let longlat = projection.invert(xy);
    console.log("mouse click at: " + xy + " which represents: " + longlat);
    
   zoomG.append("circle")
     .attr("cx", projection([103.820,1.352])[0])
     .attr("cy", projection([103.820,1.352])[1])
     .attr("r", 4)
     .transition()
     .attr("r", 0)
     .duration(2000)
     .remove();
    
})
  

d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson").then( function(data){
    
   zoomG.selectAll("path")
        .data(data.features)
        .enter().append("path")
            .attr("fill", "#eee")
            .attr("d", d3.geoPath()
               .projection(projection)
            )
            .style("stroke", "#ccc")
            
   zoomG.append("circle")
     .attr("cx", projection([0.128,51.507])[0])
     .attr("cy", projection([0.128,51.507])[1])
     .attr("r", 4)
     
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
<svg></svg>

上面我还添加了如何计算鼠标地理位置的演示。

let xy = d3.pointer(ev, zoomG.node());
let longlat = projection.invert(xy);
console.log("mouse click at: " + xy + " which represents: " + longlat);

当从投影坐标移动到像素坐标时,我们不需要担心这个问题,因为父级 G 中的路径/圆/任何内容的嵌套以及缩放变换会为我们处理这个问题。但反之,我们需要考虑鼠标实际位于投影坐标空间中的缩放变换(这就是 d3.pointer 出现的地方)。

© www.soinside.com 2019 - 2024. All rights reserved.