基於Echarts的全球COVID-19(新冠肺炎)新增病例動態視覺化

村裡的intern發表於2020-12-28

本demo主要目的是對全球的新冠病例新增資料進行動態視覺化,底層用的是echarts [1],資料來自Hopkins [2]。

效果如下:

首先看一下echarts程式碼的結構:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ECharts</title>
    <!-- 引入 echarts.js -->
    <script src="incubator-echarts-4.9.0/dist/echarts.min.js"></script>
    <script src="incubator-echarts-4.9.0/map/js/world.js"></script>
</head>
<body>
    <!-- 為ECharts準備一個具備大小(寬高)的Dom -->
    <div id="main" style="width: 100%;height:600px;"></div>
    <script type="text/javascript">
        // 基於準備好的dom,初始化echarts例項
        var myChart = echarts.init(document.getElementById('main'));
        var symbol = "/asset/get/s/data-1541516737897-uvYNg2ec4.png";

        var data2 = 
        [
	      {'date': '2020.1.20', //2020年1月20日全球各國新增新冠病例
		   'data': 
		        [{'name': 'China', 'value':77.0},
		         {'name': 'Dem. Rep. Korea', 'value':1.0},
                  ......//此處填補所有國家的資料
                 {'name': 'Saint Helena', 'value':0.0}
		        ]
          },
          {'date': '2020.1.21', //2020年1月21日全球各國新增新冠病例
		   'data': 
		        [{'name': 'China', 'value':77.0},
		         {'name': 'Dem. Rep. Korea', 'value':1.0},
                  ......
                 {'name': 'Saint Helena', 'value':0.0}
		        ]
          }
          ......//此處填補所有日期的全球新冠資料
        ];
        
        var data = data2;
        var geoCoordMap3 = {
           'Afghanistan': [67.709953, 33.93911],
           'Aland Islands': [39.390897, -99.066067],
           ......//此處為所有國家的資訊,一定注意這裡的國家名稱與上面json資料中的國家名稱要一致
           'Central African Rep.': [6.1428, 20.399599]
        };

        var geoCoordMap = geoCoordMap3;


function formatNum(strNum) {
    strNum = strNum.toFixed(0)
    if (strNum.length <= 3) {
        return strNum;
    }
    if (!/^(\+|-)?(\d+)(\.\d+)?$/.test(strNum)) {
        return strNum;
    }
    var a = RegExp.$1,
        b = RegExp.$2,
        c = RegExp.$3;
    var re = new RegExp(); 
    re.compile("(\\d)(\\d{3})(,|$)");
    while (re.test(b)) {
        b = b.replace(re, "$1,$2$3");
    }
    return a + "" + b + "" + c;
};

function topN(N) {
    var dataRaw = [];
    for (var i = 0; i < data.length; i++) {
        dataRaw[i] = {};
        dataRaw[i].date = data[i].date;
        dataRaw[i].data = data[i].data.slice(0, N);
    }
    return dataRaw;
};
var dataTopN = topN(10);
//alert(dataTopN[0].data['name'])

var convertData = function(data) {
    var res = [];
    for (var i = 0; i < data.length; i++) {
        var geoCoord = geoCoordMap[data[i].name];
        if (geoCoord) {
            res.push({
                name: data[i].name,
                value: geoCoord.concat(Math.round(data[i].value))
            });
        }
    }
    return res;
};

var showList = [];//['South Africa', 'United States', 'Russia', 'China', 'Australia', 'India', 'Zimbabwe', 'Nigeria', 'Brazil', 'Greenland']
var convertData2 = function(data) {
    var res = [];
    for (var i = 0; i < data.length; i++) {
        if (showList.indexOf(data[i].name) > -1) {
            var geoCoord = geoCoordMap[data[i].name];
            res.push({
                name: data[i].name,
                value: geoCoord.concat(Math.round(data[i].value))
            });
        }
    }
    return res;
};

