d3.js donut chart with legend outside donut - javascript

In the following code, I would like to put the donut legends outside the donut, on its right:
http://bl.ocks.org/juan-cb/1984c7f2b446fffeedde
Which line code should I change to do it?
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = -3 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d; });
Something similar to this one:
http://www.jqueryscript.net/demo/jQuery-Plugin-To-Convert-Tabular-Data-Into-Donut-Charts-Chart-js/
Edit: the solution was just a matter of changing horizontal and vertical coordinates. No need of complicated stuff.

These 2 variables:
var horz = -3 * legendRectSize; // X-axis translation.
var vert = i * height - offset; // Y-axis translation.
You could modify horz and vert formulas for translating. Like this:
var horz = 30 * legendRectSize; // Will be right side of the donut chart.
var vert = i * height - offset; // Still at the middle of the donut chart vertically.

Related

why my two graph of d3.js is not combined in the single page?

I have made two separate graph on separate page of Bar and pie chart respectively and now i wanted to combine this two graph in the single page so that I can have a dashboard. but when i start to combine to two graph in the main page its not happening and they overlap of each other.
Code:
https://github.com/Mustafa2911/d3-design/blob/main/combine.html
Combine file contain: Code of both pie and bar chart.
Bar file contain: Code of bar chart.
Pie chart contain: Code of pie chart.
Tried this with your code.
Scroll to see the bar graph axis.
NOTE: The bar graph data will not be available ∵ it is from the demo1.csv file in your repository.
Hope this helps.
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<style>
#my_dataviz {
display: inline-block;
width: 50%;
}
</style>
</head>
<body>
<div id="my_dataviz"></div>
<script>
// set the dimensions and margins of the graph
var width = 800
height = 450
margin = 40
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = Math.min(width, height) / 2 - margin
// append the svg object to the div called 'my_dataviz'
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Create dummy data
var data = {
Corporation_Tax: 15,
Income_Tax: 15,
Customs: 5,
Union_Excise_Duties: 7,
Good_and_Service_tax: 16,
Non_tax_Revenue: 5,
Non_Dept_Capital_Receipt: 2,
Borrowings_Liabilities: 35
}
// set the color scale
var color = d3.scaleOrdinal()
.domain(["a", "b", "c", "d", "e", "f", "g", "h"])
.range(d3.schemeSet1);
// Compute the position of each group on the pie:
var pie = d3.pie()
.sort(null) // Do not sort group by size
.value(function(d) {
return d.value;
})
var data_ready = pie(d3.entries(data))
// The arc generator
var arc = d3.arc()
.innerRadius(radius * 0.5) // This is the size of the donut hole
.outerRadius(radius * 0.8)
// Another arc that won't be drawn. Just for labels positioning
var outerArc = d3.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9)
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d) {
return (color(d.data.key))
})
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("opacity", 1)
// Add the polylines between chart and labels:
svg
.selectAll('allPolylines')
.data(data_ready)
.enter()
.append('polyline')
.attr("stroke", "black")
.style("fill", "none")
.attr("stroke-width", 1)
.attr('points', function(d) {
var posA = arc.centroid(d) // line insertion in the slice
var posB = outerArc.centroid(d) // line break: we use the other arc generator that has been built only for that
var posC = outerArc.centroid(d); // Label position = almost the same as posB
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 // we need the angle to see if the X position will be at the extreme right or extreme left
posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left
return [posA, posB, posC]
})
// Add the polylines between chart and labels:
svg
.selectAll('allLabels')
.data(data_ready)
.enter()
.append('text')
.text(function(d) {
console.log(d.data.key);
return d.data.key
})
.attr('transform', function(d) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
})
.style('text-anchor', function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
return (midangle < Math.PI ? 'start' : 'end')
})
</script>
<style>
#my_dataviz {
display: inline-block;
width: 50%;
}
</style>
<div id="my_dataviz_es"></div>
<script>
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 30,
bottom: 40,
left: 160
},
width = 460,
height = 400;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz_es")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Parse the Data
d3.csv("demo1.csv", function(data) {
// Add X axis
var x = d3.scaleLinear()
.domain([0, 550000])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end");
// Y axis
var y = d3.scaleBand()
.range([0, height])
.domain(data.map(function(d) {
return d.Country;
}))
.padding(.1);
svg.append("g")
.call(d3.axisLeft(y))
//Bars
svg.selectAll("myRect")
.data(data)
.enter()
.append("rect")
.attr("x", x(0))
.attr("y", function(d) {
return y(d.Country);
})
.attr("width", function(d) {
return x(d.Value);
})
.attr("height", y.bandwidth())
.attr("fill", "#69b3a2")
// .attr("x", function(d) { return x(d.Country); })
// .attr("y", function(d) { return y(d.Value); })
// .attr("width", x.bandwidth())
// .attr("height", function(d) { return height - y(d.Value); })
// .attr
})
</script>
</body>
</html>
EDIT: See here - https://codepen.io/KZJ/pen/rNpqvdq?editors=1011 - for changes made reg. the below comment
what if I want to have my bar chart at the top and on right side i want to have my pie chart
Changed -
a) Both charts were using the same name 'svg' to d3.select() the divs. This caused the charts to overlap.
b) Modified width, height, transform, and added some border CSS - only for demonstration purposes - It can be removed/edited as required.
FYR this is how it looks now -

