【问题标题】:Determining Midpoint Between 2 Coordinates确定 2 个坐标之间的中点
【发布时间】:2012-05-20 12:20:25
【问题描述】:

我正在尝试确定MKMapView 中两个位置之间的中点。我遵循here(和here)概述的方法并在Objective-C中重写了它,但地图的中心是巴芬岛东北部的某个地方,离这两点不远。

我的方法基于上面链接的java方法:

+(CLLocationCoordinate2D)findCenterPoint:(CLLocationCoordinate2D)_lo1 :(CLLocationCoordinate2D)_loc2 {
    CLLocationCoordinate2D center;

    double lon1 = _lo1.longitude * M_PI / 180;
    double lon2 = _loc2.longitude * M_PI / 100;

    double lat1 = _lo1.latitude * M_PI / 180;
    double lat2 = _loc2.latitude * M_PI / 100;

    double dLon = lon2 - lon1;

    double x = cos(lat2) * cos(dLon);
    double y = cos(lat2) * sin(dLon);

    double lat3 = atan2( sin(lat1) + sin(lat2), sqrt((cos(lat1) + x) * (cos(lat1) + x) + y * y) );
    double lon3 = lon1 + atan2(y, cos(lat1) + x);

    center.latitude  = lat3 * 180 / M_PI;
    center.longitude = lon3 * 180 / M_PI;

    return center;
}

这2个参数有如下数据:

_loc1:
    latitude = 45.4959839
    longitude = -73.67826455

_loc2:
    latitude = 45.482889
    longitude = -73.57522299

以上内容在地图上正确放置(蒙特利尔及其周边地区)。我试图将地图居中在 2 之间的中点,但我的方法返回以下内容:

latitude = 65.29055
longitude = -82.55425

在北极的某个地方,应该在南边 500 英里左右。

【问题讨论】:

    标签: objective-c mapkit core-location


    【解决方案1】:

    只是一种预感,但我注意到您的 lon2lat2 变量是使用 M_PI/100 而不是 M_PI/180 计算的。

    double lon1 = _lo1.longitude * M_PI / 180;
    double lon2 = _loc2.longitude * M_PI / 100;
    
    double lat1 = _lo1.latitude * M_PI / 180;
    double lat2 = _loc2.latitude * M_PI / 100;
    

    将这些更改为 180 可能会对您有所帮助。

    【讨论】:

      【解决方案2】:

      我觉得你想多了。做吧:

      float lon3 = ((lon1 + lon2) / 2)
      float lat3 = ((lat1 + lat2) / 2)
      

      lat3 和 lon3 将是中心点。

      【讨论】:

      • 这个答案不正确。这种方法演示了不应该如何解决主题问题。
      【解决方案3】:

      如果有人需要 Swift 中的代码,我已经在 Swift 中编写了库函数来计算多个坐标之间的中点:

      //        /** Degrees to Radian **/
      class func degreeToRadian(angle:CLLocationDegrees) -> CGFloat {
          return (  (CGFloat(angle)) / 180.0 * CGFloat(M_PI)  )
      }
      
      //        /** Radians to Degrees **/
      class func radianToDegree(radian:CGFloat) -> CLLocationDegrees {
          return CLLocationDegrees(  radian * CGFloat(180.0 / M_PI)  )
      }
      
      class func middlePointOfListMarkers(listCoords: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
      
          var x = 0.0 as CGFloat
          var y = 0.0 as CGFloat
          var z = 0.0 as CGFloat
      
          for coordinate in listCoords{
              var lat:CGFloat = degreeToRadian(coordinate.latitude)
              var lon:CGFloat = degreeToRadian(coordinate.longitude)
              x = x + cos(lat) * cos(lon)
              y = y + cos(lat) * sin(lon)
              z = z + sin(lat)
          }
      
          x = x/CGFloat(listCoords.count)
          y = y/CGFloat(listCoords.count)
          z = z/CGFloat(listCoords.count)
      
          var resultLon: CGFloat = atan2(y, x)
          var resultHyp: CGFloat = sqrt(x*x+y*y)
          var resultLat:CGFloat = atan2(z, resultHyp)
      
          var newLat = radianToDegree(resultLat)
          var newLon = radianToDegree(resultLon)
          var result:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: newLat, longitude: newLon)
      
          return result
      
      }
      

      详细答案可以找here

      为 Swift 5 更新

      func geographicMidpoint(betweenCoordinates coordinates: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
      
          guard coordinates.count > 1 else {
              return coordinates.first ?? // return the only coordinate
                  CLLocationCoordinate2D(latitude: 0, longitude: 0) // return null island if no coordinates were given
          }
      
          var x = Double(0)
          var y = Double(0)
          var z = Double(0)
      
          for coordinate in coordinates {
              let lat = coordinate.latitude.toRadians()
              let lon = coordinate.longitude.toRadians()
              x += cos(lat) * cos(lon)
              y += cos(lat) * sin(lon)
              z += sin(lat)
          }
      
          x /= Double(coordinates.count)
          y /= Double(coordinates.count)
          z /= Double(coordinates.count)
      
          let lon = atan2(y, x)
          let hyp = sqrt(x * x + y * y)
          let lat = atan2(z, hyp)
      
          return CLLocationCoordinate2D(latitude: lat.toDegrees(), longitude: lon.toDegrees())
      }
      

      }

      【讨论】:

      • 请嫁给我吧!谢了!
      【解决方案4】:

      对于 swift 用户,按照@dinjas 的建议更正变体

      import Foundation
      import MapKit
      extension CLLocationCoordinate2D {
          // MARK: CLLocationCoordinate2D+MidPoint
          func middleLocationWith(location:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
      
              let lon1 = longitude * M_PI / 180
              let lon2 = location.longitude * M_PI / 180
              let lat1 = latitude * M_PI / 180
              let lat2 = location.latitude * M_PI / 180
              let dLon = lon2 - lon1
              let x = cos(lat2) * cos(dLon)
              let y = cos(lat2) * sin(dLon)
      
              let lat3 = atan2( sin(lat1) + sin(lat2), sqrt((cos(lat1) + x) * (cos(lat1) + x) + y * y) )
              let lon3 = lon1 + atan2(y, cos(lat1) + x)
      
              let center:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat3 * 180 / M_PI, lon3 * 180 / M_PI)
              return center
          }
      }
      

      【讨论】:

      • 谢谢 - 但老兄 - 这是否解释了它可能穿过本初子午线的事实?
      【解决方案5】:

      重要的是要说 OP 用于计算地理中点的公式是基于 this formula 的,它解释了 cos/sin/sqrt 计算。

      此公式将为您提供任何距离的地理中点,包括四个四分之一和本初子午线。

      但是,如果您的计算是针对 1 公里左右的短程,则使用简单平均值将产生相同的中点结果。 即:

      let firstPoint = CLLocation(....)
      let secondPoint = CLLocation(....)
      
      let midPointLat = (firstPoint.coordinate.latitude + secondPoint.coordinate.latitude) / 2
      let midPointLong = (firstPoint.coordinate.longitude + secondPoint.coordinate.longitude) / 2
      

      您实际上可以将它用于 10 公里,但预计会出现偏差 - 如果您只需要通过快速解决方案对短程中点进行估计就足够了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-09
        • 1970-01-01
        • 2012-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多