How to add a legend and labels to the pie chart? - javascript

How can I add a legend on top of 3 pie charts (see this fiddle).
I want to specify that the light blue color corresponds to the legend "AAA", medium blue is "BBB" and blue is "CCC".
Also I would like to add numeric values inside each piece of pie. I tried this approach, but then the charts disappear:
var data = [
[{"piece_value",76.34}, {"piece_value",69.05}, {"piece_value",275.19}],
[{"piece_value",69.93}, {"piece_value",61.50}, {"piece_value",153.31}],
[{"piece_value",83.51}, {"piece_value",69.14}, {"piece_value",204.32}]
];
svg.selectAll("path")
.data(d3.layout.pie().value(function(d) { return d.piece_value; }))
.enter().append("path")
.attr("d", d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r))
.style("fill", function(d, i) { return z(i); });
UPDATE:
I also tried this, but the same result:
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.piece_value; });
svg.selectAll("path")
.data(pie(data))
.enter().append("path")
.attr("d", d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r))
.style("fill", function(d, i) { return z(i); });

You have a problem with your data array. This:
{"piece_value",76.34}
Is not a proper object. It should have a colon between the name and the value:
{"piece_value": 76.34}
So, let's change your data for this:
var data = [
[{"piece_value":76.34, name:"AAA"}, {"piece_value":69.05, name:"BBB"}, {"piece_value":275.19, name:"CCC"}],
[{"piece_value":69.93, name:"AAA"}, {"piece_value":61.50, name:"BBB"}, {"piece_value":153.31, name:"CCC"}],
[{"piece_value":83.51, name:"AAA"}, {"piece_value":69.14, name:"BBB"}, {"piece_value":204.32, name:"CCC"}]
];
And set the variables for the layout and the arc generator:
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.piece_value;
});
var arc = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r);
For adding the values in each pie, you can use arc.centroid:
svg.selectAll("foo")
.data(pie)
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("transform", d => "translate(" + arc.centroid(d) + ")")
.text(d => d.data.piece_value);
For creating the legend, add the rectangles and the texts using the inner arrays:
svg.selectAll("foo")
.data(d => d)
.enter()
.append("rect")
Here is the demo:
var data = [
[{
"piece_value": 76.34,
name: "AAA"
}, {
"piece_value": 69.05,
name: "BBB"
}, {
"piece_value": 275.19,
name: "CCC"
}],
[{
"piece_value": 69.93,
name: "AAA"
}, {
"piece_value": 61.50,
name: "BBB"
}, {
"piece_value": 153.31,
name: "CCC"
}],
[{
"piece_value": 83.51,
name: "AAA"
}, {
"piece_value": 69.14,
name: "BBB"
}, {
"piece_value": 204.32,
name: "CCC"
}]
];
var m = 10,
r = 100,
z = d3.scale.category20c();
var svg = d3.select("body").selectAll("svg")
.data(data)
.enter().append("svg")
.attr("width", (r + m) * 2)
.attr("height", (r + m) * 2)
.append("g")
.attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.piece_value;
});
var arc = d3.svg.arc()
.innerRadius(r / 2.2)
.outerRadius(r/1.2)
svg.selectAll("path")
.data(pie)
.enter().append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return z(i);
});
svg.selectAll("foo")
.data(pie)
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("transform", d => "translate(" + arc.centroid(d) + ")")
.text(d => d.data.piece_value);
svg.selectAll("foo")
.data(d=>d)
.enter()
.append("text")
.attr("transform", (d,i)=>"translate(" + (-r + 2.5*m + (i * 70)) + "," + (-r + m) + ")")
.text(d=>d.name);
svg.selectAll("foo")
.data(d=>d)
.enter()
.append("rect")
.attr("transform", (d,i)=>"translate(" + (-r + m + (i * 70)) + "," + (-r) + ")")
.attr("width", 10)
.attr("height", 10)
.style("fill", (d, i) => z(i));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Related

Text invisible yet appearing in inspector

