echarts製作疫情地圖

由於種種原因,需要製作一個疫情專題頁面,而這個任務分配到了我頭上,對於第一次接觸echarts的我來說是一個巨大的挑戰。所以在家,邊查文檔,邊思考,磕磕碰碰地把地圖完成了。

首先,看一下效果:

用到的相關技術

echart地圖配置

1. 引入jqueryecharts
<script type="text/javascript" src="/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="/js/echarts.min.js"></script>
2. 設置地圖顯示區域以及加載地圖json文件

html代碼:

<style>
          .map-view{
            height:80px;
            width:90%;
            margin: 0 auto;
        }
</style>
<body>
...
  <div id="map-view" class="map-view" ></div>
...
</body>

加載json文件:

var myChart = echarts.init(document.getElementById('map-view'));
myChart.showLoading();

// 國內省份
var cityArr = ['beijing','北京'];
var cityNameList = []; // 地圖內城市名稱

$.get('/js/map/json/province/'+ cityArr[0]  +'.json', function (geoJson) {
            myChart.hideLoading();
            echarts.registerMap(cityArr[0], geoJson);
            cityNameList = geoJson.features.map(v=>{
                return v.properties.name;
            })
            
            // 添加南海諸島嶼的圖片
            if(cityArr[1] === "海南"){
                $("#map-view").append('<img class="hainan-extra" src="/images/hainan-extra.png" />')
            }

            myChart.setOption(option = {
                // 地圖具體配置
                ...
            })
})

上面是國內省份地圖,下面是世界地圖的寫法,當然json路徑需要示具體情況而定。

// 世界地圖
var cityArr = ['world','世界'];
var cityNameList = []; // 地圖內國家和地區名稱

$.get('/js/map/json/'+ cityArr[0]  +'.json', function (geoJson) {
           myChart.hideLoading();
            echarts.registerMap(cityArr[0], geoJson);
            cityNameList = geoJson.features.map(v=>{
                return v.properties.name;
            })

            myChart.setOption(option = {
                // 地圖具體配置
                ...
            })
})

注意:陝西和山西的拼音都是一樣的,所以陝西后面帶了個‘1’

{
  "陝西":"shanxi1",
  "山西":"shanxi"
}
3. 地圖配置

這裏只說明一下用到的,具體可以查看以下文章:

ECharts地圖詳解:https://blog.csdn.net/xieweikun7/article/details/52766676

echars options詳解:https://echarts.apache.org/zh/option.html#title

var options = {
  tooltip: { // 提示
    formatter:function(params,ticket, callback){
          if(!params.data){
                  return undefined; // 無數據時不彈出提示
           }else{
                  return params.name 
                      + '<br />確診:'+params.data.confirm 
                      + '<br />治癒:'+params.data.heal 
                      + '<br />死亡:'+params.data.dead
           }
     } ,// 數據格式化
    visualMap: {
        min: 0, // 最小值
        max: 0, // 最大值
        show: true,
        splitNumber: 5, // 色階切分
        icon: 'rect', // 例圖形狀
        itemWidth: 14,
        itemHeight: 14,
        itemGap: 2,
        textStyle: { // 例圖文本樣式
            color: '#A3A3A3',
            fontSize: 12,
        }
     }
  }
}

基本配置到這裏就結束了,然後有一些配置放在了數據加載的時候。

echarts 可以多次調用myChart.setOption(option),配置的參數將會合並在一起,已有的配置將會被覆蓋掉

4. 疫情數據加載以及詳細配置

4.1 給數據分等級

爲了更好地展示效果,以及解決人數爲0的時候單獨劃爲一個等級這個問題,所以給數據做了一下處理。

function fixNum(value){
    // 0-10 10-20 20-30 30-40   40-50       50-60
    // 0    1-9   10-99 100-999 1000-9999   >=10000
    if(value===0){
        return 0;
    }else if(value < 10){
        return 15;
    }else if(value < 100){
        return 25;
    }else if(value < 1000){
        return 35;
    }else if(value < 10000){
        return 45;
    }
    return 55;
}

世界地圖顯示等級是倒過來的,不過差別不大:

