Add Spacing and Interactivity to D3 Vers 6 Stacked Bar Chart - javascript

I noticed that my bar chart doesn't have the spacing between the bars that I was aiming to achieve. I also can't seem to get the interactive tooltip to show up when I hover over the bars. I might need some help with this. The point of this is to get the value of the certain color to show up when an arrow is hovering over it.
Codepen link: codepen.io/irwinmier96/pen/qBaKmma
Here is my code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Communities Fighting Covid</title>
<!-- Bootstrap core CSS -->
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/modern-business.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
</head>
<body>
<script>
var margin = {top: 20, right: 160, bottom: 35, left: 30};
var width = 960 - margin.left - margin.right,
height = 500 - 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 = [
{ race: "A", positive: "60", total: "600"},
{ race: "C", positive: "58", total: "500"},
{ race: "D", positive: "85", total: "600"},
{ race: "E", positive: "56", total: "500"},
{ race: "F", positive: "89", total: "700"},
{ race: "G", positive: "62", total: "400"}
];
// Transpose the data into layers
var keys= ["positive", "total"];
var dataset = d3.stack().keys(keys)(data);
// Set x, y and colors
var x = d3.scaleBand()
.domain(data.map(function(d) { return d.race; }))
.range([10, width-10], 0.02);
var y = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d3.max(d, function(d) { return d[0] + d[1]; }); })])
.range([height, 0]);
var colors = ["b33040", "#d25c4d"];
// Define and draw axes
var yAxis = d3.axisLeft()
.scale(y)
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat( function(d) { return d } );
var xAxis = d3.axisBottom()
.scale(x)
.tickFormat(data.race); // change this to get x values to reflect new
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.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.data.race); })
.attr("y", function(d) { return y(d[0] + d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[0] + d[1]); })
.attr("width", x.bandwidth())
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(d) {
var xPosition = d3.pointer(this)[0] - 15;
var yPosition = d3.pointer(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {return colors.slice().reverse()[i];});
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0: return "Total Cases";
case 1: return "Positive Cases";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
</script>
</body>

The usual trick for spacing is to just add a little fudge factor:
.attr("x", function(d) { return x(d.data.race) + 2; }) //<-- move right two pixels to stay centered
.attr("y", function(d) { return y(d[0] + d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[0] + d[1]); })
.attr("width", x.bandwidth() - 4) //<-- shorten width by 4
For your tooltip you are using a code snippet from an older version of d3. For d3.js version 6, the callback was modified to pass the event object where you can get the cursor position:
.on("mousemove", function(e,d) {
var xPosition = e.offsetX - margin.left + 10;
var yPosition = e.offsetY - margin.top - 10;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d[1]);
});
Working code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Communities Fighting Covid</title>
<!-- Bootstrap core CSS -->
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/modern-business.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
</head>
<body>
<script>
var margin = {top: 20, right: 160, bottom: 35, left: 30};
var width = 960 - margin.left - margin.right,
height = 500 - 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 = [
{ race: "A", positive: "60", total: "600"},
{ race: "C", positive: "58", total: "500"},
{ race: "D", positive: "85", total: "600"},
{ race: "E", positive: "56", total: "500"},
{ race: "F", positive: "89", total: "700"},
{ race: "G", positive: "62", total: "400"}
];
// Transpose the data into layers
var keys= ["positive", "total"];
var dataset = d3.stack().keys(keys)(data);
// Set x, y and colors
var x = d3.scaleBand()
.domain(data.map(function(d) { return d.race; }))
.range([10, width-10], 0.02);
var y = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d3.max(d, function(d) { return d[0] + d[1]; }); })])
.range([height, 0]);
var colors = ["b33040", "#d25c4d"];
// Define and draw axes
var yAxis = d3.axisLeft()
.scale(y)
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat( function(d) { return d } );
var xAxis = d3.axisBottom()
.scale(x)
.tickFormat(data.race); // change this to get x values to reflect new
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.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.data.race) + 2; })
.attr("y", function(d) { return y(d[0] + d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[0] + d[1]); })
.attr("width", x.bandwidth() - 4)
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(e,d) {
var xPosition = e.offsetX - margin.left + 10;
var yPosition = e.offsetY - margin.top - 10;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d[1]);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {return colors.slice().reverse()[i];});
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0: return "Total Cases";
case 1: return "Positive Cases";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold")
.attr("fill", "black")
</script>
</body>

