如何使用缩放行为和变换移动 d3 地图?



我正在使用d3v4来构建地图。我正在使用罗宾逊投影,d3 的缩放行为进行平移和缩放,以及 css 转换以实际定位 svg 元素本身。给定一个经度,经度对,我想使用当前缩放将地图居中。

我已经看过许多如何在 d3 中居中地图以及如何附加缩放行为的示例,但到目前为止,每个示例都

  • 使用旧版本的 D3
  • 不使用缩放行为(例如,https://bl.ocks.org/mbostock/2206489)
  • 操作投影本身,而不是使用 CSS 转换 (https://bl.ocks.org/mbostock/2206340)
  • 根本不使用地图投影 (https://bl.ocks.org/catherinekerr/b3227f16cebc8dd8beee461a945fb323)

给定一个经度/经度对,我可以使用我的投影在未平移、非缩放的 svg 上获取 X/Y。如何从该点创建转换以使其在视图中居中?

我的地图设置:

const {clientHeight: height, clientWidth: width} = div;
svg.setAttribute("width", `${width}`);
svg.setAttribute("height", `${height}`);
// projection
this.projection = geoRobinson()
// center and scale to container properly
.translate([width / 2, height / 2])
.scale((width - 1) / 2 / Math.PI);
// calculate zoom limits
const actualHeight = width / ratio;
const verticalExtent = (actualHeight - height) / 2;
// setup mouse listeners for panning and zooming on svg.
this.zoomBehavior = zoom<SVGSVGElement, any>()
// set min and max zoom factor
.scaleExtent([0.75, 8]) // based on playing around with numbers until it looked pleasing
// transform map on zoom and pan
.on("zoom", () => {
const currentTransform = this.state.transform;
const newTransform: ZoomTransform = d3event.transform;
if (
!currentTransform ||
hasChanged(currentTransform.x, newTransform.x) ||
hasChanged(currentTransform.y, newTransform.y) ||
hasChanged(currentTransform.k, newTransform.k)
) {
this.setState({ // updates the css transform of the SVG map
transform: newTransform,
});
}
});
// apply zoom behavior to whole svg
const svgSelection = select(svg);
svgSelection.call(this.zoomBehavior)

我已经查看了zoomBehavior.translateTo()和其他功能,但我不知道如何转换为他们使用的任何单位。

经过大量的修补和谷歌搜索,我找到了这个例子: https://bl.ocks.org/andybarefoot/765c937c8599ef540e1e0b394ca89dc5

它具有 d3v4、缩放行为以及根据命令重新将地图居中的功能。


我所做的是这样的:

  1. 获取原点的像素位置,就像地图未缩放一样(或缩放 = 1)
  2. 使用公式获取 CSS 转换(如果该原点移动到视图的中心并缩放)。对于 x:(width / 2) - (longitude * zoom)
  3. 使用svg.call(behavior, newTransform)应用转换(显然也会更新行为。或者不需要更新。

我的代码:

const coords = this.projection([longitude, latitude]);
if (!coords) {
return;
}
let actualScale: number = 1;
if (scale !== undefined) {
actualScale = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, 1, scale));
}
// get pixel size of svg
const {clientHeight: height, clientWidth: width} = svg;
// build new transform
const transform = zoomIdentity
.translate(width / 2 - coords[0] * actualScale, height / 2 - coords[1] * actualScale)
.scale(actualScale);
// apply and save transform
select(svg)
.transition() // omit this and the next line if you don't want transitions
.duration(200)
.call(this.zoomBehavior.transform, transform);

最新更新