vue项目百度地图全攻略(含打点数据优化)

5,063 阅读4分钟

        现在很多项目对可视化数据要求越来越高了,涉及到地点的地方几乎都要能给地图展示,最近在项目中遇到了地图展示以及数据打点问题,特此总结一下。

  附所参考百度地图api:

开发平台:lbsyun.baidu.com/

地图示例:lbsyun.baidu.com/jsdemo.htm#…

基础类:lbsyun.baidu.com/cms/jsapi/r…

BMapLibapi.map.baidu.com/library/Geo…

        demo效果如下图:


引入百度地图

        项目中引入百度地图,首先要注册成为百度地图开发者,然后获取ak密钥,直接按官网步骤就可以了,然后引入项目中

<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak="></script>

       这种方法的原理,就是直接给全局widow对象添加一个BMap对象,从而可以使我们在已经加载api的页面的任何地方,使用百度地图的api。

百度地图初始化

       百度地图初始化要注意,一定要给地图div一定的宽高,要在mounted函数中进行。

        

<script>
    mounted(){
        this.mapInit(this.point);
    },
    methods:{
         mapInit(point) {
            let vue = this;
            vue.map = new BMap.Map(vue.$refs.myMap);
            let marker = new BMap.Point(vue.point.longitude, vue.point.latitude);
            // 设置中心点以及缩放级别
            vue.map.centerAndZoom(marker, this.zoom?this.zoom:11);
            if(this.controlShow){
                vue.map.addControl(new BMap.NavigationControl());
                //添加缩放控件
            }
             //滚轮控制缩放
            vue.map.enableScrollWheelZoom(true); 
            //双击缩放控制
            vue.map.enableDoubleClickZoom(false);
            //自定义点标注样式 Icon()中有3个参数:
             String类型的图片地址url;Size类型的图片大小;
             和可选参数IconOptions
            if (this.addOverlay) {
                let myIcon = new BMap.Icon(img, new BMap.Size(202, 120), {
                    anchor: new BMap.Size(110, 65),
                })
                let marker = new BMap.Marker(point, {icon: myIcon});
                vue.map.addOverlay(marker);//打点
            }
            // 事件监听   获取点击点坐标 地图点击事件
            vue.map.addEventListener("click", (e) => {
                var center = vue.map.getCenter();
                if (vue.clickadd) {
                    this.addOneOverlay(e.point);
                }
                vue.$emit("mapClick", center);
            });
            // 设置地图风格样式  可自定义
            vue.map.setMapStyle(this.mapStyle);
           /**  tilesloaded 地图加载完成事件 */
            vue.map.addEventListener("tilesloaded", ()=>{
                let area = vue.map.getBounds();
                this.$emit("visibleArea",area);
                if(this.vevisibleFalg){
                    this.visibleAreaPonit();
                }
            });
            //对地图级别变化、移动结束后 监听获取可视区域
            vue.map.addEventListener("zoomend", ()=>{
                let area = vue.map.getBounds();
                let zoomSize = vue.map.getZoom();
                console.log(zoomSize);
                this.$emit("visibleArea",area);
                if(this.vevisibleFalg){
                    this.visibleAreaPonit();
                }
            });
            vue.map.addEventListener("moveend", ()=>{
                let area = vue.map.getBounds();
                this.$emit("visibleArea",area);
                if(this.vevisibleFalg){
                    this.visibleAreaPonit();
                }
            });
        },
    }
</script>

百度地图打点

    百度地图打点三种:正常打点、海量点、点聚合。正常打点可以设置点坐标的样式,如图片,大小等,但是适合数量较少的打点需求,500到一千以内还不算太卡。对于大量点的渲染,可以用海量点完成,海量点不能自定义点icon,百度地图给了几种固定的样式供选择。百度地图还提供了点聚合功能,可以使一定区域内的点汇聚,显示更加直观

需要注意的的是海量点、点聚合并没有在百度地图的默认包中,需要另外引入MarkerClusterer_min.js    TextIconOverlay.js'  

三种打点,代码如下:

