I am trying to make a horizontal stacked bar chart, starting with this code snippet, updating to d3 v7. Instead of getting a neatly stacked bar chart, each subsequent bar in a stack is getting offset vertically down from where it should be. When I inspect the yScale value, I get the expected value, so I’m extra-confused about this behavior.
I’d include just the relevant piece of the puzzle, but I honestly don’t know where my problem is — am I appending to the wrong ‘g’ element? Using enter() on the wrong piece of data?
<script src="https://d3js.org/d3.v7.min.js"></script>
<body>
<div id="bar_chart">
<script>
var data = [{
dep_time: "5:30",
risk: 100,
details: [{
time: 19,
source: 'Drive'
},
{
time: 10,
source: 'Margin'
},
{
time: 42,
source: 'Full'
},
{
time: 35,
source: 'Crossing'
},
{
time: 23,
source: 'Drive'
}
]
},
{
dep_time: "6:20",
risk: 80,
details: [{
time: 25,
source: 'Drive'
},
{
time: 1,
source: 'Margin'
},
{
time: 38,
source: 'Full'
},
{
time: 35,
source: 'Crossing'
},
{
time: 25,
source: 'Drive'
}
]
},
{
dep_time: "7:10",
risk: 5,
details: [{
time: 8,
source: 'Drive'
},
{
time: 28,
source: 'Margin'
},
{
time: 38,
source: 'Full'
},
{
time: 35,
source: 'Crossing'
},
{
time: 18,
source: 'Drive'
}
]
}
];
var chartContainer = '.chart-container';
var units = [];
var xMax = 0;
data.forEach(function(s) {
var total = 0;
s.details.forEach(function(s) {
s["x0"] = total; //Abs left
s["x"] = s.time; //Width
s["x1"] = total + s.time; //Abs right
total = total + s.time;
if (total > xMax) xMax = total;
});
s["y"] = s.dep_time;
units.push(s.dep_time);
});
//Need it to look like: newdata = [(Drive) [19, 25, 32.] Margin [0, 1, 28]. Full [42, 38, 38]. Crossing [35, 35, 35]. Drive [23, 25, 18].]
//So it's a row in the array for each column of data.
//re-arrange the data so it makes more sense to d3 (and less sense to any sane human)
var newdata = [];
for (var i = 0; i < data[0].details.length; i++) {
var row = [];
data.forEach(function(s) {
row.push({
x: s.details[i].x,
y: s.dep_time,
x0: s.details[i].x0
});
});
newdata.push(row);
}
console.log("newdata");
console.log(newdata);
var margins = {
left: 50,
bottom: 50,
top: 25,
right: 25
};
var sizes = {
width: 500,
height: 150
};
var width = sizes.width - margins.left - margins.right;
var height = sizes.height - margins.bottom - margins.top;
var svg = d3.select("#bar_chart")
.append('svg')
.attr('width', width + margins.left + margins.right)
.attr('height', height + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ', ' + margins.top + ")");
var yScale = d3.scaleBand()
.domain(units)
.rangeRound([0, height]);
var yAxis = d3.axisLeft(yScale);
var yAxisG = svg.append("g")
.attr("transform", "translate(0,0)")
.attr("id", "yaxis")
.call(yAxis);
const xScale = d3.scaleLinear()
.domain([0, xMax])
.range([0, width]);
var xAxis = d3.axisBottom(xScale);
var xAxisG = svg.append("g")
.attr("transform", "translate(0, " + height + ")")
.attr("id", "xaxis")
.call(xAxis
.ticks(8));
var bar_colors = ['red', 'purple', 'green', 'lightblue', 'yellow'];
var colors = function(i) {
return bar_colors[i];
}
var groups = svg.selectAll('g')
.data(newdata)
//.exit()
.append('g')
.style('fill', function(d, i) {
console.log("d");
console.log(d);
//console.log("i"); console.log(i);
return colors(i);
});
console.log(groups);
groups.selectAll('rect')
.data(function(d) {
//console.log(d);
return d;
})
.enter()
.append('rect')
.attr('x', function(d) {
//console.log("x0"); console.log(d.x0);
return xScale(d.x0);
})
.attr('y', function(d, i) {
//console.log(yScale(d.y));
//console.log(i);
return yScale(d.y);
})
.attr('height', 10) //function (d) {return yScale.rangeBand();})
.attr('width', function(d) {
return xScale(d.x);
});
</script>
</div>
</body>
And, for whatever reason, it doesn’t seem to work as a StackOverflow code snippet.