<!DOCTYPE html>
<html>

  <head>
    <link data-require="normalize@6.0.0" data-semver="6.0.0" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/6.0.0/normalize.min.css" />
    <link href="https://unpkg.com/basscss@7.1.1/css/basscss.min.css" rel="stylesheet" />
    <link data-require="bootstrap@4.0.0-alpha.6" data-semver="4.0.0-alpha.6" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" />
    <link data-require="font-awesome@4.7.0" data-semver="4.7.0" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
    <link rel="stylesheet" href="style.css" />
  </head>

  <body ng-app="app">
    <app-main></app-main>
    <script data-require="jquery@3.1.1" data-semver="3.1.1" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script data-require="tether@1.4.0" data-semver="1.4.0" src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
    <script data-require="bootstrap@4.0.0-alpha.6" data-semver="4.0.0-alpha.6" src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
    <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
    <script data-require="angular.js@1.6.2" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
    <script src="app.js"></script>
    <script src="d3.service.js"></script>
    <script src="gauge.component.js"></script>
    <script src="progress-circle.component.js"></script>
  </body>

</html>
// Code goes here

angular.module('app',[
  'app.d3',
  'app.gauge',
  'app.progressCircle'
]);

angular.module('app').component('appMain', {
  templateUrl: 'app-main.html',
  controller: MainCtrl,
  controllerAs: 'main'
});

function MainCtrl($element) {
  var main = this;
  
  main.data = [
    {id: 1, total: 10},
    {id: 2, total: 50},
    {id: 3, total: 20},
    {id: 4, total: 40},
    {id: 5, total: 30},
    {id: 6, total: 100}
  ];
  // shared
  
  // access through child.parent.getBarColor(idx)
  main.getBarColor = function(idx){
    return idx % 2 === 0 ? 'red': 'blue';
  }  

 
}







/* Styles go here */



/* use padding hack for IE */
.svg-container {
  width: 100%;
  height: 0;
  padding-top: 60%; /* choose percent of height/width */
  position: relative;
}

svg {
  background: #dedede;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
}
div.svg-container.gauge {
  
}
div.svg-container.progress-circle { 
  
}
<div>
  <header class="bg-green text-white text-center">
    <h3 class="p2">
      <i class="fa fa-circle-o-notch"></i>

      Angular SVG Gauge
    </h3>
  </header>
  
  <section class="container">
    <h3>Gauge Widget</h3>
    
    <!-- Gauge component -->
    <gauge center-x="300"
      center-y="300"
      radius="200"
      max-value="180"
      gradient-interval="10"
      current-value="45"
      gradients-offset="10"> 
    </gauge>
    
  </section>
  
  <section class="container">
    <h3>Progress Circle</h3>
    
    <!-- progress-circle component -->
    
    <progress-circle></progress-circle>
    
  </section>
</div>


<h5>Gauge:</h5>
  <p>
    <label for="toggle">toggle</label>
    <input type="range" 
      name="range" id="toggle"
      ng-value="{{gauge.specs.currentValue}}"
      ng-model="gauge.specs.currentValue"
      
      min="0" max="180"/>
    <i>{{gauge.specs.currentValue}}</i>
  </p>
  <p>
    <label for="textToggle">Enter Value:</label>
    <input type="number" ng-model="gauge.specs.currentValue" 
      placeholder="" />
  </p>
<div class="svg-container gauge">

  <!-- gauge -->
  <svg class="svg-scalable"
    viewBox="0 0 600 400"
    preserveAspectRation="xMidYMid meet">
    <!-- viewBox="0 0 100 100" -->
    <g>
      <!-- <path d="M [starting x] [starting y] A [radius] [radius] 0 0 1 [ending x] [ending y]" stroke-width="[arc thickness]" stroke="[arc color]" fill="none" /> --> 
      
      <!-- background -->
      <path id="gaugeBackground" ng-attr-d="{{ gauge.background }}" stroke-width="10" stroke="black" fill="none"/>
      
      <!-- gauge value -->
      <path ng-attr-d="{{ gauge.value }}" stroke-width="10" stroke="#2a9fbc" fill="none"/>
      
      <!-- invisible arc for textPath to follow, slightly larger -->
      <path id="gradients" ng-attr-d="{{ gauge.gradients }}" stroke-width="0" fill="none" />
      
      <!--<text dx="[relative x offset]" dy="[relative y offset]" text-anchor="start|middle|end">-->
      <!--  <textPath xlink:href="[path element id]" startOffset="[percentage offset]%">-->
      <!--    text to display-->
      <!--  </textPath>-->
      <!--</text>-->
      
      <!-- gradient ticks -->
      <text ng-repeat="gradient in gauge.specs.gradients" dx="0" dy="0" text-anchor="middle" style="font: bold large arial">
        <textPath xlink:href="#gradients" startOffset="{{ gradient.offset }}%">
          {{ gradient.value }}
        </textPath>
      </text>
      
      <!-- Fix for last tick-->
      <text dx="{{ gauge.specs.maxValueCoordinates.x }}" dy="{{ gauge.specs.maxValueCoordinates.y }}" text-anchor="middle" style="font: bold large arial" transform="rotate(90, {{ gauge.specs.maxValueCoordinates.x}}, {{ gauge.specs.maxValueCoordinates.y }} )">
       {{ gauge.specs.maxValue }}
      </text>
      
      <text dx="50%" dy="50%" text-anchor="middle" 
        alignment-baseline="hanging" style="font-size: 7rem">
        {{ gauge.specs.currentValue }}
      </text>
      
    </g>
  </svg>
