gauge = (from, to, today, value, max, goalText, opts = {}) => {
let options = {
cap: false,
blue: false,
height: Math.min(width, 500),
...opts
}
let valueReal = value
if (options.cap) {
value = Math.min(max, value)
}
const svg = d3.create("svg")
.attr("viewBox", [0, -50, width, options.height+50])
const arcStart = -Math.PI*0.68
const arcEnd = Math.PI*0.68
const time01Scale = d3.scaleTime()
.domain([from, to])
.range([0, 1])
const timeScale = d3.scaleTime()
.domain([from, to])
.range([arcStart, arcEnd])
const valueScale = d3.scaleLinear()
.domain([0, max])
.range([arcStart, arcEnd])
const timePart = time01Scale(today)
const colorScale = d3.scaleLinear()
.domain([0, 0.4*max*timePart, max*0.9*timePart])
.range(['red', 'orange', 'green'])
let arcify = (num) => {
return d3.arc()
.innerRadius( 0.6 * options.height / 2 )
.outerRadius( 0.8 * options.height / 2 )
.startAngle(arcStart)
.endAngle(num)
.cornerRadius(40)
}
let arcPin = (num) => {
return d3.arc()
.innerRadius( 0.7 * options.height / 2 )
.outerRadius( 0.87 * options.height / 2 )
.startAngle(arcStart)
.endAngle(num)
.cornerRadius(10)
}
let labelArc = (num, inner) => {
const rad = inner ?
0.55 * options.height / 2 :
1 * options.height / 2
return d3.arc()
.innerRadius(rad)
.outerRadius(rad)
.startAngle(num)
.endAngle(num)
.centroid()
}
const formatTime = d3.timeFormat("%e. %b")
let datePins = [
{t: from},
{t: today},
{t: to}
]
svg.append('g')
.attr('class', 'pins')
.attr('transform', `translate(${ width / 2 }, ${ options.height / 2})` )
.append('path')
.style('stroke', 'white')
.style('stroke-width', 2)
.style('fill', "#777")
.attr('d', arcPin(timeScale(today)))
svg.append('g')
.attr('class', 'gauge')
.attr('transform', `translate(${ width / 2 }, ${ options.height / 2})` )
.append('path')
.style('stroke', 'white')
.style('stroke-width', 2)
.style('fill', "#ccc")
.attr('d', arcify(timeScale(to)))
svg.append('g')
.attr('class', 'gauge2')
.attr('transform', `translate(${ width / 2 }, ${ options.height / 2})` )
.append('path')
.style('stroke', 'white')
.style('stroke-width', 2)
.style('fill', options.blue ? "#46a" : colorScale(value))
.attr('d', arcify(valueScale(value)))
const rotation = (num) => {
return num > 0 ?
(num) * 180/Math.PI :
(num+Math.PI) * 180/Math.PI
}
const text = svg.append('g')
.attr('class', 'lablels-container')
.attr('transform', `translate(${width/2},${options.height/2})`)
.selectAll('text')
.data(datePins)
.join('text')
.attr('transform', d => `translate(${ labelArc(timeScale(d.t)).join(',') }) rotate(-90) rotate(${ rotation(timeScale(d.t)) })`)
.attr('text-anchor', 'middle')
.style('font-family', 'sans-serif')
.style('font-size', 12)
.text(d => formatTime(d.t))
svg.append('g')
.attr('class', 'maxtarget')
.attr('transform', `translate(${width/2},${options.height/2})`)
.append('text')
.attr('transform', `translate(${ labelArc(valueScale(max), true).join(',') }) rotate(-90) rotate(${ rotation(timeScale(max)) })`)
.attr('text-anchor', 'end')
.style('font-family', 'sans-serif')
.style('font-size', 12)
.text(max)
svg.append('g')
.attr('class', 'centertext')
.attr('transform', `translate(${width/2},${options.height/2})`)
.append('text')
.attr('text-anchor', 'middle')
.style('font-family', 'sans-serif')
.style('font-size', Math.round(options.height*90/500))
.text(valueReal)
svg.append('g')
.attr('class', 'supporttext')
.attr('transform', `translate(${width/2},${options.height*0.9})`)
.append('text')
.attr('text-anchor', 'middle')
.style('font-family', 'sans-serif')
.style('font-size', Math.round(options.height*32/500))
.text(goalText)
return svg.node()
}