Related

D3.js Adding legend to the Responsive bar chart

I have found a simple responsive bar chart from this Link. So, I have done some little modification into this and also added legends into bar chart. Chart is responsive but not the legends. I have been working on this since days. I have tried appending legends into x-axis that doesn't work also.
<!doctype html>
<html lang="en">
<head>
<title>Bootstrap Case</title>
<meta charset="utf-8">
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<style>
.axis path,
.axis line {
fill: none;
stroke: #bdbdbd;
}
.axis text {
font-family: 'Open Sans regular', 'Open Sans';
font-size: 13px;
}
.bar {
fill: #8bc34a;
}
.bar:hover {
fill: #039be4;
}
</style>
<div id ="chartID"></div>
<script>
var data = [{
"letter": "a",
"frequency": "4.84914547592537",
"Color": "#D3D3D3"
},
{
"letter": "b",
"frequency": "4.86872684269123",
"Color": "#D3D3D3"
},
{
"letter": "c",
"frequency": "6.63842861065779",
"Color": "#000000"
},
{
"letter": "d",
"frequency": "6.53280838923937",
"Color": "#000000"
}
]
var data2 = [];
for (var i = 0; i < 2; i++) {
data2.push(data[i]);
}
var color_hash = {
0: ["Control", "#D3D3D3"],
1: ["Case", "#000000"]
}
var margin = {
top: 30,
right: 100,
bottom: 20,
left: 80
};
var width = 960 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var xScale = d3.scale.ordinal().rangeRoundBands([0, width], .1)
var yScale = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
var svgContainer = d3.select("#chartID").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 + ")");
xScale.domain(data.map(function (d) {
return d.letter;
}));
yScale.domain([0, d3.max(data, function (d) {
return d.frequency;
})]);
var xAxis_g = svgContainer.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height) + ")")
.call(xAxis);
var yAxis_g = svgContainer.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("dx", "-10em")
.attr("transform", "rotate(-90)")
.attr("y", 6).attr("dy", "-4.0em")
.style("text-anchor", "end").text("Expression values");
svgContainer.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function (d) {
return xScale(d.letter);
})
.attr("width", xScale.rangeBand())
.attr("y", function (d) {
return yScale(d.frequency);
})
.attr("height", function (d) {
return height - yScale(d.frequency);
})
.style("fill", function (d) {
return d.Color;
});
// Add Legends
var legend = svgContainer.append("g")
// .attr("transform", "translate(0," + (10) + ")")
// .attr("x", width - 100)
// .attr("y", 250)
// .attr("height", 100)
// .attr("width", 100);
legend.selectAll('g').data(data2)
.enter()
.append('g')
.each(function (d, i) {
var g = d3.select(this);
g.append("rect")
// .attr("class", "legend")
.attr("x", width)
.attr("y", i * 25)
.attr("width", 10)
.attr("height", 10)
.style("fill", color_hash[String(i)][1]);
g.append("text")
// .attr("class", "legend")
.attr("x", width - 20 * -1)
.attr("y", i * 25 + 8)
// .attr("height", 30)
// .attr("width", 100)
// .style("text-anchor", "end")
.style("font-size", 16)
.text(color_hash[String(i)][0]);
});
d3.select(window).on('resize', resize);
function resize() {
// console.log('----resize function----');
// update width
width = parseInt(d3.select('#chartID').style('width'), 10);
width = width - margin.left - margin.right;
height = parseInt(d3.select("#chartID").style("height"));
height = height - margin.top - margin.bottom;
// console.log('----resiz width----' + width);
// console.log('----resiz height----' + height);
// resize the chart
if (width < 870) {
//xScale.range([0, width]);
xScale.rangeRoundBands([0, width], .1);
yScale.range([height, 0]);
yAxis.ticks(Math.max(height / 50, 2));
xAxis.ticks(Math.max(width / 50, 2));
d3.select(svgContainer.node().parentNode)
.style('width', (width + margin.left + margin.right) + 'px');
svgContainer.selectAll('.bar')
.attr("x", function (d) {
return xScale(d.letter);
})
.attr("width", xScale.rangeBand());
// svgContainer.selectAll('.legend')
// .attr("x", function (d) {
// return ;
// })
// .attr("width", xScale.rangeBand());
svgContainer.select('.x.axis').call(xAxis.orient('bottom'));
// svgContainer.select('.legend');
}
}
</script>
</html>
Need some help for this.
Thank you.

