Javascript d3地图上的直线未形成曲线

Javascript d3地图上的直线未形成曲线,javascript,d3.js,d3.geo,Javascript,D3.js,D3.geo,我已经使用d3.js创建了一个地图。我想显示两个位置之间的曲线。我能画出一条线,但有时它不能形成一条完美的曲线。对于某些线,这些线在地图后面(穿过反子午线)弯曲到其目的地 这是一支代码笔,演示了这个问题: 还有一个图像: 这是我的投影数据: var projection = d3.geoEquirectangular(); var path = d3.geoPath() .projection(projection); 下面是我如何画线的: arcGroup.selectAll

我已经使用d3.js创建了一个地图。我想显示两个位置之间的曲线。我能画出一条线,但有时它不能形成一条完美的曲线。对于某些线,这些线在地图后面(穿过反子午线)弯曲到其目的地

这是一支代码笔,演示了这个问题:

还有一个图像:

这是我的投影数据:

var projection = d3.geoEquirectangular();

var path = d3.geoPath()
  .projection(projection);

下面是我如何画线的:

  arcGroup.selectAll("myPath")
    .data(links)
    .enter()
    .append("path")
    .attr("class", "line")
    .attr("id", function (d, i) {
      return "line" + i;
    })
    .attr("d", function (d) {
      return path(d)
    })
    .style("fill", "none")
    .style("stroke", '#fff787')
    .style("stroke-width", 1.5);

谢谢。

D3 geoPath可用于创建沿较大圆距离的路径:它们不是按样式弯曲的,而是根据需要弯曲的,取决于投影,以表示连接两点的地球上最短路径。D3地质路径动态重新采样,以允许此操作

这种行为在web地理地图库中是不常见的,其中大多数将纬度和经度视为笛卡尔数据,而不是三维数据:其中纬度和经度是球体上的点。在将数据视为笛卡尔坐标系时,当连接两点时,直线是直的。在d3中,这可以通过以下方法实现:

如果您希望所有线段都具有一致的曲线,我们将把数据视为笛卡尔坐标并插值曲线。由于我们不会使用
d3.geoPath
进行此操作,因此不需要将目的地和源转换为geojson线字符串,我们可以直接使用点

我们可以为此使用曲线插值器,但如果不在终点和起点目标之间添加控制点,默认插值器将无法工作。相反,让我们尝试自定义曲线-有关自定义曲线的更多信息,请参见这些答案(,)

我们的自定义曲线可以取第一个点之后的任何点,以找到它和它之前的点之间的中点,并偏移一个点以创建一个控制点,在前一个点和当前点之间形成一个三角形,然后我们只需在它们之间绘制一条二次曲线:

var curve = function(context) {
  var custom = d3.curveLinear(context);
  custom._context = context;
  custom.point = function(x,y) {
    x = +x, y = +y;
    switch (this._point) {
      case 0: this._point = 1; 
        this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
        this.x0 = x; this.y0 = y;        
        break;
      case 1: this._point = 2;
      default: 
        var x1 = this.x0 * 0.5 + x * 0.5;
        var y1 = this.y0 * 0.5 + y * 0.5;
        var m = 1/(y1 - y)/(x1 - x);
        var r = -100; // offset of mid point.
        var k = r / Math.sqrt(1 + (m*m) );
        if (m == Infinity) {
          y1 += r;
        }
        else {
          y1 += k;
          x1 += m*k;
        }     
        this._context.quadraticCurveTo(x1,y1,x,y); 
        this.x0 = x; this.y0 = y;        
        break;
    }
  }
  return custom;
}
有了这个,我们可以简单地用以下东西画线:

d3.line()
 .curve(curve)
 .x(function(d) { return d.lon; })
 .y(function(d) { return d.lat; })
如下所示:

let数据=[{
“来源”:{
“lat”:40.712776,
“lon”:-74.005974
},
“目的地”:{
“lat”:21.05,
“lon”:105.55
}
},
{
“来源”:{
“lat”:40.712776,
“lon”:-74.005974
},
“目的地”:{
“lat”:-35.15,
“lon”:149.08
}
}]
变量曲线=函数(上下文){
var custom=d3.曲线(上下文);
自定义。_context=上下文;
custom.point=函数(x,y){
x=+x,y=+y;
开关(此点){
案例0:此点=1;
this.\u line?this.\u context.lineTo(x,y):this.\u context.moveTo(x,y);
this.x0=x;this.y0=y;
打破
案例1:该点=2;
违约:
var x1=这个0.x0*0.5+x*0.5;
var y1=该值为0.y0*0.5+y*0.5;
var m=1/(y1-y)/(x1-x);
var r=-100;//中点的偏移量。
var k=r/Math.sqrt(1+(m*m));
if(m==无穷大){
y1+=r;
}
否则{
y1+=k;
x1+=m*k;
}     
这是正交曲线(x1,y1,x,y);
this.x0=x;this.y0=y;
打破
}
}
退货习惯;
}
var projection=d3.geoequirectangle().translate([250150]).scale(500/Math.PI/2);
var path=d3.地质路径(投影);
var svg=d3.选择(“主体”)
.append(“svg”)
.attr(“宽度”,500)
.attr(“高度”,300);
d3.json(“https://unpkg.com/world-atlas@1/world/110m.json”)。然后(函数(world){
var worldOutline=svg.append(“路径”)
.datum(topojson.mesh(世界))
.attr(“d”,路径);
var line=d3.line()
.x(功能(d){
返回投影([d.lon,d.lat])[0];
})
.y(功能(d){
返回投影([d.lon,d.lat])[1];
})
.曲线(曲线);
svg.selectAll(空)
.数据(数据)
.输入()
.append(“路径”)
.基准(功能(d){
return[d.source,d.destination];//d3.line需要一个数组,其中每个项代表一个顶点。
})
.attr(“d”,行)
.style(“笔划”、“黑色”)
.样式(“笔划宽度”,1.5);
});
路径{
填充:无;
冲程:#ccc;
笔画宽度:1px;
}

谢谢@andrew,你的建议真的很有帮助。