react專案結合echarts,百度地圖實現熱力圖

荷風伊夏發表於2020-09-18

一.最近在一個react專案(antd pro)中需要展示一個熱力地圖。需求是:

1.熱力地圖可縮放;

2.滑鼠點選可以展示該點地理座標,及熱力值。

3.初始化時候自適應展示所有的熱力點。

4.展示熱力標尺。

二.在實現的過程中出現了不少的問題.

引入地圖出現問題
1.縮放地圖的時候中心點會漂移。
   解決方案:
      a.把地圖放到頁面的頂部。
      b.每次縮放後獲取地圖中心點,再次設定中心點。關鍵程式碼如下:

     let cp;
     bmap.addEventListener("mousemove",function(){ // 載入完成時,觸發
       cp = bmap.getCenter();
     });
     bmap.addEventListener("mouseend",function(){ // 載入完成時,觸發
       cp = bmap.getCenter();
     });
     bmap.addEventListener("tilesloaded",function(){ // 載入完成時,觸發
       bmap.setCenter(cp);
     });

2.熱力圖示尺不展示。
   解決方案:
       a.將百度地圖引入echanrts圖表中,通過配置項加熱力標尺。(visualMap屬性)

       b.手寫標尺放入百度地圖頁面。(可以自己嘗試弄,這裡我不介紹)

3.熱力地圖的點不能點選。 

   解決方案:建立標註(這個標註可能有點醜,如果不想要,可以用透明的圖片替代)

   這有篇部落格寫的很詳細,https://blog.csdn.net/zjuwwj/article/details/53374947#commentsedit

 // 建立一個標註
     var point = new BMap.Point(data[0][0], data[0][1]);
     var marker = new BMap.Marker(point);  // 建立標註
     bmap.addOverlay(marker);

4.百度地圖預設不會展示目標點的地理座標(本來引入echarts,想著直接用tooltip屬性,結果不生效,查詢原因發現熱力點是個對映關係,不是實實在在的座標點,獲取不到座標資訊)。
    解決方案: 獲取目標點地理座標,手動加入懸浮層。

5.初始化時候需要自適應展示所有的熱力點(開啟地圖的時候有預設的比例尺,或者自己設定的固定的比例尺。)

   解決方案:計算所有熱力點中最遠的兩個的距離,我是設定資料的第一個點為中心點,計算此點與其他所有點的距離,找出最大值,然後對比百度地圖等級設定。

百度地圖對應的等級可參考:http://api.map.baidu.com/lbsapi/getpoint/index.html

綜合實現這些東西,可費了不少勁呢。

三.先來看看熱力地圖程式碼:heatMap.js

這是在react專案裡使用了echarts和百度地圖。

// heatMap.js

