在Leaflet地图上集成Echarts
需求背景:
現(xiàn)在我要在地圖上加上Echarts的散點圖還有線集,看起來就很牛B的那種。上效果圖:
需求分析:
我先看了看Echarts官網(wǎng)上有提供加載地圖的例子,主要包括三種方式:
? ? ? ? 1.加載js格式的地圖文件;
? ? ? ? 2.加載json格式的地圖文件;
? ? ? ? 3.通過配置項bmap,就是百度地圖;? 參考地址:https://blog.csdn.net/wml00000/article/details/84566180
上面的我是用不了的,我用的是Leaflet地圖框架,找了找已經(jīng)有人實現(xiàn)了擴展(Echarts-Leaflet擴展地址在這:https://github.com/gnijuohz/echarts-leaflet),他是模仿著Echarts里面的bmap擴展了一個配置項leaflet,給我的感覺是在Echarts上集成了Leaflet,當(dāng)然用是沒有問題的,就是覺得有點別扭,我想要的是Leaflet上集成Echarts,是以Leaflet為主,所以就放棄了這種方式。個人來講,如果不是項目原因,還是比較推薦他的擴展的。
另外,還找到一個插件leaflet-echarts3(https://github.com/wandergis/leaflet-echarts3),這個是基于Leaflet進行擴展的,但是作者沒有公布源代碼,只有一個壓縮后的js文件,里面同時包含了leaflet源碼、Echarts源碼以及擴展代碼,這樣我想進行版本升級都不行。所以我就悄悄地把他里面的核心代碼抽了出來。
具體操作:
? ? ? ?1.下載最新Echarts源代碼,http://echarts.baidu.com/download.html? 下最大的那個2.86MB;
? ? ? ?2.通過 _theme進行定位,定位到function Echarts(){ }里面的??this._theme = theme$$1;? ?4.2.0版本大概在第26582行,添加一行代碼:? ?this._geo = Geo;?
? ? ? 3.對Leaflet? Layer 類進行擴展,新建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);//最開始為[0,0],縮放不會改變,平移會改變\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) {//重寫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),返回相對于地圖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頁面,代碼太多,就不全放了,源文件下載地址: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è)置縮放級別及中心點//Echarts相關(guān)options配置var option = {};//將Echarts加到地圖上,可以簡單理解為在地圖上添加了Echarts圖層L.overlayEcharts(option).addTo(map);詳細(xì)解讀:
如果只是照著流程來一遍,我認(rèn)為是沒有靈魂的。說一說到底是如何把Echarts集成進去的。要想深入了解,需要知道Leaflet底層的一些東西,以下內(nèi)容均為個人理解,請選擇性閱讀。
? ? ? ?這是最開始加載Leaflet地圖的一個DOM結(jié)構(gòu),我們只是創(chuàng)建了一個id為map的DIV,但是Leaflet內(nèi)部在我們的DIV里面又創(chuàng)建了眾多div,分為兩部分:map-pane和control-container。主要看map-pane,在這個map-pane里面有tile-pane、overlay-pane等,通過這大概能看出Leaflet是通過DIV的設(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.
翻譯一下:
- 地圖容器里面有個map-pane,里面有很多的div
- 圖層其實就是map-pane里面的html 元素
- 地圖把經(jīng)緯度轉(zhuǎn)換成投影坐標(biāo)系統(tǒng)下的坐標(biāo),再轉(zhuǎn)成絕對像素坐標(biāo)(投影坐標(biāo)系統(tǒng)原點與像素坐標(biāo)系原點重合)
- 當(dāng)?shù)貓D就緒時(中心點以及縮放級別都已經(jīng)確定了),左上角的絕對像素坐標(biāo)就是所謂的像素原點(注意這里加了引號,他的像素坐標(biāo)不一定是0,0)
- 每個圖層會根據(jù)像素原點以及圖層上的經(jīng)緯度坐標(biāo)轉(zhuǎn)換過來的絕對像素坐標(biāo)在map-pane上進行偏移
- 每次縮放或視圖重置坐標(biāo)原點也會重置(可以簡單理解為地圖的重新加載,就是第4條),各個圖層也得重新計算位置(因為一縮放,雖然地理坐標(biāo)不變,實際上不同縮放級別地理坐標(biāo)對應(yīng)的像素坐標(biāo)是不一樣的)
- 當(dāng)?shù)貓D平移時像素原點不會重置(實際上當(dāng)?shù)貓D平移時,map-pane這個div樣式中的transform變了,也就是說map-pane的左上角這個像素原點也走了,但是他的像素坐標(biāo)不變,注意坐標(biāo)不一定是0,0),這個map-pane上面的其他div也就跟著動了
對照著這個例子https://leafletjs.com/examples/extending/pixelorigin.html?應(yīng)該能看個差不多。
再插兩句:上面說到的絕對像素坐標(biāo)是針對地圖切片談的,并不是針對設(shè)備坐標(biāo)。每張地圖切片的大小是256*256,當(dāng)縮放級別為0時,就是用一張圖片表示全球,絕對像素坐標(biāo)原點(0,0)值位于圖片左上角,換算成經(jīng)緯度大約是(-180,85).另外一點,網(wǎng)絡(luò)地圖大都采用網(wǎng)絡(luò)墨卡托投影,他并不是指投影坐標(biāo)系,只是一種映射關(guān)系,是把球面地理坐標(biāo)轉(zhuǎn)為平面坐標(biāo)的一個過程。附個地址:https://blog.csdn.net/wml00000/article/details/85014021
Echarts的散點圖其實也是在一個Div上畫的,只要把這個div給拿到map-pane里面的overlay-pane里面不就完事了。關(guān)于點的位置,因為Echarts內(nèi)部自己有個把地理坐標(biāo)轉(zhuǎn)為像素坐標(biāo)的方法,咱不能用他的,想辦法給他重寫了,用Leaflet 的latLngToContainerPoint( )代替。
關(guān)于Echarts的DIV容器怎么搞,有幾點:
? 1.大小需要與map container匹配
? 2.每次平移,縮放,要重新加載Echarts(可以根據(jù)leaflet的moveend監(jiān)聽)
? 3.重寫Echarts內(nèi)部方法 dataToPoint
寫的有點亂了
今天突然發(fā)現(xiàn)超圖 supermap iclient? 9d for Leaflet有Echarts的例子,可以看看:http://iclient.supermap.io/examples/leaflet/examples.html#viz-ECharts
總結(jié)
以上是生活随笔為你收集整理的在Leaflet地图上集成Echarts的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于Leaflet和高德Web API扩
- 下一篇: NVM安装与使用(实现Node多版本控制