My last question was answered so quickly and smoothly I thought I'd return with another issue I'm failing to figure out on my own.
I used one of the examples to create this graph:
data = [{ "label": "1", "value": 20 },
{ "label": "2", "value": 50 },
{ "label": "3", "value": 30 },
{ "label": "4", "value": 45 }];
var width = 400,
height = 450;
var outerRadius = 200,
innerRadius = outerRadius / 3,
color = d3.scale.category20c();
var pie = d3.layout.pie()
.value(function (d) { return d.value; });
var pieData = pie(data);
var arc = d3.svg.arc()
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + outerRadius + "," + (outerRadius + 50) + ")");
svg.append("text")
.attr("x", 0)
.attr("y", -(outerRadius + 10))
.style("text-anchor", "middle")
.text("Title[enter image description here][1]");
svg.selectAll("path")
.data(pieData)
.enter().append("path")
.each(function (d) { d.outerRadius = outerRadius - 20; })
.attr("d", arc)
.attr("fill", function (d, i) { return color(i); })
.on("mouseover", arcTween(outerRadius, 0))
.on("mouseout", arcTween(outerRadius - 20, 150));
svg.selectAll("path")
.append("text")
.attr("transform", function (d) {
d.innerRadius = 0;
d.outerRadius = outerRadius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(function (d, i) { return data[i].label; });
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); };
});
};
}
The idea being that when you hover over a section on the pie chart (donut chart?) it expands. However, this made my labels dissapear and I can't manage to make them come back. I either get an error, or they just don't show up on the screen (even though I see the tag in the inspector). Any obvious thing I'm missing?
Thanks!
You cannot append a <text> element to a <path> element. It simply doesn't work in an SVG. Even not working, the <text> element will be appended.
That being said, a solution is creating a new "enter" selection for the texts:
svg.selectAll(null)
.data(pieData)
.enter()
.append("text")
.attr("transform", function(d) {
d.innerRadius = 0;
d.outerRadius = outerRadius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(function(d, i) {
return data[i].label;
});
Here is your updated code:
data = [{
"label": "1",
"value": 20
}, {
"label": "2",
"value": 50
}, {
"label": "3",
"value": 30
}, {
"label": "4",
"value": 45
}];
var width = 400,
height = 450;
var outerRadius = 200,
innerRadius = outerRadius / 3,
color = d3.scale.category20c();
var pie = d3.layout.pie()
.value(function(d) {
return d.value;
});
var pieData = pie(data);
var arc = d3.svg.arc()
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + outerRadius + "," + (outerRadius + 50) + ")");
svg.append("text")
.attr("x", 0)
.attr("y", -(outerRadius + 10))
.style("text-anchor", "middle")
.text("Title[enter image description here][1]");
svg.selectAll("path")
.data(pieData)
.enter().append("path")
.each(function(d) {
d.outerRadius = outerRadius - 20;
})
.attr("d", arc)
.attr("fill", function(d, i) {
return color(i);
})
.on("mouseover", arcTween(outerRadius, 0))
.on("mouseout", arcTween(outerRadius - 20, 150));
svg.selectAll(null)
.data(pieData)
.enter()
.append("text")
.attr("transform", function(d) {
d.innerRadius = 0;
d.outerRadius = outerRadius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(function(d, i) {
return data[i].label;
});
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);
};
});
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

How to add titles to multiple pie charts, while having a single legend?

