2011-09-02 14 views
16

Ho definito una CLLocation e mi piacerebbe spostare quel punto x metri a est e y metri a sud. Come posso ottenere quello?Spostamento di una posizione con x metri

+2

Ricorda che non puoi sempre spostarti verso nord/sud da tutti i punti del globo, ad esempio, se ti trovi al Polo Nord, puoi solo andare verso sud. Cosa stai cercando di fare qui? – duskwuff

+0

@duskwuff: Per semplificare, voglio che il mio userPos faccia 100 passi a destra e 100 passi indietro :-) – Oliver

risposta

4

C'è una funzione C che è vicina a ciò che si sta chiedendo ma richiede rilevamento e distanza. È nella mia classe UtilitiesGeo su github. Si potrebbe passare la latitudine e la longitudine dal vostro CLLocation ad esso e quindi creare un nuovo CLLocation dai Lat2 risultanti e lon2 che restituisca:

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

Se non è possibile utilizzare che, un'occhiata agli algoritmi che è stato derivato da here e here e forse è possibile modificarlo o tali siti potrebbero avere qualcosa di più vicino alle proprie esigenze.

+0

che sembra molto vicino (quasi perfetto di fatto) di ciò che sto cercando. Il cuscinetto param aiuterà :-) Ci proverò stasera. – Oliver

+0

Questa funzione utilizza distanceMeters come metri sulla superficie della sfera o attraverso la sfera? – Oliver

+0

Perché normalizzi 180 la longitudine e non la latitudine? – Oliver

16

Grande dopo, ecco l'involucro Obj-C per chi ama il copia/incolla:

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

Non funziona correttamente. Fornisce una posizione diversa per la diversa distanza con lo stesso rilevamento. – kirander

19

Una conversione a Swift, tratto da this 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) 
} 

Morgan Chen ha scritto questo :

Tutti i calcoli in questo metodo sono fatti in radianti. All'inizio del metodo , lon1 e lat1 vengono convertiti in radianti per questo scopo come . Il rilevamento è anche in radianti. Tieni presente che questo metodo prende in l'account la curvatura della Terra, che non devi veramente fare per piccole distanze.

+0

Caro Peter, grazie per la soluzione rapida, ma ho bisogno di correggere una cosa. Il rilevamento dovrebbe essere in radianti non doppio. Lo posterò come risposta poiché non posso incollare il codice qui. –

+7

Se non è chiaro a nessuno, il rilevamento si riferisce alla direzione che si desidera avanzare, in gradi, quindi per nord: rilevamento = 0, per est: rilevamento = 90, per sud-ovest: rilevamento = 225, ecc ... –

14

Soluzione rapida migliorata alla risposta di Peters. Solo la correzione è il rilevamento dovrebbe essere radiante mentre il calcolo è stato effettuato.

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

I ' ho applicato la tua soluzione. Principalmente funziona bene ma c'è anche qualche deviazione. Perché. Chiesto qui: http://stackoverflow.com/questions/36382149/moving-gmsmarker-by-x-meters-deflects –

3

leggero aggiustamento per @CocoaChris risposta: ora una categoria CLLocation, e utilizzando il built-in unità.

#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 
1

Swift implementazione utilizzando Measurement struct per fare le conversioni tra gradi e radianti.

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

} 
Problemi correlati