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); });
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 made a pie chart using d3 js and I want to add labels to the every path.
I wrote some code:
var labels = svg.selectAll('path').insert("text").data(pie(data))
.text( function (d) { return d.value; })
.attr("font-family", "sans-serif")
.attr('x', 0)
.attr('y', 0)
.attr("font-size", "12px")
.attr("fill", "red");
but there is no visible result, only I can see that there are new text elements in the Chrome developer tools.
How I need to change my code to see all labels on the pieces of my pie chart?
You cannot append text node as a child for path. You should group these elements with g tag and append paths and texts as a child for g elements.
// append "g" it is a container for your slice and label
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("g")
.attr("class", "slice");
// draw slice of pie chart
arcs.append("path")
.attr("fill", function(d, i){
return color(i);
})
.attr("d", function (d) {
return arc(d);
});
// draw label
arcs.append("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].label;}
);
In this case, your structure will look like:
<g>
<path d=...></path>
<text>some text</text>
</g>
<g>
<path d=...></path>
<text>some text</text>
</g>
...
Check working example:
var w = 280;
var h = 280;
var r = h/2;
var color = d3.scale.category20c();
var data = [{"label":"Category A", "value":20},
{"label":"Category B", "value":50},
{"label":"Category C", "value":30}];
var vis = d3.select('body').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.value;});
var arc = d3.svg.arc().outerRadius(r);
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("g")
.attr("class", "slice");
arcs.append("path")
.attr("fill", function(d, i){
return color(i);
})
.attr("d", function (d) {
return arc(d);
});
arcs.append("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].label;}
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I am new to Javascript and D3.js and I am currently trying to build a bubble chart in d3.js using an example provided here https://jrue.github.io/coding/2014/exercises/basicbubblepackchart/ and modifying it according to my assignment. I know that the example above was written in a previous version of d3, and I am using version v4 where syntax has slightly changed,however I am getting a following error when running a program:
Uncaught TypeError: d3.pack(...).sort is not a function
var diameter = 500, //max size of the bubbles
color = d3.scaleOrdinal(d3.schemeCategory10); //color category
var bubble = d3.pack()
.sort()
.size([diameter, diameter])
.padding(1.5);
var svg = d3.select("body")
.append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");
d3.csv("teams.csv", function(error, data){
data = data.map(function(d){ d.value = +d["Amount"]; return d; });
var nodes = bubble.nodes({children:data}).filter(function(d) { return !d.children; });
//setup the chart
var bubbles = svg.append("g")
.attr("transform", "translate(0,0)")
.selectAll(".bubble")
.data(nodes)
.enter();
bubbles.append("circle")
.attr("r", function(d){ return d.r; })
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; })
.style("fill", function(d) { return color(d.value); });
bubbles.append("text")
.attr("x", function(d){ return d.x; })
.attr("y", function(d){ return d.y + 5; })
.attr("text-anchor", "middle")
.text(function(d){ return d["Team"]; })
.style({
"fill":"black",
"font-family":"Helvetica Neue, Helvetica, Arial, san-serif",
"font-size": "12px"
});
What is the problem here?
Be sure to use the same version of d3 as the author in the example (it looks like you're good by the syntax). Also, it's 'd3.layout.pack().sort(null)'
:)
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)
}
I have a D3 Js Sankey diagram, with lots of data. At the end of the data are always companies. The company node is a simple circle. But around that circle i want a donut chart, with 2 values. The values are inside the company node as numsms and nummid. you can also see that in the picture below.
Example of the diagram:
So I want around the company circles a donut chart. But I can't seem to get this to work, I've only found examples for donut charts as own svg.
My code for circles:
svg.selectAll(".node.circle")
.append("circle")
.attr("r", function(d) {
if(d.node.indexOf("company-") > -1) {
return company_circle_size(d);
} else {
return page_circle_size(d);
}
})
.attr("cy", function(d) { return d.dy/2;})
.attr("cx", function(d) {
var cx = sankey.nodeWidth() - 30;
if(d.node.indexOf("company-") > -1) {
cx = cx + 15;
}
return cx;
})
.style("fill", function (d) {
if(d.node.indexOf("company-") > -1) {
if(d.name.indexOf("No data") > -1) {
return "grey";
}
var color = '';
switch(d.status) {
case 'Approved':
color = 'green';
break;
case 'inactive ':
color = 'red';
break;
case 'initial':
color = 'yellow';
break;
case 'review':
color = 'blue';
break;
default:
color = 'grey';
break;
}
return color;
}
return "grey";
//return d.color = color(d.name.replace(/ .*/, ""));
})
.style("fill-opacity", ".9")
.style("shape-rendering", "auto")
.style("stroke", function (d) {
return d3.rgb(d.color).darker(2);
})
.append("title")
.text(function (d) {
if(d.node.indexOf("company-") > -1) {
if(d.cpId != null) {
return d.name + "\n cpId: " + d.cpId;
}
return d.name;
}
return d.name + "\n" + format(d.value);
});
In a for loop I replace the class name "node circle" to "node company".
$(selector).attr("class", "node company")
I've tried some things and I think the code needs to be placed here.
svg.selectAll('.company').each(function(companyNode) {
var values = [[companyNode.numsms, companyNode.nummid]];
var total = companyNode.numsms + companyNode.nummid;
if(total > 0) {
// create donut chart
}
});
You can append a div to the node using foriegnObject. In that div pass your values to an inline jquery sparkline chart (line, pie, etc). Set the transparency of the div so that it doesn't block the diagram. Add your values to the original sankey data array(ie source,target,value,numsms,numid) and then call them in the append with d.numsms).
//sparkline plugin and setup... http://omnipotent.net/jquery.sparkline/#s-docs
//something like the following, look at the fiddle for more info
node.append("foreignObject")
.attr("width", sankey.nodeWidth()*2)
.attr("height", function(d) { return d.dy/2 })
.attr("transform", function(d) { return "translate(" + (d.x+20) + "," + (d.y+10) + ")"; })
.append("xhtml:body")
.style("font", "14px 'Helvetica Neue'")
.html("<div>Inline Sparkline: <span class='inlinesparkline'>1,4,4,7,5,9,10</span></div>");
After adding the div to each node call the plugin to activate the inline charts.
$('.inlinesparkline').sparkline();
Have a look at my fiddle. It's not a sankey but it does show you how to implement foriegnObject.
d3.js diagram with inline charts!
Hope this helps.
Finally got back to this question.
Here's a quick example combining a sankey diagram containing a donut chart. I didn't fit it to your data, this is a generic example.
// if no pie data, just use rects as normal
node.filter(function(d){
return !d.pieData;
})
.append("rect")
.attr("height", function(d) {
return d.dy;
})
.attr("width", sankey.nodeWidth())
.style("fill", function(d) {
return d.color = color(d.name.replace(/ .*/, ""));
})
.style("stroke", function(d) {
return d3.rgb(d.color).darker(2);
})
.append("title")
.text(function(d) {
return d.name + "\n" + format(d.value);
});
// set up donut chart stuff
var pColor = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(50)
.innerRadius(20);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.value; });
// if we have pie data, draw the donut chart
var g = node.filter(function(d){
return d.pieData;
})
.append("g")
.attr('transform', function(d,i){
return 'translate('+d.dx/2+','+d.dy/2+')'; // proper position
})
.attr("class","donut")
.selectAll(".arc")
.data(function(d){
return pie(d.pieData); // nested selection to assign data
})
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d,i) { return color(i); });
Sankey + Piechart implementation here: http://food.csaladen.es
(though pie-chart resides in a static div, so move that above your node every time you evoke it)