in src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js [188:363]
drawGauge(svg, data, width, height) {
const marginFactor = 0.95;
const tooltip = this.gaugeChart.tooltip;
const isTooltip = this.gaugeChart.handler.visConfig.get('addTooltip');
const isDisplayWarning = this.gaugeChart.handler.visConfig.get('isDisplayWarning', false);
const { angleFactor, maxAngle, minAngle } =
this.gaugeConfig.gaugeType === 'Circle' ? circleAngles : arcAngles;
const maxRadius = (Math.min(width, height / angleFactor) / 2) * marginFactor;
const extendRange = this.gaugeConfig.extendRange;
const maxY = _.max(data.values, 'y').y;
const min = this.gaugeConfig.colorsRange[0].from;
const max = _.last(this.gaugeConfig.colorsRange).to;
const angle = d3.scale
.linear()
.range([minAngle, maxAngle])
.domain([min, extendRange && max < maxY ? maxY : max]);
const radius = d3.scale
.linear()
.range([0, maxRadius])
.domain([this.gaugeConfig.innerSpace + 1, 0]);
const totalWidth = Math.abs(radius(0) - radius(1));
const bgPadding = (totalWidth * (1 - this.gaugeConfig.style.bgWidth)) / 2;
const gaugePadding = (totalWidth * (1 - this.gaugeConfig.style.width)) / 2;
/**
* Function to calculate the free space in the center of the gauge. This takes into account
* whether ticks are enabled or not.
*
* This is calculated using the inner diameter (radius(1) * 2) of the gauge.
* If ticks/scale are enabled we need to still subtract the tick length * 2 to make space for a tick
* on every side. If ticks/scale are disabled, the radius(1) function actually leaves space for the scale,
* so we add that free space (which is expressed via the paddings, we just use the larger of those) to the diameter.
*/
const getInnerFreeSpace = () =>
radius(1) * 2 -
(this.gaugeConfig.scale.show
? this.gaugeConfig.scale.tickLength * 2
: -Math.max(bgPadding, gaugePadding) * 2);
const arc = d3.svg
.arc()
.startAngle(minAngle)
.endAngle(function (d) {
return Math.max(0, Math.min(maxAngle, angle(Math.max(min, d.y))));
})
.innerRadius(function (d, i, j) {
return Math.max(0, radius(j + 1) + gaugePadding);
})
.outerRadius(function (d, i, j) {
return Math.max(0, radius(j) - gaugePadding);
});
const bgArc = d3.svg
.arc()
.startAngle(minAngle)
.endAngle(maxAngle)
.innerRadius(function (d, i, j) {
return Math.max(0, radius(j + 1) + bgPadding);
})
.outerRadius(function (d, i, j) {
return Math.max(0, radius(j) - bgPadding);
});
const gaugeHolders = svg
.selectAll('path')
.data([data])
.enter()
.append('g')
.attr('data-label', (d) => this.getLabel(d.values[0].y));
const gauges = gaugeHolders
.selectAll('g')
.data((d) => d.values)
.enter();
gauges
.append('path')
.attr('d', bgArc)
.attr('class', this.gaugeConfig.outline ? 'visGauge__meter--outline' : undefined)
.style('fill', this.gaugeConfig.style.bgFill);
const series = gauges
.append('path')
.attr('d', arc)
.attr('class', this.gaugeConfig.outline ? 'visGauge__meter--outline' : undefined)
.style('fill', (d) => this.getColorBucket(Math.max(min, d.y)));
const smallContainer = svg.node().getBBox().height < 70;
let hiddenLabels = smallContainer;
// If the value label is hidden we later want to hide also all other labels
// since they don't make sense as long as the actual value is hidden.
let valueLabelHidden = false;
gauges
.append('text')
.attr('class', 'chart-label')
.attr('y', -5)
.text((d) => {
if (this.gaugeConfig.percentageMode) {
const percentage = (d.y - min) / (max - min);
return data.yAxisFormatter(percentage);
}
return data.yAxisFormatter(d.y);
})
.attr('style', 'dominant-baseline: central;')
.style('text-anchor', 'middle')
.style('font-size', '2em')
.style('display', function () {
const textLength = this.getBBox().width;
// The text is too long if it's larger than the inner free space minus a couple of random pixels for padding.
const textTooLong = textLength >= getInnerFreeSpace() - 6;
if (textTooLong) {
hiddenLabels = true;
valueLabelHidden = true;
}
return textTooLong ? 'none' : 'initial';
});
if (this.gaugeConfig.labels.show) {
svg
.append('text')
.attr('class', 'chart-label')
.text(data.label)
.attr('y', -30)
.attr('style', 'dominant-baseline: central; text-anchor: middle;')
.style('display', function () {
const textLength = this.getBBox().width;
const textTooLong = textLength > maxRadius;
if (textTooLong) {
hiddenLabels = true;
}
return smallContainer || textTooLong ? 'none' : 'initial';
});
svg
.append('text')
.attr('class', 'chart-label')
.text(this.gaugeConfig.style.subText)
.attr('y', 20)
.attr('style', 'dominant-baseline: central; text-anchor: middle;')
.style('display', function () {
const textLength = this.getBBox().width;
const textTooLong = textLength > maxRadius;
if (textTooLong) {
hiddenLabels = true;
}
return valueLabelHidden || smallContainer || textTooLong ? 'none' : 'initial';
});
}
if (this.gaugeConfig.scale.show) {
this.drawScale(svg, radius(1), angle);
}
if (isTooltip) {
series.each(function () {
const gauge = d3.select(this);
gauge.call(tooltip.render());
});
}
if (hiddenLabels && isDisplayWarning) {
this.gaugeChart.handler.alerts.show('Some labels were hidden due to size constraints');
}
//center the visualization
const transformX = width / 2;
const transformY = height / 2 > maxRadius ? height / 2 : maxRadius;
svg.attr('transform', `translate(${transformX}, ${transformY})`);
return series;
}