Moving a CLLocation by x meters

前端 未结 11 939
余生分开走
余生分开走 2020-11-30 04:54

I have a CLLocation defined, and I\'d like to move that point x meters to the east and y meters to the south. How may I achieve that?

相关标签:
11条回答
  • 2020-11-30 05:29

    Swift 4.2 as a CGPoint extension

    Derived from Peter O.'s solution

    FloatingPoint extension: thanks to 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)
        }
    }
    

    Usage:

    let loc = CGPoint(x: lon, y: lat)
    let newLoc = loc.locationWithBearing(bearing: 90.degreesToRadians, distanceMeters: 500.0)
    
    0 讨论(0)
  • 2020-11-30 05:33

    A conversion to Swift, taken from 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)
    }
    

    Morgan Chen wrote this:

    All of the math in this method is done in radians. At the start of the method, lon1 and lat1 are converted to radians for this purpose as well. Bearing is in radians too. Keep in mind this method takes into account the curvature of the Earth, which you don't really need to do for small distances.

    0 讨论(0)
  • 2020-11-30 05:33

    Improved swift solution to Peters answer. Only correction is the bearing should be radian while calculation has been made.

     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)
    }
    
    0 讨论(0)
  • 2020-11-30 05:33

    Swift 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)
        }
    }
    

    Usage

        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
    
    0 讨论(0)
  • 2020-11-30 05:34

    Great post, here's the Obj-C wrapper for those who love copy/paste:

    - (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;
    }
    
    0 讨论(0)
提交回复
热议问题