// 创建点
createPoint(){
    this.points = [];
    let i = 0;
    for (; i < this.pointLength; i++) {
        let pt = new BMap.Point(Math.random() * 40 + 85, Math.random() * 30 + 21);
        pt.id = i+1;
        this.points.push(pt);
    }
},  
//  正常打点
  addPoint(points){
           points.forEach(point => {
                let vue = this; 
                let myIcon = new BMap.Icon(img, new BMap.Size(202, 120), {
                     anchor: new BMap.Size(110, 65),
                 })
                 let marker = new BMap.Marker(point,{icon: myIcon});
        // 创建标注
                  marker.addEventListener('click', function(){ 
                           console.log(point,'click');
                            vue.$emit('pointClick',point); 
                  });
                  this.map.addOverlay(marker);
          });
 },
         //  海量点
 addMassivePoint(points,color){
     let vue = this;
    // 判断当前浏览器是否支持绘制海量点
     if (document.createElement('canvas').getContext) {  
           let options = {
                 size: BMAP_POINT_SIZE_SMALL, 
                 shape: BMAP_POINT_SHAPE_CIRCLE,
                color: color?color:'red',
           }
          let pointCollection = new BMap.PointCollection(points, options);
           // 初始化PointCollection 
          pointCollection.addEventListener('click', function (e) {
                 // console.log(e)
                 // alert('单击点的坐标为:' + e.point.lng + ',' + e.point.lat);
                 // 监听点击事件            
                let element = ''
                for (var i = 0; i < points.length; i++) {
                if (points[i].lng == e.point.lng && points[i].lat == e.point.lat)
                    {
                         element = points[i];
                         break;
                    } 
                 }
                 vue.$emit('pointClick',element);
            });
                    this.map.addOverlay(pointCollection);  // 添加Overlay 
      } else {
          alert('请在chrome、safari、IE8+以上浏览器查看本示例');
      }
}, 
       // 点聚合
 addAggreGationPoint(){
    console.log(BMapLib)
    this.createPoint(); 
    this.clearPonit();
    let markers = [];
    this.points.forEach(point => {
         let marker = new BMap.Marker(point);
            // 创建标注
         markers.push(marker)
   });
   this.markerClusterer = new BMapLib.MarkerClusterer(this.map, {markers:markers});
},
        // 清除点标记
clearPonit(){
    this.map.clearOverlays();// 清除原标记
        if(this.markerClusterer){
        this.markerClusterer.clearMarkers()
    }
},

大数据打点优化方案

     1.分批请求,分批打点

        我们项目打点数据目前有二十多万,需求要二十万点位全部显示在地图中,需要解决两个问题,一个是二十万数据点前后端传输问题,二是二十万点位打点地图卡顿问题,经研究采用ajax轮询,分批请求,分批带点的方案,每次请求五千数据,五千数据打点,依次完成二十万数据的打点,除此之外,后端给的数据还需要精简,经纬度用x、y代替,这样二十万数据可以控制到10m左右。

    2.只显示可视区域内的点位

    如果项目没有需求要显示所有点的话,可以每次只显示可视区域内的点,百度有相关的函数,核心代码如下:

需要注意的是:计算可视区域的点位方法并不在百度地图默认包中,需要另外引入:importBMapLib_GeoUtils.js

  /**  tilesloaded 地图加载完成事件 */
    vue.map.addEventListener("tilesloaded", ()=>{ 
         let area = vue.map.getBounds();
         this.$emit("visibleArea",area);
         if(this.vevisibleFalg){
              this.visibleAreaPonit();
         }
    });
    //对地图级别变化、移动结束后 监听获取可视区域
    vue.map.addEventListener("zoomend", ()=>{
          let area = vue.map.getBounds();
          let zoomSize = vue.map.getZoom();
          console.log(zoomSize);
          this.$emit("visibleArea",area);
          if(this.vevisibleFalg){
             this.visibleAreaPonit();
          }
    });
    vue.map.addEventListener("moveend", ()=>{
          let area = vue.map.getBounds();
          this.$emit("visibleArea",area);
          if(this.vevisibleFalg){
              this.visibleAreaPonit();
          }
    });
// 设置地图缩放级别
  setZoom(zoom){
      this.map.setZoom(zoom);
  }, 
  visibleAreaPonit(first){
      if(first)this.setZoom(11);
      this.createPoint();
      this.clearPonit();
      this.vevisibleFalg = true;
      let j = 0;
      for(let i=0;i<this.points.length;i++){ 
          let point = this.points[i]; 
          let marker = new BMap.Marker(point);
          let result = BMapLib.GeoUtils.isPointInRect(point, this.map.getBounds());
          // if(result == true && j<10)
          if(result == true)
          {
              this.map.addOverlay(marker);
              ++j;
          }
          // else if(result == true && j>=10)
          // { 
          //     ++j;
          //     this.map.removeOverlay(marker);
          // }
          else this.map.removeOverlay(marker); 
     }
  }

        当数据达到一定量级以后,个人认为一次性显示所有点其实没什么意义,用户看到的也只是密密麻麻的一片,没有任何价值。具体项目如何打点,这取决于具体项目需求。

