逐步完成前后端服务器

This commit is contained in:
2025-09-09 15:00:30 +08:00
parent fcb09432e9
commit c7dfa1e9fc
2937 changed files with 1477567 additions and 0 deletions

View File

@ -0,0 +1,213 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { __extends } from "tslib";
import SeriesModel from '../../model/Series.js';
import createGraphFromNodeEdge from '../helper/createGraphFromNodeEdge.js';
import Model from '../../model/Model.js';
import { createTooltipMarkup } from '../../component/tooltip/tooltipMarkup.js';
var SankeySeriesModel = /** @class */function (_super) {
__extends(SankeySeriesModel, _super);
function SankeySeriesModel() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = SankeySeriesModel.type;
return _this;
}
/**
* Init a graph data structure from data in option series
*/
SankeySeriesModel.prototype.getInitialData = function (option, ecModel) {
var links = option.edges || option.links || [];
var nodes = option.data || option.nodes || [];
var levels = option.levels || [];
this.levelModels = [];
var levelModels = this.levelModels;
for (var i = 0; i < levels.length; i++) {
if (levels[i].depth != null && levels[i].depth >= 0) {
levelModels[levels[i].depth] = new Model(levels[i], this, ecModel);
} else {
if (process.env.NODE_ENV !== 'production') {
throw new Error('levels[i].depth is mandatory and should be natural number');
}
}
}
var graph = createGraphFromNodeEdge(nodes, links, this, true, beforeLink);
return graph.data;
function beforeLink(nodeData, edgeData) {
nodeData.wrapMethod('getItemModel', function (model, idx) {
var seriesModel = model.parentModel;
var layout = seriesModel.getData().getItemLayout(idx);
if (layout) {
var nodeDepth = layout.depth;
var levelModel = seriesModel.levelModels[nodeDepth];
if (levelModel) {
model.parentModel = levelModel;
}
}
return model;
});
edgeData.wrapMethod('getItemModel', function (model, idx) {
var seriesModel = model.parentModel;
var edge = seriesModel.getGraph().getEdgeByIndex(idx);
var layout = edge.node1.getLayout();
if (layout) {
var depth = layout.depth;
var levelModel = seriesModel.levelModels[depth];
if (levelModel) {
model.parentModel = levelModel;
}
}
return model;
});
}
};
SankeySeriesModel.prototype.setNodePosition = function (dataIndex, localPosition) {
var nodes = this.option.data || this.option.nodes;
var dataItem = nodes[dataIndex];
dataItem.localX = localPosition[0];
dataItem.localY = localPosition[1];
};
/**
* Return the graphic data structure
*
* @return graphic data structure
*/
SankeySeriesModel.prototype.getGraph = function () {
return this.getData().graph;
};
/**
* Get edge data of graphic data structure
*
* @return data structure of list
*/
SankeySeriesModel.prototype.getEdgeData = function () {
return this.getGraph().edgeData;
};
SankeySeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {
function noValue(val) {
return isNaN(val) || val == null;
}
// dataType === 'node' or empty do not show tooltip by default
if (dataType === 'edge') {
var params = this.getDataParams(dataIndex, dataType);
var rawDataOpt = params.data;
var edgeValue = params.value;
var edgeName = rawDataOpt.source + ' -- ' + rawDataOpt.target;
return createTooltipMarkup('nameValue', {
name: edgeName,
value: edgeValue,
noValue: noValue(edgeValue)
});
}
// dataType === 'node'
else {
var node = this.getGraph().getNodeByIndex(dataIndex);
var value = node.getLayout().value;
var name_1 = this.getDataParams(dataIndex, dataType).data.name;
return createTooltipMarkup('nameValue', {
name: name_1 != null ? name_1 + '' : null,
value: value,
noValue: noValue(value)
});
}
};
SankeySeriesModel.prototype.optionUpdated = function () {};
// Override Series.getDataParams()
SankeySeriesModel.prototype.getDataParams = function (dataIndex, dataType) {
var params = _super.prototype.getDataParams.call(this, dataIndex, dataType);
if (params.value == null && dataType === 'node') {
var node = this.getGraph().getNodeByIndex(dataIndex);
var nodeValue = node.getLayout().value;
params.value = nodeValue;
}
return params;
};
SankeySeriesModel.type = 'series.sankey';
SankeySeriesModel.defaultOption = {
// zlevel: 0,
z: 2,
coordinateSystem: 'view',
left: '5%',
top: '5%',
right: '20%',
bottom: '5%',
orient: 'horizontal',
nodeWidth: 20,
nodeGap: 8,
draggable: true,
layoutIterations: 32,
label: {
show: true,
position: 'right',
fontSize: 12
},
edgeLabel: {
show: false,
fontSize: 12
},
levels: [],
nodeAlign: 'justify',
lineStyle: {
color: '#314656',
opacity: 0.2,
curveness: 0.5
},
emphasis: {
label: {
show: true
},
lineStyle: {
opacity: 0.5
}
},
select: {
itemStyle: {
borderColor: '#212121'
}
},
animationEasing: 'linear',
animationDuration: 1000
};
return SankeySeriesModel;
}(SeriesModel);
export default SankeySeriesModel;

