D3 v6 正射投影地图如何绘制投影正确的bounding box
正射投影下指定经纬度范围边界框绘制实现
已知待绘制边界框参数
South latitude: 50.2 North latitude: 52.01 West longitude: -60 East longitude: -57.1
现存问题
需要在正射投影(orthographic)地图上绘制上述经纬度对应的边界框,目前实现无法正常运行:之前参考的基于地理经纬网生成边界轮廓的方案,在D3 v6版本下渲染结果不符合预期,暂时无法定位是版本兼容问题还是代码疏漏。
当前使用的实现代码如下:
const g = svg.append('g') let bounds = {W: -5.0, N: 50.0, E: 10.0, S: 40.0 }; let projection = projections[1].value.precision(0.1); let pathGenerator = d3.geoPath(projection); let arc = d3.geoGraticule().extentMajor([[bounds.W, bounds.S], [bounds.E, bounds.N]]); function drawBox(params) { g.selectAll('.rect') .enter().append('path') .attr('class', 'rect') .attr('d', pathGenerator(arc.outline())) }
问题定位与修正方案
核心问题集中在两个部分:
- D3 v6 中
d3.geoGraticule()的extentMajor()方法作用是调整经纬网主网格的显示范围,并非用来定义自定义边界轮廓,直接调用arc.outline()生成的轮廓和目标范围无法匹配,这是实现失效的核心原因。旧版本D3中通过把经纬网步长调整到极大值、仅保留轮廓的方案,在v6中因为经纬网生成逻辑的调整,会出现轮廓穿模、范围偏移的问题,不建议继续使用。 - 当前
drawBox函数使用的是D3 v5及更早版本的旧版选择绑定逻辑,v6版本中selectAll后没有传入绑定数据的话,enter分支永远为空,会导致path元素完全无法追加。
注意:D3 v6中geoGraticule的相关范围配置,仅用于控制网格线的显示边界,其生成的轮廓是为经纬网服务的,和自定义业务边界的结构完全不同,不适合用来绘制业务边界框。
正射投影下绘制球面边界框不需要依赖经纬网生成器,直接将目标经纬度范围封装为标准GeoJSON多边形即可,路径生成器会自动按照球面测地线规则绘制四条边,避免投影下出现穿地直线的显示问题。
修正后的实现步骤
- 将目标经纬度范围整理为顺时针闭合的坐标序列,保证GeoJSON多边形符合规范,避免投影裁剪时出现面方向反转问题:
- 西南角坐标:
[bounds.W, bounds.S] - 西北角坐标:
[bounds.W, bounds.N] - 东北角坐标:
[bounds.E, bounds.N] - 东南角坐标:
[bounds.E, bounds.S] - 回到西南角坐标完成闭合
- 西南角坐标:
- 替换原代码中依赖经纬网生成轮廓的逻辑,直接将封装好的GeoJSON对象传入路径生成器。
- 适配D3 v6的选择器API,使用
join方法替换旧的enter分支写法,保证元素正常渲染。
修正后的参考代码:
const g = svg.append('g') // 替换为目标边界框的实际经纬度 let bounds = { W: -60.0, N: 52.01, E: -57.1, S: 50.2 }; // 初始化正射投影,可根据需求调整旋转角度、缩放尺寸参数 let projection = d3.geoOrthographic().precision(0.1); let pathGenerator = d3.geoPath(projection); // 构建闭合的球面边界多边形 const bboxPolygon = { type: "Polygon", coordinates: [[ [bounds.W, bounds.S], [bounds.W, bounds.N], [bounds.E, bounds.N], [bounds.E, bounds.S], [bounds.W, bounds.S] ]] } function drawBox() { // 适配D3 v6的数据绑定逻辑 g.selectAll('.rect') .data([bboxPolygon]) .join('path') .attr('class', 'rect') .attr('d', pathGenerator) } // 执行绘制 drawBox()
内容的提问来源于stack exchange,提问作者Gleb
相关产品推荐
相关产品推荐

