Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new plot sankey 桑基图 #2016

Merged
merged 8 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions __tests__/data/sankey-energy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
export const ENERGY = {
nodes: [
{ name: "Agricultural 'waste'" },
{ name: 'Bio-conversion' },
{ name: 'Liquid' },
{ name: 'Losses' },
{ name: 'Solid' },
{ name: 'Gas' },
{ name: 'Biofuel imports' },
{ name: 'Biomass imports' },
{ name: 'Coal imports' },
{ name: 'Coal' },
{ name: 'Coal reserves' },
{ name: 'District heating' },
{ name: 'Industry' },
{ name: 'Heating and cooling - commercial' },
{ name: 'Heating and cooling - homes' },
{ name: 'Electricity grid' },
{ name: 'Over generation / exports' },
{ name: 'H2 conversion' },
{ name: 'Road transport' },
{ name: 'Agriculture' },
{ name: 'Rail transport' },
{ name: 'Lighting & appliances - commercial' },
{ name: 'Lighting & appliances - homes' },
{ name: 'Gas imports' },
{ name: 'Ngas' },
{ name: 'Gas reserves' },
{ name: 'Thermal generation' },
{ name: 'Geothermal' },
{ name: 'H2' },
{ name: 'Hydro' },
{ name: 'International shipping' },
{ name: 'Domestic aviation' },
{ name: 'International aviation' },
{ name: 'National navigation' },
{ name: 'Marine algae' },
{ name: 'Nuclear' },
{ name: 'Oil imports' },
{ name: 'Oil' },
{ name: 'Oil reserves' },
{ name: 'Other waste' },
{ name: 'Pumped heat' },
{ name: 'Solar PV' },
{ name: 'Solar Thermal' },
{ name: 'Solar' },
{ name: 'Tidal' },
{ name: 'UK land based bioenergy' },
{ name: 'Wave' },
{ name: 'Wind' },
],
links: [
{ source: 0, target: 1, value: 124.729 },
{ source: 1, target: 2, value: 0.597 },
{ source: 1, target: 3, value: 26.862 },
{ source: 1, target: 4, value: 280.322 },
{ source: 1, target: 5, value: 81.144 },
{ source: 6, target: 2, value: 35 },
{ source: 7, target: 4, value: 35 },
{ source: 8, target: 9, value: 11.606 },
{ source: 10, target: 9, value: 63.965 },
{ source: 9, target: 4, value: 75.571 },
{ source: 11, target: 12, value: 10.639 },
{ source: 11, target: 13, value: 22.505 },
{ source: 11, target: 14, value: 46.184 },
{ source: 15, target: 16, value: 104.453 },
{ source: 15, target: 14, value: 113.726 },
{ source: 15, target: 17, value: 27.14 },
{ source: 15, target: 12, value: 342.165 },
{ source: 15, target: 18, value: 37.797 },
{ source: 15, target: 19, value: 4.412 },
{ source: 15, target: 13, value: 40.858 },
{ source: 15, target: 3, value: 56.691 },
{ source: 15, target: 20, value: 7.863 },
{ source: 15, target: 21, value: 90.008 },
{ source: 15, target: 22, value: 93.494 },
{ source: 23, target: 24, value: 40.719 },
{ source: 25, target: 24, value: 82.233 },
{ source: 5, target: 13, value: 0.129 },
{ source: 5, target: 3, value: 1.401 },
{ source: 5, target: 26, value: 151.891 },
{ source: 5, target: 19, value: 2.096 },
{ source: 5, target: 12, value: 48.58 },
{ source: 27, target: 15, value: 7.013 },
{ source: 17, target: 28, value: 20.897 },
{ source: 17, target: 3, value: 6.242 },
{ source: 28, target: 18, value: 20.897 },
{ source: 29, target: 15, value: 6.995 },
{ source: 2, target: 12, value: 121.066 },
{ source: 2, target: 30, value: 128.69 },
{ source: 2, target: 18, value: 135.835 },
{ source: 2, target: 31, value: 14.458 },
{ source: 2, target: 32, value: 206.267 },
{ source: 2, target: 19, value: 3.64 },
{ source: 2, target: 33, value: 33.218 },
{ source: 2, target: 20, value: 4.413 },
{ source: 34, target: 1, value: 4.375 },
{ source: 24, target: 5, value: 122.952 },
{ source: 35, target: 26, value: 839.978 },
{ source: 36, target: 37, value: 504.287 },
{ source: 38, target: 37, value: 107.703 },
{ source: 37, target: 2, value: 611.99 },
{ source: 39, target: 4, value: 56.587 },
{ source: 39, target: 1, value: 77.81 },
{ source: 40, target: 14, value: 193.026 },
{ source: 40, target: 13, value: 70.672 },
{ source: 41, target: 15, value: 59.901 },
{ source: 42, target: 14, value: 19.263 },
{ source: 43, target: 42, value: 19.263 },
{ source: 43, target: 41, value: 59.901 },
{ source: 4, target: 19, value: 0.882 },
{ source: 4, target: 26, value: 400.12 },
{ source: 4, target: 12, value: 46.477 },
{ source: 26, target: 15, value: 525.531 },
{ source: 26, target: 3, value: 787.129 },
{ source: 26, target: 11, value: 79.329 },
{ source: 44, target: 15, value: 9.452 },
{ source: 45, target: 1, value: 182.01 },
{ source: 46, target: 15, value: 19.013 },
{ source: 47, target: 15, value: 289.366 },
],
};

