Перемещение CLLocation на x метров
У меня установлен CLLocation, и я бы хотел переместить эту точку x метров на восток и y метров на юг. Как я могу это достичь?
Ответы
Ответ 1
Есть функция C, которая близка к тому, что вы спрашиваете, но она требует пеленга и расстояния. Это в моем классе UtilitiesGeo на github. Вы передадите широту и долготу от вашего CLLocation к нему, а затем создадите новый CLLocation из результирующих lat2 и lon2, которые он возвращает:
/*-------------------------------------------------------------------------
* 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);
Если вы не можете использовать это, посмотрите на алгоритмы, которые были получены здесь и здесь, и, возможно, вы можете изменить его, или эти сайты могут иметь что-то ближе к вашим потребностям.
Ответ 2
Переход в Swift, взятый из этого answer:
func locationWithBearing(bearing: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(bearing))
let lon2 = lon1 + atan2(sin(bearing) * sin(distRadians) * cos(lat1), cos(distRadians) - sin(lat1) * sin(lat2))
return CLLocationCoordinate2D(latitude: lat2 * 180 / M_PI, longitude: lon2 * 180 / M_PI)
}
Морган Чен написал это:
Вся математика в этом методе выполняется в радианах. В начале метод, lon1 и lat1 преобразуются в радианы для этой цели, поскольку Что ж. Подшипник также находится в радианах. Имейте в виду, что этот метод учтите кривизну Земли, которую вам действительно не нужно делать для небольших расстояний.
Ответ 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;
}
Ответ 4
Улучшено быстрое решение для Peters. Только коррекция - это то, что подшипник должен быть радианным, когда расчет выполнен.
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)
}
Ответ 5
Незначительная настройка на @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
Ответ 6
Быстрая реализация с использованием 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)
}
}
Ответ 7
Более простое решение - использовать 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)
Не требуется сложных расчетов конверсии.
Ответ 8
Swift 4.2 как расширение CGPoint
Получено из решения Петра О.
Расширение FloatingPoint: спасибо fooobar.com/questions/28279/...
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)
Ответ 9
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)
}
}
использование
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
Ответ 10
Странно, что никто не думал об использовании MKCoordinateRegion из MapKit для автоматического расчета этого.
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
}
}