Stacked bar chart 24hr # 15 minute data, to only display 2hr ticks - javascript
I have been searching for a while about how to handle the X axis in a stacked bar chart (since dataset is a little different from a single bar chart).
Basically, I have data for a 24hr period in 15 minute intervals. However, I only want to display the x-axis in 2hr ticks.
Existing Fiddle: [https://jsfiddle.net/lucksp/crwb4v5u/][1]
It currently prints all the intervals.
I have tried various scale options with time but something doesn't translate with the way I have this setup.
var xScale = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([0, width - margin.left]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.tickSize(0)
.ticks(12)
.tickFormat(function(d) {
return d;
});
var rect = groups.selectAll('rect')
.data(function(d) {
return d;
})
.enter()
.append('rect')
.attr('class', function(d, i) {
return 'stacks ' + d.type;
})
.classed('stacks', true)
.attr('id', function(d, i) {
return d.type + '_' + i;
})
.attr('x', function(d) {
return xScale(d.x);
})
.attr('y', function(d) {
return yScale(d.y0 + d.y);
})
.attr('height', function(d) {
return yScale(d.y0) - yScale(d.y0 + d.y);
})
.attr('width', xScale.rangeBand());
[1]: https://jsfiddle.net/lucksp/crwb4v5u/
I know it's user error, but after looking at this for the last 2 days, I am resorting to asking this question now. Thanks!
You are currently trying to use .ticks which will only work if the scale you're using has an inbuilt ticks function. Your ordinal scale in this case does not. It will by default use all values in the domain.
To go around it, we can manually set the ticks using xAxis.tickValues(["custom tick values that match domain vals"]). Check the snippet below.
var data = [{"hour":"0:00","inProgress":3,"inQueue":0},{"hour":"0:15","inProgress":5,"inQueue":3},{"hour":"0:30","inProgress":1,"inQueue":1},{"hour":"0:45","inProgress":1,"inQueue":0},{"hour":"1:00","inProgress":2,"inQueue":0},{"hour":"1:15","inProgress":8,"inQueue":2},{"hour":"1:30","inProgress":5,"inQueue":3},{"hour":"1:45","inProgress":5,"inQueue":1},{"hour":"2:00","inProgress":6,"inQueue":0},{"hour":"2:15","inProgress":6,"inQueue":0},{"hour":"2:30","inProgress":7,"inQueue":0},{"hour":"2:45","inProgress":7,"inQueue":0},{"hour":"3:00","inProgress":8,"inQueue":0},{"hour":"3:15","inProgress":8,"inQueue":0},{"hour":"3:30","inProgress":9,"inQueue":1},{"hour":"3:45","inProgress":9,"inQueue":4},{"hour":"4:00","inProgress":10,"inQueue":6},{"hour":"4:15","inProgress":10,"inQueue":2},{"hour":"4:30","inProgress":10,"inQueue":1},{"hour":"4:45","inProgress":11,"inQueue":0},{"hour":"5:00","inProgress":11,"inQueue":0},{"hour":"5:15","inProgress":12,"inQueue":0},{"hour":"5:30","inProgress":12,"inQueue":0},{"hour":"5:45","inProgress":13,"inQueue":0},{"hour":"6:00","inProgress":13,"inQueue":0},{"hour":"6:15","inProgress":14,"inQueue":0},{"hour":"6:30","inProgress":14,"inQueue":0},{"hour":"6:45","inProgress":15,"inQueue":0},{"hour":"7:00","inProgress":15,"inQueue":3},{"hour":"7:15","inProgress":15,"inQueue":1},{"hour":"7:30","inProgress":16,"inQueue":0},{"hour":"7:45","inProgress":16,"inQueue":0},{"hour":"8:00","inProgress":17,"inQueue":2},{"hour":"8:15","inProgress":17,"inQueue":3},{"hour":"8:30","inProgress":18,"inQueue":1},{"hour":"8:45","inProgress":18,"inQueue":0},{"hour":"9:00","inProgress":19,"inQueue":0},{"hour":"9:15","inProgress":19,"inQueue":0},{"hour":"9:30","inProgress":20,"inQueue":0},{"hour":"9:45","inProgress":20,"inQueue":0},{"hour":"10:00","inProgress":20,"inQueue":0},{"hour":"10:15","inProgress":21,"inQueue":1},{"hour":"10:30","inProgress":21,"inQueue":4},{"hour":"10:45","inProgress":22,"inQueue":6},{"hour":"11:00","inProgress":22,"inQueue":2},{"hour":"11:15","inProgress":23,"inQueue":1},{"hour":"11:30","inProgress":23,"inQueue":0},{"hour":"11:45","inProgress":3,"inQueue":0},{"hour":"12:00","inProgress":5,"inQueue":0},{"hour":"12:15","inProgress":1,"inQueue":0},{"hour":"12:30","inProgress":1,"inQueue":0},{"hour":"12:45","inProgress":2,"inQueue":0},{"hour":"13:00","inProgress":8,"inQueue":0},{"hour":"13:15","inProgress":5,"inQueue":0},{"hour":"13:30","inProgress":5,"inQueue":0},{"hour":"13:45","inProgress":6,"inQueue":3},{"hour":"14:00","inProgress":6,"inQueue":1},{"hour":"14:15","inProgress":7,"inQueue":0},{"hour":"14:30","inProgress":7,"inQueue":0},{"hour":"14:45","inProgress":8,"inQueue":2},{"hour":"15:00","inProgress":8,"inQueue":3},{"hour":"15:15","inProgress":9,"inQueue":1},{"hour":"15:30","inProgress":9,"inQueue":0},{"hour":"15:45","inProgress":10,"inQueue":0},{"hour":"16:00","inProgress":10,"inQueue":0},{"hour":"16:15","inProgress":10,"inQueue":0},{"hour":"16:30","inProgress":11,"inQueue":0},{"hour":"16:45","inProgress":11,"inQueue":0},{"hour":"17:00","inProgress":12,"inQueue":1},{"hour":"17:15","inProgress":12,"inQueue":4},{"hour":"17:30","inProgress":13,"inQueue":6},{"hour":"17:45","inProgress":13,"inQueue":2},{"hour":"18:00","inProgress":14,"inQueue":1},{"hour":"18:15","inProgress":14,"inQueue":0},{"hour":"18:30","inProgress":15,"inQueue":0},{"hour":"18:45","inProgress":15,"inQueue":0},{"hour":"19:00","inProgress":15,"inQueue":0},{"hour":"19:15","inProgress":16,"inQueue":0},{"hour":"19:30","inProgress":16,"inQueue":0},{"hour":"19:45","inProgress":17,"inQueue":0},{"hour":"20:00","inProgress":17,"inQueue":0},{"hour":"20:15","inProgress":18,"inQueue":0},{"hour":"20:30","inProgress":18,"inQueue":3},{"hour":"20:45","inProgress":19,"inQueue":1},{"hour":"21:00","inProgress":19,"inQueue":0},{"hour":"21:15","inProgress":20,"inQueue":0},{"hour":"21:30","inProgress":20,"inQueue":2},{"hour":"21:45","inProgress":20,"inQueue":3},{"hour":"22:00","inProgress":21,"inQueue":1},{"hour":"22:15","inProgress":21,"inQueue":0},{"hour":"22:30","inProgress":22,"inQueue":0},{"hour":"22:45","inProgress":22,"inQueue":0},{"hour":"23:00","inProgress":23,"inQueue":0},{"hour":"23:15","inProgress":23,"inQueue":0},{"hour":"23:30","inProgress":1,"inQueue":0},{"hour":"23:45","inProgress":2,"inQueue":1}];
var margin = {top: 20, right: 50, bottom: 30, left: 20},
width = 500,
height = 300;
// Transpose the data into layers
var dataset = d3.layout.stack()(['inProgress', 'inQueue'].map(function(types) {
return data.map(function(d) {
return {
x: d.hour,
y: +d[types],
type: types
};
});
}));
var svg = d3.select('svg'),
margin = {top: 40, right: 10, bottom: 20, left: 10},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Set x, y and colors
var xScale = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([0, width - margin.left]);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})])
.range([height, 0]);
var colors = ['#56a8f8', '#c34434'];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.ticks(5)
.tickSize(0)
.tickFormat(function(d) {
return d;
});
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.tickSize(0)
.ticks(12) // this
.tickFormat(function(d) {
return d; // and this will not work with an ordinal scale
});
xAxis.tickValues(["0:00", "2:00", "4:00", "6:00", "8:00", "10:00", "12:00", "14:00", "16:00", "18:00", "20:00", "22:00"]);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
// Create groups for each series, rects for each segment
var groups = svg.selectAll('g.bar-stacks')
.data(dataset)
.enter().append('g')
.attr('class', function(d, i) {
return 'bar-stacks ' + d[i].type;
})
.classed('bar-stacks', true)
.style('fill', function(d, i) {
return colors[i];
});
var rect = groups.selectAll('rect')
.data(function(d) {
return d;
})
.enter()
.append('rect')
.attr('class', function(d, i) {
return 'stacks ' + d.type;
})
.classed('stacks', true)
.attr('id', function(d, i) {
return d.type + '_' + i;
})
.attr('x', function(d) {
return xScale(d.x);
})
.attr('y', function(d) {
return yScale(d.y0 + d.y);
})
.attr('height', function(d) {
return yScale(d.y0) - yScale(d.y0 + d.y);
})
.attr('width', xScale.rangeBand());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div>
<svg width="600" height="300"></svg>
</div>
Related
d3.js-adding different colors to one bar in stacked bar chart
I have created the stacked bar chart by using d3.js.In that I would like to display a single bar with different colors to highlight the data for particular x axis value like below. The script i have used to plot stacked chart is below: // Set the dimensions of the canvas / graph var svg = d3.select("#svgID"), margin = {top: 80, right: 140, bottom: 100, left: 100}, width = +svg.attr("width") - margin.left - margin.right, height = +svg.attr("height") - margin.top - margin.bottom, g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var padding = -100; //set the ranges var x = d3.scaleBand() .rangeRound([0, width]) .paddingInner(0.20) .align(0.1); var y = d3.scaleLinear() .rangeRound([height, 0]); var z = d3.scaleOrdinal() .range(["#008000", "#C00000", "#404040", "#4d4d4d"]); var data = $("#svgID").data("values"); var keys = ["Pass", "Fail", "Average", "Worst"]; var legendKeysbar = ["Pass", "Fail", "Average", "Worst"]; var legendColorsbar = d3.scaleOrdinal() .range(["#008000", "#C00000", "#404040", "#4d4d4d"]); // Scale the range of the data x.domain(data.map(function (d) { return d.year; })); y.domain([0, d3.max(data, function (d) { return d.total; })]).nice(); z.domain(keys); // add the Y gridlines g.append("g").selectAll(".hline").data(y.ticks(10)).enter() .append("svg:line") .attr("x1", 0) .attr("y1", function(d){ return y(d);}) .attr("x2", width) .attr("y2", function(d){ return y(d);}) .style("stroke", "white") .style("stroke-width", 1); // append the rectangles for the bar chart g.append("g") .selectAll("g") .data(d3.stack().keys(keys)(data)) .enter().append("g") .attr("fill", function (d) { return z(d.key); }) .selectAll("rect") .data(function (d) { return d; }) .enter().append("rect") .attr("x", function (d) { return x(d.data.year); }) .attr("y", function (d) { return y(d[1]); }) .attr("height", function (d) { return y(d[0]) - y(d[1]); }) Can you help me to update colors for single bar? is that possible by d3.js
Create a second color scale, then in the method where you assign color, perform a check to determine which color scale to use, e.g.,: var z2 = d3.scaleOrdinal().range(<your color array here>) ... .attr("fill", function (d) { return d.data.year === "Dec" ? z2(d.key) : z(d.key); })
is it possible to create a dynamic grid line chart in d3?
I am developing a scatterplot in d3 where you can alter both x axis and y axis using a dropdown menu. I was able to draw the grid lines but my problem is redrawing them when a new value is picked for either x axis or y axis. I hope someone could advise me what I should do to make this happen. Here is my code up until now(js): d3.csv('data.csv',function (data) { // CSV section var body = d3.select('body') var selectData = [ { "text" : "Trust words" }, { "text" : "Surprise words" }, { "text" : "Sadness words" }, { "text" : "Positive words"}, { "text" : "Negative words"}, { "text" : "Fear words"}, { "text" : "Disgust words"}, { "text" : "Anticipation words"}, { "text" : "Anger words"}, ] // Select X-axis Variable var span = body.append('span') .text('Select an Emotion word for the Horizontal scale: ') var xInput = body.append('select') .attr('id','xSelect') .on('change',xChange) .selectAll('option') .data(selectData) .enter() .append('option') .attr('value', function (d) { return d.text }) .text(function (d) { return d.text ;}) body.append('br') body.append('br') // Select Y-axis Variable var span = body.append('span') .text('Select an Emotion word for the Vertical scale: ') var yInput = body.append('select') .attr('id','ySelect') .on('change',yChange) .selectAll('option') .data(selectData) .enter() .append('option') .attr('value', function (d) { return d.text }) .text(function (d) { return d.text ;}) body.append('br') // Variables var body = d3.select('body') var margin = { top: 50, right: 50, bottom: 50, left: 50 } var h = 700 - margin.top - margin.bottom var w = 1350 - margin.left - margin.right var rscale = d3.scale.linear() // Scales var cValue = function(d) { if (parseFloat(d['Emotions words']) >=0 && parseFloat(d['Emotions words']) <= 200000) return 'Emotion Words NO: 0-200,000 inc' else if(parseFloat(d['Emotions words']) >200000 && parseFloat(d['Emotions words']) <=350000) return 'Emotion Words NO: 200,001-350,000 inc' else return 'Emotion words NO: >350,000'}, color = d3.scale.category10(); var xScale = d3.scale.linear() .domain([ d3.min([0,d3.min(data,function (d) { return parseFloat(d['Trust words']) })]), d3.max([0,d3.max(data,function (d) { return parseFloat(d['Trust words']) })]) ]) .range([0,w]) var yScale = d3.scale.linear() .domain([ d3.min([0,d3.min(data,function (d) { return parseFloat(d['Trust words']) })]), d3.max([0,d3.max(data,function (d) { return parseFloat(d['Trust words']) })]) ]) .range([h,0]) // SVG var svg = body.append('svg') .attr('height',h + margin.top + margin.bottom) .attr('width',w + margin.left + margin.right) .append('g') .attr('transform','translate(' + margin.left + ',' + margin.top + ')') // X-axis var xAxis = d3.svg.axis() .scale(xScale) .orient('bottom') .ticks(5) // Y-axis var yAxis = d3.svg.axis() .scale(yScale) .orient('left') .ticks(5) function make_x_axis() { return d3.svg.axis() .scale(xScale) .orient("bottom") .ticks(5) } function make_y_axis() { return d3.svg.axis() .scale(yScale) .orient("left") .ticks(5) } // Circles var circles = svg.selectAll('circle') .data(data) .enter() .append('circle') .attr('cx',function (d) { return xScale(d['Trust words']) }) .attr('cy',function (d) { return yScale(d['Trust words']) }) .attr('r',function (d) { return rscale(d['Average_movie_rating'])*2;}) .attr('stroke','black') .attr('stroke-width',1) .attr('fill',function (d) { return color(cValue(d)); }) .on('mouseover', function () { d3.select(this) .transition() .duration(500) .attr('r',20) .attr('stroke-width',3) }) .on('mouseout', function () { d3.select(this) .transition() .duration(500) .attr('r',10) .attr('stroke-width',1) }) .append('title') // Tooltip .text(function (d) { return 'Actor Name: ' + d['Actor_ Name'] + '\nTrust words: ' + d['Trust words'] + '\nSurprise words: ' + d['Surprise words']+ '\nSadness words: ' + d['Sadness words'] + '\nPositive words: ' + d['Positive words'] + '\nNegative words: ' + d['Negative words'] + '\nFear words: ' + d['Fear words'] + '\nDisgust words: ' + d['Disgust words'] + '\nAnticipation words: ' + d['Anticipation words'] + '\nAnger words: ' + d['Anger words'] + '\nAverage ranking: '+ d['Average_movie_rating']}) // X-axis svg.append('g') .attr('class','axis') .attr('id','xAxis') .attr('transform', 'translate(0,' + h + ')') .call(xAxis) .append('text') // X-axis Label .attr('id','xAxisLabel') .attr('y',-10) .attr('x',w) .attr('dy','.71em') .style('text-anchor','end') .text('Trust words') // Y-axis svg.append('g') .attr('class','axis') .attr('id','yAxis') .call(yAxis) .append('text') // y-axis Label .attr('id', 'yAxisLabel') .attr('transform','rotate(-90)') .attr('x',0) .attr('y',5) .attr('dy','.71em') .style('text-anchor','end') .text('Trust words') svg.append('g') .attr("class", "grid") .attr("transform", "translate(0," + h + ")") .call(make_x_axis() .tickSize(-h, 0, 0) .tickFormat("") ) svg.append('g') .attr("class", "grid") .call(make_y_axis() .tickSize(-w, 0, 0) .tickFormat("") ) function yChange() { var value = this.value // get the new y value yScale // change the yScale .domain([ d3.min([0,d3.min(data,function (d) { return parseFloat(d[value]) })]), d3.max([0,d3.max(data,function (d) { return parseFloat(d[value]) })]) ]) yAxis.scale(yScale) // change the yScale d3.select('#yAxis') // redraw the yAxis .transition().duration(1000) .call(yAxis) d3.select('#yAxisLabel') // change the yAxisLabel .text(value) d3.selectAll('circle') // move the circles .transition().duration(1000) .delay(function (d,i) { return i*100}) .attr('cy',function (d) { return yScale(d[value]) }) } function xChange() { var value = this.value // get the new x value xScale // change the xScale .domain([ d3.min([0,d3.min(data,function (d) { return parseFloat(d[value]) })]), d3.max([0,d3.max(data,function (d) { return parseFloat(d[value]) })]) ]) xAxis.scale(xScale) // change the xScale d3.select('#xAxis') // redraw the xAxis .transition().duration(1000) .call(xAxis) d3.select('#xAxisLabel') // change the xAxisLabel .transition().duration(1000) .text(value) d3.selectAll('circle') // move the circles .transition().duration(1000) .delay(function (d,i) { return i*100}) .attr('cx',function (d) { return xScale(d[value]) }) } var legend = svg.selectAll(".legend") .data(color.domain()) .enter().append("g") .attr("class", "legend") .attr("transform", function(d, i) { return "translate(0," + i * 25 + ")"; }); // draw legend colored rectangles legend.append("rect") .attr("x", w + 25) .attr("y", 490) .attr("width", 18) .attr("height", 18) .style("fill", color); // draw legend text legend.append("text") .attr("x", w - 24) .attr("y", 500) .attr("dy", ".35em") .style("text-anchor", "end") .text(function(d) { return d;}) .text(function(d) { return d;}) }) Thanks
I'm sorry, I wanted to write a comment, but I don't have enough reputation so I have to write this as an answer. Is there any chance you can provide a mini dataset so that I can run this code in my machine? It's easier to understand how the code is supposed to work if I have a dataset to run it with. Also, what do you mean by gridlines? If you mean the ticks, then I think those won't change no matter what scale you use. You set it to 5 and so I think there will always be 5 evenly spaced tick marks.
border for d3 stack bar chart on selection
Trying to implement border for selected bar in d3 stack bar chart. Here the first bar's top border goes behind second bar a little bit. How to avoid this? var svg, height, width, margin, parentWidth, parentHeight; // container size parentWidth = 700; parentHeight = 500; margin = {top: 50, right: 20, bottom: 35, left: 30}; width = parentWidth - margin.left - margin.right; height = parentHeight - margin.top - margin.bottom; var selectedSection = window.sessionStorage.getItem('selectedSection'); // data var dataset = [{"label":"DEC","Set Up":{"count":12,"id":1,"label":"Set Up","year":"2016","graphType":"setup"},"Not Set Up":{"count":12,"id":0,"label":"Not Set Up","year":"2016","graphType":"setup"}},{"label":"JAN","Set Up":{"count":6,"id":1,"label":"Set Up","year":"2017","graphType":"setup"},"Not Set Up":{"count":21,"id":0,"label":"Not Set Up","year":"2017","graphType":"setup"}},{"label":"FEB","Set Up":{"count":1,"id":1,"label":"Set Up","year":"2017","graphType":"setup"},"Not Set Up":{"count":2,"id":0,"label":"Not Set Up","year":"2017","graphType":"setup"}},{"label":"MAR","Set Up":{"count":0,"id":1,"label":"Set Up","year":"2017","graphType":"setup"},"Not Set Up":{"count":0,"id":0,"label":"Not Set Up","year":"2017","graphType":"setup"}},{"label":"APR","Set Up":{"count":0,"id":1,"label":"Set Up","year":"2017","graphType":"setup"},"Not Set Up":{"count":0,"id":0,"label":"Not Set Up","year":"2017","graphType":"setup"}}]; // x cord var x = d3.scale.ordinal() .rangeRoundBands([0, width], 0.2); // color helper var colorRange = d3.scale.category20(); var color = d3.scale.ordinal() .range(colorRange.range()); // x axis var xAxis = d3.svg.axis() .scale(x) .orient('bottom'); var colors = ['#50BEE9', '#30738C']; // Set SVG svg = d3.select('#chart') .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom ) .attr('class', 'setup') .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); color.domain(d3.keys(dataset[0]).filter(function(key) { return key !== 'label'; })); dataset.forEach(function(d) { var y0 = 0; d.values = color.domain().map(function(name) { return { name: name, y0: y0, y1: y0 += +d[name].count, patientStatus:d[name].id, graphType:d[name].graphType, fromDate:{ month:d.label, year:d[name].year }, toDate:{ month:d.label, year:d[name].year } }; }); d.total = d.values[d.values.length - 1].y1; }); var y = d3.scale.linear() .domain([0, d3.max(dataset, function(d) { return d.total; })]) .range([height, 0]); var ticks = y.ticks(), lastTick = ticks[ticks.length-1]; var newLastTick = lastTick + (ticks[1] - ticks[0]); if (lastTick<y.domain()[1]){ ticks.push(lastTick + (ticks[1] - ticks[0])); } // adjust domain for further value y.domain([y.domain()[0], newLastTick]); // y axis var yAxis = d3.svg.axis() .scale(y) .orient('left') .tickSize(-width, 0, 0) .tickFormat(d3.format('d')) .tickValues(ticks); x.domain(dataset.map(function(d) { return d.label; })); y.domain([0, d3.max(dataset, function(d) { return d.total; })]); svg.append('g') .attr('class', 'x axis') .attr('transform', 'translate(0,' + height + ')') .call(xAxis); svg.append('g') .attr('class', 'y axis') .call(yAxis); var bar = svg.selectAll('.label') .data(dataset) .enter().append('g') .attr('class', 'g') .attr('id', function(d, i) { return i; }) .attr('transform', function(d) { return 'translate(' + x(d.label) + ',0)'; }); var barEnter = bar.selectAll('rect') .data(function(d) { return d.values; }) .enter(); barEnter.append('rect') .attr('width', x.rangeBand()) .attr('y', function(d) { return y(d.y1); }) .attr('class', function(d, i){ return 'bar'; }) .attr('height', function(d) { return y(d.y0) - y(d.y1); }) .style('fill', function(d,i) { return colors[i]; }) .on('click', function(d, i) { d3.selectAll('.bar').classed('selected', false); d3.select(this) .classed('bar selected', true); }); barEnter.append('text') .text(function(d) { var calcH = y(d.y0) - y(d.y1); var inText = (d.y1-d.y0); if(calcH >= 20) { return inText; } else { return ''; } }) .attr('class','inner-text') .attr('y', function(d) { return y(d.y1)+(y(d.y0) - y(d.y1))/2 + 5; }) .attr('x', function(){ return (x.rangeBand()/2) - 10; }); svg .select('.y') .selectAll('.tick') .filter(function (d) { return d % 1 !== 0; }) .style('display','none'); svg .select('.y') .selectAll('.tick') .filter(function (d) { return d === 0; }) .select('text') .style('display','none'); JSFiddle JSFiddle with d3 v4
In a SVG, just like a real painter putting ink to a white canvas, the element that is painted last stays on top. Right now, the behaviour you're seeing is the expected one, because each stacked bar (rectangle) is in a different <g> element, and the groups, of course, have a given order in the SVG structure. The solution involves just one line: d3.select(this.parentNode).raise(); What this line does is selecting the group of the clicked rectangle and raising it (that is, moving it down in the DOM tree), so that group will be on top of all others. According to the API, raise(): Re-inserts each selected element, in order, as the last child of its parent. (emphasis mine) "Moving down", "be on top" and "be the last child" may be a bit confusing and seem contradictory, but here is the explanation. Given this SVG structure: <svg> <foo></foo> <bar></bar> <baz></baz> </svg> <baz>, being the last element, is the one painted last, and it is the element visually on the top in the SVG. So, raising an element means moving it down in the SVG tree structure, but moving it up visually speaking. Here is your updated fiddle: https://jsfiddle.net/86Lgaupt/ PS: I increased the stroke-width just to make visibly clear that the clicked rectangle is now on top.
Tag: <div id='stacked-bar'></div> Script: var initStackedBarChart = { draw: function(config) { me = this, domEle = config.element, stackKey = config.key, data = config.data, margin = {top: 20, right: 20, bottom: 30, left: 50}, parseDate = d3.timeParse("%m/%Y"), width = 550 - margin.left - margin.right, height = 400 - margin.top - margin.bottom, xScale = d3.scaleBand().range([0, width]).padding(0.1), yScale = d3.scaleLinear().range([height, 0]), color = d3.scaleOrdinal(d3.schemeCategory20), xAxis = d3.axisBottom(xScale).tickFormat(d3.timeFormat("%b")), yAxis = d3.axisLeft(yScale), svg = d3.select("#"+domEle).append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top+10 + margin.bottom+10) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var stack = d3.stack() .keys(stackKey) .order(d3.stackOrderNone) .offset(d3.stackOffsetNone); var layers= stack(data); data.sort(function(a, b) { return b.total - a.total; }); xScale.domain(data.map(function(d) { return parseDate(d.date); })); yScale.domain([0, d3.max(layers[layers.length - 1], function(d) { return d[0] + d[1]; }) ]).nice(); var layer = svg.selectAll(".layer") .data(layers) .enter().append("g") .attr("class", "layer") .style("fill", function(d, i) { return color(i); }); layer.selectAll("rect") .data(function(d) { return d; }) .enter().append("rect") .attr('class', 'bar') .attr("x", function(d) { return xScale(parseDate(d.data.date)); }) .attr("y", function(d) { return yScale(d[1]); }) .attr("height", function(d) { return yScale(d[0]) - yScale(d[1]) -1; }) .attr("width", xScale.bandwidth()) .on('click', function(d, i) { d3.selectAll('.bar').classed('selected', false); d3.select(this).classed('selected', true); }); svg.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + (height+5) + ")") .call(xAxis); svg.append("g") .attr("class", "axis axis--y") .attr("transform", "translate(0,0)") .call(yAxis); } } var data = [ {"date":"4/1854","total":45,"disease":12,"wounds":14,"other":25}, {"date":"5/1854","total":23,"disease":12,"wounds":0,"other":9}, {"date":"6/1854","total":38,"disease":11,"wounds":0,"other":6}, {"date":"7/1854","total":26,"disease":11,"wounds":8,"other":7} ]; var key = ["wounds", "other", "disease"]; initStackedBarChart.draw({ data: data, key: key, element: 'stacked-bar' }); Css: .axis text { font: 10px sans-serif; } .axis line, .axis path { fill: none; stroke: #000; shape-rendering: crispEdges; } .axis--x path { display: none; } .path-line { fill: none; stroke: yellow; stroke-width: 1.5px; } svg { background: #f0f0f0; } .selected{ stroke:#333; stroke-width:2; }
Changing the colors of each of the Stacked Bar chart with different Color in D3
This involved the assignment of different colors to each bars of the stacked bar charts as currently there is only single color in all the four bars and colors are changing in the stacked one but I am trying to assign different colors to all the four bars as well the stacked value. Here is the code var margin = { top: 20, right: 160, bottom: 35, left: 30 }; var width = 960 - margin.left - margin.right, height = 600 - margin.top - margin.bottom; var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); /* Data in strings like it would be if imported from a csv */ var data = [{ year: "A", redDelicious: "10", mcintosh: "5", oranges: "19" }, { year: "B", redDelicious: "12", mcintosh: "0", oranges: "15" }, { year: "C", redDelicious: "05", mcintosh: "0", oranges: "28" }, { year: "D", redDelicious: "14", mcintosh: "0", oranges: "12" }, ]; $("#btn").on("click", function(){ d3.selectAll("svg > g > g").remove(); data[1].mcintosh = (Number(data[1].mcintosh) + 1).toString(); console.log(1,data); update(); }); update(); function update(){ var orangeData = data.map(function(d) { return { year: d.year, oranges: +d.oranges } }); console.log(orangeData) // Transpose the data into layers var dataset = d3.layout.stack()(["redDelicious", "mcintosh"].map(function(skillset) { return data.map(function(d) { return { x: d.year, y: +d[skillset] }; }); })); console.log(dataset) // Set x, y and colors var x = d3.scale.ordinal() .domain(dataset[0].map(function(d) { return d.x; })) .rangeRoundBands([10, width - 10], 0.02); var y = d3.scale.linear() .domain([0, d3.max(dataset, function(d) { return d3.max(d, function(d) { return d.y0 + d.y; }); })]) .range([height, 0]); var colors = ["#b33040", "#d9d574"]; var backcolors = ["red", "blue","green","pink"]; // Define and draw axes var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(5) .tickSize(-width, 0, 0) .tickFormat(function(d) { return d }); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); // .tickFormat(d3.time.format("%Y")); svg.append("g") .attr("class", "y axis") .call(yAxis); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); // Creating the Average Bar for the Semester svg.selectAll(".bar1").data(orangeData).enter().append("g") .attr("class", "bar1").append("rect") .attr("x", function(d) { return x(d.year) ; // center it }) .attr("width", x.rangeBand()) // make it slimmer .attr("y", function(d) { return y(d.oranges); }) .attr("height", function(d) { return height - y(d.oranges); }); // Create groups for each series, rects for each segment in Stacked Bar var groups = svg.selectAll("g.cost") .data(dataset) .enter().append("g") .attr("class", "cost") .style("fill", function(d, i) { return colors[i]; }); var rect = groups.selectAll("rect") .data(function(d) { return d; }) .enter() .append("rect") .attr("x", function(d) { return x(d.x) + 20 ; }) .attr("y", function(d) { return y(d.y0 + d.y); }) .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); }) .attr("width", x.rangeBand() -40 ); } Here is the working fiddle
First, set your colours array: var colors = ['#7fc97f','#beaed4','#fdc086','#ffff99','#386cb0','#f0027f','#bf5b17','#666666']; Then, in your rectangles (not in your groups), set the fill of each bar using the parent's index: .attr("fill", function(d, i, j) { return colors[(j*4)+i]; }); Here, the magic number "4" is the number of groups. Change it accordingly (if you create more bars). Here is your fiddle: https://fiddle.jshell.net/747beqqc/
Adding several y axes with zoom / pan in D3js
I'm working on a horizontal line chart in d3js that displays several lines based on json input. It has zooming and panning, but also need to display a y-axis for each of the drawn lines. In my case, three. First off, is this bad practice? Should I stack all three on one side, or should I keep two on the left, one on the right or any other combination? I've tried following this tutorial, but that did really just create more mess and confusing code. I was hoping someone could guide me in the direction of how to add the additional y axes and how I could have them work with the zooming and panning as well, like the one I have now. Here's my current view: And here's my code: <script> var margin = { top: 20, right: 80, bottom: 20, left: 40 }, width = ($("#trendcontainer").width() - 50) - margin.left - margin.right, height = 650 - margin.top - margin.bottom; var svg; var format = d3.time.format("%Y-%m-%dT%H:%M:%S").parse; var x = d3.time.scale() .range([0, width]); var y0 = d3.scale.linear() .range([height, 0]); var y1 = d3.scale.linear() .range([height, 0]); var color = d3.scale.category10(); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .tickSize(-height); // TODO: Rename axis to instrument name (i.e 'depth') var yAxis0 = d3.svg.axis() .scale(y0) .orient("left") .tickSize(-width); var yAxis1 = d3.svg.axis() .scale(y1) .orient("right") .tickSize(-width); var line = d3.svg.line() .interpolate("basis") .x(function(d) { return x(d.date); }) .y(function(d) { return y0(d.value); }); d3.json('#Url.Action("DataBlob", "Trend", new {id = Model.Unit.UnitId, runId = Request.Params["runId"]})', function(error, tmparray) { var json = JSON.parse(tmparray); $('#processing').hide(); color.domain(d3.keys(json[0]).filter(function(key) { return key !== "Time" && key !== "Id"; })); json.forEach(function(d) { var date = format(d.Time); d.Time = date; }); var instruments = color.domain().map(function(name) { return { name: name, values: json.map(function(d) { return { date: d.Time, value: +d[name] }; }) }; }); x.domain(d3.extent(json, function(d) { return d.Time; })); y0.domain([ d3.min(instruments, function (c) { if (c.name == "Depth") { return d3.min(c.values, function (v) { return v.value; }); } //return d3.min(c.values, function (v) { // return v.value; //}); }), d3.max(instruments, function(c) { return d3.max(c.values, function(v) { return v.value; }); }) ]); y1.domain([ d3.min(instruments, function (c) { console.log("In y1.domain c is: " + c); if (c.name == "Weight") { return d3.min(c.values, function (v) { return v.value; }); } //return d3.min(c.values, function (v) { // return v.value; //}); }), d3.max(instruments, function(c) { return d3.max(c.values, function(v) { return v.value; }); }) ]); var zoom = d3.behavior.zoom() .x(x) .y(y0) .scaleExtent([1, 10]) .on("zoom", zoomed); svg = d3.select(".panel-body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .call(zoom) .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom); svg.append("rect") .attr("width", width) .attr("height", height); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis0); svg.append("g") .attr("class", "y axis") .call(yAxis1); var instrument = svg.selectAll(".instrument") .data(instruments) .enter().append("g") .attr("class", "instrument"); instrument.append("path") .attr("class", "line") .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return color(d.name); }); instrument.append("text") .datum(function(d) { return { name: d.name, value: d.values[d.values.length - 1] }; }) .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y0(d.value.value) + ")"; }) .attr("x", 3) .attr("dy", ".35em") .text(function(d) { return d.name; }); }); function zoomed() { svg.select(".x.axis").call(xAxis); svg.select(".y.axis").call(yAxis0); svg.select(".x.grid") .call(make_x_axis() .tickSize(-height, 0, 0) .tickFormat("")); svg.select(".y.grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("")); svg.selectAll(".line") .attr("d", function(d) { return line(d.values); }); }; var make_x_axis = function() { return d3.svg.axis() .scale(x) .orient("bottom") .ticks(5); }; var make_y_axis = function() { return d3.svg.axis() .scale(y0) .orient("left") .ticks(5); }; </script> Finally, here's what I'm trying to achieve (This component is way too slow, and does not handle large datasets well):
Finally ended up at a solution, with some kind assistance from #LarsKotthoff. Also added multiple axes zoom, based on this post. <script> /* d3 vars */ var x; var y1; var y2; var y3; var graph; var m = []; var w; var h; /* d3 axes */ var xAxis; var yAxisLeft; var yAxisLeftLeft; var yAxisRight; /* d3 lines */ var line1; var line2; var line3; /* d3 zoom */ var zoom; var zoomLeftLeft; var zoomRight; /* Data */ var speed = []; var depth = []; var weight = []; var timestamp = []; var url = '#Url.Action("DataBlob", "Trend", new {id = Model.Unit.UnitId, runId = Request.Params["runId"]})'; var data = $.getJSON(url, null, function(data) { var list = JSON.parse(data); var format = d3.time.format("%Y-%m-%dT%H:%M:%S").parse; list.forEach(function(d) { speed.push(d.Speed); depth.push(d.Depth); weight.push(d.Weight); var date = format(d.Time); d.Time = date; timestamp.push(d.Time); }); m = [10, 80, 30, 100]; // margins: top, right, bottom, left w = $("#trendcontainer").width() - m[1] - m[3]; // width h = 550 - m[0] - m[2]; // height x = d3.time.scale().domain(d3.extent(timestamp, function (d) { return d; })).range([0, w]); y1 = d3.scale.linear().domain([0, d3.max(speed)]).range([h, 0]); y2 = d3.scale.linear().domain([0, d3.max(depth)]).range([h, 0]); y3 = d3.scale.linear().domain([0, d3.max(weight)]).range([h, 0]); line1 = d3.svg.line() .interpolate("basis") .x(function (d, i) { return x(timestamp[i]); }) .y(function (d) { return y1(d); }); line2 = d3.svg.line() .interpolate("basis") .x(function (d, i) { return x(timestamp[i]); }) .y(function (d) { return y2(d); }); line3 = d3.svg.line() .interpolate("basis") .x(function (d, i) { return x(timestamp[i]); }) .y(function (d) { return y3(d); }); zoom = d3.behavior.zoom() .x(x) .y(y1) .scaleExtent([1, 10]) .on("zoom", zoomed); zoomLeftLeft = d3.behavior.zoom() .x(x) .y(y3) .scaleExtent([1, 10]); zoomRight = d3.behavior.zoom() .x(x) .y(y2) .scaleExtent([1, 10]); // Add an SVG element with the desired dimensions and margin. graph = d3.select(".panel-body").append("svg:svg") .attr("width", w + m[1] + m[3]) .attr("height", h + m[0] + m[2]) .call(zoom) .append("svg:g") .attr("transform", "translate(" + m[3] + "," + m[0] + ")"); // create xAxis xAxis = d3.svg.axis().scale(x).tickSize(-h).tickSubdivide(false); // Add the x-axis. graph.append("svg:g") .attr("class", "x axis") .attr("transform", "translate(0," + h + ")") .call(xAxis); // create left yAxis yAxisLeft = d3.svg.axis().scale(y1).ticks(10).orient("left"); // Add the y-axis to the left graph.append("svg:g") .attr("class", "y axis axisLeft") .attr("transform", "translate(-15,0)") .call(yAxisLeft); // create leftleft yAxis yAxisLeftLeft = d3.svg.axis().scale(y3).ticks(10).orient("left"); // Add the y-axis to the left graph.append("svg:g") .attr("class", "y axis axisLeftLeft") .attr("transform", "translate(-50,0)") .call(yAxisLeftLeft); // create right yAxis yAxisRight = d3.svg.axis().scale(y2).ticks(10).orient("right"); // Add the y-axis to the right graph.append("svg:g") .attr("class", "y axis axisRight") .attr("transform", "translate(" + (w + 15) + ",0)") .call(yAxisRight); // add lines // do this AFTER the axes above so that the line is above the tick-lines graph.append("svg:path").attr("d", line1(speed)).attr("class", "y1"); graph.append("svg:path").attr("d", line2(depth)).attr("class", "y2"); graph.append("svg:path").attr("d", line3(weight)).attr("class", "y3"); }); function zoomed() { zoomRight.scale(zoom.scale()).translate(zoom.translate()); zoomLeftLeft.scale(zoom.scale()).translate(zoom.translate()); graph.select(".x.axis").call(xAxis); graph.select(".y.axisLeft").call(yAxisLeft); graph.select(".y.axisLeftLeft").call(yAxisLeftLeft); graph.select(".y.axisRight").call(yAxisRight); graph.select(".x.grid") .call(make_x_axis() .tickFormat("")); graph.select(".y.axis") .call(make_y_axis() .tickSize(5, 0, 0)); graph.selectAll(".y1") .attr("d", line1(speed)); graph.selectAll(".y2") .attr("d", line2(depth)); graph.selectAll(".y3") .attr("d", line3(weight)); }; var make_x_axis = function () { return d3.svg.axis() .scale(x) .orient("bottom") .ticks(5); }; var make_y_axis = function () { return d3.svg.axis() .scale(y1) .orient("left") .ticks(5); }; </script>