由於種種原因,需要製作一個疫情專題頁面,而這個任務分配到了我頭上,對於第一次接觸echarts的我來說是一個巨大的挑戰。所以在家,邊查文檔,邊思考,磕磕碰碰地把地圖完成了。
首先,看一下效果:
用到的相關技術
- jquery
- echartsjs::https://www.echartsjs.com/zh/index.html
- 地圖數據:https://github.com/cj0x39e/echarts-map-data
- 疫情數據來源於各大權威網站,這裏不做過多敘述
echart地圖配置
1. 引入jquery
和echarts
<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"
};