function fixNum(value){
    // 70-60  60-50  50-40  40-30     30-20      20-10      10-0
    // 0      1-9    10-99  100-599  500-999   1000-9999   >=10000
    if(value>=10000){
        return 0;
    }else if(value >= 1000){
        return 15;
    }else if(value >= 500){
        return 25;
    }else if(value >= 100){
        return 35;
    }else if(value >= 10){
        return 45;
    }else if(value >= 1){
        return 55;
    }
    return 65;
}

4.2 數據預處理

然後,就是對數據做預處理,主要是爲了方便配置。

這裏講一下國內省份地圖的處理。需要處理如下問題:

  • 由於省份地圖形狀和大小不一,尤其是海南省有諸島嶼,所以海南省地圖特別大。需要對地圖所適當縮放,以及位移;
  • 解決例圖與省份地圖重疊的問題,需要修正例圖在在各個省份的顯示位置;
  • 數據分等級,色階也需要根據具體情況劃分顯示階數;
  • 地圖數據和疫情數據中文市鎮州等地區名稱需要一一對應
function genData(items){

    var confirmData = items.map(v=>{
        // 數據分等級
        v.value = fixNum(parseInt(v.confirm))
        return v;
    });

    // 數據修正 
    // 中文省份的中文 市鎮州等地區 對應地圖的中文地區
    var max = 0;
    for(var i = 0; i < confirmData.length; i++){
        max = Math.max(max, confirmData[i].value+5);
        var name = confirmData[i].name
        .replace("州區","aa")
        .replace("州市","aa")
        .replace("自治州","")
        .replace("縣","").replace("州","").replace("區","").replace("市","")
        .replace("aa","州")

        if(name.length == 1){
            name = confirmData[i].name;
        }

        for(var j = 0; j < cityNameList.length; j++){
            if(cityNameList[j].indexOf(name) >= 0){
                confirmData[i].name = cityNameList[j];
                break;
            }
        }
    }

    // 色階
    var colors = [];
    var colors0 = ['#ffffff','#FFAA85','#FF7B69','#CC2929','#8C0d0d','#660208'];
    var splitNumber = 5;

    if(max <= 20){
        max = 20;
    }

    // 合理劃分色階,最多5層
    for(var i = 0; i*10 < max; i++){
        colors.push(colors0[i]);
    }

    // 省份名稱:例圖水平位置,例圖垂直位置,縮放比例
    var pos = {
      // 各省份具體配置,看下文配置
    };

    pos = pos[cityArr[1]];

    if(!pos){
        pos = ['left','bottom', 100];
    }

    return {
        confirmData: confirmData, // 疫情數據
        max: max, // 最大值
        splitNumber: max/10, // 色階階數
        color: colors, // 色階數組
        x: pos[0], // 例圖水平位置
        y: pos[1], // 例圖垂直位置
        // 縮放比例
        layoutSize: Math.floor((pos[2] || 350)*(Math.min($(window).width(), 675)||375)/375),
    }
}

上述代碼中pos的具體配置數據:

var pos = {
        "安徽":       ["right","top",380],
        "甘肅":       ["left","bottom", 320],
        "河北":       ["right","bottom", 380],
        "江蘇":       ["left","bottom"],
        "寧夏":       ["left","top", 400],
        "陝西":       ["left","top", 380],
        "新疆":       ["left","top", 336],
        "澳門":       ["left","bottom", 380],
        "廣東":       ["left","top", 330],
        "黑龍江":     ["right","top", 330],
        "江西":       ["right","bottom",380],
        "青海":       ["left","bottom", 330],
        "四川":       ["right","bottom", 330],
        "西藏":       ["right","top", 330],
        "北京":       ["left","top", 340],
        "廣西":       ["left","top", 325],
        "河南":       ["left","top",340],
        "吉林":       ["left","bottom", 330 ],
        "山東":       ["left","top", 325],
        "臺灣":       ["left","bottom", 350],
        "雲南":       ["left","bottom",390],
        "重慶":       ["left","top", 350],
        "貴州":       ["left","top", 330],
        "湖北":       ["right","top", 330],
        "遼寧":       ["left","top", 330],
        "上海":       ["left","top", 380],
        "天津":       ["left","top", 380],
        "浙江":       ["left","top", 360],
        "福建":       ["left","top", 390],
        "海南":       ["left","bottom", 2600],
        "湖南":       ["left","bottom", 390],
        "內蒙古":      ["left","top", 330],
        "山西":       ["left","top", 390],
        "香港":       ["left","top",320],
}

