利用腾讯地图导航,如何实现车辆在下车点前50米朝向的精准感知?

前言

天地一逆旅,人生无觅迹,你我皆行人,只此一迹,记录地图相关系列知识

解决打车时车辆朝向感知难题,提前50米预知车辆来向!

在现代化生活中,打车已经成为许多人出行的首选方式。然而,打车时常常会遇到一个问题:我们不知道车辆的具体位置和朝向,不确定车辆会从哪个方向驶来。这不仅增加了等待的焦虑感,有时还可能因为车辆来向不明而导致错过或延误乘车。于是乎随机挑选一个厂家,腾讯地图,决定去实际探究,看看现有的地图组件如何满足这种日常必要的需求?

重点:计算导航路线50m处点位位置与车辆朝向

一、引入腾讯地图

开发web端地图,首要任务就是引入地图库,腾讯地图的在线JS库地址为:map.qq.com/api/gljs?v=…标签内添加 script 标签来完成。腾讯地图提供了多种引入方式,包括通过CDN引入和使用模块化引入等。

<script charset="utf-8" 
	src="https://map.qq.com/api/gljs?v=1.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77&libraries=geometry,tools"></script>

注意:

  • v=1.exp为API版本号,目前可用1.exp或者2.exp。
  • key为官方控制台申请的密钥(可用官方提供的OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77)
  • libraies为需要加载的插件(本文用到geometry几何库与tools工具库)

二、初始化地图

首先需要在HTML页面中初始化一个腾讯地图的div实例。这通常包括设置地图的中心点、缩放级别。

map = new TMap.Map('mapContainer', {
  center: new TMap.LatLng(39.980619,116.321277),//地图显示中心点
  zoom:14	//缩放级别
});

三、请求导航数据与路线绘制

3.1 JSONP请求导航数据

在JavaScript中,你可以使用JSONP来跨域请求Web服务API。由于JSONP是基于 script 标签的,它会自动处理跨域问题,但它只支持GET请求。对于你提供的腾讯地图路线规划API的URL,因此我们只需要去组织url的参数即可:

	//WebServiceAPI请求URL(驾车路线规划默认会参考实时路况进行计算)
	var url="https://apis.map.qq.com/ws/direction/v1/driving/"; //请求路径
	url+="?from=39.984039,116.307630";
  url+="&waypoints=39.981491,116.317167;39.985266,116.331934"//轨迹点坐标
	url+="&to=39.977263,116.337063";  //终点坐标
	url+="&output=jsonp&callback=cb" ;	//指定JSONP回调函数名,本例为cb
	url+="&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77"; //开发key

var script=document.createElement('script');
script.src=url;
document.body.appendChild(script);

注意:

  • url中参数分别包含from起点坐标,to终点坐标,waypoint途经点,callback回调函数以及key密钥
  • 起终点与途经点坐标可以用官方示例去拾取
  • 导航接口的返回值可在回调函数中调用

3.2 绘制导航线路数据

此步骤是用来处理上步骤返回的导航返回数据,利用导航返回数据去绘制起点和终点,并绘制导航路线示意图。

//定义请求回调函数,在此拿到计算得到的路线,并进行绘制
function cb(ret){
	var coords = ret.result.routes[0].polyline, pl = [];
	var kr = 1000000;
	for (var i = 2; i < coords.length; i++) {
	  coords[i] = Number(coords[i - 2]) + Number(coords[i]) / kr;
	}
	for (var i = 0; i < coords.length; i += 2) {
	  pl.push(new TMap.LatLng(coords[i], coords[i+1]));
	}
	//标记起终点以及途经点marker
	var marker = new TMap.MultiMarker({ 
		id: 'marker-layer',
		map: map,
		styles: {
			"start": new TMap.MarkerStyle({
				"width": 25,
				"height": 35,
				"anchor": { x: 16, y: 32 },
				"src": 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/start.png'
			}),
			"waypoint": new TMap.MarkerStyle({
				"width": 25,
				"height": 35,
				"anchor": { x: 16, y: 32 },
			}),
			"end": new TMap.MarkerStyle({
				"width": 25,
				"height": 35,
				"anchor": { x: 16, y: 32 },
				"src": 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/end.png'
			})
		},
		geometries: [{
			"id": 'start',
			"styleId": 'start',
			"position": new TMap.LatLng(39.984039,116.307630307503)
		},
		{
			"id": 'marker',
			"styleId": 'waypoint',
			"position": new TMap.LatLng(39.981491,116.317167)
		},
		{
			"id": 'marker',
			"styleId": 'waypoint',
			"position": new TMap.LatLng(39.977128,116.324964)
		}, {
			"id": 'end',
			"styleId": 'end',
			"position": new TMap.LatLng(39.977263,116.337063)
		}]
	});
}
function display_polyline(pl){
	//创建 MultiPolyline显示折线
	var polylineLayer = new TMap.MultiPolyline({
		id: 'polyline-layer',
		map: map,
		styles: {
			style_blue: new TMap.PolylineStyle({
				color: '#3777FF',
				width: 8,
				borderWidth: 0,
				showArrow: true,
				arrowOptions: {
					space: 70
				},
				lineCap: 'round',
			})
		},
		geometries: [{
			id: 'pl_1',
			styleId: 'style_blue',//绑定样式名
			paths: pl
		}]
	});
}

