====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; }) ); } }