這段時間需要實現百度地圖的一些展示效果,雖然最終效果做出來了,可是這中間也走了很多的彎路,希望有用到的可以直接拿來用,少走一些彎路。百度地圖為開發者提供了一系列的接口,點百度接口去百度接口。本文主要用到了以下幾個效果:
1、熱力圖顯示
2、自定義圖標的聚合顯示
3、雲麻點顯示
熱力圖顯示
百度地圖熱力圖是通過設置熱力圖半徑、顏色、透明度等參數直觀展示數據分布情況,而我這段時間所做的,就是通過一段時間內的訂單數量,再結合經緯度,在地圖上顯示熱力分布圖。百度地圖官方的API給的示例很Easy,創建地圖實例,初始化參數,生成點的數組,然后創建熱力圖對象,並將熱力圖對象添加到地圖實例,設置數據源,這樣這個熱力圖的效果就可以展示出來了。(當然申請AK和引入Js就不用說了),這部分源碼比較簡單,就不展示了,只需要看熱力圖API相信都是可以明白的。
可是自己做的時候,效果卻沒這么容易出來。我在做的過程中,開始最大的一處疑問就是,其他代碼部分代碼都跟官方API示例一樣,只是換了個數據源,熱力圖效果就是出不來。后來在試了無數遍之后,發現如果當前地圖放大的級別過大,也就是有些點沒有在當前地圖可視區域內展示的話,熱力圖效果是不會出來的!當然了,關於這點沒有百度地圖官方的解釋,也沒有得到求證,只是我在開發過程中發現的一個解決方案。如果你也在做這部分,恰好遇到這個問題的話不妨試試。其次為了效果明顯,最好是多添加幾個點,這樣效果會更好一些。以下是效果圖:
自定義圖標聚合顯示
說到這個聚合顯示,哎,說多了都是淚啊。本來提出的需求只是需要將數據庫中的店鋪展示出來,然后點擊的時候彈出詳細信息的提示框。我一想,這還不簡單?通過Handler從后台獲取到數據,然后以Json的格式傳給前台,然后來個for循環,依次添加到地圖不就解決了?第一版我確實是這么干的。拿到數據之后再做foreach……以下是Js腳本。

function LoadMakers(data) { var Maker = "["; $(data).each(function () { var sContent = "<div style='text-align: left;'><h4 style='margin:0 0 5px 0;padding:0.2em 0'>" + this.pointerTitle + "</h4>"; if (this.pointImg) { sContent = "<div style='text-align: left;'><h4 style='margin:0 0 5px 0;padding:0.2em 0; width:300px'>" + this.pointerTitle + "</h4>"; sContent += "<img style='float:left;margin:4px;width:104px;height:69px' id='imgDemo' src='" + this.pointImg + "' title='" + this.pointerTitle + "'/>"; } if (this.address) { if (this.pointImg) { sContent += "<p style='margin:0;line-height:1.5;font-size:13px;width:200px;margin-left:110px'>" + this.address + "</p>"; } else { sContent += "<p style='margin:0;line-height:1.5;font-size:13px;width:200px;'>" + this.address + "</p>"; } } if (this.pointerContent) { if (this.pointImg) { sContent += "<p style='margin:0;line-height:1.5;font-size:13px;width:200px;margin-left:110px'>" + this.pointerContent + "</p>"; } else { sContent += "<p style='margin:0;line-height:1.5;font-size:13px;width:200px;'>" + this.pointerContent + "</p>"; } } sContent += "</div>"; Maker += "{supId:"+this.spId+",Longitude:" + this.markersLongitude + ",Dimension:" + this.markersDimension + ",Window: { LoadEvent:'click',Content:\"" + sContent + "\"},enableMassClear:true},"; }); Maker += "]"; return eval(Maker); }
我在本地添加了將近100個點的數據,測試沒問題之后就提交到Svn了,然后跟項目經理說這個東東做完了。然后項目經理將數據切成正式數據,叫我給他演示一下。這一演示不要緊啊,臉全沒了。正式數據庫中有5000多條數據,而我取數據時后台做了過濾,只提取有經緯度的點,即使這樣也有1500多條記錄。沒錯,1500多條,這意味着瀏覽器客戶端這邊要循環1500多次來做這個操作!然后奇跡就發生了,頁面就假死了!無論怎么刷新都沒有效果了,即使等了很長時間地圖加載出來了,只要稍微一放大或者移動地圖頁面就會直接掛掉。然后項目經理就一頓巴拉巴拉巴拉,這就是你給我的結果么?
誰讓我確實這個東東沒做好呢?也沒臉跟別人爭辯,拿回來重新研究吧。於是在網上搜了一下,發現很多人也都遇到了這個問題,就是當點大於一定的數量之后,瀏覽器都會出現假死的情況。然后上百度論壇瀏覽了一下,確實有提到這個問題,當點的數量在150個以內時,這么做是沒有問題的,但當點數量大於300個時,這么做就行不通了。百度給出了兩種解決方案:
No1:Marker聚合:http://tieba.baidu.com/f?kz=1031097376
No2:數據抽希:比如有10個marker,選擇其中6個做為顯示點。
這里對比了一下需求,Marker聚合是將所有的點聚合起來,顯示一個總數,然后點擊的時候再分開;而數據抽希字面的意思是從中選取一部分來展示。而我所接到的任務是展示所有點,顯然數據抽希這里是用不上了,那就來聚合吧。這是第二版。
首先簡單介紹一下,百度聚合展示提供了開源的類庫,只需要在頁面中引用即可。以下是Js腳本代碼:

