【问题标题】:Moving a CLLocation by x meters将 CLLocation 移动 x 米
【发布时间】:2011-11-08 20:04:47
【问题描述】:

我定义了一个 CLLocation,我想将该点向东移动 x 米,向南移动 y 米。我怎样才能做到这一点?

【问题讨论】:

  • 请记住,您不能总是从地球上的所有点向北/向南移动——例如,如果您在北极,则只能向南移动。你想在这里做什么?
  • @duskwuff :为了简单起见,我希望我的 userPos 向右走 100 步,然后向后走 100 步 :-)

标签: iphone cocoa-touch core-location latitude-longitude


【解决方案1】:

转换为 Swift,取自 this answer:

func locationWithBearing(bearingRadians:Double, distanceMeters:Double, origin:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
    let distRadians = distanceMeters / (6372797.6) // earth radius in meters

    let lat1 = origin.latitude * M_PI / 180
    let lon1 = origin.longitude * M_PI / 180

    let lat2 = asin(sin(lat1) * cos(distRadians) + cos(lat1) * sin(distRadians) * cos(bearingRadians))
    let lon2 = lon1 + atan2(sin(bearingRadians) * sin(distRadians) * cos(lat1), cos(distRadians) - sin(lat1) * sin(lat2))

    return CLLocationCoordinate2D(latitude: lat2 * 180 / M_PI, longitude: lon2 * 180 / M_PI)
}

摩根陈写道:

此方法中的所有数学运算都以弧度为单位。在开始时 为此,将 lon1 和 lat1 转换为弧度为 好吧。方位也是弧度。请记住,此方法需要考虑 考虑地球的曲率,您实际上并不需要这样做 适合小距离。

我的 cmets(2021 年 3 月 25 日):

此方法中使用的计算称为求解“直接测地线问题”,C.F.F. 对此进行了讨论。 Karney 的文章“Algorithms for geodesics”,2012 年。上面给出的代码使用的技术不如 Karney 文章中介绍的算法准确。

【讨论】:

  • 亲爱的彼得,感谢您的快速解决方案,但我需要纠正一件事。方位角应该是弧度而不是双倍。我将其作为答案发布,因为我无法在此处粘贴代码。
  • 如果有人不清楚,方位指的是你要前进的方向,以度为单位,所以对于北:方位 = 0,对于东:方位 = 90,对于西南:方位 = 225,等等……
  • 一个注意事项是它不会换行(即 181 lat 不会换行到 -181)。
  • @ShakedSayag 请注意,答案的当前迭代中的方位角是以弧度为单位的,所以对于北方:bearing = 0,对于东方:bearing = 0.5 pi,对于南方:bearing = pi ;等等。
  • 速度的准确性非常好。很好的解决方案。谢谢!
【解决方案2】:

改进了彼得斯回答的快速解决方案。唯一的修正是在计算时方位角应该是弧度。

 func locationWithBearing(bearing:Double, distanceMeters:Double, origin:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
    let distRadians = distanceMeters / (6372797.6)

    var rbearing = bearing * M_PI / 180.0

    let lat1 = origin.latitude * M_PI / 180
    let lon1 = origin.longitude * M_PI / 180

    let lat2 = asin(sin(lat1) * cos(distRadians) + cos(lat1) * sin(distRadians) * cos(rbearing))
    let lon2 = lon1 + atan2(sin(rbearing) * sin(distRadians) * cos(lat1), cos(distRadians) - sin(lat1) * sin(lat2))

    return CLLocationCoordinate2D(latitude: lat2 * 180 / M_PI, longitude: lon2 * 180 / M_PI)
}

【讨论】:

  • 我已经应用了您的解决方案。大多数情况下它工作正常,但也会发生一些偏差。这是为什么。在这里提问:stackoverflow.com/questions/36382149/…
  • 我的实现几乎相同(我的实现与所有答案都相同),但我又遇到了反转位置的问题!据我所知,我们应该添加 -(distance) 但它有一个错误(十进制数字并指向有点远的地方),我无法再次提取原始位置!有什么想法吗?
【解决方案3】:

很棒的帖子,这是为喜欢复制/粘贴的人准备的 Obj-C 包装器:

