快速實現地圖遷移資料視覺化

愚坤發表於2018-12-19

這兩天接到一個資料視覺化的需求,要求展示當前選擇的省、市到其他省市的產品遷徙資料,需求並不難,而且地圖的視覺化也比較成熟,echarts也有對應的例子,我們先看下預覽圖。

預覽圖:

快速實現地圖遷移資料視覺化

簡介

為什麼要這篇筆記呢,有兩個原因:

  1. 防止下次再去找這些配置項,自己以前用過echarts,時間久了,導致接到echart的需求後,以前一些有關聯的配置項沒有記錄,就需要重新過一遍api或者google百度,很浪費時間。
  2. 大資料技術越來越多,用到地圖視覺化的效果也越來越多,把做地圖遷徙效果的思路寫一下,防止走彎路,如果大家看完後哪怕能提高十分鐘或者五分鐘的效率,這個筆記也就有一丟丟的價值了。

需求描述

我們的需求如下

    1. 預設進去後地圖展示各個省的遷出量
    1. 選中某一個省後展示當前省到其他省的遷出量
    1. 選中省的某一個市後,展示當前市到其他省的遷出量
    1. 點選到其他省後,彈出柱狀圖顯示當前城市到省會下所有城市的遷出量。

需求確定後,首先後端夥伴就是要資料格式,我們先要知道自己需要哪些資料,才能給到後端夥伴的格式。

開發步驟

1. 建立地理座標系

建立div、樣式這部分略過,然後建立geo地理座標系元件,地理座標系元件什麼意思呢?官網這麼說的,

地理座標系元件用於地圖的繪製,支援在地理座標系上繪製散點圖,線集。

官網文件,也就是我們可以在這個元件繪散點圖和線圖

官網暫時不提供地圖下載了,說部分資料不符合國家《測繪法》規定,API例子裡拔了一下china.js檔案,就是地圖的資料檔案,可以用,先看下效果。

var mapOption = {
	// 地圖示題
    title : {
        text: 'XX城S端車源流向',
        subtext: '日期:2018-10-11',
        left: 'left',
        textStyle : {
            color: '#fff',
            fontSize:30
        },
        top:20,
        left:20
    },
    // 插入地圖座標系元件
    geo: {
        // 地圖座標系資料
        map: 'china',
        show:true,
        itemStyle: {
        // 背景色
        	areaColor: '#000000',
            emphasis: {
            	label:{
            		show:false
            	}
            }
        }
    }
};

// 建立例項
myChart = echarts.init(document.getElementById('map'));
// 設定資料
myChart.setOption(mapOption);
複製程式碼

快速實現地圖遷移資料視覺化

2. 建立地圖系列色塊

接下來我們再畫一下地圖根據數值來顯示不同的顏色,這塊大家可能會有點疑惑,怎麼還畫地圖,不是已經出來了嗎?

是這樣,echart有區分元件和系列,元件有很多,比如title標題元件、legend圖例元件、xAxis資料軸元件、tooltip提示框元件等等,其中就有我們剛才上面提到的地理座標系元件系列官網是這麼解釋的是

系列列表。每個系列通過 type 決定自己的圖表型別

官網文件

系列資料放在series配置下,可以放置多種型別,折線/面積圖、柱狀/條形圖、餅圖、散點圖等。

這塊剛接觸的同學可能不太好理解,舉個例子,座標元件就相當於超市的貨架,echarts提供多種貨架,有口香糖貨架,廚房用品貨架、文具用品貨架,而貨架上方的東西雖然都是一類,但是有區分品牌,比如口香糖有綠箭、益達、炫邁等,品牌就是系列。

地理座標系元件相當於口香糖貨架,綠箭、益達、炫邁相當於地圖系列、散點系列、線圖系列。

系列資料放在serise屬性裡,我們先建立map地圖系列,根據value值大小展示不同顏色,一般設計稿不會用外掛預設的主題,配置主題色要用到visualMap視覺對映元件並設定其下的inRange屬性,想把資料分成幾段就設定幾個就可以了,**但是必須設定max屬性最大值,**程式碼如下。

