需求背景

1、查看某个地区所有业务员的坐标
2、点击业务员坐标可展示具体信息、当前具体地址
3、点击某个业务员名字,自动关联到地图上的位置
4、查看业务员当天的行程轨迹
5、动态切换地图上坐标的自定义图标
6、支持展示所有业务员在可视区内(自适应缩放地图比例,展示所有业务员位置)

之所以没有选择用vue-baidu-map,是因为有bug。。。


如果你之前没有接触过地图方面的开发,建议先去官网熟悉一下api。
百度地图API(javascript API)
具体的参考类
大概会用到以下几个api:

  1. 账号和秘钥获取
  2. 初始化地图
  3. 自定义地图样式
  4. 添加控件
  5. 添加标注
  6. 添加信息窗口
  7. 地图事件处理
  8. 出行路线规划
  9. 坐标系之间的转换

先看一下最终的效果图

显示轨迹的模块

项目是vue项目 我贴一下map.vue的文件代码
html代码????
只需要一个显示地图的div

<template>    <div class="main">        <div id="container" class="bm-view">        </div>        <div class="btn__group">            <button @click="changeType('all')" :class="{'active': item === 'all'}">全部</button>            <button @click="changeType('path')" :class="{'active': item === 'path'}"                v-show="currentItem==='car'">轨迹</button>        </div>    </div></template>

js部分????

