Does the grouping in D3v7 changed? The graph below groups elements by all ribbons, all arcs and all labels. Means the data for one group is splitted into three elements. How can I group them to bundle each needed element into one group.
So instead of all ribbons, all arcs and all labels.. it should be:
group0
thisRibbony
thisArc
thisLabelgroup1
thisRibbony
thisArc
thisLabelgroup2
thisRibbony
thisArc
thisLabel
How it is:
What I wish for:
Each entry got an own g grouping element which contains the bundled data.
////////////////////////////////////////////////////////////
////////////////////// Set-Up Data /////////////////////////
////////////////////////////////////////////////////////////
let groups = [
{ name: "A", color: "blue" },
{ name: "B", color: "red" },
{ name: "C", color: "green" },
{ name: "D", color: "grey" },
]
let paths = [
{ from: groups.name = "A", to: groups.name = "A", strength: 0 },
{ from: groups.name = "A", to: groups.name = "B", strength: 5 },
{ from: groups.name = "A", to: groups.name = "C", strength: 5 },
{ from: groups.name = "A", to: groups.name = "D", strength: 5 },
{ from: groups.name = "B", to: groups.name = "A", strength: 5 },
{ from: groups.name = "B", to: groups.name = "B", strength: 0 },
{ from: groups.name = "B", to: groups.name = "C", strength: 5 },
{ from: groups.name = "B", to: groups.name = "D", strength: 5 },
{ from: groups.name = "C", to: groups.name = "A", strength: 5 },
{ from: groups.name = "C", to: groups.name = "B", strength: 5 },
{ from: groups.name = "C", to: groups.name = "C", strength: 0 },
{ from: groups.name = "C", to: groups.name = "D", strength: 5 },
{ from: groups.name = "D", to: groups.name = "D", strength: 0 },
{ from: groups.name = "D", to: groups.name = "A", strength: 5 },
{ from: groups.name = "D", to: groups.name = "B", strength: 5 },
{ from: groups.name = "D", to: groups.name = "C", strength: 5 },
]
let matrix = []
function getMatrix(paths, groups) {
matrix = []
var mapPaths = paths.map(item => {
const container = {}
container.from = groups.findIndex(ele => ele.name == item.from)
container.to = groups.findIndex(ele => ele.name == item.to)
container.strength = item.strength
return container
})
mapPaths.forEach(function (item) {
// initialize sub-arra if not yet exists
if (!matrix[item.to]) {
matrix[item.to] = []
}
matrix[item.to][item.from] = item.strength
})
return matrix
}
////////////////////////////////////////////////////////////
///////////////////// Set-Up Visualization /////////////////
////////////////////////////////////////////////////////////
const vw = window.innerWidth
const vh = window.innerHeight
const innerRadius = Math.min(vw, vh) * 0.3;
const outerRadius = innerRadius * 1.1;
const duration = 1000;
const svg = d3.select("#chart").append("svg")
.attr("width", vw)
.attr("height", vh)
.attr("overflow", "unset")
const wrapper = svg.append("g")
.attr("id", "wrapper")
.attr("transform", "translate(" vw / 2 "," vh / 2 ")")
const ribbonsG = wrapper.append("g").attr("id", "ribbons")
const arcsG = wrapper.append("g").attr("id", "arcs")
const labelsG = wrapper.append("g").attr("id", "labels")
var chordGenerator = d3.chord()
.padAngle(0.10)
.sortSubgroups(d3.ascending)
.sortChords(d3.descending)
var arc = d3.arc()
.innerRadius(innerRadius * 1.01)
.outerRadius(outerRadius)
var ribbon = d3.ribbon()
.radius(innerRadius);
window.update = update;
update(paths, groups)
function update(thisPaths, thisGroups) {
const chords = chordGenerator(getMatrix(thisPaths, thisGroups))
// ribbons
const ribbonsUpdate = ribbonsG
.selectAll("path.ribbon")
.data(chords)
const ribbonsEnter = ribbonsUpdate
.enter()
.append("path")
ribbonsUpdate
.merge(ribbonsEnter)
.transition(duration)
.attr("opacity", 0)
.attr("class", "ribbon")
.transition()
.duration(duration)
.attr("d", ribbon)
.style("fill", function (d) { return thisGroups[d.target.index].color; })
.attr('opacity', 1)
ribbonsUpdate
.exit()
.transition()
.duration(duration)
.attr("opacity", 0)
.remove();
// arcs
const arcsUpdate = arcsG
.selectAll("path.arc")
.data(chords.groups)
const arcsEnter = arcsUpdate
.enter()
.append("path")
arcsUpdate
.merge(arcsEnter)
.transition(duration)
.attr("opacity", 0)
.attr("class", "arc")
.transition()
.duration(duration)
.attr("d", arc)
.style("fill", function (d) { return thisGroups[d.index].color; })
.attr('opacity', 1)
arcsUpdate
.exit()
.transition()
.duration(duration)
.attr("opacity", 0)
.remove();
// adding labels
const labelsUpdate = labelsG
.selectAll("text.titles")
.data(chords.groups)
const labelsEnter = labelsUpdate
.enter()
.append("text")
labelsUpdate
.merge(labelsEnter)
.attr("class", "titles")
.attr("opacity", 0)
.transition()
.duration(duration)
.each(function(d){ d.angle = (d.startAngle d.endAngle) / 2; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" (d.angle * 180 / Math.PI - 90) ")"
"translate(" (outerRadius 10) ")"
(d.angle > Math.PI ? "rotate(180)" : "");
})
.text(function(d,i){ return thisGroups[i].name; })
.attr("opacity", 1)
labelsUpdate
.exit()
.remove()
}
body {
font-size: 16px;
font-family: 'Oswald', sans-serif;
background-color: #ECF0F3;
cursor: default;
overflow: hidden;
}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>D3v7</title>
<!-- D3.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js" charset="utf-8"></script>
<!-- fontawesome stylesheet https://fontawesome.com/ -->
<script src="https://kit.fontawesome.com/98a5e27706.js" crossorigin="anonymous"></script>
</head>
<body>
<div id="chart"></div>
</body>
</html>
CodePudding user response:
The issue was, that there were 3 distinct groupings for arcs, ribbons and labels. To group the pieces of one section logically together one can create a single grouping for each section of the chart and then append an arc and a label to each section.
Afterwards one can use select
to make changes to the path and text element inside the g.group.
The ribbons are not a owned by any section, so they can not be easily assigned and should stay separately.
Please have a look at https://bost.ocks.org/mike/selection/ to get a deeper understanding of selections and how to manipulate them.
Here is a working solution of the expected behavior:
////////////////////////////////////////////////////////////
////////////////////// Set-Up Data /////////////////////////
////////////////////////////////////////////////////////////
let groups = [
{ name: "A", color: "blue" },
{ name: "B", color: "red" },
{ name: "C", color: "green" },
{ name: "D", color: "grey" },
]
let paths = [
{ from: groups.name = "A", to: groups.name = "A", strength: 0 },
{ from: groups.name = "A", to: groups.name = "B", strength: 5 },
{ from: groups.name = "A", to: groups.name = "C", strength: 5 },
{ from: groups.name = "A", to: groups.name = "D", strength: 5 },
{ from: groups.name = "B", to: groups.name = "A", strength: 5 },
{ from: groups.name = "B", to: groups.name = "B", strength: 0 },
{ from: groups.name = "B", to: groups.name = "C", strength: 5 },
{ from: groups.name = "B", to: groups.name = "D", strength: 5 },
{ from: groups.name = "C", to: groups.name = "A", strength: 5 },
{ from: groups.name = "C", to: groups.name = "B", strength: 5 },
{ from: groups.name = "C", to: groups.name = "C", strength: 0 },
{ from: groups.name = "C", to: groups.name = "D", strength: 5 },
{ from: groups.name = "D", to: groups.name = "D", strength: 0 },
{ from: groups.name = "D", to: groups.name = "A", strength: 5 },
{ from: groups.name = "D", to: groups.name = "B", strength: 5 },
{ from: groups.name = "D", to: groups.name = "C", strength: 5 },
]
let matrix = []
function getMatrix(paths, groups) {
matrix = []
var mapPaths = paths.map(item => {
const container = {}
container.from = groups.findIndex(ele => ele.name == item.from)
container.to = groups.findIndex(ele => ele.name == item.to)
container.strength = item.strength
return container
})
mapPaths.forEach(function (item) {
// initialize sub-arra if not yet exists
if (!matrix[item.to]) {
matrix[item.to] = []
}
matrix[item.to][item.from] = item.strength
})
return matrix
}
////////////////////////////////////////////////////////////
///////////////////// Set-Up Visualization /////////////////
////////////////////////////////////////////////////////////
const vw = window.innerWidth
const vh = window.innerHeight
const innerRadius = Math.min(vw, vh) * 0.3;
const outerRadius = innerRadius * 1.1;
const duration = 1000;
const svg = d3.select("#chart").append("svg")
.attr("width", vw)
.attr("height", vh)
.attr("overflow", "unset")
const wrapper = svg.append("g")
.attr("id", "wrapper")
.attr("transform", "translate(" vw / 2 "," vh / 2 ")")
const ribbonsG = wrapper.append("g").attr("id", "ribbons")
const groupsG = wrapper.append("g").attr("id", "groups")
var chordGenerator = d3.chord()
.padAngle(0.10)
.sortSubgroups(d3.ascending)
.sortChords(d3.descending)
var arc = d3.arc()
.innerRadius(innerRadius * 1.01)
.outerRadius(outerRadius)
var ribbon = d3.ribbon()
.radius(innerRadius);
update(paths, groups)
function update(thisPaths, thisGroups) {
const chords = chordGenerator(getMatrix(thisPaths, thisGroups))
// ribbons
const ribbonsUpdate = ribbonsG
.selectAll("path.ribbon")
.data(chords)
const ribbonsEnter = ribbonsUpdate
.enter()
.append("path")
ribbonsUpdate
.merge(ribbonsEnter)
.transition(duration)
.attr("opacity", 0)
.attr("class", "ribbon")
.transition()
.duration(duration)
.attr("d", ribbon)
.style("fill", function (d) { return thisGroups[d.target.index].color; })
.attr('opacity', 1)
ribbonsUpdate
.exit()
.transition()
.duration(duration)
.attr("opacity", 0)
.remove();
let itemsUpdate = groupsG.selectAll("g.group")
.data(chords.groups);
const itemsEnter = itemsUpdate.enter().append("g").attr("class", "group");
itemsEnter.append('path')
.attr("class", "arc");
itemsEnter.append('text')
.attr("class", "title");
itemsUpdate = itemsUpdate.merge(itemsEnter);
itemsUpdate.select("path.arc")
.transition(duration)
.attr("opacity", 0)
.transition()
.duration(duration)
.attr("d", arc)
.style("fill", function (d) { return thisGroups[d.index].color; })
.attr('opacity', 1)
itemsUpdate.exit()
.select("path.arc")
.transition()
.duration(duration)
.attr("opacity", 0)
.remove();
itemsUpdate.select("text.title")
.attr("opacity", 0)
.transition()
.duration(duration)
.each(function(d){ d.angle = (d.startAngle d.endAngle) / 2; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" (d.angle * 180 / Math.PI - 90) ")"
"translate(" (outerRadius 10) ")"
(d.angle > Math.PI ? "rotate(180)" : "");
})
.text(function(d,i){ return thisGroups[i].name; })
.attr("opacity", 1)
itemsUpdate.exit()
.select("text.title")
.remove()
}
body {
font-size: 16px;
font-family: 'Oswald', sans-serif;
background-color: #ECF0F3;
cursor: default;
overflow: hidden;
}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>D3v7</title>
<!-- D3.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js" charset="utf-8"></script>
<!-- fontawesome stylesheet https://fontawesome.com/ -->
<script src="https://kit.fontawesome.com/98a5e27706.js" crossorigin="anonymous"></script>
</head>
<body>
<div id="chart"></div>
</body>
</html>