今天是七夕,应该留点什么才对。恰好遇到地图上点与多边形的距离计算问题,如果是Oracle Spatial控件函数计算的话,有如下几个缺点:耗时久、计算范围有局限。所以我想要在前端地图上计算该点到范围的最短距离。
海伦公式计算面积 :
S=P(P−a)(P−b)(P−c)−−−−−−−−−−−−−−−−−−−√
三角形面积公式:
S=底∗高2
根据面积法可推断出高,即点到一条直线的最短距离。所以我们只要知道哪条边是点到这个范围最近的那条边即可。多边形是由N个点组合而成,所以只要计算点到这N个点的距离,再排序拿到最近的2个距离对应的点A 、B ,与范围外的点C 组成最近三角形,利用面积相等公式得到高,也就是点到范围最近的距离了。当然啦~只有锐角三角形才成立,钝角三角形的需要另想办法~但是先把锐角三角形的贴出来吧。
将以上想法用代码实现如下:
//求点与范围最短的距离
function getDis(point,polygon){
//得到点到范围上每个点的距离。排序拿到最短的2个。以这2个点+外点 做三角形算最短距离
var points = polygon.getPath();
var disArray = [] ;
for(var i= 0;i<points.length;i++){
var disI = map.getDistance(point,points[i]);//计算地图上点到点的距离
disArray.push( disI );
}
//顺序推导
var newArray = disArray.slice();//复制数组
newArray.sort();//复制后的数组排序
//找出新数组中第一个元素和第二个元素在原数组中的位置进而拿到下标
var ac = newArray[0];
var bc = newArray[1];
var pointA ,pointB ;
for(var i =0;i<disArray.length;i++){
if(ac == disArray[i]){
pointA = points[i];
}
if(bc ==disArray[i]){
pointB = points[i];
}
}
var ab = map.getDistance(pointA,pointB);
var p =( ab + ac+ bc)/2 ; //半周长
var S = Math.sqrt(p*(p-ab)*(p-bc)*(p-ac));//面积
var h = 2*S / ab ;
return h ;
}
–by 2016-9-29 补充说明
这次贴出判断是否锐角的代码,这样就可以比较这2点的高是否是可采取的。如果不是锐角的话,需要再判断其他的点。思前想后,这个算法也不是很完美,尽量靠近最短距离吧。请细心的朋友帮我指正^-^。
1、相邻的情况:
锐角:
钝角:
2、不相邻的情况:
需要与其他的点重做三角形判断
–我们只需要关注多边形上的2点与范围外的点组成的夹角是否是锐角即可。
直角三角形的勾股定理:a*a +b*b = c*c
//是否锐角三角形:
function ifAcuteAngle(pointA,pointB,pointC){
//求三边长:ab ac bc
var ab,ac,bc ;
var result = false ;
ab = map.getDistance(pointA,pointB);
ac = map.getDistance(pointA,pointC);
bc = map.getDistance(pointB,pointC);
return result = ((ab*ab +ac*ac - bc*bc )> 0)&&((ab*ab +bc*bc - ac*ac) >0 ) ;
}
目前计算最短距离的算法是:
1、多边形上的2点是否是相邻:则需要知道下标的绝对值是否=1
2、多边形与范围外的一点形成的夹角师傅是锐角 ==>三个坐标点
只有既是相邻点又是锐角的情况下才可以取高为最短距离。
否则就不可以取高为最短距离。
贴出整段代码:百度秘钥需要自己去申请,我就不提供了~
<html>
<head>
<title></title>
<meta http-equiv="content-type" content="text/html; charset=gbk">
</head>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=百度秘钥"></script>
<body>
input:<input type="text" id="input" />
<input type="button" value ="show" id="button" />
dis:<input type="text" id="dis" />
<input type="button" value ="clear" id="clear" />
<div id="allmap" style="width:100%;height:90%"></div>
<script type="text/javascript">
var map = new BMap.Map("allmap");
map.centerAndZoom(new BMap.Point(121.309918,31.207647), 14);
map.enableScrollWheelZoom();
//添加固定范围
var polygon = new BMap.Polygon([
new BMap.Point(121.309056,31.22383),
new BMap.Point(121.32006,31.216747),
new BMap.Point(121.322575,31.200193),
new BMap.Point(121.30752,31.189041),
new BMap.Point(121.322998,31.185036),
new BMap.Point(121.331046,31.171689),
new BMap.Point(121.331621,31.170082),
new BMap.Point(121.353324,31.177868),
new BMap.Point(121.363673,31.187755),
new BMap.Point(121.35548,31.229018),
new BMap.Point(121.335646,31.229141),
new BMap.Point(121.317967,31.224201)
], {strokeColor:"red", strokeWeight:2, strokeOpacity:0.8}); //创建多边形
map.addOverlay(polygon); //增加多边形
document.getElementById("clear").onclick = function(){
map.clearOverlays();
map.addOverlay(polygon); //增加多边形
}
document.getElementById("button").onclick = function(){
var point = document.getElementById("input").value ;
var lng = parseFloat(point.split(",")[0]);
var lat = parseFloat(point.split(",")[1]);
var pointM = new BMap.Point(lng,lat);
var marker = new BMap.Marker(pointM) ;
marker.setLabel(new BMap.Label("C",{offset:new BMap.Size(20,-10)}));
map.addOverlay(marker);
document.getElementById("dis").value = getShortDis(pointM,polygon);
}
function showMarker(point,name){
var marker = new BMap.Marker(point) ;
marker.setLabel(new BMap.Label(name,{offset:new BMap.Size(20,-10)}));
map.addOverlay(marker);
}
function showLine(pointA,pointB){
var points = new Array();
points.push(pointA);
points.push(pointB) ;
map.addOverlay( new BMap.Polyline(points));
}
function getShortDis(point,polygon){
//得到点到范围上每个点的距离。排序拿到最短的2个。以这2个点+外点 做三角形算最短距离
var points = polygon.getPath();
var disArray = [] ;
for(var i= 0;i<points.length;i++){
var disI = map.getDistance(point,points[i]);//计算地图上点到点的距离
disArray.push( disI);
}
//顺序推导
var newArray = disArray.slice();//复制数组,为的是不影响原数组的数据
newArray.sort(function(n1,n2){ return n1-n2 ; }); //数值类型的排序
//以最短的两条边找到合适的三角形
var ac = newArray[0]; //若以下判断都不适用时,直接返回ac
var bc = newArray[1];
var isNeighber = false ;
var isAcuteAngle = false ;
var pointA,pointB,m,n ;
//以距离得到AB 的坐标点
for(var i =0;i<disArray.length;i++){
if(ac == disArray[i]){
pointA = points[i];
showMarker(pointA,"A:"+ac);
showLine(point,pointA);
m = i ;
}
if(bc ==disArray[i]){
pointB = points[i];
showMarker(pointB,"B:"+bc);
showLine(point,pointB);
n = i ;
}
}
//以2点下标差值的绝对值判断仍需要计算几个多边形
isNeighber = Math.abs(m-n)==1 ;
isAcuteAngle =ifAcuteAngle(pointA,pointB,point) ;
if(isNeighber && isAcuteAngle){
return getH(pointA,pointB,point,disArray);
}else{
//是相邻的,但是是钝角。则直接以第三点为判断,若第三点也为钝角,直接以最短边为距离返回,若第3点不是钝角,则求高后进行比较再返回
if(isNeighber){
var dc = newArray[2];
var pointD =point[getIndex(dc,disArray)];
//判断ACD 和BCD 是否是相邻且是钝角
if(iFNeighber(pointA,pointD,point,disArray)&&ifAcuteAngle(pointA,pointD,point)){
var h = getH(pointA,pointD,point,disArray);
return h < ac ? h : ac ;
}else if(iFNeighber(pointB,pointD,point,disArray)&&ifAcuteAngle(pointB,pointD,point)){
var h = getH(pointB,pointD,point,disArray);
return h < ac ? h : ac ;
}else{
return ac ;
}
}else{
//不是相邻的,需要计算中间所有点的
var num = Math.abs(m-n) ;//中间点的个数
var hArray = [];
for(var k =0;k<num ;k++){
//取到中间的每个点,组合三角形。
var nc = disArray[k+2];
var pointN = points[getIndex(nc,disArray)];
var ha = getH(pointA,pointN,point,disArray) ;
var hb = getH(pointB,pointN,point,disArray) ;
var min = Math.min(ac,ha,hb) ;
if(min < ac){
hArray.push(min);
}
}
if(hArray.length == 0){
return ac ;
}else{
//取数组中最小的那个值
hArray.sort(function(n1,n2){ return n1-n2 ; });
return hArray[0];
}
}
}
}
//根据距离,得到下标
function getIndex(dis,disArray){
for(var i = 0 ;i<disArray.length;i++){
if(dis ==disArray[i] ){
return i ;
}
}
}
//是否相邻:
function iFNeighber(pointA,pointB,pointC,disArray){
var ab = map.getDistance(pointA,pointB);
var ac = map.getDistance(pointA,pointC);
var bc = map.getDistance(pointB,pointC);
var m ,n ;
//找出下标:
m = getIndex(ac,disArray);
n = getIndex(bc,disArray);
return Math.abs(m-n)==1 ;
}
//是否锐角三角形:
function ifAcuteAngle(pointA,pointB,pointC){
//求三边长:ab ac bc
var ab,ac,bc ;
var result = false ;
ab = map.getDistance(pointA,pointB);
ac = map.getDistance(pointA,pointC);
bc = map.getDistance(pointB,pointC);
return result = ((ab*ab +ac*ac - bc*bc )> 0)&&((ab*ab +bc*bc - ac*ac) >0 ) ;
}
//根据面积法得到C到AB的高,即范围外一点到多边形的最短距离
function getH(pointA,pointB,pointC,disArray){
var ab,ac,bc ,h;
var ab = map.getDistance(pointA,pointB);
var ac = map.getDistance(pointA,pointC);
var bc = map.getDistance(pointB,pointC);
if(iFNeighber(pointA,pointB,pointC,disArray)&&ifAcuteAngle(pointA,pointB,pointC)){
var p =( ab + ac+ bc)/2 ; //半周长
var S = Math.sqrt(p*(p-ab)*(p-bc)*(p-ac));//面积
h = 2*S / ab ;
return h <ac ?h:ac
}
return ac ;
}
</script>
</body>
</html>