我是 YYDataV数据可视化 

专注于 数据可视化大屏,工厂扫码装箱系统 


前言:本案例爬取数据源为腾讯疫情数据, 对数据进行分析归类处理,然后制作成32:9超宽分辨率的可视化大屏。

效果展示

1.动态实时更新数据效果图

30【源码】数据可视化大屏:基于 Echarts + Python Flask 实现的32-9超宽大屏 - 中国&国际疫情实时追踪_大屏

2.鼠标右键切换主题 

代码中预置了12个主题风格,实际开发中可根据实际情况二次增加。

30【源码】数据可视化大屏:基于 Echarts + Python Flask 实现的32-9超宽大屏 - 中国&国际疫情实时追踪_数据可视化_02

 切换主题,我们可以看到:主题的配色变化了另一种风格。

 30【源码】数据可视化大屏:基于 Echarts + Python Flask 实现的32-9超宽大屏 - 中国&国际疫情实时追踪_json_03

 30【源码】数据可视化大屏:基于 Echarts + Python Flask 实现的32-9超宽大屏 - 中国&国际疫情实时追踪_数据_04

30【源码】数据可视化大屏:基于 Echarts + Python Flask 实现的32-9超宽大屏 - 中国&国际疫情实时追踪_数据可视化_05 

 一. 确定需求方案 

1. 屏幕分辨率

这个案例的大屏分辨率是32:9,超炫的的宽屏比。

根据电脑分辨率屏幕自适应显示,F11全屏查看;

2. 部署方式 

B/S方式:支持Windows、Linux、Mac等各种主流操作系统;支持主流浏览器Chrome,Microsoft Edge,360等;服务器采用python语言编写,配置好python环境即可。

二. 整体架构设计

  1. 前端Echarts开源库:使用 WebStorm 编辑器;
  2. 后端 http服务器:基于 Python 实现,使用 Pycharm 或 VSCode 编辑器;
  3. 数据传输格式:JSON;
  4. 数据源类型:JSON文件。实际开发需求中,支持定制HTTP API接口方式或其它各种类型数据库,如PostgreSQL、MySQL、Oracle、Microsoft SQL Server、SQLite、Excel表格等。
  5. 数据更新方式:采用http get 轮询方式 。在实际应用中,也可以视情况选择j监测后端数据实时更新,实时推送到前端的方式;

三.开发思路

1. 数据爬取 requests



headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 /537.36'
}
# 国内疫情数据统计
url = 'https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=statisGradeCityDetail,diseaseh5Shelf'
response = requests.get(url, headers=headers)
chinaTotal = response.json()['data']['diseaseh5Shelf']['chinaTotal']
chinaAdd = response.json()['data']['diseaseh5Shelf']['chinaAdd']
dom_list = {
"china": {
"localConfirm": chinaTotal['localConfirm'],
"nowConfirm": chinaTotal['nowConfirm'],
"confirm": chinaTotal['confirm'],
"dead": chinaTotal['dead'],
"localConfirm_": chinaAdd['localConfirmH5'],
"nowConfirm_": chinaAdd['nowConfirm'],
"confirm_": chinaAdd['confirm'],
"dead_": chinaAdd['dead']
},
}

2.  数据准备 pandas

生成中国地图所需的json数据 - 生成 static/map_china_map/map_china_map.json 文件

statisGradeCityDetail = response.json()['data']['statisGradeCityDetail']    
# 生成中国地图数据
# pandas分组求和groupby sum
df = pd.DataFrame(statisGradeCityDetail)
gp_df = df[["confirmAdd", "confirm", "nowConfirm"]
].groupby(df['province']).sum()

# reset_index 将province变成了列,将索引设置成了自然数0 1 2 ...
gp_df = gp_df.reset_index()
# print(gp_df)
# 生成新列名
gp_df = gp_df.rename(
columns={'province': 'name'})

print("4. 生成 map_china_map 图表的数据", gp_df)
gp_df.to_json(
"static/map_china_map/map_china_map.json", orient="records")

四.编码实现 (基于篇幅及可读性考虑,此处展示部分关键代码)

1. 前端html代码

使用bootstrap container-fluid, row, col等实现。