export const ENERGY_RELATIONS = [
{ source: "Agricultural 'waste'", target: 'Bio-conversion', value: 124.729 },
{ source: 'Bio-conversion', target: 'Liquid', value: 0.597 },
{ source: 'Bio-conversion', target: 'Losses', value: 26.862 },
{ source: 'Bio-conversion', target: 'Solid', value: 280.322 },
{ source: 'Bio-conversion', target: 'Gas', value: 81.144 },
{ source: 'Biofuel imports', target: 'Liquid', value: 35 },
{ source: 'Biomass imports', target: 'Solid', value: 35 },
{ source: 'Coal imports', target: 'Coal', value: 11.606 },
{ source: 'Coal reserves', target: 'Coal', value: 63.965 },
{ source: 'Coal', target: 'Solid', value: 75.571 },
{ source: 'District heating', target: 'Industry', value: 10.639 },
{
source: 'District heating',
target: 'Heating and cooling - commercial',
value: 22.505,
},
{
source: 'District heating',
target: 'Heating and cooling - homes',
value: 46.184,
},
{
source: 'Electricity grid',
target: 'Over generation / exports',
value: 104.453,
},
{
source: 'Electricity grid',
target: 'Heating and cooling - homes',
value: 113.726,
},
{ source: 'Electricity grid', target: 'H2 conversion', value: 27.14 },
{ source: 'Electricity grid', target: 'Industry', value: 342.165 },
{ source: 'Electricity grid', target: 'Road transport', value: 37.797 },
{ source: 'Electricity grid', target: 'Agriculture', value: 4.412 },
{
source: 'Electricity grid',
target: 'Heating and cooling - commercial',
value: 40.858,
},
{ source: 'Electricity grid', target: 'Losses', value: 56.691 },
{ source: 'Electricity grid', target: 'Rail transport', value: 7.863 },
{
source: 'Electricity grid',
target: 'Lighting & appliances - commercial',
value: 90.008,
},
{
source: 'Electricity grid',
target: 'Lighting & appliances - homes',
value: 93.494,
},
{ source: 'Gas imports', target: 'Ngas', value: 40.719 },
{ source: 'Gas reserves', target: 'Ngas', value: 82.233 },
{ source: 'Gas', target: 'Heating and cooling - commercial', value: 0.129 },
{ source: 'Gas', target: 'Losses', value: 1.401 },
{ source: 'Gas', target: 'Thermal generation', value: 151.891 },
{ source: 'Gas', target: 'Agriculture', value: 2.096 },
{ source: 'Gas', target: 'Industry', value: 48.58 },
{ source: 'Geothermal', target: 'Electricity grid', value: 7.013 },
{ source: 'H2 conversion', target: 'H2', value: 20.897 },
{ source: 'H2 conversion', target: 'Losses', value: 6.242 },
{ source: 'H2', target: 'Road transport', value: 20.897 },
{ source: 'Hydro', target: 'Electricity grid', value: 6.995 },
{ source: 'Liquid', target: 'Industry', value: 121.066 },
{ source: 'Liquid', target: 'International shipping', value: 128.69 },
{ source: 'Liquid', target: 'Road transport', value: 135.835 },
{ source: 'Liquid', target: 'Domestic aviation', value: 14.458 },
{ source: 'Liquid', target: 'International aviation', value: 206.267 },
{ source: 'Liquid', target: 'Agriculture', value: 3.64 },
{ source: 'Liquid', target: 'National navigation', value: 33.218 },
{ source: 'Liquid', target: 'Rail transport', value: 4.413 },
{ source: 'Marine algae', target: 'Bio-conversion', value: 4.375 },
{ source: 'Ngas', target: 'Gas', value: 122.952 },
{ source: 'Nuclear', target: 'Thermal generation', value: 839.978 },
{ source: 'Oil imports', target: 'Oil', value: 504.287 },
{ source: 'Oil reserves', target: 'Oil', value: 107.703 },
{ source: 'Oil', target: 'Liquid', value: 611.99 },
{ source: 'Other waste', target: 'Solid', value: 56.587 },
{ source: 'Other waste', target: 'Bio-conversion', value: 77.81 },
{
source: 'Pumped heat',
target: 'Heating and cooling - homes',
value: 193.026,
},
{
source: 'Pumped heat',
target: 'Heating and cooling - commercial',
value: 70.672,
},
{ source: 'Solar PV', target: 'Electricity grid', value: 59.901 },
{
source: 'Solar Thermal',
target: 'Heating and cooling - homes',
value: 19.263,
},
{ source: 'Solar', target: 'Solar Thermal', value: 19.263 },
{ source: 'Solar', target: 'Solar PV', value: 59.901 },
{ source: 'Solid', target: 'Agriculture', value: 0.882 },
{ source: 'Solid', target: 'Thermal generation', value: 400.12 },
{ source: 'Solid', target: 'Industry', value: 46.477 },
{ source: 'Thermal generation', target: 'Electricity grid', value: 525.531 },
{ source: 'Thermal generation', target: 'Losses', value: 787.129 },
{ source: 'Thermal generation', target: 'District heating', value: 79.329 },
{ source: 'Tidal', target: 'Electricity grid', value: 9.452 },
{
source: 'UK land based bioenergy',
target: 'Bio-conversion',
value: 182.01,
},
{ source: 'Wave', target: 'Electricity grid', value: 19.013 },
{ source: 'Wind', target: 'Electricity grid', value: 289.366 },
];
126 changes: 126 additions & 0 deletions __tests__/unit/plots/sankey/index-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { Datum, Sankey } from '../../../../src';
import { createDiv } from '../../../utils/dom';
import { ENERGY_RELATIONS } from '../../../data/sankey-energy';

