我必须使用d3.js从数据中创建一个金字塔可视化。附上一个示例视图。有2个挑战,我希望得到帮助。
我知道如何创建一个金字塔,看下面给出的例子,但我不明白,我如何能添加多个元素(图像中显示的值,如A,B,C等)在每一级http:/bl.ocks.orgronakrb73e9204a66e2a9c1fee8。
我想为最低层的一些元素附加一个树状结构。
我不能用树状结构代替金字塔,因为每层的元素之间没有父子关系。
示例演示数据。
const data = [
{ Name: "G", level: 1},
{ Name: "F", level: 2},
{ Name: "DE", level: 2},
{ Name: "C", level: 3},
{ Name: "B", level: 3},
{ Name: "A", level: 3, team: [1,2,3]}
]
这里有一种方法来做 - 这是第一次 很 粗通。如果你说是沿着正确的方向,我可以改进和完善。
(我需要改进间距的大小,我还需要把队员加到字母A上,还需要大体清理一下等等)
更新时间:20年6月11日 - 清理了间距,在字母周围添加了方框,在数字周围添加了圆圈。
var width = 700,
height = 450,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#255aee","#3a6fff","#4f84ff","rgb(101,154,302)","rgb(122,175,323)", "rgb(144,197,345)", "rgb(165,218,366)"]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
//*
const data = [
{ Name: "G", level: 1},
{ Name: "F", level: 2},
{ Name: "DE", level: 2},
{ Name: "C", level: 3},
{ Name: "B", level: 3},
{ Name: "A", level: 3, team: [1,2,3]}
];
//*/
const grouped = Object.values(data.reduce((aggObj, item) => {
if (!aggObj[item.level]){
aggObj[item.level] = {level: item.level, names: []};
}
aggObj[item.level].names.push({name: item.Name, team: item.team});
return aggObj;
}, {}));
//console.log(grouped);
var pyramid = d3.pyramid()
.size([width,height])
.value(function(d) { return d.level; });
var line = d3.svg.line()
.interpolate('linear-closed')
.x(function(d,i) { return d.x; })
.y(function(d,i) { return d.y; });
var g = svg.selectAll(".pyramid-group")
.data(pyramid(grouped)) //swap for grouped data
.enter().append("g")
.attr("class", "pyramid-group");
g.append("path")
.attr("d", function (d){ return line(d.coordinates); })
.style("fill", function(d) { return color(d.level); });
var textBoxes = g.selectAll(".textBoxes")
.data((d) => {
const helperObj = {};
helperObj.coordinates = d.coordinates;
helperObj.level = d.level;
const len = d.names.length;
helperObj.len = d.names.length;
if(d.coordinates.length === 4) {
const left = d.coordinates[0].x;
const right = d.coordinates[3].x;
const topLeft = d.coordinates[1].x;
const topRight = d.coordinates[2].x;
const start = left + ((topLeft - left) / 2);
const end = right - ((right - topRight) / 2);
const sep = (end - start) / len;
helperObj.start = start;
helperObj.end = end;
helperObj.sep = sep;
const yCenter = (((d.coordinates[0].y-d.coordinates[1].y)/2)+d.coordinates[1].y) + 5;
helperObj.yCenter = yCenter;
helperObj.yDiff = (d.coordinates[0].y-d.coordinates[1].y);
} else {
const left = d.coordinates[1].x;
const right = d.coordinates[2].x;
const start = left + ((right - left) / 4);
const end = right - ((right - left) / 4);
const sep = (end - start) / len;
helperObj.start = start;
helperObj.end = end;
helperObj.sep = sep;
helperObj.yCenter = (d.coordinates[0].y + d.coordinates[1].y)/2 + 10;
helperObj.yDiff = (d.coordinates[1].y-d.coordinates[0].y) * 0.9;
}
return d.names.map((e,i) => {
const name = e.name;
const team = e.team || [];
const teamLen = team.length;
return {...helperObj, i, name, team, teamLen};
})
})
.enter().append("g");
textBoxes.append("rect")
.attr({
"y": function (d,i) {
//console.log(d)
return d.yCenter - (d.yDiff/8);
},
"x": function (d,i) {
return d.start + ((i+0.05) * d.sep);
},
"width": function (d,i) {
return d.sep * 0.9;
},
"height": function (d,i) {
return (d.yDiff/8) * 3;
},
"fill": function (d,i) {
return "#e0f4ff";
},
"rx": function (d,i) {
return d.sep * 0.1;
}
});
textBoxes.append("text")
.attr({
"y": function (d,i) {
//console.log(d)
return d.yCenter;
},
"x": function (d,i) {
return d.start + ((i+0.5) * d.sep);
}
})
.style("text-anchor", "middle")
.style("alignment-baseline", "hanging")
.style("font-size", "x-large")
.text(function(d) { return d.name; });
var teamMembers = textBoxes.selectAll(".teamMembers")
.data((d) => {
const itemCopy = {...d};
const teamCopy = [...d.team];
delete itemCopy.team;
return d.team.map(e => ({...itemCopy, teamMember: e}));
});
teamMembers.enter().append("line")
.attr({
"y1": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/3);
},
"x1": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + ((i+0.5) * ((0.7*d.sep)/d.teamLen));
},
"y2": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/6);
},
"x2": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + (0.5 * (0.7*d.sep));
},
"stroke": function (d,i) {
//console.log(d)
return "black";
},
"stroke-width": function (d,i) {
return "2";
}
});
teamMembers.enter().append("circle")
.attr({
"cy": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/3);
},
"cx": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + ((i+0.5) * ((0.7*d.sep)/d.teamLen));
},
"r": function (d,i) {
return d.sep * 0.05;
}
});
teamMembers.enter().append("text")
.attr({
"y": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/3);
},
"x": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + ((i+0.5) * ((0.7*d.sep)/d.teamLen));
},
"fill": function (d,i) {
return "white";
}
})
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.style("font-size", "large")
.text(function(d) { return d.teamMember; });
d3.select("body").append("table")
.attr({
"id" : "footer",
"width": width + "px"
})
d3.select("body #footer").append("tr")
.attr({
"class" : "PykCharts-credits",
"id" : "credit-datasource"
})
.append("td")
.style("text-align","left")
.html("<span style='pointer-events:none;'>Credits: </span><a href='http://pykcharts.com' target='_blank'>"+ "Pykcharts" +"</a>");
<html>
<head>
<style>
body {
font: 10px sans-serif;
}
.arc path {
stroke: #fff;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://s3-ap-southeast-1.amazonaws.com/charts.pykih.com/gists/pyramid.js"></script>
</body>
</html>
输出。