Javascript 在d3地理绘图的固定位置附加链接图形
我正在尝试使用d3 geo将链接图形树附加到旋转的球体上。我已经修改了看到和看到的旋转地球演示,并成功地添加了我发现的节点/链接的强制导向布局 是我到目前为止所拥有的一把小提琴。力图出现在南极附近,为跳跃链接道歉我认为这只是一个css问题,因为它在我的模拟中显示正确(我暂时不使用样式表) 由于我希望节点固定在特定的纬度/经度,所以我希望完全取消力模拟。然而,在保留节点和链接的同时删除它的所有尝试都会导致它们完全消失。我还努力修复它们的位置,并将节点覆盖在地图图形上(你可以看到节点位于陆地的后面) 总而言之,我想:Javascript 在d3地理绘图的固定位置附加链接图形,javascript,d3.js,svg,topojson,Javascript,D3.js,Svg,Topojson,我正在尝试使用d3 geo将链接图形树附加到旋转的球体上。我已经修改了看到和看到的旋转地球演示,并成功地添加了我发现的节点/链接的强制导向布局 是我到目前为止所拥有的一把小提琴。力图出现在南极附近,为跳跃链接道歉我认为这只是一个css问题,因为它在我的模拟中显示正确(我暂时不使用样式表) 由于我希望节点固定在特定的纬度/经度,所以我希望完全取消力模拟。然而,在保留节点和链接的同时删除它的所有尝试都会导致它们完全消失。我还努力修复它们的位置,并将节点覆盖在地图图形上(你可以看到节点位于陆地的后面)
- 删除强制布局,但保留节点/链接
- 在旋转期间将节点固定在特定的纬度/经度
- 在地理地图要素顶部覆盖节点/链接
剧本
(function (){
var config = {
"projection": "Orthographic",
"clip": true, "friction": 1,
"linkStrength": 1,
"linkDistance": 20,
"charge": 50,
"gravity": 1,
"theta": .8 };
var width = window.innerWidth,
height = window.innerHeight - 5,
fill = d3.scale.category20(),
feature,
origin = [0, -90],
velocity = [0.01, 0],
t0 = Date.now(),
nodes = [{x: width/2, y: height/2}],
links = [];
var projection = d3.geo.orthographic()
.scale(height/2)
.translate([(width/2)-125, height/2])
.clipAngle(config.clip ? 90 : null)
var path = d3.geo.path()
.projection(projection);
var force = d3.layout.force()
.linkDistance(config.linkDistance)
.linkStrength(config.linkStrength)
.gravity(config.gravity)
.size([width, height])
.charge(-config.charge);
var svg = d3.select("#vis").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.drag()
.origin(function() { var r = projection.rotate(); return {x: 2 * r[0], y: -2 * r[1]}; })
.on("drag", function() { force.start(); var r = [d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]]; t0 = Date.now(); origin = r; projection.rotate(r); }))
for(x=0;x<20;x++){
source = nodes[~~(Math.random() * nodes.length)]
target = {x: source.x + Math.random(), y: source.y + Math.random(), group: Math.random()}
links.push({source: source, target: target})
nodes.push(target)
}
var node = svg.selectAll("path.node")
.data(nodes)
.enter().append("path").attr("class", "node")
.style("fill", function(d) { return fill(d.group); })
.style("stroke", function(d) { return d3.rgb(fill(d.group)).darker(); })
.call(force.drag);
console.log(node)
var link = svg.selectAll("path.link")
.data(links)
.enter().append("path").attr("class", "link")
force
.nodes(nodes)
.links(links)
.on("tick", tick)
.start();
var url = "https://raw.githubusercontent.com/d3/d3.github.com/master/world-110m.v1.json";
d3.json(url, function(error, topo) {
if (error) throw error;
var land = topojson.feature(topo, topo.objects.land);
svg.append("path")
.datum(land)
.attr("class", "land")
.attr("d", path)
d3.timer(function() {
force.start();
var dt = Date.now() - t0;
projection.rotate([velocity[0] * dt + origin[0], velocity[1] * dt + origin[1]]);
svg.selectAll("path")
.filter(function(d) {
return d.type == "FeatureCollection";})
.attr("d", path);
});
});
function tick() {
node.attr("d", function(d) { var p = path({"type":"Feature","geometry":{"type":"Point","coordinates":[d.x, d.y]}}); return p ? p : 'M 0 0' });
link.attr("d", function(d) { var p = path({"type":"Feature","geometry":{"type":"LineString","coordinates":[[d.source.x, d.source.y],[d.target.x, d.target.y]]}}); return p ? p : 'M 0 0' });
}
function clip(d) {
return path(circle.clip(d));
}
})();
(函数(){
变量配置={
“投影”:“正交”,
“clip”:正确,“摩擦力”:1,
“联系强度”:1,
“链接距离”:20,
“费用”:50,
“重力”:1,
“θ”:.8};
变量宽度=window.innerWidth,
高度=窗内高度-5,
fill=d3.scale.category20(),
特写,
原点=[0,-90],
速度=[0.01,0],
t0=日期。现在(),
节点=[{x:width/2,y:height/2}],
链接=[];
var projection=d3.geo.orthographic()
.比例(高度/2)
.translate([(宽度/2)-125,高度/2])
.clipAngle(config.clip?90:null)
var path=d3.geo.path()
.投影(投影);
var-force=d3.layout.force()
.linkDistance(配置.linkDistance)
.linkStrength(config.linkStrength)
.gravity(配置重力)
.尺寸([宽度、高度])
.charge(-config.charge);
var svg=d3.选择(#vis”).追加(“svg”)
.attr(“宽度”,宽度)
.attr(“高度”,高度)
.call(d3.behavior.drag())
.origin(函数(){var r=projection.rotate();返回{x:2*r[0],y:-2*r[1]};})
.on(“拖动”,函数(){force.start();var r=[d3.event.x/2,-d3.event.y/2,projection.rotate()[2]];t0=Date.now();origin=r;projection.rotate(r);})
对于(x=0;x假设您使用了力,以便可以添加点和链接,让我们后退一步,让我们删除任何与力相关的、没有节点和链接的内容。在这种情况下,这两种情况都不需要使用力布局。让我们从地球仪开始,通过动画和拖动(并在进行此操作时移动到d3v5):
var宽度=500,
高度=500,
t0=日期。现在(),
速度=[0.01,0],
原点=[0,-45];
var projection=d3.geoporthographic()
.比例尺(高度/2.1)
.translate([宽度/2,高度/2])
clipAngle先生(90)
var path=d3.geoPath()
.投影(投影);
var svg=d3.选择(“正文”).追加(“svg”)
.attr(“宽度”,宽度)
.attr(“高度”,高度)
.call(d3.drag()
.subject(函数(){var r=projection.rotate();返回{x:2*r[0],y:-2*r[1]};})
.on(“拖动”,函数(){var r=[d3.event.x/2,-d3.event.y/2,projection.rotate()[2]];t0=Date.now();origin=r;projection.rotate(r);})
d3.json(“https://unpkg.com/world-atlas@1/world/110m.json”)。然后(函数(topo){
var land=topojson.feature(topo,topo.objects.land);
追加(“路径”)
.基准(土地)
.attr(“类别”、“土地”)
.attr(“d”,路径);
d3.定时器(函数(){
var dt=Date.now()-t0;
投影。旋转([速度[0]*dt+原点[0],速度[1]*dt+原点[1]]);
svg.selectAll(“路径”)
.attr(“d”,路径);
});
});
移动到D3 v4/5。然后,使用forceX
和forceY
设置位置。完成。我假设您已经知道要显示的节点的纬度和经度?是的,为了在示例中方便起见,我现在只是随机放置它们
(function (){
var config = {
"projection": "Orthographic",
"clip": true, "friction": 1,
"linkStrength": 1,
"linkDistance": 20,
"charge": 50,
"gravity": 1,
"theta": .8 };
var width = window.innerWidth,
height = window.innerHeight - 5,
fill = d3.scale.category20(),
feature,
origin = [0, -90],
velocity = [0.01, 0],
t0 = Date.now(),
nodes = [{x: width/2, y: height/2}],
links = [];
var projection = d3.geo.orthographic()
.scale(height/2)
.translate([(width/2)-125, height/2])
.clipAngle(config.clip ? 90 : null)
var path = d3.geo.path()
.projection(projection);
var force = d3.layout.force()
.linkDistance(config.linkDistance)
.linkStrength(config.linkStrength)
.gravity(config.gravity)
.size([width, height])
.charge(-config.charge);
var svg = d3.select("#vis").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.drag()
.origin(function() { var r = projection.rotate(); return {x: 2 * r[0], y: -2 * r[1]}; })
.on("drag", function() { force.start(); var r = [d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]]; t0 = Date.now(); origin = r; projection.rotate(r); }))
for(x=0;x<20;x++){
source = nodes[~~(Math.random() * nodes.length)]
target = {x: source.x + Math.random(), y: source.y + Math.random(), group: Math.random()}
links.push({source: source, target: target})
nodes.push(target)
}
var node = svg.selectAll("path.node")
.data(nodes)
.enter().append("path").attr("class", "node")
.style("fill", function(d) { return fill(d.group); })
.style("stroke", function(d) { return d3.rgb(fill(d.group)).darker(); })
.call(force.drag);
console.log(node)
var link = svg.selectAll("path.link")
.data(links)
.enter().append("path").attr("class", "link")
force
.nodes(nodes)
.links(links)
.on("tick", tick)
.start();
var url = "https://raw.githubusercontent.com/d3/d3.github.com/master/world-110m.v1.json";
d3.json(url, function(error, topo) {
if (error) throw error;
var land = topojson.feature(topo, topo.objects.land);
svg.append("path")
.datum(land)
.attr("class", "land")
.attr("d", path)
d3.timer(function() {
force.start();
var dt = Date.now() - t0;
projection.rotate([velocity[0] * dt + origin[0], velocity[1] * dt + origin[1]]);
svg.selectAll("path")
.filter(function(d) {
return d.type == "FeatureCollection";})
.attr("d", path);
});
});
function tick() {
node.attr("d", function(d) { var p = path({"type":"Feature","geometry":{"type":"Point","coordinates":[d.x, d.y]}}); return p ? p : 'M 0 0' });
link.attr("d", function(d) { var p = path({"type":"Feature","geometry":{"type":"LineString","coordinates":[[d.source.x, d.source.y],[d.target.x, d.target.y]]}}); return p ? p : 'M 0 0' });
}
function clip(d) {
return path(circle.clip(d));
}
})();