mirror of https://github.com/apache/superset.git
feat: Adds the ECharts Sankey chart
This commit is contained in:
parent
fe3ba12801
commit
d14da22e75
|
@ -33,7 +33,7 @@ const metadata = new ChartMetadata({
|
|||
{ url: example1, description: t('Demographics') },
|
||||
{ url: example2, description: t('Survey Responses') },
|
||||
],
|
||||
name: t('Sankey Diagram'),
|
||||
name: t('Sankey Diagram (legacy)'),
|
||||
tags: [
|
||||
t('Categorical'),
|
||||
t('Directional'),
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* 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 { SankeyTransformedProps } from './types';
|
||||
import Echart from '../components/Echart';
|
||||
|
||||
export default function Sankey(props: SankeyTransformedProps) {
|
||||
const { height, width, echartOptions, refs } = props;
|
||||
|
||||
return (
|
||||
<Echart
|
||||
refs={refs}
|
||||
height={height}
|
||||
width={width}
|
||||
echartOptions={echartOptions}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* 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 { buildQueryContext } from '@superset-ui/core';
|
||||
import { SankeyFormData } from './types';
|
||||
|
||||
export default function buildQuery(formData: SankeyFormData) {
|
||||
const { metric, sort_by_metric, source, target } = formData;
|
||||
const groupby = [source, target];
|
||||
return buildQueryContext(formData, baseQueryObject => [
|
||||
{
|
||||
...baseQueryObject,
|
||||
groupby,
|
||||
...(sort_by_metric && { orderby: [[metric, false]] }),
|
||||
},
|
||||
]);
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* 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 { t, validateNonEmpty } from '@superset-ui/core';
|
||||
import {
|
||||
ControlPanelConfig,
|
||||
dndGroupByControl,
|
||||
} from '@superset-ui/chart-controls';
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
{
|
||||
label: t('Query'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'source',
|
||||
config: {
|
||||
...dndGroupByControl,
|
||||
label: t('Source'),
|
||||
multi: false,
|
||||
description: t(
|
||||
'The column to be used as the source of the edge.',
|
||||
),
|
||||
validators: [validateNonEmpty],
|
||||
freeForm: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'target',
|
||||
config: {
|
||||
...dndGroupByControl,
|
||||
label: t('Target'),
|
||||
multi: false,
|
||||
description: t(
|
||||
'The column to be used as the target of the edge.',
|
||||
),
|
||||
validators: [validateNonEmpty],
|
||||
freeForm: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
['metric'],
|
||||
['adhoc_filters'],
|
||||
['row_limit'],
|
||||
['sort_by_metric'],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Chart Options'),
|
||||
expanded: true,
|
||||
controlSetRows: [['color_scheme']],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
Binary file not shown.
After Width: | Height: | Size: 57 KiB |
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* 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
|
||||
* regardin
|
||||
* g 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 { Behavior, ChartMetadata, ChartPlugin, t } from '@superset-ui/core';
|
||||
import buildQuery from './buildQuery';
|
||||
import controlPanel from './controlPanel';
|
||||
import transformProps from './transformProps';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
import example1 from './images/example1.png';
|
||||
import example2 from './images/example2.png';
|
||||
import { SankeyChartProps, SankeyFormData } from './types';
|
||||
|
||||
export default class EchartsSankeyChartPlugin extends ChartPlugin<
|
||||
SankeyFormData,
|
||||
SankeyChartProps
|
||||
> {
|
||||
/**
|
||||
* The constructor is used to pass relevant metadata and callbacks that get
|
||||
* registered in respective registries that are used throughout the library
|
||||
* and application. A more thorough description of each property is given in
|
||||
* the respective imported file.
|
||||
*
|
||||
* It is worth noting that `buildQuery` and is optional, and only needed for
|
||||
* advanced visualizations that require either post processing operations
|
||||
* (pivoting, rolling aggregations, sorting etc) or submitting multiple queries.
|
||||
*/
|
||||
constructor() {
|
||||
super({
|
||||
buildQuery,
|
||||
controlPanel,
|
||||
loadChart: () => import('./Sankey'),
|
||||
metadata: new ChartMetadata({
|
||||
behaviors: [Behavior.InteractiveChart],
|
||||
credits: ['https://echarts.apache.org'],
|
||||
category: t('TODO'),
|
||||
description: t(`TODO`),
|
||||
exampleGallery: [{ url: example1 }, { url: example2 }],
|
||||
name: t('Sankey Chart'),
|
||||
tags: [],
|
||||
thumbnail,
|
||||
}),
|
||||
transformProps,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* 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 { EChartsOption, SankeySeriesOption } from 'echarts';
|
||||
import { CallbackDataParams } from 'echarts/types/src/util/types';
|
||||
import {
|
||||
CategoricalColorNamespace,
|
||||
NumberFormats,
|
||||
getColumnLabel,
|
||||
getMetricLabel,
|
||||
getNumberFormatter,
|
||||
tooltipHtml,
|
||||
} from '@superset-ui/core';
|
||||
import { SankeyChartProps, SankeyTransformedProps } from './types';
|
||||
import { Refs } from '../types';
|
||||
import { getDefaultTooltip } from '../utils/tooltip';
|
||||
import { getPercentFormatter } from '../utils/formatters';
|
||||
|
||||
type Link = { source: string; target: string; value: number };
|
||||
|
||||
export default function transformProps(
|
||||
chartProps: SankeyChartProps,
|
||||
): SankeyTransformedProps {
|
||||
const refs: Refs = {};
|
||||
const { formData, height, hooks, queriesData, width } = chartProps;
|
||||
const { onLegendStateChanged } = hooks;
|
||||
const { colorScheme, metric, source, target } = formData;
|
||||
const { data } = queriesData[0];
|
||||
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
|
||||
const metricLabel = getMetricLabel(metric);
|
||||
const valueFormatter = getNumberFormatter(NumberFormats.FLOAT_2_POINT);
|
||||
const percentFormatter = getPercentFormatter(NumberFormats.PERCENT_2_POINT);
|
||||
|
||||
const links: Link[] = [];
|
||||
const set = new Set<string>();
|
||||
data.forEach(datum => {
|
||||
const sourceName = String(datum[getColumnLabel(source)]);
|
||||
const targetName = String(datum[getColumnLabel(target)]);
|
||||
const value = datum[metricLabel] as number;
|
||||
set.add(sourceName);
|
||||
set.add(targetName);
|
||||
links.push({
|
||||
source: sourceName,
|
||||
target: targetName,
|
||||
value,
|
||||
});
|
||||
});
|
||||
|
||||
const seriesData: NonNullable<SankeySeriesOption['data']> = Array.from(
|
||||
set,
|
||||
).map(name => ({
|
||||
name,
|
||||
itemStyle: {
|
||||
color: colorFn(name),
|
||||
},
|
||||
}));
|
||||
|
||||
// stores a map with the total values for each node considering the links
|
||||
const nodeValues = new Map<string, number>();
|
||||
links.forEach(link => {
|
||||
const { source, target, value } = link;
|
||||
const sourceValue = nodeValues.get(source) || 0;
|
||||
const targetValue = nodeValues.get(target) || 0;
|
||||
nodeValues.set(source, sourceValue + value);
|
||||
nodeValues.set(target, targetValue + value);
|
||||
});
|
||||
|
||||
const tooltipFormatter = (params: CallbackDataParams) => {
|
||||
const { name, data } = params;
|
||||
const value = params.value as number;
|
||||
const rows = [[metricLabel, valueFormatter.format(value)]];
|
||||
const { source, target } = data as Link;
|
||||
if (source && target) {
|
||||
rows.push([
|
||||
`% (${source})`,
|
||||
percentFormatter.format(value / nodeValues.get(source)!),
|
||||
]);
|
||||
rows.push([
|
||||
`% (${target})`,
|
||||
percentFormatter.format(value / nodeValues.get(target)!),
|
||||
]);
|
||||
}
|
||||
return tooltipHtml(rows, name);
|
||||
};
|
||||
|
||||
const echartOptions: EChartsOption = {
|
||||
series: {
|
||||
animation: false,
|
||||
data: seriesData,
|
||||
lineStyle: {
|
||||
color: 'source',
|
||||
},
|
||||
links,
|
||||
type: 'sankey',
|
||||
},
|
||||
tooltip: {
|
||||
...getDefaultTooltip(refs),
|
||||
formatter: tooltipFormatter,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
refs,
|
||||
formData,
|
||||
width,
|
||||
height,
|
||||
echartOptions,
|
||||
onLegendStateChanged,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* 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 {
|
||||
QueryFormColumn,
|
||||
QueryFormData,
|
||||
QueryFormMetric,
|
||||
} from '@superset-ui/core';
|
||||
import { BaseChartProps, BaseTransformedProps } from '../types';
|
||||
|
||||
export type SankeyFormData = QueryFormData & {
|
||||
colorScheme: string;
|
||||
metric: QueryFormMetric;
|
||||
source: QueryFormColumn;
|
||||
target: QueryFormColumn;
|
||||
};
|
||||
|
||||
export interface SankeyChartProps extends BaseChartProps<SankeyFormData> {
|
||||
formData: SankeyFormData;
|
||||
}
|
||||
|
||||
export type SankeyTransformedProps = BaseTransformedProps<SankeyFormData> & {};
|
|
@ -41,6 +41,7 @@ export {
|
|||
} from './BigNumber';
|
||||
export { default as EchartsSunburstChartPlugin } from './Sunburst';
|
||||
export { default as EchartsBubbleChartPlugin } from './Bubble';
|
||||
export { default as EchartsSankeyChartPlugin } from './Sankey';
|
||||
export { default as EchartsWaterfallChartPlugin } from './Waterfall';
|
||||
|
||||
export { default as BoxPlotTransformProps } from './BoxPlot/transformProps';
|
||||
|
@ -58,6 +59,7 @@ export { default as SunburstTransformProps } from './Sunburst/transformProps';
|
|||
export { default as BubbleTransformProps } from './Bubble/transformProps';
|
||||
export { default as WaterfallTransformProps } from './Waterfall/transformProps';
|
||||
export { default as HistogramTransformProps } from './Histogram/transformProps';
|
||||
export { default as SankeyTransformProps } from './Sankey/transformProps';
|
||||
|
||||
export { DEFAULT_FORM_DATA as TimeseriesDefaultFormData } from './Timeseries/constants';
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ import {
|
|||
EchartsHistogramChartPlugin,
|
||||
EchartsRadarChartPlugin,
|
||||
EchartsFunnelChartPlugin,
|
||||
EchartsSankeyChartPlugin,
|
||||
EchartsTreemapChartPlugin,
|
||||
EchartsMixedTimeseriesChartPlugin,
|
||||
EchartsTreeChartPlugin,
|
||||
|
@ -112,6 +113,7 @@ export default class MainPreset extends Preset {
|
|||
new DistBarChartPlugin().configure({ key: 'dist_bar' }),
|
||||
new EventFlowChartPlugin().configure({ key: 'event_flow' }),
|
||||
new EchartsFunnelChartPlugin().configure({ key: 'funnel' }),
|
||||
new EchartsSankeyChartPlugin().configure({ key: 'sankey_v2' }),
|
||||
new EchartsTreemapChartPlugin().configure({ key: 'treemap_v2' }),
|
||||
new EchartsGaugeChartPlugin().configure({ key: 'gauge_chart' }),
|
||||
new EchartsGraphChartPlugin().configure({ key: 'graph_chart' }),
|
||||
|
|
Loading…
Reference in New Issue