zoukankan      html  css  js  c++  java
  • 地图几何计算

    百度地图 JavaScript 开源库 

    类 BMapLib.GeoUtils

    源文件: GeoUtils.js.

    http://api.map.baidu.com/library/GeoUtils/1.2/docs/symbols/BMapLib.GeoUtils.html

    http://api.map.baidu.com/library/GeoUtils/1.2/docs/symbols/src/BMapLib_GeoUtils.js.html

     示例 http://api.map.baidu.com/library/GeoUtils/1.2/examples/simple.html

     /**
      * @fileoverview GeoUtils类提供若干几何算法,用来帮助用户判断点与矩形、
      * 圆形、多边形线、多边形面的关系,并提供计算折线长度和多边形的面积的公式。 
      * 主入口类是<a href="symbols/BMapLib.GeoUtils.html">GeoUtils</a>,
      * 基于Baidu Map API 1.2。
      *
      * @author Baidu Map Api Group 
      * @version 1.2
      */
     
     /** 
      * @namespace BMap的所有library类均放在BMapLib命名空间下
      */
     var BMapLib = window.BMapLib = BMapLib || {};
     (function() { 
         
         /**
          * 地球半径
          */
         var EARTHRADIUS = 6370996.81; 
     
         /** 
          * @exports GeoUtils as BMapLib.GeoUtils 
          */
         var GeoUtils =
         /**
          * GeoUtils类,静态类,勿需实例化即可使用
          * @class GeoUtils类的<b>入口</b>。
          * 该类提供的都是静态方法,勿需实例化即可使用。     
          */
         BMapLib.GeoUtils = function(){
             
         }
         
         /**
          * 判断点是否在矩形内
          * @param {Point} point 点对象
          * @param {Bounds} bounds 矩形边界对象
          * @returns {Boolean} 点在矩形内返回true,否则返回false
          */
         GeoUtils.isPointInRect = function(point, bounds){
             //检查类型是否正确
             if (!(point instanceof BMap.Point) || 
                 !(bounds instanceof BMap.Bounds)) {
                 return false;
             }
             var sw = bounds.getSouthWest(); //西南脚点
             var ne = bounds.getNorthEast(); //东北脚点
             return (point.lng >= sw.lng && point.lng <= ne.lng && point.lat >= sw.lat && point.lat <= ne.lat);
         }
         
         /**
          * 判断点是否在圆形内
          * @param {Point} point 点对象
          * @param {Circle} circle 圆形对象
          * @returns {Boolean} 点在圆形内返回true,否则返回false
          */
         GeoUtils.isPointInCircle = function(point, circle){
             //检查类型是否正确
             if (!(point instanceof BMap.Point) || 
                 !(circle instanceof BMap.Circle)) {
                 return false;
             }
     
             //point与圆心距离小于圆形半径,则点在圆内,否则在圆外
             var c = circle.getCenter();
             var r = circle.getRadius();
     
             var dis = GeoUtils.getDistance(point, c);
             if(dis <= r){
                 return true;
             } else {
                 return false;
             }
         }
         
         /**
          * 判断点是否在折线上
          * @param {Point} point 点对象
          * @param {Polyline} polyline 折线对象
          * @returns {Boolean} 点在折线上返回true,否则返回false
          */
         GeoUtils.isPointOnPolyline = function(point, polyline){
             //检查类型
             if(!(point instanceof BMap.Point) ||
                 !(polyline instanceof BMap.Polyline)){
                 return false;
             }
     
             //首先判断点是否在线的外包矩形内,如果在,则进一步判断,否则返回false
             var lineBounds = polyline.getBounds();
             if(!this.isPointInRect(point, lineBounds)){
                 return false;
             }
     
             //判断点是否在线段上,设点为Q,线段为P1P2 ,
             //判断点Q在该线段上的依据是:( Q - P1 ) × ( P2 - P1 ) = 0,且 Q 在以 P1,P2为对角顶点的矩形内
             var pts = polyline.getPath();
             for(var i = 0; i < pts.length - 1; i++){
                 var curPt = pts[i];
                 var nextPt = pts[i + 1];
                 //首先判断point是否在curPt和nextPt之间,即:此判断该点是否在该线段的外包矩形内
                 if (point.lng >= Math.min(curPt.lng, nextPt.lng) && point.lng <= Math.max(curPt.lng, nextPt.lng) &&
                     point.lat >= Math.min(curPt.lat, nextPt.lat) && point.lat <= Math.max(curPt.lat, nextPt.lat)){
                     //判断点是否在直线上公式
                     var precision = (curPt.lng - point.lng) * (nextPt.lat - point.lat) - 
                         (nextPt.lng - point.lng) * (curPt.lat - point.lat);                
                     if(precision < 2e-10 && precision > -2e-10){//实质判断是否接近0
                         return true;
                     }                
                 }
             }
             
             return false;
         }
         
         /**
          * 判断点是否多边形内
          * @param {Point} point 点对象
          * @param {Polyline} polygon 多边形对象
          * @returns {Boolean} 点在多边形内返回true,否则返回false
          */
         GeoUtils.isPointInPolygon = function(point, polygon){
             //检查类型
             if(!(point instanceof BMap.Point) ||
                 !(polygon instanceof BMap.Polygon)){
                 return false;
             }
     
             //首先判断点是否在多边形的外包矩形内,如果在,则进一步判断,否则返回false
             var polygonBounds = polygon.getBounds();
             if(!this.isPointInRect(point, polygonBounds)){
                 return false;
             }
     
             var pts = polygon.getPath();//获取多边形点
             
             //下述代码来源:http://paulbourke.net/geometry/insidepoly/,进行了部分修改
             //基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则
             //在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。
             
             var N = pts.length;
             var boundOrVertex = true; //如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
             var intersectCount = 0;//cross points count of x 
             var precision = 2e-10; //浮点类型计算时候与0比较时候的容差
             var p1, p2;//neighbour bound vertices
             var p = point; //测试点
             
             p1 = pts[0];//left vertex        
             for(var i = 1; i <= N; ++i){//check all rays            
                 if(p.equals(p1)){
                     return boundOrVertex;//p is an vertex
                 }
                 
                 p2 = pts[i % N];//right vertex            
                 if(p.lat < Math.min(p1.lat, p2.lat) || p.lat > Math.max(p1.lat, p2.lat)){//ray is outside of our interests                
                     p1 = p2; 
                     continue;//next ray left point
                 }
                 
                 if(p.lat > Math.min(p1.lat, p2.lat) && p.lat < Math.max(p1.lat, p2.lat)){//ray is crossing over by the algorithm (common part of)
                     if(p.lng <= Math.max(p1.lng, p2.lng)){//x is before of ray                    
                         if(p1.lat == p2.lat && p.lng >= Math.min(p1.lng, p2.lng)){//overlies on a horizontal ray
                             return boundOrVertex;
                         }
                         
                         if(p1.lng == p2.lng){//ray is vertical                        
                             if(p1.lng == p.lng){//overlies on a vertical ray
                                 return boundOrVertex;
                             }else{//before ray
                                 ++intersectCount;
                             } 
                         }else{//cross point on the left side                        
                             var xinters = (p.lat - p1.lat) * (p2.lng - p1.lng) / (p2.lat - p1.lat) + p1.lng;//cross point of lng                        
                             if(Math.abs(p.lng - xinters) < precision){//overlies on a ray
                                 return boundOrVertex;
                             }
                             
                             if(p.lng < xinters){//before ray
                                 ++intersectCount;
                             } 
                         }
                     }
                 }else{//special case when ray is crossing through the vertex                
                     if(p.lat == p2.lat && p.lng <= p2.lng){//p crossing over p2                    
                         var p3 = pts[(i+1) % N]; //next vertex                    
                         if(p.lat >= Math.min(p1.lat, p3.lat) && p.lat <= Math.max(p1.lat, p3.lat)){//p.lat lies between p1.lat & p3.lat
                             ++intersectCount;
                         }else{
                             intersectCount += 2;
                         }
                     }
                 }            
                 p1 = p2;//next ray left point
             }
             
             if(intersectCount % 2 == 0){//偶数在多边形外
                 return false;
             } else { //奇数在多边形内
                 return true;
             }            
         }
     
         /**
          * 将度转化为弧度
          * @param {degree} Number 度     
          * @returns {Number} 弧度
          */
         GeoUtils.degreeToRad =  function(degree){
             return Math.PI * degree/180;    
         }
         
         /**
          * 将弧度转化为度
          * @param {radian} Number 弧度     
          * @returns {Number} 度
          */
         GeoUtils.radToDegree = function(rad){
             return (180 * rad) / Math.PI;       
         }
         
         /**
          * 将v值限定在a,b之间,纬度使用
          */
         function _getRange(v, a, b){
             if(a != null){
               v = Math.max(v, a);
             }
             if(b != null){
               v = Math.min(v, b);
             }
             return v;
         }
         
         /**
          * 将v值限定在a,b之间,经度使用
          */
         function _getLoop(v, a, b){
             while( v > b){
               v -= b - a
             }
             while(v < a){
               v += b - a
             }
             return v;
         }
     
         /**
          * 计算两点之间的距离,两点坐标必须为经纬度
          * @param {point1} Point 点对象
          * @param {point2} Point 点对象
          * @returns {Number} 两点之间距离,单位为米
          */
         GeoUtils.getDistance = function(point1, point2){
             //判断类型
             if(!(point1 instanceof BMap.Point) ||
                 !(point2 instanceof BMap.Point)){
                 return 0;
             }
     
             point1.lng = _getLoop(point1.lng, -180, 180);
             point1.lat = _getRange(point1.lat, -74, 74);
             point2.lng = _getLoop(point2.lng, -180, 180);
             point2.lat = _getRange(point2.lat, -74, 74);
             
             var x1, x2, y1, y2;
             x1 = GeoUtils.degreeToRad(point1.lng);
             y1 = GeoUtils.degreeToRad(point1.lat);
             x2 = GeoUtils.degreeToRad(point2.lng);
             y2 = GeoUtils.degreeToRad(point2.lat);
     
             return EARTHRADIUS * Math.acos((Math.sin(y1) * Math.sin(y2) + Math.cos(y1) * Math.cos(y2) * Math.cos(x2 - x1)));    
         }
         
         /**
          * 计算折线或者点数组的长度
          * @param {Polyline|Array<Point>} polyline 折线对象或者点数组
          * @returns {Number} 折线或点数组对应的长度
          */
         GeoUtils.getPolylineDistance = function(polyline){
             //检查类型
             if(polyline instanceof BMap.Polyline || 
                 polyline instanceof Array){
                 //将polyline统一为数组
                 var pts;
                 if(polyline instanceof BMap.Polyline){
                     pts = polyline.getPath();
                 } else {
                     pts = polyline;
                 }
                 
                 if(pts.length < 2){//小于2个点,返回0
                     return 0;
                 }
     
                 //遍历所有线段将其相加,计算整条线段的长度
                 var totalDis = 0;
                 for(var i =0; i < pts.length - 1; i++){
                     var curPt = pts[i];
                     var nextPt = pts[i + 1]
                     var dis = GeoUtils.getDistance(curPt, nextPt);
                     totalDis += dis;
                 }
     
                 return totalDis;
                 
             } else {
                 return 0;
             }
         }
         
         /**
          * 计算多边形面或点数组构建图形的面积,注意:坐标类型只能是经纬度,且不适合计算自相交多边形的面积
          * @param {Polygon|Array<Point>} polygon 多边形面对象或者点数组
          * @returns {Number} 多边形面或点数组构成图形的面积
          */
         GeoUtils.getPolygonArea = function(polygon){
             //检查类型
             if(!(polygon instanceof BMap.Polygon) &&
                 !(polygon instanceof Array)){
                 return 0;
             }
             var pts;
             if(polygon instanceof BMap.Polygon){
                 pts = polygon.getPath();
             }else{
                 pts = polygon;    
             }
             
             if(pts.length < 3){//小于3个顶点,不能构建面
                 return 0;
             }
             
             var totalArea = 0;//初始化总面积
             var LowX = 0.0;
             var LowY = 0.0;
             var MiddleX = 0.0;
             var MiddleY = 0.0;
             var HighX = 0.0;
             var HighY = 0.0;
             var AM = 0.0;
             var BM = 0.0;
             var CM = 0.0;
             var AL = 0.0;
             var BL = 0.0;
             var CL = 0.0;
             var AH = 0.0;
             var BH = 0.0;
             var CH = 0.0;
             var CoefficientL = 0.0;
             var CoefficientH = 0.0;
             var ALtangent = 0.0;
             var BLtangent = 0.0;
             var CLtangent = 0.0;
             var AHtangent = 0.0;
             var BHtangent = 0.0;
             var CHtangent = 0.0;
             var ANormalLine = 0.0;
             var BNormalLine = 0.0;
             var CNormalLine = 0.0;
             var OrientationValue = 0.0;
             var AngleCos = 0.0;
             var Sum1 = 0.0;
             var Sum2 = 0.0;
             var Count2 = 0;
             var Count1 = 0;
             var Sum = 0.0;
             var Radius = EARTHRADIUS; //6378137.0,WGS84椭球半径 
             var Count = pts.length;        
             for (var i = 0; i < Count; i++) {
                 if (i == 0) {
                     LowX = pts[Count - 1].lng * Math.PI / 180;
                     LowY = pts[Count - 1].lat * Math.PI / 180;
                     MiddleX = pts[0].lng * Math.PI / 180;
                     MiddleY = pts[0].lat * Math.PI / 180;
                     HighX = pts[1].lng * Math.PI / 180;
                     HighY = pts[1].lat * Math.PI / 180;
                 }
                 else if (i == Count - 1) {
                     LowX = pts[Count - 2].lng * Math.PI / 180;
                     LowY = pts[Count - 2].lat * Math.PI / 180;
                     MiddleX = pts[Count - 1].lng * Math.PI / 180;
                     MiddleY = pts[Count - 1].lat * Math.PI / 180;
                     HighX = pts[0].lng * Math.PI / 180;
                     HighY = pts[0].lat * Math.PI / 180;
                 }
                 else {
                     LowX = pts[i - 1].lng * Math.PI / 180;
                     LowY = pts[i - 1].lat * Math.PI / 180;
                     MiddleX = pts[i].lng * Math.PI / 180;
                     MiddleY = pts[i].lat * Math.PI / 180;
                     HighX = pts[i + 1].lng * Math.PI / 180;
                     HighY = pts[i + 1].lat * Math.PI / 180;
                 }
                 AM = Math.cos(MiddleY) * Math.cos(MiddleX);
                 BM = Math.cos(MiddleY) * Math.sin(MiddleX);
                 CM = Math.sin(MiddleY);
                 AL = Math.cos(LowY) * Math.cos(LowX);
                 BL = Math.cos(LowY) * Math.sin(LowX);
                 CL = Math.sin(LowY);
                 AH = Math.cos(HighY) * Math.cos(HighX);
                 BH = Math.cos(HighY) * Math.sin(HighX);
                 CH = Math.sin(HighY);
                 CoefficientL = (AM * AM + BM * BM + CM * CM) / (AM * AL + BM * BL + CM * CL);
                 CoefficientH = (AM * AM + BM * BM + CM * CM) / (AM * AH + BM * BH + CM * CH);
                 ALtangent = CoefficientL * AL - AM;
                 BLtangent = CoefficientL * BL - BM;
                 CLtangent = CoefficientL * CL - CM;
                 AHtangent = CoefficientH * AH - AM;
                 BHtangent = CoefficientH * BH - BM;
                 CHtangent = CoefficientH * CH - CM;
                 AngleCos = (AHtangent * ALtangent + BHtangent * BLtangent + CHtangent * CLtangent) / (Math.sqrt(AHtangent * AHtangent + BHtangent * BHtangent + CHtangent * CHtangent) * Math.sqrt(ALtangent * ALtangent + BLtangent * BLtangent + CLtangent * CLtangent));
                 AngleCos = Math.acos(AngleCos);            
                 ANormalLine = BHtangent * CLtangent - CHtangent * BLtangent;
                 BNormalLine = 0 - (AHtangent * CLtangent - CHtangent * ALtangent);
                 CNormalLine = AHtangent * BLtangent - BHtangent * ALtangent;
                 if (AM != 0)
                     OrientationValue = ANormalLine / AM;
                 else if (BM != 0)
                     OrientationValue = BNormalLine / BM;
                 else
                     OrientationValue = CNormalLine / CM;
                 if (OrientationValue > 0) {
                     Sum1 += AngleCos;
                     Count1++;
                 }
                 else {
                     Sum2 += AngleCos;
                     Count2++;
                 }
             }        
             var tempSum1, tempSum2;
             tempSum1 = Sum1 + (2 * Math.PI * Count2 - Sum2);
             tempSum2 = (2 * Math.PI * Count1 - Sum1) + Sum2;
             if (Sum1 > Sum2) {
                 if ((tempSum1 - (Count - 2) * Math.PI) < 1)
                     Sum = tempSum1;
                 else
                     Sum = tempSum2;
             }
             else {
                 if ((tempSum2 - (Count - 2) * Math.PI) < 1)
                     Sum = tempSum2;
                 else
                     Sum = tempSum1;
             }
             totalArea = (Sum - (Count - 2) * Math.PI) * Radius * Radius;
     
             return totalArea; //返回总面积
         }
        
     })();//闭包结束

     参考文献:http://erich.realtimerendering.com/ptinpoly/

    博客地址: https://www.cnblogs.com/smartstar/
    博客版权: 本文以学习、研究和分享为主,欢迎转载,但必须在文章页面明显位置给出原文连接。
    如果文中有不妥或者错误的地方还望高手的你指出,以免误人子弟。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步!
    再次感谢您耐心的读完本篇文章。

  • 相关阅读:
    Jsp 中文乱码,解决
    dojox.grid.EnhancedGrid 和 dojox.grid.DataGrid 的继承关系
    使用 Maven 部署 artifact 到 Nexus 教程
    [LeetCode] 187. Repeated DNA Sequences 解题思路
    [LeetCode] 204. Count Primes 解题思路
    [LeetCode] 219. Contains Duplicate II 解题思路
    [LeetCode] 310. Minimum Height Trees 解题思路
    [LeetCode] 200. Number of Islands 解题思路
    PHP7添加mysqli扩展
    MySQL中的锁(表锁、行锁)
  • 原文地址:https://www.cnblogs.com/smartstar/p/10058613.html
Copyright © 2011-2022 走看看