public static CONVERTER()

in src/visual.ts [244:495]


    public static CONVERTER(
        dataView: DataView,
        host: IVisualHost,
        colorHelper: ColorHelper,
        interactivityService: IInteractivityService<DataPoint>
    ): ChartData {
        if (!dataView
            || !dataView.categorical
            || !dataView.categorical.values
            || !dataView.categorical.values[0]
            || !dataView.categorical.values[0].values
            || !dataView.categorical.categories
        ) {
            return null;
        }

        let columns: DataRoles<DataViewCategoryColumn | DataViewValueColumn> = <any>mapValues(
            Visual.RoleDisplayNames,
            (x, i) => Visual.getCategoricalColumnOfRole(dataView, i)
        );

        let valuesColumn: DataViewValueColumn = <DataViewValueColumn>columns.Value;
        let timeStampColumn = <DataViewCategoryColumn>columns.Timestamp;

        if (!timeStampColumn) {
            return null;
        }

        let isScalar: boolean = !(timeStampColumn.source && timeStampColumn.source.type && timeStampColumn.source.type.dateTime);

        const settings: PulseChartSettings = Visual.parseSettings(dataView, colorHelper);

        let categoryValues: any[] = timeStampColumn.values;

        if (!categoryValues || isEmpty(dataView.categorical.values) || !valuesColumn || isEmpty(valuesColumn.values)) {
            return null;
        }
        let minValuesValue = Math.min.apply(null, valuesColumn.values), maxValuesValue = Math.max.apply(null, valuesColumn.values);
        let minCategoryValue = Math.min.apply(null, categoryValues), maxCategoryValue = Math.max.apply(null, categoryValues);
        settings.xAxis.dateFormat =
            (maxCategoryValue - minCategoryValue < (24 * 60 * 60 * 1000)
                && new Date(maxCategoryValue).getDate() === new Date(minCategoryValue).getDate())
                ? XAxisDateFormat.TimeOnly
                : XAxisDateFormat.DateOnly;

        settings.xAxis.formatterOptions = {
            value: isScalar ? minCategoryValue : new Date(minCategoryValue),
            value2: isScalar ? maxCategoryValue : new Date(maxCategoryValue)
        };
        settings.yAxis.formatterOptions = {
            value: minValuesValue,
            value2: maxValuesValue,
            format: valueFormatter.getFormatString(valuesColumn.source, null)
        };

        if (isScalar) {
            settings.xAxis.formatterOptions.format = valueFormatter.getFormatString(timeStampColumn.source, null);
        } else {
            settings.xAxis.formatterOptions.format = Visual.getDateTimeFormatString(settings.xAxis.dateFormat, timeStampColumn.source.format);
        }

        let widthOfTooltipValueLabel = isScalar ? Visual.ScalarTooltipLabelWidth : Visual.getFullWidthOfDateFormat(timeStampColumn.source.format, Visual.getPopupValueTextProperties()) + Visual.DefaultTooltipLabelPadding;
        let heightOfTooltipDescriptionTextLine = textMeasurementService.measureSvgTextHeight(Visual.getPopupDescriptionTextProperties("lj", settings.popup.fontSize));
        let runnerCounterFormatString = columns.RunnerCounter && valueFormatter.getFormatString(columns.RunnerCounter.source, null);
        settings.popup.width = Math.max(widthOfTooltipValueLabel + 2 * Visual.DefaultTooltipLabelMargin, settings.popup.width);

        let minSize: number = settings.dots.minSize;
        let maxSize: number = settings.dots.maxSize;
        if (settings.dots) {
            minSize = settings.dots.minSize;
            maxSize = settings.dots.maxSize;
        }

        let eventSizeScale: LinearScale = <LinearScale>Visual.createScale(
            true,
            columns.EventSize ? [d3Min(<number[]>columns.EventSize.values), d3Max(<number[]>columns.EventSize.values)] : [0, 0],
            minSize,
            maxSize);

        let xAxisCardProperties: DataViewObject = Helpers.getCategoryAxisProperties(dataView.metadata);

        let hasDynamicSeries: boolean = !!(timeStampColumn.values && timeStampColumn.source);
        let hasHighlights: boolean = !!valuesColumn.highlights;

        let dataPointLabelSettings = PulseChartDataLabelUtils.getDefaultPulseChartLabelSettings();
        let gapWidths = Visual.getGapWidths(categoryValues);
        let maxGapWidth = Math.max.apply(null, gapWidths);

        let firstValueMeasureIndex: number = 0, firstGroupIndex: number = 0, secondGroupIndex = 1;
        let grouped: DataViewValueColumnGroup[] = dataView.categorical.values && dataView.categorical.values.grouped();
        let y_group0Values = grouped[firstGroupIndex]
            && grouped[firstGroupIndex].values[firstValueMeasureIndex]
            && grouped[firstGroupIndex].values[firstValueMeasureIndex].values;
        let y_group1Values = grouped[secondGroupIndex]
            && grouped[secondGroupIndex].values[firstValueMeasureIndex]
            && grouped[secondGroupIndex].values[firstValueMeasureIndex].values;

        let series: Series[] = [];
        let dataPoints: DataPoint[] = [];

        for (let categoryIndex = 0, seriesCategoryIndex = 0, len = timeStampColumn.values.length; categoryIndex < len; categoryIndex++ , seriesCategoryIndex++) {
            let categoryValue: string | Date = categoryValues[categoryIndex];
            if (isString(categoryValue)) {
                let date: Date = new Date(categoryValue);

                if (!isNaN(date.getTime())) {
                    categoryValue = date;
                    categoryValues[categoryIndex] = date;
                }
            }

            let valueFormatterLocalized = valueFormatter.create({ cultureSelector: host.locale });
            let value = AxisHelper.normalizeNonFiniteNumber(timeStampColumn.values[categoryIndex]);
            let runnerCounterValue = columns.RunnerCounter && columns.RunnerCounter.values && valueFormatterLocalized.format(columns.RunnerCounter.values[categoryIndex]);
            let identity: ISelectionId = host.createSelectionIdBuilder()
                .withCategory(timeStampColumn, categoryIndex)
                .createSelectionId();

            let minGapWidth: number = Math.max((maxCategoryValue - minCategoryValue) / Visual.MaxGapCount, <number>settings.xAxis.dateFormat);
            let gapWidth: number = gapWidths[categoryIndex];
            let isGap: boolean = settings.gaps.show
                && gapWidth > 0
                && gapWidth > (minGapWidth + (100 - settings.gaps.transparency) * (maxGapWidth - minGapWidth) / 100);

            if (isGap && dataPoints.length > 0) {
                series.push({
                    displayName: <string>grouped[firstGroupIndex].name,
                    lineIndex: series.length,
                    color: settings.series.fill,
                    data: dataPoints,
                    labelSettings: dataPointLabelSettings,
                    width: settings.series.width,
                    widthOfGap: gapWidth
                });

                seriesCategoryIndex = 0;
                dataPoints = [];
            }

            // When Scalar, skip null categories and null values so we draw connected lines and never draw isolated dots.
            if (isScalar && (categoryValue === null || value === null)) {
                continue;
            }

            let popupInfo: TooltipData = null;
            let eventSize: PrimitiveValue = (columns.EventSize && columns.EventSize.values && columns.EventSize.values[categoryIndex]) || 0;

            if ((columns.EventTitle && columns.EventTitle.values && columns.EventTitle.values[categoryIndex]) ||
                (columns.EventDescription && columns.EventDescription.values && columns.EventDescription.values[categoryIndex])) {
                let formattedValue = categoryValue;

                if (!isScalar && categoryValue) {
                    formattedValue = valueFormatter.create({ format: timeStampColumn.source.format, cultureSelector: host.locale }).format(categoryValue);
                }

                popupInfo = {
                    value: formattedValue,
                    title: columns.EventTitle && columns.EventTitle.values && valueFormatterLocalized.format(columns.EventTitle.values[categoryIndex]),
                    description: columns.EventDescription && columns.EventDescription.values && valueFormatterLocalized.format(columns.EventDescription.values[categoryIndex]),
                };
            }
            let y_value = <number>(y_group0Values && y_group0Values[categoryIndex]) || <number>(y_group1Values && y_group1Values[categoryIndex]) || 0;
            if (isNaN(y_value)) {
                y_value = 0;
            }
            let eventSizeValue: number = columns.EventSize ? eventSizeScale(<number>eventSize) : 0;
            if (isNaN(eventSizeValue)) {
                eventSizeValue = 0;
            }
            let dataPoint: DataPoint = {
                categoryValue: categoryValue,
                value: value,
                categoryIndex: categoryIndex,
                seriesIndex: series.length,
                tooltipInfo: null,
                popupInfo: popupInfo,
                selected: false,
                identity: identity,
                key: JSON.stringify({ ser: identity.getKey(), catIdx: categoryIndex }),
                labelFill: dataPointLabelSettings.labelColor,
                labelSettings: dataPointLabelSettings,
                x: <any>categoryValue,
                y: y_value,
                pointColor: settings.series.fill,
                groupIndex: Visual.getGroupIndex(categoryIndex, grouped),
                eventSize: eventSizeValue,
                runnerCounterValue: runnerCounterValue,
                runnerCounterFormatString: runnerCounterFormatString,
                specificIdentity: undefined,
                highlight: hasHighlights && !!(valuesColumn.highlights[categoryIndex])
            };

            dataPoints.push(dataPoint);
        }

        if (interactivityService) {
            interactivityService.applySelectionStateToData(dataPoints);
        }

        if (dataPoints.length > 0) {
            series.push({
                displayName: <string>grouped[firstGroupIndex].name,
                lineIndex: series.length,
                color: settings.series.fill,
                data: dataPoints,
                labelSettings: dataPointLabelSettings,
                width: settings.series.width,
                widthOfGap: 0
            });
        }

        xAxisCardProperties = Helpers.getCategoryAxisProperties(dataView.metadata);
        let valueAxisProperties = Helpers.getValueAxisProperties(dataView.metadata);

        let values = dataView.categorical.categories;

        // Convert to DataViewMetadataColumn
        let valuesMetadataArray: powerbiVisualsApi.DataViewMetadataColumn[] = [];
        if (values) {
            for (let i = 0; i < values.length; i++) {
                if (values[i] && values[i].source && values[i].source.displayName) {
                    valuesMetadataArray.push({ displayName: values[i].source.displayName });
                }
            }
        }

        let axesLabels = Visual.createAxesLabels(xAxisCardProperties, valueAxisProperties, timeStampColumn.source, valuesMetadataArray);
        let dots: DataPoint[] = Visual.getDataPointsFromSeries(series);

        if (interactivityService) {
            interactivityService.applySelectionStateToData(dots);
        }
        return {
            columns: columns,
            dots: dots,
            series: series,
            isScalar: isScalar,
            dataLabelsSettings: dataPointLabelSettings,
            axesLabels: { x: axesLabels.xAxisLabel, y: axesLabels.yAxisLabel },
            hasDynamicSeries: hasDynamicSeries,
            categoryMetadata: timeStampColumn.source,
            categories: categoryValues,
            settings: settings,
            grouped: grouped,
            hasHighlights: !!valuesColumn.highlights,
            widthOfXAxisLabel: Visual.DefaultXAxisLabelWidth,
            widthOfTooltipValueLabel: widthOfTooltipValueLabel,
            heightOfTooltipDescriptionTextLine: heightOfTooltipDescriptionTextLine,
            runnerCounterHeight: textMeasurementService.measureSvgTextHeight(
                Visual.GET_RUNNER_COUNTER_TEXT_PROPERTIES("lj", settings.runnerCounter.fontSize))
        };
    }