百度地图聚合折腾

被客户爸爸折腾了好久,最后终于做出基本满意的“热力图”

起初做的热力图,但是来回反复测试百度地图热力图和echars热力图+百度地图,终究不满足客户爸爸,原因很简单某些地区放大后,为什么啥也没有了,为什么?为什么?mmmp不知当讲不当讲,热力图放大数据稀疏的地方当然不明显,遂开始改热力图各种配置,渐变色,透明度,无奈怎么也不满足。最后技术总监说干脆用某某项目的聚合算了,接着就进入了正题百度地图--聚合折腾之路。

百度地图的聚合在文档里不太好找,在百度地图开源库里才能找到

贴一下官方的简单说明
描述
BMapLib.MarkerClusterer(map, options) MarkerClusterer
方法
方法 返回值 描述
addMarker(marker) None 添加一个聚合的标记。
addMarkers(markers) None 添加要聚合的标记数组。
clearMarkers() None 从地图上彻底清除所有的标记
getClustersCount() Number 获取聚合的总数量。
getGridSize() Number 获取网格大小
getMap() Map 获取聚合的Map实例。
getMarkers() Array 获取所有的标记数组。
getMaxZoom() Number 获取聚合的最大缩放级别。
getMinClusterSize() Number 获取单个聚合的最小数量。
getStyles() Array 获取聚合的样式风格集合
isAverageCenter() Boolean 获取单个聚合的落脚点是否是聚合内所有标记的平均中心。
removeMarker(marker) Boolean 删除单个标记
removeMarkers(markers) Boolean 删除一组标记
setGridSize(size) None 设置网格大小
setMaxZoom(maxZoom) None 设置聚合的最大缩放级别
setMinClusterSize(size) None 设置单个聚合的最小数量。
setStyles(styles) None 设置聚合的样式风格集合

事实上要不是逼急一般人不会来这里看这些文档,百度地图的demo基本上就够用。
由于数据量巨大,本项目的就有十三万数据,一股脑显示在地图上之后来回缩放,发现....卡,卡到没朋友,还有就是这十三万数据从服务器传送到前端耗时十多秒赶上网速不好就得挂在半路500错误码。
无奈之下的继续优化,终于在苦苦搜索之后找到了网上大神的优化代码 (传送门),不得不说效率确实大增,但是,优化后依然扛不住13万的数据。将来的数据可不止13万。进一步优化。
结合网上的仿照链家等地图找房例子(传送门),规划这样的方案:
1.进行区域划分,省、市、区对应不同的地图等级显示不同等级的数据,这样起码就解决的全国数据时有十三万的尴尬,这样折合下来全国数据也就30多条,效率不言而喻的高!
2.省、市、区级别之后进行自动聚合,就是百度地图真正的聚合。
3.为了避免数据多余营销效率,每次请求只请求可视区域内的数据。

然后是甩锅到本渣渣这里,开始自我猜想:
1.地图要做到拖动请求,缩放请求
2.每次请求都要知道现在的地图缩放级别是多少,以便判断当前是省、市、区
3.每次请求还要有地图可视的区域经纬度
4.省市区要用地图的自定义覆盖物,Label是个不错的选择
5.请求数据地图级别省市区的时候,数据要包含行政区域名称,人数,经纬度
基本上是这样,然后开始搞

获取地图缩放级别
var zoom = map.getZoom();  // 直接返回数字1-20吧好像

获取可视区域的经纬度
getBounds()
用法
var bs = map.getBounds();     // 获取可视区域
var bssw = bs.getSouthWest(); // bssw.lng, bssw.lat
var bsne = bs.getNorthEast(); // bsne.lng, bsne.lat
添加文本标注label

