百度地圖聚合折騰

被客戶爸爸折騰了好久,最后終于做出基本滿意的“熱力圖”

起初做的熱力圖,但是來回反復(fù)測(cè)試百度地圖熱力圖和echars熱力圖+百度地圖,終究不滿足客戶爸爸,原因很簡(jiǎn)單某些地區(qū)放大后,為什么啥也沒有了,為什么?為什么?mmmp不知當(dāng)講不當(dāng)講,熱力圖放大數(shù)據(jù)稀疏的地方當(dāng)然不明顯,遂開始改熱力圖各種配置,漸變色,透明度,無奈怎么也不滿足。最后技術(shù)總監(jiān)說干脆用某某項(xiàng)目的聚合算了,接著就進(jìn)入了正題百度地圖--聚合折騰之路。

百度地圖的聚合在文檔里不太好找,在百度地圖開源庫里才能找到

貼一下官方的簡(jiǎn)單說明
描述
BMapLib.MarkerClusterer(map, options) MarkerClusterer
方法
方法 返回值 描述
addMarker(marker) None 添加一個(gè)聚合的標(biāo)記。
addMarkers(markers) None 添加要聚合的標(biāo)記數(shù)組。
clearMarkers() None 從地圖上徹底清除所有的標(biāo)記
getClustersCount() Number 獲取聚合的總數(shù)量。
getGridSize() Number 獲取網(wǎng)格大小
getMap() Map 獲取聚合的Map實(shí)例。
getMarkers() Array 獲取所有的標(biāo)記數(shù)組。
getMaxZoom() Number 獲取聚合的最大縮放級(jí)別。
getMinClusterSize() Number 獲取單個(gè)聚合的最小數(shù)量。
getStyles() Array 獲取聚合的樣式風(fēng)格集合
isAverageCenter() Boolean 獲取單個(gè)聚合的落腳點(diǎn)是否是聚合內(nèi)所有標(biāo)記的平均中心。
removeMarker(marker) Boolean 刪除單個(gè)標(biāo)記
removeMarkers(markers) Boolean 刪除一組標(biāo)記
setGridSize(size) None 設(shè)置網(wǎng)格大小
setMaxZoom(maxZoom) None 設(shè)置聚合的最大縮放級(jí)別
setMinClusterSize(size) None 設(shè)置單個(gè)聚合的最小數(shù)量。
setStyles(styles) None 設(shè)置聚合的樣式風(fēng)格集合

事實(shí)上要不是逼急一般人不會(huì)來這里看這些文檔,百度地圖的demo基本上就夠用。
由于數(shù)據(jù)量巨大,本項(xiàng)目的就有十三萬數(shù)據(jù),一股腦顯示在地圖上之后來回縮放,發(fā)現(xiàn)....卡,卡到?jīng)]朋友,還有就是這十三萬數(shù)據(jù)從服務(wù)器傳送到前端耗時(shí)十多秒趕上網(wǎng)速不好就得掛在半路500錯(cuò)誤碼。
無奈之下的繼續(xù)優(yōu)化,終于在苦苦搜索之后找到了網(wǎng)上大神的優(yōu)化代碼 (傳送門),不得不說效率確實(shí)大增,但是,優(yōu)化后依然扛不住13萬的數(shù)據(jù)。將來的數(shù)據(jù)可不止13萬。進(jìn)一步優(yōu)化。
結(jié)合網(wǎng)上的仿照鏈家等地圖找房例子(傳送門),規(guī)劃這樣的方案:
1.進(jìn)行區(qū)域劃分,省、市、區(qū)對(duì)應(yīng)不同的地圖等級(jí)顯示不同等級(jí)的數(shù)據(jù),這樣起碼就解決的全國(guó)數(shù)據(jù)時(shí)有十三萬的尷尬,這樣折合下來全國(guó)數(shù)據(jù)也就30多條,效率不言而喻的高!
2.省、市、區(qū)級(jí)別之后進(jìn)行自動(dòng)聚合,就是百度地圖真正的聚合。
3.為了避免數(shù)據(jù)多余營(yíng)銷效率,每次請(qǐng)求只請(qǐng)求可視區(qū)域內(nèi)的數(shù)據(jù)。