View File

@ -0,0 +1,326 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { __extends } from "tslib";
import * as graphic from '../../util/graphic.js';
import { enterEmphasis, leaveEmphasis, toggleHoverEmphasis, setStatesStylesFromModel } from '../../util/states.js';
import ChartView from '../../view/Chart.js';
import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle.js';
import { getECData } from '../../util/innerStore.js';
import { isString, retrieve3 } from 'zrender/lib/core/util.js';
var SankeyPathShape = /** @class */function () {
function SankeyPathShape() {
this.x1 = 0;
this.y1 = 0;
this.x2 = 0;
this.y2 = 0;
this.cpx1 = 0;
this.cpy1 = 0;
this.cpx2 = 0;
this.cpy2 = 0;
this.extent = 0;
}
return SankeyPathShape;
}();
var SankeyPath = /** @class */function (_super) {
__extends(SankeyPath, _super);
function SankeyPath(opts) {
return _super.call(this, opts) || this;
}
SankeyPath.prototype.getDefaultShape = function () {
return new SankeyPathShape();
};
SankeyPath.prototype.buildPath = function (ctx, shape) {
var extent = shape.extent;
ctx.moveTo(shape.x1, shape.y1);
ctx.bezierCurveTo(shape.cpx1, shape.cpy1, shape.cpx2, shape.cpy2, shape.x2, shape.y2);
if (shape.orient === 'vertical') {
ctx.lineTo(shape.x2 + extent, shape.y2);
ctx.bezierCurveTo(shape.cpx2 + extent, shape.cpy2, shape.cpx1 + extent, shape.cpy1, shape.x1 + extent, shape.y1);
} else {
ctx.lineTo(shape.x2, shape.y2 + extent);
ctx.bezierCurveTo(shape.cpx2, shape.cpy2 + extent, shape.cpx1, shape.cpy1 + extent, shape.x1, shape.y1 + extent);
}
ctx.closePath();
};
SankeyPath.prototype.highlight = function () {
enterEmphasis(this);
};
SankeyPath.prototype.downplay = function () {
leaveEmphasis(this);
};
return SankeyPath;
}(graphic.Path);
var SankeyView = /** @class */function (_super) {
__extends(SankeyView, _super);
function SankeyView() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = SankeyView.type;
_this._focusAdjacencyDisabled = false;
return _this;
}
SankeyView.prototype.render = function (seriesModel, ecModel, api) {
var sankeyView = this;
var graph = seriesModel.getGraph();
var group = this.group;
var layoutInfo = seriesModel.layoutInfo;
// view width
var width = layoutInfo.width;
// view height
var height = layoutInfo.height;
var nodeData = seriesModel.getData();
var edgeData = seriesModel.getData('edge');
var orient = seriesModel.get('orient');
this._model = seriesModel;
group.removeAll();
group.x = layoutInfo.x;
group.y = layoutInfo.y;
// generate a bezire Curve for each edge
graph.eachEdge(function (edge) {
var curve = new SankeyPath();
var ecData = getECData(curve);
ecData.dataIndex = edge.dataIndex;
ecData.seriesIndex = seriesModel.seriesIndex;
ecData.dataType = 'edge';
var edgeModel = edge.getModel();
var lineStyleModel = edgeModel.getModel('lineStyle');
var curvature = lineStyleModel.get('curveness');
var n1Layout = edge.node1.getLayout();
var node1Model = edge.node1.getModel();
var dragX1 = node1Model.get('localX');
var dragY1 = node1Model.get('localY');
var n2Layout = edge.node2.getLayout();
var node2Model = edge.node2.getModel();
var dragX2 = node2Model.get('localX');
var dragY2 = node2Model.get('localY');
var edgeLayout = edge.getLayout();
var x1;
var y1;
var x2;
var y2;
var cpx1;
var cpy1;
var cpx2;
var cpy2;
curve.shape.extent = Math.max(1, edgeLayout.dy);
curve.shape.orient = orient;
if (orient === 'vertical') {
x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + edgeLayout.sy;
y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + n1Layout.dy;
x2 = (dragX2 != null ? dragX2 * width : n2Layout.x) + edgeLayout.ty;
y2 = dragY2 != null ? dragY2 * height : n2Layout.y;
cpx1 = x1;
cpy1 = y1 * (1 - curvature) + y2 * curvature;
cpx2 = x2;
cpy2 = y1 * curvature + y2 * (1 - curvature);
} else {
x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + n1Layout.dx;
y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + edgeLayout.sy;
x2 = dragX2 != null ? dragX2 * width : n2Layout.x;
y2 = (dragY2 != null ? dragY2 * height : n2Layout.y) + edgeLayout.ty;
cpx1 = x1 * (1 - curvature) + x2 * curvature;
cpy1 = y1;
cpx2 = x1 * curvature + x2 * (1 - curvature);
cpy2 = y2;
}
curve.setShape({
x1: x1,
y1: y1,
x2: x2,
y2: y2,
cpx1: cpx1,
cpy1: cpy1,
cpx2: cpx2,
cpy2: cpy2
});
curve.useStyle(lineStyleModel.getItemStyle());
// Special color, use source node color or target node color
applyCurveStyle(curve.style, orient, edge);
var defaultEdgeLabelText = "" + edgeModel.get('value');
var edgeLabelStateModels = getLabelStatesModels(edgeModel, 'edgeLabel');
setLabelStyle(curve, edgeLabelStateModels, {
labelFetcher: {
getFormattedLabel: function (dataIndex, stateName, dataType, labelDimIndex, formatter, extendParams) {
return seriesModel.getFormattedLabel(dataIndex, stateName, 'edge', labelDimIndex,
// ensure edgeLabel formatter is provided
// to prevent the inheritance from `label.formatter` of the series
retrieve3(formatter, edgeLabelStateModels.normal && edgeLabelStateModels.normal.get('formatter'), defaultEdgeLabelText), extendParams);
}
},
labelDataIndex: edge.dataIndex,
defaultText: defaultEdgeLabelText
});
curve.setTextConfig({
position: 'inside'
});
var emphasisModel = edgeModel.getModel('emphasis');
setStatesStylesFromModel(curve, edgeModel, 'lineStyle', function (model) {
var style = model.getItemStyle();
applyCurveStyle(style, orient, edge);
return style;
});
group.add(curve);
edgeData.setItemGraphicEl(edge.dataIndex, curve);
var focus = emphasisModel.get('focus');
toggleHoverEmphasis(curve, focus === 'adjacency' ? edge.getAdjacentDataIndices() : focus === 'trajectory' ? edge.getTrajectoryDataIndices() : focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));
});
// Generate a rect for each node
graph.eachNode(function (node) {
var layout = node.getLayout();
var itemModel = node.getModel();
var dragX = itemModel.get('localX');
var dragY = itemModel.get('localY');
var emphasisModel = itemModel.getModel('emphasis');
var borderRadius = itemModel.get(['itemStyle', 'borderRadius']) || 0;
var rect = new graphic.Rect({
shape: {
x: dragX != null ? dragX * width : layout.x,
y: dragY != null ? dragY * height : layout.y,
width: layout.dx,
height: layout.dy,
r: borderRadius
},
style: itemModel.getModel('itemStyle').getItemStyle(),
z2: 10
});
setLabelStyle(rect, getLabelStatesModels(itemModel), {
labelFetcher: {
getFormattedLabel: function (dataIndex, stateName) {
return seriesModel.getFormattedLabel(dataIndex, stateName, 'node');
}
},
labelDataIndex: node.dataIndex,
defaultText: node.id
});
rect.disableLabelAnimation = true;
rect.setStyle('fill', node.getVisual('color'));
rect.setStyle('decal', node.getVisual('style').decal);
setStatesStylesFromModel(rect, itemModel);
group.add(rect);
nodeData.setItemGraphicEl(node.dataIndex, rect);
getECData(rect).dataType = 'node';
var focus = emphasisModel.get('focus');
toggleHoverEmphasis(rect, focus === 'adjacency' ? node.getAdjacentDataIndices() : focus === 'trajectory' ? node.getTrajectoryDataIndices() : focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled'));
});
nodeData.eachItemGraphicEl(function (el, dataIndex) {
var itemModel = nodeData.getItemModel(dataIndex);
if (itemModel.get('draggable')) {
el.drift = function (dx, dy) {
sankeyView._focusAdjacencyDisabled = true;
this.shape.x += dx;
this.shape.y += dy;
this.dirty();
api.dispatchAction({
type: 'dragNode',
seriesId: seriesModel.id,
dataIndex: nodeData.getRawIndex(dataIndex),
localX: this.shape.x / width,
localY: this.shape.y / height
});
};
el.ondragend = function () {
sankeyView._focusAdjacencyDisabled = false;
};
el.draggable = true;
el.cursor = 'move';
}
});
if (!this._data && seriesModel.isAnimationEnabled()) {
group.setClipPath(createGridClipShape(group.getBoundingRect(), seriesModel, function () {
group.removeClipPath();
}));
}
this._data = seriesModel.getData();
};
SankeyView.prototype.dispose = function () {};
SankeyView.type = 'sankey';
return SankeyView;
}(ChartView);
/**
* Special color, use source node color or target node color
* @param curveProps curve's style to parse
* @param orient direction
* @param edge current curve data
*/
function applyCurveStyle(curveProps, orient, edge) {
switch (curveProps.fill) {
case 'source':
curveProps.fill = edge.node1.getVisual('color');
curveProps.decal = edge.node1.getVisual('style').decal;
break;
case 'target':
curveProps.fill = edge.node2.getVisual('color');
curveProps.decal = edge.node2.getVisual('style').decal;
break;
case 'gradient':
var sourceColor = edge.node1.getVisual('color');
var targetColor = edge.node2.getVisual('color');
if (isString(sourceColor) && isString(targetColor)) {
curveProps.fill = new graphic.LinearGradient(0, 0, +(orient === 'horizontal'), +(orient === 'vertical'), [{
color: sourceColor,
offset: 0
}, {
color: targetColor,
offset: 1
}]);
}
}
}
// Add animation to the view
function createGridClipShape(rect, seriesModel, cb) {
var rectEl = new graphic.Rect({
shape: {
x: rect.x - 10,
y: rect.y - 10,
width: 0,
height: rect.height + 20
}
});
graphic.initProps(rectEl, {
shape: {
width: rect.width + 20
}
}, seriesModel, cb);
return rectEl;
}
export default SankeyView;

