private drawChart()

in src/visual.ts [1145:1323]


    private drawChart(options: IDualKpiOptions) {
        let chartData: Array<IDualKpiDataPoint> = options.chartData;
        let axisConfig: IAxisConfig = options.axisConfig;
        const latestValue: number = chartData[chartData.length - 1].value,
            isTopChart: boolean = options.position === DualKpiChartPositionType.top,
            dataColor: string = isTopChart ? this.data.settings.dualKpiColors.dataColor : this.data.settings.dualKpiColorsBottom.dataColor,
            chartOpacity: number = isTopChart ? this.data.settings.dualKpiColors.opacity : this.data.settings.dualKpiColorsBottom.opacity,
            axisStrokeHighContrastColor: string = this.colorHelper.getHighContrastColor("foreground", this.data.settings.dualKpiColors.textColor),
            isHighContrastMode: boolean = this.colorHelper.isHighContrast,
            hoverLineStrokeColor: string = "#777";

        let target = this.target;
        let targetPadding = parseInt(jQuery(target).css("padding-left"), 10) || 0;

        let margin = {
            top: 7,
            right: 0,
            bottom: 0,
            left: this.chartLeftMargin
        };

        if (this.size === DualKpiSize.medium || this.size === DualKpiSize.large) {
            margin.left = 40;
        }

        let calcWidth = options.width - margin.right - margin.left,
            calcHeight = options.height - margin.top - margin.bottom,
            minValue = d3.min(chartData, (d) => d.value) || 0,
            maxValue = d3.max(chartData, (d) => d.value) || 0;

        let axisMinValue = axisConfig.min !== null && axisConfig.min !== undefined ? axisConfig.min : minValue;
        let axisMaxValue = axisConfig.max !== null && axisConfig.max !== undefined ? axisConfig.max : maxValue;

        let xScale = d3.scaleTime()
            .domain(d3.extent(chartData, (d) => d.date))
            .range([0, calcWidth]);

        let yScale = d3.scaleLinear()
            .domain([axisMinValue, axisMaxValue])
            .clamp(true)
            .range([calcHeight, 0]);

        let yAxis = d3.axisLeft(yScale)
            .tickValues([axisMinValue, axisMaxValue])
            .tickFormat((d) => {
                let axisTickLabel: string = String(this.axisNumberFormatter(d));
                if (options.valueAsPercent) {
                    axisTickLabel = axisTickLabel + "%";
                }
                return axisTickLabel;
            });

        let seriesRenderer, fill, stroke, strokeWidth;

        if (options.chartType === "area") {
            seriesRenderer = d3.area()
                .x((d: any) => xScale(d.date || new Date()))
                .y0(calcHeight)
                .y1((d: any) => yScale(d.value || 0));

            fill = dataColor;
            stroke = "none";
            strokeWidth = 0;
        } else {
            seriesRenderer = d3.line()
                .x((d: any) => xScale(d.date || new Date()))
                .y((d: any) => yScale(d.value || 0));

            fill = "none";
            stroke = dataColor;
            strokeWidth = 2;
        }

        let chartGroup: IChartGroup = options.element;
        chartGroup.group
            .attr("transform", "translate(" + margin.left + "," + (options.top + margin.top) + ")");

        let chartArea: Selection = chartGroup.area;
        chartArea
            .datum(chartData)
            .attr("style", "opacity: " + (chartOpacity / 100))
            .attr("fill", fill)
            .attr("stroke", stroke)
            .attr("stroke-width", strokeWidth)
            .attr("d", seriesRenderer as any);

        let zeroAxis: Selection = chartGroup.zeroAxis;
        let zeroPointOnAxis = axisMinValue <= 0 && axisMaxValue >= 0 ? true : false;

        // DRAW line for x axis at zero position
        // if formatting option for zero line set to true
        // and if a value of zero is on the y-axis
        if (options.showZeroLine && zeroPointOnAxis) {
            let axisLine = d3.line()
                .x((d: any) => xScale(d.date))
                .y((d: any) => yScale(0));

            zeroAxis
                .datum(chartData)
                .classed("hidden", false)
                .attr("d", axisLine as any);
        } else {
            zeroAxis
                .classed("hidden", true);
        }

        let axis: Selection = chartGroup.yAxis;
        axis
            .attr("class", "axis")
            .classed(this.sizeCssClass, true)
            .classed("axis-colored", !isHighContrastMode)
            .call(yAxis);

        if (isHighContrastMode) {
            let axisTicks: Selection = axis.selectAll("g.tick");
            axisTicks.selectAll("text").attr("fill", axisStrokeHighContrastColor);
            axisTicks.select("line").attr("stroke", axisStrokeHighContrastColor);
            axis.select("path").attr("stroke", axisStrokeHighContrastColor);
            zeroAxis.style("stroke", axisStrokeHighContrastColor);
        }

        /* Add elements for hover behavior ******************************************************/
        let hoverLine: Selection = chartGroup.hoverLine;
        hoverLine
            .classed(DualKpi.INVISIBLE, true)
            .attr("x1", 0)
            .attr("x2", 0)
            .attr("y1", 0)
            .attr("y2", calcHeight)
            .attr("stroke-width", 1)
            .attr("stroke", this.colorHelper.isHighContrast ? axisStrokeHighContrastColor : hoverLineStrokeColor);

        let chartBottom = margin.top + calcHeight;
        let chartLeft = margin.left;

        let hoverDataContainer: IHoverDataContainer = options.element.hoverDataContainer;
        this.updateHoverDataContainer(hoverDataContainer, chartBottom, chartLeft, calcWidth, isTopChart);

        this.dispatch.on("onDualKpiMouseMove." + options.position, ([leftPosition, topPosition]: number[]) => {
            let areaScale: ElementScale = DualKpi.getScale(target);
            let maxWidth: number = options.width - margin.left;

            leftPosition = leftPosition / areaScale.x - margin.left - targetPadding;

            let x = xScale.invert(leftPosition);
            let i = this.dataBisector(chartData, x, 1);
            let dataPoint = chartData[i];

            if ((leftPosition > 0) &&
                (topPosition > 0) &&
                (leftPosition < maxWidth) &&
                (topPosition < (options.height * 2 + 15)) &&
                dataPoint) {

                hoverLine.attr("transform", "translate(" + leftPosition + ", 0)");
                hoverLine.classed(DualKpi.INVISIBLE, false);

                let value: number = options.hoverDataPercentType === PercentType.lastDate ? chartData[chartData.length - 1].value
                    : options.hoverDataPercentType === PercentType.firstDate ? chartData[0].value
                        : chartData[i - 1] ? chartData[i - 1].value : 0;

                this.showHoverData(hoverDataContainer, dataPoint, value, options.hoverDataPercentType, options.valueAsPercent, options.abbreviateHoverValue);
            } else {
                this.hideHoverData(hoverDataContainer, hoverLine);
            }
        });

        this.dispatch.on("onDualKpiMouseOut." + options.position, () => {
            this.hideHoverData(hoverDataContainer, hoverLine);
        });

        if (options.showTextOverlay) {
            this.addOverlayText(options, latestValue, calcHeight, calcWidth, isTopChart);
        } else {
            options.element.chartOverlay.rect.classed(DualKpi.INVISIBLE, true);
            options.element.chartOverlay.title.classed(DualKpi.INVISIBLE, true);
            options.element.chartOverlay.text.classed(DualKpi.INVISIBLE, true);
        }
    }