D3 Gauge Chart - How to add legends for four sections and knob instead of needle?

Viewed 777

I want to achieve the following gauge chart.

I have tried something and able to achieve something like the below:

Can someone help me in achieving the remaining changes? I have tried and didn't find any solution for:

  1. adding knob instead of the needle
  2. legends for each color
  3. gauge score in the middle with legend name

My code is:

<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>        

<script id="rendered-js" >
  // data which need to be fetched
  var name = "";
  var value = 17;
  var gaugeMaxValue = 100;
  // donn?es ? calculer
  var percentValue = value / gaugeMaxValue;

  var needleClient;
  (function () {
    var barWidth, chart, chartInset, degToRad, repaintGauge,
    height, margin, numSections, padRad, percToDeg, percToRad,
    percent, radius, sectionIndx, svg, totalPercent, width;
    percent = percentValue;
    numSections = 1;
    sectionPerc = 1 / numSections / 2;
    padRad = 0.025;
    chartInset = 10;
    
    // Orientation of gauge:
    totalPercent = .75;
    el = d3.select(".chart-gauge");
    margin = {
      top: 20,
      right: 20,
      bottom: 30,
      left: 20 };
    width = el[0][0].offsetWidth - margin.left - margin.right;
    height = width;
    radius = Math.min(width, height) / 2;
    barWidth = 40 * width / 300;
    
    //Utility methods
    percToDeg = function (perc) {
      return perc * 360;
    };
    percToRad = function (perc) {
      return degToRad(percToDeg(perc));
    };
    degToRad = function (deg) {
      return deg * Math.PI / 180;
    };
    
    // Create SVG element
    svg = el.append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom);
    
    // Add layer for the panel
    chart = svg.append("g").attr("transform", "translate(" + (width + margin.left) / 2 + ", " + (height + margin.top) / 2 + ")");
    chart.append("path").attr("class", "arc chart-first");
    chart.append("path").attr("class", "arc chart-second");
    chart.append("path").attr("class", "arc chart-third");
    chart.append("path").attr("class", "arc chart-fourth");
    arc4 = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth);
    arc3 = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth);
    arc2 = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth);
    arc1 = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth);
    
    repaintGauge = function ()
    {
      perc = 0.5;
      var next_start = totalPercent;
      arcStartRad = percToRad(next_start);
      arcEndRad = arcStartRad + percToRad(perc / 4);
      next_start += perc / 4;
      arc1.startAngle(arcStartRad).endAngle(arcEndRad);
      arcStartRad = percToRad(next_start);
      arcEndRad = arcStartRad + percToRad(perc / 4);
      next_start += perc / 4;
      arc2.startAngle(arcStartRad + padRad).endAngle(arcEndRad);
      arcStartRad = percToRad(next_start);
      arcEndRad = arcStartRad + percToRad(perc / 4);
      next_start += perc / 4;
      arc3.startAngle(arcStartRad + padRad).endAngle(arcEndRad);
      arcStartRad = percToRad(next_start);
      arcEndRad = arcStartRad + percToRad(perc / 4);
      arc4.startAngle(arcStartRad + padRad).endAngle(arcEndRad);
      chart.select(".chart-first").attr("d", arc1);
      chart.select(".chart-second").attr("d", arc2);
      chart.select(".chart-third").attr("d", arc3);
      chart.select(".chart-fourth").attr("d", arc4);
    };
    
    var dataset = [{ metric: name, value: value }];
    var texts = svg.selectAll("text").
    data(dataset).
    enter();
    texts.append("text").
    text(function () {
      return dataset[0].metric;
    }).
    attr("id", "Name").
    attr("transform", "translate(" + (width + margin.left) / 6 + ", " + (height + margin.top) / 1.5 + ")").
    attr("font-size", 25).
    style("fill", "#000000");
    var trX = 180 - 210 * Math.cos(percToRad(percent / 2));
    var trY = 195 - 210 * Math.sin(percToRad(percent / 2));
    // (180, 195) are the coordinates of the center of the gauge.
    displayValue = function () {
      texts.append("text").
      text(function () {
        return dataset[0].value;
      }).
      attr("id", "Value").
      attr("transform", "translate(" + trX + ", " + trY + ")").
      attr("font-size", 18).
      style("fill", "#000000");
    };
    texts.append("text").
    text(function () {
      return 0;
    }).
    attr("id", "scale0").
    attr("transform", "translate(" + (width + margin.left) / 100 + ", " + (height + margin.top) / 2 + ")").
    attr("font-size", 15).
    style("fill", "#000000");
    texts.append("text").
    text(function () {
      return gaugeMaxValue / 2;
    }).
    attr("id", "scale10").
    attr("transform", "translate(" + (width + margin.left) / 2.15 + ", " + (height + margin.top) / 30 + ")").
    attr("font-size", 15).
    style("fill", "#000000");
    texts.append("text").
    text(function () {
      return gaugeMaxValue;
    }).
    attr("id", "scale20").
    attr("transform", "translate(" + (width + margin.left) / 1.03 + ", " + (height + margin.top) / 2 + ")").
    attr("font-size", 15).
    style("fill", "#000000");
    
    var Needle = function () {
      //Helper function that returns the `d` value for moving the needle
      var recalcPointerPos = function (perc) {
        var centerX, centerY, leftX, leftY, rightX, rightY, thetaRad, topX, topY;
        thetaRad = percToRad(perc / 2);
        centerX = 0;
        centerY = 0;
        topX = centerX - this.len * Math.cos(thetaRad);
        topY = centerY - this.len * Math.sin(thetaRad);
        leftX = centerX - this.radius * Math.cos(thetaRad - Math.PI / 2);
        leftY = centerY - this.radius * Math.sin(thetaRad - Math.PI / 2);
        rightX = centerX - this.radius * Math.cos(thetaRad + Math.PI / 2);
        rightY = centerY - this.radius * Math.sin(thetaRad + Math.PI / 2);
        return "M " + leftX + " " + leftY + " L " + topX + " " + topY + " L " + rightX + " " + rightY;
      };
      function Needle(el) {
        this.el = el;
        this.len = width / 2.5;
        this.radius = this.len / 8;
      }
      Needle.prototype.render = function () {
        this.el.append("circle").attr("class", "needle-center").attr("cx", 0).attr("cy", 0).attr("r", this.radius);
        return this.el.append("path").attr("class", "needle").attr("id", "client-needle").attr("d", recalcPointerPos.call(this, 0));
      };
      Needle.prototype.moveTo = function (perc) {
        var self,
        oldValue = this.perc || 0;
        this.perc = perc;
        self = this;
        // Reset pointer position
        this.el.transition().delay(100).ease("quad").duration(200).select(".needle").tween("reset-progress", function () {
          return function (percentOfPercent) {
            var progress = (1 - percentOfPercent) * oldValue;
            repaintGauge(progress);
            return d3.select(this).attr("d", recalcPointerPos.call(self, progress));
          };
        });
        this.el.transition().delay(300).ease("bounce").duration(1500).select(".needle").tween("progress", function () {
          return function (percentOfPercent) {
            var progress = percentOfPercent * perc;
            repaintGauge(progress);
            return d3.select(this).attr("d", recalcPointerPos.call(self, progress));
          };
        });
      };
      return Needle;
    }();
    needle = new Needle(chart);
    needle.render();
    needle.moveTo(percent);
    setTimeout(displayValue, 1350);
  })();
</script>
<style type="text/css" src="gauge.css">
  .chart-gauge {
    width: 400px;
    margin: 100px auto;
  }
  .chart-first {
    fill: #EA4335;
  }
  .chart-second {
    fill: #FBBC05;
  }
  .chart-third {
    fill: #4285F4;
  }
  .chart-fourth {
    fill: #34A853;
  }
  .needle, .needle-center {
    fill: #000000;
  }
  .text {
    color: "#112864";
    font-size: 16px;
  }
  svg {
    font: 10px sans-serif;
  }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<div class="chart-gauge"></div>

0 Answers
Related