d3.js lollipop chart - animated - javascript

I am working on a d3 application - which features a bar chart with nodules on the top. I am keen to get this animated - so the bars grow to the point of rest and the nodules sprout like flowers.
So the nodules are either developed at the start and the bars just rise -- or the bars rise up and then the nodules flower.
//old js fiddle
http://jsfiddle.net/s1f4hzpu/1/
//current animation attempts
http://jsfiddle.net/9yvn8c4q/
var $this = $('.lollipopchart');
var data = [{
label: 'Toblerone',
value: 10,
},
{
label: 'Snickers',
value: 25,
},
{
label: 'Jawbreakers',
value: 60,
},
{
label: 'Gummi Worms',
value: 20,
},
];
var width = $this.data('width'),
height = $this.data('height');
var color = d3.scaleOrdinal()
.range(["#eb6383", "#fa9191", "#ffe9c5", "#b4f2e1"]);
data.forEach(function(d) {
d.total = +d.value;
});
var margin = {
top: 20,
right: 20,
bottom: 85,
left: 20
},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width])
.padding(0.9);
var y = d3.scaleLinear()
.range([height, 0]);
x.domain(data.map(function(d) {
return d.label;
}));
y.domain([0, d3.max(data, function(d) {
return d.total;
})]);
var svg = d3.select($this[0])
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('class', 'lollipopchart')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var lollipop = svg.append('g').attr('class', 'lollipop');
var bars = lollipop
.append("g")
.attr('class', 'bars')
bars.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr('fill', function(d, i) {
return color(i);
})
.attr("x", function(d) {
return x(d.label);
})
.attr("width", x.bandwidth())
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.total);
});
var lolliradian = 10;
var circles = lollipop
.append("g")
.attr('class', 'circles');
circles.selectAll("circle")
.data(data)
.enter()
.append("circle")
//.transition()
//.duration(1000)
.attr("cx", function(d) {
return (x(d.label) + x.bandwidth() / 2);
})
.attr("cy", function(d) {
return y(d.value);
})
.attr("r", lolliradian)
.attr('fill', function(d, i) {
return color(i);
})
var innercircles = lollipop
.append("g")
.attr('class', 'innercircles');
innercircles.selectAll("circle")
.data(data)
.enter()
.append("circle")
//.transition()
//.duration(1000)
.attr("cx", function(d) {
return (x(d.label) + x.bandwidth() / 2);
})
.attr("cy", function(d) {
return y(d.value);
})
.attr("r", lolliradian - 5)
.attr('fill', '#ffffff')
lollipop.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
lollipop.append("g")
.call(d3.axisLeft(y));
body {
background: #eeeeee;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<h1>LolliPop I</h1>
<div class="lollipopchart" data-width="300" data-height="300" />

Firstly, you don't need jQuery, you can do everything you want with regular d3.
Regardless, if you want to make the bars grow, you need to know that y=0 is the top and y=height is the bottom, so you need to actually decrease y as you increase height.
I also wouldn't draw a circle in front of another circle, but would use stroke and fill colours instead. If you make a stroke of 5 pixels wide, then it looks the same as in your example.
var data = [{
label: 'Toblerone',
value: 10,
},
{
label: 'Snickers',
value: 25,
},
{
label: 'Jawbreakers',
value: 60,
},
{
label: 'Gummi Worms',
value: 20,
},
];
var width = +d3.select(".lollipopchart").attr('data-width'),
height = +d3.select(".lollipopchart").attr('data-height');
var color = d3.scaleOrdinal()
.range(["#eb6383", "#fa9191", "#ffe9c5", "#b4f2e1"]);
data.forEach(function(d) {
d.total = +d.value;
});
var margin = {
top: 20,
right: 20,
bottom: 85,
left: 20
},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width])
.padding(0.9);
var y = d3.scaleLinear()
.range([height, 0]);
x.domain(data.map(function(d) {
return d.label;
}));
y.domain([0, d3.max(data, function(d) {
return d.total;
})]);
var svg = d3.select('.lollipopchart')
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('class', 'lollipopchart')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var lollipop = svg.append('g').attr('class', 'lollipop');
var bars = lollipop
.append("g")
.attr('class', 'bars')
bars.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr('fill', function(d, i) {
return color(i);
})
.attr("x", function(d) {
return x(d.label);
})
.attr("width", x.bandwidth())
.attr("y", height)
.transition()
.duration(1500)
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.total);
});
var lolliradian = 10;
var circles = lollipop
.append("g")
.attr('class', 'circles');
circles.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return (x(d.label) + x.bandwidth() / 2);
})
.attr("cy", height)
.attr("r", x.bandwidth() / 2)
.attr("fill", "white")
.attr("stroke-width", 5)
.attr('stroke', function(d, i) {
return color(i);
})
.transition()
.duration(1500)
.attr("cy", function(d) {
return y(d.value);
})
.on("end", function() {
d3.select(this)
.transition()
.duration(500)
.attr("r", lolliradian);
});
lollipop.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
lollipop.append("g")
.call(d3.axisLeft(y));
body {
background: #eeeeee;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<h1>LolliPop I</h1>
<div class="lollipopchart" data-width="300" data-height="300" />

Related

d3v4 Lollipop chart - refactored conversion - static

-- end goal
-- current attempt
I am interested in adapting the d3v3 lollipop chart into v4.
ref - https://bl.ocks.org/laysent/5739f087ec78e4386a6a
//version 3
https://jsfiddle.net/urmtj3v4/1/
//version 4 -- more simple variant - with placeholder circles as a POC
https://jsfiddle.net/ynmcfk3a/1/
^ but the circles are not sitting on the stalks correctly - they seem offset - what the issue is with the current calculation
var lolliradian = 20;
lollipop.selectAll("circle")
.data(data)
.enter()
.append("circle")
//.transition()
//.duration(1000)
.attr("cx", function(d) { return x(d.label) - x.bandwidth()/2 + lolliradian/2 ; })
.attr("cy", function(d) { return y(d.value); })
.attr("r", lolliradian)
.attr("fill", "#69b3a2");
I've managed to finish conversion of the chart.
//d3v4 lollipop chart I
https://jsfiddle.net/ynmcfk3a/1/
A hybrid bar chart with circle tips.
LolliPop I
<!--libs-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<!--js-->
<script>
$(document).ready(function() {
var $this = $('.lollipopchart');
var data = [
{
label: 'Toblerone',
value: 10,
},
{
label: 'Snickers',
value: 25,
},
{
label: 'Jawbreakers',
value: 60,
},
{
label: 'Gummi Worms',
value: 20,
},
];
var width = $this.data('width'),
height = $this.data('height');
var color = d3.scaleOrdinal()
.range(["#eb6383", "#fa9191", "#ffe9c5", "#b4f2e1"]);
data.forEach(function(d) {
d.total = +d.value;
});
var margin = {top: 20, right: 20, bottom: 85, left: 20},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width])
.padding(0.9);
var y = d3.scaleLinear()
.range([height, 0]);
x.domain(data.map(function(d) { return d.label; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
var svg = d3.select($this[0])
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('class', 'lollipopchart')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var lollipop = svg.append('g').attr('class', 'lollipop');
var bars = lollipop
.append("g")
.attr('class', 'bars')
bars.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr('fill', function(d, i) {
return color(i);
})
.attr("x", function(d) { return x(d.label); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.total); });
var lolliradian = 10;
var circles = lollipop
.append("g")
.attr('class', 'circles');
circles.selectAll("circle")
.data(data)
.enter()
.append("circle")
//.transition()
//.duration(1000)
.attr("cx", function(d) { return (x(d.label) + x.bandwidth()/2); })
.attr("cy", function(d) { return y(d.value); })
.attr("r", lolliradian)
.attr('fill', function(d, i) {
return color(i);
})
var innercircles = lollipop
.append("g")
.attr('class', 'innercircles');
innercircles.selectAll("circle")
.data(data)
.enter()
.append("circle")
//.transition()
//.duration(1000)
.attr("cx", function(d) { return (x(d.label) + x.bandwidth()/2); })
.attr("cy", function(d) { return y(d.value); })
.attr("r", lolliradian-5)
.attr('fill', '#ffffff')
lollipop.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
lollipop.append("g")
.call(d3.axisLeft(y));
});
</script>
<!--js-->
<!--css-->
<style>
body {
background: #ffd;
}
</style>
<!--css-->
</head>
<body>
<h1>LolliPop I</h1>
<!--html-->
<div
class="lollipopchart"
data-width="300"
data-height="300"
/>
<!--html-->
</body>

Problems with grouped bar chart

I am new to d3 and trying to make a grouped bar chart following this and this websites.
The following is my code for the scales
x0 = d3.scaleTime()
.domain([
d3.min(dataset, function(d) {return d.date;}),
d3.max(dataset, function(d) {return d.date;})
])
.range([0,w]);
x1 = d3.scaleOrdinal()
.domain([dataset.WorldPopulation, dataset.InternetUser]);
// .rangeRound([0, x0.bandwidth()]);
y = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) {return d.WorldPopulation})])
.range([h,0]);
In the website above they use scaleOrdinal but I used scaleTime as x0. Therefore I'm not too sure if that works.
This is my code for the append(rect) for the bar chart
var date = svg.selectAll(".date")
.data(dataset)
.enter()
.append("g")
.attr("class", "g")
.attr("transform", function(d) {return "translate(" + x0(d.date) + ",0)";});
/* Add field1 bars */
date.selectAll(".bar.field1")
.data(d => [d])
.enter()
.append("rect")
.attr("class", "bar field1")
.style("fill","blue")
.attr("x", d => x0(d.WorldPopulation))
.attr("y", d => y(d.WorldPopulation))
.attr("width", x1.bandwidth())
.attr("height", d => {
return height - margin.top - margin.bottom - y(d.WorldPopulation)
});
/* Add field2 bars */
date.selectAll(".bar.field2")
.data(d => [d])
.enter()
.append("rect")
.attr("class", "bar field2")
.style("fill","red")
.attr("x", d => x0(d.InternetUser))
.attr("y", d => y(d.InternetUser))
.attr("width", x1.bandwidth())
.attr("height", d => {
return height - margin.top - margin.bottom - y(d.InternetUser)
});
And this is my csv file. Not too sure how to upload excel so I took a screenshot.
Any help provided will be appreciated. Feel free to request for any extra snippets of my code for clarification.
edit: After tweaking the code, no errors are shown but the svg is not produced either.
Using scaleBand for both x0 and x1, the following code works:
<script src="js/d3.v4.js"></script>
<script>
var margin = { top: 20, right: 20, bottom: 30, left: 40 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scaleBand()
.rangeRound([0, width]).padding(.1);
var x1 = d3.scaleBand();
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x0)
.tickSize(0);
var yAxis = d3.axisLeft()
.scale(y);
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 + ")");
d3.csv("InternetUserStats.csv", function (error, data) {
var dates = data.map(function (d) { return d.date; });
var ymax = d3.max(data, function (d) { return Math.max(d.WorldPopulation, d.InternetUser); });
x0.domain(dates);
x1.domain(['WorldPopulations', 'InternetUsers']).rangeRound([0, x0.bandwidth()]);
y.domain([0, ymax]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var date = svg.selectAll(".g")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function (d) { return "translate(" + x0(d.date) + ",0)"; });
/* Add field1 bars */
date/*.selectAll(".bar.field1")
.data(data)
.enter()*/
.append("rect")
.attr("class", "bar field1")
.style("fill", "blue")
.attr("x", function (d) { return x1('WorldPopulations'); })
.attr("y", function (d) { return y(+d.WorldPopulation); })
.attr("width", x0.bandwidth() / 2)
.attr("height", function (d) {
return height /*- margin.top - margin.bottom*/ - y(d.WorldPopulation)
});
/* Add field2 bars */
date/*.selectAll(".bar.field1")
.data(data)
.enter()*/
.append("rect")
.attr("class", "bar field2")
.style("fill", "red")
.attr("x", function (d) { return x1('InternetUsers'); })
.attr("y", function (d) { return y(d.InternetUser); })
.attr("width", x0.bandwidth() / 2)
.attr("height", function (d) {
return height /*- margin.top - margin.bottom*/ - y(d.InternetUser)
});
});
</script>
EDIT: the following code adds a legend that is based on your comment and your second link:
//This code goes after field2 bars are added.
/* Legend */
var legend = svg.selectAll(".legend")
.data(['WorldPopulations', 'InternetUsers']) //Bind an array of the legend entries
.enter().append("g")
.attr("class", "legend")
.attr("font-family", "sans-serif")
.attr("font-size", 13)
.style("text-anchor", "end")
.attr("transform", function (d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function (d) {
// Return "blue" for "WorldPopulations" and "red" for "InternetUsers":
return ((d === "WorldPopulations") ? "blue" : "red");
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
//.style("text-anchor", "end") //Already applied to .legend
.text(function (d) { return d; });

D3 vertical line beetween grouped chart bars spacing

The problem:
The number of groups is dynamic, and vertical line (separator) needs dynamic padding. Group width im getting with x0.rangeBand(). Is there any way to get width of space beetween two groups dynamically?
Peace of code:
.....
var slice = svg.selectAll(".chart")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function (d) {
return "translate(" + x0(d.category) + ",0)";
});
// Create rectangles of the correct width
slice.selectAll("rect")
.data(function (d) {
return d.values;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function (d) {
return x1(d.rate);
})
.style("fill", function (d) {
return color(d.rate)
})
.attr("y", function (d) {
return y(0);
})
.attr("height", function (d) {
return height - y(0);
})
.on("mouseover", function (d) {
d3.select(this).style("fill", d3.rgb(color(d.rate)).darker(2));
tip.show(d);
})
.on("mouseout", function (d) {
tip.hide
d3.select(this).style("fill", color(d.rate));
})
slice.append("line")
.attr("class", "blabla")
.attr("x1", x0.rangeBand()+20)
.attr("x2", x0.rangeBand()+20)
.attr("y1", 0)
.attr("y2", height + margin.top + margin.bottom)
.style("stroke-width", 1)
.style("stroke", "#000");
.....
This is how it looks with few groups
This is how it looks with many groups
Because I see no reason why to stick to d3v3 and I can't find the d3v3 documentation easily for ordinal scales here is a d3v5 version of the code with correct placement of the vertical bars. You have to use the bandwidth and the step to calculate the position.
It is an adaptation of the example in your other question : https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad
I doubled the number of groups and it looks nice.
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1);
var x1 = d3.scaleBand();
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x0)
.tickSize(0);
var yAxis = d3.axisLeft()
.scale(y);
var color = d3.scaleOrdinal()
.range(["#ca0020","#f4a582","#d5d5d5","#92c5de","#0571b0"]);
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 + ")");
d3.json("barchart.json", {credentials: 'same-origin'}).then(function(data) {
var categoriesNames = data.map(function(d) { return d.categorie; });
var rateNames = data[0].values.map(function(d) { return d.rate; });
x0.domain(categoriesNames);
x1.domain(rateNames).range([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(categorie) { return d3.max(categorie.values, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.style('opacity','0')
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight','bold')
.text("Value");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var slice = svg.selectAll(".slice")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform",function(d) { return "translate(" + x0(d.categorie) + ",0)"; });
slice.selectAll("rect")
.data(function(d) { return d.values; })
.enter().append("rect")
.attr("width", x1.bandwidth())
.attr("x", function(d) { return x1(d.rate); })
.style("fill", function(d) { return color(d.rate) })
.attr("y", function(d) { return y(0); })
.attr("height", function(d) { return height - y(0); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.rate)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.rate));
});
slice.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
slice.append("line")
.attr("class", "blabla")
.attr("x1", (x0.step() - x0.bandwidth())*0.5 + x0.bandwidth())
.attr("x2", (x0.step() - x0.bandwidth())*0.5 + x0.bandwidth())
.attr("y1", 0)
.attr("y2", height + margin.top + margin.bottom)
.style("stroke-width", 1)
.style("stroke", "#000");
//Legend
var legend = svg.selectAll(".legend")
.data(data[0].values.map(function(d) { return d.rate; }).reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d,i) { return "translate(0," + i * 20 + ")"; })
.style("opacity","0");
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d) { return color(d); });
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {return d; });
legend.transition().duration(500).delay(function(d,i){ return 1300 + 100 * i; }).style("opacity","1");
});
So the trick is to recalculate padding self. V3 does not have band.step() function so I added this method to get the middle of two items:
function getMiddle(x0){
var rng = x0.range();
var band = x0.rangeBand();
var padding = 0;
if(rng.length>1){
padding = (rng[1]-rng[0] - band) *0.5;
}
return band + padding;
}
And usage:
slice.append("line")
.attr("x1", getMiddle(x0))
.attr("x2", getMiddle(x0))
.attr("y1", 0)
.attr("y2", height + margin.top + margin.bottom)
.style("stroke-width", 1)
.style("stroke", "#000");

