在地图固定范围内按照一定比例打点
由标题我们可以提取几个重点
- 地图
- 固定范围内
- 一定比例
- 打点(即坐标落点)
根据上面几个重点,我们需要考虑几个问题,固定范围的坐标怎么计算,比例怎么计算,打点的时候需要注意什么。
需求
在项目中,我们有一个事件处理的业务逻辑,在上报事件时,提供一个经纬度定位事件发生的定位,查看事件的时候,我们需要把上报的经纬度展示出定位点,如果是在普通的地图上直接用经纬度进行定位就好了。但是我们展示的地图相当于是一个手绘地图的图片(业务项目中,该地图为UE地图,不能直接使用地图坐标。需要在地图区域进行转换。),此地图展示的坐标原点和朝向是以目标建筑正面方向为正北方向,右下角位置为坐标原点位置。
下图为目标区域在高德地图中的朝向和位置
但是这个区域在UE地图中展示的情况却是如下
可以看出来,UE坐标是将目标区域进行旋转之后才能得到后面的形状。图中的X轴上的150的值和Y轴上的100的值表示这个区域的尺寸闲置。我们需要把经纬度转换成(x,y)0<x<=150,0<y<=100,才能在目标区域中展示。
思路与代码
问题一:地图旋转
处理地图旋转,我们首先想到的是,先标定一个线条为旋转目标,我们观察目标区域,可以看出来UE地图的X轴上目标区域有一段直线。那我们就在这条直线上取一个相邻的两个坐标点,计算出这两个点的连线偏移地图正北方向的值。
在地图上获取坐标点的连接
获取鼠标点击经纬度
代码如下:
//定义原点坐标点 O [lng,lat]
this.originLat = lat; // 原点的纬度
this.originLng = lng; // 原点的经度
//定义相邻的坐标点 OY
let OY = [lng, lat];
//O和OY的坐标需要自己去地图上获取。
// 获取当前坐标朝向与正北方向的偏差角度
this.angle = this.getAngle(this.originLat, this.originLng, OY[1], OY[0]) - 90;
// 获取角度,和正北方向的顺时针角度
getAngle(lat1, lon1, lat2, lon2) {
const dLon = ((lon2 - lon1) * Math.PI) / 180;
const y = Math.sin(dLon) * Math.cos((lat2 * Math.PI) / 180);
const x =
Math.cos((lat1 * Math.PI) / 180) * Math.sin((lat2 * Math.PI) / 180) -
Math.sin((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.cos(dLon);
const brng = (Math.atan2(y, x) * 180) / Math.PI;
const angle = (brng + 360) % 360;
return angle;
}
我们现在就获取到了this.angle的值,也就是我们所看到的由高德地图的朝向变成UE地图朝向的角度差,为什么要-90呢,因为我们算的是X轴这条边上的一条直线,而不是算的Y轴这条边上的一条直线。
接下来this.angle非常的重要,在后续的坐标转换中都要用到
问题二:坐标点与原点的距离计算
现我们取一个在区域内的随机坐标B,需要计算B与原点坐标O的距离OBL;
方法一:我们可以通过高德地图API完成计算
两点距离
方法二:如果项目中没有引入高德地图,那我们则需要自己进行计算。在可以将计算结果和方法一的结果进行比对,如果误差在接受范围内就可以使用该方法。
// 获取两点之间的距离
calcDistance(lat1, lon1, lat2, lon2) {
const R = 6371e3; // 地球半径
const φ1 = (lat1 * Math.PI) / 180; // 第一个点的纬度转化为弧度
const φ2 = (lat2 * Math.PI) / 180; // 第二个点的纬度转化为弧度
const Δφ = ((lat2 - lat1) * Math.PI) / 180; // 两点纬度之差转化为弧度
const Δλ = ((lon2 - lon1) * Math.PI) / 180; // 两点经度之差转化为弧度
const a =
Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c; // 距离,单位为米
return distance;
}
我们可以利用上面方法得到随机坐标B与原坐标O的距离OBL;
问题三:计算尺寸比例
我们知道区域在X轴,Y轴都有一个尺寸比例,150和100。即说明目标区域Y轴的最远点坐标MY距离原点坐标O的Y轴长度MYOL,转换尺寸即为100。
我们需要手动获取MX的坐标,可使用问题一中提供的连接获取坐标。
let MY= [lng, lat];
let Y = 100;
//接下来我们需要获取MYOL的值,以及MYOL的坐标偏移角度MYOAngle
// 分别计算O->MY 的距离,利用偏移角度计算Y坐标的值,然后比对上Y的尺寸,获得Y轴尺度的比例值,后续可用于计算落点位置
let MYOL= this.calcDistance(this.originLat, this.originLng,MY[1], MY[0]);
let MYOAngle = this.getAngle(this.originLat, this.originLng, MY[1],MY[0]);
//
let OMLY= Math.cos((MYOAngle - this.angle) / 180) * MYOL;
// console.log(OALX, 'O->A的直线距离在正北方向Y轴的距离');
// 计算比例 ratio,因为考虑到X轴Y轴坐标的比例必定一致,不然目标区域会变形,不符合业务要求。所以我们只需要计算一边的比例就行,默认X轴比例Y轴比例一致。
this.ratio = Y / OMLY;
问题四:坐标尺寸转换
现在我们坐标轴的比例算出来了,坐标点与原点的距离,正北方向的偏移角度都能算出来了,那现在坐标换算不是手到擒来吗
我们现在来计算一个在目标区域内坐标点的打点参数(UEX,UEY)。计算UEX,UEY的值。
// 计算在固定坐标尺寸内以正北方向为Y轴,的X轴Y轴的距离
convertLatLngToXY(lng, lat) {
// 计算传入的目标点位落在UE地图上的位置
let originL = this.calcDistance(this.originLat, this.originLng, lat, lng);
// console.log(originL, 'originL 目标点位');
let originAngle = this.getAngle(this.originLat, this.originLng, lat, lng);
// console.log(originAngle, 'originAngle 目标点位的偏差角度');
let originY = Math.cos((Math.PI / 180) * (originAngle - this.angle)) * originL;
let originX = Math.sin((Math.PI / 180) * (originAngle - this.angle)) * originL;
console.log(originX, originY, 'originX+originY');
// 最后再换算一遍,换成UE的坐标
let UEX = originX * this.ratio;
let UEY = originY * this.ratio;
return [UEX, UEY];
}
是不是很清晰明了,是不是拿捏住了。
问题
问题一:因为这个目标区域和尺寸比例,如果定位点不在该区域内可能回导致计算之后的坐标点无法展示的问题,具体根据业务需求。