I'm trying to achieve this
but this is currently what I have
Essentially all I need to do is figure out how to have the lines emanating out from the circle starting at the arc start.
My question is exactly that, how can I translate the arc starting position into the x1,y1 attribute of an svg line. Below is the code that I currently have pertaining to drawing the lines:
// Draw lines emanating out
g.append('line')
.attr('class', 'outer-line')
.attr('x1', function(d) {
return 0;
})
.attr('x2', 0)
.attr('y1', -radius)
.attr('y2', -radius-150)
.attr('stroke', function(d, i) {
return color(i);
})
.attr('stroke-width','2')
.attr("transform", function(d) {
return "rotate(" + (d.startAngle+d.endAngle)/2 * (180/Math.PI) + ")";
});
If I understand your problem correctly, you can use just d.startAngle:
g.attr("transform", function(d) {
return "rotate(" + d.startAngle * (180/Math.PI) + ")";
});
Check here an example (click on "run code snippet"):
var dataset = [300, 200, 400, 200, 300, 100, 50];
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 50);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(pie(dataset))
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc);
var g = svg.selectAll(".groups")
.data(pie(dataset))
.enter()
.append("g");
g.append('line')
.attr('class', 'outer-line')
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', -radius + 50)
.attr('y2', -radius)
.attr('stroke', 'black')
.attr('stroke-width','2')
.attr("transform", function(d) {
return "rotate(" + d.startAngle * (180/Math.PI) + ")";
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Related
I have more than one donut in my page, each donut will have a thinner portion (like unfilled) and another arc colored.
When user clicks on the colored arc, it should have a white border. And if user selects other arc (unfilled) the unfilled area get filled with color, changes the width like the other one and will have border, same time the filled one gets unfilled. To summarize the arc colored portion should get switched on selection.
Can I achieve this by applying class/styles? In one page there should be only one arc selected at a time, all other selections will be cleared.
// data
var dataset = [{
color: "#5FC5EA",
value: 70
}, {
color: "transparent",
value: 30
}];
// size
var width = 460,
z
height = 300,
radius = Math.min(width, height) / 2;
var pie = d3.layout.pie()
.sort(null).value(function(d) {
return d.value;
});
// thin arc
var arc1 = d3.svg.arc()
.innerRadius(radius - 20)
.outerRadius(radius - 11);
// main arc
var arc = d3.svg.arc()
.innerRadius(radius - 30)
.outerRadius(radius);
// set svg
var svg = d3.select("#d3-setup-donut").append("svg")
.attr("class", 'donut-chart')
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.on('mouseout', function() {
d3.selectAll('.donut-tooltip').style('display', 'none');
});
// tooltip
var div = d3.select("body")
.append("div")
.attr("class", "donut-tooltip");
// draw thinner arc
var path = svg.selectAll(".background")
.data(pie([{
color: "#222427",
value: 1
}]))
.enter().append("path")
.attr("class", "background")
.attr("fill", function(d, i) {
return d.data.color;
})
.attr("d", arc1)
.on('click', function(d, i) {
//
})
.on("mousemove", function(d, i) {
var mouseVal = d3.mouse(this);
div.style("display", "none");
div.html(d.data.label + " : " + d.value)
.style("left", (d3.event.pageX - 40) + "px")
.style("top", (d3.event.pageY - 35) + "px")
.style("opacity", 1)
.style("display", "block");
})
.on("mouseout", function() {
div.html(" ").style("display", "none");
});
// draw main arc
var path = svg.selectAll(".foreground")
.data(pie(dataset))
.enter().append("path")
.attr("class", "foreground")
.attr("fill", function(d, i) {
return d.data.color;
})
.attr("d", arc)
.on('click', function(d, i) {
d3.select(this)
.classed('selected', true);
})
.on("mousemove", function(d, i) {
var mouseVal = d3.mouse(this);
div.style("display", "none");
div.html(d.data.label + " : " + d.value)
.style("left", (d3.event.pageX - 40) + "px")
.style("top", (d3.event.pageY - 55) + "px")
.style("opacity", 1)
.style("display", "block");
})
.on("mouseout", function() {
div.html(" ").style("display", "none");
});
// draw inner text
svg.append("text")
.text('60%')
.attr("class", "donut-inner-val")
//.attr("x", radius/12 - 30)
//.attr("y", radius/12 - 10);
svg.append("text")
.text('in progress')
.attr("class", "donut-inner-text")
.attr("x", (radius / 12) - 35)
.attr("y", (radius / 12) + 10);
JSFiddle
Try this Code
.on('click', function(d, i) {
d3.selectAll(".foreground").classed('selected', false);
if(d3.select(this).classed("active")){
d3.select(this)
.classed('selected', true);
}else{
d3.selectAll(".foreground").classed("active", false);
d3.select(this).classed("active",true);
d3.select(this)
.classed('selected', true);
}
})
DEMO
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 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.
Problem: The Arctween function will not work on the .on("mouseOver").
Intention: When hovering the arcs in the Pie Chart a highlight needs to start (opacity etc.) and information needs (infoHover) to show, next to the Arctween that I also want to activate.
I am aware that the code is not perfect at all, I'm just experimenting with d3.js.
Thanks in advance!
Javascript:
d3.json("dataExample.json", function (data) {
var width = 260,
height = 260;
var outerRadius = height / 2 - 20,
innerRadius = outerRadius / 3,
cornerRadius = 10;
colors = d3.scale.category20c();
var tempColor;
var pie = d3.layout.pie()
.padAngle(.02)
.value(function(d) {
return d.value;
})
var arc = d3.svg.arc()
.padRadius(outerRadius)
.innerRadius(innerRadius);
var infoHover = d3.select('#chart').append('div')
.style('position', 'absolute')
.style('padding', '0 30px')
.style('opacity', 0)
function arcTween(outerRadius, delay) {
return function() {
d3.select(this).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) { d.outerRadius = i(t); return arc(d); };
});
};
}
var svg = d3.select("#chart").append("svg")
.data(data)
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.selectAll('path').data(pie(data))
.enter().append('path')
.attr('fill', function(d, i) {
return colors(i);
})
.each(function(d) { d.outerRadius = outerRadius - 20; })
.attr('d', arc)
.on("mouseover", function(d) {
infoHover.transition()
.style('opacity', .9)
.style('left', '85px')
.style('top', '120px')
infoHover.html(d.value + '%')
d3.selectAll("path")
.transition()
.duration(500)
.style("opacity", .18)
d3.select(this)
.transition()
.duration(500)
.style('opacity', 1)
.style('cursor', 'pointer')
arcTween(outerRadius, 0);
})
.on("mouseout", function(d) {
d3.selectAll("path")
.transition()
.duration(500)
.style("opacity", 1)
d3.select(this)
.style('opacity', 1)
arcTween(outerRadius - 20, 150);
});
});
Your arcTween returns a function your need to call:
arcTween(outerRadius, 0).call(this);
I'm drawing charts with d3.js.
Is it possible to add radial gradients to donut chart, how on this picture?
Assuming the arc parts are path elements that are filled you can use a radial gradient to get that result.
See this similar question, we can reuse the example to arrive at:
var dataset = {
apples: [53245, 28479, 19697, 24037, 40245],
};
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 50);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var grads = svg.append("defs").selectAll("radialGradient").data(pie(dataset.apples))
.enter().append("radialGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", "100%")
.attr("id", function(d, i) { return "grad" + i; });
grads.append("stop").attr("offset", "15%").style("stop-color", function(d, i) { return color(i); });
grads.append("stop").attr("offset", "20%").style("stop-color", "white");
grads.append("stop").attr("offset", "27%").style("stop-color", function(d, i) { return color(i); });
var path = svg.selectAll("path")
.data(pie(dataset.apples))
.enter().append("path")
.attr("fill", function(d, i) { return "url(#grad" + i + ")"; })
.attr("d", arc);
Jsfiddle: http://jsfiddle.net/X8hfm/