- (CLLocationCoordinate2D) locationWithBearing:(float)bearing distance:(float)distanceMeters fromLocation:(CLLocationCoordinate2D)origin {
    CLLocationCoordinate2D target;
    const double distRadians = distanceMeters / (6372797.6); // earth radius in meters

    float lat1 = origin.latitude * M_PI / 180;
    float lon1 = origin.longitude * M_PI / 180;

    float lat2 = asin( sin(lat1) * cos(distRadians) + cos(lat1) * sin(distRadians) * cos(bearing));
    float lon2 = lon1 + atan2( sin(bearing) * sin(distRadians) * cos(lat1),
                     cos(distRadians) - sin(lat1) * sin(lat2) );

    target.latitude = lat2 * 180 / M_PI;
    target.longitude = lon2 * 180 / M_PI; // no need to normalize a heading in degrees to be within -179.999999° to 180.00000°

    return target;
}

【讨论】:

  • 无法正常工作。使用相同的方位为不同的距离提供不同的位置。
  • 在车辆正在移动并且您想知道精确时刻的位置和距离的情况下,另一个有用的添加/改进将是根据估计值计算自上次位置以来的距离移动车辆的速度。我认为您可以通过从 GPS 信号(时间戳变量)计算时间滞后于设备时钟时间来做到这一点。 Apple 设备以 1 赫兹采样 GPS 位置,但具有与 CLLocation 交互的 API 的第三方 GPS 接收器以 4 到 10 赫兹采样(例如 Dual 150 和 160 型号)。
【解决方案4】:

有一个 C 函数与您的要求接近,但它需要一个方位和距离。它在我的 github 的 UtilitiesGeo 课程中。您可以将 CLLocation 中的纬度和经度传递给它,然后从它返回的结果 lat2 和 lon2 创建一个新的 CLLocation:

/*-------------------------------------------------------------------------
* Given a starting lat/lon point on earth, distance (in meters)
* and bearing, calculates destination coordinates lat2/lon2.
*
* all params in degrees
*-------------------------------------------------------------------------*/
void destCoordsInDegrees(double lat1, double lon1,
                         double distanceMeters, double bearing,
                         double* lat2, double* lon2);

如果您不能使用它,请查看从 herehere 派生的算法,也许您可​​以修改它,或者这些网站可能有更接近您需求的东西。

【讨论】:

  • 这看起来非常接近(实际上接近完美)我正在寻找的东西。轴承参数会有所帮助:-)我今晚会试试这个。
  • 这个函数是使用distanceMeters作为球体表面的米,还是穿过球体?
  • 你为什么要标准化180经度而不是纬度?
  • normalize180 使经度始终在 -180 到 +180 的范围内。
  • 我的实现几乎相同(我的实现与所有答案都相同),但我又遇到了反转位置的问题!据我所知,我们应该添加 -(distance) 但它有一个错误(十进制数字并指向有点远的地方),我无法再次提取原始位置!有什么想法吗?
【解决方案5】:

奇怪的是,没有人想到使用 MapKit 中的 MKCoordinateRegion 来自动计算。

import MapKit

extension CLLocation {
    func movedBy(latitudinalMeters: CLLocationDistance, longitudinalMeters: CLLocationDistance) -> CLLocation {
        let region = MKCoordinateRegion(center: coordinate, latitudinalMeters: abs(latitudinalMeters), longitudinalMeters: abs(longitudinalMeters))

        let latitudeDelta = region.span.latitudeDelta
        let longitudeDelta = region.span.longitudeDelta

        let latitudialSign = CLLocationDistance(latitudinalMeters.sign == .minus ? -1 : 1)
        let longitudialSign = CLLocationDistance(longitudinalMeters.sign == .minus ? -1 : 1)

        let newLatitude = coordinate.latitude + latitudialSign * latitudeDelta
        let newLongitude = coordinate.longitude + longitudialSign * longitudeDelta

        let newCoordinate = CLLocationCoordinate2D(latitude: newLatitude, longitude: newLongitude)

        let newLocation = CLLocation(coordinate: newCoordinate, altitude: altitude, horizontalAccuracy: horizontalAccuracy, verticalAccuracy: verticalAccuracy, course: course, speed: speed, timestamp: Date())

        return newLocation
    }
}

