gpt4 book ai didi

javascript - d3 map 上的线未形成曲线

转载 作者:搜寻专家 更新时间:2023-11-01 04:12:16 24 4
gpt4 key购买 nike

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

这是演示问题的代码笔: https://codepen.io/peeyush-pant/pen/WqbPax

还有一张图片:

enter image description here

这是我的投影数据:

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 geoPaths 被动态重新采样以允许这样做。

这种行为在网络地理 map 库中并不常见,其中大多数将纬度和经度视为笛卡尔数据而不是三维数据:其中纬度和经度是球体上的点。在将数据视为笛卡尔坐标时,连接两点时的线是直的。在 d3 中,这可以通过 these 等方法来完成。 .

如果您希望所有线段的曲线一致,我们会将数据视为笛卡尔并插值曲线。由于我们不会为此使用 d3.geoPath,因此无需将您的目的地和源转换为 geojson LineString,我们可以直接使用这些点。

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

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

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 data = [{
"source": {
"lat": 40.712776,
"lon": -74.005974
},
"destination": {
"lat": 21.05,
"lon": 105.55
}
},
{
"source": {
"lat": 40.712776,
"lon": -74.005974
},
"destination": {
"lat": -35.15,
"lon": 149.08
}
}]

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;
}

var projection = d3.geoEquirectangular().translate([250,150]).scale(500/Math.PI/2);
var path = d3.geoPath(projection);

var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 300);

d3.json("https://unpkg.com/world-atlas@1/world/110m.json").then(function(world) {

var worldOutline = svg.append("path")
.datum(topojson.mesh(world))
.attr("d", path );

var line = d3.line()
.x(function(d) {
return projection([d.lon,d.lat])[0];
})
.y(function(d) {
return projection([d.lon,d.lat])[1];
})
.curve(curve);

svg.selectAll(null)
.data(data)
.enter()
.append("path")
.datum(function(d) {
return [d.source,d.destination]; // d3.line expects an array where each item represnts a vertex.
})
.attr("d",line)
.style("stroke","black")
.style("stroke-width",1.5);

});
path {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>

下面,为了好玩,我比较了使用 d3.line 的直线、使用带有自定义曲线插值器的 d3.line 的曲线,以及普通的旧 d3.geoPath 带有一些动画:

 let data = [{
"source": {
"lat": 40.712776,
"lon": -74.005974
},
"destination": {
"lat": 21.05,
"lon": 105.55
}
},
{
"source": {
"lat": 40.712776,
"lon": -74.005974
},
"destination": {
"lat": -35.15,
"lon": 149.08
}
}]

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;
}

var projection = d3.geoEquirectangular().translate([250,150]).scale(500/Math.PI/2);
var path = d3.geoPath(projection);

var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 300);

d3.json("https://unpkg.com/world-atlas@1/world/110m.json").then(function(world) {

var worldOutline = svg.append("path")
.datum(topojson.mesh(world))
.attr("d", path );

var line = d3.line()
.x(function(d) {
return projection([d.lon,d.lat])[0];
})
.y(function(d) {
return projection([d.lon,d.lat])[1];
})
.curve(curve);

var fauxArcPaths = svg.selectAll(null)
.data(data)
.enter()
.append("path")
.datum(function(d) {
return [d.source,d.destination];
})
.attr("d",line)
.style("stroke","black")
.style("stroke-width",1.5);

var greatCirclePaths = svg.selectAll(null)
.data(data)
.enter()
.append("path")
.datum(function(d) {
return {type:"LineString",coordinates:
[[d.source.lon,d.source.lat],[d.destination.lon,d.destination.lat]] }
})
.attr("d",path)
.style("stroke","steelblue")
.style("stroke-width",1.5);

var straightline = d3.line()
.x(function(d) {
return projection([d.lon,d.lat])[0];
})
.y(function(d) {
return projection([d.lon,d.lat])[1];
});

var straightPaths = svg.selectAll(null)
.data(data)
.enter()
.append("path")
.datum(function(d) {
return [d.source,d.destination];
})
.attr("d",straightline)
.style("stroke-width",1.5)
.style("stroke","orange");

// animate:
d3.interval(function(elapsed) {
projection.rotate([ -elapsed / 150, elapsed/300 ]);
straightPaths.attr("d",straightline);
greatCirclePaths.attr("d",path);
fauxArcPaths.attr("d",line);
worldOutline.attr("d",path);
}, 50);



});
path {
fill: none;
stroke: #aaa;
stroke-width: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>

关于javascript - d3 map 上的线未形成曲线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56562231/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com