// ****************************************** // Gauge for reporting // Each scenario has a score // We use a gauge to indicate the trust level // ****************************************** var gauge = function(container) { var that = {}; var config = { size : 150, clipWidth : 250, clipHeight : 100, ringInset : 20, ringWidth : 40, pointerWidth : 7, pointerTailLength : 5, pointerHeadLengthPercent : 0.8, minValue : 0, maxValue : 100, minAngle : -90, maxAngle : 90, transitionMs : 4000, majorTicks : 7, labelFormat : d3.format(',g'), labelInset : 10, arcColorFn : d3.scale.quantile() .domain([0, , 0.6, 0.8, 1]) .range([d3.rgb('#ff0000'),d3.rgb('#ffcc00'),d3.rgb('#00ff00')]) }; var range = undefined; var r = undefined; var pointerHeadLength = undefined; var value = 0; var svg = undefined; var arc = undefined; var scale = undefined; var ticks = undefined; var tickData = undefined; var pointer = undefined; var donut = d3.layout.pie(); function deg2rad(deg) { return deg * Math.PI / 180; } function newAngle(d) { var ratio = scale(d); var newAngle = config.minAngle + (ratio * range); return newAngle; } function configure() { range = config.maxAngle - config.minAngle; r = config.size / 2; pointerHeadLength = Math.round(r * config.pointerHeadLengthPercent); // a linear scale that maps domain values to a percent from 0..1 scale = d3.scale.linear() .range([0,1]) .domain([config.minValue, config.maxValue]); ticks = scale.ticks(config.majorTicks); tickData = d3.range(config.majorTicks).map(function() {return 1/config.majorTicks;}); arc = d3.svg.arc() .innerRadius(r - config.ringWidth - config.ringInset) .outerRadius(r - config.ringInset) .startAngle(function(d, i) { var ratio = d * i; return deg2rad(config.minAngle + (ratio * range)); }) .endAngle(function(d, i) { var ratio = d * (i+1); return deg2rad(config.minAngle + (ratio * range)); }); } that.configure = configure; function centerTranslation() { return 'translate('+r +','+ r +')'; } function isRendered() { return (svg !== undefined); } that.isRendered = isRendered; function render(newValue) { svg = d3.select(container) .append('svg:svg') .attr('class', 'gauge') .attr('width', config.clipWidth) .attr('height', config.clipHeight); var centerTx = centerTranslation(); var arcs = svg.append('g') .attr('class', 'arc') .attr('transform', centerTx); arcs.selectAll('path') .data(tickData) .enter().append('path') .attr('fill', function(d, i) { return config.arcColorFn(d * i); }) .attr('d', arc); var lg = svg.append('g') .attr('class', 'label') .attr('transform', centerTx); lg.selectAll('text') .data(ticks) .enter().append('text') .attr('transform', function(d) { var ratio = scale(d); var newAngle = config.minAngle + (ratio * range); return 'rotate(' +newAngle +') translate(0,' +(config.labelInset - r) +')'; }) .text(config.labelFormat); var lineData = [ [config.pointerWidth / 2, 0], [0, -pointerHeadLength], [-(config.pointerWidth / 2), 0], [0, config.pointerTailLength], [config.pointerWidth / 2, 0] ]; var pointerLine = d3.svg.line().interpolate('monotone'); var pg = svg.append('g').data([lineData]) .attr('class', 'pointer') .attr('transform', centerTx); pointer = pg.append('path') .attr('d', pointerLine/*function(d) { return pointerLine(d) +'Z';}*/ ) .attr('transform', 'rotate(' +config.minAngle +')'); update(newValue === undefined ? 0 : newValue); } that.render = render; function update(newValue, newConfiguration) { if ( newConfiguration !== undefined) { configure(newConfiguration); } var ratio = scale(newValue); var newAngle = config.minAngle + (ratio * range); pointer.transition() .duration(config.transitionMs) .ease('elastic') .attr('transform', 'rotate(' +newAngle +')'); } that.update = update; configure(); render(); return that; };