in src/sankeyDiagram.ts [425:627]
public converter(dataView: DataView): SankeyDiagramDataView {
const settings: SankeyDiagramSettings = this.parseSettings(dataView);
if (!dataView
|| !dataView.matrix
|| !dataView.matrix.rows
|| !dataView.matrix.rows.levels
|| !dataView.matrix.rows.levels[0]
|| !dataView.matrix.rows.levels[0].sources
|| !dataView.matrix.rows.levels[0].sources[0]
|| !dataView.matrix.rows.levels[0].sources[0].displayName
|| !dataView.matrix.rows.levels[1]
|| !dataView.matrix.rows.levels[1].sources
|| !dataView.matrix.rows.levels[1].sources[0]
|| !dataView.matrix.rows.levels[1].sources[0].displayName
|| !dataView.matrix.rows.root
|| !dataView.matrix.rows.root.children
|| !dataView.matrix.valueSources) {
return {
settings,
nodes: [],
links: [],
columns: []
}
}
let nodes: SankeyDiagramNode[] = [],
links: SankeyDiagramLink[] = [],
categories: any[] = [],
sourceCategories: any[] = [],
destinationCategories: any[] = [],
sourceCategoriesLabels: any[] = [],
destinationCategoriesLabels: any[] = [],
objects: any[] = [],
weights: any[] = [],
selectionIdBuilder: SelectionIdBuilder = new SelectionIdBuilder(
this.visualHost,
dataView.matrix),
valueSources = dataView.matrix.valueSources;
const sourceLabelIndex: number = valueSources.indexOf(valueSources.filter((column: powerbi.DataViewMetadataColumn) => {
return column.roles.SourceLabels;
}).pop());
const weightIndex: number = valueSources.indexOf(valueSources.filter((source: powerbi.DataViewMetadataColumn) => {
return source.roles.Weight;
}).pop());
const sourceFieldName = dataView.matrix.rows.levels[0].sources[0].displayName;
const destinationFieldName = dataView.matrix.rows.levels[1].sources[0].displayName;
const valueFieldName = dataView.matrix.valueSources[weightIndex] ? dataView.matrix.valueSources[weightIndex].displayName : null;
const formatOfWeigth = valueFormatter.getFormatStringByColumn(valueSources[weightIndex]);
let weightValues: number[] = [1];
dataView.matrix.rows.root.children.forEach(parent => {
let newSourceNode = this.createNewNode(parent, settings)
newSourceNode.identity = this.visualHost.createSelectionIdBuilder()
.withMatrixNode(parent, dataView.matrix.rows.levels)
.createSelectionId();
nodes.push(newSourceNode);
});
const valuesFormatterForWeigth = valueFormatter.create({
format: formatOfWeigth,
value: Math.max(
settings.labels.unit !== 0 ? settings.labels.unit : d3.max(weightValues) || SankeyDiagram.MinWeightValue,
SankeyDiagram.MinWeightValue),
});
dataView.matrix.rows.root.children.forEach(parent => {
let foundSource: SankeyDiagramNode = nodes.find(found => found.label.name === parent.value)
parent.children.forEach(child => {
let linkLabel = undefined;
let weigth: any = SankeyDiagram.DefaultWeightValue;
let foundDestination: SankeyDiagramNode = nodes.find(found => found.label.name === child.value)
if (!foundDestination) {
foundDestination = this.createNewNode(child, settings);
foundDestination.identity = this.visualHost.createSelectionIdBuilder()
.withMatrixNode(parent, dataView.matrix.rows.levels)
.withMatrixNode(child, dataView.matrix.rows.levels)
.createSelectionId();
nodes.push(foundDestination);
}
if (sourceLabelIndex != -1) {
linkLabel = (child.values[sourceLabelIndex] && child.values[sourceLabelIndex].value) ?
child.values[sourceLabelIndex].value || SankeyDiagram.DefaultWeightValue : SankeyDiagram.MinWeightValue;
}
// If weights are present, populate the weights array
if (weightIndex != -1) {
weigth = (child.values[weightIndex] && child.values[weightIndex].value) ?
child.values[weightIndex].value || SankeyDiagram.DefaultWeightValue : SankeyDiagram.MinWeightValue;
weightValues.push(weigth);
}
let linkFillColor = this.getColor(
SankeyDiagram.LinksPropertyIdentifier,
SankeyDiagram.DefaultColourOfLink,
child.objects);
let linkStrokeColor = this.colorHelper.isHighContrast ? this.colorHelper.getHighContrastColor("foreground", linkFillColor) : linkFillColor;
let tooltipInfo = SankeyDiagram.getTooltipDataForLink(
valuesFormatterForWeigth,
foundSource.label.formattedName,
foundDestination.label.formattedName,
weigth,
sourceFieldName,
destinationFieldName,
valueFieldName
);
let link: SankeyDiagramLink = {
label: linkLabel && linkLabel.toString(),
source: foundSource,
destination: foundDestination,
weigth: weigth,
height: 10,
fillColor: linkFillColor,
strokeColor: linkStrokeColor,
dySource: 0,
dyDestination: 0,
tooltipInfo: tooltipInfo,
identity: this.visualHost.createSelectionIdBuilder()
.withMatrixNode(parent, dataView.matrix.rows.levels)
.withMatrixNode(child, dataView.matrix.rows.levels)
.createSelectionId(),
selected: false,
direction: SankeyLinkDirrections.Forward
}
let selectableDataPoint: SelectableDataPoint = SankeyDiagram.createSelectableDataPoint(<ISelectionId>link.identity);
foundSource.selectableDataPoints.push(selectableDataPoint);
foundDestination.selectableDataPoints.push(selectableDataPoint);
links.push(link);
foundSource.links.push(link);
foundDestination.links.push(link);
SankeyDiagram.updateValueOfNode(foundSource);
SankeyDiagram.updateValueOfNode(foundDestination);
});
});
let cycles: SankeyDiagramCycleDictionary = this.checkCycles(nodes);
if (settings.cyclesLinks.drawCycles === CyclesDrawType.Duplicate) {
links = this.processCyclesForwardLinks(cycles, nodes, links, settings);
}
nodes.forEach((node: SankeyDiagramNode) => {
node.tooltipInfo = SankeyDiagram.getTooltipForNode(
valuesFormatterForWeigth,
node.label.formattedName,
node.inputWeight
? node.inputWeight
: node.outputWeight,
this.localizationManager,
node.inputWeight > 0 && node.outputWeight > 0 ? `${sourceFieldName}-${destinationFieldName}` : node.outputWeight > 0
? sourceFieldName
: destinationFieldName,
valueFieldName
);
});
let sankeyDiagramDataView = {
nodes,
links,
settings,
columns: []
};
if (settings.cyclesLinks.drawCycles === CyclesDrawType.Backward) {
SankeyDiagram.computeXPositions(sankeyDiagramDataView);
sankeyDiagramDataView.links = this.processCyclesForBackwardLinks(cycles, sankeyDiagramDataView.nodes, links, settings);
sankeyDiagramDataView.links.forEach((link: SankeyDiagramLink) => {
if (link.destination === link.source) {
link.direction = SankeyLinkDirrections.SelfLink;
}
});
}
this.checkNodePositionSettings(nodes, settings);
this.restoreNodePositions(nodes, settings);
return sankeyDiagramDataView;
}