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
相关产品推荐
相关产品推荐