View File

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import SankeyView from './SankeyView.js';
import SankeySeriesModel from './SankeySeries.js';
import sankeyLayout from './sankeyLayout.js';
import sankeyVisual from './sankeyVisual.js';
export function install(registers) {
registers.registerChartView(SankeyView);
registers.registerSeriesModel(SankeySeriesModel);
registers.registerLayout(sankeyLayout);
registers.registerVisual(sankeyVisual);
registers.registerAction({
type: 'dragNode',
event: 'dragnode',
// here can only use 'update' now, other value is not support in echarts.
update: 'update'
}, function (payload, ecModel) {
ecModel.eachComponent({
mainType: 'series',
subType: 'sankey',
query: payload
}, function (seriesModel) {
seriesModel.setNodePosition(payload.dataIndex, [payload.localX, payload.localY]);
});
});
}

View File

@ -0,0 +1,489 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as layout from '../../util/layout.js';
import * as zrUtil from 'zrender/lib/core/util.js';
import { groupData } from '../../util/model.js';
export default function sankeyLayout(ecModel, api) {
ecModel.eachSeriesByType('sankey', function (seriesModel) {
var nodeWidth = seriesModel.get('nodeWidth');
var nodeGap = seriesModel.get('nodeGap');
var layoutInfo = getViewRect(seriesModel, api);
seriesModel.layoutInfo = layoutInfo;
var width = layoutInfo.width;
var height = layoutInfo.height;
var graph = seriesModel.getGraph();
var nodes = graph.nodes;
var edges = graph.edges;
computeNodeValues(nodes);
var filteredNodes = zrUtil.filter(nodes, function (node) {
return node.getLayout().value === 0;
});
var iterations = filteredNodes.length !== 0 ? 0 : seriesModel.get('layoutIterations');
var orient = seriesModel.get('orient');
var nodeAlign = seriesModel.get('nodeAlign');
layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign);
});
}
/**
* Get the layout position of the whole view
*/
function getViewRect(seriesModel, api) {
return layout.getLayoutRect(seriesModel.getBoxLayoutParams(), {
width: api.getWidth(),
height: api.getHeight()
});
}
function layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign) {
computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign);
computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient);
computeEdgeDepths(nodes, orient);
}
/**
* Compute the value of each node by summing the associated edge's value
*/
function computeNodeValues(nodes) {
zrUtil.each(nodes, function (node) {
var value1 = sum(node.outEdges, getEdgeValue);
var value2 = sum(node.inEdges, getEdgeValue);
var nodeRawValue = node.getValue() || 0;
var value = Math.max(value1, value2, nodeRawValue);
node.setLayout({
value: value
}, true);
});
}
/**
* Compute the x-position for each node.
*
* Here we use Kahn algorithm to detect cycle when we traverse
* the node to computer the initial x position.
*/
function computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign) {
// Used to mark whether the edge is deleted. if it is deleted,
// the value is 0, otherwise it is 1.
var remainEdges = [];
// Storage each node's indegree.
var indegreeArr = [];
// Used to storage the node with indegree is equal to 0.
var zeroIndegrees = [];
var nextTargetNode = [];
var x = 0;
// let kx = 0;
for (var i = 0; i < edges.length; i++) {
remainEdges[i] = 1;
}
for (var i = 0; i < nodes.length; i++) {
indegreeArr[i] = nodes[i].inEdges.length;
if (indegreeArr[i] === 0) {
zeroIndegrees.push(nodes[i]);
}
}
var maxNodeDepth = -1;
// Traversing nodes using topological sorting to calculate the
// horizontal(if orient === 'horizontal') or vertical(if orient === 'vertical')
// position of the nodes.
while (zeroIndegrees.length) {
for (var idx = 0; idx < zeroIndegrees.length; idx++) {
var node = zeroIndegrees[idx];
var item = node.hostGraph.data.getRawDataItem(node.dataIndex);
var isItemDepth = item.depth != null && item.depth >= 0;
if (isItemDepth && item.depth > maxNodeDepth) {
maxNodeDepth = item.depth;
}
node.setLayout({
depth: isItemDepth ? item.depth : x
}, true);
orient === 'vertical' ? node.setLayout({
dy: nodeWidth
}, true) : node.setLayout({
dx: nodeWidth
}, true);
for (var edgeIdx = 0; edgeIdx < node.outEdges.length; edgeIdx++) {
var edge = node.outEdges[edgeIdx];
var indexEdge = edges.indexOf(edge);
remainEdges[indexEdge] = 0;
var targetNode = edge.node2;
var nodeIndex = nodes.indexOf(targetNode);
if (--indegreeArr[nodeIndex] === 0 && nextTargetNode.indexOf(targetNode) < 0) {
nextTargetNode.push(targetNode);
}
}
}
++x;
zeroIndegrees = nextTargetNode;
nextTargetNode = [];
}
for (var i = 0; i < remainEdges.length; i++) {
if (remainEdges[i] === 1) {
throw new Error('Sankey is a DAG, the original data has cycle!');
}
}
var maxDepth = maxNodeDepth > x - 1 ? maxNodeDepth : x - 1;
if (nodeAlign && nodeAlign !== 'left') {
adjustNodeWithNodeAlign(nodes, nodeAlign, orient, maxDepth);
}
var kx = orient === 'vertical' ? (height - nodeWidth) / maxDepth : (width - nodeWidth) / maxDepth;
scaleNodeBreadths(nodes, kx, orient);
}
function isNodeDepth(node) {
var item = node.hostGraph.data.getRawDataItem(node.dataIndex);
return item.depth != null && item.depth >= 0;
}
function adjustNodeWithNodeAlign(nodes, nodeAlign, orient, maxDepth) {
if (nodeAlign === 'right') {
var nextSourceNode = [];
var remainNodes = nodes;
var nodeHeight = 0;
while (remainNodes.length) {
for (var i = 0; i < remainNodes.length; i++) {
var node = remainNodes[i];
node.setLayout({
skNodeHeight: nodeHeight
}, true);
for (var j = 0; j < node.inEdges.length; j++) {
var edge = node.inEdges[j];
if (nextSourceNode.indexOf(edge.node1) < 0) {
nextSourceNode.push(edge.node1);
}
}
}
remainNodes = nextSourceNode;
nextSourceNode = [];
++nodeHeight;
}
zrUtil.each(nodes, function (node) {
if (!isNodeDepth(node)) {
node.setLayout({
depth: Math.max(0, maxDepth - node.getLayout().skNodeHeight)
}, true);
}
});
} else if (nodeAlign === 'justify') {
moveSinksRight(nodes, maxDepth);
}
}
/**
* All the node without outEgdes are assigned maximum x-position and
* be aligned in the last column.
*
* @param nodes. node of sankey view.
* @param maxDepth. use to assign to node without outEdges as x-position.
*/
function moveSinksRight(nodes, maxDepth) {
zrUtil.each(nodes, function (node) {
if (!isNodeDepth(node) && !node.outEdges.length) {
node.setLayout({
depth: maxDepth
}, true);
}
});
}
/**
* Scale node x-position to the width
*
* @param nodes node of sankey view
* @param kx multiple used to scale nodes
*/
function scaleNodeBreadths(nodes, kx, orient) {
zrUtil.each(nodes, function (node) {
var nodeDepth = node.getLayout().depth * kx;
orient === 'vertical' ? node.setLayout({
y: nodeDepth
}, true) : node.setLayout({
x: nodeDepth
}, true);
});
}
/**
* Using Gauss-Seidel iterations method to compute the node depth(y-position)
*
* @param nodes node of sankey view
* @param edges edge of sankey view
* @param height the whole height of the area to draw the view
* @param nodeGap the vertical distance between two nodes
* in the same column.
* @param iterations the number of iterations for the algorithm
*/
function computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient) {
var nodesByBreadth = prepareNodesByBreadth(nodes, orient);
initializeNodeDepth(nodesByBreadth, edges, height, width, nodeGap, orient);
resolveCollisions(nodesByBreadth, nodeGap, height, width, orient);
for (var alpha = 1; iterations > 0; iterations--) {
// 0.99 is a experience parameter, ensure that each iterations of
// changes as small as possible.
alpha *= 0.99;
relaxRightToLeft(nodesByBreadth, alpha, orient);
resolveCollisions(nodesByBreadth, nodeGap, height, width, orient);
relaxLeftToRight(nodesByBreadth, alpha, orient);
resolveCollisions(nodesByBreadth, nodeGap, height, width, orient);
}
}
function prepareNodesByBreadth(nodes, orient) {
var nodesByBreadth = [];
var keyAttr = orient === 'vertical' ? 'y' : 'x';
var groupResult = groupData(nodes, function (node) {
return node.getLayout()[keyAttr];
});
groupResult.keys.sort(function (a, b) {
return a - b;
});
zrUtil.each(groupResult.keys, function (key) {
nodesByBreadth.push(groupResult.buckets.get(key));
});
return nodesByBreadth;
}
/**
* Compute the original y-position for each node
*/
function initializeNodeDepth(nodesByBreadth, edges, height, width, nodeGap, orient) {
var minKy = Infinity;
zrUtil.each(nodesByBreadth, function (nodes) {
var n = nodes.length;
var sum = 0;
zrUtil.each(nodes, function (node) {
sum += node.getLayout().value;
});
var ky = orient === 'vertical' ? (width - (n - 1) * nodeGap) / sum : (height - (n - 1) * nodeGap) / sum;
if (ky < minKy) {
minKy = ky;
}
});
zrUtil.each(nodesByBreadth, function (nodes) {
zrUtil.each(nodes, function (node, i) {
var nodeDy = node.getLayout().value * minKy;
if (orient === 'vertical') {
node.setLayout({
x: i
}, true);
node.setLayout({
dx: nodeDy
}, true);
} else {
node.setLayout({
y: i
}, true);
node.setLayout({
dy: nodeDy
}, true);
}
});
});
zrUtil.each(edges, function (edge) {
var edgeDy = +edge.getValue() * minKy;
edge.setLayout({
dy: edgeDy
}, true);
});
}
/**
* Resolve the collision of initialized depth (y-position)
*/
function resolveCollisions(nodesByBreadth, nodeGap, height, width, orient) {
var keyAttr = orient === 'vertical' ? 'x' : 'y';
zrUtil.each(nodesByBreadth, function (nodes) {
nodes.sort(function (a, b) {
return a.getLayout()[keyAttr] - b.getLayout()[keyAttr];
});
var nodeX;
var node;
var dy;
var y0 = 0;
var n = nodes.length;
var nodeDyAttr = orient === 'vertical' ? 'dx' : 'dy';
for (var i = 0; i < n; i++) {
node = nodes[i];
dy = y0 - node.getLayout()[keyAttr];
if (dy > 0) {
nodeX = node.getLayout()[keyAttr] + dy;
orient === 'vertical' ? node.setLayout({
x: nodeX
}, true) : node.setLayout({
y: nodeX
}, true);
}
y0 = node.getLayout()[keyAttr] + node.getLayout()[nodeDyAttr] + nodeGap;
}
var viewWidth = orient === 'vertical' ? width : height;
// If the bottommost node goes outside the bounds, push it back up
dy = y0 - nodeGap - viewWidth;
if (dy > 0) {
nodeX = node.getLayout()[keyAttr] - dy;
orient === 'vertical' ? node.setLayout({
x: nodeX
}, true) : node.setLayout({
y: nodeX
}, true);
y0 = nodeX;
for (var i = n - 2; i >= 0; --i) {
node = nodes[i];
dy = node.getLayout()[keyAttr] + node.getLayout()[nodeDyAttr] + nodeGap - y0;
if (dy > 0) {
nodeX = node.getLayout()[keyAttr] - dy;
orient === 'vertical' ? node.setLayout({
x: nodeX
}, true) : node.setLayout({
y: nodeX
}, true);
}
y0 = node.getLayout()[keyAttr];
}
}
});
}
/**
* Change the y-position of the nodes, except most the right side nodes
* @param nodesByBreadth
* @param alpha parameter used to adjust the nodes y-position
*/
function relaxRightToLeft(nodesByBreadth, alpha, orient) {
zrUtil.each(nodesByBreadth.slice().reverse(), function (nodes) {
zrUtil.each(nodes, function (node) {
if (node.outEdges.length) {
var y = sum(node.outEdges, weightedTarget, orient) / sum(node.outEdges, getEdgeValue);
if (isNaN(y)) {
var len = node.outEdges.length;
y = len ? sum(node.outEdges, centerTarget, orient) / len : 0;
}
if (orient === 'vertical') {
var nodeX = node.getLayout().x + (y - center(node, orient)) * alpha;
node.setLayout({
x: nodeX
}, true);
} else {
var nodeY = node.getLayout().y + (y - center(node, orient)) * alpha;
node.setLayout({
y: nodeY
}, true);
}
}
});
});
}
function weightedTarget(edge, orient) {
return center(edge.node2, orient) * edge.getValue();
}
function centerTarget(edge, orient) {
return center(edge.node2, orient);
}
function weightedSource(edge, orient) {
return center(edge.node1, orient) * edge.getValue();
}
function centerSource(edge, orient) {
return center(edge.node1, orient);
}
function center(node, orient) {
return orient === 'vertical' ? node.getLayout().x + node.getLayout().dx / 2 : node.getLayout().y + node.getLayout().dy / 2;
}
function getEdgeValue(edge) {
return edge.getValue();
}
function sum(array, cb, orient) {
var sum = 0;
var len = array.length;
var i = -1;
while (++i < len) {
var value = +cb(array[i], orient);
if (!isNaN(value)) {
sum += value;
}
}
return sum;
}
/**
* Change the y-position of the nodes, except most the left side nodes
*/
function relaxLeftToRight(nodesByBreadth, alpha, orient) {
zrUtil.each(nodesByBreadth, function (nodes) {
zrUtil.each(nodes, function (node) {
if (node.inEdges.length) {
var y = sum(node.inEdges, weightedSource, orient) / sum(node.inEdges, getEdgeValue);
if (isNaN(y)) {
var len = node.inEdges.length;
y = len ? sum(node.inEdges, centerSource, orient) / len : 0;
}
if (orient === 'vertical') {
var nodeX = node.getLayout().x + (y - center(node, orient)) * alpha;
node.setLayout({
x: nodeX
}, true);
} else {
var nodeY = node.getLayout().y + (y - center(node, orient)) * alpha;
node.setLayout({
y: nodeY
}, true);
}
}
});
});
}
/**
* Compute the depth(y-position) of each edge
*/
function computeEdgeDepths(nodes, orient) {
var keyAttr = orient === 'vertical' ? 'x' : 'y';
zrUtil.each(nodes, function (node) {
node.outEdges.sort(function (a, b) {
return a.node2.getLayout()[keyAttr] - b.node2.getLayout()[keyAttr];
});
node.inEdges.sort(function (a, b) {
return a.node1.getLayout()[keyAttr] - b.node1.getLayout()[keyAttr];
});
});
zrUtil.each(nodes, function (node) {
var sy = 0;
var ty = 0;
zrUtil.each(node.outEdges, function (edge) {
edge.setLayout({
sy: sy
}, true);
sy += edge.getLayout().dy;
});
zrUtil.each(node.inEdges, function (edge) {
edge.setLayout({
ty: ty
}, true);
ty += edge.getLayout().dy;
});
});
}