// 增加地圖系列
mapOption.series.push({
    name: '地圖色塊',
    // 系列型別
    type: 'map',
    mapType: 'china',
    // 禁用縮放
    roam: false,
    // 隱藏label
    label: {
        normal: {
            show: false
        },
        emphasis: {
            show: false
        }
    },
    // 禁用提示框
    tooltip:{
        show:false,
    },
    itemStyle:{
        // 預設顏色樣式
        areaColor:'#0b1835',
        borderWidth:2,
        borderColor:'#244388',
        opacity:1
    },
    data:[
            {name: "山東",value: 541},
            {name: "山東",value: 341},
            {name: "西藏",value: 405},
            {name: "浙江",value: 380},
            {name: "廣東",value: 371},
            {name: "貴州",value: 364},
            {name: "四川",value: 287},
            {name: "河南",value: 251},
            {name: "上海",value: 218},
            {name: "山西",value: 206}
        ]
    }
});

// 增加視覺對映元件
mapOption.visualMap.push({
    {
		type:'piecewise',
		// 不顯示元件
		show:false,
		// 最大值 MaxArr為data重抽出的value陣列
		max:Math.max.apply(null,MaxArr),
        // 僅對mapOption.series陣列內下標為index系列資料生效
		seriesIndex:0,
		bottom:20,
		left:30,
		textStyle:{
			color:'#fff'
		},
		// 顏色值
		inRange: {
        	color: ['#5c8eb1', '#0574ab','#055c9b']
      }
	}
})

複製程式碼

快速實現地圖遷移資料視覺化

3. 建立散點系列動畫

設定選中省到其他省的遷徙效果,遷徙效果需要用三個系列組合,才能出現遷徙效果。

    1. 散點,在各個省會位置顯示散點動畫
    1. 實線,從當前城市指向目標城市帶箭頭的曲線。
    1. 特效線,從當前城市指向目標城市帶箭頭的曲線。

散點繪製需要座標,網上只找到了城市的座標,沒有省的,我自己把省會城市複製了一遍,然後改為省名稱,省坐與省會城市一致,程式碼如下:

