先看效果圖:api
學習高德經緯度實時定位-精確地址 👈 戳這裏數組
首先說一下大致思路:markdown
- pathArr爲空時表示沒有規劃路線,根據起點和終點位置開始規劃路線;
- 接口5秒輪詢不斷拿到司機的實時位置,而後計算司機的位置是否在這條路線上;
- 在這條路線則計算小車的行駛路線,開始行駛等操做;不在這條路線則從新規劃路線。
/** * @description: 輪詢函數 * @param {*} slnglat 起點(司機位置) * @param {*} elnglat 終點(接駕中:起始地做爲終點 / 行程中:目的地做爲終點) */
drawDistance(slnglat, elnglat) {
let startLngLat = new AMap.LngLat(slnglat.lng, slnglat.lat)
let endLngLat = new AMap.LngLat(elnglat.lng, elnglat.lat)
if (this.pathArr.length === 0) { '表示沒有路線,開始繪製'
this.drawPath(startLngLat, endLngLat)
return
}
let isPointInRing = AMap.GeometryUtil.isPointOnLine(startLngLat, this.pathArr, 80)
if (!isPointInRing) { '小車位置不在這條軌跡上'
this.drawPath(startLngLat, endLngLat)
} else {
this.setPassByPath() // 每次執行前 將上一次行駛的路線添加到總行駛路線
this.setMoveLine(startLngLat) // 設置小車5秒行駛的路線
this.moveAlongFn() // 小車開始行駛
}
}
複製代碼
跟着我一塊兒來實現吧!!👊app
下面用到的高德數學計算庫:AMap.GeometryUtilide
- distance(p1:LngLat, p2:LngLat) 計算兩個經緯度點之間的實際距離。單位:米
- closestOnLine(p:LngLat, line:[LngLat]) 計算line上距離P最近的點
- isPointOnLine(p:LngLat, line:[LngLat],tolerance:Number) 判斷P是否在line上,tolerance爲偏差範圍
- distanceOfLine(ring:[LngLat]) 計算一個經緯度路徑的實際長度。單位:米
首先咱們須要實現的是路徑規劃,經過起點和終點位置規劃一條小車須要行駛的路線,且路線分爲最快捷、最經濟、最短距離、考慮實時路況;這裏你們能夠選擇一個,暫時沒有具體對比這幾種策略哪一個更好,我選的最快捷:AMap.DrivingPolicy.LEAST_TIMEsvg
/** * @description: 規劃路線 * @param {*} startLngLat 起點 * @param {*} endLngLat 終點 */
drawPath(startLngLat, endLngLat) {
this.clearCarState() // 繪製以前要清除全部覆蓋物並重置數據(省略詳細)
this.map.plugin('AMap.Driving', () => { // 加載插件-插件爲地圖功能的擴展
let driving = new AMap.Driving({ // 構造路線導航類
hideMarkers: true, // 隱藏路徑起始點圖標
autoFitView: true, // 自動調整地圖視野
policy: AMap.DrivingPolicy.LEAST_TIME, // 駕車路線規劃策略
extensions: 'all', // 詳細信息
})
driving.search(startLngLat, endLngLat, (status, result) => {
console.log(result, '規劃的路線信息')
if (status === 'complete') {
this.setLineData(result) // * 路線數據處理
this.createCover(endLngLat) // * 根據路線開始繪製
}
})
})
}
複製代碼
對路線信息進行數據處理,獲得一維對象數組函數
setLineData(result) {
let paths = []
let routes = result.routes && result.routes.length > 0 ? result.routes[0] : null
if (routes) {
this.titDistance = routes.distance // 路徑總長度
this.titTimes = routes.time // 路徑總時長
let steps = routes.steps || []
for (let i = 0; i < steps.length; i++) {
for (let j = 0; j < steps[i].path.length; j++) {
paths.push(steps[i].path[j])
}
}
}
this.pathArr = paths
}
複製代碼
根據路線pathArr集合開始繪製:總路線Polyline、 單次行駛路線Polyline、 總行駛路線Polyline;時間距離Marker、汽車Marker;Marker和Polyline如何建立,具體能夠查看高德官網API,這裏就不作詳細介紹了;post
createCover(endLngLat) {
let paths = this.pathArr
this.carTitMarker = new AMap.Marker({ '距離和時間marker'
map: this.map,
position: paths[0], // 初始位置
content: '',
zIndex: 102,
})
this.setCarTime() '爲carTitMarker添加內容'
// this.getAngle() 下面會作詳細介紹-------
let angle = this.getAngle(paths[0], paths[1]) - 90 // Marker的初始角度0度=90度
this.carMarker = new AMap.Marker({ '小車marker'
map: this.map,
position: paths[0],
content: `<img class='caricon' src='/images/newtaxi/qiche.svg'/>`,
offset: new AMap.Pixel(-15, -10),
autoRotation: true,
angle: angle, '汽車角度'
zIndex: 102
})
this.currentLine = new AMap.Polyline({ '總路線繪製'
map: this.map,
path: paths,
strokeColor: "#45C184",
lineJoin: 'round',
lineCap: 'round',
strokeOpacity: 1, // 線條透明度
strokeWeight: 6, //線條寬度
showDir: true
});
let polyConfig = {
map: this.map,
strokeColor: "#fff", // 行駛過的路線爲白色
lineJoin: 'round',
lineCap: 'round',
strokeOpacity: 1,
strokeWeight: 7,
showDir: false
}
this.passByLine = new AMap.Polyline(polyConfig) '小車總行駛後的路線polyline'
let drivingLine = new AMap.Polyline(polyConfig) '小車5秒行駛的路線polyline'
}
複製代碼
根據兩個經緯度計算角度:學習
汽車默認繪製的時候,須要設置汽車行駛的角度;傳入兩個經緯度,經過 Math.atan2()、 Math.PI計算出汽車的角度。這裏須要注意的是高德0度=-90度。測試
getAngle(startPoint, endPoint) {
if (!(startPoint && endPoint)) {
return 0;
}
let dRotateAngle = Math.atan2(
Math.abs(startPoint.lng - endPoint.lng),
Math.abs(startPoint.lat - endPoint.lat)
);
if (endPoint.lng >= startPoint.lng) {
if (endPoint.lat >= startPoint.lat) {
} else {
dRotateAngle = Math.PI - dRotateAngle;
}
} else {
if (endPoint.lat >= startPoint.lat) {
dRotateAngle = 2 * Math.PI - dRotateAngle;
} else {
dRotateAngle = Math.PI + dRotateAngle;
}
}
dRotateAngle = (dRotateAngle * 180) / Math.PI;
return dRotateAngle;
}
複製代碼
監聽小車Marker移動事件、和移動結束後;
移動中監聽:
移動結束後:
this.carMarker.on('moving', (e) => {
drivingLine.setPath(e.passedPath)
this.movingFn(e.passedPath, endLngLat)
})
this.carMarker.on('movealong', (e) => {
this.setCarTime() // 行駛完更新時間
if (this.carIndex > 0 && this.pathArr.length > this.carIndex) {
let carLoc = this.pathArr[this.carIndex]
this.setCenter(carLoc)
}
})
movingFn(path, endLngLat) {
let distance = AMap.GeometryUtil.distance(path[path.length - 1], endLngLat)
if (distance < 150) { // 司機位置距離個人位置小於150米,軌跡隱藏
this.currentLine && this.currentLine.hide()
}
if (distance < 100) { // 距離個人位置小於100米,距離時間提示隱藏
this.carTitMarker && this.carTitMarker.hide()
}
}
複製代碼
/** * @description: 設置汽車行駛的路線 * @param {*} startLngLat 司機位置 */
setMoveLine(startLngLat) {
let line_near = AMap.GeometryUtil.closestOnLine(startLngLat, this.pathArr, 50); // 司機位置直線距離軌跡最近的點
let distanceList = [] //距離集合
for (let i = this.pathArr.length; i--;) {
let distances = AMap.GeometryUtil.distance(line_near, this.pathArr[i]) // 計算距離
distanceList[i] = distances
}
let index = this.arrayMin(distanceList) // 距離最短的點的下標
//.....
}
複製代碼
處理距離集合,循環遍歷拿到距離最短的下標值
arrayMin(arr) {
let len = arr.length
let min = Infinity
let minIndex = 0
while (len--) {
if (arr[len] < min) {
min = arr[len];
minIndex = len
}
}
return minIndex
}
複製代碼
距離最短的點的下標,也就是汽車位置的下標;好比:小車默認下標爲0,當下次輪詢獲得司機位置與距離下標爲5的位置很近,那咱們就截取0到5的數據,這就是小車須要行駛的路線‘carRunPath’。
小車從下標0的位置行駛到下標爲5的位置之後,將小車位置保存起來,必定要用全局變量去保存。
距離最近的下標大於小車當前位置,說明小車向前行駛;相反,小車不動。
計算行駛路線的長度‘moveRice’並保存,爲汽車行駛計算速度提供。
if (index > this.carIndex) { // * 司機位置小車當前位置前面,小車行駛
this.carRunPath = this.pathArr.slice(this.carIndex, index + 1)
this.moveRice = Math.round(AMap.GeometryUtil.distanceOfLine(this.carRunPath)) '計算行駛路線長度'
// * 行駛速度進行勻速處理---經過路徑長度去控制,路徑太短則等待下一次
let overlen = distanceList.length - 5 // 結尾的路程根據小車真實移動去改變位置
if (index < overlen && this.moveRice < 20) { // 路徑小於20米,小車不動--解決距離太短汽車行駛不平滑問題
this.carRunPath = []
return
}
this.carIndex = index
} else { // * 司機位置沒動或者在小車位置後面,小車不動(避免小車行駛後重復行駛)
console.log('位置沒動');
this.carRunPath = []
}
複製代碼
當設置好小車須要行駛的路線‘carRunPath’時,同時也知道了這段路線的長度‘moveRice’,由於咱們設置的5秒輪詢,全部行駛的路程須要在5秒內行駛完,否則會出現小車閃跳現象;
moveAlongFn() {
if (this.carRunPath.length > 0) {
let speed = (this.moveRice / 5) * 3.6 // 計算5秒內行駛的速度
this.carMarker.moveAlong(this.carRunPath, speed) '小車行駛'
this.carTitMarker.moveAlong(this.carRunPath, speed) '距離時間跟隨小車一塊兒行駛'
}
}
複製代碼
最後須要作的是 在小車每次行駛以前,將小車當次行駛的路線記錄到總行駛路線中,否則每次會將前面的路線會從新繪製。總行駛路線的Polyline setPath()
setPassByPath() {
if (this.carRunPath.length > 0) { // 小車位置可能沒動,就不會執行如下操做
this.carPassByPath = this.carPassByPath.concat(this.carRunPath)
this.passByLine.setPath(this.carPassByPath)
}
}
複製代碼
不少仍是須要依賴高德的api實現,服務端返回司機經緯度偏差不是很大的話,應該是沒有問題的,這裏還須要後續不斷的測試;沒有服務端支持輪詢接口,能夠寫一個定時器來模擬輪詢,而後在地圖中選擇一些扎點模擬司機位置來測試行駛。還有一些計算這裏沒有詳細介紹,感興趣的你們能夠手動試一試。