然后是甩鍋到本渣渣這里,開始自我猜想:
1.地圖要做到拖動(dòng)請(qǐng)求,縮放請(qǐng)求
2.每次請(qǐng)求都要知道現(xiàn)在的地圖縮放級(jí)別是多少,以便判斷當(dāng)前是省、市、區(qū)
3.每次請(qǐng)求還要有地圖可視的區(qū)域經(jīng)緯度
4.省市區(qū)要用地圖的自定義覆蓋物,Label是個(gè)不錯(cuò)的選擇
5.請(qǐng)求數(shù)據(jù)地圖級(jí)別省市區(qū)的時(shí)候,數(shù)據(jù)要包含行政區(qū)域名稱,人數(shù),經(jīng)緯度
基本上是這樣,然后開始搞

獲取地圖縮放級(jí)別
var zoom = map.getZoom();  // 直接返回?cái)?shù)字1-20吧好像

獲取可視區(qū)域的經(jīng)緯度
getBounds()
用法
var bs = map.getBounds();     // 獲取可視區(qū)域
var bssw = bs.getSouthWest(); // bssw.lng, bssw.lat
var bsne = bs.getNorthEast(); // bsne.lng, bsne.lat
添加文本標(biāo)注label

Label(content: String, opts: LabelOptions)
創(chuàng)建一個(gè)文本標(biāo)注實(shí)例。point參數(shù)指定了文本標(biāo)注所在的地理位置
|方法|返回值| 描述|
|setStyle(styles: Object)|none|設(shè)置文本標(biāo)注樣式,該樣式將作用于文本標(biāo)注的容器元素上。其中styles為JavaScript對(duì)象常量,比如: setStyle({ color : "red", fontSize : "12px" }) 注意:如果css的屬性名中包含連字符,需要將連字符去掉并將其后的字母進(jìn)行大寫處理,例如:背景色屬性要寫成:backgroundColor|
|setContent(content: String)|none|設(shè)置文本標(biāo)注的內(nèi)容。支持HTML|
|setPosition(position: Point)|none|設(shè)置文本標(biāo)注坐標(biāo)。僅當(dāng)通過Map.addOverlay()方法添加的文本標(biāo)注有效|

然后就是代碼說事兒

