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?
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.
A simpler solution is to use MKMapPoints.
Convert your original coordinates, and any offset distances you need to MKMapPoints using this:
let coordinatesInMapPoints = MKMapPointForCoordinate(CLLocationCoordinate2D)
let distancesInMapPoints = yourDistanceInMeters * MKMapPointsPerMeterAtLatitude(CLLocationDegrees) // Do this for both x and y directions if needed.
Then make a new MKMapPoint by simply adding your offset distances to your original coordinates:
let newCoordinatesInMapPoints = MKMapPointMake(coordinatesInMapPoints.x + distancesInMapPoints, coordinatesInMapPoints.y)
Finally, convert the new coordinates from a MKMapPoint back to CLLocationCoordinate2D:
let newCoordinate = MKCoordinateForMapPoint(newCoordinatesInMapPoints)
No complex conversion calculations needed.
Slight adjustment to @CocoaChris answer: now a category on CLLocation, and using the built-in units.
#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
Swift implementation using Measurement
struct to do the conversions between degrees and radians.
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)
}
}
I posted an updated answer to a measurement question, which includes an answer to this plotting one. Here: CLLocation Category for Calculating Bearing w/ Haversine function
Strange that nobody thought of using MKCoordinateRegion from MapKit to calculate that automatically.
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
}
}