====Donut/Pie Chart (Discrete Values)==== A Pie or Donut chart is used to show the percentage of a given population that have a particular characteristic, or the percentage of time that things were in given states. Note that generally pie charts are **not** the best visualisation. Because the human eye has more difficulty comparing angles than lines, it's usually better to show this sort of information through a bar-chart. An example can be found in the [[https://demo.optrix.com.au/s/long/displaylist/info?report=supportmodes|Roof Support Mode Donut Chart]]. ==Example==
==Description== You should replace the following... ^Element^Replace With^ |[PROPERTY]|The name of the property you want to report on| |[PROPERTYID]|The ARDI ID number for the property you want to report on|
class ActiveReport extends SVGReport {
initialise() {
super.initialise();
};
create() {
this.liveReport("'[PROPERTY]' PROPERTY ALLPOINTS");
}
update() {
this.draw([]);
}
draw(data) {
//Calculate resulting size...
if (this.group == null)
{
// append the svg object to the div called 'my_dataviz'
this.group = d3.select("#reportsvg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
}
var width = this.width;
var height = this.height;
var margin = this.margin;
// 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.left + margin.right);
var svg = this.group;
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Create variables to keep information
var finaldata = [];
var domain_names = [];
var domain_colours = [];
var totalitems = 0;
//For every possible value (in ValueSet[PROPERTYID])...
for(var q=0;q enter.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.data.name)) })
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("opacity", 0.7)
.attr("name",function(d) { return d.data.name; })
.attr("value",function(d) { return d.data.value; })
.attr("units"," supports")
.call(this.tip)
.attr("class","slices"),
update => update
.attr('d', arc)
.attr('fill', function(d){ return(color(d.data.name)) })
.attr("value",function(d) { return d.data.value; })
);
//NOTE: Strange distortions will occur if you try to transition the arc.
// Add the polylines between chart and labels:
svg.selectAll('.pointerlines')
.data(data_ready)
.join(
enter => enter.append("polyline")
.attr("stroke", "white")
.style("fill", "none")
.attr("class","pointerlines")
.attr("stroke-width", 1)
.attr("opacity", function (d) {
if (d.value == 0) return 0;
return 1;
})
.attr('points', function(d,i) {
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
posC[1] -= i*8;
posB[1] -= i*8;
return [posA, posB, posC]
}),
update => update
.transition()
.duration(1000)
.attr('points', function(d,i) {
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
posC[1] -= i*8;
posB[1] -= i*8;
return [posA, posB, posC]
})
.attr("opacity", function (d) {
if (d.value == 0) return 0;
return 1;
})
);
// Add the polylines between chart and labels:
svg
.selectAll('.labels')
.data(data_ready)
.join(
enter => enter
.append('text')
.attr("class","labels")
.attr("opacity", function (d) {
if (d.value == 0) return 0;
return 1;
})
.text( function(d) { return d.data.name + " ( " + d.data.value + "/" + totalitems + " )" } )
.attr('transform', function(d,i) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
pos[1] -= i*8;
return 'translate(' + pos + ')';
})
.attr("fill","white")
.style('text-anchor', function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
return (midangle < Math.PI ? 'start' : 'end')
}),
update => update
.text( function(d) { return d.data.name + " ( " + d.data.value + "/" + totalitems + " )" } )
.style('text-anchor', function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
return (midangle < Math.PI ? 'start' : 'end')
})
.transition()
.duration(1000)
.attr('transform', function(d,i) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
pos[1] -= i*8;
return 'translate(' + pos + ')';
})
.attr("opacity", function (d) {
if (d.value == 0) return 0;
return 1;
})
);
}
}