I'm using dc.js to draw some charts.
In the d3 code I'm calculating dynamicly the total sum of a few columns and add them then to the pie chart which I draw with d3.js.
This is the code which calculates the total sum of the columns:
var pieChart = [];
classesJson.forEach(function(classJson){
var memDegree = ndx.groupAll().reduceSum(function(d){
return d[classJson.name];
}).value();
//console.log(memDegree);
pieChart.push({name:classJson.name, memDegree:memDegree});
});
The drawing for the first time works fine. But when I click elements on the dc.js bar charts the d3.js pie chart didn't update. How can accomplish that the GroupAll values from the above code also update in the d3.js pie chart?
This is the total d3 code for the pie chart:
radius = Math.min(300, 234) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.memDegree; });
var svg = d3.select("#membership-degree-pie-chart").append("svg")
.attr("width", 300)
.attr("height", 234)
.append("g")
.attr("transform", "translate(" + 300 / 2 + "," + 234 / 2 + ")");
var pieChart = [];
classesJson.forEach(function(classJson){
var memDegree = ndx.groupAll().reduceSum(function(d){
return d[classJson.name];
}).value();
//console.log(memDegree);
pieChart.push({name:classJson.name, memDegree:memDegree});
});
pieChart.forEach(function(d) {
d.memDegree = +d.memDegree;
});
var g = svg.selectAll(".arc")
.data(pie(pieChart))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.name); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.name; });
You can use a listener on the dc chart to detect that is has been filtered and then call your update function for the d3 chart.
yourDCChart.on("filtered", function (chart, filter) {
// update function for d3
updateD3Chart();
});
Without fiddle or plnkr it's difficult to tell.
But I have edited your code without testing. Please check if it helps, I have created the change function to update the graph. you can call change function where you want to update the graph. Hope it helps.
var g = svg.selectAll(".arc")
.data(pie(pieChart))
.enter().append("g")
.attr("class", "arc")
.append("path")
.attr("d", arc)
.style("fill", function (d) { return color(d.data.name); })
.each(function(d) { this._current = d; }); // store the initial angles;
g.append("text")
.attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function (d) { return d.data.name; });
//For updating change in data
function change() {
pie.value(function(d) { return d.memDegree; }); // change the value function
g = g.data(pie); // compute the new angles
g.transition().duration(750).attrTween("d", function (a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function (t) {
return arc(i(t));
};
}); // redraw the arcs
}
I attached D3 draw function for my custom visualizations to dc chart, each time the chart was updated/rendered D3 chart got drawn again :
dcTable
.on("renderlet.<renderletKey>", function (d3ChartData) {
drawD3(d3ChartData)
}
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>
I've been trying to get a pie chart to update and it works in CodePen but when I try to make it larger scale it completely fails.
This is the code I'm having trouble with:
var d2_data, d2_path, d2_pie, d2_text, d2_arc;
function D2_Update()
{
d2_data = [d2_employed,d2_student,d2_unemployed,d2_retired];
d2_path.data(d2_pie(d2_data))
.transition().duration(1000)
.attrTween("d", arcTween(null, d2_arc)); // Redraw the arcs
d2_text.data(d2_pie(d2_data))
.transition().duration(1000)
.attr("transform", function (d) {
return "translate(" + d2_arc.centroid(d) + ")";
})
.text(function(d){return d.data;});
}
function arcTween(a, arc) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
When this update function is called the text on the chart correctly updates--once--and then it fails to actually change the arcs like in the CodePen I linked above. All I get in return is a console error reading TypeError: r is not a function coming from my D3js JavaScript file.I am completely confused here because I'm not really sure what I should be doing differently.
Any help is appreciated in understanding my issue. I will also display my code for this graph pie chart:
// D2 pie
d2_data = [d2_employed,d2_student,d2_unemployed,d2_retired];
var d2_dataKey = ["Employed","Student","Unemployed","Retired"];
var d2_width = 400,
d2_height = 400,
d2_radius = Math.min(d2_width, d2_height) / 2;
d2_arc = d3.svg.arc()
.outerRadius(d2_radius - 10)
.innerRadius(d2_radius - 85);
d2_pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d;
});
var d2_svg = d3.select("#general_d2-graph").append("svg")
.attr("width", d2_width)
.attr("height", d2_height)
.append("g")
.attr("id", "pieChart")
.attr("transform", "translate(" + d2_width / 2 + "," + d2_height / 2 + ")");
d2_path = d2_svg.selectAll("path")
.data(d2_pie(d2_data))
.enter()
.append("path");
d2_text = d2_svg.selectAll("text")
.data(d2_pie(d2_data))
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("transform", function (d) {
return "translate(" + d2_arc.centroid(d) + ")";
})
.text(function(d){return d.data;});
d2_path.transition()
.duration(1000)
.attr("fill", function(d, i) {
return color(d.data);
})
.attr("d", d2_arc)
.each(function(d) {
this._current = d;
}); // Store the initial angles
Thank you.
Hello All instead of simple Circles, i want to add pie charts in my Pack layout.
Lets Suppose this is my pie chart data and pie layout
var data=[2,3,4,5]
var arc = d3.svg.arc()
.outerRadius(50)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d; });
And this is how the packlayout draws the circle
var circle = svg.selectAll("circle")
.data(nodes1)
.enter().append("circle")
.attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
.style("fill", function(d) { return d.children ? color(d.depth) : null; })
.on("click", function(d) { if (focus !== d) zoom(d), d3.event.stopPropagation(); });
Can anyone please explain me how instead of appending circles in pack layout i could rather append paths and make pie charts out of it?![enter image description here][1]
Instead of using the pack layout results directly, you can use the r value output from the pack layout to define the outerRadius of your arc generator. Then, instead of appending svg circle elements to the chart, you can append svg g elements, and append each of the arcs inside that:
Full example: http://bl.ocks.org/jsl6906/4a1b818b64847fb05d56
Relevant code:
var bubble = d3.layout.pack()
.value(function(d) { return d3.sum(d[1]); })
.sort(null)
.size([diameter, diameter])
.padding(1.5),
arc = d3.svg.arc().innerRadius(0),
pie = d3.layout.pie();
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");
var nodes = svg.selectAll("g.node")
.data(bubble.nodes({children: data}).filter(function(d) { return !d.children; }));
nodes.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
var arcGs = nodes.selectAll("g.arc")
.data(function(d) {
return pie(d[1]).map(function(m) { m.r = d.r; return m; });
});
var arcEnter = arcGs.enter().append("g").attr("class", "arc");
arcEnter.append("path")
.attr("d", function(d) {
arc.outerRadius(d.r);
return arc(d);
})
.style("fill", function(d, i) { return color(i); });
Fair warning: I'm a D3 rookie here. I'm building a donut chart using D3 and all is well so far, except that the labels on the slices aren't aligning with the slices. Using the code below, the labels for each slice are rendered in the middle of the chart, stacked on top of each other so they're unreadable. I've dropped the arc.centroid in my transform attribute, but it's returning "NaN,NaN" instead of actual coordinates, and I can't understand where it's reading from that it's not finding a number. My innerRadius and outerRadius are defined in the arc variable. Any help?
(pardon the lack of a jsfiddle but I'm pulling data from a .csv here)
var width = 300,
height = 300,
radius = Math.min(width, height) / 2;
var color = ["#f68b1f", "#39b54a", "#2772b2"];
var pie = d3.layout.pie()
.value(function(d) { return d.taskforce1; })
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 85)
.outerRadius(radius);
var svg = d3.select("#pieplate").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.csv("data.csv", type, function(error, data) {
var path = svg.datum(data).selectAll("path")
.data(pie)
.enter().append("path")
.attr("fill", function(d, i) { return color[i]; })
.attr("d", arc)
.each(function(d) { this._current = d; }); // store the initial angles
var text = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text( function (d) { return d.taskforce1; })
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("fill", "black");
d3.selectAll("a")
.on("click", switcher);
function switcher() {
var value = this.id;
var j = value + 1;
pie.value(function(d) { return d[value]; }); // change the value function
path = path.data(pie); // compute the new angles
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
textLabels = text.text( function (d) { return d[value]; });
}
});
function type(d) {
d.taskforce1 = +d.taskforce1;
d.taskforce2 = +d.taskforce2;
d.taskforce3 = +d.taskforce3;
return d;
}
// Store the displayed angles in _current.
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
Finally got it. The arc.centroid function expects data with precomputed startAngle and endAngle which is the result of pie(data). So the following helped me:
var text = svg.selectAll("text")
.data(pie(data))
followed by the rest of the calls. Note that you might have to change the way to access the text data that you want to display. You can always check it with
// while adding the text elements
.text(function(d){ console.log(d); return d.data.textAttribute })
I have a dynamic data source that creates a new json in the browser frequently.
I was able to create a pie chart from this json using the code below (also at this fiddle)
var data=[{"crimeType":"mip","totalCrimes":24},{"crimeType":"theft","totalCrimes":558},{"crimeType":"drugs","totalCrimes":81},{"crimeType":"arson","totalCrimes":3},{"crimeType":"assault","totalCrimes":80},{"crimeType":"burglary","totalCrimes":49},{"crimeType":"disorderlyConduct","totalCrimes":63},{"crimeType":"mischief","totalCrimes":189},{"crimeType":"dui","totalCrimes":107},{"crimeType":"resistingArrest","totalCrimes":11},{"crimeType":"sexCrimes","totalCrimes":24},{"crimeType":"other","totalCrimes":58}];
var width = 800,
height = 250,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) {
return d.totalCrimes;
});
var svg = d3.select("body").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.data.crimeType);
});
g.append("text")
.attr("transform", function (d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function (d) {
return d.data.crimeType;
});
This data updates frequenty so what would be the best way to update the pie? Look at this fiddle. Here I have another json called data2.
How could I simply replace data with data2 and have the pie animate/update?
Note: on some updates values could == 0
I have created a working version and have posted it here: http://www.ninjaPixel.io/StackOverflow/doughnutTransition.html (for some reason I couldn't get the transitions to play ball in fiddle, so have just posted it to my website instead).
To make the code clearer I have omitted your labelling, renamed 'data' to 'data1', and have stuck in some radio buttons to flip between the data arrays. The following snippet shows the important bits. You can get the whole code from my page above.
var svg = d3.select("#chartDiv").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("id", "pieChart")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(pie(data1))
.enter()
.append("path");
path.transition()
.duration(500)
.attr("fill", function(d, i) { return color(d.data.crimeType); })
.attr("d", arc)
.each(function(d) { this._current = d; }); // store the initial angles
function change(data){
path.data(pie(data));
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}
// Store the displayed angles in _current.
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
You may find this code of Mike Bostock's helpful, it is where I learned how to do this.
Here are some other similar questions that might help:
How to update pie chart using d3.js
d3 pie chart transition with attrtween
simple d3.js pie chart transitions *without* data joins?
Adding new segments to a Animated Pie Chart in D3.js
https://groups.google.com/forum/#!msg/d3-js/2o5NTVjVJgA/AslmRSxXUAgJ