I have this data:
var data = [
[
{"piece_value":76.34,name:"AG",group:"G1"},
{"piece_value":69.05,name:"A3",group:"G1"},
{"piece_value":275.19,name:"A4",group:"G1"}
],
[
{"piece_value":69.93,name:"AG",group:"G2"},
{"piece_value":61.50,name:"A3",group:"G2"},
{"piece_value":153.31,name:"A4",group:"G2"}
],
[
{"piece_value":67.48,name:"AG",group:"G3"},
{"piece_value":58.03,name:"A3",group:"G3"},
{"piece_value":145.93,name:"A4",group:"G3"}
]
];
and I have 3 pie charts (see the code snippet). I want to put the corresponding group values on top of each pie chart, and move a legend to the right side (a vertical legend). The problem is that now I have 3 legends (one per pie chart), but in fact it's always the same legend. Therefore instead of duplicating it 3 times, I want to have a single legend on the right side, while putting group values as titles.
How to do this?
var data = [
[
{"piece_value":76.34,name:"AG",group:"G1"},
{"piece_value":69.05,name:"A3",group:"G1"},
{"piece_value":275.19,name:"A4",group:"G1"}
],
[
{"piece_value":69.93,name:"AG",group:"G2"},
{"piece_value":61.50,name:"A3",group:"G2"},
{"piece_value":153.31,name:"A4",group:"G2"}
],
[
{"piece_value":67.48,name:"AG",group:"G3"},
{"piece_value":58.03,name:"A3",group:"G3"},
{"piece_value":145.93,name:"A4",group:"G3"}
]
];
var m = 10,
r = 90,
z = d3.scale.category20c();
var svg = d3.select("body").selectAll("svg")
.data(data)
.enter().append("svg")
.attr("width", (r + m) * 2)
.attr("height", (r + m) * 2)
.append("g")
.attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.piece_value;
});
var arc = d3.svg.arc()
.innerRadius(r / 2.2)
.outerRadius(r/1.2)
svg.selectAll("path")
.data(pie)
.enter().append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return z(i);
});
svg.selectAll("foo")
.data(pie)
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("transform", d => "translate(" + arc.centroid(d) + ")")
.text(d => d.data.piece_value);
svg.selectAll("foo")
.data(d=>d)
.enter()
.append("text")
.attr("transform", (d,i)=>"translate(" + (-r + 2.5*m + (i * 70)) + "," + (-r + m) + ")")
.text(d=>d.name);
svg.selectAll("foo")
.data(d=>d)
.enter()
.append("rect")
.attr("transform", (d,i)=>"translate(" + (-r + m + (i * 70)) + "," + (-r) + ")")
.attr("width", 10)
.attr("height", 10)
.style("fill", (d, i) => z(i));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
The idea behind of having one legend for each donut is that it allows having different data (regarding the categorical variables). However, since you said you want only one legend, I'm assuming that the categorical variables are the same for each chart. That being the case, we can create a new data array...
var legendData = data[0].map(d=>d.name);
... using any index (since, again, the arrays are the same regading the categorical variable), and instead of drawing another SVG, we can simply create the legend with HTML:
var legendDiv = d3.select("#legend");
var legendRow = legendDiv.selectAll("foo")
.data(legendData)
.enter()
.append("div")
.style("margin-bottom", "2px");
legendRow.append("div")
.html("&nbsp")
.attr("class", "rect")
.style("background-color", (d, i) => z(i));
legendRow.append("div")
.html(d => d);
Here is a demo with the vertical legend:
var data = [
[
{"piece_value":76.34,name:"AG",group:"G1"},
{"piece_value":69.05,name:"A3",group:"G1"},
{"piece_value":275.19,name:"A4",group:"G1"}
],
[
{"piece_value":69.93,name:"AG",group:"G2"},
{"piece_value":61.50,name:"A3",group:"G2"},
{"piece_value":153.31,name:"A4",group:"G2"}
],
[
{"piece_value":67.48,name:"AG",group:"G3"},
{"piece_value":58.03,name:"A3",group:"G3"},
{"piece_value":145.93,name:"A4",group:"G3"}
]
];
var m = 10,
r = 90,
z = d3.scale.category20c();
var svg = d3.select("body").selectAll("svg")
.data(data)
.enter().append("svg")
.attr("width", (r + m) * 2)
.attr("height", (r + m) * 2)
.append("g")
.attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.piece_value;
});
var arc = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r/1)
svg.selectAll("path")
.data(pie)
.enter().append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return z(i);
});
svg.selectAll("foo")
.data(pie)
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("transform", d => "translate(" + arc.centroid(d) + ")")
.text(d => d.data.piece_value);
var legendData = data[0].map(d=>d.name);
var legendDiv = d3.select("#legend");
var legendRow = legendDiv.selectAll("foo")
.data(legendData)
.enter()
.append("div")
.style("margin-bottom", "2px");
legendRow.append("div")
.html("&nbsp")
.attr("class", "rect")
.style("background-color", (d, i) => z(i));
legendRow.append("div")
.html(d=>d);
.rect {
width: 20px;
float: left;
margin-right: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="legend">Legend:</div>
PS: you said that you want the legend at the right hand side. To do that, simply create 2 divs, side by side: on the left one you append the SVGs, and on the right one you append the legend. I'll leave this as a homework for you.
PS2: pay attention to the fact that scale.category20 works on a first-come, first-served basis. So, for the legends to be accurate, the order of the categorical variables in the inner arrays has to be the same.
I would simply add a totally separate svg for the legend. Also, unless you need different svg, you might consider appending one group per pie, and one group for the legend (but that's more work with regard to translation).
For the sake of performances, I would also stick to a single binding for enter, and store it in local variable.
Finally, I would create one group per legend item to avoid duplication of translate code:
var data = [
[
{"piece_value":76.34,name:"AG",group:"G1"},
{"piece_value":69.05,name:"A3",group:"G1"},
{"piece_value":275.19,name:"A4",group:"G1"}
],
[
{"piece_value":69.93,name:"AG",group:"G2"},
{"piece_value":61.50,name:"A3",group:"G2"},
{"piece_value":153.31,name:"A4",group:"G2"}
],
[
{"piece_value":67.48,name:"AG",group:"G3"},
{"piece_value":58.03,name:"A3",group:"G3"},
{"piece_value":145.93,name:"A4",group:"G3"}
]
];
var m = 10,
r = 90,
z = d3.scale.category20c();
var svg = d3.select("body")
.selectAll("svg")
.data(data)
.enter()
.append("svg")
.attr("width", (r + m) * 2)
.attr("height", (r + m) * 2)
.append("g")
.attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.piece_value;
});
var arc = d3.svg.arc()
.innerRadius(r / 2.2)
.outerRadius(r/1.2)
svg.selectAll("path")
.data(pie)
.enter()
.append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return z(i);
});
var foo = svg.selectAll("foo")
.data(pie)
.enter();
foo.append("text")
.attr("text-anchor", "middle")
.attr("transform", d => "translate(" + arc.centroid(d) + ")")
.text(d => d.data.piece_value);
//the legend
var legendGroup = d3.select("body")
.append('svg')
.append('g')
.classed('legend',true);
var enterLegend = legendGroup
.selectAll('.legend-item')
.data(pie)
.enter();
var legendItem = enterLegend
.append('g')
.classed('legend-item',true)
.attr("transform", (d,i)=>"translate(" + (-r + (i * 70)) + "," + (-r + m) + ")");
legendItem.append("text")
.text(d=>d.name);
legendItem.append("rect")
.attr('x',10)
.attr("width", 10)
.attr("height", 10)
.style("fill", (d, i) => z(i));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I did not run the code, but I hope you get the idea.

