Related
In the code below, I need to make the bubbles with the highest values float to the left of the screen, but I have no deep knowledge of D3.js and I can't find a way to do this.
My code
<script type="text/javascript">
dataset = {
"children": [{"Name":"Olives","Count":10},
{"Name":"Tea","Count":8},
{"Name":"Mashed Potatoes","Count":6},
{"Name":"Boiled Potatoes","Count":5},
{"Name":"Milk","Count":4},
{"Name":"Chicken Salad","Count":4},
{"Name":"Vanilla Ice Cream","Count":2},
{"Name":"Cocoa","Count":7}];
var diameter = 600;
var color = d3.scaleOrdinal(d3.schemeCategory20);
var bubble = d3.pack(dataset)
.size([diameter, diameter])
.padding(1.5);
var svg = d3.select("body")
.append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");
var nodes = d3.hierarchy(dataset)
.sum(function(d) { return d.Count; });
var node = svg.selectAll(".node")
.data(bubble(nodes).descendants())
.enter()
.filter(function(d){
return !d.children
})
.append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.append("title")
.text(function(d) {
return d.Name + ": " + d.Count;
});
node.append("circle")
.attr("r", function(d) {
return d.r;
})
.style("fill", function(d,i) {
return color(i);
});
node.append("text")
.attr("dy", ".2em")
.style("text-anchor", "middle")
.text(function(d) {
return d.data.Name.substring(0, d.r / 3);
})
.attr("font-family", "sans-serif")
.attr("font-size", function(d){
return d.r/5;
})
.attr("fill", "white");
node.append("text")
.attr("dy", "1.3em")
.style("text-anchor", "middle")
.text(function(d) {
return d.data.Count;
})
.attr("font-family", "Gill Sans", "Gill Sans MT")
.attr("font-size", function(d){
return d.r/5;
})
.attr("fill", "white");
d3.select(self.frameElement)
.style("height", diameter + "px");
</script>
Edit in JS Fiddle
The code produces this result
I need the bubbles with the highest values to be on the left, as in the image below.
What property or function of D3.js can I use to control the severity of the bubbles as I need them?
Thanks!
There's not a function specifically in d3.pack for this. d3.force allows you specify x and y positions based on the data's value, and could achieve the result you are looking for.
The force simulation has an .x and .y function that is based on the data's count, and then to avoid overlaps, the .collide function adjusts the circles positions based on their radius (plus a small paddding of 3px).
var simulation = d3.forceSimulation(nodes)
.force("forceX", d3.forceX().strength(.051).x(d => xScale(d.Count)))
.force("forceY", d3.forceY().strength(.051).y(d => yScale(d.Count)))
.force('collision', d3.forceCollide().radius(d => rScale(d.Count) + 3))
dataset = {
"children": [{"Name":"Olives","Count":10},
{"Name":"Tea","Count":8},
{"Name":"Mashed Potatoes","Count":6},
{"Name":"Boiled Potatoes","Count":5},
{"Name":"Milk","Count":4},
{"Name":"Chicken Salad","Count":4},
{"Name":"Vanilla Ice Cream","Count":2},
{"Name":"Cocoa","Count":7}]
}
let nodes = dataset.children
var width = 600;
var height = 600;
var margin = 50
var color = d3.scaleOrdinal(d3.schemeCategory20);
let extentCount = d3.extent(nodes, d => d.Count)
let maxRadius = 100
let yScale = d3.scaleLinear()
.domain(extentCount)
.range([height - maxRadius, maxRadius])
let xScale = d3.scaleLinear()
.domain(extentCount)
.range([(width - maxRadius), maxRadius])
let rScale = d3.scaleSqrt()
.domain(extentCount)
.range([5, maxRadius])
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin + margin)
.attr("height", height + margin + margin)
.attr("class", "bubble");
var g = svg.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")")
var simulation = d3.forceSimulation(nodes)
.force("forceX", d3.forceX().strength(.051).x(d => xScale(d.Count)))
.force("forceY", d3.forceY().strength(.051).y(d => yScale(d.Count)))
.force('collision', d3.forceCollide().radius(d => rScale(d.Count) + 3))
.on("tick", function(d){
node
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; })
})
.stop()
for (var i = 0; i < 120; i++) {
simulation.tick()
}
var node = g.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.append("title")
.text(function(d) {
return d.Name + ": " + d.Count;
});
node.append("circle")
.attr("r", d => rScale(d.Count))
.style("fill", function(d,i) {
return color(i);
});
node.append("text")
.attr("dy", ".2em")
.style("text-anchor", "middle")
.text(function(d) {
return d.Name.substring(0, rScale(d.Count) / 3);
})
.attr("font-family", "sans-serif")
.attr("font-size", function(d){
return rScale(d.Count)/5;
})
.attr("fill", "white");
node.append("text")
.attr("dy", "1.3em")
.style("text-anchor", "middle")
.text(function(d) {
return d.Count;
})
.attr("font-family", "Gill Sans", "Gill Sans MT")
.attr("font-size", function(d){
return d.r/5;
})
.attr("fill", "white");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
I have this bubble chart and want to zoom in to be able to see the very small bubbles. I tried this code by Mike Bostock but I have not succeeded getting good zooming functions, I think it because I have another chart concept.
Other examples online apply zoom on charts that have axes but my chart has no axes.
Here is my Code:
d3.json("Data/New/Treemap_source.json", function (error, data) {
if (error) throw error;
var diameter = 693;
var color = d3.scaleOrdinal(d3.schemeCategory20);
var format = function (d){ return "BTC " + d3.format(",.2f")(d); }
var bubble = d3.pack(data)
.size([diameter, diameter])
.padding(1.5);
var svg = d3.select("#bubblediv")
.append("svg")
.attr("width", diameter + margin.left + margin.right)
.attr("height", diameter + margin.top + margin.bottom)
.attr("class", "bubble");
var nodes = d3.hierarchy(data)
.sum(function(d) { return d.VolumeBTC; });
var node = svg.selectAll(".node")
.data(bubble(nodes).descendants())
.enter()
.filter(function(d){
return !d.children
})
.append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + (d.x + margin.left)+ "," + (d.y + margin.top+20) + ")";
});
node.on("click", function (d) {
alert("This bubble contains: " + d.data.Symbol);
// var sel = d.data.Symbol;
d3.select('#my-select').property('value', d.data.Symbol);
// print_filter(d3.select('#my-select').property('value', d.data.Symbol));
// d3.select('#my-select').property('value', d.data.Symbol);
});
node.append("title")
.text(function(d) {
return d.data.Symbol + ": " + format(d.value);
});
d3.select("svg").append("text")
.attr("transform", "translate(" + (diameter / 2 -20) + " ,30)")
.attr('class','chartlabel')
.style("text-anchor", "middle")
.text("Altcoins Trading in BTC");
node.append("circle")
.attr("r", function(d) {
return d.r;
})
.style('stroke', '#263432')
.style('stroke-width', '1.5')
.style("fill", function(d,i) {
return color(i);
});
node.append("text")
.attr("dy", ".2em")
.style("text-anchor", "middle")
.text(function(d) {
return d.data.Symbol.substring(0, d.r / 3);
})
.attr("font-family", "sans-serif")
.attr("font-size", function(d){
return d.r/5;
})
.attr("fill", "white");
node.append("text")
.attr("dy", "1.3em")
.style("text-anchor", "middle")
.text(function(d) {
return format(d.data.VolumeBTC);
})
.attr("font-family", "Gill Sans", "Gill Sans MT")
.attr("font-size", function(d){
return d.r/6;
})
.attr("fill", "white");
d3.select(self.frameElement)
.style("height", diameter + "px");
});
As of d3 v4, you can zoom on any svg with the following:
function zoomed() {
svg.attr("transform", d3.event.transform);
}
var zoom = d3.zoom().on("zoom", zoomed);
svg.call(zoom);
To get it to work exactly as you want, you'll need to use d3-zoom:
https://github.com/d3/d3-zoom
Lasly, you'll probably want to have a button to reset the zoom, which can be done like this:
d3.select('#zoom-reset-button').on("click", function() {
zoom.transform(svg, d3.zoomIdentity);
});
I'm working on creating a graph that updates when a button is clicked, however when clicking the button, it seems only the axis are updating, and not the data itself.
The current version is in this plunker, I've also attached the code below:
http://plnkr.co/edit/85H6i25YPbTB0MRKtpZn?p=preview
I'm still quite new to D3 and have used a a few books and a lot of reading to get me to an ok level, but am struggling to find an answer to this specific question after trawling through many pages of the internet.
It would be amazing if anyone could give me some guidance on where I'm going wrong.
<body>
<svg width="960" height="500"></svg>
<div id="option">
<input name="updateButton" type="button" value="Click here to update the chart with results after the snap election" onclick="updateData()" />
</div>
<script type="text/javascript">
//graph 1
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
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(["#0087dc", "#d50000", "#FDBB30"]);
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.Year;
}));
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")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) {
return "translate(" + x0(d.Year) + ",0)";
})
.selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
})
.enter().append("rect")
.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.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Seats before snap election");
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.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 - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) {
return d;
});
});
// ** Update data section (Called from the onclick)
function updateData() {
//call data
d3.csv("data_copy.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);
//scale range of data again
x0.domain(data.map(function(d) {
return d.Year;
}));
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();
var sel = svg.selectAll("g")
.data(data);
//remove
sel.exit().remove("g");
sel.enter().append("g")
.attr("transform", function(d) {
return "translate(" + x0(d.Year) + ",0)";
})
.selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
})
//remove
svg.selectAll("rect");
sel.exit().remove("rect");
sel.enter().append("rect")
.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.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Seats after snap election");
});
}
#thedude's answer is right, but doesn't correct everything: it updates the bars, but the heights are wrong, for instance. Something to do with the inner .data join and the subsequent secondary formatting, I guess.
I checked at the same time and came up with the solution below. The core change that makes the update button update is this:
var sel = svg.selectAll("g.chartarea").selectAll("g.year").data(data);
sel.exit().remove();
sel.enter().append("g").classed("year", true);
// continuing with sel didn't update the just appended elements
// so I repeated the selection to get the new elements as well
sel = svg.selectAll("g.chartarea").selectAll("g.year");
sel.attr( // and so on
Complete script:
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
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(["#0087dc", "#d50000", "#FDBB30"]);
// added class to enable precise selection
g.append("g").classed("chartarea", true);
// added classes to enable precise selection
g.append("g")
.classed("axis", true)
.classed("x-axis", true);
// added classes to enable precise selection
g.append("g")
.classed("axis", true)
.classed("y-axis", true);
updateGraph("data.csv");
// ** Update data section (Called from the onclick)
function updateData() {
updateGraph("data_copy.csv");
}
function updateGraph(file) {
//call data
d3.csv(file, 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);
//scale range of data again
x0.domain(data.map(function(d) {
return d.Year;
}));
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();
var sel = svg.selectAll("g.chartarea").selectAll("g.year")
.data(data);
//remove
sel.exit().remove();
// added classes to enable precise selection
sel.enter().append("g").classed("year", true);
sel = svg.selectAll("g.chartarea").selectAll("g.year");
sel.attr("transform", function(d) {
return "translate(" + x0(d.Year) + ",0)";
})
.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);
});
var parties =
sel.selectAll("rect.party")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
});
parties.exit().remove();
// added classes to enable precise selection
parties.enter().append("rect").classed("party", true);
parties = sel.selectAll("rect.party");
parties.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);
});
// select the axes instead of appending them here
g.selectAll("g.x-axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0));
g.selectAll("g.y-axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Seats after snap election");
});
}
Added some further changes that may be worth a look:
Don't differentiate between initialization and update. This is exactly what D3 excels at: doing everything with the same code. In my refactored version, the code is reduced to a single updateGraph function that does both.
Use classes or identifiers to differentiate your graphical elements. There are several places where you select too much with selectAll("g") which will select nearly all elements in your chart.
Don't add stuff multiple times. For example, the axes should be added only once. In the original code, they were added twice, overlaying each other. Instead, add them once, leave them uninitialized, then later select them and set their attributes correctly.
You need to update your rect selection in your click handler like this:
...
//remove
sel = svg.selectAll("rect");
sel.exit().remove("rect");
sel.enter().append("rect")
sel.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
...
You can see it in action here:
http://plnkr.co/edit/3R9lauiQQIB0IgrAk3X2?p=preview
Edit - I've updated the plunker with a working example that addresses several other issues
I'm trying to modify Mike's Force-Directed Graph example to use rectangles instead of circles as nodes. Also, I want text inside the rectangle.
I have rectangles showing up with text correctly, however they're not attached to the links, and they do not move.
Here's a codepen link: http://codepen.io/anon/pen/gpgWaz
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
.call(force.drag);
node.append("rect")
.attr("class", "node")
.attr("width", 100)
.attr("height", 35)
.style("fill", function(d) {
return color(d.group);
})
.style("stroke", "black")
.style("stroke-width", "1px");
node.append("text")
.text(function(d) {
return d.name;
})
.style("font-size", "12px")
.attr("dy", "1em");
node.append("title")
.text(function(d) {
return d.name;
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
});
Update
Thanks to Lars's comment, and his codepen it works now.
Updates I made to my code:
Add transform illustrated by Lars
Changed links to connect at the center of rectangles
Added rounded corners to rectangles
Gave the text a slight margin indentation
Changed to use window width/height
Here my new codepen: http://codepen.io/anon/pen/bdgREd
var width = window.innerWidth,
height = window.innerHeight,
nodeWidth = 100,
nodeHeight = 35;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-1500)
.linkDistance(100)
.friction(0.5)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
.call(force.drag);
node.append("rect")
.attr("class", "node")
.attr("width", nodeWidth)
.attr("height", nodeHeight)
.attr("rx", 5)
.attr("ry", 5)
.style("fill", function(d) {
return color(d.group);
})
.style("stroke", "black")
.style("stroke-width", "1px");
node.append("text")
.attr("x", 5)
.attr("y", 2)
.text(function(d) {
return d.name;
})
.style("font-size", "12px")
.attr("dy", "1em");
node.append("title")
.text(function(d) {
return d.name;
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x + (nodeWidth / 2);
})
.attr("y1", function(d) {
return d.source.y + (nodeHeight / 2);
})
.attr("x2", function(d) {
return d.target.x + (nodeWidth / 2);
})
.attr("y2", function(d) {
return d.target.y + (nodeHeight / 2);
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
You're setting the x and y attributes on the g elements to change their positions -- this won't do anything. You need to set the transform attribute instead, like you're doing when adding the g elements. So your tick handler function would contain
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
instead of
node.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
Complete demo here.
I am unable to add a segment to a D3.js pie chart. I know I need to use .enter() and .append() to stage the new data -- but I am not sure how to apply that when I have the arcs grouped (which I need for the labels).
Here is my update function:
var updateChart = function(dataset) {
arcs.data(donut(dataset));
arcs.transition()
.duration(duration)
.attrTween("d", arcTween);
sliceLabel.data(donut(dataset));
sliceLabel.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; })
.style("fill-opacity", function(d) {
if (d.value === 0) { return 1e-6; }
else { return 1; }
});
};
How I setup the initial graph:
var arc = d3.svg.arc()
.innerRadius(radius * .4)
.outerRadius(radius);
var svg = d3.select("body")
.append("svg")
.append("svg")
.attr("width", width)
.attr("height", height);
var arc_grp = svg.append("g")
.attr("class", "arcGrp")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var label_group = svg.append("g")
.attr("class", "lblGroup")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var arcs = arc_grp.selectAll("path")
.data(donut(data));
arcs.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.each(function(d) { return this.current = d; });
var sliceLabel = label_group.selectAll("text")
.data(donut(data));
sliceLabel.enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; })
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) { return 1e-6; }
else { return 1; }
})
.text(function(d) { return d.data.label; });
Complete jsfiddle: http://jsfiddle.net/kPM5L/
What is a clean way to add the new data to the chart?
To get the transition to work smoothly, you need to add the code that you're using initially to your update function as well. Working jsfiddle here.
And some code to make SO happy -- this is what needs to be in the update function as well:
.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.each(function(d) { return this.current = d; });
.enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; })
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) { return 1e-6; }
else { return 1; }
})
.text(function(d) { return d.data.label; });