在Leaflet地图上集成Echarts
需求背景:
現(xiàn)在我要在地圖上加上Echarts的散點(diǎn)圖還有線集,看起來(lái)就很牛B的那種。上效果圖:
需求分析:
我先看了看Echarts官網(wǎng)上有提供加載地圖的例子,主要包括三種方式:
? ? ? ? 1.加載js格式的地圖文件;
? ? ? ? 2.加載json格式的地圖文件;
? ? ? ? 3.通過(guò)配置項(xiàng)bmap,就是百度地圖;? 參考地址:https://blog.csdn.net/wml00000/article/details/84566180
上面的我是用不了的,我用的是Leaflet地圖框架,找了找已經(jīng)有人實(shí)現(xiàn)了擴(kuò)展(Echarts-Leaflet擴(kuò)展地址在這:https://github.com/gnijuohz/echarts-leaflet),他是模仿著Echarts里面的bmap擴(kuò)展了一個(gè)配置項(xiàng)leaflet,給我的感覺(jué)是在Echarts上集成了Leaflet,當(dāng)然用是沒(méi)有問(wèn)題的,就是覺(jué)得有點(diǎn)別扭,我想要的是Leaflet上集成Echarts,是以Leaflet為主,所以就放棄了這種方式。個(gè)人來(lái)講,如果不是項(xiàng)目原因,還是比較推薦他的擴(kuò)展的。
另外,還找到一個(gè)插件leaflet-echarts3(https://github.com/wandergis/leaflet-echarts3),這個(gè)是基于Leaflet進(jìn)行擴(kuò)展的,但是作者沒(méi)有公布源代碼,只有一個(gè)壓縮后的js文件,里面同時(shí)包含了leaflet源碼、Echarts源碼以及擴(kuò)展代碼,這樣我想進(jìn)行版本升級(jí)都不行。所以我就悄悄地把他里面的核心代碼抽了出來(lái)。
具體操作:
? ? ? ?1.下載最新Echarts源代碼,http://echarts.baidu.com/download.html? 下最大的那個(gè)2.86MB;
? ? ? ?2.通過(guò) _theme進(jìn)行定位,定位到function Echarts(){ }里面的??this._theme = theme$$1;? ?4.2.0版本大概在第26582行,添加一行代碼:? ?this._geo = Geo;?
? ? ? 3.對(duì)Leaflet? Layer 類進(jìn)行擴(kuò)展,新建js文件 leaflet-echarts.js,? 加入下面代碼:
L.OverlayEcharts = (L.version < "1.0" ? L.Class : L.Layer).extend({includes: L.version < "1.0" ? L.Mixin.Events : [],_echartsContainer: null,_map: null,_echart: null,_echartsOption: null,initialize: function(echartsOption) {this._echartsOption = echartsOption;},onAdd: function(map) {this._map = map;this._initEchartsContainer();map.on("moveend", this._redraw, this);//當(dāng)?shù)貓D中心改變后觸發(fā)this._redraw();},onRemove: function(map) {this._echartsContainer && map.getPanes().overlayPane.removeChild(this._echartsContainer);this._echart.dispose();map.off("moveend", this._redraw, this);},// addTo: function(map) {// console.log(this);// return map.addLayer(this),// this// },_initEchartsContainer: function() {var size = this._map.getSize(),echartsContainer = document.createElement("div");console.log("=====>mapsize"+size);echartsContainer.style.position = "absolute";echartsContainer.style.height = size.y + "px";echartsContainer.style.width = size.x + "px";echartsContainer.style.zIndex = 999;this._echartsContainer = echartsContainer;this._map.getPanes().overlayPane.appendChild(this._echartsContainer);//在map容器的overlayPane上疊加Echarts容器},_resetCanvasPosition: function() {var bound = this._map.getBounds(),origin = this._map.latLngToLayerPoint(bound.getNorthWest());console.log(origin);//最開(kāi)始為[0,0],縮放不會(huì)改變,平移會(huì)改變\L.DomUtil.setPosition(this._echartsContainer, origin);//設(shè)置Echarts容器的位置,以便與當(dāng)前地圖匹配},_redraw: function() {return this._resetCanvasPosition(),this._echartsContainer.innerHTML = "",this.initECharts(),this.setOption(this._echartsOption),this},clear: function() {this._echartsContainer.innerHTML = "",this.echartsOption = {}},redraw: function() {console.log("=======>redraw");this._redraw();},initECharts: function(){if(this._echart === null || this._echart === undefined){this._initECharts();}else {this._echart.dispose();this._initECharts();}},_initECharts: function() {if (this._echart = echarts.init(this._echartsContainer),"3.0" <= echarts.version) {var me = this;console.log(echarts.version);console.log(me._echart);me._echart._geo.prototype.dataToPoint = function(lnglat) {//重寫(xiě)Echarts內(nèi)部方法,Ecahrts內(nèi)部有一套將經(jīng)緯度轉(zhuǎn)為像素坐標(biāo)的方法,這里要換成與Leaflet相匹配的var latlng = new L.latLng(lnglat[1],lnglat[0]), pixel = me._map.latLngToContainerPoint(latlng);return [pixel.x, pixel.y]; //給定地理坐標(biāo),返回相對(duì)于地圖container容器的相應(yīng)像素坐標(biāo)。}}this._unbindEvent();//屏蔽Echarts相關(guān)事件},setOption: function(echartsOption) {if (echartsOption.series) {//var series = echartsOption.series || {};this._echart.setOption(echartsOption);}},_unbindEvent: function() {echarts.version < "3.0" ? (this._echart.getZrender().un("dragstart", function() {}),this._echart.getZrender().un("dragend", function() {}),this._echart.getZrender().un("mouseup", function() {}),this._echart.getZrender().un("mousedown", function() {}),this._echart.getZrender().un("mousewheel", function() {})) : (this._echart.getZr().off("dragstart", function() {}),this._echart.getZr().off("dragend", function() {}),this._echart.getZr().off("mouseup", function() {}),this._echart.getZr().off("mousedown", function() {}),this._echart.getZr().off("mousewheel", function() {}))} }),L.overlayEcharts = function(options) {return new L.OverlayEcharts(options) }? ? ?4. 新建html頁(yè)面,代碼太多,就不全放了,源文件下載地址:https://download.csdn.net/download/wml00000/10837134
<script src="lib/leaflet/leaflet.js"></script> <script src="lib/echarts.js"></script> <script src="lib/leaflet/leaflet-echarts.js"></script><div id="map"></div> <script>var map = L.map('map'); L.tileLayer('http://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}').addTo(map);map.setView(L.latLng(37.550339, 104.114129), 4); //設(shè)置縮放級(jí)別及中心點(diǎn)//Echarts相關(guān)options配置var option = {};//將Echarts加到地圖上,可以簡(jiǎn)單理解為在地圖上添加了Echarts圖層L.overlayEcharts(option).addTo(map);詳細(xì)解讀:
如果只是照著流程來(lái)一遍,我認(rèn)為是沒(méi)有靈魂的。說(shuō)一說(shuō)到底是如何把Echarts集成進(jìn)去的。要想深入了解,需要知道Leaflet底層的一些東西,以下內(nèi)容均為個(gè)人理解,請(qǐng)選擇性閱讀。
? ? ? ?這是最開(kāi)始加載Leaflet地圖的一個(gè)DOM結(jié)構(gòu),我們只是創(chuàng)建了一個(gè)id為map的DIV,但是Leaflet內(nèi)部在我們的DIV里面又創(chuàng)建了眾多div,分為兩部分:map-pane和control-container。主要看map-pane,在這個(gè)map-pane里面有tile-pane、overlay-pane等,通過(guò)這大概能看出Leaflet是通過(guò)DIV的設(shè)置實(shí)現(xiàn)圖層、marker、覆蓋物等之間的疊加,那么他又是如何根據(jù)地理坐標(biāo)確定在屏幕上的位置關(guān)系的?
? ? ? ?可以先參考一下Leaflet的介紹:https://leafletjs.com/examples/extending/extending-2-layers.html??
核心內(nèi)容:
- The?L.Map?container has “map panes”, which are?<div>s.
- L.Layers are HTML elements inside a map pane
- The map transforms all?LatLngs to coordinates in the map’s CRS, and from that into absolute “pixel coordinates” (the origin of the CRS is the same as the origin of the pixel coordinates)
- When the?L.Map?is ready (has a center?LatLng?and a zoom level), the absolute pixel coordinates of the top-left corner become the “pixel origin”
- Each?L.Layer?is offset from its map pane according to the pixel origin and the absolute pixel coordinates of the layer’s?LatLngs
- The pixel origin is reset after each?zoomend?or?viewreset?event on the?L.Map, and every?L.Layer?has to recalculate its position (if needed)
- The pixel origin is?not?reset when panning the map around; instead, the whole panes are repositioned.
翻譯一下:
- 地圖容器里面有個(gè)map-pane,里面有很多的div
- 圖層其實(shí)就是map-pane里面的html 元素
- 地圖把經(jīng)緯度轉(zhuǎn)換成投影坐標(biāo)系統(tǒng)下的坐標(biāo),再轉(zhuǎn)成絕對(duì)像素坐標(biāo)(投影坐標(biāo)系統(tǒng)原點(diǎn)與像素坐標(biāo)系原點(diǎn)重合)
- 當(dāng)?shù)貓D就緒時(shí)(中心點(diǎn)以及縮放級(jí)別都已經(jīng)確定了),左上角的絕對(duì)像素坐標(biāo)就是所謂的像素原點(diǎn)(注意這里加了引號(hào),他的像素坐標(biāo)不一定是0,0)
- 每個(gè)圖層會(huì)根據(jù)像素原點(diǎn)以及圖層上的經(jīng)緯度坐標(biāo)轉(zhuǎn)換過(guò)來(lái)的絕對(duì)像素坐標(biāo)在map-pane上進(jìn)行偏移
- 每次縮放或視圖重置坐標(biāo)原點(diǎn)也會(huì)重置(可以簡(jiǎn)單理解為地圖的重新加載,就是第4條),各個(gè)圖層也得重新計(jì)算位置(因?yàn)橐豢s放,雖然地理坐標(biāo)不變,實(shí)際上不同縮放級(jí)別地理坐標(biāo)對(duì)應(yīng)的像素坐標(biāo)是不一樣的)
- 當(dāng)?shù)貓D平移時(shí)像素原點(diǎn)不會(huì)重置(實(shí)際上當(dāng)?shù)貓D平移時(shí),map-pane這個(gè)div樣式中的transform變了,也就是說(shuō)map-pane的左上角這個(gè)像素原點(diǎn)也走了,但是他的像素坐標(biāo)不變,注意坐標(biāo)不一定是0,0),這個(gè)map-pane上面的其他div也就跟著動(dòng)了
對(duì)照著這個(gè)例子https://leafletjs.com/examples/extending/pixelorigin.html?應(yīng)該能看個(gè)差不多。
再插兩句:上面說(shuō)到的絕對(duì)像素坐標(biāo)是針對(duì)地圖切片談的,并不是針對(duì)設(shè)備坐標(biāo)。每張地圖切片的大小是256*256,當(dāng)縮放級(jí)別為0時(shí),就是用一張圖片表示全球,絕對(duì)像素坐標(biāo)原點(diǎn)(0,0)值位于圖片左上角,換算成經(jīng)緯度大約是(-180,85).另外一點(diǎn),網(wǎng)絡(luò)地圖大都采用網(wǎng)絡(luò)墨卡托投影,他并不是指投影坐標(biāo)系,只是一種映射關(guān)系,是把球面地理坐標(biāo)轉(zhuǎn)為平面坐標(biāo)的一個(gè)過(guò)程。附個(gè)地址:https://blog.csdn.net/wml00000/article/details/85014021
Echarts的散點(diǎn)圖其實(shí)也是在一個(gè)Div上畫(huà)的,只要把這個(gè)div給拿到map-pane里面的overlay-pane里面不就完事了。關(guān)于點(diǎn)的位置,因?yàn)镋charts內(nèi)部自己有個(gè)把地理坐標(biāo)轉(zhuǎn)為像素坐標(biāo)的方法,咱不能用他的,想辦法給他重寫(xiě)了,用Leaflet 的latLngToContainerPoint( )代替。
關(guān)于Echarts的DIV容器怎么搞,有幾點(diǎn):
? 1.大小需要與map container匹配
? 2.每次平移,縮放,要重新加載Echarts(可以根據(jù)leaflet的moveend監(jiān)聽(tīng))
? 3.重寫(xiě)Echarts內(nèi)部方法 dataToPoint
寫(xiě)的有點(diǎn)亂了
今天突然發(fā)現(xiàn)超圖 supermap iclient? 9d for Leaflet有Echarts的例子,可以看看:http://iclient.supermap.io/examples/leaflet/examples.html#viz-ECharts
總結(jié)
以上是生活随笔為你收集整理的在Leaflet地图上集成Echarts的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 基于Leaflet和高德Web API扩
- 下一篇: NVM安装与使用(实现Node多版本控制