Related
I am trying to create an interactive line chart with mouseover function. When you place your mouse over the line you can see the value of the GDP and the year at that certain point. But for some reason,I am getting the below error but I am not sure why it is happening as i am passing the right format data-set into the function.
Uncaught TypeError: Cannot read property 'year' of undefined
My code is below:
HTML
<!DOCTYPE html>
<htmL>
<meta charset="utf-8">
<head>
<style>
.line-chart2{
margin-top:200px;
margin-left:100px;
border:1px solid lightgray;
}
circle {
fill: steelblue;
}
body {
font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
div.tooltip {
position: absolute;
text-align: center;
width: 80px;
height: 64px;
padding: 2px;
font: 14px sans-serif;
color: black;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: #F1F3F3;
stroke: #6F257F;
stroke-width: 5px;
}
.hover-line {
stroke: #6F257F;
stroke-width: 2px;
stroke-dasharray: 3,3;
}
</style>
</head>
<body>
<svg class='line-chart2'></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="math.js" type="text/javascript"></script>
<script src="./regression.js"></script>
</body>
</htmL>
Javascript
var gdp=[ 387.65, 410.32, 415.73, 452.69, 462.14,
478.96, 508.06, 599.59, 699.68, 808.90,
920.31, 1201.11, 1186.95, 1323.94, 1656.61,
1823.04, 1827.63, 1856.72, 2039.12, 2102.39,
2274.22, 2600.81]; //y or GDP of India
var years=['1996','1997','1998','1999','2000','2001','2002','2003','2004','2005','2006','2007','2008','2009','2010','2011','2012','2013','2014','2015','2016','2017'];
var data_gdp=[]
for(i=0;i<forexp.length;i++){
data_gdp.push({year:years[i],value:gdp[i]})
}
function drawChart_gdp(data,class_name) {
var svgWidth = 1200, svgHeight = 400;
var margin = { top: 60, right: 60, bottom: 60, left: 60 };
var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;
var svg = d3.select(class_name)
.attr("width", svgWidth)
.attr("height", svgHeight);
var bisectDate= d3.bisector(function(d) { return d.year; }).left;
var g = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")"
);
var x = d3.scaleTime().range([0,width]);
var y = d3.scaleLinear().rangeRound([height, 0]);
var line = d3.line()
.x(function(d) { return x(new Date(parseInt(d.year),0))})
.y(function(d) { return y(d.value)})
x.domain(d3.extent(data, function (d) { return new Date(parseInt(d.year),0); }));
y.domain(d3.extent(data, function(d) { return d.value }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.append("text")
.attr("fill", "#000")
.text("Year")
.attr("dy", "1.90em")
.attr("y", 5)
.attr("x",500)
.attr("font-size", "20px")
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", -80)
.attr("x",-55)
.attr("dy", "1.90em")
.attr("text-anchor", "center")
.attr("font-size", "20px")
.text("GDP ($)")
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
var focus = g.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("line")
.attr("class", "x-hover-line hover-line")
.attr("y1", 0)
.attr("y2", height);
focus.append("line")
.attr("class", "y-hover-line hover-line")
.attr("x1", width)
.attr("x2", width);
focus.append("circle")
.attr("r", 7.5);
focus.append("text")
.attr("x", 15)
.attr("dy", ".31em");
svg.append("rect")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", function() { //problem in this function
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.year > d1.year - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.year) + "," + y(d.value) + ")");
focus.select("text").text(function() { return d.value; });
focus.select(".x-hover-line").attr("y2", height - y(d.value));
focus.select(".y-hover-line").attr("x2", width + width);
});
}
drawChart_gdp(data_gdp,'.line-chart2');
Since x is a time scale, the returned value of...
var x0 = x.invert(d3.mouse(this)[0])
... is a date object, like this:
Fri Nov 29 1996 19:56:00
However, in your data, you have strings:
[{year: "1996", value: 387.65}, {year: "1997", value: 410.32} etc...];
The solution is quite straightforward, just format the dates:
var x0 = d3.timeFormat("%Y")(x.invert(d3.mouse(this)[0]))
Here is your code with that change:
var gdp = [387.65, 410.32, 415.73, 452.69, 462.14,
478.96, 508.06, 599.59, 699.68, 808.90,
920.31, 1201.11, 1186.95, 1323.94, 1656.61,
1823.04, 1827.63, 1856.72, 2039.12, 2102.39,
2274.22, 2600.81
]; //y or GDP of India
var years = ['1996', '1997', '1998', '1999', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017'];
var data_gdp = []
for (i = 0; i < years.length; i++) {
data_gdp.push({
year: years[i],
value: gdp[i]
})
}
function drawChart_gdp(data, class_name) {
var svgWidth = 1200,
svgHeight = 400;
var margin = {
top: 60,
right: 60,
bottom: 60,
left: 60
};
var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;
var svg = d3.select(class_name)
.attr("width", svgWidth)
.attr("height", svgHeight);
var bisectDate = d3.bisector(function(d) {
return d.year;
}).left;
var g = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")"
);
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().rangeRound([height, 0]);
var line = d3.line()
.x(function(d) {
return x(new Date(parseInt(d.year), 0))
})
.y(function(d) {
return y(d.value)
})
x.domain(d3.extent(data, function(d) {
return new Date(parseInt(d.year), 0);
}));
y.domain(d3.extent(data, function(d) {
return d.value
}));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.append("text")
.attr("fill", "#000")
.text("Year")
.attr("dy", "1.90em")
.attr("y", 5)
.attr("x", 500)
.attr("font-size", "20px")
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", -80)
.attr("x", -55)
.attr("dy", "1.90em")
.attr("text-anchor", "center")
.attr("font-size", "20px")
.text("GDP ($)")
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
var focus = g.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("line")
.attr("class", "x-hover-line hover-line")
.attr("y1", 0)
.attr("y2", height);
focus.append("line")
.attr("class", "y-hover-line hover-line")
.attr("x1", width)
.attr("x2", width);
focus.append("circle")
.attr("r", 7.5);
focus.append("text")
.attr("x", 15)
.attr("dy", ".31em");
svg.append("rect")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() {
focus.style("display", null);
})
.on("mouseout", function() {
focus.style("display", "none");
})
.on("mousemove", function() { //problem in this function
var x0 = d3.timeFormat("%Y")(x.invert(d3.mouse(this)[0])),
i = bisectDate(data, x0, 1);
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.year > d1.year - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.year) + "," + y(d.value) + ")");
focus.select("text").text(function() {
return d.value;
});
focus.select(".x-hover-line").attr("y2", height - y(d.value));
focus.select(".y-hover-line").attr("x2", width + width);
});
}
drawChart_gdp(data_gdp, '.line-chart2');
<head>
<style>
circle {
fill: steelblue;
}
body {
font: 12px Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
div.tooltip {
position: absolute;
text-align: center;
width: 80px;
height: 64px;
padding: 2px;
font: 14px sans-serif;
color: black;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: #F1F3F3;
stroke: #6F257F;
stroke-width: 5px;
}
.hover-line {
stroke: #6F257F;
stroke-width: 2px;
stroke-dasharray: 3, 3;
}
</style>
</head>
<body>
<svg class='line-chart2'></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
</body>
I found a vertical stack bar graph sample on google - http://bl.ocks.org/juan-cb/43f10523858abf6053ae
I want to convert it in horizontal stacked bar graph. I have done the changes but something is wrong. Graph is not correct.I think all the bars are overlapped.
Please help me to resolve this. Code is copied.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
width: 960px;
height: 500px;
position: relative;
}
svg {
width: 100%;
height: 100%;
position: center;
}
text{
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.toolTip {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 white;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: black;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
.legend {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 60%;
}
text {
font: 10px sans-serif;
}
.axis text {
font: 10px sans-serif;
}
.axis path{
fill: none;
stroke: #000;
}
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<body>
<div class="barGraph" id='stacked-bar'></div>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.7.0/d3-legend.min.js"></script>
<script>
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
init();
function init(){
var dataset = [{
"goodRating": 27,
"avgRating": 21,
"badRating": 16,
"rooms": "0.01"
},
{
"goodRating": 26,
"avgRating": 22,
"badRating": 31,
"rooms": "0.02"
},
{
"goodRating": 100,
"avgRating": 0,
"badRating": 0,
"rooms": "1"
}];
var groupSpacing = 6;
var margin = {top: 10, right: 10, bottom: 60, left: 100},
width = 1000 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var y = d3.scaleBand()
.range([height, 0]);
var x = d3.scaleLinear()
.range([0, width], .1,.3);
// var colorRange = d3.scale.category20();
var color = d3.scaleOrdinal(d3.schemeCategory20);
var xAxis = d3.axisBottom(x).tickFormat(dataset.rooms),
yAxis = d3.axisLeft(y);
var svg = d3.select("#stacked-bar").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 + ")");
var divTooltip = d3.select("body").append("div").attr("class", "toolTip");
color.domain(d3.keys(dataset[0]).filter(function(key) { return key !== "rooms"; }));
dataset.forEach(function(d) {
var y0 = 0;
var y1 = 0;
d.values = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.values[d.values.length - 1].y1;
});
y.domain(dataset.map(function(d) { return d.rooms; }));
x.domain([0, d3.max(dataset, function(d) { return d.total; })]);
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", ".5em")
.attr("transform", "rotate(-65)");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 9)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Satisfaction %");
var bar = svg.selectAll(".rooms")
.data(dataset)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + y(d.rooms) + ",0)"; });
svg.selectAll(".x.axis .tick text")
.call(wrap, y.bandwidth());
var bar_enter = bar.selectAll("rect")
.data(function(d) { return d.values; })
.enter();
bar_enter.append("rect")
.attr("height", y.bandwidth())
.attr("x", function(d) { return x(d.y1); })
.attr("width", function(d) { return x(d.y1) - x(d.y0) })
.style("fill", function(d) { return color(d.name); });
bar_enter.append("text")
.text(function(d) { return d3.format(".2s")(d.y1-d.y0)+"%"; })
.attr("x", function(d) { return x(d.y1)+(x(d.y0) - x(d.y1))/2; })
.attr("y", y.bandwidth()/3)
.style("fill", '#ffffff');
bar.on("mousemove", function(d){
divTooltip.style("left", d3.event.pageX+10+"px");
divTooltip.style("top", d3.event.pageY-25+"px");
divTooltip.style("display", "inline-block");
var elements = document.querySelectorAll(':hover');
l = elements.length
l = l-1
element = elements[l].__data__
value = element.y1 - element.y0
divTooltip.html("Room No : "+(d.rooms)+"<br>"+element.name+" : "+value+"%");
});
bar.on("mouseout", function(d){
divTooltip.style("display", "none");
});
}
</script>
</body>
Regards,
Pinki Sharma
A few minor things were missing/incorrect:
The group (<g>) containing the bars was being transformed incorrectly (I'm guessing you missed this while changing from vertical stacked to horizontal) i.e. the following line
.attr("transform", function(d) { return "translate(" + y(d.rooms) + ",0)"; });
translates the bar groups from the left and the y position would be 0 and hence the overlap. I've changed that to this:
.attr("transform", function(d) { return "translate(0, " + y(d.rooms) + ")"; });
The rects' x value is changed from x(d.y1) to x(d.y0) (might be a typo)
bar_enter.append("rect")
.attr("height", y.bandwidth())
.attr("x", function(d) { return x(d.y0); })
Axis padding was missing for the scaleBand(). I've added that (check docs for more info)
var y = d3.scaleBand()
.rangeRound([height, 0]).padding(0.1);
Reset the margins to adapt to the SVG dimensions:
var margin = {top: 10, right: 60, bottom: 60, left: 50},
Combining all of the above, here's a fork of your codepen:
HORIZONTAL STACKED BAR CHART DEMO
Hope this helps.
I am trying to animate the bars when the user clicks on the legend. I've built the update function - but I am unsure on how to do the required filter and animation for this feature
http://jsfiddle.net/0ht35rpb/211/
function update(){
console.log("data", data);
var barData = data.filter(function(d1) {
console.log("d1", d1);
return !d1.disabled;
});
console.log("barData", barData);
var bar = chartHolder.selectAll(".bar")
.data(data)
console.log("bar", bar);
bar.transition()
.attr("width", x0.rangeBand())
.attr("y", function(d) {
return 0;
//return y(d.value);
})
.attr("height", function(d) {
return 0;
//return height - y(d.value);
});
bar.exit().remove();
/*
var bar = bar.selectAll("rect")
bar.transition()
//.attr("id", function(d){ return 'tag'+d.state.replace(/\s|\(|\)|\'|\,+/g, '');})
.attr("x", function(d) { return x1(d.name); })
.attr("width", x0.rangeBand())
.attr("y", function(d) {
return 0;
//return y(d.value);
})
.attr("height", function(d) {
return 0;
//return height - y(d.value);
});
//bar.exit().remove();
*/
This code var bar = chartHolder.selectAll(".bar") returns empty selection. You should select all .bars apply new data with method data, after that select all react, set animation parameters transition, duration, delay and set new values for approptiate attributes.
I rewrite your code - http://jsfiddle.net/168x28p3/1/ Here, I simply animate height of bars after click on legend. But this way you can create the another animation which you need.
var $this = document.querySelector('.chart');
var data = [{
label: "a",
"Current Period": 20,
"Prior Year": 10
}, {
label: "b",
"Current Period": 15,
"Prior Year": 30
}, {
label: "c",
"Current Period": 25,
"Prior Year": 40
}, {
label: "d",
"Current Period": 5,
"Prior Year": 60
}];
var configuration = [{
"yLabel": "People Count"
}];
var w = 660;
var h = 500;
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f", "#8bf41b"];
return colores_g[n % colores_g.length];
}
var margin = {
top: 20,
right: 110,
bottom: 30,
left: 20
},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;
var svg = d3.select($this).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 + ")");
var axisHolder = svg.append("g")
.attr("class", "axis");
var chartHolder = svg.append("g")
.attr("class", "chart");
var legendHolder = svg.append("g")
.attr("class", "legend");
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
//var colorRange = d3.scale.category20();
//var color = d3.scale.ordinal()
//.range(colorRange.range());
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var divTooltip = d3.select("body").append("div").attr("class", "toolTip");
var options = d3.keys(data[0]).filter(function(key) {
return key !== "label";
});
data.forEach(function(d) {
d.valores = options.map(function(name) {
return {
name: name,
value: +d[name]
};
});
});
x0.domain(data.map(function(d) {
return d.label;
}));
x1.domain(options).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.valores, function(d) {
return d.value;
});
})]);
axisHolder.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
axisHolder.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(configuration[0].yLabel);
var bar = chartHolder.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bars")
.attr("transform", function(d) {
return "translate(" + x0(d.label) + ",0)";
});
bar.selectAll("rect")
.data(function(d) {
return d.valores;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) {
return x1(d.name);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("value", function(d) {
return d.name;
})
.attr("height", function(d) {
return height - y(d.value);
})
.style("fill", function(d, i) {
return colores_google(i);
});
bar
.on("mousemove", function(d) {
divTooltip.style("left", d3.event.pageX + 10 + "px");
divTooltip.style("top", d3.event.pageY - 25 + "px");
divTooltip.style("display", "inline-block");
var x = d3.event.pageX,
y = d3.event.pageY
var elements = document.querySelectorAll(':hover');
l = elements.length
l = l - 1
elementData = elements[l].__data__
divTooltip.html((d.label) + "<br>" + elementData.name + "<br>" + elementData.value + "%");
});
bar
.on("mouseout", function(d) {
divTooltip.style("display", "none");
});
var legend = legendHolder.selectAll(".legend")
.data(options.slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(" + margin.right + "," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colores_google(i);
})
.on("click", function(d) {
console.log("d", d);
d.disabled = !d.disabled;
update();
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
function update() {
var barData = data.map(item => {
item.valores = item.valores.map(valor => {
return Object.assign({}, valor, { value: Math.random() * 40 })
})
return item;
})
var bar = chartHolder.selectAll(".bars")
.data(barData)
var rect = bar.selectAll("rect")
.data(function(d) {
return d.valores;
})
rect
.transition()
.duration(1000)
.delay(100)
.attr("width", x0.rangeBand() / 2)
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
}
* {
margin: 0;
padding: 0;
border: 0;
}
body {
background: #ffd;
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
position: relative;
}
text {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.toolTip {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 white;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: black;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
.legend {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 60%;
}
rect {
stroke-width: 2;
}
text {
font: 10px sans-serif;
}
.axis text {
font: 10px sans-serif;
}
.axis path {
fill: none;
stroke: #000;
}
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis .tick line {
stroke-width: 1;
stroke: rgba(0, 0, 0, 0.2);
}
.axisHorizontal path {
fill: none;
}
.axisHorizontal line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axisHorizontal .tick line {
stroke-width: 1;
stroke: rgba(0, 0, 0, 0.2);
}
.bar {
fill: steelblue;
fill-opacity: .9;
}
/*
.x.axis path {
display: none;
}*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div class="chart"></div>
I've got this d3js v3 chart and was wondering if it's possible to append the values in a text format to both the bar and the donut chart, and if so, how you would go about doing it?
Here is the code:
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
<style>
body {
font-family:arial;
font-size:10px;
margin:auto;
width:1100px;
}
.axis text {
font: 10px sans-serif;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
select {
background-color: #fff;
border: 1px solid #fff;
border-bottom: 1px solid #ccc;
color: #000;
padding: 3px 3px;
text-align: center;
text-decoration: none;
font-size: 10px;
margin: 2px 2px;
cursor: pointer;
}
select:focus {outline:0;}
.Row
{
display: table;
width: 100%;
table-layout: fixed;
}
.Column
{
display: table-cell;
position:relative;
}
</style>
</head>
<body>
<div class="Row">
<div class="Column" id="chart"></div>
</div>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var dispatch = d3.dispatch("load", "statechange");
var groups = [
"Team 1",
"Team 2",
"Team 3"
];
d3.csv("data.csv", type, function(error, states) {
if (error) throw error;
var stateById = d3.map();
states.forEach(function(d) { stateById.set(d.id, d); });
dispatch.load(stateById);
dispatch.statechange(stateById.get("CA"));
});
// A drop-down menu for selecting a state; uses the "menu" namespace.
dispatch.on("load.menu", function(stateById) {
var select = d3.select("#chart")
.append("div")
.append("select")
.on("change", function() { dispatch.statechange(stateById.get(this.value)); });
select.selectAll("option")
.data(stateById.values())
.enter().append("option")
.attr("value", function(d) { return d.id; })
.text(function(d) { return d.id; });
dispatch.on("statechange.menu", function(state) {
select.property("value", state.id);
});
});
// A bar chart to show total population; uses the "bar" namespace.
dispatch.on("load.bar", function(stateById) {
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 80 - margin.left - margin.right,
height = 290 - margin.top - margin.bottom;
var y = d3.scale.linear()
.domain([0, d3.max(stateById.values(), function(d) { return d.total; })])
.rangeRound([height, 0])
.nice();
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("#chart").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 + ")");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var rect = svg.append("rect")
.attr("x", 4)
.attr("width", width - 4)
.attr("y", height)
.attr("height", 0)
.style("fill", "#aaa");
dispatch.on("statechange.bar", function(d) {
rect.transition()
.attr("y", y(d.total))
.attr("height", y(0) - y(d.total));
});
});
// A pie chart to show population by age group; uses the "pie" namespace.
dispatch.on("load.pie", function(stateById) {
var width = 260,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.domain(groups)
.range(["steelblue", "lightblue", "darkorange"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 60);
var pie = d3.layout.pie()
.sort(null);
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(groups)
.enter().append("path")
.style("fill", color)
.each(function() { this._current = {startAngle: 0, endAngle: 0}; });
dispatch.on("statechange.pie", function(d) {
path.data(pie.value(function(g) { return d[g]; })(groups)).transition()
.attrTween("d", function(d) {
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
});
});
});
// Coerce population counts to numbers and compute total per state.
function type(d) {
d.total = d3.sum(groups, function(k) { return d[k] = +d[k]; });
return d;
}
</script>
</body>
</html>
And here's the dataset:
id,Team 1,Team 2,Team 3
AL,3105,5523,2590
AK,5208,8564,4215
AZ,5159,8286,3626
AR,2020,3432,1572
CA,2704,4499,2159
CO,3582,5871,2617
CT,2116,4036,1969
DE,5931,9949,4741
DC,3635,5043,2522
FL,1140,1938,9250
GA,7405,1250,5578
HI,8720,1340,6401
You have to create the label :
var label = svg.append("text")
.attr("x", 4)
.attr("y", height)
.attr("dy", ".35em")
.text(function(d) { return "0"; });
Then add a transition on this new text
label.transition()
.attr("y", y(d.total) + 5)
.text(d.total);
See https://plnkr.co/edit/wtq96BAZ3Zh1SaczjLT6?p=preview
I want to create stacked bar chart with d3.
I have this data in CSV file:
Type Sum Color
Regular 29756.85897 green
Regular 9756.85897 blue
and I want that each row will appear above the other in Y axis.
for example in this photo, the blue area should start in y=9756 until y=39512.
what should I change?
this is the relevant html code:
the whole code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
margin:auto;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar1 {
fill: #00FF66;
}
.bar1:hover {
fill: black ;
}
.x.axis path {
display: none;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script>
var margin = {top: 80, right: 90, bottom: 30, left: 90},
width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
//יצירת X
//יאכלס את סוגי הרכב השונים
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
//יצירת ציר y
//יציג בר עבור מחיר הרכב המוצע לדילרים
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
//יצירת ציר הY
//והצמדתו לצד שמאל
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(4)
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong></strong>"+d.Type+"<br><strong></strong> <span style='color:#00FF66'>" + d.Sum + "</span>";
})
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 + ")");
svg.call(tip);
//קליטת הטבלה והגדרת הטווחים על הצירים
d3.csv("Targil2.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.Type; }));
y.domain([0, d3.max(data, function(d) { return d.Sum*2; })]);
var stack = d3.layout.stack();
.x(function(d) { return d.Type }) // tell d3 to use Type as x value
.y(function(d) { return d.Sum }); // tell d3 to use Sum as y value
var stackData = stack(data);
//הוספה של 2 הצירים
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis axisLeft")
.attr("transform", "translate(0,0)")
.call(yAxis)
.append("text")
.attr("y", 6)
.attr("dy", "-2em")
.style("text-anchor", "end")
.style("text-anchor", "end")
.text("Price");
//הוספת בר הנתונים
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return x(d.Type); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return d.y0 })
.attr("height", function(d) { return (height - y(d.Sum)); })
.style("fill", function(d){
if(d["Color"] == "green"){ return "green";}
else return "#0066FF";})
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
});
function type(d) {
d.Sum = +d.Sum;
return d;
}
</script>
</body>
</html>
I tried to use that stack function as you told me, and changed the attribute of "y" , but it's not work for me now. I think I did something wrong.
Here you go.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
margin:auto;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar1 {
fill: #00FF66;
}
.bar1:hover {
fill: black ;
}
.x.axis path {
display: none;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script>
var margin = {top: 80, right: 90, bottom: 30, left: 90},
width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
//יצירת X
//יאכלס את סוגי הרכב השונים
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
//יצירת ציר y
//יציג בר עבור מחיר הרכב המוצע לדילרים
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
//יצירת ציר הY
//והצמדתו לצד שמאל
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(4)
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong></strong>"+d.Type+"<br><strong></strong> <span style='color:#00FF66'>" + d.Sum + "</span>";
})
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 + ")");
svg.call(tip);
//קליטת הטבלה והגדרת הטווחים על הצירים
d3.csv("Targil2.csv", type, function(error, data) {
window.dataSet = data;
data.sort(function(x,y){
var a = x.Sum;
var b = y.Sum;
return a > b ? -1 : a < b ? 1 : 0
})
x.domain(data.map(function(d) { return d.Type; }));
y.domain([0, d3.max(data, function(d) { return d.Sum*2; })]);
var stack = d3.layout.stack()
.x(function(d) { return d.Type }) // tell d3 to use Type as x value
.y(function(d) { return d.Sum }); // tell d3 to use Sum as y value
// var stackData = stack(data);
//הוספה של 2 הצירים
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis axisLeft")
.attr("transform", "translate(0,0)")
.call(yAxis)
.append("text")
.attr("y", 6)
.attr("dy", "-2em")
.style("text-anchor", "end")
.style("text-anchor", "end")
.text("Price");
var stackSoFar = 0;
//הוספת בר הנתונים
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return x(d.Type); })
.attr("width", x.rangeBand())
.attr("y", function(d){
d3.select(this)
.attr("height", function(d2){
var thisHeight = height - y(d.Sum);
stackSoFar += thisHeight
return thisHeight
});
return (height - stackSoFar)
})
.style("fill", function(d){
if(d["Color"] == "green"){ return "green";}
else return "#0066FF";})
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
});
function type(d) {
d.Sum = +d.Sum;
return d;
}
</script>
</body>
</html>
first of all, I guess that when you say
for example in this photo, the blue area should start in y=9756 until
y=39512
You actually mean
for example in this photo, the blue area should start in y=29756 until
y=39512
What happens is that your green area is painted from 0 to 29756 then your blue area on top of it from 0 to 9756. You need to shift each area on top of the previous one.
Easiest is to preprocess your data to do it.
D3.js can do it for you, see Stack Layout. This computes the y0 and y for all of your layers.
EDIT:
var stack = d3.layout.stack()
.x(function(d) { return d.Type }) // tell d3 to use Type as x value
.y(function(d) { return d.Sum }); // tell d3 to use Sum as y value
var stackData = stack(data);
After that, you data is augmented, i.e. each entry contains an additional y and y0 values that you can use directly to plot (in your y and height attribute).