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