I am trying to figure out how to show more text on a pie chart using mouseover than just the data that is bound to the pie. Below is my functional code
function Pie(value,names){
svg.selectAll("g.arc").remove()
var outerRadius = 100;
var innerRadius = 0;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie();
var color = d3.scale.category10();
var arcs = svg.selectAll("g.arc")
.data(pie(value))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(950,80)");
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
.on("mouseover",function(d,i) {
arcs.append("text")
.attr("dy", ".5em")
.style("text-anchor", "middle")
.style("fill", function(d,i){return "black";})
.text(d.data)
})
.on("mouseout", function(d) {
arcs.select("text").remove();
});}
The names array has the same length as the value array which is passed to the pie. I really hoped that something like this would work by replacing the above mouseover.
.on("mouseover",function(d,i) {
arcs.append("text")
.attr("dy", ".5em")
.style("text-anchor", "middle")
.style("fill", function(d,i){return "black";})
.text(function(d,i){return (d.data +" " + names[i]);)
})
But the only thing it does is to show all the elements of the values array stacked one on top of the other and the last element of the names array. It seems that i is always the last index in this case. How would I go about that? Could I show the text I want in another way? Thank you in advance.
First, the variable arcs is a data-bound d3 selection which represents all the arcs of the pie. So, by calling arcs.append, you are going to append a text element for each piece of your pie chart. I think you mean to only append one text element based on what you moused-over so re-write that as:
svg.append('text')
...
Second, in this expression:
.text(function(d,i){return (d.data +" " + names[i]);)
d and i in the mouseover function already represent the data and index of the pie slice being moused over. There is no reason to wrap this in another function and should be re-written:
.text(d.data +" " + names[i]);
Here's a complete example:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.arc path {
stroke: #fff;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.category10();
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var data = [{
value: Math.random(),
}, {
value: Math.random(),
}, {
value: Math.random(),
}, {
value: Math.random(),
}, {
value: Math.random(),
}]
var names = ["A","B","C","D","E"];
var arcs = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.style("fill", function(d,i) {
return color(i);
})
.on("mouseover", function(d, i) {
console.log(d);
svg.append("text")
.attr("dy", ".5em")
.style("text-anchor", "middle")
.style("font-size", 45)
.attr("class","label")
.style("fill", function(d,i){return "black";})
.text(names[i]);
})
.on("mouseout", function(d) {
svg.select(".label").remove();
});
</script>
</body>
</html>
Related
I have an assortment of data, and I have used it to create a donut chart. I want to make a pie chart using a breakdown of the data, which I've acquired using d3.nest to subdivide the data I had (it was currently in 3 categories: nest breaks it down into 129). Basically, I have Olympic data based on medals awarded, and I want to subdivide the data on interaction into which sports they were earned in.
I'm just not sure how to use nested data to create a pie chart, particularly if the keys are variable. I'll include my implementation for the donut chart.
var pie = d3.pie();
// color based on medal awarded
// order: gold, silver, bronze
var color = d3.scaleOrdinal()
.range(['#e5ce0c', '#e5e4e0', '#a4610a']);
var arcs = d3.select(svg).selectAll("g.arc")
.data(pie(data))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" + (w/2) + "," + ((h-25)/2) + ")");
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
.attr("stroke", "white")
.style("stroke-width", "0.5px")
.on('mouseover', function(d) {
d3.select(this).attr('opacity', .7);
})
.on('mouseleave', function(d) {
d3.select(this).attr('opacity', 1);
});
// title
d3.select(svg).append('text')
.attr('x', function(d) {
return ((w/2) - 85);
})
.attr('y', '20')
.text(function(d) {
return ('Medal breakdown for ' + country);
})
.attr('font-size', '16px');
arcs.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.text(function(d) {
return d.value;
});
Can you please confirm is this the format of data you have ?
Please find the code below and let me know if you have any doubt.
var w = 400;
var h = 400;
var r = h/2;
var color = d3.scale.category20c();
var data = [
{name:"football", medal:1},
{name:"hockey", medal:2},
{name:"cricket", medal:3},
{name:"tennis", medal:4},
{name:"table tennis", medal:5},
];
var vis = d3.select('#chart').append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.layout.pie().value(function(d){return d.medal;});
// declare an arc generator function
var arc = d3.svg.arc().outerRadius(r);
// select paths, use arc generator to draw
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.attr("fill", function(d, i){
return color(i);
})
.attr("d", function (d) {
// log the result of the arc generator to show how cool it is :)
return arc(d);
});
// add the text
arcs.append("svg:text").attr("transform", function(d){
d.innerRadius = 0;
d.outerRadius = r;
return "translate(" + arc.centroid(d) + ")";}).attr("text-anchor", "middle").text( function(d, i) {
return data[i].name;}
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.15/d3.min.js"></script>
<div id="chart"></div>
My code is like this. Can you say what is the error? My color is not showing properly although the section text is showing. the data set is the death rate of the US over time.
function clicked(d,i) {
var dataset = [d.females, d.males];
var pcolor = ["green", "pink"];
var outerRadius = 60;
var innerRadius = 0;
var arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.pie();
//Easy colors accessible via a 10-step ordinal scale
//var color = d3.scaleOrdinal(d3.schemeCategory10);
//Set up groups
var arcs = svg.selectAll("g.arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arcs")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")");
//Draw arc paths
arcs.append("path")
.attr("d", arc)
.attr("fill", function(d, i) {
console.log(d);
return pcolor(i);
});
//Labels
arcs.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.text(function(d) {
return d.value;
});
}
After plotting a donut chart, I'm trying to add some legend, based on the following example:
http://bl.ocks.org/ZJONSSON/3918369
However, I'm receiving this error:
TypeError: undefined is not an object (evaluating 'n.apply')
I've printed the return of line 131 and all the legend names are printed.
I don't know what's is causing the undefined error print.
This is my main code:
var width = 300,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(colorrange);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var svg = d3.select("#info").attr("align", "center").append("svg")
.attr("class", "piechart")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.attr("data-legend", function(d) {
return d.data.name;
})
.style("fill", function(d, i) {
return color(i);
});
g.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.text(function(d) {
return d.data.label;
});
legend = svg.append("g")
.attr("class", "legend")
.attr("transform", "translate(50,30)")
.style("font-size", "12px")
.call(d3.legend)
And this is a minimal example:
https://jsfiddle.net/g6vyk7t1/12/
You need to upload the code http://bl.ocks.org/ZJONSSON/3918369#d3.legend.js for the legend in your Javascript (just copy-and-paste it the code, it's the function d3.legend).
I am using aster plot of d3 in my project.
I want legend labels along with the arc radius outside the circle.
I could get an example of piechart showing labels along and outside the arc.
http://bl.ocks.org/Guerino1/2295263
But i am unable to implement the same in aster plot of d3.
http://bl.ocks.org/bbest/2de0e25d4840c68f2db1
Any help would be appreciated.
Thanks
Couple things to fix.
1.) You have to introduce margins into the aster plot for the labels.
2.) You then have to take the outer arcs, add a an svg g do you can group a path with a text:
var outerGroup = svg.selectAll(".solidArc")
.data(pie(data))
.enter()
.append("g")
outerGroup
.append("path")
.attr("fill", function(d) { return d.data.color; })
.attr("class", "solidArc")
.attr("stroke", "gray")
.attr("d", arc)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
outerGroup
.append("text")
.attr("transform", function(d) {
return "translate(" + centroid(60, width, d.startAngle, d.endAngle) + ")";
})
.attr("text-anchor", "middle")
.text(function(d) { return d.data.label });
Note I had to create my own centroid function to move the labels outside the arc. The code in the pie chart example you linked did not work for me (it's using a old d3 version).
Here's my centroid function stolen from the d3 source:
function centroid(innerR, outerR, startAngle, endAngle){
var r = (innerR + outerR) / 2, a = (startAngle + endAngle) / 2 - (Math.PI / 2);
return [ Math.cos(a) * r, Math.sin(a) * r ];
}
Here's a working example.
Full code:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Testing Pie Chart</title>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.4.5"></script>
<style type="text/css">
.slice text {
font-size: 16pt;
font-family: Arial;
}
</style>
</head>
<body>
<script type="text/javascript">
var canvasWidth = 500, //width
canvasHeight = 500, //height
outerRadius = 150, //radius
//outerRadius = Math.min(canvasWidth, canvasHeight) / 2,
color = d3.scale.category20(); //builtin range of colors
innerRadius =0
var colorsArray = ['#0099ff','#cc00ff','#ff3366','#cc3300','#ff6600','#ffff33','#cccc00','#0066ff'];
var dataSet = [
{"legendLabel":"Testing Text Is", "magnitude":30,'score':4.8,width:20,color:colorsArray[0] },
{"legendLabel":"Two", "magnitude":8,'score':3.2,width:20,color:colorsArray[1] },
{"legendLabel":"Three", "magnitude":40,'score':3.9,width:20,color:colorsArray[2] },
{"legendLabel":"Four", "magnitude":50,'score':3.1,width:20,color:colorsArray[3] },
{"legendLabel":"Five", "magnitude":16,'score':4.2,width:20,color:colorsArray[4] },
{"legendLabel":"Six", "magnitude":50,'score':3.1,width:20,color:colorsArray[5] },
{"legendLabel":"Seven", "magnitude":30,'score':4.3,width:20,color:colorsArray[6] },
{"legendLabel":"Eight", "magnitude":20,'score':2.3,width:20,color:colorsArray[7] }
];
var vis = d3.select("body")
.append("svg:svg")
.data([dataSet])
.attr("width", canvasWidth)
.attr("height", canvasHeight)
.append("svg:g")
.attr("transform", "translate(" + 1.5*outerRadius + "," + 1.5*outerRadius + ")") // relocate center of pie to 'outerRadius,outerRadius'
var arc = d3.svg.arc()
.outerRadius(outerRadius);
var arc1 = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(function (d) {
return (outerRadius - innerRadius) * (d.data.score / 5.0) + innerRadius;
});
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.width; });
// Select all <g> elements with class slice (there aren't any yet)
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice");
arcs.append("svg:path")
//set the color for each slice to be chosen from the color function defined above
.attr("fill", function(d, i) { return d.data.color; } )
//this creates the actual SVG path using the associated data (pie) with the arc drawing function
.attr("d", arc1);
var text = arcs.append("svg:text")
.attr("transform", function(d) {
d.outerRadius = outerRadius + 75;
d.innerRadius = outerRadius + 70;
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle") //center the text on it's origin
.style("fill", "black")
.style("font", "bold 12px Arial")
.each(function (d) {
var arr = d.data.legendLabel.split(" ");
if (arr != undefined) {
for (i = 0; i < arr.length; i++) {
d3.select(this).append("tspan")
.text(arr[i])
.attr("dy", i ? "1.2em" : 0)
.attr("x", 0)
.attr("text-anchor", "middle")
.attr("class", "tspan" + i);
}
}
});
//.text(function(d, i) { return dataSet[i].legendLabel; })
// .html(function(d, i) { return '<tspan>'+dataSet[i].legendLabel+'</tspan></n><tspan>'+dataSet[i].score+'</tspan>'})
/* arcs.append("foreignObject")
.attr("transform", function(d) {
d.outerRadius = outerRadius + 75;
d.innerRadius = outerRadius + 70;
return "translate(" + arc.centroid(d) + ")";
})
.attr("width", 50)
.attr("height", 50)
.append("xhtml:body")
.style("font", "14px 'Helvetica Neue'")
.html(function(d, i) { return dataSet[i].legendLabel+'<br>'+dataSet[i].score; });*/
</script>
</body>
</html>
I try to modify this example. I would like to create two data arrays and merge them using special mergingAr() function instead of data.csv. But it does not work. There is one-colour chart without any data. I can`t find the problem place in the code. So, here it is:
var width = 250,
height = 250,
radius = 230;
var arr1 = [44, 64]; //age
var arr2 = [14106543, 8819342]; //population
function type(d) {
d[1] = +d[1];
return d;
}
function mergingAr(array1, array2)
{
var i, out = [];
for(i=0;i<array1.length;i++)
{
out.push([array1[i],array2[i]]);
}
return out;
}
var data = mergingAr(arr1, arr2);
var color = d3.scale.ordinal()
.range(["#EB7221", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 100)
.innerRadius(radius - 180);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d[1]; });
var svg = d3.select("#pie").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d[0]); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d[0]; });
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.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", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
Thanks all for help!
Your data is there, but your color is wrong. Right now, you have this line setting path color:
.style("fill", function(d) { return color(d[0]); });
The problem is that your data is an object, not an array. (It's transformed by the pie function), so you need to reference the data attribute on the object, and then get the zero index of that array, like so:
.style("fill", function(d) { return color(d.data[0]); });