drawGauge()

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;
  }