<body id="container_body">
<div class="container-fluid">
<!-- 外框 1 -->
<div class="row" id="container_1">
<h3 style="color:#f5efef; text-align: center;">30【源码】数据可视化大屏:基于 Echarts +
Python
Flask 实现的32-9超宽大屏 - 中国&国际疫情实时追踪 </h3>
</div>

<!-- 外框 2 -->
<div class="row" id="container_2">
<!-- 左侧 -->
<div class="col-3">
<!-- 左侧第1行 -->
<div class="row" style="height: 20%">
<div id="container_l1" class="col-3">
</div>

<div id="container_l2" class="col-3">
</div>

<div id="container_l3" class="col-3">
</div>

<div id="container_l4" class="col-3">
</div>
</div>
<!-- 左侧第2行 -->
<div class="row" style="height: 44%">
<div id="container_l5" class="col">
</div>
</div>

<!-- 左侧第3行 -->
<div class="row" style="height: 35%">
<div id="container_l6" class="col">
<dv-scroll-board id="vue_app" :config="config" />
</div>
</div>
</div>

<!-- 中间 -->
<div class="col-3">
<div class="row" style="height: 100%">
<div id="container_m1_root" class="col">
<div class="div-section">
<button type="button" class="btn btn-info" style="width: fit-content;"
onclick="async_echart_china('container_m1', 'map_china_map/map_china_map.json', 'confirmAdd')">新增确诊</button>
<button type="button" class="btn btn-info" style="width: fit-content;"
onclick="async_echart_china('container_m1', 'map_china_map/map_china_map.json', 'confirm')">累计确诊</button>
<button type="button" class="btn btn-info" style="width: fit-content;"
onclick="async_echart_china('container_m1', 'map_china_map/map_china_map.json', 'nowConfirm')">现有确诊</button>
</div>
<div id="container_m1"></div>
</div>
</div>
</div>
<!-- 中间 -->
<div class="col-3">
<div class="row" style="height: 100%">
<div class="col" id="container_m2_root">
<div class="div-section">
<button type="button" class="btn btn-info" style="width: fit-content;"
onclick="async_echart_map_geo_map_world('container_m2', 'map_geo_map_world/map_geo_map_world.json', 'confirmAdd')">新增确诊</button>
<button type="button" class="btn btn-info" style="width: fit-content;"
onclick="async_echart_map_geo_map_world('container_m2', 'map_geo_map_world/map_geo_map_world.json', 'confirm')">累计确诊</button>
<button type="button" class="btn btn-info" style="width: fit-content;"
onclick="async_echart_map_geo_map_world('container_m2', 'map_geo_map_world/map_geo_map_world.json', 'nowConfirm')">现有确诊</button>
</div>
<div id="container_m2"></div>
</div>
</div>

</div>

<!-- 右侧 -->
<div class="col-3">
<!-- 第1行 -->
<div class="row" style="height: 20%">
<div id="container_r1" class="col">
</div>

<div id="container_r2" class="col">
</div>

<div id="container_r3" class="col">
</div>

<div id="container_r4" class="col">
</div>
</div>
<!-- 第2行 -->
<div class="row" style="height: 44%">
<div id="container_r5" class="col">
</div>
</div>

<!-- 第3行 -->
<div class="row" style="height: 35%">
<div id="container_r6" class="col">
<dv-scroll-board id="vue_app_2" :config="config" />
</div>
</div>
</div>
</div>
</div>


<!-- 鼠标右键切换主题 theme [1/2] -->
<!-- style="width: 200px;" 必须写在html,不能写在css文件中 -->
<ul id="right_menu" style="width: 200px;">
<li><img src="img/drop-down.png"> 主题列表</li>
<li>infographic</li>
<li>macarons</li>
<li>roma</li>
<li>shine</li>
<li>walden</li>
<li>westeros</li>
<li>wonderland</li>
<li>vintage</li>
<li>purple-passion</li>
<li>chalk</li>
<li>dark</li>
<li>essos</li>
</ul>
</body>

2. 世界疫情地图 echarts world