對於世界地圖來說,並不需要這麼多處理。地圖不需要縮放,例圖位置可以寫死。

  • 數據分等級,色階目前可以寫死爲6階;
  • 地圖數據和疫情數據國家和地區中英文名稱需要一一對應

首先色階如下:

  // 色階
  var colors = ['#A34830', '#CD503B','#E6664D','#F1813A','#FFC551','#FFF6B3', '#EFEFEF'];

國家中英文對照,這裏需要特別聲明一下,部分國家和地區數據可能有誤,如果有發現的小夥伴可以在評論區反饋一下。

// 由於數據太多,這裏將數據放在最底下。
// 部分國家和地區有重複出現,但並不影響使用。

同理也需要對疫情數據國家和地區的名稱做一下處理。

for(var i = 0; i < confirmData.length; i++){
    var name = confirmData[i].name // 中文需要轉英文
    var cname = name;
    name = cityMap[name];

    if(!name){
        continue;
    }

    // var flag = false
    for(var j = 0; j < cityNameList.length; j++){
        if(cityNameList[j] === name){
            confirmData[i].name = cityNameList[j];
            confirmData[i].cname = cname;
            flag = true
            break;
        }
    }
    // !flag && console.log("=========地圖上沒有這個國家:", name);
}

4.3 數據加載

好了,到這裏,需要將所有方法組織起來。

// data:疫情數據 locCity:當前所在地區
function handleConfirmData(data, locCity){
    // 數據預處理
    var fmtedData = genData(data);
    // 當前所在地區
    var selectedIndex = -1; 

    // 數據和配置
    var series = [{
        name: cityArr[0],
        type: 'map',
        map: cityArr[0], // 需要與加載省份地圖設置的名稱一致
        label: { // 地區區域文本
            normal: {
                show: false // 正常情況下不顯示文字信息
            },
            emphasis: { // 點擊地區樣式
                show: true,
                color: '#000', // 區域顏色
                fontSize: 10, // 區域
                align: 'center',
                baseline: 'middle'
            }
        },
        // 單選,設置默認提示區域時,需要設置這一項爲single
        selectedMode: 'single',

        layoutCenter: [ '50%', '50%' ], // 位置
        layoutSize : fmtedData.layoutSize, // 縮放

        data: fmtedData.confirmData // 數據
    }]

    // 海南省份的數據需要做一下位移
    if(cityArr[1] === "海南" ){
        if($(window).width() > 750){
            series[0].layoutCenter = ['156%', '300%']
        }else{
            series[0].layoutCenter = ['170%', '330%']
        }
    }

    // 獲取選擇城市數據的索引
    var flag = true;
    if(locCity && fmtedData.length){
        for(var j = 0; j < fmtedData.length; j++){
            if(locCity.indexOf(fmtedData[j].name) >= 0){
                selectedIndex = j;
                flag = false;
                break;
            }
        }
    }

    // 當前省的數據
    myChart && myChart.setOption(option = {
        visualMap: {
            max: fmtedData.max, // 最大值
            show: true, // 顯示例圖
            splitNumber: fmtedData.splitNumber, // 顏色分階-階數
            inRange: {
                color: fmtedData.color // 顏色組
            },
            formatter: function(left){ // 例圖項文本顯示
                return {
                    "0": "0",
                    "10": "1-9",
                    "20": "10-99",
                    "30": "100-999",
                    "40": "1000-9999",
                    "50": "≥10000"
                }[left];
            },
            x: fmtedData.x,
            y: fmtedData.y,
        },
        series: series, // 處理後的疫情數據
    });

    if(myChart && selectedIndex >= 0){
        setTimeout(function(){
            // 當前地區 彈出提示
            confirmMap.dispatchAction({
                type: 'showTip',
                seriesIndex:0,  // 顯示第幾個series
                dataIndex: selectedIndex // 顯示第幾個數據
            });
            
            // 當前地區 選中當前地區
            confirmMap.dispatchAction({
                type:'mapSelect',
                seriesIndex: 0,
                dataIndex:selectedIndex
            });
        });

    }
}