// 省會與城市座標
var geoCoordMap = {
    "海門":[121.15,31.89],
    "鄂爾多斯":[109.781327,39.608266],
    "招遠":[120.38,37.35],
    "舟山":[122.207216,29.985295],
    "齊齊哈爾":[123.97,47.33],
    "鹽城":[120.13,33.38],
    "赤峰":[118.87,42.28],
    "青島":[120.33,36.07],
    "乳山":[121.52,36.89],
    "金昌":[102.188043,38.520089],
    "泉州":[118.58,24.93],
    "萊西":[120.53,36.86],
    "日照":[119.46,35.42],
    "膠南":[119.97,35.88],
    "南通":[121.05,32.08],
    "西藏":[91.11,29.97],
    "拉薩":[91.11,29.97],
    "雲浮":[112.02,22.93],
    "梅州":[116.1,24.55],
    "文登":[122.05,37.2],
    "上海":[121.48,31.22],
    "上海":[121.48,31.22],
    "攀枝花":[101.718637,26.582347],
    "威海":[122.1,37.5],
    "承德":[117.93,40.97],
    "廈門":[118.1,24.46],
    "汕尾":[115.375279,22.786211],
    "潮州":[116.63,23.68],
    "丹東":[124.37,40.13],
    "太倉":[121.1,31.45],
    "曲靖":[103.79,25.51],
    "煙臺":[121.39,37.52],
    "福建":[119.3,26.08],
    "福州":[119.3,26.08],
    "瓦房店":[121.979603,39.627114],
    "即墨":[120.45,36.38],
    "撫順":[123.97,41.97],
    "玉溪":[102.52,24.35],
    "張家口":[114.87,40.82],
    "陽泉":[113.57,37.85],
    "萊州":[119.942327,37.177017],
    "湖州":[120.1,30.86],
    "汕頭":[116.69,23.39],
    "崑山":[120.95,31.39],
    "寧波":[121.56,29.86],
    "湛江":[110.359377,21.270708],
    "揭陽":[116.35,23.55],
    "榮成":[122.41,37.16],
    "連雲港":[119.16,34.59],
    "葫蘆島":[120.836932,40.711052],
    "常熟":[120.74,31.64],
    "東莞":[113.75,23.04],
    "河源":[114.68,23.73],
    "淮安":[119.15,33.5],
    "泰州":[119.9,32.49],
    "南寧":[108.33,22.84],
    "營口":[122.18,40.65],
    "惠州":[114.4,23.09],
    "江陰":[120.26,31.91],
    "蓬萊":[120.75,37.8],
    "韶關":[113.62,24.84],
    "嘉峪關":[98.289152,39.77313],
    "廣東":[113.23,23.16],
    "廣州":[113.23,23.16],
    "延安":[109.47,36.6],
    "山西":[112.53,37.87],
    "太原":[112.53,37.87],
    "清遠":[113.01,23.7],
    "中山":[113.38,22.52],
    "雲南":[102.73,25.04],
    "昆明":[102.73,25.04],
    "壽光":[118.73,36.86],
    "盤錦":[122.070714,41.119997],
    "長治":[113.08,36.18],
    "深圳":[114.07,22.62],
    "珠海":[113.52,22.3],
    "宿遷":[118.3,33.96],
    "咸陽":[108.72,34.36],
    "銅川":[109.11,35.09],
    "平度":[119.97,36.77],
    "佛山":[113.11,23.05],
    "海南":[110.35,20.02],
    "海口":[110.35,20.02],
    "江門":[113.06,22.61],
    "章丘":[117.53,36.72],
    "肇慶":[112.44,23.05],
    "大連":[121.62,38.92],
    "臨汾":[111.5,36.08],
    "吳江":[120.63,31.16],
    "石嘴山":[106.39,39.04],
    "遼寧":[123.38,41.8],
    "瀋陽":[123.38,41.8],
    "蘇州":[120.62,31.32],
    "茂名":[110.88,21.68],
    "嘉興":[120.76,30.77],
    "吉林":[125.35,43.88],
    "長春":[125.35,43.88],
    "膠州":[120.03336,36.264622],
    "寧夏":[106.27,38.47],
    "銀川":[106.27,38.47],
    "張家港":[120.555821,31.875428],
    "三門峽":[111.19,34.76],
    "錦州":[121.15,41.13],
    "江西":[115.89,28.68],
    "南昌":[115.89,28.68],
    "柳州":[109.4,24.33],
    "三亞":[109.511909,18.252847],
    "自貢":[104.778442,29.33903],
    "吉林":[126.57,43.87],
    "陽江":[111.95,21.85],
    "瀘州":[105.39,28.91],
    "西寧":[101.74,36.56],
    "青海":[101.74,36.56],
    "宜賓":[104.56,29.77],
    "內蒙古":[111.65,40.82],
    "呼和浩特":[111.65,40.82],
    "四川":[104.06,30.67],
    "成都":[104.06,30.67],
    "大同":[113.3,40.12],
    "江蘇":[119.44,32.2],
    "鎮江":[119.44,32.2],
    "廣西":[110.28,25.29],
    "桂林":[110.28,25.29],
    "張家界":[110.479191,29.117096],
    "宜興":[119.82,31.36],
    "北海":[109.12,21.49],
    "陝西":[108.95,34.27],
    "西安":[108.95,34.27],
    "金壇":[119.56,31.74],
    "東營":[118.49,37.46],
    "牡丹江":[129.58,44.6],
    "遵義":[106.9,27.7],
    "紹興":[120.58,30.01],
    "揚州":[119.42,32.39],
    "常州":[119.95,31.79],
    "濰坊":[119.1,36.62],
    "重慶":[106.54,29.59],
    "重慶":[106.54,29.59],
    "台州":[121.420757,28.656386],
    "江蘇":[118.78,32.04],
    "南京":[118.78,32.04],
    "濱州":[118.03,37.36],
    "貴州":[106.71,26.57],
    "貴陽":[106.71,26.57],
    "無錫":[120.29,31.59],
    "本溪":[123.73,41.3],
    "克拉瑪依":[84.77,45.59],
    "渭南":[109.5,34.52],
    "馬鞍山":[118.48,31.56],
    "寶雞":[107.15,34.38],
    "焦作":[113.21,35.24],
    "句容":[119.16,31.95],
    "北京":[116.46,39.92],
    "北京":[116.46,39.92],
    "徐州":[117.2,34.26],
    "衡水":[115.72,37.72],
    "包頭":[110,40.58],
    "綿陽":[104.73,31.48],
    "新疆":[87.68,43.77],
    "烏魯木齊":[87.68,43.77],
    "棗莊":[117.57,34.86],
    "浙江":[120.19,30.26],
    "杭州":[120.19,30.26],
    "淄博":[118.05,36.78],
    "鞍山":[122.85,41.12],
    "溧陽":[119.48,31.43],
    "庫爾勒":[86.06,41.68],
    "安陽":[114.35,36.1],
    "開封":[114.35,34.79],
    "山東":[117,36.65],
    "濟南":[117,36.65],
    "德陽":[104.37,31.13],
    "溫州":[120.65,28.01],
    "九江":[115.97,29.71],
    "邯鄲":[114.47,36.6],
    "臨安":[119.72,30.23],
    "甘肅":[103.73,36.03],
    "蘭州":[103.73,36.03],
    "滄州":[116.83,38.33],
    "臨沂":[118.35,35.05],
    "南充":[106.110698,30.837793],
    "天津":[117.2,39.13],
    "天津":[117.2,39.13],
    "富陽":[119.95,30.07],
    "泰安":[117.13,36.18],
    "諸暨":[120.23,29.71],
    "河南":[113.65,34.76],
    "鄭州":[113.65,34.76],
    "黑龍江":[126.63,45.75],
    "哈爾濱":[126.63,45.75],
    "聊城":[115.97,36.45],
    "蕪湖":[118.38,31.33],
    "唐山":[118.02,39.63],
    "平頂山":[113.29,33.75],
    "邢臺":[114.48,37.05],
    "德州":[116.29,37.45],
    "濟寧":[116.59,35.38],
    "荊州":[112.239741,30.335165],
    "宜昌":[111.3,30.7],
    "義烏":[120.06,29.32],
    "麗水":[119.92,28.45],
    "洛陽":[112.44,34.7],
    "秦皇島":[119.57,39.95],
    "株洲":[113.16,27.83],
    "河北":[114.48,38.03],
    "石家莊":[114.48,38.03],
    "萊蕪":[117.67,36.19],
    "常德":[111.69,29.05],
    "保定":[115.48,38.85],
    "湘潭":[112.91,27.87],
    "金華":[119.64,29.12],
    "岳陽":[113.09,29.37],
    "湖南":[113,28.21],
    "長沙":[113,28.21],
    "衢州":[118.88,28.97],
    "廊坊":[116.7,39.53],
    "菏澤":[115.480656,35.23375],
    "安徽":[117.27,31.86],
    "合肥":[117.27,31.86],
    "武漢":[114.31,30.52],
    "湖北":[114.31,30.52],
    "大慶":[125.03,46.58]
};
複製程式碼