【讨论】:

    【解决方案6】:

    对@CocoaChris 的回答稍作调整:现在是 CLLocation 上的一个类别,并使用内置单位。

    #import <CoreLocation/CoreLocation.h>
    
    
    @interface CLLocation (Movement)
    
    - (CLLocation *)locationByMovingDistance:(double)distanceMeters withBearing:(CLLocationDirection)bearingDegrees;
    
    @end
    
    
    @implementation CLLocation (Movement)
    
    - (CLLocation *)locationByMovingDistance:(double)distanceMeters withBearing:(CLLocationDirection)bearingDegrees
    {
        const double distanceRadians = distanceMeters / (6372797.6); // earth radius in meters
        const double bearingRadians = bearingDegrees * M_PI / 180;
    
        float lat1 = self.coordinate.latitude * M_PI / 180;
        float lon1 = self.coordinate.longitude * M_PI / 180;
    
        float lat2 = asin(sin(lat1) * cos(distanceRadians) + cos(lat1) * sin(distanceRadians) * cos(bearingRadians));
        float lon2 = lon1 + atan2(sin(bearingRadians) * sin(distanceRadians) * cos(lat1),
                                  cos(distanceRadians) - sin(lat1) * sin(lat2) );
    
        return [[CLLocation alloc] initWithLatitude:lat2 * 180 / M_PI
                                          longitude:lon2 * 180 / M_PI];
    }
    
    @end
    

    【讨论】:

    • 我的实现几乎相同(我的实现与所有答案都相同),但我又遇到了反转位置的问题!据我所知,我们应该添加 -(distance) 但它有一个错误(十进制数字并指向有点远的地方),我无法再次提取原始位置!有什么想法吗?
    • 嗨@Mohamad,这可能是一个数值精度错误,您是否尝试过使用双精度而不是浮点数?
    • 嗨@joerick,我使用的是double,即使在参考网站movable-type.co.uk/scripts/latlong.html“目标点给定距离和距起点的方位”我不知道出了什么问题。
    【解决方案7】:

    Swift 实现使用Measurement struct 进行度数和弧度之间的转换。

    class GPSLocation {
    
    public class func degreesToRadians(degrees: Double) -> Double {
            return Measurement(value: degrees, unit: UnitAngle.degrees).converted(to: .radians).value
        }
    
        public class func radiansToDegrees(radians: Double) -> Double {
            return Measurement(value: radians, unit: UnitAngle.radians).converted(to: .degrees).value
        }
    
        public class func location(location: CLLocation, byMovingDistance distance: Double, withBearing bearingDegrees:CLLocationDirection) -> CLLocation {
            let distanceRadians: Double = distance / 6372797.6
            let bearingRadians: Double = GPSLocation.degreesToRadians(degrees: bearingDegrees)
    
            let lat1 = GPSLocation.degreesToRadians(degrees: location.coordinate.latitude)
            let lon1 = GPSLocation.degreesToRadians(degrees: location.coordinate.longitude)
    
            let lat2 = GPSLocation.radiansToDegrees(radians:asin(sin(lat1) * cos(distanceRadians) + cos(lat1) * sin(distanceRadians) * cos(bearingRadians)))
            let lon2 = GPSLocation.radiansToDegrees(radians:lon1 + atan2(sin(bearingRadians) * sin(distanceRadians * cos(lat1)), cos(distanceRadians) - sin(lat1) * sin(lat2)))
    
            return CLLocation(latitude: lat2, longitude: lon2)
        }
    
    }
    

    【讨论】:

      【解决方案8】:

      更简单的解决方案是使用 MKMapPoints。

      使用此方法将您的原始坐标以及您需要的任何偏移距离转换为 MKMapPoints:

      let coordinatesInMapPoints = MKMapPointForCoordinate(CLLocationCoordinate2D)
      let distancesInMapPoints = yourDistanceInMeters * MKMapPointsPerMeterAtLatitude(CLLocationDegrees) // Do this for both x and y directions if needed.
      

      然后通过简单地将偏移距离添加到原始坐标来创建一个新的 MKMapPoint:

      let newCoordinatesInMapPoints = MKMapPointMake(coordinatesInMapPoints.x + distancesInMapPoints, coordinatesInMapPoints.y)
      

      最后,将新坐标从 MKMapPoint 转换回 CLLocationCoordinate2D:

      let newCoordinate = MKCoordinateForMapPoint(newCoordinatesInMapPoints)
      

      无需复杂的转换计算。

      【讨论】:

      • y方向怎么做?
      • 如果偏移距离不同,则使用不同的“yourDistanceInMeters”重复第 2 行并将其添加到“coordinatesInMapPoints.y”。如果偏移距离相同,则只需将“distancesInMapPoints”添加到“coordinatesInMapPoints.y”
      【解决方案9】:

      Swift 4.2 作为 CGPoint 扩展

      源自 Peter O. 的解决方案

      浮点扩展:感谢https://stackoverflow.com/a/29179878/2500428

      extension FloatingPoint
      {
          var degreesToRadians: Self { return self * .pi / 180 }
          var radiansToDegrees: Self { return self * 180 / .pi }
      }
      
      extension CGPoint
      {
          // NOTE: bearing is in radians
          func locationWithBearing(bearing: Double, distanceMeters: Double) -> CGPoint
          {
              let distRadians = distanceMeters / (6372797.6) // earth radius in meters
      
              let origLat = Double(self.y.degreesToRadians)
              let origLon = Double(self.x.degreesToRadians)
      
              let newLat = asin(sin(origLat) * cos(distRadians) + cos(origLat) * sin(distRadians) * cos(bearing))
              let newLon = origLon + atan2(sin(bearing) * sin(distRadians) * cos(origLat), cos(distRadians) - sin(origLat) * sin(newLat))
      
              return CGPoint(x: newLon.radiansToDegrees, y: newLat.radiansToDegrees)
          }
      }
      

      用法:

      let loc = CGPoint(x: lon, y: lat)
      let newLoc = loc.locationWithBearing(bearing: 90.degreesToRadians, distanceMeters: 500.0)
      

      【讨论】:

        【解决方案10】:

        斯威夫特 4

        extension CLLocationCoordinate2D {
        
            /// Get coordinate moved from current to `distanceMeters` meters with azimuth `azimuth` [0, Double.pi)
            ///
            /// - Parameters:
            ///   - distanceMeters: the distance in meters
            ///   - azimuth: the azimuth (bearing)
            /// - Returns: new coordinate
            func shift(byDistance distanceMeters: Double, azimuth: Double) -> CLLocationCoordinate2D {
                let bearing = azimuth
                let origin = self
                let distRadians = distanceMeters / (6372797.6) // earth radius in meters
        
                let lat1 = origin.latitude * Double.pi / 180
                let lon1 = origin.longitude * Double.pi / 180
        
                let lat2 = asin(sin(lat1) * cos(distRadians) + cos(lat1) * sin(distRadians) * cos(bearing))
                let lon2 = lon1 + atan2(sin(bearing) * sin(distRadians) * cos(lat1), cos(distRadians) - sin(lat1) * sin(lat2))
                return CLLocationCoordinate2D(latitude: lat2 * 180 / Double.pi, longitude: lon2 * 180 / Double.pi)
            }
        }
        

        用法

            let point: CLLocationCoordinate2D!
            let north100 = point.shift(byDistance: 100, azimuth: 0) // 100m to North
            let south100 = point.shift(byDistance: 100, azimuth: Double.pi) // 100m to South
        

        【讨论】:

        • 这个答案很清楚。您能否分享“向东 100 米”和“向西 100 米”的方位角值?
        • @thus West = pi/2, East = -pi/2
        【解决方案11】:

        我发布了一个测量问题的更新答案,其中包括这个绘图问题的答案。这里:CLLocation Category for Calculating Bearing w/ Haversine function

        【讨论】:

          猜你喜欢
          • 2016-07-22
          • 1970-01-01
          • 2016-07-23
          • 2014-12-23
          • 2011-03-05
          • 1970-01-01
          • 2019-05-16
          • 1970-01-01
          相关资源
          最近更新 更多