</div>







angular.module('app.gauge', []);

angular.module('app.gauge')
  .component('gauge', {
    require: {
      parent: '^appMain'
    },
    bindings: {
      centerX: '=',
      centerY: '=',
      radius: '<',
      maxValue: '<',
      gradientInterval: '<',
      currentValue: '<',
      gradientsOffset: '<'
    },
    controller: GaugeCtrl,
    controllerAs: 'gauge',
    templateUrl: 'gauge.html',
    bindToController: true
  });
  
function GaugeCtrl(d3, $scope) {
  var gauge = this;
  // set defaults
  gauge.specs = {
    centerX: 0, // pass in 300
    centerY: 0, // pass in 300
    radius: 0, // pass in 200
    maxValue: 0, // pass in 180
    gradientInterval: 0,
    currentValue: 0, // 45 passed in
    gradients: [],
    gradientsOffset: 0, // 10 
    maxValueCoordinates: null
  };
  
  // pass in values from component passed-in values
  function initPassedInValues() {
    // grab all props from controller
    var keys = Object.keys(gauge);
    // if ctrl key is in gauge.specs object, copy over to specs
    keys.forEach(function(key,idx){
      if (gauge.specs.hasOwnProperty(key)) {
        gauge.specs[key] = gauge[key];
      }
    });
    
  }

  // passedin padding
  gauge.$onInit = function() {
    
    initPassedInValues(); // process passed-in values from component
    initGauge();
    initGradients();
  }
  
  gauge.$postLink = function() {
  // d3 logic
  }
  
  gauge.$onChanges = function() {
    
  }
  
  // function defs
  var getCoordinatesForAngle = function(centerX, centerY, radius, angleInDegrees) {
    var angleInRadians = ((angleInDegrees - 180.0) * Math.PI / 180.0);
    return {
      x: parseInt(centerX + (radius * Math.cos(angleInRadians))),
      y: parseInt(centerY + (radius * Math.sin(angleInRadians)))
    };
  };
  
  // calc background and value arc
    // radius as param - diff for circle vs. text path 
  var getArcPathForAngle = function(startingAngle, endingAngle, radius) {
    var startingPt = getCoordinatesForAngle(
        gauge.specs.centerX,
        gauge.specs.centerY,
        radius,
        startingAngle);
    var endingPt = getCoordinatesForAngle(
        gauge.specs.centerX,
        gauge.specs.centerY,
        radius,
        endingAngle);
    
    return ["M", startingPt.x, startingPt.y, "A", radius, radius, 0, 0, 1, endingPt.x, endingPt.y].join(' ');
  };
  
  // textPath ticks
  function initGradients() {
    // use < instead of <= so doesn't show last value, taken care of with fixLastGradientTextValue fn
    for (var value = 0, offset = 0; value < gauge.specs.maxValue; value += gauge.specs.gradientInterval, offset += 100/18) {
      gauge.specs.gradients.push({value: value, offset: offset});
    }
  }
  
  function initGauge() {
    // draw background
    gauge.background = getArcPathForAngle(0, gauge.specs.maxValue, gauge.specs.radius);
    // draw gauge value
    gauge.value = getArcPathForAngle(0, gauge.specs.currentValue, gauge.specs.radius);
    // draw gradient tick values
    gauge.gradients = getArcPathForAngle(0, gauge.specs.maxValue, gauge.specs.radius + gauge.specs.gradientsOffset);
    // fix last text value and rotate
    gauge.specs.maxValueCoordinates = getCoordinatesForAngle(
      gauge.specs.centerX,
      gauge.specs.centerY,
      gauge.specs.radius + gauge.specs.gradientsOffset,
      gauge.specs.maxValue);
  }
  
  
  // called by ng-changes on range input element
  // gauge.recalcValue = function() {
  //   gauge.value = getArcPathForAngle(0, gauge.specs.currentValue, gauge.specs.radius);
  // };
  
  // additional watcher for currentValue
  $scope.$watch('gauge.specs.currentValue', function(oldValue, newValue) {
    initGauge();
  }, true);
  
  
}





























angular.module('app.progressCircle', []);

angular.module('app.progressCircle')
  .component('progressCircle', {
    require: {
      parent: '^appMain'
    },
    bindings: {
    },
    controller: ProgressCircleCtrl,
    controllerAs: 'progressCircle',
    templateUrl: 'progress-circle.html',
    bindToController: true
  });
  
function ProgressCircleCtrl() {
  var progressCircle = this;
  
  // passedin padding
  progressCircle.$onInit = function() {
  
// d3 logic
    // d3.select('.vertical-bars svg')
    // .append('text')
    //   .text('vertical bars + d3')
    //   .attr('text-anchor','middle')
    //   .attr('alignment-baseline', 'middle')
    //   .attr('font-size','16px')
    //   .attr('fill','black')
    //   .attr('x', '50%')
    //   .attr('y', '50%')
  }
  
  progressCircle.$postLink = function() {

  }
 

}












<!-- could refactor these:

https://codepen.io/craig-o-curtis/pen/LLdaad
https://codepen.io/craig-o-curtis/pen/vZRPMV
https://codepen.io/craig-o-curtis/pen/PjRLrv

--> 
<h5>Progress Circle:</h5>
<div class="svg-container progress-circle">
  
  <!-- progress circle -->
  <svg class="svg-scalable"
    viewBox="0 0 200 100"
    preserveAspectRation="xMinYMin meet">
    <g>
      
    </g>
      
  </svg>
</div>
angular.module('app.d3',[])
  .factory('d3', function() {
    return d3;
  });