How to update a pie chart?

I'm trying to make a pie chart that is updated when I press the button "2016" but instead of updating I create a new pie chart, how can I change the values of my pie chart? Thanks in advance. I tried to search a question but all of them are so specific.
var dataset = [{
key: "Alumnos",
value: 15
}, {
key: "AlumnosFCT",
value: 12
}];
var w = 300;
var h = 300;
var outerRadius = w / 2;
var innerRadius = 0;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var color = d3.scale.ordinal()
.domain([15, 12])
.range(["#FF4081", "#3F51B5"]);
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arcs = svg.selectAll("g.arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" + outerRadius + ", " + outerRadius + ")");
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
arcs.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.text(function(d) {
return d.value;
});
d3.selectAll("button").on("click", function() {
var paragraphID = d3.select(this).attr("id");
if (paragraphID == "2016") {
dataset.push({
key: "Alumnos",
value: 20
}, {
key: "AlumnosFCT",
value: 18
});
dataset.shift();
dataset.shift();
}
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var color = d3.scale.ordinal()
.domain([15, 12])
.range(["#FF4081", "#3F51B5"]);
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arcs = svg.selectAll("g.arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" + outerRadius + ", " + outerRadius + ")");
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
arcs.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.text(function(d) {
return d.value;
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button id="2016">2016</button>
In general, d3 is pretty good about managing DOM elements as long as you work within their API. In that way you can write a function that can create new elements for new data, or update existing elements with new data pertaining to those elements.
See the following updated version of your code snippet, specifically pulling out the data dependent DOM manipulations into a function called update:
/***
* Original Code
***/
var dataset = [{
key: "Alumnos",
value: 15
}, {
key: "AlumnosFCT",
value: 12
}];
var w = 300;
var h = 300;
var outerRadius = w / 2;
var innerRadius = 0;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var color = d3.scale.ordinal()
.domain([15, 12])
.range(["#FF4081", "#3F51B5"]);
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
/***
* update function for data dependent manipulations
***/
function update(data) {
var arcs = svg.selectAll("g.arc").data(pie(data));
arcs.exit().remove();
arcs.enter().append("g")
.attr("class", "arc")
.attr("transform", "translate(" + outerRadius + ", " + outerRadius + ")");
var paths = arcs.selectAll('path').data(function (d, i) {
d.idx = i;
return [d];
})
paths.enter().append('path');
paths
.attr("fill", function(d) {
return color(d.idx);
})
.attr("d", arc);
var texts = arcs.selectAll('text').data(function (d) {
return [d];
})
texts.enter().append('text');
texts.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.text(function(d) {
return d.value;
});
}
update(dataset);
/***
* Handler to set new data based on button clicked
***/
d3.select('button').on('click', function() {
var newData;
if (this.id === '2016') {
newData = [{
key: "Alumnos",
value: 20
}, {
key: "AlumnosFCT",
value: 18
}];
update(newData);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button id="2016">2016</button>
(depending on your browser, you might need to scroll the view to see the "2016" button)
Note the following advantages:
only elements whose data need to change are updated when update is called.
if you add a new data point when updating, a new element will be added without touching elements that should remain unchanged (via enter)
if you remove a data point when updating, that element will be removed (via exit)
d3 version: 3.4.11

How to correctly draw the text over my pie chart?

I created a pie chart and it is showing great.
Only, I noticed that some text is hidden by pie slice. By looking carefully, I noticed that each text can drawn over with the next slice.
so the rendering order goes like this : slice 1, text 1, slice 2, text 2, slice 3, text 3, etc...
How can I make it so the rendering order is slice 1, slice 2, slice 3...slice n.
Then text 1, text 2, text 3...text n
and then the text will always show since it will be drawn on top of every slice of the pie.
Thanks, here is my code
function createChart(dom, newData, title) {
d3.select(dom).selectAll("*").remove();
// Define size & radius of donut pie chart
var width = 450,
height = 800,
radius = Math.min(width, height) / 2;
// Define arc colours
var colour = d3.scale.category20();
// Define arc ranges
var arcText = d3.scale.ordinal()
.rangeRoundBands([0, width], .1, .3);
// Determine size of arcs
var arc = d3.svg.arc()
.innerRadius(radius - 130)
.outerRadius(radius - 10);
// Create the donut pie chart layout
var pie = d3.layout.pie()
.value(function (d) { return d.count; })
.sort(null);
// Append SVG attributes and append g to the SVG
var mySvg = d3.select(dom).append("svg")
.attr("width", width)
.attr("height", height);
var svg = mySvg
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
var svgText = mySvg
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
// Define inner circle
svg.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 100)
.attr("fill", "#fff") ;
// Calculate SVG paths and fill in the colours
var g = svg.selectAll(".arc")
.data(pie(newData))
.enter().append("g")
.attr("class", "arc");
// Append the path to each g
g.append("path")
.attr("d", arc)
//.attr("data-legend", function(d, i){ return parseInt(newData[i].count) + ' ' + newData[i].emote; })
.attr("fill", function(d, i) {
return colour(i);
});
// Append text labels to each arc
g.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("fill", "#fff")
.text(function(d,i) { return newData[i].count > 0 ? newData[i].emote : ''; })
// Append text to the inner circle
svg.append("text")
.style("text-anchor", "middle")
.attr("fill", "#36454f")
.text(function(d) { return title; })
.style("font-size","16px")
.style("font-weight", "bold");
}
Simplest approach is to give the text labels there own g and rebind the data:
// there own g
var textG = svg.selectAll(".labels")
.data(pie(newData))
.enter().append("g")
.attr("class", "labels");
// Append text labels to each arc
textG.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("fill", "#fff")
.text(function(d, i) {
return d.data.count > 0 ? d.data.emote : ''; // you can use d.data instead of indexing
});
Full example:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<script>
var newData = [{
count: 1,
emote: "OneTwoThree"
}, {
count: 1,
emote: "FourFiveSix"
}, {
count: 1,
emote: "SevenEightNine"
}, {
count: 15,
emote: "TenElevenTwelve"
},
]
// Define size & radius of donut pie chart
var width = 450,
height = 800,
radius = Math.min(width, height) / 2;
// Define arc colours
var colour = d3.scale.category20();
// Define arc ranges
var arcText = d3.scale.ordinal()
.rangeRoundBands([0, width], .1, .3);
// Determine size of arcs
var arc = d3.svg.arc()
.innerRadius(radius - 130)
.outerRadius(radius - 10);
// Create the donut pie chart layout
var pie = d3.layout.pie()
.value(function(d) {
return d.count;
})
.sort(null);
// Append SVG attributes and append g to the SVG
var mySvg = d3.select('body').append("svg")
.attr("width", width)
.attr("height", height);
var svg = mySvg
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
var svgText = mySvg
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
// Define inner circle
svg.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 100)
.attr("fill", "#fff");
// Calculate SVG paths and fill in the colours
var g = svg.selectAll(".arc")
.data(pie(newData))
.enter().append("g")
.attr("class", "arc");
// Append the path to each g
g.append("path")
.attr("d", arc)
//.attr("data-legend", function(d, i){ return parseInt(newData[i].count) + ' ' + newData[i].emote; })
.attr("fill", function(d, i) {
return colour(i);
});
var textG = svg.selectAll(".labels")
.data(pie(newData))
.enter().append("g")
.attr("class", "labels");
// Append text labels to each arc
textG.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("fill", "#fff")
.text(function(d, i) {
return d.data.count > 0 ? d.data.emote : '';
});
</script>
</body>
</html>

Legend in D3 circle pack diagram

I need to create a legend for the bubble/circle pack chart. I'm displaying the values inside the circle. I need the names as the legend. For an instance, in the below provided data, if the value is 60, i need that name "Petrol" in the legend. How could i achieve it?
Snippet:
var diameter = 200,
format = d3.format(",d"),
color = ["#7b6888", "#ccc", "#aaa", "#6b486b"];
var bubble = d3.layout.pack().size([diameter, diameter]);
var svg = d3.select("#bubbleCharts").append("svg")
.attr("width", diameter + 10)
.attr("height", diameter)
.attr("class", "bubble");
d3.json("flare.json", function(error, root) {
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root))
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + 20 + "," + d.y + ")"; });
node.append("circle").attr("r", function(d) { return d.r+ 7; })
.style("fill", function(d,i) { return color[i];} );
node.append("text").attr("dy", ".3em").style("text-anchor", "middle")
.text(function(d) { return d.value+"%"; });
});
function classes(root) {
var classes = [];
function recurse(name, node) {
if (node.children)
node.children.forEach(function(child){
recurse(node.name, child);
});
else
classes.push({packageName: name, value: node.value});
}
recurse(null, root);
return {children: classes};
}
var legend = d3.select("#bubbleChart").append("svg")
.selectAll("g").data(node.children).enter().append("g")
.attr("class","legend")
.attr("width", radius)
.attr("height", radius * 2)
.attr("transform", function(d, i) {return "translate(" + i *10 + "0" + ")"; });
legend.append("rect").attr("width", 18).attr("height", 10)
.style("fill", function(d, i) { return color[i];});
legend.append("text").attr("x", 24).attr("y", 5).attr("dy", ".35em")
.text(function(d) { return d; });
My data:
{
"name": "Spending Activity",
"children": [
{"name": "Petrol", "value": 10},
{"name": "Travel", "value": 60},
{"name": "Medical", "value": 25},
{"name": "Shopping", "value": 5}
]
}
How would i take the values from json and create a legend? Thanks.
You can simply iterate through your data set and add those values:
legend.selectAll("circle").data(data.children)
.enter()
.append("circle")
.attr("cy", function(d,i) { return (i+1) * 10; })
.attr("r", function(d) { return d.r+ 7; })
.style("fill", function(d,i) {
return color[i];
});
legend.selectAll("text").data(data.children)
.enter()
.append("text")
.attr("transform", function(d,i) {
return "translate(10," + ((i+1) * 10) + ")";
});

Categories

Resources