注意:

  • cb函数为导航请求回调函数(负责接受并处理导航数据,并绘制起终点及途经点)
  • display_polyline函数为绘制折线函数(负责根据处理后的数据进行在地图上绘制折线)

四、计算距离下一个途经点50m处的位置与车辆朝向

讲解这一步骤之前,需要弄清楚几个概念,我在下图中已列出。
其中:

  • 导航路线由轨迹段和轨迹点组成
  • 导航会根据途经点,将其归化到最近的导航线路上,此点解释为归化点。

首先确定归化站点(途经点)在导航轨迹点组中的序号,利用序号,依次去倒序遍历导航轨迹点数组,计算每一个轨迹段的距离,通过距离累加值和50m去做判断。此处根据50m位置在归化点是否在上一个轨迹段分两种情况。完整步骤如下:

  1. 获取轨迹站点的序号
  2. 遍历途经点
  3. 绘制途经点标记
  4. 计算累计距离并查找导航点
  5. 计算航向和导航点位置
  6. 显示信息窗口
	// 获取轨迹站点的序号
	let waypointIndex = data.waypoints.map(item => item.polyline_idx);
	// 在返回轨迹数组中,计算轨迹站点往前的累计距离
	for (let i = 0; i < waypointIndex.length; i++) {
		let maxDistance = 50
		let addDistance = 0
		// 绘制导航轨迹上的途经点(即归化点)
		new TMap.MultiMarker({ 
			map: this.map,
			styles: {
				"marker": new TMap.MarkerStyle({
					"width": 25,
					"height": 35,
					"anchor": { x: 16, y: 32 },
				})
			},
			geometries: [{
				"id": 'marker',
				"styleId": 'marker',
				"position": new TMap.LatLng(data.polyline[waypointIndex[i]],data.polyline[waypointIndex[i]+1])
			}]
		});
		// 按照归化点的序号去倒序循环导航轨迹点数组,计算距离,依次相加,计算出满足要求点位,并计算航向
		for (let j = waypointIndex[i]; j > 0; j-=2) {			
			let point1 = new TMap.LatLng(data.polyline[j], data.polyline[j+1]);
			let point2 = new TMap.LatLng(data.polyline[j-2], data.polyline[j-1]);
			let distance = TMap.geometry.computeDistance([point1,point2]);
			// 计算累加距离
			addDistance += distance
			// 如果累加距离大于规定最大距离,则进行航线和最大距离点查找
			if(addDistance>=maxDistance) {
				// 计算整体航向
				let head = TMap.geometry.computeHeading(point2, point1, true)
				// 去除负值航向
				if(head<0){head = 360+head}
				// 计算最大距离50m点
				let lastpoint
				// 注意:此处要区别最大距离点与归化点,在第一段还是其他段,根据循环判断,若循环为第一次,则证明为第一段
				// computeDestination函数作用为,输入起始点和朝向以及距离,计算起始点沿朝向固定距离出的点
				if(waypointIndex[i]==j){ 
					let heading1 = TMap.geometry.computeHeading(point1, point2, true)
					lastpoint = TMap.geometry.computeDestination(point1, heading1, maxDistance)
				} else {
					let heading2 = TMap.geometry.computeHeading(new TMap.LatLng(data.polyline[j], data.polyline[j+3]), new TMap.LatLng(data.polyline[j-2], data.polyline[j+1]), true)
					lastpoint = TMap.geometry.computeDestination(
						new TMap.LatLng(data.polyline[j], data.polyline[j+3]), 
						heading2,
						maxDistance-addDistance+distance
					)
				}
				new TMap.InfoWindow({
						map: map, 
						position: lastpoint, //设置信息框位置
						content: '距离最近途经点为50m'+'车头朝向为:'+ head//设置信息框内容
					});
				new TMap.MultiMarker({ 
					map: map,
					styles: {
						"marker": new TMap.MarkerStyle({
							"width": 25,
							"height": 35,
							"anchor": { x: 16, y: 32 },
						})
					},
					geometries: [{
						"id": 'marker',
						"styleId": 'marker',
						"position": lastpoint
					}]
				});
				break
			}
		}
	}

