先看效果图: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实现,服务端返回司机经纬度偏差不是很大的话,应该是没有问题的,这里还须要后续不断的测试;没有服务端支持轮询接口,能够写一个定时器来模拟轮询,而后在地图中选择一些扎点模拟司机位置来测试行驶。还有一些计算这里没有详细介绍,感兴趣的你们能够手动试一试。