Цель состоит в том, чтобы элегантно добавить несколько небольших древовидных диаграмм в форме матрицы. Я подготовил простую версию предполагаемого результата; прямоугольники и тексты показывают, как должен выглядеть шаблон. Далее необходимо добавить простой двухуровневый древовидный график под прямоугольниками.
Я работаю со структурой данных, в которой данные дерева хранятся в объекте внутри каждого элемента массива объектов:
var data = [
{name:"jackie chan", tree: {
"name": "Top Level",
"children": [
{ "name": "Fund 1" },
{ "name": "Fund 2" },
{ "name": "Fund 3"}
]
}},
{name:"jet li", tree: {
"name": "Top Level",
"children": [
{ "name": "Fund 1" },
{ "name": "Fund 2" },
{ "name": "Fund 3"},
{ "name": "Fund 4"},
]
}},
...
Фрагмент:
var margins = {top:100, bottom:300, left:100, right:100};
var height = 600;
var width = 900;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
var data = [
{name:"jackie chan", tree: {
"name": "Top Level",
"children": [
{ "name": "Fund 1" },
{ "name": "Fund 2" },
{ "name": "Fund 3"}
]
}},
{name:"jet li", tree: {
"name": "Top Level",
"children": [
{ "name": "Fund 1" },
{ "name": "Fund 2" },
{ "name": "Fund 3"},
{ "name": "Fund 4"},
]
}},
{name:"donnie yen", tree: {
"name": "Top Level",
"children": [
{ "name": "Fund 1" },
{ "name": "Fund 2" },
]
}},
{name:"chow yun fat", tree: {
"name": "Top Level",
"children": [
{ "name": "Fund 1" },
{ "name": "Fund 2" },
{ "name": "Fund 3"},
{ "name": "Fund 4"},
{ "name": "Fund 5"},
]
}},
];
//var formatComma = d3.format(",");
var columns = 3;
var spacing = 150;
var vSpacing = 180;
var pmG = graphGroup.selectAll('.pm')
.data(data)
.enter()
.append('g')
.attr('class', 'pm')
.attr('id', (d, i) => 'pm' + i)
.attr('transform', (d, k) => {
var horSpace = (k % columns) * spacing;
var vertSpace = ~~((k / columns)) * vSpacing;
return "translate(" + horSpace + "," + vertSpace + ")";
});
var miniTree = d3.tree()
.size([150, 75]);
pmG.append('rect')
.attr('x',0)
.attr('y',0)
.attr('width',100)
.attr('height',25)
.style('fill',"#003366");
pmG.append('text')
.attr('x',50)
.attr('y',-10)
.attr('text-anchor','middle')
.text(function(d) {return d.name});
pmG.selectAll(null)
.data( function(d) {return miniTree(d3.hierarchy(d.tree).descendants().slice(1))})
.enter().append("path")
.attr("class", "link")
.attr("d", function(d) {
return "M" + d.x + "," + d.y
+ "C" + d.x + "," + (d.y + d.parent.y) / 2
+ " " + d.parent.x + "," + (d.y + d.parent.y) / 2
+ " " + d.parent.x + "," + d.parent.y;
});
pmG.selectAll(null)
.data( function(d) {return miniTree(d3.hierarchy(d.tree).descendants())})
.enter().append("g")
.attr("class", function(d) {
return "node" +
(d.children ? " node--internal" : " node--leaf"); })
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; });
<script src = "https://d3js.org/d3.v5.min.js"></script>
Я думал, что смогу добиться желаемого результата, инкапсулировав все методы дерева в один вызов больших данных, а затем использовать тот же подход для добавления прямоугольников и текстов, как показано в результате фрагмента:
pmG.selectAll(null)
.data( function(d) {return miniTree(d3.hierarchy(d.tree).descendants().slice(1))})
.enter().append("path")
.attr("class", "link")
.attr("d", function(d) {
return "M" + d.x + "," + d.y
+ "C" + d.x + "," + (d.y + d.parent.y) / 2
+ " " + d.parent.x + "," + (d.y + d.parent.y) / 2
+ " " + d.parent.x + "," + d.parent.y;
});
Однако я получил ошибку:
Ошибка необработанного типа: i.eachBefore не является функцией
Я никогда раньше не видел такой ошибки, и я боюсь, что назревает большая проблема.
Основываясь на моей матричной структуре (посмотрите, как прямоугольники и текст добавляются во фрагмент), как я могу аналогичным образом добавить небольшие «мини-деревья»?
У вас ошибка при создании дерева:
.data( function(d) {return miniTree(d3.hierarchy(d.tree).descendants().slice(1)) })
Должно быть:
.data( function(d) {return miniTree(d3.hierarchy(d.tree)).descendants().slice(1) })
Вам нужны не потомки d3.hierarchy, а miniTree - пропустили его в первые несколько раз, когда я смотрел на него.
var margins = {top:100, bottom:300, left:100, right:100};
var height = 600;
var width = 900;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
var data = [
{name:"jackie chan", tree: {
"name": "Top Level",
"children": [
{ "name": "Fund 1" },
{ "name": "Fund 2" },
{ "name": "Fund 3"}
]
}},
{name:"jet li", tree: {
"name": "Top Level",
"children": [
{ "name": "Fund 1" },
{ "name": "Fund 2" },
{ "name": "Fund 3"},
{ "name": "Fund 4"},
]
}},
{name:"donnie yen", tree: {
"name": "Top Level",
"children": [
{ "name": "Fund 1" },
{ "name": "Fund 2" },
]
}},
{name:"chow yun fat", tree: {
"name": "Top Level",
"children": [
{ "name": "Fund 1" },
{ "name": "Fund 2" },
{ "name": "Fund 3"},
{ "name": "Fund 4"},
{ "name": "Fund 5"},
]
}},
];
//var formatComma = d3.format(",");
var columns = 3;
var spacing = 150;
var vSpacing = 180;
var pmG = graphGroup.selectAll('.pm')
.data(data)
.enter()
.append('g')
.attr('class', 'pm')
.attr('id', (d, i) => 'pm' + i)
.attr('transform', (d, k) => {
var horSpace = (k % columns) * spacing;
var vertSpace = ~~((k / columns)) * vSpacing;
return "translate(" + horSpace + "," + vertSpace + ")";
});
var miniTree = d3.tree()
.size([150, 75]);
pmG.append('rect')
.attr('x',0)
.attr('y',0)
.attr('width',100)
.attr('height',25)
.style('fill',"#003366");
pmG.append('text')
.attr('x',50)
.attr('y',-10)
.attr('text-anchor','middle')
.text(function(d) {return d.name});
pmG.selectAll(null)
.data( function(d) {
return miniTree(d3.hierarchy(d.tree)).descendants().slice(1)
})
.enter().append("path")
.attr("transform", "translate(-25,20)") // extra positioning.
.attr("class", "link")
.attr("d", function(d) {
return "M" + d.x + "," + d.y
+ "C" + d.x + "," + (d.y + d.parent.y) / 2
+ " " + d.parent.x + "," + (d.y + d.parent.y) / 2
+ " " + d.parent.x + "," + d.parent.y;
});
pmG.selectAll(null)
.data( function(d) {return miniTree(d3.hierarchy(d.tree)).descendants() })
.enter().append("g")
.attr("class", function(d) {
return "node" +
(d.children ? " node--internal" : " node--leaf"); })
.attr("transform", function(d) {
return "translate(" + (d.x - 25) + "," + (d.y+20) + ")"; }) // with extra positioning.
.append("circle")
path {
stroke-width: 1px;
fill: none;
stroke: #003366;
}
circle {
r: 5px;
fill: #003366;
}
<script src = "https://d3js.org/d3.v5.min.js"></script>
Я добавил строку для добавления кругов, их радиус указан в css, в противном случае вы просто добавляли элементы g в конец фрагмента.
Спасибо! Пути, кажется, добавлены, но кажется, что круги все еще отсутствуют. Вероятно, это еще один недосмотр моего кода, не стесняйтесь выбрасывать этот раздел, если вы знаете более простой способ добавления кругов к узлам.