Related
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 -
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')
})
})
I'm trying to display labels with its value and tooltip in semi donut pie chart using D3 JS.
I'm not able to display labels and their values at the simultaneously.
And how can I add the tooltip on this chart?
I tried implementing this fiddle.
https://jsfiddle.net/SampathPerOxide/hcvuqjt2/6/
var width = 400;
var height = 300; //this is the double because are showing just the half of the pie
var radius = Math.min(width, height) / 2;
var labelr = radius + 30; // radius for label anchor
//array of colors for the pie (in the same order as the dataset)
var color = d3.scale
.ordinal()
.range(['#2b5eac', '#0dadd3', '#ffea61', '#ff917e', '#ff3e41']);
data = [
{ label: 'CDU', value: 10 },
{ label: 'SPD', value: 15 },
{ label: 'Die Grünen', value: 8 },
{ label: 'Die Mitte', value: 1 },
{ label: 'Frei Wähler', value: 3 }
];
var vis = d3
.select('#chart')
.append('svg') //create the SVG element inside the <body>
.data([data]) //associate our data with the document
.attr('width', width) //set the width and height of our visualization (these will be attributes of the <svg> tag
.attr('height', height)
.append('svg:g') //make a group to hold our pie chart
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')'); //move the center of the pie chart from 0, 0 to radius, radius
var arc = d3.svg
.arc() //this will create <path> elements for us using arc data
.innerRadius(79)
// .outerRadius(radius);
.outerRadius(radius - 10); // full height semi pie
//.innerRadius(0);
var pie = d3.layout
.pie() //this will create arc data for us given a list of values
.startAngle(-90 * (Math.PI / 180))
.endAngle(90 * (Math.PI / 180))
.padAngle(0.02) // some space between slices
.sort(null) //No! we don't want to order it by size
.value(function(d) {
return d.value;
}); //we must tell it out to access the value of each element in our data array
var arcs = vis
.selectAll('g.slice') //this selects all <g> elements with class slice (there aren't any yet)
.data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties)
.enter() //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
.append('svg:g') //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
.attr('class', 'slice'); //allow us to style things in the slices (like text)
arcs
.append('svg:path')
.attr('fill', function(d, i) {
return color(i);
}) //set the color for each slice to be chosen from the color function defined above
.attr('d', arc); //this creates the actual SVG path using the associated data (pie) with the arc drawing function
arcs
.append('svg:text')
.attr('class', 'labels') //add a label to each slice
.attr('fill', 'grey')
.attr('transform', function(d) {
var c = arc.centroid(d),
xp = c[0],
yp = c[1],
// pythagorean theorem for hypotenuse
hp = Math.sqrt(xp * xp + yp * yp);
return 'translate(' + (xp / hp) * labelr + ',' + (yp / hp) * labelr + ')';
})
.attr('text-anchor', 'middle') //center the text on it's origin
.text(function(d, i) {
return data[i].value;
})
.text(function(d, i) {
return data[i].label;
}); //get the label from our original data array
I'm trying to achieve this. https://i.imgur.com/kTXeAXt.png
First of all, if you console.log the data (from .data(pie)) you used for displaying text label and value, you will noticed that label can only be access via d.data.label instead of data[i].label.
{data: {label: "Frei Wähler", value: 3}, value: 3, startAngle: 1.304180706233562, endAngle: 1.5707963267948966, padAngle: 0.02}
Therefore in order to correctly display both label and value, the code should be:
arcs.append("svg:text")
.attr("class", "labels")//add a label to each slice
.attr("fill", "grey")
.attr("transform", function(d) {
var c = arc.centroid(d),
xp = c[0],
yp = c[1],
// pythagorean theorem for hypotenuse
hp = Math.sqrt(xp*xp + yp*yp);
return "translate(" + (xp/hp * labelr) + ',' +
(yp/hp * labelr) + ")";
})
.attr("text-anchor", "middle") //center the text on it's origin
.text(function(d, i) { return d.data.value; })
.text(function(d, i) { return d.data.label; });
How to add tooltip
As for how to create d3 tooltip, it needs a little of css, html and then add d3 event handling.
1) add the following html to your index.html:
<div id="tooltip" class="hidden"><p id="tooltip-data"></p></div>
2) add a little bit css to set the div to position:absolute and hide the tooltip with display:none, and gives it a little bit styling per your preference:
<style>
#tooltip {
position:absolute;
background: #ffffe0;
color: black;
width: 180px;
border-radius: 3px;
box-shadow: 2px 2px 6px rgba(40, 40, 40, 0.5);
}
#tooltip.hidden {
display:none;
}
#tooltip p {
margin: 0px;
padding: 8px;
font-size: 12px;
}
3) We then add mouseover event handler, the idea is when the mouse is over the chart, we will set the top and left properties of the #tooltip css style to where the mouse is, and set the css display property to show the tooltip.
//tooltip
arcs.on("mouseover", function(d) {
d3.select("#tooltip")
.style("left", `${d3.event.clientX}px`)
.style("top", `${d3.event.clientY}px`)
.classed("hidden", false);
d3.select("#tooltip-data")
.html(`Label: ${d.data.label}<br>Value: ${d.data.value}`);
});
arcs.on("mouseout", function(d) {
d3.select("#tooltip")
.classed("hidden", true);
});
selection.text([value])
If a value is specified, sets the text content to the specified value on all selected elements, replacing any existing child elements.
So you are setting the text content with the value and immediately replacing it with the label.
What you can do is return a combined string from the value and label of your datum in a template literal like this:
.text(function(d, i) { return `${data[i].value} - ${data[i].label}`; })
var width = 400;
var height = 300; //this is the double because are showing just the half of the pie
var radius = Math.min(width, height) / 2;
var labelr = radius + 30; // radius for label anchor
//array of colors for the pie (in the same order as the dataset)
var color = d3.scale.ordinal()
.range(['#2b5eac', '#0dadd3', '#ffea61', '#ff917e', '#ff3e41']);
data = [{
label: 'CDU',
value: 10
},
{
label: 'SPD',
value: 15
},
{
label: 'Die Grünen',
value: 8
},
{
label: 'Die Mitte',
value: 1
},
{
label: 'Frei Wähler',
value: 3
}
];
var vis = d3.select("#chart")
.append("svg") //create the SVG element inside the <body>
.data([data]) //associate our data with the document
.attr("width", width) //set the width and height of our visualization (these will be attributes of the <svg> tag
.attr("height", height)
.append("svg:g") //make a group to hold our pie chart
.attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')'); //move the center of the pie chart from 0, 0 to radius, radius
var arc = d3.svg.arc() //this will create <path> elements for us using arc data
.innerRadius(79)
// .outerRadius(radius);
.outerRadius(radius - 10) // full height semi pie
//.innerRadius(0);
var pie = d3.layout.pie() //this will create arc data for us given a list of values
.startAngle(-90 * (Math.PI / 180))
.endAngle(90 * (Math.PI / 180))
.padAngle(.02) // some space between slices
.sort(null) //No! we don't want to order it by size
.value(function(d) {
return d.value;
}); //we must tell it out to access the value of each element in our data array
var arcs = vis.selectAll("g.slice") //this selects all <g> elements with class slice (there aren't any yet)
.data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties)
.enter() //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
.append("svg:g") //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
.attr("class", "slice"); //allow us to style things in the slices (like text)
arcs.append("svg:path")
.attr("fill", function(d, i) {
return color(i);
}) //set the color for each slice to be chosen from the color function defined above
.attr("d", arc); //this creates the actual SVG path using the associated data (pie) with the arc drawing function
arcs.append("svg:text")
.attr("class", "labels") //add a label to each slice
.attr("fill", "grey")
.attr("transform", function(d) {
var c = arc.centroid(d),
xp = c[0],
yp = c[1],
// pythagorean theorem for hypotenuse
hp = Math.sqrt(xp * xp + yp * yp);
return "translate(" + (xp / hp * labelr) + ',' +
(yp / hp * labelr) + ")";
})
.attr("text-anchor", "middle") //center the text on it's origin
.text(function(d, i) {
return `${data[i].value} - ${data[i].label}`;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<div id="chart" style="width: 330px;height: 200px;"></div>
Edit:
If you want the two text strings to be on separate lines you would have to append some <tspan> elements and position them.
var width = 400;
var height = 300; //this is the double because are showing just the half of the pie
var radius = Math.min(width, height) / 2;
var labelr = radius + 30; // radius for label anchor
//array of colors for the pie (in the same order as the dataset)
var color = d3.scale.ordinal()
.range(['#2b5eac', '#0dadd3', '#ffea61', '#ff917e', '#ff3e41']);
data = [{
label: 'CDU',
value: 10
},
{
label: 'SPD',
value: 15
},
{
label: 'Die Grünen',
value: 8
},
{
label: 'Die Mitte',
value: 1
},
{
label: 'Frei Wähler',
value: 3
}
];
var vis = d3.select("#chart")
.append("svg") //create the SVG element inside the <body>
.data([data]) //associate our data with the document
.attr("width", width) //set the width and height of our visualization (these will be attributes of the <svg> tag
.attr("height", height)
.append("svg:g") //make a group to hold our pie chart
.attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')'); //move the center of the pie chart from 0, 0 to radius, radius
var arc = d3.svg.arc() //this will create <path> elements for us using arc data
.innerRadius(79)
// .outerRadius(radius);
.outerRadius(radius - 10) // full height semi pie
//.innerRadius(0);
var pie = d3.layout.pie() //this will create arc data for us given a list of values
.startAngle(-90 * (Math.PI / 180))
.endAngle(90 * (Math.PI / 180))
.padAngle(.02) // some space between slices
.sort(null) //No! we don't want to order it by size
.value(function(d) {
return d.value;
}); //we must tell it out to access the value of each element in our data array
var arcs = vis.selectAll("g.slice") //this selects all <g> elements with class slice (there aren't any yet)
.data(pie) //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties)
.enter() //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
.append("svg:g") //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
.attr("class", "slice"); //allow us to style things in the slices (like text)
arcs.append("svg:path")
.attr("fill", function(d, i) {
return color(i);
}) //set the color for each slice to be chosen from the color function defined above
.attr("d", arc); //this creates the actual SVG path using the associated data (pie) with the arc drawing function
const textEl = arcs.append("svg:text")
.attr("class", "labels") //add a label to each slice
.attr("fill", "grey")
.attr("transform", function(d) {
var c = arc.centroid(d),
xp = c[0],
yp = c[1],
// pythagorean theorem for hypotenuse
hp = Math.sqrt(xp * xp + yp * yp);
return "translate(" + (xp / hp * labelr) + ',' +
(yp / hp * labelr) + ")";
})
.attr("text-anchor", "middle"); //center the text on it's origin
textEl.append('tspan')
.text(function(d, i) {
return data[i].label;
});
textEl.append('tspan')
.text(function(d, i) {
return data[i].value;
})
.attr('x', '0')
.attr('dy', '1.2em');
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<div id="chart" style="width: 330px;height: 200px;"></div>
I have this donut chart currently working in an AngularJS app:
But the design mockup says we would like this, note the border-radius property on the green portion of the arc:
How do I add a border-radius to the SVG that d3js outputs, the code I'm currently using looks like this:
let data = [
{
label: 'Data',
count: scope.data
},
{
label: 'Fill',
count: 100 - scope.data
}
];
let width = 60;
let height = 60;
let radius = Math.min(width, height) / 2;
let color = d3.scale
.ordinal()
.range(['#3CC692', '#F3F3F4']);
let selector = '#donut-asset-' + scope.chartId;
d3
.select(selector)
.selectAll('*')
.remove();
let svg = d3
.selectAll(selector)
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr(
'transform',
'translate(' + width / 2 + ',' + height / 2 + ')'
);
let arc = d3.svg
.arc()
.innerRadius(23)
.outerRadius(radius);
let pie = d3.layout
.pie()
.value(function(d) {
return d.count;
})
.sort(null);
let path = svg
.selectAll('path')
.data(pie(data))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d, i) {
return color(d.data.label);
});
let legend = svg
.selectAll('.legend')
.data(data)
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
return 'translate(' + 0 + ',' + 0 + ')';
});
legend
.append('text')
.attr('x', 1)
.attr('y', 1)
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'central')
.text(function(d) {
return d.count + '%';
});
};
I know to use cornerRadius but when I do it sets a radius for both arcs, it just needs to exist on the colored one. Any ideas? Thanks in advance for any help!
You can apply a corner radius to a d3 arc which allows rounding on the corners:
let arc = d3.svg
.arc()
.innerRadius(23)
.outerRadius(radius)
.cornerRadius(10);
But, the downside is that all arcs' borders are rounded:
If you apply the cornerRadius to only the darkened arc - the other arc won't fill in the background behind the rounded corners. Instead, we could append a circular arc (full donut) and place the darkened arc on top with rounding (my example doesn't adapt your code, just shows how that it can be done, also with d3v4 which uses d3.arc() rather than d3.svg.arc() ):
var backgroundArc = d3.arc()
.innerRadius(30)
.outerRadius(50)
.startAngle(0)
.endAngle(Math.PI*2);
var mainArc = d3.arc()
.innerRadius(30)
.outerRadius(50)
.cornerRadius(10)
.startAngle(0)
.endAngle(function(d) { return d/100*Math.PI* 2 });
var data = [10,20,30,40,50] // percents.
var svg = d3.select("body").append("svg")
.attr("width", 600)
.attr("height", 200);
var charts = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform",function(d,i) {
return "translate("+(i*100+50)+",100)";
});
charts.append("path")
.attr("d", backgroundArc)
.attr("fill","#ccc")
charts.append("path")
.attr("d", mainArc)
.attr("fill","orange")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Try playing with stroke attributes like:
stroke
stroke-dasharray
stroke-dashoffset
stroke-linecap
stroke-linejoin
stroke-miterlimit
stroke-opacity
stroke-width
And set width of bar to lower values, or 0.
Reference: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute
But the better way is to make charts on canvas, because you can draw everything you want. Or to use an library.
How can I create a multi layer pie chart with d3.js which looks like below
Every section doesn't have an inner subsection and when it has a subsection then it has darker color than the outer subsection as shown in the above image.
I tried searching for multilayer pie chart but what all I could do is this.
http://jsfiddle.net/ZpQ3x/
Here is corresponding javascript code
var dataset = {
final: [7000],
process: [1000, 1000, 1000, 7000],
initial: [10000],
};
var width = 660,
height = 500,
cwidth = 75;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("class","wrapper")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
var gs = svg.selectAll("g.wrapper").data(d3.values(dataset)).enter()
.append("g")
.attr("id",function(d,i){
return Object.keys(dataset)[i];
});
var gsLabels = svg.selectAll("g.wrapper").data(d3.values(dataset)).enter()
.append("g")
.attr("id",function(d,i){
return "label_" + Object.keys(dataset)[i];
});
var count = 0;
var path = gs.selectAll("path")
.data(function(d) { return pie(d); })
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i, j) {
d._tmp = d.endAngle;
d.endAngle = d.startAngle;
if(Object.keys(dataset)[j] === "final"){
d.arc = d3.svg.arc().innerRadius(cwidth*j).outerRadius(cwidth*(j+1));
}
else{
d.arc = d3.svg.arc().innerRadius(10+cwidth*j).outerRadius(cwidth*(j+1));
}
return d.arc(d);
})
.transition().delay(function(d, i, j) {
return i * 500;
}).duration(500)
.attrTween('d', function(d,x,y) {
var i = d3.interpolate(d.startAngle, d._tmp);
return function(t) {
d.endAngle = i(t);
return d.arc(d);
}
});
Thank you very much.
I have changed your dataset into a single JSON.
Just to ensure that mentioned above array x and x1 are related together i made data set like this.
data = [{
major: 100,//this is the X array first element
minor: 70,//this is the X1 array first element
grp: 1//here grp is for coloring the segment
}, {
major: 100,
minor: 30,
grp: 2
}, {
major: 100,
minor: 50,
grp: 3
}, {
major: 140,
minor: 70,
grp: 4
}, {
major: 80,
minor: 10,
grp: 5
}];
I have made two arc function.
var arcMajor = d3.svg.arc()
.outerRadius(function (d) {
return radius - 10;
})
.innerRadius(0);
//this for making the minor arc with variable radius as per scale
var arcMinor = d3.svg.arc()
.outerRadius(function (d) {
// scale for calculating the radius range([20, radius - 40])
return scale((d.data.major - d.data.minor));
})
This is the code which makes the path.
//this makes the major arc
g.append("path")
.attr("d", function (d) {
return arcMajor(d);
})
.style("fill", function (d) {
return d3.rgb(color(d.data.grp));
});
//this makes the minor arcs
g.append("path")
.attr("d", function (d) {
return arcMinor(d);
})
.style("fill", function (d) {
return d3.rgb(color(d.data.grp)).darker(2);//for making the inner path darker
});
Working code here with comments
Hope this helps!