【问题标题】:D3 Zoom's translateExtent not working as expectedD3 Zoom 的 translateExtent 未按预期工作
【发布时间】:2019-08-23 12:29:39
【问题描述】:

我正在尝试使用d3 tiled3 zoom 创建平铺地图。我已将zoom.extentzoom.translateExtent 都设置为[[0,0], [width,height]]。但是当我缩放时,地图会跳到左上角。

这里是示例代码

const tile = d3.tile()
    .extent([[0, 0], [width, height]])
    .tileSize(512);

const zoom = d3.zoom()       
    .translateExtent([[0, 0], [width, height]]) //  issue in this line
    .extent([[0, 0], [width, height]])
    .scaleExtent([1 << 10, 1 << 15])
    .on("zoom", () => zoomed(d3.event.transform));

// applied zoom like this
svg
    .call(zoom)
    .call(zoom.transform, d3.zoomIdentity
    .translate(width / 2, height / 2)
    .scale(-initialScale)
    .translate(...projection(initialCenter))
    .scale(-1));

function zoomed(transform) {
    projection
        .scale(transform.k / (2 * Math.PI))
        .translate([transform.x, transform.y]);
}

这里是complete demo in Observable

【问题讨论】:

  • 试图统一缩放、投影、地理、屏幕和平铺坐标,足以让人抓狂。几天后我会看看——现在在路上,下周晚些时候回家,但是单位清晰度的问题促使我创建了一个alternative(当然还没有完成,更多的是演示什么可能 - d3.tile 现在更好(早期版本有点麻烦),但单位在清晰度方面仍有一些不足)
  • 感谢@AndrewReid 的评论。在您的 d3-slippy 中,您有一个方法 zoomTranslateConstrain 我已将其传递给 d3 zoom。在我的情况下会做同样的工作吗?

标签: javascript d3.js zooming


【解决方案1】:

这里的关键挑战是了解您的限制。投影的原始比例是 1/(2π):世界被一个像素一个像素地投影到一个空间(每个像素 2π 或每个像素 360 度)。这样可以更轻松地与 d3-zoom 集成,但肯定不直观。

D3 缩放用于修改投影,其缩放值沿1&lt;&lt;n 的行。这表示应用缩放后世界的宽度。如果基础投影对世界使用 1 像素的宽度,则缩放会将其拉伸到 1&lt;&lt;n 像素。例如 1

现在,棘手的部分是缩放平移范围以 1 的缩放比例应用。因此,我们需要将平移范围应用于世界,就好像它显示在 1x1 像素框内一样,即使,给定较低的比例范围值 1024 (1

因此,我们的平移范围应该在由两个角包围的框内:[-1,-1] 和 [1,1]。这有助于解释 Cornel 的评论。但是我们可以以编程方式做到这一点吗?是的。

这就是让我对这个库发疯的原因:缩放范围不是以地理坐标或屏幕坐标等简单的方式注册的,而是相对于 1x1 像素世界的缩放单位,引入了一个新的坐标系跟踪哪些可以改为在幕后跟踪。这当然不直观,部分驱使我离开这个模块(解释我评论中的链接,d3-slippy 仍然是相当实验性的,但应该显示可以做什么 - 顺便说一句,链接中的约束方法只有在您将所有内容都转换为它)。

所以,让我们以编程方式获取屏幕的缩放平移范围,如图所示。

首先,在我们调用缩放并应用初始变换后,投影会更新以反映新的缩放级别并进行平移,因此我们可以使用 projection.invert() 获取左上角和底部的地理坐标屏幕右侧。

其次,我们可以(重新)为我们的一个像素世界创建一个投影,并通过它推动左上角和右下角的地理坐标,以获得它们在一个像素世界内的投影坐标。

第三,我们在一个 1 像素的世界中获取左上角和右下角的投影坐标,并将它们传递给 zoom.translateExtent:

   // Call zoom, same as before:
   svg
      .call(zoom)
      .call(zoom.transform, d3.zoomIdentity
          .translate(width / 2, height / 2)
          .scale(-initialScale)
          .translate(...projection(initialCenter))
          .scale(-1));

  // Now we can work on setting the zoom translate extent:
  // Zooomed projected limits of screen in lat long:
  var top = projection.invert([0,0])[1];
  var left = projection.invert([0,0])[0];
  var bottom = projection.invert([width,height])[1];
  var right = projection.invert([width,height])[0];

  // original projection:
  var baseProjection = d3.geoMercator()
    .translate([0,0])
    .scale(1/Math.PI/2);

  // Projected limits of screen in projected unzoomed coordinates:
  var topLeft = baseProjection([left,top])
  var bottomRight = baseProjection([right,bottom])

  // Set the zoom translate extent:
  zoom.translateExtent([topLeft,bottomRight])

【讨论】:

  • 感谢@Andrew Reid 的详细回答。但是它现在仍然跳到底角。你能在我的演示中应用答案吗?
  • 我没看到,会再看一遍,但要到星期四才能看到 - 我有 24 小时的驾驶时间来度过接下来的两段时间......我直接从修改后的 obseevable 中复制了代码,但会再次检查。
  • @AbdulAlim,现在可以查看更多内容,但是,我没有看到任何问题:observablehq.com/d/a028605a652a6274
  • 感谢@Andrew Reid 的回答。您是否发布了可观察的示例?我无法打开它。
  • 我的错:observablehq.com/@andrew-reid/…(之前从未在 observable 上发表过任何内容)
猜你喜欢
  • 2019-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-25
  • 2020-02-18
  • 2021-06-04
相关资源
最近更新 更多