• 地球坐标系转为百度坐标系


    因项目需要,现在已有的是天地图的经纬度数据,即地球坐标系,也称WGS-84

    业务需求需要判断某个点的经纬度是否在某个面里,天地图不支持对应的API,然后在百度里找到了一个

    BMapLib.GeoUtils.isPointInPolygon

    这个方法需要提前引入js库

    <script src="./js/GeoUtils.js"></script>

    GeoUtils.js具体代码如下,直接拷贝保存即可

    /**
     * @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; //返回总面积
        }
       
    })();//闭包结束

    然后有了这个方法以后,真正的技术难题是如何把地球坐标系的经纬度转为百度坐标系,坐标转换具体原因可自行百度查阅。

    最后经过千辛万苦,终于在一位大佬的博文里看到了曲线救国的方法

    先把地球坐标系转为火星坐标系,再把火星坐标系转为百度坐标系,虽然麻烦一些,但是总归完成了效果

    具体代码如下 

    tips

    outOfChina方法中
    原博文里的lat和lon顺序反了,这里特此纠正
    function transformWDS2BD (nowlat, nowlon) {
        // 地球坐标系(WGS-84)转火星坐标系(GCJ)
        var pi = 3.14159265358979324;
        var a = 6378245.0;
        var ee = 0.00669342162296594323;
        /*判断是否在国内,不在国内则不做偏移*/
      function outOfChina(lat, lon)
        {
            if ((lon < 72.004 || lon > 137.8347)&&(lat < 0.8293 || lat > 55.8271)){
                return true;
            }else {
                return false;
            }
        }
        function transformLat(x,y)
        {
            var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
            ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
            ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
            ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
            return ret;
        }
    
        function transformLon(x,y)
        {
            var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
            ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
            ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
            ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
            return ret;
        }
    
        function transform(wgLat,wgLon)
        {
            var mars_point={lon:0,lat:0};
            if (outOfChina(wgLat, wgLon))
            {
                mars_point.lat = wgLat;
                mars_point.lon = wgLon;
                return mars_point;
            }
            var dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
            var dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
            var radLat = wgLat / 180.0 * pi;
            var magic = Math.sin(radLat);
            magic = 1 - ee * magic * magic;
            var sqrtMagic = Math.sqrt(magic);
            dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
            dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
            mars_point.lat = wgLat + dLat;
            mars_point.lon = wgLon + dLon;
            return mars_point
        }
      // 火星坐标转百度坐标
      var x_pi=3.14159265358979324 * 3000.0 / 180.0;
      function marsTobaidu(mars_point){
          var baidu_point={lon:0,lat:0};
          var x=mars_point.lon;
          var y=mars_point.lat;
          var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
          var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
          baidu_point.lon = z * Math.cos(theta) + 0.0065;
          baidu_point.lat = z * Math.sin(theta) + 0.006;
          return baidu_point;
      }
    
      let nowMars_point = transform(+nowlat, +nowlon)
      let baidu_point = marsTobaidu(nowMars_point)
      return baidu_point
    }

    使用的时候直接引入

    <script src="./js/transformLatLon.js"></script>
    let baidu_point = transformWDS2BD(lat, lon)// lat、lon为你自己的经纬度

    完结撒花,如有不妥,还请指教

  • 相关阅读:
    一文搞懂 ThreadLocal 原理
    听说用 Lombok 可以早点下班?
    原来 CPU 为程序性能优化做了这么多
    如何优雅地中止线程?
    线程数,射多少更舒适?
    Elasticsearch 之聚合分析入门
    Go语言之旅:基本类型
    Go语言之旅:包
    网络七层协议之数据链路层
    网络七层协议之物理层
  • 原文地址:https://www.cnblogs.com/LeoXnote/p/12580048.html
Copyright © 2020-2023  润新知