var map = new BMap.Map("echars_heatmap", {minZoom:5});
var BMapLib = window.BMapLib = BMapLib || {};        
var Page = {
        init: function() {
            this.initMap();
            this.echarsHeatmap(this.data.heatmapDatas);
        },
        data:{
            timerMapGetdata:null,
            zoom: 5,
            condition:{
                zoom: 5,
                level: 1,
            },
            markerClusterer: null,
            heatmapDatas: {
                centerPoint: null,
                points: null,
                lengthMax: 0
            },
        },
        // 初始化地圖觸發(fā)事件
        initMap:function(){
            var _this = this;
            var point = new BMap.Point(116.403981, 39.91571);

            // 設(shè)置中心點(diǎn)坐標(biāo)和地圖級(jí)別 5全國(guó) 10省
            map.centerAndZoom(point, 5); 

            // 允許滾輪縮放
            map.enableScrollWheelZoom(); 
            
            // 獲取地圖的當(dāng)前縮放級(jí)別
            _this.data.zoom = map.getZoom(); 

             // 地圖縮放事件,每次縮放都要重新觸發(fā)獲取視野內(nèi)新數(shù)據(jù)
            map.addEventListener('zoomend', function(){
                console.log('縮放事件:');
                // 事件觸發(fā)優(yōu)化,防止短時(shí)間內(nèi)多次觸發(fā)事件頻繁請(qǐng)求數(shù)據(jù),造成服務(wù)器壓力,同時(shí)也為了用戶體驗(yàn)
                clearTimeout(_this.data.timerMapGetdata);
                _this.data.timerMapGetdata = null;
                _this.data.timerMapGetdata = setTimeout(function() {
                    _this.getHeatData();
                }, 300);
            });

             // 地圖拖動(dòng)事件,每次拖動(dòng)地圖,地圖的可視區(qū)域都會(huì)變化,都要重新觸發(fā)獲取視野內(nèi)新數(shù)據(jù)
            map.addEventListener("moveend", function(){
                console.log('拖動(dòng)事件:');
                // 事件觸發(fā)優(yōu)化,防止短時(shí)間內(nèi)多次觸發(fā)事件頻繁請(qǐng)求數(shù)據(jù),造成服務(wù)器壓力,同時(shí)也為了用戶體驗(yàn)
                clearTimeout(_this.data.timerMapGetdata);
                _this.data.timerMapGetdata = null;
                _this.data.timerMapGetdata = setTimeout(function() {
                    _this.getHeatData();
                }, 300);
            });
        },
         getHeatData: function() {
            var _this = this;
            // layer彈窗插件,加載彈窗
            layer.load(1, {
                shade: [0.1, '#fff'],
            });
            //獲取可視區(qū)域
            var bs = map.getBounds();   
            var bssw = bs.getSouthWest();
            var bsne = bs.getNorthEast();

            // 獲取地圖縮放級(jí)別,并且劃分請(qǐng)求級(jí)別
            var zoom = map.getZoom();
            var level = (zoom < 8) ? 1 : ((zoom >= 8 && zoom <= 11) ? 2 : ((zoom > 11 && zoom < 15) ? 3 : 4));
            _this.data.level = level;
            _this.data.condition['level'] = level;
            _this.data.condition['left'] = bssw.lng;
            _this.data.condition['top'] = bssw.lat;
            _this.data.condition['right'] = bsne.lng;
            _this.data.condition['bottom'] = bsne.lat;
            $.ajax({
                url: '',
                data: {
                    condition: _this.data.condition
                },
                type: 'POST',
                success: function(res) {
                    layer.closeAll();
                     
                     // 重新渲染地圖
                    _this.data.heatmapDatas.points = res.data;
                    _this.data.heatmapDatas.lengthMax = res.max;
                    _this.echarsHeatmap(_this.data.heatmapDatas);
                }
            });
        },

        // 熱力圖
        echarsHeatmap: function(obj) {
            var _this = this;
            var points = obj.points ? obj.points : [];
            _this.data.zoom = map.getZoom();
            var zoom = Number(_this.data.zoom);
            var level = _this.data.level;
            // 清除覆蓋物
            map.clearOverlays();
            if(_this.markerClusterer) {
                _this.markerClusterer.clearMarkers();
            }
            // 1.當(dāng)請(qǐng)求級(jí)別是4的時(shí)候,百度地圖自動(dòng)集合;
            // 2.當(dāng)請(qǐng)求級(jí)別小于4,使用后臺(tái)做聚合查詢,前端label展示。
            if (level != 4) {
                for (var i = 0; i < points.length; i++) {
                    _this.addLabel(points[i]);
                }
            } else {
                var markers = [];
                var pt = null;
                for (var i = 0; i < points.length; i++) {
                    pt = new BMap.Point(points[i].lng, points[i].lat);
                    var maker = new BMap.Marker(pt);
                    maker.setTitle(points[i].macs);
                    markers.push(maker);
                }
                //最簡(jiǎn)單的用法,生成一個(gè)marker數(shù)組,然后調(diào)用markerClusterer類即可。
                var tt1 = new Date().getTime();
                _this.markerClusterer = new BMapLib.MarkerClusterer(map, {markers:markers});
                var tt2 = new Date().getTime();
            }

        },
        // 添加覆蓋物
        addLabel: function(pointObj) {
            var zoom = Number(this.data.zoom);

            var point = new BMap.Point(pointObj.longitude, pointObj.latitude);
            var label = new BMap.Label();
            label.setStyle({
                maxWidth: 'none',
                color:"#fff",                   //顏色
                fontSize:"12px",               //字號(hào)
                border:"0",                    //邊
                borderRadius: '50%',
                height:"60px",                //高度
                width:"60px",                 //寬
                textAlign:"center",            //文字水平居中顯示
                background: "#F99D32",
                cursor:"pointer",
                position: "absolute",
                left: "-60px",
                top: "-60px"
            });

            var content = '<p style="text-align:center;margin-top:16px;">'+ pointObj.name +'</p>';
                content += '<p style="text-align:center;">'+ pointObj.macs +'</p>';
            label.setContent(content);
            label.setPosition(point);
            label.addEventListener("click", function() {  //拖動(dòng)事件
                map.centerAndZoom(point, zoom+2)
            });
            map.addOverlay(label);
        },
};