Append text to Grouped Bar Chart in d3js v4

Im trying to append text to a Grouped Bar Chart in d3js v4, more specifically, the values corresponding to each bar. I want the numbers to be displayed inside the bars and I can't get it to work. (Like this: http://bl.ocks.org/ctiml/541d7cc770108ccff79a)
But I want it to work in d3js v4 instead.
Here's my code, I've commented out the part of the code that is supposed to append the text
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
<style>
body {
margin:auto;
width:1100px;
}
.axis .domain {
display: none;
}
.bar1 {
opacity:.9;
}
.yaxis {
stroke-dasharray: 1 1;
opacity:.8;
font-family:arial;
font-size:10px;
}
path {
display:none;
}
.baseline {
stroke:#000;
stroke-width:1px;
shape-rendering: crispEdges;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body><br>
<div id="chart1"></div>
<script>
var margin = {top: 20, right: 110, bottom: 30, left: 40},
width = 350 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
formatCount = d3.format("s");
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1);
var x1 = d3.scaleBand();
//.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["steelblue", "lightblue", "darkorange"]);
var g = d3.select("#chart1").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("data.csv", function(d, i, columns) {
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
return d;
}, function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { return d[key]; }); })]).nice();
g.append("g")
.attr("class", "yaxis")
.call(d3.axisLeft(y).ticks(8,"s").tickSize(-width));
g.append("line")
.attr("class", "baseline")
.attr("x1",0)
.attr("x2",width)
.attr("y1",y(0))
.attr("y2",y(0));
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; })
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("class", "bar1")
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x1.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return z(d.key); });
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0));
//g.selectAll(".bar-text")
// .data(data)
//.enter().append("text")
// .attr("class",function(d) { return "bar-text " + d.value; })
// .attr("x", function(d) { return x1(d.key)+20; })
// .attr("y", function(d) { return y(d.value)+10; })
//.attr("fill","#000")
// .text(function(d) { return formatCount(d.value)});
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "start")
.selectAll("g")
.data(keys.slice()) //.reverse
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width + 15)
.attr("y", 4)
.attr("width", 12)
.attr("height", 12)
.attr("fill", z);
legend.append("text")
.attr("x", width + 35)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) { return d; });
});
</script>
</body>
</html>
And here's the csv format:
State,Team 1,Team 2,Team 3
2015,2704659,4499890,2159981
2016,2027307,3277946,1420518
Lots of ways to do this; here's how I would do it.
First, keep a reference to the groups g element so that we can append our text with the bars:
var gE = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) {
return "translate(" + x0(d.State) + ",0)";
});
gE.selectAll("rect")
.data(function(d) {
...
Then use a sub-selection to add the text:
gE.selectAll("text")
.data(function(d) {
return [d['Team 1'], d['Team 2'], d['Team 3']];
})
.enter()
.append("text")
...
Running code is here.

D3 V4: Zoom & drag multi-series line chart

I am drawing charts with d3 4.2.2 in my Angular2 project. I created a multi series line chart and added zoom and drag properties. Now the chart is zooming on mouse scroll event but it zoom only X-axis and Y-axis. And it can be dragged only X-axis & Y-axis but chart cannot be dragged. When I do zooming or dragging those events are applying only to the two axis es but not for the chart. Following is what I am doing with my code.
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var zoom = d3.zoom()
.scaleExtent([1, 5])
.translateExtent([[0, -100], [width + 90, height + 100]])
.on("zoom", zoomed);
var svg = d3.select(this.htmlElement).append("svg")
.attr("class", "line-graph")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("pointer-events", "all")
.call(zoom);
var view = svg.append("rect")
.attr("class", "view")
.attr("x", 0.5)
.attr("y", 0.5)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("fill", "#EEEEEE")
.style("stroke", "#000")
.style("stroke-width", "0px");
// parse the date / time
var parseDate = d3.timeParse("%Y-%m-%d");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var z = d3.scaleOrdinal(d3.schemeCategory10);
// define the line
var line = d3.line()
.x( (d) => {
return x(d.date);
})
.y( (d) => {
return y(d.lookbookcount);
});
z.domain(d3.keys(data[0]).filter(function (key) {
return key !== "date";
}));
// format the data
data.forEach( (d)=> {
d.date = parseDate(d.date);
});
var lookBookData = z.domain().map(function (name) {
return {
name: name,
values: data.map( (d) => {
return {date: d.date, lookbookcount: d[name], name: name};
})
};
});
x.domain(d3.extent(data, (d) => {
return d.date;
}));
y.domain([
d3.min([0]),
d3.max(lookBookData, (c) => {
return d3.max(c.values,
(d) => {
return d.lookbookcount;
});
})
]);
z.domain(lookBookData.map( (c) => {
return c.name;
}));
var xAxis = d3.axisBottom(x)
.ticks(d3.timeDay.every(1))
.tickFormat(d3.timeFormat("%d/%m"));
var yAxis = d3.axisLeft(y)
.ticks(10);
// Add the X Axis
var gX = svg.append("g")
.style("font", "14px open-sans")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
var gY = svg.append("g")
.style("font", "14px open-sans")
.attr("class", "axis axis--x")
.call(yAxis)
.style("cursor", "ns-resize");
// Add Axis labels
svg.append("text")
.style("font", "14px open-sans")
.attr("text-anchor", "end")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.text("Sales / Searches");
svg.append("text")
.style("font", "14px open-sans")
.attr("text-anchor", "end")
.attr("dx", ".71em")
.attr("transform", "translate(" + width + "," + (height +
(margin.bottom)) + ")")
.text("Departure Date");
var chartdata = svg.selectAll(".chartdata")
.data(lookBookData)
.enter().append("g")
.attr("class", "chartdata");
chartdata.append("path")
.classed("line", true)
.attr("class", "line")
.attr("d", function (d) {
return line(d.values);
})
.style("fill", "none")
.style("stroke", function (d) {
return z(d.name);
})
.style("stroke-width", "2px");
chartdata.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) + "," + y(d.value.lookbookcount) + ")";
})
.attr("x", 3)
.attr("dy", "0.35em")
.style("font", "14px open-sans")
.text(function (d) {
return d.name;
});
// add the dots with tooltips
chartdata.selectAll(".circle")
.data(function (d) {
return d.values;
})
.enter().append("circle")
.attr("class", "circle")
.attr("r", 4)
.attr("cx", function (d) {
console.log(d);
return x(d.date);
})
.attr("cy", function (d) {
return y(d.lookbookcount);
})
.style("fill", function (d) { // Add the colours dynamically
return z(d.name);
});
function zoomed() {
view.attr("transform", d3.event.transform);
gX.call(xAxis.scale(d3.event.transform.rescaleX(x)));
}
function resetted() {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity);
}
Any suggestions would be highly appreciated.
Thank you

Categories

Resources