option = {
    baseOption: {
        backgroundColor: new echarts.graphic.RadialGradient(0.3, 0.3, 0.8, [{
            offset: 0,
            color: '#f7f8fa'
        }, {
            offset: 1,
            color: '#cdd0d5'
        }]),

        title: [{
                //  text: '全球新冠累計病例',
                //  subtext: '單位:人數',
                left: 'center',
                top: '5%',
                textStyle: {
                    fontSize: 30,
                    color: 'rgba(23,23,31, 0.7)',
                    textShadowColor: '(0,0,0,0.3)',
                    textShadowBlur: 5,
                    textShadowOffsetX: 2,
                    textShadowOffsety: 4
                }
            },
            {
                id: 'top 10',
                test: ''
            }
        ],
        visualMap: {
            min: 0,    
            max: 10000,
            show: false,
            left: 'left',
            top: 'bottom',
            text: ['高', '低'], // 文字,預設為數值文字
            calculable: true,
            seriesIndex: [0,2],
	            inRange: {
	                color: ['rgba(255,255,255,1)', 'rgba(129,0,0,1)'],
	                //color: ['rgba(255,255,255,0.4)', 'rgba(129,0,0,1)'],
	                //symbolSize: [10, 50]
	            }
            
        },
        
        timeline: {
            show: false,
            axisType: 'category',
            orient: 'vertical',
            autoPlay: true,
            loop: false,
            //  inverse: true,
            playInterval: 200,
            left: null,
            right: 30,
            top: 330,
            bottom: 100,
            //  width: 46,
            height: null,
            label: {
                normal: {
                    show: true,
                    interval: 0,
                },
            },
            symbol: 'none',
            lineStyle: {
                color: '#ccc',
                show: false
            },
            checkpointStyle: {
                symbol: 'none',
                color: '#bbb',
                borderColor: '#777',
                show: false,
                borderWidth: 1
            },
            controlStyle: {
                showNextBtn: false,
                showPrevBtn: false,
                normal: {
                    color: '#666',
                    show: false,
                    borderColor: '#666'
                },
                emphasis: {
                    color: '#aaa',
                    borderColor: '#aaa'
                }
            },
            data: data.map(function(ele) {
                return ele.date
            })
        },
        grid: {
            left: '6%',
            right: '70%',
            top: '65%',
            height: 'auto',
            bottom: '6%'
        },
        xAxis: {},
        yAxis: {},
        geo: {
            map: 'world',
            show: true,
            roam: true,
            label: {
                emphasis: {
                    show: false
                }
            },
            itemStyle: {
                normal: {
                    areaColor: '#ffffff',
                    borderColor: '#1773c3',
                    shadowColor: '#ffffff',
                    shadowBlur: 1
                }
            },
            top: '16%',
            bottom: '5%'
        },
        series: [{
                name: '底圖',
                type: 'map',
                mapType: 'world',
                roam: true,
                top: '16%',
                bottom: '5%',
                label: {
                    normal: {
                        show: true,
                        formatter: '{c}',
                        color: 'rgba(220,220,220,1)'
                    },
                    emphasis: {
                        show: true
                    }
                },
                itemStyle: {
                    normal: {
                        areaColor: '#ffffff',
                        borderColor: '#1773c3',
                        shadowColor: '#ffffff',
                        shadowBlur: 20,
                        opacity: 0.6
                    }
                },
                data: []
            },
            {
                name: '前5',
                type: 'effectScatter',
                coordinateSystem: 'geo',
                data: [],
            },
            {
                id: 'bar',
                type: 'bar',
                data: []
            }
        ]
    },
    options: []
}


