makeGauge = function(
element,
dataset,
{
width = 450,
height = 450,
margin = 40,
ringWidth = 20,
startAngle = -Math.PI,
endAngle = Math.PI / 2
}
) {
var radius = Math.min(width, height) / 2 - margin;
var ringGap = ringWidth / 5;
var segmentGap = (5 * (Math.PI * 2)) / 360;
console.log("makeGauge", width, height);
var svg = d3
.select(element)
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
let i = 0;
for (let data of dataset) {
let samples = data.samples;
let level = data.level;
let colors = data.colors
? function(i) {
let i_m = i % data.colors.length;
return data.colors[i_m];
}
: d3
.scaleOrdinal(d3.schemeCategory10)
.domain([...Array(samples.length).keys()]);
let levelColor = data.levelColor || "red";
let maximum =
data.maximum ||
samples.reduce(function(previous, current) {
return previous + current;
}, 0);
let segmentRadians = endAngle - startAngle - samples.length * segmentGap;
let outerRadius = radius - i * ringWidth - i * ringGap;
let innerRadius = outerRadius - ringWidth;
i++;
let fillIndex = 0;
let segmentStartAngle = startAngle;
let arc = function(d) {
let segmentAngle = segmentRadians * (d / maximum);
let segmentEndAngle = segmentStartAngle + segmentAngle;
let arcOp = d3
.arc()
.startAngle(segmentStartAngle)
.endAngle(segmentEndAngle)
.innerRadius(innerRadius)
.outerRadius(outerRadius);
segmentStartAngle = segmentEndAngle + segmentGap;
return arcOp;
};
svg
.selectAll('whatever')
.data(samples)
.enter()
.append('path')
.attr('d', function(d) {
return arc(d)();
})
.attr('fill', function(d) {
let fillColor = colors(fillIndex);
fillIndex++;
return fillColor;
})
.attr("stroke", "gray")
.style("stroke-width", "1px")
.style("opacity", 0.7);
if (level) {
let levelAngle = startAngle + segmentRadians * (level / maximum);
// why is this necessary?
levelAngle -= Math.PI / 2;
let innerX = Math.cos(levelAngle) * innerRadius;
let innerY = Math.sin(levelAngle) * innerRadius;
let outerX = Math.cos(levelAngle) * outerRadius;
let outerY = Math.sin(levelAngle) * outerRadius;
svg
.append("line")
.attr("x1", innerX)
.attr("y1", innerY)
.attr("x2", outerX)
.attr("y2", outerY)
.style("stroke-width", "3px")
.attr("stroke", levelColor);
}
let textRadius = outerRadius - 2; //(innerRadius + outerRadius) / 2;
// why are sin/cos reversed?
let textY = Math.cos(startAngle + Math.PI) * textRadius;
let textX = Math.sin(startAngle + Math.PI) * textRadius;
//console.log("text", startAngle, textX, textY);
svg
.append("g")
.attr("font-family", "sans-serif")
.attr("font-weight", "100")
.attr("font-size", 12)
.attr("text-anchor", "start")
.selectAll("text")
.data([data.label])
.join("text")
// no rotation needed
//.attr("transform", d => `translate(${textX + 5},${textY})`) // rotate(${-90})`)
.attr("x", textX + 5)
.attr("y", textY)
.call(text => text.append("tspan").text(data.label));
}
//console.log(svg);
return element;
}