demo完整代码如下:

<template>
    <div class="map-point">
        <div class="button">
            <el-input class="input-number" v-model="pointLength"
                 placeholder="请输入生成点数量" type="number">
            </el-input>
            <el-select v-model="select_data.value" 
               :placeholder="select_data.placeholder"> 
               <el-option
                    v-for="item in select_data.options"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value">
                </el-option>
            </el-select>
            <el-button @click="addOneOverlayBatch">打点</el-button>
            <el-button @click="addAggreGationPoint">点聚合</el-button>
            <el-button @click="clearPonit">清除点</el-button>
            <el-button @click="visibleAreaPonit(true)">可见区域打点</el-button>
        </div>
        <div ref="myMap" :style="mapStyle" id="myMap" class="myMap"></div>
    </div>
</template>
<script>
import '../assets/js/mapJs/MarkerClusterer_min.js'
import '../assets/js/mapJs/TextIconOverlay.js'
import '../assets/js/mapJs/BMapLib_GeoUtils.js'
import img from "../assets/imgs/marker.png";
export default {
    name:'mapPoint',
    props:{
        controlShow:{// 是否显示缩放控件
            type:Boolean,
            default:false,
        },
        point:{           // 中心点
            type:Object,
            default(){
                return {
                    longitude: '105.000',
                    latitude: '38.000'
                }
            } 
       },
        clickadd:{        // 点击打点
            type:Boolean,
            default:false,
        },
        zoom:{           // 缩放级别 
           type:Number,
            default:5
        }
    },
    data(){
        return {
            map: '',
            mapStyle: {style: "midnight"},
            pointLength:1000,
            points:[],
            select_data:{
                placeholder:'请选择打点模式',
                options:[
                    {value:'1',label:'正常点'},
                    {value:'2',label:'海量点'},
                ],
                value:'2',
            },
            vevisibleFalg:false,
        }
    },
    mounted(){
        this.mapInit(this.point);
    },
    methods:{
        mapInit(point) {
            let vue = this;
            vue.map = new BMap.Map(vue.$refs.myMap);
            let marker = new BMap.Point(vue.point.longitude, vue.point.latitude);
            // 设置中心点以及缩放级别
            vue.map.centerAndZoom(marker, this.zoom?this.zoom:11);
            if(this.controlShow){
                vue.map.addControl(new BMap.NavigationControl());    //添加缩放控件
            }
            vue.map.enableScrollWheelZoom(true);                 //滚轮控制缩放 
           vue.map.enableDoubleClickZoom(false);                //双击缩放控制 
           //自定义点标注样式 Icon()中有3个参数:String类型的图片地址url;
           //Size类型的图片大小;和可选参数IconOptions
            if (this.addOverlay) {
                let myIcon = new BMap.Icon(img, new BMap.Size(202, 120), {
                    anchor: new BMap.Size(110, 65),
                })
                let marker = new BMap.Marker(point, {icon: myIcon});
                vue.map.addOverlay(marker);      //打点
            }
            // 事件监听   获取点击点坐标 地图点击事件
            vue.map.addEventListener("click", (e) => {
                var center = vue.map.getCenter();
                if (vue.clickadd) {
                    this.addOneOverlay(e.point);
                }
                vue.$emit("mapClick", center);
            });
            // 设置地图风格样式  可自定义
            vue.map.setMapStyle(this.mapStyle);
            /**  tilesloaded 地图加载完成事件 */
            vue.map.addEventListener("tilesloaded", ()=>{
                let area = vue.map.getBounds();
                this.$emit("visibleArea",area);
                if(this.vevisibleFalg){
                    this.visibleAreaPonit();
                }
            });
            //对地图级别变化、移动结束后 监听获取可视区域
            vue.map.addEventListener("zoomend", ()=>{
                let area = vue.map.getBounds();
                let zoomSize = vue.map.getZoom();
                console.log(zoomSize);
                this.$emit("visibleArea",area);
                if(this.vevisibleFalg){
                    this.visibleAreaPonit();
                }
            });
            vue.map.addEventListener("moveend", ()=>{
                let area = vue.map.getBounds();
                this.$emit("visibleArea",area);
                if(this.vevisibleFalg){
                    this.visibleAreaPonit();
                }
            });
            // 坐标是否在可视区域范围内
            // let result = BMapLib.GeoUtils.isPointInRect(markers[i].point, map.getBounds());
        }, 
       // 创建点
        createPoint(){
            this.points = [];
            let i = 0;
            for (; i < this.pointLength; i++) {
                let pt = new BMap.Point(Math.random() * 40 + 85, Math.random() * 30 + 21);
                pt.id = i+1;
                this.points.push(pt);
            }
        },
        // 批量打点
        addOneOverlayBatch(){
            this.createPoint();
            this.clearPonit(); 
            if(this.select_data.value === '1')           // 正常打点
            {
                this.addPoint(this.points) 
            }else if(this.select_data.value === '2')       // 海量点
            {
                this.addMassivePoint(this.points)
            }
        },
        //  正常打点
        addPoint(points){
                points.forEach(point => {
                      let vue = this;
                      let myIcon = new BMap.Icon(img, new BMap.Size(202, 120), {
                            anchor: new BMap.Size(110, 65),
                      })
                      let marker = new BMap.Marker(point,{icon: myIcon});        // 创建标注
                      marker.addEventListener('click', function(){
                            console.log(point,'click');
                            vue.$emit('pointClick',point);
                      });
                      this.map.addOverlay(marker);
               });
        },
         //  海量点
        addMassivePoint(points,color){
                let vue = this;
                if (document.createElement('canvas').getContext) {  // 判断当前浏览器是否支持绘制海量点
                    let options = {
                        size: BMAP_POINT_SIZE_SMALL,
                        shape: BMAP_POINT_SHAPE_CIRCLE, 
                        color: color?color:'red',
                    }
                    let pointCollection = new BMap.PointCollection(points, options);  // 初始化PointCollection
                    pointCollection.addEventListener('click', function (e) {
                        // console.log(e)
                        // alert('单击点的坐标为:' + e.point.lng + ',' + e.point.lat);
                       // 监听点击事件
                        let element = '' 
                       for (var i = 0; i < points.length; i++) {
                            if (points[i].lng == e.point.lng && points[i].lat == e.point.lat) {
                                element = points[i];
                                break;
                            }
                        }
                        vue.$emit('pointClick',element);
                    });
                    this.map.addOverlay(pointCollection);  // 添加Overlay
                } else {
                    alert('请在chrome、safari、IE8+以上浏览器查看本示例');
                } 
       },
        // 点聚合 
       addAggreGationPoint(){
            console.log(BMapLib)
            this.createPoint();
            this.clearPonit();
            let markers = [];
            this.points.forEach(point => {
                    let marker = new BMap.Marker(point);        // 创建标注 
                     markers.push(marker)
            });
            this.markerClusterer = new BMapLib.MarkerClusterer(this.map, {markers:markers});
        }, 
       // 清楚点标记
        clearPonit(){
            this.map.clearOverlays();// 清除原标记 
            if(this.markerClusterer){ 
               this.markerClusterer.clearMarkers()
            } 
        },
        // 设置地图缩放级别
        setZoom(zoom){
            this.map.setZoom(zoom);
        },
        visibleAreaPonit(first){ 
           if(first)this.setZoom(11);
            this.createPoint();
            this.clearPonit();
            this.vevisibleFalg = true;
            let j = 0;
            for(let i=0;i<this.points.length;i++){
                let point = this.points[i];
                let marker = new BMap.Marker(point);
                  let result = BMapLib.GeoUtils.isPointInRect(point, this.map.getBounds());
                // if(result == true && j<10)
                 if(result == true)
                 {
                    this.map.addOverlay(marker);
                    ++j;
                }
                else this.map.removeOverlay(marker);
            }
        }
    },
    }
</script>
<style lang="less" scoped>
.map-point{
    .button{
        text-align: left;
        margin-bottom: 10px;
        .input-number{
            width: 200px;
            margin-right: 10px;
        }
    }
    .myMap{
        width: 1000px;
        height: 800px;
    }
}
</style>

--------------------------------------------------------------------------------

git地址:https://github.com/aicai0/test_components.git

如有问题,欢迎探讨,如果满意,请手动点赞,谢谢!🙏

及时获取更多姿势,请您关注!!!