<script>    import mapStyle from '@/assets/mapStyleJson.json'    import { drawTrack } from '@/libs/util.js'    export default {        name: 'map-module',        props: {            currentItem: {                type: String,                default: ''            }        },        data() {            return {                stewardCoordList: [],                zoom: 10,                // 地图样式                mapStyle: {                    styleJson: mapStyle                },                currentCity: '',                myMap: null,                item: '',                mapShow: true,                infoWindow: null,                trackList: []            }        },        watch: {            list: {                handler(newVal, oldVal) {                    if (!newVal.length) {                        this.myMap.clearOverlays()                        return                    }                    this.stewardCoordList = newVal                    // this.currentItem === 'car' && (this.item = 'all')                    let translateList = []                    this.stewardCoordList.forEach(e => {                        // 数组中加icon的url                        e.url = this.currentItem === 'car' ? (!e.currentOrder.length ? (                            '/static/img/normal.png') : (                            '/static/img/warning.png')) : (e.currentOrder.length ?                            ('/static/img/servicer-busy.svg') : (                                '/static/img/servicer-free.svg'))                        // 需要转换的坐标数组                                translateList.push(new BMap.Point(e.geo.lng, e.geo.lat))                    })                    let convertor = new BMap.Convertor();                    let myGeo = new BMap.Geocoder();                    let that = this;                    // 百度地图经纬度解析成具体地址                    let geocodeSearch = (pt, item) => {                        myGeo.getLocation(pt, function (rs) {                            if (rs !== null) item.address = rs.address                        });                    }                    let pageSize = 10,                        pageCount = translateList.length % 10 === 0 ? Math.floor(translateList.length / 10) : Math                        .floor((                            translateList.length / 10) + 1);                    for (let i = 0; i < pageCount; i++) {                        let startIndex = pageSize * i;                        let sliceArr = translateList.slice(startIndex, pageSize * (i + 1));                        convertor.translate(sliceArr, 1, 5, function (data) {                            if (data.status === 0) {                                for (let i = 0; i < data.points.length; i++) {                                    const el = data.points[i];                                    that.stewardCoordList[startIndex + i].geo.lng = el.lng                                    that.stewardCoordList[startIndex + i].geo.lat = el.lat                                    geocodeSearch(sliceArr[i], that.stewardCoordList[startIndex + i])                                }                            }                            // 添加图标marker                            setTimeout(() => {                                that.addMarker(that.stewardCoordList)                            }, 100);                        })                    }                },                deep: true            },            currentItem(val) {                this.item = val === 'car' ? 'all' : ''            }        },        computed: {            list() {                return this.$store.getters.getter_stewardCoordList            },            geo() {                return this.stewardCoordList.map(e => {                    return new BMap.Point(e.geo.lng, e.geo.lat)                })            },        },        mounted() {            this.initMap()            // 接收其他组件传过来的业务员坐标 (是一个数组) 这里实现的功能是:获取业务员的轨迹、自动移动地图当前可视区,定位到小哥的当前位置            //兄弟组件的事件分发 this.$emit('getCurpos', { arr}) 注意:这里传过来的数组坐标是转成百度地图坐标之后的            this.$bus.$on('getCurPos', async val => {                if (!this.item || this.item === 'all') { //查看业务员坐标                    // 这里重新渲染车和人的图标要写成异步的                     await this.addMarker(this.stewardCoordList)                    setTimeout(() => {                        this.myMap.centerAndZoom(new BMap.Point(val.arr[0], val.arr[1]), 18);                    }, 300);                } else { //查看轨迹                    await this.getDataLoc(val.account)                    let translateList = []                    if (this.trackList && this.trackList.length) {                        this.trackList.forEach(e => translateList.push(new BMap.Point(e.lng, e.lat)))                    }                    let convertor = new BMap.Convertor();                    let myGeo = new BMap.Geocoder();                    let that = this;                    let pageSize = 10,                        pageCount = translateList.length % 10 === 0 ? Math.floor(translateList.length /                            10) : Math                        .floor((                            translateList.length / 10) + 1);                            //这里是因为百度地图每天的计算量是有限的,所以就分批处理了                    for (let i = 0; i < pageCount; i++) {                        let startIndex = pageSize * i;                        let sliceArr = translateList.slice(startIndex, pageSize * (i + 1));                        convertor.translate(sliceArr, 1, 5, function (data) {                            if (data.status === 0) {                                for (let i = 0; i < data.points.length; i++) {                                    const el = data.points[i];                                    that.trackList[startIndex + i].lng = el.lng                                    that.trackList[startIndex + i].lat = el.lat                                }                            }                            setTimeout(() => {                                drawTrack(that.myMap, that.trackList, val.name)                            }, 100);                        })                    }                }            })            this.$nextTick(this.setMapHeight())        },        methods: {            initMap() {                // 初始化地图                this.myMap = new BMap.Map('container', {                    enableMapClick: false                });                let lng,                    lat;                if (this.list.length) {                    lng = this.list[0].geo.lng,                        lat = this.list[0].geo.lat;                    //设置地图中心点以及zoom值                    this.myMap.centerAndZoom(new BMap.Point(lng, lat), 12);                } else{                    //设置地图默认中心点                    this.myMap.centerAndZoom(new BMap.Point(116.406605, 39.921585), 10);                }                                    //是否允许鼠标滚轮缩放                // this.myMap.enableScrollWheelZoom(false);                this.myMap.addControl(new BMap.NavigationControl({                    offset: new BMap.Size(10, 45)                }))                // this.myMap.addControl(new BMap.OverviewMapControl())                //个性化地图样式                this.myMap.setMapStyle({                    styleJson: mapStyle                });            },            // 获取每个业务员轨迹坐标 数据由后端接口提供            getDataLoc(account) {                return api.***(account).then(res => {                    const data = res.data;                    if (data.resultCode === '0000') {                        this.trackList = data.resultData.map(v => {                            return {                                lng: '' + v.longitude,                                lat: '' + v.latitude                            }                        })                    }                })            },            addMarker(arr) {                new Promise(_ => {                    let that = this                    //加标注之前,先清空之前的标注。避免数据更新之后,之前的标注还在                    that.myMap.clearOverlays();                    if (arr != undefined && arr.length > 0 && that.myMap !== null) {                        let len = arr.length,                            content,                            marker,                            markers = []                                                   for (let i = 0; i < len; i++) {                            //自定义mark图标                            let myIcon = new BMap.Icon(arr[i].url, new BMap.Size(30, 26), {                                // 指定定位位置                                offset: new BMap.Size(10, 25),                                //  anchor: new BMap.Size(10, 30)                                 // 当需要从一幅较大的图片中截取某部分作为标注图标时,需要指定大图的偏移位置                                   // imageOffset: new BMap.Size(0, 0 - i * 25) // 设置图片偏移                              });                            let point = new BMap.Point(arr[i].geo.lng, arr[i].geo.lat);                            // 创建标注对象并添加到地图                             marker = new BMap.Marker(point, {                                icon: myIcon                            });                            // 给图标添加事件                            marker.addEventListener('click', function (e) {                                let item = arr[i],                                    html = ''                                // 以下是构造信息窗口里的内容 具体的内容按照你们的需求来                                const infoHtml = (!item.currentOrder.length) ? e => `                                        <p>${item.name} <span style="margin-left:5px;">${item.userMobile}</span><span style="color:#00bebebe;margin-left:5px;">${'空闲'}</span></p><br>                                    ` : e => `                                        <p>${item.name} <span style="margin-left:5px;">${item.userMobile}</span><span style="color:#f00;margin-left:5px;">${'***'}</span></p><br>                                    `                                                                const orderHtml = (item.currentOrder.length) ? (                                    that.currentItem === 'car' ? addrs => `                                            <div>                                            <p style="font-weight:bold;">***信息:</p>                                            ${item.currentOrder.map(info => `                                                <div>                                                    <p><span style="margin-right:6px;">${info.startDate}</span><sapn>${info.endData === null ? '' : info.endData}</sapn></p>                                                    <p>信息1:${info.soType}</p>                                                    <p>信息2:${info.so}</p>                                                    <p>信息3:${info.cusName}</p>                                                    <p>信息4:${info.vin}</p>                                                </div>                                            `)}                                            </div><br>                                            ` : addrs => `                                            <div>                                                <p style="font-weight:bold;">***信息:</p>                                            ${item.currentOrder.map(info => `                                                <div>                                                    <p>信息1:${info.type}</p>                                                    <p>信息2:${info.so}</p>                                                    <p>信息3:${info.cusName}</p>                                                </div>                                            `)}                                            </div><br>                                            `                                ) : addrs => `<p></p>`                                //------------------------结束-----------------                                html = `<div class="info__box">                                                ${infoHtml()}                                                ${orderHtml()}                                                <p style="font-weight:bold;">当前位置:<span style="font-weight:normal;">${item.address}</span></p>                                            </div>`                                //  打开信息窗口                                           openInfo(html, point)                            }, false);                            markers.push(marker)                            that.myMap.addOverlay(marker);                            that.myMap.setViewport(that.geo)                        };                    } else if (!arr.length) {                        that.myMap.clearOverlays();                        // that.myMap.centerAndZoom(that.formItem.cityName, 10)                    }                    //开启信息窗口                    function openInfo(content, point) {                        var opts = {                            boxStyle: {                                width: "280px",                                minHeight: "195px",                                opacity: "0.8",                            },                            enableAutoPan: true,                            // title: '信息',                            closeIconUrl: close,                            closeIconMargin: '0px',                            closeIconZIndex: 1,                            closeIconWidth: '15px'                        }                        // let point = new BMap.Point(e.target.point.lng, e.target.point.lat);                        let infoWindow = new BMap.InfoWindow(content, opts); // 创建信息窗口对象                         that.myMap.openInfoWindow(infoWindow, point); //开启信息窗口                        return false                    }                })            },            async changeType(e) {                this.item = e;                this.myMap.clearOverlays();                if (this.geo.length === 1) {                    let list = this.stewardCoordList;                    this.myMap.centerAndZoom(new BMap.Point(list[0].geo.lng, list[0].geo.lat), 18);                } else {                    if (e === 'all') {                        await this.addMarker(this.stewardCoordList)                        this.myMap.setViewport(this.geo)                    }                }            },            // 设置地图高度            setMapHeight() {                let boxHeight = document.querySelector('.content-wrapper').offsetHeight;                // document.querySelector('.bm-view').style.height = boxHeight * 2 + 'px'            },        }    }</script>

绘制轨迹的方法????

export const drawTrack = (map, arr, name) => {    // 先清空之前的marker    map.clearOverlays();    let driving = new BMap.DrivingRoute(map); //创建轨迹实例     // 生成坐标点    let trackPoint = arr.map(v => new BMap.Point('' + v.lng, '' + v.lat))    for (let i = 0, len = trackPoint.length; i < len; i++) {        if (i != trackPoint.length - 1) {            driving.search(trackPoint[i], trackPoint[i + 1]);        }    }    // icon的参数    let size = new BMap.Size(28, 32),        offset = new BMap.Size(0, -13),        imageSize = new BMap.Size(28, 32),        icon = new BMap.Icon("/static/car.png", size, {            imageSize: imageSize        });    driving.setSearchCompleteCallback(() => {        let pts = driving.getResults().getPlan(0).getRoute(0)            .getPath(); //通过轨迹实例,获得一系列点的数组          let polyline = new BMap.Polyline(pts, {            strokeColor: '#00bebebe',            strokeOpacity: 1,        });        map.addOverlay(polyline);        let labStyle = {            minWidth: '28px',            height: '28px',            lineHeight: '28px',            textAlign: 'center',            color: '#FFF',            fontSize: '12px',            backgroundColor: 'none',            border: '1px solid #00bebebe',            borderRadius: '4px',        }        // 画图标、想要展示的起点终点途经点        for (let i = 0, len = trackPoint.length; i < len; i++) {            let marker = new BMap.Marker(trackPoint[i], {                icon: icon,                offset: offset            });            let lab,            nameLab;            if (i == 0) {                lab = new BMap.Label("起点", {                    position: trackPoint[i]                });                lab.setStyle(labStyle)            } else if (i == trackPoint.length - 1) {                lab = new BMap.Label("终点", {                    position: trackPoint[i]                });                lab.setStyle(labStyle)                //添加一个名字label                nameLab = new BMap.Label(name, {                    offset:new BMap.Size(0,-30),                    position: trackPoint[i]                })                nameLab.setStyle(labStyle)            }             map.addOverlay(lab);            map.addOverlay(nameLab);        }        map.setViewport(trackPoint);    });}

mapStyleJson.json的内容我也贴出来

[{  "featureType": "water",  "elementType": "all",  "stylers": {      "color": "#00253d"  }},{  "featureType": "highway",  "elementType": "geometry.fill",  "stylers": {      "color": "#000000"  }},{  "featureType": "highway",  "elementType": "geometry.stroke",  "stylers": {      "color": "#296f82ff"  }},{  "featureType": "arterial",  "elementType": "geometry.fill",  "stylers": {      "color": "#000000"  }},{  "featureType": "arterial",  "elementType": "geometry.stroke",  "stylers": {      "color": "#296f82"  }},{  "featureType": "local",  "elementType": "geometry",  "stylers": {      "color": "#000000"  }},{  "featureType": "land",  "elementType": "all",  "stylers": {      "color": "#011D32"  }},{  "featureType": "railway",  "elementType": "geometry.fill",  "stylers": {      "color": "#000000"  }},{  "featureType": "railway",  "elementType": "geometry.stroke",  "stylers": {      "color": "#296f82ff"  }},{  "featureType": "subway",  "elementType": "geometry",  "stylers": {      "lightness": -70  }},{  "featureType": "building",  "elementType": "geometry.fill",  "stylers": {      "color": "#000000"  }},{  "featureType": "all",  "elementType": "labels.text.fill",  "stylers": {      "color": "#857f7fff"  }},{  "featureType": "all",  "elementType": "labels.text.stroke",  "stylers": {      "color": "#000000"  }},{  "featureType": "building",  "elementType": "geometry",  "stylers": {      "color": "#022338"  }},{  "featureType": "green",  "elementType": "geometry",  "stylers": {      "color": "#062032"  }},{  "featureType": "boundary",  "elementType": "all",  "stylers": {      "color": "#1e1c1c"  }},{  "featureType": "manmade",  "elementType": "geometry",  "stylers": {      "color": "#022338"  }},{  "featureType": "poi",  "elementType": "all",  "stylers": {      "visibility": "on"  }},{  "featureType": "all",  "elementType": "labels.icon",  "stylers": {      "visibility": "off"  }},{  "featureType": "all",  "elementType": "labels.text.fill",  "stylers": {      "color": "#2da0c6ff",      "visibility": "on"  }},{  "featureType": "highway",  "elementType": "geometry",  "stylers": {      "visibility": "off"  }},{  "featureType": "local",  "elementType": "geometry",  "stylers": {      "color": "#00ff00ff",      "visibility": "off"  }},{  "featureType": "railway",  "elementType": "geometry",  "stylers": {      "color": "#00ff00ff",      "visibility": "off"  }},{  "featureType": "subway",  "elementType": "geometry",  "stylers": {      "color": "#00ff00ff",      "visibility": "off"  }},{  "featureType": "arterial",  "elementType": "geometry",  "stylers": {      "color": "#073146",      "visibility": "on"  }}]

css样式代码我就不贴出来了,你们随意发挥~

注意的问题点如下????

  • 地图容器的div一定要设置宽高,不然地图显示不出来
  • 确认拿到的坐标系。如果是其他坐标系,需要转为百度地图的坐标系,否则显示的位置会有偏差,坐标系的转换我上面已经贴了链接,目前国内主要有以下三种坐标系:
    WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。

    GCJ02:又称火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。

    BD09:为百度坐标系,在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标。

    非中国地区地图,服务坐标统一使用WGS84坐标。
    百度对外接口的坐标系为BD09坐标系,并不是GPS采集的真实经纬度,在使用百度地图JavaScript API服务前,需先将非百度坐标通过坐标转换接口转换成百度坐标。

  • 特别注意的是,保证每个坐标数组都是转成百度地图坐标之后的值,这个很关键。
    • *

代码有点乱, 我自己都有点难理清楚,如果有小伙伴遇到问题了可以联系我
QQ:602353272
如果我分享的这个文章对你有帮助的话,不要吝啬你的赞,给我点个赞吧。谢谢~~