superset集成Echarts的地图并实现省市区下钻
作者:互联网
第一步:左侧控制栏开发
查询、过滤等等条件配置配置在此文件夹中
/*
* @FilePath: /biSuperset/superset-frontend/src/explore/controlPanels/EchartsMap.js
*/
import {
t,
// validateNonEmpty
} from '@superset-ui/translation';
import { sections } from '@superset-ui/chart-controls';
export default {
controlPanelSections: [
sections.legacyRegularTime,
{
label: t('Query'),
expanded: true,
controlSetRows: [
['groupby'],
['metrics'],
['adhoc_filters'],
['row_limit'],
],
},
],
controlOverrides: {
groupby: {
label: '省字段(必须与表字段省字段相匹配)',
description: '',
// TODO 此处需要放开
// validators: [validateNonEmpty],
},
metric: {
label: t('Metric'),
description: '',
// TODO 此处需要放开
// validators: [validateNonEmpty],
},
time_grain_sqla: {
default: null,
},
},
};
第二步:左侧栏注册
位置:explore/components/controls/VizTypeControl/VizTypeGallery
参照案例即可
'echarts_map',
第三步:右侧渲染层开发
EchartsMap.js (主要渲染文件)
/* eslint-disable array-callback-return */
import echarts from 'echarts';
// eslint-disable-next-line import/no-extraneous-dependencies
import d3 from 'd3';
import PropTypes from 'prop-types';
import 'echarts/map/js/china';
import { cityJson, provinceJson } from './map';
// TODO 待优化
// 数据类型检查
const propTypes = {
width: PropTypes.number,
height: PropTypes.number,
};
function EchartsMap(element, props) {
const { width, height, data, formData } = props; // transformProps.js 返回的数据
const div = d3.select(element);
const sliceId = `echarts-map-${10}`;
const html = `<div id="${sliceId}" style="height:${height}px; width:${width}px;border:1px"></div>`;
div.html(html);
const myChart = echarts.init(document.getElementById(sliceId));
document.oncontextmenu = function () {
return false;
}; // 取消浏览器的邮件点击事件
const groupbys = formData.groupby;
const { metrics } = formData;
let lab = 1;
let dataValue = data[groupbys[0]];
const dataName = new Set();
function getSeries(type) {
const result = [];
dataName.clear();
metrics.map(opt => {
dataValue.map(datax => {
if (
datax.name === opt ||
JSON.stringify(datax.name) === JSON.stringify(opt)
) {
datax.data.map(item => {
dataName.add(item.name);
});
const midx = {
name: datax.name,
type: 'map',
map: type,
selectedMode: 'single',
roam: 'scale',
data: datax.data,
label: {
normal: {
show: true,
textStyle: { color: '#b6a38a' },
},
emphasis: {
show: true,
textStyle: { color: '#ff6347' },
},
},
itemStyle: {
emphasis: {
areaColor: '#2e4783',
borderWidth: 0,
},
},
};
result.push(midx);
}
});
});
return result;
}
let option = {
title: {
subtext: '点击进入下一级,右键返回中国地图',
x: 'center',
bottom: '5%',
},
tooltip: {
trigger: 'item',
formatter(params) {
let res = `${params.name}<br/>`;
const myseries = option.series;
for (let i = 0; i < myseries.length; i += 1) {
for (let j = 0; j < myseries[i].data.length; j += 1) {
if (myseries[i].data[j].name === params.name) {
let field = myseries[i].name;
if (typeof field === 'object') {
field = field.column.column_name;
}
res += `${field} : ${myseries[i].data[j].value}</br>`;
}
}
}
return res;
},
},
toolbox: {
show: true,
orient: 'vertical',
left: 'right',
top: 'center',
feature: {
dataView: { readOnly: false },
restore: {},
saveAsImage: {},
},
},
visualMap: {
// type: 'continuous',
min: 0,
max: data.max,
text: ['高', '低'],
realtime: false,
calculable: true,
// right:'-15%',
inRange: {
color: [
'#d0f4fc',
'#a9dbf6',
'#9cd3f4',
'#93cdf3',
'#83c2f0',
'#6eb5ed',
'yellow',
],
},
},
series: getSeries('china'),
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
// 异步加载其余地图
provinceJson.map(item => {
$.getJSON(
`http://${window.document.location.host}/static/assets/echarts/province/${item.label}.json`,
function (res) {
echarts.registerMap(item.name, res);
},
);
});
// 用点击事件来切换地图实现下钻功能,该省份有值时才可以下钻
myChart.on('click', function (chinaParam) {
if (
chinaParam.name === '香港' ||
chinaParam.name === '澳门' ||
chinaParam.name === '台湾' ||
chinaParam.name === '南海诸岛'
) {
return;
}
// TODO XXXXX
// if (dataName.has(chinaParam.name) && lab === 1 && groupbys.length > 1) {
if (lab === 1) {
lab = 2;
// dataValue = data[groupbys[1]];
dataValue = data[groupbys[0]];
// ! TODO 为了简易的显示图表而注释,此处可以安接口方式,走返回不同的层级的数据结构
// let resultValue = [];
// dataValue.map(item => {
// let midValue = [];
// item['data'].map(opt => {
// if (opt['key'] === chinaParam.name) {
// midValue.push({ 'name': opt['name'], 'value': opt['value'] })
// }
// });
// resultValue.push({ 'name': item['name'], 'data': midValue })
// });
// dataValue = resultValue;
option = myChart.getOption();
option.series = getSeries(chinaParam.name);
myChart.setOption(option);
// TODO 三级目录暂未开发,考虑到公司不需要地级县
cityJson.map(item => {
if (item.dep === chinaParam.name) {
$.getJSON(
`http://${window.document.location.host}/static/assets/echarts/citys/${item.value}.json`,
function (res) {
echarts.registerMap(item.name, res);
},
);
}
});
}
if (dataName.has(chinaParam.name) && lab === 2 && groupbys.length > 2) {
lab = 3;
dataValue = data[groupbys[2]];
const resultValue = [];
dataValue.map(item => {
const midValue = [];
item.data.map(opt => {
if (opt.key === chinaParam.name) {
midValue.push({ name: opt.name, value: opt.value });
}
});
resultValue.push({ name: item.name, data: midValue });
});
dataValue = resultValue;
option = myChart.getOption();
option.series = getSeries(chinaParam.name);
myChart.setOption(option);
}
});
// 用双击事件来返回最上层的中国地图,当不在中国地图时生效
myChart.on('contextmenu', function () {
if (myChart.getOption().series[0].map !== 'china') {
lab = 1;
dataValue = data[groupbys[0]];
option = myChart.getOption();
option.series = getSeries('china');
myChart.setOption(option);
}
});
}
EchartsMap.displayName = 'Echarts Map';
EchartsMap.propTypes = propTypes;
export default EchartsMap;
**EchartsMapChartPlugin.js ** 插件文件
/*
* @Author: bo.lin
* @Date: 2021-10-14 14:11:25
* @LastEditors: lijuan.sun
* @LastEditTime: 2021-10-21 10:35:45
* @PageTitle: XXX页面
* @Description: XXX
* @FilePath: /biSuperset/superset-frontend/src/visualizations/EchartsMap/EchartsMapChartPlugin.js
*/
import { t, ChartMetadata, ChartPlugin } from '@superset-ui/core';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';
const metadata = new ChartMetadata({
name: t('Echarts Map'),
description: '',
credits: ['https://www.echartsjs.com/examples/en/editor.html?c=mix-line-bar'],
thumbnail,
useLegacyApi: true,
});
export default class EchartsMapChartPlugin extends ChartPlugin {
constructor() {
super({
metadata,
transformProps,
loadChart: () => import('./ReactEchartsMap'), // 前端渲染逻辑
});
}
}
map.js 地图文件
/*
* @Author: lijuan.sun
* @Date: 2021-10-20 16:46:43
* @LastEditors: lijuan.sun
* @LastEditTime: 2021-10-26 12:54:09
* @PageTitle: 页面...
* @Description: 描述...
* @FilePath: /biSuperset/superset-frontend/src/visualizations/EchartsMap/map.js
*/
const provinceJson = [
{ name: '安徽', value: 110000, label: 'anhui' },
{ name: '澳门', value: 820000, label: 'aomen' },
{ name: '北京', value: 110000, label: 'beijing' },
{ name: '重庆', value: 110000, label: 'chongqing' },
{ name: '福建', value: 110000, label: 'fujian' },
{ name: '甘肃', value: 110000, label: 'gansu' },
{ name: '广东', value: 110000, label: 'guangdong' },
{ name: '广西', value: 810000, label: 'guangxi' },
{ name: '贵州', value: 810000, label: 'guizhou' },
{ name: '海南', value: 810000, label: 'hainan' },
{ name: '湖北', value: 120000, label: 'hebei' },
{ name: '黑龙江', value: 810000, label: 'heilongjiang' },
{ name: '河南', value: 810000, label: 'henan' },
{ name: '湖北', value: 810000, label: 'hubei' },
{ name: '湖南', value: 120000, label: 'hunan' },
{ name: '江苏', value: 810000, label: 'jiangsu' },
{ name: '江西', value: 810000, label: 'jiangxi' },
{ name: '吉林', value: 810000, label: 'jilin' },
{ name: '辽宁', value: 120000, label: 'liaoning' },
{ name: '内蒙古', value: 810000, label: 'neimenggu' },
{ name: '宁夏', value: 810000, label: 'ningxia' },
{ name: '青海', value: 810000, label: 'qinghai' },
{ name: '山东', value: 120000, label: 'shandong' },
{ name: '上海', value: 810000, label: 'shanghai' },
{ name: '陕西', value: 810000, label: 'shanxi1' },
{ name: '山西', value: 810000, label: 'shanxi' },
{ name: '四川', value: 810000, label: 'sichuan' },
{ name: '台湾', value: 120000, label: 'taiwan' },
{ name: '天津', value: 810000, label: 'tianjin' },
{ name: '香港', value: 810000, label: 'xianggang' },
{ name: '新疆', value: 810000, label: 'xinjiang' },
{ name: '西藏', value: 810000, label: 'xizang' },
{ name: '云南', value: 120000, label: 'yunnan' },
{ name: '浙江', value: 810000, label: 'zhejiang' },
];
// TODO 此处暂时未使用到,如果需要三级,需要在此处新增以及寻找相关城市的json
const cityJson = [
{ name: '石家庄市', value: 130100, dep: '河北' },
{ name: '唐山市', value: 130200, dep: '河北' },
{ name: '秦皇岛市', value: 130300, dep: '河北' },
{ name: '可克达拉市', value: 659008, dep: '新疆' },
{ name: '昆玉市', value: 659009, dep: '新疆' },
];
export { cityJson, provinceJson };
ReactEchartsMap.js
/*
* @Author: bo.lin
* @Date: 2021-10-14 14:14:10
* @LastEditors: lijuan.sun
* @LastEditTime: 2021-10-21 10:36:57
* @PageTitle: XXX页面
* @Description: XXX
* @FilePath: /biSuperset/superset-frontend/src/visualizations/EchartsMap/ReactEchartsMap.js
*/
import reactify from '@superset-ui/core/esm/chart/components/reactify';
import Component from './EchartsMap';
export default reactify(Component);
transformProps.js
/*
* @Author: lijuan.sun
* @Date: 2021-10-19 11:44:59
* @LastEditors: lijuan.sun
* @LastEditTime: 2021-10-21 11:03:33
* @PageTitle: 页面...
* @Description: 描述...
* @FilePath: /biSuperset/superset-frontend/src/visualizations/EchartsMap/transformProps.js
*/
export default function transformProps(chartProps) {
// TODO XXXXX
const { width, height, queriesData, formData } = chartProps;
// formData 前端页面的数据
// queryData 后端返回的数据
return {
data: queriesData[0].data,
width,
height,
formData,
};
}
第四步:右侧模块注册
assets/src/visualizations/presets/MainPreset.js 参照其它案例即可完成
import EchartsMapChartPlugin from '../EchartsMap/EchartsMapChartPlugin';
new EchartsMapChartPlugin().configure({ key: 'echarts_map' }),
第五步:修改package.json(必须)
"echarts": "^4.7.0",
第六步: 后端开发
superset/viz.py
class EchartsMapViz(NVD3Viz):
""" echarts map"""
viz_type = 'echarts_map' # 对应前端名字
verbose_name = _('Echarts Map')
credits = 'a <a href="https://github.com/airbnb/superset">Superset</a> original'
is_timeseries = False
def run_extra_queries(self):
qry = super().query_obj()
metrics = qry['metrics']
groupbys = qry['groupby']
# 模仿country
if not metrics:
raise QueryObjectValidationError("Must specify a metric")
if not groupbys:
raise QueryObjectValidationError("Must provide ISO codes")
by = []
self.dataframes = {}
for groupby in groupbys:
by.append(groupby)
for metric in metrics:
qry.update({'metrics': [metric],'groupby':by})
df = self.get_df_payload(query_obj=qry).get("df")
# 为了字符串和对象的拼接
self.dataframes[groupby+ '_' +str(metric)] = df
def get_data(self, df):
tmp_df = df
metrics = self.form_data.get("metrics") or []
groupbys = self.form_data.get("groupby") or []
d = {}
max_num = 0
index = 0
for groupby in groupbys:
index = index + 1
midlist = []
for metric in metrics:
tmp_df = self.dataframes.get(groupby+ '_' +str(metric))
list = tmp_df.itertuples(index=False)
if index == 1:
data = [
{'name': row[0], 'value': row[1]} for row in list
]
mid_max = 0
# 新增data是否为空的判断
if len(data):
mid_max = max([data[i].get('value') for i in range(len(data))])
if mid_max > max_num:
max_num = mid_max
else:
data = [
{'name': row[index-1], 'value': row[index],'key':row[index-2]} for row in list
]
midlist.append({'name': metric, 'data': data})
d.update({groupby: midlist})
d.update({'max': max_num})
return d
标签:map,name,value,label,810000,省市区,superset,data,Echarts 来源: https://blog.csdn.net/weixin_47027124/article/details/123110948