d3.js: Stacked Bar Chart with low values

I faced with a problem when some values in a bar very small when at the same time most of the other values are big enough. As the result these chunks with low values are almost not visible. I did not find any solution hot to correctly round chunks(not manually because I now that I can round them to more higher values via scale + invert(in order to determine what values I needed to show them more or less visible)). As an example below: as you see the last bar with low values is almost not visible. So can you suggest how to fix it? It would be great to have an opportunity to be able to specify the min size of stacked bar chart chunk. Thank you in advance.
http://jsfiddle.net/vhcdt13x/
// Setup svg using Bostock's margin convention
var margin = {top: 20, right: 160, bottom: 35, left: 30};
var width = 960 - margin.left - margin.right,
height = 500 - 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: "2006", redDelicious: "100", mcintosh: "150", oranges: "90", pears: "60" },
{ year: "2012", redDelicious: "1", mcintosh: "1", oranges: "1", pears: "1" }
];
var parse = d3.time.format("%Y").parse;
// Transpose the data into layers
var dataset = d3.layout.stack()(["redDelicious", "mcintosh", "oranges", "pears"].map(function(fruit) {
return data.map(function(d) {
return {x: parse(d.year), y: +d[fruit]};
});
}));
// 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", "#d25c4d", "#f2b447", "#d9d574"];
// 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);
// Create groups for each series, rects for each segment
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); })
.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())
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {return colors.slice().reverse()[i];});
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0: return "Anjou pears";
case 1: return "Naval oranges";
case 2: return "McIntosh apples";
case 3: return "Red Delicious apples";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");

display data from csv file into BarGraph using d3.js