View File

@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as zrUtil from 'zrender/lib/core/util.js';
import VisualMapping from '../../visual/VisualMapping.js';
export default function sankeyVisual(ecModel) {
ecModel.eachSeriesByType('sankey', function (seriesModel) {
var graph = seriesModel.getGraph();
var nodes = graph.nodes;
var edges = graph.edges;
if (nodes.length) {
var minValue_1 = Infinity;
var maxValue_1 = -Infinity;
zrUtil.each(nodes, function (node) {
var nodeValue = node.getLayout().value;
if (nodeValue < minValue_1) {
minValue_1 = nodeValue;
}
if (nodeValue > maxValue_1) {
maxValue_1 = nodeValue;
}
});
zrUtil.each(nodes, function (node) {
var mapping = new VisualMapping({
type: 'color',
mappingMethod: 'linear',
dataExtent: [minValue_1, maxValue_1],
visual: seriesModel.get('color')
});
var mapValueToColor = mapping.mapValueToVisual(node.getLayout().value);
var customColor = node.getModel().get(['itemStyle', 'color']);
if (customColor != null) {
node.setVisual('color', customColor);
node.setVisual('style', {
fill: customColor
});
} else {
node.setVisual('color', mapValueToColor);
node.setVisual('style', {
fill: mapValueToColor
});
}
});
}
if (edges.length) {
zrUtil.each(edges, function (edge) {
var edgeStyle = edge.getModel().get('lineStyle');
edge.setVisual('style', edgeStyle);
});
}
});
}