Перемещение CLLocation на х метров - PullRequest
21 голосов
/ 02 сентября 2011

У меня определено местоположение CLL, и я бы хотел переместить эту точку на x метров на восток и на y метров на юг. Как мне этого достичь?

Ответы [ 10 ]

27 голосов
/ 22 октября 2014

Преобразование в Свифт, взятое из этого ответа :

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 преобразуются в радианы для этой цели как Что ж. Подшипник тоже в радианах. Имейте в виду, что этот метод учитывает учесть кривизну Земли, что вам на самом деле не нужно делать на небольшие расстояния.

16 голосов
/ 30 июня 2015

Улучшено быстрое решение для ответа Петерса.Единственное исправление заключается в том, что подшипник должен быть радиан, пока был произведен расчет.

 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)
}
16 голосов
/ 27 ноября 2013

Отличный пост, вот обертка 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;
}
6 голосов
/ 02 сентября 2011

Существует функция 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);

Если вы не можете использовать это, взгляните наалгоритмы, которые были получены из здесь и здесь и, возможно, вы можете изменить его, или эти сайты могут иметь что-то ближе к вашим потребностям.

4 голосов
/ 12 октября 2016

Небольшая настройка ответа @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
3 голосов
/ 22 марта 2018

Более простое решение - использовать 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)

Не требуется сложных вычислений преобразования.

3 голосов
/ 24 октября 2017

Быстрая реализация, использующая 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)
    }

}
1 голос
/ 04 марта 2019

Swift 4.2 в качестве расширения CGPoint

Получено из решения Петра О.

Расширение FloatingPoint: благодаря 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)
0 голосов
/ 28 апреля 2019

Странно, что никто не думал об использовании 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
    }
}
0 голосов
/ 01 апреля 2019

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
...