Hello I am trying to display the data from csv file into BarGraph using javascript. says attribute y: Expected length, "NaN". in javascript. the code that I have tried so far is:
var margin = {top: 20, right: 10, bottom: 100, left:50},
width = 700 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("body")
.append("svg")
.attr ({
"width": width + margin.right + margin.left,
"height": height + margin.top + margin.bottom
})
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.right + ")");
var xScale = d3.scale.ordinal()
.rangeRoundBands([0,width], 0.2, 0.2);
var yScale = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
d3.csv("aa.csv", function(error,data) {
if(error) console.log("Error: can not be loaded!");
data.forEach(function(d) {
d.c= d.c;
d.a= +d.a;
console.log(d.a);
});
data.sort(function(a,b) {
return b.a- a.a;
});
xScale.domain(data.map(function(d) { return d.c; }) );
yScale.domain([0, d3.max(data, function(d) { return d.a; } ) ]);
svg.selectAll('rect')
.data(data)
.enter().append('rect')
.append('rect')
.attr("height", 0)
.attr("y", height)
.transition().duration(3000)
.delay( function(d,i) { return i * 200; })
.attr({
"x": function(d) { return xScale(d.c); },
"y": function(d) { return yScale(d.a); },
"width": xScale.rangeBand(),
"height": function(d) { return height - yScale(d.a); }
})
.style("fill", function(d,i) { return 'rgb(20, 20, ' + ((i * 30) + 100) + ')'});
svg.selectAll('text')
.data(data)
.enter()
.append('text')
.text(function(d){
return d.a;
})
.attr({
"x": function(d){ return xScale(d.c) + xScale.rangeBand()/2; },
"y": function(d){ return yScale(d.a)+ 12; },
"font-family": 'sans-serif',
"font-size": '13px',
"font-weight": 'bold',
"fill": 'white',
"text-anchor": 'middle'
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("dx", "-.8em")
.attr("dy", ".25em")
.attr("transform", "rotate(-60)" )
.style("text-anchor", "end")
.attr("font-size", "10px");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -height/2)
.attr("dy", "-3em")
.style("text-anchor", "middle")
.text("Trillions of US Dollars ($)");
});
And below is the index.html file code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Show data in BaraGraph</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"
charset="utf-8"></script>
</head>
<body>
<h1 style="text-align:center;">Show Maths data in BaraGraph</h1>
<script type="text/javascript" src="showData.js"></script>
</body>
</html>
please can anybody help me to fix this problem because I spent too much time on it without finding the right solution
and the data that I've used is:
c a
----- ------
John 9.4
Smith 3.5
Mark 3.3
Alan 8.9
I'm not getting any error message while running your code.
Also there is a mistake in your code. You are appending 'rect' element twice (see snippet below):
svg.selectAll('rect')
.data(data)
.enter().append('rect')
.append('rect') // 'rect' element appended twice
Here's an updated and working code :
var margin = {
top: 20,
right: 10,
bottom: 100,
left: 50
},
width = 700 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("body")
.append("svg")
.attr({
"width": width + margin.right + margin.left,
"height": height + margin.top + margin.bottom
})
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")");
var xScale = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.2, 0.2);
var yScale = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
d3.csv("mathResults.csv", function(error, data) {
if (error) console.log("Error: can not be loaded!");
data.forEach(function(d) {
d.Name = d.Name;
d.Grade = +d.Grade;
console.log(d.Grade);
});
data.sort(function(a, b) {
return b.Grade - a.Grade;
});
xScale.domain(data.map(function(d) {
return d.Name;
}));
yScale.domain([0, d3.max(data, function(d) {
return d.Grade;
})]);
svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr("height", 0)
.attr("y", height)
.transition().duration(3000)
.delay(function(d, i) {
return i * 200;
})
.attr({
"x": function(d) {
return xScale(d.Name);
},
"y": function(d) {
return yScale(d.Grade);
},
"width": xScale.rangeBand(),
"height": function(d) {
return height - yScale(d.Grade);
}
})
.style("fill", function(d, i) {
return 'rgb(20, 20, ' + ((i * 30) + 100) + ')'
});
svg.selectAll('text')
.data(data)
.enter()
.append('text')
.text(function(d) {
return d.Grade;
})
.attr({
"x": function(d) {
return xScale(d.Name) + xScale.rangeBand() / 2;
},
"y": function(d) {
return yScale(d.Grade) + 12;
},
"font-family": 'sans-serif',
"font-size": '13px',
"font-weight": 'bold',
"fill": 'white',
"text-anchor": 'middle'
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("dx", "-.8em")
.attr("dy", ".25em")
.attr("transform", "rotate(-60)")
.style("text-anchor", "end")
.attr("font-size", "10px");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("dy", "-3em")
.style("text-anchor", "middle")
.text("Trillions of US Dollars ($)");
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Show data in BaraGraph</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script>
</head>
<body>
<h1 style="text-align:center;">Show Maths data in BaraGraph</h1>
</body>
</html>

D3.js - rotate text to vertical

The problem:
I'm new to D3.js and I've created a bar chart that gets data from an array. I placed text on top of the bars and the text is wider than the bars themselves. I want to rotate it vertical so that the text goes through the middle of the bar.
I know .attr("transform", "rotate(180)") won't work because it rotates my text off-screen. I found an answer from Mike Bostock here, but it doesn't seem to work for me, I don't know what I'm doing wrong. He says I need to change the rotation point of origin, but I can't get it to work.
Any help is appreciated, you can find the code below:
var label = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d){
return d;
})
.attr("x", function(d, i){
xText = i*(w/data.length);
return xText;
})
.attr("y", function(d){
yText = h - yScale(d);
return yText;
})
.attr("transform", "translate(" + xText + "," + yText + ") rotate(90)")
.attr("fill", "black")
.attr("font-family", "sans-serif")
.attr("font-size", "11px");
Thanks!
You are setting both the x and y attributes and translated the text. With the rotate, you only want to use the translate:
.attr("transform", function(d,i){
var xText = i * (w / data.length);
var yText = h - yScale(d.v);
return "translate(" + xText + "," + yText + ") rotate(90)";
})
Also, I find your example code confusing, I was assuming that your data was an array of strings since you do:
.text(function(d) {
return d;
})
but that would make yScale(d), return NaN.
Here's an example that works nicely:
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<script>
var w = 300,
h = 300,
data = [{
t: "Now",
v: 50
},{
t: "is",
v: 25
},{
t: "the",
v: 10
},{
t: "winter",
v: 30
}];
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h);
var yScale = d3.scale.linear()
.range([0, h])
.domain([60, 0]);
var label = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d.t;
})
.attr("transform", function(d,i){
var xText = i * (w / data.length);
var yText = h - yScale(d.v);
return "translate(" + xText + "," + yText + ") rotate(90)";
})
.attr("fill", "black")
.attr("font-family", "sans-serif")
.attr("font-size", "11px");
</script>
</body>
</html>
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var data = [{
"letter": "A",
"frequency": 0.08167
}, {
"letter": "B",
"frequency": 0.01492
}, {
"letter": "C",
"frequency": 0.02782
}, {
"letter": "D",
"frequency": 0.04253
}, {
"letter": "E",
"frequency": 0.12702
}];
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 500 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10, "%");
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 + ")");
x.domain(data.map(function(d) {
return d.letter;
}));
y.domain([0, d3.max(data, function(d) {
return d.frequency;
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.letter);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.frequency);
})
.attr("height", function(d) {
return height - y(d.frequency);
});
function type(d) {
d.frequency = +d.frequency;
return d;
}
var text = svg.selectAll(".text").data(data);
text.enter()
.append("text")
.attr("class", "text")
.attr("x", function(d, i) {
return x(d.letter);
})
.attr("y", function(d) {
return y(d.frequency);
})
.text(function(d) {
return d.letter;
})
.attr("transform", function(d) {
return "rotate(90 " + x(d.letter) + "," + (y(d.frequency) - d.letter.length) + ")"
});
</script>
You can try out this, and as a base you can modify this as per your need. I hope it helps.

