You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

D3.js中地图点投影偏移问题求助:如何正确将经纬度点投影至地图

解决地图点偏移的问题

我帮你找到了问题所在!你的点出现偏移主要是因为初始加载时没有正确初始化投影,而且代码里的投影处理逻辑也有优化空间。具体来说:

  • 你把投影proj和路径生成器path放在了resize事件回调里,页面第一次加载时不会触发resize,所以地图和点用的是d3.geoAlbers的默认投影参数(不是适配容器后的fitSize参数),这直接导致了点和地图的位置不匹配。
  • 每次resize都重新创建投影和path,虽然能运行,但不够高效,也容易因为作用域问题引发意外。

下面是修复后的代码,我标注了关键修改的地方:

<!DOCTYPE html>
<meta charset="utf-8" />
<body>
<div id="vizook" style="position:relative; width:100vw; height:100vh; background: black;"></div>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/topojson@3.0.2/dist/topojson.js"></script>
<script>
d3.json("https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json")
 .then(json => {
 // Handle data
 const mapdata = json;
 console.log(mapdata);
 var selected = d3.set(['02', '15', '60', '66', '69', '72', '78']);
 const usfilt = mapdata;
 usfilt.objects.states.geometries = mapdata.objects.states.geometries.filter(function(d) {
 return !selected.has(d.id);
 });
 const portlist = [{port: 'KEY WEST, FL', lat: 24.5551, lng: -81.7811 },
 {port: 'GALVESTON, TX', lat: 29.3036, lng: -94.7975},
 {port: 'NEW YORK, NY', lat: 40.6943, lng: -73.9249},
 {port: 'BOSTON, MA', lat: 42.3118, lng: -71.0852}];
 var geomap = topojson.feature(usfilt, usfilt.objects.states);

 /////////////////////////////////////////////////////////////////////////////
 // 关键修改1:把投影和路径生成器提升到外层作用域,方便复用
 var proj = d3.geoAlbers();
 var path = d3.geoPath().projection(proj);
 
 // 关键修改2:封装resize逻辑为单独函数,方便初始调用和resize触发
 function updateMap() {
 let divrect = document.querySelector("#vizook").getBoundingClientRect();
 // total width and height
 const divw = divrect.width;
 const divh = divrect.height - 50;

 svg1.attr('width', divw).attr('height', divh);

 // 适配投影到容器尺寸
 proj.fitSize([divw, divh], geomap);

 // 更新地图路径
 usmap.attr("class", "usmap")
 .attr("d", path)
 .attr("stroke", "#333333")
 .attr("fill", "#555555"); // 给地图加个填充色,方便看清楚

 // 更新港口点位置
 portloc
 .attr("cx", d => proj([d.lng, d.lat])[0])
 .attr("cy", d => proj([d.lng, d.lat])[1])
 .attr("r", 5)
 .attr("fill", "#FFFFFF");
 }

 const vizspace = d3.select('#vizook');
 var svg1 = vizspace.append("svg").attr('class', "svgook");

 var usmap = svg1.append("g")
 .datum(geomap)
 .append("path")

 var portloc = svg1.append("g")
 .selectAll("circle")
 .data(portlist)
 .join("circle")

 // 关键修改3:页面初始化时手动调用一次updateMap,确保第一次渲染就用正确投影
 updateMap();

 // 监听resize事件,触发地图更新
 window.addEventListener("resize", updateMap);
 })
 .catch(error => {
 // Handle error
 console.error(error);
 });
</script>
</body>

我还额外给地图加了灰色填充,这样能更清楚地看到点和地图的对应关系。现在这些港口点应该会准确地落在对应的沿海位置了!

内容的提问来源于stack exchange,提问作者Hannah Butler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 10:02:33