南海諸島嶼顯示問題

關於海南省的地圖顯示,由於南海省包含了南海諸島嶼,所以顯示的時候需要將南海諸島單獨提取出來顯示。這個問題,困擾了我好久,最後借鑑某網站解決了這個問題。

解決方法就是在canvas上層以圖片的形式放置南海諸島。

// 添加南海諸島嶼的圖片
if(cityArr[1] === "海南"){
    $("#map-view").append('<img class="hainan-extra" src="/images/hainan-extra.png" />')
}

樣式:

.hainan-extra{
    width: 3rem;
    position: absolute;
    bottom: 1rem;
    right: 1rem;
    z-index: 1;
}

好了,到這裏就全部結束了。寫這份代碼頗爲費勁,所以在這裏總結一下。目前源碼包含有其他業務代碼,不方面展示,如有需要源碼的夥伴,只能說聲抱歉,請參照上述代碼自行進行修改。

國家和地區中英文對照

var cityMap = {
    "阿富汗": "Afghanistan",
    "新加坡": "Singapore",
    "安哥拉": "Angola",
    "阿爾巴尼亞": "Albania",
    "阿聯酋": "United Arab Emirates",
    "阿根廷": "Argentina",
    "亞美尼亞": "Armenia",
    "法屬南半球和南極領地": "French Southern and Antarctic Lands",
    "澳大利亞": "Australia",
    "奧地利": "Austria",
    "阿塞拜疆": "Azerbaijan",
    "布隆迪": "Burundi",
    "比利時": "Belgium",
    "貝寧": "Benin",
    "布基納法索": "Burkina Faso",
    "孟加拉國": "Bangladesh",
    "保加利亞": "Bulgaria",
    "巴哈馬": "Bahamas",
    "波斯尼亞和黑塞哥維那": "Bosnia and Herz.",
    "波黑": "Bosnia and Herz.",
    "白俄羅斯": "Belarus",
    "伯利茲": "Belize",
    "百慕大": "Bermuda",
    "玻利維亞": "Bolivia",
    "巴西": "Brazil",
    "文萊": "Brunei",
    "不丹": "Bhutan",
    "博茨瓦納": "Botswana",
    "中非共和國": "Central African Rep.",
    "加拿大": "Canada",
    "瑞士": "Switzerland",
    "智利": "Chile",
    "中國": "China",
    "象牙海岸": "Côte d'Ivoire",
    "喀麥隆": "Cameroon",
    "剛果民主共和國": "Dem. Rep. Congo",
    "剛果(金)": "Dem. Rep. Congo",
    "剛果(布)": "Congo",
    "剛果共和國": "Congo",
    "剛果": "Congo",
    "哥倫比亞": "Colombia",
    "哥斯達黎加": "Costa Rica",
    "古巴": "Cuba",
    "北塞浦路斯": "Northern Cyprus",
    "塞浦路斯": "Cyprus",
    "捷克共和國": "Czech Rep.",
    "捷克": "Czech Rep.",
    "德國": "Germany",
    "吉布提": "Djibouti",
    "丹麥": "Denmark",
    "阿爾及利亞": "Algeria",
    "厄瓜多爾": "Ecuador",
    "埃及": "Egypt",
    "厄立特里亞": "Eritrea",
    "西班牙": "Spain",
    "愛沙尼亞": "Estonia",
    "埃塞俄比亞": "Ethiopia",
    "芬蘭": "Finland",
    "斐": "Fiji",
    "福克蘭羣島": "Falkland Is.",
    "法國": "France",
    "加蓬": "Gabon",
    "英國": "United Kingdom",
    "格魯吉亞": "Georgia",
    "加納": "Ghana",
    "岡比亞": "Gambia",
    "幾內亞": "Guinea",
    "幾內亞比紹": "Guinea-Bissau",
    "赤道幾內亞": "Eq. Guinea",
    "希臘": "Greece",
    "格陵蘭": "Greenland",
    "危地馬拉": "Guatemala",
    "法屬圭亞那": "Fr. S. Antarctic Lands",
    "圭亞那": "Guyana",
    "洪都拉斯": "Honduras",
    "克羅地亞": "Croatia",
    "海地": "Haiti",
    "匈牙利": "Hungary",
    "印尼": "Indonesia",
    "印度": "India",
    "愛爾蘭": "Ireland",
    "伊朗": "Iran",
    "伊拉克": "Iraq",
    "冰島": "Iceland",
    "以色列": "Israel",
    "意大利": "Italy",
    "牙買加": "Jamaica",
    "約旦": "Jordan",
    "日本": "Japan",
    "日本本土": "Japan",
    "哈薩克斯坦": "Kazakhstan",
    "肯尼亞": "Kenya",
    "吉爾吉斯斯坦": "Kyrgyzstan",
    "柬埔寨": "Cambodia",
    "韓國": "Korea",
    "朝鮮": "Dem. Rep. Korea",
    "北朝鮮": "Dem. Rep. Korea",
    "科索沃": "Kosovo",
    "科威特": "Kuwait",
    "老撾": "Lao PDR",
    "黎巴嫩": "Lebanon",
    "利比里亞": "Liberia",
    "利比亞": "Libya",
    "斯里蘭卡": "Sri Lanka",
    "萊索托": "Lesotho",
    "立陶宛": "Lithuania",
    "盧森堡": "Luxembourg",
    "拉脫維亞": "Latvia",
    "摩洛哥": "Morocco",
    "摩爾多瓦": "Moldova",
    "馬達加斯加": "Madagascar",
    "墨西哥": "Mexico",
    "馬其頓": "Macedonia",
    "北馬其頓": "Macedonia",
    "馬裏": "Mali",
    "緬甸": "Myanmar",
    "黑山": "Montenegro",
    "蒙古": "Mongolia",
    "莫桑比克": "Mozambique",
    "毛里塔尼亞": "Mauritania",
    "馬拉維": "Malawi",
    "馬來西亞": "Malaysia",
    "納米比亞": "Namibia",
    "新喀里多尼亞": "New Caledonia",
    "尼日爾": "Niger",
    "尼日利亞": "Nigeria",
    "尼加拉瓜": "Nicaragua",
    "荷蘭": "Netherlands",
    "挪威": "Norway",
    "尼泊爾": "Nepal",
    "新西蘭": "New Zealand",
    "阿曼": "Oman",
    "巴基斯坦": "Pakistan",
    "巴拿馬": "Panama",
    "祕魯": "Peru",
    "菲律賓": "Philippines",
    "巴布亞新幾內亞": "Papua New Guinea",
    "波蘭": "Poland",
    "波多黎各": "Puerto Rico",
    "葡萄牙": "Portugal",
    "巴拉圭": "Paraguay",
    "卡塔爾": "Qatar",
    "羅馬尼亞": "Romania",
    "俄羅斯": "Russia",
    "盧旺達": "Rwanda",
    "西撒哈拉": "W. Sahara",
    "沙特阿拉伯": "Saudi Arabia",
    "蘇丹": "Sudan",
    "南蘇丹": "S. Sudan",
    "塞內加爾": "Senegal",
    "所羅門羣島": "Solomon Is.",
    "塞拉利昂": "Sierra Leone",
    "薩爾瓦多": "El Salvador",
    "索馬里蘭": "Somaliland",
    "索馬里": "Somalia",
    "塞爾維亞": "Serbia",
    "蘇里南": "Suriname",
    "斯洛伐克": "Slovakia",
    "斯洛文尼亞": "Slovenia",
    "瑞典": "Sweden",
    "斯威士蘭": "Swaziland",
    "敘利亞": "Syria",
    "乍得": "Chad",
    "多哥": "Togo",
    "泰國": "Thailand",
    "塔吉克斯坦": "Tajikistan",
    "土庫曼斯坦": "Turkmenistan",
    "東帝汶": "Timor-Leste",
    "特里尼達和多巴哥": "Trinidad and Tobago",
    "突尼斯": "Tunisia",
    "土耳其": "Turkey",
    "坦桑尼亞": "Tanzania",
    "烏干達": "Uganda",
    "烏克蘭": "Ukraine",
    "烏拉圭": "Uruguay",
    "美國": "United States",
    "烏茲別克斯坦": "Uzbekistan",
    "委內瑞拉": "Venezuela",
    "越南": "Vietnam",
    "瓦努阿圖": "Vanuatu",
    "西岸": "West Bank",
    "也門": "Yemen",
    "南非": "South Africa",
    "贊比亞": "Zambia",
    "津巴布韋": "Zimbabwe",
    "印度尼西亞": "Indonesia",
    "奧蘭羣島": "Aland",
    "美屬薩摩亞": "American Samoa",
    "安道爾": "Andorra",
    "安圭拉": "Anguilla",
    "安提瓜和巴布達": "Antigua and Barb.",
    "阿魯巴": "Aruba",
    "孟加拉": "Bangladesh",
    "巴林": "Bahrain",
    "巴巴多斯": "Barbados",
    "布維島": "Bouvet Island",
    "佛得角": "Cape Verde",
    "中非": "Central African Rep.",
    "聖誕島": "Christmas Islands",
    "科科斯(基林)羣島": "Cocos (keeling) Islands",
    "科摩羅": "Comoros",
    "庫克羣島": "Cook Islands",
    "科特迪瓦": "Côte d'Ivoire",
    "多米尼加": "Dominica",
    "多米尼克": "Dominica",
    "多明尼加共和國": "Dominican Rep.",
    "法羅羣島": "Faeroe Is.",
    "斐濟": "Fiji",
    "法國大都會": "MetropolitanFrance",
    "法屬波利尼西亞": "French Polynesia",
    "直布羅陀": "Gibraltar",
    "格林納達": "Grenada",
    "瓜德羅普島": "Fr. S. Antarctic Lands",
    "法屬瓜德羅普島": "Fr. S. Antarctic Lands",
    "關島": "Guam",
    "根西島": "Guernsey",
    "馬恩島": "Isle of Man",
    "澤西島": "Jersey",
    "基里巴斯": "Kiribati",
    "列支敦士登": "Liechtenstein",
    "列支敦士登公國": "Liechtenstein",
    "馬爾代夫": "Maldives",
    "馬耳他": "Malta",
    "馬紹爾羣島": "Marshall Islands",
    "馬提尼克島": "Fr. S. Antarctic Lands",
    "毛里求斯": "Mauritius",
    "馬約特": "Fr. S. Antarctic Lands",
    "馬約特島": "Fr. S. Antarctic Lands",
    "密克羅尼西亞": "Micronesia",
    "摩納哥": "Monaco",
    "蒙特塞拉特": "Montserrat",
    "瑙魯": "Nauru",
    "紐埃": "Niue",
    "諾福克島": "Norfolk Island",
    "帕勞": "Palau",
    "巴勒斯坦": "Palestine",
    "皮特凱恩羣島": "Pitcairn Islands",
    "留尼汪島": "Fr. S. Antarctic Lands",
    "留尼汪": "Fr. S. Antarctic Lands",
    "俄羅斯聯邦": "Russian Federation",

    "聖赫勒拿": "Saint Helena",
    "聖盧西亞": "Saint Lucia",
    "聖基茨和尼維斯": "Saint Kitts-Nevis",
    "聖文森特和格林納丁斯": "St. Vin. and Gren.",

    "薩摩亞": "Samoa",
    "聖馬力諾": "San Marino",
    "聖多美和普林西比": "Sao Tome and Principe",
    "塞舌爾": "Seychelles",
    "特立尼達和多巴哥": "Trinidad and Tobago",
    "托克勞": "Tokelau",
    "湯加": "Tonga",
    "圖瓦盧": "Tuvalu",
    "阿拉伯聯合酋長國": "United Arab Emirates",
    "梵蒂岡": "Vatican City",
    "瓦利斯羣島和富圖納羣島": "Wallis and Futuna",
    "南斯拉夫": "Yugoslavia"
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章