Label(content: String, opts: LabelOptions)
创建一个文本标注实例。point参数指定了文本标注所在的地理位置
|方法|返回值| 描述|
|setStyle(styles: Object)|none|设置文本标注样式,该样式将作用于文本标注的容器元素上。其中styles为JavaScript对象常量,比如: setStyle({ color : "red", fontSize : "12px" }) 注意:如果css的属性名中包含连字符,需要将连字符去掉并将其后的字母进行大写处理,例如:背景色属性要写成:backgroundColor|
|setContent(content: String)|none|设置文本标注的内容。支持HTML|
|setPosition(position: Point)|none|设置文本标注坐标。仅当通过Map.addOverlay()方法添加的文本标注有效|

然后就是代码说事儿

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
            },
        },
        // 初始化地图触发事件
        initMap:function(){
            var _this = this;
            var point = new BMap.Point(116.403981, 39.91571);

            // 设置中心点坐标和地图级别 5全国 10省
            map.centerAndZoom(point, 5); 

            // 允许滚轮缩放
            map.enableScrollWheelZoom(); 
            
            // 获取地图的当前缩放级别
            _this.data.zoom = map.getZoom(); 

             // 地图缩放事件,每次缩放都要重新触发获取视野内新数据
            map.addEventListener('zoomend', function(){
                console.log('缩放事件:');
                // 事件触发优化,防止短时间内多次触发事件频繁请求数据,造成服务器压力,同时也为了用户体验
                clearTimeout(_this.data.timerMapGetdata);
                _this.data.timerMapGetdata = null;
                _this.data.timerMapGetdata = setTimeout(function() {
                    _this.getHeatData();
                }, 300);
            });

             // 地图拖动事件,每次拖动地图,地图的可视区域都会变化,都要重新触发获取视野内新数据
            map.addEventListener("moveend", function(){
                console.log('拖动事件:');
                // 事件触发优化,防止短时间内多次触发事件频繁请求数据,造成服务器压力,同时也为了用户体验
                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'],
            });
            //获取可视区域
            var bs = map.getBounds();   
            var bssw = bs.getSouthWest();
            var bsne = bs.getNorthEast();

            // 获取地图缩放级别,并且划分请求级别
            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.当请求级别是4的时候,百度地图自动集合;
            // 2.当请求级别小于4,使用后台做聚合查询,前端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);
                }
                //最简单的用法,生成一个marker数组,然后调用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",               //字号
                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() {  //拖动事件
                map.centerAndZoom(point, zoom+2)
            });
            map.addOverlay(label);
        },
};

后台用的php,这里贴上sql说明一下问题就行

当请求级别是小于4的时候,sql如下:

/*
接受参数:
conditioin:{
  level: 1,
  left: 110.329288,
  right: 119.895882,
  top:31.356779,
  bottom: 36.253414
}
简要说明:
当请求级别小于4的时候,后台数据要做聚合处理,从user表查询出所有人数,通过‘order by city’做分组聚合处理(根据请求级别 省1:state,市2:city,区3:country),然后关联address表(tf_address_region表存储中国所有的省市区对应的名字、经纬度,具体表结构和sql文件在最下边展示与下载)查询出每个地址对应的经纬度
*/
SELECT A.NAME,
    A.LEVEL,
    A.longitude,
    A.latitude,
    b.member_skey 
FROM
    tf_address_region A,
    (
    SELECT
        city, /*省1:state,市2:city,区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,区3:country*/
    ) b 
WHERE
    ( b.city = A.NAME ) /*省1:state,市2:city,区3:country*/

当请求级别是4的时候,sql如下:

/*
接受参数:
conditioin:{
  level: 4,
  left: 113.622692
  right: 113.697431
  top:34.739694,
  bottom: 34.775282
}
简要说明:
当请求级别等于4的时候,后台数据不要做聚合处理,直接从user表查询出所有人数,聚合工作直接给百度地图处理
*/
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表的说明:

表结构:

名称 类型 备注
id bigint(11) 表id
name varchar(32) 地区名称
level tinyint(4) 地区等级 分省市县区
pid bigint(10) 父id
longitude float 百度坐标,经度
latitude float 百度坐标,纬度

address 表sql下载地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容