for (var i = 0; i < data.length; i++) {
    option.options.push({
        title: [{
                text: "全球新增確診新冠病例分佈 "+data[i].date
            },
            {
                id: 'top 10',
                text: 'Top 10',
                left: '6%',
                right: '70%',
                top: '60%',
                height: 'auto',
                bottom: '6%'
            }
        ],
        xAxis: {
            show: false,
            axisLine: {
                show: false,
                lineStyle: {
                    color: 'rgba(121,121,121,0.3)'
                    //color:'red'
                }
            },
            splitLine: {
                show: false
            }
        },
        yAxis: {
            type: 'category',
            axisTick: {
                show: false
            },
            axisLine: {
                show: false,
                lineStyle: {
                    color: 'rgba(121,121,121,0.3)'
                    //color:'red'
                }
            },
            axisLabel: {
                show: false,
                textStyle: {
                    //  color: '#ddd'
                }
            },
            data: dataTopN[i].data.map(function(ele) {
                return ele.name
            }).reverse()
        },
        geo: {
            map: 'world',
            show: true,
            roam: true,
            label: {
                emphasis: {
                    show: false
                }
            },
            itemStyle: {
                normal: {
                    areaColor: '#ffffff',
                    borderColor: '#1773c3',
                    shadowColor: '#ffffff',
                    shadowBlur: 20
                }
            }
        },
        series: [{
                label: {
                    normal: {
                        show: false,
                        formatter: '{c}'
                    },
                    emphasis: {
                        show: true
                    }
                },
                data: data[i].data
                //data:dataTopN[i].data
            },
            {
                name: '前10',
                type: 'effectScatter',
                coordinateSystem: 'geo',
                data: convertData2(data[i].data),
                // data: convertData(dataTopN[i].data),
                symbolSize: 1,
                // color: (238,25,27,1),
                showEffectOn: 'render',
                rippleEffect: {
                    brushType: 'stroke'
                },
                hoverAnimation: true,
                label: {
                    normal: {
                        formatter: function(p) {
                            return p.name + ' : ' + formatNum(p.value[2])
                        },
                        position: 'right',
                        show: true,
                        color: 'white',
                        fontSize: 15,
                        fontWeight: 'bold',
                        // textBorderColor: (0,0,0,1),
                        shadowColor: 'black',
                        shadowBlur: 10
                    }
                },
                // itemStyle: {
                //     normal: {
                //         color: '#f4e925',
                //         shadowBlur: 1,
                //         shadowColor: '#333'
                //     }
                // },
                zlevel: 1
            },
            {
                id: 'bar',
                itemStyle: {
                    normal: {
                        color: 'rgba(140, 140, 140, 0)',
                        label: {
                            show: true
                        },
                    }
                },
                label: {
                    normal: {
                        position: 'insideLeft',
                        formatter: function(p) {
                            return p.name + ' : ' + formatNum(Math.round(p.value))
                        },
                        color: '#6f3071'
               
                    }
                },
                data: dataTopN[i].data.map(function(ele) {
                    return ele.value
                }).reverse()//從大到小排列 2020-12-05
                //.sort(function(a, b) {
                //    return a > b
                //})
            }
        ]
    })
}

        // 使用剛指定的配置項和資料顯示圖表。
        myChart.setOption(option);
    </script>
</body>
</html>

其實通過全球新增新冠病例的趨勢發現,最早的暴發集中於北緯30°~北緯50°之間 [3]。

[1] Echarts. http://echarts.apache.org/zh/index.html

[2] Johns Hopkins University Center for Systems Science and Engineering (JHU CSSE). COVID-19 Data. https://github.com/CSSEGISandData/COVID-19/tree/master/csse_covid_19_data/csse_covid_19_time_series.

[3] Sajadi MM, Habibzadeh P, Vintzileos A, Shokouhi S, Miralles-Wilhelm F, Amoroso A. Temperature, Humidity, and Latitude Analysis to Estimate Potential Spread and Seasonality of Coronavirus Disease 2019 (COVID-19). JAMA Netw Open. 2020 Jun 1;3(6):e2011834. doi: 10.1001/jamanetworkopen.2020.11834. 

相關文章