网上查阅了部分资料,有些是用echarts2实现的,因echarts2无法满足项目中其他部分的要求,故只能采用echarts3(2017/9/18,echarts3官网突然把基于geo的demo下架了,只剩下基于百度地图的示例,汗-_-||)。
参考:Echart2在Openlayers2的应用-航班的炫光特效
openlayers3与echarts3的结合使用(普通图表-pie、bar……)
Leaflet 地图Leaflet 地图和 ECharts 的结合,参考前端代码
扩展OpenLayers.Layer图层,用于装载Map类型图表,侦听地图刷新事件,同步进行echarts的Option中坐标变换,转变为屏幕坐标;
1 <!DOCTYPE html> 2 <head> 3 <meta charset="utf-8"> 4 <title>ECharts</title> 5 <script type="text/javascript" src="resources/js/jquery-3.1.0.min.js"></script> 6 <script type="text/javascript" src="resources/js/ol2-release-2.13.1/OpenLayers.js"></script> 7 <link rel="stylesheet" type="text/css" href="resources/js/ol2-release-2.13.1/theme/default/style.css"> 8 <!-- ECharts单文件引入 --> 9 <script type="text/javascript" src="resources/js/echarts.js"></script> 10 <script type="text/javascript" src="resources/js/EchartMapLayer.js"></script> 11 12 <style> 13 #map{ 14 width:1000px; 15 height: 700px; 16 position: absolute; 17 } 18 </style> 19 </head> 20 <body> 21 <div id="map" class="smallmap"></div> 22 <script type="text/javascript"> 23 var map = new OpenLayers.Map(\'map\', { 24 controls: [ 25 new OpenLayers.Control.Navigation(), 26 new OpenLayers.Control.PanZoomBar(), 27 new OpenLayers.Control.LayerSwitcher({\'ascending\':false}), 28 new OpenLayers.Control.ScaleLine(), 29 new OpenLayers.Control.MousePosition(), 30 new OpenLayers.Control.OverviewMap(), 31 // new OpenLayers.Control.KeyboardDefaults() 32 ], 33 numZoomLevels: 17 34 }); 35 36 var osmlayer = new OpenLayers.Layer.OSM("OSM"); 37 map.addLayers([osmlayer]); 38 39 map.setCenter(new OpenLayers.LonLat(11400246.56,4439064.42), 4); 40 41 //create echarts\'s map 42 43 var geoCoordMap = { 44 \'上海\': [121.4648, 31.2891], 45 \'东莞\': [113.8953, 22.901], 46 \'东营\': [118.7073, 37.5513], 47 \'中山\': [113.4229, 22.478], 48 \'临汾\': [111.4783, 36.1615], 49 \'临沂\': [118.3118, 35.2936], 50 \'丹东\': [124.541, 40.4242], 51 \'丽水\': [119.5642, 28.1854], 52 \'乌鲁木齐\': [87.9236, 43.5883], 53 \'佛山\': [112.8955, 23.1097], 54 \'保定\': [115.0488, 39.0948], 55 \'兰州\': [103.5901, 36.3043], 56 \'包头\': [110.3467, 41.4899], 57 \'北京\': [116.4551, 40.2539], 58 \'北海\': [109.314, 21.6211], 59 \'南京\': [118.8062, 31.9208], 60 \'南宁\': [108.479, 23.1152], 61 \'南昌\': [116.0046, 28.6633], 62 \'南通\': [121.1023, 32.1625], 63 \'厦门\': [118.1689, 24.6478], 64 \'台州\': [121.1353, 28.6688], 65 \'合肥\': [117.29, 32.0581], 66 \'呼和浩特\': [111.4124, 40.4901], 67 \'咸阳\': [108.4131, 34.8706], 68 \'哈尔滨\': [127.9688, 45.368], 69 \'唐山\': [118.4766, 39.6826], 70 \'嘉兴\': [120.9155, 30.6354], 71 \'大同\': [113.7854, 39.8035], 72 \'大连\': [122.2229, 39.4409], 73 \'天津\': [117.4219, 39.4189], 74 \'太原\': [112.3352, 37.9413], 75 \'威海\': [121.9482, 37.1393], 76 \'宁波\': [121.5967, 29.6466], 77 \'宝鸡\': [107.1826, 34.3433], 78 \'宿迁\': [118.5535, 33.7775], 79 \'常州\': [119.4543, 31.5582], 80 \'广州\': [113.5107, 23.2196], 81 \'廊坊\': [116.521, 39.0509], 82 \'延安\': [109.1052, 36.4252], 83 \'张家口\': [115.1477, 40.8527], 84 \'徐州\': [117.5208, 34.3268], 85 \'德州\': [116.6858, 37.2107], 86 \'惠州\': [114.6204, 23.1647], 87 \'成都\': [103.9526, 30.7617], 88 \'扬州\': [119.4653, 32.8162], 89 \'承德\': [117.5757, 41.4075], 90 \'拉萨\': [91.1865, 30.1465], 91 \'无锡\': [120.3442, 31.5527], 92 \'日照\': [119.2786, 35.5023], 93 \'昆明\': [102.9199, 25.4663], 94 \'杭州\': [119.5313, 29.8773], 95 \'枣庄\': [117.323, 34.8926], 96 \'柳州\': [109.3799, 24.9774], 97 \'株洲\': [113.5327, 27.0319], 98 \'武汉\': [114.3896, 30.6628], 99 \'汕头\': [117.1692, 23.3405], 100 \'江门\': [112.6318, 22.1484], 101 \'沈阳\': [123.1238, 42.1216], 102 \'沧州\': [116.8286, 38.2104], 103 \'河源\': [114.917, 23.9722], 104 \'泉州\': [118.3228, 25.1147], 105 \'泰安\': [117.0264, 36.0516], 106 \'泰州\': [120.0586, 32.5525], 107 \'济南\': [117.1582, 36.8701], 108 \'济宁\': [116.8286, 35.3375], 109 \'海口\': [110.3893, 19.8516], 110 \'淄博\': [118.0371, 36.6064], 111 \'淮安\': [118.927, 33.4039], 112 \'深圳\': [114.5435, 22.5439], 113 \'清远\': [112.9175, 24.3292], 114 \'温州\': [120.498, 27.8119], 115 \'渭南\': [109.7864, 35.0299], 116 \'湖州\': [119.8608, 30.7782], 117 \'湘潭\': [112.5439, 27.7075], 118 \'滨州\': [117.8174, 37.4963], 119 \'潍坊\': [119.0918, 36.524], 120 \'烟台\': [120.7397, 37.5128], 121 \'玉溪\': [101.9312, 23.8898], 122 \'珠海\': [113.7305, 22.1155], 123 \'盐城\': [120.2234, 33.5577], 124 \'盘锦\': [121.9482, 41.0449], 125 \'石家庄\': [114.4995, 38.1006], 126 \'福州\': [119.4543, 25.9222], 127 \'秦皇岛\': [119.2126, 40.0232], 128 \'绍兴\': [120.564, 29.7565], 129 \'聊城\': [115.9167, 36.4032], 130 \'肇庆\': [112.1265, 23.5822], 131 \'舟山\': [122.2559, 30.2234], 132 \'苏州\': [120.6519, 31.3989], 133 \'莱芜\': [117.6526, 36.2714], 134 \'菏泽\': [115.6201, 35.2057], 135 \'营口\': [122.4316, 40.4297], 136 \'葫芦岛\': [120.1575, 40.578], 137 \'衡水\': [115.8838, 37.7161], 138 \'衢州\': [118.6853, 28.8666], 139 \'西宁\': [101.4038, 36.8207], 140 \'西安\': [109.1162, 34.2004], 141 \'贵阳\': [106.6992, 26.7682], 142 \'连云港\': [119.1248, 34.552], 143 \'邢台\': [114.8071, 37.2821], 144 \'邯郸\': [114.4775, 36.535], 145 \'郑州\': [113.4668, 34.6234], 146 \'鄂尔多斯\': [108.9734, 39.2487], 147 \'重庆\': [107.7539, 30.1904], 148 \'金华\': [120.0037, 29.1028], 149 \'铜川\': [109.0393, 35.1947], 150 \'银川\': [106.3586, 38.1775], 151 \'镇江\': [119.4763, 31.9702], 152 \'长春\': [125.8154, 44.2584], 153 \'长沙\': [113.0823, 28.2568], 154 \'长治\': [112.8625, 36.4746], 155 \'阳泉\': [113.4778, 38.0951], 156 \'青岛\': [120.4651, 36.3373], 157 \'韶关\': [113.7964, 24.7028] 158 }; 159 160 var BJData = [ 161 [{name: \'北京\'}, {name: \'上海\', value: 95}], 162 [{name: \'北京\'}, {name: \'广州\', value: 90}], 163 [{name: \'北京\'}, {name: \'大连\', value: 80}], 164 [{name: \'北京\'}, {name: \'南宁\', value: 70}], 165 [{name: \'北京\'}, {name: \'南昌\', value: 60}], 166 [{name: \'北京\'}, {name: \'拉萨\', value: 50}], 167 [{name: \'北京\'}, {name: \'长春\', value: 40}], 168 [{name: \'北京\'}, {name: \'包头\', value: 30}], 169 [{name: \'北京\'}, {name: \'重庆\', value: 20}], 170 [{name: \'北京\'}, {name: \'常州\', value: 10}] 171 ]; 172 173 var SHData = [ 174 [{name: \'上海\'}, {name: \'包头\', value: 95}], 175 [{name: \'上海\'}, {name: \'昆明\', value: 90}], 176 [{name: \'上海\'}, {name: \'广州\', value: 80}], 177 [{name: \'上海\'}, {name: \'郑州\', value: 70}], 178 [{name: \'上海\'}, {name: \'长春\', value: 60}], 179 [{name: \'上海\'}, {name: \'重庆\', value: 50}], 180 [{name: \'上海\'}, {name: \'长沙\', value: 40}], 181 [{name: \'上海\'}, {name: \'北京\', value: 30}], 182 [{name: \'上海\'}, {name: \'丹东\', value: 20}], 183 [{name: \'上海\'}, {name: \'大连\', value: 10}] 184 ]; 185 186 var GZData = [ 187 [{name: \'广州\'}, {name: \'福州\', value: 95}], 188 [{name: \'广州\'}, {name: \'太原\', value: 90}], 189 [{name: \'广州\'}, {name: \'长春\', value: 80}], 190 [{name: \'广州\'}, {name: \'重庆\', value: 70}], 191 [{name: \'广州\'}, {name: \'西安\', value: 60}], 192 [{name: \'广州\'}, {name: \'成都\', value: 50}], 193 [{name: \'广州\'}, {name: \'常州\', value: 40}], 194 [{name: \'广州\'}, {name: \'北京\', value: 30}], 195 [{name: \'广州\'}, {name: \'北海\', value: 20}], 196 [{name: \'广州\'}, {name: \'海口\', value: 10}] 197 ]; 198 199 var planePath = \'path://M1705.06,1318.313v-89.254l-319.9-221.799l0.073-208.063c0.521-84.662-26.629-121.796-63.961-121.491c-37.332-0.305-64.482,36.829-63.961,121.491l0.073,208.063l-319.9,221.799v89.254l330.343-157.288l12.238,241.308l-134.449,92.931l0.531,42.034l175.125-42.917l175.125,42.917l0.531-42.034l-134.449-92.931l12.238-241.308L1705.06,1318.313z\'; 200 201 var convertData = function (data) { 202 var res = []; 203 for (var i = 0; i < data.length; i++) { 204 var dataItem = data[i]; 205 var fromCoord = geoCoordMap[dataItem[0].name]; 206 var toCoord = geoCoordMap[dataItem[1].name]; 207 if (fromCoord && toCoord) { 208 res.push([{ 209 coord: fromCoord 210 }, { 211 coord: toCoord 212 }]); 213 } 214 } 215 return res; 216 }; 217 218 var color = [\'#a6c84c\', \'#ffa022\', \'#46bee9\']; 219 var series = []; 220 [[\'北京\', BJData], [\'上海\', SHData], [\'广州\', GZData]].forEach(function (item, i) { 221 series.push({ 222 name: item[0] + \' Top10\', 223 type: \'lines\', 224 zlevel: 1, 225 effect: { 226 show: true, 227 period: 6, 228 trailLength: 0.7, 229 color: \'#fff\', 230 symbolSize: 3 231 }, 232 lineStyle: { 233 normal: { 234 color: color[i], 235 width: 0, 236 curveness: 0.2 237 } 238 }, 239 data: convertData(item[1]) 240 }, 241 { 242 name: item[0] + \' Top10\', 243 type: \'lines\', 244 zlevel: 2, 245 effect: { 246 show: true, 247 period: 6, 248 trailLength: 0, 249 symbol: planePath, 250 symbolSize: 15 251 }, 252 lineStyle: { 253 normal: { 254 color: color[i], 255 width: 1, 256 opacity: 0.4, 257 curveness: 0.2 258 } 259 }, 260 data: convertData(item[1]) 261 }, 262 { 263 name: item[0] + \' Top10\', 264 type: \'effectScatter\', 265 coordinateSystem: \'geo\', 266 zlevel: 2, 267 rippleEffect: { 268 brushType: \'stroke\' 269 }, 270 label: { 271 normal: { 272 show: true, 273 position: \'right\', 274 formatter: \'{b}\' 275 } 276 }, 277 symbolSize: function (val) { 278 return val[2] / 8; 279 }, 280 itemStyle: { 281 normal: { 282 color: color[i] 283 } 284 }, 285 data: item[1].map(function (dataItem) { 286 return { 287 name: dataItem[1].name, 288 value: geoCoordMap[dataItem[1].name].concat([dataItem[1].value]) 289 }; 290 }) 291 }); 292 }); 293 294 option = { 295 // backgroundColor: \'#404a59\', 296 title: { 297 text: \'Leaflet扩展Echarts3之模拟迁徙\', 298 subtext: \'Develop By WanderGIS\', 299 left: \'center\', 300 textStyle: { 301 color: \'#fff\' 302 } 303 }, 304 tooltip: { 305 trigger: \'item\' 306 }, 307 legend: { 308 orient: \'vertical\', 309 top: \'bottom\', 310 left: \'left\', 311 data: [\'北京 Top10\', \'上海 Top10\', \'广州 Top10\'], 312 textStyle: { 313 color: \'#fff\' 314 }, 315 selectedMode: \'single\' 316 }, 317 geo: { 318 map: \'\', 319 label: { 320 emphasis: { 321 show: false 322 } 323 }, 324 roam: true, 325 itemStyle: { 326 normal: { 327 areaColor: \'#323c48\', 328 borderColor: \'#404a59\' 329 }, 330 emphasis: { 331 areaColor: \'#2a333d\' 332 } 333 } 334 }, 335 series: series 336 }; 337 // 使用刚指定的配置项和数据显示图表。 338 var echartlayer = new OpenLayers.Layer.EchartMapLayer("echartlayer",map,echarts,{ 339 option:option 340 }); 341 map.addLayer(echartlayer); 342 343 echartlayer.alwaysInRange = true; 344 345 </script> 346 </body>
注意:series的geo一定要设置,不然地图无法创建,map要置空,浏览器调试状态下会报错("Map not exists"),我们已经通过EchartMapLayer替代map的坐标转换功能;
EchartMapLayer的封装也是参考前面连接里的思路,代码如下:
1 OpenLayers.Layer.EchartMapLayer = OpenLayers.Class(OpenLayers.Layer, { 2 isBaseLayer : false, 3 alwaysInRange: true, 4 echart : null, 5 mapLayer : null, 6 initialize : function (name, map, echart, options) 7 { 8 var scope = this, echartdiv = document.createElement("div"), handler; 9 10 OpenLayers.Layer.prototype.initialize.apply(this, [name, options]); 11 12 echartdiv.style.cssText = "position:absolute;width:" + map.size.w + "px;height:" 13 + map.size.h + "px;"; 14 scope.div.appendChild(echartdiv); 15 scope.map = map; 16 scope.echartdiv = echartdiv; 17 scope.opacity = options.opacity; 18 scope.echart = echart; 19 scope.option = options.option; 20 handler = function (e) 21 { 22 scope.updateLayer(e); 23 }; 24 scope.updateLayer(); 25
26 map.events.register("moveend", this, handler); 27 }, 28 geoCoord2Pixel:function(geoCoord){ 29 var scope = this; 30 var lonLat = new OpenLayers.LonLat(geoCoord[0], geoCoord[1]); 31 var proj = new OpenLayers.Projection("EPSG:4326"); 32 lonLat.transform(proj, scope.map.getProjectionObject()); 33 34 var scrPt = scope.map.getPixelFromLonLat(lonLat); 35 var x = scrPt.x, 36 y = scrPt.y; 37 return [x,y]; 38 }, 39 updateLayer : function (e) 40 { 41 var scope = this; 42 var myChart = scope.echart.getInstanceByDom(scope.echartdiv); 43 if(!myChart){ 44 myChart = scope.echart.init(scope.echartdiv); 45 } 46 var orgXy, w, h; 47 if(e){ 48 orgXy = e.object.layerContainerOriginPx; 49 50 } 51 else{ 52 orgXy={x:0,y:0}; 53 } 54 w = scope.map.size.w; 55 h = scope.map.size.h; 56 scope.echartdiv.style.cssText = "position:absolute;top:"+(-orgXy.y)+"px;left:"+(-orgXy.x)+ 57 "px;width:" + w + "px;height:" + h + "px;"; 58 var ecOption = scope.getEcOption(); 59 myChart.setOption(ecOption); 60 }, 61 /** 62 *将echart的option转换 63 * @returns {*} 64 */ 65 getEcOption: function(){ 66 var scope = this; 67 scope._option = $.extend(true, {}, scope.option); //deep copy 68 var series = scope._option.series || {}; 69 70 for(var dex in series){ 71 var obj = series[dex]; 72 var type = obj.type; 73 if(type == "lines"){ 74 var datas = obj.data; 75 for(var i = 0; i < datas.length; i ++){ 76 var dataArray = datas[i]; 77 for(var j = 0; j < dataArray.length; j ++){ 78 var coords = dataArray[j]; 79 var coord = coords.coord; 80 var newGeo = scope._AddPos(coords); 81 scope._option.series[dex].data[i][j] = newGeo; 82 } 83 } 84 } 85 else if(type == "effectScatter"){ 86 var datas = obj.data; 87 for(var i = 0; i < datas.length; i ++){ 88 var dataArray = datas[i]; 89 var newObj = scope._AddPosEffect(dataArray); 90 scope._option.series[dex].data[i] = newObj; 91 } 92 93 } 94 95 } 96 return scope._option; 97 }, 98 _AddPos:function(coords){ 99 var scope = this; 100 var pos = scope.geoCoord2Pixel(coords.coord); 101 var newCoord = { 102 coord:[pos[0],pos[1]] 103 }; 104 return newCoord; 105 }, 106 _AddPosEffect:function(effectObj){ 107 var scope = this; 108 var pos = scope.geoCoord2Pixel(effectObj.value); 109 var newObj = { 110 name: effectObj.name, 111 value: [ 112 pos[0], 113 pos[1], 114 effectObj.value[2] 115 ] 116 }; 117 return newObj; 118 }, 119 destroy : function () 120 { 121 OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments); 122 }, 123 CLASS_NAME : "OpenLayers.Layer.EchartMapLayer" 124 } 125 );
注意:getEcOption内的坐标转换需根据不同的series包含的数据,分层查找;
效果图,OSM地图颜色偏白,可自行修改chart的配色。