var map = option.Map||new BMap.Map("MapContent"); // 創建地圖實例 var point = new BMap.Point(116.418261, 39.921984); map.centerAndZoom(point, 10); // 初始化地圖,設置中心點坐標和地圖級別 map.enableScrollWheelZoom(); // 允許滾輪縮放 map.enableKeyboard = true; map.addControl(new BMap.NavigationControl()); map.addControl(new BMap.ScaleControl()); // 啟用比例尺。 map.addControl(new BMap.MapTypeControl()); // 是否啟用衛星地圖等等。 var MAX = resultData.count; var markers = []; var pt = null, mypt = null; var i = 0; var infoWindow; var myIcon = new BMap.Icon("/Admin/Images/red.png", new BMap.Size(28, 37), { }); for (; i < MAX; i++) { pt = new BMap.Point(resultData.data[i].Longitude, resultData.data[i].Dimension); mypt = new BMap.Marker(pt); mypt.setIcon(myIcon); mypt.addEventListener("click", function (no) { return function () { infoWindow = LoadMakerInfo(resultData.data[no]); this.openInfoWindow(infoWindow); //createInfoWin(resultData.data[no]).redraw(); }; } (i)); markers.push(mypt); } ////最簡單的用法,生成一個marker數組,然后調用markerClusterer類即可。 var markerClusterer = new BMapLib.MarkerClusterer(map, { markers: markers.reverse(), isAverangeCenter: true,girdSize:100,maxZoom:15 }); }
這段代碼大部分都很簡單,只有一個地方做個簡單的說明,網上很多人也問到了這里。就是說循環添加點,然后給每一個點都綁定了Click事件,然后實際點擊的時候卻彈出的內容都是一樣的。這是由於Js的作用域以及閉包引起的,詳情請看我的另一篇博客:那些必須要知道的Javascript,這里不再贅述。其中代碼里setIcon是為了把百度默認的圖標換成我們自己預先設定的圖標,下圖是運行之后的效果:
與第一版相比,性能確實有了很大的改進,至少在本地加載個1500多條數據已經沒問題了。可是通過聚合之后又引入了新的問題,就像上圖中的綠色圓圈,有兩個4,一個6,正常這些數據點擊之后都會散開的,當一直點擊的時候應該可以看到沒有聚合的點。可是這里出現的問題是有些可以點開,有些居然點不開。開始我以為是數據的問題,以為相隔比較近的點是無法展開的,后來就對數據做了處理,如果有相同的數據則通過加減隨機數,確保拿出來的數據沒有相同的,可是這個問題還是沒有解決。希望有了解的朋友可以告知一二。通過這樣聚合,展示數據確實比第一版快很多,可是當在公網上的時候,頁面縮放或者移動的時候還是會卡。所以還得重新找解決方案。
這里值得一提的就是百度地圖我們可以看到那種鼠標在左邊搜索列表滑動,右邊地圖中搜到的結果跟着變化的那種效果,這里我們也是可以做的。監測左邊列表的MoueEnter事件,然后根據當前Enter的點的經緯度創建一個點,然后Foreach右邊搜索結果中的點,如果通過point的equals方法,判斷是否是同一個點,如果是則改變當前的Icon即可。Js腳本:

function ReDrawMap(data) { if (data.markersLongitude && data.markersDimension) { var tmpInfo = LoadMakerInfo(data); var point = new BMap.Point(data.markersLongitude, data.markersDimension); // 創建點坐標 var mkrs = option.Map.getOverlays(); var position; for (var i = 0; i < mkrs.length; i++) { position = mkrs[i].point; if (position&&position.equals(point)) { mkrs[i].openInfoWindow(tmpInfo); } } } }
雲麻點效果展示
最初沒有用這個雲麻點展示效果是因為一些敏感數據,可是后來發現沒有其他合適的解決方案的時候,就采用了這個方案。雲麻點效果也就是LBS雲存儲,用戶將數據存放在百度的雲服務器,然后直接從百度雲獲取數據。這個流程是:先在LBS開放平台創建數據表,然后添加需要的字段,然后就可以在后台批量上傳數據。數據上傳至百度服務器后,會生產相應的雲麻點圖層,這些圖層在加載的時候直接被展示出來,這樣可以很大程度上提高性能。現在假設后台數據已經准備完畢了,前台所作的操作:
首先,初始化地圖的數據:

var map = new BMap.Map("MapContent"); //初始化數據 map.enableScrollWheelZoom(); // 啟用滾輪放大縮小 map.enableContinuousZoom(); // 啟用地圖慣性拖拽,默認禁用 map.enableInertialDragging(); // 啟用連續縮放效果,默認禁用。 map.addControl(new BMap.NavigationControl()); // 添加平移縮放控件 map.addControl(new BMap.ScaleControl()); // 添加比例尺控件 map.addControl(new BMap.OverviewMapControl()); // 添加縮略地圖控件 map.addControl(new BMap.MapTypeControl()); // 添加地圖類型控件 map.centerAndZoom(new BMap.Point(116.418261, 39.921984), 5); // 初始化地圖,設置中心點坐標和地圖級別 map.addControl(new BMap.NavigationControl()); // 啟用魚骨頭。 map.setCurrentCity("北京"); //由於有3D圖,需要設置
然后,設置篩選的條件:

//檢索模塊相關代碼 var keyword = "", //檢索關鍵詞 page = 0, //當前頁碼 points = [], //存儲檢索出來的結果的坐標數組 customLayer = null; //麻點圖層 customLayer = new BMap.CustomLayer({ geotableId: 00000,//后台生成表的Id q: '', //檢索關鍵字 tags: '', //空格分隔的多字符串 filter: '' //過濾條件,參考http://developer.baidu.com/map/lbs-geosearch.htm#.search.nearby }); //新建麻點圖圖層對象 map.addTileLayer(customLayer); //將麻點圖添加到地圖當中 customLayer.addEventListener('hotspotclick', hotspotclickCallback); //給麻點圖添加點擊麻點回調函數 /** * 麻點圖點擊麻點的回調函數 * @param 麻點圖點擊事件返回的單條數據 */ function hotspotclickCallback(e) { if (e.content.phone == "NULL") { e.content.phone = "暫無"; } var customPoi = e.customPoi, str = []; str.push("address = " + customPoi.address); str.push("phoneNumber = " + customPoi.phoneNumber); var content ='<img src="' + e.content.shopPhoto + '"style="width:111px;height:83px;float:left;margin-right:5px;"/>'+ '<p style="width:280px;margin:0;line-height:20px;">地址:' + customPoi.address + '</p>'; content += '<p style="width:280px;margin:0;line-height:20px;">電話:' + e.content.phone + '</p>'; //創建檢索信息窗口對象 var searchInfoWindow = new BMapLib.SearchInfoWindow(map, content, { title: e.content.nickName, //標題 width: 290, //寬度 height: 40, //高度 enableAutoPan: true, //自動平移 enableSendToPhone: true, //是否顯示發送到手機按鈕 searchTypes: [ BMAPLIB_TAB_SEARCH, //周邊檢索 BMAPLIB_TAB_TO_HERE, //到這里去 BMAPLIB_TAB_FROM_HERE //從這里出發 ] }); var point = new BMap.Point(customPoi.point.lng, customPoi.point.lat); searchInfoWindow.open(point); //打開信息窗口 }
之后就是最關鍵的一步,請求數據:

var url = "http://api.map.baidu.com/geosearch/v2/local?callback=?"; $.getJSON(url, { 'q': '', //檢索關鍵字 'page_index': page, //頁碼 // 'filter': filter.join('|'), //過濾條件 'region': '131', //北京的城市id 'scope': '2', //顯示詳細信息 'geotable_id': 00000, 'page_size': 12, 'ak': 'XXXXXXXX' //用戶ak }, function (e) { renderMap(e, page + 1); });
完了之后再調整一下樣式,結果如圖:
通過LBS雲存儲,可以很好的解決海量數據加載緩慢的問題,但是這樣做的缺點就是一方面數據必須上傳到百度的雲服務器,另一方面數據維護還是比較麻煩的,必須得通過接口來操作。以上是最近做百度接口總結的一些問題,希望對您能有所幫助,如果您遇到了跟我類似的問題還沒有解決,歡迎隨時跟我交流。如果您覺得文章對您有一點點作用,一定要頂哦,原創不容易啊!