Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moving a CLLocation by x meters

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?

like image 228
Oliver Avatar asked Sep 02 '11 00:09

Oliver


4 Answers

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.

My comments (Mar. 25, 2021):

The calculation used in this method is called solving the "direct geodesic problem", and this is discussed in C.F.F. Karney's article "Algorithms for geodesics", 2012. The code given above uses a technique that is less accurate than the algorithms presented in Karney's article.

like image 56
4 revs, 3 users 92% Avatar answered Nov 18 '22 16:11

4 revs, 3 users 92%


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)
}
like image 36
Koray Birand Avatar answered Nov 18 '22 15:11

Koray Birand


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;
}
like image 16
CocoaChris Avatar answered Nov 18 '22 17:11

CocoaChris


There is a C function that is close to what you are asking but it takes a bearing and distance. It's in my UtilitiesGeo class on github. You would pass the latitude and longitude from your CLLocation to it and then create a new CLLocation from the resulting lat2 and lon2 that it returns:

/*-------------------------------------------------------------------------
* 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);

If you can't use that, take a look at the algorithms that it was derived from here and here and perhaps you can modify it or those sites might have something closer to your needs.

like image 6
progrmr Avatar answered Nov 18 '22 16:11

progrmr