d3 donut chart transform: translateY

I am making a graph in which it shows the percentage of each data type like this:
but I have a problem copy the code and I have created it the same as the previous example but the titles put them above the percentage and I would like to fix it by placing them above the numbers as it is in the first image, since I have it like this:
This is the code where I send to call the text and in example it already throws me a transformation but I want to upload it so that it remains as a title
svg
.selectAll("allLabels")
.data(name_ready)
.enter()
.append("text")
.text(function (d) {
console.log(d.data.key);
return d.data.key;
})
.style("font-size", "1rem")
.attr("transform", function (d) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2;
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
return "translate(" + pos + ")";
})
.attr("class", "fontDonut")
.style("text-anchor", function (d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2;
return midangle < Math.PI ? "start" : "end";
});
The code is like this since it is automatic and it positions itself depending on how much data there is
// set the dimensions and margins of the graph
var width = 400;
var height = 250;
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = 100;
// append the svg object to the div called 'my_dataviz'
var svg = d3
.select("#my_char")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Create dummy data
var data = {
"25%": 25,
"30% ": 30,
"20%": 20,
"25% ": 25
}
var name = {
"Married": 30,
"Divorced": 30,
"Single": 40,
"Single2 ": 25
}
// set the color scale
var color = d3
.scaleOrdinal([`#C8DBFB`, `#93B6F8`, `#256EF1`]);
// Compute the position of each group on the pie:
var pie = d3
.pie()
.sort(null) // Do not sort group by size
.value(function(d) {
return d.value;
});
var data_ready = pie(d3.entries(data));
var name_ready = pie(d3.entries(name));
// The arc generator
var arc = d3
.arc()
.innerRadius(radius * 0.6) // This is the size of the donut hole
.outerRadius(radius * 0.8);
// Another arc that won't be drawn. Just for labels positioning
var outerArc = d3
.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll("allSlices")
.data(data_ready)
.enter()
.append("path")
.attr("d", arc)
.attr("fill", function(d) {
return color(d.data.key);
})
.style("opacity", 0.7);
// Add the polylines between chart and labels:
svg
.selectAll("allLabels")
.data(data_ready)
.enter()
.append("text")
.text(function(d) {
console.log(d.data.key);
return d.data.key;
})
.style("font-size", "2rem")
.style("font-weight", "700")
.attr("transform", function(d) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2;
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
return "translate(" + pos + ")";
})
.style("text-anchor", function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2;
return midangle < Math.PI ? "start" : "end";
});
svg
.selectAll("allLabels")
.data(name_ready)
.enter()
.append("text")
.text(function(d) {
console.log(d.data.key);
return d.data.key;
})
.style("font-size", "1rem")
.attr("transform", function(d) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2;
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
return "translate(" + pos + ")";
})
.attr("class", "fontDonut")
.style("text-anchor", function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2;
return midangle < Math.PI ? "start" : "end";
});
.fontDonut {
margin-top: 8rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<div class="text-center bg-white shadow py-6 px-6 chart-container">
<div class="flex justify-start font-bold mb-4 ">
<p class="font-bold ml-4">Children</p>
</div>
<div id="my_char" />
</div>
Don't treat the two values as separate, but use them like a bloc instead. I used basic trigonometry to find the angle of the midpoint like you did, but then draw a line from the centre, so the label is aligned with the middle of the arc and all labels are the same distance from the donut.
Then, I don't have to fiddle with the labels, and instead just add the percentage on top, with a small offset. Note that margin only works in HTML, not in SVG.
// set the dimensions and margins of the graph
var width = 400;
var height = 250;
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = 100;
// append the svg object to the div called 'my_dataviz'
var svg = d3
.select("#my_char")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Create dummy data
var data = {
//"Married": 30,
"Divorced": 30,
"Single": 40,
"Single2 ": 25
}
// set the color scale
var color = d3
.scaleOrdinal([`#C8DBFB`, `#93B6F8`, `#256EF1`, `darkblue`]);
// Compute the position of each group on the pie:
var pie = d3
.pie()
.sort(null) // Do not sort group by size
.value(function(d) {
return d.value;
});
var data_ready = pie(d3.entries(data));
// The arc generator
var arc = d3
.arc()
.innerRadius(radius * 0.6) // This is the size of the donut hole
.outerRadius(radius * 0.8);
// Another arc that won't be drawn. Just for labels positioning
var outerArc = d3
.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll("path")
.data(data_ready)
.enter()
.append("path")
.attr("d", arc)
.attr("fill", function(d) {
return color(d.data.key);
})
.style("opacity", 0.7);
// Add the polylines between chart and labels:
const labelGroup = svg
.selectAll(".labelGroup")
.data(data_ready)
.enter()
.append("g")
.attr("class", "labelGroup")
// Transform the whole group, not the individual text items
.attr("transform", function(d) {
// Get the angle
var midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;
// Define the radius
var textRadius = 1.4 * radius;
// Use trigonometry to find the correct position
var x = Math.sin(midAngle) * textRadius;
var y = -Math.cos(midAngle) * textRadius;
y = Math.min(y, height / 2 - 20);
return "translate(" + [x, y] + ")";
})
.style("text-anchor", "middle");
labelGroup
.append("text")
.text(function(d) {
return d.data.key;
})
.attr("dominant-baseline", "hanging")
.attr("dy", 5)
.style("font-size", "2rem")
.style("font-weight", "700");
labelGroup
.append("text")
.text(function(d) {
return d.data.value + "%";
})
.attr("dy", -5)
.style("font-size", "1rem");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<div class="text-center bg-white shadow py-6 px-6 chart-container">
<div class="flex justify-start font-bold mb-4 ">
<p class="font-bold ml-4">Children</p>
</div>
<div id="my_char" />
</div>

Javascript d3 pie chart doesn't pull data from JSON file with list of dictionaries

I have a .json file with data, and I'd like to make a d3 donut (pie) chart from it. I'm not especially fluent in javascript, and every example I can find either pulls from inline json data or the json file is structured differently than mine (mine is a list of dictionaries; theirs are often single dictionaries). I've been troubleshooting for a few days, and somehow can't land on anything that actually works. Any thoughts/tips?
The example at https://www.d3-graph-gallery.com/graph/donut_label.html uses inline json data to render a donut chart with labels. I've attempted to modify it that code by:
pulling json data from /data/all-facet-digitized.json
pull labels each dictionary's "facet" key ("true" and "false"), and values from each dictionary's "count" key (373977 and 55433).
change the color scale domain to match the facet keys ("true" and "false")
/data/all-facet-digitized.json looks like:
[
{
"count": "55433",
"facet": "true"
},
{
"count": "373977",
"facet": "false"
}
]
Code in the of my html file looks like:
<div id="chart"></div> <!-- div containing the donut chart -->
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
// set the dimensions and margins of the graph
var width = 450
height = 450
margin = 40
// The radius of the pieplot is half the width or half the height (smallest one) minus margin.
var radius = Math.min(width, height) / 2 - margin
// append the svg object to the div called 'chart'
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Parse the Data
d3.json("/data/all-facet-digitized.json", function(data) {
// set the color scale
var color = d3.scaleOrdinal()
.domain(["true","false"])
.range(d3.schemeDark2);
// Compute the position of each group on the pie:
var pie = d3.pie()
.sort(null) // Do not sort group by size
.value(function(d) {return d.count; })
var data_ready = pie(d3.entries(data))
// The arc generator
var arc = d3.arc()
.innerRadius(radius * 0.5) // This is the size of the donut hole
.outerRadius(radius * 0.8)
// Another arc that won't be drawn. Just for labels positioning
var outerArc = d3.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9)
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.facet)) })
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("opacity", 0.7)
// Add the polylines between chart and labels:
svg
.selectAll('allPolylines')
.data(data_ready)
.enter()
.append('polyline')
.attr("stroke", "black")
.style("fill", "none")
.attr("stroke-width", 1)
.attr('points', function(d) {
var posA = arc.centroid(d) // line insertion in the slice
var posB = outerArc.centroid(d) // line break: we use the other arc generator that has been built only for that
var posC = outerArc.centroid(d); // Label position = almost the same as posB
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 // we need the angle to see if the X position will be at the extreme right or extreme left
posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left
return [posA, posB, posC]
})
// Add the polylines between chart and labels:
svg
.selectAll('allLabels')
.data(data_ready)
.enter()
.append('text')
.text( function(d) { console.log(d.facet) ; return d.facet} )
.attr('transform', function(d) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
})
.style('text-anchor', function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
return (midangle < Math.PI ? 'start' : 'end')
})
})
</script>
My result renders as an empty space:
<div id="chart">
<svg width="450" height="450">
<g transform="translate(225,225)"></g>
</svg>
</div>
The schemeDark2 doens't exist in d3 v4. I've replaced it with schemeCategory10:
var color = d3.scaleOrdinal()
.domain(["true","false"])
.range(d3.schemeCategory10);
Since you have an array of objects, you don't need d3.entries. That takes an object and converts it to an array where each key is an item of the array. But since you already have an array here, you can put it directly in pie():
// Compute the position of each group on the pie:
var pie = d3.pie()
.sort(null) // Do not sort group by size
.value(function(d) {return d.count; })
var data_ready = pie(data)
Now that you've got the data, you can access it on any of the functions: try putting console.log(data_ready) to see what's available. You'll see that the data is bound for each object as the .data property. pie() takes an array and puts it in a format that's convenient to make pie charts with.
Say we want to access the facet property: we would access that as item.data.facet. So in your functions, to access, you can do:
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.data.facet)) })
<head></head>
<div id="chart"></div> <!-- div containing the donut chart -->
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
// set the dimensions and margins of the graph
var width = 450
height = 450
margin = 40
// The radius of the pieplot is half the width or half the height (smallest one) minus margin.
var radius = Math.min(width, height) / 2 - margin
// append the svg object to the div called 'chart'
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Parse the Data
var data = [
{
"count": "55433",
"facet": "true"
},
{
"count": "373977",
"facet": "false"
}
]
// set the color scale
var color = d3.scaleOrdinal()
.domain(["true","false"])
.range(d3.schemeCategory10);
// Compute the position of each group on the pie:
var pie = d3.pie()
.sort(null) // Do not sort group by size
.value(function(d) {return d.count; })
var data_ready = pie(data)
console.log('data_r', data_ready)
// The arc generator
var arc = d3.arc()
.innerRadius(radius * 0.5) // This is the size of the donut hole
.outerRadius(radius * 0.8)
// Another arc that won't be drawn. Just for labels positioning
var outerArc = d3.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9)
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.data.facet)) })
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("opacity", 0.7)
// Add the polylines between chart and labels:
svg
.selectAll('allPolylines')
.data(data_ready)
.enter()
.append('polyline')
.attr("stroke", "black")
.style("fill", "none")
.attr("stroke-width", 1)
.attr('points', function(d) {
var posA = arc.centroid(d) // line insertion in the slice
var posB = outerArc.centroid(d) // line break: we use the other arc generator that has been built only for that
var posC = outerArc.centroid(d); // Label position = almost the same as posB
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 // we need the angle to see if the X position will be at the extreme right or extreme left
posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left
return [posA, posB, posC]
})
// Add the polylines between chart and labels:
svg
.selectAll('allLabels')
.data(data_ready)
.enter()
.append('text')
.text( function(d) { return d.data.facet} )
.attr('transform', function(d) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
})
.style('text-anchor', function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
return (midangle < Math.PI ? 'start' : 'end')
})
</script>
Ok, the issues here is that you've completely missed how data_ready is structured after converting the JSON response. You might want to add console.log(data_ready) just after you set data_ready and inspect it in the console for better understanding of the following fixes.
First a color fix:
.attr('fill', function(d){ return(color(d.data.value.facet)) })
Then a data fix:
.value(function(d) {return d.value.count; })
And lastly a label fix:
.text( function(d) { console.log(d.data.key) ; return d.data.value.facet } )
Your script should look like this:
// set the dimensions and margins of the graph
var width = 450
height = 450
margin = 40
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = Math.min(width, height) / 2 - margin
// append the svg object to the div called 'my_dataviz'
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.json("/data/all-facet-digitized.json", function(data) {
// set the color scale
var color = d3.scaleOrdinal()
.domain(["true","false"])
.range(d3.schemeDark2);
// Compute the position of each group on the pie:
var pie = d3.pie()
.sort(null) // Do not sort group by size
.value(function(d) {return d.value.count; })
var data_ready = pie(d3.entries(data))
// The arc generator
var arc = d3.arc()
.innerRadius(radius * 0.5) // This is the size of the donut hole
.outerRadius(radius * 0.8)
// Another arc that won't be drawn. Just for labels positioning
var outerArc = d3.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9)
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.data.value.facet)) })
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("opacity", 0.7)
// Add the polylines between chart and labels:
svg
.selectAll('allPolylines')
.data(data_ready)
.enter()
.append('polyline')
.attr("stroke", "black")
.style("fill", "none")
.attr("stroke-width", 1)
.attr('points', function(d) {
var posA = arc.centroid(d) // line insertion in the slice
var posB = outerArc.centroid(d) // line break: we use the other arc generator that has been built only for that
var posC = outerArc.centroid(d); // Label position = almost the same as posB
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 // we need the angle to see if the X position will be at the extreme right or extreme left
posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left
return [posA, posB, posC]
})
// Add the polylines between chart and labels:
svg
.selectAll('allLabels')
.data(data_ready)
.enter()
.append('text')
.text( function(d) { console.log(d.data.key) ; return d.data.value.facet } )
.attr('transform', function(d) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
})
.style('text-anchor', function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
return (midangle < Math.PI ? 'start' : 'end')
})
})

