I am trying to add labels in a d3 pie as displayed at http://bl.ocks.org/dbuezas/9306799 but it is always displays the labels inside the slices.
var dataset = ${pieList};
var width = 700,
height = 700,
outerRadius = Math.min(width, height) / 2,
innerRadius = outerRadius * .999,
innerRadiusFinal = outerRadius * .5,
innerRadiusFinal3 = outerRadius * .45,
color = d3.scale.category20()
;
var vis = d3.select("#pieChart")
.append("svg:svg")
.data([dataset])
.attr("width", width)
.attr("height", height)
.append("svg:g")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")")
;
var arc = d3.svg.arc()
.outerRadius(outerRadius).innerRadius(innerRadius);
var arcFinal = d3.svg.arc().innerRadius(innerRadiusFinal).outerRadius(outerRadius);
var arcFinal3 = d3.svg.arc().innerRadius(innerRadiusFinal3).outerRadius(outerRadius);
var pie = d3.layout.pie()
.value(function (d) {
return d.measure;
});
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", up)
;
arcs.append("svg:path")
.attr("fill", function (d, i) {
return color(i);
})
.attr("d", arc)
.append("svg:title")
.text(function (d) {
return d.data.category + ": " + formatAsPercentage(d.data.measure);
});
d3.selectAll("g.slice").selectAll("path").transition()
.duration(750)
.delay(10)
.attr("d", arcFinal)
;
arcs.filter(function (d) {
return d.endAngle - d.startAngle > .2;
})
.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", function (d) {
return "translate(" + arcFinal.centroid(d) + ")rotate(" + angle(d) + ")";
})
.text(function (d) {
return d.data.category;
})
;
.attr("transform", function (d) {
return "translate(" + arcFinal.centroid(d)
+ ")rotate(" + angle(d)
+ ")translate(" + 700 + ",0)";
})
function angle(d) {
var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
return a > 90 ? a - 180 : a;
}
// Pie chart title
vis.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text("Attendance report 2015")
.attr("class", "title")
;
function mouseover() {
d3.select(this).select("path").transition()
.duration(750)
//.attr("stroke","red")
//.attr("stroke-width", 1.5)
.attr("d", arcFinal3)
;
}
function mouseout() {
d3.select(this).select("path").transition()
.duration(750)
//.attr("stroke","blue")
//.attr("stroke-width", 1.5)
.attr("d", arcFinal)
;
}
function up(d, i) {
/* update bar chart when user selects piece of the pie chart */
//updateBarChart(dataset[i].category);
updateBarChart(d.data.category, color(i));
updateLineChart(d.data.category, color(i));
}
I want to display the labels outside of the pie slices but it always display inside the slices.
One option would be to translate your text outward by the radius of the pie, which might be easiest to do after the rotate. So, something like:
.attr("transform", function (d) {
return "translate(" + arcFinal.centroid(d)
+ ")rotate(" + angle(d)
+ ")translate(" + radius + ",0)";
})
Where radius is the radius of your pie chart. Sometimes you might want to translate a few pixels further, so that you have a margin between the chart and the label.
This is certainly not the only answer, here are some more issues to watch out for though:
Rotating your text can end up with it being upside down on one side of the graph, which is not very readable.
After fixing the rotation issue, you might find that your label's textanchor causes long labels to go over the graph anyway.
If your pie slices are thin, you can end up writing labels over one another.
Related
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 creating the legends with triangle shapes. One is "Yes", the other one is "No". By running the code below, it generate two triangles but they are overlapping. I am trying to seperate them by using this line of code .attr("y", function(d,i) {return 50+i*40;}) but seems like it doesn't work.
Can anyone tell me how to fix it? Thanks!
Click here! This is an html sreenshot for this part of script
var legendname = ["Yes","No"];
var legend = svg.selectAll(".legend")
.data(legendname)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(" + (w + 150) + "," + (m.t - 30) + ")";
});
legend.append("path")
.attr("d", d3.svg.symbol().type("triangle-up").size(128))
*** .attr("y", function(d,i) {return 50+i*40;})
.style('fill', function(d) {return color(d);});
legend.append("text")
.attr("y", function(d,i) {return 50+i*20;})
.attr("x", 30)
.text(function(d) { return d; })
You will have to update the translate y attribute of groups instead of the paths. And also there is no need for extra calculations for y attributes of texts and paths then.
.attr("transform", function(d, i) {
return "translate(" + (w + 150) + "," + (30+i*40) + ")";
});
Working Code Snippet:
var w=40; //Sample chart width
var color = d3.scale.category20c();
var svg = d3.select("body").append("svg").attr({ height: 500, width: 400 });
var legendname = ["Yes", "No"];
var legend = svg.selectAll(".legend")
.data(legendname)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(" + (w + 150) + "," + (30+i*40) + ")";
});
legend.append("path")
.attr("d", d3.svg.symbol().type("triangle-up").size(128))
.style('fill', function(d,i) {
return color(i);
});
legend.append("text")
.attr("dx",10)
.attr("dy",".4em")
.text(function(d) {
return d;
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Im new to d3.js im able to create donut chart and im able to rotate chart but i dont want labels on chart to rotate. Can anyone help how to proceed. how shall i make sure that labels on chart should not rotate
var dataset = [
{
count: 10
},
{
count: 20
},
{
label: 'Cantaloupe',
count: 30
}
];
var width = 360;
var height = 360;
var radius = 100;
var color = d3.scale.ordinal()
.range(["red", "blue", "green"]);
var svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')');
var arc = d3.svg.arc()
.innerRadius(radius - 60)
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function (d) {
return d.count;
});
var arcs = svg.selectAll('.arc')
.data(pie(dataset))
.enter()
.append('g')
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.attr("fill", function (d) {
return color(d.data.count);
})
.on("click", function (d) {
var curAngle = 180;
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ") rotate(" + curAngle + ")");
});
arcs.append("text")
.attr("transform", function (d) {
return "translate(" + arc.centroid(d) + ")";
}).attr("text-anchor", "middle")
.attr("font-size", "1.5em")
.text(function (d) {
return d.data.count
});
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>