地图上点到范围的最短距离算法

今天是七夕,应该留点什么才对。恰好遇到地图上点与多边形的距离计算问题,如果是Oracle Spatial控件函数计算的话,有如下几个缺点:耗时久、计算范围有局限。所以我想要在前端地图上计算该点到范围的最短距离。


海伦公式计算面积 :

S=P(Pa)(Pb)(Pc)

三角形面积公式:
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>
  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
SPSS中的最短距离算法是指在聚类分析中使用的一种方法。在层次聚类法中,最短距离算法是一种常用的距离测量方法之一。 最短距离算法是通过计算两个簇中最近的两个点之间的距离来确定簇与簇之间的距离。具体而言,对于两个簇A和B,最短距离算法会计算簇A中的每个点与簇B中的每个点之间的距离,然后选择最小的距离作为簇A和簇B之间的距离。 在SPSS中,你可以使用层次聚类法进行聚类分析,并选择最短距离算法作为距离测量方法。具体操作步骤如下: 1. 打开SPSS软件并加载你的数据集。 2. 在菜单栏中选择“分析”选项,然后选择“分类变量”。 3. 在分类变量对话框中,选择你要进行聚类分析的变量,并点击“确定”。 4. 在分类变量对话框中,选择“层次聚类法”作为聚类方法,并点击“确定”。 5. 在层次聚类对话框中,选择你想要使用的最短距离算法,通常是“最短距离”选项,并点击“确定”。 通过以上步骤,你可以在SPSS中使用最短距离算法进行聚类分析。这种方法可以帮助你识别和组织数据中的相似性,从而进行更深入的数据分析和解释。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [数学建模常用算法汇总及python,MATLAB实现(七) —— sklearn和SPSS实现主成分分析](https://blog.csdn.net/qq_19300283/article/details/125734008)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [聚类分析与SPSS实现——《社会统计分析方法》](https://blog.csdn.net/weixin_51372096/article/details/124041944)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值