How to make a horizontal Stacked bar chart with D3

I just started using D3.js, and have been trying to make this stacked bar chart horizontal(link below).
https://github.com/DeBraid/www.cacheflow.ca/blob/master/styles/js/d3kickchart.js
http://cacheflow.ca/blog/index.html (You can find the chart in the middle of the page)
As a result, I completely stacked on the half way... I would greatly appreciate it if anyone gives me some guides to archive this.
Problem Solved:
I found the way to made the virtical bar chart horizontal. Hope this could help someone who are facing same problem.
Below is the fixed code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<div id="chart-svg"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 50, bottom: 100, left: 75},
width = 740 - margin.left - margin.right,
height = 170 - margin.top - margin.bottom;
var svg = d3.select("#chart-svg").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 + ")");
d3.csv("mta.csv", function (data){
var headers = ["meeting","construction","management","aa","bb","cc"];
var layers = d3.layout.stack()(headers.map(function(period) {
return data.map(function(d) {
return {x: d.Category, y: +d[period]};
});
}));
console.log(layers);
var yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); });
var yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var yScale = d3.scale.ordinal()
.domain(layers[0].map(function(d) { return d.x; }))
.rangeRoundBands([25, height], .2);
var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);
var color = d3.scale.ordinal()
.domain(headers)
.range(["#98ABC5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(1)
.tickPadding(15)
.tickFormat(d3.format(".2s"));
var yAxis = d3.svg.axis()
.scale(yScale)
.tickSize(0)
.tickPadding(6)
.orient("left");
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return yScale(d.x); })
.attr("x", 0)
.attr("height", yScale.rangeBand())
.attr("width", 0)
.on("click", function(d) {
console.log(d);
});
rect.transition()
.delay(function(d, i) { return i * 100; })
.attr("x", function(d) { return x(d.y0); })
.attr("width", function(d) { return x(d.y); });
//********** AXES ************
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text").style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em");
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis)
.append("text")
.attr({"x": 0 / 2, "y": height+50})
.attr("dx", ".75em")
.style("text-anchor", "end")
.text("Time");
var legend = svg.selectAll(".legend")
.data(headers.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(-20," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
</script>

Categories