五、全部代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>TMap地图导航</title>
</head> 
<script charset="utf-8" 
src="https://map.qq.com/api/gljs?v=1.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77&libraries=geometry,tools"></script>
<style>
html,
body {
height: 100%;
margin: 0px;
padding: 0px;
}
#mapContainer {
width: 100%;
height: 100%;
}
</style>
<body onload="initMap()">
<div id="mapContainer"></div>
</body>
</html>
<script>
//注:本例仅为说明流程,不保证严谨
let map;
let measureTool 
function initMap(){
//初始化地图
map = new TMap.Map('mapContainer', {
center: new TMap.LatLng(39.980619,116.321277),//地图显示中心点
zoom:14	//缩放级别
});
// 创建测量工具
measureTool = new TMap.tools.MeasureTool({
// TMap.tools.MeasureTool文档地址:https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocEditor#5F
map: map,
});
//WebServiceAPI请求URL(驾车路线规划默认会参考实时路况进行计算)
var url="https://apis.map.qq.com/ws/direction/v1/driving/"; //请求路径
url+="?from=39.984039,116.307630";  //起点坐标
// 轨迹点坐标拾取网址:https://lbs.qq.com/webDemoCenter/glAPI/glMap/mapPosition
url+="&waypoints=39.981491,116.317167;39.985266,116.331934"//轨迹点坐标
url+="&to=39.977263,116.337063";  //终点坐标
url+="&output=jsonp&callback=cb" ;	//指定JSONP回调函数名,本例为cb
url+="&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77"; //开发key
//发起JSONP请求,获取路线规划结果
jsonp_request(url);
}
//浏览器调用WebServiceAPI需要通过Jsonp的方式,此处定义了发送JOSNP请求的函数
function jsonp_request(url){
var script=document.createElement('script');
script.src=url;
document.body.appendChild(script);
}
//定义请求回调函数,在此拿到计算得到的路线,并进行绘制
function cb(ret){
measureTool.enable();
measureTool.measureDistance()
var coords = ret.result.routes[0].polyline, pl = [];
var kr = 1000000;
for (var i = 2; i < coords.length; i++) {
coords[i] = Number(coords[i - 2]) + Number(coords[i]) / kr;
}
for (var i = 0; i < coords.length; i += 2) {
pl.push(new TMap.LatLng(coords[i], coords[i+1]));
}
display_polyline(pl)//显示路线
foundPoint(ret.result.routes[0],pl)
//标记起终点marker
var marker = new TMap.MultiMarker({ 
id: 'marker-layer',
map: map,
styles: {
"start": new TMap.MarkerStyle({
"width": 25,
"height": 35,
"anchor": { x: 16, y: 32 },
"src": 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/start.png'
}),
"waypoint": new TMap.MarkerStyle({
"width": 25,
"height": 35,
"anchor": { x: 16, y: 32 },
}),
"end": new TMap.MarkerStyle({
"width": 25,
"height": 35,
"anchor": { x: 16, y: 32 },
"src": 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/end.png'
})
},
geometries: [{
"id": 'start',
"styleId": 'start',
"position": new TMap.LatLng(39.984039,116.307630307503)
},
{
"id": 'marker',
"styleId": 'waypoint',
"position": new TMap.LatLng(39.981491,116.317167)
},
{
"id": 'marker',
"styleId": 'waypoint',
"position": new TMap.LatLng(39.977128,116.324964)
}, {
"id": 'end',
"styleId": 'end',
"position": new TMap.LatLng(39.977263,116.337063)
}]
});
}
function display_polyline(pl){
//创建 MultiPolyline显示折线
var polylineLayer = new TMap.MultiPolyline({
id: 'polyline-layer',
map: map,
styles: {
style_blue: new TMap.PolylineStyle({
color: '#3777FF',
width: 8,
borderWidth: 0,
showArrow: true,
arrowOptions: {
space: 70
},
lineCap: 'round',
})
},
geometries: [{
id: 'pl_1',
styleId: 'style_blue',//绑定样式名
paths: pl
}]
});
}
// 寻找45-50m的路线段
// 首先找到轨迹点在路径中的序号,然后去根据序号在路径点组中往前找点位坐标,
// 并依次计算点位坐标的距离,依次相加,直到大于45m
function foundPoint(data,pl){
for (let i = 0; i < data.polyline.length-1; i += 2) {
new TMap.MultiMarker({ 
map: map,
styles: {
"start": new TMap.MarkerStyle({
"width": 25,
"height": 35,
"anchor": { x: 16, y: 32 },
"src": 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/start.png'
})
},
geometries: [{
"id": 'start',
"styleId": 'start',
"position": new TMap.LatLng(data.polyline[i],data.polyline[i+1])
}]
});
}
// 获取轨迹站点的序号
let waypointIndex = data.waypoints.map(item => item.polyline_idx);
// 在返回轨迹数组中,计算轨迹站点往前的累计距离
for (let i = 0; i < waypointIndex.length; i++) {
let maxDistance = 50
let addDistance = 0
// 绘制导航轨迹上的途经点(即归化点)
new TMap.MultiMarker({ 
map: this.map,
styles: {
"marker": new TMap.MarkerStyle({
"width": 25,
"height": 35,
"anchor": { x: 16, y: 32 },
})
},
geometries: [{
"id": 'marker',
"styleId": 'marker',
"position": new TMap.LatLng(data.polyline[waypointIndex[i]],data.polyline[waypointIndex[i]+1])
}]
});
// 按照归化点的序号去倒序循环导航轨迹点数组,计算距离,依次相加,计算出满足要求点位,并计算航向
for (let j = waypointIndex[i]; j > 0; j-=2) {			
let point1 = new TMap.LatLng(data.polyline[j], data.polyline[j+1]);
let point2 = new TMap.LatLng(data.polyline[j-2], data.polyline[j-1]);
let distance = TMap.geometry.computeDistance([point1,point2]);
// 计算累加距离
addDistance += distance
// 如果累加距离大于规定最大距离,则进行航线和最大距离点查找
if(addDistance>=maxDistance) {
// 计算整体航向
let head = TMap.geometry.computeHeading(point2, point1, true)
// 去除负值航向
if(head<0){head = 360+head}
// 计算最大距离50m点
let lastpoint
// 注意:此处要区别最大距离点与归化点,在第一段还是其他段,根据循环判断,若循环为第一次,则证明为第一段
// computeDestination函数作用为,输入起始点和朝向以及距离,计算起始点沿朝向固定距离出的点
if(waypointIndex[i]==j){ 
let heading1 = TMap.geometry.computeHeading(point1, point2, true)
lastpoint = TMap.geometry.computeDestination(point1, heading1, maxDistance)
} else {
let heading2 = TMap.geometry.computeHeading(new TMap.LatLng(data.polyline[j], data.polyline[j+3]), new TMap.LatLng(data.polyline[j-2], data.polyline[j+1]), true)
lastpoint = TMap.geometry.computeDestination(
new TMap.LatLng(data.polyline[j], data.polyline[j+3]), 
heading2,
maxDistance-addDistance+distance
)
}
new TMap.InfoWindow({
map: map, 
position: lastpoint, //设置信息框位置
content: '距离最近途经点为50m'+'车头朝向为:'+ head//设置信息框内容
});
new TMap.MultiMarker({ 
map: map,
styles: {
"marker": new TMap.MarkerStyle({
"width": 25,
"height": 35,
"anchor": { x: 16, y: 32 },
})
},
geometries: [{
"id": 'marker',
"styleId": 'marker',
"position": lastpoint
}]
});
break
}
}
}
}
</script>

原文链接:https://juejin.cn/post/7347221064428716082 作者:人生陈迹

(0)
上一篇 2024年3月18日 下午4:12
下一篇 2024年3月18日 下午4:23

相关推荐

发表回复

登录后才能评论