D3 Zoom 的 translateExtent 未按预期工作
D3 Zoom's translateExtent not working as expected
我正在尝试使用 d3 tile
和 d3 zoom
创建一个瓦片地图。我已将 zoom.extent
和 zoom.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]);
}
这里的主要挑战是了解您要限制的内容。投影的原始比例为 1/(2π):世界被投影为 space 一个像素乘一个像素(每个像素 2π 或每个像素 360 度)。这样可以更轻松地与 d3-zoom 集成,但肯定不直观。
D3 zoom 用于修改投影,其比例值为 1<<n
。这表示应用缩放后世界的宽度。如果基础投影对世界使用 1 像素的宽度,则缩放会将其拉伸到 1<<n
像素。例如 1<<10 为 1024。
现在,棘手的部分是缩放比例为 1 时应用缩放平移范围。因此,我们需要将平移范围应用于世界,就好像它显示在 1x1 像素框内一样,即使,给定一个较低的比例范围值 1024 (1<<10),我们实际上从未将世界显示为占据一个像素。
因此,我们的平移范围应该在由两个角包围的框内:[-1,-1] 和 [1,1]。这有助于解释 Cornel 的评论。但是我们可以通过编程来做到这一点吗?是的
这就是让我对这个库感到疯狂的原因:缩放范围不是在地理坐标或屏幕坐标等简单的东西中注册的,而是相对于 1x1 像素世界的缩放单位,引入了用于跟踪的新坐标系可以在幕后进行跟踪。这当然不直观,部分驱使我放弃这个模块(在我的评论中解释 link,d3-slippy 仍然是非常实验性的,但应该展示可以做什么 - 顺便说一句,link 只有将所有内容都转换成它才有效)。
那么,让我们以编程方式获取屏幕的缩放平移范围,如图所示。
首先,在我们调用缩放并应用初始变换后,投影会更新以反映新的缩放级别并进行平移,因此我们可以使用 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])
我正在尝试使用 d3 tile
和 d3 zoom
创建一个瓦片地图。我已将 zoom.extent
和 zoom.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]);
}
这里的主要挑战是了解您要限制的内容。投影的原始比例为 1/(2π):世界被投影为 space 一个像素乘一个像素(每个像素 2π 或每个像素 360 度)。这样可以更轻松地与 d3-zoom 集成,但肯定不直观。
D3 zoom 用于修改投影,其比例值为 1<<n
。这表示应用缩放后世界的宽度。如果基础投影对世界使用 1 像素的宽度,则缩放会将其拉伸到 1<<n
像素。例如 1<<10 为 1024。
现在,棘手的部分是缩放比例为 1 时应用缩放平移范围。因此,我们需要将平移范围应用于世界,就好像它显示在 1x1 像素框内一样,即使,给定一个较低的比例范围值 1024 (1<<10),我们实际上从未将世界显示为占据一个像素。
因此,我们的平移范围应该在由两个角包围的框内:[-1,-1] 和 [1,1]。这有助于解释 Cornel 的评论。但是我们可以通过编程来做到这一点吗?是的
这就是让我对这个库感到疯狂的原因:缩放范围不是在地理坐标或屏幕坐标等简单的东西中注册的,而是相对于 1x1 像素世界的缩放单位,引入了用于跟踪的新坐标系可以在幕后进行跟踪。这当然不直观,部分驱使我放弃这个模块(在我的评论中解释 link,d3-slippy 仍然是非常实验性的,但应该展示可以做什么 - 顺便说一句,link 只有将所有内容都转换成它才有效)。
那么,让我们以编程方式获取屏幕的缩放平移范围,如图所示。
首先,在我们调用缩放并应用初始变换后,投影会更新以反映新的缩放级别并进行平移,因此我们可以使用 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])