echart要求的散點系列要求的資料格式如下:

{
    "name":"安徽",
    "value":220,
    "tooltip":"遷出人效:0.22臺\/人<br>總遷出量:220<br>總遷入量:149"
}
複製程式碼

需要省會名和value,使用symbolSize回撥根據value設定散點大小,預設圖示科技感不強,用symbol設定成自己做的png圖片,然後在label的formatter方法設定一下要顯示的label欄位。

// 資料格式
var pan = [
    {"name":"安徽","value":220,"tooltip":"遷出人效:0.22臺\/人<br>總遷出量:220<br>總遷入量:149"},
	{"name":"山東","value":24,"tooltip":"超值車數量:12<br>非超值數量:12<br>總數量:24"},
	{"name":"浙江","value":22,"tooltip":"超值車數量:17<br>非超值數量:5<br>總數量:22"},
	{"name":"廣東","value":22,"tooltip":"超值車數量:14<br>非超值數量:8<br>總數量:22"},
	{"name":"西藏","value":15,"tooltip":"超值車數量:14<br>非超值數量:1<br>總數量:15"},
	{"name":"四川","value":14,"tooltip":"超值車數量:10<br>非超值數量:4<br>總數量:14"},
	{"name":"河南","value":14,"tooltip":"超值車數量:10<br>非超值數量:4<br>總數量:14"},
	{"name":"河北","value":9,"tooltip":"超值車數量:5<br>非超值數量:4<br>總數量:9"},
	{"name":"江西","value":8,"tooltip":"超值車數量:5<br>非超值數量:3<br>總數量:8"},
	{"name":"湖北","value":8,"tooltip":"超值車數量:7<br>非超值數量:1<br>總數量:8"},
	{"name":"陝西","value":8,"tooltip":"超值車數量:5<br>非超值數量:3<br>總數量:8"}
];

