【问题标题】:Google Maps Two Circles Intersection Points谷歌地图两个圆的交点
【发布时间】:2014-03-18 03:01:42
【问题描述】:

有没有一种简单的方法来获取Google Maps API V3 中两个circles 的交点(如果有)的lat/lng?还是我应该使用hard 方式?

编辑:在我的问题中,圆总是具有相同的半径,以防使解决方案更容易。

【问题讨论】:

    标签: javascript algorithm google-maps google-maps-api-3 geometry


    【解决方案1】:

    是的,对于相等的圆圈,可以制定相当简单的解决方案:
    让我们第一个圆心是A点,第二个圆心是F,中点是C,交点是B,D。 ABC是直角球面三角形,角为直角C。

    我们要找到角度 A - 这是从 A-F 方向的偏差角度。球面三角学(直角球面三角形的纳皮尔规则)给了我们公式:

    cos(A)= tg(AC) * ctg(AB) 其中一个符号表示球面角,双符号表示大圆弧的角度(AB,AC)。我们可以看到 AB = 圆半径(当然是弧度),AC = 大圆弧上 A 和 F 之间的一半距离。 要查找 AC(和其他值) - 我将使用代码 from this excellent page

    var R = 6371; // km
    var dLat = (lat2-lat1).toRad();
    var dLon = (lon2-lon1).toRad();
    var lat1 = lat1.toRad();
    var lat2 = lat2.toRad();
    
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
    

    和我们的

    AC = c/2
    

    如果给定的圆半径Rd是公里,那么

    AB = Rd / R = Rd / 6371
    

    现在我们可以找到角度了

    A = arccos(tg(AC) * ctg(AB))
    

    起始方位(AF方向):

    var y = Math.sin(dLon) * Math.cos(lat2);
    var x = Math.cos(lat1)*Math.sin(lat2) -
            Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
    var brng = Math.atan2(y, x);
    

    交叉点的方位:

    B_bearing = brng - A
    D_bearing = brng + A
    

    交点坐标:

    var latB = Math.asin( Math.sin(lat1)*Math.cos(Rd/R) + 
                  Math.cos(lat1)*Math.sin(Rd/R)*Math.cos(B_bearing) );
    var lonB = lon1.toRad() + Math.atan2(Math.sin(B_bearing)*Math.sin(Rd/R)*Math.cos(lat1), 
                         Math.cos(Rd/R)-Math.sin(lat1)*Math.sin(lat2));
    

    同样适用于 D_bearing

    latB, lonB 以弧度为单位

    【讨论】:

    • 不幸的是,该功能不起作用(很可能我遗漏了一些东西)。你能检查一下这个小提琴jsfiddle.net/rNW76/18,让我知道我错过了什么吗?
    • 请注意,'lonSol1 = ...` 表达式中的 lon1 仍然是度数,但 atan2 返回弧度!
    • 我在这里修复了它jsfiddle.net/rNW76/24,但仍然没有显示预期的结果。
    【解决方案2】:

    computation the "hard" way 可以简化为 r1 = r2 =: r 的情况。我们仍然首先必须将圆心 P1,P2 从 (lat,lng) 转换为笛卡尔坐标 (x,y,z)。

    var DEG2RAD = Math.PI/180;
    function LatLng2Cartesian(lat_deg,lng_deg)
    {
      var lat_rad = lat_deg*DEG2RAD;
      var lng_rad = lng_deg*DEG2RAD;
      var cos_lat = Math.cos(lat_rad);
      return {x: Math.cos(lng_rad)*cos_lat,
              y: Math.sin(lng_rad)*cos_lat,
              z: Math.sin(lat_rad)};
    }
    
    var P1 = LatLng2Cartesian(lat1, lng1);
    var P2 = LatLng2Cartesian(lat2, lng2);
    

    但是可以更容易地计算出包含圆的平面的交线。设d 为实际圆心(平面内)到曲面上对应点 P1 或 P2 的距离。一个简单的推导显示(R为地球半径):

    var R = 6371; // earth radius in km
    var r = 100; // the direct distance (in km) of the given points to the intersections points
    // if the value rs for the distance along the surface is known, it has to be converted:
    // var r = 2*R*Math.sin(rs/(2*R*Math.PI));
    var d = r*r/(2*R);
    

    现在让 S1 和 S2 成为交点,S 成为它们的中点。对于 s = |OS|t = |SS1| = |SS2|(其中 O = (0,0,0) 是地球的中心),我们可以从简单的推导中得到:

    var a = Math.acos(P1.x*P2.x + P1.y*P2.y + P1.z*P2.z); // the angle P1OP2
    var s = (R-d)/Math.cos(a/2);
    var t = Math.sqrt(R*R - s*s);
    

    现在由于r1 = r2 点 S、S1、S2 位于 P1 和 P2 之间的中间平面中。对于v_s = OS,我们得到:

    function vecLen(v)
    { return Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z); }
    function vecScale(scale,v)
    { return {x: scale*v.x, y: scale*v.y, z: scale*v.z}; }
    
    var v = {x: P1.x+P2.x, y: P1.y+P2.y, z:P1.z+P2.z}; // P1+P2 is in the middle of OP1 and OP2
    var S = vecScale(s/vecLen(v), v);
    
    function crossProd(v1,v2)
    {
      return {x: v1.y*v2.z - v1.z*v2.y,
              y: v1.z*v2.x - v1.x*v2.z,
              z: v1.x*v2.y - v1.y*v2.x};
    }
    var n = crossProd(P1,P2); // normal vector to plane OP1P2 = vector along S1S2
    var SS1 = vecScale(t/vecLen(n),n);
    
    var S1 = {x: S.x+SS1.x, y: S.y+SS1.y, z: S.z+SS1.z}; // S + SS1
    var S2 = {x: S.x-SS1.x, y: S.y-SS2.y, z: S.z-SS1.z}; // S - SS1
    

    最后我们必须转换回 (lat,lng):

    function Cartesian2LatLng(P)
    {
      var P_xy = {x: P.x, y:P.y, z:0}
      return {lat: Math.atan2(P.y,P.x)/DEG2RAD, lng: Math.atan2(P.z,vecLen(P_xy))/DEG2RAD};
    }
    var S1_latlng = Cartesian2LatLng(S1);
    var S2_latlng = Cartesian2LatLng(S2);
    

    【讨论】:

      【解决方案3】:

      Yazanpro,很抱歉对此迟到的回应。

      您可能对 MBo 方法的简洁变体感兴趣,它在两个方面进行了简化:

      • 首先通过利用 google.maps API 的一些内置功能来避免大量的数学运算。
      • 其次,使用2D模型来计算夹角,代替MBo的球面模型。我最初不确定这种简化的有效性,但我对a fork of MBo's fiddle 中的测试感到满意,即误差很小,但相对于地球大小而言是最大的圆圈(例如,在低缩放级别下)。

      函数如下:

      function getIntersections(circleA, circleB) {
          /* 
           * Find the points of intersection of two google maps circles or equal radius
           * circleA: a google.maps.Circle object 
           * circleB: a google.maps.Circle object
           * returns: null if 
           *    the two radii are not equal 
           *    the two circles are coincident
           *    the two circles don't intersect
           * otherwise returns: array containing the two points of intersection of circleA and circleB
           */
      
          var R, centerA, centerB, D, h, h_;
      
          try {
      
              R = circleA.getRadius();
              centerA = circleA.getCenter();
              centerB = circleB.getCenter();
      
              if(R !== circleB.getRadius()) {
                  throw( new Error("Radii are not equal.") );
              }
              if(centerA.equals(centerB)) {
                  throw( new Error("Circle centres are coincident.") );
              }
      
              D = google.maps.geometry.spherical.computeDistanceBetween(centerA, centerB); //Distance between the two centres (in meters)
      
              // Check that the two circles intersect
              if(D > (2 * R)) {
                  throw( new Error("Circles do not intersect.") );
              }
      
              h = google.maps.geometry.spherical.computeHeading(centerA, centerB); //Heading from centre of circle A to centre of circle B. (in degrees)
              h_ = Math.acos(D / 2 / R) * 180 / Math.PI; //Included angle between the intersections (for either of the two circles) (in degrees). This is trivial only because the two radii are equal.
      
              //Return an array containing the two points of intersection as google.maps.latLng objects
              return [
                  google.maps.geometry.spherical.computeOffset(centerA, R, h + h_),
                  google.maps.geometry.spherical.computeOffset(centerA, R, h - h_)
              ];
          }
          catch(e) {
              console.error("getIntersections() :: " + e.message);
              return null;
          }
      }
      

      顺便说一句,不要不尊重 MBo - 这是一个很好的答案。

      【讨论】:

      • 感谢您指出这一点。令我惊讶的是,即使您认为是 2D,您的解决方案在地图上也比 MBo 的更准确!
      • Yazanpro,是的,我在发帖后玩了一下,发现是一样的。要考虑的一件事是,我的解决方案在一个方面只是 2D - h_ 的计算(MBo 的 A)。我只能假设整体计算对这种简化不是特别敏感,并且更高的准确性来自于使用.computeDistanceBetween()', .computeHeading()` 和.computeOffset() 的google.maps.geometry.spherical API。 @MBo 可能对此有看法 - 他似乎比我更了解整个主题(尽管我的解决方案更准确:-))。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-29
      • 1970-01-01
      • 2015-06-10
      • 1970-01-01
      • 1970-01-01
      • 2013-05-05
      • 1970-01-01
      相关资源
      最近更新 更多