I already created the collapsible tree by appending circle,rect,symbols and images.
But I want to build it and need to place pie charts on place of nodes. I am new to D3, so try to add Pie chart as place of nodes.But it is only appending to first .
Can you please let me know the way to do that.Here is what that i tried so far.
nodeEnter.append("svg").data([data]).attr("width", "50")
.attr("height", "50").append("g")
.attr("transform", "translate(" + r + "," + r + ")")
.selectAll("g.slice").data(pie).enter().append("g").attr("class", "slice")
.append("svg:path")
.attr("fill", function(d, i){
return color[i];
})
.attr("d", function (d) {
return arc(d);
});
I am attaching the working fiddle : https://jsfiddle.net/0odgqgsk/
Please modify it accordingly.
Thanks in advance.
Related
I am trying to make a stacked bar graph through d3js and have it update when new data is passed through an update function. I call this update function to initially call the graph and it works fine. However, when I change the data and call it again, it erases all the "rect" elements from the graph (When I console log the data, it appears to be passing through). How can I make the graph be redrawn appropriately? I have tried experimenting with the .remove() statement at the beginning, but without it the data doesn't pass through when the bars are redrawn.
function update(my_data) {
svg.selectAll(".year").remove();
var year = svg.selectAll(".year")
.data(my_data)
.enter().append("g")
.attr("class", "year")
.attr("transform", function(d) { return "translate(" + x0(d.Year) + ",0)"; });
var bar = year.selectAll(".bar")
.data( function(d){ return d.locations; });
bar
.enter().append("rect")
.attr("class", "bar")
.attr("width", x0.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
}
update(data);
It's hard to tell exactly what you're doing cause your question doesn't include the data or the DOM. It would help if you included a link to a work-in-progress jsFiddle or something.
If I had to guess what's going wrong, it looks like you're doing a nested join where each year gets bound to a g element and then each location gets bound to a rect inside each g element.
The issue is likely you are only specifying the enter behavior, but not the update behavior or the exit behavior. As a result, when you try to redraw, nothing updates and nothing exits - but new data elements will get added.
It would seem that is why you have to add the selectAll().remove() to get anything to redraw. By removing everything, all the data elements will trigger the enter condition and get added again.
Take a look at these tutorials to better understand how the enter/update/exit pattern works and how nested joins work.
General Update Pattern: https://bl.ocks.org/mbostock/3808218
Nested Selections: https://bost.ocks.org/mike/nest/
Also, here is a jsFiddle I wrote some time ago to demonstrate how to use nested selections and the general update pattern together:
https://jsfiddle.net/reblace/bWp8L/
var series = svg.selectAll("g.row").data(data, function(d) { return d.key; });
/*
* This section handles the "enter" for each row
*/
// Adding a g element to wrap the svg elements of each row
var seriesEnter = series.enter().append("g");
seriesEnter
.attr("class", "row")
.attr("transform", function(d, i){
return "translate(" + margin.left + "," + (margin.top + (span*i)) + ")";
})
.attr("opacity", 0).transition().duration(200).attr("opacity", 1);
// Adding a text label for each series
seriesEnter.append("text")
.style("text-anchor", "end")
.attr("x", -6)
.attr("y", boxMargin + (boxDim/2))
.attr("dy", ".32em")
.text(function(d){ return d.key; });
// nested selection for the rects associated with each row
var seriesEnterRect = seriesEnter.selectAll("rect").data(function(d){ return d.values; });
// rect enter. don't need to worry about updates/exit when a row is added
seriesEnterRect.enter().append("rect")
.attr("fill", function(d){ return colorScale(d)})
.attr("x", function(d, i){ return i*span + boxMargin; })
.attr("y", boxMargin)
.attr("height", boxDim)
.attr("width", boxDim);
/*
* This section handles updates to each row
*/
var seriesUpdateRect = series.selectAll("rect").data(function(d){ return d.values});
// rect update (Will handle updates after enter)
// rect enter
seriesUpdateRect.enter().append("rect")
.attr("x", function(d, i){ return i*span + boxMargin; })
.attr("y", boxMargin)
.attr("height", boxDim)
.attr("width", boxDim);
// rect enter + update
seriesUpdateRect
.attr("fill", function(d){ return colorScale(d)});
// Exit
seriesUpdateRect.exit();
/*
* This section handles row exit
*/
series.exit()
.attr("opacity", 1)
.transition().duration(200).attr("opacity", 0)
.remove();
I'm building a d3 pie chart with labels on the centroids just like so many examples. I've checked my code against many of those examples but can't figure out where or how my centroids are being calculated. The labels appear to be arranged around the origin of the svg and not the center of the chart like I'd expect. I feel like there's a grouping issue since the text is added but it's not grouped with the arc. Unfortunately, I have no idea how to fix it.
var arcs = svg.datum(domain)
.selectAll('path')
.data(pie);
arcs.enter()
.append('path')
.attr('fill', function(d, i){
return color[i];
})
.attr('d', arc)
.each(function(d) {
this._current = d;
}) // store the initial angles
.attr('transform', 'translate(' + outerRadius + ", " + outerRadius + ')');
arcs.enter()
.append("text")
.attr("transform", function(d) {
console.log(arc.centroid(d));
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.text(function(d,i) {
return data[i].playerName+": "+data[i].playerScore;
});
Here's a fiddle with the complete code.
You need to also move the text over to adjust for the radius of the chart:
arcs.enter()
.append("text")
.attr("transform", function(d) {
console.log(arc.centroid(d));
return "translate(" + arc.centroid(d) + ")translate(" + outerRadius + ", " + outerRadius + ")";
})
.attr("text-anchor", "middle")
.text(function(d,i) {
return data[i].playerName+": "+data[i].playerScore;
});
This will shift everything over to match the locations of the arcs' centroids.
My initial array contains quarterly data, needed to build a boxplot, faceted by ProspRating. I created number of complex g objects (one for each ProspRating) based on the first quarter data. Each g incapsulates simple boxplot elements - lines, rectangles, etc.)
To create the objects I used the following code (excerpt for one simple boxplot element (line.range) only):
d3.select("svg").selectAll("g.box")
.data(data10Q1)
.enter()
.append("g")
.attr("class", "box")
.attr("transform", function(d) {return "translate(" + xScale(d.ProspRating) +"," + yScale(d.v_mdn) + ")"})
.each(function(d,i) {
d3.select(this)
.append('line')
.attr("class", "range")
.attr("x1", 0)
.attr("x2", 0)
.attr("y1", yScale(d.v_max) - yScale(d.v_mdn))
.attr("y2", yScale(d.v_min) - yScale(d.v_mdn))
.style("stroke", "black")
.style("stroke-width", "4px");
The initial boxplot based on the code works well. Currently am trying to write a function to update the element attributes based on data for another quarter.
Excerpt from the code (for one boxplot's element (line.range) only):
function update (quarter) {
var filtered = data.filter(function(d) {
return d.quarter === quarter})
var boxes = d3.select("svg").selectAll("g.box")
.data(filtered, function(d){return d.ProspRating})
.attr("transform", function(d) {return "translate(" + xScale(d.ProspRating) +"," + yScale(d.v_mdn) + ")"})
.each(function(d,i) {
d3.select(this)
.select("line.range")
.attr("y1", yScale(d.v_max) - yScale(d.v_mdn))
.attr("y2", yScale(d.v_min) - yScale(d.v_mdn))
Looks like d3.select(this).select("line.range") does not work properly. What would be the best way to access the line.range and other similar elements to update their attributes?
I guess you missed the .enter() after var boxes = d3.select [...] .data [...]
I'm trying to set up a pie on d3.js that will constantly update its data (slices) and change the labels.
The slices of data may vary and are not constant (could be a 2 slices pie and 5 slices, etc.)
I've reviewed several of examples such as:
http://jsfiddle.net/emepyc/VYud2/3/
http://bl.ocks.org/mbostock/3808218
But the thing is, my implementation is quite different since I use both text lables and paths.
The problem: data is not updated and text is not removed.
Here is my code: http://jsfiddle.net/Kitt0s/vfkSs/
function tests (data) {
data = data ? data : { "slice1": Math.floor((Math.random()*10)+1), "slice2": Math.floor((Math.random()*10)+1), "slice3": Math.floor((Math.random()*10)+1), "slice4": Math.floor((Math.random()*10)+1) };
var dataa = d3.entries(data);
var cv_path = cv_svg.selectAll("g.slice")
.data(cv_pie(dataa));
cv_path.enter()
.append("g")
.attr("class", "slice")
.append("path")
.attr("fill", function(d, i) { return cv_color(i); } )
.attr("d", cv_arc)
.each(function(d) { this._current = d; });
cv_path.transition().duration(750).attrTween("d", cv_arcTween);
cv_path.exit().remove();
var cv_text = d3.selectAll(".slice")
.append("text")
.attr("transform", function(d) {
d.innerRadius = 0;
d.outerRadius = cv_r;
return "translate(" + cv_arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.attr("font-weight", "bold")
.attr("fill", "#FFFFFF")
.attr("font-size", "30px")
.text(function(d) { return d.data.key + "(" + d.data.value + ")"; });
cv_text.exit().remove();
}
I've spent an awful lot of time trying to get this right, but still seem to get stuck (every time something else breaks.. :P)
Your help would be highly appreciated!
You were almost there. The key is to treat the text exactly the same as the paths and handle the .exit() selection by removing it. I've modified your jsfiddle here. I've removed the g elements to make it a bit easier to understand.
I'm creating a donut (or Piechart) and I want to place the labels just outside the area.
I've created a fiddle http://jsfiddle.net/VeeTee/mA3V7/ for it.
arcs.append("svg:text")
.attr("transform", function(d) {
//this is where I want to make a translation to the outside border
d.innerRadius = radius;
d.outerRadius = height/2;
return "translate(" + arc.centroid(d) +")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d, i) { return d.value.toFixed(2); });
arc.centroid(d) -> is always giving the same result (and therefore the same translation)
Not sure what you mean by that it's always giving you the same result, but you can place the labels just outside the chart by multiplying the coordinates of the centroid by 1.5. The code is something like this.
.attr("transform", function(d) {
var c = arc.centroid(d);
return "translate(" + c[0]*1.5 +"," + c[1]*1.5 + ")";
})
Updated jsfiddle here.