// 新增系列資料
mapOption.series.push(		
    {
        name: '圓盤',
        type: 'effectScatter',
        coordinateSystem: 'geo',
        zlevel: 0,
        // 散點動畫
        rippleEffect: {
        	 scale:3,
            brushType: 'stroke',
        },
        // 散點圖示
        symbol:'image://images/symbol1.png',
        // 顯示文字
        label: {
            normal: {
                show: true,
                position: 'right',
                color:'#fff',
                fontSize:10,
                // 會名稱 + value
                formatter: function(e){
                    return e.name +':'+ e.data.value[2]
                },
                opacity:1
            },
        },
        // 根據value控制散點大小
        symbolSize: function (val) {
            return val[2] / 25;
        },
        itemStyle: {
        	 borderWidth:1,
        	 opacity:1,
        	 shadowColor: '#fff',
        },
        data: pan.map(function (dataItem) {
            return {
                name: dataItem.name,
                value:geoCoordMap[dataItem.name].concat([dataItem.value]),
                tooltip:dataItem.name + '<br>' +dataItem.tooltip
            };
        })
    }
)
複製程式碼

快速實現地圖遷移資料視覺化

4. 建立線條系列實線與特效線

一條為實線,一條帶動畫的特效線,疊加在一起,效果就出來了。

echarts要求的lines系列資料格式如下

// linses
{
    fromName: "安徽", 
    toName: "浙江", 
    coords: [
        [117.27, 31.86], // 起點座標
        [120.19, 30.26]  // 末尾座標
    ]
}
複製程式碼

**這樣來看我們就需要當前省的座標和目標省座標,**我們之前已經準備好座標資料了,等於介面返回給我們當前省名稱和目標省名稱,我們自己再遍歷取一下座標就可以了,使用symbolSize調整剪頭合適的大小。

畫線的時候顏色需要注意下,實線深色,動畫線淺色,這樣有對比,效果要稍微好一些。

我們要求得到的資料如下:

