Reputation: 105
as you can see from the image ( https://i.sstatic.net/TtIP7.jpg ) I have set up my pie/donut graph fairly well. However, how would I make the legend use descriptive strings included in the JSON file (name) instead of the data values (total)? Thanks!
const project = d3.select(".project")
const svg = project.append("svg").attr("width",700).attr("height",600)
const margin = {top:20,right:20,bottom:70,left:70}
const graphWidth = 600-margin.left - margin.right
const graphHeight = 600 - margin.top - margin.bottom
const arcPath = d3.arc().outerRadius(190).innerRadius(70)
const legendRectSize = 18; // NEW
const legendSpacing = 4;
// title
svg.append("text").attr("class","title").attr("dy","10%").attr("dx","10%").text("Impact of COVID-19 on Business")
.attr("fill", "black")
// choose colour scheme
const colourScale = d3.scaleOrdinal(d3["schemeDark2"])
// set up a canvas and the pie chart
const pieCanvas = svg.append("g").attr("width",graphWidth/2).attr("height",graphWidth/2)
.attr("transform", `translate(${margin.left + 200},${margin.top + 250})`)
const pie = d3.pie().sort(null).value(data=>data.total)
// get data from CSV
function getData() {
d3.json("./data/piedata1.json", function(d) {return d}).then(drawPie)
}
getData()
//draw the pie chart
function drawPie(data) {
colourScale.domain(data.map(d=>d.total))
const angles = pie(data)
const paths = pieCanvas.selectAll("path").data(angles)
paths.enter().append("path").attr("d", arcPath).attr("class","arc")
.attr("stroke","white").attr("fill", d=>colourScale(d.data.total))
//add legend
const legend = svg.selectAll('.legend').data(colourScale.domain()).enter().append('g')
.attr('class', 'legend').attr('transform', function(d, i) {
const height = legendRectSize + legendSpacing
const offset = height * colourScale.domain().length / 2
const horz = 30 * legendRectSize
const vert = i * height - offset + 200
return 'translate(' + horz + ',' + vert + ')'});
legend.append('rect').attr('width', legendRectSize).attr('height', legendRectSize)
.style('fill', colourScale).style('stroke', colourScale)
legend.append('text').attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing).text(function(d) { return d })
}
JSON file is here
[
{"name": "critical impact","total": 100,"Percent":1.39},
{"name": "significant impact","total": 1500,"Percent":20.83},
{"name": "limited impact","total": 4100,"Percent":56.94},
{"name": "no impact","total": 400,"Percent":5.56},
{"name": "positive impact","total": 1100,"Percent":15.28}
]
Upvotes: 1
Views: 84
Reputation: 38151
Currently you create a color scale domain comprising of totals. You probably shouldn't do this: some totals may be equal, but your probably won't want them colored the same if they represent different things.
Instead of : colourScale.domain(data.map(d=>d.total))
use colourScale.domain(data.map(d=>d.name))
Which means you also need to pass the correct parameter to the scale as well when colouring the wedges: .attr("stroke","white").attr("fill", d=>colourScale(d.data.name))
Here's an example:
var data = [
{"name": "critical impact","total": 100,"Percent":1.39},
{"name": "significant impact","total": 1500,"Percent":20.83},
{"name": "limited impact","total": 4100,"Percent":56.94},
{"name": "no impact","total": 400,"Percent":5.56},
{"name": "positive impact","total": 1100,"Percent":15.28}
]
const project = d3.select(".project")
const svg = project.append("svg").attr("width",700).attr("height",600)
const margin = {top:20,right:20,bottom:70,left:70}
const graphWidth = 600-margin.left - margin.right
const graphHeight = 600 - margin.top - margin.bottom
const arcPath = d3.arc().outerRadius(190).innerRadius(70)
const legendRectSize = 18; // NEW
const legendSpacing = 4;
// title
svg.append("text").attr("class","title").attr("dy","10%").attr("dx","10%").text("Impact of COVID-19 on Business")
.attr("fill", "black")
// choose colour scheme
const colourScale = d3.scaleOrdinal(d3["schemeDark2"])
// set up a canvas and the pie chart
const pieCanvas = svg.append("g").attr("width",graphWidth/2).attr("height",graphWidth/2)
.attr("transform", `translate(${margin.left + 200},${margin.top + 250})`)
const pie = d3.pie().sort(null).value(data=>data.total)
// get data from CSV
drawPie(data);
//draw the pie chart
function drawPie(data) {
colourScale.domain(data.map(d=>d.name))
const angles = pie(data)
const paths = pieCanvas.selectAll("path").data(angles)
paths.enter().append("path").attr("d", arcPath).attr("class","arc")
.attr("stroke","white").attr("fill", d=>colourScale(d.data.name))
//add legend
const legend = svg.selectAll('.legend').data(colourScale.domain()).enter().append('g')
.attr('class', 'legend').attr('transform', function(d, i) {
const height = legendRectSize + legendSpacing
const offset = height * colourScale.domain().length / 2
const horz = 30 * legendRectSize
const vert = i * height - offset + 200
return 'translate(' + horz + ',' + vert + ')'});
legend.append('rect').attr('width', legendRectSize).attr('height', legendRectSize)
.style('fill', colourScale).style('stroke', colourScale)
legend.append('text').attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing).text(function(d) { return d })
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="project"></div>
Say you wanted to access both total and name, then we could use the entire dataset when creating the legend, and simply pass the correct property to the scales and when appending text (as opposed to the whole datum if passing the colour scale domain):
var data = [
{"name": "critical impact","total": 100,"Percent":1.39},
{"name": "significant impact","total": 1500,"Percent":20.83},
{"name": "limited impact","total": 4100,"Percent":56.94},
{"name": "no impact","total": 400,"Percent":5.56},
{"name": "positive impact","total": 1100,"Percent":15.28}
]
const project = d3.select(".project")
const svg = project.append("svg").attr("width",700).attr("height",600)
const margin = {top:20,right:20,bottom:70,left:70}
const graphWidth = 600-margin.left - margin.right
const graphHeight = 600 - margin.top - margin.bottom
const arcPath = d3.arc().outerRadius(190).innerRadius(70)
const legendRectSize = 18; // NEW
const legendSpacing = 4;
// title
svg.append("text").attr("class","title").attr("dy","10%").attr("dx","10%").text("Impact of COVID-19 on Business")
.attr("fill", "black")
// choose colour scheme
const colourScale = d3.scaleOrdinal(d3["schemeDark2"])
// set up a canvas and the pie chart
const pieCanvas = svg.append("g").attr("width",graphWidth/2).attr("height",graphWidth/2)
.attr("transform", `translate(${margin.left + 200},${margin.top + 250})`)
const pie = d3.pie().sort(null).value(data=>data.total)
// get data from CSV
drawPie(data);
//draw the pie chart
function drawPie(data) {
colourScale.domain(data.map(d=>d.name))
const angles = pie(data)
const paths = pieCanvas.selectAll("path").data(angles)
paths.enter().append("path").attr("d", arcPath).attr("class","arc")
.attr("stroke","white").attr("fill", d=>colourScale(d.data.name))
//add legend
const legend = svg.selectAll('.legend').data(data).enter().append('g')
.attr('class', 'legend').attr('transform', function(d, i) {
const height = legendRectSize + legendSpacing
const offset = height * colourScale.domain().length / 2
const horz = 30 * legendRectSize
const vert = i * height - offset + 200
return 'translate(' + horz + ',' + vert + ')'});
legend.append('rect').attr('width', legendRectSize).attr('height', legendRectSize)
.style('fill', d=>colourScale(d.name)).style('stroke', d=>colourScale(d.name))
legend.append('text').attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing).text(function(d) { return d.name + "(" + d.total + ")"; })
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="project"></div>
Upvotes: 1