百度地圖--實現增長打點(mark)自定義圖標、自定義地圖樣式、 運動軌跡

需求背景

一、查看某個地區全部業務員的座標
二、點擊業務員座標可展現具體信息、當前具體地址
三、點擊某個業務員名字,自動關聯到地圖上的位置
四、查看業務員當天的行程軌跡
五、動態切換地圖上座標的自定義圖標
六、支持展現全部業務員在可視區內(自適應縮放地圖比例,展現全部業務員位置)javascript

之因此沒有選擇用vue-baidu-map,是由於有bug。。。php


若是你以前沒有接觸過地圖方面的開發,建議先去官網熟悉一下api。
百度地圖API(javascript API)
具體的參考類
大概會用到如下幾個api:css

  1. 帳號和祕鑰獲取
  2. 初始化地圖
  3. 自定義地圖樣式
  4. 添加控件
  5. 添加標註
  6. 添加信息窗口
  7. 地圖事件處理
  8. 出行路線規劃
  9. 座標系之間的轉換

先看一下最終的效果圖html

map.png

顯示軌跡的模塊vue

guiji.png

項目是vue項目 我貼一下map.vue的文件代碼
html代碼👇
只須要一個顯示地圖的divjava

<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部分👇git

<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>

繪製軌跡的方法👇json

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樣式代碼我就不貼出來了,大家隨意發揮~api

注意的問題點以下👇

  • 地圖容器的div必定要設置寬高,否則地圖顯示不出來
  • 確認拿到的座標系。若是是其餘座標系,須要轉爲百度地圖的座標系,不然顯示的位置會有誤差,座標系的轉換我上面已經貼了連接,目前國內主要有如下三種座標系:
    WGS84:爲一種大地座標系,也是目前普遍使用的GPS全球衛星定位系統使用的座標系。

    GCJ02:又稱火星座標系,是由中國國家測繪局制訂的地理信息系統的座標系統。由WGS84座標系經加密後的座標系。

    BD09:爲百度座標系,在GCJ02座標系基礎上再次加密。其中bd09ll表示百度經緯度座標,bd09mc表示百度墨卡托米制座標。

    非中國地區地圖,服務座標統一使用WGS84座標。
    百度對外接口的座標系爲BD09座標系,並非GPS採集的真實經緯度,在使用百度地圖JavaScript API服務前,需先將非百度座標經過座標轉換接口轉換成百度座標。

  • 特別注意的是,保證每一個座標數組都是轉成百度地圖座標以後的值,這個很關鍵。
    • *

代碼有點亂, 我本身都有點難理清楚,若是有小夥伴遇到問題了能夠聯繫我QQ:602353272若是我分享的這個文章對你有幫助的話,不要吝嗇你的贊,給我點個贊吧。謝謝~~

相關文章
相關標籤/搜索