describe('sankey', () => {
it('sankey', () => {
const sankey = new Sankey(createDiv(), {
height: 500,
data: ENERGY_RELATIONS,
sourceField: 'source',
targetField: 'target',
weightField: 'value',
});

sankey.render();

// 默认值
expect(sankey.options.nodeStyle).toEqual({
opacity: 1,
fillOpacity: 1,
lineWidth: 1,
});
expect(sankey.options.edgeStyle).toEqual({
opacity: 0.3,
lineWidth: 0,
});

expect(sankey.options.nodeWidthRatio).toBe(0.008);
expect(sankey.options.nodePaddingRatio).toBe(0.01);

expect(sankey.options.appendPadding).toEqual(8);

// node
expect(sankey.chart.views[0].geometries[0].type).toBe('polygon');
expect(sankey.chart.views[0].geometries[0].data.length).toBe(48);
expect(sankey.chart.views[0].geometries[0].data[0]).toEqual({
name: "Agricultural 'waste'",
x: [0, 0.008, 0.008, 0],
y: [0.26075939300940637, 0.26075939300940637, 0.2963247055394385, 0.2963247055394385],
});

// edge
expect(sankey.chart.views[1].geometries[0].type).toBe('edge');
expect(sankey.chart.views[1].geometries[0].data.length).toBe(68);
expect(sankey.chart.views[1].geometries[0].data[0]).toEqual({
name: "Agricultural 'waste'",
source: "Agricultural 'waste'",
target: 'Bio-conversion',
value: 124.729,
x: [0.008, 0.008, 0.1417142857142857, 0.1417142857142857],
y: [0.2963247055394385, 0.26075939300940637, 0.3018199231660282, 0.26625461063599604],
});

// label
expect(sankey.chart.views[0].geometries[0].labelsContainer.getChildren().length).toBe(48);
expect(sankey.chart.views[0].geometries[0].labelsContainer.getChildByIndex(0).cfg.children[0].attr('text')).toBe(
"Agricultural 'waste'"
);
expect(sankey.chart.views[1].geometries[0].labelsContainer.getChildren().length).toBe(0);

// tooltip
sankey.chart.showTooltip({ x: 100, y: 100 });
expect(document.querySelector('.g2-tooltip-name').textContent).toBe('Oil imports -> Oil');
expect(document.querySelector('.g2-tooltip-value').textContent).toBe('504.287');

sankey.destroy();
});

it('sankey style', () => {
const DATA = [
{ source: '首次打开', target: '首页 UV', value: 160 },
{ source: '结果页', target: '首页 UV', value: 40 },
{ source: '验证页', target: '首页 UV', value: 10 },
{ source: '我的', target: '首页 UV', value: 10 },
{ source: '朋友', target: '首页 UV', value: 8 },
{ source: '其他来源', target: '首页 UV', value: 27 },
{ source: '首页 UV', target: '理财', value: 30 },
{ source: '首页 UV', target: '扫一扫', value: 40 },
{ source: '首页 UV', target: '服务', value: 35 },
{ source: '首页 UV', target: '蚂蚁森林', value: 25 },
{ source: '首页 UV', target: '跳失', value: 10 },
{ source: '首页 UV', target: '借呗', value: 30 },
{ source: '首页 UV', target: '花呗', value: 40 },
{ source: '首页 UV', target: '其他流向', value: 45 },
];

let d = null;
const sankey = new Sankey(createDiv(), {
data: DATA,
sourceField: 'source',
targetField: 'target',
weightField: 'value',
nodeStyle: (datum: Datum) => {
d = datum;
return {
fill: 'red',
};
},
edgeStyle: {
fill: '#ccc',
fillOpacity: 0.5,
},
});

sankey.render();

// @ts-ignore
expect(sankey.chart.views[1].geometries[0].styleOption.cfg).toEqual({
fill: '#ccc',
fillOpacity: 0.5,
lineWidth: 0,
opacity: 0.3,
});

// @ts-ignore
expect(sankey.chart.views[0].geometries[0].styleOption.fields).toEqual(['x', 'y', 'name']);

expect(d).toEqual({
name: '其他流向',
x: [0.992, 1, 1, 0.992],
y: [0.8358823529411765, 0.8358823529411765, 1, 1],
});

sankey.destroy();
});
});
30 changes: 30 additions & 0 deletions __tests__/unit/plots/sankey/util/data-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { transformDataToSankey } from '../../../../../src/plots/sankey/util/data';

describe('sankey util', () => {
it('transformDataToSankey', () => {
// @ts-ignore
expect(transformDataToSankey({})).toEqual({ nodes: [], links: [] });
// @ts-ignore
expect(transformDataToSankey(1)).toEqual({ nodes: [], links: [] });

expect(
transformDataToSankey(
[
{ source: '杭州', target: '上海', value: 1 },
{ source: '上海', target: '北京', value: 2 },
{ source: '杭州', target: '北京', value: 3 },
],
'source',
'target',
'value'
)
).toEqual({
nodes: [{ name: '杭州' }, { name: '上海' }, { name: '北京' }],
links: [
{ source: 0, target: 1, value: 1 },
{ source: 1, target: 2, value: 2 },
{ source: 0, target: 2, value: 3 },
],
});
});
});
Loading