import React, { Component } from 'react'; import echarts from 'echarts/lib/echarts'; // 引入熱力圖 import 'echarts/lib/chart/heatmap'; // 引入提示框和標題元件 import 'echarts/lib/component/tooltip'; import 'echarts/lib/component/title'; import 'echarts/extension/bmap/bmap'; import 'echarts/lib/component/visualMap'; class EchartsTest extends Component { constructor(props) { super(props); this.state = { data: props.data, }; } componentDidMount() { this.getCharts(); } // 保證再次請求資料時候,地圖重新載入 // eslint-disable-next-line no-unused-vars componentWillReceiveProps(nextProps, nextContext) { this.setState({ data: nextProps.data, }); setTimeout(() => { if(!nextProps.data.length===0){ this.getCharts(); } }, 500); } getCharts = () => { const { data } = this.state;
// 這裡data格式:[[120.122,35.666,67],[120.12323,23.45555,23]] ,裡面是經度,維度,熱力值 const maxdata
= data .map(item => item[2]) .sort() .reverse()[0]; const myChart = echarts.init(document.getElementById('main')); const option = { animation: false, // tooltip: { // 懸浮層提示框在熱力圖上無效 // trigger: 'item', // triggerOn: 'click', // }, bmap: { center: [data[0][0], data[0][1]], zoom: this.getMapGrade(data), roam: true, }, visualMap: { show: true, // top: 'top', bottom: 50, left: 0, min: 0, max: maxdata, seriesIndex: 0, calculable: true, inRange: { color: ['blue', 'green', 'yellow', 'red'], }, }, series: [ { name: 'gid熱力值', type: 'heatmap', coordinateSystem: 'bmap', data, pointSize: 8, blurSize: 8, }, ], }; myChart.setOption(option); // 新增百度地圖外掛 const bmap = myChart .getModel() .getComponent('bmap') .getBMap(); // 解決地圖放大地圖中心點漂移的問題。當地圖不在頁面頂部使用 // let cp; // bmap.addEventListener("mousemove",function(){ // 載入完成時,觸發 // cp = bmap.getCenter(); // }); // bmap.addEventListener("mouseend",function(){ // 載入完成時,觸發 // cp = bmap.getCenter(); // }); // bmap.addEventListener("tilesloaded",function(){ // 載入完成時,觸發 // bmap.setCenter(cp); // }); // eslint-disable-next-line no-undef bmap.addControl(new BMap.NavigationControl()); // 地圖平移縮放控制元件 // eslint-disable-next-line no-undef bmap.addControl(new BMap.ScaleControl()); // 地圖比例尺控制元件 // 建立一個標註 // var point = new BMap.Point(data[0][0], data[0][1]); // var marker = new BMap.Marker(point); // 建立標註 // bmap.addOverlay(marker); // eslint-disable-next-line no-plusplus for (let i = 0; i < data.length; i++) { // eslint-disable-next-line no-undef const hotPoint = new BMap.Point(data[i][0], data[i][1]); // eslint-disable-next-line no-undef const marker = new BMap.Marker(hotPoint); // 建立標註 bmap.addOverlay(marker); // 將標註新增到地圖中 // eslint-disable-next-line no-use-before-define // marker.addEventListener("click",getAttr); // function getAttr(){ // var p = marker.getPosition(); // 獲取marker的位置 // p.id = "123"; // console.log("點的位置是" + hotPoint.lng + "," + hotPoint.lat); // console.log("marker的位置是" + p.lng + "," + p.lat); // } // eslint-disable-next-line no-undef const infoWindow = new BMap.InfoWindow(` <div style="margin:0;line-height:20px;padding:2px;"> 標題:熱點詳細資訊 <br/>地理位置:${data[i][0]}, ${data[i][1]} <br/>最近三個月熱力值:${data[i][2]} </div>`); marker.infoWindow = infoWindow; // 給當前標註新增一個屬性以便儲存視窗資訊infoWindow marker.addEventListener('click', function(e) { this.openInfoWindow(e.target.infoWindow); // 點選標註時,開啟改標註對開啟改標註對應的回撥資訊 // 如果使用下面的方式,那樣就會導致每次標註點選後,彈出的視窗資訊都是最後一次迴圈的infoWindow。因為在click的時候只會去找infoWindow這個變數值,而你的click肯定是在所有迴圈的,標註都產生完之後,此時infoWindow變數已經被賦值成了最後一次迴圈的值。 // this.openInfoWindow(infoWindow); }); } // eslint-disable-next-line no-undef bmap.addControl( // eslint-disable-next-line no-undef new BMap.MapTypeControl({ mapTypes: [ // eslint-disable-next-line no-undef BMAP_NORMAL_MAP, // BMAP_HYBRID_MAP ], }) ); // 去掉上面的{mapTypes:[...]} 就會顯示地圖,衛星,三維三個圖層 }; // 計算經緯度距離(千米),四個引數分別是點A的緯度,經度,點B的緯度,經度(位置不要搞錯了,我就弄錯了,搞了好久) getDistance =(lat1, lng1, lat2, lng2)=>{ const radLat1 = lat1*Math.PI / 180.0; const radLat2 = lat2*Math.PI / 180.0; const a = radLat1 - radLat2; const b = lng1*Math.PI / 180.0 - lng2*Math.PI / 180.0; // eslint-disable-next-line no-restricted-properties let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) + // eslint-disable-next-line no-restricted-properties Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2))); s *=6378.137 ; s = Math.round(s * 10000) / 10000; return s; }; // 計算地圖初始化所有地理座標中距離最大值 getZoom = (val)=>{ const arr = []; if(val.length===1){ arr.push(1) }else{ for(let i=1;i<val.length;i+=1){ arr.push(this.getDistance(val[0][1],val[0][0],val[i][1],val[i][0])) } } // console.log("所有距離",arr); return Math.max(...arr) }; // 計算比例尺對應的百度地圖等級 getMapGrade=(val)=>{ // console.log("資料 ",val); const num=this.getZoom(val); // console.log("最大距離",num); let zoom=0; if(num<=1){ zoom=15 }else if(num>1&&num<=50){ zoom=10 }else if(num>50&&num<=100){ zoom=9 }else if(num>100&&num<=500){ zoom=7 }else if(num>500&&num<=1000){ zoom=6 }else{ zoom=4 } return zoom }; render() { return ( <div id="main" style={{ width: '100%', height: 600 }}></div> ); } } export default EchartsTest;

四.引用

1.在專案的入口html檔案中引入:

需要自己申請百度地圖的金鑰,直接用下面的金鑰也可以:

<script type="text/javascript" src="http://api.map.baidu.com/api?v=3.0&ak=Imejyag6D5IPg4lOfu0LiDUWBGh2SNmc"></script>
<script type="text/javascript" src="http://api.map.baidu.com/library/Heatmap/2.0/src/Heatmap_min.js"></script>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts/extension/bmap.min.js"></script>

2.在自己頁面中引入heatMap.js

import HeatMap from './heatMap';  //引入自己寫好的熱力地圖
const arry=[
[120.1234555,35.234234,56],
[120.3287898,34.876575,10],
];
<HeatMap data={arry} /> //直接在頁面引入標籤使用 arry的資料格式你可以有自己的格式,不過你的格式改了的話,heatMap.js引數使用也得自己改下。

 

相關文章