最近处理GPS定位坐标在Cesium中显示的问题,需要将定位坐标绘制到指定的路线上。但实际的显示结果是,因为定位不准确,最终绘制的GPS坐标基本都是在路线周围乱跳。为了效果好看一点,最终的想法将球面上短距离的路线看作是平面坐标中的直线,将定位坐标通过计算,算出坐标点到直线的垂足点,将垂足点作为最终的定位坐标。
整理相关 JS 方法如下:
1. 计算点到直线的垂足点
/** * 点到直线的垂足点 */ function getFootPoint(point, start, end) { var A = end.Latitude - start.Latitude; var B = start.Longitude - end.Longitude; var C = end.Longitude * start.Latitude - start.Longitude * end.Latitude; if (A * A + B * B < 1e-13) { return start; // start与end重叠 } else if (Math.abs(A * point.Longitude + B * point.Latitude + C) < 1e-13) { return point; // point在直线上(start_end) } else { var Longitude = (B * B * point.Longitude - A * B * point.Latitude - A * C) / (A * A + B * B); var Latitude = (-A * B * point.Longitude + A * A * point.Latitude - B * C) / (A * A + B * B); return { Longitude: Longitude, Latitude: Latitude }; } }
2. 计算点到直线的距离(平面坐标)
/** * 点到直线的距离 */ function getDistancePoineToLine_planeCoord(point, start, end) { var A = end.Latitude - start.Latitude; var B = start.Longitude - end.Longitude; var C = end.Longitude * start.Latitude - start.Longitude * end.Latitude; if (A * A + B * B < 1e-13) { // pnt1与pnt2重叠 var dx = point.Longitude - start.Longitude; var dy = point.Latitude - start.Latitude; return Math.sqrt(dx * dx + dy * dy); } else if (Math.abs(A * point.Longitude + B * point.Latitude + C) < 1e-13) { return 0; // point在直线上(pnt1_pnt2) } else { var distance = Math.abs(A * point.Longitude + B * point.Latitude + C) / Math.sqrt(A * A + B * B); return distance; } }
3. 计算点到线的距离(WGS84坐标)
/** * 点到线的距离 */ function getDistance_wgs84(p1_x, p1_y, p2_x, p2_y) { var lat1 = p1_x; var lon1 = p1_y; var lat2 = p2_x; var lon2 = p2_y; var earch_radius = 6371008.8; // 地球半径 平均值 米 //用haversine公式计算球面两点间的距离 //经纬度转换成弧度 var h_lat1 = lat1 * Math.PI / 180.0; var h_lon1 = lon1 * Math.PI / 180.0; var h_lat2 = lat2 * Math.PI / 180.0; var h_lon2 = lon2 * Math.PI / 180.0; //差值 var vlon = Math.abs(h_lon1 - h_lon2); var vlat = Math.abs(h_lat1 - h_lat2); // var h = HaverSin(vlat) + Math.cos(h_lat1) * Math.cos(h_lat2) * HaverSin(vlon); // var d = 2 * earch_radius * Math.asin(Math.sqrt(h)); // return d; } function HaverSin(theta) { var v = Math.sin(theta / 2); return v * v; }
4. 计算点到线的距离(笛卡尔坐标)
/** * 点到线的距离 */ function getDistanceByPointToLine_Coordinate(point, pnt1, pnt2) { var dis = 0; if (pnt1.x == pnt2.x) { if (pnt1.y == pnt2.y) { var dx = point.x - pnt1.x; var dy = point.y - pnt1.y; dis = Math.sqrt(dx * dx + dy * dy); } else dis = Math.abs(point.x - pnt1.x); } else { var lineK = (pnt2.y - pnt1.y) / (pnt2.x - pnt1.x); var lineC = (pnt2.x * pnt1.y - pnt1.x * pnt2.y) / (pnt2.x - pnt1.x); dis = Math.abs(lineK * point.x - point.y + lineC) / (Math.sqrt(lineK * lineK + 1)); } return dis; }
通过上面的方法,将很短距离的球面坐标看做平面坐标,从而计算出点到直线的垂足。最后,使用计算的垂足点再进行处理,经过验证,定位效果基本上符合要求。