d3:节点+链接,形成多代家谱;如何解析数据以绘制线条
d3: Nodes + Links to make a multi-generation family tree; how to parse data to draw lines?
我正在d3.js中制作一个三代或四代家谱。您可以在这里看到早期版本:
http://jsfiddle.net/Asparagirl/uenh4j92/8/
代码:
// People
var nodes = [
{ id: 1, name: "Aaron", x: 50, y: 100, gender: "male", dob: "1900", hasParent: false, hasSpouse: true, spouse1_id: 2 },
{ id: 2, name: "Brina" , x: 400, y: 100, gender: "female", dob: "1900", hasParent: false, hasSpouse: true, spouse1_id: 1 },
{ id: 3, name: "Caden", x: 100, y: 260, gender: "female", dob: "1925", hasParent: true, parent1_id: 1, parent2_id: 2, hasSpouse: false },
{ id: 4, name: "David", x: 200, y: 260, gender: "male", dob: "1930", hasParent: true, parent1_id: 1, parent2_id: 2, hasSpouse: false },
{ id: 5, name: "Ewa", x: 320, y: 260, gender: "female", dob: "1935", hasParent: true, parent1_id: 1, parent2_id: 2, hasSpouse: true, spouse_id: 6 },
{ id: 6, name: "Feivel", x: 450, y: 260, gender: "male", dob: "1935", hasParent: false, hasSpouse: true, spouse_id: 5 },
{ id: 7, name: "Gershon", x: 390, y: 370, gender: "male", dob: "1955", hasParent: true, parent1_id: 5, parent2_id: 6, hasSpouse: false }
];
var links = [
{ source: 0, target: 1 }
];
// Make the viewport automatically adjust to max X and Y values for nodes
var max_x = 0;
var max_y = 0;
for (var i=0; i<nodes.length; i++) {
var temp_x, temp_y;
var temp_x = nodes[i].x + 200;
var temp_y = nodes[i].y + 40;
if ( temp_x >= max_x ) { max_x = temp_x; }
if ( temp_y >= max_y ) { max_y = temp_y; }
}
// Variables
var width = max_x,
height = max_y,
margin = {top: 10, right: 10, bottom: 10, left: 10},
circleRadius = 20,
circleStrokeWidth = 3;
// Basic setup
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "visualization")
.attr("xmlns", "http://www.w3.org/2000/svg");
var elem = svg.selectAll("g")
.data(nodes)
var elemEnter = elem.enter()
.append("g")
.attr("data-name", function(d){ return d.name })
.attr("data-gender", function(d){ return d.gender })
.attr("data-dob", function(d){ return d.dob })
// Draw one circle per node
var circle = elemEnter.append("circle")
.attr("cx", function(d){ return d.x })
.attr("cy", function(d){ return d.y })
.attr("r", circleRadius)
.attr("stroke-width", circleStrokeWidth)
.attr("class", function(d) {
var returnGender;
if (d.gender === "female") { returnGender = "circle female"; }
else if (d.gender === "male") { returnGender = "circle male"; }
else { returnGender = "circle"; }
return returnGender;
});
// Add text to the nodes
elemEnter.append("text")
.attr("dx", function(d){ return (d.x + 28) })
.attr("dy", function(d){ return d.y - 5 })
.text(function(d){return d.name})
.attr("class", "text");
// Add text to the nodes
elemEnter.append("text")
.attr("dx", function(d){ return (d.x + 28) })
.attr("dy", function(d){ return d.y + 16 })
.text(function(d){return "b. " + d.dob})
.attr("class", "text");
// Add links between nodes
var linksEls = svg.selectAll(".link")
.data(links)
.enter()
// Draw the first line (between the primary couple, nodes 0 and 1)
.append("line")
.attr("x1",function(d){ return nodes[d.source].x + circleRadius + circleStrokeWidth; })
.attr("y1",function(d){ return nodes[d.source].y; })
.attr("x2",function(d){ return nodes[d.target].x - circleRadius - circleStrokeWidth; })
.attr("y2",function(d){ return nodes[d.target].y; })
.attr("class","line");
// Draw subsequent lines (from each of the children to the couple line's midpoint)
function drawLines(d){
var x1 = nodes[d.source].x;
var y1 = nodes[d.source].y;
var x2 = nodes[d.target].x;
var y2 = nodes[d.target].y;
var childNodes = nodes.filter(function(d){ return ( (d.hasParent===true) && (d.id!=7) ) });
childNodes.forEach(function(childNode){
svg.append("line")
// This draws from the node *up* to the couple line's midpoint
.attr("x1",function(d){ return childNode.x; })
.attr("y1",function(d){ return childNode.y - circleRadius - circleStrokeWidth + 1; })
.attr("x2",function(d){ return (x1+x2)/2; })
.attr("y2",function(d){ return (y1+y2)/2; })
.attr("class","line2");
})
}
linksEls.each(drawLines);
所以,这对一代人来说还可以。问题是,到了下一代的时候(Ewa嫁给了Feivel,孩子是Gershom),我们必须想办法复制一种伴侣之间的直线结构,从父母的夫妻线中点向下延伸到孩子。一个相关的问题是,现在,第一对被识别为一对(不同的线型)只是因为它们是我的节点列表中的前两条数据,而不是通过读取数据(即hasSpouse、spouse1_id等)来真正识别
我们非常感谢您的想法和想法,使这项工作变得更好!
让所有hasSpouse属性值为true的人都有一个spouse_id(而不是spouse1_id或spouse_id),并从节点数组生成链接数组,如下所示。couple
对象用于防止链路冗余,如0->1和1->0的链路。
var couple = {},
links = [];
nodes.forEach(function(d, i) {
if (d.hasSpouse) {
var link = {};
link["source"] = i;
var targetIdx;
nodes.forEach(function(s, sIdx) {
if (s.id == d.spouse_id) targetIdx = sIdx;
});
link["target"] = targetIdx;
if (!couple[i + "->" + targetIdx] && !couple[targetIdx + "->" + i]) {
couple[i + "->" + targetIdx] = true;
links.push(link);
}
}
});
现在,您需要在drawLines
方法中查找子节点的代码中做一个小的更改。通过匹配其父ID来查找子节点。
function drawLines(d) {
var src = nodes[d.source];
var tgt = nodes[d.target];
var x1 = src.x, y1 = src.y, x2 = tgt.x, y2 = tgt.y;
var childNodes = nodes.filter(function(d) {
//Code change
return ((d.hasParent === true) && (d.parent1_id == src.id && d.parent2_id == tgt.id))
});
......................
}
这是更新的fiddle
相关文章:
- 从 JSON 响应解析数据
- 使用jQuery对解析数据进行排序
- 如何在jquery中将xml解析数据获取为全局变量
- 使用路由的解析数据进行角度注入
- CSS选择器代码,用于从棘手的网站抓取/解析数据
- AngularJS:在模板外部获取路由解析数据
- 如何解析数据和二进制Websocket消息
- 在 JavaScript 循环中显示地图标记的 JSON 解析数据
- 从 JSON 字符串解析数据
- 通过 JQuery 解析数据
- 节点中的JSTOXML转换器模块未以正确的结构解析数据
- 更新不同状态的 UI 路由器解析数据
- d3:节点+链接,形成多代家谱;如何解析数据以绘制线条
- Angular UI路由器解析数据未定义
- 需要通过JavaScript从XML解析数据的帮助
- 解析数据从js变量包含html
- 在第一个附加的d3元素上没有解析数据
- 解析数据加密…把钥匙放在哪里?
- 无法在express应用程序中使用xmlreader从解析云中的xml文件解析数据
- Chrome扩展:可以Javascript拦截文件下载和解析数据