How to position the legend to the right of the donut chart

Here is my plunkr code, in this position of the legend is at center, how do I position it to the right of the donut chart?
var svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2 - radius) +
',' + (height / 2 - radius) + ')');
I tried with the above code but it's not working.
Define new variable and set your legend width in pixels:
var legendWidth = 150;
Use this variable for svg element width:
var svg = d3.select('#chart')
.append('svg')
.attr('width', width + legendWidth) // <== !!!
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')');
Rewrite your function for legend transform attribute this way:
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var vert = i * height - offset;
return 'translate(' + width / 2 + ',' + vert + ')'; // <== move every legend item on half of width
});
Check working example here.
Checkout this version of your jsfiddle. I have corrected some calculation to show legend on right side. highlighted code change below.
// For increasing horizontal space
var width = 600;
// For arranging chart in full space
var svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 4) +
',' + (height / 2) + ')');
// For moving legend to right
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = 7 * legendRectSize;

Linear scale not using range specified in d3

For some reason my graph is going into my padding, even though I've incorporated the padding into the linear scale of y values. Here's my code:
$(document).ready(function(){
//padding
var padding = {
top: 20,
right: 20,
bottom: 20,
left: 20,
};
//graph dimensions
var w = 1000
var h = 500
//append and assign variable reference to svg
var svg = d3.select('section').append('svg').attr('id', 'graph').attr('width', w).attr('height', h);
//get json data
d3.json('https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/GDP-data.json', function(error, data){
//error handling
if (error){
console.warn(error);
}
var dataset = data.data;
//minimum and maximum date data points
var minDate = new Date(dataset[0][0]);
var maxDate = new Date(dataset[274][0]);
//scale data to svg dimensions
var xScale = d3.time.scale().domain([minDate, maxDate]).range([padding.left, w - padding.right]);
var yScale = d3.scale.linear().domain([0, d3.max(dataset, function(d){return d[1];})]).range([h - padding.bottom, padding.top]);
console.log(yScale(0));
//make x and y axis
var xAxis = d3.svg.axis().orient('bottom').scale(xScale);
//generate graph
svg.selectAll('rect')
.data(dataset)
.enter()
.append('rect')
.attr('x', function(d, i){
return i * ((w - padding.left - padding.right) / dataset.length) + padding.left;
})
.attr('y', function(d){
return yScale(d[1]);
})
.attr('width', (w - padding.left - padding.right) / dataset.length)
.attr('height', function(d){return d[1]})
.attr('fill', '#4682B4');
//generate x and y axix
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(0,' + (h - padding.bottom) + ')')
.call(xAxis);
});
});
Here's the page on Codepen.
Does anyone know why the graph is cutting into the x axis like that? How do I fix it so that the bars start padding.bottom pixels away from the bottom
I've found the answer, I changed height attribute of svg to:
.attr('height', function(d){return h - padding.bottom - yScale(d[1])});
and it worked

Categories