function init_echart_map_geo_map_world(container) {
var chartDom = document.getElementById(container);
var myChart = echarts.init(chartDom, window.gTheme);

option = {
title: {
text: "世界疫情实时数据",
top: "2%",
left: "center",
textStyle: {
// color: "hsl(200, 86%, 48%)",
fontSize: "20",
},
},

tooltip: {
trigger: "item",
formatter: function (params) {
console.log(params);
value = 0;
if (params.value) value = params.value;
return params.seriesName + "<br>" + params.name + " : " + value + "人";
},
},

visualMap: {
type: "piecewise",
splitNumber: 5,
pieces: [
{ gt: 10000 },
{ gt: 1000, lte: 9999 },
{ gt: 500, lte: 999 },
{ gt: 100, lte: 499 },
{ gt: 10, lte: 99 },
{ gt: 1, lte: 9 },
{ lte: 0 },
],

textStyle: {
color: "#fff",
},
top: "bottom",
},
geo: [
{
map: "world",
layoutCenter: ["50%", "50%"],
zoom: 1.2,
roam: true,
// 地图放大或缩小的尺寸
layoutSize: "100%",
selectedMode: "single",
label: {
emphasis: {
show: false,
},
},
// 地图区域的多边形 图形样式。
itemStyle: {
// 图形阴影颜色。支持的格式同color。
shadowColor: "black",
// 图形阴影的模糊大小。
shadowBlur: 10,
// 阴影水平方向上的偏移距离。
shadowOffsetX: 2,
// 阴影垂直方向上的偏移距离。
shadowOffsetY: 2,
},
// 高亮状态下的多边形和标签样式。
emphasis: {
itemStyle: {
borderWidth: 3,
borderColor: "purple",
// areaColor: "#ffdead",
},
},
},
],
series: {
name: "累计确诊",
type: "map",
coordinateSystem: "geo",
geoIndex: 0,
zoom: 1.2,
roam: true,
},
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
}

3. 动态加载数据代码

function async_echart_map_geo_map_world(container, filename, section) {
$.getJSON(filename).done(function (data) {
var myChart = echarts.init(
document.getElementById(container),
window.gTheme
);
series = "";
data_section = [];
if (section == "confirmAdd") {
series = "新增确诊";
data_section = data.map((item) => {
return { name: item.name, value: item.confirmAdd };
});
}
if (section == "confirm") {
series = "累计确诊";
data_section = data.map((item) => {
return { name: item.name, value: item.confirm };
});
}
if (section == "nowConfirm") {
series = "现有确诊";
data_section = data.map((item) => {
return { name: item.name, value: item.nowConfirm };
});
}
myChart.setOption({
series: { name: series, data: data_section },
});
}); //end $.getJSON
}

4. 服务器端 JSON 通信数据定义 

世界疫情地图数据

[
{
"name": "Afghanistan",
"nameChinese": "\u963f\u5bcc\u6c57",
"confirmAdd": 0,
"confirm": 145,
"nowConfirm": 139
},
{
"name": "Albania",
"nameChinese": "\u963f\u5c14\u5df4\u5c3c\u4e9a",
"confirmAdd": 0,
"confirm": 212,
"nowConfirm": 169
},
{
"name": "Algeria",
"nameChinese": "\u963f\u5c14\u53ca\u5229\u4e9a",
"confirmAdd": 0,
"confirm": 511,
"nowConfirm": 448
},
... ...
]

5. 前端JS - 数据定时更新控制

支持在每个echarts图表中独立控制定时更新的间隔。

// 定时1s执行数据更新函数
setInterval(function () {
async_echart_bar_horizontal(
container,
path_bar_horizontal + "bar_horizontal.json"
);
}, 1000);

6.后端 flask 服务器

from flask import Flask
app = Flask(__name__, static_folder="static", template_folder="template")


# 主程序在这里
if __name__ == "__main__":

# 开启线程,触发动态数据
a = threading.Thread(target=asyncJson.loop)
a.start()

# 开启 flask 服务
app.run(host='0.0.0.0', port=88, debug=True)

四. 启动命令

<!-- 启动server命令 -->
python main.py

<!-- 浏览器中输入网址查看大屏(端口为 main.py 中的 port 参数定义) -->
http://localhost:88/static/index.html

<!-- 更多资料参考我的博客主页 -->


<!-- 更多案例参考 -->


我的微信号:6550523 欢迎多多交流

五. 运行效果

30【源码】数据可视化大屏:基于 Echarts + Python Flask 实现的32-9超宽大屏 - 中国&国际疫情实时追踪_数据可视化_06

六. 源码下载