How can I add legend and tooltips in this pie chart? - d3.js

How can I add tooltips in this piechart?
`
``
<div>
<button onclick="update(data2006)">2006</button>
<button onclick="update(data2007)">2007</button>
</div>
<select id="selectButton">
<option></option>
</select>
<div id="pie_chart"></div>
<script>
var width = 450;
height = 450;
margin = 40;
var radius = Math.min(width, height) / 2 - margin;
var tooltip = d3
.select('#pie_chart')
.append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
var svg = d3
.select('#pie_chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
var data2006 = {
AsiaMusic: 2,
Movies: 1,
Lifestyle: 1,
Hiphop: 1,
Entertainment: 2,
Actiongame: 1,
Music: 3,
};
var data2007 = {
AsiaMusic: 2,
Movies: 1,
Lifestyle: 1,
Hiphop: 1,
Entertainment: 2,
Actiongame: 1,
Music: 3,
};
function update(data) {
var pie = d3
.pie()
.value(function (d) {
return d.value;
})
.sort(function (a, b) {
console.log(a);
return d3.ascending(a.key, b.key);
});
var data_ready = pie(d3.entries(data));
var u = svg.selectAll('path').data(data_ready);
u.enter()
.append('path')
.merge(u)
.transition()
.duration(1000)
.attr('d', d3.arc().innerRadius(0).outerRadius(radius))
.attr('fill', function (d) {
return color(d.data.key);
})
.attr('stroke', 'white')
.style('stroke-width', '2px')
.style('opacity', 1);
u.exit().remove();
});
}
update(data2006);
</script>
I have been trying to find out the way to put some legend or tooltips in this pie chart but I don't know how to do it. I tried to tool tip and legend in this piechart but I cannot figure it out how to do it
Also, I hope I can know where I can find officical documentation of d3js.

Related

D3 path.transition is not a function

So I have a piechart that all transitions will not work on with the message that they're not a function. Which is true when I dig in the console. The window.d3 har a transition function, but not d3.selectAll('path').transition
I'm a bit of a loss as to why this does not work. Obviously my selection to do the transition is wrong, but how?
(function(d3) {
'use strict';
var tooltip = d3.select('body')
.append('div')
.attr('class', 'pie-tooltip')
.style("opacity", 0);
/**
* Width and height has to be the same for a circle, the variable is in pixels.
*/
var width = 350;
var height = 350;
var radius = Math.min(width, height) / 2;
/**
* D3 allows colours to be defined as a range, beneath is input the ranges in same order as our data set above. /Nicklas
*/
var color = d3.scaleOrdinal()
.range(['#ff875e', '#f6bc58', '#eae860', '#85d280']);
var svg = d3.select('#piechart')
.append('svg')
.attr('width', width+20)
.attr('height', height+20)
.append('g')
.attr('transform', 'translate(' + ((width+20) / 2) +
',' + ((height+20) / 2) + ')');
var arc = d3.arc()
.innerRadius(0)
.outerRadius(radius);
/**
* bArc = biggerArc, this is the arc with a bigger outerRadius thats used when a user mouseovers.
*/
var bArc = d3.arc()
.innerRadius(0)
.outerRadius(radius*1.05);
var pie = d3.pie()
.value(function(d){
return d.value;
})
.sort(null);
var path = svg.selectAll('path')
.data(pie(data))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d) {
return color(d.data.color);
});
path.transition()
.duration(600)
.attrTween("d", makePieAnimation);
path.on("mouseover", function(d){
d3.select(this)
.attr("width", width+10)
.attr("height", height+10);
tooltip.transition()
.duration(200)
.style("opacity", .9)
.style("display", null)
.text(d.data.label + ": " + d.data.value);
d3.select(this).transition()
.duration(300)
.style('fill', d.data.highlight).attr("d", bArc);
});
path.on("mousemove", function(){
tooltip.style("top", (event.pageY-10)+"px")
.style("left",(event.pageX+10)+"px");
});
path.on("mouseout", function(d){
d3.select(this).style('fill', d.data.color);
tooltip.transition()
.duration(300)
.style("opacity", 0);
d3.select(this).transition()
.duration(300)
.attr("d", arc);
});
/**
* makePieAnimation() animates the creation of the pie, setting startangles to 0, interpolating to full circle on creation in path.transition. D3 magic.
* b is an array of arc objects.
*/
function makePieAnimation(b) {
b.innerRadius = 0;
var angles = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) {
return arc(angles(t));
};
}
})(window.d3);
$.each(data, function (index, value) {
$('#legend').append('<span class="label label-legend" style="background-color: ' + value['color'] + '">' + value['label'] + ': ' + value['value'] + '</span>');
});
EDIT:
After digging around Ive found that the d3 file used by typo3 is manually edited: https://forge.typo3.org/issues/83741
I cannot see how this impacts this issue, but it does. When using a CDN with d3 v4.12.2 the error disappears.

SVG path goes beyond chart area on d3 brush

When I try to brush & zoom a portion of the line chart, some parts of the selected area render outside the chart.
Code and behavior reproduction can be found at this jsbin.
Click & drag to select a portion and zoom in, double click to zoom out.
var svg = d3
.select('body')
.append('svg')
.attr('class', 'chart')
.attr('width', 960)
.attr('height', 500);
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 40
};
var width = +svg.attr('width') - margin.left - margin.right;
var height = +svg.attr('height') - margin.top - margin.bottom;
var g = svg
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var timeParser = d3.timeParse('%Y-%m-%d');
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var brush = d3.brush().on('end', brushended);
var idleTimeout;
var idleDelay = 350;
var x0;
var y0;
var xAxis;
var yAxis;
var line = d3
.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.price);
})
.curve(d3.curveNatural);
var start = new Date();
var end = new Date(start.toDateString());
start.setFullYear(end.getFullYear() - 1);
var startStr = start.toISOString().slice(0, 10);
var endStr = end.toISOString().slice(0, 10);
var url = "https://api.coindesk.com/v1/bpi/historical/close.json?start=" + startStr + "&end=" + endStr;
d3.json(url, function(error, response) {
var data = Object.keys(response.bpi).map(function(date) {
return {
date: timeParser(date),
price: response.bpi[date]
};
});
x0 = d3.extent(data, function(d) {
return d.date;
});
y0 = d3.extent(data, function(d) {
return d.price;
});
x.domain(x0);
y.domain(y0);
xAxis = d3.axisBottom(x);
yAxis = d3.axisLeft(y);
g
.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
g
.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
g
.append('path')
.attr('class', 'line')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('d', line);
svg
.append('g')
.attr('class', 'brush')
.call(brush);
});
function brushended() {
var s = d3.event.selection;
if (!s) {
if (!idleTimeout) {
return (idleTimeout = setTimeout(idled, idleDelay));
}
x.domain(x0);
y.domain(y0);
} else {
x.domain([s[0][0] - 40, s[1][0] - 40].map(x.invert, x));
y.domain([s[1][1] - 40, s[0][1] - 40].map(y.invert, y));
svg.select('.brush').call(brush.move, null);
}
zoom();
}
function idled() {
idleTimeout = null;
}
function zoom() {
var t = svg.transition().duration(750);
svg
.select('.axis--x')
.transition(t)
.call(xAxis);
svg
.select('.axis--y')
.transition(t)
.call(yAxis);
svg
.select('.line')
.transition(t)
.attr('d', line);
}
.chart {
border: 1px solid #bdbdbd;
box-sizing: border-box;
}
<script src="https://unpkg.com/d3#4.12.2/build/d3.min.js"></script>
That's the expected behaviour. The most common way to deal with that is using a <clipPath>.
For instance, in your case:
var clipPath = g.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
Then, in your path:
g.append('path')
//etc...
.attr("clip-path", "url(#clip)");
Here is the updated JSBin: https://jsbin.com/tatuhipevi/1/edit?js,output
And here the updated S.O. snippet:
var svg = d3
.select('body')
.append('svg')
.attr('class', 'chart')
.attr('width', 960)
.attr('height', 500);
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 40
};
var width = +svg.attr('width') - margin.left - margin.right;
var height = +svg.attr('height') - margin.top - margin.bottom;
var g = svg
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var clipPath = g.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var timeParser = d3.timeParse('%Y-%m-%d');
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var brush = d3.brush().on('end', brushended);
var idleTimeout;
var idleDelay = 350;
var x0;
var y0;
var xAxis;
var yAxis;
var line = d3
.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.price);
})
.curve(d3.curveNatural);
var start = new Date();
var end = new Date(start.toDateString());
start.setFullYear(end.getFullYear() - 1);
var startStr = start.toISOString().slice(0, 10);
var endStr = end.toISOString().slice(0, 10);
var url = "https://api.coindesk.com/v1/bpi/historical/close.json?start=" + startStr + "&end=" + endStr;
d3.json(url, function(error, response) {
var data = Object.keys(response.bpi).map(function(date) {
return {
date: timeParser(date),
price: response.bpi[date]
};
});
x0 = d3.extent(data, function(d) {
return d.date;
});
y0 = d3.extent(data, function(d) {
return d.price;
});
x.domain(x0);
y.domain(y0);
xAxis = d3.axisBottom(x);
yAxis = d3.axisLeft(y);
g
.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
g
.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
g
.append('path')
.attr('class', 'line')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('d', line)
.attr("clip-path", "url(#clip)");
svg
.append('g')
.attr('class', 'brush')
.call(brush);
});
function brushended() {
var s = d3.event.selection;
if (!s) {
if (!idleTimeout) {
return (idleTimeout = setTimeout(idled, idleDelay));
}
x.domain(x0);
y.domain(y0);
} else {
x.domain([s[0][0] - 40, s[1][0] - 40].map(x.invert, x));
y.domain([s[1][1] - 40, s[0][1] - 40].map(y.invert, y));
svg.select('.brush').call(brush.move, null);
}
zoom();
}
function idled() {
idleTimeout = null;
}
function zoom() {
var t = svg.transition().duration(750);
svg
.select('.axis--x')
.transition(t)
.call(xAxis);
svg
.select('.axis--y')
.transition(t)
.call(yAxis);
svg
.select('.line')
.transition(t)
.attr('d', line);
}
.chart {
border: 1px solid #bdbdbd;
box-sizing: border-box;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
Also, it's a good idea using a <clipPath> in the axes as well.

Changing the angle of a D3 doughnut chart to 180

I need to make my doughnut chart a horizontal graph like in this image >
this is the code that i use for other doughnut charts
var dataset = {
hddrives: [total - value, value],
};
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range([secondColor, mainColor]);
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 70);
var svg = d3.select(divName).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
//Draw the Circle
svg.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 65)
.attr("fill", "#F6FBF3");
var path = svg.selectAll("path")
.data(pie(dataset.hddrives))
.enter().append("path")
.attr("class", "arc")
.attr("fill", function (d, i) { return color(i); })
.attr("d", arc);
svg.append("text")
.attr("dy", "0em")
.style("text-anchor", "middle")
.attr("class", "inside")
.attr("font-size", "30px")
.text(function (d) { return value; });
svg.append("text")
.attr("dy", "1.5em")
.style("text-anchor", "middle")
.attr("class", "data")
.text(function (d) { return nomeGtin; });
}
I tried messing around with the attr values and the arc value, but without success, any ideas on how to approach this? Thanks
That isn't much of a donut chart, it's now a stacked bar chart (with a single bar). The pie and arc helpers aren't much help for that, they are concerned with calculating angles and circular things; you are now dealing with rectangles. d3.stack could help, but is probably overkill. Here's a quicky where I've just done the math (ie positioning) myself:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
</head>
<body>
<script>
var width = 500,
height = 200,
w = 300,
h = 100;
var svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height);
var total = 0,
l = 0,
// fake random data
raw = d3.range(5).map(function(d){
var v = Math.random() * 10;
total += v;
return v;
}),
// calculate percents and cumulative position
data = raw.map(function(d){
var rv = {
v: d,
l: l,
p: d/total
}
l += rv.p;
return rv;
});
// scale and color
var s = d3.scale.linear()
.range([0, w])
.domain([0, 1]),
c = d3.scale.category20();
svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('x', function(d){
return s(d.l) + width/2 - w/2; // place based on cumulative
})
.attr('width', function(d){
return s(d.p); // width from scale
})
.attr('height', h)
.attr('y', height/2 - h/2)
.style('fill', function(d,i){
return c(i);
})
.style('stroke', 'white')
.style('stroke-width', '2px');
</script>
</body>
</html>

Change bar chart into pie chart

initiaa Bar chart
I want to convert this bar chart into pie chart. I have tried plaing around with the transition function and appending code. But it doesnot seem to work.
edited pen
Here's the code for my pie chart
var width = 150;
var height = 150;
var radius = Math.min(width, height) / 2;
var donutWidth = 75;
var legendRectSize = 18;
var legendSpacing = 4;
var color = d3.scale.category20b();
var svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')');
var arc = d3.svg.arc()
.innerRadius(radius - donutWidth)
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function (d) {
return d.count;
})
.sort(null);
var tooltip = d3.select('#chart')
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'label');
tooltip.append('div')
.attr('class', 'count');
tooltip.append('div')
.attr('class', 'percent');
//d3.csv('weekdays.csv', function (error, dataset) {
dataset.forEach(function (d) {
d.count = +d.count;
d.enabled = true; // NEW
});
var path = svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function (d, i) {
return color(d.data.label);
}) // UPDATED (removed semicolon)
.each(function (d) {
this._current = d;
}); // NEW
path.on('mouseover', function (d) {
var total = d3.sum(dataset.map(function (d) {
return (d.enabled) ? d.count : 0; // UPDATED
}));
var percent = Math.round(1000 * d.data.count / total) / 10;
tooltip.select('.label').html(d.data.label);
tooltip.select('.count').html(d.data.count);
tooltip.select('.percent').html(percent + '%');
tooltip.style('display', 'block');
});
path.on('mouseout', function () {
tooltip.style('display', 'none');
});
The data linkage seemd to be the issue. Everytime the console said invalid values.
Here;s the working fiddle
var data = [10,20,30,40,60, 80, 20, 50];
// the D3 bits...
var color = d3.scale.category10();
var width = 180;
var height = 180;
var pie = d3.layout.pie().sort(null);
var arc = d3.svg.arc()
.outerRadius(width / 2 * 0.9)
.innerRadius(width / 2 * 0.5);
var svg = d3.select(element[0]).append('svg')
.attr({width: width, height: height})
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
// add the <path>s for each arc slice
svg.selectAll('path').data(pie(data)) // our data
.enter().append('path')
.style('stroke', 'white')
.attr('d', arc)
.attr('fill', function(d, i){ return color(i) });

Adding a legend to a pie chart in D3js

I'm trying to plot a pie chart with a legend inside of it. And I got into troubles to get it plotted, since I get the errors abound undefined variables. I managed to draw the chart itself and the half of the legend, but not in the right colors, what should match the pie chart.
function drawPieChart(d3div, chart_data) {
// chart_data.data is a list of data elements.
// each should contain fields: val, col, name
d3div.html(""); // clear the div
var title = getopt(chart_data, 'title', '');
// desired width and height of chart
var w = getopt(chart_data, 'width', 300);
var h = getopt(chart_data, 'height', 300);
var pad = getopt(chart_data, 'pad', 50);
var textmargin = getopt(chart_data, 'textmargin', 20);
var r = Math.min(w, h) / 2 - pad; // radius of pie chart
var div = d3div.append('div');
if(title !== '') {
div.append('p').attr('class', 'pietitle').text(title);
}
var arc = d3.svg.arc()
.outerRadius(r)
.cornerRadius(20)
.innerRadius(150);
var arcLarge = d3.svg.arc()
.innerRadius(150)
.cornerRadius(20)
.outerRadius(r + 50);
var toggleArc = function(p){
p.state = !p.state;
var dest = p.state ? arcLarge : arc;
d3.select(this).select("path").transition()
.duration(160)
.attr("d", dest);};
var pie = d3.layout.pie()
.padAngle(.03)
.sort(null)
.value(function(d) { return d.val; });
var svg = d3.select("#piechart").append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(chart_data.data))
.enter().append("g")
.attr("class", "arc")
.attr("stroke", "#999")
.attr("id",function(d){return d.data;})
.on("mouseover",toggleArc)
.on("mouseout",toggleArc);
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return d.data.col; });
var color = d3.scale.category20b();
var legendRectSize = 18;
var legendSpacing = 4;
// FROM here the code is not produced the desired result
var legend = svg.selectAll('.legend')
.data(chart_data.data)
.enter()
.append('g')
.attr('class', 'legend')
.attr("id",function(d){return d.data;})
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * chart_data.data.length / 2;
var horz = -2 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.data(chart_data.data)
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style("fill", function(d) { return d.data.col; });
legend.append("text")
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d.data.name; });
}
The code actually works fine untill the line var legend = svg.selectAll('.legend')
Then i start to define the legend, but D3 complains about undefined d.data every time i try to access d.data below the line I written above(also in the last line of the code).
I don't understand where i got on the wrong way.
If instead of defining the whole non working part(var legend...) i write this code:
g.append("text")
.attr("stroke", "none")
.attr("fill", function(d) { return d.data.col; })
.text(function(d) { return d.data.name; });
I'm able to access the d.data.name.
Unfortunately wrong colors of the boxes and not description.
Thanks!

Resources