后臺(tái)用的php,這里貼上sql說明一下問題就行

當(dāng)請(qǐng)求級(jí)別是小于4的時(shí)候,sql如下:

/*
接受參數(shù):
conditioin:{
  level: 1,
  left: 110.329288,
  right: 119.895882,
  top:31.356779,
  bottom: 36.253414
}
簡(jiǎn)要說明:
當(dāng)請(qǐng)求級(jí)別小于4的時(shí)候,后臺(tái)數(shù)據(jù)要做聚合處理,從user表查詢出所有人數(shù),通過‘order by city’做分組聚合處理(根據(jù)請(qǐng)求級(jí)別 省1:state,市2:city,區(qū)3:country),然后關(guān)聯(lián)address表(tf_address_region表存儲(chǔ)中國(guó)所有的省市區(qū)對(duì)應(yīng)的名字、經(jīng)緯度,具體表結(jié)構(gòu)和sql文件在最下邊展示與下載)查詢出每個(gè)地址對(duì)應(yīng)的經(jīng)緯度
*/
SELECT A.NAME,
    A.LEVEL,
    A.longitude,
    A.latitude,
    b.member_skey 
FROM
    tf_address_region A,
    (
    SELECT
        city, /*省1:state,市2:city,區(qū)3:country*/
        COUNT ( 1 ) member_skey 
    FROM
        td_user 
    WHERE
        ( 
            AND ( latitude <> '' AND longitude IS NOT NULL ) 
            AND ( latitude BETWEEN 31.356779 AND 36.253414 AND longitude BETWEEN 110.329288 AND 119.895882 ) 
        ) 
    GROUP BY
        city  /*省1:state,市2:city,區(qū)3:country*/
    ) b 
WHERE
    ( b.city = A.NAME ) /*省1:state,市2:city,區(qū)3:country*/

當(dāng)請(qǐng)求級(jí)別是4的時(shí)候,sql如下:

/*
接受參數(shù):
conditioin:{
  level: 4,
  left: 113.622692
  right: 113.697431
  top:34.739694,
  bottom: 34.775282
}
簡(jiǎn)要說明:
當(dāng)請(qǐng)求級(jí)別等于4的時(shí)候,后臺(tái)數(shù)據(jù)不要做聚合處理,直接從user表查詢出所有人數(shù),聚合工作直接給百度地圖處理
*/
SELECT
    longitude lng,
    latitude lat,
    COUNT ( 1 ) member_skey 
FROM
    td_user
WHERE
    ( 
        AND ( latitude <> '' AND longitude IS NOT NULL ) 
        AND ( latitude BETWEEN 34.739694 AND 34.775282 AND longitude BETWEEN 113.622692 AND 113.697431 ) 
    ) 
GROUP BY
    latitude,
    longitude
效果圖
map1

map2

map3

map4
附加address表的說明:

表結(jié)構(gòu):

名稱 類型 備注
id bigint(11) 表id
name varchar(32) 地區(qū)名稱
level tinyint(4) 地區(qū)等級(jí) 分省市縣區(qū)
pid bigint(10) 父id
longitude float 百度坐標(biāo),經(jīng)度
latitude float 百度坐標(biāo),緯度

address 表sql下載地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容