var linesData = [
    [{"name":"安徽"},{"name":"浙江"}],
    [{"name":"安徽"},{"name":"上海"],
    [{"name":"安徽"},{"name":"西藏"],
    [{"name":"安徽"},{"name":"四川"],
    [{"name":"安徽"},{"name":"河南"],
    [{"name":"安徽"},{"name":"山西"],
    [{"name":"安徽"},{"name":"廣東"],
    [{"name":"安徽"},{"name":"山東"],
    [{"name":"安徽"},{"name":"貴州"]
]
複製程式碼

遍歷資料獲取座標,把資料塞進系列裡

// lines資料遍歷方法
var convertData = function (data) {
    var res = [];
    for (var i = 0; i < data.length; i++) {
        var dataItem = data[i];
        var fromCoord = geoCoordMap[dataItem[0].name];
        var toCoord = geoCoordMap[dataItem[1].name];
        if (fromCoord && toCoord) {
            res.push({
                fromName: dataItem[0].name,
                toName: dataItem[1].name,
                coords: [fromCoord, toCoord],
            });
        }
    }
    return res;
};	

// 塞入資料
mapOption.series.push(
    // step3 實體線
    {
        name: '實體線',
        type: 'lines',
        zlevel: 1,
        symbol: ['none', 'arrow'],
        symbolSize: 5,
        lineStyle: {
        	opacity: 0.1,
            normal: {
                color: '#67035a',
                width: 1,
                curveness: 0.2
            }
        },
        tooltip:{
        	show:false,
        },
        data: convertData(linesData)
    },

    // step4 發光線
    {
        name: '發光',
        type: 'lines',
        zlevel: 2,
        // 特效配置
        effect: {
            show: true,
            period: 7,
            trailLength: 0.7,
            color: '#ff69ec',
            symbolSize: 3
        },
        tooltip:{
        	show:false,
        },
        lineStyle: {
            normal: {
                color: '#fbff82',
                width: 0,
                curveness: 0.2
            }
        },
        data: convertData(linesData)
    }
)
複製程式碼

快速實現地圖遷移資料視覺化

效果已經出來了。

5. 當前城市到目標省城,點選目標省彈出柱狀圖

城市到省就更簡單了,把省的名稱換成城市就好了,不再贅述。

簡單說一下點選目標省出現當前省下所有城市的柱狀圖的思路。

給echart物件新增點選事件,只有echart物件可以新增點選事件,系列和元件都家布行,需要通過params判斷點選的型別,如果是點選的散點,就展示div,建立一個chart就好了。

myChart.on('click', function (params) {
	if(params.componentSubType == "effectScatter"){
		$('.tootip,.mark').show();
		let option = {
			// some data  	
		};
		let Chart = echarts.init(document.getElementById('tootip'));
		Chart.setOption(option);
	}
});

$('.clsBtn').click(function(){
	$('.tootip,.mark').hide();
});
複製程式碼

快速實現地圖遷移資料視覺化

基本就實現了,下邊是一些echart配置的關聯項,翻文件或者搜尋翻出來的,記錄一下,熟悉的直接略過就可以了。

視覺對映元件的縱軸配置 dimension

在做左側條形圖的時候碰到一個問題,設定視覺對映元件後不生效,和預期不一樣,見下圖(左側實際,右側預期):

原因是對映元件的資料維度指向錯誤,調整一下dimension指向就好了。

快速實現地圖遷移資料視覺化

視覺對映元件僅對某一組系列資料生效 seriesIndex

在做地圖的時候,希望資料對映元件只對地圖系列生效,對散點不生效,以為是用id來繫結,後來翻了一下文件,用seriesIndex指向mapOption.series的下標,就只對當前下標生效了。

系列元件tooltip的顯示與隱藏

需求是隻有經過散點圖的時候才顯示tooltip元件,所以需要給tooltip.trigger屬性為item,然後給不需要展示的系列增加tooltip.show屬性為false

總結

基本上就這些了,按照掘金的原則:

很多技術內容的講解,尤其是一些新的技術,最先要講明白背後的概念,讓讀者對這個內容有了一個概括性的瞭解,然後再深入背後的細節完成實體的內容。

如果大家覺得哪裡講解的不是很清楚,希望提出來,我修改為可讀性更高的筆記,筆記也需要迭代?,共同交流學習。

相關文章